blob: 5ffd9bc784f248d655c5289c6d2c6117cc73c17e [file] [log] [blame]
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -08001/**
2* Copyright 2012, Big Switch Networks, Inc.
3* Originally created by David Erickson, Stanford University
4*
5* Licensed under the Apache License, Version 2.0 (the "License"); you may
6* not use this file except in compliance with the License. You may obtain
7* a copy of the License at
8*
9* http://www.apache.org/licenses/LICENSE-2.0
10*
11* Unless required by applicable law or agreed to in writing, software
12* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
13* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
14* License for the specific language governing permissions and limitations
15* under the License.
16**/
17
18package net.floodlightcontroller.core.internal;
19
20import java.io.IOException;
21import java.net.SocketAddress;
22import 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.concurrent.ConcurrentHashMap;
31import java.util.concurrent.ConcurrentMap;
32import java.util.concurrent.Future;
33import java.util.concurrent.atomic.AtomicInteger;
34import java.util.concurrent.locks.Lock;
35import java.util.concurrent.locks.ReentrantReadWriteLock;
36
37import net.floodlightcontroller.core.FloodlightContext;
38import net.floodlightcontroller.core.IFloodlightProviderService;
39import net.floodlightcontroller.core.IOFMessageListener;
40import net.floodlightcontroller.core.IFloodlightProviderService.Role;
41import net.floodlightcontroller.core.IOFSwitch;
42import net.floodlightcontroller.core.annotations.LogMessageDoc;
43import net.floodlightcontroller.core.annotations.LogMessageDocs;
44import net.floodlightcontroller.core.web.serializers.DPIDSerializer;
45import net.floodlightcontroller.threadpool.IThreadPoolService;
46import net.floodlightcontroller.util.TimedCache;
47
48import org.codehaus.jackson.annotate.JsonIgnore;
49import org.codehaus.jackson.annotate.JsonProperty;
50import org.codehaus.jackson.map.annotate.JsonSerialize;
Pankaj Berde5024ec12013-01-31 17:07:29 -080051import org.codehaus.jackson.map.ser.std.ToStringSerializer;
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -080052import org.jboss.netty.channel.Channel;
53import org.openflow.protocol.OFFeaturesReply;
54import org.openflow.protocol.OFFeaturesRequest;
55import org.openflow.protocol.OFFlowMod;
56import org.openflow.protocol.OFMatch;
57import org.openflow.protocol.OFMessage;
58import org.openflow.protocol.OFPhysicalPort;
59import org.openflow.protocol.OFPort;
60import org.openflow.protocol.OFType;
61import org.openflow.protocol.OFVendor;
62import org.openflow.protocol.OFPhysicalPort.OFPortConfig;
63import org.openflow.protocol.OFPhysicalPort.OFPortState;
64import org.openflow.protocol.OFStatisticsRequest;
65import org.openflow.protocol.statistics.OFDescriptionStatistics;
66import org.openflow.protocol.statistics.OFStatistics;
67import org.openflow.util.HexString;
68import org.openflow.util.U16;
69import org.openflow.vendor.nicira.OFNiciraVendorData;
70import org.openflow.vendor.nicira.OFRoleRequestVendorData;
71import org.openflow.vendor.nicira.OFRoleVendorData;
72import org.slf4j.Logger;
73import org.slf4j.LoggerFactory;
74
75/**
76 * This is the internal representation of an openflow switch.
77 */
78public class OFSwitchImpl implements IOFSwitch {
79 // TODO: should we really do logging in the class or should we throw
80 // exception that can then be handled by callers?
81 protected static Logger log = LoggerFactory.getLogger(OFSwitchImpl.class);
82
83 private static final String HA_CHECK_SWITCH =
84 "Check the health of the indicated switch. If the problem " +
85 "persists or occurs repeatedly, it likely indicates a defect " +
86 "in the switch HA implementation.";
87
88 protected ConcurrentMap<Object, Object> attributes;
89 protected IFloodlightProviderService floodlightProvider;
90 protected IThreadPoolService threadPool;
91 protected Date connectedSince;
92 protected String stringId;
93 protected Channel channel;
94 protected AtomicInteger transactionIdSource;
95 // Lock to protect modification of the port maps. We only need to
96 // synchronize on modifications. For read operations we are fine since
97 // we rely on ConcurrentMaps which works for our use case.
98 private Object portLock;
99 // Map port numbers to the appropriate OFPhysicalPort
100 protected ConcurrentHashMap<Short, OFPhysicalPort> portsByNumber;
101 // Map port names to the appropriate OFPhyiscalPort
102 // XXX: The OF spec doesn't specify if port names need to be unique but
103 // according it's always the case in practice.
104 protected ConcurrentHashMap<String, OFPhysicalPort> portsByName;
105 protected Map<Integer,OFStatisticsFuture> statsFutureMap;
106 protected Map<Integer, IOFMessageListener> iofMsgListenersMap;
107 protected Map<Integer,OFFeaturesReplyFuture> featuresFutureMap;
108 protected boolean connected;
109 protected Role role;
110 protected TimedCache<Long> timedCache;
111 protected ReentrantReadWriteLock listenerLock;
112 protected ConcurrentMap<Short, Long> portBroadcastCacheHitMap;
113 /**
114 * When sending a role request message, the role request is added
115 * to this queue. If a role reply is received this queue is checked to
116 * verify that the reply matches the expected reply. We require in order
117 * delivery of replies. That's why we use a Queue.
118 * The RoleChanger uses a timeout to ensure we receive a timely reply.
119 *
120 * Need to synchronize on this instance if a request is sent, received,
121 * checked.
122 */
123 protected LinkedList<PendingRoleRequestEntry> pendingRoleRequests;
124
125 /* Switch features from initial featuresReply */
126 protected int capabilities;
127 protected int buffers;
128 protected int actions;
129 protected byte tables;
130 protected long datapathId;
131
132 public static IOFSwitchFeatures switchFeatures;
133 protected static final ThreadLocal<Map<OFSwitchImpl,List<OFMessage>>> local_msg_buffer =
134 new ThreadLocal<Map<OFSwitchImpl,List<OFMessage>>>() {
135 @Override
136 protected Map<OFSwitchImpl,List<OFMessage>> initialValue() {
137 return new HashMap<OFSwitchImpl,List<OFMessage>>();
138 }
139 };
140
141 // for managing our map sizes
142 protected static final int MAX_MACS_PER_SWITCH = 1000;
143
144 protected static class PendingRoleRequestEntry {
145 protected int xid;
146 protected Role role;
147 // cookie is used to identify the role "generation". roleChanger uses
148 protected long cookie;
149 public PendingRoleRequestEntry(int xid, Role role, long cookie) {
150 this.xid = xid;
151 this.role = role;
152 this.cookie = cookie;
153 }
154 }
155
156 public OFSwitchImpl() {
157 this.stringId = null;
158 this.attributes = new ConcurrentHashMap<Object, Object>();
159 this.connectedSince = new Date();
160 this.transactionIdSource = new AtomicInteger();
161 this.portLock = new Object();
162 this.portsByNumber = new ConcurrentHashMap<Short, OFPhysicalPort>();
163 this.portsByName = new ConcurrentHashMap<String, OFPhysicalPort>();
164 this.connected = true;
165 this.statsFutureMap = new ConcurrentHashMap<Integer,OFStatisticsFuture>();
166 this.featuresFutureMap = new ConcurrentHashMap<Integer,OFFeaturesReplyFuture>();
167 this.iofMsgListenersMap = new ConcurrentHashMap<Integer,IOFMessageListener>();
168 this.role = null;
169 this.timedCache = new TimedCache<Long>(100, 5*1000 ); // 5 seconds interval
170 this.listenerLock = new ReentrantReadWriteLock();
171 this.portBroadcastCacheHitMap = new ConcurrentHashMap<Short, Long>();
172 this.pendingRoleRequests = new LinkedList<OFSwitchImpl.PendingRoleRequestEntry>();
173
174 // Defaults properties for an ideal switch
175 this.setAttribute(PROP_FASTWILDCARDS, OFMatch.OFPFW_ALL);
176 this.setAttribute(PROP_SUPPORTS_OFPP_FLOOD, new Boolean(true));
177 this.setAttribute(PROP_SUPPORTS_OFPP_TABLE, new Boolean(true));
178 }
179
180
181 @Override
182 public Object getAttribute(String name) {
183 if (this.attributes.containsKey(name)) {
184 return this.attributes.get(name);
185 }
186 return null;
187 }
188
189 @Override
190 public void setAttribute(String name, Object value) {
191 this.attributes.put(name, value);
192 return;
193 }
194
195 @Override
196 public Object removeAttribute(String name) {
197 return this.attributes.remove(name);
198 }
199
200 @Override
201 public boolean hasAttribute(String name) {
202 return this.attributes.containsKey(name);
203 }
204
205 @Override
206 @JsonIgnore
207 public Channel getChannel() {
208 return this.channel;
209 }
210
211 @JsonIgnore
212 public void setChannel(Channel channel) {
213 this.channel = channel;
214 }
215
216 @Override
217 public void write(OFMessage m, FloodlightContext bc) throws IOException {
218 Map<OFSwitchImpl,List<OFMessage>> msg_buffer_map = local_msg_buffer.get();
219 List<OFMessage> msg_buffer = msg_buffer_map.get(this);
220 if (msg_buffer == null) {
221 msg_buffer = new ArrayList<OFMessage>();
222 msg_buffer_map.put(this, msg_buffer);
223 }
224
225 this.floodlightProvider.handleOutgoingMessage(this, m, bc);
226 msg_buffer.add(m);
227
228 if ((msg_buffer.size() >= Controller.BATCH_MAX_SIZE) ||
229 ((m.getType() != OFType.PACKET_OUT) && (m.getType() != OFType.FLOW_MOD))) {
230 this.write(msg_buffer);
231 msg_buffer.clear();
232 }
233 }
234
235 @Override
236 @LogMessageDoc(level="WARN",
237 message="Sending OF message that modifies switch " +
238 "state while in the slave role: {switch}",
239 explanation="An application has sent a message to a switch " +
240 "that is not valid when the switch is in a slave role",
241 recommendation=LogMessageDoc.REPORT_CONTROLLER_BUG)
242 public void write(List<OFMessage> msglist,
243 FloodlightContext bc) throws IOException {
244 for (OFMessage m : msglist) {
245 if (role == Role.SLAVE) {
246 switch (m.getType()) {
247 case PACKET_OUT:
248 case FLOW_MOD:
249 case PORT_MOD:
250 log.warn("Sending OF message that modifies switch " +
251 "state while in the slave role: {}",
252 m.getType().name());
253 break;
254 default:
255 break;
256 }
257 }
258 this.floodlightProvider.handleOutgoingMessage(this, m, bc);
259 }
260 this.write(msglist);
261 }
262
263 public void write(List<OFMessage> msglist) throws IOException {
264 this.channel.write(msglist);
265 }
266
267 @Override
268 public void disconnectOutputStream() {
269 channel.close();
270 }
Umesh Krishnaswamyf962d642013-01-23 19:04:23 -0800271
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800272 @Override
273 @JsonIgnore
274 public void setFeaturesReply(OFFeaturesReply featuresReply) {
275 synchronized(portLock) {
276 if (stringId == null) {
277 /* ports are updated via port status message, so we
278 * only fill in ports on initial connection.
279 */
280 for (OFPhysicalPort port : featuresReply.getPorts()) {
281 setPort(port);
282 }
283 }
284 this.datapathId = featuresReply.getDatapathId();
285 this.capabilities = featuresReply.getCapabilities();
286 this.buffers = featuresReply.getBuffers();
287 this.actions = featuresReply.getActions();
288 this.tables = featuresReply.getTables();
289 this.stringId = HexString.toHexString(this.datapathId);
290 }
291 }
292
293 @Override
294 @JsonIgnore
295 public Collection<OFPhysicalPort> getEnabledPorts() {
296 List<OFPhysicalPort> result = new ArrayList<OFPhysicalPort>();
297 for (OFPhysicalPort port : portsByNumber.values()) {
298 if (portEnabled(port)) {
299 result.add(port);
300 }
301 }
302 return result;
303 }
304
305 @Override
306 @JsonIgnore
307 public Collection<Short> getEnabledPortNumbers() {
308 List<Short> result = new ArrayList<Short>();
309 for (OFPhysicalPort port : portsByNumber.values()) {
310 if (portEnabled(port)) {
311 result.add(port.getPortNumber());
312 }
313 }
314 return result;
315 }
316
317 @Override
318 public OFPhysicalPort getPort(short portNumber) {
319 return portsByNumber.get(portNumber);
320 }
321
322 @Override
323 public OFPhysicalPort getPort(String portName) {
324 return portsByName.get(portName);
325 }
326
327 @Override
328 @JsonIgnore
329 public void setPort(OFPhysicalPort port) {
330 synchronized(portLock) {
331 portsByNumber.put(port.getPortNumber(), port);
332 portsByName.put(port.getName(), port);
333 }
334 }
335
336 @Override
337 @JsonProperty("ports")
338 public Collection<OFPhysicalPort> getPorts() {
339 return Collections.unmodifiableCollection(portsByNumber.values());
340 }
341
342 @Override
343 public void deletePort(short portNumber) {
344 synchronized(portLock) {
345 portsByName.remove(portsByNumber.get(portNumber).getName());
346 portsByNumber.remove(portNumber);
347 }
348 }
349
350 @Override
351 public void deletePort(String portName) {
352 synchronized(portLock) {
353 portsByNumber.remove(portsByName.get(portName).getPortNumber());
354 portsByName.remove(portName);
355 }
356 }
357
358 @Override
359 public boolean portEnabled(short portNumber) {
360 if (portsByNumber.get(portNumber) == null) return false;
361 return portEnabled(portsByNumber.get(portNumber));
362 }
363
364 @Override
365 public boolean portEnabled(String portName) {
366 if (portsByName.get(portName) == null) return false;
367 return portEnabled(portsByName.get(portName));
368 }
369
370 @Override
371 public boolean portEnabled(OFPhysicalPort port) {
372 if (port == null)
373 return false;
374 if ((port.getConfig() & OFPortConfig.OFPPC_PORT_DOWN.getValue()) > 0)
375 return false;
376 if ((port.getState() & OFPortState.OFPPS_LINK_DOWN.getValue()) > 0)
377 return false;
378 // Port STP state doesn't work with multiple VLANs, so ignore it for now
379 //if ((port.getState() & OFPortState.OFPPS_STP_MASK.getValue()) == OFPortState.OFPPS_STP_BLOCK.getValue())
380 // return false;
381 return true;
382 }
383
384 @Override
385 @JsonSerialize(using=DPIDSerializer.class)
386 @JsonProperty("dpid")
387 public long getId() {
388 if (this.stringId == null)
389 throw new RuntimeException("Features reply has not yet been set");
390 return this.datapathId;
391 }
392
393 @JsonIgnore
394 @Override
395 public String getStringId() {
396 return stringId;
397 }
398
399 /* (non-Javadoc)
400 * @see java.lang.Object#toString()
401 */
402 @Override
403 public String toString() {
404 return "OFSwitchImpl [" + channel.getRemoteAddress() + " DPID[" + ((stringId != null) ? stringId : "?") + "]]";
405 }
406
407 @Override
408 public ConcurrentMap<Object, Object> getAttributes() {
409 return this.attributes;
410 }
411
412 @Override
413 public Date getConnectedSince() {
414 return connectedSince;
415 }
416
417 @JsonIgnore
418 @Override
419 public int getNextTransactionId() {
420 return this.transactionIdSource.incrementAndGet();
421 }
422
423 @Override
424 public void sendStatsQuery(OFStatisticsRequest request, int xid,
425 IOFMessageListener caller) throws IOException {
426 request.setXid(xid);
427 this.iofMsgListenersMap.put(xid, caller);
428 List<OFMessage> msglist = new ArrayList<OFMessage>(1);
429 msglist.add(request);
430 this.channel.write(msglist);
431 return;
432 }
433
434 @Override
435 public Future<List<OFStatistics>> getStatistics(OFStatisticsRequest request) throws IOException {
436 request.setXid(getNextTransactionId());
437 OFStatisticsFuture future = new OFStatisticsFuture(threadPool, this, request.getXid());
438 this.statsFutureMap.put(request.getXid(), future);
439 List<OFMessage> msglist = new ArrayList<OFMessage>(1);
440 msglist.add(request);
441 this.channel.write(msglist);
442 return future;
443 }
444
445 @Override
446 public void deliverStatisticsReply(OFMessage reply) {
447 OFStatisticsFuture future = this.statsFutureMap.get(reply.getXid());
448 if (future != null) {
449 future.deliverFuture(this, reply);
450 // The future will ultimately unregister itself and call
451 // cancelStatisticsReply
452 return;
453 }
454 /* Transaction id was not found in statsFutureMap.check the other map */
455 IOFMessageListener caller = this.iofMsgListenersMap.get(reply.getXid());
456 if (caller != null) {
457 caller.receive(this, reply, null);
458 }
459 }
460
461 @Override
462 public void cancelStatisticsReply(int transactionId) {
463 if (null == this.statsFutureMap.remove(transactionId)) {
464 this.iofMsgListenersMap.remove(transactionId);
465 }
466 }
467
468 @Override
469 public void cancelAllStatisticsReplies() {
470 /* we don't need to be synchronized here. Even if another thread
471 * modifies the map while we're cleaning up the future will eventuall
472 * timeout */
473 for (OFStatisticsFuture f : statsFutureMap.values()) {
474 f.cancel(true);
475 }
476 statsFutureMap.clear();
477 iofMsgListenersMap.clear();
478 }
479
480
481 /**
482 * @param floodlightProvider the floodlightProvider to set
483 */
484 @JsonIgnore
485 public void setFloodlightProvider(IFloodlightProviderService floodlightProvider) {
486 this.floodlightProvider = floodlightProvider;
487 }
488
489 @JsonIgnore
490 public void setThreadPoolService(IThreadPoolService tp) {
491 this.threadPool = tp;
492 }
493
494 @JsonIgnore
495 @Override
496 public synchronized boolean isConnected() {
497 return connected;
498 }
499
500 @Override
501 @JsonIgnore
502 public synchronized void setConnected(boolean connected) {
503 this.connected = connected;
504 }
505
506 @Override
507 public Role getRole() {
508 return role;
509 }
510
511 @JsonIgnore
512 @Override
513 public boolean isActive() {
514 return (role != Role.SLAVE);
515 }
516
517 @Override
518 @JsonIgnore
519 public void setSwitchProperties(OFDescriptionStatistics description) {
520 if (switchFeatures != null) {
521 switchFeatures.setFromDescription(this, description);
522 }
523 }
524
525 @Override
526 @LogMessageDoc(level="ERROR",
527 message="Failed to clear all flows on switch {switch}",
528 explanation="An I/O error occured while trying to clear " +
529 "flows on the switch.",
530 recommendation=LogMessageDoc.CHECK_SWITCH)
531 public void clearAllFlowMods() {
532 // Delete all pre-existing flows
533 OFMatch match = new OFMatch().setWildcards(OFMatch.OFPFW_ALL);
534 OFMessage fm = ((OFFlowMod) floodlightProvider.getOFMessageFactory()
535 .getMessage(OFType.FLOW_MOD))
536 .setMatch(match)
537 .setCommand(OFFlowMod.OFPFC_DELETE)
538 .setOutPort(OFPort.OFPP_NONE)
539 .setLength(U16.t(OFFlowMod.MINIMUM_LENGTH));
540 try {
541 List<OFMessage> msglist = new ArrayList<OFMessage>(1);
542 msglist.add(fm);
543 channel.write(msglist);
544 } catch (Exception e) {
545 log.error("Failed to clear all flows on switch " + this, e);
546 }
547 }
548
549 @Override
550 public boolean updateBroadcastCache(Long entry, Short port) {
551 if (timedCache.update(entry)) {
552 Long count = portBroadcastCacheHitMap.putIfAbsent(port, new Long(1));
553 if (count != null) {
554 count++;
555 }
556 return true;
557 } else {
558 return false;
559 }
560 }
561
562 @Override
563 @JsonIgnore
564 public Map<Short, Long> getPortBroadcastHits() {
565 return this.portBroadcastCacheHitMap;
566 }
567
568
569 @Override
570 public void flush() {
571 Map<OFSwitchImpl,List<OFMessage>> msg_buffer_map = local_msg_buffer.get();
572 List<OFMessage> msglist = msg_buffer_map.get(this);
573 if ((msglist != null) && (msglist.size() > 0)) {
574 try {
575 this.write(msglist);
576 } catch (IOException e) {
577 // TODO: log exception
578 e.printStackTrace();
579 }
580 msglist.clear();
581 }
582 }
583
584 public static void flush_all() {
585 Map<OFSwitchImpl,List<OFMessage>> msg_buffer_map = local_msg_buffer.get();
586 for (OFSwitchImpl sw : msg_buffer_map.keySet()) {
587 sw.flush();
588 }
589 }
590
591 /**
592 * Return a read lock that must be held while calling the listeners for
593 * messages from the switch. Holding the read lock prevents the active
594 * switch list from being modified out from under the listeners.
595 * @return
596 */
597 @JsonIgnore
598 public Lock getListenerReadLock() {
599 return listenerLock.readLock();
600 }
601
602 /**
603 * Return a write lock that must be held when the controllers modifies the
604 * list of active switches. This is to ensure that the active switch list
605 * doesn't change out from under the listeners as they are handling a
606 * message from the switch.
607 * @return
608 */
609 @JsonIgnore
610 public Lock getListenerWriteLock() {
611 return listenerLock.writeLock();
612 }
613
614 /**
615 * Get the IP Address for the switch
616 * @return the inet address
617 */
618 @JsonSerialize(using=ToStringSerializer.class)
619 public SocketAddress getInetAddress() {
620 return channel.getRemoteAddress();
621 }
622
623 /**
624 * Send NX role request message to the switch requesting the specified role.
625 *
626 * This method should ONLY be called by @see RoleChanger.submitRequest().
627 *
628 * After sending the request add it to the queue of pending request. We
629 * use the queue to later verify that we indeed receive the correct reply.
630 * @param sw switch to send the role request message to
631 * @param role role to request
632 * @param cookie an opaque value that will be stored in the pending queue so
633 * RoleChanger can check for timeouts.
634 * @return transaction id of the role request message that was sent
635 */
636 protected int sendNxRoleRequest(Role role, long cookie)
637 throws IOException {
638 synchronized(pendingRoleRequests) {
639 // Convert the role enum to the appropriate integer constant used
640 // in the NX role request message
641 int nxRole = 0;
642 switch (role) {
643 case EQUAL:
644 nxRole = OFRoleVendorData.NX_ROLE_OTHER;
645 break;
646 case MASTER:
647 nxRole = OFRoleVendorData.NX_ROLE_MASTER;
648 break;
649 case SLAVE:
650 nxRole = OFRoleVendorData.NX_ROLE_SLAVE;
651 break;
652 default:
653 log.error("Invalid Role specified for switch {}."
654 + " Disconnecting.", this);
655 // TODO: should throw an error
656 return 0;
657 }
658
659 // Construct the role request message
660 OFVendor roleRequest = (OFVendor)floodlightProvider.
661 getOFMessageFactory().getMessage(OFType.VENDOR);
662 int xid = this.getNextTransactionId();
663 roleRequest.setXid(xid);
664 roleRequest.setVendor(OFNiciraVendorData.NX_VENDOR_ID);
665 OFRoleRequestVendorData roleRequestData = new OFRoleRequestVendorData();
666 roleRequestData.setRole(nxRole);
667 roleRequest.setVendorData(roleRequestData);
668 roleRequest.setLengthU(OFVendor.MINIMUM_LENGTH +
669 roleRequestData.getLength());
670
671 // Send it to the switch
672 List<OFMessage> msglist = new ArrayList<OFMessage>(1);
673 msglist.add(roleRequest);
674 // FIXME: should this use this.write() in order for messages to
675 // be processed by handleOutgoingMessage()
676 this.channel.write(msglist);
677
678 pendingRoleRequests.add(new PendingRoleRequestEntry(xid, role, cookie));
679 return xid;
680 }
681 }
682
683 /**
684 * Deliver a RoleReply message to this switch. Checks if the reply
685 * message matches the expected reply (head of the pending request queue).
686 * We require in-order delivery of replies. If there's any deviation from
687 * our expectations we disconnect the switch.
688 *
689 * We must not check the received role against the controller's current
690 * role because there's no synchronization but that's fine @see RoleChanger
691 *
692 * Will be called by the OFChannelHandler's receive loop
693 *
694 * @param xid Xid of the reply message
695 * @param role The Role in the the reply message
696 */
697 @LogMessageDocs({
698 @LogMessageDoc(level="ERROR",
699 message="Switch {switch}: received unexpected role reply for " +
700 "Role {role}" +
701 " Disconnecting switch",
702 explanation="The switch sent an unexpected HA role reply",
703 recommendation=HA_CHECK_SWITCH),
704 @LogMessageDoc(level="ERROR",
705 message="Switch {switch}: expected role reply with " +
706 "Xid {xid}, got {xid}. Disconnecting switch",
707 explanation="The switch sent an unexpected HA role reply",
708 recommendation=HA_CHECK_SWITCH),
709 @LogMessageDoc(level="ERROR",
710 message="Switch {switch}: expected role reply with " +
711 "Role {role}, got {role}. Disconnecting switch",
712 explanation="The switch sent an unexpected HA role reply",
713 recommendation=HA_CHECK_SWITCH)
714 })
715 protected void deliverRoleReply(int xid, Role role) {
716 synchronized(pendingRoleRequests) {
717 PendingRoleRequestEntry head = pendingRoleRequests.poll();
718 if (head == null) {
719 // Maybe don't disconnect if the role reply we received is
720 // for the same role we are already in.
721 log.error("Switch {}: received unexpected role reply for Role {}" +
722 " Disconnecting switch", this, role );
723 this.channel.close();
724 }
725 else if (head.xid != xid) {
726 // check xid before role!!
727 log.error("Switch {}: expected role reply with " +
728 "Xid {}, got {}. Disconnecting switch",
729 new Object[] { this, head.xid, xid } );
730 this.channel.close();
731 }
732 else if (head.role != role) {
733 log.error("Switch {}: expected role reply with " +
734 "Role {}, got {}. Disconnecting switch",
735 new Object[] { this, head.role, role } );
736 this.channel.close();
737 }
738 else {
739 log.debug("Received role reply message from {}, setting role to {}",
740 this, role);
741 if (this.role == null && getAttribute(SWITCH_SUPPORTS_NX_ROLE) == null) {
742 // The first role reply we received. Set the attribute
743 // that the switch supports roles
744 setAttribute(SWITCH_SUPPORTS_NX_ROLE, true);
745 }
746 this.role = role;
747 }
748 }
749 }
750
751 /**
752 * Checks whether the given xid matches the xid of the first pending
753 * role request.
754 * @param xid
755 * @return
756 */
757 protected boolean checkFirstPendingRoleRequestXid (int xid) {
758 synchronized(pendingRoleRequests) {
759 PendingRoleRequestEntry head = pendingRoleRequests.peek();
760 if (head == null)
761 return false;
762 else
763 return head.xid == xid;
764 }
765 }
766
767 /**
768 * Checks whether the given request cookie matches the cookie of the first
769 * pending request
770 * @param cookie
771 * @return
772 */
773 protected boolean checkFirstPendingRoleRequestCookie(long cookie) {
774 synchronized(pendingRoleRequests) {
775 PendingRoleRequestEntry head = pendingRoleRequests.peek();
776 if (head == null)
777 return false;
778 else
779 return head.cookie == cookie;
780 }
781 }
782
783 /**
784 * Called if we receive a vendor error message indicating that roles
785 * are not supported by the switch. If the xid matches the first pending
786 * one, we'll mark the switch as not supporting roles and remove the head.
787 * Otherwise we ignore it.
788 * @param xid
789 */
790 protected void deliverRoleRequestNotSupported(int xid) {
791 synchronized(pendingRoleRequests) {
792 PendingRoleRequestEntry head = pendingRoleRequests.poll();
793 this.role = null;
794 if (head!=null && head.xid == xid) {
795 setAttribute(SWITCH_SUPPORTS_NX_ROLE, false);
796 }
797 else {
798 this.channel.close();
799 }
800 }
801 }
802
803 @Override
804 public Future<OFFeaturesReply> getFeaturesReplyFromSwitch()
805 throws IOException {
806 OFMessage request = new OFFeaturesRequest();
807 request.setXid(getNextTransactionId());
808 OFFeaturesReplyFuture future =
809 new OFFeaturesReplyFuture(threadPool, this, request.getXid());
810 this.featuresFutureMap.put(request.getXid(), future);
811 List<OFMessage> msglist = new ArrayList<OFMessage>(1);
812 msglist.add(request);
813 this.channel.write(msglist);
814 return future;
815 }
816
817 @Override
818 public void deliverOFFeaturesReply(OFMessage reply) {
819 OFFeaturesReplyFuture future = this.featuresFutureMap.get(reply.getXid());
820 if (future != null) {
821 future.deliverFuture(this, reply);
822 // The future will ultimately unregister itself and call
823 // cancelFeaturesReply
824 return;
825 }
826 log.error("Switch {}: received unexpected featureReply", this);
827 }
828
829 @Override
830 public void cancelFeaturesReply(int transactionId) {
831 this.featuresFutureMap.remove(transactionId);
832 }
833
834
835 @Override
836 public int getBuffers() {
837 return buffers;
838 }
839
840
841 @Override
842 public int getActions() {
843 return actions;
844 }
845
846
847 @Override
848 public int getCapabilities() {
849 return capabilities;
850 }
851
852
853 @Override
854 public byte getTables() {
855 return tables;
856 }
Umesh Krishnaswamyf962d642013-01-23 19:04:23 -0800857
858
859 @Override
860 public void setupRemoteSwitch(Long dpid) {
861 this.datapathId = dpid;
862 this.stringId = HexString.toHexString(this.datapathId);
863 }
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800864}