blob: d92719c87647e1a08605ba0133967bfc2067f0a3 [file] [log] [blame]
Brian O'Connorc67f9fa2014-08-07 18:17:46 -07001package net.floodlightcontroller.core.internal;
2
3/**
4 * Copyright 2012, Big Switch Networks, Inc.
5 * Originally created by David Erickson, Stanford University
6 *
7 * Licensed under the Apache License, Version 2.0 (the "License"); you may
8 * not use this file except in compliance with the License. You may obtain
9 * a copy of the License at
10 *
11 * http://www.apache.org/licenses/LICENSE-2.0
12 *
13 * Unless required by applicable law or agreed to in writing, software
14 * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
15 * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
16 * License for the specific language governing permissions and limitations
17 * under the License.
18 **/
19
20import java.io.IOException;
Fahad Naeem Khan1f8fb2e2014-09-25 16:39:18 -070021import java.net.SocketAddress;
Brian O'Connorc67f9fa2014-08-07 18:17:46 -070022import java.util.ArrayList;
23import java.util.Collection;
24import java.util.Collections;
25import java.util.Date;
26import java.util.HashMap;
27import java.util.LinkedList;
28import java.util.List;
29import java.util.Map;
30import java.util.Set;
31import java.util.WeakHashMap;
32import java.util.concurrent.ConcurrentHashMap;
33import java.util.concurrent.ConcurrentMap;
34import java.util.concurrent.Future;
35import java.util.concurrent.atomic.AtomicInteger;
36import java.util.concurrent.locks.Lock;
37import java.util.concurrent.locks.ReentrantReadWriteLock;
38
39import net.floodlightcontroller.core.FloodlightContext;
40import net.floodlightcontroller.core.IFloodlightProviderService;
41import net.floodlightcontroller.core.IFloodlightProviderService.Role;
42import net.floodlightcontroller.core.IOFMessageListener;
43import net.floodlightcontroller.core.IOFSwitch;
44import net.floodlightcontroller.core.SwitchDriverSubHandshakeAlreadyStarted;
45import net.floodlightcontroller.core.SwitchDriverSubHandshakeCompleted;
46import net.floodlightcontroller.core.SwitchDriverSubHandshakeNotStarted;
47import net.floodlightcontroller.core.annotations.LogMessageDoc;
48import net.floodlightcontroller.core.web.serializers.DPIDSerializer;
49import net.floodlightcontroller.debugcounter.IDebugCounter;
50import net.floodlightcontroller.debugcounter.IDebugCounterService;
51import net.floodlightcontroller.debugcounter.IDebugCounterService.CounterException;
52import net.floodlightcontroller.debugcounter.IDebugCounterService.CounterType;
53import net.floodlightcontroller.debugcounter.NullDebugCounter;
54import net.floodlightcontroller.threadpool.IThreadPoolService;
55import net.floodlightcontroller.util.LinkedHashSetWrapper;
56import net.floodlightcontroller.util.OrderedCollection;
Brian O'Connorc67f9fa2014-08-07 18:17:46 -070057
58import org.codehaus.jackson.annotate.JsonIgnore;
59import org.codehaus.jackson.annotate.JsonProperty;
60import org.codehaus.jackson.map.annotate.JsonSerialize;
61import org.jboss.netty.channel.Channel;
62import org.projectfloodlight.openflow.protocol.OFActionType;
63import org.projectfloodlight.openflow.protocol.OFCapabilities;
64import org.projectfloodlight.openflow.protocol.OFDescStatsReply;
Jonathan Harta213bce2014-08-11 15:44:07 -070065import org.projectfloodlight.openflow.protocol.OFFactories;
66import org.projectfloodlight.openflow.protocol.OFFactory;
Brian O'Connorc67f9fa2014-08-07 18:17:46 -070067import org.projectfloodlight.openflow.protocol.OFFeaturesReply;
Jonathan Hart23daf192014-08-22 10:56:58 -070068import org.projectfloodlight.openflow.protocol.OFFlowDelete.Builder;
Brian O'Connorc67f9fa2014-08-07 18:17:46 -070069import org.projectfloodlight.openflow.protocol.OFMessage;
70import org.projectfloodlight.openflow.protocol.OFPortConfig;
71import org.projectfloodlight.openflow.protocol.OFPortDesc;
72import org.projectfloodlight.openflow.protocol.OFPortDescStatsReply;
73import org.projectfloodlight.openflow.protocol.OFPortReason;
74import org.projectfloodlight.openflow.protocol.OFPortState;
75import org.projectfloodlight.openflow.protocol.OFPortStatus;
76import org.projectfloodlight.openflow.protocol.OFStatsReply;
77import org.projectfloodlight.openflow.protocol.OFStatsRequest;
78import org.projectfloodlight.openflow.protocol.OFType;
79import org.projectfloodlight.openflow.protocol.OFVersion;
80import org.projectfloodlight.openflow.types.DatapathId;
81import org.projectfloodlight.openflow.types.OFAuxId;
82import org.projectfloodlight.openflow.types.OFGroup;
83import org.projectfloodlight.openflow.types.OFPort;
84import org.projectfloodlight.openflow.types.TableId;
85import org.projectfloodlight.openflow.types.U64;
86import org.slf4j.Logger;
87import org.slf4j.LoggerFactory;
88
Brian O'Connorc67f9fa2014-08-07 18:17:46 -070089/**
90 * This is the internal representation of an openflow switch.
91 */
92public class OFSwitchImplBase implements IOFSwitch {
93 // TODO: should we really do logging in the class or should we throw
94 // exception that can then be handled by callers?
95 protected final static Logger log = LoggerFactory.getLogger(OFSwitchImplBase.class);
96
97 private static final String HA_CHECK_SWITCH =
98 "Check the health of the indicated switch. If the problem " +
99 "persists or occurs repeatedly, it likely indicates a defect " +
100 "in the switch HA implementation.";
101
102 protected ConcurrentMap<Object, Object> attributes;
103 protected IFloodlightProviderService floodlightProvider;
104 protected IThreadPoolService threadPool;
105 protected Date connectedSince;
106 protected String stringId;
107 protected Channel channel;
108 // transaction id used for messages sent out to this switch from
109 // this controller instance. This xid has significance only between this
110 // controller<->switch pair.
111 protected AtomicInteger transactionIdSource;
112
113 // generation id used for roleRequest messages sent to switches (see section
114 // 6.3.5 of the OF1.3.4 spec). This generationId has significance between
115 // all the controllers that this switch is connected to; and only for role
116 // request messages with role MASTER or SLAVE. The set of Controllers that
117 // this switch is connected to should coordinate the next generation id,
118 // via transactional semantics.
119 protected long generationIdSource;
120
121 // Lock to protect modification of the port maps. We only need to
122 // synchronize on modifications. For read operations we are fine since
123 // we rely on ConcurrentMaps which works for our use case.
Saurav Dasfcdad072014-08-13 14:15:21 -0700124 // private Object portLock; XXX S remove this
Brian O'Connorc67f9fa2014-08-07 18:17:46 -0700125
126 // Map port numbers to the appropriate OFPortDesc
127 protected ConcurrentHashMap<Integer, OFPortDesc> portsByNumber;
128 // Map port names to the appropriate OFPhyiscalPort
129 // XXX: The OF spec doesn't specify if port names need to be unique but
Saurav Dasfcdad072014-08-13 14:15:21 -0700130 // according it's always the case in practice.
Brian O'Connorc67f9fa2014-08-07 18:17:46 -0700131 protected ConcurrentHashMap<String, OFPortDesc> portsByName;
132 protected Map<Integer, OFStatisticsFuture> statsFutureMap;
Saurav Dasfcdad072014-08-13 14:15:21 -0700133 // XXX Consider removing the following 2 maps - not used anymore
134 protected Map<Integer, IOFMessageListener> iofMsgListenersMap;
Brian O'Connorc67f9fa2014-08-07 18:17:46 -0700135 protected Map<Integer, OFFeaturesReplyFuture> featuresFutureMap;
136 protected boolean connected;
137 protected Role role;
Brian O'Connorc67f9fa2014-08-07 18:17:46 -0700138 protected ReentrantReadWriteLock listenerLock;
139 protected ConcurrentMap<Short, Long> portBroadcastCacheHitMap;
140 /**
Saurav Dasfcdad072014-08-13 14:15:21 -0700141 * When sending a role request message, the role request is added to this
142 * queue. If a role reply is received this queue is checked to verify that
143 * the reply matches the expected reply. We require in order delivery of
144 * replies. That's why we use a Queue. The RoleChanger uses a timeout to
145 * ensure we receive a timely reply.
Brian O'Connorc67f9fa2014-08-07 18:17:46 -0700146 * <p/>
147 * Need to synchronize on this instance if a request is sent, received,
148 * checked.
149 */
150 protected LinkedList<PendingRoleRequestEntry> pendingRoleRequests;
151
152 /** OpenFlow version for this switch */
153 protected OFVersion ofversion;
154 // Description stats reply describing this switch
155 private OFDescStatsReply switchDescription;
156 // Switch features from initial featuresReply
157 protected Set<OFCapabilities> capabilities;
158 protected int buffers;
159 protected Set<OFActionType> actions;
160 protected byte tables;
161 protected DatapathId datapathId;
Saurav Dasfcdad072014-08-13 14:15:21 -0700162 private OFAuxId auxId;
Brian O'Connorc67f9fa2014-08-07 18:17:46 -0700163
164 private IDebugCounterService debugCounters;
Saurav Dasfcdad072014-08-13 14:15:21 -0700165 private boolean debugCountersRegistered;
166 @SuppressWarnings("unused")
Brian O'Connorc67f9fa2014-08-07 18:17:46 -0700167 private IDebugCounter ctrSwitch, ctrSwitchPktin, ctrSwitchWrite,
Saurav Dasfcdad072014-08-13 14:15:21 -0700168 ctrSwitchPktinDrops, ctrSwitchWriteDrops;
Brian O'Connorc67f9fa2014-08-07 18:17:46 -0700169
Saurav Dasfcdad072014-08-13 14:15:21 -0700170 protected boolean startDriverHandshakeCalled = false;
171 private boolean flowTableFull = false;
Brian O'Connorc67f9fa2014-08-07 18:17:46 -0700172
Saurav Dasfcdad072014-08-13 14:15:21 -0700173 private final PortManager portManager;
Brian O'Connorc67f9fa2014-08-07 18:17:46 -0700174
175 protected static final ThreadLocal<Map<OFSwitchImplBase, List<OFMessage>>> local_msg_buffer =
Saurav Dasfcdad072014-08-13 14:15:21 -0700176 new ThreadLocal<Map<OFSwitchImplBase, List<OFMessage>>>() {
177 @Override
178 protected Map<OFSwitchImplBase, List<OFMessage>> initialValue() {
179 return new WeakHashMap<OFSwitchImplBase, List<OFMessage>>();
180 }
181 };
Brian O'Connorc67f9fa2014-08-07 18:17:46 -0700182
Saurav Dasfcdad072014-08-13 14:15:21 -0700183 private static final String BASE = "switchbase";
Brian O'Connorc67f9fa2014-08-07 18:17:46 -0700184
185 protected static class PendingRoleRequestEntry {
186 protected int xid;
187 protected Role role;
188 // cookie is used to identify the role "generation". roleChanger uses
189 protected long cookie;
190
191 public PendingRoleRequestEntry(int xid, Role role, long cookie) {
192 this.xid = xid;
193 this.role = role;
194 this.cookie = cookie;
195 }
196 }
197
198 public OFSwitchImplBase() {
199 this.stringId = null;
200 this.attributes = new ConcurrentHashMap<Object, Object>();
201 this.connectedSince = new Date();
202 this.transactionIdSource = new AtomicInteger();
Saurav Dasfcdad072014-08-13 14:15:21 -0700203 this.generationIdSource = 0; // XXX S this is wrong; should be
204 // negotiated
Brian O'Connorc67f9fa2014-08-07 18:17:46 -0700205 // XXX S no need this.portLock = new Object();
206 this.portsByNumber = new ConcurrentHashMap<Integer, OFPortDesc>();
207 this.portsByName = new ConcurrentHashMap<String, OFPortDesc>();
208 this.connected = true;
209 this.statsFutureMap = new ConcurrentHashMap<Integer, OFStatisticsFuture>();
210 this.featuresFutureMap = new ConcurrentHashMap<Integer, OFFeaturesReplyFuture>();
211 this.iofMsgListenersMap = new ConcurrentHashMap<Integer, IOFMessageListener>();
212 this.role = null;
Brian O'Connorc67f9fa2014-08-07 18:17:46 -0700213 this.listenerLock = new ReentrantReadWriteLock();
214 this.pendingRoleRequests = new LinkedList<OFSwitchImplBase.PendingRoleRequestEntry>();
215 this.portManager = new PortManager();
216 // by default the base impl declares no support for Nx_role_requests.
217 // OF1.0 switches like OVS that do support these messages should set the
218 // attribute in the associated switch driver.
219 setAttribute(SWITCH_SUPPORTS_NX_ROLE, false);
220
221 }
222
Saurav Dasfcdad072014-08-13 14:15:21 -0700223 // *******************************************
224 // Setters and Getters
225 // *******************************************
Brian O'Connorc67f9fa2014-08-07 18:17:46 -0700226
227 @Override
228 public Object getAttribute(String name) {
229 if (this.attributes.containsKey(name)) {
230 return this.attributes.get(name);
231 }
232 return null;
233 }
234
235 @Override
236 public ConcurrentMap<Object, Object> getAttributes() {
237 return this.attributes;
238 }
239
240 @Override
241 public void setAttribute(String name, Object value) {
242 this.attributes.put(name, value);
243 return;
244 }
245
246 @Override
247 public Object removeAttribute(String name) {
248 return this.attributes.remove(name);
249 }
250
251 @Override
252 public boolean hasAttribute(String name) {
253 return this.attributes.containsKey(name);
254 }
255
256 @Override
257 @JsonSerialize(using = DPIDSerializer.class)
258 @JsonProperty("dpid")
259 public long getId() {
260 if (this.stringId == null)
261 throw new RuntimeException("Features reply has not yet been set");
262 return this.datapathId.getLong();
263 }
264
265 @JsonIgnore
266 @Override
267 public String getStringId() {
268 return stringId;
269 }
270
Jonathan Harta213bce2014-08-11 15:44:07 -0700271 @Override
272 public OFFactory getFactory() {
273 return OFFactories.getFactory(ofversion);
Brian O'Connorc67f9fa2014-08-07 18:17:46 -0700274 }
275
Jonathan Harta213bce2014-08-11 15:44:07 -0700276 @Override
277 public OFVersion getOFVersion() {
278 return ofversion;
279 }
280
281 @Override
Brian O'Connorc67f9fa2014-08-07 18:17:46 -0700282 public void setOFVersion(OFVersion ofv) {
Jonathan Harta213bce2014-08-11 15:44:07 -0700283 ofversion = ofv;
Brian O'Connorc67f9fa2014-08-07 18:17:46 -0700284 }
285
286 /**
287 * @param floodlightProvider the floodlightProvider to set
288 */
289 @JsonIgnore
290 @Override
291 public void setFloodlightProvider(IFloodlightProviderService floodlightProvider) {
292 this.floodlightProvider = floodlightProvider;
293 }
294
295 @JsonIgnore
296 @Override
297 public void setThreadPoolService(IThreadPoolService tp) {
298 this.threadPool = tp;
299 }
300
301 @Override
302 @JsonIgnore
303 public void setDebugCounterService(IDebugCounterService debugCounters)
304 throws CounterException {
305 this.debugCounters = debugCounters;
306 registerOverloadCounters();
307 }
308
309 /* (non-Javadoc)
310 * @see java.lang.Object#toString()
311 */
312 @Override
313 public String toString() {
314 return "OFSwitchImpl [" + ((channel != null) ? channel.getRemoteAddress() : "?")
Saurav Dasfcdad072014-08-13 14:15:21 -0700315 + " DPID[" + ((stringId != null) ? stringId : "?") + "]]";
Brian O'Connorc67f9fa2014-08-07 18:17:46 -0700316 }
317
Saurav Dasfcdad072014-08-13 14:15:21 -0700318 // *******************************************
319 // Channel related methods
320 // *******************************************
Brian O'Connorc67f9fa2014-08-07 18:17:46 -0700321
322 @JsonIgnore
323 @Override
324 public void setChannel(Channel channel) {
325 this.channel = channel;
326 }
Fahad Naeem Khan1f8fb2e2014-09-25 16:39:18 -0700327 @Override
328 public SocketAddress getChannelSocketAddress(){
329 return channel.getRemoteAddress();
330 }
Brian O'Connorc67f9fa2014-08-07 18:17:46 -0700331
332 @Override
333 public void write(OFMessage m, FloodlightContext bc) throws IOException {
334 Map<OFSwitchImplBase, List<OFMessage>> msg_buffer_map = local_msg_buffer.get();
335 List<OFMessage> msg_buffer = msg_buffer_map.get(this);
336 if (msg_buffer == null) {
337 msg_buffer = new ArrayList<OFMessage>();
338 msg_buffer_map.put(this, msg_buffer);
339 }
340 // XXX S will change when iFloodlight provider changes
Saurav Dasfcdad072014-08-13 14:15:21 -0700341 // this.floodlightProvider.handleOutgoingMessage(this, m, bc);
Brian O'Connorc67f9fa2014-08-07 18:17:46 -0700342 msg_buffer.add(m);
343
344 if ((msg_buffer.size() >= Controller.BATCH_MAX_SIZE) ||
345 ((m.getType() != OFType.PACKET_OUT) && (m.getType() != OFType.FLOW_MOD))) {
346 this.write(msg_buffer);
347 msg_buffer.clear();
348 }
349 }
350
351 @Override
352 @LogMessageDoc(level = "WARN",
353 message = "Sending OF message that modifies switch " +
354 "state while in the slave role: {switch}",
355 explanation = "An application has sent a message to a switch " +
356 "that is not valid when the switch is in a slave role",
357 recommendation = LogMessageDoc.REPORT_CONTROLLER_BUG)
358 public void write(List<OFMessage> msglist,
Saurav Dasfcdad072014-08-13 14:15:21 -0700359 FloodlightContext bc) throws IOException {
Brian O'Connorc67f9fa2014-08-07 18:17:46 -0700360 for (OFMessage m : msglist) {
361 if (role == Role.SLAVE) {
362 switch (m.getType()) {
Saurav Dasfcdad072014-08-13 14:15:21 -0700363 case PACKET_OUT:
364 case FLOW_MOD:
365 case PORT_MOD:
366 log.warn("Sending OF message that modifies switch " +
367 "state while in the slave role: {}",
368 m.getType().name());
369 break;
370 default:
371 break;
Brian O'Connorc67f9fa2014-08-07 18:17:46 -0700372 }
373 }
374 // XXX S again
Saurav Dasfcdad072014-08-13 14:15:21 -0700375 // this.floodlightProvider.handleOutgoingMessage(this, m, bc);
Brian O'Connorc67f9fa2014-08-07 18:17:46 -0700376 }
377 this.write(msglist);
378 }
379
Saurav Das0a344b02014-09-26 14:18:52 -0700380 protected void write(List<OFMessage> msglist) throws IOException {
Brian O'Connorc67f9fa2014-08-07 18:17:46 -0700381 this.channel.write(msglist);
382 }
383
384 @Override
385 public void disconnectSwitch() {
386 channel.close();
387 }
388
389 @Override
390 public Date getConnectedSince() {
391 return connectedSince;
392 }
393
394 @JsonIgnore
395 @Override
396 public int getNextTransactionId() {
397 return this.transactionIdSource.incrementAndGet();
398 }
399
400 @JsonIgnore
401 @Override
402 public synchronized boolean isConnected() {
403 return connected;
404 }
405
406 @Override
407 @JsonIgnore
408 public synchronized void setConnected(boolean connected) {
409 this.connected = connected;
410 }
411
Saurav Dasfcdad072014-08-13 14:15:21 -0700412 // *******************************************
413 // Switch features related methods
414 // *******************************************
Brian O'Connorc67f9fa2014-08-07 18:17:46 -0700415
416 /**
417 * Set the features reply for this switch from the handshake
418 */
419 protected void setFeaturesReply(OFFeaturesReply featuresReply) {
Saurav Dasfcdad072014-08-13 14:15:21 -0700420 if (featuresReply == null) {
421 log.error("Error setting featuresReply for switch: {}", getStringId());
422 return;
423 }
424 this.datapathId = featuresReply.getDatapathId();
425 this.capabilities = featuresReply.getCapabilities();
426 this.buffers = (int) featuresReply.getNBuffers();
427 this.tables = (byte) featuresReply.getNTables();
428 this.stringId = this.datapathId.toString();
429 if (ofversion == OFVersion.OF_13) {
430 auxId = featuresReply.getAuxiliaryId();
431 if (!auxId.equals(OFAuxId.MAIN)) {
432 log.warn("This controller does not handle auxiliary connections. "
433 + "Aux connection id {} received from switch {}",
434 auxId, getStringId());
435 }
436 }
Brian O'Connorc67f9fa2014-08-07 18:17:46 -0700437
Saurav Dasfcdad072014-08-13 14:15:21 -0700438 if (ofversion == OFVersion.OF_10) {
439 this.actions = featuresReply.getActions();
440 portManager.compareAndUpdatePorts(featuresReply.getPorts(), true);
441 }
Brian O'Connorc67f9fa2014-08-07 18:17:46 -0700442 }
443
444 /**
Saurav Dasfcdad072014-08-13 14:15:21 -0700445 * Set the port descriptions for this switch from the handshake for an OF1.3
446 * switch.
Brian O'Connorc67f9fa2014-08-07 18:17:46 -0700447 */
448 protected void setPortDescReply(OFPortDescStatsReply pdrep) {
Saurav Dasfcdad072014-08-13 14:15:21 -0700449 if (ofversion != OFVersion.OF_13)
450 return;
451 if (pdrep == null) {
452 log.error("Error setting ports description for switch: {}", getStringId());
453 return;
454 }
455 portManager.updatePorts(pdrep.getEntries());
Brian O'Connorc67f9fa2014-08-07 18:17:46 -0700456 }
457
458 @Override
459 public int getNumBuffers() {
460 return buffers;
461 }
Saurav Dasfcdad072014-08-13 14:15:21 -0700462
Brian O'Connorc67f9fa2014-08-07 18:17:46 -0700463 @Override
464 public Set<OFActionType> getActions() {
465 return actions;
466 }
Saurav Dasfcdad072014-08-13 14:15:21 -0700467
Brian O'Connorc67f9fa2014-08-07 18:17:46 -0700468 @Override
Saurav Dasfcdad072014-08-13 14:15:21 -0700469 public Set<OFCapabilities> getCapabilities() {
Brian O'Connorc67f9fa2014-08-07 18:17:46 -0700470 return capabilities;
471 }
Saurav Dasfcdad072014-08-13 14:15:21 -0700472
Brian O'Connorc67f9fa2014-08-07 18:17:46 -0700473 @Override
474 public byte getNumTables() {
475 return tables;
476 }
477
Saurav Dasfcdad072014-08-13 14:15:21 -0700478 // public Future<OFFeaturesReply> getFeaturesReplyFromSwitch()
479 // throws IOException {
480 // // XXX S fix this later
481 // OFMessage request = floodlightProvider.getOFMessageFactory_13()
482 // .buildFeaturesRequest()
483 // .setXid(getNextTransactionId())
484 // .build();
485 // OFFeaturesReplyFuture future =
486 // new OFFeaturesReplyFuture(threadPool, this, (int) request.getXid());
487 // this.featuresFutureMap.put((int) request.getXid(), future);
488 // this.channel.write(Collections.singletonList(request));
489 // return future;
490 //
491 // }
492 //
493 // public void deliverOFFeaturesReply(OFMessage reply) {
494 // OFFeaturesReplyFuture future =
495 // this.featuresFutureMap.get(reply.getXid());
496 // if (future != null) {
497 // future.deliverFuture(this, reply);
498 // // The future will ultimately unregister itself and call
499 // // cancelFeaturesReply
500 // return;
501 // }
502 // log.error("Switch {}: received unexpected featureReply", this);
503 // }
Brian O'Connorc67f9fa2014-08-07 18:17:46 -0700504
505 @Override
506 public void cancelFeaturesReply(int transactionId) {
507 this.featuresFutureMap.remove(transactionId);
508 }
509
510 @JsonIgnore
511 public void setSwitchDescription(OFDescStatsReply desc) {
Saurav Dasfcdad072014-08-13 14:15:21 -0700512 switchDescription = desc;
Brian O'Connorc67f9fa2014-08-07 18:17:46 -0700513 }
514
515 @Override
516 @JsonIgnore
517 public OFDescStatsReply getSwitchDescription() {
Saurav Dasfcdad072014-08-13 14:15:21 -0700518 return switchDescription;
Brian O'Connorc67f9fa2014-08-07 18:17:46 -0700519 }
520
Saurav Dasfcdad072014-08-13 14:15:21 -0700521 // *******************************************
522 // Switch port handling
523 // *******************************************
Brian O'Connorc67f9fa2014-08-07 18:17:46 -0700524
525 @Override
526 @JsonIgnore
527 public Collection<OFPortDesc> getEnabledPorts() {
528 return portManager.getEnabledPorts();
529 }
530
531 @Override
532 @JsonIgnore
533 public Collection<Integer> getEnabledPortNumbers() {
534 return portManager.getEnabledPortNumbers();
535 }
536
537 @Override
538 public OFPortDesc getPort(int portNumber) {
539 return portManager.getPort(portNumber);
540 }
541
542 @Override
543 public OFPortDesc getPort(String portName) {
544 return portManager.getPort(portName);
545 }
546
547 @Override
548 @JsonIgnore
549 public OrderedCollection<PortChangeEvent> processOFPortStatus(OFPortStatus ps) {
550 return portManager.handlePortStatusMessage(ps);
551 }
552
553 @Override
554 @JsonProperty("ports")
555 public Collection<OFPortDesc> getPorts() {
Saurav Dasfcdad072014-08-13 14:15:21 -0700556 return portManager.getPorts();
Brian O'Connorc67f9fa2014-08-07 18:17:46 -0700557 }
558
559 @Override
560 public boolean portEnabled(int portNumber) {
561 return isEnabled(portManager.getPort(portNumber));
562 }
563
564 @Override
565 public boolean portEnabled(String portName) {
Saurav Dasfcdad072014-08-13 14:15:21 -0700566 return isEnabled(portManager.getPort(portName));
Brian O'Connorc67f9fa2014-08-07 18:17:46 -0700567 }
568
569 private boolean isEnabled(OFPortDesc p) {
Saurav Dasfcdad072014-08-13 14:15:21 -0700570 return (p != null &&
571 !p.getState().contains(OFPortState.LINK_DOWN) &&
572 !p.getState().contains(OFPortState.BLOCKED) && !p.getConfig().contains(
573 OFPortConfig.PORT_DOWN));
Brian O'Connorc67f9fa2014-08-07 18:17:46 -0700574 }
575
576 @Override
577 public OrderedCollection<PortChangeEvent> comparePorts(Collection<OFPortDesc> ports) {
578 return portManager.comparePorts(ports);
579 }
580
581 @Override
582 @JsonIgnore
583 public OrderedCollection<PortChangeEvent> setPorts(Collection<OFPortDesc> ports) {
584 return portManager.updatePorts(ports);
585 }
586
587 /**
588 * Manages the ports of this switch.
Jonathan Hart23daf192014-08-22 10:56:58 -0700589 *
Brian O'Connorc67f9fa2014-08-07 18:17:46 -0700590 * Provides methods to query and update the stored ports. The class ensures
Saurav Dasfcdad072014-08-13 14:15:21 -0700591 * that every port name and port number is unique. When updating ports the
592 * class checks if port number <-> port name mappings have change due to the
593 * update. If a new port P has number and port that are inconsistent with
594 * the previous mapping(s) the class will delete all previous ports with
595 * name or number of the new port and then add the new port.
Jonathan Hart23daf192014-08-22 10:56:58 -0700596 *
Brian O'Connorc67f9fa2014-08-07 18:17:46 -0700597 * Port names are stored as-is but they are compared case-insensitive
Jonathan Hart23daf192014-08-22 10:56:58 -0700598 *
Brian O'Connorc67f9fa2014-08-07 18:17:46 -0700599 * The methods that change the stored ports return a list of
Saurav Dasfcdad072014-08-13 14:15:21 -0700600 * PortChangeEvents that represent the changes that have been applied to the
601 * port list so that IOFSwitchListeners can be notified about the changes.
Jonathan Hart23daf192014-08-22 10:56:58 -0700602 *
Saurav Dasfcdad072014-08-13 14:15:21 -0700603 * Implementation notes: - We keep several different representations of the
604 * ports to allow for fast lookups - Ports are stored in unchangeable lists.
605 * When a port is modified new data structures are allocated. - We use a
606 * read-write-lock for synchronization, so multiple readers are allowed. -
607 * All port numbers have int representation (no more shorts)
Brian O'Connorc67f9fa2014-08-07 18:17:46 -0700608 */
609 protected class PortManager {
610 private final ReentrantReadWriteLock lock;
611 private List<OFPortDesc> portList;
612 private List<OFPortDesc> enabledPortList;
613 private List<Integer> enabledPortNumbers;
Saurav Dasfcdad072014-08-13 14:15:21 -0700614 private Map<Integer, OFPortDesc> portsByNumber;
615 private Map<String, OFPortDesc> portsByName;
Brian O'Connorc67f9fa2014-08-07 18:17:46 -0700616
617 public PortManager() {
618 this.lock = new ReentrantReadWriteLock();
619 this.portList = Collections.emptyList();
620 this.enabledPortList = Collections.emptyList();
621 this.enabledPortNumbers = Collections.emptyList();
622 this.portsByName = Collections.emptyMap();
623 this.portsByNumber = Collections.emptyMap();
624 }
625
626 /**
Saurav Dasfcdad072014-08-13 14:15:21 -0700627 * Set the internal data structure storing this switch's port to the
628 * ports specified by newPortsByNumber
Jonathan Hart23daf192014-08-22 10:56:58 -0700629 *
Brian O'Connorc67f9fa2014-08-07 18:17:46 -0700630 * CALLER MUST HOLD WRITELOCK
Jonathan Hart23daf192014-08-22 10:56:58 -0700631 *
Brian O'Connorc67f9fa2014-08-07 18:17:46 -0700632 * @param newPortsByNumber
633 * @throws IllegaalStateException if called without holding the
Saurav Dasfcdad072014-08-13 14:15:21 -0700634 * writelock
Brian O'Connorc67f9fa2014-08-07 18:17:46 -0700635 */
636 private void updatePortsWithNewPortsByNumber(
Saurav Dasfcdad072014-08-13 14:15:21 -0700637 Map<Integer, OFPortDesc> newPortsByNumber) {
Brian O'Connorc67f9fa2014-08-07 18:17:46 -0700638 if (!lock.writeLock().isHeldByCurrentThread()) {
639 throw new IllegalStateException("Method called without " +
Saurav Dasfcdad072014-08-13 14:15:21 -0700640 "holding writeLock");
Brian O'Connorc67f9fa2014-08-07 18:17:46 -0700641 }
Saurav Dasfcdad072014-08-13 14:15:21 -0700642 Map<String, OFPortDesc> newPortsByName =
Brian O'Connorc67f9fa2014-08-07 18:17:46 -0700643 new HashMap<String, OFPortDesc>();
644 List<OFPortDesc> newPortList =
645 new ArrayList<OFPortDesc>();
646 List<OFPortDesc> newEnabledPortList =
647 new ArrayList<OFPortDesc>();
648 List<Integer> newEnabledPortNumbers = new ArrayList<Integer>();
649
Saurav Dasfcdad072014-08-13 14:15:21 -0700650 for (OFPortDesc p : newPortsByNumber.values()) {
Brian O'Connorc67f9fa2014-08-07 18:17:46 -0700651 newPortList.add(p);
652 newPortsByName.put(p.getName().toLowerCase(), p);
653 if (isEnabled(p)) {
654 newEnabledPortList.add(p);
655 newEnabledPortNumbers.add(p.getPortNo().getPortNumber());
656 }
657 }
658 portsByName = Collections.unmodifiableMap(newPortsByName);
659 portsByNumber =
660 Collections.unmodifiableMap(newPortsByNumber);
661 enabledPortList =
662 Collections.unmodifiableList(newEnabledPortList);
663 enabledPortNumbers =
664 Collections.unmodifiableList(newEnabledPortNumbers);
665 portList = Collections.unmodifiableList(newPortList);
666 }
667
668 /**
Saurav Dasfcdad072014-08-13 14:15:21 -0700669 * Handle a OFPortStatus delete message for the given port. Updates the
670 * internal port maps/lists of this switch and returns the
671 * PortChangeEvents caused by the delete. If the given port exists as
672 * it, it will be deleted. If the name<->number for the given port is
673 * inconsistent with the ports stored by this switch the method will
674 * delete all ports with the number or name of the given port.
Jonathan Hart23daf192014-08-22 10:56:58 -0700675 *
Brian O'Connorc67f9fa2014-08-07 18:17:46 -0700676 * This method will increment error/warn counters and log
Jonathan Hart23daf192014-08-22 10:56:58 -0700677 *
Saurav Dasfcdad072014-08-13 14:15:21 -0700678 * @param delPort the port from the port status message that should be
679 * deleted.
Brian O'Connorc67f9fa2014-08-07 18:17:46 -0700680 * @return ordered collection of port changes applied to this switch
681 */
682 private OrderedCollection<PortChangeEvent> handlePortStatusDelete(
Saurav Dasfcdad072014-08-13 14:15:21 -0700683 OFPortDesc delPort) {
Brian O'Connorc67f9fa2014-08-07 18:17:46 -0700684 lock.writeLock().lock();
685 OrderedCollection<PortChangeEvent> events =
686 new LinkedHashSetWrapper<PortChangeEvent>();
687 try {
Saurav Dasfcdad072014-08-13 14:15:21 -0700688 Map<Integer, OFPortDesc> newPortByNumber =
Brian O'Connorc67f9fa2014-08-07 18:17:46 -0700689 new HashMap<Integer, OFPortDesc>(portsByNumber);
690 OFPortDesc prevPort =
691 portsByNumber.get(delPort.getPortNo().getPortNumber());
692 if (prevPort == null) {
693 // so such port. Do we have a port with the name?
694 prevPort = portsByName.get(delPort.getName());
695 if (prevPort != null) {
696 newPortByNumber.remove(prevPort.getPortNo().getPortNumber());
697 events.add(new PortChangeEvent(prevPort,
Saurav Dasfcdad072014-08-13 14:15:21 -0700698 PortChangeType.DELETE));
Brian O'Connorc67f9fa2014-08-07 18:17:46 -0700699 }
700 } else if (prevPort.getName().equals(delPort.getName())) {
701 // port exists with consistent name-number mapping
702 newPortByNumber.remove(delPort.getPortNo().getPortNumber());
703 events.add(new PortChangeEvent(delPort,
Saurav Dasfcdad072014-08-13 14:15:21 -0700704 PortChangeType.DELETE));
Brian O'Connorc67f9fa2014-08-07 18:17:46 -0700705 } else {
706 // port with same number exists but its name differs. This
707 // is weird. The best we can do is to delete the existing
708 // port(s) that have delPort's name and number.
709 newPortByNumber.remove(delPort.getPortNo().getPortNumber());
710 events.add(new PortChangeEvent(prevPort,
Saurav Dasfcdad072014-08-13 14:15:21 -0700711 PortChangeType.DELETE));
Brian O'Connorc67f9fa2014-08-07 18:17:46 -0700712 // is there another port that has delPort's name?
713 prevPort = portsByName.get(delPort.getName().toLowerCase());
714 if (prevPort != null) {
715 newPortByNumber.remove(prevPort.getPortNo().getPortNumber());
716 events.add(new PortChangeEvent(prevPort,
Saurav Dasfcdad072014-08-13 14:15:21 -0700717 PortChangeType.DELETE));
Brian O'Connorc67f9fa2014-08-07 18:17:46 -0700718 }
719 }
720 updatePortsWithNewPortsByNumber(newPortByNumber);
721 return events;
722 } finally {
723 lock.writeLock().unlock();
724 }
725 }
726
727 /**
728 * Handle a OFPortStatus message, update the internal data structures
729 * that store ports and return the list of OFChangeEvents.
Jonathan Hart23daf192014-08-22 10:56:58 -0700730 *
Brian O'Connorc67f9fa2014-08-07 18:17:46 -0700731 * This method will increment error/warn counters and log
Jonathan Hart23daf192014-08-22 10:56:58 -0700732 *
Brian O'Connorc67f9fa2014-08-07 18:17:46 -0700733 * @param ps
734 * @return
735 */
736 public OrderedCollection<PortChangeEvent> handlePortStatusMessage(OFPortStatus ps) {
737 if (ps == null) {
738 throw new NullPointerException("OFPortStatus message must " +
Saurav Dasfcdad072014-08-13 14:15:21 -0700739 "not be null");
Brian O'Connorc67f9fa2014-08-07 18:17:46 -0700740 }
741 lock.writeLock().lock();
742 try {
743 OFPortReason reason = ps.getReason();
744 if (reason == null) {
745 throw new IllegalArgumentException("Unknown PortStatus " +
746 "reason code " + ps.getReason());
747 }
748
749 if (log.isDebugEnabled()) {
750 log.debug("Handling OFPortStatus: {} for {}",
Saurav Dasfcdad072014-08-13 14:15:21 -0700751 reason, ps);
Brian O'Connorc67f9fa2014-08-07 18:17:46 -0700752 }
753
754 if (reason == OFPortReason.DELETE)
Saurav Dasfcdad072014-08-13 14:15:21 -0700755 return handlePortStatusDelete(ps.getDesc());
Brian O'Connorc67f9fa2014-08-07 18:17:46 -0700756
757 // We handle ADD and MODIFY the same way. Since OpenFlow
758 // doesn't specify what uniquely identifies a port the
759 // notion of ADD vs. MODIFY can also be hazy. So we just
760 // compare the new port to the existing ones.
Saurav Dasfcdad072014-08-13 14:15:21 -0700761 Map<Integer, OFPortDesc> newPortByNumber =
762 new HashMap<Integer, OFPortDesc>(portsByNumber);
Brian O'Connorc67f9fa2014-08-07 18:17:46 -0700763 OrderedCollection<PortChangeEvent> events =
Saurav Dasfcdad072014-08-13 14:15:21 -0700764 getSinglePortChanges(ps.getDesc());
765 for (PortChangeEvent e : events) {
766 switch (e.type) {
767 case DELETE:
768 newPortByNumber.remove(e.port.getPortNo().getPortNumber());
769 break;
770 case ADD:
771 if (reason != OFPortReason.ADD) {
772 // weird case
773 }
774 newPortByNumber.put(e.port.getPortNo().getPortNumber(),
775 e.port);
776 break;
777 case DOWN:
778 newPortByNumber.put(e.port.getPortNo().getPortNumber(),
779 e.port);
780 break;
781 case OTHER_UPDATE:
782 newPortByNumber.put(e.port.getPortNo().getPortNumber(),
783 e.port);
784 break;
785 case UP:
786 newPortByNumber.put(e.port.getPortNo().getPortNumber(),
787 e.port);
788 break;
Brian O'Connorc67f9fa2014-08-07 18:17:46 -0700789 }
790 }
791 updatePortsWithNewPortsByNumber(newPortByNumber);
792 return events;
793 } finally {
794 lock.writeLock().unlock();
795 }
796
797 }
798
799 /**
800 * Given a new or modified port newPort, returns the list of
Saurav Dasfcdad072014-08-13 14:15:21 -0700801 * PortChangeEvents to "transform" the current ports stored by this
802 * switch to include / represent the new port. The ports stored by this
803 * switch are <b>NOT</b> updated.
Jonathan Hart23daf192014-08-22 10:56:58 -0700804 *
Saurav Dasfcdad072014-08-13 14:15:21 -0700805 * This method acquires the readlock and is thread-safe by itself. Most
806 * callers will need to acquire the write lock before calling this
807 * method though (if the caller wants to update the ports stored by this
808 * switch)
Jonathan Hart23daf192014-08-22 10:56:58 -0700809 *
Brian O'Connorc67f9fa2014-08-07 18:17:46 -0700810 * @param newPort the new or modified port.
811 * @return the list of changes
812 */
813 public OrderedCollection<PortChangeEvent> getSinglePortChanges(
Saurav Dasfcdad072014-08-13 14:15:21 -0700814 OFPortDesc newPort) {
Brian O'Connorc67f9fa2014-08-07 18:17:46 -0700815 lock.readLock().lock();
816 try {
817 OrderedCollection<PortChangeEvent> events =
818 new LinkedHashSetWrapper<PortChangeEvent>();
819 // Check if we have a port by the same number in our
820 // old map.
821 OFPortDesc prevPort =
822 portsByNumber.get(newPort.getPortNo().getPortNumber());
823 if (newPort.equals(prevPort)) {
824 // nothing has changed
825 return events;
826 }
827
828 if (prevPort != null &&
829 prevPort.getName().equals(newPort.getName())) {
830 // A simple modify of a existing port
831 // A previous port with this number exists and it's name
832 // also matches the new port. Find the differences
833 if (isEnabled(prevPort) && !isEnabled(newPort)) {
834 events.add(new PortChangeEvent(newPort,
Saurav Dasfcdad072014-08-13 14:15:21 -0700835 PortChangeType.DOWN));
Brian O'Connorc67f9fa2014-08-07 18:17:46 -0700836 } else if (!isEnabled(prevPort) && isEnabled(newPort)) {
837 events.add(new PortChangeEvent(newPort,
Saurav Dasfcdad072014-08-13 14:15:21 -0700838 PortChangeType.UP));
Brian O'Connorc67f9fa2014-08-07 18:17:46 -0700839 } else {
840 events.add(new PortChangeEvent(newPort,
Saurav Dasfcdad072014-08-13 14:15:21 -0700841 PortChangeType.OTHER_UPDATE));
Brian O'Connorc67f9fa2014-08-07 18:17:46 -0700842 }
843 return events;
844 }
845
846 if (prevPort != null) {
847 // There exists a previous port with the same port
848 // number but the port name is different (otherwise we would
849 // never have gotten here)
850 // Remove the port. Name-number mapping(s) have changed
851 events.add(new PortChangeEvent(prevPort,
Saurav Dasfcdad072014-08-13 14:15:21 -0700852 PortChangeType.DELETE));
Brian O'Connorc67f9fa2014-08-07 18:17:46 -0700853 }
854
855 // We now need to check if there exists a previous port sharing
856 // the same name as the new/updated port.
857 prevPort = portsByName.get(newPort.getName().toLowerCase());
858 if (prevPort != null) {
859 // There exists a previous port with the same port
860 // name but the port number is different (otherwise we
861 // never have gotten here).
862 // Remove the port. Name-number mapping(s) have changed
863 events.add(new PortChangeEvent(prevPort,
Saurav Dasfcdad072014-08-13 14:15:21 -0700864 PortChangeType.DELETE));
Brian O'Connorc67f9fa2014-08-07 18:17:46 -0700865 }
866
867 // We always need to add the new port. Either no previous port
868 // existed or we just deleted previous ports with inconsistent
869 // name-number mappings
870 events.add(new PortChangeEvent(newPort, PortChangeType.ADD));
871 return events;
872 } finally {
873 lock.readLock().unlock();
874 }
875 }
876
877 /**
878 * Compare the current ports of this switch to the newPorts list and
879 * return the changes that would be applied to transfort the current
Saurav Dasfcdad072014-08-13 14:15:21 -0700880 * ports to the new ports. No internal data structures are updated see
881 * {@link #compareAndUpdatePorts(List, boolean)}
Jonathan Hart23daf192014-08-22 10:56:58 -0700882 *
Brian O'Connorc67f9fa2014-08-07 18:17:46 -0700883 * @param newPorts the list of new ports
884 * @return The list of differences between the current ports and
Saurav Dasfcdad072014-08-13 14:15:21 -0700885 * newPortList
Brian O'Connorc67f9fa2014-08-07 18:17:46 -0700886 */
887 public OrderedCollection<PortChangeEvent> comparePorts(
Saurav Dasfcdad072014-08-13 14:15:21 -0700888 Collection<OFPortDesc> newPorts) {
Brian O'Connorc67f9fa2014-08-07 18:17:46 -0700889 return compareAndUpdatePorts(newPorts, false);
890 }
891
892 /**
893 * Compare the current ports of this switch to the newPorts list and
894 * return the changes that would be applied to transform the current
Saurav Dasfcdad072014-08-13 14:15:21 -0700895 * ports to the new ports. No internal data structures are updated see
896 * {@link #compareAndUpdatePorts(List, boolean)}
Jonathan Hart23daf192014-08-22 10:56:58 -0700897 *
Brian O'Connorc67f9fa2014-08-07 18:17:46 -0700898 * @param newPorts the list of new ports
899 * @return The list of differences between the current ports and
Saurav Dasfcdad072014-08-13 14:15:21 -0700900 * newPortList
Brian O'Connorc67f9fa2014-08-07 18:17:46 -0700901 */
902 public OrderedCollection<PortChangeEvent> updatePorts(
Saurav Dasfcdad072014-08-13 14:15:21 -0700903 Collection<OFPortDesc> newPorts) {
Brian O'Connorc67f9fa2014-08-07 18:17:46 -0700904 return compareAndUpdatePorts(newPorts, true);
905 }
906
907 /**
Saurav Dasfcdad072014-08-13 14:15:21 -0700908 * Compare the current ports stored in this switch instance with the new
909 * port list given and return the differences in the form of
Brian O'Connorc67f9fa2014-08-07 18:17:46 -0700910 * PortChangeEvents. If the doUpdate flag is true, newPortList will
911 * replace the current list of this switch (and update the port maps)
Jonathan Hart23daf192014-08-22 10:56:58 -0700912 *
Saurav Dasfcdad072014-08-13 14:15:21 -0700913 * Implementation note: Since this method can optionally modify the
914 * current ports and since it's not possible to upgrade a read-lock to a
915 * write-lock we need to hold the write-lock for the entire operation.
916 * If this becomes a problem and if compares() are common we can
917 * consider splitting in two methods but this requires lots of code
918 * duplication
Jonathan Hart23daf192014-08-22 10:56:58 -0700919 *
Brian O'Connorc67f9fa2014-08-07 18:17:46 -0700920 * @param newPorts the list of new ports.
Saurav Dasfcdad072014-08-13 14:15:21 -0700921 * @param doUpdate If true the newPortList will replace the current port
922 * list for this switch. If false this switch will not be
923 * changed.
Brian O'Connorc67f9fa2014-08-07 18:17:46 -0700924 * @return The list of differences between the current ports and
Saurav Dasfcdad072014-08-13 14:15:21 -0700925 * newPorts
Brian O'Connorc67f9fa2014-08-07 18:17:46 -0700926 * @throws NullPointerException if newPortsList is null
927 * @throws IllegalArgumentException if either port names or port numbers
Saurav Dasfcdad072014-08-13 14:15:21 -0700928 * are duplicated in the newPortsList.
Brian O'Connorc67f9fa2014-08-07 18:17:46 -0700929 */
930 private OrderedCollection<PortChangeEvent> compareAndUpdatePorts(
931 Collection<OFPortDesc> newPorts, boolean doUpdate) {
932 if (newPorts == null) {
933 throw new NullPointerException("newPortsList must not be null");
934 }
935 lock.writeLock().lock();
936 try {
937 OrderedCollection<PortChangeEvent> events =
938 new LinkedHashSetWrapper<PortChangeEvent>();
939
Saurav Dasfcdad072014-08-13 14:15:21 -0700940 Map<Integer, OFPortDesc> newPortsByNumber =
Brian O'Connorc67f9fa2014-08-07 18:17:46 -0700941 new HashMap<Integer, OFPortDesc>();
Saurav Dasfcdad072014-08-13 14:15:21 -0700942 Map<String, OFPortDesc> newPortsByName =
Brian O'Connorc67f9fa2014-08-07 18:17:46 -0700943 new HashMap<String, OFPortDesc>();
944 List<OFPortDesc> newEnabledPortList =
945 new ArrayList<OFPortDesc>();
946 List<Integer> newEnabledPortNumbers =
947 new ArrayList<Integer>();
948 List<OFPortDesc> newPortsList =
949 new ArrayList<OFPortDesc>(newPorts);
950
Saurav Dasfcdad072014-08-13 14:15:21 -0700951 for (OFPortDesc p : newPortsList) {
Brian O'Connorc67f9fa2014-08-07 18:17:46 -0700952 if (p == null) {
953 throw new NullPointerException("portList must not " +
954 "contain null values");
955 }
956
957 // Add the port to the new maps and lists and check
958 // that every port is unique
959 OFPortDesc duplicatePort;
960 duplicatePort = newPortsByNumber.put(
Saurav Dasfcdad072014-08-13 14:15:21 -0700961 p.getPortNo().getPortNumber(), p);
Brian O'Connorc67f9fa2014-08-07 18:17:46 -0700962 if (duplicatePort != null) {
963 String msg = String.format("Cannot have two ports " +
964 "with the same number: %s <-> %s",
965 p, duplicatePort);
966 throw new IllegalArgumentException(msg);
967 }
968 duplicatePort =
969 newPortsByName.put(p.getName().toLowerCase(), p);
970 if (duplicatePort != null) {
971 String msg = String.format("Cannot have two ports " +
972 "with the same name: %s <-> %s",
Saurav Dasfcdad072014-08-13 14:15:21 -0700973 p.toString().substring(0, 80),
Brian O'Connorc67f9fa2014-08-07 18:17:46 -0700974 duplicatePort.toString().substring(0, 80));
975 throw new IllegalArgumentException(msg);
976 }
977 if (isEnabled(p)) {
978 newEnabledPortList.add(p);
979 newEnabledPortNumbers.add(p.getPortNo().getPortNumber());
980 }
981
982 // get changes
983 events.addAll(getSinglePortChanges(p));
984 }
985 // find deleted ports
986 // We need to do this after looping through all the new ports
987 // to we can handle changed name<->number mappings correctly
988 // We could pull it into the loop of we address this but
989 // it's probably not worth it
Saurav Dasfcdad072014-08-13 14:15:21 -0700990 for (OFPortDesc oldPort : this.portList) {
991 if (!newPortsByNumber
992 .containsKey(oldPort.getPortNo().getPortNumber())) {
Brian O'Connorc67f9fa2014-08-07 18:17:46 -0700993 PortChangeEvent ev =
994 new PortChangeEvent(oldPort,
Saurav Dasfcdad072014-08-13 14:15:21 -0700995 PortChangeType.DELETE);
Brian O'Connorc67f9fa2014-08-07 18:17:46 -0700996 events.add(ev);
997 }
998 }
999
Brian O'Connorc67f9fa2014-08-07 18:17:46 -07001000 if (doUpdate) {
1001 portsByName = Collections.unmodifiableMap(newPortsByName);
1002 portsByNumber =
1003 Collections.unmodifiableMap(newPortsByNumber);
1004 enabledPortList =
1005 Collections.unmodifiableList(newEnabledPortList);
1006 enabledPortNumbers =
1007 Collections.unmodifiableList(newEnabledPortNumbers);
1008 portList = Collections.unmodifiableList(newPortsList);
1009 }
1010 return events;
1011 } finally {
1012 lock.writeLock().unlock();
1013 }
1014 }
1015
1016 public OFPortDesc getPort(String name) {
1017 if (name == null) {
1018 throw new NullPointerException("Port name must not be null");
1019 }
1020 lock.readLock().lock();
1021 try {
1022 return portsByName.get(name.toLowerCase());
1023 } finally {
1024 lock.readLock().unlock();
1025 }
1026 }
1027
1028 public OFPortDesc getPort(int portNumber) {
1029 lock.readLock().lock();
1030 try {
1031 return portsByNumber.get(portNumber);
1032 } finally {
1033 lock.readLock().unlock();
1034 }
1035 }
1036
1037 public List<OFPortDesc> getPorts() {
1038 lock.readLock().lock();
1039 try {
1040 return portList;
1041 } finally {
1042 lock.readLock().unlock();
1043 }
1044 }
1045
1046 public List<OFPortDesc> getEnabledPorts() {
1047 lock.readLock().lock();
1048 try {
1049 return enabledPortList;
1050 } finally {
1051 lock.readLock().unlock();
1052 }
1053 }
1054
1055 public List<Integer> getEnabledPortNumbers() {
1056 lock.readLock().lock();
1057 try {
1058 return enabledPortNumbers;
1059 } finally {
1060 lock.readLock().unlock();
1061 }
1062 }
1063 }
1064
Saurav Dasfcdad072014-08-13 14:15:21 -07001065 // *******************************************
1066 // Switch stats handling
1067 // *******************************************
Brian O'Connorc67f9fa2014-08-07 18:17:46 -07001068
1069 @Override
1070 public Future<List<OFStatsReply>> getStatistics(OFStatsRequest<?> request)
Saurav Dasfcdad072014-08-13 14:15:21 -07001071 throws IOException {
Brian O'Connorc67f9fa2014-08-07 18:17:46 -07001072 OFStatisticsFuture future = new OFStatisticsFuture(threadPool, this,
Saurav Dasfcdad072014-08-13 14:15:21 -07001073 (int) request.getXid());
Brian O'Connorc67f9fa2014-08-07 18:17:46 -07001074 log.info("description STAT REQUEST XID {}", request.getXid());
Saurav Dasfcdad072014-08-13 14:15:21 -07001075 this.statsFutureMap.put((int) request.getXid(), future);
Brian O'Connorc67f9fa2014-08-07 18:17:46 -07001076
1077 this.channel.write(Collections.singletonList(request));
1078 return future;
1079 }
1080
1081 @Override
1082 public void deliverStatisticsReply(OFMessage reply) {
Saurav Dasfcdad072014-08-13 14:15:21 -07001083 OFStatisticsFuture future = this.statsFutureMap.get((int) reply.getXid());
Brian O'Connorc67f9fa2014-08-07 18:17:46 -07001084 if (future != null) {
1085 future.deliverFuture(this, reply);
1086 // The future will ultimately unregister itself and call
1087 // cancelStatisticsReply
1088 return;
1089 }
1090 // Transaction id was not found in statsFutureMap.check the other map
1091 IOFMessageListener caller = this.iofMsgListenersMap.get(reply.getXid());
1092 if (caller != null) {
Saurav Dasfcdad072014-08-13 14:15:21 -07001093 caller.receive(this, reply, null);
Brian O'Connorc67f9fa2014-08-07 18:17:46 -07001094 }
1095 }
1096
1097 @Override
1098 public void cancelStatisticsReply(int transactionId) {
1099 if (null == this.statsFutureMap.remove(transactionId)) {
1100 this.iofMsgListenersMap.remove(transactionId);
1101 }
1102 }
1103
1104 @Override
1105 public void cancelAllStatisticsReplies() {
1106 /* we don't need to be synchronized here. Even if another thread
1107 * modifies the map while we're cleaning up the future will eventuall
1108 * timeout */
1109 for (OFStatisticsFuture f : statsFutureMap.values()) {
1110 f.cancel(true);
1111 }
1112 statsFutureMap.clear();
1113 iofMsgListenersMap.clear();
1114 }
1115
Saurav Dasfcdad072014-08-13 14:15:21 -07001116 // *******************************************
1117 // Switch role handling
1118 // *******************************************
Brian O'Connorc67f9fa2014-08-07 18:17:46 -07001119
1120 // XXX S this is completely wrong. The generation id should be obtained
1121 // after coordinating with all controllers connected to this switch.
1122 // ie. should be part of registry service (account for 1.3 vs 1.0)
1123 // For now we are just generating this locally and keeping it constant.
1124 public U64 getNextGenerationId() {
Saurav Dasfcdad072014-08-13 14:15:21 -07001125 // TODO: Pankaj, fix nextGenerationId as part of Registry interface
1126 return U64.of(generationIdSource);
Brian O'Connorc67f9fa2014-08-07 18:17:46 -07001127 }
1128
1129 @Override
1130 public Role getRole() {
1131 return role;
1132 }
1133
1134 @JsonIgnore
1135 @Override
1136 public void setRole(Role role) {
1137 this.role = role;
1138 }
1139
Saurav Dasfcdad072014-08-13 14:15:21 -07001140 // *******************************************
1141 // Switch utility methods
1142 // *******************************************
Brian O'Connorc67f9fa2014-08-07 18:17:46 -07001143
1144 private void registerOverloadCounters() throws CounterException {
1145 if (debugCountersRegistered) {
1146 return;
1147 }
1148 if (debugCounters == null) {
1149 log.error("Debug Counter Service not found");
1150 debugCounters = new NullDebugCounter();
1151 debugCountersRegistered = true;
1152 }
1153 // every level of the hierarchical counter has to be registered
1154 // even if they are not used
1155 ctrSwitch = debugCounters.registerCounter(
Saurav Dasfcdad072014-08-13 14:15:21 -07001156 BASE, stringId,
1157 "Counter for this switch",
1158 CounterType.ALWAYS_COUNT);
Brian O'Connorc67f9fa2014-08-07 18:17:46 -07001159 ctrSwitchPktin = debugCounters.registerCounter(
Saurav Dasfcdad072014-08-13 14:15:21 -07001160 BASE, stringId + "/pktin",
1161 "Packet in counter for this switch",
1162 CounterType.ALWAYS_COUNT);
Brian O'Connorc67f9fa2014-08-07 18:17:46 -07001163 ctrSwitchWrite = debugCounters.registerCounter(
Saurav Dasfcdad072014-08-13 14:15:21 -07001164 BASE, stringId + "/write",
1165 "Write counter for this switch",
1166 CounterType.ALWAYS_COUNT);
Brian O'Connorc67f9fa2014-08-07 18:17:46 -07001167 ctrSwitchPktinDrops = debugCounters.registerCounter(
Saurav Dasfcdad072014-08-13 14:15:21 -07001168 BASE, stringId + "/pktin/drops",
1169 "Packet in throttle drop count",
1170 CounterType.ALWAYS_COUNT,
1171 IDebugCounterService.CTR_MDATA_WARN);
Brian O'Connorc67f9fa2014-08-07 18:17:46 -07001172 ctrSwitchWriteDrops = debugCounters.registerCounter(
Saurav Dasfcdad072014-08-13 14:15:21 -07001173 BASE, stringId + "/write/drops",
1174 "Switch write throttle drop count",
1175 CounterType.ALWAYS_COUNT,
1176 IDebugCounterService.CTR_MDATA_WARN);
Brian O'Connorc67f9fa2014-08-07 18:17:46 -07001177 }
1178
1179 @Override
1180 public void startDriverHandshake() throws IOException {
Saurav Dasfcdad072014-08-13 14:15:21 -07001181 log.debug("Starting driver handshake for sw {}", getStringId());
Brian O'Connorc67f9fa2014-08-07 18:17:46 -07001182 if (startDriverHandshakeCalled)
1183 throw new SwitchDriverSubHandshakeAlreadyStarted();
1184 startDriverHandshakeCalled = true;
1185 }
1186
1187 @Override
1188 public boolean isDriverHandshakeComplete() {
1189 if (!startDriverHandshakeCalled)
1190 throw new SwitchDriverSubHandshakeNotStarted();
1191 return true;
1192 }
1193
1194 @Override
1195 public void processDriverHandshakeMessage(OFMessage m) {
1196 if (startDriverHandshakeCalled)
1197 throw new SwitchDriverSubHandshakeCompleted(m);
1198 else
1199 throw new SwitchDriverSubHandshakeNotStarted();
1200 }
1201
1202 @Override
1203 @JsonIgnore
Saurav Dasfcdad072014-08-13 14:15:21 -07001204 @LogMessageDoc(level = "WARN",
1205 message = "Switch {switch} flow table is full",
1206 explanation = "The controller received flow table full " +
1207 "message from the switch, could be caused by increased " +
1208 "traffic pattern",
1209 recommendation = LogMessageDoc.REPORT_CONTROLLER_BUG)
Brian O'Connorc67f9fa2014-08-07 18:17:46 -07001210 public void setTableFull(boolean isFull) {
1211 if (isFull && !flowTableFull) {
1212 floodlightProvider.addSwitchEvent(this.datapathId.getLong(),
1213 "SWITCH_FLOW_TABLE_FULL " +
Saurav Dasfcdad072014-08-13 14:15:21 -07001214 "Table full error from switch", false);
Brian O'Connorc67f9fa2014-08-07 18:17:46 -07001215 log.warn("Switch {} flow table is full", stringId);
1216 }
Saurav Dasfcdad072014-08-13 14:15:21 -07001217 flowTableFull = isFull;
Brian O'Connorc67f9fa2014-08-07 18:17:46 -07001218 }
1219
1220 @Override
1221 @LogMessageDoc(level = "ERROR",
1222 message = "Failed to clear all flows on switch {switch}",
1223 explanation = "An I/O error occured while trying to clear " +
1224 "flows on the switch.",
1225 recommendation = LogMessageDoc.CHECK_SWITCH)
1226 public void clearAllFlowMods() {
1227 // Delete all pre-existing flows
1228
Jonathan Harta213bce2014-08-11 15:44:07 -07001229 // by default if match is not specified, then an empty list of matches
1230 // is sent, resulting in a wildcard-all flows
Jonathan Hart23daf192014-08-22 10:56:58 -07001231 Builder builder = getFactory()
Saurav Dasfcdad072014-08-13 14:15:21 -07001232 .buildFlowDelete()
Jonathan Hart23daf192014-08-22 10:56:58 -07001233 .setOutPort(OFPort.ANY);
1234
1235 if (ofversion.wireVersion >= OFVersion.OF_13.wireVersion) {
1236 builder.setOutGroup(OFGroup.ANY)
1237 .setTableId(TableId.ALL);
1238 }
1239
1240 OFMessage fm = builder.build();
Brian O'Connorc67f9fa2014-08-07 18:17:46 -07001241
1242 try {
1243 channel.write(Collections.singletonList(fm));
1244 } catch (Exception e) {
1245 log.error("Failed to clear all flows on switch " + this, e);
1246 }
1247 }
1248
1249 @Override
1250 public void flush() {
1251 Map<OFSwitchImplBase, List<OFMessage>> msg_buffer_map = local_msg_buffer.get();
1252 List<OFMessage> msglist = msg_buffer_map.get(this);
1253 if ((msglist != null) && (msglist.size() > 0)) {
1254 try {
1255 this.write(msglist);
1256 } catch (IOException e) {
Saurav Dasfcdad072014-08-13 14:15:21 -07001257 log.error("Failed flushing messages", e);
Brian O'Connorc67f9fa2014-08-07 18:17:46 -07001258 }
1259 msglist.clear();
1260 }
1261 }
1262
1263 public static void flush_all() {
1264 Map<OFSwitchImplBase, List<OFMessage>> msg_buffer_map = local_msg_buffer.get();
1265 for (OFSwitchImplBase sw : msg_buffer_map.keySet()) {
1266 sw.flush();
1267 }
1268 }
1269
1270 /**
1271 * Return a read lock that must be held while calling the listeners for
1272 * messages from the switch. Holding the read lock prevents the active
1273 * switch list from being modified out from under the listeners.
Jonathan Hart23daf192014-08-22 10:56:58 -07001274 *
Brian O'Connorc67f9fa2014-08-07 18:17:46 -07001275 * @return listener read lock
1276 */
1277 @JsonIgnore
1278 public Lock getListenerReadLock() {
1279 return listenerLock.readLock();
1280 }
1281
1282 /**
1283 * Return a write lock that must be held when the controllers modifies the
1284 * list of active switches. This is to ensure that the active switch list
1285 * doesn't change out from under the listeners as they are handling a
1286 * message from the switch.
Jonathan Hart23daf192014-08-22 10:56:58 -07001287 *
Brian O'Connorc67f9fa2014-08-07 18:17:46 -07001288 * @return listener write lock
1289 */
1290 @JsonIgnore
1291 public Lock getListenerWriteLock() {
1292 return listenerLock.writeLock();
1293 }
Saurav Dasfc5e3eb2014-09-25 19:05:21 -07001294
1295 public String getSwitchDriverState() {
1296 return "";
1297 }
1298
Brian O'Connorc67f9fa2014-08-07 18:17:46 -07001299}