blob: 3c6acf52b7cfc3589a80b1e4dd7d1db43160f9d1 [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;
23import org.onosproject.bmv2.api.runtime.Bmv2MatchKey;
Carmelo Casconeaa8b6292016-04-13 14:27:06 -070024import org.onosproject.bmv2.api.runtime.Bmv2RuntimeException;
Carmelo Cascone2954f132016-04-15 10:26:40 -070025import org.onosproject.bmv2.api.runtime.Bmv2TableEntry;
Carmelo Cascone2ea177b2016-02-25 18:38:42 -080026import org.onosproject.bmv2.ctl.Bmv2ThriftClient;
Carmelo Cascone2954f132016-04-15 10:26:40 -070027import org.onosproject.drivers.bmv2.translators.Bmv2DefaultFlowRuleTranslator;
28import org.onosproject.drivers.bmv2.translators.Bmv2FlowRuleTranslator;
29import org.onosproject.drivers.bmv2.translators.Bmv2FlowRuleTranslatorException;
30import org.onosproject.net.DeviceId;
Carmelo Cascone2ea177b2016-02-25 18:38:42 -080031import org.onosproject.net.driver.AbstractHandlerBehaviour;
32import org.onosproject.net.flow.DefaultFlowEntry;
33import org.onosproject.net.flow.FlowEntry;
34import org.onosproject.net.flow.FlowRule;
35import org.onosproject.net.flow.FlowRuleProgrammable;
Carmelo Cascone2ea177b2016-02-25 18:38:42 -080036import org.slf4j.Logger;
37import org.slf4j.LoggerFactory;
38
39import java.util.Collection;
40import java.util.Collections;
41import java.util.List;
Carmelo Cascone2954f132016-04-15 10:26:40 -070042import java.util.concurrent.ConcurrentMap;
Carmelo Cascone2ea177b2016-02-25 18:38:42 -080043
44public class Bmv2FlowRuleDriver extends AbstractHandlerBehaviour
45 implements FlowRuleProgrammable {
46
Carmelo Cascone2954f132016-04-15 10:26:40 -070047 private static final Logger LOG =
48 LoggerFactory.getLogger(Bmv2FlowRuleDriver.class);
49 // There's no Bmv2 client method to poll flow entries from the device device. gitNeed a local store.
50 private static final ConcurrentMap<Triple<DeviceId, String, Bmv2MatchKey>, Pair<Long, FlowEntry>>
51 ENTRIES_MAP = Maps.newConcurrentMap();
52 private static final Bmv2FlowRuleTranslator TRANSLATOR = new Bmv2DefaultFlowRuleTranslator();
Carmelo Cascone2ea177b2016-02-25 18:38:42 -080053
54 @Override
55 public Collection<FlowEntry> getFlowEntries() {
Carmelo Cascone2954f132016-04-15 10:26:40 -070056
57 DeviceId deviceId = handler().data().deviceId();
58
59 List<FlowEntry> entryList = Lists.newArrayList();
60
61 // FIXME: improve this, e.g. might store a separate Map<DeviceId, Collection<FlowEntry>>
62 ENTRIES_MAP.forEach((key, value) -> {
63 if (key.getLeft() == deviceId && value != null) {
64 entryList.add(value.getRight());
65 }
66 });
67
68 return Collections.unmodifiableCollection(entryList);
Carmelo Cascone2ea177b2016-02-25 18:38:42 -080069 }
70
71 @Override
72 public Collection<FlowRule> applyFlowRules(Collection<FlowRule> rules) {
Carmelo Cascone2ea177b2016-02-25 18:38:42 -080073
Carmelo Cascone2954f132016-04-15 10:26:40 -070074 return processFlowRules(rules, Operation.APPLY);
Carmelo Cascone2ea177b2016-02-25 18:38:42 -080075 }
76
77 @Override
78 public Collection<FlowRule> removeFlowRules(Collection<FlowRule> rules) {
Carmelo Cascone2954f132016-04-15 10:26:40 -070079
80 return processFlowRules(rules, Operation.REMOVE);
81 }
82
83 private Collection<FlowRule> processFlowRules(Collection<FlowRule> rules, Operation operation) {
84
85 DeviceId deviceId = handler().data().deviceId();
86
Carmelo Cascone2ea177b2016-02-25 18:38:42 -080087 Bmv2ThriftClient deviceClient;
88 try {
Carmelo Cascone2954f132016-04-15 10:26:40 -070089 deviceClient = Bmv2ThriftClient.of(deviceId);
Carmelo Casconeaa8b6292016-04-13 14:27:06 -070090 } catch (Bmv2RuntimeException e) {
Carmelo Cascone2954f132016-04-15 10:26:40 -070091 LOG.error("Failed to connect to Bmv2 device", e);
Carmelo Cascone2ea177b2016-02-25 18:38:42 -080092 return Collections.emptyList();
93 }
94
Carmelo Cascone2954f132016-04-15 10:26:40 -070095 List<FlowRule> processedFlowRules = Lists.newArrayList();
Carmelo Cascone2ea177b2016-02-25 18:38:42 -080096
97 for (FlowRule rule : rules) {
98
Carmelo Cascone2954f132016-04-15 10:26:40 -070099 Bmv2TableEntry bmv2Entry;
Carmelo Cascone2ea177b2016-02-25 18:38:42 -0800100
Carmelo Cascone2954f132016-04-15 10:26:40 -0700101 try {
102 bmv2Entry = TRANSLATOR.translate(rule);
103 } catch (Bmv2FlowRuleTranslatorException e) {
104 LOG.error("Unable to translate flow rule: {}", e.getMessage());
105 continue;
Carmelo Cascone2ea177b2016-02-25 18:38:42 -0800106 }
Carmelo Cascone2954f132016-04-15 10:26:40 -0700107
108 String tableName = bmv2Entry.tableName();
109 Triple<DeviceId, String, Bmv2MatchKey> entryKey = Triple.of(deviceId, tableName, bmv2Entry.matchKey());
110
111 /*
112 From here on threads are synchronized over entryKey, i.e. serialize operations
113 over the same matchKey of a specific table and device.
114 */
115 ENTRIES_MAP.compute(entryKey, (key, value) -> {
116 try {
117 if (operation == Operation.APPLY) {
118 // Apply entry
119 long entryId;
120 if (value == null) {
121 // New entry
122 entryId = deviceClient.addTableEntry(bmv2Entry);
123 } else {
124 // Existing entry
125 entryId = value.getKey();
126 // FIXME: check if priority or timeout changed
127 // In this case we should to re-add the entry (not modify)
128 deviceClient.modifyTableEntry(tableName, entryId, bmv2Entry.action());
129 }
130 // TODO: evaluate flow entry life, bytes and packets
131 FlowEntry flowEntry = new DefaultFlowEntry(
132 rule, FlowEntry.FlowEntryState.ADDED, 0, 0, 0);
133 value = Pair.of(entryId, flowEntry);
134 } else {
135 // Remove entry
136 if (value == null) {
137 // Entry not found in map, how come?
138 LOG.debug("Trying to remove entry, but entry ID not found: " + entryKey);
139 } else {
140 deviceClient.deleteTableEntry(tableName, value.getKey());
141 value = null;
142 }
143 }
144 // If here, no exceptions... things went well :)
145 processedFlowRules.add(rule);
146 } catch (Bmv2RuntimeException e) {
147 LOG.error("Unable to " + operation.name().toLowerCase() + " flow rule", e);
148 } catch (Exception e) {
149 LOG.error("Uncaught exception while processing flow rule", e);
150 }
151 return value;
152 });
Carmelo Cascone2ea177b2016-02-25 18:38:42 -0800153 }
154
Carmelo Cascone2954f132016-04-15 10:26:40 -0700155 return processedFlowRules;
Carmelo Cascone2ea177b2016-02-25 18:38:42 -0800156 }
157
Carmelo Cascone2954f132016-04-15 10:26:40 -0700158 private enum Operation {
159 APPLY, REMOVE
Carmelo Cascone2ea177b2016-02-25 18:38:42 -0800160 }
Carmelo Cascone2954f132016-04-15 10:26:40 -0700161}