blob: 094eef05d16064ea8bb35934f8c6e307dd6ef48c [file] [log] [blame]
Brian O'Connorc67f9fa2014-08-07 18:17:46 -07001package net.floodlightcontroller.core.internal;
2
3/**
4 * Copyright 2012, Big Switch Networks, Inc.
5 * Originally created by David Erickson, Stanford University
6 *
7 * Licensed under the Apache License, Version 2.0 (the "License"); you may
8 * not use this file except in compliance with the License. You may obtain
9 * a copy of the License at
10 *
11 * http://www.apache.org/licenses/LICENSE-2.0
12 *
13 * Unless required by applicable law or agreed to in writing, software
14 * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
15 * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
16 * License for the specific language governing permissions and limitations
17 * under the License.
18 **/
19
20import java.io.IOException;
Fahad Naeem Khan1f8fb2e2014-09-25 16:39:18 -070021import java.net.SocketAddress;
Brian O'Connorc67f9fa2014-08-07 18:17:46 -070022import java.util.ArrayList;
23import java.util.Collection;
24import java.util.Collections;
25import java.util.Date;
26import java.util.HashMap;
27import java.util.LinkedList;
28import java.util.List;
29import java.util.Map;
30import java.util.Set;
31import java.util.WeakHashMap;
32import java.util.concurrent.ConcurrentHashMap;
33import java.util.concurrent.ConcurrentMap;
34import java.util.concurrent.Future;
35import java.util.concurrent.atomic.AtomicInteger;
36import java.util.concurrent.locks.Lock;
37import java.util.concurrent.locks.ReentrantReadWriteLock;
38
39import net.floodlightcontroller.core.FloodlightContext;
40import net.floodlightcontroller.core.IFloodlightProviderService;
41import net.floodlightcontroller.core.IFloodlightProviderService.Role;
42import net.floodlightcontroller.core.IOFMessageListener;
43import net.floodlightcontroller.core.IOFSwitch;
44import net.floodlightcontroller.core.SwitchDriverSubHandshakeAlreadyStarted;
45import net.floodlightcontroller.core.SwitchDriverSubHandshakeCompleted;
46import net.floodlightcontroller.core.SwitchDriverSubHandshakeNotStarted;
47import net.floodlightcontroller.core.annotations.LogMessageDoc;
48import net.floodlightcontroller.core.web.serializers.DPIDSerializer;
49import net.floodlightcontroller.debugcounter.IDebugCounter;
50import net.floodlightcontroller.debugcounter.IDebugCounterService;
51import net.floodlightcontroller.debugcounter.IDebugCounterService.CounterException;
52import net.floodlightcontroller.debugcounter.IDebugCounterService.CounterType;
53import net.floodlightcontroller.debugcounter.NullDebugCounter;
54import net.floodlightcontroller.threadpool.IThreadPoolService;
55import net.floodlightcontroller.util.LinkedHashSetWrapper;
56import net.floodlightcontroller.util.OrderedCollection;
Brian O'Connorc67f9fa2014-08-07 18:17:46 -070057
58import org.codehaus.jackson.annotate.JsonIgnore;
59import org.codehaus.jackson.annotate.JsonProperty;
60import org.codehaus.jackson.map.annotate.JsonSerialize;
61import org.jboss.netty.channel.Channel;
62import org.projectfloodlight.openflow.protocol.OFActionType;
Saurav Dasd84178f2014-09-29 17:48:54 -070063import org.projectfloodlight.openflow.protocol.OFBarrierReply;
Brian O'Connorc67f9fa2014-08-07 18:17:46 -070064import org.projectfloodlight.openflow.protocol.OFCapabilities;
65import org.projectfloodlight.openflow.protocol.OFDescStatsReply;
Jonathan Harta213bce2014-08-11 15:44:07 -070066import org.projectfloodlight.openflow.protocol.OFFactories;
67import org.projectfloodlight.openflow.protocol.OFFactory;
Brian O'Connorc67f9fa2014-08-07 18:17:46 -070068import org.projectfloodlight.openflow.protocol.OFFeaturesReply;
Jonathan Hart23daf192014-08-22 10:56:58 -070069import org.projectfloodlight.openflow.protocol.OFFlowDelete.Builder;
Brian O'Connorc67f9fa2014-08-07 18:17:46 -070070import org.projectfloodlight.openflow.protocol.OFMessage;
71import org.projectfloodlight.openflow.protocol.OFPortConfig;
72import org.projectfloodlight.openflow.protocol.OFPortDesc;
73import org.projectfloodlight.openflow.protocol.OFPortDescStatsReply;
74import org.projectfloodlight.openflow.protocol.OFPortReason;
75import org.projectfloodlight.openflow.protocol.OFPortState;
76import org.projectfloodlight.openflow.protocol.OFPortStatus;
77import org.projectfloodlight.openflow.protocol.OFStatsReply;
78import org.projectfloodlight.openflow.protocol.OFStatsRequest;
79import org.projectfloodlight.openflow.protocol.OFType;
80import org.projectfloodlight.openflow.protocol.OFVersion;
81import org.projectfloodlight.openflow.types.DatapathId;
82import org.projectfloodlight.openflow.types.OFAuxId;
83import org.projectfloodlight.openflow.types.OFGroup;
84import org.projectfloodlight.openflow.types.OFPort;
85import org.projectfloodlight.openflow.types.TableId;
86import org.projectfloodlight.openflow.types.U64;
87import org.slf4j.Logger;
88import org.slf4j.LoggerFactory;
89
Brian O'Connorc67f9fa2014-08-07 18:17:46 -070090/**
91 * This is the internal representation of an openflow switch.
92 */
93public class OFSwitchImplBase implements IOFSwitch {
94 // TODO: should we really do logging in the class or should we throw
95 // exception that can then be handled by callers?
96 protected final static Logger log = LoggerFactory.getLogger(OFSwitchImplBase.class);
97
98 private static final String HA_CHECK_SWITCH =
99 "Check the health of the indicated switch. If the problem " +
100 "persists or occurs repeatedly, it likely indicates a defect " +
101 "in the switch HA implementation.";
102
103 protected ConcurrentMap<Object, Object> attributes;
104 protected IFloodlightProviderService floodlightProvider;
105 protected IThreadPoolService threadPool;
106 protected Date connectedSince;
107 protected String stringId;
108 protected Channel channel;
109 // transaction id used for messages sent out to this switch from
110 // this controller instance. This xid has significance only between this
111 // controller<->switch pair.
112 protected AtomicInteger transactionIdSource;
113
114 // generation id used for roleRequest messages sent to switches (see section
115 // 6.3.5 of the OF1.3.4 spec). This generationId has significance between
116 // all the controllers that this switch is connected to; and only for role
117 // request messages with role MASTER or SLAVE. The set of Controllers that
118 // this switch is connected to should coordinate the next generation id,
119 // via transactional semantics.
120 protected long generationIdSource;
121
122 // Lock to protect modification of the port maps. We only need to
123 // synchronize on modifications. For read operations we are fine since
124 // we rely on ConcurrentMaps which works for our use case.
Saurav Dasfcdad072014-08-13 14:15:21 -0700125 // private Object portLock; XXX S remove this
Brian O'Connorc67f9fa2014-08-07 18:17:46 -0700126
127 // Map port numbers to the appropriate OFPortDesc
128 protected ConcurrentHashMap<Integer, OFPortDesc> portsByNumber;
129 // Map port names to the appropriate OFPhyiscalPort
130 // XXX: The OF spec doesn't specify if port names need to be unique but
Saurav Dasfcdad072014-08-13 14:15:21 -0700131 // according it's always the case in practice.
Brian O'Connorc67f9fa2014-08-07 18:17:46 -0700132 protected ConcurrentHashMap<String, OFPortDesc> portsByName;
133 protected Map<Integer, OFStatisticsFuture> statsFutureMap;
Saurav Dasfcdad072014-08-13 14:15:21 -0700134 // XXX Consider removing the following 2 maps - not used anymore
135 protected Map<Integer, IOFMessageListener> iofMsgListenersMap;
Brian O'Connorc67f9fa2014-08-07 18:17:46 -0700136 protected Map<Integer, OFFeaturesReplyFuture> featuresFutureMap;
Saurav Dasd84178f2014-09-29 17:48:54 -0700137 protected Map<Long, OFBarrierReplyFuture> barrierFutureMap;
Brian O'Connorc67f9fa2014-08-07 18:17:46 -0700138 protected boolean connected;
139 protected Role role;
Brian O'Connorc67f9fa2014-08-07 18:17:46 -0700140 protected ReentrantReadWriteLock listenerLock;
141 protected ConcurrentMap<Short, Long> portBroadcastCacheHitMap;
142 /**
Saurav Dasfcdad072014-08-13 14:15:21 -0700143 * When sending a role request message, the role request is added to this
144 * queue. If a role reply is received this queue is checked to verify that
145 * the reply matches the expected reply. We require in order delivery of
146 * replies. That's why we use a Queue. The RoleChanger uses a timeout to
147 * ensure we receive a timely reply.
Brian O'Connorc67f9fa2014-08-07 18:17:46 -0700148 * <p/>
149 * Need to synchronize on this instance if a request is sent, received,
150 * checked.
151 */
152 protected LinkedList<PendingRoleRequestEntry> pendingRoleRequests;
153
154 /** OpenFlow version for this switch */
155 protected OFVersion ofversion;
156 // Description stats reply describing this switch
157 private OFDescStatsReply switchDescription;
158 // Switch features from initial featuresReply
159 protected Set<OFCapabilities> capabilities;
160 protected int buffers;
161 protected Set<OFActionType> actions;
162 protected byte tables;
163 protected DatapathId datapathId;
Saurav Dasfcdad072014-08-13 14:15:21 -0700164 private OFAuxId auxId;
Brian O'Connorc67f9fa2014-08-07 18:17:46 -0700165
166 private IDebugCounterService debugCounters;
Saurav Dasfcdad072014-08-13 14:15:21 -0700167 private boolean debugCountersRegistered;
168 @SuppressWarnings("unused")
Brian O'Connorc67f9fa2014-08-07 18:17:46 -0700169 private IDebugCounter ctrSwitch, ctrSwitchPktin, ctrSwitchWrite,
Saurav Dasfcdad072014-08-13 14:15:21 -0700170 ctrSwitchPktinDrops, ctrSwitchWriteDrops;
Brian O'Connorc67f9fa2014-08-07 18:17:46 -0700171
Saurav Dasfcdad072014-08-13 14:15:21 -0700172 protected boolean startDriverHandshakeCalled = false;
173 private boolean flowTableFull = false;
Brian O'Connorc67f9fa2014-08-07 18:17:46 -0700174
Saurav Dasfcdad072014-08-13 14:15:21 -0700175 private final PortManager portManager;
Brian O'Connorc67f9fa2014-08-07 18:17:46 -0700176
177 protected static final ThreadLocal<Map<OFSwitchImplBase, List<OFMessage>>> local_msg_buffer =
Saurav Dasfcdad072014-08-13 14:15:21 -0700178 new ThreadLocal<Map<OFSwitchImplBase, List<OFMessage>>>() {
179 @Override
180 protected Map<OFSwitchImplBase, List<OFMessage>> initialValue() {
181 return new WeakHashMap<OFSwitchImplBase, List<OFMessage>>();
182 }
183 };
Brian O'Connorc67f9fa2014-08-07 18:17:46 -0700184
Saurav Dasfcdad072014-08-13 14:15:21 -0700185 private static final String BASE = "switchbase";
Brian O'Connorc67f9fa2014-08-07 18:17:46 -0700186
187 protected static class PendingRoleRequestEntry {
188 protected int xid;
189 protected Role role;
190 // cookie is used to identify the role "generation". roleChanger uses
191 protected long cookie;
192
193 public PendingRoleRequestEntry(int xid, Role role, long cookie) {
194 this.xid = xid;
195 this.role = role;
196 this.cookie = cookie;
197 }
198 }
199
200 public OFSwitchImplBase() {
201 this.stringId = null;
202 this.attributes = new ConcurrentHashMap<Object, Object>();
203 this.connectedSince = new Date();
204 this.transactionIdSource = new AtomicInteger();
Saurav Dasfcdad072014-08-13 14:15:21 -0700205 this.generationIdSource = 0; // XXX S this is wrong; should be
206 // negotiated
Brian O'Connorc67f9fa2014-08-07 18:17:46 -0700207 // XXX S no need this.portLock = new Object();
208 this.portsByNumber = new ConcurrentHashMap<Integer, OFPortDesc>();
209 this.portsByName = new ConcurrentHashMap<String, OFPortDesc>();
210 this.connected = true;
211 this.statsFutureMap = new ConcurrentHashMap<Integer, OFStatisticsFuture>();
212 this.featuresFutureMap = new ConcurrentHashMap<Integer, OFFeaturesReplyFuture>();
213 this.iofMsgListenersMap = new ConcurrentHashMap<Integer, IOFMessageListener>();
Saurav Dasd84178f2014-09-29 17:48:54 -0700214 this.barrierFutureMap = new ConcurrentHashMap<Long, OFBarrierReplyFuture>();
Brian O'Connorc67f9fa2014-08-07 18:17:46 -0700215 this.role = null;
Brian O'Connorc67f9fa2014-08-07 18:17:46 -0700216 this.listenerLock = new ReentrantReadWriteLock();
217 this.pendingRoleRequests = new LinkedList<OFSwitchImplBase.PendingRoleRequestEntry>();
218 this.portManager = new PortManager();
219 // by default the base impl declares no support for Nx_role_requests.
220 // OF1.0 switches like OVS that do support these messages should set the
221 // attribute in the associated switch driver.
222 setAttribute(SWITCH_SUPPORTS_NX_ROLE, false);
223
224 }
225
Saurav Dasfcdad072014-08-13 14:15:21 -0700226 // *******************************************
227 // Setters and Getters
228 // *******************************************
Brian O'Connorc67f9fa2014-08-07 18:17:46 -0700229
230 @Override
231 public Object getAttribute(String name) {
232 if (this.attributes.containsKey(name)) {
233 return this.attributes.get(name);
234 }
235 return null;
236 }
237
238 @Override
239 public ConcurrentMap<Object, Object> getAttributes() {
240 return this.attributes;
241 }
242
243 @Override
244 public void setAttribute(String name, Object value) {
245 this.attributes.put(name, value);
246 return;
247 }
248
249 @Override
250 public Object removeAttribute(String name) {
251 return this.attributes.remove(name);
252 }
253
254 @Override
255 public boolean hasAttribute(String name) {
256 return this.attributes.containsKey(name);
257 }
258
259 @Override
260 @JsonSerialize(using = DPIDSerializer.class)
261 @JsonProperty("dpid")
262 public long getId() {
263 if (this.stringId == null)
264 throw new RuntimeException("Features reply has not yet been set");
265 return this.datapathId.getLong();
266 }
267
268 @JsonIgnore
269 @Override
270 public String getStringId() {
271 return stringId;
272 }
273
Jonathan Harta213bce2014-08-11 15:44:07 -0700274 @Override
275 public OFFactory getFactory() {
276 return OFFactories.getFactory(ofversion);
Brian O'Connorc67f9fa2014-08-07 18:17:46 -0700277 }
278
Jonathan Harta213bce2014-08-11 15:44:07 -0700279 @Override
280 public OFVersion getOFVersion() {
281 return ofversion;
282 }
283
284 @Override
Brian O'Connorc67f9fa2014-08-07 18:17:46 -0700285 public void setOFVersion(OFVersion ofv) {
Jonathan Harta213bce2014-08-11 15:44:07 -0700286 ofversion = ofv;
Brian O'Connorc67f9fa2014-08-07 18:17:46 -0700287 }
288
289 /**
290 * @param floodlightProvider the floodlightProvider to set
291 */
292 @JsonIgnore
293 @Override
294 public void setFloodlightProvider(IFloodlightProviderService floodlightProvider) {
295 this.floodlightProvider = floodlightProvider;
296 }
297
298 @JsonIgnore
299 @Override
300 public void setThreadPoolService(IThreadPoolService tp) {
301 this.threadPool = tp;
302 }
303
304 @Override
305 @JsonIgnore
306 public void setDebugCounterService(IDebugCounterService debugCounters)
307 throws CounterException {
308 this.debugCounters = debugCounters;
309 registerOverloadCounters();
310 }
311
312 /* (non-Javadoc)
313 * @see java.lang.Object#toString()
314 */
315 @Override
316 public String toString() {
317 return "OFSwitchImpl [" + ((channel != null) ? channel.getRemoteAddress() : "?")
Saurav Dasfcdad072014-08-13 14:15:21 -0700318 + " DPID[" + ((stringId != null) ? stringId : "?") + "]]";
Brian O'Connorc67f9fa2014-08-07 18:17:46 -0700319 }
320
Saurav Dasfcdad072014-08-13 14:15:21 -0700321 // *******************************************
322 // Channel related methods
323 // *******************************************
Brian O'Connorc67f9fa2014-08-07 18:17:46 -0700324
325 @JsonIgnore
326 @Override
327 public void setChannel(Channel channel) {
328 this.channel = channel;
329 }
Fahad Naeem Khan1f8fb2e2014-09-25 16:39:18 -0700330 @Override
331 public SocketAddress getChannelSocketAddress(){
332 return channel.getRemoteAddress();
333 }
Brian O'Connorc67f9fa2014-08-07 18:17:46 -0700334
335 @Override
336 public void write(OFMessage m, FloodlightContext bc) throws IOException {
337 Map<OFSwitchImplBase, List<OFMessage>> msg_buffer_map = local_msg_buffer.get();
338 List<OFMessage> msg_buffer = msg_buffer_map.get(this);
339 if (msg_buffer == null) {
340 msg_buffer = new ArrayList<OFMessage>();
341 msg_buffer_map.put(this, msg_buffer);
342 }
343 // XXX S will change when iFloodlight provider changes
Saurav Dasfcdad072014-08-13 14:15:21 -0700344 // this.floodlightProvider.handleOutgoingMessage(this, m, bc);
Brian O'Connorc67f9fa2014-08-07 18:17:46 -0700345 msg_buffer.add(m);
346
347 if ((msg_buffer.size() >= Controller.BATCH_MAX_SIZE) ||
348 ((m.getType() != OFType.PACKET_OUT) && (m.getType() != OFType.FLOW_MOD))) {
349 this.write(msg_buffer);
350 msg_buffer.clear();
351 }
352 }
353
354 @Override
355 @LogMessageDoc(level = "WARN",
356 message = "Sending OF message that modifies switch " +
357 "state while in the slave role: {switch}",
358 explanation = "An application has sent a message to a switch " +
359 "that is not valid when the switch is in a slave role",
360 recommendation = LogMessageDoc.REPORT_CONTROLLER_BUG)
361 public void write(List<OFMessage> msglist,
Saurav Dasfcdad072014-08-13 14:15:21 -0700362 FloodlightContext bc) throws IOException {
Brian O'Connorc67f9fa2014-08-07 18:17:46 -0700363 for (OFMessage m : msglist) {
364 if (role == Role.SLAVE) {
365 switch (m.getType()) {
Saurav Dasfcdad072014-08-13 14:15:21 -0700366 case PACKET_OUT:
367 case FLOW_MOD:
368 case PORT_MOD:
369 log.warn("Sending OF message that modifies switch " +
370 "state while in the slave role: {}",
371 m.getType().name());
372 break;
373 default:
374 break;
Brian O'Connorc67f9fa2014-08-07 18:17:46 -0700375 }
376 }
377 // XXX S again
Saurav Dasfcdad072014-08-13 14:15:21 -0700378 // this.floodlightProvider.handleOutgoingMessage(this, m, bc);
Brian O'Connorc67f9fa2014-08-07 18:17:46 -0700379 }
380 this.write(msglist);
381 }
382
Saurav Das0a344b02014-09-26 14:18:52 -0700383 protected void write(List<OFMessage> msglist) throws IOException {
Brian O'Connorc67f9fa2014-08-07 18:17:46 -0700384 this.channel.write(msglist);
385 }
386
387 @Override
388 public void disconnectSwitch() {
389 channel.close();
390 }
391
392 @Override
393 public Date getConnectedSince() {
394 return connectedSince;
395 }
396
397 @JsonIgnore
398 @Override
399 public int getNextTransactionId() {
400 return this.transactionIdSource.incrementAndGet();
401 }
402
403 @JsonIgnore
404 @Override
405 public synchronized boolean isConnected() {
406 return connected;
407 }
408
409 @Override
410 @JsonIgnore
411 public synchronized void setConnected(boolean connected) {
412 this.connected = connected;
413 }
414
Saurav Dasfcdad072014-08-13 14:15:21 -0700415 // *******************************************
416 // Switch features related methods
417 // *******************************************
Brian O'Connorc67f9fa2014-08-07 18:17:46 -0700418
419 /**
420 * Set the features reply for this switch from the handshake
421 */
422 protected void setFeaturesReply(OFFeaturesReply featuresReply) {
Saurav Dasfcdad072014-08-13 14:15:21 -0700423 if (featuresReply == null) {
424 log.error("Error setting featuresReply for switch: {}", getStringId());
425 return;
426 }
427 this.datapathId = featuresReply.getDatapathId();
428 this.capabilities = featuresReply.getCapabilities();
429 this.buffers = (int) featuresReply.getNBuffers();
430 this.tables = (byte) featuresReply.getNTables();
431 this.stringId = this.datapathId.toString();
432 if (ofversion == OFVersion.OF_13) {
433 auxId = featuresReply.getAuxiliaryId();
434 if (!auxId.equals(OFAuxId.MAIN)) {
435 log.warn("This controller does not handle auxiliary connections. "
436 + "Aux connection id {} received from switch {}",
437 auxId, getStringId());
438 }
439 }
Brian O'Connorc67f9fa2014-08-07 18:17:46 -0700440
Saurav Dasfcdad072014-08-13 14:15:21 -0700441 if (ofversion == OFVersion.OF_10) {
442 this.actions = featuresReply.getActions();
443 portManager.compareAndUpdatePorts(featuresReply.getPorts(), true);
444 }
Brian O'Connorc67f9fa2014-08-07 18:17:46 -0700445 }
446
447 /**
Saurav Dasfcdad072014-08-13 14:15:21 -0700448 * Set the port descriptions for this switch from the handshake for an OF1.3
449 * switch.
Brian O'Connorc67f9fa2014-08-07 18:17:46 -0700450 */
451 protected void setPortDescReply(OFPortDescStatsReply pdrep) {
Saurav Dasfcdad072014-08-13 14:15:21 -0700452 if (ofversion != OFVersion.OF_13)
453 return;
454 if (pdrep == null) {
455 log.error("Error setting ports description for switch: {}", getStringId());
456 return;
457 }
458 portManager.updatePorts(pdrep.getEntries());
Brian O'Connorc67f9fa2014-08-07 18:17:46 -0700459 }
460
461 @Override
462 public int getNumBuffers() {
463 return buffers;
464 }
Saurav Dasfcdad072014-08-13 14:15:21 -0700465
Brian O'Connorc67f9fa2014-08-07 18:17:46 -0700466 @Override
467 public Set<OFActionType> getActions() {
468 return actions;
469 }
Saurav Dasfcdad072014-08-13 14:15:21 -0700470
Brian O'Connorc67f9fa2014-08-07 18:17:46 -0700471 @Override
Saurav Dasfcdad072014-08-13 14:15:21 -0700472 public Set<OFCapabilities> getCapabilities() {
Brian O'Connorc67f9fa2014-08-07 18:17:46 -0700473 return capabilities;
474 }
Saurav Dasfcdad072014-08-13 14:15:21 -0700475
Brian O'Connorc67f9fa2014-08-07 18:17:46 -0700476 @Override
477 public byte getNumTables() {
478 return tables;
479 }
480
Saurav Dasfcdad072014-08-13 14:15:21 -0700481 // public Future<OFFeaturesReply> getFeaturesReplyFromSwitch()
482 // throws IOException {
483 // // XXX S fix this later
484 // OFMessage request = floodlightProvider.getOFMessageFactory_13()
485 // .buildFeaturesRequest()
486 // .setXid(getNextTransactionId())
487 // .build();
488 // OFFeaturesReplyFuture future =
489 // new OFFeaturesReplyFuture(threadPool, this, (int) request.getXid());
490 // this.featuresFutureMap.put((int) request.getXid(), future);
491 // this.channel.write(Collections.singletonList(request));
492 // return future;
493 //
494 // }
495 //
496 // public void deliverOFFeaturesReply(OFMessage reply) {
497 // OFFeaturesReplyFuture future =
498 // this.featuresFutureMap.get(reply.getXid());
499 // if (future != null) {
500 // future.deliverFuture(this, reply);
501 // // The future will ultimately unregister itself and call
502 // // cancelFeaturesReply
503 // return;
504 // }
505 // log.error("Switch {}: received unexpected featureReply", this);
506 // }
Brian O'Connorc67f9fa2014-08-07 18:17:46 -0700507
508 @Override
509 public void cancelFeaturesReply(int transactionId) {
510 this.featuresFutureMap.remove(transactionId);
511 }
512
513 @JsonIgnore
514 public void setSwitchDescription(OFDescStatsReply desc) {
Saurav Dasfcdad072014-08-13 14:15:21 -0700515 switchDescription = desc;
Brian O'Connorc67f9fa2014-08-07 18:17:46 -0700516 }
517
518 @Override
519 @JsonIgnore
520 public OFDescStatsReply getSwitchDescription() {
Saurav Dasfcdad072014-08-13 14:15:21 -0700521 return switchDescription;
Brian O'Connorc67f9fa2014-08-07 18:17:46 -0700522 }
523
Saurav Dasfcdad072014-08-13 14:15:21 -0700524 // *******************************************
525 // Switch port handling
526 // *******************************************
Brian O'Connorc67f9fa2014-08-07 18:17:46 -0700527
528 @Override
529 @JsonIgnore
530 public Collection<OFPortDesc> getEnabledPorts() {
531 return portManager.getEnabledPorts();
532 }
533
534 @Override
535 @JsonIgnore
536 public Collection<Integer> getEnabledPortNumbers() {
537 return portManager.getEnabledPortNumbers();
538 }
539
540 @Override
541 public OFPortDesc getPort(int portNumber) {
542 return portManager.getPort(portNumber);
543 }
544
545 @Override
546 public OFPortDesc getPort(String portName) {
547 return portManager.getPort(portName);
548 }
549
550 @Override
551 @JsonIgnore
552 public OrderedCollection<PortChangeEvent> processOFPortStatus(OFPortStatus ps) {
553 return portManager.handlePortStatusMessage(ps);
554 }
555
556 @Override
557 @JsonProperty("ports")
558 public Collection<OFPortDesc> getPorts() {
Saurav Dasfcdad072014-08-13 14:15:21 -0700559 return portManager.getPorts();
Brian O'Connorc67f9fa2014-08-07 18:17:46 -0700560 }
561
562 @Override
563 public boolean portEnabled(int portNumber) {
564 return isEnabled(portManager.getPort(portNumber));
565 }
566
567 @Override
568 public boolean portEnabled(String portName) {
Saurav Dasfcdad072014-08-13 14:15:21 -0700569 return isEnabled(portManager.getPort(portName));
Brian O'Connorc67f9fa2014-08-07 18:17:46 -0700570 }
571
572 private boolean isEnabled(OFPortDesc p) {
Saurav Dasfcdad072014-08-13 14:15:21 -0700573 return (p != null &&
574 !p.getState().contains(OFPortState.LINK_DOWN) &&
575 !p.getState().contains(OFPortState.BLOCKED) && !p.getConfig().contains(
576 OFPortConfig.PORT_DOWN));
Brian O'Connorc67f9fa2014-08-07 18:17:46 -0700577 }
578
579 @Override
580 public OrderedCollection<PortChangeEvent> comparePorts(Collection<OFPortDesc> ports) {
581 return portManager.comparePorts(ports);
582 }
583
584 @Override
585 @JsonIgnore
586 public OrderedCollection<PortChangeEvent> setPorts(Collection<OFPortDesc> ports) {
587 return portManager.updatePorts(ports);
588 }
589
590 /**
591 * Manages the ports of this switch.
Jonathan Hart23daf192014-08-22 10:56:58 -0700592 *
Brian O'Connorc67f9fa2014-08-07 18:17:46 -0700593 * Provides methods to query and update the stored ports. The class ensures
Saurav Dasfcdad072014-08-13 14:15:21 -0700594 * that every port name and port number is unique. When updating ports the
595 * class checks if port number <-> port name mappings have change due to the
596 * update. If a new port P has number and port that are inconsistent with
597 * the previous mapping(s) the class will delete all previous ports with
598 * name or number of the new port and then add the new port.
Jonathan Hart23daf192014-08-22 10:56:58 -0700599 *
Brian O'Connorc67f9fa2014-08-07 18:17:46 -0700600 * Port names are stored as-is but they are compared case-insensitive
Jonathan Hart23daf192014-08-22 10:56:58 -0700601 *
Brian O'Connorc67f9fa2014-08-07 18:17:46 -0700602 * The methods that change the stored ports return a list of
Saurav Dasfcdad072014-08-13 14:15:21 -0700603 * PortChangeEvents that represent the changes that have been applied to the
604 * port list so that IOFSwitchListeners can be notified about the changes.
Jonathan Hart23daf192014-08-22 10:56:58 -0700605 *
Saurav Dasfcdad072014-08-13 14:15:21 -0700606 * Implementation notes: - We keep several different representations of the
607 * ports to allow for fast lookups - Ports are stored in unchangeable lists.
608 * When a port is modified new data structures are allocated. - We use a
609 * read-write-lock for synchronization, so multiple readers are allowed. -
610 * All port numbers have int representation (no more shorts)
Brian O'Connorc67f9fa2014-08-07 18:17:46 -0700611 */
612 protected class PortManager {
613 private final ReentrantReadWriteLock lock;
614 private List<OFPortDesc> portList;
615 private List<OFPortDesc> enabledPortList;
616 private List<Integer> enabledPortNumbers;
Saurav Dasfcdad072014-08-13 14:15:21 -0700617 private Map<Integer, OFPortDesc> portsByNumber;
618 private Map<String, OFPortDesc> portsByName;
Brian O'Connorc67f9fa2014-08-07 18:17:46 -0700619
620 public PortManager() {
621 this.lock = new ReentrantReadWriteLock();
622 this.portList = Collections.emptyList();
623 this.enabledPortList = Collections.emptyList();
624 this.enabledPortNumbers = Collections.emptyList();
625 this.portsByName = Collections.emptyMap();
626 this.portsByNumber = Collections.emptyMap();
627 }
628
629 /**
Saurav Dasfcdad072014-08-13 14:15:21 -0700630 * Set the internal data structure storing this switch's port to the
631 * ports specified by newPortsByNumber
Jonathan Hart23daf192014-08-22 10:56:58 -0700632 *
Brian O'Connorc67f9fa2014-08-07 18:17:46 -0700633 * CALLER MUST HOLD WRITELOCK
Jonathan Hart23daf192014-08-22 10:56:58 -0700634 *
Brian O'Connorc67f9fa2014-08-07 18:17:46 -0700635 * @param newPortsByNumber
636 * @throws IllegaalStateException if called without holding the
Saurav Dasfcdad072014-08-13 14:15:21 -0700637 * writelock
Brian O'Connorc67f9fa2014-08-07 18:17:46 -0700638 */
639 private void updatePortsWithNewPortsByNumber(
Saurav Dasfcdad072014-08-13 14:15:21 -0700640 Map<Integer, OFPortDesc> newPortsByNumber) {
Brian O'Connorc67f9fa2014-08-07 18:17:46 -0700641 if (!lock.writeLock().isHeldByCurrentThread()) {
642 throw new IllegalStateException("Method called without " +
Saurav Dasfcdad072014-08-13 14:15:21 -0700643 "holding writeLock");
Brian O'Connorc67f9fa2014-08-07 18:17:46 -0700644 }
Saurav Dasfcdad072014-08-13 14:15:21 -0700645 Map<String, OFPortDesc> newPortsByName =
Brian O'Connorc67f9fa2014-08-07 18:17:46 -0700646 new HashMap<String, OFPortDesc>();
647 List<OFPortDesc> newPortList =
648 new ArrayList<OFPortDesc>();
649 List<OFPortDesc> newEnabledPortList =
650 new ArrayList<OFPortDesc>();
651 List<Integer> newEnabledPortNumbers = new ArrayList<Integer>();
652
Saurav Dasfcdad072014-08-13 14:15:21 -0700653 for (OFPortDesc p : newPortsByNumber.values()) {
Brian O'Connorc67f9fa2014-08-07 18:17:46 -0700654 newPortList.add(p);
655 newPortsByName.put(p.getName().toLowerCase(), p);
656 if (isEnabled(p)) {
657 newEnabledPortList.add(p);
658 newEnabledPortNumbers.add(p.getPortNo().getPortNumber());
659 }
660 }
661 portsByName = Collections.unmodifiableMap(newPortsByName);
662 portsByNumber =
663 Collections.unmodifiableMap(newPortsByNumber);
664 enabledPortList =
665 Collections.unmodifiableList(newEnabledPortList);
666 enabledPortNumbers =
667 Collections.unmodifiableList(newEnabledPortNumbers);
668 portList = Collections.unmodifiableList(newPortList);
669 }
670
671 /**
Saurav Dasfcdad072014-08-13 14:15:21 -0700672 * Handle a OFPortStatus delete message for the given port. Updates the
673 * internal port maps/lists of this switch and returns the
674 * PortChangeEvents caused by the delete. If the given port exists as
675 * it, it will be deleted. If the name<->number for the given port is
676 * inconsistent with the ports stored by this switch the method will
677 * delete all ports with the number or name of the given port.
Jonathan Hart23daf192014-08-22 10:56:58 -0700678 *
Brian O'Connorc67f9fa2014-08-07 18:17:46 -0700679 * This method will increment error/warn counters and log
Jonathan Hart23daf192014-08-22 10:56:58 -0700680 *
Saurav Dasfcdad072014-08-13 14:15:21 -0700681 * @param delPort the port from the port status message that should be
682 * deleted.
Brian O'Connorc67f9fa2014-08-07 18:17:46 -0700683 * @return ordered collection of port changes applied to this switch
684 */
685 private OrderedCollection<PortChangeEvent> handlePortStatusDelete(
Saurav Dasfcdad072014-08-13 14:15:21 -0700686 OFPortDesc delPort) {
Brian O'Connorc67f9fa2014-08-07 18:17:46 -0700687 lock.writeLock().lock();
688 OrderedCollection<PortChangeEvent> events =
689 new LinkedHashSetWrapper<PortChangeEvent>();
690 try {
Saurav Dasfcdad072014-08-13 14:15:21 -0700691 Map<Integer, OFPortDesc> newPortByNumber =
Brian O'Connorc67f9fa2014-08-07 18:17:46 -0700692 new HashMap<Integer, OFPortDesc>(portsByNumber);
693 OFPortDesc prevPort =
694 portsByNumber.get(delPort.getPortNo().getPortNumber());
695 if (prevPort == null) {
696 // so such port. Do we have a port with the name?
697 prevPort = portsByName.get(delPort.getName());
698 if (prevPort != null) {
699 newPortByNumber.remove(prevPort.getPortNo().getPortNumber());
700 events.add(new PortChangeEvent(prevPort,
Saurav Dasfcdad072014-08-13 14:15:21 -0700701 PortChangeType.DELETE));
Brian O'Connorc67f9fa2014-08-07 18:17:46 -0700702 }
703 } else if (prevPort.getName().equals(delPort.getName())) {
704 // port exists with consistent name-number mapping
705 newPortByNumber.remove(delPort.getPortNo().getPortNumber());
706 events.add(new PortChangeEvent(delPort,
Saurav Dasfcdad072014-08-13 14:15:21 -0700707 PortChangeType.DELETE));
Brian O'Connorc67f9fa2014-08-07 18:17:46 -0700708 } else {
709 // port with same number exists but its name differs. This
710 // is weird. The best we can do is to delete the existing
711 // port(s) that have delPort's name and number.
712 newPortByNumber.remove(delPort.getPortNo().getPortNumber());
713 events.add(new PortChangeEvent(prevPort,
Saurav Dasfcdad072014-08-13 14:15:21 -0700714 PortChangeType.DELETE));
Brian O'Connorc67f9fa2014-08-07 18:17:46 -0700715 // is there another port that has delPort's name?
716 prevPort = portsByName.get(delPort.getName().toLowerCase());
717 if (prevPort != null) {
718 newPortByNumber.remove(prevPort.getPortNo().getPortNumber());
719 events.add(new PortChangeEvent(prevPort,
Saurav Dasfcdad072014-08-13 14:15:21 -0700720 PortChangeType.DELETE));
Brian O'Connorc67f9fa2014-08-07 18:17:46 -0700721 }
722 }
723 updatePortsWithNewPortsByNumber(newPortByNumber);
724 return events;
725 } finally {
726 lock.writeLock().unlock();
727 }
728 }
729
730 /**
731 * Handle a OFPortStatus message, update the internal data structures
732 * that store ports and return the list of OFChangeEvents.
Jonathan Hart23daf192014-08-22 10:56:58 -0700733 *
Brian O'Connorc67f9fa2014-08-07 18:17:46 -0700734 * This method will increment error/warn counters and log
Jonathan Hart23daf192014-08-22 10:56:58 -0700735 *
Brian O'Connorc67f9fa2014-08-07 18:17:46 -0700736 * @param ps
737 * @return
738 */
739 public OrderedCollection<PortChangeEvent> handlePortStatusMessage(OFPortStatus ps) {
740 if (ps == null) {
741 throw new NullPointerException("OFPortStatus message must " +
Saurav Dasfcdad072014-08-13 14:15:21 -0700742 "not be null");
Brian O'Connorc67f9fa2014-08-07 18:17:46 -0700743 }
744 lock.writeLock().lock();
745 try {
746 OFPortReason reason = ps.getReason();
747 if (reason == null) {
748 throw new IllegalArgumentException("Unknown PortStatus " +
749 "reason code " + ps.getReason());
750 }
751
752 if (log.isDebugEnabled()) {
Saurav Das80d17392014-10-01 10:24:56 -0700753 log.debug("Handling OFPortStatus: {} for {} in sw {}",
754 reason, ps, getStringId());
Brian O'Connorc67f9fa2014-08-07 18:17:46 -0700755 }
756
757 if (reason == OFPortReason.DELETE)
Saurav Dasfcdad072014-08-13 14:15:21 -0700758 return handlePortStatusDelete(ps.getDesc());
Brian O'Connorc67f9fa2014-08-07 18:17:46 -0700759
760 // We handle ADD and MODIFY the same way. Since OpenFlow
761 // doesn't specify what uniquely identifies a port the
762 // notion of ADD vs. MODIFY can also be hazy. So we just
763 // compare the new port to the existing ones.
Saurav Dasfcdad072014-08-13 14:15:21 -0700764 Map<Integer, OFPortDesc> newPortByNumber =
765 new HashMap<Integer, OFPortDesc>(portsByNumber);
Brian O'Connorc67f9fa2014-08-07 18:17:46 -0700766 OrderedCollection<PortChangeEvent> events =
Saurav Dasfcdad072014-08-13 14:15:21 -0700767 getSinglePortChanges(ps.getDesc());
768 for (PortChangeEvent e : events) {
769 switch (e.type) {
770 case DELETE:
771 newPortByNumber.remove(e.port.getPortNo().getPortNumber());
772 break;
773 case ADD:
774 if (reason != OFPortReason.ADD) {
775 // weird case
776 }
777 newPortByNumber.put(e.port.getPortNo().getPortNumber(),
778 e.port);
779 break;
780 case DOWN:
781 newPortByNumber.put(e.port.getPortNo().getPortNumber(),
782 e.port);
783 break;
784 case OTHER_UPDATE:
785 newPortByNumber.put(e.port.getPortNo().getPortNumber(),
786 e.port);
787 break;
788 case UP:
789 newPortByNumber.put(e.port.getPortNo().getPortNumber(),
790 e.port);
791 break;
Brian O'Connorc67f9fa2014-08-07 18:17:46 -0700792 }
793 }
794 updatePortsWithNewPortsByNumber(newPortByNumber);
795 return events;
796 } finally {
797 lock.writeLock().unlock();
798 }
799
800 }
801
802 /**
803 * Given a new or modified port newPort, returns the list of
Saurav Dasfcdad072014-08-13 14:15:21 -0700804 * PortChangeEvents to "transform" the current ports stored by this
805 * switch to include / represent the new port. The ports stored by this
806 * switch are <b>NOT</b> updated.
Jonathan Hart23daf192014-08-22 10:56:58 -0700807 *
Saurav Dasfcdad072014-08-13 14:15:21 -0700808 * This method acquires the readlock and is thread-safe by itself. Most
809 * callers will need to acquire the write lock before calling this
810 * method though (if the caller wants to update the ports stored by this
811 * switch)
Jonathan Hart23daf192014-08-22 10:56:58 -0700812 *
Brian O'Connorc67f9fa2014-08-07 18:17:46 -0700813 * @param newPort the new or modified port.
814 * @return the list of changes
815 */
816 public OrderedCollection<PortChangeEvent> getSinglePortChanges(
Saurav Dasfcdad072014-08-13 14:15:21 -0700817 OFPortDesc newPort) {
Brian O'Connorc67f9fa2014-08-07 18:17:46 -0700818 lock.readLock().lock();
819 try {
820 OrderedCollection<PortChangeEvent> events =
821 new LinkedHashSetWrapper<PortChangeEvent>();
822 // Check if we have a port by the same number in our
823 // old map.
824 OFPortDesc prevPort =
825 portsByNumber.get(newPort.getPortNo().getPortNumber());
826 if (newPort.equals(prevPort)) {
827 // nothing has changed
828 return events;
829 }
830
831 if (prevPort != null &&
832 prevPort.getName().equals(newPort.getName())) {
833 // A simple modify of a existing port
834 // A previous port with this number exists and it's name
835 // also matches the new port. Find the differences
836 if (isEnabled(prevPort) && !isEnabled(newPort)) {
837 events.add(new PortChangeEvent(newPort,
Saurav Dasfcdad072014-08-13 14:15:21 -0700838 PortChangeType.DOWN));
Brian O'Connorc67f9fa2014-08-07 18:17:46 -0700839 } else if (!isEnabled(prevPort) && isEnabled(newPort)) {
840 events.add(new PortChangeEvent(newPort,
Saurav Dasfcdad072014-08-13 14:15:21 -0700841 PortChangeType.UP));
Brian O'Connorc67f9fa2014-08-07 18:17:46 -0700842 } else {
843 events.add(new PortChangeEvent(newPort,
Saurav Dasfcdad072014-08-13 14:15:21 -0700844 PortChangeType.OTHER_UPDATE));
Brian O'Connorc67f9fa2014-08-07 18:17:46 -0700845 }
846 return events;
847 }
848
849 if (prevPort != null) {
850 // There exists a previous port with the same port
851 // number but the port name is different (otherwise we would
852 // never have gotten here)
853 // Remove the port. Name-number mapping(s) have changed
854 events.add(new PortChangeEvent(prevPort,
Saurav Dasfcdad072014-08-13 14:15:21 -0700855 PortChangeType.DELETE));
Brian O'Connorc67f9fa2014-08-07 18:17:46 -0700856 }
857
858 // We now need to check if there exists a previous port sharing
859 // the same name as the new/updated port.
860 prevPort = portsByName.get(newPort.getName().toLowerCase());
861 if (prevPort != null) {
862 // There exists a previous port with the same port
863 // name but the port number is different (otherwise we
864 // never have gotten here).
865 // Remove the port. Name-number mapping(s) have changed
866 events.add(new PortChangeEvent(prevPort,
Saurav Dasfcdad072014-08-13 14:15:21 -0700867 PortChangeType.DELETE));
Brian O'Connorc67f9fa2014-08-07 18:17:46 -0700868 }
869
870 // We always need to add the new port. Either no previous port
871 // existed or we just deleted previous ports with inconsistent
872 // name-number mappings
873 events.add(new PortChangeEvent(newPort, PortChangeType.ADD));
874 return events;
875 } finally {
876 lock.readLock().unlock();
877 }
878 }
879
880 /**
881 * Compare the current ports of this switch to the newPorts list and
882 * return the changes that would be applied to transfort the current
Saurav Dasfcdad072014-08-13 14:15:21 -0700883 * ports to the new ports. No internal data structures are updated see
884 * {@link #compareAndUpdatePorts(List, boolean)}
Jonathan Hart23daf192014-08-22 10:56:58 -0700885 *
Brian O'Connorc67f9fa2014-08-07 18:17:46 -0700886 * @param newPorts the list of new ports
887 * @return The list of differences between the current ports and
Saurav Dasfcdad072014-08-13 14:15:21 -0700888 * newPortList
Brian O'Connorc67f9fa2014-08-07 18:17:46 -0700889 */
890 public OrderedCollection<PortChangeEvent> comparePorts(
Saurav Dasfcdad072014-08-13 14:15:21 -0700891 Collection<OFPortDesc> newPorts) {
Brian O'Connorc67f9fa2014-08-07 18:17:46 -0700892 return compareAndUpdatePorts(newPorts, false);
893 }
894
895 /**
896 * Compare the current ports of this switch to the newPorts list and
897 * return the changes that would be applied to transform the current
Saurav Dasfcdad072014-08-13 14:15:21 -0700898 * ports to the new ports. No internal data structures are updated see
899 * {@link #compareAndUpdatePorts(List, boolean)}
Jonathan Hart23daf192014-08-22 10:56:58 -0700900 *
Brian O'Connorc67f9fa2014-08-07 18:17:46 -0700901 * @param newPorts the list of new ports
902 * @return The list of differences between the current ports and
Saurav Dasfcdad072014-08-13 14:15:21 -0700903 * newPortList
Brian O'Connorc67f9fa2014-08-07 18:17:46 -0700904 */
905 public OrderedCollection<PortChangeEvent> updatePorts(
Saurav Dasfcdad072014-08-13 14:15:21 -0700906 Collection<OFPortDesc> newPorts) {
Brian O'Connorc67f9fa2014-08-07 18:17:46 -0700907 return compareAndUpdatePorts(newPorts, true);
908 }
909
910 /**
Saurav Dasfcdad072014-08-13 14:15:21 -0700911 * Compare the current ports stored in this switch instance with the new
912 * port list given and return the differences in the form of
Brian O'Connorc67f9fa2014-08-07 18:17:46 -0700913 * PortChangeEvents. If the doUpdate flag is true, newPortList will
914 * replace the current list of this switch (and update the port maps)
Jonathan Hart23daf192014-08-22 10:56:58 -0700915 *
Saurav Dasfcdad072014-08-13 14:15:21 -0700916 * Implementation note: Since this method can optionally modify the
917 * current ports and since it's not possible to upgrade a read-lock to a
918 * write-lock we need to hold the write-lock for the entire operation.
919 * If this becomes a problem and if compares() are common we can
920 * consider splitting in two methods but this requires lots of code
921 * duplication
Jonathan Hart23daf192014-08-22 10:56:58 -0700922 *
Brian O'Connorc67f9fa2014-08-07 18:17:46 -0700923 * @param newPorts the list of new ports.
Saurav Dasfcdad072014-08-13 14:15:21 -0700924 * @param doUpdate If true the newPortList will replace the current port
925 * list for this switch. If false this switch will not be
926 * changed.
Brian O'Connorc67f9fa2014-08-07 18:17:46 -0700927 * @return The list of differences between the current ports and
Saurav Dasfcdad072014-08-13 14:15:21 -0700928 * newPorts
Brian O'Connorc67f9fa2014-08-07 18:17:46 -0700929 * @throws NullPointerException if newPortsList is null
930 * @throws IllegalArgumentException if either port names or port numbers
Saurav Dasfcdad072014-08-13 14:15:21 -0700931 * are duplicated in the newPortsList.
Brian O'Connorc67f9fa2014-08-07 18:17:46 -0700932 */
933 private OrderedCollection<PortChangeEvent> compareAndUpdatePorts(
934 Collection<OFPortDesc> newPorts, boolean doUpdate) {
935 if (newPorts == null) {
936 throw new NullPointerException("newPortsList must not be null");
937 }
938 lock.writeLock().lock();
939 try {
940 OrderedCollection<PortChangeEvent> events =
941 new LinkedHashSetWrapper<PortChangeEvent>();
942
Saurav Dasfcdad072014-08-13 14:15:21 -0700943 Map<Integer, OFPortDesc> newPortsByNumber =
Brian O'Connorc67f9fa2014-08-07 18:17:46 -0700944 new HashMap<Integer, OFPortDesc>();
Saurav Dasfcdad072014-08-13 14:15:21 -0700945 Map<String, OFPortDesc> newPortsByName =
Brian O'Connorc67f9fa2014-08-07 18:17:46 -0700946 new HashMap<String, OFPortDesc>();
947 List<OFPortDesc> newEnabledPortList =
948 new ArrayList<OFPortDesc>();
949 List<Integer> newEnabledPortNumbers =
950 new ArrayList<Integer>();
951 List<OFPortDesc> newPortsList =
952 new ArrayList<OFPortDesc>(newPorts);
953
Saurav Dasfcdad072014-08-13 14:15:21 -0700954 for (OFPortDesc p : newPortsList) {
Brian O'Connorc67f9fa2014-08-07 18:17:46 -0700955 if (p == null) {
956 throw new NullPointerException("portList must not " +
957 "contain null values");
958 }
959
960 // Add the port to the new maps and lists and check
961 // that every port is unique
962 OFPortDesc duplicatePort;
963 duplicatePort = newPortsByNumber.put(
Saurav Dasfcdad072014-08-13 14:15:21 -0700964 p.getPortNo().getPortNumber(), p);
Brian O'Connorc67f9fa2014-08-07 18:17:46 -0700965 if (duplicatePort != null) {
966 String msg = String.format("Cannot have two ports " +
967 "with the same number: %s <-> %s",
968 p, duplicatePort);
969 throw new IllegalArgumentException(msg);
970 }
971 duplicatePort =
972 newPortsByName.put(p.getName().toLowerCase(), p);
973 if (duplicatePort != null) {
974 String msg = String.format("Cannot have two ports " +
975 "with the same name: %s <-> %s",
Saurav Dasfcdad072014-08-13 14:15:21 -0700976 p.toString().substring(0, 80),
Brian O'Connorc67f9fa2014-08-07 18:17:46 -0700977 duplicatePort.toString().substring(0, 80));
978 throw new IllegalArgumentException(msg);
979 }
980 if (isEnabled(p)) {
981 newEnabledPortList.add(p);
982 newEnabledPortNumbers.add(p.getPortNo().getPortNumber());
983 }
984
985 // get changes
986 events.addAll(getSinglePortChanges(p));
987 }
988 // find deleted ports
989 // We need to do this after looping through all the new ports
990 // to we can handle changed name<->number mappings correctly
991 // We could pull it into the loop of we address this but
992 // it's probably not worth it
Saurav Dasfcdad072014-08-13 14:15:21 -0700993 for (OFPortDesc oldPort : this.portList) {
994 if (!newPortsByNumber
995 .containsKey(oldPort.getPortNo().getPortNumber())) {
Brian O'Connorc67f9fa2014-08-07 18:17:46 -0700996 PortChangeEvent ev =
997 new PortChangeEvent(oldPort,
Saurav Dasfcdad072014-08-13 14:15:21 -0700998 PortChangeType.DELETE);
Brian O'Connorc67f9fa2014-08-07 18:17:46 -0700999 events.add(ev);
1000 }
1001 }
1002
Brian O'Connorc67f9fa2014-08-07 18:17:46 -07001003 if (doUpdate) {
1004 portsByName = Collections.unmodifiableMap(newPortsByName);
1005 portsByNumber =
1006 Collections.unmodifiableMap(newPortsByNumber);
1007 enabledPortList =
1008 Collections.unmodifiableList(newEnabledPortList);
1009 enabledPortNumbers =
1010 Collections.unmodifiableList(newEnabledPortNumbers);
1011 portList = Collections.unmodifiableList(newPortsList);
1012 }
1013 return events;
1014 } finally {
1015 lock.writeLock().unlock();
1016 }
1017 }
1018
1019 public OFPortDesc getPort(String name) {
1020 if (name == null) {
1021 throw new NullPointerException("Port name must not be null");
1022 }
1023 lock.readLock().lock();
1024 try {
1025 return portsByName.get(name.toLowerCase());
1026 } finally {
1027 lock.readLock().unlock();
1028 }
1029 }
1030
1031 public OFPortDesc getPort(int portNumber) {
1032 lock.readLock().lock();
1033 try {
1034 return portsByNumber.get(portNumber);
1035 } finally {
1036 lock.readLock().unlock();
1037 }
1038 }
1039
1040 public List<OFPortDesc> getPorts() {
1041 lock.readLock().lock();
1042 try {
1043 return portList;
1044 } finally {
1045 lock.readLock().unlock();
1046 }
1047 }
1048
1049 public List<OFPortDesc> getEnabledPorts() {
1050 lock.readLock().lock();
1051 try {
1052 return enabledPortList;
1053 } finally {
1054 lock.readLock().unlock();
1055 }
1056 }
1057
1058 public List<Integer> getEnabledPortNumbers() {
1059 lock.readLock().lock();
1060 try {
1061 return enabledPortNumbers;
1062 } finally {
1063 lock.readLock().unlock();
1064 }
1065 }
1066 }
1067
Saurav Dasfcdad072014-08-13 14:15:21 -07001068 // *******************************************
1069 // Switch stats handling
1070 // *******************************************
Brian O'Connorc67f9fa2014-08-07 18:17:46 -07001071
1072 @Override
1073 public Future<List<OFStatsReply>> getStatistics(OFStatsRequest<?> request)
Saurav Dasfcdad072014-08-13 14:15:21 -07001074 throws IOException {
Brian O'Connorc67f9fa2014-08-07 18:17:46 -07001075 OFStatisticsFuture future = new OFStatisticsFuture(threadPool, this,
Saurav Dasfcdad072014-08-13 14:15:21 -07001076 (int) request.getXid());
Brian O'Connorc67f9fa2014-08-07 18:17:46 -07001077 log.info("description STAT REQUEST XID {}", request.getXid());
Saurav Dasfcdad072014-08-13 14:15:21 -07001078 this.statsFutureMap.put((int) request.getXid(), future);
Brian O'Connorc67f9fa2014-08-07 18:17:46 -07001079
1080 this.channel.write(Collections.singletonList(request));
1081 return future;
1082 }
1083
1084 @Override
1085 public void deliverStatisticsReply(OFMessage reply) {
Saurav Dasfcdad072014-08-13 14:15:21 -07001086 OFStatisticsFuture future = this.statsFutureMap.get((int) reply.getXid());
Brian O'Connorc67f9fa2014-08-07 18:17:46 -07001087 if (future != null) {
1088 future.deliverFuture(this, reply);
1089 // The future will ultimately unregister itself and call
1090 // cancelStatisticsReply
1091 return;
1092 }
1093 // Transaction id was not found in statsFutureMap.check the other map
1094 IOFMessageListener caller = this.iofMsgListenersMap.get(reply.getXid());
1095 if (caller != null) {
Saurav Dasfcdad072014-08-13 14:15:21 -07001096 caller.receive(this, reply, null);
Brian O'Connorc67f9fa2014-08-07 18:17:46 -07001097 }
1098 }
1099
1100 @Override
1101 public void cancelStatisticsReply(int transactionId) {
1102 if (null == this.statsFutureMap.remove(transactionId)) {
1103 this.iofMsgListenersMap.remove(transactionId);
1104 }
1105 }
1106
1107 @Override
1108 public void cancelAllStatisticsReplies() {
1109 /* we don't need to be synchronized here. Even if another thread
1110 * modifies the map while we're cleaning up the future will eventuall
1111 * timeout */
1112 for (OFStatisticsFuture f : statsFutureMap.values()) {
1113 f.cancel(true);
1114 }
1115 statsFutureMap.clear();
1116 iofMsgListenersMap.clear();
1117 }
1118
Saurav Dasfcdad072014-08-13 14:15:21 -07001119 // *******************************************
1120 // Switch role handling
1121 // *******************************************
Brian O'Connorc67f9fa2014-08-07 18:17:46 -07001122
1123 // XXX S this is completely wrong. The generation id should be obtained
1124 // after coordinating with all controllers connected to this switch.
1125 // ie. should be part of registry service (account for 1.3 vs 1.0)
1126 // For now we are just generating this locally and keeping it constant.
1127 public U64 getNextGenerationId() {
Saurav Dasfcdad072014-08-13 14:15:21 -07001128 // TODO: Pankaj, fix nextGenerationId as part of Registry interface
1129 return U64.of(generationIdSource);
Brian O'Connorc67f9fa2014-08-07 18:17:46 -07001130 }
1131
1132 @Override
1133 public Role getRole() {
1134 return role;
1135 }
1136
1137 @JsonIgnore
1138 @Override
1139 public void setRole(Role role) {
1140 this.role = role;
1141 }
1142
Saurav Dasfcdad072014-08-13 14:15:21 -07001143 // *******************************************
1144 // Switch utility methods
1145 // *******************************************
Brian O'Connorc67f9fa2014-08-07 18:17:46 -07001146
1147 private void registerOverloadCounters() throws CounterException {
1148 if (debugCountersRegistered) {
1149 return;
1150 }
1151 if (debugCounters == null) {
1152 log.error("Debug Counter Service not found");
1153 debugCounters = new NullDebugCounter();
1154 debugCountersRegistered = true;
1155 }
1156 // every level of the hierarchical counter has to be registered
1157 // even if they are not used
1158 ctrSwitch = debugCounters.registerCounter(
Saurav Dasfcdad072014-08-13 14:15:21 -07001159 BASE, stringId,
1160 "Counter for this switch",
1161 CounterType.ALWAYS_COUNT);
Brian O'Connorc67f9fa2014-08-07 18:17:46 -07001162 ctrSwitchPktin = debugCounters.registerCounter(
Saurav Dasfcdad072014-08-13 14:15:21 -07001163 BASE, stringId + "/pktin",
1164 "Packet in counter for this switch",
1165 CounterType.ALWAYS_COUNT);
Brian O'Connorc67f9fa2014-08-07 18:17:46 -07001166 ctrSwitchWrite = debugCounters.registerCounter(
Saurav Dasfcdad072014-08-13 14:15:21 -07001167 BASE, stringId + "/write",
1168 "Write counter for this switch",
1169 CounterType.ALWAYS_COUNT);
Brian O'Connorc67f9fa2014-08-07 18:17:46 -07001170 ctrSwitchPktinDrops = debugCounters.registerCounter(
Saurav Dasfcdad072014-08-13 14:15:21 -07001171 BASE, stringId + "/pktin/drops",
1172 "Packet in throttle drop count",
1173 CounterType.ALWAYS_COUNT,
1174 IDebugCounterService.CTR_MDATA_WARN);
Brian O'Connorc67f9fa2014-08-07 18:17:46 -07001175 ctrSwitchWriteDrops = debugCounters.registerCounter(
Saurav Dasfcdad072014-08-13 14:15:21 -07001176 BASE, stringId + "/write/drops",
1177 "Switch write throttle drop count",
1178 CounterType.ALWAYS_COUNT,
1179 IDebugCounterService.CTR_MDATA_WARN);
Brian O'Connorc67f9fa2014-08-07 18:17:46 -07001180 }
1181
1182 @Override
1183 public void startDriverHandshake() throws IOException {
Saurav Dasfcdad072014-08-13 14:15:21 -07001184 log.debug("Starting driver handshake for sw {}", getStringId());
Brian O'Connorc67f9fa2014-08-07 18:17:46 -07001185 if (startDriverHandshakeCalled)
1186 throw new SwitchDriverSubHandshakeAlreadyStarted();
1187 startDriverHandshakeCalled = true;
1188 }
1189
1190 @Override
1191 public boolean isDriverHandshakeComplete() {
1192 if (!startDriverHandshakeCalled)
1193 throw new SwitchDriverSubHandshakeNotStarted();
1194 return true;
1195 }
1196
1197 @Override
1198 public void processDriverHandshakeMessage(OFMessage m) {
1199 if (startDriverHandshakeCalled)
1200 throw new SwitchDriverSubHandshakeCompleted(m);
1201 else
1202 throw new SwitchDriverSubHandshakeNotStarted();
1203 }
1204
1205 @Override
1206 @JsonIgnore
Saurav Dasfcdad072014-08-13 14:15:21 -07001207 @LogMessageDoc(level = "WARN",
1208 message = "Switch {switch} flow table is full",
1209 explanation = "The controller received flow table full " +
1210 "message from the switch, could be caused by increased " +
1211 "traffic pattern",
1212 recommendation = LogMessageDoc.REPORT_CONTROLLER_BUG)
Brian O'Connorc67f9fa2014-08-07 18:17:46 -07001213 public void setTableFull(boolean isFull) {
1214 if (isFull && !flowTableFull) {
1215 floodlightProvider.addSwitchEvent(this.datapathId.getLong(),
1216 "SWITCH_FLOW_TABLE_FULL " +
Saurav Dasfcdad072014-08-13 14:15:21 -07001217 "Table full error from switch", false);
Brian O'Connorc67f9fa2014-08-07 18:17:46 -07001218 log.warn("Switch {} flow table is full", stringId);
1219 }
Saurav Dasfcdad072014-08-13 14:15:21 -07001220 flowTableFull = isFull;
Brian O'Connorc67f9fa2014-08-07 18:17:46 -07001221 }
1222
1223 @Override
1224 @LogMessageDoc(level = "ERROR",
1225 message = "Failed to clear all flows on switch {switch}",
1226 explanation = "An I/O error occured while trying to clear " +
1227 "flows on the switch.",
1228 recommendation = LogMessageDoc.CHECK_SWITCH)
1229 public void clearAllFlowMods() {
1230 // Delete all pre-existing flows
1231
Jonathan Harta213bce2014-08-11 15:44:07 -07001232 // by default if match is not specified, then an empty list of matches
1233 // is sent, resulting in a wildcard-all flows
Jonathan Hart23daf192014-08-22 10:56:58 -07001234 Builder builder = getFactory()
Saurav Dasfcdad072014-08-13 14:15:21 -07001235 .buildFlowDelete()
Jonathan Hart23daf192014-08-22 10:56:58 -07001236 .setOutPort(OFPort.ANY);
1237
1238 if (ofversion.wireVersion >= OFVersion.OF_13.wireVersion) {
1239 builder.setOutGroup(OFGroup.ANY)
1240 .setTableId(TableId.ALL);
1241 }
1242
1243 OFMessage fm = builder.build();
Brian O'Connorc67f9fa2014-08-07 18:17:46 -07001244
1245 try {
1246 channel.write(Collections.singletonList(fm));
1247 } catch (Exception e) {
1248 log.error("Failed to clear all flows on switch " + this, e);
1249 }
1250 }
1251
1252 @Override
1253 public void flush() {
1254 Map<OFSwitchImplBase, List<OFMessage>> msg_buffer_map = local_msg_buffer.get();
1255 List<OFMessage> msglist = msg_buffer_map.get(this);
1256 if ((msglist != null) && (msglist.size() > 0)) {
1257 try {
1258 this.write(msglist);
1259 } catch (IOException e) {
Saurav Dasfcdad072014-08-13 14:15:21 -07001260 log.error("Failed flushing messages", e);
Brian O'Connorc67f9fa2014-08-07 18:17:46 -07001261 }
1262 msglist.clear();
1263 }
1264 }
1265
1266 public static void flush_all() {
1267 Map<OFSwitchImplBase, List<OFMessage>> msg_buffer_map = local_msg_buffer.get();
1268 for (OFSwitchImplBase sw : msg_buffer_map.keySet()) {
1269 sw.flush();
1270 }
1271 }
1272
1273 /**
1274 * Return a read lock that must be held while calling the listeners for
1275 * messages from the switch. Holding the read lock prevents the active
1276 * switch list from being modified out from under the listeners.
Jonathan Hart23daf192014-08-22 10:56:58 -07001277 *
Brian O'Connorc67f9fa2014-08-07 18:17:46 -07001278 * @return listener read lock
1279 */
1280 @JsonIgnore
1281 public Lock getListenerReadLock() {
1282 return listenerLock.readLock();
1283 }
1284
1285 /**
1286 * Return a write lock that must be held when the controllers modifies the
1287 * list of active switches. This is to ensure that the active switch list
1288 * doesn't change out from under the listeners as they are handling a
1289 * message from the switch.
Jonathan Hart23daf192014-08-22 10:56:58 -07001290 *
Brian O'Connorc67f9fa2014-08-07 18:17:46 -07001291 * @return listener write lock
1292 */
1293 @JsonIgnore
1294 public Lock getListenerWriteLock() {
1295 return listenerLock.writeLock();
1296 }
Saurav Dasfc5e3eb2014-09-25 19:05:21 -07001297
1298 public String getSwitchDriverState() {
1299 return "";
1300 }
1301
Saurav Dasd84178f2014-09-29 17:48:54 -07001302 public OFBarrierReplyFuture sendBarrier() throws IOException {
1303 long xid = getNextTransactionId();
1304 OFMessage br = getFactory()
1305 .buildBarrierRequest()
1306 .setXid(xid)
1307 .build();
1308 write(Collections.singletonList(br));
1309 OFBarrierReplyFuture future = new OFBarrierReplyFuture(threadPool, this,
1310 (int) xid);
1311 barrierFutureMap.put(xid, future);
1312 return future;
1313 }
1314
1315 public void deliverBarrierReply(OFBarrierReply br) {
1316 OFBarrierReplyFuture f = barrierFutureMap.get(br.getXid());
1317 if (f != null) {
1318 f.deliverFuture(this, br);
1319 barrierFutureMap.remove(br.getXid());
1320 }
1321 }
Brian O'Connorc67f9fa2014-08-07 18:17:46 -07001322}