2  * Copyright 2015 Open Networking Laboratory
 
   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
 
   8  *     http://www.apache.org/licenses/LICENSE-2.0
 
  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.
 
  17 package org.onosproject.provider.of.group.impl;
 
  19 import com.google.common.collect.Maps;
 
  21 import org.apache.felix.scr.annotations.Activate;
 
  22 import org.apache.felix.scr.annotations.Component;
 
  23 import org.apache.felix.scr.annotations.Deactivate;
 
  24 import org.apache.felix.scr.annotations.Reference;
 
  25 import org.apache.felix.scr.annotations.ReferenceCardinality;
 
  26 import org.onosproject.core.DefaultGroupId;
 
  27 import org.onosproject.core.GroupId;
 
  28 import org.onosproject.net.DeviceId;
 
  29 import org.onosproject.net.group.DefaultGroup;
 
  30 import org.onosproject.net.group.Group;
 
  31 import org.onosproject.net.group.GroupBuckets;
 
  32 import org.onosproject.net.group.GroupDescription;
 
  33 import org.onosproject.net.group.GroupOperation;
 
  34 import org.onosproject.net.group.GroupOperations;
 
  35 import org.onosproject.net.group.GroupProvider;
 
  36 import org.onosproject.net.group.GroupProviderRegistry;
 
  37 import org.onosproject.net.group.GroupProviderService;
 
  38 import org.onosproject.net.group.StoredGroupBucketEntry;
 
  39 import org.onosproject.net.provider.AbstractProvider;
 
  40 import org.onosproject.net.provider.ProviderId;
 
  41 import org.onosproject.openflow.controller.Dpid;
 
  42 import org.onosproject.openflow.controller.OpenFlowController;
 
  43 import org.onosproject.openflow.controller.OpenFlowEventListener;
 
  44 import org.onosproject.openflow.controller.OpenFlowSwitch;
 
  45 import org.onosproject.openflow.controller.OpenFlowSwitchListener;
 
  46 import org.onosproject.openflow.controller.RoleState;
 
  47 import org.projectfloodlight.openflow.protocol.OFBucketCounter;
 
  48 import org.projectfloodlight.openflow.protocol.OFErrorMsg;
 
  49 import org.projectfloodlight.openflow.protocol.OFErrorType;
 
  50 import org.projectfloodlight.openflow.protocol.OFGroupDescStatsEntry;
 
  51 import org.projectfloodlight.openflow.protocol.OFGroupDescStatsReply;
 
  52 import org.projectfloodlight.openflow.protocol.OFGroupMod;
 
  53 import org.projectfloodlight.openflow.protocol.OFGroupStatsEntry;
 
  54 import org.projectfloodlight.openflow.protocol.OFGroupStatsReply;
 
  55 import org.projectfloodlight.openflow.protocol.OFGroupType;
 
  56 import org.projectfloodlight.openflow.protocol.OFMessage;
 
  57 import org.projectfloodlight.openflow.protocol.OFPortStatus;
 
  58 import org.projectfloodlight.openflow.protocol.OFStatsReply;
 
  59 import org.projectfloodlight.openflow.protocol.OFStatsType;
 
  60 import org.projectfloodlight.openflow.protocol.OFVersion;
 
  61 import org.slf4j.Logger;
 
  63 import java.util.Collection;
 
  65 import java.util.Optional;
 
  66 import java.util.concurrent.atomic.AtomicLong;
 
  68 import static org.slf4j.LoggerFactory.getLogger;
 
  71  * Provider which uses an OpenFlow controller to handle Group.
 
  73 @Component(immediate = true)
 
  74 public class OpenFlowGroupProvider extends AbstractProvider implements GroupProvider {
 
  76     private final Logger log = getLogger(getClass());
 
  78     @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
 
  79     protected OpenFlowController controller;
 
  81     @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
 
  82     protected GroupProviderRegistry providerRegistry;
 
  84     private GroupProviderService providerService;
 
  86     static final int POLL_INTERVAL = 10;
 
  88     private final InternalGroupProvider listener = new InternalGroupProvider();
 
  90     private static final AtomicLong XID_COUNTER = new AtomicLong(1);
 
  91     private final Map<Dpid, GroupStatsCollector> collectors = Maps.newHashMap();
 
  92     private final Map<Long, OFStatsReply> groupStats = Maps.newConcurrentMap();
 
  93     private final Map<GroupId, GroupOperation> pendingGroupOperations =
 
  94             Maps.newConcurrentMap();
 
  96     /* Map<Group ID, Transaction ID> */
 
  97     private final Map<GroupId, Long> pendingXidMaps = Maps.newConcurrentMap();
 
 100      * Creates a OpenFlow group provider.
 
 102     public OpenFlowGroupProvider() {
 
 103         super(new ProviderId("of", "org.onosproject.provider.group"));
 
 107     public void activate() {
 
 108         providerService = providerRegistry.register(this);
 
 109         controller.addListener(listener);
 
 110         controller.addEventListener(listener);
 
 112         for (OpenFlowSwitch sw : controller.getSwitches()) {
 
 113             if (isGroupSupported(sw)) {
 
 114                 GroupStatsCollector gsc = new GroupStatsCollector(sw, POLL_INTERVAL);
 
 116                 collectors.put(new Dpid(sw.getId()), gsc);
 
 124     public void deactivate() {
 
 125         providerRegistry.unregister(this);
 
 126         providerService = null;
 
 132     public void performGroupOperation(DeviceId deviceId, GroupOperations groupOps) {
 
 133         Map<OFGroupMod, OpenFlowSwitch> mods = Maps.newIdentityHashMap();
 
 134         final Dpid dpid = Dpid.dpid(deviceId.uri());
 
 135         OpenFlowSwitch sw = controller.getSwitch(dpid);
 
 136         for (GroupOperation groupOperation: groupOps.operations()) {
 
 138                 log.error("SW {} is not found", dpid);
 
 141             final Long groupModXid = XID_COUNTER.getAndIncrement();
 
 142             GroupModBuilder builder =
 
 143                     GroupModBuilder.builder(groupOperation.buckets(),
 
 144                             groupOperation.groupId(),
 
 145                             groupOperation.groupType(),
 
 147                             Optional.of(groupModXid));
 
 148             OFGroupMod groupMod = null;
 
 149             switch (groupOperation.opType()) {
 
 151                     groupMod = builder.buildGroupAdd();
 
 154                     groupMod = builder.buildGroupMod();
 
 157                     groupMod = builder.buildGroupDel();
 
 160                     log.error("Unsupported Group operation");
 
 162             sw.sendMsg(groupMod);
 
 163             GroupId groudId = new DefaultGroupId(groupMod.getGroup().getGroupNumber());
 
 164             pendingGroupOperations.put(groudId, groupOperation);
 
 165             pendingXidMaps.put(groudId, groupModXid);
 
 169     private void pushGroupMetrics(Dpid dpid, OFStatsReply statsReply) {
 
 170         DeviceId deviceId = DeviceId.deviceId(Dpid.uri(dpid));
 
 172         OFGroupStatsReply groupStatsReply = null;
 
 173         OFGroupDescStatsReply groupDescStatsReply = null;
 
 175         synchronized (groupStats) {
 
 176             if (statsReply.getStatsType() == OFStatsType.GROUP) {
 
 177                 OFStatsReply reply = groupStats.get(statsReply.getXid() + 1);
 
 179                     groupStatsReply = (OFGroupStatsReply) statsReply;
 
 180                     groupDescStatsReply = (OFGroupDescStatsReply) reply;
 
 181                     groupStats.remove(statsReply.getXid() + 1);
 
 183                     groupStats.put(statsReply.getXid(), statsReply);
 
 185             } else if (statsReply.getStatsType() == OFStatsType.GROUP_DESC) {
 
 186                 OFStatsReply reply = groupStats.get(statsReply.getXid() - 1);
 
 188                     groupStatsReply = (OFGroupStatsReply) reply;
 
 189                     groupDescStatsReply = (OFGroupDescStatsReply) statsReply;
 
 190                     groupStats.remove(statsReply.getXid() - 1);
 
 192                     groupStats.put(statsReply.getXid(), statsReply);
 
 197         if (groupStatsReply != null && groupDescStatsReply != null) {
 
 198             Collection<Group> groups = buildGroupMetrics(deviceId,
 
 199                     groupStatsReply, groupDescStatsReply);
 
 200             providerService.pushGroupMetrics(deviceId, groups);
 
 201             for (Group group: groups) {
 
 202                 pendingGroupOperations.remove(group.id());
 
 203                 pendingXidMaps.remove(group.id());
 
 208     private Collection<Group> buildGroupMetrics(DeviceId deviceId,
 
 209                                    OFGroupStatsReply groupStatsReply,
 
 210                                    OFGroupDescStatsReply groupDescStatsReply) {
 
 212         Map<Integer, Group> groups = Maps.newHashMap();
 
 215         for (OFGroupDescStatsEntry entry: groupDescStatsReply.getEntries()) {
 
 216             int id = entry.getGroup().getGroupNumber();
 
 217             GroupId groupId = new DefaultGroupId(id);
 
 218             GroupDescription.Type type = getGroupType(entry.getGroupType());
 
 219             GroupBuckets buckets = new GroupBucketEntryBuilder(entry.getBuckets(),
 
 220                     entry.getGroupType()).build();
 
 221             DefaultGroup group = new DefaultGroup(groupId, deviceId, type, buckets);
 
 222             groups.put(id, group);
 
 225         for (OFGroupStatsEntry entry: groupStatsReply.getEntries()) {
 
 226             int groupId = entry.getGroup().getGroupNumber();
 
 227             DefaultGroup group = (DefaultGroup) groups.get(groupId);
 
 229                 group.setBytes(entry.getByteCount().getValue());
 
 230                 group.setLife(entry.getDurationSec());
 
 231                 group.setPackets(entry.getPacketCount().getValue());
 
 232                 group.setReferenceCount(entry.getRefCount());
 
 234                 for (OFBucketCounter bucketStats:entry.getBucketStats()) {
 
 235                     ((StoredGroupBucketEntry) group.buckets().buckets()
 
 237                             .setPackets(bucketStats
 
 238                                         .getPacketCount().getValue());
 
 239                     ((StoredGroupBucketEntry) group.buckets().buckets()
 
 241                             .setBytes(entry.getBucketStats()
 
 243                                       .getByteCount().getValue());
 
 249         return groups.values();
 
 252     private GroupDescription.Type getGroupType(OFGroupType type) {
 
 255                 return  GroupDescription.Type.ALL;
 
 257                 return GroupDescription.Type.INDIRECT;
 
 259                 return GroupDescription.Type.SELECT;
 
 261                 return GroupDescription.Type.FAILOVER;
 
 263                 log.error("Unsupported OF group type : {}", type);
 
 270      * Returns a transaction ID for entire group operations and increases
 
 271      * the counter by the number given.
 
 273      * @param increase the amount to increase the counter by
 
 274      * @return a transaction ID
 
 276     public static long getXidAndAdd(int increase) {
 
 277         return XID_COUNTER.getAndAdd(increase);
 
 280     private boolean isGroupSupported(OpenFlowSwitch sw) {
 
 281         if (sw.factory().getVersion() == OFVersion.OF_10 ||
 
 282                 sw.factory().getVersion() == OFVersion.OF_11 ||
 
 283                 sw.factory().getVersion() == OFVersion.OF_12) {
 
 290     private class InternalGroupProvider
 
 291             implements OpenFlowSwitchListener, OpenFlowEventListener {
 
 294         public void handleMessage(Dpid dpid, OFMessage msg) {
 
 295             switch (msg.getType()) {
 
 297                     pushGroupMetrics(dpid, (OFStatsReply) msg);
 
 300                     OFErrorMsg errorMsg = (OFErrorMsg) msg;
 
 301                     if (errorMsg.getErrType() == OFErrorType.GROUP_MOD_FAILED) {
 
 302                         GroupId pendingGroupId = null;
 
 303                         for (Map.Entry<GroupId, Long> entry: pendingXidMaps.entrySet()) {
 
 304                             if (entry.getValue() == errorMsg.getXid()) {
 
 305                                 pendingGroupId = entry.getKey();
 
 309                         if (pendingGroupId == null) {
 
 310                             log.warn("Error for unknown group operation: {}",
 
 313                             GroupOperation operation =
 
 314                                     pendingGroupOperations.get(pendingGroupId);
 
 315                             DeviceId deviceId = DeviceId.deviceId(Dpid.uri(dpid));
 
 316                             if (operation != null) {
 
 317                                 providerService.groupOperationFailed(deviceId,
 
 319                                 pendingGroupOperations.remove(pendingGroupId);
 
 320                                 pendingXidMaps.remove(pendingGroupId);
 
 321                                 log.warn("Received an group mod error {}", msg);
 
 323                                 log.error("Cannot find pending group operation with group ID: {}",
 
 335         public void switchAdded(Dpid dpid) {
 
 336             OpenFlowSwitch sw = controller.getSwitch(dpid);
 
 337             if (isGroupSupported(sw)) {
 
 338                 GroupStatsCollector gsc = new GroupStatsCollector(
 
 339                         controller.getSwitch(dpid), POLL_INTERVAL);
 
 341                 collectors.put(dpid, gsc);
 
 346         public void switchRemoved(Dpid dpid) {
 
 347             GroupStatsCollector collector = collectors.remove(dpid);
 
 348             if (collector != null) {
 
 354         public void switchChanged(Dpid dpid) {
 
 358         public void portChanged(Dpid dpid, OFPortStatus status) {
 
 362         public void receivedRoleReply(Dpid dpid, RoleState requested, RoleState response) {