a7e334f4e4ae3c7da74ef84fab499f093d4306b4
[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 package org.onosproject.provider.of.device.impl;
17
18 import static com.google.common.base.Preconditions.checkArgument;
19 import static com.google.common.base.Strings.isNullOrEmpty;
20 import static org.onlab.util.Tools.get;
21 import static org.onosproject.net.DeviceId.deviceId;
22 import static org.onosproject.net.Port.Type.COPPER;
23 import static org.onosproject.net.Port.Type.FIBER;
24 import static org.onosproject.openflow.controller.Dpid.dpid;
25 import static org.onosproject.openflow.controller.Dpid.uri;
26 import static org.slf4j.LoggerFactory.getLogger;
27
28 import java.util.ArrayList;
29 import java.util.Collection;
30 import java.util.Collections;
31 import java.util.Dictionary;
32 import java.util.HashMap;
33 import java.util.HashSet;
34 import java.util.List;
35
36 import org.apache.felix.scr.annotations.Activate;
37 import org.apache.felix.scr.annotations.Component;
38 import org.apache.felix.scr.annotations.Deactivate;
39 import org.apache.felix.scr.annotations.Modified;
40 import org.apache.felix.scr.annotations.Property;
41 import org.apache.felix.scr.annotations.Reference;
42 import org.apache.felix.scr.annotations.ReferenceCardinality;
43 import org.onlab.packet.ChassisId;
44 import org.onlab.util.Frequency;
45 import org.onlab.util.Spectrum;
46 import org.onosproject.cfg.ComponentConfigService;
47 import org.onosproject.net.AnnotationKeys;
48 import org.onosproject.net.ChannelSpacing;
49 import org.onosproject.net.DefaultAnnotations;
50 import org.onosproject.net.Device;
51 import org.onosproject.net.DeviceId;
52 import org.onosproject.net.GridType;
53 import org.onosproject.net.MastershipRole;
54 import org.onosproject.net.OchSignal;
55 import org.onosproject.net.OduCltPort;
56 import org.onosproject.net.OduSignalType;
57 import org.onosproject.net.Port;
58 import org.onosproject.net.PortNumber;
59 import org.onosproject.net.SparseAnnotations;
60 import org.onosproject.net.device.DefaultDeviceDescription;
61 import org.onosproject.net.device.DefaultPortDescription;
62 import org.onosproject.net.device.DefaultPortStatistics;
63 import org.onosproject.net.device.DeviceDescription;
64 import org.onosproject.net.device.DeviceProvider;
65 import org.onosproject.net.device.DeviceProviderRegistry;
66 import org.onosproject.net.device.DeviceProviderService;
67 import org.onosproject.net.device.OchPortDescription;
68 import org.onosproject.net.device.OduCltPortDescription;
69 import org.onosproject.net.device.OmsPortDescription;
70 import org.onosproject.net.device.PortDescription;
71 import org.onosproject.net.device.PortStatistics;
72 import org.onosproject.net.provider.AbstractProvider;
73 import org.onosproject.net.provider.ProviderId;
74 import org.onosproject.openflow.controller.Dpid;
75 import org.onosproject.openflow.controller.OpenFlowController;
76 import org.onosproject.openflow.controller.OpenFlowEventListener;
77 import org.onosproject.openflow.controller.OpenFlowOpticalSwitch;
78 import org.onosproject.openflow.controller.OpenFlowSwitch;
79 import org.onosproject.openflow.controller.OpenFlowSwitchListener;
80 import org.onosproject.openflow.controller.PortDescPropertyType;
81 import org.onosproject.openflow.controller.RoleState;
82 import org.osgi.service.component.ComponentContext;
83 import org.projectfloodlight.openflow.protocol.OFCalientPortDescStatsEntry;
84 import org.projectfloodlight.openflow.protocol.OFExpPort;
85 import org.projectfloodlight.openflow.protocol.OFExpPortDescPropOpticalTransport;
86 import org.projectfloodlight.openflow.protocol.OFExpPortOpticalTransportLayerEntry;
87 import org.projectfloodlight.openflow.protocol.OFFactory;
88 import org.projectfloodlight.openflow.protocol.OFMessage;
89 import org.projectfloodlight.openflow.protocol.OFObject;
90 import org.projectfloodlight.openflow.protocol.OFPortConfig;
91 import org.projectfloodlight.openflow.protocol.OFPortDesc;
92 import org.projectfloodlight.openflow.protocol.OFPortDescPropOpticalTransport;
93 import org.projectfloodlight.openflow.protocol.OFPortFeatures;
94 import org.projectfloodlight.openflow.protocol.OFPortOptical;
95 import org.projectfloodlight.openflow.protocol.OFPortOpticalTransportLayerClass;
96 import org.projectfloodlight.openflow.protocol.OFPortOpticalTransportSignalType;
97 import org.projectfloodlight.openflow.protocol.OFPortReason;
98 import org.projectfloodlight.openflow.protocol.OFPortState;
99 import org.projectfloodlight.openflow.protocol.OFPortStatsEntry;
100 import org.projectfloodlight.openflow.protocol.OFPortStatsReply;
101 import org.projectfloodlight.openflow.protocol.OFPortStatus;
102 import org.projectfloodlight.openflow.protocol.OFStatsReply;
103 import org.projectfloodlight.openflow.protocol.OFStatsReplyFlags;
104 import org.projectfloodlight.openflow.protocol.OFStatsType;
105 import org.projectfloodlight.openflow.protocol.OFVersion;
106 import org.projectfloodlight.openflow.types.PortSpeed;
107 import org.slf4j.Logger;
108
109 import com.google.common.base.Strings;
110 import com.google.common.collect.Lists;
111 import com.google.common.collect.Maps;
112 import com.google.common.collect.Sets;
113
114 /**
115  * Provider which uses an OpenFlow controller to detect network
116  * infrastructure devices.
117  */
118 @Component(immediate = true)
119 public class OpenFlowDeviceProvider extends AbstractProvider implements DeviceProvider {
120
121     private static final Logger LOG = getLogger(OpenFlowDeviceProvider.class);
122
123     private static final long MBPS = 1_000 * 1_000;
124     private static final Frequency FREQ100 = Frequency.ofGHz(100);
125     private static final Frequency FREQ193_1 = Frequency.ofTHz(193.1);
126     private static final Frequency FREQ4_4 = Frequency.ofTHz(4.4);
127
128     @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
129     protected DeviceProviderRegistry providerRegistry;
130
131     @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
132     protected OpenFlowController controller;
133
134     @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
135     protected ComponentConfigService cfgService;
136
137     private DeviceProviderService providerService;
138
139     private final InternalDeviceProvider listener = new InternalDeviceProvider();
140
141     // TODO: We need to make the poll interval configurable.
142     static final int POLL_INTERVAL = 5;
143     @Property(name = "PortStatsPollFrequency", intValue = POLL_INTERVAL,
144     label = "Frequency (in seconds) for polling switch Port statistics")
145     private int portStatsPollFrequency = POLL_INTERVAL;
146
147     private HashMap<Dpid, PortStatsCollector> collectors = Maps.newHashMap();
148
149     /**
150      * Creates an OpenFlow device provider.
151      */
152     public OpenFlowDeviceProvider() {
153         super(new ProviderId("of", "org.onosproject.provider.openflow"));
154     }
155
156     @Activate
157     public void activate(ComponentContext context) {
158         cfgService.registerProperties(getClass());
159         providerService = providerRegistry.register(this);
160         controller.addListener(listener);
161         controller.addEventListener(listener);
162         connectInitialDevices();
163         LOG.info("Started");
164     }
165
166     @Deactivate
167     public void deactivate(ComponentContext context) {
168         cfgService.unregisterProperties(getClass(), false);
169         controller.removeListener(listener);
170         disconnectDevices();
171         providerRegistry.unregister(this);
172         collectors.values().forEach(PortStatsCollector::stop);
173         providerService = null;
174         LOG.info("Stopped");
175     }
176
177     @Modified
178     public void modified(ComponentContext context) {
179         Dictionary<?, ?> properties = context.getProperties();
180         int newPortStatsPollFrequency;
181         try {
182             String s = get(properties, "PortStatsPollFrequency");
183             newPortStatsPollFrequency = isNullOrEmpty(s) ? portStatsPollFrequency : Integer.parseInt(s.trim());
184
185         } catch (NumberFormatException | ClassCastException e) {
186             newPortStatsPollFrequency = portStatsPollFrequency;
187         }
188
189         if (newPortStatsPollFrequency != portStatsPollFrequency) {
190             portStatsPollFrequency = newPortStatsPollFrequency;
191             collectors.values().forEach(psc -> psc.adjustPollInterval(portStatsPollFrequency));
192         }
193
194         LOG.info("Settings: portStatsPollFrequency={}", portStatsPollFrequency);
195     }
196
197     private void connectInitialDevices() {
198         for (OpenFlowSwitch sw : controller.getSwitches()) {
199             try {
200                 listener.switchAdded(new Dpid(sw.getId()));
201             } catch (Exception e) {
202                 LOG.warn("Failed initially adding {} : {}", sw.getStringId(), e.getMessage());
203                 LOG.debug("Error details:", e);
204                 // disconnect to trigger switch-add later
205                 sw.disconnectSwitch();
206             }
207             PortStatsCollector psc = new PortStatsCollector(sw, portStatsPollFrequency);
208             psc.start();
209             collectors.put(new Dpid(sw.getId()), psc);
210         }
211     }
212
213     private void disconnectDevices() {
214         // Only disconnect the devices for which we are currently master.
215         controller.getMasterSwitches().forEach(sw -> listener.switchRemoved(new Dpid(sw.getId())));
216     }
217
218     @Override
219     public boolean isReachable(DeviceId deviceId) {
220         OpenFlowSwitch sw = controller.getSwitch(dpid(deviceId.uri()));
221         return sw != null && sw.isConnected();
222     }
223
224     @Override
225     public void triggerProbe(DeviceId deviceId) {
226         LOG.debug("Triggering probe on device {}", deviceId);
227
228         final Dpid dpid = dpid(deviceId.uri());
229         OpenFlowSwitch sw = controller.getSwitch(dpid);
230         if (sw == null || !sw.isConnected()) {
231             LOG.error("Failed to probe device {} on sw={}", deviceId, sw);
232             providerService.deviceDisconnected(deviceId);
233             return;
234         } else {
235             LOG.trace("Confirmed device {} connection", deviceId);
236         }
237
238         // Prompt an update of port information. We can use any XID for this.
239         OFFactory fact = sw.factory();
240         switch (fact.getVersion()) {
241             case OF_10:
242                 sw.sendMsg(fact.buildFeaturesRequest().setXid(0).build());
243                 break;
244             case OF_13:
245                 sw.sendMsg(fact.buildPortDescStatsRequest().setXid(0).build());
246                 break;
247             default:
248                 LOG.warn("Unhandled protocol version");
249         }
250     }
251
252     @Override
253     public void roleChanged(DeviceId deviceId, MastershipRole newRole) {
254         switch (newRole) {
255             case MASTER:
256                 controller.setRole(dpid(deviceId.uri()), RoleState.MASTER);
257                 break;
258             case STANDBY:
259                 controller.setRole(dpid(deviceId.uri()), RoleState.EQUAL);
260                 break;
261             case NONE:
262                 controller.setRole(dpid(deviceId.uri()), RoleState.SLAVE);
263                 break;
264             default:
265                 LOG.error("Unknown Mastership state : {}", newRole);
266
267         }
268         LOG.debug("Accepting mastership role change for device {}", deviceId);
269     }
270
271     private void pushPortMetrics(Dpid dpid, List<OFPortStatsEntry> portStatsEntries) {
272         DeviceId deviceId = DeviceId.deviceId(dpid.uri(dpid));
273         Collection<PortStatistics> stats = buildPortStatistics(deviceId, portStatsEntries);
274         providerService.updatePortStatistics(deviceId, stats);
275     }
276
277     private Collection<PortStatistics> buildPortStatistics(DeviceId deviceId,
278                                                            List<OFPortStatsEntry> entries) {
279         HashSet<PortStatistics> stats = Sets.newHashSet();
280
281         for (OFPortStatsEntry entry : entries) {
282             try {
283                 if (entry == null || entry.getPortNo() == null || entry.getPortNo().getPortNumber() < 0) {
284                     continue;
285                 }
286                 DefaultPortStatistics.Builder builder = DefaultPortStatistics.builder();
287                 DefaultPortStatistics stat = builder.setDeviceId(deviceId)
288                         .setPort(entry.getPortNo().getPortNumber())
289                         .setPacketsReceived(entry.getRxPackets().getValue())
290                         .setPacketsSent(entry.getTxPackets().getValue())
291                         .setBytesReceived(entry.getRxBytes().getValue())
292                         .setBytesSent(entry.getTxBytes().getValue())
293                         .setPacketsRxDropped(entry.getRxDropped().getValue())
294                         .setPacketsTxDropped(entry.getTxDropped().getValue())
295                         .setPacketsRxErrors(entry.getRxErrors().getValue())
296                         .setPacketsTxErrors(entry.getTxErrors().getValue())
297                         .setDurationSec(entry.getVersion() == OFVersion.OF_10 ? 0 : entry.getDurationSec())
298                         .setDurationNano(entry.getVersion() == OFVersion.OF_10 ? 0 : entry.getDurationNsec())
299                         .build();
300
301                 stats.add(stat);
302             } catch (Exception e) {
303                 LOG.warn("Unable to process port stats", e);
304             }
305         }
306
307         return Collections.unmodifiableSet(stats);
308
309     }
310
311     private class InternalDeviceProvider implements OpenFlowSwitchListener, OpenFlowEventListener {
312
313         private HashMap<Dpid, List<OFPortStatsEntry>> portStatsReplies = new HashMap<>();
314
315         @Override
316         public void switchAdded(Dpid dpid) {
317             if (providerService == null) {
318                 return;
319             }
320             DeviceId did = deviceId(uri(dpid));
321             OpenFlowSwitch sw = controller.getSwitch(dpid);
322             if (sw == null) {
323                 return;
324             }
325
326             ChassisId cId = new ChassisId(dpid.value());
327
328             SparseAnnotations annotations = DefaultAnnotations.builder()
329                     .set(AnnotationKeys.PROTOCOL, sw.factory().getVersion().toString())
330                     .set(AnnotationKeys.CHANNEL_ID, sw.channelId())
331                     .set(AnnotationKeys.MANAGEMENT_ADDRESS, sw.channelId().split(":")[0])
332                     .build();
333
334             DeviceDescription description =
335                     new DefaultDeviceDescription(did.uri(), sw.deviceType(),
336                                                  sw.manufacturerDescription(),
337                                                  sw.hardwareDescription(),
338                                                  sw.softwareDescription(),
339                                                  sw.serialNumber(),
340                                                  cId, annotations);
341             providerService.deviceConnected(did, description);
342             providerService.updatePorts(did, buildPortDescriptions(sw));
343
344             PortStatsCollector psc =
345                     new PortStatsCollector(sw, portStatsPollFrequency);
346             psc.start();
347             collectors.put(dpid, psc);
348
349             //figure out race condition for collectors.remove() and collectors.put()
350             if (controller.getSwitch(dpid) == null) {
351                 switchRemoved(dpid);
352             }
353         }
354
355         @Override
356         public void switchRemoved(Dpid dpid) {
357             if (providerService == null) {
358                 return;
359             }
360             providerService.deviceDisconnected(deviceId(uri(dpid)));
361
362             PortStatsCollector collector = collectors.remove(dpid);
363             if (collector != null) {
364                 collector.stop();
365             }
366         }
367
368         @Override
369         public void switchChanged(Dpid dpid) {
370             if (providerService == null) {
371                 return;
372             }
373             DeviceId did = deviceId(uri(dpid));
374             OpenFlowSwitch sw = controller.getSwitch(dpid);
375             if (sw == null) {
376                 return;
377             }
378             providerService.updatePorts(did, buildPortDescriptions(sw));
379         }
380
381         @Override
382         public void portChanged(Dpid dpid, OFPortStatus status) {
383             PortDescription portDescription = buildPortDescription(status);
384             providerService.portStatusChanged(deviceId(uri(dpid)), portDescription);
385         }
386
387         @Override
388         public void receivedRoleReply(Dpid dpid, RoleState requested, RoleState response) {
389             MastershipRole request = roleOf(requested);
390             MastershipRole reply = roleOf(response);
391             providerService.receivedRoleReply(deviceId(uri(dpid)), request, reply);
392         }
393
394         /**
395          * Translates a RoleState to the corresponding MastershipRole.
396          *
397          * @param response role state
398          * @return a MastershipRole
399          */
400         private MastershipRole roleOf(RoleState response) {
401             switch (response) {
402                 case MASTER:
403                     return MastershipRole.MASTER;
404                 case EQUAL:
405                     return MastershipRole.STANDBY;
406                 case SLAVE:
407                     return MastershipRole.NONE;
408                 default:
409                     LOG.warn("unknown role {}", response);
410                     return null;
411             }
412         }
413
414         /**
415          * Builds a list of port descriptions for a given list of ports.
416          *
417          * @return list of portdescriptions
418          */
419         private List<PortDescription> buildPortDescriptions(OpenFlowSwitch sw) {
420             final List<PortDescription> portDescs = new ArrayList<>(sw.getPorts().size());
421             if (!(Device.Type.ROADM.equals(sw.deviceType()))) {
422                   sw.getPorts().forEach(port -> portDescs.add(buildPortDescription(port)));
423             }
424
425             OpenFlowOpticalSwitch opsw;
426             switch (sw.deviceType()) {
427                 case ROADM:
428                     opsw = (OpenFlowOpticalSwitch) sw;
429                     List<OFPortDesc> ports = opsw.getPorts();
430                     LOG.debug("SW ID {} , ETH- ODU CLT Ports {}", opsw.getId(), ports);
431                     // ODU client ports are reported as ETH
432                     ports.forEach(port -> portDescs.add(buildOduCltPortDescription(port)));
433
434                     opsw.getPortTypes().forEach(type -> {
435                     List<? extends OFObject> portsOf = opsw.getPortsOf(type);
436                     LOG.debug("Ports Of{}", portsOf);
437                     portsOf.forEach(
438                         op -> {
439                             portDescs.add(buildPortDescription(type, (OFObject) op));
440                         }
441                      );
442                     });
443                     break;
444                 case FIBER_SWITCH:
445                     opsw = (OpenFlowOpticalSwitch) sw;
446                     opsw.getPortTypes().forEach(type -> {
447                         opsw.getPortsOf(type).forEach(
448                                 op -> {
449                                     portDescs.add(buildPortDescription((OFCalientPortDescStatsEntry) op));
450                                 }
451                         );
452                     });
453                     break;
454                 default:
455                     break;
456             }
457
458             return portDescs;
459         }
460
461         private PortDescription buildOduCltPortDescription(OFPortDesc port) {
462             PortNumber portNo = PortNumber.portNumber(port.getPortNo().getPortNumber());
463             boolean enabled = !port.getState().contains(OFPortState.LINK_DOWN) &&
464                               !port.getConfig().contains(OFPortConfig.PORT_DOWN);
465             Long portSpeed = portSpeed(port);
466             OduCltPort.SignalType sigType = null;
467
468             switch (portSpeed.toString()) {
469                 case "1":
470                     sigType = OduCltPort.SignalType.CLT_1GBE;
471                     break;
472                 case "10":
473                     sigType = OduCltPort.SignalType.CLT_10GBE;
474                     break;
475                 case "40":
476                     sigType = OduCltPort.SignalType.CLT_40GBE;
477                     break;
478                 case "100":
479                     sigType = OduCltPort.SignalType.CLT_100GBE;
480                     break;
481                 default:
482                     throw new RuntimeException("Un recognize OduClt speed: " + portSpeed.toString());
483             }
484
485             SparseAnnotations annotations = buildOduCltAnnotation(port);
486             return new OduCltPortDescription(portNo, enabled, sigType, annotations);
487         }
488
489         private SparseAnnotations buildOduCltAnnotation(OFPortDesc port) {
490             SparseAnnotations annotations = null;
491             String portName = Strings.emptyToNull(port.getName());
492             if (portName != null) {
493                  annotations = DefaultAnnotations.builder()
494                         .set(AnnotationKeys.PORT_NAME, portName)
495                         .set(AnnotationKeys.STATIC_PORT, Boolean.TRUE.toString()).build();
496             }
497             return annotations;
498         }
499
500         private PortDescription buildPortDescription(PortDescPropertyType ptype, OFObject port) {
501             if (port instanceof  OFPortOptical) {
502                return buildPortDescription(ptype, (OFPortOptical) port);
503             }
504             return buildPortDescription(ptype, (OFExpPort) port);
505         }
506
507         /**
508          * Build a portDescription from a given a port description describing some
509          * Optical port.
510          *
511          * @param ptype description property type.
512          * @param port the port to build from.
513          * @return portDescription for the port.
514          */
515         private PortDescription buildPortDescription(PortDescPropertyType ptype, OFExpPort port) {
516             PortNumber portNo = PortNumber.portNumber(port.getPortNo().getPortNumber());
517             boolean enabled = !port.getState().contains(OFPortState.LINK_DOWN)
518                     && !port.getConfig().contains(OFPortConfig.PORT_DOWN);
519             SparseAnnotations annotations = makePortNameAnnotation(port.getName());
520
521             OFExpPortDescPropOpticalTransport firstProp = port.getProperties().get(0);
522             OFPortOpticalTransportSignalType sigType = firstProp.getPortSignalType();
523
524             DefaultPortDescription portDes = null;
525             switch (sigType) {
526             case OMSN:
527                 portDes =  new OmsPortDescription(portNo, enabled, FREQ193_1, FREQ193_1.add(FREQ4_4),
528                        FREQ100, annotations);
529                 break;
530             case OCH:
531                 OFExpPortOpticalTransportLayerEntry entry = firstProp.getFeatures().get(0).getValue().get(0);
532                 OFPortOpticalTransportLayerClass layerClass =  entry.getLayerClass();
533                 if (!OFPortOpticalTransportLayerClass.ODU.equals(layerClass)) {
534                     LOG.error("Unsupported layer Class {} ", layerClass);
535                     return null;
536                 }
537
538                 // convert to ONOS OduSignalType
539                 OduSignalType oduSignalType = OpenFlowDeviceValueMapper.
540                         lookupOduSignalType((byte) entry.getSignalType());
541                 //OchSignal is needed for OchPortDescription constructor,
542                 //yet not relevant for tunable OCH port, creating with default parameters
543                 OchSignal signalId = new OchSignal(GridType.DWDM, ChannelSpacing.CHL_50GHZ, 1, 1);
544
545                 portDes = new OchPortDescription(portNo, enabled,
546                         oduSignalType, true, signalId, annotations);
547
548                 break;
549             case OTU2:
550             case OTU4:
551                   LOG.error("Signal tpye OTU2/4 not supported yet ", port.toString());
552                   break;
553             default:
554                 break;
555             }
556
557             return portDes;
558         }
559
560         /**
561          * Creates an annotation for the port name if one is available.
562          *
563          * @param port description of the port
564          * @return annotation containing the port name if one is found,
565          *         null otherwise
566          */
567         private SparseAnnotations makePortNameAnnotation(String port) {
568             SparseAnnotations annotations = null;
569             String portName = Strings.emptyToNull(port);
570             if (portName != null) {
571                 annotations = DefaultAnnotations.builder()
572                         .set(AnnotationKeys.PORT_NAME, portName).build();
573             }
574             return annotations;
575         }
576
577         /**
578          * Build a portDescription from a given Ethernet port description.
579          *
580          * @param port the port to build from.
581          * @return portDescription for the port.
582          */
583         private PortDescription buildPortDescription(OFPortDesc port) {
584             PortNumber portNo = PortNumber.portNumber(port.getPortNo().getPortNumber());
585             boolean enabled =
586                     !port.getState().contains(OFPortState.LINK_DOWN) &&
587                             !port.getConfig().contains(OFPortConfig.PORT_DOWN);
588             Port.Type type = port.getCurr().contains(OFPortFeatures.PF_FIBER) ? FIBER : COPPER;
589             SparseAnnotations annotations = makePortNameAnnotation(port.getName());
590             return new DefaultPortDescription(portNo, enabled, type,
591                                               portSpeed(port), annotations);
592         }
593
594         /**
595          * Build a portDescription from a given a port description describing some
596          * Optical port.
597          *
598          * @param port description property type.
599          * @param port the port to build from.
600          * @return portDescription for the port.
601          */
602         private PortDescription buildPortDescription(PortDescPropertyType ptype, OFPortOptical port) {
603             checkArgument(port.getDesc().size() >= 1);
604
605             // Minimally functional fixture. This needs to be fixed as we add better support.
606             PortNumber portNo = PortNumber.portNumber(port.getPortNo().getPortNumber());
607
608             boolean enabled = !port.getState().contains(OFPortState.LINK_DOWN)
609                     && !port.getConfig().contains(OFPortConfig.PORT_DOWN);
610             SparseAnnotations annotations = makePortNameAnnotation(port.getName());
611
612             if (port.getVersion() == OFVersion.OF_13
613                     && ptype == PortDescPropertyType.OPTICAL_TRANSPORT) {
614                 // At this point, not much is carried in the optical port message.
615                 LOG.debug("Optical transport port message {}", port.toString());
616             } else {
617                 // removable once 1.4+ support complete.
618                 LOG.debug("Unsupported optical port properties");
619             }
620
621             OFPortDescPropOpticalTransport desc = port.getDesc().get(0);
622             switch (desc.getPortSignalType()) {
623                 // FIXME: use constants once loxi has full optical extensions
624                 case 2:     // OMS port
625                     // Assume complete optical spectrum and 50 GHz grid
626                     // LINC-OE is only supported optical OF device for now
627                     return new OmsPortDescription(portNo, enabled,
628                             Spectrum.U_BAND_MIN, Spectrum.O_BAND_MAX, Frequency.ofGHz(50), annotations);
629                 case 5:     // OCH port
630                     OchSignal signal = new OchSignal(GridType.DWDM, ChannelSpacing.CHL_50GHZ, 0, 4);
631                     return new OchPortDescription(portNo, enabled, OduSignalType.ODU4,
632                             true, signal, annotations);
633                 default:
634                     break;
635             }
636
637             return new DefaultPortDescription(portNo, enabled, FIBER, 0, annotations);
638         }
639
640         /**
641          * Build a portDescription from a given port description describing a fiber switch optical port.
642          *
643          * @param port description property type.
644          * @param port the port to build from.
645          * @return portDescription for the port.
646          */
647         private PortDescription buildPortDescription(OFCalientPortDescStatsEntry port) {
648             PortNumber portNo = PortNumber.portNumber(port.getPortNo().getPortNumber());
649
650             // FIXME when Calient OF agent reports port status
651             boolean enabled = true;
652             SparseAnnotations annotations = makePortNameAnnotation(port.getName());
653
654             // S160 data sheet
655             // Wavelength range: 1260 - 1630 nm, grid is irrelevant for this type of switch
656             return new OmsPortDescription(portNo, enabled,
657                     Spectrum.U_BAND_MIN, Spectrum.O_BAND_MAX, Frequency.ofGHz(100), annotations);
658         }
659
660         private PortDescription buildPortDescription(OFPortStatus status) {
661             OFPortDesc port = status.getDesc();
662             if (status.getReason() != OFPortReason.DELETE) {
663                 return buildPortDescription(port);
664             } else {
665                 PortNumber portNo = PortNumber.portNumber(port.getPortNo().getPortNumber());
666                 Port.Type type = port.getCurr().contains(OFPortFeatures.PF_FIBER) ? FIBER : COPPER;
667                 SparseAnnotations annotations = makePortNameAnnotation(port.getName());
668                 return new DefaultPortDescription(portNo, false, type,
669                                                   portSpeed(port), annotations);
670             }
671         }
672
673         private long portSpeed(OFPortDesc port) {
674             if (port.getVersion() == OFVersion.OF_13) {
675                 return port.getCurrSpeed() / MBPS;
676             }
677
678             PortSpeed portSpeed = PortSpeed.SPEED_NONE;
679             for (OFPortFeatures feat : port.getCurr()) {
680                 portSpeed = PortSpeed.max(portSpeed, feat.getPortSpeed());
681             }
682             return portSpeed.getSpeedBps() / MBPS;
683         }
684
685         @Override
686         public void handleMessage(Dpid dpid, OFMessage msg) {
687             switch (msg.getType()) {
688                 case STATS_REPLY:
689                     if (((OFStatsReply) msg).getStatsType() == OFStatsType.PORT) {
690                         OFPortStatsReply portStatsReply = (OFPortStatsReply) msg;
691                         List<OFPortStatsEntry> portStatsReplyList = portStatsReplies.get(dpid);
692                         if (portStatsReplyList == null) {
693                             portStatsReplyList = Lists.newArrayList();
694                         }
695                         portStatsReplyList.addAll(portStatsReply.getEntries());
696                         portStatsReplies.put(dpid, portStatsReplyList);
697                         if (!portStatsReply.getFlags().contains(OFStatsReplyFlags.REPLY_MORE)) {
698                             pushPortMetrics(dpid, portStatsReplies.get(dpid));
699                             portStatsReplies.get(dpid).clear();
700                         }
701                     }
702                     break;
703                 default:
704                     break;
705             }
706         }
707     }
708 }