Commit 3221010e authored by David Hancock's avatar David Hancock

Reworked P4Command abstraction supporting the three command types

Previous abstraction had a built in assumption that it was always
a table_add, neglecting differences with table_modify and
table_delete.

A bit of a hack but now a P4Command simply has an 'attributes'
attribute, a dictionary with the attribute name as the key,
and strings (table, action), lists (match params, action params),
or ints (handles) as values as appropriate.

The 'command_type' attribute remains to differentiate handling.
parent e1572802
......@@ -20,6 +20,9 @@ class ModRuleError(Exception):
class DeleteRuleError(Exception):
pass
# TODO: eliminate, probably
NO_HANDLE_YET = -1
class Device():
def __init__(self, rta, max_entries, phys_ports):
self.rta = rta
......@@ -31,6 +34,7 @@ class Device():
self.phys_ports_remaining = list(phys_ports)
def send_command(self, cmd_str_rep):
# return handle (regardless of command type)
pass
def do_table_delete(self, rule_identifier):
......@@ -59,20 +63,39 @@ class Bmv2_SSwitch(Device):
@staticmethod
def command_to_string(cmd):
command_str = cmd.command_type
command_str += ' ' + cmd.rule.table
command_str += ' ' + cmd.rule.action
command_str += ' ' + ' '.join(cmd.rule.mparams)
command_str += ' => ' + ' '.join(cmd.rule.aparams)
command_str += ' ' + cmd.attributes['table']
if command_type == 'table_add':
command_str += ' ' + cmd.attributes['action']
command_str += ' '.join(cmd.attributes['mparams'])
command_str += ' => ' + join(cmd.attributes['aparams'])
elif command_type == 'table_modify':
command_str += ' ' + cmd.attributes['action']
command_str += ' ' + str(cmd.attributes['handle'])
command_str += ' '.join(cmd.attributes['aparams'])
elif command_type == 'table_delete':
command_str += ' ' + str(cmd.attributes['handle'])
return command_str
@staticmethod
def string_to_command(string):
command_str = re.split('\s*=>\s*', string)
command_type = command_str[0].split()[0]
table = command_str[0].split()[1]
action = command_str[0].split()[2]
mparams = command_str[0].split()[3:]
aparams = command_str[1].split()
return P4Command(command_type, Rule(table, action, mparams, aparams))
attributes = {}
attributes['table'] = command_str[0].split()[1]
if command_type == 'table_add':
# table_add <table name> <action name> <match fields> => <action parameters> [priority]
attributes['action'] = command_str[0].split()[2]
attributes['mparams'] = command_str[0].split()[3:]
attributes['aparams'] = command_str[1].split()
elif command_type == 'table_modify':
# table_modify <table name> <action name> <entry handle> [action parameters]
attributes['action'] = command_str[0].split()[2]
attributes['handle'] = int(command_str[0].split()[3])
attributes['aparams'] = command_str[0].split()[4:]
elif command_type == 'table_delete':
# table_delete <table name> <entry handle>
attributes['handle'] = int(command_str[0].split()[3])
return P4Command(command_type, attributes)
class Agilio(Device):
@staticmethod
......
......@@ -10,7 +10,7 @@ import device
import virtualdevice
from hp4loader import HP4Loader
from composition import Chain
from hp4translator import VDevCommand_to_HP4Command
from hp4translator import Translator
import code
......@@ -36,20 +36,20 @@ class Lease():
def withdraw_vdev(self, vdev_name):
"Remove virtual device from Lease (does not destroy virtual device)"
num_entries = len(self.vdevs[vdev_name].table_rules_handles)
num_entries += len(self.vdevs[vdev_name].code_handles)
num_entries = len(self.vdevs[vdev_name].table_rules)
num_entries += len(self.vdevs[vdev_name].code)
# pull data plane-related rules from device
for handle in self.vdevs[vdev_name].table_rules_handles.keys():
table = self.vdevs[vdev_name].table_rules_handles[handle].table
for handle in self.vdevs[vdev_name].table_rules.keys():
table = self.vdevs[vdev_name].table_rules[handle].table
rule_identifier = table + ' ' + str(handle)
self.device.do_table_delete(rule_identifier)
del self.vdevs[vdev_name].table_rules_handles[handle]
del self.vdevs[vdev_name].table_rules[handle]
# pull code-related rules from device
for handle in self.vdevs[vdev_name].code_handles.keys():
table = self.vdevs[vdev_name].code_handles[handle].rule.table
for handle in self.vdevs[vdev_name].code.keys():
table = self.vdevs[vdev_name].code[handle].rule.table
rule_identifier = table + ' ' + str(handle)
self.device.do_table_delete(rule_identifier)
del self.vdevs[vdev_name].code_handles[handle]
del self.vdevs[vdev_name].code[handle]
self.entry_usage -= num_entries
# if applicable, remove vdev from composition
if vdev_name in self.composition.vdevs:
......@@ -243,6 +243,9 @@ class Controller(object):
return 'Virtual device ' + vdev_name + ' withdrawn from ' + dev_name
def translate(self, parameters):
# TODO: Fix the native -> hp4 rule handle confusion. Need to track
# virtual (native) rule handles to support table_delete and table_modify
# commands.
# parameters:
# <slice name> <virtual device> <style: 'bmv2' | 'agilio'> <command>
hp4slice = parameters[0]
......@@ -255,9 +258,52 @@ class Controller(object):
p4command = Agilio.string_to_command(vdev_command_str)
else:
return 'Error - ' + style + ' not one of (\'bmv2\', \'agilio\')'
if hp4slice not in self.slices:
return 'Error - ' + hp4slice + ' not a valid slice'
if vdev_name not in self.slices[hp4slice].vdevs:
return 'Error - ' + vdev_name + ' not a virtual device in ' + hp4slice
vdev = self.slices[hp4slice].vdevs[vdev_name]
hp4commands = Translator.translate(vdev, p4command)
# accounting
entries_available = (self.slices[hp4slice].leases[vdev.dev_name].entry_limit
- self.slices[hp4slice].leases[vdev.dev_name].entry_usage)
diff = 0
for hp4command in hp4commands:
if hp4command.command_type == 'table_add':
diff += 1
elif hp4command.command_type == 'table_delete':
diff -= 1
if diff > entries_available:
return 'Error - entries net increase(' + str(diff) \
+ ') exceeds availability(' + str(entries_available) + ')'
# if p4command.command_type == 'table_add', we will have a new
# Origin_to_HP4Map, including new hp4_rule_handles list, to add to
# vdev.origin_table_rules
# if p4command.command_type == 'table_modify', we need to replace
# the existing hp4_rule_handles list with a new one
# if p4command.command_type == 'table_delete', we need to del
# the vdev.origin_table_rules entry
origin_rule_handles = []
for hp4command in hp4commands:
# return value should be handle for all commands
handle = self.slices[hp4slice].leases[vdev.dev_name].send_command(hp4command)
if hp4command.command_type == 'table_add' or hp4command.command_type == 'table_modify':
vdev.table_rule_handles[handle] = hp4command.rule
else: # command_type == 'table_delete'
del vdev.table_rule_handles[handle]
# accounting
if hp4command.command_type == 'table_add':
self.slices[hp4slice].leases[vdev.dev_name].entry_usage += 1
origin_rule_handles.append(handle)
elif hp4command.command_type == 'table_modify':
origin_rule_handles.append(handle)
elif hp4command.command_type == 'table_delete':
self.slices[hp4slice].leases[vdev.dev_name].entry_usage -= 1
return 'Not implemented yet'
return 'Translated: ' + vdev_command_str + ' for ' + vdev_name + ' on ' + vev.dev_name
def serverloop(self):
serversocket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
......
import virtualdevice
import p4command
from p4command import P4Command
class VDevCommand_to_HP4Command():
def translate(self, vdev, p4cmd):
class Translator():
@staticmethod
def translate(self, vdev, p4command):
pass
import p4rule
class P4Command():
def __init__(self, command_type, rule):
def __init__(self, command_type, attributes):
self.command_type = command_type
self.rule = rule
self.attributes = attributes # {<attrib_name>: <attribute}
from p4command import P4Command
from p4rule import P4Rule
class VirtualDevice():
def __init__(self, virtual_device_ID, code, guide):
self.virtual_device_ID = virtual_device_ID
self.code = code # [P4Command]
self.table_rules = [] # [P4Rule]
self.guide = guide
self.code_handles = {} # {handle (int): p4cmd (P4Command)}
self.table_rules_handles = {} # {handle (int): p4r (P4Rule)}
self.code = {} # {handle (int): p4cmd (P4Command)}
self.origin_table_rules = {} # {handle (int): map (Origin_to_HP4Map)}
self.hp4_table_rules = {} # {handle (int): p4r (P4Rule)}
self.dev_name = 'none'
class Origin_to_HP4Map():
def __init__(self, rule, handles):
self.origin_rule = rule
self.hp4_rule_handles = handles
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment