2c19837e941a1e247bcd73e886eafaf51a40f8e1
[onosfw.git] /
1 /*
2  * Copyright 2014-2015 Open Networking Laboratory
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *     http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16
17 package org.onosproject.openflow.controller.driver;
18
19 import org.jboss.netty.channel.Channel;
20 import org.onlab.packet.IpAddress;
21 import org.onosproject.net.Device;
22 import org.onosproject.net.driver.AbstractHandlerBehaviour;
23 import org.onosproject.openflow.controller.Dpid;
24 import org.onosproject.openflow.controller.RoleState;
25 import org.projectfloodlight.openflow.protocol.OFDescStatsReply;
26 import org.projectfloodlight.openflow.protocol.OFErrorMsg;
27 import org.projectfloodlight.openflow.protocol.OFExperimenter;
28 import org.projectfloodlight.openflow.protocol.OFFactories;
29 import org.projectfloodlight.openflow.protocol.OFFactory;
30 import org.projectfloodlight.openflow.protocol.OFFeaturesReply;
31 import org.projectfloodlight.openflow.protocol.OFMessage;
32 import org.projectfloodlight.openflow.protocol.OFNiciraControllerRoleRequest;
33 import org.projectfloodlight.openflow.protocol.OFPortDesc;
34 import org.projectfloodlight.openflow.protocol.OFPortDescStatsReply;
35 import org.projectfloodlight.openflow.protocol.OFPortStatus;
36 import org.projectfloodlight.openflow.protocol.OFRoleReply;
37 import org.projectfloodlight.openflow.protocol.OFRoleRequest;
38 import org.projectfloodlight.openflow.protocol.OFVersion;
39 import org.slf4j.Logger;
40 import org.slf4j.LoggerFactory;
41
42 import java.io.IOException;
43 import java.net.InetSocketAddress;
44 import java.net.SocketAddress;
45 import java.util.ArrayList;
46 import java.util.Collections;
47 import java.util.List;
48 import java.util.concurrent.atomic.AtomicInteger;
49 import java.util.stream.Collectors;
50
51 /**
52  * An abstract representation of an OpenFlow switch. Can be extended by others
53  * to serve as a base for their vendor specific representation of a switch.
54  */
55 public abstract class AbstractOpenFlowSwitch extends AbstractHandlerBehaviour
56         implements OpenFlowSwitchDriver {
57
58     protected final Logger log = LoggerFactory.getLogger(getClass());
59
60     private Channel channel;
61     protected String channelId;
62
63     private boolean connected;
64     protected boolean startDriverHandshakeCalled = false;
65     private Dpid dpid;
66     private OpenFlowAgent agent;
67     private final AtomicInteger xidCounter = new AtomicInteger(0);
68
69     private OFVersion ofVersion;
70
71     protected List<OFPortDescStatsReply> ports = new ArrayList<>();
72
73     protected boolean tableFull;
74
75     private RoleHandler roleMan;
76
77     protected RoleState role;
78
79     protected OFFeaturesReply features;
80     protected OFDescStatsReply desc;
81
82     List<OFMessage> messagesPendingMastership;
83
84     @Override
85     public void init(Dpid dpid, OFDescStatsReply desc, OFVersion ofv) {
86         this.dpid = dpid;
87         this.desc = desc;
88         this.ofVersion = ofv;
89     }
90
91     //************************
92     // Channel related
93     //************************
94
95     @Override
96     public final void disconnectSwitch() {
97         this.channel.close();
98     }
99
100     @Override
101     public void sendMsg(OFMessage msg) {
102         this.sendMsg(Collections.singletonList(msg));
103     }
104
105     @Override
106     public final void sendMsg(List<OFMessage> msgs) {
107         if (role == RoleState.MASTER && channel.isConnected()) {
108             channel.write(msgs);
109         } else if (messagesPendingMastership != null) {
110             messagesPendingMastership.addAll(msgs);
111             log.debug("Enqueue message for switch {}. queue size after is {}",
112                       dpid, messagesPendingMastership.size());
113         } else {
114             log.warn("Dropping message for switch {} (role: {}, connected: {}): {}",
115                      dpid, role, channel.isConnected(), msgs);
116         }
117     }
118
119     @Override
120     public final void sendRoleRequest(OFMessage msg) {
121         if (msg instanceof OFRoleRequest ||
122                 msg instanceof OFNiciraControllerRoleRequest) {
123             channel.write(Collections.singletonList(msg));
124             return;
125         }
126         throw new IllegalArgumentException("Someone is trying to send " +
127                                                    "a non role request message");
128     }
129
130     @Override
131     public final void sendHandshakeMessage(OFMessage message) {
132         if (!this.isDriverHandshakeComplete()) {
133             channel.write(Collections.singletonList(message));
134         }
135     }
136
137     @Override
138     public final boolean isConnected() {
139         return this.connected;
140     }
141
142     @Override
143     public final void setConnected(boolean connected) {
144         this.connected = connected;
145     }
146
147     @Override
148     public final void setChannel(Channel channel) {
149         this.channel = channel;
150         final SocketAddress address = channel.getRemoteAddress();
151         if (address instanceof InetSocketAddress) {
152             final InetSocketAddress inetAddress = (InetSocketAddress) address;
153             final IpAddress ipAddress = IpAddress.valueOf(inetAddress.getAddress());
154             if (ipAddress.isIp4()) {
155                 channelId = ipAddress.toString() + ':' + inetAddress.getPort();
156             } else {
157                 channelId = '[' + ipAddress.toString() + "]:" + inetAddress.getPort();
158             }
159         }
160     }
161
162     @Override
163     public String channelId() {
164         return channelId;
165     }
166
167     //************************
168     // Switch features related
169     //************************
170
171     @Override
172     public final long getId() {
173         return this.dpid.value();
174     }
175
176     @Override
177     public final String getStringId() {
178         return this.dpid.toString();
179     }
180
181     @Override
182     public final void setOFVersion(OFVersion ofV) {
183         this.ofVersion = ofV;
184     }
185
186     @Override
187     public void setTableFull(boolean full) {
188         this.tableFull = full;
189     }
190
191     @Override
192     public void setFeaturesReply(OFFeaturesReply featuresReply) {
193         this.features = featuresReply;
194     }
195
196     @Override
197     public abstract Boolean supportNxRole();
198
199     //************************
200     //  Message handling
201     //************************
202     /**
203      * Handle the message coming from the dataplane.
204      *
205      * @param m the actual message
206      */
207     @Override
208     public final void handleMessage(OFMessage m) {
209         if (this.role == RoleState.MASTER || m instanceof OFPortStatus) {
210             this.agent.processMessage(dpid, m);
211         }
212     }
213
214     @Override
215     public RoleState getRole() {
216         return role;
217     }
218
219     @Override
220     public final boolean connectSwitch() {
221         return this.agent.addConnectedSwitch(dpid, this);
222     }
223
224     @Override
225     public final boolean activateMasterSwitch() {
226         return this.agent.addActivatedMasterSwitch(dpid, this);
227     }
228
229     @Override
230     public final boolean activateEqualSwitch() {
231         return this.agent.addActivatedEqualSwitch(dpid, this);
232     }
233
234     @Override
235     public final void transitionToEqualSwitch() {
236         this.agent.transitionToEqualSwitch(dpid);
237     }
238
239     @Override
240     public final void transitionToMasterSwitch() {
241         this.agent.transitionToMasterSwitch(dpid);
242         if (messagesPendingMastership != null) {
243             this.sendMsg(messagesPendingMastership);
244             log.debug("Sending {} pending messages to switch {}",
245                      messagesPendingMastership.size(), dpid);
246             messagesPendingMastership = null;
247         }
248     }
249
250     @Override
251     public final void removeConnectedSwitch() {
252         this.agent.removeConnectedSwitch(dpid);
253     }
254
255     @Override
256     public OFFactory factory() {
257         return OFFactories.getFactory(ofVersion);
258     }
259
260     @Override
261     public void setPortDescReply(OFPortDescStatsReply portDescReply) {
262         this.ports.add(portDescReply);
263     }
264
265     @Override
266     public void setPortDescReplies(List<OFPortDescStatsReply> portDescReplies) {
267         this.ports.addAll(portDescReplies);
268     }
269
270     @Override
271     public void returnRoleReply(RoleState requested, RoleState response) {
272         this.agent.returnRoleReply(dpid, requested, response);
273     }
274
275     @Override
276     public abstract void startDriverHandshake();
277
278     @Override
279     public abstract boolean isDriverHandshakeComplete();
280
281     @Override
282     public abstract void processDriverHandshakeMessage(OFMessage m);
283
284
285     // Role Handling
286
287     @Override
288     public void setRole(RoleState role) {
289         try {
290             if (this.roleMan.sendRoleRequest(role, RoleRecvStatus.MATCHED_SET_ROLE)) {
291                 log.debug("Sending role {} to switch {}", role, getStringId());
292                 if (role == RoleState.SLAVE || role == RoleState.EQUAL) {
293                     this.role = role;
294                 } else {
295                     if (messagesPendingMastership == null) {
296                         log.debug("Initializing new queue for switch {}", dpid);
297                         messagesPendingMastership = new ArrayList<>();
298                     }
299                 }
300             } else {
301                 this.role = role;
302             }
303         } catch (IOException e) {
304             log.error("Unable to write to switch {}.", this.dpid);
305         }
306     }
307
308     @Override
309     public void reassertRole() {
310         if (this.getRole() == RoleState.MASTER) {
311             log.warn("Received permission error from switch {} while " +
312                     "being master. Reasserting master role.",
313                     this.getStringId());
314             this.setRole(RoleState.MASTER);
315         }
316     }
317
318
319
320     @Override
321     public void handleRole(OFMessage m) throws SwitchStateException {
322         RoleReplyInfo rri = roleMan.extractOFRoleReply((OFRoleReply) m);
323         RoleRecvStatus rrs = roleMan.deliverRoleReply(rri);
324         if (rrs == RoleRecvStatus.MATCHED_SET_ROLE) {
325             if (rri.getRole() == RoleState.MASTER) {
326                 this.role = rri.getRole();
327                 this.transitionToMasterSwitch();
328             } else if (rri.getRole() == RoleState.EQUAL ||
329                     rri.getRole() == RoleState.SLAVE) {
330                 this.transitionToEqualSwitch();
331             }
332         }  else {
333             log.warn("Failed to set role for {}", this.getStringId());
334         }
335     }
336
337     @Override
338     public void handleNiciraRole(OFMessage m) throws SwitchStateException {
339         RoleState r = this.roleMan.extractNiciraRoleReply((OFExperimenter) m);
340         if (r == null) {
341             // The message wasn't really a Nicira role reply. We just
342             // dispatch it to the OFMessage listeners in this case.
343             this.handleMessage(m);
344             return;
345         }
346
347         RoleRecvStatus rrs = this.roleMan.deliverRoleReply(
348                 new RoleReplyInfo(r, null, m.getXid()));
349         if (rrs == RoleRecvStatus.MATCHED_SET_ROLE) {
350             if (r == RoleState.MASTER) {
351                 this.role = r;
352                 this.transitionToMasterSwitch();
353             } else if (r == RoleState.EQUAL ||
354                     r == RoleState.SLAVE) {
355                 this.transitionToEqualSwitch();
356             }
357         } else {
358             this.disconnectSwitch();
359         }
360     }
361
362     @Override
363     public boolean handleRoleError(OFErrorMsg error) {
364         try {
365             return RoleRecvStatus.OTHER_EXPECTATION != this.roleMan.deliverError(error);
366         } catch (SwitchStateException e) {
367             this.disconnectSwitch();
368         }
369         return true;
370     }
371
372
373
374     @Override
375     public final void setAgent(OpenFlowAgent ag) {
376         if (this.agent == null) {
377             this.agent = ag;
378         }
379     }
380
381     @Override
382     public final void setRoleHandler(RoleHandler roleHandler) {
383         if (this.roleMan == null) {
384             this.roleMan = roleHandler;
385         }
386     }
387
388     @Override
389     public void setSwitchDescription(OFDescStatsReply d) {
390         this.desc = d;
391     }
392
393     @Override
394     public int getNextTransactionId() {
395         return this.xidCounter.getAndIncrement();
396     }
397
398     @Override
399     public List<OFPortDesc> getPorts() {
400         return this.ports.stream()
401                   .flatMap((portReply) -> (portReply.getEntries().stream()))
402                   .collect(Collectors.toList());
403         //return Collections.unmodifiableList(ports.getEntries());
404     }
405
406     @Override
407     public String manufacturerDescription() {
408         return this.desc.getMfrDesc();
409     }
410
411
412     @Override
413     public String datapathDescription() {
414         return this.desc.getDpDesc();
415     }
416
417
418     @Override
419     public String hardwareDescription() {
420         return this.desc.getHwDesc();
421     }
422
423     @Override
424     public String softwareDescription() {
425         return this.desc.getSwDesc();
426     }
427
428     @Override
429     public String serialNumber() {
430         return this.desc.getSerialNum();
431     }
432
433
434     @Override
435     public Device.Type deviceType() {
436         return Device.Type.SWITCH;
437     }
438
439
440     @Override
441     public String toString() {
442         return this.getClass().getName() + " [" + ((channel != null)
443                 ? channel.getRemoteAddress() : "?")
444                 + " DPID[" + ((getStringId() != null) ? getStringId() : "?") + "]]";
445     }
446
447
448
449 }