Many CLI cleanups and flow preintegration
Changes:
- auto-completion for device and logical device IDs
- a set of test CLI commands to push down various flows
to Voltha (aids test and integration)
- sample code in simulated_olt and onu to show how
to process incoming bulk flow table
- extended Tibit OLT and ONU code with remaining flow
directives they need to handle in the PON use-case
Change-Id: Id101e087cc79f4493805e3b4a051a10a4619bf53
diff --git a/cli/device.py b/cli/device.py
index 94e24e6..3ab4b0d 100644
--- a/cli/device.py
+++ b/cli/device.py
@@ -21,6 +21,7 @@
from cmd2 import Cmd
from simplejson import dumps
+from cli.table import print_pb_as_table, print_pb_list_as_table
from cli.utils import print_flows, pb2dict
from voltha.protos import third_party
@@ -47,8 +48,16 @@
def do_show(self, line):
"""Show detailed device information"""
- self.poutput(dumps(pb2dict(self.get_device(depth=-1)),
- indent=4, sort_keys=True))
+ print_pb_as_table('Device {}'.format(self.device_id),
+ self.get_device(depth=-1))
+
+ def do_ports(self, line):
+ """Show ports of device"""
+ device = self.get_device(depth=-1)
+ omit_fields = {
+ }
+ print_pb_list_as_table('Device ports:', device.ports,
+ omit_fields, self.poutput)
def do_flows(self, line):
"""Show flow table for device"""
diff --git a/cli/logical_device.py b/cli/logical_device.py
index 286faf0..3e90f41 100644
--- a/cli/logical_device.py
+++ b/cli/logical_device.py
@@ -21,9 +21,11 @@
from cmd2 import Cmd
from simplejson import dumps
+from cli.table import print_pb_as_table, print_pb_list_as_table
from cli.utils import pb2dict
from cli.utils import print_flows
from voltha.protos import third_party
+from google.protobuf.empty_pb2 import Empty
_ = third_party
from voltha.protos import voltha_pb2
@@ -45,14 +47,34 @@
metadata=(('get-depth', str(depth)), ))
return res
+ def get_device(self, id):
+ stub = voltha_pb2.VolthaLocalServiceStub(self.get_channel())
+ return stub.GetDevice(voltha_pb2.ID(id=id))
+
+ def get_devices(self):
+ stub = voltha_pb2.VolthaLocalServiceStub(self.get_channel())
+ res = stub.ListDevices(Empty())
+ return res.items
+
do_exit = Cmd.do_quit
- def do_show(self, arg):
+ def do_show(self, _):
"""Show detailed logical device information"""
- self.poutput(dumps(pb2dict(self.get_logical_device(depth=-1)),
- indent=4, sort_keys=True))
+ print_pb_as_table('Logical device {}'.format(self.logical_device_id),
+ self.get_logical_device(depth=-1))
- def do_flows(self, arg):
+ def do_ports(self, _):
+ """Show ports of logical device"""
+ device = self.get_logical_device(depth=-1)
+ omit_fields = {
+ 'ofp_port.advertised',
+ 'ofp_port.peer',
+ 'ofp_port.max_speed',
+ }
+ print_pb_list_as_table('Logical device ports:', device.ports,
+ omit_fields, self.poutput)
+
+ def do_flows(self, _):
"""Show flow table for logical device"""
logical_device = pb2dict(self.get_logical_device(-1))
print_flows(
@@ -63,3 +85,22 @@
groups=logical_device['flow_groups']['items']
)
+ def do_devices(self, line):
+ """List devices that belong to this logical device"""
+ logical_device = self.get_logical_device()
+ root_device_id = logical_device.root_device_id
+ devices = [self.get_device(root_device_id)]
+ for d in self.get_devices():
+ if d.parent_id == root_device_id:
+ devices.append(d)
+ omit_fields = {
+ 'adapter',
+ 'vendor',
+ 'model',
+ 'hardware_version',
+ 'software_version',
+ 'firmware_version',
+ 'serial_number'
+ }
+ print_pb_list_as_table('Devices:', devices, omit_fields, self.poutput)
+
diff --git a/cli/main.py b/cli/main.py
index 7b4b241..49fc812 100755
--- a/cli/main.py
+++ b/cli/main.py
@@ -27,11 +27,11 @@
from cli.device import DeviceCli
from cli.logical_device import LogicalDeviceCli
-from cli.table import TablePrinter, print_pb_table
+from cli.table import TablePrinter, print_pb_list_as_table
from voltha.core.flow_decomposer import *
from voltha.protos import third_party
from voltha.protos import voltha_pb2
-from voltha.protos.openflow_13_pb2 import FlowTableUpdate
+from voltha.protos.openflow_13_pb2 import FlowTableUpdate, FlowGroupTableUpdate
_ = third_party
from cli.utils import pb2dict, dict2line
@@ -144,7 +144,7 @@
stub = voltha_pb2.VolthaLocalServiceStub(self.get_channel())
res = stub.ListAdapters(Empty())
omit_fields = {}
- print_pb_table('Adapters:', res.items, omit_fields, self.poutput)
+ print_pb_list_as_table('Adapters:', res.items, omit_fields, self.poutput)
def get_devices(self):
stub = voltha_pb2.VolthaLocalServiceStub(self.get_channel())
@@ -168,7 +168,7 @@
'firmware_version',
'serial_number'
}
- print_pb_table('Devices:', devices, omit_fields, self.poutput)
+ print_pb_list_as_table('Devices:', devices, omit_fields, self.poutput)
def do_logical_devices(self, line):
"""List logical devices in Voltha"""
@@ -182,8 +182,8 @@
'desc.serial_number',
'switch_features.capabilities'
}
- print_pb_table('Logical devices:', res.items, omit_fields,
- self.poutput)
+ print_pb_list_as_table('Logical devices:', res.items, omit_fields,
+ self.poutput)
def do_device(self, line):
"""Enter device level command mode"""
@@ -257,6 +257,12 @@
self.prompt = '(' + self.colorize(self.colorize('test', 'cyan'),
'bold') + ') '
+ def get_device(self, device_id, depth=0):
+ stub = voltha_pb2.VolthaLocalServiceStub(self.get_channel())
+ res = stub.GetDevice(voltha_pb2.ID(id=device_id),
+ metadata=(('get-depth', str(depth)), ))
+ return res
+
@options([
make_option('-t', '--device-type', action="store", dest='device_type',
help="Device type", default='simulated_olt'),
@@ -305,15 +311,41 @@
def do_arrive_onus(self, line):
"""
- Simulate the arrival of ONUs
+ Simulate the arrival of ONUs (available only on simulated_olt)
"""
device_id = line or self.default_device_id
+
+ # verify that device is of type simulated_olt
+ device = self.get_device(device_id)
+ assert device.type == 'simulated_olt', (
+ 'Cannot use it on this device type (only on simulated_olt type)')
+
requests.get('http://{}/devices/{}/detect_onus'.format(
self.voltha_sim_rest, device_id
))
complete_arrive_onus = VolthaCli.complete_device
+ def get_logical_ports(self, logical_device_id):
+ """
+ Return the NNI port number and the first usable UNI port of logical
+ device, and the vlan associated with the latter.
+ """
+ stub = voltha_pb2.VolthaLocalServiceStub(self.get_channel())
+ ports = stub.ListLogicalDevicePorts(
+ voltha_pb2.ID(id=logical_device_id)).items
+ nni = uni = vlan = None
+ for port in ports:
+ if nni is None and port.root_port:
+ nni = port.ofp_port.port_no
+ if uni is None and not port.root_port:
+ uni = port.ofp_port.port_no
+ uni_device = self.get_device(port.device_id)
+ vlan = uni_device.vlan
+ if nni is not None and uni is not None:
+ return nni, uni, vlan
+ raise Exception('No valid port pair found (no ONUs yet?)')
+
def do_install_eapol_flow(self, line):
"""
Install an EAPOL flow on the given logical device. If device is not
@@ -321,14 +353,19 @@
OLT device.
"""
logical_device_id = line or self.default_logical_device_id
+
+ # gather NNI and UNI port IDs
+ nni_port_no, uni_port_no = self.get_logical_ports(logical_device_id)
+
+ # construct and push flow rule
update = FlowTableUpdate(
id=logical_device_id,
- flow_mod = mk_simple_flow_mod(
+ flow_mod=mk_simple_flow_mod(
priority=2000,
- match_fields=[in_port(241), eth_type(0x888e)],
+ match_fields=[in_port(uni_port_no), eth_type(0x888e)],
actions=[
- push_vlan(0x8100),
- set_field(vlan_vid(4096 + 4000)),
+ # push_vlan(0x8100),
+ # set_field(vlan_vid(4096 + 4000)),
output(ofp.OFPP_CONTROLLER)
]
)
@@ -339,6 +376,255 @@
complete_install_eapol_flow = VolthaCli.complete_logical_device
+ def do_install_all_controller_bound_flows(self, line):
+ """
+ Install all flow rules for controller bound flows, including EAPOL,
+ IGMP and DHCP. If device is not given, it will be applied to logical
+ device of the last pre-provisioned OLT device.
+ """
+ logical_device_id = line or self.default_logical_device_id
+
+ # gather NNI and UNI port IDs
+ nni_port_no, uni_port_no = self.get_logical_ports(logical_device_id)
+
+ # construct and push flow rules
+ stub = voltha_pb2.VolthaLocalServiceStub(self.get_channel())
+
+ stub.UpdateLogicalDeviceFlowTable(FlowTableUpdate(
+ id=logical_device_id,
+ flow_mod=mk_simple_flow_mod(
+ priority=2000,
+ match_fields=[in_port(uni_port_no), eth_type(0x888e)],
+ actions=[
+ # push_vlan(0x8100),
+ # set_field(vlan_vid(4096 + 4000)),
+ output(ofp.OFPP_CONTROLLER)
+ ]
+ )
+ ))
+ stub.UpdateLogicalDeviceFlowTable(FlowTableUpdate(
+ id=logical_device_id,
+ flow_mod=mk_simple_flow_mod(
+ priority=1000,
+ match_fields=[eth_type(0x800), ip_proto(2)],
+ actions=[output(ofp.OFPP_CONTROLLER)]
+ )
+ ))
+ stub.UpdateLogicalDeviceFlowTable(FlowTableUpdate(
+ id=logical_device_id,
+ flow_mod=mk_simple_flow_mod(
+ priority=1000,
+ match_fields=[eth_type(0x800), ip_proto(17), udp_dst(67)],
+ actions=[output(ofp.OFPP_CONTROLLER)]
+ )
+ ))
+
+ self.poutput('success')
+
+ complete_install_all_controller_bound_flows = \
+ VolthaCli.complete_logical_device
+
+ def do_install_all_sample_flows(self, line):
+ """
+ Install all flows that are representative of the virtualized access
+ scenario in a PON network.
+ """
+ logical_device_id = line or self.default_logical_device_id
+
+ # gather NNI and UNI port IDs
+ nni_port_no, uni_port_no, c_vid = \
+ self.get_logical_ports(logical_device_id)
+
+ # construct and push flow rules
+ stub = voltha_pb2.VolthaLocalServiceStub(self.get_channel())
+
+ # Controller-bound flows
+ stub.UpdateLogicalDeviceFlowTable(FlowTableUpdate(
+ id=logical_device_id,
+ flow_mod=mk_simple_flow_mod(
+ priority=2000,
+ match_fields=[in_port(uni_port_no), eth_type(0x888e)],
+ actions=[
+ # push_vlan(0x8100),
+ # set_field(vlan_vid(4096 + 4000)),
+ output(ofp.OFPP_CONTROLLER)
+ ]
+ )
+ ))
+ stub.UpdateLogicalDeviceFlowTable(FlowTableUpdate(
+ id=logical_device_id,
+ flow_mod=mk_simple_flow_mod(
+ priority=1000,
+ match_fields=[eth_type(0x800), ip_proto(2)],
+ actions=[output(ofp.OFPP_CONTROLLER)]
+ )
+ ))
+ stub.UpdateLogicalDeviceFlowTable(FlowTableUpdate(
+ id=logical_device_id,
+ flow_mod=mk_simple_flow_mod(
+ priority=1000,
+ match_fields=[eth_type(0x800), ip_proto(17), udp_dst(67)],
+ actions=[output(ofp.OFPP_CONTROLLER)]
+ )
+ ))
+
+ # Unicast flows:
+ # Downstream flow 1
+ stub.UpdateLogicalDeviceFlowTable(FlowTableUpdate(
+ id=logical_device_id,
+ flow_mod=mk_simple_flow_mod(
+ priority=500,
+ match_fields=[
+ in_port(nni_port_no),
+ vlan_vid(4096 + 1000)
+ ],
+ actions=[pop_vlan()],
+ next_table_id=1
+ )
+ ))
+ # Downstream flow 2
+ stub.UpdateLogicalDeviceFlowTable(FlowTableUpdate(
+ id=logical_device_id,
+ flow_mod=mk_simple_flow_mod(
+ priority=500,
+ match_fields=[in_port(nni_port_no), vlan_vid(4096 + c_vid)],
+ actions=[set_field(vlan_vid(4096 + 0)), output(uni_port_no)]
+ )
+ ))
+ # Upstream flow 1 for 0-tagged case
+ stub.UpdateLogicalDeviceFlowTable(FlowTableUpdate(
+ id=logical_device_id,
+ flow_mod=mk_simple_flow_mod(
+ priority=500,
+ match_fields=[in_port(uni_port_no), vlan_vid(4096 + 0)],
+ actions=[set_field(vlan_vid(4096 + c_vid))],
+ next_table_id=1
+ )
+ ))
+ # Upstream flow 1 for untagged case
+ stub.UpdateLogicalDeviceFlowTable(FlowTableUpdate(
+ id=logical_device_id,
+ flow_mod=mk_simple_flow_mod(
+ priority=500,
+ match_fields=[in_port(uni_port_no), vlan_vid(0)],
+ actions=[push_vlan(0x8100), set_field(vlan_vid(4096 + c_vid))],
+ next_table_id=1
+ )
+ ))
+ # Upstream flow 2 for s-tag
+ stub.UpdateLogicalDeviceFlowTable(FlowTableUpdate(
+ id=logical_device_id,
+ flow_mod=mk_simple_flow_mod(
+ priority=500,
+ match_fields=[in_port(uni_port_no), vlan_vid(4096 + c_vid)],
+ actions=[
+ push_vlan(0x8100),
+ set_field(vlan_vid(4096 + 1000)),
+ output(nni_port_no)
+ ]
+ )
+ ))
+
+ # Push a few multicast flows
+ # 1st with one bucket for our uni
+ stub.UpdateLogicalDeviceFlowGroupTable(FlowGroupTableUpdate(
+ id=logical_device_id,
+ group_mod=mk_multicast_group_mod(
+ group_id=1,
+ buckets=[
+ ofp.ofp_bucket(actions=[pop_vlan(), output(uni_port_no)])
+ ]
+ )
+ ))
+ stub.UpdateLogicalDeviceFlowTable(FlowTableUpdate(
+ id=logical_device_id,
+ flow_mod=mk_simple_flow_mod(
+ priority=1000,
+ match_fields=[
+ in_port(nni_port_no),
+ eth_type(0x800),
+ vlan_vid(4096 + 140),
+ ipv4_dst(0xe4010101)
+ ],
+ actions=[group(1)]
+ )
+ ))
+ # 2st with one bucket for our uni
+ stub.UpdateLogicalDeviceFlowGroupTable(FlowGroupTableUpdate(
+ id=logical_device_id,
+ group_mod=mk_multicast_group_mod(
+ group_id=2,
+ buckets=[
+ ofp.ofp_bucket(actions=[pop_vlan(), output(uni_port_no)])
+ ]
+ )
+ ))
+ stub.UpdateLogicalDeviceFlowTable(FlowTableUpdate(
+ id=logical_device_id,
+ flow_mod=mk_simple_flow_mod(
+ priority=1000,
+ match_fields=[
+ in_port(nni_port_no),
+ eth_type(0x800),
+ vlan_vid(4096 + 140),
+ ipv4_dst(0xe4020202)
+ ],
+ actions=[group(2)]
+ )
+ ))
+ # 3rd with empty bucket
+ stub.UpdateLogicalDeviceFlowGroupTable(FlowGroupTableUpdate(
+ id=logical_device_id,
+ group_mod=mk_multicast_group_mod(
+ group_id=3,
+ buckets=[]
+ )
+ ))
+ stub.UpdateLogicalDeviceFlowTable(FlowTableUpdate(
+ id=logical_device_id,
+ flow_mod=mk_simple_flow_mod(
+ priority=1000,
+ match_fields=[
+ in_port(nni_port_no),
+ eth_type(0x800),
+ vlan_vid(4096 + 140),
+ ipv4_dst(0xe4030303)
+ ],
+ actions=[group(3)]
+ )
+ ))
+
+ self.poutput('success')
+
+ complete_install_all_sample_flows = VolthaCli.complete_logical_device
+
+ def do_delete_all_flows(self, line):
+ """
+ Remove all flows and flow groups from given logical device
+ """
+ logical_device_id = line or self.default_logical_device_id
+ stub = voltha_pb2.VolthaLocalServiceStub(self.get_channel())
+ stub.UpdateLogicalDeviceFlowTable(FlowTableUpdate(
+ id=logical_device_id,
+ flow_mod=ofp.ofp_flow_mod(
+ command=ofp.OFPFC_DELETE,
+ table_id=ofp.OFPTT_ALL,
+ cookie_mask=0,
+ out_port=ofp.OFPP_ANY,
+ out_group=ofp.OFPG_ANY
+ )
+ ))
+ stub.UpdateLogicalDeviceFlowGroupTable(FlowGroupTableUpdate(
+ id=logical_device_id,
+ group_mod=ofp.ofp_group_mod(
+ command=ofp.OFPGC_DELETE,
+ group_id=ofp.OFPG_ALL
+ )
+ ))
+ self.poutput('success')
+
+ complete_delete_all_flows = VolthaCli.complete_logical_device
+
def do_send_simulated_upstream_eapol(self, line):
"""
Send an EAPOL upstream from a simulated OLT
diff --git a/cli/table.py b/cli/table.py
index 3eb828a..6485787 100644
--- a/cli/table.py
+++ b/cli/table.py
@@ -15,6 +15,7 @@
#
import sys
+from google.protobuf.internal.containers import RepeatedCompositeFieldContainer
from google.protobuf.message import Message
from termcolor import colored
@@ -37,6 +38,9 @@
row[field_key] = value
self._update_max_length(field_key, value)
+ def number_of_rows(self):
+ return len(self.cell_values)
+
def print_table(self, header=None, printfn=_printfn, dividers=10):
if header is not None:
@@ -85,7 +89,7 @@
assert self.field_names[field_key] == field_name
-def print_pb_table(header, items, fields_to_omit=None, printfn=_printfn):
+def print_pb_list_as_table(header, items, fields_to_omit=None, printfn=_printfn):
from cli.utils import pb2dict
t = TablePrinter()
@@ -111,6 +115,34 @@
t.print_table(header, printfn)
+def print_pb_as_table(header, pb, fields_to_omit={}, printfn=_printfn):
+ from cli.utils import pb2dict
+
+ t = TablePrinter()
+
+ def pr(_pb, prefix=''):
+ d = pb2dict(_pb)
+ for field in sorted(_pb._fields, key=lambda f: f.number):
+ fname = prefix + field.name
+ if fname in fields_to_omit:
+ continue
+ value = getattr(_pb, field.name)
+ if isinstance(value, Message):
+ pr(value, fname + '.')
+ elif isinstance(value, RepeatedCompositeFieldContainer):
+ row = t.number_of_rows()
+ t.add_cell(row, 0, 'field', fname)
+ t.add_cell(row, 1, 'value', '{} item(s)'.format(
+ len(d.get(field.name))))
+ else:
+ row = t.number_of_rows()
+ t.add_cell(row, 0, 'field', fname)
+ t.add_cell(row, 1, 'value', d.get(field.name))
+
+ pr(pb)
+
+ t.print_table(header, printfn)
+
if __name__ == '__main__':
import random
t = TablePrinter()
diff --git a/cli/utils.py b/cli/utils.py
index 941ad9c..b1aad1a 100644
--- a/cli/utils.py
+++ b/cli/utils.py
@@ -83,8 +83,12 @@
'VLAN_VID': lambda f: (101, 'vlan_vid', p_vlan_vid(f['vlan_vid'])),
'VLAN_PCP': lambda f: (102, 'vlan_pcp', str(f['vlan_pcp'])),
'ETH_TYPE': lambda f: (103, 'eth_type', '%X' % f['eth_type']),
- 'IPV4_DST': lambda f: (104, 'ipv4_dst', p_ipv4(f['ipv4_dst'])),
- 'IP_PROTO': lambda f: (105, 'ip_proto', str(f['ip_proto']))
+ 'IP_PROTO': lambda f: (104, 'ip_proto', str(f['ip_proto'])),
+ 'IPV4_DST': lambda f: (105, 'ipv4_dst', p_ipv4(f['ipv4_dst'])),
+ 'UDP_SRC': lambda f: (106, 'udp_src', str(f['udp_src'])),
+ 'UDP_DST': lambda f: (107, 'udp_dst', str(f['udp_dst'])),
+ 'TCP_SRC': lambda f: (108, 'tcp_src', str(f['tcp_src'])),
+ 'TCP_DST': lambda f: (109, 'tcp_dst', str(f['tcp_dst'])),
}
diff --git a/voltha/adapters/simulated_olt/simulated_olt.py b/voltha/adapters/simulated_olt/simulated_olt.py
index f550f97..1fdc965 100644
--- a/voltha/adapters/simulated_olt/simulated_olt.py
+++ b/voltha/adapters/simulated_olt/simulated_olt.py
@@ -30,6 +30,7 @@
from common.utils.asleep import asleep
from voltha.adapters.interface import IAdapterInterface
+from voltha.core.flow_decomposer import *
from voltha.core.logical_device_agent import mac_str_to_tuple
from voltha.protos.adapter_pb2 import Adapter, AdapterConfig
from voltha.protos.device_pb2 import DeviceType, DeviceTypes, Device, Port
@@ -355,6 +356,143 @@
log.debug('bulk-flow-update', device_id=device.id,
flows=flows, groups=groups)
+ # sample code that analyzes the incoming flow table
+ assert len(groups.items) == 0, "Cannot yet deal with groups"
+
+ for flow in flows.items:
+ in_port = get_in_port(flow)
+ assert in_port is not None
+
+ if in_port == 2:
+
+ # Downstream rule
+
+ for field in get_ofb_fields(flow):
+ if field.type == ETH_TYPE:
+ _type = field.eth_type
+ pass # construct ether type based condition here
+
+ elif field.type == IP_PROTO:
+ _proto = field.ip_proto
+ pass # construct ip_proto based condition here
+
+ elif field.type == IN_PORT:
+ _port = field.port
+ pass # construct in_port based condition here
+
+ elif field.type == VLAN_VID:
+ _vlan_vid = field.vlan_vid
+ pass # construct VLAN ID based filter condition here
+
+ elif field.type == VLAN_PCP:
+ _vlan_pcp = field.vlan_pcp
+ pass # construct VLAN PCP based filter condition here
+
+ # TODO
+ else:
+ raise NotImplementedError('field.type={}'.format(
+ field.type))
+
+ for action in get_actions(flow):
+
+ if action.type == OUTPUT:
+ pass # construct packet emit rule here
+
+ elif action.type == PUSH_VLAN:
+ if action.push.ethertype != 0x8100:
+ log.error('unhandled-ether-type',
+ ethertype=action.push.ethertype)
+ pass # construct vlan push command here
+
+ elif action.type == POP_VLAN:
+ pass # construct vlan pop command here
+
+ elif action.type == SET_FIELD:
+ assert (action.set_field.field.oxm_class ==
+ ofp.OFPXMC_OPENFLOW_BASIC)
+ field = action.set_field.field.ofb_field
+ if field.type == VLAN_VID:
+ pass # construct vlan_id set command here
+ else:
+ log.error('unsupported-action-set-field-type',
+ field_type=field.type)
+
+ else:
+ log.error('unsupported-action-type',
+ action_type=action.type)
+
+ # final assembly of low level device flow rule and pushing it
+ # down to device
+ pass
+
+ elif in_port == 1:
+
+ # Upstream rule
+
+ for field in get_ofb_fields(flow):
+
+ if field.type == ETH_TYPE:
+ _type = field.eth_type
+ pass # construct ether type based condition here
+
+ elif field.type == IP_PROTO:
+ _proto = field.ip_proto
+ pass # construct ip_proto based condition here
+
+ elif field.type == IN_PORT:
+ _port = field.port
+ pass # construct in_port based condition here
+
+ elif field.type == VLAN_VID:
+ _vlan_vid = field.vlan_vid
+ pass # construct VLAN ID based filter condition here
+
+ elif field.type == VLAN_PCP:
+ _vlan_pcp = field.vlan_pcp
+ pass # construct VLAN PCP based filter condition here
+
+ elif field.type == UDP_DST:
+ _udp_dst = field.udp_dst
+ pass # construct UDP SDT based filter here
+
+ # TODO
+ else:
+ raise NotImplementedError('field.type={}'.format(
+ field.type))
+
+ for action in get_actions(flow):
+
+ if action.type == OUTPUT:
+ pass # construct packet emit rule here
+
+ elif action.type == PUSH_VLAN:
+ if action.push.ethertype != 0x8100:
+ log.error('unhandled-ether-type',
+ ethertype=action.push.ethertype)
+ pass # construct vlan push command here
+
+ elif action.type == SET_FIELD:
+ assert (action.set_field.field.oxm_class ==
+ ofp.OFPXMC_OPENFLOW_BASIC)
+ field = action.set_field.field.ofb_field
+ if field.type == VLAN_VID:
+ pass # construct vlan_id set command here
+ else:
+ log.error('unsupported-action-set-field-type',
+ field_type=field.type)
+
+ else:
+ log.error('unsupported-action-type',
+ action_type=action.type)
+
+ # final assembly of low level device flow rule and pushing it
+ # down to device
+ pass
+
+ else:
+ raise Exception('Port should be 1 or 2 by our convention')
+
+
def update_flows_incrementally(self, device, flow_changes, group_changes):
raise NotImplementedError()
diff --git a/voltha/adapters/simulated_onu/simulated_onu.py b/voltha/adapters/simulated_onu/simulated_onu.py
index 73a2f8f..bdddb99 100644
--- a/voltha/adapters/simulated_onu/simulated_onu.py
+++ b/voltha/adapters/simulated_onu/simulated_onu.py
@@ -26,6 +26,7 @@
from common.utils.asleep import asleep
from voltha.adapters.interface import IAdapterInterface
+from voltha.core.flow_decomposer import *
from voltha.core.logical_device_agent import mac_str_to_tuple
from voltha.protos.adapter_pb2 import Adapter, AdapterConfig
from voltha.protos.device_pb2 import DeviceType, DeviceTypes, Device, Port
@@ -184,6 +185,146 @@
log.debug('bulk-flow-update', device_id=device.id,
flows=flows, groups=groups)
+ # sample code that analyzes the incoming flow table
+ assert len(groups.items) == 0, "Cannot yet deal with groups"
+
+ for flow in flows.items:
+ in_port = get_in_port(flow)
+ assert in_port is not None
+
+ if in_port == 2:
+
+ # Downstream rule
+
+ for field in get_ofb_fields(flow):
+ if field.type == ETH_TYPE:
+ _type = field.eth_type
+ pass # construct ether type based condition here
+
+ elif field.type == IP_PROTO:
+ _proto = field.ip_proto
+ pass # construct ip_proto based condition here
+
+ elif field.type == IN_PORT:
+ _port = field.port
+ pass # construct in_port based condition here
+
+ elif field.type == VLAN_VID:
+ _vlan_vid = field.vlan_vid
+ pass # construct VLAN ID based filter condition here
+
+ elif field.type == VLAN_PCP:
+ _vlan_pcp = field.vlan_pcp
+ pass # construct VLAN PCP based filter condition here
+
+ # TODO
+ else:
+ raise NotImplementedError('field.type={}'.format(
+ field.type))
+
+ for action in get_actions(flow):
+
+ if action.type == OUTPUT:
+ pass # construct packet emit rule here
+
+ elif action.type == PUSH_VLAN:
+ if action.push.ethertype != 0x8100:
+ log.error('unhandled-ether-type',
+ ethertype=action.push.ethertype)
+ pass # construct vlan push command here
+
+ elif action.type == POP_VLAN:
+ pass # construct vlan pop command here
+
+ elif action.type == SET_FIELD:
+ assert (action.set_field.field.oxm_class ==
+ ofp.OFPXMC_OPENFLOW_BASIC)
+ field = action.set_field.field.ofb_field
+ if field.type == VLAN_VID:
+ pass # construct vlan_id set command here
+ else:
+ log.error('unsupported-action-set-field-type',
+ field_type=field.type)
+
+ else:
+ log.error('unsupported-action-type',
+ action_type=action.type)
+
+ # final assembly of low level device flow rule and pushing it
+ # down to device
+ pass
+
+ elif in_port == 1:
+
+ # Upstream rule
+
+ for field in get_ofb_fields(flow):
+ if field.type == ETH_TYPE:
+ _type = field.eth_type
+ pass # construct ether type based condition here
+
+ elif field.type == IP_PROTO:
+ _proto = field.ip_proto
+ pass # construct ip_proto based condition here
+
+ elif field.type == IN_PORT:
+ _port = field.port
+ pass # construct in_port based condition here
+
+ elif field.type == VLAN_VID:
+ _vlan_vid = field.vlan_vid
+ pass # construct VLAN ID based filter condition here
+
+ elif field.type == VLAN_PCP:
+ _vlan_pcp = field.vlan_pcp
+ pass # construct VLAN PCP based filter condition here
+
+ elif field.type == IPV4_DST:
+ _ipv4_dst = field.ipv4_dst
+ pass # construct IPv4 DST address based condition
+
+ elif field.type == UDP_DST:
+ _udp_dst = field.udp_dst
+ pass # construct UDP SDT based filter here
+
+ # TODO
+ else:
+ raise NotImplementedError('field.type={}'.format(
+ field.type))
+
+ for action in get_actions(flow):
+
+ if action.type == OUTPUT:
+ pass # construct packet emit rule here
+
+ elif action.type == PUSH_VLAN:
+ if action.push.ethertype != 0x8100:
+ log.error('unhandled-ether-type',
+ ethertype=action.push.ethertype)
+ pass # construct vlan push command here
+
+ elif action.type == SET_FIELD:
+ assert (action.set_field.field.oxm_class ==
+ ofp.OFPXMC_OPENFLOW_BASIC)
+ field = action.set_field.field.ofb_field
+ if field.type == VLAN_VID:
+ pass # construct vlan_id set command here
+ else:
+ log.error('unsupported-action-set-field-type',
+ field_type=field.type)
+
+ else:
+ log.error('unsupported-action-type',
+ action_type=action.type)
+
+ # final assembly of low level device flow rule and pushing it
+ # down to device
+ pass
+
+ else:
+ raise Exception('Port should be 1 or 2 by our convention')
+
+
def update_flows_incrementally(self, device, flow_changes, group_changes):
raise NotImplementedError()
diff --git a/voltha/adapters/tibit_olt/tibit_olt.py b/voltha/adapters/tibit_olt/tibit_olt.py
index 45d610f..094b01d 100644
--- a/voltha/adapters/tibit_olt/tibit_olt.py
+++ b/voltha/adapters/tibit_olt/tibit_olt.py
@@ -59,6 +59,7 @@
is_tibit_frame = BpfProgramFilter(frame_match)
#is_tibit_frame = lambda x: True
+
# To be removed in favor of OAM
class TBJSON(Packet):
""" TBJSON 'packet' layer. """
@@ -67,6 +68,7 @@
bind_layers(Ether, TBJSON, type=0x9001)
+
@implementer(IAdapterInterface)
class TibitOltAdapter(object):
@@ -363,6 +365,7 @@
req /= PortIngressRuleHeader(precedence=precedence)
for field in get_ofb_fields(flow):
+
if field.type == ETH_TYPE:
_type = field.eth_type
req /= PortIngressRuleClauseMatchLength02(
@@ -370,15 +373,39 @@
operator=1,
match0=(_type >> 8) & 0xff,
match1=_type & 0xff)
+
elif field.type == IP_PROTO:
- pass
- # TODO etc
+ _proto = field.ip_proto
+ pass # construct ip_proto based condition here
+
+ elif field.type == IN_PORT:
+ _port = field.port
+ pass # construct in_port based condition here
+
+ elif field.type == VLAN_VID:
+ _vlan_vid = field.vlan_vid
+ pass # construct VLAN ID based filter condition here
+
+ elif field.type == VLAN_PCP:
+ _vlan_pcp = field.vlan_pcp
+ pass # construct VLAN PCP based filter condition here
+
+ elif field.type == UDP_DST:
+ _udp_dst = field.udp_dst
+ pass # construct UDP SDT based filter here
+
+ else:
+ raise NotImplementedError('field.type={}'.format(
+ field.type))
for action in get_actions(flow):
if action.type == OUTPUT:
req /= PortIngressRuleResultForward()
+ elif action.type == POP_VLAN:
+ pass # construct vlan pop command here
+
elif action.type == PUSH_VLAN:
if action.push.ethertype != 0x8100:
log.error('unhandled-ether-type',
diff --git a/voltha/adapters/tibit_onu/tibit_onu.py b/voltha/adapters/tibit_onu/tibit_onu.py
index 1d7f279..1ab620d 100644
--- a/voltha/adapters/tibit_onu/tibit_onu.py
+++ b/voltha/adapters/tibit_onu/tibit_onu.py
@@ -30,6 +30,7 @@
from twisted.internet.defer import DeferredQueue, inlineCallbacks
from twisted.internet import reactor
+from voltha.core.flow_decomposer import *
from voltha.core.logical_device_agent import mac_str_to_tuple
from voltha.adapters.interface import IAdapterInterface
@@ -197,6 +198,145 @@
log.debug('bulk-flow-update', device_id=device.id,
flows=flows, groups=groups)
+ # sample code that analyzes the incoming flow table
+ assert len(groups.items) == 0, "Cannot yet deal with groups"
+
+ for flow in flows.items:
+ in_port = get_in_port(flow)
+ assert in_port is not None
+
+ if in_port == 2:
+
+ # Downstream rule
+
+ for field in get_ofb_fields(flow):
+ if field.type == ETH_TYPE:
+ _type = field.eth_type
+ pass # construct ether type based condition here
+
+ elif field.type == IP_PROTO:
+ _proto = field.ip_proto
+ pass # construct ip_proto based condition here
+
+ elif field.type == IN_PORT:
+ _port = field.port
+ pass # construct in_port based condition here
+
+ elif field.type == VLAN_VID:
+ _vlan_vid = field.vlan_vid
+ pass # construct VLAN ID based filter condition here
+
+ elif field.type == VLAN_PCP:
+ _vlan_pcp = field.vlan_pcp
+ pass # construct VLAN PCP based filter condition here
+
+ # TODO
+ else:
+ raise NotImplementedError('field.type={}'.format(
+ field.type))
+
+ for action in get_actions(flow):
+
+ if action.type == OUTPUT:
+ pass # construct packet emit rule here
+
+ elif action.type == PUSH_VLAN:
+ if action.push.ethertype != 0x8100:
+ log.error('unhandled-ether-type',
+ ethertype=action.push.ethertype)
+ pass # construct vlan push command here
+
+ elif action.type == POP_VLAN:
+ pass # construct vlan pop command here
+
+ elif action.type == SET_FIELD:
+ assert (action.set_field.field.oxm_class ==
+ ofp.OFPXMC_OPENFLOW_BASIC)
+ field = action.set_field.field.ofb_field
+ if field.type == VLAN_VID:
+ pass # construct vlan_id set command here
+ else:
+ log.error('unsupported-action-set-field-type',
+ field_type=field.type)
+
+ else:
+ log.error('unsupported-action-type',
+ action_type=action.type)
+
+ # final assembly of low level device flow rule and pushing it
+ # down to device
+ pass
+
+ elif in_port == 1:
+
+ # Upstream rule
+
+ for field in get_ofb_fields(flow):
+ if field.type == ETH_TYPE:
+ _type = field.eth_type
+ pass # construct ether type based condition here
+
+ elif field.type == IP_PROTO:
+ _proto = field.ip_proto
+ pass # construct ip_proto based condition here
+
+ elif field.type == IN_PORT:
+ _port = field.port
+ pass # construct in_port based condition here
+
+ elif field.type == VLAN_VID:
+ _vlan_vid = field.vlan_vid
+ pass # construct VLAN ID based filter condition here
+
+ elif field.type == VLAN_PCP:
+ _vlan_pcp = field.vlan_pcp
+ pass # construct VLAN PCP based filter condition here
+
+ elif field.type == IPV4_DST:
+ _ipv4_dst = field.ipv4_dst
+ pass # construct IPv4 DST address based condition
+
+ elif field.type == UDP_DST:
+ _udp_dst = field.udp_dst
+ pass # construct UDP SDT based filter here
+
+ # TODO
+ else:
+ raise NotImplementedError('field.type={}'.format(
+ field.type))
+
+ for action in get_actions(flow):
+
+ if action.type == OUTPUT:
+ pass # construct packet emit rule here
+
+ elif action.type == PUSH_VLAN:
+ if action.push.ethertype != 0x8100:
+ log.error('unhandled-ether-type',
+ ethertype=action.push.ethertype)
+ pass # construct vlan push command here
+
+ elif action.type == SET_FIELD:
+ assert (action.set_field.field.oxm_class ==
+ ofp.OFPXMC_OPENFLOW_BASIC)
+ field = action.set_field.field.ofb_field
+ if field.type == VLAN_VID:
+ pass # construct vlan_id set command here
+ else:
+ log.error('unsupported-action-set-field-type',
+ field_type=field.type)
+
+ else:
+ log.error('unsupported-action-type',
+ action_type=action.type)
+
+ # final assembly of low level device flow rule and pushing it
+ # down to device
+ pass
+
+ else:
+ raise Exception('Port should be 1 or 2 by our convention')
+
def update_flows_incrementally(self, device, flow_changes, group_changes):
raise NotImplementedError()
diff --git a/voltha/core/logical_device_agent.py b/voltha/core/logical_device_agent.py
index afc4ef3..f11247e 100644
--- a/voltha/core/logical_device_agent.py
+++ b/voltha/core/logical_device_agent.py
@@ -417,7 +417,8 @@
else:
flows = list(self.flows_proxy.get('/').items)
- flows_changed, flows = self.flows_delete_by_group_id(flows, group_id)
+ flows_changed, flows = self.flows_delete_by_group_id(
+ flows, group_id)
del groups[group_id]
groups_changed = True
self.log.debug('group-deleted', group_id=group_id)