blob: 8aa3d77d69d6d3543f29ae518a2ff1eaf1b413c6 [file] [log] [blame]
Thomas Vachuska781d18b2014-10-27 10:31:25 -07001/*
Thomas Vachuska4f1a60c2014-10-28 13:39:07 -07002 * Copyright 2014 Open Networking Laboratory
Thomas Vachuska781d18b2014-10-27 10:31:25 -07003 *
Thomas Vachuska4f1a60c2014-10-28 13:39:07 -07004 * 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
Thomas Vachuska781d18b2014-10-27 10:31:25 -07007 *
Thomas Vachuska4f1a60c2014-10-28 13:39:07 -07008 * 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.
Thomas Vachuska781d18b2014-10-27 10:31:25 -070015 */
tomb5a46e62014-08-26 14:20:00 -070016package org.onlab.onos.provider.of.device.impl;
17
18import org.apache.felix.scr.annotations.Activate;
19import org.apache.felix.scr.annotations.Component;
20import org.apache.felix.scr.annotations.Deactivate;
21import org.apache.felix.scr.annotations.Reference;
22import org.apache.felix.scr.annotations.ReferenceCardinality;
tomab21e7c2014-08-26 15:23:08 -070023import org.onlab.onos.net.Device;
alshabibc944fd02014-09-10 17:55:17 -070024import org.onlab.onos.net.DeviceId;
tomab21e7c2014-08-26 15:23:08 -070025import org.onlab.onos.net.MastershipRole;
Thomas Vachuskad16ce182014-10-29 17:25:29 -070026import org.onlab.onos.net.Port;
alshabib25c8eec2014-09-04 16:41:31 -070027import org.onlab.onos.net.PortNumber;
tomd1900f32014-09-03 14:08:16 -070028import org.onlab.onos.net.device.DefaultDeviceDescription;
alshabib25c8eec2014-09-04 16:41:31 -070029import org.onlab.onos.net.device.DefaultPortDescription;
alshabibf1216ed2014-09-03 11:53:54 -070030import org.onlab.onos.net.device.DeviceDescription;
tomab21e7c2014-08-26 15:23:08 -070031import org.onlab.onos.net.device.DeviceProvider;
tom96dfcab2014-08-28 09:26:03 -070032import org.onlab.onos.net.device.DeviceProviderRegistry;
tomab21e7c2014-08-26 15:23:08 -070033import org.onlab.onos.net.device.DeviceProviderService;
alshabib25c8eec2014-09-04 16:41:31 -070034import org.onlab.onos.net.device.PortDescription;
tomab21e7c2014-08-26 15:23:08 -070035import org.onlab.onos.net.provider.AbstractProvider;
36import org.onlab.onos.net.provider.ProviderId;
tom9c94c5b2014-09-17 13:14:42 -070037import org.onlab.onos.openflow.controller.Dpid;
38import org.onlab.onos.openflow.controller.OpenFlowController;
39import org.onlab.onos.openflow.controller.OpenFlowSwitch;
40import org.onlab.onos.openflow.controller.OpenFlowSwitchListener;
41import org.onlab.onos.openflow.controller.RoleState;
Ayaka Koshibee8708e32014-10-22 13:40:18 -070042import org.onlab.onos.openflow.controller.driver.OpenFlowSwitchDriver;
alshabib7911a052014-10-16 17:49:37 -070043import org.onlab.packet.ChassisId;
Ayaka Koshibee8708e32014-10-22 13:40:18 -070044import org.projectfloodlight.openflow.protocol.OFFactory;
alshabib4680bb62014-09-04 17:15:08 -070045import org.projectfloodlight.openflow.protocol.OFPortConfig;
alshabib25c8eec2014-09-04 16:41:31 -070046import org.projectfloodlight.openflow.protocol.OFPortDesc;
Thomas Vachuskad16ce182014-10-29 17:25:29 -070047import org.projectfloodlight.openflow.protocol.OFPortFeatures;
alshabib4680bb62014-09-04 17:15:08 -070048import org.projectfloodlight.openflow.protocol.OFPortState;
alshabiba14f3642014-09-05 09:31:31 -070049import org.projectfloodlight.openflow.protocol.OFPortStatus;
Thomas Vachuskad16ce182014-10-29 17:25:29 -070050import org.projectfloodlight.openflow.protocol.OFVersion;
51import org.projectfloodlight.openflow.types.PortSpeed;
tomb5a46e62014-08-26 14:20:00 -070052import org.slf4j.Logger;
tom5f38b3a2014-08-27 23:50:54 -070053
tom782a7cf2014-09-11 23:58:38 -070054import java.util.ArrayList;
55import java.util.List;
56
57import static org.onlab.onos.net.DeviceId.deviceId;
Thomas Vachuskad16ce182014-10-29 17:25:29 -070058import static org.onlab.onos.net.Port.Type.COPPER;
59import static org.onlab.onos.net.Port.Type.FIBER;
tom9c94c5b2014-09-17 13:14:42 -070060import static org.onlab.onos.openflow.controller.Dpid.dpid;
61import static org.onlab.onos.openflow.controller.Dpid.uri;
tom782a7cf2014-09-11 23:58:38 -070062import static org.slf4j.LoggerFactory.getLogger;
63
tomb5a46e62014-08-26 14:20:00 -070064/**
tomb1260e42014-08-26 18:39:57 -070065 * Provider which uses an OpenFlow controller to detect network
tome06f8552014-08-26 16:58:42 -070066 * infrastructure devices.
tomb5a46e62014-08-26 14:20:00 -070067 */
tomb1260e42014-08-26 18:39:57 -070068@Component(immediate = true)
tomab21e7c2014-08-26 15:23:08 -070069public class OpenFlowDeviceProvider extends AbstractProvider implements DeviceProvider {
tomb5a46e62014-08-26 14:20:00 -070070
alshabiba89cc582014-09-09 16:43:00 -070071 private static final Logger LOG = getLogger(OpenFlowDeviceProvider.class);
Thomas Vachuskad16ce182014-10-29 17:25:29 -070072 private static final long MBPS = 1_000 * 1_000;
tomb5a46e62014-08-26 14:20:00 -070073
74 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
tom96dfcab2014-08-28 09:26:03 -070075 protected DeviceProviderRegistry providerRegistry;
tomab21e7c2014-08-26 15:23:08 -070076
tom5f38b3a2014-08-27 23:50:54 -070077 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
78 protected OpenFlowController controller;
79
tomab21e7c2014-08-26 15:23:08 -070080 private DeviceProviderService providerService;
tomb5a46e62014-08-26 14:20:00 -070081
alshabib4680bb62014-09-04 17:15:08 -070082 private final OpenFlowSwitchListener listener = new InternalDeviceProvider();
tomd40fc7a2014-09-04 16:41:10 -070083
tomab21e7c2014-08-26 15:23:08 -070084 /**
85 * Creates an OpenFlow device provider.
86 */
87 public OpenFlowDeviceProvider() {
tom7e02cda2014-09-18 12:05:46 -070088 super(new ProviderId("of", "org.onlab.onos.provider.openflow"));
tomab21e7c2014-08-26 15:23:08 -070089 }
90
tomb5a46e62014-08-26 14:20:00 -070091 @Activate
92 public void activate() {
tom96dfcab2014-08-28 09:26:03 -070093 providerService = providerRegistry.register(this);
tomd40fc7a2014-09-04 16:41:10 -070094 controller.addListener(listener);
alshabibc944fd02014-09-10 17:55:17 -070095 for (OpenFlowSwitch sw : controller.getSwitches()) {
96 listener.switchAdded(new Dpid(sw.getId()));
97 }
alshabiba89cc582014-09-09 16:43:00 -070098 LOG.info("Started");
tomb5a46e62014-08-26 14:20:00 -070099 }
100
101 @Deactivate
102 public void deactivate() {
alshabibc944fd02014-09-10 17:55:17 -0700103 for (OpenFlowSwitch sw : controller.getSwitches()) {
tom782a7cf2014-09-11 23:58:38 -0700104 providerService.deviceDisconnected(DeviceId.deviceId(uri(sw.getId())));
alshabibc944fd02014-09-10 17:55:17 -0700105 }
tom96dfcab2014-08-28 09:26:03 -0700106 providerRegistry.unregister(this);
tomd40fc7a2014-09-04 16:41:10 -0700107 controller.removeListener(listener);
tomab21e7c2014-08-26 15:23:08 -0700108 providerService = null;
alshabibc944fd02014-09-10 17:55:17 -0700109
alshabiba89cc582014-09-09 16:43:00 -0700110 LOG.info("Stopped");
tomb5a46e62014-08-26 14:20:00 -0700111 }
112
Ayaka Koshibee60d4522014-10-28 15:07:00 -0700113
114 @Override
115 public boolean isReachable(Device device) {
116 // FIXME if possible, we might want this to be part of
117 // OpenFlowSwitch interface so the driver interface isn't misused.
118 OpenFlowSwitch sw = controller.getSwitch(dpid(device.id().uri()));
119 if (sw == null || !((OpenFlowSwitchDriver) sw).isConnected()) {
120 return false;
121 }
122 return true;
123 //return checkChannel(device, sw);
124 }
125
tomab21e7c2014-08-26 15:23:08 -0700126 @Override
127 public void triggerProbe(Device device) {
alshabiba89cc582014-09-09 16:43:00 -0700128 LOG.info("Triggering probe on device {}", device.id());
Ayaka Koshibee8708e32014-10-22 13:40:18 -0700129
Ayaka Koshibee8708e32014-10-22 13:40:18 -0700130 OpenFlowSwitch sw = controller.getSwitch(dpid(device.id().uri()));
Ayaka Koshibee60d4522014-10-28 15:07:00 -0700131 //if (!checkChannel(device, sw)) {
Thomas Vachuskad16ce182014-10-29 17:25:29 -0700132 // LOG.error("Failed to probe device {} on sw={}", device, sw);
Ayaka Koshibee60d4522014-10-28 15:07:00 -0700133 // providerService.deviceDisconnected(device.id());
Thomas Vachuskad16ce182014-10-29 17:25:29 -0700134 //return;
Ayaka Koshibee60d4522014-10-28 15:07:00 -0700135 //}
Ayaka Koshibee8708e32014-10-22 13:40:18 -0700136
Ayaka Koshibee60d4522014-10-28 15:07:00 -0700137 // Prompt an update of port information. We can use any XID for this.
Ayaka Koshibee8708e32014-10-22 13:40:18 -0700138 OFFactory fact = sw.factory();
139 switch (fact.getVersion()) {
140 case OF_10:
141 sw.sendMsg(fact.buildFeaturesRequest().setXid(0).build());
142 break;
143 case OF_13:
144 sw.sendMsg(fact.buildPortDescStatsRequest().setXid(0).build());
145 break;
146 default:
147 LOG.warn("Unhandled protocol version");
148 }
tomab21e7c2014-08-26 15:23:08 -0700149 }
150
Ayaka Koshibee60d4522014-10-28 15:07:00 -0700151 // Checks if the OF channel is connected.
152 //private boolean checkChannel(Device device, OpenFlowSwitch sw) {
Thomas Vachuskad16ce182014-10-29 17:25:29 -0700153 // FIXME if possible, we might want this to be part of
154 // OpenFlowSwitch interface so the driver interface isn't misused.
Ayaka Koshibee60d4522014-10-28 15:07:00 -0700155 // if (sw == null || !((OpenFlowSwitchDriver) sw).isConnected()) {
Thomas Vachuskad16ce182014-10-29 17:25:29 -0700156 // return false;
157 // }
Ayaka Koshibee60d4522014-10-28 15:07:00 -0700158 // return true;
Thomas Vachuskad16ce182014-10-29 17:25:29 -0700159 // }
Ayaka Koshibee60d4522014-10-28 15:07:00 -0700160
tomab21e7c2014-08-26 15:23:08 -0700161 @Override
162 public void roleChanged(Device device, MastershipRole newRole) {
alshabibf1216ed2014-09-03 11:53:54 -0700163 switch (newRole) {
tom782a7cf2014-09-11 23:58:38 -0700164 case MASTER:
165 controller.setRole(dpid(device.id().uri()), RoleState.MASTER);
166 break;
167 case STANDBY:
168 controller.setRole(dpid(device.id().uri()), RoleState.EQUAL);
169 break;
170 case NONE:
171 controller.setRole(dpid(device.id().uri()), RoleState.SLAVE);
172 break;
173 default:
174 LOG.error("Unknown Mastership state : {}", newRole);
alshabibf1216ed2014-09-03 11:53:54 -0700175
176 }
alshabiba89cc582014-09-09 16:43:00 -0700177 LOG.info("Accepting mastership role change for device {}", device.id());
tomab21e7c2014-08-26 15:23:08 -0700178 }
179
alshabibf1216ed2014-09-03 11:53:54 -0700180 private class InternalDeviceProvider implements OpenFlowSwitchListener {
alshabibf1216ed2014-09-03 11:53:54 -0700181 @Override
tomd1900f32014-09-03 14:08:16 -0700182 public void switchAdded(Dpid dpid) {
alshabib6f5460b2014-09-03 14:46:17 -0700183 if (providerService == null) {
184 return;
185 }
tom782a7cf2014-09-11 23:58:38 -0700186 DeviceId did = deviceId(uri(dpid));
alshabib6f5460b2014-09-03 14:46:17 -0700187 OpenFlowSwitch sw = controller.getSwitch(dpid);
188
Praseed Balakrishnana22eadf2014-10-20 14:21:45 -0700189 Device.Type deviceType = sw.isOptical() ? Device.Type.ROADM :
190 Device.Type.SWITCH;
alshabib7911a052014-10-16 17:49:37 -0700191 ChassisId cId = new ChassisId(dpid.value());
tomd1900f32014-09-03 14:08:16 -0700192 DeviceDescription description =
Praseed Balakrishnana22eadf2014-10-20 14:21:45 -0700193 new DefaultDeviceDescription(did.uri(), deviceType,
tom782a7cf2014-09-11 23:58:38 -0700194 sw.manfacturerDescription(),
195 sw.hardwareDescription(),
196 sw.softwareDescription(),
alshabib7911a052014-10-16 17:49:37 -0700197 sw.serialNumber(),
Thomas Vachuskad16ce182014-10-29 17:25:29 -0700198 cId);
tom782a7cf2014-09-11 23:58:38 -0700199 providerService.deviceConnected(did, description);
200 providerService.updatePorts(did, buildPortDescriptions(sw.getPorts()));
alshabib25c8eec2014-09-04 16:41:31 -0700201 }
202
alshabibf1216ed2014-09-03 11:53:54 -0700203 @Override
204 public void switchRemoved(Dpid dpid) {
alshabib6f5460b2014-09-03 14:46:17 -0700205 if (providerService == null) {
206 return;
207 }
tom782a7cf2014-09-11 23:58:38 -0700208 providerService.deviceDisconnected(deviceId(uri(dpid)));
alshabibf1216ed2014-09-03 11:53:54 -0700209 }
210
Ayaka Koshibe38594c22014-10-22 13:36:12 -0700211
212 @Override
213 public void switchChanged(Dpid dpid) {
214 if (providerService == null) {
215 return;
216 }
217 DeviceId did = deviceId(uri(dpid));
218 OpenFlowSwitch sw = controller.getSwitch(dpid);
219 providerService.updatePorts(did, buildPortDescriptions(sw.getPorts()));
220 }
221
alshabiba14f3642014-09-05 09:31:31 -0700222 @Override
223 public void portChanged(Dpid dpid, OFPortStatus status) {
tom782a7cf2014-09-11 23:58:38 -0700224 PortDescription portDescription = buildPortDescription(status.getDesc());
225 providerService.portStatusChanged(deviceId(uri(dpid)), portDescription);
alshabibf1216ed2014-09-03 11:53:54 -0700226 }
227
Ayaka Koshibeab91cc42014-09-25 10:20:52 -0700228 @Override
229 public void roleAssertFailed(Dpid dpid, RoleState role) {
230 MastershipRole failed;
231 switch (role) {
232 case MASTER:
233 failed = MastershipRole.MASTER;
234 break;
235 case EQUAL:
236 failed = MastershipRole.STANDBY;
237 break;
238 case SLAVE:
239 failed = MastershipRole.NONE;
240 break;
241 default:
242 LOG.warn("unknown role {}", role);
243 return;
244 }
245 providerService.unableToAssertRole(deviceId(uri(dpid)), failed);
246 }
247
alshabiba14f3642014-09-05 09:31:31 -0700248 /**
249 * Builds a list of port descriptions for a given list of ports.
tomff7eb7c2014-09-08 12:49:03 -0700250 *
alshabiba14f3642014-09-05 09:31:31 -0700251 * @param ports the list of ports
252 * @return list of portdescriptions
253 */
Thomas Vachuskad16ce182014-10-29 17:25:29 -0700254 private List<PortDescription> buildPortDescriptions(List<OFPortDesc> ports) {
Yuta HIGUCHI38294a92014-10-16 18:09:49 -0700255 final List<PortDescription> portDescs = new ArrayList<>(ports.size());
alshabib4680bb62014-09-04 17:15:08 -0700256 for (OFPortDesc port : ports) {
alshabiba14f3642014-09-05 09:31:31 -0700257 portDescs.add(buildPortDescription(port));
alshabib4680bb62014-09-04 17:15:08 -0700258 }
259 return portDescs;
260 }
261
alshabiba14f3642014-09-05 09:31:31 -0700262 /**
263 * Build a portDescription from a given port.
tomff7eb7c2014-09-08 12:49:03 -0700264 *
alshabiba14f3642014-09-05 09:31:31 -0700265 * @param port the port to build from.
266 * @return portDescription for the port.
267 */
268 private PortDescription buildPortDescription(OFPortDesc port) {
Thomas Vachuskad16ce182014-10-29 17:25:29 -0700269 PortNumber portNo = PortNumber.portNumber(port.getPortNo().getPortNumber());
270 boolean enabled =
271 !port.getState().contains(OFPortState.LINK_DOWN) &&
272 !port.getConfig().contains(OFPortConfig.PORT_DOWN);
273 Port.Type type = port.getCurr().contains(OFPortFeatures.PF_FIBER) ? FIBER : COPPER;
274 return new DefaultPortDescription(portNo, enabled, type, portSpeed(port));
alshabiba14f3642014-09-05 09:31:31 -0700275 }
Ayaka Koshibeab91cc42014-09-25 10:20:52 -0700276
Thomas Vachuskad16ce182014-10-29 17:25:29 -0700277 private long portSpeed(OFPortDesc port) {
278 if (port.getVersion() == OFVersion.OF_13) {
279 return port.getCurrSpeed() / MBPS;
280 }
281
282 PortSpeed portSpeed = PortSpeed.SPEED_NONE;
283 for (OFPortFeatures feat : port.getCurr()) {
284 portSpeed = PortSpeed.max(portSpeed, feat.getPortSpeed());
285 }
286 return portSpeed.getSpeedBps() / MBPS;
287 }
alshabibf1216ed2014-09-03 11:53:54 -0700288 }
289
tomb5a46e62014-08-26 14:20:00 -0700290}