Commit 7ba6a5c9 authored by David Hancock's avatar David Hancock

Refactor

Tested: create_device, list_devices

Note, use bash scripts in parent ('controller' and 'client'),
which invoke python -m and make relative imports work.  Use
command line parameters as normal, '--help' works.
parent 260d2dfc
#!/bin/bash
python -m hp4controller.clients.client $@
#!/bin/bash
python -m hp4controller.controller $@
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.guide = guide
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'
self.next_handle = 0
def assign_handle(self):
handle = self.next_handle
self.next_handle += 1
return handle
class Origin_to_HP4Map():
def __init__(self, rule, handles):
self.origin_rule = rule
self.hp4_rule_handles = handles
import virtualdevice
from ..virtualdevice.virtualdevice import VirtualDevice
class Composition():
def __init__(self):
......
......@@ -4,15 +4,15 @@ import argparse
import sys
import runtime_CLI
import socket
import hp4loader
import os
import device
import virtualdevice
import p4rule
import devices.device as device
from virtualdevice.virtualdevice import VirtualDevice
from virtualdevice.interpret import Interpretation
from p4command import P4Command
#import p4rule
import textwrap
from hp4loader import HP4Loader
from composition import Chain
from hp4translator import Translator
from compositions.composition import Chain
#from interpret import Interpreter
import code
......@@ -125,7 +125,7 @@ class Controller(object):
max_entries = int(parameters[6])
ports = parameters[7:]
prelookup = {'None': 0, 'SimplePre': 1, 'SimplePreLAG': 2}
try:
hp4_client, mc_client = runtime_CLI.thrift_connect(ip, port,
runtime_CLI.RuntimeAPI.get_thrift_services(prelookup[pre]))
......@@ -137,12 +137,13 @@ class Controller(object):
rta = runtime_CLI.RuntimeAPI(pre, hp4_client)
if dev_type == 'bmv2_SSwitch':
self.devices[dev_name] = device.Bmv2_SSwitch(rta, max_entries, ports)
self.devices[dev_name] = device.Bmv2_SSwitch(rta, max_entries, ports, ip, port)
elif dev_type == 'Agilio':
self.devices[dev_name] = device.Agilio(rta, max_entries, ports)
self.devices[dev_name] = device.Agilio(rta, max_entries, ports, ip, port)
else:
return 'Error - device type ' + dev_type + ' unknown'
self.dbugprint("Reached return statement")
return "Added device: " + dev_name
def create_slice(self, parameters):
......@@ -179,6 +180,19 @@ class Controller(object):
>>> print(wrapper.fill(message))
'''
def list_devices(self, parameters):
"List devices"
# parameters:
# <'admin'>
message = ''
for hp4devicename in self.devices:
hp4device = self.devices[hp4devicename]
message += hp4devicename
for line in str(hp4device).splitlines():
message += '\n ' + line
return message
def grant_lease(self, parameters):
# parameters:
# <'admin'> <slice> <device> <memory limit> <ports>
......@@ -280,7 +294,7 @@ class Controller(object):
self.slices[hp4slice].leases[dev_name].withdraw_vdev(vdev_name)
return 'Virtual device ' + vdev_name + ' withdrawn from ' + dev_name
def translate(self, parameters):
def interpret(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.
......@@ -301,7 +315,7 @@ class Controller(object):
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)
hp4commands = Interpreter.interpret(vdev, p4command)
# accounting
entries_available = (self.slices[hp4slice].leases[vdev.dev_name].entry_limit
......@@ -343,7 +357,7 @@ class Controller(object):
p4command.attributes['mparams'],
p4command.attributes['aparams'])
vdev.origin_table_rules[vdev.assign_handle()] = \
virtualdevice.Origin_to_HP4Map(rule, hp4_rule_handles)
virtualdevice.Interpretation(rule, hp4_rule_handles)
elif p4command.command_type == 'table_modify':
# replace Origin_to_HP4Map w/ one with new rule, new hp4_rules_handles list
......@@ -354,7 +368,7 @@ class Controller(object):
aparams = p4command.attributes['aparams']
rule = p4rule.P4Rule(table, action, mparams, aparams)
vdev.origin_table_rules[handle] = \
virtualdevice.Origin_to_HP4Map(rule, hp4_rule_handles)
virtualdevice.Interpretation(rule, hp4_rule_handles)
elif p4command.command_type == 'table_delete':
handle = p4command.attributes['handle']
......
We need to do a more careful design for rule translation.
How does the information flow?
RELATED CLASSES, METHODS, ATTRIBUTES
Prior to translation request, create a VirtualDevice with rule translation guide:
- Controller
-- handle_request(request) (1)
return create_vdev(request)
-- create_vdev(request) (2)
vdev <- VDF.create_vdev(request.vdev_ID, request.program_path)
slices[request.slice].vdevs[request.vdev_name] <- vdev
return 'success'
- VirtualDeviceFactory
-- create_vdev(vdev_ID, program_path) (3)
code_rep <- Compiler.compile_to_hp4(program_path)
code <- link(code_rep.object_code_path, vdev_ID)
return VirtualDevice(vdev_ID, code, code_rep.rule_translation_guide_path)
- Compiler
-- compile_to_hp4(program_path) (4)
output object code to object_code_path
output rule translation guide to rule_translation_guide_path
return CodeRepresentation(object_code_path, rule_translation_guide_path)
- CodeRepresentation
-- __init__(object_code_path, rule_translation_guide_path) (5)
initialize
- VirtualDevice
-- __init__(vdev_ID, code, guide) (6)
initialize
Servicing the translation request:
- Client
-- send_request(request)
- Controller
-- handle_request(request)
call translate(request)
return response
-- slices checked for slice validity
-- translate(request) responds to client requests by invoking Translator.translate
-- return value depends on the command type
-- 'rule deleted'
-- 'rule with handle XX modified'
-- 'rule added with handle XX'
- Translator
-- translate method
- CodeRepresentation
-- rule_tranlsation_guide_path
- VirtualDevice
- Origin_to_HP4Map
- P4Command
- Rule
- Lease
-- entry_usage / entry_limit
-- send_command method
- Device
-- command_to_string
from p4command import P4Command
from ..p4command import P4Command
import code
# http://stackoverflow.com/questions/16571150/how-to-capture-stdout-output-from-a-python-function-call
class Capturing(list):
......@@ -21,7 +23,7 @@ class DeleteRuleError(Exception):
pass
class Device():
def __init__(self, rta, max_entries, phys_ports):
def __init__(self, rta, max_entries, phys_ports, ip, port):
self.rta = rta
self.assignments = {} # {pport : vdev_ID}
self.assignment_handles = {} # {pport : tset_context rule handle}
......@@ -29,6 +31,8 @@ class Device():
self.reserved_entries = 0
self.phys_ports = phys_ports
self.phys_ports_remaining = list(phys_ports)
self.ip = ip # management iface
self.port = port # management iface
def send_command(self, cmd_str_rep):
# return handle (regardless of command type)
......@@ -56,6 +60,15 @@ class Device():
def string_to_command(string):
pass
def __str__(self):
ret = 'Type: ' + self.__class__.__name__ + '\n'
ret += 'Address: ' + self.ip + ':' + self.port + '\n'
ret += 'Entry max: ' + str(self.max_entries) + '\n'
ret += 'Entries reserved: ' + str(self.reserved_entries) + '\n'
ret += 'Physical ports: ' + str(self.phys_ports) + '\n'
ret += 'Physical ports remaining: ' + str(self.phys_ports_remaining)
return ret
class Bmv2_SSwitch(Device):
@staticmethod
def command_to_string(cmd):
......
from p4command import P4Command
attribs = {'table': 'mytable'}
thing = P4Command('table_add', attribs)
print(thing.command_type)
create_virtual_device -> hp4loader.load -> what was previously done in controller2.py::Controller::link
Question: Should we, avoid specifying the physical ports at time of virtual device creation?
We should. If we can't, we should. We need to look at hp4l to see how physical port mappings are handled. We may need to defer some of the work that happens in hp4l until composition placement time.
We should. Examination of hp4l and p4c-hp4.py suggests that the --phys_ports option (and the code using it) is obsolete. It does not appear that p4c-hp4.py ever produces [PPORT #] tokens.
Therefore when looking at controller2.py::Controller::link code, don't call hp4l with the --phys_ports option.
create_device alpha localhost 22222 bmv2_SSwitch SimplePre 100 1 2 3 4
create_slice bob
grant_lease bob alpha 50 1 2
from virtualdevice import VirtualDevice
import hp4compiler
from hp4translator import RuleTranslationGuide
from hp4translator import InterpretationGuide
class CompileError(Exception):
pass
class HP4Loader():
class VirtualDeviceFactory():
def __init__(self):
compiled_programs = {} # {program_path (str) : cr (hp4compiler.CodeRepresentation)}
hp4c = hp4compiler.P4_to_HP4()
def finish(self, object_code_path, vdev_ID):
def link(self, object_code_path, vdev_ID):
f_ac = open(object_code_path, 'r')
code = []
......@@ -104,7 +104,7 @@ class HP4Loader():
return code
def load(self, vdev_name, vdev_ID, program_path):
def create_vdev(self, vdev_ID, program_path):
if program_path not in compiled_programs:
# compile
if program_path.endswith('.p4'):
......@@ -116,20 +116,20 @@ class HP4Loader():
raise CompileError('filetype not supported')
object_code_path = self.compiled_programs[program_path].object_code_path
rtg_path = self.compiled_programs[program_path].rule_translation_guide_path
ig_path = self.compiled_programs[program_path].rule_translation_guide_path
code = finish(object_code_path, vdev_ID)
guide = RuleTranslationGuide(rtg_path) # TODO: verify parameters
code = link(object_code_path, vdev_ID)
guide = InterpretationGuide(ig_path) # TODO: verify parameters
return VirtualDevice(vdev_ID, code, guide)
def writefile(self, program_path, outfile):
pass
def loader(args):
hp4loader = HP4Loader()
hp4loader.load(args.output, args.vdevID, args.input)
hp4loader.writefile(args.input, args.output)
def produce_vdev(args):
vdf = VirtualDeviceFactory()
vdf.load(args.output, args.vdevID, args.input)
vdf.writefile(args.input, args.output)
def parse_args(args):
parser = argparse.ArgumentParser(description='HP4 Annotated Commands Converter')
......@@ -140,7 +140,7 @@ def parse_args(args):
parser.add_argument('--vdevID', help='Virtual Device ID',
type=str, action="store", default='1')
parser.set_defaults(func=loader)
parser.set_defaults(func=produce_vdev)
return parser.parse_args()
......
import virtualdevice
import json
from p4command import P4Command
from ..p4command import P4Command
from p4rule import P4Rule
class Translator():
class Interpreter():
@staticmethod
def translate(self, vdev, p4command):
# key method: ~/hp4-src/p4c-hp4/controller.py::DPMUServer::translate
def table_add(self):
pass
class RuleTranslationGuide():
@staticmethod
def table_modify(self):
pass
@staticmethod
def table_delete(self):
pass
class Interpretation():
def __init__(self, rule, handles):
self.origin_rule = rule
self.hp4_rule_handles = handles
class InterpretationGuide():
def __init__(self, rtg_path):
# key method: ~/hp4-src/p4c-hp4/controller.py::DPMUServer::parse_json
self.templates_match = {}
......@@ -35,7 +48,7 @@ class RuleTranslationGuide():
attributes['src_aparam_id'] = hp4_command['src_aparam_id']
if templates_prims.has_key(key) == False:
templates_prims[key] = []
self.templates_prims[key].append(P4Command(command_type, attributes)
self.templates_prims[key].append(P4Command(command_type, attributes))
else:
print("ERROR: Unrecognized class: %s" % hp4_command['__class__'])
self.templates = {}
......
from ..p4command import P4Command
from p4rule import P4Rule
from interpret import Interpretation, InterpretationGuide
from ..compilers import compiler as hp4compiler
from ..compilers.compiler import CodeRepresentation
class VirtualDevice():
def __init__(self, virtual_device_ID, code, guide):
self.virtual_device_ID = virtual_device_ID
self.guide = guide
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'
self.next_handle = 0
def interpret(self, p4command):
# key method: ~/hp4-src/p4c-hp4/controller.py::DPMUServer::translate
p4commands = []
# this may be problematic, reusing the origin rule handle as the match_ID
match_ID = vdev.assign_handle()
self.origin_table_rules[match_ID] # TODO...
return p4commands
def assign_handle(self):
handle = self.next_handle
self.next_handle += 1
return handle
class CompileError(Exception):
pass
class VirtualDeviceFactory():
def __init__(self):
compiled_programs = {} # {program_path (str) : cr (hp4compiler.CodeRepresentation)}
hp4c = hp4compiler.P4_to_HP4()
def link(self, object_code_path, vdev_ID):
f_ac = open(object_code_path, 'r')
code = []
sr = {}
sr['[vdev ID]'] = vdev_ID
sr['[PROCEED]'] = '0'
sr['[PARSE_SELECT_SEB]'] = '1'
sr['[PARSE_SELECT_20_29]'] = '2'
sr['[PARSE_SELECT_30_39]'] = '3'
sr['[PARSE_SELECT_40_49]'] = '4'
sr['[PARSE_SELECT_50_59]'] = '5'
sr['[PARSE_SELECT_60_69]'] = '6'
sr['[PARSE_SELECT_70_79]'] = '7'
sr['[PARSE_SELECT_80_89]'] = '8'
sr['[PARSE_SELECT_90_99]'] = '9'
sr['[EXTRACT_MORE]'] ='10'
sr['[DONE]'] = '0'
sr['[EXTRACTED_EXACT]'] = '1'
sr['[METADATA_EXACT]'] = '2'
sr['[STDMETA_EXACT]'] = '3'
sr['[EXTRACTED_VALID]'] = '4'
sr['[STDMETA_INGRESS_PORT_EXACT]'] = '5'
sr['[STDMETA_PACKET_LENGTH_EXACT]'] = '6'
sr['[STDMETA_INSTANCE_TYPE_EXACT]'] = '7'
sr['[STDMETA_EGRESS_SPEC_EXACT]'] = '8'
sr['[MATCHLESS]'] = '99'
sr['[COMPLETE]'] = '1'
sr['[CONTINUE]'] = '2'
sr['[MODIFY_FIELD]'] = '0'
sr['[ADD_HEADER]'] = '1'
sr['[COPY_HEADER]'] = '2'
sr['[REMOVE_HEADER]'] = '3'
sr['[MODIFY_FIELD_WITH_HBO]'] = '4'
sr['[TRUNCATE]'] = '5'
sr['[DROP]'] = '6'
sr['[NO_OP]'] = '7'
sr['[PUSH]'] = '8'
sr['[POP]'] = '9'
sr['[COUNT]'] = '10'
sr['[METER]'] = '11'
sr['[GENERATE_DIGEST]'] = '12'
sr['[RECIRCULATE]'] = '13'
sr['[RESUBMIT]'] = '14'
sr['[CLONE_INGRESS_INGRESS]'] = '15'
sr['[CLONE_EGRESS_INGRESS]'] = '16'
sr['[CLONE_INGRESS_EGRESS]'] = '17'
sr['[CLONE_EGRESS_EGRESS]'] = '18'
sr['[MULTICAST]'] = '19'
sr['[MATH_ON_FIELD]'] = '20'
found_sr = False
for line in f_ac:
if line == '# SEARCH AND REPLACE\n':
found_sr = True
break
if(found_sr):
line = f_ac.next()
while line != '\n':
linetoks = line.split()
sr[linetoks[1]] = linetoks[3]
line = f_ac.next()
f_ac.seek(0)
for line in f_ac:
# strip out comments and white space
if line[0] == '#' or line[0] == '\n':
continue
i = line.find('#')
if i != -1:
line = line[0:i]
while line.endswith(' '):
line = line[0:-1]
line += '\n'
for key in sr.keys():
line = line.replace(key, sr[key])
for token in re.findall("\[.*?\]", line):
replace = ""
if re.search("\[[0-9]*x00s\]", token):
numzeros = int(re.search("[0-9]+", token).group())
for i in range(numzeros):
replace += "00"
line = line.replace(token, replace)
code.append(line)
f_ac.close()
return code
def create_vdev(self, vdev_ID, program_path):
if program_path not in compiled_programs:
# compile
if program_path.endswith('.p4'):
try:
self.compiled_programs[program_path] = self.hp4c.compile_to_hp4(program_path)
except CompileError as e:
return "Compile Error: " + str(e)
else:
raise CompileError('filetype not supported')
object_code_path = self.compiled_programs[program_path].object_code_path
ig_path = self.compiled_programs[program_path].rule_translation_guide_path
code = link(object_code_path, vdev_ID)
guide = InterpretationGuide(ig_path) # TODO: verify parameters
return VirtualDevice(vdev_ID, code, guide)
def writefile(self, program_path, outfile):
pass
def produce_vdev(args):
vdf = VirtualDeviceFactory()
vdf.load(args.output, args.vdevID, args.input)
vdf.writefile(args.input, args.output)
def parse_args(args):
parser = argparse.ArgumentParser(description='HP4-targeting Compiler / Linker')
parser.add_argument('--input', help='Annotated hp4 commands file',
type=str, action="store", required=True)
parser.add_argument('--output', help='Where to write hp4-ready commands file',
type=str, action="store", required=True)
parser.add_argument('--vdevID', help='Virtual Device ID',
type=str, action="store", default='1')
parser.set_defaults(func=produce_vdev)
return parser.parse_args()
def main():
args = parse_args(sys.argv[1:])
args.func(args)
if __name__ == '__main__':
main()
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