Carmelo Cascone
Committed by Jonathan Hart

Removed hardcoded model from BMv2 driver

Now it uses the model stored in device annotations. Also refactored flow
rule translator classes to reflect this change.

Change-Id: I46541bcc2ab5a267eef4becb6250b9a99684056a
......@@ -16,10 +16,15 @@
package org.onosproject.drivers.bmv2;
import com.eclipsesource.json.Json;
import com.google.common.cache.CacheBuilder;
import com.google.common.cache.CacheLoader;
import com.google.common.cache.LoadingCache;
import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
import org.apache.commons.lang3.tuple.Pair;
import org.apache.commons.lang3.tuple.Triple;
import org.onosproject.bmv2.api.model.Bmv2Model;
import org.onosproject.bmv2.api.runtime.Bmv2Client;
import org.onosproject.bmv2.api.runtime.Bmv2MatchKey;
import org.onosproject.bmv2.api.runtime.Bmv2RuntimeException;
......@@ -28,7 +33,10 @@ import org.onosproject.bmv2.ctl.Bmv2ThriftClient;
import org.onosproject.drivers.bmv2.translators.Bmv2DefaultFlowRuleTranslator;
import org.onosproject.drivers.bmv2.translators.Bmv2FlowRuleTranslator;
import org.onosproject.drivers.bmv2.translators.Bmv2FlowRuleTranslatorException;
import org.onosproject.drivers.bmv2.translators.Bmv2SimpleTranslatorConfig;
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;
......@@ -41,6 +49,8 @@ import java.util.Collection;
import java.util.Collections;
import java.util.List;
import java.util.concurrent.ConcurrentMap;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.TimeUnit;
/**
* Flow rule programmable device behaviour implementation for BMv2.
......@@ -50,10 +60,21 @@ public class Bmv2FlowRuleProgrammable extends AbstractHandlerBehaviour
private static final Logger LOG =
LoggerFactory.getLogger(Bmv2FlowRuleProgrammable.class);
// There's no Bmv2 client method to poll flow entries from the device device. gitNeed a local store.
// There's no Bmv2 client method to poll flow entries from the device device. Need a local store.
private static final ConcurrentMap<Triple<DeviceId, String, Bmv2MatchKey>, Pair<Long, FlowEntry>>
ENTRIES_MAP = Maps.newConcurrentMap();
private static final Bmv2FlowRuleTranslator TRANSLATOR = new Bmv2DefaultFlowRuleTranslator();
// Cache model objects instead of parsing the JSON each time.
private static final LoadingCache<String, Bmv2Model> MODEL_CACHE = CacheBuilder.newBuilder()
.expireAfterAccess(60, TimeUnit.SECONDS)
.build(new CacheLoader<String, Bmv2Model>() {
@Override
public Bmv2Model load(String jsonString) throws Exception {
// Expensive call.
return Bmv2Model.parse(Json.parse(jsonString).asObject());
}
});
@Override
public Collection<FlowEntry> getFlowEntries() {
......@@ -96,6 +117,8 @@ public class Bmv2FlowRuleProgrammable extends AbstractHandlerBehaviour
return Collections.emptyList();
}
Bmv2FlowRuleTranslator translator = getTranslator(deviceId);
List<FlowRule> processedFlowRules = Lists.newArrayList();
for (FlowRule rule : rules) {
......@@ -103,7 +126,7 @@ public class Bmv2FlowRuleProgrammable extends AbstractHandlerBehaviour
Bmv2TableEntry bmv2Entry;
try {
bmv2Entry = TRANSLATOR.translate(rule);
bmv2Entry = translator.translate(rule);
} catch (Bmv2FlowRuleTranslatorException e) {
LOG.error("Unable to translate flow rule: {}", e.getMessage());
continue;
......@@ -159,6 +182,46 @@ public class Bmv2FlowRuleProgrammable extends AbstractHandlerBehaviour
return processedFlowRules;
}
/**
* Gets the appropriate flow rule translator based on the device running configuration.
*
* @param deviceId a device id
* @return a flow rule translator
*/
private Bmv2FlowRuleTranslator getTranslator(DeviceId deviceId) {
DeviceService deviceService = handler().get(DeviceService.class);
if (deviceService == null) {
LOG.error("Unable to get device service");
return null;
}
Device device = deviceService.getDevice(deviceId);
if (device == null) {
LOG.error("Unable to get device {}", deviceId);
return null;
}
String jsonString = device.annotations().value("bmv2JsonConfigValue");
if (jsonString == null) {
LOG.error("Unable to read bmv2 JSON config from device {}", deviceId);
return null;
}
Bmv2Model model;
try {
model = MODEL_CACHE.get(jsonString);
} catch (ExecutionException e) {
LOG.error("Unable to parse bmv2 JSON config for device {}:", deviceId, e.getCause());
return null;
}
// TODO: get translator config dynamically.
// Now it's hardcoded, selection should be based on the device bmv2 model.
Bmv2FlowRuleTranslator.TranslatorConfig translatorConfig = new Bmv2SimpleTranslatorConfig(model);
return new Bmv2DefaultFlowRuleTranslator(translatorConfig);
}
private enum Operation {
APPLY, REMOVE
}
......
......@@ -18,7 +18,6 @@ package org.onosproject.drivers.bmv2.translators;
import com.google.common.annotations.Beta;
import org.onlab.util.ImmutableByteSequence;
import org.onosproject.bmv2.api.model.Bmv2Model;
import org.onosproject.bmv2.api.model.Bmv2ModelField;
import org.onosproject.bmv2.api.model.Bmv2ModelTable;
import org.onosproject.bmv2.api.model.Bmv2ModelTableKey;
......@@ -66,9 +65,11 @@ import org.onosproject.net.flow.instructions.Instructions.ExtensionInstructionWr
@Beta
public class Bmv2DefaultFlowRuleTranslator implements Bmv2FlowRuleTranslator {
// TODO: config is harcoded now, instead it should be selected based on device model
private final TranslatorConfig config = new Bmv2SimpleTranslatorConfig();
private final Bmv2Model model = config.model();
private final TranslatorConfig config;
public Bmv2DefaultFlowRuleTranslator(TranslatorConfig config) {
this.config = config;
}
private static Bmv2TernaryMatchParam buildTernaryParam(Bmv2ModelField field, Criterion criterion, int byteWidth)
throws Bmv2FlowRuleTranslatorException {
......@@ -201,7 +202,7 @@ public class Bmv2DefaultFlowRuleTranslator implements Bmv2FlowRuleTranslator {
int tableId = rule.tableId();
Bmv2ModelTable table = model.table(tableId);
Bmv2ModelTable table = config.model().table(tableId);
if (table == null) {
throw new Bmv2FlowRuleTranslatorException("Unknown table ID: " + tableId);
......
/*
* Copyright 2016-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.translators;
import com.google.common.annotations.Beta;
import org.onosproject.bmv2.api.model.Bmv2Model;
import org.onosproject.net.flow.criteria.Criterion;
import java.util.Map;
import static org.onosproject.drivers.bmv2.translators.Bmv2FlowRuleTranslator.TranslatorConfig;
/**
* Default implementation of a BMv2 flow rule translator configuration.
*/
@Beta
public abstract class Bmv2DefaultTranslatorConfig implements TranslatorConfig {
private final Bmv2Model model;
private final Map<String, Criterion.Type> fieldMap;
/**
* Creates a new translator configuration.
*
* @param model a BMv2 packet processing model
* @param fieldMap a field-to-criterion type map
*/
protected Bmv2DefaultTranslatorConfig(Bmv2Model model, Map<String, Criterion.Type> fieldMap) {
this.model = model;
this.fieldMap = fieldMap;
}
@Override
public Bmv2Model model() {
return this.model;
}
@Override
public Map<String, Criterion.Type> fieldToCriterionTypeMap() {
return this.fieldMap;
}
}
......@@ -16,10 +16,8 @@
package org.onosproject.drivers.bmv2.translators;
import com.eclipsesource.json.Json;
import com.eclipsesource.json.JsonObject;
import com.google.common.annotations.Beta;
import com.google.common.collect.Maps;
import com.google.common.collect.ImmutableMap;
import org.onlab.util.ImmutableByteSequence;
import org.onosproject.bmv2.api.model.Bmv2Model;
import org.onosproject.bmv2.api.runtime.Bmv2Action;
......@@ -29,10 +27,6 @@ import org.onosproject.net.flow.criteria.Criterion;
import org.onosproject.net.flow.instructions.Instruction;
import org.onosproject.net.flow.instructions.Instructions;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.util.Map;
/**
......@@ -40,24 +34,21 @@ import java.util.Map;
* simple.p4 model.
*/
@Beta
public class Bmv2SimpleTranslatorConfig implements Bmv2FlowRuleTranslator.TranslatorConfig {
public class Bmv2SimpleTranslatorConfig extends Bmv2DefaultTranslatorConfig {
private static final String JSON_CONFIG_PATH = "/simple.json";
private final Map<String, Criterion.Type> fieldMap = Maps.newHashMap();
private final Bmv2Model model;
// Lazily populate field map.
private static final Map<String, Criterion.Type> FIELD_MAP = ImmutableMap.of(
"standard_metadata.ingress_port", Criterion.Type.IN_PORT,
"ethernet.dstAddr", Criterion.Type.ETH_DST,
"ethernet.srcAddr", Criterion.Type.ETH_SRC,
"ethernet.etherType", Criterion.Type.ETH_TYPE);
/**
* Creates a new simple pipeline translator configuration.
*/
public Bmv2SimpleTranslatorConfig() {
this.model = getModel();
// populate fieldMap
fieldMap.put("standard_metadata.ingress_port", Criterion.Type.IN_PORT);
fieldMap.put("ethernet.dstAddr", Criterion.Type.ETH_DST);
fieldMap.put("ethernet.srcAddr", Criterion.Type.ETH_SRC);
fieldMap.put("ethernet.etherType", Criterion.Type.ETH_TYPE);
public Bmv2SimpleTranslatorConfig(Bmv2Model model) {
// Populate fieldMap.
super(model, FIELD_MAP);
}
private static Bmv2Action buildDropAction() {
......@@ -94,41 +85,16 @@ public class Bmv2SimpleTranslatorConfig implements Bmv2FlowRuleTranslator.Transl
return actionBuilder.build();
}
private static Bmv2Model getModel() {
InputStream inputStream = Bmv2SimpleTranslatorConfig.class
.getResourceAsStream(JSON_CONFIG_PATH);
InputStreamReader reader = new InputStreamReader(inputStream);
BufferedReader bufReader = new BufferedReader(reader);
JsonObject json = null;
try {
json = Json.parse(bufReader).asObject();
} catch (IOException e) {
throw new RuntimeException("Unable to parse JSON file: " + e.getMessage());
}
return Bmv2Model.parse(json);
}
@Override
public Bmv2Model model() {
return this.model;
}
@Override
public Map<String, Criterion.Type> fieldToCriterionTypeMap() {
return fieldMap;
}
@Override
public Bmv2Action buildAction(TrafficTreatment treatment)
throws Bmv2FlowRuleTranslatorException {
if (treatment.allInstructions().size() == 0) {
// no instructions means drop
// No instructions means drop.
return buildDropAction();
} else if (treatment.allInstructions().size() > 1) {
// otherwise, we understand treatments with only 1 instruction
// Otherwise, we understand treatments with only 1 instruction.
throw new Bmv2FlowRuleTranslatorException(
"Treatment not supported, more than 1 instructions found: "
+ treatment.toString());
......
......@@ -16,7 +16,10 @@
package org.onosproject.drivers.bmv2;
import com.eclipsesource.json.Json;
import com.eclipsesource.json.JsonObject;
import com.google.common.testing.EqualsTester;
import org.junit.Before;
import org.junit.Test;
import org.onlab.packet.MacAddress;
import org.onosproject.bmv2.api.model.Bmv2Model;
......@@ -26,6 +29,7 @@ import org.onosproject.core.ApplicationId;
import org.onosproject.core.DefaultApplicationId;
import org.onosproject.drivers.bmv2.translators.Bmv2DefaultFlowRuleTranslator;
import org.onosproject.drivers.bmv2.translators.Bmv2FlowRuleTranslator;
import org.onosproject.drivers.bmv2.translators.Bmv2SimpleTranslatorConfig;
import org.onosproject.net.DeviceId;
import org.onosproject.net.PortNumber;
import org.onosproject.net.flow.DefaultFlowRule;
......@@ -35,6 +39,10 @@ import org.onosproject.net.flow.FlowRule;
import org.onosproject.net.flow.TrafficSelector;
import org.onosproject.net.flow.TrafficTreatment;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.util.Random;
import static org.hamcrest.CoreMatchers.equalTo;
......@@ -46,9 +54,30 @@ import static org.hamcrest.MatcherAssert.assertThat;
*/
public class Bmv2DefaultFlowRuleTranslatorTest {
private static final String JSON_CONFIG_PATH = "/simple.json";
private Random random = new Random();
private Bmv2FlowRuleTranslator translator = new Bmv2DefaultFlowRuleTranslator();
private Bmv2Model model = translator.config().model();
private Bmv2Model model;
private Bmv2FlowRuleTranslator.TranslatorConfig config;
private Bmv2FlowRuleTranslator translator;
@Before
public void setUp() throws Exception {
InputStream inputStream = Bmv2SimpleTranslatorConfig.class
.getResourceAsStream(JSON_CONFIG_PATH);
InputStreamReader reader = new InputStreamReader(inputStream);
BufferedReader bufReader = new BufferedReader(reader);
JsonObject json = null;
try {
json = Json.parse(bufReader).asObject();
} catch (IOException e) {
throw new RuntimeException("Unable to parse JSON file: " + e.getMessage());
}
this.model = Bmv2Model.parse(json);
this.config = new Bmv2SimpleTranslatorConfig(model);
this.translator = new Bmv2DefaultFlowRuleTranslator(config);
}
@Test
......
......@@ -240,7 +240,7 @@
]
},
{
"name": "send_to_cpu",
"name": "push_to_cp",
"id": 3,
"runtime_data": [],
"primitives": [
......@@ -256,7 +256,7 @@
},
{
"type": "hexstr",
"value": "0xff"
"value": "0x200"
}
]
}
......@@ -315,13 +315,13 @@
"actions": [
"fwd",
"flood",
"send_to_cpu",
"push_to_cp",
"_drop"
],
"next_tables": {
"fwd": null,
"flood": null,
"send_to_cpu": null,
"push_to_cp": null,
"_drop": null
},
"default_action": null,
......