Merge branch 'master' of ssh://gerrit.onlab.us:29418/onos-next
Showing
88 changed files
with
2181 additions
and
552 deletions
... | @@ -2,35 +2,40 @@ | ... | @@ -2,35 +2,40 @@ |
2 | <project xmlns="http://maven.apache.org/POM/4.0.0" | 2 | <project xmlns="http://maven.apache.org/POM/4.0.0" |
3 | xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" | 3 | xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" |
4 | xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd"> | 4 | xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd"> |
5 | - <modelVersion>4.0.0</modelVersion> | 5 | + <modelVersion>4.0.0</modelVersion> |
6 | 6 | ||
7 | - <parent> | 7 | + <parent> |
8 | - <groupId>org.onlab.onos</groupId> | 8 | + <groupId>org.onlab.onos</groupId> |
9 | - <artifactId>onos-apps</artifactId> | 9 | + <artifactId>onos-apps</artifactId> |
10 | - <version>1.0.0-SNAPSHOT</version> | 10 | + <version>1.0.0-SNAPSHOT</version> |
11 | - <relativePath>../pom.xml</relativePath> | 11 | + <relativePath>../pom.xml</relativePath> |
12 | - </parent> | 12 | + </parent> |
13 | 13 | ||
14 | - <artifactId>onos-app-sdnip</artifactId> | 14 | + <artifactId>onos-app-sdnip</artifactId> |
15 | - <packaging>bundle</packaging> | 15 | + <packaging>bundle</packaging> |
16 | 16 | ||
17 | - <description>SDN-IP peering application</description> | 17 | + <description>SDN-IP peering application</description> |
18 | 18 | ||
19 | - <dependencies> | 19 | + <dependencies> |
20 | - <dependency> | 20 | + <dependency> |
21 | - <groupId>org.codehaus.jackson</groupId> | 21 | + <groupId>org.codehaus.jackson</groupId> |
22 | - <artifactId>jackson-core-asl</artifactId> | 22 | + <artifactId>jackson-core-asl</artifactId> |
23 | - </dependency> | 23 | + </dependency> |
24 | - <dependency> | 24 | + <dependency> |
25 | - <groupId>org.codehaus.jackson</groupId> | 25 | + <groupId>org.codehaus.jackson</groupId> |
26 | - <artifactId>jackson-mapper-asl</artifactId> | 26 | + <artifactId>jackson-mapper-asl</artifactId> |
27 | - </dependency> | 27 | + </dependency> |
28 | - <dependency> | 28 | + <dependency> |
29 | - <groupId>com.fasterxml.jackson.core</groupId> | 29 | + <groupId>com.fasterxml.jackson.core</groupId> |
30 | - <artifactId>jackson-annotations</artifactId> | 30 | + <artifactId>jackson-annotations</artifactId> |
31 | - <version>2.4.2</version> | 31 | + <version>2.4.2</version> |
32 | - <scope>provided</scope> | 32 | + <scope>provided</scope> |
33 | - </dependency> | 33 | + </dependency> |
34 | - </dependencies> | 34 | + |
35 | + <dependency> | ||
36 | + <groupId>com.google.guava</groupId> | ||
37 | + <artifactId>guava</artifactId> | ||
38 | + </dependency> | ||
39 | + </dependencies> | ||
35 | 40 | ||
36 | </project> | 41 | </project> | ... | ... |
1 | +package org.onlab.onos.sdnip; | ||
2 | + | ||
3 | +import static com.google.common.base.Preconditions.checkNotNull; | ||
4 | + | ||
5 | +import java.util.Set; | ||
6 | + | ||
7 | +import org.apache.commons.lang.NotImplementedException; | ||
8 | +import org.onlab.onos.net.ConnectPoint; | ||
9 | +import org.onlab.onos.net.host.HostService; | ||
10 | +import org.onlab.onos.net.host.PortAddresses; | ||
11 | +import org.onlab.onos.sdnip.config.Interface; | ||
12 | +import org.onlab.packet.IpAddress; | ||
13 | + | ||
14 | +import com.google.common.collect.Sets; | ||
15 | + | ||
16 | + | ||
17 | + | ||
18 | +/** | ||
19 | + * Provides IntefaceService using PortAddresses data from the HostService. | ||
20 | + */ | ||
21 | +public class HostServiceBasedInterfaceService implements InterfaceService { | ||
22 | + | ||
23 | + private final HostService hostService; | ||
24 | + | ||
25 | + public HostServiceBasedInterfaceService(HostService hostService) { | ||
26 | + this.hostService = checkNotNull(hostService); | ||
27 | + } | ||
28 | + | ||
29 | + @Override | ||
30 | + public Set<Interface> getInterfaces() { | ||
31 | + Set<PortAddresses> addresses = hostService.getAddressBindings(); | ||
32 | + Set<Interface> interfaces = Sets.newHashSetWithExpectedSize(addresses.size()); | ||
33 | + for (PortAddresses a : addresses) { | ||
34 | + interfaces.add(new Interface(a)); | ||
35 | + } | ||
36 | + return interfaces; | ||
37 | + } | ||
38 | + | ||
39 | + @Override | ||
40 | + public Interface getInterface(ConnectPoint connectPoint) { | ||
41 | + checkNotNull(connectPoint); | ||
42 | + | ||
43 | + PortAddresses portAddresses = | ||
44 | + hostService.getAddressBindingsForPort(connectPoint); | ||
45 | + | ||
46 | + if (!portAddresses.ips().isEmpty()) { | ||
47 | + return new Interface(portAddresses); | ||
48 | + } | ||
49 | + | ||
50 | + return null; | ||
51 | + } | ||
52 | + | ||
53 | + @Override | ||
54 | + public Interface getMatchingInterface(IpAddress ipAddress) { | ||
55 | + // TODO implement | ||
56 | + throw new NotImplementedException("getMatchingInteface is not yet implemented"); | ||
57 | + } | ||
58 | + | ||
59 | +} |
1 | +package org.onlab.onos.sdnip; | ||
2 | + | ||
3 | +import java.util.Set; | ||
4 | + | ||
5 | +import org.onlab.onos.net.ConnectPoint; | ||
6 | +import org.onlab.onos.sdnip.config.Interface; | ||
7 | +import org.onlab.packet.IpAddress; | ||
8 | + | ||
9 | +/** | ||
10 | + * Provides information about the interfaces in the network. | ||
11 | + */ | ||
12 | +public interface InterfaceService { | ||
13 | + /** | ||
14 | + * Retrieves the entire set of interfaces in the network. | ||
15 | + * | ||
16 | + * @return the set of interfaces | ||
17 | + */ | ||
18 | + Set<Interface> getInterfaces(); | ||
19 | + | ||
20 | + /** | ||
21 | + * Retrieves the interface associated with the given connect point. | ||
22 | + * | ||
23 | + * @param connectPoint the connect point to retrieve interface information | ||
24 | + * for | ||
25 | + * @return the interface | ||
26 | + */ | ||
27 | + Interface getInterface(ConnectPoint connectPoint); | ||
28 | + | ||
29 | + /** | ||
30 | + * Retrieves the interface that matches the given IP address. Matching | ||
31 | + * means that the IP address is in one of the interface's assigned subnets. | ||
32 | + * | ||
33 | + * @param ipAddress IP address to match | ||
34 | + * @return the matching interface | ||
35 | + */ | ||
36 | + Interface getMatchingInterface(IpAddress ipAddress); | ||
37 | +} |
1 | +package org.onlab.onos.sdnip; | ||
2 | + | ||
3 | +import java.util.List; | ||
4 | + | ||
5 | +import org.onlab.onos.net.ConnectPoint; | ||
6 | +import org.onlab.onos.net.flow.DefaultTrafficSelector; | ||
7 | +import org.onlab.onos.net.flow.DefaultTrafficTreatment; | ||
8 | +import org.onlab.onos.net.flow.TrafficSelector; | ||
9 | +import org.onlab.onos.net.flow.TrafficTreatment; | ||
10 | +import org.onlab.onos.net.intent.IntentId; | ||
11 | +import org.onlab.onos.net.intent.IntentService; | ||
12 | +import org.onlab.onos.net.intent.PointToPointIntent; | ||
13 | +import org.onlab.onos.sdnip.config.BgpPeer; | ||
14 | +import org.onlab.onos.sdnip.config.BgpSpeaker; | ||
15 | +import org.onlab.onos.sdnip.config.Interface; | ||
16 | +import org.onlab.onos.sdnip.config.InterfaceAddress; | ||
17 | +import org.onlab.onos.sdnip.config.SdnIpConfigService; | ||
18 | +import org.onlab.packet.Ethernet; | ||
19 | +import org.onlab.packet.IPv4; | ||
20 | +import org.onlab.packet.IpAddress; | ||
21 | +import org.onlab.packet.IpPrefix; | ||
22 | +import org.slf4j.Logger; | ||
23 | +import org.slf4j.LoggerFactory; | ||
24 | + | ||
25 | +/** | ||
26 | + * Manages the connectivity requirements between peers. | ||
27 | + */ | ||
28 | +public class PeerConnectivity { | ||
29 | + | ||
30 | + private static final Logger log = LoggerFactory.getLogger( | ||
31 | + PeerConnectivity.class); | ||
32 | + | ||
33 | + // TODO these shouldn't be defined here | ||
34 | + private static final short BGP_PORT = 179; | ||
35 | + private static final int IPV4_BIT_LENGTH = 32; | ||
36 | + | ||
37 | + private final SdnIpConfigService configInfoService; | ||
38 | + private final InterfaceService interfaceService; | ||
39 | + private final IntentService intentService; | ||
40 | + | ||
41 | + // TODO this sucks. | ||
42 | + private int intentId = 0; | ||
43 | + | ||
44 | + public PeerConnectivity(SdnIpConfigService configInfoService, | ||
45 | + InterfaceService interfaceService, IntentService intentService) { | ||
46 | + this.configInfoService = configInfoService; | ||
47 | + this.interfaceService = interfaceService; | ||
48 | + this.intentService = intentService; | ||
49 | + } | ||
50 | + | ||
51 | + public void start() { | ||
52 | + // TODO are any of these errors? | ||
53 | + if (interfaceService.getInterfaces().isEmpty()) { | ||
54 | + | ||
55 | + log.warn("The interface in configuration file is empty. " | ||
56 | + + "Thus, the SDN-IP application can not be started."); | ||
57 | + } else if (configInfoService.getBgpPeers().isEmpty()) { | ||
58 | + | ||
59 | + log.warn("The BGP peer in configuration file is empty." | ||
60 | + + "Thus, the SDN-IP application can not be started."); | ||
61 | + } else if (configInfoService.getBgpSpeakers() == null) { | ||
62 | + | ||
63 | + log.error("The BGP speaker in configuration file is empty. " | ||
64 | + + "Thus, the SDN-IP application can not be started."); | ||
65 | + return; | ||
66 | + } | ||
67 | + | ||
68 | + setupBgpPaths(); | ||
69 | + setupIcmpPaths(); | ||
70 | + } | ||
71 | + | ||
72 | + /** | ||
73 | + * Sets up paths for all {@link BgpSpeaker}s and all external peers. | ||
74 | + * <p/> | ||
75 | + * Run a loop for all BGP speakers and a loop for all BGP peers outside. | ||
76 | + * Push intents for paths from each BGP speaker to all peers. Push intents | ||
77 | + * for paths from all peers to each BGP speaker. | ||
78 | + */ | ||
79 | + private void setupBgpPaths() { | ||
80 | + for (BgpSpeaker bgpSpeaker : configInfoService.getBgpSpeakers() | ||
81 | + .values()) { | ||
82 | + log.debug("Start to set up BGP paths for BGP speaker: {}", | ||
83 | + bgpSpeaker); | ||
84 | + ConnectPoint bgpdConnectPoint = bgpSpeaker.connectPoint(); | ||
85 | + | ||
86 | + List<InterfaceAddress> interfaceAddresses = | ||
87 | + bgpSpeaker.interfaceAddresses(); | ||
88 | + | ||
89 | + for (BgpPeer bgpPeer : configInfoService.getBgpPeers().values()) { | ||
90 | + | ||
91 | + log.debug("Start to set up BGP paths between BGP speaker: {} " | ||
92 | + + "to BGP peer: {}", bgpSpeaker, bgpPeer); | ||
93 | + | ||
94 | + Interface peerInterface = interfaceService.getInterface( | ||
95 | + bgpPeer.connectPoint()); | ||
96 | + if (peerInterface == null) { | ||
97 | + log.error("Can not find the corresponding Interface from " | ||
98 | + + "configuration for BGP peer {}", | ||
99 | + bgpPeer.ipAddress()); | ||
100 | + continue; | ||
101 | + } | ||
102 | + | ||
103 | + IpAddress bgpdAddress = null; | ||
104 | + for (InterfaceAddress interfaceAddress : interfaceAddresses) { | ||
105 | + if (interfaceAddress.connectPoint().equals( | ||
106 | + peerInterface.connectPoint())) { | ||
107 | + bgpdAddress = interfaceAddress.ipAddress(); | ||
108 | + break; | ||
109 | + } | ||
110 | + } | ||
111 | + if (bgpdAddress == null) { | ||
112 | + log.debug("There is no interface IP address for bgpPeer: {}" | ||
113 | + + " on interface {}", bgpPeer, bgpPeer.connectPoint()); | ||
114 | + return; | ||
115 | + } | ||
116 | + | ||
117 | + IpAddress bgpdPeerAddress = bgpPeer.ipAddress(); | ||
118 | + ConnectPoint bgpdPeerConnectPoint = peerInterface.connectPoint(); | ||
119 | + | ||
120 | + // install intent for BGP path from BGPd to BGP peer matching | ||
121 | + // destination TCP port 179 | ||
122 | + | ||
123 | + // TODO: The usage of PacketMatchBuilder will be improved, then we | ||
124 | + // only need to new the PacketMatchBuilder once. | ||
125 | + // By then, the code here will be improved accordingly. | ||
126 | + TrafficSelector selector = DefaultTrafficSelector.builder() | ||
127 | + .matchEthType(Ethernet.TYPE_IPV4) | ||
128 | + .matchIPProtocol(IPv4.PROTOCOL_TCP) | ||
129 | + .matchIPSrc(IpPrefix.valueOf(bgpdAddress.toRealInt(), IPV4_BIT_LENGTH)) | ||
130 | + .matchIPDst(IpPrefix.valueOf(bgpdPeerAddress.toRealInt(), IPV4_BIT_LENGTH)) | ||
131 | + .matchTcpDst(BGP_PORT) | ||
132 | + .build(); | ||
133 | + | ||
134 | + TrafficTreatment treatment = DefaultTrafficTreatment.builder() | ||
135 | + .build(); | ||
136 | + | ||
137 | + PointToPointIntent intentMatchDstTcpPort = new PointToPointIntent( | ||
138 | + nextIntentId(), selector, treatment, | ||
139 | + bgpdConnectPoint, bgpdPeerConnectPoint); | ||
140 | + intentService.submit(intentMatchDstTcpPort); | ||
141 | + log.debug("Submitted BGP path intent matching dst TCP port 179 " | ||
142 | + + "from BGPd {} to peer {}: {}", | ||
143 | + bgpdAddress, bgpdPeerAddress, intentMatchDstTcpPort); | ||
144 | + | ||
145 | + // install intent for BGP path from BGPd to BGP peer matching | ||
146 | + // source TCP port 179 | ||
147 | + selector = DefaultTrafficSelector.builder() | ||
148 | + .matchEthType(Ethernet.TYPE_IPV4) | ||
149 | + .matchIPProtocol(IPv4.PROTOCOL_TCP) | ||
150 | + .matchIPSrc(IpPrefix.valueOf(bgpdAddress.toRealInt(), IPV4_BIT_LENGTH)) | ||
151 | + .matchIPDst(IpPrefix.valueOf(bgpdPeerAddress.toRealInt(), IPV4_BIT_LENGTH)) | ||
152 | + .matchTcpSrc(BGP_PORT) | ||
153 | + .build(); | ||
154 | + | ||
155 | + PointToPointIntent intentMatchSrcTcpPort = new PointToPointIntent( | ||
156 | + nextIntentId(), selector, treatment, | ||
157 | + bgpdConnectPoint, bgpdPeerConnectPoint); | ||
158 | + intentService.submit(intentMatchSrcTcpPort); | ||
159 | + log.debug("Submitted BGP path intent matching src TCP port 179" | ||
160 | + + "from BGPd {} to peer {}: {}", | ||
161 | + bgpdAddress, bgpdPeerAddress, intentMatchSrcTcpPort); | ||
162 | + | ||
163 | + // install intent for reversed BGP path from BGP peer to BGPd | ||
164 | + // matching destination TCP port 179 | ||
165 | + selector = DefaultTrafficSelector.builder() | ||
166 | + .matchEthType(Ethernet.TYPE_IPV4) | ||
167 | + .matchIPProtocol(IPv4.PROTOCOL_TCP) | ||
168 | + .matchIPSrc(IpPrefix.valueOf(bgpdPeerAddress.toRealInt(), IPV4_BIT_LENGTH)) | ||
169 | + .matchIPDst(IpPrefix.valueOf(bgpdAddress.toRealInt(), IPV4_BIT_LENGTH)) | ||
170 | + .matchTcpDst(BGP_PORT) | ||
171 | + .build(); | ||
172 | + | ||
173 | + PointToPointIntent reversedIntentMatchDstTcpPort = new PointToPointIntent( | ||
174 | + nextIntentId(), selector, treatment, | ||
175 | + bgpdPeerConnectPoint, bgpdConnectPoint); | ||
176 | + intentService.submit(reversedIntentMatchDstTcpPort); | ||
177 | + log.debug("Submitted BGP path intent matching dst TCP port 179" | ||
178 | + + "from BGP peer {} to BGPd {} : {}", | ||
179 | + bgpdPeerAddress, bgpdAddress, reversedIntentMatchDstTcpPort); | ||
180 | + | ||
181 | + // install intent for reversed BGP path from BGP peer to BGPd | ||
182 | + // matching source TCP port 179 | ||
183 | + selector = DefaultTrafficSelector.builder() | ||
184 | + .matchEthType(Ethernet.TYPE_IPV4) | ||
185 | + .matchIPProtocol(IPv4.PROTOCOL_TCP) | ||
186 | + .matchIPSrc(IpPrefix.valueOf(bgpdPeerAddress.toRealInt(), IPV4_BIT_LENGTH)) | ||
187 | + .matchIPDst(IpPrefix.valueOf(bgpdAddress.toRealInt(), IPV4_BIT_LENGTH)) | ||
188 | + .matchTcpSrc(BGP_PORT) | ||
189 | + .build(); | ||
190 | + | ||
191 | + PointToPointIntent reversedIntentMatchSrcTcpPort = new PointToPointIntent( | ||
192 | + nextIntentId(), selector, treatment, | ||
193 | + bgpdPeerConnectPoint, bgpdConnectPoint); | ||
194 | + intentService.submit(reversedIntentMatchSrcTcpPort); | ||
195 | + log.debug("Submitted BGP path intent matching src TCP port 179" | ||
196 | + + "from BGP peer {} to BGPd {} : {}", | ||
197 | + bgpdPeerAddress, bgpdAddress, reversedIntentMatchSrcTcpPort); | ||
198 | + | ||
199 | + } | ||
200 | + } | ||
201 | + } | ||
202 | + | ||
203 | + /** | ||
204 | + * Sets up ICMP paths between each {@link BgpSpeaker} and all BGP peers | ||
205 | + * located in other external networks. | ||
206 | + * <p/> | ||
207 | + * Run a loop for all BGP speakers and a loop for all BGP Peers. Push | ||
208 | + * intents for paths from each BGP speaker to all peers. Push intents | ||
209 | + * for paths from all peers to each BGP speaker. | ||
210 | + */ | ||
211 | + private void setupIcmpPaths() { | ||
212 | + for (BgpSpeaker bgpSpeaker : configInfoService.getBgpSpeakers() | ||
213 | + .values()) { | ||
214 | + log.debug("Start to set up ICMP paths for BGP speaker: {}", | ||
215 | + bgpSpeaker); | ||
216 | + ConnectPoint bgpdConnectPoint = bgpSpeaker.connectPoint(); | ||
217 | + List<InterfaceAddress> interfaceAddresses = bgpSpeaker | ||
218 | + .interfaceAddresses(); | ||
219 | + | ||
220 | + for (BgpPeer bgpPeer : configInfoService.getBgpPeers().values()) { | ||
221 | + | ||
222 | + Interface peerInterface = interfaceService.getInterface( | ||
223 | + bgpPeer.connectPoint()); | ||
224 | + | ||
225 | + if (peerInterface == null) { | ||
226 | + log.error("Can not find the corresponding Interface from " | ||
227 | + + "configuration for BGP peer {}", | ||
228 | + bgpPeer.ipAddress()); | ||
229 | + continue; | ||
230 | + } | ||
231 | + IpAddress bgpdAddress = null; | ||
232 | + for (InterfaceAddress interfaceAddress : interfaceAddresses) { | ||
233 | + if (interfaceAddress.connectPoint().equals( | ||
234 | + peerInterface.connectPoint())) { | ||
235 | + bgpdAddress = interfaceAddress.ipAddress(); | ||
236 | + break; | ||
237 | + } | ||
238 | + | ||
239 | + } | ||
240 | + if (bgpdAddress == null) { | ||
241 | + log.debug("There is no IP address for bgpPeer: {} on " | ||
242 | + + "interface port: {}", bgpPeer, | ||
243 | + bgpPeer.connectPoint()); | ||
244 | + return; | ||
245 | + } | ||
246 | + | ||
247 | + IpAddress bgpdPeerAddress = bgpPeer.ipAddress(); | ||
248 | + ConnectPoint bgpdPeerConnectPoint = peerInterface.connectPoint(); | ||
249 | + | ||
250 | + // install intent for ICMP path from BGPd to BGP peer | ||
251 | + TrafficSelector selector = DefaultTrafficSelector.builder() | ||
252 | + .matchEthType(Ethernet.TYPE_IPV4) | ||
253 | + .matchIPProtocol(IPv4.PROTOCOL_ICMP) | ||
254 | + .matchIPSrc(IpPrefix.valueOf(bgpdAddress.toRealInt(), IPV4_BIT_LENGTH)) | ||
255 | + .matchIPDst(IpPrefix.valueOf(bgpdPeerAddress.toRealInt(), IPV4_BIT_LENGTH)) | ||
256 | + .build(); | ||
257 | + | ||
258 | + TrafficTreatment treatment = DefaultTrafficTreatment.builder() | ||
259 | + .build(); | ||
260 | + | ||
261 | + PointToPointIntent intent = new PointToPointIntent( | ||
262 | + nextIntentId(), selector, treatment, | ||
263 | + bgpdConnectPoint, bgpdPeerConnectPoint); | ||
264 | + intentService.submit(intent); | ||
265 | + log.debug("Submitted ICMP path intent from BGPd {} to peer {} :" | ||
266 | + + " {}", bgpdAddress, bgpdPeerAddress, intent); | ||
267 | + | ||
268 | + // install intent for reversed ICMP path from BGP peer to BGPd | ||
269 | + selector = DefaultTrafficSelector.builder() | ||
270 | + .matchEthType(Ethernet.TYPE_IPV4) | ||
271 | + .matchIPProtocol(IPv4.PROTOCOL_ICMP) | ||
272 | + .matchIPSrc(IpPrefix.valueOf(bgpdPeerAddress.toRealInt(), IPV4_BIT_LENGTH)) | ||
273 | + .matchIPDst(IpPrefix.valueOf(bgpdAddress.toRealInt(), IPV4_BIT_LENGTH)) | ||
274 | + .build(); | ||
275 | + | ||
276 | + PointToPointIntent reversedIntent = new PointToPointIntent( | ||
277 | + nextIntentId(), selector, treatment, | ||
278 | + bgpdPeerConnectPoint, bgpdConnectPoint); | ||
279 | + intentService.submit(reversedIntent); | ||
280 | + log.debug("Submitted ICMP path intent from BGP peer {} to BGPd" | ||
281 | + + " {} : {}", | ||
282 | + bgpdPeerAddress, bgpdAddress, reversedIntent); | ||
283 | + } | ||
284 | + } | ||
285 | + } | ||
286 | + | ||
287 | + private IntentId nextIntentId() { | ||
288 | + return new IntentId(intentId++); | ||
289 | + } | ||
290 | +} |
... | @@ -5,6 +5,11 @@ import static org.slf4j.LoggerFactory.getLogger; | ... | @@ -5,6 +5,11 @@ import static org.slf4j.LoggerFactory.getLogger; |
5 | import org.apache.felix.scr.annotations.Activate; | 5 | import org.apache.felix.scr.annotations.Activate; |
6 | import org.apache.felix.scr.annotations.Component; | 6 | import org.apache.felix.scr.annotations.Component; |
7 | import org.apache.felix.scr.annotations.Deactivate; | 7 | import org.apache.felix.scr.annotations.Deactivate; |
8 | +import org.apache.felix.scr.annotations.Reference; | ||
9 | +import org.apache.felix.scr.annotations.ReferenceCardinality; | ||
10 | +import org.onlab.onos.net.host.HostService; | ||
11 | +import org.onlab.onos.net.intent.IntentService; | ||
12 | +import org.onlab.onos.sdnip.config.SdnIpConfigReader; | ||
8 | import org.slf4j.Logger; | 13 | import org.slf4j.Logger; |
9 | 14 | ||
10 | /** | 15 | /** |
... | @@ -15,9 +20,27 @@ public class SdnIp { | ... | @@ -15,9 +20,27 @@ public class SdnIp { |
15 | 20 | ||
16 | private final Logger log = getLogger(getClass()); | 21 | private final Logger log = getLogger(getClass()); |
17 | 22 | ||
23 | + @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY) | ||
24 | + protected IntentService intentService; | ||
25 | + | ||
26 | + @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY) | ||
27 | + protected HostService hostService; | ||
28 | + | ||
29 | + private SdnIpConfigReader config; | ||
30 | + private PeerConnectivity peerConnectivity; | ||
31 | + | ||
18 | @Activate | 32 | @Activate |
19 | protected void activate() { | 33 | protected void activate() { |
20 | log.debug("SDN-IP started"); | 34 | log.debug("SDN-IP started"); |
35 | + | ||
36 | + config = new SdnIpConfigReader(); | ||
37 | + config.init(); | ||
38 | + | ||
39 | + InterfaceService interfaceService = new HostServiceBasedInterfaceService(hostService); | ||
40 | + | ||
41 | + peerConnectivity = new PeerConnectivity(config, interfaceService, intentService); | ||
42 | + peerConnectivity.start(); | ||
43 | + | ||
21 | } | 44 | } |
22 | 45 | ||
23 | @Deactivate | 46 | @Deactivate | ... | ... |
1 | +package org.onlab.onos.sdnip.config; | ||
2 | + | ||
3 | +import java.util.Objects; | ||
4 | + | ||
5 | +import org.codehaus.jackson.annotate.JsonProperty; | ||
6 | +import org.onlab.onos.net.ConnectPoint; | ||
7 | +import org.onlab.onos.net.DeviceId; | ||
8 | +import org.onlab.onos.net.PortNumber; | ||
9 | +import org.onlab.packet.IpAddress; | ||
10 | + | ||
11 | +import com.google.common.base.MoreObjects; | ||
12 | + | ||
13 | +/** | ||
14 | + * Configuration details for a BGP peer. | ||
15 | + */ | ||
16 | +public class BgpPeer { | ||
17 | + private final ConnectPoint connectPoint; | ||
18 | + private final IpAddress ipAddress; | ||
19 | + | ||
20 | + /** | ||
21 | + * Creates a new BgpPeer. | ||
22 | + * | ||
23 | + * @param dpid the DPID of the switch the peer is attached at, as a String | ||
24 | + * @param port the port the peer is attached at | ||
25 | + * @param ipAddress the IP address of the peer as a String | ||
26 | + */ | ||
27 | + public BgpPeer(@JsonProperty("attachmentDpid") String dpid, | ||
28 | + @JsonProperty("attachmentPort") int port, | ||
29 | + @JsonProperty("ipAddress") String ipAddress) { | ||
30 | + this.connectPoint = new ConnectPoint( | ||
31 | + DeviceId.deviceId(SdnIpConfigReader.dpidToUri(dpid)), | ||
32 | + PortNumber.portNumber(port)); | ||
33 | + this.ipAddress = IpAddress.valueOf(ipAddress); | ||
34 | + } | ||
35 | + | ||
36 | + /** | ||
37 | + * Gets the connection point of the peer. | ||
38 | + * | ||
39 | + * @return the connection point | ||
40 | + */ | ||
41 | + public ConnectPoint connectPoint() { | ||
42 | + return connectPoint; | ||
43 | + } | ||
44 | + | ||
45 | + /** | ||
46 | + * Gets the IP address of the peer. | ||
47 | + * | ||
48 | + * @return the IP address | ||
49 | + */ | ||
50 | + public IpAddress ipAddress() { | ||
51 | + return ipAddress; | ||
52 | + } | ||
53 | + | ||
54 | + @Override | ||
55 | + public int hashCode() { | ||
56 | + return Objects.hash(connectPoint, ipAddress); | ||
57 | + } | ||
58 | + | ||
59 | + @Override | ||
60 | + public boolean equals(Object obj) { | ||
61 | + if (obj == this) { | ||
62 | + return true; | ||
63 | + } | ||
64 | + | ||
65 | + if (!(obj instanceof BgpPeer)) { | ||
66 | + return false; | ||
67 | + } | ||
68 | + | ||
69 | + BgpPeer that = (BgpPeer) obj; | ||
70 | + return Objects.equals(this.connectPoint, that.connectPoint) | ||
71 | + && Objects.equals(this.ipAddress, that.ipAddress); | ||
72 | + } | ||
73 | + | ||
74 | + @Override | ||
75 | + public String toString() { | ||
76 | + return MoreObjects.toStringHelper(getClass()) | ||
77 | + .add("connectPoint", connectPoint) | ||
78 | + .add("ipAddress", ipAddress) | ||
79 | + .toString(); | ||
80 | + } | ||
81 | +} |
1 | +package org.onlab.onos.sdnip.config; | ||
2 | + | ||
3 | +import java.util.List; | ||
4 | +import java.util.Objects; | ||
5 | + | ||
6 | +import org.codehaus.jackson.annotate.JsonCreator; | ||
7 | +import org.codehaus.jackson.annotate.JsonProperty; | ||
8 | +import org.onlab.onos.net.ConnectPoint; | ||
9 | +import org.onlab.onos.net.DeviceId; | ||
10 | +import org.onlab.onos.net.PortNumber; | ||
11 | +import org.onlab.packet.MacAddress; | ||
12 | + | ||
13 | +import com.google.common.base.MoreObjects; | ||
14 | + | ||
15 | +/** | ||
16 | + * Represents a BGP daemon in SDN network. | ||
17 | + * <p/> | ||
18 | + * Each BGP speaker has a attachment point, which includes a switch DPID and a | ||
19 | + * switch port. Each BGP speaker has one MAC address and several IP addresses, | ||
20 | + * which are used to peer with BGP peers outside the SDN network. For each | ||
21 | + * peer outside the SDN network, we configure a different IP address to BGP | ||
22 | + * speaker inside the SDN network. | ||
23 | + * <p/> | ||
24 | + * Each BGP speaker has a name, which is a unique identifying String that is | ||
25 | + * used to reference this speaker in the configuration. | ||
26 | + */ | ||
27 | +public class BgpSpeaker { | ||
28 | + private final String name; | ||
29 | + private final ConnectPoint connectPoint; | ||
30 | + private final MacAddress macAddress; | ||
31 | + private List<InterfaceAddress> interfaceAddresses; | ||
32 | + | ||
33 | + /** | ||
34 | + * Class constructor used by the JSON library to create an object. | ||
35 | + * | ||
36 | + * @param name the name of the BGP speaker inside SDN network | ||
37 | + * @param attachmentDpid the DPID where the BGP speaker is attached to | ||
38 | + * @param attachmentPort the port where the BGP speaker is attached to | ||
39 | + * @param macAddress the MAC address of the BGP speaker | ||
40 | + */ | ||
41 | + @JsonCreator | ||
42 | + public BgpSpeaker(@JsonProperty("name") String name, | ||
43 | + @JsonProperty("attachmentDpid") String attachmentDpid, | ||
44 | + @JsonProperty("attachmentPort") int attachmentPort, | ||
45 | + @JsonProperty("macAddress") String macAddress) { | ||
46 | + | ||
47 | + this.name = name; | ||
48 | + this.macAddress = MacAddress.valueOf(macAddress); | ||
49 | + this.connectPoint = new ConnectPoint( | ||
50 | + DeviceId.deviceId(SdnIpConfigReader.dpidToUri(attachmentDpid)), | ||
51 | + PortNumber.portNumber(attachmentPort)); | ||
52 | + } | ||
53 | + | ||
54 | + /** | ||
55 | + * Sets the addresses we configured for the BGP speaker on all virtual | ||
56 | + * {@link Interface}s. | ||
57 | + * | ||
58 | + * @param interfaceAddresses a list of IP addresses of the BGP speaker | ||
59 | + * configured on all virtual interfaces | ||
60 | + */ | ||
61 | + @JsonProperty("interfaceAddresses") | ||
62 | + public void setInterfaceAddresses( | ||
63 | + List<InterfaceAddress> interfaceAddresses) { | ||
64 | + this.interfaceAddresses = interfaceAddresses; | ||
65 | + } | ||
66 | + | ||
67 | + /** | ||
68 | + * Gets the BGP speaker name. | ||
69 | + * | ||
70 | + * @return the BGP speaker name | ||
71 | + */ | ||
72 | + public String name() { | ||
73 | + return name; | ||
74 | + } | ||
75 | + | ||
76 | + /** | ||
77 | + * Gets the connect point where the BGP speaker is attached. | ||
78 | + * | ||
79 | + * @return the connect point | ||
80 | + */ | ||
81 | + public ConnectPoint connectPoint() { | ||
82 | + return connectPoint; | ||
83 | + } | ||
84 | + | ||
85 | + /** | ||
86 | + * Gets the MAC address of the BGP speaker. | ||
87 | + * | ||
88 | + * @return the MAC address | ||
89 | + */ | ||
90 | + public MacAddress macAddress() { | ||
91 | + return macAddress; | ||
92 | + } | ||
93 | + | ||
94 | + /** | ||
95 | + * Gets all IP addresses configured on all {@link Interface}s of the | ||
96 | + * BGP speaker. | ||
97 | + * | ||
98 | + * @return a list of IP addresses of the BGP speaker configured on all | ||
99 | + * virtual interfaces | ||
100 | + */ | ||
101 | + public List<InterfaceAddress> interfaceAddresses() { | ||
102 | + return interfaceAddresses; | ||
103 | + } | ||
104 | + | ||
105 | + @Override | ||
106 | + public boolean equals(Object other) { | ||
107 | + if (!(other instanceof BgpSpeaker)) { | ||
108 | + return false; | ||
109 | + } | ||
110 | + | ||
111 | + BgpSpeaker otherBgpSpeaker = (BgpSpeaker) other; | ||
112 | + | ||
113 | + return name.equals(otherBgpSpeaker.name) && | ||
114 | + connectPoint.equals( | ||
115 | + otherBgpSpeaker.connectPoint) && | ||
116 | + macAddress.equals(otherBgpSpeaker.macAddress) && | ||
117 | + interfaceAddresses.equals(otherBgpSpeaker.interfaceAddresses); | ||
118 | + } | ||
119 | + | ||
120 | + @Override | ||
121 | + public int hashCode() { | ||
122 | + return Objects.hash(name, connectPoint, macAddress, | ||
123 | + interfaceAddresses); | ||
124 | + | ||
125 | + } | ||
126 | + | ||
127 | + @Override | ||
128 | + public String toString() { | ||
129 | + return MoreObjects.toStringHelper(getClass()) | ||
130 | + .add("speakerName", name) | ||
131 | + .add("connectPoint", connectPoint) | ||
132 | + .add("macAddress", macAddress) | ||
133 | + .add("interfaceAddresses", interfaceAddresses) | ||
134 | + .toString(); | ||
135 | + } | ||
136 | +} |
1 | +package org.onlab.onos.sdnip.config; | ||
2 | + | ||
3 | +import java.util.Collections; | ||
4 | +import java.util.List; | ||
5 | + | ||
6 | +import org.codehaus.jackson.annotate.JsonProperty; | ||
7 | + | ||
8 | +/** | ||
9 | + * Contains the configuration data for SDN-IP that has been read from a | ||
10 | + * JSON-formatted configuration file. | ||
11 | + */ | ||
12 | +public class Configuration { | ||
13 | + // We call the BGP routers in our SDN network the BGP speakers, and call | ||
14 | + // the BGP routers outside our SDN network the BGP peers. | ||
15 | + private List<BgpSpeaker> bgpSpeakers; | ||
16 | + private List<BgpPeer> peers; | ||
17 | + | ||
18 | + /** | ||
19 | + * Default constructor. | ||
20 | + */ | ||
21 | + public Configuration() { | ||
22 | + } | ||
23 | + | ||
24 | + /** | ||
25 | + * Gets a list of bgpSpeakers in the system, represented by | ||
26 | + * {@link BgpSpeaker} objects. | ||
27 | + * | ||
28 | + * @return the list of BGP speakers | ||
29 | + */ | ||
30 | + public List<BgpSpeaker> getBgpSpeakers() { | ||
31 | + return Collections.unmodifiableList(bgpSpeakers); | ||
32 | + } | ||
33 | + | ||
34 | + /** | ||
35 | + * Sets a list of bgpSpeakers in the system. | ||
36 | + * | ||
37 | + * @param bgpSpeakers the list of BGP speakers | ||
38 | + */ | ||
39 | + @JsonProperty("bgpSpeakers") | ||
40 | + public void setBgpSpeakers(List<BgpSpeaker> bgpSpeakers) { | ||
41 | + this.bgpSpeakers = bgpSpeakers; | ||
42 | + } | ||
43 | + | ||
44 | + /** | ||
45 | + * Gets a list of BGP peers we are configured to peer with. Peers are | ||
46 | + * represented by {@link BgpPeer} objects. | ||
47 | + * | ||
48 | + * @return the list of BGP peers | ||
49 | + */ | ||
50 | + public List<BgpPeer> getPeers() { | ||
51 | + return Collections.unmodifiableList(peers); | ||
52 | + } | ||
53 | + | ||
54 | + /** | ||
55 | + * Sets a list of BGP peers we are configured to peer with. | ||
56 | + * | ||
57 | + * @param peers the list of BGP peers | ||
58 | + */ | ||
59 | + @JsonProperty("bgpPeers") | ||
60 | + public void setPeers(List<BgpPeer> peers) { | ||
61 | + this.peers = peers; | ||
62 | + } | ||
63 | + | ||
64 | +} |
1 | +package org.onlab.onos.sdnip.config; | ||
2 | + | ||
3 | +import java.util.Objects; | ||
4 | +import java.util.Set; | ||
5 | + | ||
6 | +import org.onlab.onos.net.ConnectPoint; | ||
7 | +import org.onlab.onos.net.host.PortAddresses; | ||
8 | +import org.onlab.packet.IpPrefix; | ||
9 | +import org.onlab.packet.MacAddress; | ||
10 | + | ||
11 | +import com.google.common.base.MoreObjects; | ||
12 | +import com.google.common.collect.Sets; | ||
13 | + | ||
14 | +/** | ||
15 | + * An Interface is a set of addresses that are logically mapped to a switch | ||
16 | + * port in the network. | ||
17 | + */ | ||
18 | +public class Interface { | ||
19 | + private final ConnectPoint connectPoint; | ||
20 | + private final Set<IpPrefix> ipAddresses; | ||
21 | + private final MacAddress macAddress; | ||
22 | + | ||
23 | + /** | ||
24 | + * Creates an Interface based on a connection point, a set of IP addresses | ||
25 | + * and a MAC address. | ||
26 | + * | ||
27 | + * @param connectPoint the connect point this interface is mapped to | ||
28 | + * @param prefixAddress the IP addresses for the interface | ||
29 | + * @param macAddress the MAC address of the interface | ||
30 | + */ | ||
31 | + public Interface(ConnectPoint connectPoint, Set<IpPrefix> prefixAddress, | ||
32 | + MacAddress macAddress) { | ||
33 | + this.connectPoint = connectPoint; | ||
34 | + this.ipAddresses = Sets.newHashSet(prefixAddress); | ||
35 | + this.macAddress = macAddress; | ||
36 | + } | ||
37 | + | ||
38 | + /** | ||
39 | + * Creates an Interface based on a PortAddresses object. | ||
40 | + * | ||
41 | + * @param portAddresses the PortAddresses object to turn into an Interface | ||
42 | + */ | ||
43 | + public Interface(PortAddresses portAddresses) { | ||
44 | + connectPoint = portAddresses.connectPoint(); | ||
45 | + ipAddresses = Sets.newHashSet(portAddresses.ips()); | ||
46 | + macAddress = portAddresses.mac(); | ||
47 | + } | ||
48 | + | ||
49 | + /** | ||
50 | + * Retrieves the connection point that this interface maps to. | ||
51 | + * | ||
52 | + * @return the connection point | ||
53 | + */ | ||
54 | + public ConnectPoint connectPoint() { | ||
55 | + return connectPoint; | ||
56 | + } | ||
57 | + | ||
58 | + /** | ||
59 | + * Retrieves the set of IP addresses that are assigned to the interface. | ||
60 | + * | ||
61 | + * @return the set of IP addresses | ||
62 | + */ | ||
63 | + public Set<IpPrefix> ips() { | ||
64 | + return ipAddresses; | ||
65 | + } | ||
66 | + | ||
67 | + /** | ||
68 | + * Retrieves the MAC address that is assigned to the interface. | ||
69 | + * | ||
70 | + * @return the MAC address | ||
71 | + */ | ||
72 | + public MacAddress mac() { | ||
73 | + return macAddress; | ||
74 | + } | ||
75 | + | ||
76 | + @Override | ||
77 | + public boolean equals(Object other) { | ||
78 | + if (!(other instanceof Interface)) { | ||
79 | + return false; | ||
80 | + } | ||
81 | + | ||
82 | + Interface otherInterface = (Interface) other; | ||
83 | + | ||
84 | + return connectPoint.equals(otherInterface.connectPoint) && | ||
85 | + ipAddresses.equals(otherInterface.ipAddresses) && | ||
86 | + macAddress.equals(otherInterface.macAddress); | ||
87 | + } | ||
88 | + | ||
89 | + @Override | ||
90 | + public int hashCode() { | ||
91 | + return Objects.hash(connectPoint, ipAddresses, macAddress); | ||
92 | + } | ||
93 | + | ||
94 | + @Override | ||
95 | + public String toString() { | ||
96 | + return MoreObjects.toStringHelper(getClass()) | ||
97 | + .add("connectPoint", connectPoint) | ||
98 | + .add("ipAddresses", ipAddresses) | ||
99 | + .add("macAddress", macAddress) | ||
100 | + .toString(); | ||
101 | + } | ||
102 | +} |
1 | +package org.onlab.onos.sdnip.config; | ||
2 | + | ||
3 | +import java.util.Objects; | ||
4 | + | ||
5 | +import org.codehaus.jackson.annotate.JsonProperty; | ||
6 | +import org.onlab.onos.net.ConnectPoint; | ||
7 | +import org.onlab.onos.net.DeviceId; | ||
8 | +import org.onlab.onos.net.PortNumber; | ||
9 | +import org.onlab.packet.IpAddress; | ||
10 | + | ||
11 | +import com.google.common.base.MoreObjects; | ||
12 | + | ||
13 | +/** | ||
14 | + * Represents an address of a {@link BgpSpeaker} configured on an | ||
15 | + * {@link Interface}. | ||
16 | + * <p/> | ||
17 | + * Each InterfaceAddress includes the interface name and an IP address. | ||
18 | + */ | ||
19 | +public class InterfaceAddress { | ||
20 | + private final ConnectPoint connectPoint; | ||
21 | + private final IpAddress ipAddress; | ||
22 | + | ||
23 | + /** | ||
24 | + * Creates an InterfaceAddress object. | ||
25 | + * | ||
26 | + * @param dpid the DPID of the interface as a String | ||
27 | + * @param port the port of the interface | ||
28 | + * @param ipAddress the IP address of a {@link BgpSpeaker} configured on | ||
29 | + * the interface | ||
30 | + */ | ||
31 | + public InterfaceAddress(@JsonProperty("interfaceDpid") String dpid, | ||
32 | + @JsonProperty("interfacePort") int port, | ||
33 | + @JsonProperty("ipAddress") String ipAddress) { | ||
34 | + this.connectPoint = new ConnectPoint( | ||
35 | + DeviceId.deviceId(SdnIpConfigReader.dpidToUri(dpid)), | ||
36 | + PortNumber.portNumber(port)); | ||
37 | + this.ipAddress = IpAddress.valueOf(ipAddress); | ||
38 | + } | ||
39 | + | ||
40 | + /** | ||
41 | + * Gets the connection point of the peer. | ||
42 | + * | ||
43 | + * @return the connection point | ||
44 | + */ | ||
45 | + public ConnectPoint connectPoint() { | ||
46 | + return connectPoint; | ||
47 | + } | ||
48 | + | ||
49 | + /** | ||
50 | + * Gets the IP address of a BGP speaker configured on an {@link Interface}. | ||
51 | + * | ||
52 | + * @return the IP address | ||
53 | + */ | ||
54 | + public IpAddress ipAddress() { | ||
55 | + return ipAddress; | ||
56 | + } | ||
57 | + | ||
58 | + @Override | ||
59 | + public int hashCode() { | ||
60 | + return Objects.hash(connectPoint, ipAddress); | ||
61 | + } | ||
62 | + | ||
63 | + @Override | ||
64 | + public boolean equals(Object obj) { | ||
65 | + if (obj == this) { | ||
66 | + return true; | ||
67 | + } | ||
68 | + | ||
69 | + if (!(obj instanceof InterfaceAddress)) { | ||
70 | + return false; | ||
71 | + } | ||
72 | + | ||
73 | + InterfaceAddress that = (InterfaceAddress) obj; | ||
74 | + return Objects.equals(this.connectPoint, that.connectPoint) | ||
75 | + && Objects.equals(this.ipAddress, that.ipAddress); | ||
76 | + } | ||
77 | + | ||
78 | + @Override | ||
79 | + public String toString() { | ||
80 | + return MoreObjects.toStringHelper(getClass()) | ||
81 | + .add("connectPoint", connectPoint) | ||
82 | + .add("ipAddress", ipAddress) | ||
83 | + .toString(); | ||
84 | + } | ||
85 | +} |
1 | +package org.onlab.onos.sdnip.config; | ||
2 | + | ||
3 | +import java.io.File; | ||
4 | +import java.io.IOException; | ||
5 | +import java.util.Collections; | ||
6 | +import java.util.Map; | ||
7 | +import java.util.concurrent.ConcurrentHashMap; | ||
8 | + | ||
9 | +import org.codehaus.jackson.map.ObjectMapper; | ||
10 | +import org.onlab.packet.IpAddress; | ||
11 | +import org.slf4j.Logger; | ||
12 | +import org.slf4j.LoggerFactory; | ||
13 | + | ||
14 | +/** | ||
15 | + * SDN-IP Config Reader provides IConfigInfoService | ||
16 | + * by reading from an SDN-IP configuration file. | ||
17 | + * It must be enabled on the nodes within the cluster | ||
18 | + * not running SDN-IP. | ||
19 | + * <p/> | ||
20 | + * TODO: As a long term solution, a module providing | ||
21 | + * general network configuration to ONOS nodes should be used. | ||
22 | + */ | ||
23 | +public class SdnIpConfigReader implements SdnIpConfigService { | ||
24 | + | ||
25 | + private static final Logger log = LoggerFactory.getLogger(SdnIpConfigReader.class); | ||
26 | + | ||
27 | + private static final String DEFAULT_CONFIG_FILE = "config/sdnip.json"; | ||
28 | + private String configFileName = DEFAULT_CONFIG_FILE; | ||
29 | + //private Map<String, Interface> interfaces; | ||
30 | + // We call the BGP routers in our SDN network the BGP speakers, and call | ||
31 | + // the BGP routers outside our SDN network the BGP peers. | ||
32 | + private Map<String, BgpSpeaker> bgpSpeakers; | ||
33 | + private Map<IpAddress, BgpPeer> bgpPeers; | ||
34 | + //private InvertedRadixTree<Interface> interfaceRoutes; | ||
35 | + | ||
36 | + /** | ||
37 | + * Reads the info contained in the configuration file. | ||
38 | + * | ||
39 | + * @param configFilename The name of configuration file for SDN-IP application. | ||
40 | + */ | ||
41 | + private void readConfiguration(String configFilename) { | ||
42 | + File gatewaysFile = new File(configFilename); | ||
43 | + ObjectMapper mapper = new ObjectMapper(); | ||
44 | + | ||
45 | + try { | ||
46 | + Configuration config = mapper.readValue(gatewaysFile, Configuration.class); | ||
47 | + /*interfaces = new ConcurrentHashMap<>(); | ||
48 | + for (Interface intf : config.getInterfaces()) { | ||
49 | + interfaces.put(intf.getName(), intf); | ||
50 | + }*/ | ||
51 | + bgpSpeakers = new ConcurrentHashMap<>(); | ||
52 | + for (BgpSpeaker speaker : config.getBgpSpeakers()) { | ||
53 | + bgpSpeakers.put(speaker.name(), speaker); | ||
54 | + } | ||
55 | + bgpPeers = new ConcurrentHashMap<>(); | ||
56 | + for (BgpPeer peer : config.getPeers()) { | ||
57 | + bgpPeers.put(peer.ipAddress(), peer); | ||
58 | + } | ||
59 | + } catch (IOException e) { | ||
60 | + log.error("Error reading JSON file", e); | ||
61 | + //throw new ConfigurationRuntimeException("Error in JSON file", e); | ||
62 | + } | ||
63 | + | ||
64 | + // Populate the interface InvertedRadixTree | ||
65 | + /*for (Interface intf : interfaces.values()) { | ||
66 | + Ip4Prefix prefix = intf.getIp4Prefix(); | ||
67 | + String binaryString = RouteEntry.createBinaryString(prefix); | ||
68 | + interfaceRoutes.put(binaryString, intf); | ||
69 | + }*/ | ||
70 | + } | ||
71 | + | ||
72 | + /** | ||
73 | + * To find the Interface which has longest matchable IP prefix (sub-network | ||
74 | + * prefix) to next hop IP address. | ||
75 | + * | ||
76 | + * @param address the IP address of next hop router | ||
77 | + * @return the Interface which has longest matchable IP prefix | ||
78 | + */ | ||
79 | + /*private Interface longestInterfacePrefixMatch(IpAddress address) { | ||
80 | + Ip4Prefix prefixToSearchFor = | ||
81 | + new Ip4Prefix(address, (short) Ip4Address.BIT_LENGTH); | ||
82 | + String binaryString = RouteEntry.createBinaryString(prefixToSearchFor); | ||
83 | + | ||
84 | + Iterator<Interface> it = | ||
85 | + interfaceRoutes.getValuesForKeysPrefixing(binaryString).iterator(); | ||
86 | + Interface intf = null; | ||
87 | + // Find the last prefix, which will be the longest prefix | ||
88 | + while (it.hasNext()) { | ||
89 | + intf = it.next(); | ||
90 | + } | ||
91 | + | ||
92 | + return intf; | ||
93 | + }*/ | ||
94 | + | ||
95 | + /*@Override | ||
96 | + public Interface getOutgoingInterface(IpAddress dstIpAddress) { | ||
97 | + return longestInterfacePrefixMatch(dstIpAddress); | ||
98 | + }*/ | ||
99 | + | ||
100 | + public void init() { | ||
101 | + //interfaceRoutes = new ConcurrentInvertedRadixTree<>( | ||
102 | + //new DefaultByteArrayNodeFactory()); | ||
103 | + | ||
104 | + // Reading config values | ||
105 | + /*String configFilenameParameter = context.getConfigParams(this).get("configfile"); | ||
106 | + if (configFilenameParameter != null) { | ||
107 | + currentConfigFilename = configFilenameParameter; | ||
108 | + }*/ | ||
109 | + log.debug("Config file set to {}", configFileName); | ||
110 | + | ||
111 | + readConfiguration(configFileName); | ||
112 | + } | ||
113 | + | ||
114 | + /*@Override | ||
115 | + public Map<String, Interface> getInterfaces() { | ||
116 | + return Collections.unmodifiableMap(interfaces); | ||
117 | + }*/ | ||
118 | + | ||
119 | + @Override | ||
120 | + public Map<String, BgpSpeaker> getBgpSpeakers() { | ||
121 | + return Collections.unmodifiableMap(bgpSpeakers); | ||
122 | + } | ||
123 | + | ||
124 | + @Override | ||
125 | + public Map<IpAddress, BgpPeer> getBgpPeers() { | ||
126 | + return Collections.unmodifiableMap(bgpPeers); | ||
127 | + } | ||
128 | + | ||
129 | + static String dpidToUri(String dpid) { | ||
130 | + return "of:" + dpid.replace(":", ""); | ||
131 | + } | ||
132 | +} |
1 | +package org.onlab.onos.sdnip.config; | ||
2 | + | ||
3 | +import java.util.Map; | ||
4 | + | ||
5 | +import org.onlab.packet.IpAddress; | ||
6 | + | ||
7 | +/** | ||
8 | + * Provides information about the layer 3 properties of the network. | ||
9 | + * This is based on IP addresses configured on ports in the network. | ||
10 | + */ | ||
11 | +public interface SdnIpConfigService { | ||
12 | + | ||
13 | + /** | ||
14 | + * Gets the list of virtual external-facing interfaces. | ||
15 | + * | ||
16 | + * @return the map of interface names to interface objects | ||
17 | + */ | ||
18 | + //public Map<String, Interface> getInterfaces(); | ||
19 | + | ||
20 | + /** | ||
21 | + * Gets the list of BGP speakers inside the SDN network. | ||
22 | + * | ||
23 | + * @return the map of BGP speaker names to BGP speaker objects | ||
24 | + */ | ||
25 | + public Map<String, BgpSpeaker> getBgpSpeakers(); | ||
26 | + | ||
27 | + /** | ||
28 | + * Gets the list of configured BGP peers. | ||
29 | + * | ||
30 | + * @return the map from peer IP address to BgpPeer object | ||
31 | + */ | ||
32 | + public Map<IpAddress, BgpPeer> getBgpPeers(); | ||
33 | + | ||
34 | + /** | ||
35 | + * Gets the Interface object for the interface that packets | ||
36 | + * to dstIpAddress will be sent out of. Returns null if dstIpAddress is not | ||
37 | + * in a directly connected network, or if no interfaces are configured. | ||
38 | + * | ||
39 | + * @param dstIpAddress destination IP address that we want to match to | ||
40 | + * an outgoing interface | ||
41 | + * @return the Interface object if one is found, otherwise null | ||
42 | + */ | ||
43 | + //public Interface getOutgoingInterface(IpAddress dstIpAddress); | ||
44 | + | ||
45 | +} |
... | @@ -45,7 +45,7 @@ | ... | @@ -45,7 +45,7 @@ |
45 | <action class="org.onlab.onos.cli.net.DeviceRoleCommand"/> | 45 | <action class="org.onlab.onos.cli.net.DeviceRoleCommand"/> |
46 | <completers> | 46 | <completers> |
47 | <ref component-id="deviceIdCompleter"/> | 47 | <ref component-id="deviceIdCompleter"/> |
48 | - <ref component-id="roleCompleter"/> | 48 | + <ref component-id="nodeIdCompleter"/> |
49 | <ref component-id="roleCompleter"/> | 49 | <ref component-id="roleCompleter"/> |
50 | <null/> | 50 | <null/> |
51 | </completers> | 51 | </completers> | ... | ... |
... | @@ -20,6 +20,7 @@ public class DefaultHost extends AbstractElement implements Host { | ... | @@ -20,6 +20,7 @@ public class DefaultHost extends AbstractElement implements Host { |
20 | private final MacAddress mac; | 20 | private final MacAddress mac; |
21 | private final VlanId vlan; | 21 | private final VlanId vlan; |
22 | private final HostLocation location; | 22 | private final HostLocation location; |
23 | + // FIXME: should be IpAddress | ||
23 | private final Set<IpPrefix> ips; | 24 | private final Set<IpPrefix> ips; |
24 | 25 | ||
25 | /** | 26 | /** | ... | ... |
... | @@ -38,6 +38,7 @@ public interface Host extends Element { | ... | @@ -38,6 +38,7 @@ public interface Host extends Element { |
38 | * | 38 | * |
39 | * @return set of IP addresses; empty if no IP address is bound | 39 | * @return set of IP addresses; empty if no IP address is bound |
40 | */ | 40 | */ |
41 | + // FIXME: Switch to IpAddress | ||
41 | Set<IpPrefix> ipAddresses(); | 42 | Set<IpPrefix> ipAddresses(); |
42 | 43 | ||
43 | /** | 44 | /** | ... | ... |
... | @@ -4,8 +4,6 @@ import static com.google.common.base.Preconditions.checkNotNull; | ... | @@ -4,8 +4,6 @@ import static com.google.common.base.Preconditions.checkNotNull; |
4 | 4 | ||
5 | import java.util.Objects; | 5 | import java.util.Objects; |
6 | 6 | ||
7 | -import org.onlab.onos.net.link.LinkDescription; | ||
8 | - | ||
9 | import com.google.common.base.MoreObjects; | 7 | import com.google.common.base.MoreObjects; |
10 | 8 | ||
11 | // TODO Consider renaming. | 9 | // TODO Consider renaming. |
... | @@ -69,16 +67,6 @@ public final class LinkKey { | ... | @@ -69,16 +67,6 @@ public final class LinkKey { |
69 | return new LinkKey(link.src(), link.dst()); | 67 | return new LinkKey(link.src(), link.dst()); |
70 | } | 68 | } |
71 | 69 | ||
72 | - /** | ||
73 | - * Creates a link identifier for the specified link. | ||
74 | - * | ||
75 | - * @param desc link description | ||
76 | - * @return a link identifier | ||
77 | - */ | ||
78 | - public static LinkKey linkKey(LinkDescription desc) { | ||
79 | - return new LinkKey(desc.src(), desc.dst()); | ||
80 | - } | ||
81 | - | ||
82 | @Override | 70 | @Override |
83 | public int hashCode() { | 71 | public int hashCode() { |
84 | return Objects.hash(src(), dst); | 72 | return Objects.hash(src(), dst); | ... | ... |
1 | package org.onlab.onos.net.host; | 1 | package org.onlab.onos.net.host; |
2 | 2 | ||
3 | -import java.util.Set; | ||
4 | - | ||
5 | import org.onlab.onos.net.ConnectPoint; | 3 | import org.onlab.onos.net.ConnectPoint; |
6 | import org.onlab.onos.net.HostId; | 4 | import org.onlab.onos.net.HostId; |
7 | 5 | ||
... | @@ -47,20 +45,4 @@ public interface HostAdminService { | ... | @@ -47,20 +45,4 @@ public interface HostAdminService { |
47 | */ | 45 | */ |
48 | void clearAddresses(ConnectPoint connectPoint); | 46 | void clearAddresses(ConnectPoint connectPoint); |
49 | 47 | ||
50 | - /** | ||
51 | - * Returns the addresses information for all connection points. | ||
52 | - * | ||
53 | - * @return the set of address bindings for all connection points | ||
54 | - */ | ||
55 | - Set<PortAddresses> getAddressBindings(); | ||
56 | - | ||
57 | - /** | ||
58 | - * Retrieves the addresses that have been bound to the given connection | ||
59 | - * point. | ||
60 | - * | ||
61 | - * @param connectPoint the connection point to retrieve address bindings | ||
62 | - * for | ||
63 | - * @return addresses bound to the port | ||
64 | - */ | ||
65 | - PortAddresses getAddressBindingsForPort(ConnectPoint connectPoint); | ||
66 | } | 48 | } | ... | ... |
... | @@ -37,6 +37,7 @@ public interface HostDescription extends Description { | ... | @@ -37,6 +37,7 @@ public interface HostDescription extends Description { |
37 | * | 37 | * |
38 | * @return host IP address | 38 | * @return host IP address |
39 | */ | 39 | */ |
40 | + // FIXME: Switch to IpAddress | ||
40 | IpPrefix ipAddress(); | 41 | IpPrefix ipAddress(); |
41 | 42 | ||
42 | } | 43 | } | ... | ... |
... | @@ -110,6 +110,23 @@ public interface HostService { | ... | @@ -110,6 +110,23 @@ public interface HostService { |
110 | void requestMac(IpAddress ip); | 110 | void requestMac(IpAddress ip); |
111 | 111 | ||
112 | /** | 112 | /** |
113 | + * Returns the addresses information for all connection points. | ||
114 | + * | ||
115 | + * @return the set of address bindings for all connection points | ||
116 | + */ | ||
117 | + Set<PortAddresses> getAddressBindings(); | ||
118 | + | ||
119 | + /** | ||
120 | + * Retrieves the addresses that have been bound to the given connection | ||
121 | + * point. | ||
122 | + * | ||
123 | + * @param connectPoint the connection point to retrieve address bindings | ||
124 | + * for | ||
125 | + * @return addresses bound to the port | ||
126 | + */ | ||
127 | + PortAddresses getAddressBindingsForPort(ConnectPoint connectPoint); | ||
128 | + | ||
129 | + /** | ||
113 | * Adds the specified host listener. | 130 | * Adds the specified host listener. |
114 | * | 131 | * |
115 | * @param listener host listener | 132 | * @param listener host listener | ... | ... |
... | @@ -29,6 +29,7 @@ public interface HostStore extends Store<HostEvent, HostStoreDelegate> { | ... | @@ -29,6 +29,7 @@ public interface HostStore extends Store<HostEvent, HostStoreDelegate> { |
29 | HostEvent createOrUpdateHost(ProviderId providerId, HostId hostId, | 29 | HostEvent createOrUpdateHost(ProviderId providerId, HostId hostId, |
30 | HostDescription hostDescription); | 30 | HostDescription hostDescription); |
31 | 31 | ||
32 | + // FIXME: API to remove only IpAddress is missing | ||
32 | /** | 33 | /** |
33 | * Removes the specified host from the inventory. | 34 | * Removes the specified host from the inventory. |
34 | * | 35 | * |
... | @@ -81,6 +82,7 @@ public interface HostStore extends Store<HostEvent, HostStoreDelegate> { | ... | @@ -81,6 +82,7 @@ public interface HostStore extends Store<HostEvent, HostStoreDelegate> { |
81 | * @param ip ip address | 82 | * @param ip ip address |
82 | * @return set of hosts with the given IP | 83 | * @return set of hosts with the given IP |
83 | */ | 84 | */ |
85 | + // FIXME: Switch to IpAddress | ||
84 | Set<Host> getHosts(IpPrefix ip); | 86 | Set<Host> getHosts(IpPrefix ip); |
85 | 87 | ||
86 | /** | 88 | /** | ... | ... |
... | @@ -17,6 +17,7 @@ import com.google.common.base.MoreObjects; | ... | @@ -17,6 +17,7 @@ import com.google.common.base.MoreObjects; |
17 | public class PortAddresses { | 17 | public class PortAddresses { |
18 | 18 | ||
19 | private final ConnectPoint connectPoint; | 19 | private final ConnectPoint connectPoint; |
20 | + // TODO: Should this be IpAddress or IpPrefix? | ||
20 | private final Set<IpPrefix> ipAddresses; | 21 | private final Set<IpPrefix> ipAddresses; |
21 | private final MacAddress macAddress; | 22 | private final MacAddress macAddress; |
22 | 23 | ... | ... |
1 | package org.onlab.onos.net.proxyarp; | 1 | package org.onlab.onos.net.proxyarp; |
2 | 2 | ||
3 | +import org.onlab.onos.net.ConnectPoint; | ||
3 | import org.onlab.onos.net.packet.PacketContext; | 4 | import org.onlab.onos.net.packet.PacketContext; |
4 | import org.onlab.packet.Ethernet; | 5 | import org.onlab.packet.Ethernet; |
5 | import org.onlab.packet.IpPrefix; | 6 | import org.onlab.packet.IpPrefix; |
... | @@ -23,8 +24,9 @@ public interface ProxyArpService { | ... | @@ -23,8 +24,9 @@ public interface ProxyArpService { |
23 | * will be flooded at all edge ports. | 24 | * will be flooded at all edge ports. |
24 | * | 25 | * |
25 | * @param eth an arp request | 26 | * @param eth an arp request |
27 | + * @param inPort the port the request was received on | ||
26 | */ | 28 | */ |
27 | - void reply(Ethernet eth); | 29 | + void reply(Ethernet eth, ConnectPoint inPort); |
28 | 30 | ||
29 | /** | 31 | /** |
30 | * Forwards an ARP request to its destination. Floods at the edge the ARP request if the | 32 | * Forwards an ARP request to its destination. Floods at the edge the ARP request if the | ... | ... |
... | @@ -75,4 +75,14 @@ public class HostServiceAdapter implements HostService { | ... | @@ -75,4 +75,14 @@ public class HostServiceAdapter implements HostService { |
75 | public void removeListener(HostListener listener) { | 75 | public void removeListener(HostListener listener) { |
76 | } | 76 | } |
77 | 77 | ||
78 | + @Override | ||
79 | + public Set<PortAddresses> getAddressBindings() { | ||
80 | + return null; | ||
81 | + } | ||
82 | + | ||
83 | + @Override | ||
84 | + public PortAddresses getAddressBindingsForPort(ConnectPoint connectPoint) { | ||
85 | + return null; | ||
86 | + } | ||
87 | + | ||
78 | } | 88 | } | ... | ... |
... | @@ -5,6 +5,7 @@ import static com.google.common.base.Preconditions.checkNotNull; | ... | @@ -5,6 +5,7 @@ import static com.google.common.base.Preconditions.checkNotNull; |
5 | import static org.slf4j.LoggerFactory.getLogger; | 5 | import static org.slf4j.LoggerFactory.getLogger; |
6 | 6 | ||
7 | import java.nio.ByteBuffer; | 7 | import java.nio.ByteBuffer; |
8 | +import java.util.Collections; | ||
8 | import java.util.List; | 9 | import java.util.List; |
9 | import java.util.Map.Entry; | 10 | import java.util.Map.Entry; |
10 | import java.util.Set; | 11 | import java.util.Set; |
... | @@ -15,6 +16,7 @@ import org.apache.felix.scr.annotations.Deactivate; | ... | @@ -15,6 +16,7 @@ import org.apache.felix.scr.annotations.Deactivate; |
15 | import org.apache.felix.scr.annotations.Reference; | 16 | import org.apache.felix.scr.annotations.Reference; |
16 | import org.apache.felix.scr.annotations.ReferenceCardinality; | 17 | import org.apache.felix.scr.annotations.ReferenceCardinality; |
17 | import org.apache.felix.scr.annotations.Service; | 18 | import org.apache.felix.scr.annotations.Service; |
19 | +import org.onlab.onos.net.ConnectPoint; | ||
18 | import org.onlab.onos.net.Device; | 20 | import org.onlab.onos.net.Device; |
19 | import org.onlab.onos.net.Host; | 21 | import org.onlab.onos.net.Host; |
20 | import org.onlab.onos.net.HostId; | 22 | import org.onlab.onos.net.HostId; |
... | @@ -27,6 +29,7 @@ import org.onlab.onos.net.device.DeviceService; | ... | @@ -27,6 +29,7 @@ import org.onlab.onos.net.device.DeviceService; |
27 | import org.onlab.onos.net.flow.DefaultTrafficTreatment; | 29 | import org.onlab.onos.net.flow.DefaultTrafficTreatment; |
28 | import org.onlab.onos.net.flow.TrafficTreatment; | 30 | import org.onlab.onos.net.flow.TrafficTreatment; |
29 | import org.onlab.onos.net.host.HostService; | 31 | import org.onlab.onos.net.host.HostService; |
32 | +import org.onlab.onos.net.host.PortAddresses; | ||
30 | import org.onlab.onos.net.link.LinkEvent; | 33 | import org.onlab.onos.net.link.LinkEvent; |
31 | import org.onlab.onos.net.link.LinkListener; | 34 | import org.onlab.onos.net.link.LinkListener; |
32 | import org.onlab.onos.net.link.LinkService; | 35 | import org.onlab.onos.net.link.LinkService; |
... | @@ -37,7 +40,9 @@ import org.onlab.onos.net.packet.PacketService; | ... | @@ -37,7 +40,9 @@ import org.onlab.onos.net.packet.PacketService; |
37 | import org.onlab.onos.net.proxyarp.ProxyArpService; | 40 | import org.onlab.onos.net.proxyarp.ProxyArpService; |
38 | import org.onlab.packet.ARP; | 41 | import org.onlab.packet.ARP; |
39 | import org.onlab.packet.Ethernet; | 42 | import org.onlab.packet.Ethernet; |
43 | +import org.onlab.packet.IpAddress; | ||
40 | import org.onlab.packet.IpPrefix; | 44 | import org.onlab.packet.IpPrefix; |
45 | +import org.onlab.packet.MacAddress; | ||
41 | import org.onlab.packet.VlanId; | 46 | import org.onlab.packet.VlanId; |
42 | import org.slf4j.Logger; | 47 | import org.slf4j.Logger; |
43 | 48 | ||
... | @@ -101,12 +106,46 @@ public class ProxyArpManager implements ProxyArpService { | ... | @@ -101,12 +106,46 @@ public class ProxyArpManager implements ProxyArpService { |
101 | } | 106 | } |
102 | 107 | ||
103 | @Override | 108 | @Override |
104 | - public void reply(Ethernet eth) { | 109 | + public void reply(Ethernet eth, ConnectPoint inPort) { |
105 | checkNotNull(eth, REQUEST_NULL); | 110 | checkNotNull(eth, REQUEST_NULL); |
106 | checkArgument(eth.getEtherType() == Ethernet.TYPE_ARP, | 111 | checkArgument(eth.getEtherType() == Ethernet.TYPE_ARP, |
107 | REQUEST_NOT_ARP); | 112 | REQUEST_NOT_ARP); |
108 | ARP arp = (ARP) eth.getPayload(); | 113 | ARP arp = (ARP) eth.getPayload(); |
109 | checkArgument(arp.getOpCode() == ARP.OP_REQUEST, NOT_ARP_REQUEST); | 114 | checkArgument(arp.getOpCode() == ARP.OP_REQUEST, NOT_ARP_REQUEST); |
115 | + checkNotNull(inPort); | ||
116 | + | ||
117 | + // If the source address matches one of our external addresses | ||
118 | + // it could be a request from an internal host to an external | ||
119 | + // address. Forward it over to the correct port. | ||
120 | + IpAddress source = IpAddress.valueOf(arp.getSenderProtocolAddress()); | ||
121 | + PortAddresses sourceAddresses = findOutsidePortInSubnet(source); | ||
122 | + if (sourceAddresses != null && !isOutsidePort(inPort)) { | ||
123 | + for (IpPrefix subnet : sourceAddresses.ips()) { | ||
124 | + if (subnet.toIpAddress().equals(source)) { | ||
125 | + sendTo(eth, sourceAddresses.connectPoint()); | ||
126 | + return; | ||
127 | + } | ||
128 | + } | ||
129 | + } | ||
130 | + | ||
131 | + // If the request came from outside the network, only reply if it was | ||
132 | + // for one of our external addresses. | ||
133 | + if (isOutsidePort(inPort)) { | ||
134 | + IpAddress target = IpAddress.valueOf(arp.getTargetProtocolAddress()); | ||
135 | + PortAddresses addresses = hostService.getAddressBindingsForPort(inPort); | ||
136 | + | ||
137 | + for (IpPrefix interfaceAddress : addresses.ips()) { | ||
138 | + if (interfaceAddress.toIpAddress().equals(target)) { | ||
139 | + Ethernet arpReply = buildArpReply(interfaceAddress, | ||
140 | + addresses.mac(), eth); | ||
141 | + sendTo(arpReply, inPort); | ||
142 | + } | ||
143 | + } | ||
144 | + | ||
145 | + return; | ||
146 | + } | ||
147 | + | ||
148 | + // Continue with normal proxy ARP case | ||
110 | 149 | ||
111 | VlanId vlan = VlanId.vlanId(eth.getVlanID()); | 150 | VlanId vlan = VlanId.vlanId(eth.getVlanID()); |
112 | Set<Host> hosts = hostService.getHostsByIp(IpPrefix.valueOf(arp | 151 | Set<Host> hosts = hostService.getHostsByIp(IpPrefix.valueOf(arp |
... | @@ -128,12 +167,62 @@ public class ProxyArpManager implements ProxyArpService { | ... | @@ -128,12 +167,62 @@ public class ProxyArpManager implements ProxyArpService { |
128 | return; | 167 | return; |
129 | } | 168 | } |
130 | 169 | ||
131 | - Ethernet arpReply = buildArpReply(dst, eth); | 170 | + Ethernet arpReply = buildArpReply(dst.ipAddresses().iterator().next(), |
171 | + dst.mac(), eth); | ||
132 | // TODO: check send status with host service. | 172 | // TODO: check send status with host service. |
173 | + sendTo(arpReply, src.location()); | ||
174 | + } | ||
175 | + | ||
176 | + /** | ||
177 | + * Outputs the given packet out the given port. | ||
178 | + * | ||
179 | + * @param packet the packet to send | ||
180 | + * @param outPort the port to send it out | ||
181 | + */ | ||
182 | + private void sendTo(Ethernet packet, ConnectPoint outPort) { | ||
183 | + if (internalPorts.containsEntry( | ||
184 | + deviceService.getDevice(outPort.deviceId()), outPort.port())) { | ||
185 | + // Sanity check to make sure we don't send the packet out an | ||
186 | + // internal port and create a loop (could happen due to | ||
187 | + // misconfiguration). | ||
188 | + return; | ||
189 | + } | ||
190 | + | ||
133 | TrafficTreatment.Builder builder = DefaultTrafficTreatment.builder(); | 191 | TrafficTreatment.Builder builder = DefaultTrafficTreatment.builder(); |
134 | - builder.setOutput(src.location().port()); | 192 | + builder.setOutput(outPort.port()); |
135 | - packetService.emit(new DefaultOutboundPacket(src.location().deviceId(), | 193 | + packetService.emit(new DefaultOutboundPacket(outPort.deviceId(), |
136 | - builder.build(), ByteBuffer.wrap(arpReply.serialize()))); | 194 | + builder.build(), ByteBuffer.wrap(packet.serialize()))); |
195 | + } | ||
196 | + | ||
197 | + /** | ||
198 | + * Finds the port with an address in the subnet of the target address, if | ||
199 | + * one exists. | ||
200 | + * | ||
201 | + * @param target the target address to find a matching external port for | ||
202 | + * @return a PortAddresses object containing the external addresses if one | ||
203 | + * was found, otherwise null. | ||
204 | + */ | ||
205 | + private PortAddresses findOutsidePortInSubnet(IpAddress target) { | ||
206 | + for (PortAddresses addresses : hostService.getAddressBindings()) { | ||
207 | + for (IpPrefix prefix : addresses.ips()) { | ||
208 | + if (prefix.contains(target)) { | ||
209 | + return new PortAddresses(addresses.connectPoint(), | ||
210 | + Collections.singleton(prefix), addresses.mac()); | ||
211 | + } | ||
212 | + } | ||
213 | + } | ||
214 | + return null; | ||
215 | + } | ||
216 | + | ||
217 | + /** | ||
218 | + * Returns whether the given port is an outside-facing port with an IP | ||
219 | + * address configured. | ||
220 | + * | ||
221 | + * @param port the port to check | ||
222 | + * @return true if the port is an outside-facing port, otherwise false | ||
223 | + */ | ||
224 | + private boolean isOutsidePort(ConnectPoint port) { | ||
225 | + return !hostService.getAddressBindingsForPort(port).ips().isEmpty(); | ||
137 | } | 226 | } |
138 | 227 | ||
139 | @Override | 228 | @Override |
... | @@ -167,7 +256,7 @@ public class ProxyArpManager implements ProxyArpService { | ... | @@ -167,7 +256,7 @@ public class ProxyArpManager implements ProxyArpService { |
167 | if (arp.getOpCode() == ARP.OP_REPLY) { | 256 | if (arp.getOpCode() == ARP.OP_REPLY) { |
168 | forward(ethPkt); | 257 | forward(ethPkt); |
169 | } else if (arp.getOpCode() == ARP.OP_REQUEST) { | 258 | } else if (arp.getOpCode() == ARP.OP_REQUEST) { |
170 | - reply(ethPkt); | 259 | + reply(ethPkt, context.inPacket().receivedFrom()); |
171 | } | 260 | } |
172 | context.block(); | 261 | context.block(); |
173 | return true; | 262 | return true; |
... | @@ -185,12 +274,16 @@ public class ProxyArpManager implements ProxyArpService { | ... | @@ -185,12 +274,16 @@ public class ProxyArpManager implements ProxyArpService { |
185 | 274 | ||
186 | synchronized (externalPorts) { | 275 | synchronized (externalPorts) { |
187 | for (Entry<Device, PortNumber> entry : externalPorts.entries()) { | 276 | for (Entry<Device, PortNumber> entry : externalPorts.entries()) { |
277 | + ConnectPoint cp = new ConnectPoint(entry.getKey().id(), entry.getValue()); | ||
278 | + if (isOutsidePort(cp)) { | ||
279 | + continue; | ||
280 | + } | ||
281 | + | ||
188 | builder = DefaultTrafficTreatment.builder(); | 282 | builder = DefaultTrafficTreatment.builder(); |
189 | builder.setOutput(entry.getValue()); | 283 | builder.setOutput(entry.getValue()); |
190 | packetService.emit(new DefaultOutboundPacket(entry.getKey().id(), | 284 | packetService.emit(new DefaultOutboundPacket(entry.getKey().id(), |
191 | builder.build(), buf)); | 285 | builder.build(), buf)); |
192 | } | 286 | } |
193 | - | ||
194 | } | 287 | } |
195 | } | 288 | } |
196 | 289 | ||
... | @@ -234,15 +327,19 @@ public class ProxyArpManager implements ProxyArpService { | ... | @@ -234,15 +327,19 @@ public class ProxyArpManager implements ProxyArpService { |
234 | } | 327 | } |
235 | 328 | ||
236 | /** | 329 | /** |
237 | - * Builds an arp reply based on a request. | 330 | + * Builds an ARP reply based on a request. |
238 | - * @param h the host we want to send to | 331 | + * |
239 | - * @param request the arp request we got | 332 | + * @param srcIp the IP address to use as the reply source |
240 | - * @return an ethernet frame containing the arp reply | 333 | + * @param srcMac the MAC address to use as the reply source |
334 | + * @param request the ARP request we got | ||
335 | + * @return an Ethernet frame containing the ARP reply | ||
241 | */ | 336 | */ |
242 | - private Ethernet buildArpReply(Host h, Ethernet request) { | 337 | + private Ethernet buildArpReply(IpPrefix srcIp, MacAddress srcMac, |
338 | + Ethernet request) { | ||
339 | + | ||
243 | Ethernet eth = new Ethernet(); | 340 | Ethernet eth = new Ethernet(); |
244 | eth.setDestinationMACAddress(request.getSourceMACAddress()); | 341 | eth.setDestinationMACAddress(request.getSourceMACAddress()); |
245 | - eth.setSourceMACAddress(h.mac().getAddress()); | 342 | + eth.setSourceMACAddress(srcMac.getAddress()); |
246 | eth.setEtherType(Ethernet.TYPE_ARP); | 343 | eth.setEtherType(Ethernet.TYPE_ARP); |
247 | eth.setVlanID(request.getVlanID()); | 344 | eth.setVlanID(request.getVlanID()); |
248 | 345 | ||
... | @@ -253,12 +350,12 @@ public class ProxyArpManager implements ProxyArpService { | ... | @@ -253,12 +350,12 @@ public class ProxyArpManager implements ProxyArpService { |
253 | 350 | ||
254 | arp.setProtocolAddressLength((byte) IpPrefix.INET_LEN); | 351 | arp.setProtocolAddressLength((byte) IpPrefix.INET_LEN); |
255 | arp.setHardwareAddressLength((byte) Ethernet.DATALAYER_ADDRESS_LENGTH); | 352 | arp.setHardwareAddressLength((byte) Ethernet.DATALAYER_ADDRESS_LENGTH); |
256 | - arp.setSenderHardwareAddress(h.mac().getAddress()); | 353 | + arp.setSenderHardwareAddress(srcMac.getAddress()); |
257 | arp.setTargetHardwareAddress(request.getSourceMACAddress()); | 354 | arp.setTargetHardwareAddress(request.getSourceMACAddress()); |
258 | 355 | ||
259 | arp.setTargetProtocolAddress(((ARP) request.getPayload()) | 356 | arp.setTargetProtocolAddress(((ARP) request.getPayload()) |
260 | .getSenderProtocolAddress()); | 357 | .getSenderProtocolAddress()); |
261 | - arp.setSenderProtocolAddress(h.ipAddresses().iterator().next().toRealInt()); | 358 | + arp.setSenderProtocolAddress(srcIp.toRealInt()); |
262 | eth.setPayload(arp); | 359 | eth.setPayload(arp); |
263 | return eth; | 360 | return eth; |
264 | } | 361 | } | ... | ... |
... | @@ -13,6 +13,7 @@ import java.util.Arrays; | ... | @@ -13,6 +13,7 @@ import java.util.Arrays; |
13 | import java.util.Collections; | 13 | import java.util.Collections; |
14 | import java.util.Comparator; | 14 | import java.util.Comparator; |
15 | import java.util.List; | 15 | import java.util.List; |
16 | +import java.util.Set; | ||
16 | 17 | ||
17 | import org.junit.Before; | 18 | import org.junit.Before; |
18 | import org.junit.Test; | 19 | import org.junit.Test; |
... | @@ -31,6 +32,7 @@ import org.onlab.onos.net.device.DeviceService; | ... | @@ -31,6 +32,7 @@ import org.onlab.onos.net.device.DeviceService; |
31 | import org.onlab.onos.net.flow.instructions.Instruction; | 32 | import org.onlab.onos.net.flow.instructions.Instruction; |
32 | import org.onlab.onos.net.flow.instructions.Instructions.OutputInstruction; | 33 | import org.onlab.onos.net.flow.instructions.Instructions.OutputInstruction; |
33 | import org.onlab.onos.net.host.HostService; | 34 | import org.onlab.onos.net.host.HostService; |
35 | +import org.onlab.onos.net.host.PortAddresses; | ||
34 | import org.onlab.onos.net.link.LinkListener; | 36 | import org.onlab.onos.net.link.LinkListener; |
35 | import org.onlab.onos.net.link.LinkService; | 37 | import org.onlab.onos.net.link.LinkService; |
36 | import org.onlab.onos.net.packet.OutboundPacket; | 38 | import org.onlab.onos.net.packet.OutboundPacket; |
... | @@ -50,12 +52,13 @@ import com.google.common.collect.Sets; | ... | @@ -50,12 +52,13 @@ import com.google.common.collect.Sets; |
50 | */ | 52 | */ |
51 | public class ProxyArpManagerTest { | 53 | public class ProxyArpManagerTest { |
52 | 54 | ||
53 | - private static final int NUM_DEVICES = 4; | 55 | + private static final int NUM_DEVICES = 6; |
54 | private static final int NUM_PORTS_PER_DEVICE = 3; | 56 | private static final int NUM_PORTS_PER_DEVICE = 3; |
55 | - private static final int NUM_FLOOD_PORTS = 4; | 57 | + private static final int NUM_ADDRESS_PORTS = NUM_DEVICES / 2; |
58 | + private static final int NUM_FLOOD_PORTS = 3; | ||
56 | 59 | ||
57 | - private static final IpPrefix IP1 = IpPrefix.valueOf("10.0.0.1/24"); | 60 | + private static final IpPrefix IP1 = IpPrefix.valueOf("192.168.1.1/24"); |
58 | - private static final IpPrefix IP2 = IpPrefix.valueOf("10.0.0.2/24"); | 61 | + private static final IpPrefix IP2 = IpPrefix.valueOf("192.168.1.2/24"); |
59 | 62 | ||
60 | private static final ProviderId PID = new ProviderId("of", "foo"); | 63 | private static final ProviderId PID = new ProviderId("of", "foo"); |
61 | 64 | ||
... | @@ -104,6 +107,9 @@ public class ProxyArpManagerTest { | ... | @@ -104,6 +107,9 @@ public class ProxyArpManagerTest { |
104 | * The default topology is a unidirectional ring topology. Each switch has | 107 | * The default topology is a unidirectional ring topology. Each switch has |
105 | * 3 ports. Ports 2 and 3 have the links to neighbor switches, and port 1 | 108 | * 3 ports. Ports 2 and 3 have the links to neighbor switches, and port 1 |
106 | * is free (edge port). | 109 | * is free (edge port). |
110 | + * The first half of the switches have IP addresses configured on their | ||
111 | + * free ports (port 1). The second half of the switches have no IP | ||
112 | + * addresses configured. | ||
107 | */ | 113 | */ |
108 | private void createTopology() { | 114 | private void createTopology() { |
109 | deviceService = createMock(DeviceService.class); | 115 | deviceService = createMock(DeviceService.class); |
... | @@ -114,6 +120,7 @@ public class ProxyArpManagerTest { | ... | @@ -114,6 +120,7 @@ public class ProxyArpManagerTest { |
114 | 120 | ||
115 | createDevices(NUM_DEVICES, NUM_PORTS_PER_DEVICE); | 121 | createDevices(NUM_DEVICES, NUM_PORTS_PER_DEVICE); |
116 | createLinks(NUM_DEVICES); | 122 | createLinks(NUM_DEVICES); |
123 | + addAddressBindings(); | ||
117 | } | 124 | } |
118 | 125 | ||
119 | /** | 126 | /** |
... | @@ -138,10 +145,11 @@ public class ProxyArpManagerTest { | ... | @@ -138,10 +145,11 @@ public class ProxyArpManagerTest { |
138 | ports.add(port); | 145 | ports.add(port); |
139 | } | 146 | } |
140 | 147 | ||
141 | - expect(deviceService.getPorts(devId)).andReturn(ports); | 148 | + expect(deviceService.getPorts(devId)).andReturn(ports).anyTimes(); |
149 | + expect(deviceService.getDevice(devId)).andReturn(device).anyTimes(); | ||
142 | } | 150 | } |
143 | 151 | ||
144 | - expect(deviceService.getDevices()).andReturn(devices); | 152 | + expect(deviceService.getDevices()).andReturn(devices).anyTimes(); |
145 | replay(deviceService); | 153 | replay(deviceService); |
146 | } | 154 | } |
147 | 155 | ||
... | @@ -173,6 +181,31 @@ public class ProxyArpManagerTest { | ... | @@ -173,6 +181,31 @@ public class ProxyArpManagerTest { |
173 | replay(linkService); | 181 | replay(linkService); |
174 | } | 182 | } |
175 | 183 | ||
184 | + private void addAddressBindings() { | ||
185 | + Set<PortAddresses> addresses = Sets.newHashSet(); | ||
186 | + | ||
187 | + for (int i = 1; i <= NUM_ADDRESS_PORTS; i++) { | ||
188 | + ConnectPoint cp = new ConnectPoint(getDeviceId(i), P1); | ||
189 | + IpPrefix prefix1 = IpPrefix.valueOf("10.0." + (2 * i - 1) + ".1/24"); | ||
190 | + IpPrefix prefix2 = IpPrefix.valueOf("10.0." + (2 * i) + ".1/24"); | ||
191 | + PortAddresses pa = new PortAddresses(cp, | ||
192 | + Sets.newHashSet(prefix1, prefix2), MacAddress.valueOf(i)); | ||
193 | + addresses.add(pa); | ||
194 | + | ||
195 | + expect(hostService.getAddressBindingsForPort(cp)) | ||
196 | + .andReturn(pa).anyTimes(); | ||
197 | + } | ||
198 | + | ||
199 | + expect(hostService.getAddressBindings()).andReturn(addresses).anyTimes(); | ||
200 | + | ||
201 | + for (int i = 1; i <= NUM_FLOOD_PORTS; i++) { | ||
202 | + ConnectPoint cp = new ConnectPoint(getDeviceId(i + NUM_ADDRESS_PORTS), | ||
203 | + P1); | ||
204 | + expect(hostService.getAddressBindingsForPort(cp)) | ||
205 | + .andReturn(new PortAddresses(cp, null, null)).anyTimes(); | ||
206 | + } | ||
207 | + } | ||
208 | + | ||
176 | /** | 209 | /** |
177 | * Tests {@link ProxyArpManager#known(IpPrefix)} in the case where the | 210 | * Tests {@link ProxyArpManager#known(IpPrefix)} in the case where the |
178 | * IP address is not known. | 211 | * IP address is not known. |
... | @@ -210,10 +243,10 @@ public class ProxyArpManagerTest { | ... | @@ -210,10 +243,10 @@ public class ProxyArpManagerTest { |
210 | */ | 243 | */ |
211 | @Test | 244 | @Test |
212 | public void testReplyKnown() { | 245 | public void testReplyKnown() { |
213 | - Host replyer = new DefaultHost(PID, HID1, MAC1, VLAN1, LOC2, | 246 | + Host replyer = new DefaultHost(PID, HID1, MAC1, VLAN1, getLocation(4), |
214 | Collections.singleton(IP1)); | 247 | Collections.singleton(IP1)); |
215 | 248 | ||
216 | - Host requestor = new DefaultHost(PID, HID2, MAC2, VLAN1, LOC1, | 249 | + Host requestor = new DefaultHost(PID, HID2, MAC2, VLAN1, getLocation(5), |
217 | Collections.singleton(IP2)); | 250 | Collections.singleton(IP2)); |
218 | 251 | ||
219 | expect(hostService.getHostsByIp(IpPrefix.valueOf(IP1.toOctets()))) | 252 | expect(hostService.getHostsByIp(IpPrefix.valueOf(IP1.toOctets()))) |
... | @@ -224,11 +257,11 @@ public class ProxyArpManagerTest { | ... | @@ -224,11 +257,11 @@ public class ProxyArpManagerTest { |
224 | 257 | ||
225 | Ethernet arpRequest = buildArp(ARP.OP_REQUEST, MAC2, null, IP2, IP1); | 258 | Ethernet arpRequest = buildArp(ARP.OP_REQUEST, MAC2, null, IP2, IP1); |
226 | 259 | ||
227 | - proxyArp.reply(arpRequest); | 260 | + proxyArp.reply(arpRequest, getLocation(5)); |
228 | 261 | ||
229 | assertEquals(1, packetService.packets.size()); | 262 | assertEquals(1, packetService.packets.size()); |
230 | Ethernet arpReply = buildArp(ARP.OP_REPLY, MAC1, MAC2, IP1, IP2); | 263 | Ethernet arpReply = buildArp(ARP.OP_REPLY, MAC1, MAC2, IP1, IP2); |
231 | - verifyPacketOut(arpReply, LOC1, packetService.packets.get(0)); | 264 | + verifyPacketOut(arpReply, getLocation(5), packetService.packets.get(0)); |
232 | } | 265 | } |
233 | 266 | ||
234 | /** | 267 | /** |
... | @@ -238,7 +271,7 @@ public class ProxyArpManagerTest { | ... | @@ -238,7 +271,7 @@ public class ProxyArpManagerTest { |
238 | */ | 271 | */ |
239 | @Test | 272 | @Test |
240 | public void testReplyUnknown() { | 273 | public void testReplyUnknown() { |
241 | - Host requestor = new DefaultHost(PID, HID2, MAC2, VLAN1, LOC1, | 274 | + Host requestor = new DefaultHost(PID, HID2, MAC2, VLAN1, getLocation(5), |
242 | Collections.singleton(IP2)); | 275 | Collections.singleton(IP2)); |
243 | 276 | ||
244 | expect(hostService.getHostsByIp(IpPrefix.valueOf(IP1.toOctets()))) | 277 | expect(hostService.getHostsByIp(IpPrefix.valueOf(IP1.toOctets()))) |
... | @@ -249,7 +282,7 @@ public class ProxyArpManagerTest { | ... | @@ -249,7 +282,7 @@ public class ProxyArpManagerTest { |
249 | 282 | ||
250 | Ethernet arpRequest = buildArp(ARP.OP_REQUEST, MAC2, null, IP2, IP1); | 283 | Ethernet arpRequest = buildArp(ARP.OP_REQUEST, MAC2, null, IP2, IP1); |
251 | 284 | ||
252 | - proxyArp.reply(arpRequest); | 285 | + proxyArp.reply(arpRequest, getLocation(5)); |
253 | 286 | ||
254 | verifyFlood(arpRequest); | 287 | verifyFlood(arpRequest); |
255 | } | 288 | } |
... | @@ -262,10 +295,10 @@ public class ProxyArpManagerTest { | ... | @@ -262,10 +295,10 @@ public class ProxyArpManagerTest { |
262 | */ | 295 | */ |
263 | @Test | 296 | @Test |
264 | public void testReplyDifferentVlan() { | 297 | public void testReplyDifferentVlan() { |
265 | - Host replyer = new DefaultHost(PID, HID1, MAC1, VLAN2, LOC2, | 298 | + Host replyer = new DefaultHost(PID, HID1, MAC1, VLAN2, getLocation(4), |
266 | Collections.singleton(IP1)); | 299 | Collections.singleton(IP1)); |
267 | 300 | ||
268 | - Host requestor = new DefaultHost(PID, HID2, MAC2, VLAN1, LOC1, | 301 | + Host requestor = new DefaultHost(PID, HID2, MAC2, VLAN1, getLocation(5), |
269 | Collections.singleton(IP2)); | 302 | Collections.singleton(IP2)); |
270 | 303 | ||
271 | expect(hostService.getHostsByIp(IpPrefix.valueOf(IP1.toOctets()))) | 304 | expect(hostService.getHostsByIp(IpPrefix.valueOf(IP1.toOctets()))) |
... | @@ -276,11 +309,84 @@ public class ProxyArpManagerTest { | ... | @@ -276,11 +309,84 @@ public class ProxyArpManagerTest { |
276 | 309 | ||
277 | Ethernet arpRequest = buildArp(ARP.OP_REQUEST, MAC2, null, IP2, IP1); | 310 | Ethernet arpRequest = buildArp(ARP.OP_REQUEST, MAC2, null, IP2, IP1); |
278 | 311 | ||
279 | - proxyArp.reply(arpRequest); | 312 | + proxyArp.reply(arpRequest, getLocation(5)); |
280 | 313 | ||
281 | verifyFlood(arpRequest); | 314 | verifyFlood(arpRequest); |
282 | } | 315 | } |
283 | 316 | ||
317 | + @Test | ||
318 | + public void testReplyToRequestForUs() { | ||
319 | + IpPrefix theirIp = IpPrefix.valueOf("10.0.1.254/24"); | ||
320 | + IpPrefix ourFirstIp = IpPrefix.valueOf("10.0.1.1/24"); | ||
321 | + IpPrefix ourSecondIp = IpPrefix.valueOf("10.0.2.1/24"); | ||
322 | + MacAddress ourMac = MacAddress.valueOf(1L); | ||
323 | + | ||
324 | + Host requestor = new DefaultHost(PID, HID2, MAC2, VLAN1, LOC1, | ||
325 | + Collections.singleton(theirIp)); | ||
326 | + | ||
327 | + expect(hostService.getHost(HID2)).andReturn(requestor); | ||
328 | + replay(hostService); | ||
329 | + | ||
330 | + Ethernet arpRequest = buildArp(ARP.OP_REQUEST, MAC2, null, theirIp, ourFirstIp); | ||
331 | + | ||
332 | + proxyArp.reply(arpRequest, LOC1); | ||
333 | + | ||
334 | + assertEquals(1, packetService.packets.size()); | ||
335 | + Ethernet arpReply = buildArp(ARP.OP_REPLY, ourMac, MAC2, ourFirstIp, theirIp); | ||
336 | + verifyPacketOut(arpReply, LOC1, packetService.packets.get(0)); | ||
337 | + | ||
338 | + // Test a request for the second address on that port | ||
339 | + packetService.packets.clear(); | ||
340 | + arpRequest = buildArp(ARP.OP_REQUEST, MAC2, null, theirIp, ourSecondIp); | ||
341 | + | ||
342 | + proxyArp.reply(arpRequest, LOC1); | ||
343 | + | ||
344 | + assertEquals(1, packetService.packets.size()); | ||
345 | + arpReply = buildArp(ARP.OP_REPLY, ourMac, MAC2, ourSecondIp, theirIp); | ||
346 | + verifyPacketOut(arpReply, LOC1, packetService.packets.get(0)); | ||
347 | + } | ||
348 | + | ||
349 | + @Test | ||
350 | + public void testReplyExternalPortBadRequest() { | ||
351 | + replay(hostService); // no further host service expectations | ||
352 | + | ||
353 | + IpPrefix theirIp = IpPrefix.valueOf("10.0.1.254/24"); | ||
354 | + | ||
355 | + // Request for a valid external IP address but coming in the wrong port | ||
356 | + Ethernet arpRequest = buildArp(ARP.OP_REQUEST, MAC1, null, theirIp, | ||
357 | + IpPrefix.valueOf("10.0.3.1")); | ||
358 | + proxyArp.reply(arpRequest, LOC1); | ||
359 | + assertEquals(0, packetService.packets.size()); | ||
360 | + | ||
361 | + // Request for a valid internal IP address but coming in an external port | ||
362 | + packetService.packets.clear(); | ||
363 | + arpRequest = buildArp(ARP.OP_REQUEST, MAC1, null, theirIp, IP1); | ||
364 | + proxyArp.reply(arpRequest, LOC1); | ||
365 | + assertEquals(0, packetService.packets.size()); | ||
366 | + } | ||
367 | + | ||
368 | + @Test | ||
369 | + public void testReplyToRequestFromUs() { | ||
370 | + replay(hostService); // no further host service expectations | ||
371 | + | ||
372 | + IpPrefix ourIp = IpPrefix.valueOf("10.0.1.1/24"); | ||
373 | + MacAddress ourMac = MacAddress.valueOf(1L); | ||
374 | + IpPrefix theirIp = IpPrefix.valueOf("10.0.1.100/24"); | ||
375 | + | ||
376 | + // This is a request from something inside our network (like a BGP | ||
377 | + // daemon) to an external host. | ||
378 | + Ethernet arpRequest = buildArp(ARP.OP_REQUEST, ourMac, null, ourIp, theirIp); | ||
379 | + proxyArp.reply(arpRequest, getLocation(5)); | ||
380 | + | ||
381 | + assertEquals(1, packetService.packets.size()); | ||
382 | + verifyPacketOut(arpRequest, getLocation(1), packetService.packets.get(0)); | ||
383 | + | ||
384 | + // The same request from a random external port should fail | ||
385 | + packetService.packets.clear(); | ||
386 | + proxyArp.reply(arpRequest, getLocation(2)); | ||
387 | + assertEquals(0, packetService.packets.size()); | ||
388 | + } | ||
389 | + | ||
284 | /** | 390 | /** |
285 | * Tests {@link ProxyArpManager#forward(Ethernet)} in the case where the | 391 | * Tests {@link ProxyArpManager#forward(Ethernet)} in the case where the |
286 | * destination host is known. | 392 | * destination host is known. |
... | @@ -338,7 +444,8 @@ public class ProxyArpManagerTest { | ... | @@ -338,7 +444,8 @@ public class ProxyArpManagerTest { |
338 | }); | 444 | }); |
339 | 445 | ||
340 | for (int i = 0; i < NUM_FLOOD_PORTS; i++) { | 446 | for (int i = 0; i < NUM_FLOOD_PORTS; i++) { |
341 | - ConnectPoint cp = new ConnectPoint(getDeviceId(i + 1), PortNumber.portNumber(1)); | 447 | + ConnectPoint cp = new ConnectPoint(getDeviceId(NUM_ADDRESS_PORTS + i + 1), |
448 | + PortNumber.portNumber(1)); | ||
342 | 449 | ||
343 | OutboundPacket outboundPacket = packetService.packets.get(i); | 450 | OutboundPacket outboundPacket = packetService.packets.get(i); |
344 | verifyPacketOut(packet, cp, outboundPacket); | 451 | verifyPacketOut(packet, cp, outboundPacket); |
... | @@ -372,6 +479,10 @@ public class ProxyArpManagerTest { | ... | @@ -372,6 +479,10 @@ public class ProxyArpManagerTest { |
372 | return DeviceId.deviceId("" + i); | 479 | return DeviceId.deviceId("" + i); |
373 | } | 480 | } |
374 | 481 | ||
482 | + private static HostLocation getLocation(int i) { | ||
483 | + return new HostLocation(new ConnectPoint(getDeviceId(i), P1), 123L); | ||
484 | + } | ||
485 | + | ||
375 | /** | 486 | /** |
376 | * Builds an ARP packet with the given parameters. | 487 | * Builds an ARP packet with the given parameters. |
377 | * | 488 | * | ... | ... |
1 | package org.onlab.onos.net.topology.impl; | 1 | package org.onlab.onos.net.topology.impl; |
2 | 2 | ||
3 | import com.google.common.collect.ImmutableSet; | 3 | import com.google.common.collect.ImmutableSet; |
4 | + | ||
4 | import org.junit.After; | 5 | import org.junit.After; |
5 | import org.junit.Before; | 6 | import org.junit.Before; |
6 | import org.junit.Test; | 7 | import org.junit.Test; |
... | @@ -21,10 +22,12 @@ import org.onlab.onos.net.topology.TopologyProviderService; | ... | @@ -21,10 +22,12 @@ import org.onlab.onos.net.topology.TopologyProviderService; |
21 | 22 | ||
22 | import java.util.List; | 23 | import java.util.List; |
23 | import java.util.Set; | 24 | import java.util.Set; |
25 | +import java.util.concurrent.Phaser; | ||
26 | +import java.util.concurrent.TimeUnit; | ||
27 | +import java.util.concurrent.TimeoutException; | ||
24 | 28 | ||
25 | -import static org.junit.Assert.assertEquals; | 29 | +import static org.junit.Assert.*; |
26 | -import static org.junit.Assert.assertNotNull; | 30 | +import static org.hamcrest.Matchers.*; |
27 | -import static org.onlab.junit.TestTools.assertAfter; | ||
28 | import static org.onlab.onos.net.NetTestTools.device; | 31 | import static org.onlab.onos.net.NetTestTools.device; |
29 | import static org.onlab.onos.net.NetTestTools.link; | 32 | import static org.onlab.onos.net.NetTestTools.link; |
30 | import static org.onlab.onos.net.device.DeviceEvent.Type.DEVICE_ADDED; | 33 | import static org.onlab.onos.net.device.DeviceEvent.Type.DEVICE_ADDED; |
... | @@ -41,6 +44,9 @@ public class DefaultTopologyProviderTest { | ... | @@ -41,6 +44,9 @@ public class DefaultTopologyProviderTest { |
41 | private TestLinkService linkService = new TestLinkService(); | 44 | private TestLinkService linkService = new TestLinkService(); |
42 | private TestTopoProviderService providerService; | 45 | private TestTopoProviderService providerService; |
43 | 46 | ||
47 | + // phase corresponds to number of topologyChanged called | ||
48 | + private Phaser topologyChangedCounts = new Phaser(1); | ||
49 | + | ||
44 | @Before | 50 | @Before |
45 | public void setUp() { | 51 | public void setUp() { |
46 | provider.deviceService = deviceService; | 52 | provider.deviceService = deviceService; |
... | @@ -66,26 +72,23 @@ public class DefaultTopologyProviderTest { | ... | @@ -66,26 +72,23 @@ public class DefaultTopologyProviderTest { |
66 | } | 72 | } |
67 | 73 | ||
68 | @Test | 74 | @Test |
69 | - public void basics() { | 75 | + public void basics() throws InterruptedException, TimeoutException { |
70 | - assertAfter(100, new Runnable() { | 76 | + assertEquals(1, topologyChangedCounts.awaitAdvanceInterruptibly(0, 1, TimeUnit.SECONDS)); |
71 | - @Override | 77 | + validateSubmission(); |
72 | - public void run() { | ||
73 | - validateSubmission(); | ||
74 | - } | ||
75 | - }); | ||
76 | } | 78 | } |
77 | 79 | ||
78 | @Test | 80 | @Test |
79 | - public void eventDriven() { | 81 | + public void eventDriven() throws InterruptedException, TimeoutException { |
80 | - assertAfter(100, new Runnable() { | 82 | + assertEquals(1, topologyChangedCounts.awaitAdvanceInterruptibly(0, 1, TimeUnit.SECONDS)); |
81 | - @Override | 83 | + validateSubmission(); |
82 | - public void run() { | 84 | + |
83 | - validateSubmission(); | 85 | + deviceService.post(new DeviceEvent(DEVICE_ADDED, device("z"), null)); |
84 | - deviceService.post(new DeviceEvent(DEVICE_ADDED, device("z"), null)); | 86 | + linkService.post(new LinkEvent(LINK_ADDED, link("z", 1, "a", 4))); |
85 | - linkService.post(new LinkEvent(LINK_ADDED, link("z", 1, "a", 4))); | 87 | + assertThat(topologyChangedCounts.awaitAdvanceInterruptibly(1, 1, TimeUnit.SECONDS), |
86 | - validateSubmission(); | 88 | + is(greaterThanOrEqualTo(2))); |
87 | - } | 89 | + // Note: posting event, to trigger topologyChanged call, |
88 | - }); | 90 | + // but dummy topology will not change. |
91 | + validateSubmission(); | ||
89 | } | 92 | } |
90 | 93 | ||
91 | 94 | ||
... | @@ -119,6 +122,7 @@ public class DefaultTopologyProviderTest { | ... | @@ -119,6 +122,7 @@ public class DefaultTopologyProviderTest { |
119 | @Override | 122 | @Override |
120 | public void topologyChanged(GraphDescription graphDescription, List<Event> reasons) { | 123 | public void topologyChanged(GraphDescription graphDescription, List<Event> reasons) { |
121 | graphDesc = graphDescription; | 124 | graphDesc = graphDescription; |
125 | + topologyChangedCounts.arrive(); | ||
122 | } | 126 | } |
123 | } | 127 | } |
124 | 128 | ... | ... |
... | @@ -18,7 +18,6 @@ import org.onlab.onos.cluster.DefaultControllerNode; | ... | @@ -18,7 +18,6 @@ import org.onlab.onos.cluster.DefaultControllerNode; |
18 | import org.onlab.onos.cluster.NodeId; | 18 | import org.onlab.onos.cluster.NodeId; |
19 | import org.onlab.onos.store.AbstractStore; | 19 | import org.onlab.onos.store.AbstractStore; |
20 | import org.onlab.onos.store.cluster.messaging.ClusterCommunicationAdminService; | 20 | import org.onlab.onos.store.cluster.messaging.ClusterCommunicationAdminService; |
21 | -import org.onlab.onos.store.cluster.messaging.impl.ClusterCommunicationManager; | ||
22 | import org.onlab.packet.IpPrefix; | 21 | import org.onlab.packet.IpPrefix; |
23 | import org.slf4j.Logger; | 22 | import org.slf4j.Logger; |
24 | import org.slf4j.LoggerFactory; | 23 | import org.slf4j.LoggerFactory; |
... | @@ -48,7 +47,7 @@ public class DistributedClusterStore | ... | @@ -48,7 +47,7 @@ public class DistributedClusterStore |
48 | private final Map<NodeId, State> states = new ConcurrentHashMap<>(); | 47 | private final Map<NodeId, State> states = new ConcurrentHashMap<>(); |
49 | private final Cache<NodeId, ControllerNode> livenessCache = CacheBuilder.newBuilder() | 48 | private final Cache<NodeId, ControllerNode> livenessCache = CacheBuilder.newBuilder() |
50 | .maximumSize(1000) | 49 | .maximumSize(1000) |
51 | - .expireAfterWrite(ClusterCommunicationManager.HEART_BEAT_INTERVAL_MILLIS * 3, TimeUnit.MILLISECONDS) | 50 | + .expireAfterWrite(/*ClusterCommunicationManager.HEART_BEAT_INTERVAL_MILLIS * */3, TimeUnit.MILLISECONDS) |
52 | .removalListener(new LivenessCacheRemovalListener()).build(); | 51 | .removalListener(new LivenessCacheRemovalListener()).build(); |
53 | 52 | ||
54 | @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY) | 53 | @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY) | ... | ... |
1 | -package org.onlab.onos.store.cluster.messaging; | ||
2 | - | ||
3 | -import org.onlab.onos.cluster.ControllerNode; | ||
4 | -import org.onlab.onos.store.cluster.impl.ClusterNodesDelegate; | ||
5 | - | ||
6 | -// TODO: This service interface can be removed, once we properly start | ||
7 | -// using ClusterService | ||
8 | -/** | ||
9 | - * Service for administering communications manager. | ||
10 | - */ | ||
11 | -public interface ClusterCommunicationAdminService { | ||
12 | - | ||
13 | - /** | ||
14 | - * Initialize. | ||
15 | - */ | ||
16 | - void initialize(ControllerNode localNode, ClusterNodesDelegate nodesDelegate); | ||
17 | - | ||
18 | - /** | ||
19 | - * Adds the node to the list of monitored nodes. | ||
20 | - * | ||
21 | - * @param node node to be added | ||
22 | - */ | ||
23 | - void addNode(ControllerNode node); | ||
24 | - | ||
25 | - /** | ||
26 | - * Removes the node from the list of monitored nodes. | ||
27 | - * | ||
28 | - * @param node node to be removed | ||
29 | - */ | ||
30 | - void removeNode(ControllerNode node); | ||
31 | -} | ||
... | \ No newline at end of file | ... | \ No newline at end of file |
1 | -package org.onlab.onos.store.cluster.messaging; | ||
2 | - | ||
3 | -// FIXME: not used any more? remove | ||
4 | -/** | ||
5 | - * Service for encoding & decoding intra-cluster message payload. | ||
6 | - */ | ||
7 | -public interface SerializationService { | ||
8 | - | ||
9 | - /** | ||
10 | - * Decodes the specified byte buffer to obtain the message within. | ||
11 | - * | ||
12 | - * @param buffer byte buffer with message(s) | ||
13 | - * @return parsed message | ||
14 | - */ | ||
15 | - <T> T decode(byte[] data); | ||
16 | - | ||
17 | - /** | ||
18 | - * Encodes the specified message into the given byte buffer. | ||
19 | - * | ||
20 | - * @param message message to be encoded | ||
21 | - * @param buffer byte buffer to receive the message data | ||
22 | - */ | ||
23 | - byte[] encode(Object message); | ||
24 | - | ||
25 | -} |
... | @@ -4,8 +4,6 @@ import static com.google.common.base.Preconditions.checkArgument; | ... | @@ -4,8 +4,6 @@ import static com.google.common.base.Preconditions.checkArgument; |
4 | 4 | ||
5 | import java.io.IOException; | 5 | import java.io.IOException; |
6 | import java.util.Set; | 6 | import java.util.Set; |
7 | -import java.util.Timer; | ||
8 | -import java.util.TimerTask; | ||
9 | import org.apache.felix.scr.annotations.Activate; | 7 | import org.apache.felix.scr.annotations.Activate; |
10 | import org.apache.felix.scr.annotations.Component; | 8 | import org.apache.felix.scr.annotations.Component; |
11 | import org.apache.felix.scr.annotations.Deactivate; | 9 | import org.apache.felix.scr.annotations.Deactivate; |
... | @@ -16,9 +14,6 @@ import org.onlab.onos.cluster.ClusterService; | ... | @@ -16,9 +14,6 @@ import org.onlab.onos.cluster.ClusterService; |
16 | import org.onlab.onos.cluster.ControllerNode; | 14 | import org.onlab.onos.cluster.ControllerNode; |
17 | import org.onlab.onos.cluster.NodeId; | 15 | import org.onlab.onos.cluster.NodeId; |
18 | import org.onlab.onos.store.cluster.impl.ClusterMembershipEvent; | 16 | import org.onlab.onos.store.cluster.impl.ClusterMembershipEvent; |
19 | -import org.onlab.onos.store.cluster.impl.ClusterMembershipEventType; | ||
20 | -import org.onlab.onos.store.cluster.impl.ClusterNodesDelegate; | ||
21 | -import org.onlab.onos.store.cluster.messaging.ClusterCommunicationAdminService; | ||
22 | import org.onlab.onos.store.cluster.messaging.ClusterCommunicationService; | 17 | import org.onlab.onos.store.cluster.messaging.ClusterCommunicationService; |
23 | import org.onlab.onos.store.cluster.messaging.ClusterMessage; | 18 | import org.onlab.onos.store.cluster.messaging.ClusterMessage; |
24 | import org.onlab.onos.store.cluster.messaging.ClusterMessageHandler; | 19 | import org.onlab.onos.store.cluster.messaging.ClusterMessageHandler; |
... | @@ -39,19 +34,13 @@ import org.slf4j.LoggerFactory; | ... | @@ -39,19 +34,13 @@ import org.slf4j.LoggerFactory; |
39 | @Component(immediate = true) | 34 | @Component(immediate = true) |
40 | @Service | 35 | @Service |
41 | public class ClusterCommunicationManager | 36 | public class ClusterCommunicationManager |
42 | - implements ClusterCommunicationService, ClusterCommunicationAdminService { | 37 | + implements ClusterCommunicationService { |
43 | 38 | ||
44 | private final Logger log = LoggerFactory.getLogger(getClass()); | 39 | private final Logger log = LoggerFactory.getLogger(getClass()); |
45 | 40 | ||
46 | - private ControllerNode localNode; | ||
47 | - | ||
48 | @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY) | 41 | @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY) |
49 | private ClusterService clusterService; | 42 | private ClusterService clusterService; |
50 | 43 | ||
51 | - private ClusterNodesDelegate nodesDelegate; | ||
52 | - private final Timer timer = new Timer("onos-controller-heatbeats"); | ||
53 | - public static final long HEART_BEAT_INTERVAL_MILLIS = 1000L; | ||
54 | - | ||
55 | // TODO: This probably should not be a OSGi service. | 44 | // TODO: This probably should not be a OSGi service. |
56 | private MessagingService messagingService; | 45 | private MessagingService messagingService; |
57 | 46 | ||
... | @@ -72,7 +61,7 @@ public class ClusterCommunicationManager | ... | @@ -72,7 +61,7 @@ public class ClusterCommunicationManager |
72 | 61 | ||
73 | @Activate | 62 | @Activate |
74 | public void activate() { | 63 | public void activate() { |
75 | - localNode = clusterService.getLocalNode(); | 64 | + ControllerNode localNode = clusterService.getLocalNode(); |
76 | NettyMessagingService netty = new NettyMessagingService(localNode.tcpPort()); | 65 | NettyMessagingService netty = new NettyMessagingService(localNode.tcpPort()); |
77 | // FIXME: workaround until it becomes a service. | 66 | // FIXME: workaround until it becomes a service. |
78 | try { | 67 | try { |
... | @@ -92,8 +81,9 @@ public class ClusterCommunicationManager | ... | @@ -92,8 +81,9 @@ public class ClusterCommunicationManager |
92 | } | 81 | } |
93 | 82 | ||
94 | @Override | 83 | @Override |
95 | - public boolean broadcast(ClusterMessage message) { | 84 | + public boolean broadcast(ClusterMessage message) throws IOException { |
96 | boolean ok = true; | 85 | boolean ok = true; |
86 | + final ControllerNode localNode = clusterService.getLocalNode(); | ||
97 | for (ControllerNode node : clusterService.getNodes()) { | 87 | for (ControllerNode node : clusterService.getNodes()) { |
98 | if (!node.equals(localNode)) { | 88 | if (!node.equals(localNode)) { |
99 | ok = unicast(message, node.id()) && ok; | 89 | ok = unicast(message, node.id()) && ok; |
... | @@ -103,8 +93,9 @@ public class ClusterCommunicationManager | ... | @@ -103,8 +93,9 @@ public class ClusterCommunicationManager |
103 | } | 93 | } |
104 | 94 | ||
105 | @Override | 95 | @Override |
106 | - public boolean multicast(ClusterMessage message, Set<NodeId> nodes) { | 96 | + public boolean multicast(ClusterMessage message, Set<NodeId> nodes) throws IOException { |
107 | boolean ok = true; | 97 | boolean ok = true; |
98 | + final ControllerNode localNode = clusterService.getLocalNode(); | ||
108 | for (NodeId nodeId : nodes) { | 99 | for (NodeId nodeId : nodes) { |
109 | if (!nodeId.equals(localNode.id())) { | 100 | if (!nodeId.equals(localNode.id())) { |
110 | ok = unicast(message, nodeId) && ok; | 101 | ok = unicast(message, nodeId) && ok; |
... | @@ -114,7 +105,7 @@ public class ClusterCommunicationManager | ... | @@ -114,7 +105,7 @@ public class ClusterCommunicationManager |
114 | } | 105 | } |
115 | 106 | ||
116 | @Override | 107 | @Override |
117 | - public boolean unicast(ClusterMessage message, NodeId toNodeId) { | 108 | + public boolean unicast(ClusterMessage message, NodeId toNodeId) throws IOException { |
118 | ControllerNode node = clusterService.getNode(toNodeId); | 109 | ControllerNode node = clusterService.getNode(toNodeId); |
119 | checkArgument(node != null, "Unknown nodeId: %s", toNodeId); | 110 | checkArgument(node != null, "Unknown nodeId: %s", toNodeId); |
120 | Endpoint nodeEp = new Endpoint(node.ip().toString(), node.tcpPort()); | 111 | Endpoint nodeEp = new Endpoint(node.ip().toString(), node.tcpPort()); |
... | @@ -124,9 +115,8 @@ public class ClusterCommunicationManager | ... | @@ -124,9 +115,8 @@ public class ClusterCommunicationManager |
124 | return true; | 115 | return true; |
125 | } catch (IOException e) { | 116 | } catch (IOException e) { |
126 | log.error("Failed to send cluster message to nodeId: " + toNodeId, e); | 117 | log.error("Failed to send cluster message to nodeId: " + toNodeId, e); |
118 | + throw e; | ||
127 | } | 119 | } |
128 | - | ||
129 | - return false; | ||
130 | } | 120 | } |
131 | 121 | ||
132 | @Override | 122 | @Override |
... | @@ -135,61 +125,6 @@ public class ClusterCommunicationManager | ... | @@ -135,61 +125,6 @@ public class ClusterCommunicationManager |
135 | messagingService.registerHandler(subject.value(), new InternalClusterMessageHandler(subscriber)); | 125 | messagingService.registerHandler(subject.value(), new InternalClusterMessageHandler(subscriber)); |
136 | } | 126 | } |
137 | 127 | ||
138 | - @Override | ||
139 | - public void initialize(ControllerNode localNode, | ||
140 | - ClusterNodesDelegate delegate) { | ||
141 | - this.localNode = localNode; | ||
142 | - this.nodesDelegate = delegate; | ||
143 | - this.addSubscriber(new MessageSubject("CLUSTER_MEMBERSHIP_EVENT"), new ClusterMemebershipEventHandler()); | ||
144 | - timer.schedule(new KeepAlive(), 0, HEART_BEAT_INTERVAL_MILLIS); | ||
145 | - } | ||
146 | - | ||
147 | - @Override | ||
148 | - public void addNode(ControllerNode node) { | ||
149 | - //members.put(node.id(), node); | ||
150 | - } | ||
151 | - | ||
152 | - @Override | ||
153 | - public void removeNode(ControllerNode node) { | ||
154 | - broadcast(new ClusterMessage( | ||
155 | - localNode.id(), | ||
156 | - new MessageSubject("CLUSTER_MEMBERSHIP_EVENT"), | ||
157 | - SERIALIZER.encode(new ClusterMembershipEvent(ClusterMembershipEventType.LEAVING_MEMBER, node)))); | ||
158 | - //members.remove(node.id()); | ||
159 | - } | ||
160 | - | ||
161 | - // Sends a heart beat to all peers. | ||
162 | - private class KeepAlive extends TimerTask { | ||
163 | - | ||
164 | - @Override | ||
165 | - public void run() { | ||
166 | - broadcast(new ClusterMessage( | ||
167 | - localNode.id(), | ||
168 | - new MessageSubject("CLUSTER_MEMBERSHIP_EVENT"), | ||
169 | - SERIALIZER.encode(new ClusterMembershipEvent(ClusterMembershipEventType.HEART_BEAT, localNode)))); | ||
170 | - } | ||
171 | - } | ||
172 | - | ||
173 | - private class ClusterMemebershipEventHandler implements ClusterMessageHandler { | ||
174 | - | ||
175 | - @Override | ||
176 | - public void handle(ClusterMessage message) { | ||
177 | - | ||
178 | - ClusterMembershipEvent event = SERIALIZER.decode(message.payload()); | ||
179 | - ControllerNode node = event.node(); | ||
180 | - if (event.type() == ClusterMembershipEventType.HEART_BEAT) { | ||
181 | - log.info("Node {} sent a hearbeat", node.id()); | ||
182 | - nodesDelegate.nodeDetected(node.id(), node.ip(), node.tcpPort()); | ||
183 | - } else if (event.type() == ClusterMembershipEventType.LEAVING_MEMBER) { | ||
184 | - log.info("Node {} is leaving", node.id()); | ||
185 | - nodesDelegate.nodeRemoved(node.id()); | ||
186 | - } else if (event.type() == ClusterMembershipEventType.UNREACHABLE_MEMBER) { | ||
187 | - log.info("Node {} is unreachable", node.id()); | ||
188 | - nodesDelegate.nodeVanished(node.id()); | ||
189 | - } | ||
190 | - } | ||
191 | - } | ||
192 | - | ||
193 | private final class InternalClusterMessageHandler implements MessageHandler { | 128 | private final class InternalClusterMessageHandler implements MessageHandler { |
194 | 129 | ||
195 | private final ClusterMessageHandler handler; | 130 | private final ClusterMessageHandler handler; | ... | ... |
1 | -package org.onlab.onos.store.cluster.messaging.impl; | ||
2 | - | ||
3 | -import org.apache.felix.scr.annotations.Activate; | ||
4 | -import org.apache.felix.scr.annotations.Component; | ||
5 | -import org.apache.felix.scr.annotations.Deactivate; | ||
6 | -import org.apache.felix.scr.annotations.Service; | ||
7 | -import org.onlab.onos.store.cluster.messaging.MessageSubject; | ||
8 | -import org.onlab.onos.store.cluster.messaging.SerializationService; | ||
9 | -import org.onlab.onos.store.serializers.KryoPoolUtil; | ||
10 | -import org.onlab.util.KryoPool; | ||
11 | -import org.slf4j.Logger; | ||
12 | -import org.slf4j.LoggerFactory; | ||
13 | - | ||
14 | -//FIXME: not used any more? remove | ||
15 | -/** | ||
16 | - * Factory for parsing messages sent between cluster members. | ||
17 | - */ | ||
18 | -@Component(immediate = true) | ||
19 | -@Service | ||
20 | -public class MessageSerializer implements SerializationService { | ||
21 | - | ||
22 | - private final Logger log = LoggerFactory.getLogger(getClass()); | ||
23 | - | ||
24 | - private static final int METADATA_LENGTH = 12; // 8 + 4 | ||
25 | - private static final int LENGTH_OFFSET = 8; | ||
26 | - | ||
27 | - private static final long MARKER = 0xfeedcafebeaddeadL; | ||
28 | - | ||
29 | - private KryoPool serializerPool; | ||
30 | - | ||
31 | - @Activate | ||
32 | - public void activate() { | ||
33 | - setupKryoPool(); | ||
34 | - log.info("Started"); | ||
35 | - } | ||
36 | - | ||
37 | - @Deactivate | ||
38 | - public void deactivate() { | ||
39 | - log.info("Stopped"); | ||
40 | - } | ||
41 | - | ||
42 | - /** | ||
43 | - * Sets up the common serialzers pool. | ||
44 | - */ | ||
45 | - protected void setupKryoPool() { | ||
46 | - serializerPool = KryoPool.newBuilder() | ||
47 | - .register(KryoPoolUtil.API) | ||
48 | - // TODO: Should MessageSubject be in API bundle? | ||
49 | - .register(MessageSubject.class) | ||
50 | - .build() | ||
51 | - .populate(1); | ||
52 | - } | ||
53 | - | ||
54 | - | ||
55 | - @Override | ||
56 | - public <T> T decode(byte[] data) { | ||
57 | - return serializerPool.deserialize(data); | ||
58 | - } | ||
59 | - | ||
60 | - @Override | ||
61 | - public byte[] encode(Object payload) { | ||
62 | - return serializerPool.serialize(payload); | ||
63 | - } | ||
64 | -} |
... | @@ -86,14 +86,11 @@ public class GossipDeviceStore | ... | @@ -86,14 +86,11 @@ public class GossipDeviceStore |
86 | 86 | ||
87 | private final Logger log = getLogger(getClass()); | 87 | private final Logger log = getLogger(getClass()); |
88 | 88 | ||
89 | - public static final String DEVICE_NOT_FOUND = "Device with ID %s not found"; | 89 | + private static final String DEVICE_NOT_FOUND = "Device with ID %s not found"; |
90 | 90 | ||
91 | - // TODO: Check if inner Map can be replaced with plain Map. | ||
92 | // innerMap is used to lock a Device, thus instance should never be replaced. | 91 | // innerMap is used to lock a Device, thus instance should never be replaced. |
93 | - | ||
94 | // collection of Description given from various providers | 92 | // collection of Description given from various providers |
95 | - private final ConcurrentMap<DeviceId, | 93 | + private final ConcurrentMap<DeviceId, Map<ProviderId, DeviceDescriptions>> |
96 | - ConcurrentMap<ProviderId, DeviceDescriptions>> | ||
97 | deviceDescs = Maps.newConcurrentMap(); | 94 | deviceDescs = Maps.newConcurrentMap(); |
98 | 95 | ||
99 | // cache of Device and Ports generated by compositing descriptions from providers | 96 | // cache of Device and Ports generated by compositing descriptions from providers |
... | @@ -208,9 +205,9 @@ public class GossipDeviceStore | ... | @@ -208,9 +205,9 @@ public class GossipDeviceStore |
208 | final Timestamped<DeviceDescription> deltaDesc = new Timestamped<>(deviceDescription, newTimestamp); | 205 | final Timestamped<DeviceDescription> deltaDesc = new Timestamped<>(deviceDescription, newTimestamp); |
209 | final DeviceEvent event; | 206 | final DeviceEvent event; |
210 | final Timestamped<DeviceDescription> mergedDesc; | 207 | final Timestamped<DeviceDescription> mergedDesc; |
211 | - synchronized (getDeviceDescriptions(deviceId)) { | 208 | + synchronized (getOrCreateDeviceDescriptionsMap(deviceId)) { |
212 | event = createOrUpdateDeviceInternal(providerId, deviceId, deltaDesc); | 209 | event = createOrUpdateDeviceInternal(providerId, deviceId, deltaDesc); |
213 | - mergedDesc = getDeviceDescriptions(deviceId).get(providerId).getDeviceDesc(); | 210 | + mergedDesc = getOrCreateDeviceDescriptionsMap(deviceId).get(providerId).getDeviceDesc(); |
214 | } | 211 | } |
215 | if (event != null) { | 212 | if (event != null) { |
216 | log.info("Notifying peers of a device update topology event for providerId: {} and deviceId: {}", | 213 | log.info("Notifying peers of a device update topology event for providerId: {} and deviceId: {}", |
... | @@ -230,8 +227,8 @@ public class GossipDeviceStore | ... | @@ -230,8 +227,8 @@ public class GossipDeviceStore |
230 | Timestamped<DeviceDescription> deltaDesc) { | 227 | Timestamped<DeviceDescription> deltaDesc) { |
231 | 228 | ||
232 | // Collection of DeviceDescriptions for a Device | 229 | // Collection of DeviceDescriptions for a Device |
233 | - ConcurrentMap<ProviderId, DeviceDescriptions> providerDescs | 230 | + Map<ProviderId, DeviceDescriptions> providerDescs |
234 | - = getDeviceDescriptions(deviceId); | 231 | + = getOrCreateDeviceDescriptionsMap(deviceId); |
235 | 232 | ||
236 | synchronized (providerDescs) { | 233 | synchronized (providerDescs) { |
237 | // locking per device | 234 | // locking per device |
... | @@ -241,9 +238,7 @@ public class GossipDeviceStore | ... | @@ -241,9 +238,7 @@ public class GossipDeviceStore |
241 | return null; | 238 | return null; |
242 | } | 239 | } |
243 | 240 | ||
244 | - DeviceDescriptions descs | 241 | + DeviceDescriptions descs = getOrCreateProviderDeviceDescriptions(providerDescs, providerId, deltaDesc); |
245 | - = createIfAbsentUnchecked(providerDescs, providerId, | ||
246 | - new InitDeviceDescs(deltaDesc)); | ||
247 | 242 | ||
248 | final Device oldDevice = devices.get(deviceId); | 243 | final Device oldDevice = devices.get(deviceId); |
249 | final Device newDevice; | 244 | final Device newDevice; |
... | @@ -338,7 +333,7 @@ public class GossipDeviceStore | ... | @@ -338,7 +333,7 @@ public class GossipDeviceStore |
338 | private DeviceEvent markOfflineInternal(DeviceId deviceId, Timestamp timestamp) { | 333 | private DeviceEvent markOfflineInternal(DeviceId deviceId, Timestamp timestamp) { |
339 | 334 | ||
340 | Map<ProviderId, DeviceDescriptions> providerDescs | 335 | Map<ProviderId, DeviceDescriptions> providerDescs |
341 | - = getDeviceDescriptions(deviceId); | 336 | + = getOrCreateDeviceDescriptionsMap(deviceId); |
342 | 337 | ||
343 | // locking device | 338 | // locking device |
344 | synchronized (providerDescs) { | 339 | synchronized (providerDescs) { |
... | @@ -401,9 +396,9 @@ public class GossipDeviceStore | ... | @@ -401,9 +396,9 @@ public class GossipDeviceStore |
401 | final List<DeviceEvent> events; | 396 | final List<DeviceEvent> events; |
402 | final Timestamped<List<PortDescription>> merged; | 397 | final Timestamped<List<PortDescription>> merged; |
403 | 398 | ||
404 | - synchronized (getDeviceDescriptions(deviceId)) { | 399 | + synchronized (getOrCreateDeviceDescriptionsMap(deviceId)) { |
405 | events = updatePortsInternal(providerId, deviceId, timestampedInput); | 400 | events = updatePortsInternal(providerId, deviceId, timestampedInput); |
406 | - final DeviceDescriptions descs = getDeviceDescriptions(deviceId).get(providerId); | 401 | + final DeviceDescriptions descs = getOrCreateDeviceDescriptionsMap(deviceId).get(providerId); |
407 | List<PortDescription> mergedList = | 402 | List<PortDescription> mergedList = |
408 | FluentIterable.from(portDescriptions) | 403 | FluentIterable.from(portDescriptions) |
409 | .transform(new Function<PortDescription, PortDescription>() { | 404 | .transform(new Function<PortDescription, PortDescription>() { |
... | @@ -435,7 +430,7 @@ public class GossipDeviceStore | ... | @@ -435,7 +430,7 @@ public class GossipDeviceStore |
435 | Device device = devices.get(deviceId); | 430 | Device device = devices.get(deviceId); |
436 | checkArgument(device != null, DEVICE_NOT_FOUND, deviceId); | 431 | checkArgument(device != null, DEVICE_NOT_FOUND, deviceId); |
437 | 432 | ||
438 | - ConcurrentMap<ProviderId, DeviceDescriptions> descsMap = deviceDescs.get(deviceId); | 433 | + Map<ProviderId, DeviceDescriptions> descsMap = deviceDescs.get(deviceId); |
439 | checkArgument(descsMap != null, DEVICE_NOT_FOUND, deviceId); | 434 | checkArgument(descsMap != null, DEVICE_NOT_FOUND, deviceId); |
440 | 435 | ||
441 | List<DeviceEvent> events = new ArrayList<>(); | 436 | List<DeviceEvent> events = new ArrayList<>(); |
... | @@ -539,10 +534,34 @@ public class GossipDeviceStore | ... | @@ -539,10 +534,34 @@ public class GossipDeviceStore |
539 | NewConcurrentHashMap.<PortNumber, Port>ifNeeded()); | 534 | NewConcurrentHashMap.<PortNumber, Port>ifNeeded()); |
540 | } | 535 | } |
541 | 536 | ||
542 | - private ConcurrentMap<ProviderId, DeviceDescriptions> getDeviceDescriptions( | 537 | + private Map<ProviderId, DeviceDescriptions> getOrCreateDeviceDescriptionsMap( |
543 | DeviceId deviceId) { | 538 | DeviceId deviceId) { |
544 | - return createIfAbsentUnchecked(deviceDescs, deviceId, | 539 | + Map<ProviderId, DeviceDescriptions> r; |
545 | - NewConcurrentHashMap.<ProviderId, DeviceDescriptions>ifNeeded()); | 540 | + r = deviceDescs.get(deviceId); |
541 | + if (r == null) { | ||
542 | + r = new HashMap<ProviderId, DeviceDescriptions>(); | ||
543 | + final Map<ProviderId, DeviceDescriptions> concurrentlyAdded; | ||
544 | + concurrentlyAdded = deviceDescs.putIfAbsent(deviceId, r); | ||
545 | + if (concurrentlyAdded != null) { | ||
546 | + r = concurrentlyAdded; | ||
547 | + } | ||
548 | + } | ||
549 | + return r; | ||
550 | + } | ||
551 | + | ||
552 | + // Guarded by deviceDescs value (=Device lock) | ||
553 | + private DeviceDescriptions getOrCreateProviderDeviceDescriptions( | ||
554 | + Map<ProviderId, DeviceDescriptions> device, | ||
555 | + ProviderId providerId, Timestamped<DeviceDescription> deltaDesc) { | ||
556 | + | ||
557 | + synchronized (device) { | ||
558 | + DeviceDescriptions r = device.get(providerId); | ||
559 | + if (r == null) { | ||
560 | + r = new DeviceDescriptions(deltaDesc); | ||
561 | + device.put(providerId, r); | ||
562 | + } | ||
563 | + return r; | ||
564 | + } | ||
546 | } | 565 | } |
547 | 566 | ||
548 | @Override | 567 | @Override |
... | @@ -555,9 +574,9 @@ public class GossipDeviceStore | ... | @@ -555,9 +574,9 @@ public class GossipDeviceStore |
555 | = new Timestamped<>(portDescription, newTimestamp); | 574 | = new Timestamped<>(portDescription, newTimestamp); |
556 | final DeviceEvent event; | 575 | final DeviceEvent event; |
557 | final Timestamped<PortDescription> mergedDesc; | 576 | final Timestamped<PortDescription> mergedDesc; |
558 | - synchronized (getDeviceDescriptions(deviceId)) { | 577 | + synchronized (getOrCreateDeviceDescriptionsMap(deviceId)) { |
559 | event = updatePortStatusInternal(providerId, deviceId, deltaDesc); | 578 | event = updatePortStatusInternal(providerId, deviceId, deltaDesc); |
560 | - mergedDesc = getDeviceDescriptions(deviceId).get(providerId) | 579 | + mergedDesc = getOrCreateDeviceDescriptionsMap(deviceId).get(providerId) |
561 | .getPortDesc(portDescription.portNumber()); | 580 | .getPortDesc(portDescription.portNumber()); |
562 | } | 581 | } |
563 | if (event != null) { | 582 | if (event != null) { |
... | @@ -579,7 +598,7 @@ public class GossipDeviceStore | ... | @@ -579,7 +598,7 @@ public class GossipDeviceStore |
579 | Device device = devices.get(deviceId); | 598 | Device device = devices.get(deviceId); |
580 | checkArgument(device != null, DEVICE_NOT_FOUND, deviceId); | 599 | checkArgument(device != null, DEVICE_NOT_FOUND, deviceId); |
581 | 600 | ||
582 | - ConcurrentMap<ProviderId, DeviceDescriptions> descsMap = deviceDescs.get(deviceId); | 601 | + Map<ProviderId, DeviceDescriptions> descsMap = deviceDescs.get(deviceId); |
583 | checkArgument(descsMap != null, DEVICE_NOT_FOUND, deviceId); | 602 | checkArgument(descsMap != null, DEVICE_NOT_FOUND, deviceId); |
584 | 603 | ||
585 | synchronized (descsMap) { | 604 | synchronized (descsMap) { |
... | @@ -591,7 +610,7 @@ public class GossipDeviceStore | ... | @@ -591,7 +610,7 @@ public class GossipDeviceStore |
591 | 610 | ||
592 | DeviceDescriptions descs = descsMap.get(providerId); | 611 | DeviceDescriptions descs = descsMap.get(providerId); |
593 | // assuming all providers must to give DeviceDescription | 612 | // assuming all providers must to give DeviceDescription |
594 | - checkArgument(descs != null, | 613 | + verify(descs != null, |
595 | "Device description for Device ID %s from Provider %s was not found", | 614 | "Device description for Device ID %s from Provider %s was not found", |
596 | deviceId, providerId); | 615 | deviceId, providerId); |
597 | 616 | ||
... | @@ -661,7 +680,7 @@ public class GossipDeviceStore | ... | @@ -661,7 +680,7 @@ public class GossipDeviceStore |
661 | private DeviceEvent removeDeviceInternal(DeviceId deviceId, | 680 | private DeviceEvent removeDeviceInternal(DeviceId deviceId, |
662 | Timestamp timestamp) { | 681 | Timestamp timestamp) { |
663 | 682 | ||
664 | - Map<ProviderId, DeviceDescriptions> descs = getDeviceDescriptions(deviceId); | 683 | + Map<ProviderId, DeviceDescriptions> descs = getOrCreateDeviceDescriptionsMap(deviceId); |
665 | synchronized (descs) { | 684 | synchronized (descs) { |
666 | // accept removal request if given timestamp is newer than | 685 | // accept removal request if given timestamp is newer than |
667 | // the latest Timestamp from Primary provider | 686 | // the latest Timestamp from Primary provider |
... | @@ -751,14 +770,14 @@ public class GossipDeviceStore | ... | @@ -751,14 +770,14 @@ public class GossipDeviceStore |
751 | * | 770 | * |
752 | * @param device device the port is on | 771 | * @param device device the port is on |
753 | * @param number port number | 772 | * @param number port number |
754 | - * @param providerDescs Collection of Descriptions from multiple providers | 773 | + * @param descsMap Collection of Descriptions from multiple providers |
755 | * @return Port instance | 774 | * @return Port instance |
756 | */ | 775 | */ |
757 | private Port composePort(Device device, PortNumber number, | 776 | private Port composePort(Device device, PortNumber number, |
758 | - ConcurrentMap<ProviderId, DeviceDescriptions> providerDescs) { | 777 | + Map<ProviderId, DeviceDescriptions> descsMap) { |
759 | 778 | ||
760 | - ProviderId primary = pickPrimaryPID(providerDescs); | 779 | + ProviderId primary = pickPrimaryPID(descsMap); |
761 | - DeviceDescriptions primDescs = providerDescs.get(primary); | 780 | + DeviceDescriptions primDescs = descsMap.get(primary); |
762 | // if no primary, assume not enabled | 781 | // if no primary, assume not enabled |
763 | // TODO: revisit this default port enabled/disabled behavior | 782 | // TODO: revisit this default port enabled/disabled behavior |
764 | boolean isEnabled = false; | 783 | boolean isEnabled = false; |
... | @@ -770,7 +789,7 @@ public class GossipDeviceStore | ... | @@ -770,7 +789,7 @@ public class GossipDeviceStore |
770 | annotations = merge(annotations, portDesc.value().annotations()); | 789 | annotations = merge(annotations, portDesc.value().annotations()); |
771 | } | 790 | } |
772 | 791 | ||
773 | - for (Entry<ProviderId, DeviceDescriptions> e : providerDescs.entrySet()) { | 792 | + for (Entry<ProviderId, DeviceDescriptions> e : descsMap.entrySet()) { |
774 | if (e.getKey().equals(primary)) { | 793 | if (e.getKey().equals(primary)) { |
775 | continue; | 794 | continue; |
776 | } | 795 | } |
... | @@ -893,41 +912,48 @@ public class GossipDeviceStore | ... | @@ -893,41 +912,48 @@ public class GossipDeviceStore |
893 | private DeviceAntiEntropyAdvertisement createAdvertisement() { | 912 | private DeviceAntiEntropyAdvertisement createAdvertisement() { |
894 | final NodeId self = clusterService.getLocalNode().id(); | 913 | final NodeId self = clusterService.getLocalNode().id(); |
895 | 914 | ||
896 | - Map<DeviceFragmentId, Timestamp> devices = new HashMap<>(deviceDescs.size()); | 915 | + final int numDevices = deviceDescs.size(); |
897 | - final int portsPerDevice = 8; // random guess to minimize reallocation | 916 | + Map<DeviceFragmentId, Timestamp> adDevices = new HashMap<>(numDevices); |
898 | - Map<PortFragmentId, Timestamp> ports = new HashMap<>(devices.size() * portsPerDevice); | 917 | + final int portsPerDevice = 8; // random factor to minimize reallocation |
899 | - Map<DeviceId, Timestamp> offline = new HashMap<>(devices.size()); | 918 | + Map<PortFragmentId, Timestamp> adPorts = new HashMap<>(numDevices * portsPerDevice); |
919 | + Map<DeviceId, Timestamp> adOffline = new HashMap<>(numDevices); | ||
900 | 920 | ||
901 | - for (Entry<DeviceId, ConcurrentMap<ProviderId, DeviceDescriptions>> | 921 | + for (Entry<DeviceId, Map<ProviderId, DeviceDescriptions>> |
902 | provs : deviceDescs.entrySet()) { | 922 | provs : deviceDescs.entrySet()) { |
903 | 923 | ||
924 | + // for each Device... | ||
904 | final DeviceId deviceId = provs.getKey(); | 925 | final DeviceId deviceId = provs.getKey(); |
905 | - final ConcurrentMap<ProviderId, DeviceDescriptions> devDescs = provs.getValue(); | 926 | + final Map<ProviderId, DeviceDescriptions> devDescs = provs.getValue(); |
906 | synchronized (devDescs) { | 927 | synchronized (devDescs) { |
907 | 928 | ||
908 | - offline.put(deviceId, this.offline.get(deviceId)); | 929 | + // send device offline timestamp |
930 | + Timestamp lOffline = this.offline.get(deviceId); | ||
931 | + if (lOffline != null) { | ||
932 | + adOffline.put(deviceId, lOffline); | ||
933 | + } | ||
909 | 934 | ||
910 | for (Entry<ProviderId, DeviceDescriptions> | 935 | for (Entry<ProviderId, DeviceDescriptions> |
911 | prov : devDescs.entrySet()) { | 936 | prov : devDescs.entrySet()) { |
912 | 937 | ||
938 | + // for each Provider Descriptions... | ||
913 | final ProviderId provId = prov.getKey(); | 939 | final ProviderId provId = prov.getKey(); |
914 | final DeviceDescriptions descs = prov.getValue(); | 940 | final DeviceDescriptions descs = prov.getValue(); |
915 | 941 | ||
916 | - devices.put(new DeviceFragmentId(deviceId, provId), | 942 | + adDevices.put(new DeviceFragmentId(deviceId, provId), |
917 | descs.getDeviceDesc().timestamp()); | 943 | descs.getDeviceDesc().timestamp()); |
918 | 944 | ||
919 | for (Entry<PortNumber, Timestamped<PortDescription>> | 945 | for (Entry<PortNumber, Timestamped<PortDescription>> |
920 | portDesc : descs.getPortDescs().entrySet()) { | 946 | portDesc : descs.getPortDescs().entrySet()) { |
921 | 947 | ||
922 | final PortNumber number = portDesc.getKey(); | 948 | final PortNumber number = portDesc.getKey(); |
923 | - ports.put(new PortFragmentId(deviceId, provId, number), | 949 | + adPorts.put(new PortFragmentId(deviceId, provId, number), |
924 | portDesc.getValue().timestamp()); | 950 | portDesc.getValue().timestamp()); |
925 | } | 951 | } |
926 | } | 952 | } |
927 | } | 953 | } |
928 | } | 954 | } |
929 | 955 | ||
930 | - return new DeviceAntiEntropyAdvertisement(self, devices, ports, offline); | 956 | + return new DeviceAntiEntropyAdvertisement(self, adDevices, adPorts, adOffline); |
931 | } | 957 | } |
932 | 958 | ||
933 | /** | 959 | /** |
... | @@ -950,7 +976,7 @@ public class GossipDeviceStore | ... | @@ -950,7 +976,7 @@ public class GossipDeviceStore |
950 | Collection<DeviceFragmentId> reqDevices = new ArrayList<>(); | 976 | Collection<DeviceFragmentId> reqDevices = new ArrayList<>(); |
951 | Collection<PortFragmentId> reqPorts = new ArrayList<>(); | 977 | Collection<PortFragmentId> reqPorts = new ArrayList<>(); |
952 | 978 | ||
953 | - for (Entry<DeviceId, ConcurrentMap<ProviderId, DeviceDescriptions>> de : deviceDescs.entrySet()) { | 979 | + for (Entry<DeviceId, Map<ProviderId, DeviceDescriptions>> de : deviceDescs.entrySet()) { |
954 | final DeviceId deviceId = de.getKey(); | 980 | final DeviceId deviceId = de.getKey(); |
955 | final Map<ProviderId, DeviceDescriptions> lDevice = de.getValue(); | 981 | final Map<ProviderId, DeviceDescriptions> lDevice = de.getValue(); |
956 | 982 | ||
... | @@ -1199,7 +1225,7 @@ public class GossipDeviceStore | ... | @@ -1199,7 +1225,7 @@ public class GossipDeviceStore |
1199 | 1225 | ||
1200 | @Override | 1226 | @Override |
1201 | public void handle(ClusterMessage message) { | 1227 | public void handle(ClusterMessage message) { |
1202 | - log.info("Received Device advertisement from peer: {}", message.sender()); | 1228 | + log.debug("Received Device Anti-Entropy advertisement from peer: {}", message.sender()); |
1203 | DeviceAntiEntropyAdvertisement advertisement = SERIALIZER.decode(message.payload()); | 1229 | DeviceAntiEntropyAdvertisement advertisement = SERIALIZER.decode(message.payload()); |
1204 | handleAdvertisement(advertisement); | 1230 | handleAdvertisement(advertisement); |
1205 | } | 1231 | } | ... | ... |
... | @@ -4,10 +4,14 @@ import com.google.common.collect.HashMultimap; | ... | @@ -4,10 +4,14 @@ import com.google.common.collect.HashMultimap; |
4 | import com.google.common.collect.ImmutableSet; | 4 | import com.google.common.collect.ImmutableSet; |
5 | import com.google.common.collect.Multimap; | 5 | import com.google.common.collect.Multimap; |
6 | import com.google.common.collect.Sets; | 6 | import com.google.common.collect.Sets; |
7 | + | ||
7 | import org.apache.felix.scr.annotations.Activate; | 8 | import org.apache.felix.scr.annotations.Activate; |
8 | import org.apache.felix.scr.annotations.Component; | 9 | import org.apache.felix.scr.annotations.Component; |
9 | import org.apache.felix.scr.annotations.Deactivate; | 10 | import org.apache.felix.scr.annotations.Deactivate; |
11 | +import org.apache.felix.scr.annotations.Reference; | ||
12 | +import org.apache.felix.scr.annotations.ReferenceCardinality; | ||
10 | import org.apache.felix.scr.annotations.Service; | 13 | import org.apache.felix.scr.annotations.Service; |
14 | +import org.onlab.onos.cluster.ClusterService; | ||
11 | import org.onlab.onos.net.Annotations; | 15 | import org.onlab.onos.net.Annotations; |
12 | import org.onlab.onos.net.ConnectPoint; | 16 | import org.onlab.onos.net.ConnectPoint; |
13 | import org.onlab.onos.net.DefaultHost; | 17 | import org.onlab.onos.net.DefaultHost; |
... | @@ -15,6 +19,7 @@ import org.onlab.onos.net.DeviceId; | ... | @@ -15,6 +19,7 @@ import org.onlab.onos.net.DeviceId; |
15 | import org.onlab.onos.net.Host; | 19 | import org.onlab.onos.net.Host; |
16 | import org.onlab.onos.net.HostId; | 20 | import org.onlab.onos.net.HostId; |
17 | import org.onlab.onos.net.HostLocation; | 21 | import org.onlab.onos.net.HostLocation; |
22 | +import org.onlab.onos.net.host.HostClockService; | ||
18 | import org.onlab.onos.net.host.HostDescription; | 23 | import org.onlab.onos.net.host.HostDescription; |
19 | import org.onlab.onos.net.host.HostEvent; | 24 | import org.onlab.onos.net.host.HostEvent; |
20 | import org.onlab.onos.net.host.HostStore; | 25 | import org.onlab.onos.net.host.HostStore; |
... | @@ -22,11 +27,21 @@ import org.onlab.onos.net.host.HostStoreDelegate; | ... | @@ -22,11 +27,21 @@ import org.onlab.onos.net.host.HostStoreDelegate; |
22 | import org.onlab.onos.net.host.PortAddresses; | 27 | import org.onlab.onos.net.host.PortAddresses; |
23 | import org.onlab.onos.net.provider.ProviderId; | 28 | import org.onlab.onos.net.provider.ProviderId; |
24 | import org.onlab.onos.store.AbstractStore; | 29 | import org.onlab.onos.store.AbstractStore; |
30 | +import org.onlab.onos.store.Timestamp; | ||
31 | +import org.onlab.onos.store.cluster.messaging.ClusterCommunicationService; | ||
32 | +import org.onlab.onos.store.cluster.messaging.ClusterMessage; | ||
33 | +import org.onlab.onos.store.cluster.messaging.ClusterMessageHandler; | ||
34 | +import org.onlab.onos.store.cluster.messaging.MessageSubject; | ||
35 | +import org.onlab.onos.store.common.impl.Timestamped; | ||
36 | +import org.onlab.onos.store.serializers.DistributedStoreSerializers; | ||
37 | +import org.onlab.onos.store.serializers.KryoSerializer; | ||
25 | import org.onlab.packet.IpPrefix; | 38 | import org.onlab.packet.IpPrefix; |
26 | import org.onlab.packet.MacAddress; | 39 | import org.onlab.packet.MacAddress; |
27 | import org.onlab.packet.VlanId; | 40 | import org.onlab.packet.VlanId; |
41 | +import org.onlab.util.KryoPool; | ||
28 | import org.slf4j.Logger; | 42 | import org.slf4j.Logger; |
29 | 43 | ||
44 | +import java.io.IOException; | ||
30 | import java.util.HashSet; | 45 | import java.util.HashSet; |
31 | import java.util.Map; | 46 | import java.util.Map; |
32 | import java.util.Set; | 47 | import java.util.Set; |
... | @@ -35,14 +50,14 @@ import java.util.concurrent.ConcurrentHashMap; | ... | @@ -35,14 +50,14 @@ import java.util.concurrent.ConcurrentHashMap; |
35 | import static org.onlab.onos.net.host.HostEvent.Type.*; | 50 | import static org.onlab.onos.net.host.HostEvent.Type.*; |
36 | import static org.slf4j.LoggerFactory.getLogger; | 51 | import static org.slf4j.LoggerFactory.getLogger; |
37 | 52 | ||
53 | +//TODO: multi-provider, annotation not supported. | ||
38 | /** | 54 | /** |
39 | - * Manages inventory of end-station hosts using trivial in-memory | 55 | + * Manages inventory of end-station hosts in distributed data store |
40 | - * implementation. | 56 | + * that uses optimistic replication and gossip based techniques. |
41 | */ | 57 | */ |
42 | -//FIXME: I LIE I AM NOT DISTRIBUTED | ||
43 | @Component(immediate = true) | 58 | @Component(immediate = true) |
44 | @Service | 59 | @Service |
45 | -public class DistributedHostStore | 60 | +public class GossipHostStore |
46 | extends AbstractStore<HostEvent, HostStoreDelegate> | 61 | extends AbstractStore<HostEvent, HostStoreDelegate> |
47 | implements HostStore { | 62 | implements HostStore { |
48 | 63 | ||
... | @@ -51,14 +66,41 @@ public class DistributedHostStore | ... | @@ -51,14 +66,41 @@ public class DistributedHostStore |
51 | // Host inventory | 66 | // Host inventory |
52 | private final Map<HostId, StoredHost> hosts = new ConcurrentHashMap<>(2000000, 0.75f, 16); | 67 | private final Map<HostId, StoredHost> hosts = new ConcurrentHashMap<>(2000000, 0.75f, 16); |
53 | 68 | ||
69 | + private final Map<HostId, Timestamped<Host>> removedHosts = new ConcurrentHashMap<>(2000000, 0.75f, 16); | ||
70 | + | ||
54 | // Hosts tracked by their location | 71 | // Hosts tracked by their location |
55 | private final Multimap<ConnectPoint, Host> locations = HashMultimap.create(); | 72 | private final Multimap<ConnectPoint, Host> locations = HashMultimap.create(); |
56 | 73 | ||
57 | private final Map<ConnectPoint, PortAddresses> portAddresses = | 74 | private final Map<ConnectPoint, PortAddresses> portAddresses = |
58 | new ConcurrentHashMap<>(); | 75 | new ConcurrentHashMap<>(); |
59 | 76 | ||
77 | + @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY) | ||
78 | + protected HostClockService hostClockService; | ||
79 | + | ||
80 | + @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY) | ||
81 | + protected ClusterCommunicationService clusterCommunicator; | ||
82 | + | ||
83 | + @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY) | ||
84 | + protected ClusterService clusterService; | ||
85 | + | ||
86 | + private static final KryoSerializer SERIALIZER = new KryoSerializer() { | ||
87 | + @Override | ||
88 | + protected void setupKryoPool() { | ||
89 | + serializerPool = KryoPool.newBuilder() | ||
90 | + .register(DistributedStoreSerializers.COMMON) | ||
91 | + .register(InternalHostRemovedEvent.class) | ||
92 | + .build() | ||
93 | + .populate(1); | ||
94 | + } | ||
95 | + }; | ||
96 | + | ||
60 | @Activate | 97 | @Activate |
61 | public void activate() { | 98 | public void activate() { |
99 | + clusterCommunicator.addSubscriber( | ||
100 | + GossipHostStoreMessageSubjects.HOST_UPDATED, new InternalHostEventListener()); | ||
101 | + clusterCommunicator.addSubscriber( | ||
102 | + GossipHostStoreMessageSubjects.HOST_REMOVED, new InternalHostRemovedEventListener()); | ||
103 | + | ||
62 | log.info("Started"); | 104 | log.info("Started"); |
63 | } | 105 | } |
64 | 106 | ||
... | @@ -70,34 +112,60 @@ public class DistributedHostStore | ... | @@ -70,34 +112,60 @@ public class DistributedHostStore |
70 | @Override | 112 | @Override |
71 | public HostEvent createOrUpdateHost(ProviderId providerId, HostId hostId, | 113 | public HostEvent createOrUpdateHost(ProviderId providerId, HostId hostId, |
72 | HostDescription hostDescription) { | 114 | HostDescription hostDescription) { |
115 | + Timestamp timestamp = hostClockService.getTimestamp(hostId); | ||
116 | + HostEvent event = createOrUpdateHostInternal(providerId, hostId, hostDescription, timestamp); | ||
117 | + if (event != null) { | ||
118 | + log.info("Notifying peers of a host topology event for providerId: " | ||
119 | + + "{}; hostId: {}; hostDescription: {}", providerId, hostId, hostDescription); | ||
120 | + try { | ||
121 | + notifyPeers(new InternalHostEvent(providerId, hostId, hostDescription, timestamp)); | ||
122 | + } catch (IOException e) { | ||
123 | + log.error("Failed to notify peers of a host topology event for providerId: " | ||
124 | + + "{}; hostId: {}; hostDescription: {}", providerId, hostId, hostDescription); | ||
125 | + } | ||
126 | + } | ||
127 | + return event; | ||
128 | + } | ||
129 | + | ||
130 | + private HostEvent createOrUpdateHostInternal(ProviderId providerId, HostId hostId, | ||
131 | + HostDescription hostDescription, Timestamp timestamp) { | ||
73 | StoredHost host = hosts.get(hostId); | 132 | StoredHost host = hosts.get(hostId); |
74 | if (host == null) { | 133 | if (host == null) { |
75 | - return createHost(providerId, hostId, hostDescription); | 134 | + return createHost(providerId, hostId, hostDescription, timestamp); |
76 | } | 135 | } |
77 | - return updateHost(providerId, host, hostDescription); | 136 | + return updateHost(providerId, host, hostDescription, timestamp); |
78 | } | 137 | } |
79 | 138 | ||
80 | // creates a new host and sends HOST_ADDED | 139 | // creates a new host and sends HOST_ADDED |
81 | private HostEvent createHost(ProviderId providerId, HostId hostId, | 140 | private HostEvent createHost(ProviderId providerId, HostId hostId, |
82 | - HostDescription descr) { | 141 | + HostDescription descr, Timestamp timestamp) { |
83 | - StoredHost newhost = new StoredHost(providerId, hostId, | ||
84 | - descr.hwAddress(), | ||
85 | - descr.vlan(), | ||
86 | - descr.location(), | ||
87 | - ImmutableSet.of(descr.ipAddress())); | ||
88 | synchronized (this) { | 142 | synchronized (this) { |
143 | + // If this host was previously removed, first ensure | ||
144 | + // this new request is "newer" | ||
145 | + if (removedHosts.containsKey(hostId)) { | ||
146 | + if (removedHosts.get(hostId).isNewer(timestamp)) { | ||
147 | + return null; | ||
148 | + } else { | ||
149 | + removedHosts.remove(hostId); | ||
150 | + } | ||
151 | + } | ||
152 | + StoredHost newhost = new StoredHost(providerId, hostId, | ||
153 | + descr.hwAddress(), | ||
154 | + descr.vlan(), | ||
155 | + new Timestamped<>(descr.location(), timestamp), | ||
156 | + ImmutableSet.of(descr.ipAddress())); | ||
89 | hosts.put(hostId, newhost); | 157 | hosts.put(hostId, newhost); |
90 | locations.put(descr.location(), newhost); | 158 | locations.put(descr.location(), newhost); |
159 | + return new HostEvent(HOST_ADDED, newhost); | ||
91 | } | 160 | } |
92 | - return new HostEvent(HOST_ADDED, newhost); | ||
93 | } | 161 | } |
94 | 162 | ||
95 | // checks for type of update to host, sends appropriate event | 163 | // checks for type of update to host, sends appropriate event |
96 | private HostEvent updateHost(ProviderId providerId, StoredHost host, | 164 | private HostEvent updateHost(ProviderId providerId, StoredHost host, |
97 | - HostDescription descr) { | 165 | + HostDescription descr, Timestamp timestamp) { |
98 | HostEvent event; | 166 | HostEvent event; |
99 | - if (!host.location().equals(descr.location())) { | 167 | + if (!host.location.isNewer(timestamp) && !host.location().equals(descr.location())) { |
100 | - host.setLocation(descr.location()); | 168 | + host.setLocation(new Timestamped<>(descr.location(), timestamp)); |
101 | return new HostEvent(HOST_MOVED, host); | 169 | return new HostEvent(HOST_MOVED, host); |
102 | } | 170 | } |
103 | 171 | ||
... | @@ -109,7 +177,7 @@ public class DistributedHostStore | ... | @@ -109,7 +177,7 @@ public class DistributedHostStore |
109 | addresses.add(descr.ipAddress()); | 177 | addresses.add(descr.ipAddress()); |
110 | StoredHost updated = new StoredHost(providerId, host.id(), | 178 | StoredHost updated = new StoredHost(providerId, host.id(), |
111 | host.mac(), host.vlan(), | 179 | host.mac(), host.vlan(), |
112 | - descr.location(), addresses); | 180 | + host.location, addresses); |
113 | event = new HostEvent(HOST_UPDATED, updated); | 181 | event = new HostEvent(HOST_UPDATED, updated); |
114 | synchronized (this) { | 182 | synchronized (this) { |
115 | hosts.put(host.id(), updated); | 183 | hosts.put(host.id(), updated); |
... | @@ -121,10 +189,25 @@ public class DistributedHostStore | ... | @@ -121,10 +189,25 @@ public class DistributedHostStore |
121 | 189 | ||
122 | @Override | 190 | @Override |
123 | public HostEvent removeHost(HostId hostId) { | 191 | public HostEvent removeHost(HostId hostId) { |
192 | + Timestamp timestamp = hostClockService.getTimestamp(hostId); | ||
193 | + HostEvent event = removeHostInternal(hostId, timestamp); | ||
194 | + if (event != null) { | ||
195 | + log.info("Notifying peers of a host removed topology event for hostId: {}", hostId); | ||
196 | + try { | ||
197 | + notifyPeers(new InternalHostRemovedEvent(hostId, timestamp)); | ||
198 | + } catch (IOException e) { | ||
199 | + log.info("Failed to notify peers of a host removed topology event for hostId: {}", hostId); | ||
200 | + } | ||
201 | + } | ||
202 | + return event; | ||
203 | + } | ||
204 | + | ||
205 | + private HostEvent removeHostInternal(HostId hostId, Timestamp timestamp) { | ||
124 | synchronized (this) { | 206 | synchronized (this) { |
125 | Host host = hosts.remove(hostId); | 207 | Host host = hosts.remove(hostId); |
126 | if (host != null) { | 208 | if (host != null) { |
127 | locations.remove((host.location()), host); | 209 | locations.remove((host.location()), host); |
210 | + removedHosts.put(hostId, new Timestamped<>(host, timestamp)); | ||
128 | return new HostEvent(HOST_REMOVED, host); | 211 | return new HostEvent(HOST_REMOVED, host); |
129 | } | 212 | } |
130 | return null; | 213 | return null; |
... | @@ -270,7 +353,7 @@ public class DistributedHostStore | ... | @@ -270,7 +353,7 @@ public class DistributedHostStore |
270 | 353 | ||
271 | // Auxiliary extension to allow location to mutate. | 354 | // Auxiliary extension to allow location to mutate. |
272 | private class StoredHost extends DefaultHost { | 355 | private class StoredHost extends DefaultHost { |
273 | - private HostLocation location; | 356 | + private Timestamped<HostLocation> location; |
274 | 357 | ||
275 | /** | 358 | /** |
276 | * Creates an end-station host using the supplied information. | 359 | * Creates an end-station host using the supplied information. |
... | @@ -284,19 +367,71 @@ public class DistributedHostStore | ... | @@ -284,19 +367,71 @@ public class DistributedHostStore |
284 | * @param annotations optional key/value annotations | 367 | * @param annotations optional key/value annotations |
285 | */ | 368 | */ |
286 | public StoredHost(ProviderId providerId, HostId id, | 369 | public StoredHost(ProviderId providerId, HostId id, |
287 | - MacAddress mac, VlanId vlan, HostLocation location, | 370 | + MacAddress mac, VlanId vlan, Timestamped<HostLocation> location, |
288 | Set<IpPrefix> ips, Annotations... annotations) { | 371 | Set<IpPrefix> ips, Annotations... annotations) { |
289 | - super(providerId, id, mac, vlan, location, ips, annotations); | 372 | + super(providerId, id, mac, vlan, location.value(), ips, annotations); |
290 | this.location = location; | 373 | this.location = location; |
291 | } | 374 | } |
292 | 375 | ||
293 | - void setLocation(HostLocation location) { | 376 | + void setLocation(Timestamped<HostLocation> location) { |
294 | this.location = location; | 377 | this.location = location; |
295 | } | 378 | } |
296 | 379 | ||
297 | @Override | 380 | @Override |
298 | public HostLocation location() { | 381 | public HostLocation location() { |
299 | - return location; | 382 | + return location.value(); |
383 | + } | ||
384 | + } | ||
385 | + | ||
386 | + private void notifyPeers(InternalHostRemovedEvent event) throws IOException { | ||
387 | + broadcastMessage(GossipHostStoreMessageSubjects.HOST_REMOVED, event); | ||
388 | + } | ||
389 | + | ||
390 | + private void notifyPeers(InternalHostEvent event) throws IOException { | ||
391 | + broadcastMessage(GossipHostStoreMessageSubjects.HOST_UPDATED, event); | ||
392 | + } | ||
393 | + | ||
394 | + private void broadcastMessage(MessageSubject subject, Object event) throws IOException { | ||
395 | + ClusterMessage message = new ClusterMessage( | ||
396 | + clusterService.getLocalNode().id(), | ||
397 | + subject, | ||
398 | + SERIALIZER.encode(event)); | ||
399 | + clusterCommunicator.broadcast(message); | ||
400 | + } | ||
401 | + | ||
402 | + private void notifyDelegateIfNotNull(HostEvent event) { | ||
403 | + if (event != null) { | ||
404 | + notifyDelegate(event); | ||
405 | + } | ||
406 | + } | ||
407 | + | ||
408 | + private class InternalHostEventListener implements ClusterMessageHandler { | ||
409 | + @Override | ||
410 | + public void handle(ClusterMessage message) { | ||
411 | + | ||
412 | + log.info("Received host update event from peer: {}", message.sender()); | ||
413 | + InternalHostEvent event = (InternalHostEvent) SERIALIZER.decode(message.payload()); | ||
414 | + | ||
415 | + ProviderId providerId = event.providerId(); | ||
416 | + HostId hostId = event.hostId(); | ||
417 | + HostDescription hostDescription = event.hostDescription(); | ||
418 | + Timestamp timestamp = event.timestamp(); | ||
419 | + | ||
420 | + notifyDelegateIfNotNull(createOrUpdateHostInternal(providerId, hostId, hostDescription, timestamp)); | ||
421 | + } | ||
422 | + } | ||
423 | + | ||
424 | + private class InternalHostRemovedEventListener implements ClusterMessageHandler { | ||
425 | + @Override | ||
426 | + public void handle(ClusterMessage message) { | ||
427 | + | ||
428 | + log.info("Received host removed event from peer: {}", message.sender()); | ||
429 | + InternalHostRemovedEvent event = (InternalHostRemovedEvent) SERIALIZER.decode(message.payload()); | ||
430 | + | ||
431 | + HostId hostId = event.hostId(); | ||
432 | + Timestamp timestamp = event.timestamp(); | ||
433 | + | ||
434 | + notifyDelegateIfNotNull(removeHostInternal(hostId, timestamp)); | ||
300 | } | 435 | } |
301 | } | 436 | } |
302 | } | 437 | } | ... | ... |
core/store/dist/src/main/java/org/onlab/onos/store/host/impl/GossipHostStoreMessageSubjects.java
0 → 100644
1 | +package org.onlab.onos.store.host.impl; | ||
2 | + | ||
3 | +import org.onlab.onos.store.cluster.messaging.MessageSubject; | ||
4 | + | ||
5 | +public final class GossipHostStoreMessageSubjects { | ||
6 | + private GossipHostStoreMessageSubjects() {} | ||
7 | + public static final MessageSubject HOST_UPDATED = new MessageSubject("peer-host-updated"); | ||
8 | + public static final MessageSubject HOST_REMOVED = new MessageSubject("peer-host-removed"); | ||
9 | +} |
1 | +package org.onlab.onos.store.host.impl; | ||
2 | + | ||
3 | +import org.onlab.onos.net.HostId; | ||
4 | +import org.onlab.onos.net.host.HostDescription; | ||
5 | +import org.onlab.onos.net.provider.ProviderId; | ||
6 | +import org.onlab.onos.store.Timestamp; | ||
7 | + | ||
8 | +/** | ||
9 | + * Information published by GossipHostStore to notify peers of a host | ||
10 | + * change (create/update) event. | ||
11 | + */ | ||
12 | +public class InternalHostEvent { | ||
13 | + | ||
14 | + private final ProviderId providerId; | ||
15 | + private final HostId hostId; | ||
16 | + private final HostDescription hostDescription; | ||
17 | + private final Timestamp timestamp; | ||
18 | + | ||
19 | + public InternalHostEvent(ProviderId providerId, HostId hostId, | ||
20 | + HostDescription hostDescription, Timestamp timestamp) { | ||
21 | + this.providerId = providerId; | ||
22 | + this.hostId = hostId; | ||
23 | + this.hostDescription = hostDescription; | ||
24 | + this.timestamp = timestamp; | ||
25 | + } | ||
26 | + | ||
27 | + public ProviderId providerId() { | ||
28 | + return providerId; | ||
29 | + } | ||
30 | + | ||
31 | + public HostId hostId() { | ||
32 | + return hostId; | ||
33 | + } | ||
34 | + | ||
35 | + public HostDescription hostDescription() { | ||
36 | + return hostDescription; | ||
37 | + } | ||
38 | + | ||
39 | + public Timestamp timestamp() { | ||
40 | + return timestamp; | ||
41 | + } | ||
42 | + | ||
43 | + // Needed for serialization. | ||
44 | + @SuppressWarnings("unused") | ||
45 | + private InternalHostEvent() { | ||
46 | + providerId = null; | ||
47 | + hostId = null; | ||
48 | + hostDescription = null; | ||
49 | + timestamp = null; | ||
50 | + } | ||
51 | +} |
core/store/dist/src/main/java/org/onlab/onos/store/host/impl/InternalHostRemovedEvent.java
0 → 100644
1 | +package org.onlab.onos.store.host.impl; | ||
2 | + | ||
3 | +import org.onlab.onos.net.HostId; | ||
4 | +import org.onlab.onos.store.Timestamp; | ||
5 | + | ||
6 | +/** | ||
7 | + * Information published by GossipHostStore to notify peers of a host | ||
8 | + * removed event. | ||
9 | + */ | ||
10 | +public class InternalHostRemovedEvent { | ||
11 | + | ||
12 | + private final HostId hostId; | ||
13 | + private final Timestamp timestamp; | ||
14 | + | ||
15 | + public InternalHostRemovedEvent(HostId hostId, Timestamp timestamp) { | ||
16 | + this.hostId = hostId; | ||
17 | + this.timestamp = timestamp; | ||
18 | + } | ||
19 | + | ||
20 | + public HostId hostId() { | ||
21 | + return hostId; | ||
22 | + } | ||
23 | + | ||
24 | + public Timestamp timestamp() { | ||
25 | + return timestamp; | ||
26 | + } | ||
27 | + | ||
28 | + // for serialization. | ||
29 | + @SuppressWarnings("unused") | ||
30 | + private InternalHostRemovedEvent() { | ||
31 | + hostId = null; | ||
32 | + timestamp = null; | ||
33 | + } | ||
34 | +} |
... | @@ -9,7 +9,6 @@ import com.google.common.collect.Maps; | ... | @@ -9,7 +9,6 @@ import com.google.common.collect.Maps; |
9 | import com.google.common.collect.SetMultimap; | 9 | import com.google.common.collect.SetMultimap; |
10 | 10 | ||
11 | import org.apache.commons.lang3.RandomUtils; | 11 | import org.apache.commons.lang3.RandomUtils; |
12 | -import org.apache.commons.lang3.concurrent.ConcurrentUtils; | ||
13 | import org.apache.felix.scr.annotations.Activate; | 12 | import org.apache.felix.scr.annotations.Activate; |
14 | import org.apache.felix.scr.annotations.Component; | 13 | import org.apache.felix.scr.annotations.Component; |
15 | import org.apache.felix.scr.annotations.Deactivate; | 14 | import org.apache.felix.scr.annotations.Deactivate; |
... | @@ -46,7 +45,6 @@ import org.onlab.onos.store.common.impl.Timestamped; | ... | @@ -46,7 +45,6 @@ import org.onlab.onos.store.common.impl.Timestamped; |
46 | import org.onlab.onos.store.serializers.DistributedStoreSerializers; | 45 | import org.onlab.onos.store.serializers.DistributedStoreSerializers; |
47 | import org.onlab.onos.store.serializers.KryoSerializer; | 46 | import org.onlab.onos.store.serializers.KryoSerializer; |
48 | import org.onlab.util.KryoPool; | 47 | import org.onlab.util.KryoPool; |
49 | -import org.onlab.util.NewConcurrentHashMap; | ||
50 | import org.slf4j.Logger; | 48 | import org.slf4j.Logger; |
51 | 49 | ||
52 | import java.io.IOException; | 50 | import java.io.IOException; |
... | @@ -87,7 +85,7 @@ public class GossipLinkStore | ... | @@ -87,7 +85,7 @@ public class GossipLinkStore |
87 | private final Logger log = getLogger(getClass()); | 85 | private final Logger log = getLogger(getClass()); |
88 | 86 | ||
89 | // Link inventory | 87 | // Link inventory |
90 | - private final ConcurrentMap<LinkKey, ConcurrentMap<ProviderId, Timestamped<LinkDescription>>> linkDescs = | 88 | + private final ConcurrentMap<LinkKey, Map<ProviderId, Timestamped<LinkDescription>>> linkDescs = |
91 | new ConcurrentHashMap<>(); | 89 | new ConcurrentHashMap<>(); |
92 | 90 | ||
93 | // Link instance cache | 91 | // Link instance cache |
... | @@ -238,7 +236,7 @@ public class GossipLinkStore | ... | @@ -238,7 +236,7 @@ public class GossipLinkStore |
238 | 236 | ||
239 | final Timestamped<LinkDescription> deltaDesc = new Timestamped<>(linkDescription, newTimestamp); | 237 | final Timestamped<LinkDescription> deltaDesc = new Timestamped<>(linkDescription, newTimestamp); |
240 | 238 | ||
241 | - LinkKey key = linkKey(linkDescription); | 239 | + LinkKey key = linkKey(linkDescription.src(), linkDescription.dst()); |
242 | final LinkEvent event; | 240 | final LinkEvent event; |
243 | final Timestamped<LinkDescription> mergedDesc; | 241 | final Timestamped<LinkDescription> mergedDesc; |
244 | synchronized (getLinkDescriptions(key)) { | 242 | synchronized (getLinkDescriptions(key)) { |
... | @@ -265,8 +263,9 @@ public class GossipLinkStore | ... | @@ -265,8 +263,9 @@ public class GossipLinkStore |
265 | ProviderId providerId, | 263 | ProviderId providerId, |
266 | Timestamped<LinkDescription> linkDescription) { | 264 | Timestamped<LinkDescription> linkDescription) { |
267 | 265 | ||
268 | - LinkKey key = linkKey(linkDescription.value()); | 266 | + LinkKey key = linkKey(linkDescription.value().src(), |
269 | - ConcurrentMap<ProviderId, Timestamped<LinkDescription>> descs = getLinkDescriptions(key); | 267 | + linkDescription.value().dst()); |
268 | + Map<ProviderId, Timestamped<LinkDescription>> descs = getLinkDescriptions(key); | ||
270 | 269 | ||
271 | synchronized (descs) { | 270 | synchronized (descs) { |
272 | // if the link was previously removed, we should proceed if and | 271 | // if the link was previously removed, we should proceed if and |
... | @@ -293,12 +292,12 @@ public class GossipLinkStore | ... | @@ -293,12 +292,12 @@ public class GossipLinkStore |
293 | 292 | ||
294 | // Guarded by linkDescs value (=locking each Link) | 293 | // Guarded by linkDescs value (=locking each Link) |
295 | private Timestamped<LinkDescription> createOrUpdateLinkDescription( | 294 | private Timestamped<LinkDescription> createOrUpdateLinkDescription( |
296 | - ConcurrentMap<ProviderId, Timestamped<LinkDescription>> existingLinkDescriptions, | 295 | + Map<ProviderId, Timestamped<LinkDescription>> descs, |
297 | ProviderId providerId, | 296 | ProviderId providerId, |
298 | Timestamped<LinkDescription> linkDescription) { | 297 | Timestamped<LinkDescription> linkDescription) { |
299 | 298 | ||
300 | // merge existing attributes and merge | 299 | // merge existing attributes and merge |
301 | - Timestamped<LinkDescription> existingLinkDescription = existingLinkDescriptions.get(providerId); | 300 | + Timestamped<LinkDescription> existingLinkDescription = descs.get(providerId); |
302 | if (existingLinkDescription != null && existingLinkDescription.isNewer(linkDescription)) { | 301 | if (existingLinkDescription != null && existingLinkDescription.isNewer(linkDescription)) { |
303 | return null; | 302 | return null; |
304 | } | 303 | } |
... | @@ -313,7 +312,7 @@ public class GossipLinkStore | ... | @@ -313,7 +312,7 @@ public class GossipLinkStore |
313 | linkDescription.value().type(), merged), | 312 | linkDescription.value().type(), merged), |
314 | linkDescription.timestamp()); | 313 | linkDescription.timestamp()); |
315 | } | 314 | } |
316 | - return existingLinkDescriptions.put(providerId, newLinkDescription); | 315 | + return descs.put(providerId, newLinkDescription); |
317 | } | 316 | } |
318 | 317 | ||
319 | // Creates and stores the link and returns the appropriate event. | 318 | // Creates and stores the link and returns the appropriate event. |
... | @@ -379,7 +378,7 @@ public class GossipLinkStore | ... | @@ -379,7 +378,7 @@ public class GossipLinkStore |
379 | } | 378 | } |
380 | 379 | ||
381 | private LinkEvent removeLinkInternal(LinkKey key, Timestamp timestamp) { | 380 | private LinkEvent removeLinkInternal(LinkKey key, Timestamp timestamp) { |
382 | - ConcurrentMap<ProviderId, Timestamped<LinkDescription>> linkDescriptions = | 381 | + Map<ProviderId, Timestamped<LinkDescription>> linkDescriptions = |
383 | getLinkDescriptions(key); | 382 | getLinkDescriptions(key); |
384 | synchronized (linkDescriptions) { | 383 | synchronized (linkDescriptions) { |
385 | // accept removal request if given timestamp is newer than | 384 | // accept removal request if given timestamp is newer than |
... | @@ -408,10 +407,10 @@ public class GossipLinkStore | ... | @@ -408,10 +407,10 @@ public class GossipLinkStore |
408 | * @return primary ProviderID, or randomly chosen one if none exists | 407 | * @return primary ProviderID, or randomly chosen one if none exists |
409 | */ | 408 | */ |
410 | private ProviderId pickPrimaryProviderId( | 409 | private ProviderId pickPrimaryProviderId( |
411 | - ConcurrentMap<ProviderId, Timestamped<LinkDescription>> providerDescs) { | 410 | + Map<ProviderId, Timestamped<LinkDescription>> linkDescriptions) { |
412 | 411 | ||
413 | ProviderId fallBackPrimary = null; | 412 | ProviderId fallBackPrimary = null; |
414 | - for (Entry<ProviderId, Timestamped<LinkDescription>> e : providerDescs.entrySet()) { | 413 | + for (Entry<ProviderId, Timestamped<LinkDescription>> e : linkDescriptions.entrySet()) { |
415 | if (!e.getKey().isAncillary()) { | 414 | if (!e.getKey().isAncillary()) { |
416 | return e.getKey(); | 415 | return e.getKey(); |
417 | } else if (fallBackPrimary == null) { | 416 | } else if (fallBackPrimary == null) { |
... | @@ -422,9 +421,9 @@ public class GossipLinkStore | ... | @@ -422,9 +421,9 @@ public class GossipLinkStore |
422 | return fallBackPrimary; | 421 | return fallBackPrimary; |
423 | } | 422 | } |
424 | 423 | ||
425 | - private Link composeLink(ConcurrentMap<ProviderId, Timestamped<LinkDescription>> linkDescriptions) { | 424 | + private Link composeLink(Map<ProviderId, Timestamped<LinkDescription>> descs) { |
426 | - ProviderId primaryProviderId = pickPrimaryProviderId(linkDescriptions); | 425 | + ProviderId primaryProviderId = pickPrimaryProviderId(descs); |
427 | - Timestamped<LinkDescription> base = linkDescriptions.get(primaryProviderId); | 426 | + Timestamped<LinkDescription> base = descs.get(primaryProviderId); |
428 | 427 | ||
429 | ConnectPoint src = base.value().src(); | 428 | ConnectPoint src = base.value().src(); |
430 | ConnectPoint dst = base.value().dst(); | 429 | ConnectPoint dst = base.value().dst(); |
... | @@ -432,7 +431,7 @@ public class GossipLinkStore | ... | @@ -432,7 +431,7 @@ public class GossipLinkStore |
432 | DefaultAnnotations annotations = DefaultAnnotations.builder().build(); | 431 | DefaultAnnotations annotations = DefaultAnnotations.builder().build(); |
433 | annotations = merge(annotations, base.value().annotations()); | 432 | annotations = merge(annotations, base.value().annotations()); |
434 | 433 | ||
435 | - for (Entry<ProviderId, Timestamped<LinkDescription>> e : linkDescriptions.entrySet()) { | 434 | + for (Entry<ProviderId, Timestamped<LinkDescription>> e : descs.entrySet()) { |
436 | if (primaryProviderId.equals(e.getKey())) { | 435 | if (primaryProviderId.equals(e.getKey())) { |
437 | continue; | 436 | continue; |
438 | } | 437 | } |
... | @@ -449,9 +448,20 @@ public class GossipLinkStore | ... | @@ -449,9 +448,20 @@ public class GossipLinkStore |
449 | return new DefaultLink(primaryProviderId , src, dst, type, annotations); | 448 | return new DefaultLink(primaryProviderId , src, dst, type, annotations); |
450 | } | 449 | } |
451 | 450 | ||
452 | - private ConcurrentMap<ProviderId, Timestamped<LinkDescription>> getLinkDescriptions(LinkKey key) { | 451 | + private Map<ProviderId, Timestamped<LinkDescription>> getLinkDescriptions(LinkKey key) { |
453 | - return ConcurrentUtils.createIfAbsentUnchecked(linkDescs, key, | 452 | + Map<ProviderId, Timestamped<LinkDescription>> r; |
454 | - NewConcurrentHashMap.<ProviderId, Timestamped<LinkDescription>>ifNeeded()); | 453 | + r = linkDescs.get(key); |
454 | + if (r != null) { | ||
455 | + return r; | ||
456 | + } | ||
457 | + r = new HashMap<>(); | ||
458 | + final Map<ProviderId, Timestamped<LinkDescription>> concurrentlyAdded; | ||
459 | + concurrentlyAdded = linkDescs.putIfAbsent(key, r); | ||
460 | + if (concurrentlyAdded != null) { | ||
461 | + return concurrentlyAdded; | ||
462 | + } else { | ||
463 | + return r; | ||
464 | + } | ||
455 | } | 465 | } |
456 | 466 | ||
457 | private Timestamped<LinkDescription> getLinkDescription(LinkKey key, ProviderId providerId) { | 467 | private Timestamped<LinkDescription> getLinkDescription(LinkKey key, ProviderId providerId) { |
... | @@ -470,13 +480,13 @@ public class GossipLinkStore | ... | @@ -470,13 +480,13 @@ public class GossipLinkStore |
470 | } | 480 | } |
471 | } | 481 | } |
472 | 482 | ||
473 | - private static final Predicate<Provided> IS_PRIMARY = new IsPrimary(); | ||
474 | - private static final Predicate<Provided> isPrimary() { | ||
475 | - return IS_PRIMARY; | ||
476 | - } | ||
477 | - | ||
478 | private static final class IsPrimary implements Predicate<Provided> { | 483 | private static final class IsPrimary implements Predicate<Provided> { |
479 | 484 | ||
485 | + private static final Predicate<Provided> IS_PRIMARY = new IsPrimary(); | ||
486 | + public static final Predicate<Provided> isPrimary() { | ||
487 | + return IS_PRIMARY; | ||
488 | + } | ||
489 | + | ||
480 | @Override | 490 | @Override |
481 | public boolean apply(Provided input) { | 491 | public boolean apply(Provided input) { |
482 | return !input.providerId().isAncillary(); | 492 | return !input.providerId().isAncillary(); |
... | @@ -581,11 +591,11 @@ public class GossipLinkStore | ... | @@ -581,11 +591,11 @@ public class GossipLinkStore |
581 | Map<LinkFragmentId, Timestamp> linkTimestamps = new HashMap<>(linkDescs.size()); | 591 | Map<LinkFragmentId, Timestamp> linkTimestamps = new HashMap<>(linkDescs.size()); |
582 | Map<LinkKey, Timestamp> linkTombstones = new HashMap<>(removedLinks.size()); | 592 | Map<LinkKey, Timestamp> linkTombstones = new HashMap<>(removedLinks.size()); |
583 | 593 | ||
584 | - for (Entry<LinkKey, ConcurrentMap<ProviderId, Timestamped<LinkDescription>>> | 594 | + for (Entry<LinkKey, Map<ProviderId, Timestamped<LinkDescription>>> |
585 | provs : linkDescs.entrySet()) { | 595 | provs : linkDescs.entrySet()) { |
586 | 596 | ||
587 | final LinkKey linkKey = provs.getKey(); | 597 | final LinkKey linkKey = provs.getKey(); |
588 | - final ConcurrentMap<ProviderId, Timestamped<LinkDescription>> linkDesc = provs.getValue(); | 598 | + final Map<ProviderId, Timestamped<LinkDescription>> linkDesc = provs.getValue(); |
589 | synchronized (linkDesc) { | 599 | synchronized (linkDesc) { |
590 | for (Map.Entry<ProviderId, Timestamped<LinkDescription>> e : linkDesc.entrySet()) { | 600 | for (Map.Entry<ProviderId, Timestamped<LinkDescription>> e : linkDesc.entrySet()) { |
591 | linkTimestamps.put(new LinkFragmentId(linkKey, e.getKey()), e.getValue().timestamp()); | 601 | linkTimestamps.put(new LinkFragmentId(linkKey, e.getKey()), e.getValue().timestamp()); |
... | @@ -670,7 +680,7 @@ public class GossipLinkStore | ... | @@ -670,7 +680,7 @@ public class GossipLinkStore |
670 | 680 | ||
671 | @Override | 681 | @Override |
672 | public void handle(ClusterMessage message) { | 682 | public void handle(ClusterMessage message) { |
673 | - log.info("Received Link Anti-Entropy advertisement from peer: {}", message.sender()); | 683 | + log.debug("Received Link Anti-Entropy advertisement from peer: {}", message.sender()); |
674 | LinkAntiEntropyAdvertisement advertisement = SERIALIZER.decode(message.payload()); | 684 | LinkAntiEntropyAdvertisement advertisement = SERIALIZER.decode(message.payload()); |
675 | handleAntiEntropyAdvertisement(advertisement); | 685 | handleAntiEntropyAdvertisement(advertisement); |
676 | } | 686 | } | ... | ... |
... | @@ -40,22 +40,18 @@ public class ClusterCommunicationManagerTest { | ... | @@ -40,22 +40,18 @@ public class ClusterCommunicationManagerTest { |
40 | 40 | ||
41 | @Before | 41 | @Before |
42 | public void setUp() throws Exception { | 42 | public void setUp() throws Exception { |
43 | - MessageSerializer messageSerializer = new MessageSerializer(); | ||
44 | - messageSerializer.activate(); | ||
45 | 43 | ||
46 | NettyMessagingService messagingService = new NettyMessagingService(); | 44 | NettyMessagingService messagingService = new NettyMessagingService(); |
47 | messagingService.activate(); | 45 | messagingService.activate(); |
48 | 46 | ||
49 | ccm1 = new ClusterCommunicationManager(); | 47 | ccm1 = new ClusterCommunicationManager(); |
50 | -// ccm1.serializationService = messageSerializer; | ||
51 | ccm1.activate(); | 48 | ccm1.activate(); |
52 | 49 | ||
53 | ccm2 = new ClusterCommunicationManager(); | 50 | ccm2 = new ClusterCommunicationManager(); |
54 | -// ccm2.serializationService = messageSerializer; | ||
55 | ccm2.activate(); | 51 | ccm2.activate(); |
56 | 52 | ||
57 | - ccm1.initialize(node1, cnd1); | 53 | +// ccm1.initialize(node1, cnd1); |
58 | - ccm2.initialize(node2, cnd2); | 54 | +// ccm2.initialize(node2, cnd2); |
59 | } | 55 | } |
60 | 56 | ||
61 | @After | 57 | @After |
... | @@ -70,7 +66,7 @@ public class ClusterCommunicationManagerTest { | ... | @@ -70,7 +66,7 @@ public class ClusterCommunicationManagerTest { |
70 | cnd1.latch = new CountDownLatch(1); | 66 | cnd1.latch = new CountDownLatch(1); |
71 | cnd2.latch = new CountDownLatch(1); | 67 | cnd2.latch = new CountDownLatch(1); |
72 | 68 | ||
73 | - ccm1.addNode(node2); | 69 | +// ccm1.addNode(node2); |
74 | validateDelegateEvent(cnd1, Op.DETECTED, node2.id()); | 70 | validateDelegateEvent(cnd1, Op.DETECTED, node2.id()); |
75 | validateDelegateEvent(cnd2, Op.DETECTED, node1.id()); | 71 | validateDelegateEvent(cnd2, Op.DETECTED, node1.id()); |
76 | } | 72 | } |
... | @@ -81,7 +77,7 @@ public class ClusterCommunicationManagerTest { | ... | @@ -81,7 +77,7 @@ public class ClusterCommunicationManagerTest { |
81 | cnd1.latch = new CountDownLatch(1); | 77 | cnd1.latch = new CountDownLatch(1); |
82 | cnd2.latch = new CountDownLatch(1); | 78 | cnd2.latch = new CountDownLatch(1); |
83 | 79 | ||
84 | - ccm1.addNode(node2); | 80 | +// ccm1.addNode(node2); |
85 | validateDelegateEvent(cnd1, Op.DETECTED, node2.id()); | 81 | validateDelegateEvent(cnd1, Op.DETECTED, node2.id()); |
86 | validateDelegateEvent(cnd2, Op.DETECTED, node1.id()); | 82 | validateDelegateEvent(cnd2, Op.DETECTED, node1.id()); |
87 | 83 | ... | ... |
... | @@ -151,7 +151,7 @@ public class DistributedLinkStore | ... | @@ -151,7 +151,7 @@ public class DistributedLinkStore |
151 | @Override | 151 | @Override |
152 | public LinkEvent createOrUpdateLink(ProviderId providerId, | 152 | public LinkEvent createOrUpdateLink(ProviderId providerId, |
153 | LinkDescription linkDescription) { | 153 | LinkDescription linkDescription) { |
154 | - LinkKey key = linkKey(linkDescription); | 154 | + LinkKey key = linkKey(linkDescription.src(), linkDescription.dst()); |
155 | Optional<DefaultLink> link = links.getUnchecked(key); | 155 | Optional<DefaultLink> link = links.getUnchecked(key); |
156 | if (!link.isPresent()) { | 156 | if (!link.isPresent()) { |
157 | return createLink(providerId, key, linkDescription); | 157 | return createLink(providerId, key, linkDescription); | ... | ... |
... | @@ -35,6 +35,7 @@ import java.util.concurrent.ConcurrentHashMap; | ... | @@ -35,6 +35,7 @@ import java.util.concurrent.ConcurrentHashMap; |
35 | import static org.onlab.onos.net.host.HostEvent.Type.*; | 35 | import static org.onlab.onos.net.host.HostEvent.Type.*; |
36 | import static org.slf4j.LoggerFactory.getLogger; | 36 | import static org.slf4j.LoggerFactory.getLogger; |
37 | 37 | ||
38 | +// TODO: multi-provider, annotation not supported. | ||
38 | /** | 39 | /** |
39 | * Manages inventory of end-station hosts using trivial in-memory | 40 | * Manages inventory of end-station hosts using trivial in-memory |
40 | * implementation. | 41 | * implementation. | ... | ... |
... | @@ -149,7 +149,7 @@ public class SimpleLinkStore | ... | @@ -149,7 +149,7 @@ public class SimpleLinkStore |
149 | @Override | 149 | @Override |
150 | public LinkEvent createOrUpdateLink(ProviderId providerId, | 150 | public LinkEvent createOrUpdateLink(ProviderId providerId, |
151 | LinkDescription linkDescription) { | 151 | LinkDescription linkDescription) { |
152 | - LinkKey key = linkKey(linkDescription); | 152 | + LinkKey key = linkKey(linkDescription.src(), linkDescription.dst()); |
153 | 153 | ||
154 | ConcurrentMap<ProviderId, LinkDescription> descs = getLinkDescriptions(key); | 154 | ConcurrentMap<ProviderId, LinkDescription> descs = getLinkDescriptions(key); |
155 | synchronized (descs) { | 155 | synchronized (descs) { | ... | ... |
... | @@ -67,16 +67,6 @@ | ... | @@ -67,16 +67,6 @@ |
67 | <bundle>mvn:org.onlab.onos/onos-core-hz-cluster/1.0.0-SNAPSHOT</bundle> | 67 | <bundle>mvn:org.onlab.onos/onos-core-hz-cluster/1.0.0-SNAPSHOT</bundle> |
68 | </feature> | 68 | </feature> |
69 | 69 | ||
70 | - <feature name="onos-core-hazelcast" version="1.0.0" | ||
71 | - description="ONOS core components built on hazelcast"> | ||
72 | - <feature>onos-api</feature> | ||
73 | - <bundle>mvn:org.onlab.onos/onos-core-net/1.0.0-SNAPSHOT</bundle> | ||
74 | - <bundle>mvn:org.onlab.onos/onos-core-hz-common/1.0.0-SNAPSHOT</bundle> | ||
75 | - <bundle>mvn:org.onlab.onos/onos-core-serializers/1.0.0-SNAPSHOT</bundle> | ||
76 | - <bundle>mvn:org.onlab.onos/onos-core-hz-cluster/1.0.0-SNAPSHOT</bundle> | ||
77 | - <bundle>mvn:org.onlab.onos/onos-core-hz-net/1.0.0-SNAPSHOT</bundle> | ||
78 | - </feature> | ||
79 | - | ||
80 | <feature name="onos-core-trivial" version="1.0.0" | 70 | <feature name="onos-core-trivial" version="1.0.0" |
81 | description="ONOS core components"> | 71 | description="ONOS core components"> |
82 | <feature>onos-api</feature> | 72 | <feature>onos-api</feature> |
... | @@ -163,4 +153,10 @@ | ... | @@ -163,4 +153,10 @@ |
163 | <bundle>mvn:org.onlab.onos/onos-app-config/1.0.0-SNAPSHOT</bundle> | 153 | <bundle>mvn:org.onlab.onos/onos-app-config/1.0.0-SNAPSHOT</bundle> |
164 | </feature> | 154 | </feature> |
165 | 155 | ||
156 | + <feature name="onos-app-sdnip" version="1.0.0" | ||
157 | + description="SDN-IP peering application"> | ||
158 | + <feature>onos-api</feature> | ||
159 | + <bundle>mvn:org.onlab.onos/onos-app-sdnip/1.0.0-SNAPSHOT</bundle> | ||
160 | + </feature> | ||
161 | + | ||
166 | </features> | 162 | </features> | ... | ... |
... | @@ -5,6 +5,7 @@ import org.onlab.onos.openflow.controller.driver.SwitchDriverSubHandshakeComplet | ... | @@ -5,6 +5,7 @@ import org.onlab.onos.openflow.controller.driver.SwitchDriverSubHandshakeComplet |
5 | import org.onlab.onos.openflow.controller.driver.SwitchDriverSubHandshakeNotStarted; | 5 | import org.onlab.onos.openflow.controller.driver.SwitchDriverSubHandshakeNotStarted; |
6 | import org.onlab.onos.openflow.controller.Dpid; | 6 | import org.onlab.onos.openflow.controller.Dpid; |
7 | import org.onlab.onos.openflow.controller.driver.AbstractOpenFlowSwitch; | 7 | import org.onlab.onos.openflow.controller.driver.AbstractOpenFlowSwitch; |
8 | +import org.projectfloodlight.openflow.protocol.OFBarrierRequest; | ||
8 | import org.projectfloodlight.openflow.protocol.OFCircuitPortsReply; | 9 | import org.projectfloodlight.openflow.protocol.OFCircuitPortsReply; |
9 | import org.projectfloodlight.openflow.protocol.OFCircuitPortsRequest; | 10 | import org.projectfloodlight.openflow.protocol.OFCircuitPortsRequest; |
10 | import org.projectfloodlight.openflow.protocol.OFDescStatsReply; | 11 | import org.projectfloodlight.openflow.protocol.OFDescStatsReply; |
... | @@ -21,7 +22,6 @@ import org.projectfloodlight.openflow.protocol.oxm.OFOxmInPort; | ... | @@ -21,7 +22,6 @@ import org.projectfloodlight.openflow.protocol.oxm.OFOxmInPort; |
21 | import org.projectfloodlight.openflow.protocol.oxm.OFOxmOchSigid; | 22 | import org.projectfloodlight.openflow.protocol.oxm.OFOxmOchSigid; |
22 | import org.projectfloodlight.openflow.protocol.oxm.OFOxmOchSigidBasic; | 23 | import org.projectfloodlight.openflow.protocol.oxm.OFOxmOchSigidBasic; |
23 | import org.projectfloodlight.openflow.protocol.oxm.OFOxmOchSigtype; | 24 | import org.projectfloodlight.openflow.protocol.oxm.OFOxmOchSigtype; |
24 | -import org.projectfloodlight.openflow.protocol.oxm.OFOxmOchSigtypeBasic; | ||
25 | import org.projectfloodlight.openflow.types.CircuitSignalID; | 25 | import org.projectfloodlight.openflow.types.CircuitSignalID; |
26 | import org.projectfloodlight.openflow.types.OFPort; | 26 | import org.projectfloodlight.openflow.types.OFPort; |
27 | import org.projectfloodlight.openflow.types.U8; | 27 | import org.projectfloodlight.openflow.types.U8; |
... | @@ -119,11 +119,12 @@ public class OFOpticalSwitchImplLINC13 extends AbstractOpenFlowSwitch { | ... | @@ -119,11 +119,12 @@ public class OFOpticalSwitchImplLINC13 extends AbstractOpenFlowSwitch { |
119 | processHandshakeOFExperimenterPortDescRequest( | 119 | processHandshakeOFExperimenterPortDescRequest( |
120 | (OFCircuitPortsReply) m); | 120 | (OFCircuitPortsReply) m); |
121 | driverHandshakeComplete.set(true); | 121 | driverHandshakeComplete.set(true); |
122 | - /* try { | 122 | + try { |
123 | testMA(); | 123 | testMA(); |
124 | + testReverseMA(); | ||
124 | } catch (IOException e) { | 125 | } catch (IOException e) { |
125 | e.printStackTrace(); | 126 | e.printStackTrace(); |
126 | - }*/ | 127 | + } |
127 | break; | 128 | break; |
128 | default: | 129 | default: |
129 | log.debug("Received message {} during switch-driver " + | 130 | log.debug("Received message {} during switch-driver " + |
... | @@ -163,22 +164,71 @@ public class OFOpticalSwitchImplLINC13 extends AbstractOpenFlowSwitch { | ... | @@ -163,22 +164,71 @@ public class OFOpticalSwitchImplLINC13 extends AbstractOpenFlowSwitch { |
163 | "message " + | 164 | "message " + |
164 | "{}", | 165 | "{}", |
165 | circuitPortsRequest.toString()); | 166 | circuitPortsRequest.toString()); |
166 | - channel.write(Collections.singletonList(circuitPortsRequest)); | 167 | + sendMsg(Collections.<OFMessage>singletonList(circuitPortsRequest)); |
167 | } | 168 | } |
168 | 169 | ||
169 | 170 | ||
170 | - | 171 | + public static final U8 SIGNAL_TYPE = U8.of((short) 10); |
171 | - //todo for testing | ||
172 | - public static final U8 SIGNAL_TYPE = U8.of((short) 1); | ||
173 | private void testMA() throws IOException { | 172 | private void testMA() throws IOException { |
174 | log.debug("LINC OE *** Testing MA "); | 173 | log.debug("LINC OE *** Testing MA "); |
175 | - short lambda = 100; | 174 | + short lambda = 1; |
176 | - if (getId() == 0x0000ffffffffff02L) { | 175 | + if (getId() == 0x0000ffffffffff01L) { |
177 | final int inport = 10; | 176 | final int inport = 10; |
178 | final int outport = 20; | 177 | final int outport = 20; |
179 | //Circuit signal id | 178 | //Circuit signal id |
180 | CircuitSignalID sigID = getSignalID(lambda); | 179 | CircuitSignalID sigID = getSignalID(lambda); |
181 | 180 | ||
181 | + OFOxmOchSigidBasic ofOxmOchSigidBasic = | ||
182 | + factory().oxms().ochSigidBasic(sigID); | ||
183 | + | ||
184 | + | ||
185 | + //Match Port | ||
186 | + OFOxmInPort fieldPort = factory().oxms() | ||
187 | + .inPort(OFPort.of(inport)); | ||
188 | + OFMatchV3 matchPort = | ||
189 | + factory() | ||
190 | + .buildMatchV3(). | ||
191 | + setOxmList(OFOxmList.of(fieldPort)).build(); | ||
192 | + | ||
193 | + | ||
194 | + // Set Action outport ,sigType and sigID | ||
195 | + List<OFAction> actionList = new ArrayList<>(); | ||
196 | + OFAction actionOutPort = | ||
197 | + factory().actions().output(OFPort.of(outport), | ||
198 | + 0xffff); | ||
199 | + | ||
200 | + OFActionCircuit actionCircuit = factory() | ||
201 | + .actions() | ||
202 | + .circuit(ofOxmOchSigidBasic); | ||
203 | + | ||
204 | + actionList.add(actionCircuit); | ||
205 | + actionList.add(actionOutPort); | ||
206 | + | ||
207 | + OFInstruction instructionAction = | ||
208 | + factory().instructions().buildApplyActions() | ||
209 | + .setActions(actionList) | ||
210 | + .build(); | ||
211 | + List<OFInstruction> instructions = | ||
212 | + Collections.singletonList(instructionAction); | ||
213 | + | ||
214 | + OFMessage opticalFlowEntry = | ||
215 | + factory().buildFlowAdd() | ||
216 | + .setMatch(matchPort) | ||
217 | + .setPriority(100) | ||
218 | + .setInstructions(instructions) | ||
219 | + .setXid(getNextTransactionId()) | ||
220 | + .build(); | ||
221 | + log.debug("Adding optical flow in sw {}", getStringId()); | ||
222 | + List<OFMessage> msglist = new ArrayList<>(1); | ||
223 | + msglist.add(opticalFlowEntry); | ||
224 | + write(msglist); | ||
225 | + sendBarrier(true); | ||
226 | + } else if (getId() == 0x0000ffffffffff03L) { | ||
227 | + final int inport = 30; | ||
228 | + final int outport = 31; | ||
229 | + //Circuit signal id | ||
230 | + CircuitSignalID sigID = getSignalID(lambda); | ||
231 | + | ||
182 | OFOxmOchSigid fieldSigIDMatch = factory().oxms().ochSigid(sigID); | 232 | OFOxmOchSigid fieldSigIDMatch = factory().oxms().ochSigid(sigID); |
183 | OFOxmOchSigtype fieldSigType = factory() | 233 | OFOxmOchSigtype fieldSigType = factory() |
184 | .oxms() | 234 | .oxms() |
... | @@ -187,8 +237,119 @@ public class OFOpticalSwitchImplLINC13 extends AbstractOpenFlowSwitch { | ... | @@ -187,8 +237,119 @@ public class OFOpticalSwitchImplLINC13 extends AbstractOpenFlowSwitch { |
187 | OFOxmOchSigidBasic ofOxmOchSigidBasic = | 237 | OFOxmOchSigidBasic ofOxmOchSigidBasic = |
188 | factory().oxms().ochSigidBasic(sigID); | 238 | factory().oxms().ochSigidBasic(sigID); |
189 | 239 | ||
190 | - OFOxmOchSigtypeBasic ofOxmOchSigtypeBasic = | 240 | + //Match Port,SigType,SigID |
191 | - factory().oxms().ochSigtypeBasic(SIGNAL_TYPE); | 241 | + OFOxmInPort fieldPort = factory() |
242 | + .oxms() | ||
243 | + .inPort(OFPort.of(inport)); | ||
244 | + OFMatchV3 matchPort = factory() | ||
245 | + .buildMatchV3() | ||
246 | + .setOxmList(OFOxmList.of(fieldPort, | ||
247 | + fieldSigIDMatch, | ||
248 | + fieldSigType | ||
249 | + )) | ||
250 | + .build(); | ||
251 | + | ||
252 | + // Set Action outport ,SigType, sigID | ||
253 | + List<OFAction> actionList = new ArrayList<>(); | ||
254 | + OFAction actionOutPort = | ||
255 | + factory().actions().output(OFPort.of(outport), | ||
256 | + 0xffff); | ||
257 | + | ||
258 | + OFActionCircuit actionCircuit = factory() | ||
259 | + .actions() | ||
260 | + .circuit(ofOxmOchSigidBasic); | ||
261 | + | ||
262 | + | ||
263 | + | ||
264 | + //actionList.add(setActionSigType); | ||
265 | + actionList.add(actionCircuit); | ||
266 | + actionList.add(actionOutPort); | ||
267 | + | ||
268 | + OFInstruction instructionAction = | ||
269 | + factory().instructions().buildApplyActions() | ||
270 | + .setActions(actionList) | ||
271 | + .build(); | ||
272 | + List<OFInstruction> instructions = | ||
273 | + Collections.singletonList(instructionAction); | ||
274 | + | ||
275 | + OFMessage opticalFlowEntry = | ||
276 | + factory().buildFlowAdd() | ||
277 | + .setMatch(matchPort) | ||
278 | + .setPriority(100) | ||
279 | + .setInstructions(instructions) | ||
280 | + .setXid(getNextTransactionId()) | ||
281 | + .build(); | ||
282 | + log.debug("Adding optical flow in sw {}", getStringId()); | ||
283 | + List<OFMessage> msglist = new ArrayList<>(1); | ||
284 | + msglist.add(opticalFlowEntry); | ||
285 | + write(msglist); | ||
286 | + sendBarrier(true); | ||
287 | + | ||
288 | + } else if (getId() == 0x0000ffffffffff02L) { | ||
289 | + final int inport = 21; | ||
290 | + final int outport = 11; | ||
291 | + //Circuit signal id | ||
292 | + CircuitSignalID sigID = getSignalID(lambda); | ||
293 | + | ||
294 | + OFOxmOchSigid fieldSigIDMatch = factory().oxms().ochSigid(sigID); | ||
295 | + OFOxmOchSigtype fieldSigType = factory() | ||
296 | + .oxms() | ||
297 | + .ochSigtype(SIGNAL_TYPE); | ||
298 | + | ||
299 | + | ||
300 | + //Match Port, sig type and sig id | ||
301 | + OFOxmInPort fieldPort = factory() | ||
302 | + .oxms() | ||
303 | + .inPort(OFPort.of(inport)); | ||
304 | + OFMatchV3 matchPort = | ||
305 | + factory().buildMatchV3() | ||
306 | + .setOxmList(OFOxmList.of(fieldPort, | ||
307 | + fieldSigIDMatch, | ||
308 | + fieldSigType | ||
309 | + )) | ||
310 | + .build(); | ||
311 | + | ||
312 | + // Set Action outport | ||
313 | + List<OFAction> actionList = new ArrayList<>(); | ||
314 | + OFAction actionOutPort = | ||
315 | + factory().actions().output(OFPort.of(outport), | ||
316 | + 0xffff); | ||
317 | + | ||
318 | + actionList.add(actionOutPort); | ||
319 | + | ||
320 | + OFInstruction instructionAction = | ||
321 | + factory().instructions().buildApplyActions() | ||
322 | + .setActions(actionList) | ||
323 | + .build(); | ||
324 | + List<OFInstruction> instructions = | ||
325 | + Collections.singletonList(instructionAction); | ||
326 | + | ||
327 | + OFMessage opticalFlowEntry = | ||
328 | + factory().buildFlowAdd() | ||
329 | + .setMatch(matchPort) | ||
330 | + .setPriority(100) | ||
331 | + .setInstructions(instructions) | ||
332 | + .setXid(getNextTransactionId()) | ||
333 | + .build(); | ||
334 | + log.debug("Adding optical flow in sw {}", getStringId()); | ||
335 | + List<OFMessage> msglist = new ArrayList<>(1); | ||
336 | + msglist.add(opticalFlowEntry); | ||
337 | + write(msglist); | ||
338 | + sendBarrier(true); | ||
339 | + } | ||
340 | + | ||
341 | + } | ||
342 | + private void testReverseMA() throws IOException { | ||
343 | + log.debug("LINC OE *** Testing MA "); | ||
344 | + short lambda = 1; | ||
345 | + if (getId() == 0x0000ffffffffff02L) { | ||
346 | + final int inport = 11; | ||
347 | + final int outport = 21; | ||
348 | + //Circuit signal id | ||
349 | + CircuitSignalID sigID = getSignalID(lambda); | ||
350 | + | ||
351 | + OFOxmOchSigidBasic ofOxmOchSigidBasic = | ||
352 | + factory().oxms().ochSigidBasic(sigID); | ||
192 | 353 | ||
193 | //Match Port | 354 | //Match Port |
194 | OFOxmInPort fieldPort = factory().oxms() | 355 | OFOxmInPort fieldPort = factory().oxms() |
... | @@ -196,27 +357,20 @@ public class OFOpticalSwitchImplLINC13 extends AbstractOpenFlowSwitch { | ... | @@ -196,27 +357,20 @@ public class OFOpticalSwitchImplLINC13 extends AbstractOpenFlowSwitch { |
196 | OFMatchV3 matchPort = | 357 | OFMatchV3 matchPort = |
197 | factory() | 358 | factory() |
198 | .buildMatchV3(). | 359 | .buildMatchV3(). |
199 | - setOxmList(OFOxmList.of(fieldPort, | 360 | + setOxmList(OFOxmList.of(fieldPort)).build(); |
200 | - fieldSigType, | ||
201 | - fieldSigIDMatch)).build(); | ||
202 | 361 | ||
203 | 362 | ||
204 | // Set Action outport ,sigType and sigID | 363 | // Set Action outport ,sigType and sigID |
205 | List<OFAction> actionList = new ArrayList<>(); | 364 | List<OFAction> actionList = new ArrayList<>(); |
206 | OFAction actionOutPort = | 365 | OFAction actionOutPort = |
207 | factory().actions().output(OFPort.of(outport), | 366 | factory().actions().output(OFPort.of(outport), |
208 | - Short.MAX_VALUE); | 367 | + 0xffff); |
209 | 368 | ||
210 | OFActionCircuit actionCircuit = factory() | 369 | OFActionCircuit actionCircuit = factory() |
211 | .actions() | 370 | .actions() |
212 | .circuit(ofOxmOchSigidBasic); | 371 | .circuit(ofOxmOchSigidBasic); |
213 | - OFActionCircuit setActionSigType = factory() | ||
214 | - .actions() | ||
215 | - .circuit(ofOxmOchSigtypeBasic); | ||
216 | - | ||
217 | - actionList.add(actionOutPort); | ||
218 | - actionList.add(setActionSigType); | ||
219 | actionList.add(actionCircuit); | 372 | actionList.add(actionCircuit); |
373 | + actionList.add(actionOutPort); | ||
220 | 374 | ||
221 | OFInstruction instructionAction = | 375 | OFInstruction instructionAction = |
222 | factory().instructions().buildApplyActions() | 376 | factory().instructions().buildApplyActions() |
... | @@ -228,6 +382,7 @@ public class OFOpticalSwitchImplLINC13 extends AbstractOpenFlowSwitch { | ... | @@ -228,6 +382,7 @@ public class OFOpticalSwitchImplLINC13 extends AbstractOpenFlowSwitch { |
228 | OFMessage opticalFlowEntry = | 382 | OFMessage opticalFlowEntry = |
229 | factory().buildFlowAdd() | 383 | factory().buildFlowAdd() |
230 | .setMatch(matchPort) | 384 | .setMatch(matchPort) |
385 | + .setPriority(100) | ||
231 | .setInstructions(instructions) | 386 | .setInstructions(instructions) |
232 | .setXid(getNextTransactionId()) | 387 | .setXid(getNextTransactionId()) |
233 | .build(); | 388 | .build(); |
... | @@ -235,9 +390,10 @@ public class OFOpticalSwitchImplLINC13 extends AbstractOpenFlowSwitch { | ... | @@ -235,9 +390,10 @@ public class OFOpticalSwitchImplLINC13 extends AbstractOpenFlowSwitch { |
235 | List<OFMessage> msglist = new ArrayList<>(1); | 390 | List<OFMessage> msglist = new ArrayList<>(1); |
236 | msglist.add(opticalFlowEntry); | 391 | msglist.add(opticalFlowEntry); |
237 | write(msglist); | 392 | write(msglist); |
393 | + sendBarrier(true); | ||
238 | } else if (getId() == 0x0000ffffffffff03L) { | 394 | } else if (getId() == 0x0000ffffffffff03L) { |
239 | - final int inport = 21; | 395 | + final int inport = 31; |
240 | - final int outport = 22; | 396 | + final int outport = 30; |
241 | //Circuit signal id | 397 | //Circuit signal id |
242 | CircuitSignalID sigID = getSignalID(lambda); | 398 | CircuitSignalID sigID = getSignalID(lambda); |
243 | 399 | ||
... | @@ -249,9 +405,6 @@ public class OFOpticalSwitchImplLINC13 extends AbstractOpenFlowSwitch { | ... | @@ -249,9 +405,6 @@ public class OFOpticalSwitchImplLINC13 extends AbstractOpenFlowSwitch { |
249 | OFOxmOchSigidBasic ofOxmOchSigidBasic = | 405 | OFOxmOchSigidBasic ofOxmOchSigidBasic = |
250 | factory().oxms().ochSigidBasic(sigID); | 406 | factory().oxms().ochSigidBasic(sigID); |
251 | 407 | ||
252 | - OFOxmOchSigtypeBasic ofOxmOchSigtypeBasic = | ||
253 | - factory().oxms().ochSigtypeBasic(SIGNAL_TYPE); | ||
254 | - | ||
255 | //Match Port,SigType,SigID | 408 | //Match Port,SigType,SigID |
256 | OFOxmInPort fieldPort = factory() | 409 | OFOxmInPort fieldPort = factory() |
257 | .oxms() | 410 | .oxms() |
... | @@ -259,27 +412,22 @@ public class OFOpticalSwitchImplLINC13 extends AbstractOpenFlowSwitch { | ... | @@ -259,27 +412,22 @@ public class OFOpticalSwitchImplLINC13 extends AbstractOpenFlowSwitch { |
259 | OFMatchV3 matchPort = factory() | 412 | OFMatchV3 matchPort = factory() |
260 | .buildMatchV3() | 413 | .buildMatchV3() |
261 | .setOxmList(OFOxmList.of(fieldPort, | 414 | .setOxmList(OFOxmList.of(fieldPort, |
262 | - fieldSigType, | 415 | + fieldSigIDMatch, |
263 | - fieldSigIDMatch)) | 416 | + fieldSigType |
417 | + )) | ||
264 | .build(); | 418 | .build(); |
265 | 419 | ||
266 | // Set Action outport ,SigType, sigID | 420 | // Set Action outport ,SigType, sigID |
267 | List<OFAction> actionList = new ArrayList<>(); | 421 | List<OFAction> actionList = new ArrayList<>(); |
268 | OFAction actionOutPort = | 422 | OFAction actionOutPort = |
269 | factory().actions().output(OFPort.of(outport), | 423 | factory().actions().output(OFPort.of(outport), |
270 | - Short.MAX_VALUE); | 424 | + 0xffff); |
271 | - | ||
272 | - OFActionCircuit setActionSigType = factory() | ||
273 | - .actions() | ||
274 | - .circuit(ofOxmOchSigtypeBasic); | ||
275 | OFActionCircuit actionCircuit = factory() | 425 | OFActionCircuit actionCircuit = factory() |
276 | .actions() | 426 | .actions() |
277 | .circuit(ofOxmOchSigidBasic); | 427 | .circuit(ofOxmOchSigidBasic); |
278 | 428 | ||
279 | - | ||
280 | - actionList.add(actionOutPort); | ||
281 | - actionList.add(setActionSigType); | ||
282 | actionList.add(actionCircuit); | 429 | actionList.add(actionCircuit); |
430 | + actionList.add(actionOutPort); | ||
283 | 431 | ||
284 | OFInstruction instructionAction = | 432 | OFInstruction instructionAction = |
285 | factory().instructions().buildApplyActions() | 433 | factory().instructions().buildApplyActions() |
... | @@ -290,18 +438,20 @@ public class OFOpticalSwitchImplLINC13 extends AbstractOpenFlowSwitch { | ... | @@ -290,18 +438,20 @@ public class OFOpticalSwitchImplLINC13 extends AbstractOpenFlowSwitch { |
290 | 438 | ||
291 | OFMessage opticalFlowEntry = | 439 | OFMessage opticalFlowEntry = |
292 | factory().buildFlowAdd() | 440 | factory().buildFlowAdd() |
293 | - .setMatch(matchPort) | 441 | + .setMatch(matchPort) |
294 | - .setInstructions(instructions) | 442 | + .setPriority(100) |
295 | - .setXid(getNextTransactionId()) | 443 | + .setInstructions(instructions) |
296 | - .build(); | 444 | + .setXid(getNextTransactionId()) |
445 | + .build(); | ||
297 | log.debug("Adding optical flow in sw {}", getStringId()); | 446 | log.debug("Adding optical flow in sw {}", getStringId()); |
298 | List<OFMessage> msglist = new ArrayList<>(1); | 447 | List<OFMessage> msglist = new ArrayList<>(1); |
299 | msglist.add(opticalFlowEntry); | 448 | msglist.add(opticalFlowEntry); |
300 | write(msglist); | 449 | write(msglist); |
450 | + sendBarrier(true); | ||
301 | 451 | ||
302 | - } else if (getId() == 0x0000ffffffffff04L) { | 452 | + } else if (getId() == 0x0000ffffffffff01L) { |
303 | - final int inport = 23; | 453 | + final int inport = 20; |
304 | - final int outport = 11; | 454 | + final int outport = 10; |
305 | //Circuit signal id | 455 | //Circuit signal id |
306 | CircuitSignalID sigID = getSignalID(lambda); | 456 | CircuitSignalID sigID = getSignalID(lambda); |
307 | 457 | ||
... | @@ -318,15 +468,16 @@ public class OFOpticalSwitchImplLINC13 extends AbstractOpenFlowSwitch { | ... | @@ -318,15 +468,16 @@ public class OFOpticalSwitchImplLINC13 extends AbstractOpenFlowSwitch { |
318 | OFMatchV3 matchPort = | 468 | OFMatchV3 matchPort = |
319 | factory().buildMatchV3() | 469 | factory().buildMatchV3() |
320 | .setOxmList(OFOxmList.of(fieldPort, | 470 | .setOxmList(OFOxmList.of(fieldPort, |
321 | - fieldSigType, | 471 | + fieldSigIDMatch, |
322 | - fieldSigIDMatch)) | 472 | + fieldSigType |
473 | + )) | ||
323 | .build(); | 474 | .build(); |
324 | 475 | ||
325 | // Set Action outport | 476 | // Set Action outport |
326 | List<OFAction> actionList = new ArrayList<>(); | 477 | List<OFAction> actionList = new ArrayList<>(); |
327 | OFAction actionOutPort = | 478 | OFAction actionOutPort = |
328 | factory().actions().output(OFPort.of(outport), | 479 | factory().actions().output(OFPort.of(outport), |
329 | - Short.MAX_VALUE); | 480 | + 0xffff); |
330 | 481 | ||
331 | actionList.add(actionOutPort); | 482 | actionList.add(actionOutPort); |
332 | 483 | ||
... | @@ -339,18 +490,21 @@ public class OFOpticalSwitchImplLINC13 extends AbstractOpenFlowSwitch { | ... | @@ -339,18 +490,21 @@ public class OFOpticalSwitchImplLINC13 extends AbstractOpenFlowSwitch { |
339 | 490 | ||
340 | OFMessage opticalFlowEntry = | 491 | OFMessage opticalFlowEntry = |
341 | factory().buildFlowAdd() | 492 | factory().buildFlowAdd() |
342 | - .setMatch(matchPort) | 493 | + .setMatch(matchPort) |
343 | - .setInstructions(instructions) | 494 | + .setPriority(100) |
344 | - .setXid(getNextTransactionId()) | 495 | + .setInstructions(instructions) |
345 | - .build(); | 496 | + .setXid(getNextTransactionId()) |
497 | + .build(); | ||
346 | log.debug("Adding optical flow in sw {}", getStringId()); | 498 | log.debug("Adding optical flow in sw {}", getStringId()); |
347 | List<OFMessage> msglist = new ArrayList<>(1); | 499 | List<OFMessage> msglist = new ArrayList<>(1); |
348 | msglist.add(opticalFlowEntry); | 500 | msglist.add(opticalFlowEntry); |
349 | write(msglist); | 501 | write(msglist); |
502 | + sendBarrier(true); | ||
350 | } | 503 | } |
351 | 504 | ||
352 | } | 505 | } |
353 | 506 | ||
507 | + | ||
354 | // Todo remove - for testing purpose only | 508 | // Todo remove - for testing purpose only |
355 | private static CircuitSignalID getSignalID(short lambda) { | 509 | private static CircuitSignalID getSignalID(short lambda) { |
356 | byte myGrid = 1; | 510 | byte myGrid = 1; |
... | @@ -365,9 +519,21 @@ public class OFOpticalSwitchImplLINC13 extends AbstractOpenFlowSwitch { | ... | @@ -365,9 +519,21 @@ public class OFOpticalSwitchImplLINC13 extends AbstractOpenFlowSwitch { |
365 | return signalID; | 519 | return signalID; |
366 | } | 520 | } |
367 | 521 | ||
522 | + private void sendBarrier(boolean finalBarrier) throws IOException { | ||
523 | + int xid = getNextTransactionId(); | ||
524 | + if (finalBarrier) { | ||
525 | + barrierXidToWaitFor = xid; | ||
526 | + } | ||
527 | + OFBarrierRequest br = factory() | ||
528 | + .buildBarrierRequest() | ||
529 | + .setXid(xid) | ||
530 | + .build(); | ||
531 | + sendMsg(br); | ||
532 | + } | ||
533 | + | ||
368 | @Override | 534 | @Override |
369 | public void write(OFMessage msg) { | 535 | public void write(OFMessage msg) { |
370 | - this.channel.write(msg); | 536 | + this.sendMsg(msg); |
371 | } | 537 | } |
372 | 538 | ||
373 | @Override | 539 | @Override | ... | ... |
... | @@ -304,7 +304,9 @@ | ... | @@ -304,7 +304,9 @@ |
304 | <plugin> | 304 | <plugin> |
305 | <groupId>org.apache.maven.plugins</groupId> | 305 | <groupId>org.apache.maven.plugins</groupId> |
306 | <artifactId>maven-compiler-plugin</artifactId> | 306 | <artifactId>maven-compiler-plugin</artifactId> |
307 | - <version>3.1</version> | 307 | + <!-- TODO: update once following issue is fixed. --> |
308 | + <!-- https://jira.codehaus.org/browse/MCOMPILER-205 --> | ||
309 | + <version>2.5.1</version> | ||
308 | <configuration> | 310 | <configuration> |
309 | <source>1.7</source> | 311 | <source>1.7</source> |
310 | <target>1.7</target> | 312 | <target>1.7</target> | ... | ... |
... | @@ -161,10 +161,10 @@ public class FlowModBuilder { | ... | @@ -161,10 +161,10 @@ public class FlowModBuilder { |
161 | switch (l3m.subtype()) { | 161 | switch (l3m.subtype()) { |
162 | case IP_DST: | 162 | case IP_DST: |
163 | ip = (ModIPInstruction) i; | 163 | ip = (ModIPInstruction) i; |
164 | - return factory.actions().setNwDst(IPv4Address.of(ip.ip().toInt())); | 164 | + return factory.actions().setNwDst(IPv4Address.of(ip.ip().toRealInt())); |
165 | case IP_SRC: | 165 | case IP_SRC: |
166 | ip = (ModIPInstruction) i; | 166 | ip = (ModIPInstruction) i; |
167 | - return factory.actions().setNwSrc(IPv4Address.of(ip.ip().toInt())); | 167 | + return factory.actions().setNwSrc(IPv4Address.of(ip.ip().toRealInt())); |
168 | default: | 168 | default: |
169 | log.warn("Unimplemented action type {}.", l3m.subtype()); | 169 | log.warn("Unimplemented action type {}.", l3m.subtype()); |
170 | break; | 170 | break; |
... | @@ -220,21 +220,21 @@ public class FlowModBuilder { | ... | @@ -220,21 +220,21 @@ public class FlowModBuilder { |
220 | case IPV4_DST: | 220 | case IPV4_DST: |
221 | ip = (IPCriterion) c; | 221 | ip = (IPCriterion) c; |
222 | if (ip.ip().isMasked()) { | 222 | if (ip.ip().isMasked()) { |
223 | - Masked<IPv4Address> maskedIp = Masked.of(IPv4Address.of(ip.ip().toInt()), | 223 | + Masked<IPv4Address> maskedIp = Masked.of(IPv4Address.of(ip.ip().toRealInt()), |
224 | - IPv4Address.of(ip.ip().netmask().toInt())); | 224 | + IPv4Address.of(ip.ip().netmask().toRealInt())); |
225 | mBuilder.setMasked(MatchField.IPV4_DST, maskedIp); | 225 | mBuilder.setMasked(MatchField.IPV4_DST, maskedIp); |
226 | } else { | 226 | } else { |
227 | - mBuilder.setExact(MatchField.IPV4_DST, IPv4Address.of(ip.ip().toInt())); | 227 | + mBuilder.setExact(MatchField.IPV4_DST, IPv4Address.of(ip.ip().toRealInt())); |
228 | } | 228 | } |
229 | break; | 229 | break; |
230 | case IPV4_SRC: | 230 | case IPV4_SRC: |
231 | ip = (IPCriterion) c; | 231 | ip = (IPCriterion) c; |
232 | if (ip.ip().isMasked()) { | 232 | if (ip.ip().isMasked()) { |
233 | - Masked<IPv4Address> maskedIp = Masked.of(IPv4Address.of(ip.ip().toInt()), | 233 | + Masked<IPv4Address> maskedIp = Masked.of(IPv4Address.of(ip.ip().toRealInt()), |
234 | - IPv4Address.of(ip.ip().netmask().toInt())); | 234 | + IPv4Address.of(ip.ip().netmask().toRealInt())); |
235 | mBuilder.setMasked(MatchField.IPV4_SRC, maskedIp); | 235 | mBuilder.setMasked(MatchField.IPV4_SRC, maskedIp); |
236 | } else { | 236 | } else { |
237 | - mBuilder.setExact(MatchField.IPV4_SRC, IPv4Address.of(ip.ip().toInt())); | 237 | + mBuilder.setExact(MatchField.IPV4_SRC, IPv4Address.of(ip.ip().toRealInt())); |
238 | } | 238 | } |
239 | break; | 239 | break; |
240 | case IP_PROTO: | 240 | case IP_PROTO: | ... | ... |
... | @@ -9,6 +9,10 @@ export KARAF_ZIP=${KARAF_ZIP:-~/Downloads/apache-karaf-3.0.1.zip} | ... | @@ -9,6 +9,10 @@ export KARAF_ZIP=${KARAF_ZIP:-~/Downloads/apache-karaf-3.0.1.zip} |
9 | export KARAF_TAR=${KARAF_TAR:-~/Downloads/apache-karaf-3.0.1.tar.gz} | 9 | export KARAF_TAR=${KARAF_TAR:-~/Downloads/apache-karaf-3.0.1.tar.gz} |
10 | export KARAF_DIST=$(basename $KARAF_ZIP .zip) | 10 | export KARAF_DIST=$(basename $KARAF_ZIP .zip) |
11 | 11 | ||
12 | +# Add ONOS-specific directories to the exectable PATH | ||
13 | +export PATH="$PATH:$ONOS_ROOT/tools/dev/bin:$ONOS_ROOT/tools/test/bin" | ||
14 | +export PATH="$PATH:$ONOS_ROOT/tools/build" | ||
15 | + | ||
12 | # Fallback build number us derived from from the user name & time | 16 | # Fallback build number us derived from from the user name & time |
13 | export BUILD_NUMBER=${BUILD_NUMBER:-$(id -un)~$(date +'%Y/%m/%d@%H:%M')} | 17 | export BUILD_NUMBER=${BUILD_NUMBER:-$(id -un)~$(date +'%Y/%m/%d@%H:%M')} |
14 | 18 | ... | ... |
1 | #!/bin/bash | 1 | #!/bin/bash |
2 | -#------------------------------------------------------------------------------- | 2 | +# ----------------------------------------------------------------------------- |
3 | # Builds the ONOS from source. | 3 | # Builds the ONOS from source. |
4 | -#------------------------------------------------------------------------------- | 4 | +# ----------------------------------------------------------------------------- |
5 | 5 | ||
6 | [ ! -d "$ONOS_ROOT" ] && echo "ONOS_ROOT is not defined" >&2 && exit 1 | 6 | [ ! -d "$ONOS_ROOT" ] && echo "ONOS_ROOT is not defined" >&2 && exit 1 |
7 | . $ONOS_ROOT/tools/build/envDefaults | 7 | . $ONOS_ROOT/tools/build/envDefaults |
8 | 8 | ||
9 | cd $ONOS_ROOT | 9 | cd $ONOS_ROOT |
10 | -mvn clean install && mvn javadoc:aggregate | ||
... | \ No newline at end of file | ... | \ No newline at end of file |
10 | +mvn clean install && mvn javadoc:aggregate | ... | ... |
1 | #!/bin/bash | 1 | #!/bin/bash |
2 | -#------------------------------------------------------------------------------- | 2 | +# ----------------------------------------------------------------------------- |
3 | # Packages ONOS distributable into onos.tar.gz | 3 | # Packages ONOS distributable into onos.tar.gz |
4 | -#------------------------------------------------------------------------------- | 4 | +# ----------------------------------------------------------------------------- |
5 | 5 | ||
6 | [ ! -d "$ONOS_ROOT" ] && echo "ONOS_ROOT is not defined" >&2 && exit 1 | 6 | [ ! -d "$ONOS_ROOT" ] && echo "ONOS_ROOT is not defined" >&2 && exit 1 |
7 | . $ONOS_ROOT/tools/build/envDefaults | 7 | . $ONOS_ROOT/tools/build/envDefaults | ... | ... |
1 | #!/bin/bash | 1 | #!/bin/bash |
2 | -#------------------------------------------------------------------------------- | 2 | +# ----------------------------------------------------------------------------- |
3 | # Launches the ONOS tests on the current cell environment. | 3 | # Launches the ONOS tests on the current cell environment. |
4 | -#------------------------------------------------------------------------------- | 4 | +# ----------------------------------------------------------------------------- |
5 | 5 | ||
6 | [ ! -d "$ONOS_ROOT" ] && echo "ONOS_ROOT is not defined" >&2 && exit 1 | 6 | [ ! -d "$ONOS_ROOT" ] && echo "ONOS_ROOT is not defined" >&2 && exit 1 |
7 | . $ONOS_ROOT/tools/build/envDefaults | 7 | . $ONOS_ROOT/tools/build/envDefaults | ... | ... |
... | @@ -61,15 +61,14 @@ function cell { | ... | @@ -61,15 +61,14 @@ function cell { |
61 | if [ -n "$1" ]; then | 61 | if [ -n "$1" ]; then |
62 | [ ! -f $ONOS_ROOT/tools/test/cells/$1 ] && \ | 62 | [ ! -f $ONOS_ROOT/tools/test/cells/$1 ] && \ |
63 | echo "No such cell: $1" >&2 && return 1 | 63 | echo "No such cell: $1" >&2 && return 1 |
64 | + unset ONOS_CELL ONOS_NIC ONOS_FEATURES | ||
64 | unset OC1 OC2 OC3 OC4 OC5 OC6 OC7 OC8 OC9 OCN OCI | 65 | unset OC1 OC2 OC3 OC4 OC5 OC6 OC7 OC8 OC9 OCN OCI |
65 | . $ONOS_ROOT/tools/test/cells/$1 | 66 | . $ONOS_ROOT/tools/test/cells/$1 |
66 | - export OCI=$OC1 | ||
67 | - export ONOS_CELL=$1 | ||
68 | cell | 67 | cell |
69 | else | 68 | else |
70 | env | egrep "ONOS_CELL" | 69 | env | egrep "ONOS_CELL" |
71 | env | egrep "OCI" | 70 | env | egrep "OCI" |
72 | - env | egrep "OC[0-9]+" | sort | 71 | + env | egrep "OC[1-9]+" | sort |
73 | env | egrep "OCN" | 72 | env | egrep "OCN" |
74 | env | egrep "ONOS_" | egrep -v 'ONOS_ROOT|ONOS_CELL' | 73 | env | egrep "ONOS_" | egrep -v 'ONOS_ROOT|ONOS_CELL' |
75 | fi | 74 | fi | ... | ... |
1 | #!/bin/bash | 1 | #!/bin/bash |
2 | -#------------------------------------------------------------------------------ | 2 | +# ---------------------------------------------------------------------------- |
3 | # Selectively builds only those projects that contained modified Java files. | 3 | # Selectively builds only those projects that contained modified Java files. |
4 | -#------------------------------------------------------------------------------ | 4 | +# ---------------------------------------------------------------------------- |
5 | 5 | ||
6 | projects=$(find $ONOS_ROOT -name '*.java' \ | 6 | projects=$(find $ONOS_ROOT -name '*.java' \ |
7 | -not -path '*/openflowj/*' -and -not -path '.git/*' \ | 7 | -not -path '*/openflowj/*' -and -not -path '.git/*' \ |
... | @@ -9,4 +9,4 @@ projects=$(find $ONOS_ROOT -name '*.java' \ | ... | @@ -9,4 +9,4 @@ projects=$(find $ONOS_ROOT -name '*.java' \ |
9 | sort -u | sed "s:$ONOS_ROOT::g" | tr '\n' ',' | \ | 9 | sort -u | sed "s:$ONOS_ROOT::g" | tr '\n' ',' | \ |
10 | sed 's:/,:,:g;s:,/:,:g;s:^/::g;s:,$::g') | 10 | sed 's:/,:,:g;s:,/:,:g;s:^/::g;s:,$::g') |
11 | 11 | ||
12 | -[ -n "$projects" ] && cd $ONOS_ROOT && mvn --projects $projects ${@:-clean install} | ||
... | \ No newline at end of file | ... | \ No newline at end of file |
12 | +[ -n "$projects" ] && cd $ONOS_ROOT && mvn --projects $projects ${@:-clean install} | ... | ... |
1 | #!/bin/bash | 1 | #!/bin/bash |
2 | -#------------------------------------------------------------------------------ | 2 | +# ---------------------------------------------------------------------------- |
3 | # Echoes project-level directory if a Java file within is newer than the | 3 | # Echoes project-level directory if a Java file within is newer than the |
4 | # target directory. | 4 | # target directory. |
5 | -#------------------------------------------------------------------------------ | 5 | +# ---------------------------------------------------------------------------- |
6 | 6 | ||
7 | javaFile=${1#*\/src\/*\/java/} | 7 | javaFile=${1#*\/src\/*\/java/} |
8 | basename=${1/*\//} | 8 | basename=${1/*\//} |
... | @@ -14,4 +14,3 @@ project=${src/src*/} | ... | @@ -14,4 +14,3 @@ project=${src/src*/} |
14 | target=$project/target | 14 | target=$project/target |
15 | 15 | ||
16 | [ $target -nt ${src}$javaFile ] || echo ${src/src*/} | 16 | [ $target -nt ${src}$javaFile ] || echo ${src/src*/} |
17 | - | ... | ... |
1 | #!/bin/bash | 1 | #!/bin/bash |
2 | -#------------------------------------------------------------------------------ | 2 | +# ---------------------------------------------------------------------------- |
3 | # Continuously watches the Apache Karaf log; survives 'karaf clean' | 3 | # Continuously watches the Apache Karaf log; survives 'karaf clean' |
4 | -#------------------------------------------------------------------------------ | 4 | +# ---------------------------------------------------------------------------- |
5 | KARAF_LOG=${KARAF_LOG:-~/apache-karaf-3.0.1/data/log/karaf.log} | 5 | KARAF_LOG=${KARAF_LOG:-~/apache-karaf-3.0.1/data/log/karaf.log} |
6 | 6 | ||
7 | while true; do | 7 | while true; do |
8 | [ ! -f $KARAF_LOG ] && sleep 2 && continue | 8 | [ ! -f $KARAF_LOG ] && sleep 2 && continue |
9 | tail -n 512 -f -F $KARAF_LOG | 9 | tail -n 512 -f -F $KARAF_LOG |
10 | done | 10 | done |
11 | - | ... | ... |
1 | #!/bin/bash | 1 | #!/bin/bash |
2 | -#------------------------------------------------------------------------------- | 2 | +# ----------------------------------------------------------------------------- |
3 | # ONOS command-line client | 3 | # ONOS command-line client |
4 | -#------------------------------------------------------------------------------- | 4 | +# ----------------------------------------------------------------------------- |
5 | 5 | ||
6 | export JAVA_HOME=${JAVA_HOME:-/usr/lib/jvm/java-7-openjdk-amd64/} | 6 | export JAVA_HOME=${JAVA_HOME:-/usr/lib/jvm/java-7-openjdk-amd64/} |
7 | 7 | ||
8 | cd $(dirname $0)/../apache-karaf-*/bin | 8 | cd $(dirname $0)/../apache-karaf-*/bin |
9 | ./client -h localhost "$@" | 9 | ./client -h localhost "$@" |
10 | - | ... | ... |
1 | #!/bin/bash | 1 | #!/bin/bash |
2 | -#------------------------------------------------------------------------------- | 2 | +# ----------------------------------------------------------------------------- |
3 | # Starts ONOS Apache Karaf container | 3 | # Starts ONOS Apache Karaf container |
4 | -#------------------------------------------------------------------------------- | 4 | +# ----------------------------------------------------------------------------- |
5 | 5 | ||
6 | export JAVA_HOME=${JAVA_HOME:-/usr/lib/jvm/java-7-openjdk-amd64/} | 6 | export JAVA_HOME=${JAVA_HOME:-/usr/lib/jvm/java-7-openjdk-amd64/} |
7 | export JAVA_OPTS="-Xms256M -Xmx2048M" | 7 | export JAVA_OPTS="-Xms256M -Xmx2048M" |
8 | 8 | ||
9 | cd /opt/onos | 9 | cd /opt/onos |
10 | /opt/onos/apache-karaf-3.0.1/bin/karaf "$@" | 10 | /opt/onos/apache-karaf-3.0.1/bin/karaf "$@" |
11 | - | ... | ... |
1 | #!/bin/bash | 1 | #!/bin/bash |
2 | -#------------------------------------------------------------------------------- | 2 | +# ----------------------------------------------------------------------------- |
3 | # ONOS remote command-line client. | 3 | # ONOS remote command-line client. |
4 | -#------------------------------------------------------------------------------- | 4 | +# ----------------------------------------------------------------------------- |
5 | + | ||
6 | +[ ! -d "$ONOS_ROOT" ] && echo "ONOS_ROOT is not defined" >&2 && exit 1 | ||
7 | +. $ONOS_ROOT/tools/build/envDefaults | ||
5 | 8 | ||
6 | [ "$1" = "-w" ] && shift && onos-wait-for-start $1 | 9 | [ "$1" = "-w" ] && shift && onos-wait-for-start $1 |
7 | 10 | ... | ... |
1 | #!/bin/bash | 1 | #!/bin/bash |
2 | -#------------------------------------------------------------------------------- | 2 | +# ----------------------------------------------------------------------------- |
3 | # Checks the logs of the remote ONOS instance and makes sure they are clean. | 3 | # Checks the logs of the remote ONOS instance and makes sure they are clean. |
4 | -#------------------------------------------------------------------------------- | 4 | +# ----------------------------------------------------------------------------- |
5 | 5 | ||
6 | [ ! -d "$ONOS_ROOT" ] && echo "ONOS_ROOT is not defined" >&2 && exit 1 | 6 | [ ! -d "$ONOS_ROOT" ] && echo "ONOS_ROOT is not defined" >&2 && exit 1 |
7 | . $ONOS_ROOT/tools/build/envDefaults | 7 | . $ONOS_ROOT/tools/build/envDefaults | ... | ... |
1 | #!/bin/bash | 1 | #!/bin/bash |
2 | -#------------------------------------------------------------------------------- | 2 | +# ----------------------------------------------------------------------------- |
3 | # Remotely configures & starts ONOS for the first time. | 3 | # Remotely configures & starts ONOS for the first time. |
4 | -#------------------------------------------------------------------------------- | 4 | +# ----------------------------------------------------------------------------- |
5 | 5 | ||
6 | [ ! -d "$ONOS_ROOT" ] && echo "ONOS_ROOT is not defined" >&2 && exit 1 | 6 | [ ! -d "$ONOS_ROOT" ] && echo "ONOS_ROOT is not defined" >&2 && exit 1 |
7 | . $ONOS_ROOT/tools/build/envDefaults | 7 | . $ONOS_ROOT/tools/build/envDefaults |
... | @@ -24,5 +24,4 @@ ssh $remote " | ... | @@ -24,5 +24,4 @@ ssh $remote " |
24 | echo \"onos.ip = \$(ifconfig | grep $ONOS_NIC | cut -d: -f2 | cut -d\\ -f1)\" \ | 24 | echo \"onos.ip = \$(ifconfig | grep $ONOS_NIC | cut -d: -f2 | cut -d\\ -f1)\" \ |
25 | >> $ONOS_INSTALL_DIR/$KARAF_DIST/etc/system.properties | 25 | >> $ONOS_INSTALL_DIR/$KARAF_DIST/etc/system.properties |
26 | " | 26 | " |
27 | - | ||
28 | -scp -q $CDEF_FILE $remote:$ONOS_INSTALL_DIR/config/ | ||
... | \ No newline at end of file | ... | \ No newline at end of file |
27 | +scp -q $CDEF_FILE $remote:$ONOS_INSTALL_DIR/config/ | ... | ... |
1 | #!/bin/bash | 1 | #!/bin/bash |
2 | -#------------------------------------------------------------------------------- | 2 | +# ----------------------------------------------------------------------------- |
3 | # Remotely fetches the ONOS test VMs from a local share into ~/Downloads. | 3 | # Remotely fetches the ONOS test VMs from a local share into ~/Downloads. |
4 | -#------------------------------------------------------------------------------- | 4 | +# ----------------------------------------------------------------------------- |
5 | 5 | ||
6 | [ ! -d "$ONOS_ROOT" ] && echo "ONOS_ROOT is not defined" >&2 && exit 1 | 6 | [ ! -d "$ONOS_ROOT" ] && echo "ONOS_ROOT is not defined" >&2 && exit 1 |
7 | . $ONOS_ROOT/tools/build/envDefaults | 7 | . $ONOS_ROOT/tools/build/envDefaults | ... | ... |
1 | #!/bin/bash | 1 | #!/bin/bash |
2 | -#------------------------------------------------------------------------------- | 2 | +# ----------------------------------------------------------------------------- |
3 | # Launches ONOS GUI on the specified node. | 3 | # Launches ONOS GUI on the specified node. |
4 | -#------------------------------------------------------------------------------- | 4 | +# ----------------------------------------------------------------------------- |
5 | + | ||
6 | +[ ! -d "$ONOS_ROOT" ] && echo "ONOS_ROOT is not defined" >&2 && exit 1 | ||
7 | +. $ONOS_ROOT/tools/build/envDefaults | ||
5 | 8 | ||
6 | host=${1:-$OCI} | 9 | host=${1:-$OCI} |
7 | host=${host:-localhost} | 10 | host=${host:-localhost} |
8 | 11 | ||
9 | -open http://$host:8181/onos/tvue | ||
... | \ No newline at end of file | ... | \ No newline at end of file |
12 | +open http://$host:8181/onos/tvue | ... | ... |
1 | #!/bin/bash | 1 | #!/bin/bash |
2 | -#------------------------------------------------------------------------------- | 2 | +# ----------------------------------------------------------------------------- |
3 | # Remotely pushes bits to a remote node and installs ONOS on it. | 3 | # Remotely pushes bits to a remote node and installs ONOS on it. |
4 | -#------------------------------------------------------------------------------- | 4 | +# ----------------------------------------------------------------------------- |
5 | 5 | ||
6 | [ ! -d "$ONOS_ROOT" ] && echo "ONOS_ROOT is not defined" >&2 && exit 1 | 6 | [ ! -d "$ONOS_ROOT" ] && echo "ONOS_ROOT is not defined" >&2 && exit 1 |
7 | . $ONOS_ROOT/tools/build/envDefaults | 7 | . $ONOS_ROOT/tools/build/envDefaults |
... | @@ -18,7 +18,7 @@ ssh $remote " | ... | @@ -18,7 +18,7 @@ ssh $remote " |
18 | [ -d $ONOS_INSTALL_DIR/bin ] && echo \"ONOS is already installed\" && exit 1 | 18 | [ -d $ONOS_INSTALL_DIR/bin ] && echo \"ONOS is already installed\" && exit 1 |
19 | 19 | ||
20 | # Prepare a landing zone and unroll the bits | 20 | # Prepare a landing zone and unroll the bits |
21 | - sudo mkdir -p $ONOS_INSTALL_DIR && sudo chown sdn:sdn $ONOS_INSTALL_DIR | 21 | + sudo mkdir -p $ONOS_INSTALL_DIR && sudo chown ${ONOS_USER}:${ONOS_USER} $ONOS_INSTALL_DIR |
22 | tar zxmf /tmp/$ONOS_BITS.tar.gz -C $ONOS_INSTALL_DIR --strip-components=1 | 22 | tar zxmf /tmp/$ONOS_BITS.tar.gz -C $ONOS_INSTALL_DIR --strip-components=1 |
23 | 23 | ||
24 | # Make a link to the log file directory and make a home for auxiliaries | 24 | # Make a link to the log file directory and make a home for auxiliaries | ... | ... |
1 | #!/bin/bash | 1 | #!/bin/bash |
2 | -#------------------------------------------------------------------------------- | 2 | +# ----------------------------------------------------------------------------- |
3 | # Remotely kills the ONOS service on the specified node. | 3 | # Remotely kills the ONOS service on the specified node. |
4 | -#------------------------------------------------------------------------------- | 4 | +# ----------------------------------------------------------------------------- |
5 | 5 | ||
6 | [ ! -d "$ONOS_ROOT" ] && echo "ONOS_ROOT is not defined" >&2 && exit 1 | 6 | [ ! -d "$ONOS_ROOT" ] && echo "ONOS_ROOT is not defined" >&2 && exit 1 |
7 | . $ONOS_ROOT/tools/build/envDefaults | 7 | . $ONOS_ROOT/tools/build/envDefaults |
8 | 8 | ||
9 | -ssh $ONOS_USER@${1:-$OCI} "kill -9 \$(ps -ef | grep karaf.jar | grep -v grep | cut -c10-15)" | ||
... | \ No newline at end of file | ... | \ No newline at end of file |
9 | +ssh $ONOS_USER@${1:-$OCI} "kill -9 \$(ps -ef | grep karaf.jar | grep -v grep | cut -c10-15)" | ... | ... |
tools/test/bin/onos-list-cells
0 → 100755
1 | +#!/bin/bash | ||
2 | +# ----------------------------------------------------------------------------- | ||
3 | +# List available ONOS cells configuration. | ||
4 | +# ----------------------------------------------------------------------------- | ||
5 | + | ||
6 | +[ ! -d "$ONOS_ROOT" ] && echo "ONOS_ROOT is not defined" >&2 && exit 1 | ||
7 | +. $ONOS_ROOT/tools/build/envDefaults | ||
8 | + | ||
9 | +# Lists available cells | ||
10 | +for cell in $(ls -1 $ONOS_ROOT/tools/test/cells); do | ||
11 | + if [ ${cell} = "${ONOS_CELL}" ]; then | ||
12 | + cell_id="${cell} *" | ||
13 | + else | ||
14 | + cell_id="${cell}" | ||
15 | + fi | ||
16 | + cell_descr="$(grep '^#' $ONOS_ROOT/tools/test/cells/$cell | head -n 1)" | ||
17 | + printf "%-12s %s\n" "${cell_id}" "${cell_descr}" | ||
18 | +done |
1 | #!/bin/bash | 1 | #!/bin/bash |
2 | -#------------------------------------------------------------------------------- | 2 | +# ----------------------------------------------------------------------------- |
3 | # Monitors remote ONOS log file on the specified node. | 3 | # Monitors remote ONOS log file on the specified node. |
4 | -#------------------------------------------------------------------------------- | 4 | +# ----------------------------------------------------------------------------- |
5 | 5 | ||
6 | [ ! -d "$ONOS_ROOT" ] && echo "ONOS_ROOT is not defined" >&2 && exit 1 | 6 | [ ! -d "$ONOS_ROOT" ] && echo "ONOS_ROOT is not defined" >&2 && exit 1 |
7 | . $ONOS_ROOT/tools/build/envDefaults | 7 | . $ONOS_ROOT/tools/build/envDefaults | ... | ... |
1 | #!/bin/bash | 1 | #!/bin/bash |
2 | -#------------------------------------------------------------------------------- | 2 | +# ----------------------------------------------------------------------------- |
3 | # Remotely patches the ONOS VM to tailor its hostname. | 3 | # Remotely patches the ONOS VM to tailor its hostname. |
4 | -#------------------------------------------------------------------------------- | 4 | +# ----------------------------------------------------------------------------- |
5 | 5 | ||
6 | [ ! -d "$ONOS_ROOT" ] && echo "ONOS_ROOT is not defined" >&2 && exit 1 | 6 | [ ! -d "$ONOS_ROOT" ] && echo "ONOS_ROOT is not defined" >&2 && exit 1 |
7 | . $ONOS_ROOT/tools/build/envDefaults | 7 | . $ONOS_ROOT/tools/build/envDefaults | ... | ... |
1 | #!/bin/bash | 1 | #!/bin/bash |
2 | -#------------------------------------------------------------------------------- | 2 | +# ----------------------------------------------------------------------------- |
3 | # Pushes the local id_rsa.pub to the authorized_keys on a remote ONOS node. | 3 | # Pushes the local id_rsa.pub to the authorized_keys on a remote ONOS node. |
4 | -#------------------------------------------------------------------------------- | 4 | +# ----------------------------------------------------------------------------- |
5 | 5 | ||
6 | [ ! -d "$ONOS_ROOT" ] && echo "ONOS_ROOT is not defined" >&2 && exit 1 | 6 | [ ! -d "$ONOS_ROOT" ] && echo "ONOS_ROOT is not defined" >&2 && exit 1 |
7 | . $ONOS_ROOT/tools/build/envDefaults | 7 | . $ONOS_ROOT/tools/build/envDefaults | ... | ... |
1 | #!/bin/bash | 1 | #!/bin/bash |
2 | -#------------------------------------------------------------------------------- | 2 | +# ----------------------------------------------------------------------------- |
3 | # Pushes the specified bundle to the remote ONOS cell machines and updates it. | 3 | # Pushes the specified bundle to the remote ONOS cell machines and updates it. |
4 | -#------------------------------------------------------------------------------- | 4 | +# ----------------------------------------------------------------------------- |
5 | 5 | ||
6 | [ ! -d "$ONOS_ROOT" ] && echo "ONOS_ROOT is not defined" >&2 && exit 1 | 6 | [ ! -d "$ONOS_ROOT" ] && echo "ONOS_ROOT is not defined" >&2 && exit 1 |
7 | . $ONOS_ROOT/tools/build/envDefaults | 7 | . $ONOS_ROOT/tools/build/envDefaults | ... | ... |
1 | #!/bin/bash | 1 | #!/bin/bash |
2 | -#------------------------------------------------------------------------------- | 2 | +# ----------------------------------------------------------------------------- |
3 | # Remotely administers the ONOS service on the specified node. | 3 | # Remotely administers the ONOS service on the specified node. |
4 | -#------------------------------------------------------------------------------- | 4 | +# ----------------------------------------------------------------------------- |
5 | 5 | ||
6 | [ ! -d "$ONOS_ROOT" ] && echo "ONOS_ROOT is not defined" >&2 && exit 1 | 6 | [ ! -d "$ONOS_ROOT" ] && echo "ONOS_ROOT is not defined" >&2 && exit 1 |
7 | . $ONOS_ROOT/tools/build/envDefaults | 7 | . $ONOS_ROOT/tools/build/envDefaults |
8 | 8 | ||
9 | -ssh $ONOS_USER@${1:-$OCI} "sudo service onos ${2:-status}" | ||
... | \ No newline at end of file | ... | \ No newline at end of file |
9 | +ssh $ONOS_USER@${1:-$OCI} "sudo service onos ${2:-status}" | ... | ... |
tools/test/bin/onos-show-cell
0 → 100755
1 | +#!/bin/bash | ||
2 | +# ----------------------------------------------------------------------------- | ||
3 | +# Print the configuration of an ONOS cell. | ||
4 | +# ----------------------------------------------------------------------------- | ||
5 | + | ||
6 | +[ ! -d "$ONOS_ROOT" ] && echo "ONOS_ROOT is not defined" >&2 && exit 1 | ||
7 | +. $ONOS_ROOT/tools/build/envDefaults | ||
8 | + | ||
9 | +function print_usage { | ||
10 | + echo "Print the configuration of an ONOS cell." | ||
11 | + echo "If no arguments are specified, it will print the configuration for the default" | ||
12 | + echo "ONOS cell as specified in the 'ONOS_CELL' environmental variable." | ||
13 | + echo | ||
14 | + echo "Optional arguments:" | ||
15 | + echo " [cell-name] Print the configuration of 'cell-name'" | ||
16 | + echo " [-h | --help] Print this help" | ||
17 | +} | ||
18 | + | ||
19 | +if [ "${1}" = "-h" -o "${1}" = "--help" ]; then | ||
20 | + print_usage | ||
21 | + exit 0 | ||
22 | +fi | ||
23 | + | ||
24 | +if [ -n "${1}" ]; then | ||
25 | + cell="${1}" | ||
26 | +else | ||
27 | + if [ -z "${ONOS_CELL}" ]; then | ||
28 | + echo "Environmental variable 'ONOS_CELL' is not defiled" | ||
29 | + exit 1 | ||
30 | + else | ||
31 | + cell="${ONOS_CELL}" | ||
32 | + fi | ||
33 | +fi | ||
34 | + | ||
35 | +if [ ! -f $ONOS_ROOT/tools/test/cells/${cell} ]; then | ||
36 | + echo "No such cell: ${cell}" | ||
37 | + exit 1 | ||
38 | +fi | ||
39 | + | ||
40 | +# Load the cell setup | ||
41 | +. $ONOS_ROOT/tools/test/cells/${cell} | ||
42 | + | ||
43 | +echo "ONOS_CELL=${ONOS_CELL}" | ||
44 | +echo "ONOS_NIC=${ONOS_NIC}" | ||
45 | +for n in {1..9}; do | ||
46 | + ocn="OC${n}" | ||
47 | + if [ -n "${!ocn}" ]; then | ||
48 | + echo "$ocn=${!ocn}" | ||
49 | + fi | ||
50 | +done | ||
51 | +echo "OCN=${OCN}" | ||
52 | +echo "OCI=${OCI}" | ||
53 | +echo "ONOS_FEATURES=${ONOS_FEATURES}" |
1 | #!/bin/bash | 1 | #!/bin/bash |
2 | -#------------------------------------------------------------------------------- | 2 | +# ----------------------------------------------------------------------------- |
3 | # Logs in to the remote ONOS node. | 3 | # Logs in to the remote ONOS node. |
4 | -#------------------------------------------------------------------------------- | 4 | +# ----------------------------------------------------------------------------- |
5 | 5 | ||
6 | [ ! -d "$ONOS_ROOT" ] && echo "ONOS_ROOT is not defined" >&2 && exit 1 | 6 | [ ! -d "$ONOS_ROOT" ] && echo "ONOS_ROOT is not defined" >&2 && exit 1 |
7 | . $ONOS_ROOT/tools/build/envDefaults | 7 | . $ONOS_ROOT/tools/build/envDefaults | ... | ... |
1 | #!/bin/bash | 1 | #!/bin/bash |
2 | -#------------------------------------------------------------------------------- | 2 | +# ----------------------------------------------------------------------------- |
3 | # Verifies connectivity to each node in ONOS cell. | 3 | # Verifies connectivity to each node in ONOS cell. |
4 | -#------------------------------------------------------------------------------- | 4 | +# ----------------------------------------------------------------------------- |
5 | 5 | ||
6 | [ ! -d "$ONOS_ROOT" ] && echo "ONOS_ROOT is not defined" >&2 && exit 1 | 6 | [ ! -d "$ONOS_ROOT" ] && echo "ONOS_ROOT is not defined" >&2 && exit 1 |
7 | . $ONOS_ROOT/tools/build/envDefaults | 7 | . $ONOS_ROOT/tools/build/envDefaults | ... | ... |
1 | #!/bin/bash | 1 | #!/bin/bash |
2 | -#------------------------------------------------------------------------------- | 2 | +# ----------------------------------------------------------------------------- |
3 | # Remotely stops & uninstalls ONOS on the specified node. | 3 | # Remotely stops & uninstalls ONOS on the specified node. |
4 | -#------------------------------------------------------------------------------- | 4 | +# ----------------------------------------------------------------------------- |
5 | 5 | ||
6 | [ ! -d "$ONOS_ROOT" ] && echo "ONOS_ROOT is not defined" >&2 && exit 1 | 6 | [ ! -d "$ONOS_ROOT" ] && echo "ONOS_ROOT is not defined" >&2 && exit 1 |
7 | . $ONOS_ROOT/tools/build/envDefaults | 7 | . $ONOS_ROOT/tools/build/envDefaults | ... | ... |
1 | #!/bin/bash | 1 | #!/bin/bash |
2 | -#------------------------------------------------------------------------------- | 2 | +# ----------------------------------------------------------------------------- |
3 | # Update bundle on locally running karaf. | 3 | # Update bundle on locally running karaf. |
4 | -#------------------------------------------------------------------------------- | 4 | +# ----------------------------------------------------------------------------- |
5 | 5 | ||
6 | [ ! -d "$ONOS_ROOT" ] && echo "ONOS_ROOT is not defined" >&2 && exit 1 | 6 | [ ! -d "$ONOS_ROOT" ] && echo "ONOS_ROOT is not defined" >&2 && exit 1 |
7 | . $ONOS_ROOT/tools/build/envDefaults | 7 | . $ONOS_ROOT/tools/build/envDefaults | ... | ... |
1 | #!/bin/bash | 1 | #!/bin/bash |
2 | -#------------------------------------------------------------------------------- | 2 | +# ----------------------------------------------------------------------------- |
3 | # Verifies connectivity to each node in ONOS cell. | 3 | # Verifies connectivity to each node in ONOS cell. |
4 | -#------------------------------------------------------------------------------- | 4 | +# ----------------------------------------------------------------------------- |
5 | 5 | ||
6 | [ ! -d "$ONOS_ROOT" ] && echo "ONOS_ROOT is not defined" >&2 && exit 1 | 6 | [ ! -d "$ONOS_ROOT" ] && echo "ONOS_ROOT is not defined" >&2 && exit 1 |
7 | . $ONOS_ROOT/tools/build/envDefaults | 7 | . $ONOS_ROOT/tools/build/envDefaults |
8 | 8 | ||
9 | for node in $(env | sort | egrep "OC[0-9N]+" | cut -d= -f2); do | 9 | for node in $(env | sort | egrep "OC[0-9N]+" | cut -d= -f2); do |
10 | printf "%s: " $node; ssh -n -o PasswordAuthentication=no $ONOS_USER@$node date | 10 | printf "%s: " $node; ssh -n -o PasswordAuthentication=no $ONOS_USER@$node date |
11 | -done | ||
... | \ No newline at end of file | ... | \ No newline at end of file |
11 | +done | ... | ... |
1 | #!/bin/bash | 1 | #!/bin/bash |
2 | -#------------------------------------------------------------------------------- | 2 | +# ----------------------------------------------------------------------------- |
3 | # Waits for ONOS to reach run-level 100 on the specified remote node. | 3 | # Waits for ONOS to reach run-level 100 on the specified remote node. |
4 | -#------------------------------------------------------------------------------- | 4 | +# ----------------------------------------------------------------------------- |
5 | 5 | ||
6 | [ ! -d "$ONOS_ROOT" ] && echo "ONOS_ROOT is not defined" >&2 && exit 1 | 6 | [ ! -d "$ONOS_ROOT" ] && echo "ONOS_ROOT is not defined" >&2 && exit 1 |
7 | . $ONOS_ROOT/tools/build/envDefaults | 7 | . $ONOS_ROOT/tools/build/envDefaults | ... | ... |
1 | #!/bin/bash | 1 | #!/bin/bash |
2 | -#------------------------------------------------------------------------------- | 2 | +# ----------------------------------------------------------------------------- |
3 | # Monitors selected set of ONOS commands using the system watch command. | 3 | # Monitors selected set of ONOS commands using the system watch command. |
4 | -#------------------------------------------------------------------------------- | 4 | +# ----------------------------------------------------------------------------- |
5 | 5 | ||
6 | [ ! -d "$ONOS_ROOT" ] && echo "ONOS_ROOT is not defined" >&2 && exit 1 | 6 | [ ! -d "$ONOS_ROOT" ] && echo "ONOS_ROOT is not defined" >&2 && exit 1 |
7 | . $ONOS_ROOT/tools/build/envDefaults | 7 | . $ONOS_ROOT/tools/build/envDefaults | ... | ... |
1 | # Local VirtualBox-based single ONOS instance & ONOS mininet box | 1 | # Local VirtualBox-based single ONOS instance & ONOS mininet box |
2 | 2 | ||
3 | +export ONOS_CELL="cbench" | ||
4 | + | ||
3 | export ONOS_NIC=192.168.56.* | 5 | export ONOS_NIC=192.168.56.* |
4 | export OC1="192.168.56.103" | 6 | export OC1="192.168.56.103" |
5 | export OCN="192.168.56.103" | 7 | export OCN="192.168.56.103" |
8 | +export OCI="${OC1}" | ||
6 | 9 | ||
7 | export ONOS_FEATURES="webconsole,onos-api,onos-core-trivial,onos-cli,onos-openflow,onos-app-fwd" | 10 | export ONOS_FEATURES="webconsole,onos-api,onos-core-trivial,onos-cli,onos-openflow,onos-app-fwd" | ... | ... |
1 | # Local VirtualBox-based ONOS instances 1,2 & ONOS mininet box | 1 | # Local VirtualBox-based ONOS instances 1,2 & ONOS mininet box |
2 | 2 | ||
3 | +export ONOS_CELL="local" | ||
4 | + | ||
3 | export ONOS_NIC=192.168.56.* | 5 | export ONOS_NIC=192.168.56.* |
4 | export OC1="192.168.56.101" | 6 | export OC1="192.168.56.101" |
5 | export OC2="192.168.56.102" | 7 | export OC2="192.168.56.102" |
6 | - | ||
7 | export OCN="192.168.56.103" | 8 | export OCN="192.168.56.103" |
9 | +export OCI="${OC1}" | ||
8 | 10 | ||
9 | - | 11 | +export ONOS_FEATURES="" | ... | ... |
1 | # ProxMox-based cell of ONOS instance; no mininet-box | 1 | # ProxMox-based cell of ONOS instance; no mininet-box |
2 | 2 | ||
3 | -export ONOS_FEATURES="webconsole,onos-api,onos-core-trivial,onos-cli,onos-openflow,onos-app-fwd,onos-app-mobility,onos-app-tvue,onos-app-proxyarp" | 3 | +export ONOS_CELL="office" |
4 | 4 | ||
5 | export ONOS_NIC="10.128.4.*" | 5 | export ONOS_NIC="10.128.4.*" |
6 | export OC1="10.128.4.60" | 6 | export OC1="10.128.4.60" |
7 | +export OCI="${OC1}" | ||
7 | 8 | ||
9 | +export ONOS_FEATURES="webconsole,onos-api,onos-core-trivial,onos-cli,onos-openflow,onos-app-fwd,onos-app-mobility,onos-app-tvue,onos-app-proxyarp" | ... | ... |
1 | # ProxMox-based cell of ONOS instances 1,2 & ONOS mininet box | 1 | # ProxMox-based cell of ONOS instances 1,2 & ONOS mininet box |
2 | 2 | ||
3 | +export ONOS_CELL="prox" | ||
4 | + | ||
3 | export ONOS_NIC="10.1.9.*" | 5 | export ONOS_NIC="10.1.9.*" |
4 | export OC1="10.1.9.94" | 6 | export OC1="10.1.9.94" |
5 | export OC2="10.1.9.82" | 7 | export OC2="10.1.9.82" |
6 | - | ||
7 | export OCN="10.1.9.93" | 8 | export OCN="10.1.9.93" |
9 | +export OCI="${OC1}" | ||
10 | + | ||
11 | +export ONOS_FEATURES="" | ... | ... |
1 | # Local VirtualBox-based single ONOS instance & ONOS mininet box | 1 | # Local VirtualBox-based single ONOS instance & ONOS mininet box |
2 | 2 | ||
3 | +export ONOS_CELL="single" | ||
4 | + | ||
3 | export ONOS_NIC=192.168.56.* | 5 | export ONOS_NIC=192.168.56.* |
4 | export OC1="192.168.56.101" | 6 | export OC1="192.168.56.101" |
5 | export OCN="192.168.56.103" | 7 | export OCN="192.168.56.103" |
8 | +export OCI="${OC1}" | ||
6 | 9 | ||
10 | +export ONOS_FEATURES="" | ... | ... |
1 | # Local VirtualBox-based ONOS instances 1,2,3 & ONOS mininet box | 1 | # Local VirtualBox-based ONOS instances 1,2,3 & ONOS mininet box |
2 | 2 | ||
3 | +export ONOS_CELL="triple" | ||
4 | + | ||
3 | export ONOS_NIC=192.168.56.* | 5 | export ONOS_NIC=192.168.56.* |
4 | export OC1="192.168.56.101" | 6 | export OC1="192.168.56.101" |
5 | export OC2="192.168.56.102" | 7 | export OC2="192.168.56.102" |
6 | export OC3="192.168.56.104" | 8 | export OC3="192.168.56.104" |
7 | - | ||
8 | export OCN="192.168.56.103" | 9 | export OCN="192.168.56.103" |
10 | +export OCI="${OC1}" | ||
9 | 11 | ||
12 | +export ONOS_FEATURES="" | ... | ... |
... | @@ -181,6 +181,15 @@ public final class IpAddress { | ... | @@ -181,6 +181,15 @@ public final class IpAddress { |
181 | return address; | 181 | return address; |
182 | } | 182 | } |
183 | 183 | ||
184 | + public int toRealInt() { | ||
185 | + int val = 0; | ||
186 | + for (int i = 0; i < octets.length; i++) { | ||
187 | + val <<= 8; | ||
188 | + val |= octets[i] & 0xff; | ||
189 | + } | ||
190 | + return val; | ||
191 | + } | ||
192 | + | ||
184 | /** | 193 | /** |
185 | * Helper for computing the mask value from CIDR. | 194 | * Helper for computing the mask value from CIDR. |
186 | * | 195 | * | ... | ... |
... | @@ -3,7 +3,6 @@ package org.onlab.util; | ... | @@ -3,7 +3,6 @@ package org.onlab.util; |
3 | import java.util.concurrent.ConcurrentHashMap; | 3 | import java.util.concurrent.ConcurrentHashMap; |
4 | import java.util.concurrent.ConcurrentMap; | 4 | import java.util.concurrent.ConcurrentMap; |
5 | 5 | ||
6 | -import org.apache.commons.lang3.concurrent.ConcurrentException; | ||
7 | import org.apache.commons.lang3.concurrent.ConcurrentInitializer; | 6 | import org.apache.commons.lang3.concurrent.ConcurrentInitializer; |
8 | 7 | ||
9 | /** | 8 | /** |
... | @@ -27,7 +26,7 @@ public final class NewConcurrentHashMap<K, V> | ... | @@ -27,7 +26,7 @@ public final class NewConcurrentHashMap<K, V> |
27 | } | 26 | } |
28 | 27 | ||
29 | @Override | 28 | @Override |
30 | - public ConcurrentMap<K, V> get() throws ConcurrentException { | 29 | + public ConcurrentMap<K, V> get() { |
31 | return new ConcurrentHashMap<>(); | 30 | return new ConcurrentHashMap<>(); |
32 | } | 31 | } |
33 | } | 32 | } | ... | ... |
... | @@ -108,5 +108,10 @@ public class IpPrefixTest { | ... | @@ -108,5 +108,10 @@ public class IpPrefixTest { |
108 | IpAddress addr = IpAddress.valueOf("192.168.10.1"); | 108 | IpAddress addr = IpAddress.valueOf("192.168.10.1"); |
109 | 109 | ||
110 | assertTrue(intf.contains(addr)); | 110 | assertTrue(intf.contains(addr)); |
111 | + | ||
112 | + IpPrefix intf1 = IpPrefix.valueOf("10.0.0.101/24"); | ||
113 | + IpAddress addr1 = IpAddress.valueOf("10.0.0.4"); | ||
114 | + | ||
115 | + assertTrue(intf1.contains(addr1)); | ||
111 | } | 116 | } |
112 | } | 117 | } | ... | ... |
... | @@ -15,7 +15,7 @@ import static org.onlab.util.Tools.namedThreads; | ... | @@ -15,7 +15,7 @@ import static org.onlab.util.Tools.namedThreads; |
15 | */ | 15 | */ |
16 | public abstract class AbstractLoopTest { | 16 | public abstract class AbstractLoopTest { |
17 | 17 | ||
18 | - protected static final long MAX_MS_WAIT = 500; | 18 | + protected static final long MAX_MS_WAIT = 1500; |
19 | 19 | ||
20 | /** Block on specified countdown latch. Return when countdown reaches | 20 | /** Block on specified countdown latch. Return when countdown reaches |
21 | * zero, or fail the test if the {@value #MAX_MS_WAIT} ms timeout expires. | 21 | * zero, or fail the test if the {@value #MAX_MS_WAIT} ms timeout expires. | ... | ... |
... | @@ -2,6 +2,9 @@ | ... | @@ -2,6 +2,9 @@ |
2 | <html> | 2 | <html> |
3 | <head> | 3 | <head> |
4 | <title>ONOS GUI</title> | 4 | <title>ONOS GUI</title> |
5 | + | ||
6 | + <script src="libs/d3.min.js"></script> | ||
7 | + <script src="libs/jquery-2.1.1.min.js"></script> | ||
5 | </head> | 8 | </head> |
6 | <body> | 9 | <body> |
7 | <h1>ONOS GUI</h1> | 10 | <h1>ONOS GUI</h1> | ... | ... |
-
Please register or login to post a comment