blob: 30b8059b9ef5707be1ff749521eed98394274e67 [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;
Jonathan Hart23daf192014-08-22 10:56:58 -070067import org.projectfloodlight.openflow.protocol.OFFlowDelete.Builder;
Brian O'Connorc67f9fa2014-08-07 18:17:46 -070068import org.projectfloodlight.openflow.protocol.OFMessage;
69import org.projectfloodlight.openflow.protocol.OFPortConfig;
70import org.projectfloodlight.openflow.protocol.OFPortDesc;
71import org.projectfloodlight.openflow.protocol.OFPortDescStatsReply;
72import org.projectfloodlight.openflow.protocol.OFPortReason;
73import org.projectfloodlight.openflow.protocol.OFPortState;
74import org.projectfloodlight.openflow.protocol.OFPortStatus;
75import org.projectfloodlight.openflow.protocol.OFStatsReply;
76import org.projectfloodlight.openflow.protocol.OFStatsRequest;
77import org.projectfloodlight.openflow.protocol.OFType;
78import org.projectfloodlight.openflow.protocol.OFVersion;
79import org.projectfloodlight.openflow.types.DatapathId;
80import org.projectfloodlight.openflow.types.OFAuxId;
81import org.projectfloodlight.openflow.types.OFGroup;
82import org.projectfloodlight.openflow.types.OFPort;
83import org.projectfloodlight.openflow.types.TableId;
84import org.projectfloodlight.openflow.types.U64;
85import org.slf4j.Logger;
86import org.slf4j.LoggerFactory;
87
Brian O'Connorc67f9fa2014-08-07 18:17:46 -070088/**
89 * This is the internal representation of an openflow switch.
90 */
91public class OFSwitchImplBase implements IOFSwitch {
92 // TODO: should we really do logging in the class or should we throw
93 // exception that can then be handled by callers?
94 protected final static Logger log = LoggerFactory.getLogger(OFSwitchImplBase.class);
95
96 private static final String HA_CHECK_SWITCH =
97 "Check the health of the indicated switch. If the problem " +
98 "persists or occurs repeatedly, it likely indicates a defect " +
99 "in the switch HA implementation.";
100
101 protected ConcurrentMap<Object, Object> attributes;
102 protected IFloodlightProviderService floodlightProvider;
103 protected IThreadPoolService threadPool;
104 protected Date connectedSince;
105 protected String stringId;
106 protected Channel channel;
107 // transaction id used for messages sent out to this switch from
108 // this controller instance. This xid has significance only between this
109 // controller<->switch pair.
110 protected AtomicInteger transactionIdSource;
111
112 // generation id used for roleRequest messages sent to switches (see section
113 // 6.3.5 of the OF1.3.4 spec). This generationId has significance between
114 // all the controllers that this switch is connected to; and only for role
115 // request messages with role MASTER or SLAVE. The set of Controllers that
116 // this switch is connected to should coordinate the next generation id,
117 // via transactional semantics.
118 protected long generationIdSource;
119
120 // Lock to protect modification of the port maps. We only need to
121 // synchronize on modifications. For read operations we are fine since
122 // we rely on ConcurrentMaps which works for our use case.
Saurav Dasfcdad072014-08-13 14:15:21 -0700123 // private Object portLock; XXX S remove this
Brian O'Connorc67f9fa2014-08-07 18:17:46 -0700124
125 // Map port numbers to the appropriate OFPortDesc
126 protected ConcurrentHashMap<Integer, OFPortDesc> portsByNumber;
127 // Map port names to the appropriate OFPhyiscalPort
128 // XXX: The OF spec doesn't specify if port names need to be unique but
Saurav Dasfcdad072014-08-13 14:15:21 -0700129 // according it's always the case in practice.
Brian O'Connorc67f9fa2014-08-07 18:17:46 -0700130 protected ConcurrentHashMap<String, OFPortDesc> portsByName;
131 protected Map<Integer, OFStatisticsFuture> statsFutureMap;
Saurav Dasfcdad072014-08-13 14:15:21 -0700132 // XXX Consider removing the following 2 maps - not used anymore
133 protected Map<Integer, IOFMessageListener> iofMsgListenersMap;
Brian O'Connorc67f9fa2014-08-07 18:17:46 -0700134 protected Map<Integer, OFFeaturesReplyFuture> featuresFutureMap;
135 protected boolean connected;
136 protected Role role;
Brian O'Connorc67f9fa2014-08-07 18:17:46 -0700137 protected ReentrantReadWriteLock listenerLock;
138 protected ConcurrentMap<Short, Long> portBroadcastCacheHitMap;
139 /**
Saurav Dasfcdad072014-08-13 14:15:21 -0700140 * When sending a role request message, the role request is added to this
141 * queue. If a role reply is received this queue is checked to verify that
142 * the reply matches the expected reply. We require in order delivery of
143 * replies. That's why we use a Queue. The RoleChanger uses a timeout to
144 * ensure we receive a timely reply.
Brian O'Connorc67f9fa2014-08-07 18:17:46 -0700145 * <p/>
146 * Need to synchronize on this instance if a request is sent, received,
147 * checked.
148 */
149 protected LinkedList<PendingRoleRequestEntry> pendingRoleRequests;
150
151 /** OpenFlow version for this switch */
152 protected OFVersion ofversion;
153 // Description stats reply describing this switch
154 private OFDescStatsReply switchDescription;
155 // Switch features from initial featuresReply
156 protected Set<OFCapabilities> capabilities;
157 protected int buffers;
158 protected Set<OFActionType> actions;
159 protected byte tables;
160 protected DatapathId datapathId;
Saurav Dasfcdad072014-08-13 14:15:21 -0700161 private OFAuxId auxId;
Brian O'Connorc67f9fa2014-08-07 18:17:46 -0700162
163 private IDebugCounterService debugCounters;
Saurav Dasfcdad072014-08-13 14:15:21 -0700164 private boolean debugCountersRegistered;
165 @SuppressWarnings("unused")
Brian O'Connorc67f9fa2014-08-07 18:17:46 -0700166 private IDebugCounter ctrSwitch, ctrSwitchPktin, ctrSwitchWrite,
Saurav Dasfcdad072014-08-13 14:15:21 -0700167 ctrSwitchPktinDrops, ctrSwitchWriteDrops;
Brian O'Connorc67f9fa2014-08-07 18:17:46 -0700168
Saurav Dasfcdad072014-08-13 14:15:21 -0700169 protected boolean startDriverHandshakeCalled = false;
170 private boolean flowTableFull = false;
Brian O'Connorc67f9fa2014-08-07 18:17:46 -0700171
Saurav Dasfcdad072014-08-13 14:15:21 -0700172 private final PortManager portManager;
Brian O'Connorc67f9fa2014-08-07 18:17:46 -0700173
174 protected static final ThreadLocal<Map<OFSwitchImplBase, List<OFMessage>>> local_msg_buffer =
Saurav Dasfcdad072014-08-13 14:15:21 -0700175 new ThreadLocal<Map<OFSwitchImplBase, List<OFMessage>>>() {
176 @Override
177 protected Map<OFSwitchImplBase, List<OFMessage>> initialValue() {
178 return new WeakHashMap<OFSwitchImplBase, List<OFMessage>>();
179 }
180 };
Brian O'Connorc67f9fa2014-08-07 18:17:46 -0700181
Saurav Dasfcdad072014-08-13 14:15:21 -0700182 private static final String BASE = "switchbase";
Brian O'Connorc67f9fa2014-08-07 18:17:46 -0700183
184 protected static class PendingRoleRequestEntry {
185 protected int xid;
186 protected Role role;
187 // cookie is used to identify the role "generation". roleChanger uses
188 protected long cookie;
189
190 public PendingRoleRequestEntry(int xid, Role role, long cookie) {
191 this.xid = xid;
192 this.role = role;
193 this.cookie = cookie;
194 }
195 }
196
197 public OFSwitchImplBase() {
198 this.stringId = null;
199 this.attributes = new ConcurrentHashMap<Object, Object>();
200 this.connectedSince = new Date();
201 this.transactionIdSource = new AtomicInteger();
Saurav Dasfcdad072014-08-13 14:15:21 -0700202 this.generationIdSource = 0; // XXX S this is wrong; should be
203 // negotiated
Brian O'Connorc67f9fa2014-08-07 18:17:46 -0700204 // XXX S no need this.portLock = new Object();
205 this.portsByNumber = new ConcurrentHashMap<Integer, OFPortDesc>();
206 this.portsByName = new ConcurrentHashMap<String, OFPortDesc>();
207 this.connected = true;
208 this.statsFutureMap = new ConcurrentHashMap<Integer, OFStatisticsFuture>();
209 this.featuresFutureMap = new ConcurrentHashMap<Integer, OFFeaturesReplyFuture>();
210 this.iofMsgListenersMap = new ConcurrentHashMap<Integer, IOFMessageListener>();
211 this.role = null;
Brian O'Connorc67f9fa2014-08-07 18:17:46 -0700212 this.listenerLock = new ReentrantReadWriteLock();
213 this.pendingRoleRequests = new LinkedList<OFSwitchImplBase.PendingRoleRequestEntry>();
214 this.portManager = new PortManager();
215 // by default the base impl declares no support for Nx_role_requests.
216 // OF1.0 switches like OVS that do support these messages should set the
217 // attribute in the associated switch driver.
218 setAttribute(SWITCH_SUPPORTS_NX_ROLE, false);
219
220 }
221
Saurav Dasfcdad072014-08-13 14:15:21 -0700222 // *******************************************
223 // Setters and Getters
224 // *******************************************
Brian O'Connorc67f9fa2014-08-07 18:17:46 -0700225
226 @Override
227 public Object getAttribute(String name) {
228 if (this.attributes.containsKey(name)) {
229 return this.attributes.get(name);
230 }
231 return null;
232 }
233
234 @Override
235 public ConcurrentMap<Object, Object> getAttributes() {
236 return this.attributes;
237 }
238
239 @Override
240 public void setAttribute(String name, Object value) {
241 this.attributes.put(name, value);
242 return;
243 }
244
245 @Override
246 public Object removeAttribute(String name) {
247 return this.attributes.remove(name);
248 }
249
250 @Override
251 public boolean hasAttribute(String name) {
252 return this.attributes.containsKey(name);
253 }
254
255 @Override
256 @JsonSerialize(using = DPIDSerializer.class)
257 @JsonProperty("dpid")
258 public long getId() {
259 if (this.stringId == null)
260 throw new RuntimeException("Features reply has not yet been set");
261 return this.datapathId.getLong();
262 }
263
264 @JsonIgnore
265 @Override
266 public String getStringId() {
267 return stringId;
268 }
269
Jonathan Harta213bce2014-08-11 15:44:07 -0700270 @Override
271 public OFFactory getFactory() {
272 return OFFactories.getFactory(ofversion);
Brian O'Connorc67f9fa2014-08-07 18:17:46 -0700273 }
274
Jonathan Harta213bce2014-08-11 15:44:07 -0700275 @Override
276 public OFVersion getOFVersion() {
277 return ofversion;
278 }
279
280 @Override
Brian O'Connorc67f9fa2014-08-07 18:17:46 -0700281 public void setOFVersion(OFVersion ofv) {
Jonathan Harta213bce2014-08-11 15:44:07 -0700282 ofversion = ofv;
Brian O'Connorc67f9fa2014-08-07 18:17:46 -0700283 }
284
285 /**
286 * @param floodlightProvider the floodlightProvider to set
287 */
288 @JsonIgnore
289 @Override
290 public void setFloodlightProvider(IFloodlightProviderService floodlightProvider) {
291 this.floodlightProvider = floodlightProvider;
292 }
293
294 @JsonIgnore
295 @Override
296 public void setThreadPoolService(IThreadPoolService tp) {
297 this.threadPool = tp;
298 }
299
300 @Override
301 @JsonIgnore
302 public void setDebugCounterService(IDebugCounterService debugCounters)
303 throws CounterException {
304 this.debugCounters = debugCounters;
305 registerOverloadCounters();
306 }
307
308 /* (non-Javadoc)
309 * @see java.lang.Object#toString()
310 */
311 @Override
312 public String toString() {
313 return "OFSwitchImpl [" + ((channel != null) ? channel.getRemoteAddress() : "?")
Saurav Dasfcdad072014-08-13 14:15:21 -0700314 + " DPID[" + ((stringId != null) ? stringId : "?") + "]]";
Brian O'Connorc67f9fa2014-08-07 18:17:46 -0700315 }
316
Saurav Dasfcdad072014-08-13 14:15:21 -0700317 // *******************************************
318 // Channel related methods
319 // *******************************************
Brian O'Connorc67f9fa2014-08-07 18:17:46 -0700320
321 @JsonIgnore
322 @Override
323 public void setChannel(Channel channel) {
324 this.channel = channel;
325 }
326
327 @Override
328 public void write(OFMessage m, FloodlightContext bc) throws IOException {
329 Map<OFSwitchImplBase, List<OFMessage>> msg_buffer_map = local_msg_buffer.get();
330 List<OFMessage> msg_buffer = msg_buffer_map.get(this);
331 if (msg_buffer == null) {
332 msg_buffer = new ArrayList<OFMessage>();
333 msg_buffer_map.put(this, msg_buffer);
334 }
335 // XXX S will change when iFloodlight provider changes
Saurav Dasfcdad072014-08-13 14:15:21 -0700336 // this.floodlightProvider.handleOutgoingMessage(this, m, bc);
Brian O'Connorc67f9fa2014-08-07 18:17:46 -0700337 msg_buffer.add(m);
338
339 if ((msg_buffer.size() >= Controller.BATCH_MAX_SIZE) ||
340 ((m.getType() != OFType.PACKET_OUT) && (m.getType() != OFType.FLOW_MOD))) {
341 this.write(msg_buffer);
342 msg_buffer.clear();
343 }
344 }
345
346 @Override
347 @LogMessageDoc(level = "WARN",
348 message = "Sending OF message that modifies switch " +
349 "state while in the slave role: {switch}",
350 explanation = "An application has sent a message to a switch " +
351 "that is not valid when the switch is in a slave role",
352 recommendation = LogMessageDoc.REPORT_CONTROLLER_BUG)
353 public void write(List<OFMessage> msglist,
Saurav Dasfcdad072014-08-13 14:15:21 -0700354 FloodlightContext bc) throws IOException {
Brian O'Connorc67f9fa2014-08-07 18:17:46 -0700355 for (OFMessage m : msglist) {
356 if (role == Role.SLAVE) {
357 switch (m.getType()) {
Saurav Dasfcdad072014-08-13 14:15:21 -0700358 case PACKET_OUT:
359 case FLOW_MOD:
360 case PORT_MOD:
361 log.warn("Sending OF message that modifies switch " +
362 "state while in the slave role: {}",
363 m.getType().name());
364 break;
365 default:
366 break;
Brian O'Connorc67f9fa2014-08-07 18:17:46 -0700367 }
368 }
369 // XXX S again
Saurav Dasfcdad072014-08-13 14:15:21 -0700370 // this.floodlightProvider.handleOutgoingMessage(this, m, bc);
Brian O'Connorc67f9fa2014-08-07 18:17:46 -0700371 }
372 this.write(msglist);
373 }
374
Saurav Das0a344b02014-09-26 14:18:52 -0700375 protected void write(List<OFMessage> msglist) throws IOException {
Brian O'Connorc67f9fa2014-08-07 18:17:46 -0700376 this.channel.write(msglist);
377 }
378
379 @Override
380 public void disconnectSwitch() {
381 channel.close();
382 }
383
384 @Override
385 public Date getConnectedSince() {
386 return connectedSince;
387 }
388
389 @JsonIgnore
390 @Override
391 public int getNextTransactionId() {
392 return this.transactionIdSource.incrementAndGet();
393 }
394
395 @JsonIgnore
396 @Override
397 public synchronized boolean isConnected() {
398 return connected;
399 }
400
401 @Override
402 @JsonIgnore
403 public synchronized void setConnected(boolean connected) {
404 this.connected = connected;
405 }
406
Saurav Dasfcdad072014-08-13 14:15:21 -0700407 // *******************************************
408 // Switch features related methods
409 // *******************************************
Brian O'Connorc67f9fa2014-08-07 18:17:46 -0700410
411 /**
412 * Set the features reply for this switch from the handshake
413 */
414 protected void setFeaturesReply(OFFeaturesReply featuresReply) {
Saurav Dasfcdad072014-08-13 14:15:21 -0700415 if (featuresReply == null) {
416 log.error("Error setting featuresReply for switch: {}", getStringId());
417 return;
418 }
419 this.datapathId = featuresReply.getDatapathId();
420 this.capabilities = featuresReply.getCapabilities();
421 this.buffers = (int) featuresReply.getNBuffers();
422 this.tables = (byte) featuresReply.getNTables();
423 this.stringId = this.datapathId.toString();
424 if (ofversion == OFVersion.OF_13) {
425 auxId = featuresReply.getAuxiliaryId();
426 if (!auxId.equals(OFAuxId.MAIN)) {
427 log.warn("This controller does not handle auxiliary connections. "
428 + "Aux connection id {} received from switch {}",
429 auxId, getStringId());
430 }
431 }
Brian O'Connorc67f9fa2014-08-07 18:17:46 -0700432
Saurav Dasfcdad072014-08-13 14:15:21 -0700433 if (ofversion == OFVersion.OF_10) {
434 this.actions = featuresReply.getActions();
435 portManager.compareAndUpdatePorts(featuresReply.getPorts(), true);
436 }
Brian O'Connorc67f9fa2014-08-07 18:17:46 -0700437 }
438
439 /**
Saurav Dasfcdad072014-08-13 14:15:21 -0700440 * Set the port descriptions for this switch from the handshake for an OF1.3
441 * switch.
Brian O'Connorc67f9fa2014-08-07 18:17:46 -0700442 */
443 protected void setPortDescReply(OFPortDescStatsReply pdrep) {
Saurav Dasfcdad072014-08-13 14:15:21 -0700444 if (ofversion != OFVersion.OF_13)
445 return;
446 if (pdrep == null) {
447 log.error("Error setting ports description for switch: {}", getStringId());
448 return;
449 }
450 portManager.updatePorts(pdrep.getEntries());
Brian O'Connorc67f9fa2014-08-07 18:17:46 -0700451 }
452
453 @Override
454 public int getNumBuffers() {
455 return buffers;
456 }
Saurav Dasfcdad072014-08-13 14:15:21 -0700457
Brian O'Connorc67f9fa2014-08-07 18:17:46 -0700458 @Override
459 public Set<OFActionType> getActions() {
460 return actions;
461 }
Saurav Dasfcdad072014-08-13 14:15:21 -0700462
Brian O'Connorc67f9fa2014-08-07 18:17:46 -0700463 @Override
Saurav Dasfcdad072014-08-13 14:15:21 -0700464 public Set<OFCapabilities> getCapabilities() {
Brian O'Connorc67f9fa2014-08-07 18:17:46 -0700465 return capabilities;
466 }
Saurav Dasfcdad072014-08-13 14:15:21 -0700467
Brian O'Connorc67f9fa2014-08-07 18:17:46 -0700468 @Override
469 public byte getNumTables() {
470 return tables;
471 }
472
Saurav Dasfcdad072014-08-13 14:15:21 -0700473 // public Future<OFFeaturesReply> getFeaturesReplyFromSwitch()
474 // throws IOException {
475 // // XXX S fix this later
476 // OFMessage request = floodlightProvider.getOFMessageFactory_13()
477 // .buildFeaturesRequest()
478 // .setXid(getNextTransactionId())
479 // .build();
480 // OFFeaturesReplyFuture future =
481 // new OFFeaturesReplyFuture(threadPool, this, (int) request.getXid());
482 // this.featuresFutureMap.put((int) request.getXid(), future);
483 // this.channel.write(Collections.singletonList(request));
484 // return future;
485 //
486 // }
487 //
488 // public void deliverOFFeaturesReply(OFMessage reply) {
489 // OFFeaturesReplyFuture future =
490 // this.featuresFutureMap.get(reply.getXid());
491 // if (future != null) {
492 // future.deliverFuture(this, reply);
493 // // The future will ultimately unregister itself and call
494 // // cancelFeaturesReply
495 // return;
496 // }
497 // log.error("Switch {}: received unexpected featureReply", this);
498 // }
Brian O'Connorc67f9fa2014-08-07 18:17:46 -0700499
500 @Override
501 public void cancelFeaturesReply(int transactionId) {
502 this.featuresFutureMap.remove(transactionId);
503 }
504
505 @JsonIgnore
506 public void setSwitchDescription(OFDescStatsReply desc) {
Saurav Dasfcdad072014-08-13 14:15:21 -0700507 switchDescription = desc;
Brian O'Connorc67f9fa2014-08-07 18:17:46 -0700508 }
509
510 @Override
511 @JsonIgnore
512 public OFDescStatsReply getSwitchDescription() {
Saurav Dasfcdad072014-08-13 14:15:21 -0700513 return switchDescription;
Brian O'Connorc67f9fa2014-08-07 18:17:46 -0700514 }
515
Saurav Dasfcdad072014-08-13 14:15:21 -0700516 // *******************************************
517 // Switch port handling
518 // *******************************************
Brian O'Connorc67f9fa2014-08-07 18:17:46 -0700519
520 @Override
521 @JsonIgnore
522 public Collection<OFPortDesc> getEnabledPorts() {
523 return portManager.getEnabledPorts();
524 }
525
526 @Override
527 @JsonIgnore
528 public Collection<Integer> getEnabledPortNumbers() {
529 return portManager.getEnabledPortNumbers();
530 }
531
532 @Override
533 public OFPortDesc getPort(int portNumber) {
534 return portManager.getPort(portNumber);
535 }
536
537 @Override
538 public OFPortDesc getPort(String portName) {
539 return portManager.getPort(portName);
540 }
541
542 @Override
543 @JsonIgnore
544 public OrderedCollection<PortChangeEvent> processOFPortStatus(OFPortStatus ps) {
545 return portManager.handlePortStatusMessage(ps);
546 }
547
548 @Override
549 @JsonProperty("ports")
550 public Collection<OFPortDesc> getPorts() {
Saurav Dasfcdad072014-08-13 14:15:21 -0700551 return portManager.getPorts();
Brian O'Connorc67f9fa2014-08-07 18:17:46 -0700552 }
553
554 @Override
555 public boolean portEnabled(int portNumber) {
556 return isEnabled(portManager.getPort(portNumber));
557 }
558
559 @Override
560 public boolean portEnabled(String portName) {
Saurav Dasfcdad072014-08-13 14:15:21 -0700561 return isEnabled(portManager.getPort(portName));
Brian O'Connorc67f9fa2014-08-07 18:17:46 -0700562 }
563
564 private boolean isEnabled(OFPortDesc p) {
Saurav Dasfcdad072014-08-13 14:15:21 -0700565 return (p != null &&
566 !p.getState().contains(OFPortState.LINK_DOWN) &&
567 !p.getState().contains(OFPortState.BLOCKED) && !p.getConfig().contains(
568 OFPortConfig.PORT_DOWN));
Brian O'Connorc67f9fa2014-08-07 18:17:46 -0700569 }
570
571 @Override
572 public OrderedCollection<PortChangeEvent> comparePorts(Collection<OFPortDesc> ports) {
573 return portManager.comparePorts(ports);
574 }
575
576 @Override
577 @JsonIgnore
578 public OrderedCollection<PortChangeEvent> setPorts(Collection<OFPortDesc> ports) {
579 return portManager.updatePorts(ports);
580 }
581
582 /**
583 * Manages the ports of this switch.
Jonathan Hart23daf192014-08-22 10:56:58 -0700584 *
Brian O'Connorc67f9fa2014-08-07 18:17:46 -0700585 * Provides methods to query and update the stored ports. The class ensures
Saurav Dasfcdad072014-08-13 14:15:21 -0700586 * that every port name and port number is unique. When updating ports the
587 * class checks if port number <-> port name mappings have change due to the
588 * update. If a new port P has number and port that are inconsistent with
589 * the previous mapping(s) the class will delete all previous ports with
590 * name or number of the new port and then add the new port.
Jonathan Hart23daf192014-08-22 10:56:58 -0700591 *
Brian O'Connorc67f9fa2014-08-07 18:17:46 -0700592 * Port names are stored as-is but they are compared case-insensitive
Jonathan Hart23daf192014-08-22 10:56:58 -0700593 *
Brian O'Connorc67f9fa2014-08-07 18:17:46 -0700594 * The methods that change the stored ports return a list of
Saurav Dasfcdad072014-08-13 14:15:21 -0700595 * PortChangeEvents that represent the changes that have been applied to the
596 * port list so that IOFSwitchListeners can be notified about the changes.
Jonathan Hart23daf192014-08-22 10:56:58 -0700597 *
Saurav Dasfcdad072014-08-13 14:15:21 -0700598 * Implementation notes: - We keep several different representations of the
599 * ports to allow for fast lookups - Ports are stored in unchangeable lists.
600 * When a port is modified new data structures are allocated. - We use a
601 * read-write-lock for synchronization, so multiple readers are allowed. -
602 * All port numbers have int representation (no more shorts)
Brian O'Connorc67f9fa2014-08-07 18:17:46 -0700603 */
604 protected class PortManager {
605 private final ReentrantReadWriteLock lock;
606 private List<OFPortDesc> portList;
607 private List<OFPortDesc> enabledPortList;
608 private List<Integer> enabledPortNumbers;
Saurav Dasfcdad072014-08-13 14:15:21 -0700609 private Map<Integer, OFPortDesc> portsByNumber;
610 private Map<String, OFPortDesc> portsByName;
Brian O'Connorc67f9fa2014-08-07 18:17:46 -0700611
612 public PortManager() {
613 this.lock = new ReentrantReadWriteLock();
614 this.portList = Collections.emptyList();
615 this.enabledPortList = Collections.emptyList();
616 this.enabledPortNumbers = Collections.emptyList();
617 this.portsByName = Collections.emptyMap();
618 this.portsByNumber = Collections.emptyMap();
619 }
620
621 /**
Saurav Dasfcdad072014-08-13 14:15:21 -0700622 * Set the internal data structure storing this switch's port to the
623 * ports specified by newPortsByNumber
Jonathan Hart23daf192014-08-22 10:56:58 -0700624 *
Brian O'Connorc67f9fa2014-08-07 18:17:46 -0700625 * CALLER MUST HOLD WRITELOCK
Jonathan Hart23daf192014-08-22 10:56:58 -0700626 *
Brian O'Connorc67f9fa2014-08-07 18:17:46 -0700627 * @param newPortsByNumber
628 * @throws IllegaalStateException if called without holding the
Saurav Dasfcdad072014-08-13 14:15:21 -0700629 * writelock
Brian O'Connorc67f9fa2014-08-07 18:17:46 -0700630 */
631 private void updatePortsWithNewPortsByNumber(
Saurav Dasfcdad072014-08-13 14:15:21 -0700632 Map<Integer, OFPortDesc> newPortsByNumber) {
Brian O'Connorc67f9fa2014-08-07 18:17:46 -0700633 if (!lock.writeLock().isHeldByCurrentThread()) {
634 throw new IllegalStateException("Method called without " +
Saurav Dasfcdad072014-08-13 14:15:21 -0700635 "holding writeLock");
Brian O'Connorc67f9fa2014-08-07 18:17:46 -0700636 }
Saurav Dasfcdad072014-08-13 14:15:21 -0700637 Map<String, OFPortDesc> newPortsByName =
Brian O'Connorc67f9fa2014-08-07 18:17:46 -0700638 new HashMap<String, OFPortDesc>();
639 List<OFPortDesc> newPortList =
640 new ArrayList<OFPortDesc>();
641 List<OFPortDesc> newEnabledPortList =
642 new ArrayList<OFPortDesc>();
643 List<Integer> newEnabledPortNumbers = new ArrayList<Integer>();
644
Saurav Dasfcdad072014-08-13 14:15:21 -0700645 for (OFPortDesc p : newPortsByNumber.values()) {
Brian O'Connorc67f9fa2014-08-07 18:17:46 -0700646 newPortList.add(p);
647 newPortsByName.put(p.getName().toLowerCase(), p);
648 if (isEnabled(p)) {
649 newEnabledPortList.add(p);
650 newEnabledPortNumbers.add(p.getPortNo().getPortNumber());
651 }
652 }
653 portsByName = Collections.unmodifiableMap(newPortsByName);
654 portsByNumber =
655 Collections.unmodifiableMap(newPortsByNumber);
656 enabledPortList =
657 Collections.unmodifiableList(newEnabledPortList);
658 enabledPortNumbers =
659 Collections.unmodifiableList(newEnabledPortNumbers);
660 portList = Collections.unmodifiableList(newPortList);
661 }
662
663 /**
Saurav Dasfcdad072014-08-13 14:15:21 -0700664 * Handle a OFPortStatus delete message for the given port. Updates the
665 * internal port maps/lists of this switch and returns the
666 * PortChangeEvents caused by the delete. If the given port exists as
667 * it, it will be deleted. If the name<->number for the given port is
668 * inconsistent with the ports stored by this switch the method will
669 * delete all ports with the number or name of the given port.
Jonathan Hart23daf192014-08-22 10:56:58 -0700670 *
Brian O'Connorc67f9fa2014-08-07 18:17:46 -0700671 * This method will increment error/warn counters and log
Jonathan Hart23daf192014-08-22 10:56:58 -0700672 *
Saurav Dasfcdad072014-08-13 14:15:21 -0700673 * @param delPort the port from the port status message that should be
674 * deleted.
Brian O'Connorc67f9fa2014-08-07 18:17:46 -0700675 * @return ordered collection of port changes applied to this switch
676 */
677 private OrderedCollection<PortChangeEvent> handlePortStatusDelete(
Saurav Dasfcdad072014-08-13 14:15:21 -0700678 OFPortDesc delPort) {
Brian O'Connorc67f9fa2014-08-07 18:17:46 -0700679 lock.writeLock().lock();
680 OrderedCollection<PortChangeEvent> events =
681 new LinkedHashSetWrapper<PortChangeEvent>();
682 try {
Saurav Dasfcdad072014-08-13 14:15:21 -0700683 Map<Integer, OFPortDesc> newPortByNumber =
Brian O'Connorc67f9fa2014-08-07 18:17:46 -0700684 new HashMap<Integer, OFPortDesc>(portsByNumber);
685 OFPortDesc prevPort =
686 portsByNumber.get(delPort.getPortNo().getPortNumber());
687 if (prevPort == null) {
688 // so such port. Do we have a port with the name?
689 prevPort = portsByName.get(delPort.getName());
690 if (prevPort != null) {
691 newPortByNumber.remove(prevPort.getPortNo().getPortNumber());
692 events.add(new PortChangeEvent(prevPort,
Saurav Dasfcdad072014-08-13 14:15:21 -0700693 PortChangeType.DELETE));
Brian O'Connorc67f9fa2014-08-07 18:17:46 -0700694 }
695 } else if (prevPort.getName().equals(delPort.getName())) {
696 // port exists with consistent name-number mapping
697 newPortByNumber.remove(delPort.getPortNo().getPortNumber());
698 events.add(new PortChangeEvent(delPort,
Saurav Dasfcdad072014-08-13 14:15:21 -0700699 PortChangeType.DELETE));
Brian O'Connorc67f9fa2014-08-07 18:17:46 -0700700 } else {
701 // port with same number exists but its name differs. This
702 // is weird. The best we can do is to delete the existing
703 // port(s) that have delPort's name and number.
704 newPortByNumber.remove(delPort.getPortNo().getPortNumber());
705 events.add(new PortChangeEvent(prevPort,
Saurav Dasfcdad072014-08-13 14:15:21 -0700706 PortChangeType.DELETE));
Brian O'Connorc67f9fa2014-08-07 18:17:46 -0700707 // is there another port that has delPort's name?
708 prevPort = portsByName.get(delPort.getName().toLowerCase());
709 if (prevPort != null) {
710 newPortByNumber.remove(prevPort.getPortNo().getPortNumber());
711 events.add(new PortChangeEvent(prevPort,
Saurav Dasfcdad072014-08-13 14:15:21 -0700712 PortChangeType.DELETE));
Brian O'Connorc67f9fa2014-08-07 18:17:46 -0700713 }
714 }
715 updatePortsWithNewPortsByNumber(newPortByNumber);
716 return events;
717 } finally {
718 lock.writeLock().unlock();
719 }
720 }
721
722 /**
723 * Handle a OFPortStatus message, update the internal data structures
724 * that store ports and return the list of OFChangeEvents.
Jonathan Hart23daf192014-08-22 10:56:58 -0700725 *
Brian O'Connorc67f9fa2014-08-07 18:17:46 -0700726 * This method will increment error/warn counters and log
Jonathan Hart23daf192014-08-22 10:56:58 -0700727 *
Brian O'Connorc67f9fa2014-08-07 18:17:46 -0700728 * @param ps
729 * @return
730 */
731 public OrderedCollection<PortChangeEvent> handlePortStatusMessage(OFPortStatus ps) {
732 if (ps == null) {
733 throw new NullPointerException("OFPortStatus message must " +
Saurav Dasfcdad072014-08-13 14:15:21 -0700734 "not be null");
Brian O'Connorc67f9fa2014-08-07 18:17:46 -0700735 }
736 lock.writeLock().lock();
737 try {
738 OFPortReason reason = ps.getReason();
739 if (reason == null) {
740 throw new IllegalArgumentException("Unknown PortStatus " +
741 "reason code " + ps.getReason());
742 }
743
744 if (log.isDebugEnabled()) {
745 log.debug("Handling OFPortStatus: {} for {}",
Saurav Dasfcdad072014-08-13 14:15:21 -0700746 reason, ps);
Brian O'Connorc67f9fa2014-08-07 18:17:46 -0700747 }
748
749 if (reason == OFPortReason.DELETE)
Saurav Dasfcdad072014-08-13 14:15:21 -0700750 return handlePortStatusDelete(ps.getDesc());
Brian O'Connorc67f9fa2014-08-07 18:17:46 -0700751
752 // We handle ADD and MODIFY the same way. Since OpenFlow
753 // doesn't specify what uniquely identifies a port the
754 // notion of ADD vs. MODIFY can also be hazy. So we just
755 // compare the new port to the existing ones.
Saurav Dasfcdad072014-08-13 14:15:21 -0700756 Map<Integer, OFPortDesc> newPortByNumber =
757 new HashMap<Integer, OFPortDesc>(portsByNumber);
Brian O'Connorc67f9fa2014-08-07 18:17:46 -0700758 OrderedCollection<PortChangeEvent> events =
Saurav Dasfcdad072014-08-13 14:15:21 -0700759 getSinglePortChanges(ps.getDesc());
760 for (PortChangeEvent e : events) {
761 switch (e.type) {
762 case DELETE:
763 newPortByNumber.remove(e.port.getPortNo().getPortNumber());
764 break;
765 case ADD:
766 if (reason != OFPortReason.ADD) {
767 // weird case
768 }
769 newPortByNumber.put(e.port.getPortNo().getPortNumber(),
770 e.port);
771 break;
772 case DOWN:
773 newPortByNumber.put(e.port.getPortNo().getPortNumber(),
774 e.port);
775 break;
776 case OTHER_UPDATE:
777 newPortByNumber.put(e.port.getPortNo().getPortNumber(),
778 e.port);
779 break;
780 case UP:
781 newPortByNumber.put(e.port.getPortNo().getPortNumber(),
782 e.port);
783 break;
Brian O'Connorc67f9fa2014-08-07 18:17:46 -0700784 }
785 }
786 updatePortsWithNewPortsByNumber(newPortByNumber);
787 return events;
788 } finally {
789 lock.writeLock().unlock();
790 }
791
792 }
793
794 /**
795 * Given a new or modified port newPort, returns the list of
Saurav Dasfcdad072014-08-13 14:15:21 -0700796 * PortChangeEvents to "transform" the current ports stored by this
797 * switch to include / represent the new port. The ports stored by this
798 * switch are <b>NOT</b> updated.
Jonathan Hart23daf192014-08-22 10:56:58 -0700799 *
Saurav Dasfcdad072014-08-13 14:15:21 -0700800 * This method acquires the readlock and is thread-safe by itself. Most
801 * callers will need to acquire the write lock before calling this
802 * method though (if the caller wants to update the ports stored by this
803 * switch)
Jonathan Hart23daf192014-08-22 10:56:58 -0700804 *
Brian O'Connorc67f9fa2014-08-07 18:17:46 -0700805 * @param newPort the new or modified port.
806 * @return the list of changes
807 */
808 public OrderedCollection<PortChangeEvent> getSinglePortChanges(
Saurav Dasfcdad072014-08-13 14:15:21 -0700809 OFPortDesc newPort) {
Brian O'Connorc67f9fa2014-08-07 18:17:46 -0700810 lock.readLock().lock();
811 try {
812 OrderedCollection<PortChangeEvent> events =
813 new LinkedHashSetWrapper<PortChangeEvent>();
814 // Check if we have a port by the same number in our
815 // old map.
816 OFPortDesc prevPort =
817 portsByNumber.get(newPort.getPortNo().getPortNumber());
818 if (newPort.equals(prevPort)) {
819 // nothing has changed
820 return events;
821 }
822
823 if (prevPort != null &&
824 prevPort.getName().equals(newPort.getName())) {
825 // A simple modify of a existing port
826 // A previous port with this number exists and it's name
827 // also matches the new port. Find the differences
828 if (isEnabled(prevPort) && !isEnabled(newPort)) {
829 events.add(new PortChangeEvent(newPort,
Saurav Dasfcdad072014-08-13 14:15:21 -0700830 PortChangeType.DOWN));
Brian O'Connorc67f9fa2014-08-07 18:17:46 -0700831 } else if (!isEnabled(prevPort) && isEnabled(newPort)) {
832 events.add(new PortChangeEvent(newPort,
Saurav Dasfcdad072014-08-13 14:15:21 -0700833 PortChangeType.UP));
Brian O'Connorc67f9fa2014-08-07 18:17:46 -0700834 } else {
835 events.add(new PortChangeEvent(newPort,
Saurav Dasfcdad072014-08-13 14:15:21 -0700836 PortChangeType.OTHER_UPDATE));
Brian O'Connorc67f9fa2014-08-07 18:17:46 -0700837 }
838 return events;
839 }
840
841 if (prevPort != null) {
842 // There exists a previous port with the same port
843 // number but the port name is different (otherwise we would
844 // never have gotten here)
845 // Remove the port. Name-number mapping(s) have changed
846 events.add(new PortChangeEvent(prevPort,
Saurav Dasfcdad072014-08-13 14:15:21 -0700847 PortChangeType.DELETE));
Brian O'Connorc67f9fa2014-08-07 18:17:46 -0700848 }
849
850 // We now need to check if there exists a previous port sharing
851 // the same name as the new/updated port.
852 prevPort = portsByName.get(newPort.getName().toLowerCase());
853 if (prevPort != null) {
854 // There exists a previous port with the same port
855 // name but the port number is different (otherwise we
856 // never have gotten here).
857 // Remove the port. Name-number mapping(s) have changed
858 events.add(new PortChangeEvent(prevPort,
Saurav Dasfcdad072014-08-13 14:15:21 -0700859 PortChangeType.DELETE));
Brian O'Connorc67f9fa2014-08-07 18:17:46 -0700860 }
861
862 // We always need to add the new port. Either no previous port
863 // existed or we just deleted previous ports with inconsistent
864 // name-number mappings
865 events.add(new PortChangeEvent(newPort, PortChangeType.ADD));
866 return events;
867 } finally {
868 lock.readLock().unlock();
869 }
870 }
871
872 /**
873 * Compare the current ports of this switch to the newPorts list and
874 * return the changes that would be applied to transfort the current
Saurav Dasfcdad072014-08-13 14:15:21 -0700875 * ports to the new ports. No internal data structures are updated see
876 * {@link #compareAndUpdatePorts(List, boolean)}
Jonathan Hart23daf192014-08-22 10:56:58 -0700877 *
Brian O'Connorc67f9fa2014-08-07 18:17:46 -0700878 * @param newPorts the list of new ports
879 * @return The list of differences between the current ports and
Saurav Dasfcdad072014-08-13 14:15:21 -0700880 * newPortList
Brian O'Connorc67f9fa2014-08-07 18:17:46 -0700881 */
882 public OrderedCollection<PortChangeEvent> comparePorts(
Saurav Dasfcdad072014-08-13 14:15:21 -0700883 Collection<OFPortDesc> newPorts) {
Brian O'Connorc67f9fa2014-08-07 18:17:46 -0700884 return compareAndUpdatePorts(newPorts, false);
885 }
886
887 /**
888 * Compare the current ports of this switch to the newPorts list and
889 * return the changes that would be applied to transform the current
Saurav Dasfcdad072014-08-13 14:15:21 -0700890 * ports to the new ports. No internal data structures are updated see
891 * {@link #compareAndUpdatePorts(List, boolean)}
Jonathan Hart23daf192014-08-22 10:56:58 -0700892 *
Brian O'Connorc67f9fa2014-08-07 18:17:46 -0700893 * @param newPorts the list of new ports
894 * @return The list of differences between the current ports and
Saurav Dasfcdad072014-08-13 14:15:21 -0700895 * newPortList
Brian O'Connorc67f9fa2014-08-07 18:17:46 -0700896 */
897 public OrderedCollection<PortChangeEvent> updatePorts(
Saurav Dasfcdad072014-08-13 14:15:21 -0700898 Collection<OFPortDesc> newPorts) {
Brian O'Connorc67f9fa2014-08-07 18:17:46 -0700899 return compareAndUpdatePorts(newPorts, true);
900 }
901
902 /**
Saurav Dasfcdad072014-08-13 14:15:21 -0700903 * Compare the current ports stored in this switch instance with the new
904 * port list given and return the differences in the form of
Brian O'Connorc67f9fa2014-08-07 18:17:46 -0700905 * PortChangeEvents. If the doUpdate flag is true, newPortList will
906 * replace the current list of this switch (and update the port maps)
Jonathan Hart23daf192014-08-22 10:56:58 -0700907 *
Saurav Dasfcdad072014-08-13 14:15:21 -0700908 * Implementation note: Since this method can optionally modify the
909 * current ports and since it's not possible to upgrade a read-lock to a
910 * write-lock we need to hold the write-lock for the entire operation.
911 * If this becomes a problem and if compares() are common we can
912 * consider splitting in two methods but this requires lots of code
913 * duplication
Jonathan Hart23daf192014-08-22 10:56:58 -0700914 *
Brian O'Connorc67f9fa2014-08-07 18:17:46 -0700915 * @param newPorts the list of new ports.
Saurav Dasfcdad072014-08-13 14:15:21 -0700916 * @param doUpdate If true the newPortList will replace the current port
917 * list for this switch. If false this switch will not be
918 * changed.
Brian O'Connorc67f9fa2014-08-07 18:17:46 -0700919 * @return The list of differences between the current ports and
Saurav Dasfcdad072014-08-13 14:15:21 -0700920 * newPorts
Brian O'Connorc67f9fa2014-08-07 18:17:46 -0700921 * @throws NullPointerException if newPortsList is null
922 * @throws IllegalArgumentException if either port names or port numbers
Saurav Dasfcdad072014-08-13 14:15:21 -0700923 * are duplicated in the newPortsList.
Brian O'Connorc67f9fa2014-08-07 18:17:46 -0700924 */
925 private OrderedCollection<PortChangeEvent> compareAndUpdatePorts(
926 Collection<OFPortDesc> newPorts, boolean doUpdate) {
927 if (newPorts == null) {
928 throw new NullPointerException("newPortsList must not be null");
929 }
930 lock.writeLock().lock();
931 try {
932 OrderedCollection<PortChangeEvent> events =
933 new LinkedHashSetWrapper<PortChangeEvent>();
934
Saurav Dasfcdad072014-08-13 14:15:21 -0700935 Map<Integer, OFPortDesc> newPortsByNumber =
Brian O'Connorc67f9fa2014-08-07 18:17:46 -0700936 new HashMap<Integer, OFPortDesc>();
Saurav Dasfcdad072014-08-13 14:15:21 -0700937 Map<String, OFPortDesc> newPortsByName =
Brian O'Connorc67f9fa2014-08-07 18:17:46 -0700938 new HashMap<String, OFPortDesc>();
939 List<OFPortDesc> newEnabledPortList =
940 new ArrayList<OFPortDesc>();
941 List<Integer> newEnabledPortNumbers =
942 new ArrayList<Integer>();
943 List<OFPortDesc> newPortsList =
944 new ArrayList<OFPortDesc>(newPorts);
945
Saurav Dasfcdad072014-08-13 14:15:21 -0700946 for (OFPortDesc p : newPortsList) {
Brian O'Connorc67f9fa2014-08-07 18:17:46 -0700947 if (p == null) {
948 throw new NullPointerException("portList must not " +
949 "contain null values");
950 }
951
952 // Add the port to the new maps and lists and check
953 // that every port is unique
954 OFPortDesc duplicatePort;
955 duplicatePort = newPortsByNumber.put(
Saurav Dasfcdad072014-08-13 14:15:21 -0700956 p.getPortNo().getPortNumber(), p);
Brian O'Connorc67f9fa2014-08-07 18:17:46 -0700957 if (duplicatePort != null) {
958 String msg = String.format("Cannot have two ports " +
959 "with the same number: %s <-> %s",
960 p, duplicatePort);
961 throw new IllegalArgumentException(msg);
962 }
963 duplicatePort =
964 newPortsByName.put(p.getName().toLowerCase(), p);
965 if (duplicatePort != null) {
966 String msg = String.format("Cannot have two ports " +
967 "with the same name: %s <-> %s",
Saurav Dasfcdad072014-08-13 14:15:21 -0700968 p.toString().substring(0, 80),
Brian O'Connorc67f9fa2014-08-07 18:17:46 -0700969 duplicatePort.toString().substring(0, 80));
970 throw new IllegalArgumentException(msg);
971 }
972 if (isEnabled(p)) {
973 newEnabledPortList.add(p);
974 newEnabledPortNumbers.add(p.getPortNo().getPortNumber());
975 }
976
977 // get changes
978 events.addAll(getSinglePortChanges(p));
979 }
980 // find deleted ports
981 // We need to do this after looping through all the new ports
982 // to we can handle changed name<->number mappings correctly
983 // We could pull it into the loop of we address this but
984 // it's probably not worth it
Saurav Dasfcdad072014-08-13 14:15:21 -0700985 for (OFPortDesc oldPort : this.portList) {
986 if (!newPortsByNumber
987 .containsKey(oldPort.getPortNo().getPortNumber())) {
Brian O'Connorc67f9fa2014-08-07 18:17:46 -0700988 PortChangeEvent ev =
989 new PortChangeEvent(oldPort,
Saurav Dasfcdad072014-08-13 14:15:21 -0700990 PortChangeType.DELETE);
Brian O'Connorc67f9fa2014-08-07 18:17:46 -0700991 events.add(ev);
992 }
993 }
994
Brian O'Connorc67f9fa2014-08-07 18:17:46 -0700995 if (doUpdate) {
996 portsByName = Collections.unmodifiableMap(newPortsByName);
997 portsByNumber =
998 Collections.unmodifiableMap(newPortsByNumber);
999 enabledPortList =
1000 Collections.unmodifiableList(newEnabledPortList);
1001 enabledPortNumbers =
1002 Collections.unmodifiableList(newEnabledPortNumbers);
1003 portList = Collections.unmodifiableList(newPortsList);
1004 }
1005 return events;
1006 } finally {
1007 lock.writeLock().unlock();
1008 }
1009 }
1010
1011 public OFPortDesc getPort(String name) {
1012 if (name == null) {
1013 throw new NullPointerException("Port name must not be null");
1014 }
1015 lock.readLock().lock();
1016 try {
1017 return portsByName.get(name.toLowerCase());
1018 } finally {
1019 lock.readLock().unlock();
1020 }
1021 }
1022
1023 public OFPortDesc getPort(int portNumber) {
1024 lock.readLock().lock();
1025 try {
1026 return portsByNumber.get(portNumber);
1027 } finally {
1028 lock.readLock().unlock();
1029 }
1030 }
1031
1032 public List<OFPortDesc> getPorts() {
1033 lock.readLock().lock();
1034 try {
1035 return portList;
1036 } finally {
1037 lock.readLock().unlock();
1038 }
1039 }
1040
1041 public List<OFPortDesc> getEnabledPorts() {
1042 lock.readLock().lock();
1043 try {
1044 return enabledPortList;
1045 } finally {
1046 lock.readLock().unlock();
1047 }
1048 }
1049
1050 public List<Integer> getEnabledPortNumbers() {
1051 lock.readLock().lock();
1052 try {
1053 return enabledPortNumbers;
1054 } finally {
1055 lock.readLock().unlock();
1056 }
1057 }
1058 }
1059
Saurav Dasfcdad072014-08-13 14:15:21 -07001060 // *******************************************
1061 // Switch stats handling
1062 // *******************************************
Brian O'Connorc67f9fa2014-08-07 18:17:46 -07001063
1064 @Override
1065 public Future<List<OFStatsReply>> getStatistics(OFStatsRequest<?> request)
Saurav Dasfcdad072014-08-13 14:15:21 -07001066 throws IOException {
Brian O'Connorc67f9fa2014-08-07 18:17:46 -07001067 OFStatisticsFuture future = new OFStatisticsFuture(threadPool, this,
Saurav Dasfcdad072014-08-13 14:15:21 -07001068 (int) request.getXid());
Brian O'Connorc67f9fa2014-08-07 18:17:46 -07001069 log.info("description STAT REQUEST XID {}", request.getXid());
Saurav Dasfcdad072014-08-13 14:15:21 -07001070 this.statsFutureMap.put((int) request.getXid(), future);
Brian O'Connorc67f9fa2014-08-07 18:17:46 -07001071
1072 this.channel.write(Collections.singletonList(request));
1073 return future;
1074 }
1075
1076 @Override
1077 public void deliverStatisticsReply(OFMessage reply) {
Saurav Dasfcdad072014-08-13 14:15:21 -07001078 OFStatisticsFuture future = this.statsFutureMap.get((int) reply.getXid());
Brian O'Connorc67f9fa2014-08-07 18:17:46 -07001079 if (future != null) {
1080 future.deliverFuture(this, reply);
1081 // The future will ultimately unregister itself and call
1082 // cancelStatisticsReply
1083 return;
1084 }
1085 // Transaction id was not found in statsFutureMap.check the other map
1086 IOFMessageListener caller = this.iofMsgListenersMap.get(reply.getXid());
1087 if (caller != null) {
Saurav Dasfcdad072014-08-13 14:15:21 -07001088 caller.receive(this, reply, null);
Brian O'Connorc67f9fa2014-08-07 18:17:46 -07001089 }
1090 }
1091
1092 @Override
1093 public void cancelStatisticsReply(int transactionId) {
1094 if (null == this.statsFutureMap.remove(transactionId)) {
1095 this.iofMsgListenersMap.remove(transactionId);
1096 }
1097 }
1098
1099 @Override
1100 public void cancelAllStatisticsReplies() {
1101 /* we don't need to be synchronized here. Even if another thread
1102 * modifies the map while we're cleaning up the future will eventuall
1103 * timeout */
1104 for (OFStatisticsFuture f : statsFutureMap.values()) {
1105 f.cancel(true);
1106 }
1107 statsFutureMap.clear();
1108 iofMsgListenersMap.clear();
1109 }
1110
Saurav Dasfcdad072014-08-13 14:15:21 -07001111 // *******************************************
1112 // Switch role handling
1113 // *******************************************
Brian O'Connorc67f9fa2014-08-07 18:17:46 -07001114
1115 // XXX S this is completely wrong. The generation id should be obtained
1116 // after coordinating with all controllers connected to this switch.
1117 // ie. should be part of registry service (account for 1.3 vs 1.0)
1118 // For now we are just generating this locally and keeping it constant.
1119 public U64 getNextGenerationId() {
Saurav Dasfcdad072014-08-13 14:15:21 -07001120 // TODO: Pankaj, fix nextGenerationId as part of Registry interface
1121 return U64.of(generationIdSource);
Brian O'Connorc67f9fa2014-08-07 18:17:46 -07001122 }
1123
1124 @Override
1125 public Role getRole() {
1126 return role;
1127 }
1128
1129 @JsonIgnore
1130 @Override
1131 public void setRole(Role role) {
1132 this.role = role;
1133 }
1134
Saurav Dasfcdad072014-08-13 14:15:21 -07001135 // *******************************************
1136 // Switch utility methods
1137 // *******************************************
Brian O'Connorc67f9fa2014-08-07 18:17:46 -07001138
1139 private void registerOverloadCounters() throws CounterException {
1140 if (debugCountersRegistered) {
1141 return;
1142 }
1143 if (debugCounters == null) {
1144 log.error("Debug Counter Service not found");
1145 debugCounters = new NullDebugCounter();
1146 debugCountersRegistered = true;
1147 }
1148 // every level of the hierarchical counter has to be registered
1149 // even if they are not used
1150 ctrSwitch = debugCounters.registerCounter(
Saurav Dasfcdad072014-08-13 14:15:21 -07001151 BASE, stringId,
1152 "Counter for this switch",
1153 CounterType.ALWAYS_COUNT);
Brian O'Connorc67f9fa2014-08-07 18:17:46 -07001154 ctrSwitchPktin = debugCounters.registerCounter(
Saurav Dasfcdad072014-08-13 14:15:21 -07001155 BASE, stringId + "/pktin",
1156 "Packet in counter for this switch",
1157 CounterType.ALWAYS_COUNT);
Brian O'Connorc67f9fa2014-08-07 18:17:46 -07001158 ctrSwitchWrite = debugCounters.registerCounter(
Saurav Dasfcdad072014-08-13 14:15:21 -07001159 BASE, stringId + "/write",
1160 "Write counter for this switch",
1161 CounterType.ALWAYS_COUNT);
Brian O'Connorc67f9fa2014-08-07 18:17:46 -07001162 ctrSwitchPktinDrops = debugCounters.registerCounter(
Saurav Dasfcdad072014-08-13 14:15:21 -07001163 BASE, stringId + "/pktin/drops",
1164 "Packet in throttle drop count",
1165 CounterType.ALWAYS_COUNT,
1166 IDebugCounterService.CTR_MDATA_WARN);
Brian O'Connorc67f9fa2014-08-07 18:17:46 -07001167 ctrSwitchWriteDrops = debugCounters.registerCounter(
Saurav Dasfcdad072014-08-13 14:15:21 -07001168 BASE, stringId + "/write/drops",
1169 "Switch write throttle drop count",
1170 CounterType.ALWAYS_COUNT,
1171 IDebugCounterService.CTR_MDATA_WARN);
Brian O'Connorc67f9fa2014-08-07 18:17:46 -07001172 }
1173
1174 @Override
1175 public void startDriverHandshake() throws IOException {
Saurav Dasfcdad072014-08-13 14:15:21 -07001176 log.debug("Starting driver handshake for sw {}", getStringId());
Brian O'Connorc67f9fa2014-08-07 18:17:46 -07001177 if (startDriverHandshakeCalled)
1178 throw new SwitchDriverSubHandshakeAlreadyStarted();
1179 startDriverHandshakeCalled = true;
1180 }
1181
1182 @Override
1183 public boolean isDriverHandshakeComplete() {
1184 if (!startDriverHandshakeCalled)
1185 throw new SwitchDriverSubHandshakeNotStarted();
1186 return true;
1187 }
1188
1189 @Override
1190 public void processDriverHandshakeMessage(OFMessage m) {
1191 if (startDriverHandshakeCalled)
1192 throw new SwitchDriverSubHandshakeCompleted(m);
1193 else
1194 throw new SwitchDriverSubHandshakeNotStarted();
1195 }
1196
1197 @Override
1198 @JsonIgnore
Saurav Dasfcdad072014-08-13 14:15:21 -07001199 @LogMessageDoc(level = "WARN",
1200 message = "Switch {switch} flow table is full",
1201 explanation = "The controller received flow table full " +
1202 "message from the switch, could be caused by increased " +
1203 "traffic pattern",
1204 recommendation = LogMessageDoc.REPORT_CONTROLLER_BUG)
Brian O'Connorc67f9fa2014-08-07 18:17:46 -07001205 public void setTableFull(boolean isFull) {
1206 if (isFull && !flowTableFull) {
1207 floodlightProvider.addSwitchEvent(this.datapathId.getLong(),
1208 "SWITCH_FLOW_TABLE_FULL " +
Saurav Dasfcdad072014-08-13 14:15:21 -07001209 "Table full error from switch", false);
Brian O'Connorc67f9fa2014-08-07 18:17:46 -07001210 log.warn("Switch {} flow table is full", stringId);
1211 }
Saurav Dasfcdad072014-08-13 14:15:21 -07001212 flowTableFull = isFull;
Brian O'Connorc67f9fa2014-08-07 18:17:46 -07001213 }
1214
1215 @Override
1216 @LogMessageDoc(level = "ERROR",
1217 message = "Failed to clear all flows on switch {switch}",
1218 explanation = "An I/O error occured while trying to clear " +
1219 "flows on the switch.",
1220 recommendation = LogMessageDoc.CHECK_SWITCH)
1221 public void clearAllFlowMods() {
1222 // Delete all pre-existing flows
1223
Jonathan Harta213bce2014-08-11 15:44:07 -07001224 // by default if match is not specified, then an empty list of matches
1225 // is sent, resulting in a wildcard-all flows
Jonathan Hart23daf192014-08-22 10:56:58 -07001226 Builder builder = getFactory()
Saurav Dasfcdad072014-08-13 14:15:21 -07001227 .buildFlowDelete()
Jonathan Hart23daf192014-08-22 10:56:58 -07001228 .setOutPort(OFPort.ANY);
1229
1230 if (ofversion.wireVersion >= OFVersion.OF_13.wireVersion) {
1231 builder.setOutGroup(OFGroup.ANY)
1232 .setTableId(TableId.ALL);
1233 }
1234
1235 OFMessage fm = builder.build();
Brian O'Connorc67f9fa2014-08-07 18:17:46 -07001236
1237 try {
1238 channel.write(Collections.singletonList(fm));
1239 } catch (Exception e) {
1240 log.error("Failed to clear all flows on switch " + this, e);
1241 }
1242 }
1243
1244 @Override
1245 public void flush() {
1246 Map<OFSwitchImplBase, List<OFMessage>> msg_buffer_map = local_msg_buffer.get();
1247 List<OFMessage> msglist = msg_buffer_map.get(this);
1248 if ((msglist != null) && (msglist.size() > 0)) {
1249 try {
1250 this.write(msglist);
1251 } catch (IOException e) {
Saurav Dasfcdad072014-08-13 14:15:21 -07001252 log.error("Failed flushing messages", e);
Brian O'Connorc67f9fa2014-08-07 18:17:46 -07001253 }
1254 msglist.clear();
1255 }
1256 }
1257
1258 public static void flush_all() {
1259 Map<OFSwitchImplBase, List<OFMessage>> msg_buffer_map = local_msg_buffer.get();
1260 for (OFSwitchImplBase sw : msg_buffer_map.keySet()) {
1261 sw.flush();
1262 }
1263 }
1264
1265 /**
1266 * Return a read lock that must be held while calling the listeners for
1267 * messages from the switch. Holding the read lock prevents the active
1268 * switch list from being modified out from under the listeners.
Jonathan Hart23daf192014-08-22 10:56:58 -07001269 *
Brian O'Connorc67f9fa2014-08-07 18:17:46 -07001270 * @return listener read lock
1271 */
1272 @JsonIgnore
1273 public Lock getListenerReadLock() {
1274 return listenerLock.readLock();
1275 }
1276
1277 /**
1278 * Return a write lock that must be held when the controllers modifies the
1279 * list of active switches. This is to ensure that the active switch list
1280 * doesn't change out from under the listeners as they are handling a
1281 * message from the switch.
Jonathan Hart23daf192014-08-22 10:56:58 -07001282 *
Brian O'Connorc67f9fa2014-08-07 18:17:46 -07001283 * @return listener write lock
1284 */
1285 @JsonIgnore
1286 public Lock getListenerWriteLock() {
1287 return listenerLock.writeLock();
1288 }
Saurav Dasfc5e3eb2014-09-25 19:05:21 -07001289
1290 public String getSwitchDriverState() {
1291 return "";
1292 }
1293
Brian O'Connorc67f9fa2014-08-07 18:17:46 -07001294}