blob: 082d1d6756db8c7ae0e17d49af88e104d26b52fc [file] [log] [blame]
Carmelo Cascone2ea177b2016-02-25 18:38:42 -08001/*
Carmelo Cascone2954f132016-04-15 10:26:40 -07002 * Copyright 2016-present Open Networking Laboratory
Carmelo Cascone2ea177b2016-02-25 18:38:42 -08003 *
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
17package org.onosproject.drivers.bmv2;
18
Carmelo Cascone2ea177b2016-02-25 18:38:42 -080019import com.google.common.collect.Lists;
20import com.google.common.collect.Maps;
Carmelo Cascone2954f132016-04-15 10:26:40 -070021import org.apache.commons.lang3.tuple.Pair;
22import org.apache.commons.lang3.tuple.Triple;
Carmelo Cascone37d5dbf2016-04-18 15:15:48 -070023import org.onosproject.bmv2.api.runtime.Bmv2Client;
Carmelo Cascone2954f132016-04-15 10:26:40 -070024import org.onosproject.bmv2.api.runtime.Bmv2MatchKey;
Carmelo Casconeaa8b6292016-04-13 14:27:06 -070025import org.onosproject.bmv2.api.runtime.Bmv2RuntimeException;
Carmelo Cascone2954f132016-04-15 10:26:40 -070026import org.onosproject.bmv2.api.runtime.Bmv2TableEntry;
Carmelo Cascone2ea177b2016-02-25 18:38:42 -080027import org.onosproject.bmv2.ctl.Bmv2ThriftClient;
Carmelo Cascone2954f132016-04-15 10:26:40 -070028import org.onosproject.drivers.bmv2.translators.Bmv2DefaultFlowRuleTranslator;
29import org.onosproject.drivers.bmv2.translators.Bmv2FlowRuleTranslator;
30import org.onosproject.drivers.bmv2.translators.Bmv2FlowRuleTranslatorException;
31import org.onosproject.net.DeviceId;
Carmelo Cascone2ea177b2016-02-25 18:38:42 -080032import org.onosproject.net.driver.AbstractHandlerBehaviour;
33import org.onosproject.net.flow.DefaultFlowEntry;
34import org.onosproject.net.flow.FlowEntry;
35import org.onosproject.net.flow.FlowRule;
36import org.onosproject.net.flow.FlowRuleProgrammable;
Carmelo Cascone2ea177b2016-02-25 18:38:42 -080037import org.slf4j.Logger;
38import org.slf4j.LoggerFactory;
39
40import java.util.Collection;
41import java.util.Collections;
42import java.util.List;
Carmelo Cascone2954f132016-04-15 10:26:40 -070043import java.util.concurrent.ConcurrentMap;
Carmelo Cascone2ea177b2016-02-25 18:38:42 -080044
Carmelo Casconee9121642016-04-27 17:02:38 -070045/**
46 * Flow rule programmable device behaviour implementation for BMv2.
47 */
48public class Bmv2FlowRuleProgrammable extends AbstractHandlerBehaviour
Carmelo Cascone2ea177b2016-02-25 18:38:42 -080049 implements FlowRuleProgrammable {
50
Carmelo Cascone2954f132016-04-15 10:26:40 -070051 private static final Logger LOG =
Carmelo Casconee9121642016-04-27 17:02:38 -070052 LoggerFactory.getLogger(Bmv2FlowRuleProgrammable.class);
Carmelo Cascone2954f132016-04-15 10:26:40 -070053 // There's no Bmv2 client method to poll flow entries from the device device. gitNeed a local store.
54 private static final ConcurrentMap<Triple<DeviceId, String, Bmv2MatchKey>, Pair<Long, FlowEntry>>
55 ENTRIES_MAP = Maps.newConcurrentMap();
56 private static final Bmv2FlowRuleTranslator TRANSLATOR = new Bmv2DefaultFlowRuleTranslator();
Carmelo Cascone2ea177b2016-02-25 18:38:42 -080057
58 @Override
59 public Collection<FlowEntry> getFlowEntries() {
Carmelo Cascone2954f132016-04-15 10:26:40 -070060
61 DeviceId deviceId = handler().data().deviceId();
62
63 List<FlowEntry> entryList = Lists.newArrayList();
64
65 // FIXME: improve this, e.g. might store a separate Map<DeviceId, Collection<FlowEntry>>
66 ENTRIES_MAP.forEach((key, value) -> {
67 if (key.getLeft() == deviceId && value != null) {
68 entryList.add(value.getRight());
69 }
70 });
71
72 return Collections.unmodifiableCollection(entryList);
Carmelo Cascone2ea177b2016-02-25 18:38:42 -080073 }
74
75 @Override
76 public Collection<FlowRule> applyFlowRules(Collection<FlowRule> rules) {
Carmelo Cascone2ea177b2016-02-25 18:38:42 -080077
Carmelo Cascone2954f132016-04-15 10:26:40 -070078 return processFlowRules(rules, Operation.APPLY);
Carmelo Cascone2ea177b2016-02-25 18:38:42 -080079 }
80
81 @Override
82 public Collection<FlowRule> removeFlowRules(Collection<FlowRule> rules) {
Carmelo Cascone2954f132016-04-15 10:26:40 -070083
84 return processFlowRules(rules, Operation.REMOVE);
85 }
86
87 private Collection<FlowRule> processFlowRules(Collection<FlowRule> rules, Operation operation) {
88
89 DeviceId deviceId = handler().data().deviceId();
90
Carmelo Cascone37d5dbf2016-04-18 15:15:48 -070091 Bmv2Client deviceClient;
Carmelo Cascone2ea177b2016-02-25 18:38:42 -080092 try {
Carmelo Cascone2954f132016-04-15 10:26:40 -070093 deviceClient = Bmv2ThriftClient.of(deviceId);
Carmelo Casconeaa8b6292016-04-13 14:27:06 -070094 } catch (Bmv2RuntimeException e) {
Carmelo Cascone2954f132016-04-15 10:26:40 -070095 LOG.error("Failed to connect to Bmv2 device", e);
Carmelo Cascone2ea177b2016-02-25 18:38:42 -080096 return Collections.emptyList();
97 }
98
Carmelo Cascone2954f132016-04-15 10:26:40 -070099 List<FlowRule> processedFlowRules = Lists.newArrayList();
Carmelo Cascone2ea177b2016-02-25 18:38:42 -0800100
101 for (FlowRule rule : rules) {
102
Carmelo Cascone2954f132016-04-15 10:26:40 -0700103 Bmv2TableEntry bmv2Entry;
Carmelo Cascone2ea177b2016-02-25 18:38:42 -0800104
Carmelo Cascone2954f132016-04-15 10:26:40 -0700105 try {
106 bmv2Entry = TRANSLATOR.translate(rule);
107 } catch (Bmv2FlowRuleTranslatorException e) {
108 LOG.error("Unable to translate flow rule: {}", e.getMessage());
109 continue;
Carmelo Cascone2ea177b2016-02-25 18:38:42 -0800110 }
Carmelo Cascone2954f132016-04-15 10:26:40 -0700111
112 String tableName = bmv2Entry.tableName();
113 Triple<DeviceId, String, Bmv2MatchKey> entryKey = Triple.of(deviceId, tableName, bmv2Entry.matchKey());
114
115 /*
116 From here on threads are synchronized over entryKey, i.e. serialize operations
117 over the same matchKey of a specific table and device.
118 */
119 ENTRIES_MAP.compute(entryKey, (key, value) -> {
120 try {
121 if (operation == Operation.APPLY) {
122 // Apply entry
123 long entryId;
124 if (value == null) {
125 // New entry
126 entryId = deviceClient.addTableEntry(bmv2Entry);
127 } else {
128 // Existing entry
129 entryId = value.getKey();
130 // FIXME: check if priority or timeout changed
131 // In this case we should to re-add the entry (not modify)
132 deviceClient.modifyTableEntry(tableName, entryId, bmv2Entry.action());
133 }
134 // TODO: evaluate flow entry life, bytes and packets
135 FlowEntry flowEntry = new DefaultFlowEntry(
136 rule, FlowEntry.FlowEntryState.ADDED, 0, 0, 0);
137 value = Pair.of(entryId, flowEntry);
138 } else {
139 // Remove entry
140 if (value == null) {
141 // Entry not found in map, how come?
142 LOG.debug("Trying to remove entry, but entry ID not found: " + entryKey);
143 } else {
144 deviceClient.deleteTableEntry(tableName, value.getKey());
145 value = null;
146 }
147 }
148 // If here, no exceptions... things went well :)
149 processedFlowRules.add(rule);
150 } catch (Bmv2RuntimeException e) {
151 LOG.error("Unable to " + operation.name().toLowerCase() + " flow rule", e);
152 } catch (Exception e) {
153 LOG.error("Uncaught exception while processing flow rule", e);
154 }
155 return value;
156 });
Carmelo Cascone2ea177b2016-02-25 18:38:42 -0800157 }
158
Carmelo Cascone2954f132016-04-15 10:26:40 -0700159 return processedFlowRules;
Carmelo Cascone2ea177b2016-02-25 18:38:42 -0800160 }
161
Carmelo Cascone2954f132016-04-15 10:26:40 -0700162 private enum Operation {
163 APPLY, REMOVE
Carmelo Cascone2ea177b2016-02-25 18:38:42 -0800164 }
Carmelo Cascone2954f132016-04-15 10:26:40 -0700165}