blob: c51875f496430e2f1e3593e27b8d538b1029d4df [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;
21import java.util.ArrayList;
22import java.util.Collection;
23import java.util.Collections;
24import java.util.Date;
25import java.util.HashMap;
26import java.util.LinkedList;
27import java.util.List;
28import java.util.Map;
29import java.util.Set;
30import java.util.WeakHashMap;
31import java.util.concurrent.ConcurrentHashMap;
32import java.util.concurrent.ConcurrentMap;
33import java.util.concurrent.Future;
34import java.util.concurrent.atomic.AtomicInteger;
35import java.util.concurrent.locks.Lock;
36import java.util.concurrent.locks.ReentrantReadWriteLock;
37
38import net.floodlightcontroller.core.FloodlightContext;
39import net.floodlightcontroller.core.IFloodlightProviderService;
40import net.floodlightcontroller.core.IFloodlightProviderService.Role;
41import net.floodlightcontroller.core.IOFMessageListener;
42import net.floodlightcontroller.core.IOFSwitch;
43import net.floodlightcontroller.core.SwitchDriverSubHandshakeAlreadyStarted;
44import net.floodlightcontroller.core.SwitchDriverSubHandshakeCompleted;
45import net.floodlightcontroller.core.SwitchDriverSubHandshakeNotStarted;
46import net.floodlightcontroller.core.annotations.LogMessageDoc;
47import net.floodlightcontroller.core.web.serializers.DPIDSerializer;
48import net.floodlightcontroller.debugcounter.IDebugCounter;
49import net.floodlightcontroller.debugcounter.IDebugCounterService;
50import net.floodlightcontroller.debugcounter.IDebugCounterService.CounterException;
51import net.floodlightcontroller.debugcounter.IDebugCounterService.CounterType;
52import net.floodlightcontroller.debugcounter.NullDebugCounter;
53import net.floodlightcontroller.threadpool.IThreadPoolService;
54import net.floodlightcontroller.util.LinkedHashSetWrapper;
55import net.floodlightcontroller.util.OrderedCollection;
Brian O'Connorc67f9fa2014-08-07 18:17:46 -070056
57import org.codehaus.jackson.annotate.JsonIgnore;
58import org.codehaus.jackson.annotate.JsonProperty;
59import org.codehaus.jackson.map.annotate.JsonSerialize;
60import org.jboss.netty.channel.Channel;
61import org.projectfloodlight.openflow.protocol.OFActionType;
62import org.projectfloodlight.openflow.protocol.OFCapabilities;
63import org.projectfloodlight.openflow.protocol.OFDescStatsReply;
Jonathan Harta213bce2014-08-11 15:44:07 -070064import org.projectfloodlight.openflow.protocol.OFFactories;
65import org.projectfloodlight.openflow.protocol.OFFactory;
Brian O'Connorc67f9fa2014-08-07 18:17:46 -070066import org.projectfloodlight.openflow.protocol.OFFeaturesReply;
67import org.projectfloodlight.openflow.protocol.OFMessage;
68import org.projectfloodlight.openflow.protocol.OFPortConfig;
69import org.projectfloodlight.openflow.protocol.OFPortDesc;
70import org.projectfloodlight.openflow.protocol.OFPortDescStatsReply;
71import org.projectfloodlight.openflow.protocol.OFPortReason;
72import org.projectfloodlight.openflow.protocol.OFPortState;
73import org.projectfloodlight.openflow.protocol.OFPortStatus;
74import org.projectfloodlight.openflow.protocol.OFStatsReply;
75import org.projectfloodlight.openflow.protocol.OFStatsRequest;
76import org.projectfloodlight.openflow.protocol.OFType;
77import org.projectfloodlight.openflow.protocol.OFVersion;
78import org.projectfloodlight.openflow.types.DatapathId;
79import org.projectfloodlight.openflow.types.OFAuxId;
80import org.projectfloodlight.openflow.types.OFGroup;
81import org.projectfloodlight.openflow.types.OFPort;
82import org.projectfloodlight.openflow.types.TableId;
83import org.projectfloodlight.openflow.types.U64;
84import org.slf4j.Logger;
85import org.slf4j.LoggerFactory;
86
Brian O'Connorc67f9fa2014-08-07 18:17:46 -070087/**
88 * This is the internal representation of an openflow switch.
89 */
90public class OFSwitchImplBase implements IOFSwitch {
91 // TODO: should we really do logging in the class or should we throw
92 // exception that can then be handled by callers?
93 protected final static Logger log = LoggerFactory.getLogger(OFSwitchImplBase.class);
94
95 private static final String HA_CHECK_SWITCH =
96 "Check the health of the indicated switch. If the problem " +
97 "persists or occurs repeatedly, it likely indicates a defect " +
98 "in the switch HA implementation.";
99
100 protected ConcurrentMap<Object, Object> attributes;
101 protected IFloodlightProviderService floodlightProvider;
102 protected IThreadPoolService threadPool;
103 protected Date connectedSince;
104 protected String stringId;
105 protected Channel channel;
106 // transaction id used for messages sent out to this switch from
107 // this controller instance. This xid has significance only between this
108 // controller<->switch pair.
109 protected AtomicInteger transactionIdSource;
110
111 // generation id used for roleRequest messages sent to switches (see section
112 // 6.3.5 of the OF1.3.4 spec). This generationId has significance between
113 // all the controllers that this switch is connected to; and only for role
114 // request messages with role MASTER or SLAVE. The set of Controllers that
115 // this switch is connected to should coordinate the next generation id,
116 // via transactional semantics.
117 protected long generationIdSource;
118
119 // Lock to protect modification of the port maps. We only need to
120 // synchronize on modifications. For read operations we are fine since
121 // we rely on ConcurrentMaps which works for our use case.
Saurav Dasfcdad072014-08-13 14:15:21 -0700122 // private Object portLock; XXX S remove this
Brian O'Connorc67f9fa2014-08-07 18:17:46 -0700123
124 // Map port numbers to the appropriate OFPortDesc
125 protected ConcurrentHashMap<Integer, OFPortDesc> portsByNumber;
126 // Map port names to the appropriate OFPhyiscalPort
127 // XXX: The OF spec doesn't specify if port names need to be unique but
Saurav Dasfcdad072014-08-13 14:15:21 -0700128 // according it's always the case in practice.
Brian O'Connorc67f9fa2014-08-07 18:17:46 -0700129 protected ConcurrentHashMap<String, OFPortDesc> portsByName;
130 protected Map<Integer, OFStatisticsFuture> statsFutureMap;
Saurav Dasfcdad072014-08-13 14:15:21 -0700131 // XXX Consider removing the following 2 maps - not used anymore
132 protected Map<Integer, IOFMessageListener> iofMsgListenersMap;
Brian O'Connorc67f9fa2014-08-07 18:17:46 -0700133 protected Map<Integer, OFFeaturesReplyFuture> featuresFutureMap;
134 protected boolean connected;
135 protected Role role;
Brian O'Connorc67f9fa2014-08-07 18:17:46 -0700136 protected ReentrantReadWriteLock listenerLock;
137 protected ConcurrentMap<Short, Long> portBroadcastCacheHitMap;
138 /**
Saurav Dasfcdad072014-08-13 14:15:21 -0700139 * When sending a role request message, the role request is added to this
140 * queue. If a role reply is received this queue is checked to verify that
141 * the reply matches the expected reply. We require in order delivery of
142 * replies. That's why we use a Queue. The RoleChanger uses a timeout to
143 * ensure we receive a timely reply.
Brian O'Connorc67f9fa2014-08-07 18:17:46 -0700144 * <p/>
145 * Need to synchronize on this instance if a request is sent, received,
146 * checked.
147 */
148 protected LinkedList<PendingRoleRequestEntry> pendingRoleRequests;
149
150 /** OpenFlow version for this switch */
151 protected OFVersion ofversion;
152 // Description stats reply describing this switch
153 private OFDescStatsReply switchDescription;
154 // Switch features from initial featuresReply
155 protected Set<OFCapabilities> capabilities;
156 protected int buffers;
157 protected Set<OFActionType> actions;
158 protected byte tables;
159 protected DatapathId datapathId;
Saurav Dasfcdad072014-08-13 14:15:21 -0700160 private OFAuxId auxId;
Brian O'Connorc67f9fa2014-08-07 18:17:46 -0700161
162 private IDebugCounterService debugCounters;
Saurav Dasfcdad072014-08-13 14:15:21 -0700163 private boolean debugCountersRegistered;
164 @SuppressWarnings("unused")
Brian O'Connorc67f9fa2014-08-07 18:17:46 -0700165 private IDebugCounter ctrSwitch, ctrSwitchPktin, ctrSwitchWrite,
Saurav Dasfcdad072014-08-13 14:15:21 -0700166 ctrSwitchPktinDrops, ctrSwitchWriteDrops;
Brian O'Connorc67f9fa2014-08-07 18:17:46 -0700167
Saurav Dasfcdad072014-08-13 14:15:21 -0700168 protected boolean startDriverHandshakeCalled = false;
169 private boolean flowTableFull = false;
Brian O'Connorc67f9fa2014-08-07 18:17:46 -0700170
Saurav Dasfcdad072014-08-13 14:15:21 -0700171 private final PortManager portManager;
Brian O'Connorc67f9fa2014-08-07 18:17:46 -0700172
173 protected static final ThreadLocal<Map<OFSwitchImplBase, List<OFMessage>>> local_msg_buffer =
Saurav Dasfcdad072014-08-13 14:15:21 -0700174 new ThreadLocal<Map<OFSwitchImplBase, List<OFMessage>>>() {
175 @Override
176 protected Map<OFSwitchImplBase, List<OFMessage>> initialValue() {
177 return new WeakHashMap<OFSwitchImplBase, List<OFMessage>>();
178 }
179 };
Brian O'Connorc67f9fa2014-08-07 18:17:46 -0700180
Saurav Dasfcdad072014-08-13 14:15:21 -0700181 private static final String BASE = "switchbase";
Brian O'Connorc67f9fa2014-08-07 18:17:46 -0700182
183 protected static class PendingRoleRequestEntry {
184 protected int xid;
185 protected Role role;
186 // cookie is used to identify the role "generation". roleChanger uses
187 protected long cookie;
188
189 public PendingRoleRequestEntry(int xid, Role role, long cookie) {
190 this.xid = xid;
191 this.role = role;
192 this.cookie = cookie;
193 }
194 }
195
196 public OFSwitchImplBase() {
197 this.stringId = null;
198 this.attributes = new ConcurrentHashMap<Object, Object>();
199 this.connectedSince = new Date();
200 this.transactionIdSource = new AtomicInteger();
Saurav Dasfcdad072014-08-13 14:15:21 -0700201 this.generationIdSource = 0; // XXX S this is wrong; should be
202 // negotiated
Brian O'Connorc67f9fa2014-08-07 18:17:46 -0700203 // XXX S no need this.portLock = new Object();
204 this.portsByNumber = new ConcurrentHashMap<Integer, OFPortDesc>();
205 this.portsByName = new ConcurrentHashMap<String, OFPortDesc>();
206 this.connected = true;
207 this.statsFutureMap = new ConcurrentHashMap<Integer, OFStatisticsFuture>();
208 this.featuresFutureMap = new ConcurrentHashMap<Integer, OFFeaturesReplyFuture>();
209 this.iofMsgListenersMap = new ConcurrentHashMap<Integer, IOFMessageListener>();
210 this.role = null;
Brian O'Connorc67f9fa2014-08-07 18:17:46 -0700211 this.listenerLock = new ReentrantReadWriteLock();
212 this.pendingRoleRequests = new LinkedList<OFSwitchImplBase.PendingRoleRequestEntry>();
213 this.portManager = new PortManager();
214 // by default the base impl declares no support for Nx_role_requests.
215 // OF1.0 switches like OVS that do support these messages should set the
216 // attribute in the associated switch driver.
217 setAttribute(SWITCH_SUPPORTS_NX_ROLE, false);
218
219 }
220
Saurav Dasfcdad072014-08-13 14:15:21 -0700221 // *******************************************
222 // Setters and Getters
223 // *******************************************
Brian O'Connorc67f9fa2014-08-07 18:17:46 -0700224
225 @Override
226 public Object getAttribute(String name) {
227 if (this.attributes.containsKey(name)) {
228 return this.attributes.get(name);
229 }
230 return null;
231 }
232
233 @Override
234 public ConcurrentMap<Object, Object> getAttributes() {
235 return this.attributes;
236 }
237
238 @Override
239 public void setAttribute(String name, Object value) {
240 this.attributes.put(name, value);
241 return;
242 }
243
244 @Override
245 public Object removeAttribute(String name) {
246 return this.attributes.remove(name);
247 }
248
249 @Override
250 public boolean hasAttribute(String name) {
251 return this.attributes.containsKey(name);
252 }
253
254 @Override
255 @JsonSerialize(using = DPIDSerializer.class)
256 @JsonProperty("dpid")
257 public long getId() {
258 if (this.stringId == null)
259 throw new RuntimeException("Features reply has not yet been set");
260 return this.datapathId.getLong();
261 }
262
263 @JsonIgnore
264 @Override
265 public String getStringId() {
266 return stringId;
267 }
268
Jonathan Harta213bce2014-08-11 15:44:07 -0700269 @Override
270 public OFFactory getFactory() {
271 return OFFactories.getFactory(ofversion);
Brian O'Connorc67f9fa2014-08-07 18:17:46 -0700272 }
273
Jonathan Harta213bce2014-08-11 15:44:07 -0700274 @Override
275 public OFVersion getOFVersion() {
276 return ofversion;
277 }
278
279 @Override
Brian O'Connorc67f9fa2014-08-07 18:17:46 -0700280 public void setOFVersion(OFVersion ofv) {
Jonathan Harta213bce2014-08-11 15:44:07 -0700281 ofversion = ofv;
Brian O'Connorc67f9fa2014-08-07 18:17:46 -0700282 }
283
284 /**
285 * @param floodlightProvider the floodlightProvider to set
286 */
287 @JsonIgnore
288 @Override
289 public void setFloodlightProvider(IFloodlightProviderService floodlightProvider) {
290 this.floodlightProvider = floodlightProvider;
291 }
292
293 @JsonIgnore
294 @Override
295 public void setThreadPoolService(IThreadPoolService tp) {
296 this.threadPool = tp;
297 }
298
299 @Override
300 @JsonIgnore
301 public void setDebugCounterService(IDebugCounterService debugCounters)
302 throws CounterException {
303 this.debugCounters = debugCounters;
304 registerOverloadCounters();
305 }
306
307 /* (non-Javadoc)
308 * @see java.lang.Object#toString()
309 */
310 @Override
311 public String toString() {
312 return "OFSwitchImpl [" + ((channel != null) ? channel.getRemoteAddress() : "?")
Saurav Dasfcdad072014-08-13 14:15:21 -0700313 + " DPID[" + ((stringId != null) ? stringId : "?") + "]]";
Brian O'Connorc67f9fa2014-08-07 18:17:46 -0700314 }
315
Saurav Dasfcdad072014-08-13 14:15:21 -0700316 // *******************************************
317 // Channel related methods
318 // *******************************************
Brian O'Connorc67f9fa2014-08-07 18:17:46 -0700319
320 @JsonIgnore
321 @Override
322 public void setChannel(Channel channel) {
323 this.channel = channel;
324 }
325
326 @Override
327 public void write(OFMessage m, FloodlightContext bc) throws IOException {
328 Map<OFSwitchImplBase, List<OFMessage>> msg_buffer_map = local_msg_buffer.get();
329 List<OFMessage> msg_buffer = msg_buffer_map.get(this);
330 if (msg_buffer == null) {
331 msg_buffer = new ArrayList<OFMessage>();
332 msg_buffer_map.put(this, msg_buffer);
333 }
334 // XXX S will change when iFloodlight provider changes
Saurav Dasfcdad072014-08-13 14:15:21 -0700335 // this.floodlightProvider.handleOutgoingMessage(this, m, bc);
Brian O'Connorc67f9fa2014-08-07 18:17:46 -0700336 msg_buffer.add(m);
337
338 if ((msg_buffer.size() >= Controller.BATCH_MAX_SIZE) ||
339 ((m.getType() != OFType.PACKET_OUT) && (m.getType() != OFType.FLOW_MOD))) {
340 this.write(msg_buffer);
341 msg_buffer.clear();
342 }
343 }
344
345 @Override
346 @LogMessageDoc(level = "WARN",
347 message = "Sending OF message that modifies switch " +
348 "state while in the slave role: {switch}",
349 explanation = "An application has sent a message to a switch " +
350 "that is not valid when the switch is in a slave role",
351 recommendation = LogMessageDoc.REPORT_CONTROLLER_BUG)
352 public void write(List<OFMessage> msglist,
Saurav Dasfcdad072014-08-13 14:15:21 -0700353 FloodlightContext bc) throws IOException {
Brian O'Connorc67f9fa2014-08-07 18:17:46 -0700354 for (OFMessage m : msglist) {
355 if (role == Role.SLAVE) {
356 switch (m.getType()) {
Saurav Dasfcdad072014-08-13 14:15:21 -0700357 case PACKET_OUT:
358 case FLOW_MOD:
359 case PORT_MOD:
360 log.warn("Sending OF message that modifies switch " +
361 "state while in the slave role: {}",
362 m.getType().name());
363 break;
364 default:
365 break;
Brian O'Connorc67f9fa2014-08-07 18:17:46 -0700366 }
367 }
368 // XXX S again
Saurav Dasfcdad072014-08-13 14:15:21 -0700369 // this.floodlightProvider.handleOutgoingMessage(this, m, bc);
Brian O'Connorc67f9fa2014-08-07 18:17:46 -0700370 }
371 this.write(msglist);
372 }
373
374 public void write(List<OFMessage> msglist) throws IOException {
375 this.channel.write(msglist);
376 }
377
378 @Override
379 public void disconnectSwitch() {
380 channel.close();
381 }
382
383 @Override
384 public Date getConnectedSince() {
385 return connectedSince;
386 }
387
388 @JsonIgnore
389 @Override
390 public int getNextTransactionId() {
391 return this.transactionIdSource.incrementAndGet();
392 }
393
394 @JsonIgnore
395 @Override
396 public synchronized boolean isConnected() {
397 return connected;
398 }
399
400 @Override
401 @JsonIgnore
402 public synchronized void setConnected(boolean connected) {
403 this.connected = connected;
404 }
405
Saurav Dasfcdad072014-08-13 14:15:21 -0700406 // *******************************************
407 // Switch features related methods
408 // *******************************************
Brian O'Connorc67f9fa2014-08-07 18:17:46 -0700409
410 /**
411 * Set the features reply for this switch from the handshake
412 */
413 protected void setFeaturesReply(OFFeaturesReply featuresReply) {
Saurav Dasfcdad072014-08-13 14:15:21 -0700414 if (featuresReply == null) {
415 log.error("Error setting featuresReply for switch: {}", getStringId());
416 return;
417 }
418 this.datapathId = featuresReply.getDatapathId();
419 this.capabilities = featuresReply.getCapabilities();
420 this.buffers = (int) featuresReply.getNBuffers();
421 this.tables = (byte) featuresReply.getNTables();
422 this.stringId = this.datapathId.toString();
423 if (ofversion == OFVersion.OF_13) {
424 auxId = featuresReply.getAuxiliaryId();
425 if (!auxId.equals(OFAuxId.MAIN)) {
426 log.warn("This controller does not handle auxiliary connections. "
427 + "Aux connection id {} received from switch {}",
428 auxId, getStringId());
429 }
430 }
Brian O'Connorc67f9fa2014-08-07 18:17:46 -0700431
Saurav Dasfcdad072014-08-13 14:15:21 -0700432 if (ofversion == OFVersion.OF_10) {
433 this.actions = featuresReply.getActions();
434 portManager.compareAndUpdatePorts(featuresReply.getPorts(), true);
435 }
Brian O'Connorc67f9fa2014-08-07 18:17:46 -0700436 }
437
438 /**
Saurav Dasfcdad072014-08-13 14:15:21 -0700439 * Set the port descriptions for this switch from the handshake for an OF1.3
440 * switch.
Brian O'Connorc67f9fa2014-08-07 18:17:46 -0700441 */
442 protected void setPortDescReply(OFPortDescStatsReply pdrep) {
Saurav Dasfcdad072014-08-13 14:15:21 -0700443 if (ofversion != OFVersion.OF_13)
444 return;
445 if (pdrep == null) {
446 log.error("Error setting ports description for switch: {}", getStringId());
447 return;
448 }
449 portManager.updatePorts(pdrep.getEntries());
Brian O'Connorc67f9fa2014-08-07 18:17:46 -0700450 }
451
452 @Override
453 public int getNumBuffers() {
454 return buffers;
455 }
Saurav Dasfcdad072014-08-13 14:15:21 -0700456
Brian O'Connorc67f9fa2014-08-07 18:17:46 -0700457 @Override
458 public Set<OFActionType> getActions() {
459 return actions;
460 }
Saurav Dasfcdad072014-08-13 14:15:21 -0700461
Brian O'Connorc67f9fa2014-08-07 18:17:46 -0700462 @Override
Saurav Dasfcdad072014-08-13 14:15:21 -0700463 public Set<OFCapabilities> getCapabilities() {
Brian O'Connorc67f9fa2014-08-07 18:17:46 -0700464 return capabilities;
465 }
Saurav Dasfcdad072014-08-13 14:15:21 -0700466
Brian O'Connorc67f9fa2014-08-07 18:17:46 -0700467 @Override
468 public byte getNumTables() {
469 return tables;
470 }
471
Saurav Dasfcdad072014-08-13 14:15:21 -0700472 // public Future<OFFeaturesReply> getFeaturesReplyFromSwitch()
473 // throws IOException {
474 // // XXX S fix this later
475 // OFMessage request = floodlightProvider.getOFMessageFactory_13()
476 // .buildFeaturesRequest()
477 // .setXid(getNextTransactionId())
478 // .build();
479 // OFFeaturesReplyFuture future =
480 // new OFFeaturesReplyFuture(threadPool, this, (int) request.getXid());
481 // this.featuresFutureMap.put((int) request.getXid(), future);
482 // this.channel.write(Collections.singletonList(request));
483 // return future;
484 //
485 // }
486 //
487 // public void deliverOFFeaturesReply(OFMessage reply) {
488 // OFFeaturesReplyFuture future =
489 // this.featuresFutureMap.get(reply.getXid());
490 // if (future != null) {
491 // future.deliverFuture(this, reply);
492 // // The future will ultimately unregister itself and call
493 // // cancelFeaturesReply
494 // return;
495 // }
496 // log.error("Switch {}: received unexpected featureReply", this);
497 // }
Brian O'Connorc67f9fa2014-08-07 18:17:46 -0700498
499 @Override
500 public void cancelFeaturesReply(int transactionId) {
501 this.featuresFutureMap.remove(transactionId);
502 }
503
504 @JsonIgnore
505 public void setSwitchDescription(OFDescStatsReply desc) {
Saurav Dasfcdad072014-08-13 14:15:21 -0700506 switchDescription = desc;
Brian O'Connorc67f9fa2014-08-07 18:17:46 -0700507 }
508
509 @Override
510 @JsonIgnore
511 public OFDescStatsReply getSwitchDescription() {
Saurav Dasfcdad072014-08-13 14:15:21 -0700512 return switchDescription;
Brian O'Connorc67f9fa2014-08-07 18:17:46 -0700513 }
514
Saurav Dasfcdad072014-08-13 14:15:21 -0700515 // *******************************************
516 // Switch port handling
517 // *******************************************
Brian O'Connorc67f9fa2014-08-07 18:17:46 -0700518
519 @Override
520 @JsonIgnore
521 public Collection<OFPortDesc> getEnabledPorts() {
522 return portManager.getEnabledPorts();
523 }
524
525 @Override
526 @JsonIgnore
527 public Collection<Integer> getEnabledPortNumbers() {
528 return portManager.getEnabledPortNumbers();
529 }
530
531 @Override
532 public OFPortDesc getPort(int portNumber) {
533 return portManager.getPort(portNumber);
534 }
535
536 @Override
537 public OFPortDesc getPort(String portName) {
538 return portManager.getPort(portName);
539 }
540
541 @Override
542 @JsonIgnore
543 public OrderedCollection<PortChangeEvent> processOFPortStatus(OFPortStatus ps) {
544 return portManager.handlePortStatusMessage(ps);
545 }
546
547 @Override
548 @JsonProperty("ports")
549 public Collection<OFPortDesc> getPorts() {
Saurav Dasfcdad072014-08-13 14:15:21 -0700550 return portManager.getPorts();
Brian O'Connorc67f9fa2014-08-07 18:17:46 -0700551 }
552
553 @Override
554 public boolean portEnabled(int portNumber) {
555 return isEnabled(portManager.getPort(portNumber));
556 }
557
558 @Override
559 public boolean portEnabled(String portName) {
Saurav Dasfcdad072014-08-13 14:15:21 -0700560 return isEnabled(portManager.getPort(portName));
Brian O'Connorc67f9fa2014-08-07 18:17:46 -0700561 }
562
563 private boolean isEnabled(OFPortDesc p) {
Saurav Dasfcdad072014-08-13 14:15:21 -0700564 return (p != null &&
565 !p.getState().contains(OFPortState.LINK_DOWN) &&
566 !p.getState().contains(OFPortState.BLOCKED) && !p.getConfig().contains(
567 OFPortConfig.PORT_DOWN));
Brian O'Connorc67f9fa2014-08-07 18:17:46 -0700568 }
569
570 @Override
571 public OrderedCollection<PortChangeEvent> comparePorts(Collection<OFPortDesc> ports) {
572 return portManager.comparePorts(ports);
573 }
574
575 @Override
576 @JsonIgnore
577 public OrderedCollection<PortChangeEvent> setPorts(Collection<OFPortDesc> ports) {
578 return portManager.updatePorts(ports);
579 }
580
581 /**
582 * Manages the ports of this switch.
Saurav Dasfcdad072014-08-13 14:15:21 -0700583 *
Brian O'Connorc67f9fa2014-08-07 18:17:46 -0700584 * Provides methods to query and update the stored ports. The class ensures
Saurav Dasfcdad072014-08-13 14:15:21 -0700585 * that every port name and port number is unique. When updating ports the
586 * class checks if port number <-> port name mappings have change due to the
587 * update. If a new port P has number and port that are inconsistent with
588 * the previous mapping(s) the class will delete all previous ports with
589 * name or number of the new port and then add the new port.
590 *
Brian O'Connorc67f9fa2014-08-07 18:17:46 -0700591 * Port names are stored as-is but they are compared case-insensitive
Saurav Dasfcdad072014-08-13 14:15:21 -0700592 *
Brian O'Connorc67f9fa2014-08-07 18:17:46 -0700593 * The methods that change the stored ports return a list of
Saurav Dasfcdad072014-08-13 14:15:21 -0700594 * PortChangeEvents that represent the changes that have been applied to the
595 * port list so that IOFSwitchListeners can be notified about the changes.
596 *
597 * Implementation notes: - We keep several different representations of the
598 * ports to allow for fast lookups - Ports are stored in unchangeable lists.
599 * When a port is modified new data structures are allocated. - We use a
600 * read-write-lock for synchronization, so multiple readers are allowed. -
601 * All port numbers have int representation (no more shorts)
Brian O'Connorc67f9fa2014-08-07 18:17:46 -0700602 */
603 protected class PortManager {
604 private final ReentrantReadWriteLock lock;
605 private List<OFPortDesc> portList;
606 private List<OFPortDesc> enabledPortList;
607 private List<Integer> enabledPortNumbers;
Saurav Dasfcdad072014-08-13 14:15:21 -0700608 private Map<Integer, OFPortDesc> portsByNumber;
609 private Map<String, OFPortDesc> portsByName;
Brian O'Connorc67f9fa2014-08-07 18:17:46 -0700610
611 public PortManager() {
612 this.lock = new ReentrantReadWriteLock();
613 this.portList = Collections.emptyList();
614 this.enabledPortList = Collections.emptyList();
615 this.enabledPortNumbers = Collections.emptyList();
616 this.portsByName = Collections.emptyMap();
617 this.portsByNumber = Collections.emptyMap();
618 }
619
620 /**
Saurav Dasfcdad072014-08-13 14:15:21 -0700621 * Set the internal data structure storing this switch's port to the
622 * ports specified by newPortsByNumber
623 *
Brian O'Connorc67f9fa2014-08-07 18:17:46 -0700624 * CALLER MUST HOLD WRITELOCK
Saurav Dasfcdad072014-08-13 14:15:21 -0700625 *
Brian O'Connorc67f9fa2014-08-07 18:17:46 -0700626 * @param newPortsByNumber
627 * @throws IllegaalStateException if called without holding the
Saurav Dasfcdad072014-08-13 14:15:21 -0700628 * writelock
Brian O'Connorc67f9fa2014-08-07 18:17:46 -0700629 */
630 private void updatePortsWithNewPortsByNumber(
Saurav Dasfcdad072014-08-13 14:15:21 -0700631 Map<Integer, OFPortDesc> newPortsByNumber) {
Brian O'Connorc67f9fa2014-08-07 18:17:46 -0700632 if (!lock.writeLock().isHeldByCurrentThread()) {
633 throw new IllegalStateException("Method called without " +
Saurav Dasfcdad072014-08-13 14:15:21 -0700634 "holding writeLock");
Brian O'Connorc67f9fa2014-08-07 18:17:46 -0700635 }
Saurav Dasfcdad072014-08-13 14:15:21 -0700636 Map<String, OFPortDesc> newPortsByName =
Brian O'Connorc67f9fa2014-08-07 18:17:46 -0700637 new HashMap<String, OFPortDesc>();
638 List<OFPortDesc> newPortList =
639 new ArrayList<OFPortDesc>();
640 List<OFPortDesc> newEnabledPortList =
641 new ArrayList<OFPortDesc>();
642 List<Integer> newEnabledPortNumbers = new ArrayList<Integer>();
643
Saurav Dasfcdad072014-08-13 14:15:21 -0700644 for (OFPortDesc p : newPortsByNumber.values()) {
Brian O'Connorc67f9fa2014-08-07 18:17:46 -0700645 newPortList.add(p);
646 newPortsByName.put(p.getName().toLowerCase(), p);
647 if (isEnabled(p)) {
648 newEnabledPortList.add(p);
649 newEnabledPortNumbers.add(p.getPortNo().getPortNumber());
650 }
651 }
652 portsByName = Collections.unmodifiableMap(newPortsByName);
653 portsByNumber =
654 Collections.unmodifiableMap(newPortsByNumber);
655 enabledPortList =
656 Collections.unmodifiableList(newEnabledPortList);
657 enabledPortNumbers =
658 Collections.unmodifiableList(newEnabledPortNumbers);
659 portList = Collections.unmodifiableList(newPortList);
660 }
661
662 /**
Saurav Dasfcdad072014-08-13 14:15:21 -0700663 * Handle a OFPortStatus delete message for the given port. Updates the
664 * internal port maps/lists of this switch and returns the
665 * PortChangeEvents caused by the delete. If the given port exists as
666 * it, it will be deleted. If the name<->number for the given port is
667 * inconsistent with the ports stored by this switch the method will
668 * delete all ports with the number or name of the given port.
669 *
Brian O'Connorc67f9fa2014-08-07 18:17:46 -0700670 * This method will increment error/warn counters and log
Saurav Dasfcdad072014-08-13 14:15:21 -0700671 *
672 * @param delPort the port from the port status message that should be
673 * deleted.
Brian O'Connorc67f9fa2014-08-07 18:17:46 -0700674 * @return ordered collection of port changes applied to this switch
675 */
676 private OrderedCollection<PortChangeEvent> handlePortStatusDelete(
Saurav Dasfcdad072014-08-13 14:15:21 -0700677 OFPortDesc delPort) {
Brian O'Connorc67f9fa2014-08-07 18:17:46 -0700678 lock.writeLock().lock();
679 OrderedCollection<PortChangeEvent> events =
680 new LinkedHashSetWrapper<PortChangeEvent>();
681 try {
Saurav Dasfcdad072014-08-13 14:15:21 -0700682 Map<Integer, OFPortDesc> newPortByNumber =
Brian O'Connorc67f9fa2014-08-07 18:17:46 -0700683 new HashMap<Integer, OFPortDesc>(portsByNumber);
684 OFPortDesc prevPort =
685 portsByNumber.get(delPort.getPortNo().getPortNumber());
686 if (prevPort == null) {
687 // so such port. Do we have a port with the name?
688 prevPort = portsByName.get(delPort.getName());
689 if (prevPort != null) {
690 newPortByNumber.remove(prevPort.getPortNo().getPortNumber());
691 events.add(new PortChangeEvent(prevPort,
Saurav Dasfcdad072014-08-13 14:15:21 -0700692 PortChangeType.DELETE));
Brian O'Connorc67f9fa2014-08-07 18:17:46 -0700693 }
694 } else if (prevPort.getName().equals(delPort.getName())) {
695 // port exists with consistent name-number mapping
696 newPortByNumber.remove(delPort.getPortNo().getPortNumber());
697 events.add(new PortChangeEvent(delPort,
Saurav Dasfcdad072014-08-13 14:15:21 -0700698 PortChangeType.DELETE));
Brian O'Connorc67f9fa2014-08-07 18:17:46 -0700699 } else {
700 // port with same number exists but its name differs. This
701 // is weird. The best we can do is to delete the existing
702 // port(s) that have delPort's name and number.
703 newPortByNumber.remove(delPort.getPortNo().getPortNumber());
704 events.add(new PortChangeEvent(prevPort,
Saurav Dasfcdad072014-08-13 14:15:21 -0700705 PortChangeType.DELETE));
Brian O'Connorc67f9fa2014-08-07 18:17:46 -0700706 // is there another port that has delPort's name?
707 prevPort = portsByName.get(delPort.getName().toLowerCase());
708 if (prevPort != null) {
709 newPortByNumber.remove(prevPort.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 }
713 }
714 updatePortsWithNewPortsByNumber(newPortByNumber);
715 return events;
716 } finally {
717 lock.writeLock().unlock();
718 }
719 }
720
721 /**
722 * Handle a OFPortStatus message, update the internal data structures
723 * that store ports and return the list of OFChangeEvents.
Saurav Dasfcdad072014-08-13 14:15:21 -0700724 *
Brian O'Connorc67f9fa2014-08-07 18:17:46 -0700725 * This method will increment error/warn counters and log
Saurav Dasfcdad072014-08-13 14:15:21 -0700726 *
Brian O'Connorc67f9fa2014-08-07 18:17:46 -0700727 * @param ps
728 * @return
729 */
730 public OrderedCollection<PortChangeEvent> handlePortStatusMessage(OFPortStatus ps) {
731 if (ps == null) {
732 throw new NullPointerException("OFPortStatus message must " +
Saurav Dasfcdad072014-08-13 14:15:21 -0700733 "not be null");
Brian O'Connorc67f9fa2014-08-07 18:17:46 -0700734 }
735 lock.writeLock().lock();
736 try {
737 OFPortReason reason = ps.getReason();
738 if (reason == null) {
739 throw new IllegalArgumentException("Unknown PortStatus " +
740 "reason code " + ps.getReason());
741 }
742
743 if (log.isDebugEnabled()) {
744 log.debug("Handling OFPortStatus: {} for {}",
Saurav Dasfcdad072014-08-13 14:15:21 -0700745 reason, ps);
Brian O'Connorc67f9fa2014-08-07 18:17:46 -0700746 }
747
748 if (reason == OFPortReason.DELETE)
Saurav Dasfcdad072014-08-13 14:15:21 -0700749 return handlePortStatusDelete(ps.getDesc());
Brian O'Connorc67f9fa2014-08-07 18:17:46 -0700750
751 // We handle ADD and MODIFY the same way. Since OpenFlow
752 // doesn't specify what uniquely identifies a port the
753 // notion of ADD vs. MODIFY can also be hazy. So we just
754 // compare the new port to the existing ones.
Saurav Dasfcdad072014-08-13 14:15:21 -0700755 Map<Integer, OFPortDesc> newPortByNumber =
756 new HashMap<Integer, OFPortDesc>(portsByNumber);
Brian O'Connorc67f9fa2014-08-07 18:17:46 -0700757 OrderedCollection<PortChangeEvent> events =
Saurav Dasfcdad072014-08-13 14:15:21 -0700758 getSinglePortChanges(ps.getDesc());
759 for (PortChangeEvent e : events) {
760 switch (e.type) {
761 case DELETE:
762 newPortByNumber.remove(e.port.getPortNo().getPortNumber());
763 break;
764 case ADD:
765 if (reason != OFPortReason.ADD) {
766 // weird case
767 }
768 newPortByNumber.put(e.port.getPortNo().getPortNumber(),
769 e.port);
770 break;
771 case DOWN:
772 newPortByNumber.put(e.port.getPortNo().getPortNumber(),
773 e.port);
774 break;
775 case OTHER_UPDATE:
776 newPortByNumber.put(e.port.getPortNo().getPortNumber(),
777 e.port);
778 break;
779 case UP:
780 newPortByNumber.put(e.port.getPortNo().getPortNumber(),
781 e.port);
782 break;
Brian O'Connorc67f9fa2014-08-07 18:17:46 -0700783 }
784 }
785 updatePortsWithNewPortsByNumber(newPortByNumber);
786 return events;
787 } finally {
788 lock.writeLock().unlock();
789 }
790
791 }
792
793 /**
794 * Given a new or modified port newPort, returns the list of
Saurav Dasfcdad072014-08-13 14:15:21 -0700795 * PortChangeEvents to "transform" the current ports stored by this
796 * switch to include / represent the new port. The ports stored by this
797 * switch are <b>NOT</b> updated.
798 *
799 * This method acquires the readlock and is thread-safe by itself. Most
800 * callers will need to acquire the write lock before calling this
801 * method though (if the caller wants to update the ports stored by this
802 * switch)
803 *
Brian O'Connorc67f9fa2014-08-07 18:17:46 -0700804 * @param newPort the new or modified port.
805 * @return the list of changes
806 */
807 public OrderedCollection<PortChangeEvent> getSinglePortChanges(
Saurav Dasfcdad072014-08-13 14:15:21 -0700808 OFPortDesc newPort) {
Brian O'Connorc67f9fa2014-08-07 18:17:46 -0700809 lock.readLock().lock();
810 try {
811 OrderedCollection<PortChangeEvent> events =
812 new LinkedHashSetWrapper<PortChangeEvent>();
813 // Check if we have a port by the same number in our
814 // old map.
815 OFPortDesc prevPort =
816 portsByNumber.get(newPort.getPortNo().getPortNumber());
817 if (newPort.equals(prevPort)) {
818 // nothing has changed
819 return events;
820 }
821
822 if (prevPort != null &&
823 prevPort.getName().equals(newPort.getName())) {
824 // A simple modify of a existing port
825 // A previous port with this number exists and it's name
826 // also matches the new port. Find the differences
827 if (isEnabled(prevPort) && !isEnabled(newPort)) {
828 events.add(new PortChangeEvent(newPort,
Saurav Dasfcdad072014-08-13 14:15:21 -0700829 PortChangeType.DOWN));
Brian O'Connorc67f9fa2014-08-07 18:17:46 -0700830 } else if (!isEnabled(prevPort) && isEnabled(newPort)) {
831 events.add(new PortChangeEvent(newPort,
Saurav Dasfcdad072014-08-13 14:15:21 -0700832 PortChangeType.UP));
Brian O'Connorc67f9fa2014-08-07 18:17:46 -0700833 } else {
834 events.add(new PortChangeEvent(newPort,
Saurav Dasfcdad072014-08-13 14:15:21 -0700835 PortChangeType.OTHER_UPDATE));
Brian O'Connorc67f9fa2014-08-07 18:17:46 -0700836 }
837 return events;
838 }
839
840 if (prevPort != null) {
841 // There exists a previous port with the same port
842 // number but the port name is different (otherwise we would
843 // never have gotten here)
844 // Remove the port. Name-number mapping(s) have changed
845 events.add(new PortChangeEvent(prevPort,
Saurav Dasfcdad072014-08-13 14:15:21 -0700846 PortChangeType.DELETE));
Brian O'Connorc67f9fa2014-08-07 18:17:46 -0700847 }
848
849 // We now need to check if there exists a previous port sharing
850 // the same name as the new/updated port.
851 prevPort = portsByName.get(newPort.getName().toLowerCase());
852 if (prevPort != null) {
853 // There exists a previous port with the same port
854 // name but the port number is different (otherwise we
855 // never have gotten here).
856 // Remove the port. Name-number mapping(s) have changed
857 events.add(new PortChangeEvent(prevPort,
Saurav Dasfcdad072014-08-13 14:15:21 -0700858 PortChangeType.DELETE));
Brian O'Connorc67f9fa2014-08-07 18:17:46 -0700859 }
860
861 // We always need to add the new port. Either no previous port
862 // existed or we just deleted previous ports with inconsistent
863 // name-number mappings
864 events.add(new PortChangeEvent(newPort, PortChangeType.ADD));
865 return events;
866 } finally {
867 lock.readLock().unlock();
868 }
869 }
870
871 /**
872 * Compare the current ports of this switch to the newPorts list and
873 * return the changes that would be applied to transfort the current
Saurav Dasfcdad072014-08-13 14:15:21 -0700874 * ports to the new ports. No internal data structures are updated see
875 * {@link #compareAndUpdatePorts(List, boolean)}
876 *
Brian O'Connorc67f9fa2014-08-07 18:17:46 -0700877 * @param newPorts the list of new ports
878 * @return The list of differences between the current ports and
Saurav Dasfcdad072014-08-13 14:15:21 -0700879 * newPortList
Brian O'Connorc67f9fa2014-08-07 18:17:46 -0700880 */
881 public OrderedCollection<PortChangeEvent> comparePorts(
Saurav Dasfcdad072014-08-13 14:15:21 -0700882 Collection<OFPortDesc> newPorts) {
Brian O'Connorc67f9fa2014-08-07 18:17:46 -0700883 return compareAndUpdatePorts(newPorts, false);
884 }
885
886 /**
887 * Compare the current ports of this switch to the newPorts list and
888 * return the changes that would be applied to transform the current
Saurav Dasfcdad072014-08-13 14:15:21 -0700889 * ports to the new ports. No internal data structures are updated see
890 * {@link #compareAndUpdatePorts(List, boolean)}
891 *
Brian O'Connorc67f9fa2014-08-07 18:17:46 -0700892 * @param newPorts the list of new ports
893 * @return The list of differences between the current ports and
Saurav Dasfcdad072014-08-13 14:15:21 -0700894 * newPortList
Brian O'Connorc67f9fa2014-08-07 18:17:46 -0700895 */
896 public OrderedCollection<PortChangeEvent> updatePorts(
Saurav Dasfcdad072014-08-13 14:15:21 -0700897 Collection<OFPortDesc> newPorts) {
Brian O'Connorc67f9fa2014-08-07 18:17:46 -0700898 return compareAndUpdatePorts(newPorts, true);
899 }
900
901 /**
Saurav Dasfcdad072014-08-13 14:15:21 -0700902 * Compare the current ports stored in this switch instance with the new
903 * port list given and return the differences in the form of
Brian O'Connorc67f9fa2014-08-07 18:17:46 -0700904 * PortChangeEvents. If the doUpdate flag is true, newPortList will
905 * replace the current list of this switch (and update the port maps)
Saurav Dasfcdad072014-08-13 14:15:21 -0700906 *
907 * Implementation note: Since this method can optionally modify the
908 * current ports and since it's not possible to upgrade a read-lock to a
909 * write-lock we need to hold the write-lock for the entire operation.
910 * If this becomes a problem and if compares() are common we can
911 * consider splitting in two methods but this requires lots of code
912 * duplication
913 *
Brian O'Connorc67f9fa2014-08-07 18:17:46 -0700914 * @param newPorts the list of new ports.
Saurav Dasfcdad072014-08-13 14:15:21 -0700915 * @param doUpdate If true the newPortList will replace the current port
916 * list for this switch. If false this switch will not be
917 * changed.
Brian O'Connorc67f9fa2014-08-07 18:17:46 -0700918 * @return The list of differences between the current ports and
Saurav Dasfcdad072014-08-13 14:15:21 -0700919 * newPorts
Brian O'Connorc67f9fa2014-08-07 18:17:46 -0700920 * @throws NullPointerException if newPortsList is null
921 * @throws IllegalArgumentException if either port names or port numbers
Saurav Dasfcdad072014-08-13 14:15:21 -0700922 * are duplicated in the newPortsList.
Brian O'Connorc67f9fa2014-08-07 18:17:46 -0700923 */
924 private OrderedCollection<PortChangeEvent> compareAndUpdatePorts(
925 Collection<OFPortDesc> newPorts, boolean doUpdate) {
926 if (newPorts == null) {
927 throw new NullPointerException("newPortsList must not be null");
928 }
929 lock.writeLock().lock();
930 try {
931 OrderedCollection<PortChangeEvent> events =
932 new LinkedHashSetWrapper<PortChangeEvent>();
933
Saurav Dasfcdad072014-08-13 14:15:21 -0700934 Map<Integer, OFPortDesc> newPortsByNumber =
Brian O'Connorc67f9fa2014-08-07 18:17:46 -0700935 new HashMap<Integer, OFPortDesc>();
Saurav Dasfcdad072014-08-13 14:15:21 -0700936 Map<String, OFPortDesc> newPortsByName =
Brian O'Connorc67f9fa2014-08-07 18:17:46 -0700937 new HashMap<String, OFPortDesc>();
938 List<OFPortDesc> newEnabledPortList =
939 new ArrayList<OFPortDesc>();
940 List<Integer> newEnabledPortNumbers =
941 new ArrayList<Integer>();
942 List<OFPortDesc> newPortsList =
943 new ArrayList<OFPortDesc>(newPorts);
944
Saurav Dasfcdad072014-08-13 14:15:21 -0700945 for (OFPortDesc p : newPortsList) {
Brian O'Connorc67f9fa2014-08-07 18:17:46 -0700946 if (p == null) {
947 throw new NullPointerException("portList must not " +
948 "contain null values");
949 }
950
951 // Add the port to the new maps and lists and check
952 // that every port is unique
953 OFPortDesc duplicatePort;
954 duplicatePort = newPortsByNumber.put(
Saurav Dasfcdad072014-08-13 14:15:21 -0700955 p.getPortNo().getPortNumber(), p);
Brian O'Connorc67f9fa2014-08-07 18:17:46 -0700956 if (duplicatePort != null) {
957 String msg = String.format("Cannot have two ports " +
958 "with the same number: %s <-> %s",
959 p, duplicatePort);
960 throw new IllegalArgumentException(msg);
961 }
962 duplicatePort =
963 newPortsByName.put(p.getName().toLowerCase(), p);
964 if (duplicatePort != null) {
965 String msg = String.format("Cannot have two ports " +
966 "with the same name: %s <-> %s",
Saurav Dasfcdad072014-08-13 14:15:21 -0700967 p.toString().substring(0, 80),
Brian O'Connorc67f9fa2014-08-07 18:17:46 -0700968 duplicatePort.toString().substring(0, 80));
969 throw new IllegalArgumentException(msg);
970 }
971 if (isEnabled(p)) {
972 newEnabledPortList.add(p);
973 newEnabledPortNumbers.add(p.getPortNo().getPortNumber());
974 }
975
976 // get changes
977 events.addAll(getSinglePortChanges(p));
978 }
979 // find deleted ports
980 // We need to do this after looping through all the new ports
981 // to we can handle changed name<->number mappings correctly
982 // We could pull it into the loop of we address this but
983 // it's probably not worth it
Saurav Dasfcdad072014-08-13 14:15:21 -0700984 for (OFPortDesc oldPort : this.portList) {
985 if (!newPortsByNumber
986 .containsKey(oldPort.getPortNo().getPortNumber())) {
Brian O'Connorc67f9fa2014-08-07 18:17:46 -0700987 PortChangeEvent ev =
988 new PortChangeEvent(oldPort,
Saurav Dasfcdad072014-08-13 14:15:21 -0700989 PortChangeType.DELETE);
Brian O'Connorc67f9fa2014-08-07 18:17:46 -0700990 events.add(ev);
991 }
992 }
993
Brian O'Connorc67f9fa2014-08-07 18:17:46 -0700994 if (doUpdate) {
995 portsByName = Collections.unmodifiableMap(newPortsByName);
996 portsByNumber =
997 Collections.unmodifiableMap(newPortsByNumber);
998 enabledPortList =
999 Collections.unmodifiableList(newEnabledPortList);
1000 enabledPortNumbers =
1001 Collections.unmodifiableList(newEnabledPortNumbers);
1002 portList = Collections.unmodifiableList(newPortsList);
1003 }
1004 return events;
1005 } finally {
1006 lock.writeLock().unlock();
1007 }
1008 }
1009
1010 public OFPortDesc getPort(String name) {
1011 if (name == null) {
1012 throw new NullPointerException("Port name must not be null");
1013 }
1014 lock.readLock().lock();
1015 try {
1016 return portsByName.get(name.toLowerCase());
1017 } finally {
1018 lock.readLock().unlock();
1019 }
1020 }
1021
1022 public OFPortDesc getPort(int portNumber) {
1023 lock.readLock().lock();
1024 try {
1025 return portsByNumber.get(portNumber);
1026 } finally {
1027 lock.readLock().unlock();
1028 }
1029 }
1030
1031 public List<OFPortDesc> getPorts() {
1032 lock.readLock().lock();
1033 try {
1034 return portList;
1035 } finally {
1036 lock.readLock().unlock();
1037 }
1038 }
1039
1040 public List<OFPortDesc> getEnabledPorts() {
1041 lock.readLock().lock();
1042 try {
1043 return enabledPortList;
1044 } finally {
1045 lock.readLock().unlock();
1046 }
1047 }
1048
1049 public List<Integer> getEnabledPortNumbers() {
1050 lock.readLock().lock();
1051 try {
1052 return enabledPortNumbers;
1053 } finally {
1054 lock.readLock().unlock();
1055 }
1056 }
1057 }
1058
Saurav Dasfcdad072014-08-13 14:15:21 -07001059 // *******************************************
1060 // Switch stats handling
1061 // *******************************************
Brian O'Connorc67f9fa2014-08-07 18:17:46 -07001062
1063 @Override
1064 public Future<List<OFStatsReply>> getStatistics(OFStatsRequest<?> request)
Saurav Dasfcdad072014-08-13 14:15:21 -07001065 throws IOException {
Brian O'Connorc67f9fa2014-08-07 18:17:46 -07001066 OFStatisticsFuture future = new OFStatisticsFuture(threadPool, this,
Saurav Dasfcdad072014-08-13 14:15:21 -07001067 (int) request.getXid());
Brian O'Connorc67f9fa2014-08-07 18:17:46 -07001068 log.info("description STAT REQUEST XID {}", request.getXid());
Saurav Dasfcdad072014-08-13 14:15:21 -07001069 this.statsFutureMap.put((int) request.getXid(), future);
Brian O'Connorc67f9fa2014-08-07 18:17:46 -07001070
1071 this.channel.write(Collections.singletonList(request));
1072 return future;
1073 }
1074
1075 @Override
1076 public void deliverStatisticsReply(OFMessage reply) {
Saurav Dasfcdad072014-08-13 14:15:21 -07001077 OFStatisticsFuture future = this.statsFutureMap.get((int) reply.getXid());
Brian O'Connorc67f9fa2014-08-07 18:17:46 -07001078 if (future != null) {
1079 future.deliverFuture(this, reply);
1080 // The future will ultimately unregister itself and call
1081 // cancelStatisticsReply
1082 return;
1083 }
1084 // Transaction id was not found in statsFutureMap.check the other map
1085 IOFMessageListener caller = this.iofMsgListenersMap.get(reply.getXid());
1086 if (caller != null) {
Saurav Dasfcdad072014-08-13 14:15:21 -07001087 caller.receive(this, reply, null);
Brian O'Connorc67f9fa2014-08-07 18:17:46 -07001088 }
1089 }
1090
1091 @Override
1092 public void cancelStatisticsReply(int transactionId) {
1093 if (null == this.statsFutureMap.remove(transactionId)) {
1094 this.iofMsgListenersMap.remove(transactionId);
1095 }
1096 }
1097
1098 @Override
1099 public void cancelAllStatisticsReplies() {
1100 /* we don't need to be synchronized here. Even if another thread
1101 * modifies the map while we're cleaning up the future will eventuall
1102 * timeout */
1103 for (OFStatisticsFuture f : statsFutureMap.values()) {
1104 f.cancel(true);
1105 }
1106 statsFutureMap.clear();
1107 iofMsgListenersMap.clear();
1108 }
1109
Saurav Dasfcdad072014-08-13 14:15:21 -07001110 // *******************************************
1111 // Switch role handling
1112 // *******************************************
Brian O'Connorc67f9fa2014-08-07 18:17:46 -07001113
1114 // XXX S this is completely wrong. The generation id should be obtained
1115 // after coordinating with all controllers connected to this switch.
1116 // ie. should be part of registry service (account for 1.3 vs 1.0)
1117 // For now we are just generating this locally and keeping it constant.
1118 public U64 getNextGenerationId() {
Saurav Dasfcdad072014-08-13 14:15:21 -07001119 // TODO: Pankaj, fix nextGenerationId as part of Registry interface
1120 return U64.of(generationIdSource);
Brian O'Connorc67f9fa2014-08-07 18:17:46 -07001121 }
1122
1123 @Override
1124 public Role getRole() {
1125 return role;
1126 }
1127
1128 @JsonIgnore
1129 @Override
1130 public void setRole(Role role) {
1131 this.role = role;
1132 }
1133
Saurav Dasfcdad072014-08-13 14:15:21 -07001134 // *******************************************
1135 // Switch utility methods
1136 // *******************************************
Brian O'Connorc67f9fa2014-08-07 18:17:46 -07001137
1138 private void registerOverloadCounters() throws CounterException {
1139 if (debugCountersRegistered) {
1140 return;
1141 }
1142 if (debugCounters == null) {
1143 log.error("Debug Counter Service not found");
1144 debugCounters = new NullDebugCounter();
1145 debugCountersRegistered = true;
1146 }
1147 // every level of the hierarchical counter has to be registered
1148 // even if they are not used
1149 ctrSwitch = debugCounters.registerCounter(
Saurav Dasfcdad072014-08-13 14:15:21 -07001150 BASE, stringId,
1151 "Counter for this switch",
1152 CounterType.ALWAYS_COUNT);
Brian O'Connorc67f9fa2014-08-07 18:17:46 -07001153 ctrSwitchPktin = debugCounters.registerCounter(
Saurav Dasfcdad072014-08-13 14:15:21 -07001154 BASE, stringId + "/pktin",
1155 "Packet in counter for this switch",
1156 CounterType.ALWAYS_COUNT);
Brian O'Connorc67f9fa2014-08-07 18:17:46 -07001157 ctrSwitchWrite = debugCounters.registerCounter(
Saurav Dasfcdad072014-08-13 14:15:21 -07001158 BASE, stringId + "/write",
1159 "Write counter for this switch",
1160 CounterType.ALWAYS_COUNT);
Brian O'Connorc67f9fa2014-08-07 18:17:46 -07001161 ctrSwitchPktinDrops = debugCounters.registerCounter(
Saurav Dasfcdad072014-08-13 14:15:21 -07001162 BASE, stringId + "/pktin/drops",
1163 "Packet in throttle drop count",
1164 CounterType.ALWAYS_COUNT,
1165 IDebugCounterService.CTR_MDATA_WARN);
Brian O'Connorc67f9fa2014-08-07 18:17:46 -07001166 ctrSwitchWriteDrops = debugCounters.registerCounter(
Saurav Dasfcdad072014-08-13 14:15:21 -07001167 BASE, stringId + "/write/drops",
1168 "Switch write throttle drop count",
1169 CounterType.ALWAYS_COUNT,
1170 IDebugCounterService.CTR_MDATA_WARN);
Brian O'Connorc67f9fa2014-08-07 18:17:46 -07001171 }
1172
1173 @Override
1174 public void startDriverHandshake() throws IOException {
Saurav Dasfcdad072014-08-13 14:15:21 -07001175 log.debug("Starting driver handshake for sw {}", getStringId());
Brian O'Connorc67f9fa2014-08-07 18:17:46 -07001176 if (startDriverHandshakeCalled)
1177 throw new SwitchDriverSubHandshakeAlreadyStarted();
1178 startDriverHandshakeCalled = true;
1179 }
1180
1181 @Override
1182 public boolean isDriverHandshakeComplete() {
1183 if (!startDriverHandshakeCalled)
1184 throw new SwitchDriverSubHandshakeNotStarted();
1185 return true;
1186 }
1187
1188 @Override
1189 public void processDriverHandshakeMessage(OFMessage m) {
1190 if (startDriverHandshakeCalled)
1191 throw new SwitchDriverSubHandshakeCompleted(m);
1192 else
1193 throw new SwitchDriverSubHandshakeNotStarted();
1194 }
1195
1196 @Override
1197 @JsonIgnore
Saurav Dasfcdad072014-08-13 14:15:21 -07001198 @LogMessageDoc(level = "WARN",
1199 message = "Switch {switch} flow table is full",
1200 explanation = "The controller received flow table full " +
1201 "message from the switch, could be caused by increased " +
1202 "traffic pattern",
1203 recommendation = LogMessageDoc.REPORT_CONTROLLER_BUG)
Brian O'Connorc67f9fa2014-08-07 18:17:46 -07001204 public void setTableFull(boolean isFull) {
1205 if (isFull && !flowTableFull) {
1206 floodlightProvider.addSwitchEvent(this.datapathId.getLong(),
1207 "SWITCH_FLOW_TABLE_FULL " +
Saurav Dasfcdad072014-08-13 14:15:21 -07001208 "Table full error from switch", false);
Brian O'Connorc67f9fa2014-08-07 18:17:46 -07001209 log.warn("Switch {} flow table is full", stringId);
1210 }
Saurav Dasfcdad072014-08-13 14:15:21 -07001211 flowTableFull = isFull;
Brian O'Connorc67f9fa2014-08-07 18:17:46 -07001212 }
1213
1214 @Override
1215 @LogMessageDoc(level = "ERROR",
1216 message = "Failed to clear all flows on switch {switch}",
1217 explanation = "An I/O error occured while trying to clear " +
1218 "flows on the switch.",
1219 recommendation = LogMessageDoc.CHECK_SWITCH)
1220 public void clearAllFlowMods() {
1221 // Delete all pre-existing flows
1222
Jonathan Harta213bce2014-08-11 15:44:07 -07001223 // by default if match is not specified, then an empty list of matches
1224 // is sent, resulting in a wildcard-all flows
1225 // XXX fix this later to be sure it works for both 1.0 and 1.3
1226 OFMessage fm = getFactory()
Saurav Dasfcdad072014-08-13 14:15:21 -07001227 .buildFlowDelete()
1228 .setOutPort(OFPort.ANY)
1229 .setOutGroup(OFGroup.ANY)
1230 .setTableId(TableId.ALL)
1231 .build();
Brian O'Connorc67f9fa2014-08-07 18:17:46 -07001232
1233 try {
1234 channel.write(Collections.singletonList(fm));
1235 } catch (Exception e) {
1236 log.error("Failed to clear all flows on switch " + this, e);
1237 }
1238 }
1239
1240 @Override
1241 public void flush() {
1242 Map<OFSwitchImplBase, List<OFMessage>> msg_buffer_map = local_msg_buffer.get();
1243 List<OFMessage> msglist = msg_buffer_map.get(this);
1244 if ((msglist != null) && (msglist.size() > 0)) {
1245 try {
1246 this.write(msglist);
1247 } catch (IOException e) {
Saurav Dasfcdad072014-08-13 14:15:21 -07001248 log.error("Failed flushing messages", e);
Brian O'Connorc67f9fa2014-08-07 18:17:46 -07001249 }
1250 msglist.clear();
1251 }
1252 }
1253
1254 public static void flush_all() {
1255 Map<OFSwitchImplBase, List<OFMessage>> msg_buffer_map = local_msg_buffer.get();
1256 for (OFSwitchImplBase sw : msg_buffer_map.keySet()) {
1257 sw.flush();
1258 }
1259 }
1260
1261 /**
1262 * Return a read lock that must be held while calling the listeners for
1263 * messages from the switch. Holding the read lock prevents the active
1264 * switch list from being modified out from under the listeners.
Saurav Dasfcdad072014-08-13 14:15:21 -07001265 *
Brian O'Connorc67f9fa2014-08-07 18:17:46 -07001266 * @return listener read lock
1267 */
1268 @JsonIgnore
1269 public Lock getListenerReadLock() {
1270 return listenerLock.readLock();
1271 }
1272
1273 /**
1274 * Return a write lock that must be held when the controllers modifies the
1275 * list of active switches. This is to ensure that the active switch list
1276 * doesn't change out from under the listeners as they are handling a
1277 * message from the switch.
Saurav Dasfcdad072014-08-13 14:15:21 -07001278 *
Brian O'Connorc67f9fa2014-08-07 18:17:46 -07001279 * @return listener write lock
1280 */
1281 @JsonIgnore
1282 public Lock getListenerWriteLock() {
1283 return listenerLock.writeLock();
1284 }
1285}