blob: c08c5a7a698d8d4454456dc8f7cb4802f7203ebf [file] [log] [blame]
Frank Wang0e805082017-07-21 14:37:35 +08001/*
2 * Copyright 2017-present 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
17package org.onosproject.drivers.bmv2;
18
19import com.google.common.collect.Maps;
20import org.onosproject.net.Device;
21import org.onosproject.net.DeviceId;
22import org.onosproject.net.device.DeviceService;
23import org.onosproject.net.driver.AbstractHandlerBehaviour;
24import org.onosproject.net.flow.DefaultFlowEntry;
25import org.onosproject.net.flow.FlowEntry;
26import org.onosproject.net.flow.FlowRule;
27import org.onosproject.net.flow.FlowRuleProgrammable;
28import org.onosproject.net.pi.model.PiPipeconf;
29import org.onosproject.net.pi.model.PiPipelineInterpreter;
30import org.onosproject.net.pi.runtime.PiFlowRuleTranslationService;
31import org.onosproject.net.pi.runtime.PiPipeconfService;
32import org.onosproject.net.pi.runtime.PiTableEntry;
33import org.onosproject.p4runtime.api.P4RuntimeClient;
34import org.onosproject.p4runtime.api.P4RuntimeClient.WriteOperationType;
35import org.onosproject.p4runtime.api.P4RuntimeController;
36import org.slf4j.Logger;
37import org.slf4j.LoggerFactory;
38
39import java.util.Collection;
40import java.util.Collections;
41import java.util.Map;
42import java.util.stream.Collectors;
43
44/**
45 * Implementation of the flow rule programmable behaviour for BMv2.
46 */
47public class Bmv2FlowRuleProgrammable extends AbstractHandlerBehaviour implements FlowRuleProgrammable {
48
49 private final Logger log = LoggerFactory.getLogger(getClass());
50 private static final Map<DeviceId, Collection<Bmv2FlowRuleWrapper>> INSTALLED_FLOWENTRIES = Maps.newHashMap();
51
52 P4RuntimeClient client;
53 PiPipeconf pipeconf;
54 PiPipelineInterpreter interpreter;
55 DeviceId deviceId;
56 PiFlowRuleTranslationService piFlowRuleTranslationService;
57
58 private boolean init() {
59 deviceId = handler().data().deviceId();
60
61 P4RuntimeController controller = handler().get(P4RuntimeController.class);
62 if (!controller.hasClient(deviceId)) {
63 log.warn("Unable to find client for {}, aborting flow rule operation", deviceId);
64 return false;
65 }
66
67 client = controller.getClient(deviceId);
68 piFlowRuleTranslationService = handler().get(PiFlowRuleTranslationService.class);
69 PiPipeconfService piPipeconfService = handler().get(PiPipeconfService.class);
70
71 if (piPipeconfService.ofDevice(deviceId).isPresent() &&
72 piPipeconfService.getPipeconf(piPipeconfService.ofDevice(deviceId).get()).isPresent()) {
73 pipeconf = piPipeconfService.getPipeconf(piPipeconfService.ofDevice(deviceId).get()).get();
74 } else {
75 log.warn("Unable to get the pipeconf of {}", deviceId);
76 return false;
77 }
78
79 DeviceService deviceService = handler().get(DeviceService.class);
80 Device device = deviceService.getDevice(deviceId);
81 interpreter = device.is(PiPipelineInterpreter.class) ? device.as(PiPipelineInterpreter.class) : null;
82 if (device.is(PiPipelineInterpreter.class)) {
83 log.warn("Device {} unable to instantiate interpreter of pipeconf {}", deviceId, pipeconf.id());
84 return false;
85 }
86
87 return true;
88 }
89
90 @Override
91 public Collection<FlowEntry> getFlowEntries() {
92
93 if (!init()) {
94 return Collections.emptyList();
95 }
96
97 //TD DO: retrieve statistics.
98 long packets = 0;
99 long bytes = 0;
100 Collection<FlowEntry> flowEntries = INSTALLED_FLOWENTRIES
101 .get(deviceId).stream().map(wrapper -> new DefaultFlowEntry(wrapper.rule(),
102 FlowEntry.FlowEntryState.ADDED,
103 wrapper.lifeInSeconds(),
104 packets,
105 bytes))
106 .collect(Collectors.toList());
107
108 return Collections.unmodifiableCollection(flowEntries);
109 }
110
111 @Override
112 public Collection<FlowRule> applyFlowRules(Collection<FlowRule> rules) {
113
114 Collection<FlowRule> insertFlowRules = rules.stream()
115 .filter(r -> INSTALLED_FLOWENTRIES.get(deviceId) == null ||
116 INSTALLED_FLOWENTRIES.get(deviceId).stream().noneMatch(x -> x.rule().id() == r.id()))
117 .collect(Collectors.toList());
118
119 Collection<FlowRule> updateFlowRules = rules.stream()
120 .filter(r -> INSTALLED_FLOWENTRIES.get(deviceId) != null &&
121 INSTALLED_FLOWENTRIES.get(deviceId).stream().anyMatch(x -> x.rule().id() == r.id()))
122 .collect(Collectors.toList());
123
124 Collection<FlowRule> flowRules = processFlowRules(insertFlowRules, WriteOperationType.INSERT);
125 flowRules.addAll(processFlowRules(updateFlowRules, WriteOperationType.MODIFY));
126 return flowRules;
127 }
128
129 @Override
130 public Collection<FlowRule> removeFlowRules(Collection<FlowRule> rules) {
131
132 return processFlowRules(rules, WriteOperationType.DELETE);
133 }
134
135 private Collection<FlowRule> processFlowRules(Collection<FlowRule> rules, WriteOperationType opType) {
136
137 if (!init()) {
138 return Collections.emptyList();
139 }
140
141 Collection<PiTableEntry> piTableEntries = rules.stream().map(r -> {
142 PiTableEntry piTableEntry = null;
143 try {
144 piTableEntry = piFlowRuleTranslationService.translate(r, pipeconf);
145 } catch (PiFlowRuleTranslationService.PiFlowRuleTranslationException e) {
146 log.error("Flow rule {} can not translte to PiTableEntry {}", r.toString(), e.getMessage());
147 }
148 return piTableEntry;
149 }).collect(Collectors.toList());
150
151 Collection<FlowRule> installedEntries = Collections.emptyList();
152 client.writeTableEntries(piTableEntries, opType, pipeconf).whenComplete((r, e) -> {
153 if (r) {
154
155 Collection<Bmv2FlowRuleWrapper> bmv2FlowRuleWrappers = rules.stream()
156 .map(rule -> new Bmv2FlowRuleWrapper(rule, System.currentTimeMillis()))
157 .collect(Collectors.toList());
158
159 if (opType == WriteOperationType.INSERT) {
160 INSTALLED_FLOWENTRIES.put(deviceId, bmv2FlowRuleWrappers);
161 } else if (opType == WriteOperationType.MODIFY) {
162 rules.stream().forEach(rule -> INSTALLED_FLOWENTRIES
163 .get(deviceId).removeIf(x -> x.rule().id() == rule.id()));
164 INSTALLED_FLOWENTRIES.put(deviceId, bmv2FlowRuleWrappers);
165 } else if (opType == WriteOperationType.DELETE) {
166 INSTALLED_FLOWENTRIES.get(deviceId).removeAll(bmv2FlowRuleWrappers);
167 }
168 installedEntries.addAll(rules);
169 }
170 });
171
172 return installedEntries;
173 }
174}