MINI Sh3ll
"""Defines decoders for OpenFlow actions.
"""
from ovs.flow.decoders import (
decode_default,
decode_time,
decode_flag,
decode_int,
)
from ovs.flow.kv import (
nested_kv_decoder,
KVDecoders,
KeyValue,
KVParser,
ParseError,
)
from ovs.flow.list import nested_list_decoder, ListDecoders
from ovs.flow.ofp_fields import field_decoders, field_aliases
def decode_output(value):
"""Decodes the output value.
Does not support field specification.
"""
if len(value.split(",")) > 1:
return nested_kv_decoder(
KVDecoders({"port": decode_default, "max_len": decode_int})
)(value)
try:
return {"port": int(value)}
except ValueError:
return {"port": value.strip('"')}
def decode_controller(value):
"""Decodes the controller action."""
if not value:
return KeyValue("output", {"port": "CONTROLLER"})
else:
# Try controller:max_len
try:
max_len = int(value)
return {
"max_len": max_len,
}
except ValueError:
pass
# controller(key[=val], ...)
return nested_kv_decoder(
KVDecoders(
{
"max_len": decode_int,
"reason": decode_default,
"id": decode_int,
"userdata": decode_default,
"pause": decode_flag,
"meter_id": decode_int,
}
)
)(value)
def decode_bundle_load(value):
return decode_bundle(value, True)
def decode_bundle(value, load=False):
"""Decode bundle action."""
result = {}
keys = ["fields", "basis", "algorithm", "ofport"]
if load:
keys.append("dst")
for key in keys:
parts = value.partition(",")
nvalue = parts[0]
value = parts[2]
if key == "ofport":
continue
result[key] = decode_default(nvalue)
# Handle members:
mvalues = value.split("members:")
result["members"] = [int(port) for port in mvalues[1].split(",")]
return result
def decode_encap(value):
"""Decodes encap action. Examples:
encap(ethernet)
encap(nsh(md_type=2,tlv(0x1000,10,0x12345678)))
The generated dict has the following keys: "header", "props", e.g:
{
"header": "ethernet",
}
{
"header": "nsh",
"props": {
"md_type": 2,
"tlv": {
"class": 0x100,
"type": 10,
"value": 0x123456
}
}
}
"""
def free_hdr_decoder(free_val):
if free_val not in ["ethernet", "mpls", "mpls_mc", "nsh"]:
raise ValueError(
"Malformed encap action. Unkown header: {}".format(free_val)
)
return "header", free_val
parser = KVParser(
value,
KVDecoders(
{
"nsh": nested_kv_decoder(
KVDecoders(
{
"md_type": decode_default,
"tlv": nested_list_decoder(
ListDecoders(
[
("class", decode_int),
("type", decode_int),
("value", decode_int),
]
)
),
}
)
),
},
default_free=free_hdr_decoder,
),
)
parser.parse()
if len(parser.kv()) > 1:
raise ValueError("Malformed encap action: {}".format(value))
result = {}
if parser.kv()[0].key == "header":
result["header"] = parser.kv()[0].value
elif parser.kv()[0].key == "nsh":
result["header"] = "nsh"
result["props"] = parser.kv()[0].value
return result
def decode_field(value):
"""Decodes a field as defined in the 'Field Specification' of the actions
man page:
http://www.openvswitch.org/support/dist-docs/ovs-actions.7.txt."""
parts = value.strip("]\n\r").split("[")
if (
parts[0] not in field_decoders.keys()
and parts[0] not in field_aliases.keys()
):
raise ParseError("Field not supported: {}".format(parts[0]))
result = {
"field": parts[0],
}
if len(parts) > 1 and parts[1]:
field_range = parts[1].split("..")
start = field_range[0]
end = field_range[1] if len(field_range) > 1 else start
if start:
result["start"] = int(start)
if end:
result["end"] = int(end)
return result
def decode_load_field(value):
"""Decodes LOAD actions such as: 'load:value->dst'."""
parts = value.split("->")
if len(parts) != 2:
raise ValueError("Malformed load action : %s" % value)
# If the load action is performed within a learn() action,
# The value can be specified as another field.
try:
return {"value": int(parts[0], 0), "dst": decode_field(parts[1])}
except ValueError:
return {"src": decode_field(parts[0]), "dst": decode_field(parts[1])}
def decode_set_field(field_decoders, value):
"""Decodes SET_FIELD actions such as: 'set_field:value/mask->dst'.
The value is decoded by field_decoders which is a KVDecoders instance.
Args:
field_decoders(KVDecoders): The KVDecoders to be used to decode the
field.
"""
parts = value.split("->")
if len(parts) != 2:
raise ValueError("Malformed set_field action : %s" % value)
val = parts[0]
dst = parts[1]
val_result = field_decoders.decode(dst, val)
return {
"value": {val_result[0]: val_result[1]},
"dst": decode_field(dst),
}
def decode_move_field(value):
"""Decodes MOVE actions such as 'move:src->dst'."""
parts = value.split("->")
if len(parts) != 2:
raise ValueError("Malformed move action : %s" % value)
return {
"src": decode_field(parts[0]),
"dst": decode_field(parts[1]),
}
def decode_dec_ttl(value):
"""Decodes dec_ttl and dec_ttl(id, id[2], ...) actions."""
if not value:
return True
return [int(idx) for idx in value.split(",")]
def decode_chk_pkt_larger(value):
"""Decodes 'check_pkt_larger(pkt_len)->dst' actions."""
parts = value.split("->")
if len(parts) != 2:
raise ValueError("Malformed check_pkt_larger action : %s" % value)
pkt_len = int(parts[0].strip("()"))
dst = decode_field(parts[1])
return {"pkt_len": pkt_len, "dst": dst}
# CT decoders
def decode_zone(value):
"""Decodes the value of the 'zone' keyword (part of the ct action)."""
try:
return int(value, 0)
except ValueError:
pass
return decode_field(value)
def decode_learn(action_decoders):
"""Create the decoder to be used to decode the 'learn' action.
The learn action has two added complexities:
1) It can hold any valid action key-value. Therefore we must take
the precalculated action_decoders and use them. That's why we require
them as argument.
2) The way fields can be specified is augmented. Not only we have
'field=value', but we also have:
- 'field=_src_' (where _src_ is another field name)
- and just 'field'
For this we need to create a wrapper of field_decoders that, for each
"field=X" key-value we check if X is a field_name or if it's actually
a value that we need to send to the appropriate field_decoder to
process.
Args:
action_decoders (dict): Dictionary of decoders to be used in nested
action decoding.
"""
def learn_field_decoding_kv(key, value):
"""Decodes a key, value pair from the learn action.
The key must be a decodable field. The value can be either a value
in the format defined for the field or another field.
"""
key_field = decode_field(key)
try:
return key, decode_field(value)
except ParseError:
return key, field_decoders.get(key_field.get("field"))(value)
def learn_field_decoding_free(key):
"""Decodes the free fields found in the learn action.
Free fields indicate that the filed is to be copied from the original.
In order to express that in a dictionary, return the fieldspec as
value. So, the free fild NXM_OF_IP_SRC[], is encoded as:
"NXM_OF_IP_SRC[]": {
"field": "NXM_OF_IP_SRC"
}
That way we also ensure the actual free key is correct.
"""
key_field = decode_field(key)
return key, key_field
learn_decoders = {
**action_decoders,
"idle_timeout": decode_time,
"hard_timeout": decode_time,
"fin_idle_timeout": decode_time,
"fin_hard_timeout": decode_time,
"priority": decode_int,
"cookie": decode_int,
"send_flow_rem": decode_flag,
"table": decode_int,
"delete_learned": decode_flag,
"limit": decode_int,
"result_dst": decode_field,
}
learn_decoder = KVDecoders(
learn_decoders,
default=learn_field_decoding_kv,
default_free=learn_field_decoding_free,
)
return nested_kv_decoder(learn_decoder, is_list=True)
OHA YOOOO