blob: c08c5a7a698d8d4454456dc8f7cb4802f7203ebf [file] [log] [blame]
/*
* Copyright 2017-present Open Networking Laboratory
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.onosproject.drivers.bmv2;
import com.google.common.collect.Maps;
import org.onosproject.net.Device;
import org.onosproject.net.DeviceId;
import org.onosproject.net.device.DeviceService;
import org.onosproject.net.driver.AbstractHandlerBehaviour;
import org.onosproject.net.flow.DefaultFlowEntry;
import org.onosproject.net.flow.FlowEntry;
import org.onosproject.net.flow.FlowRule;
import org.onosproject.net.flow.FlowRuleProgrammable;
import org.onosproject.net.pi.model.PiPipeconf;
import org.onosproject.net.pi.model.PiPipelineInterpreter;
import org.onosproject.net.pi.runtime.PiFlowRuleTranslationService;
import org.onosproject.net.pi.runtime.PiPipeconfService;
import org.onosproject.net.pi.runtime.PiTableEntry;
import org.onosproject.p4runtime.api.P4RuntimeClient;
import org.onosproject.p4runtime.api.P4RuntimeClient.WriteOperationType;
import org.onosproject.p4runtime.api.P4RuntimeController;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.util.Collection;
import java.util.Collections;
import java.util.Map;
import java.util.stream.Collectors;
/**
* Implementation of the flow rule programmable behaviour for BMv2.
*/
public class Bmv2FlowRuleProgrammable extends AbstractHandlerBehaviour implements FlowRuleProgrammable {
private final Logger log = LoggerFactory.getLogger(getClass());
private static final Map<DeviceId, Collection<Bmv2FlowRuleWrapper>> INSTALLED_FLOWENTRIES = Maps.newHashMap();
P4RuntimeClient client;
PiPipeconf pipeconf;
PiPipelineInterpreter interpreter;
DeviceId deviceId;
PiFlowRuleTranslationService piFlowRuleTranslationService;
private boolean init() {
deviceId = handler().data().deviceId();
P4RuntimeController controller = handler().get(P4RuntimeController.class);
if (!controller.hasClient(deviceId)) {
log.warn("Unable to find client for {}, aborting flow rule operation", deviceId);
return false;
}
client = controller.getClient(deviceId);
piFlowRuleTranslationService = handler().get(PiFlowRuleTranslationService.class);
PiPipeconfService piPipeconfService = handler().get(PiPipeconfService.class);
if (piPipeconfService.ofDevice(deviceId).isPresent() &&
piPipeconfService.getPipeconf(piPipeconfService.ofDevice(deviceId).get()).isPresent()) {
pipeconf = piPipeconfService.getPipeconf(piPipeconfService.ofDevice(deviceId).get()).get();
} else {
log.warn("Unable to get the pipeconf of {}", deviceId);
return false;
}
DeviceService deviceService = handler().get(DeviceService.class);
Device device = deviceService.getDevice(deviceId);
interpreter = device.is(PiPipelineInterpreter.class) ? device.as(PiPipelineInterpreter.class) : null;
if (device.is(PiPipelineInterpreter.class)) {
log.warn("Device {} unable to instantiate interpreter of pipeconf {}", deviceId, pipeconf.id());
return false;
}
return true;
}
@Override
public Collection<FlowEntry> getFlowEntries() {
if (!init()) {
return Collections.emptyList();
}
//TD DO: retrieve statistics.
long packets = 0;
long bytes = 0;
Collection<FlowEntry> flowEntries = INSTALLED_FLOWENTRIES
.get(deviceId).stream().map(wrapper -> new DefaultFlowEntry(wrapper.rule(),
FlowEntry.FlowEntryState.ADDED,
wrapper.lifeInSeconds(),
packets,
bytes))
.collect(Collectors.toList());
return Collections.unmodifiableCollection(flowEntries);
}
@Override
public Collection<FlowRule> applyFlowRules(Collection<FlowRule> rules) {
Collection<FlowRule> insertFlowRules = rules.stream()
.filter(r -> INSTALLED_FLOWENTRIES.get(deviceId) == null ||
INSTALLED_FLOWENTRIES.get(deviceId).stream().noneMatch(x -> x.rule().id() == r.id()))
.collect(Collectors.toList());
Collection<FlowRule> updateFlowRules = rules.stream()
.filter(r -> INSTALLED_FLOWENTRIES.get(deviceId) != null &&
INSTALLED_FLOWENTRIES.get(deviceId).stream().anyMatch(x -> x.rule().id() == r.id()))
.collect(Collectors.toList());
Collection<FlowRule> flowRules = processFlowRules(insertFlowRules, WriteOperationType.INSERT);
flowRules.addAll(processFlowRules(updateFlowRules, WriteOperationType.MODIFY));
return flowRules;
}
@Override
public Collection<FlowRule> removeFlowRules(Collection<FlowRule> rules) {
return processFlowRules(rules, WriteOperationType.DELETE);
}
private Collection<FlowRule> processFlowRules(Collection<FlowRule> rules, WriteOperationType opType) {
if (!init()) {
return Collections.emptyList();
}
Collection<PiTableEntry> piTableEntries = rules.stream().map(r -> {
PiTableEntry piTableEntry = null;
try {
piTableEntry = piFlowRuleTranslationService.translate(r, pipeconf);
} catch (PiFlowRuleTranslationService.PiFlowRuleTranslationException e) {
log.error("Flow rule {} can not translte to PiTableEntry {}", r.toString(), e.getMessage());
}
return piTableEntry;
}).collect(Collectors.toList());
Collection<FlowRule> installedEntries = Collections.emptyList();
client.writeTableEntries(piTableEntries, opType, pipeconf).whenComplete((r, e) -> {
if (r) {
Collection<Bmv2FlowRuleWrapper> bmv2FlowRuleWrappers = rules.stream()
.map(rule -> new Bmv2FlowRuleWrapper(rule, System.currentTimeMillis()))
.collect(Collectors.toList());
if (opType == WriteOperationType.INSERT) {
INSTALLED_FLOWENTRIES.put(deviceId, bmv2FlowRuleWrappers);
} else if (opType == WriteOperationType.MODIFY) {
rules.stream().forEach(rule -> INSTALLED_FLOWENTRIES
.get(deviceId).removeIf(x -> x.rule().id() == rule.id()));
INSTALLED_FLOWENTRIES.put(deviceId, bmv2FlowRuleWrappers);
} else if (opType == WriteOperationType.DELETE) {
INSTALLED_FLOWENTRIES.get(deviceId).removeAll(bmv2FlowRuleWrappers);
}
installedEntries.addAll(rules);
}
});
return installedEntries;
}
}