blob: 53a31b904b1acf22d989b21047ba68d1077e6a03 [file] [log] [blame]
/*
* 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.bmv2.api.context;
import com.eclipsesource.json.JsonArray;
import com.eclipsesource.json.JsonObject;
import com.google.common.base.MoreObjects;
import com.google.common.base.Objects;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
import com.google.common.collect.Sets;
import org.onosproject.bmv2.api.runtime.Bmv2MatchParam;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.SortedMap;
import static com.google.common.base.Preconditions.checkArgument;
/**
* Default implementation of a BMv2 configuration backed by a JSON object.
*/
public final class Bmv2DefaultConfiguration implements Bmv2Configuration {
private final JsonObject json;
private final DualKeySortedMap<Bmv2HeaderTypeModel> headerTypes = new DualKeySortedMap<>();
private final DualKeySortedMap<Bmv2HeaderModel> headers = new DualKeySortedMap<>();
private final DualKeySortedMap<Bmv2ActionModel> actions = new DualKeySortedMap<>();
private final DualKeySortedMap<Bmv2TableModel> tables = new DualKeySortedMap<>();
private Bmv2DefaultConfiguration(JsonObject json) {
this.json = JsonObject.unmodifiableObject(json);
}
/**
* Returns a new BMv2 configuration object by parsing the passed JSON.
*
* @param json json
* @return a new BMv2 configuration object
* @see <a href="https://github.com/p4lang/behavioral-configuration/blob/master/docs/JSON_format.md">
* BMv2 JSON specification</a>
*/
public static Bmv2DefaultConfiguration parse(JsonObject json) {
checkArgument(json != null, "json cannot be null");
// TODO: implement caching, no need to parse a json if we already have the configuration
Bmv2DefaultConfiguration configuration = new Bmv2DefaultConfiguration(json);
configuration.doParse();
return configuration;
}
@Override
public Bmv2HeaderTypeModel headerType(int id) {
return headerTypes.get(id);
}
@Override
public Bmv2HeaderTypeModel headerType(String name) {
return headerTypes.get(name);
}
@Override
public List<Bmv2HeaderTypeModel> headerTypes() {
return ImmutableList.copyOf(headerTypes.sortedMap().values());
}
@Override
public Bmv2HeaderModel header(int id) {
return headers.get(id);
}
@Override
public Bmv2HeaderModel header(String name) {
return headers.get(name);
}
@Override
public List<Bmv2HeaderModel> headers() {
return ImmutableList.copyOf(headers.sortedMap().values());
}
@Override
public Bmv2ActionModel action(int id) {
return actions.get(id);
}
@Override
public Bmv2ActionModel action(String name) {
return actions.get(name);
}
@Override
public List<Bmv2ActionModel> actions() {
return ImmutableList.copyOf(actions.sortedMap().values());
}
@Override
public Bmv2TableModel table(int id) {
return tables.get(id);
}
@Override
public Bmv2TableModel table(String name) {
return tables.get(name);
}
@Override
public List<Bmv2TableModel> tables() {
return ImmutableList.copyOf(tables.sortedMap().values());
}
@Override
public JsonObject json() {
return this.json;
}
/**
* Generates a hash code for this BMv2 configuration. The hash function is based solely on the JSON backing this
* configuration.
*/
@Override
public int hashCode() {
return json.hashCode();
}
/**
* Indicates whether some other BMv2 configuration is equal to this one.
* Equality is based solely on the low-level JSON representation.
*
* @param obj other object
* @return true if equals, false elsewhere
*/
@Override
public boolean equals(Object obj) {
if (this == obj) {
return true;
}
if (obj == null || getClass() != obj.getClass()) {
return false;
}
final Bmv2DefaultConfiguration other = (Bmv2DefaultConfiguration) obj;
return Objects.equal(this.json, other.json);
}
@Override
public String toString() {
return MoreObjects.toStringHelper(this)
.add("jsonHash", json.hashCode())
.toString();
}
/**
* Parse the JSON object and build the corresponding objects.
*/
private void doParse() {
// parse header types
json.get("header_types").asArray().forEach(val -> {
JsonObject jHeaderType = val.asObject();
// populate fields list
List<Bmv2FieldTypeModel> fieldTypes = Lists.newArrayList();
jHeaderType.get("fields").asArray().forEach(x -> fieldTypes.add(
new Bmv2FieldTypeModel(
x.asArray().get(0).asString(),
x.asArray().get(1).asInt())));
// add header type instance
String name = jHeaderType.get("name").asString();
int id = jHeaderType.get("id").asInt();
Bmv2HeaderTypeModel headerType = new Bmv2HeaderTypeModel(name,
id,
fieldTypes);
headerTypes.put(name, id, headerType);
});
// parse headers
json.get("headers").asArray().forEach(val -> {
JsonObject jHeader = val.asObject();
String name = jHeader.get("name").asString();
int id = jHeader.get("id").asInt();
String typeName = jHeader.get("header_type").asString();
Bmv2HeaderModel header = new Bmv2HeaderModel(name,
id,
headerTypes.get(typeName),
jHeader.get("metadata").asBoolean());
// add instance
headers.put(name, id, header);
});
// parse actions
json.get("actions").asArray().forEach(val -> {
JsonObject jAction = val.asObject();
// populate runtime data list
List<Bmv2RuntimeDataModel> runtimeDatas = Lists.newArrayList();
jAction.get("runtime_data").asArray().forEach(jData -> runtimeDatas.add(
new Bmv2RuntimeDataModel(
jData.asObject().get("name").asString(),
jData.asObject().get("bitwidth").asInt()
)));
// add action instance
String name = jAction.get("name").asString();
int id = jAction.get("id").asInt();
Bmv2ActionModel action = new Bmv2ActionModel(name,
id,
runtimeDatas);
actions.put(name, id, action);
});
// parse tables
json.get("pipelines").asArray().forEach(pipeline -> {
pipeline.asObject().get("tables").asArray().forEach(val -> {
JsonObject jTable = val.asObject();
// populate keys
List<Bmv2TableKeyModel> keys = Lists.newArrayList();
jTable.get("key").asArray().forEach(jKey -> {
JsonArray target = jKey.asObject().get("target").asArray();
Bmv2HeaderModel header = header(target.get(0).asString());
String typeName = target.get(1).asString();
Bmv2FieldModel field = new Bmv2FieldModel(
header, header.type().field(typeName));
String matchTypeStr = jKey.asObject().get("match_type").asString();
Bmv2MatchParam.Type matchType;
switch (matchTypeStr) {
case "ternary":
matchType = Bmv2MatchParam.Type.TERNARY;
break;
case "exact":
matchType = Bmv2MatchParam.Type.EXACT;
break;
case "lpm":
matchType = Bmv2MatchParam.Type.LPM;
break;
case "valid":
matchType = Bmv2MatchParam.Type.VALID;
break;
default:
throw new RuntimeException(
"Unable to parse match type: " + matchTypeStr);
}
keys.add(new Bmv2TableKeyModel(matchType, field));
});
// populate actions set
Set<Bmv2ActionModel> actionzz = Sets.newHashSet();
jTable.get("actions").asArray().forEach(
jAction -> actionzz.add(action(jAction.asString())));
// add table instance
String name = jTable.get("name").asString();
int id = jTable.get("id").asInt();
Bmv2TableModel table = new Bmv2TableModel(name,
id,
jTable.get("match_type").asString(),
jTable.get("type").asString(),
jTable.get("max_size").asInt(),
jTable.get("with_counters").asBoolean(),
jTable.get("support_timeout").asBoolean(),
keys,
actionzz);
tables.put(name, id, table);
});
});
}
/**
* Handy class for a map indexed by two keys, a string and an integer.
*
* @param <T> type of value stored by the map
*/
private class DualKeySortedMap<T> {
private final SortedMap<Integer, T> intMap = Maps.newTreeMap();
private final Map<String, Integer> strToIntMap = Maps.newHashMap();
private void put(String name, int id, T object) {
strToIntMap.put(name, id);
intMap.put(id, object);
}
private T get(int id) {
return intMap.get(id);
}
private T get(String name) {
return strToIntMap.get(name) == null ? null : get(strToIntMap.get(name));
}
private SortedMap<Integer, T> sortedMap() {
return intMap;
}
@Override
public int hashCode() {
return Objects.hashCode(intMap, strToIntMap);
}
@Override
public boolean equals(Object obj) {
if (this == obj) {
return true;
}
if (obj == null || getClass() != obj.getClass()) {
return false;
}
final DualKeySortedMap other = (DualKeySortedMap) obj;
return Objects.equal(this.intMap, other.intMap)
&& Objects.equal(this.strToIntMap, other.strToIntMap);
}
}
}