Cleaner CLI
Change-Id: I81916ee10450e6f2137a3ff72a693dcf521c8a85
diff --git a/cli/device.py b/cli/device.py
index be5450b..94e24e6 100644
--- a/cli/device.py
+++ b/cli/device.py
@@ -43,12 +43,14 @@
metadata=(('get-depth', str(depth)), ))
return res
- def do_show(self, arg):
- """Show detailed device information"""
- print dumps(pb2dict(self.get_device(depth=-1)),
- indent=4, sort_keys=True)
+ do_exit = Cmd.do_quit
- def do_flows(self, arg):
+ def do_show(self, line):
+ """Show detailed device information"""
+ self.poutput(dumps(pb2dict(self.get_device(depth=-1)),
+ indent=4, sort_keys=True))
+
+ def do_flows(self, line):
"""Show flow table for device"""
device = pb2dict(self.get_device(-1))
print_flows(
diff --git a/cli/logical_device.py b/cli/logical_device.py
index d60c6eb..286faf0 100644
--- a/cli/logical_device.py
+++ b/cli/logical_device.py
@@ -45,10 +45,12 @@
metadata=(('get-depth', str(depth)), ))
return res
+ do_exit = Cmd.do_quit
+
def do_show(self, arg):
"""Show detailed logical device information"""
- print dumps(pb2dict(self.get_logical_device(depth=-1)),
- indent=4, sort_keys=True)
+ self.poutput(dumps(pb2dict(self.get_logical_device(depth=-1)),
+ indent=4, sort_keys=True))
def do_flows(self, arg):
"""Show flow table for logical device"""
diff --git a/cli/main.py b/cli/main.py
index e9e881a..7b4b241 100755
--- a/cli/main.py
+++ b/cli/main.py
@@ -14,10 +14,10 @@
# See the License for the specific language governing permissions and
# limitations under the License.
#
+
import readline
-import sys
from optparse import make_option
-from time import sleep
+from time import sleep, time
import grpc
import requests
@@ -27,21 +27,21 @@
from cli.device import DeviceCli
from cli.logical_device import LogicalDeviceCli
+from cli.table import TablePrinter, print_pb_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
_ = third_party
-from cli.utils import pb2dict
-
+from cli.utils import pb2dict, dict2line
banner = """\
- _ _ _ _ _
- __ _____| | |_| |_ __ _ __| (_)
- \ V / _ \ | _| ' \/ _` | / _| | |
- \_/\___/_|\__|_||_\__,_| \__|_|_|
-(to exit type q, exit or quit or hit Ctrl-D)
+ _ _ _ _ _
+__ _____| | |_| |_ __ _ __| (_)
+\ V / _ \ | _| ' \/ _` | / _| | |
+ \_/\___/_|\__|_||_\__,_| \__|_|_|
+(to exit type quit or hit Ctrl-D)
"""
class VolthaCli(Cmd):
@@ -67,11 +67,30 @@
'is specified',
))
+ # cleanup of superflous commands from cmd2
+ del Cmd.do_cmdenvironment
+ # del Cmd.do_eof
+ del Cmd.do_exit
+ del Cmd.do_q
+ del Cmd.do_hi
+ del Cmd.do_l
+ del Cmd.do_li
+ del Cmd.do_r
+ del Cmd.do__load
+ del Cmd.do__relative_load
+ Cmd.do_edit = Cmd.do_ed
+
+
def __init__(self, *args, **kw):
+
Cmd.__init__(self, *args, **kw)
self.prompt = '(' + self.colorize(
- self.colorize(self.prompt, 'red'), 'bold') + ') '
+ self.colorize(self.prompt, 'blue'), 'bold') + ') '
self.channel = None
+ self.device_ids_cache = None
+ self.device_ids_cache_ts = time()
+ self.logical_device_ids_cache = None
+ self.logical_device_ids_cache_ts = time()
def load_history(self):
"""Load saved command history from local history file"""
@@ -86,81 +105,144 @@
def save_history(self):
try:
- with file(self.history_file_name, 'w') as f:
+ with open(self.history_file_name, 'w') as f:
f.write('\n'.join(self.history[-self.max_history_lines:]))
- except IOError, e:
- print >> sys.stderr, 'Could not save history in {}: {}'.format(
- self.history_file_name, e.msg)
+ except IOError as e:
+ self.perror('Could not save history in {}: {}'.format(
+ self.history_file_name, e))
else:
- print >> sys.stderr, 'History saved as {}'.format(
- self.history_file_name)
+ self.perror('History saved as {}'.format(
+ self.history_file_name))
+
+ def perror(self, errmsg, statement=None):
+ # Touch it up to make sure error is prefixed and colored
+ Cmd.perror(self, self.colorize('***ERROR: ', 'red') + errmsg,
+ statement)
def get_channel(self):
if self.channel is None:
self.channel = grpc.insecure_channel(self.voltha_grpc)
return self.channel
- def preloop(self):
- self.poutput(banner)
+ # ~~~~~~~~~~~~~~~~~ ACTUAL COMMAND IMPLEMENTATIONS ~~~~~~~~~~~~~~~~~~~~~~~~
- def do_reset_history(self, arg):
+ def do_reset_history(self, line):
"""Reset CLI history"""
while self.history:
self.history.pop()
- def do_launch(self, arg):
+ def do_launch(self, line):
"""If Voltha is not running yet, launch it"""
- pass
+ raise NotImplementedError('not implemented yet')
- def do_restart(self, arg):
+ def do_restart(self, line):
"""Launch Voltha, but if it is already running, terminate it first"""
pass
- def do_devices(self, arg):
- """List devices registered in Voltha"""
+ def do_adapters(self, line):
+ """List loaded adapter"""
+ stub = voltha_pb2.VolthaLocalServiceStub(self.get_channel())
+ res = stub.ListAdapters(Empty())
+ omit_fields = {}
+ print_pb_table('Adapters:', res.items, omit_fields, self.poutput)
+
+ def get_devices(self):
stub = voltha_pb2.VolthaLocalServiceStub(self.get_channel())
res = stub.ListDevices(Empty())
- for device in res.items:
- print self.colorize('# ====== device {}'.format(device.id), 'blue')
- print dumps(pb2dict(device), indent=4, sort_keys=True)
+ return res.items
- def do_logical_devices(self, arg):
+ def get_logical_devices(self):
+ stub = voltha_pb2.VolthaLocalServiceStub(self.get_channel())
+ res = stub.ListLogicalDevices(Empty())
+ return res.items
+
+ def do_devices(self, line):
+ """List devices registered in Voltha"""
+ devices = self.get_devices()
+ omit_fields = {
+ 'adapter',
+ 'vendor',
+ 'model',
+ 'hardware_version',
+ 'software_version',
+ 'firmware_version',
+ 'serial_number'
+ }
+ print_pb_table('Devices:', devices, omit_fields, self.poutput)
+
+ def do_logical_devices(self, line):
"""List logical devices in Voltha"""
stub = voltha_pb2.VolthaLocalServiceStub(self.get_channel())
res = stub.ListLogicalDevices(Empty())
- for logical_device in res.items:
- print self.colorize('# ====== logical device {}'.format(
- logical_device.id), 'blue')
- print dumps(pb2dict(logical_device), indent=4, sort_keys=True)
+ omit_fields = {
+ 'desc.mfr_desc',
+ 'desc.hw_desc',
+ 'desc.sw_desc',
+ 'desc.dp_desc',
+ 'desc.serial_number',
+ 'switch_features.capabilities'
+ }
+ print_pb_table('Logical devices:', res.items, omit_fields,
+ self.poutput)
- def do_device(self, arg):
+ def do_device(self, line):
"""Enter device level command mode"""
- device_id = arg or self.default_device_id
+ device_id = line.strip() or self.default_device_id
if not device_id:
raise Exception('<device-id> parameter needed')
sub = DeviceCli(self.get_channel, device_id)
sub.cmdloop()
- def do_logical_device(self, arg):
+ def do_logical_device(self, line):
"""Enter logical device level command mode"""
- logical_device_id = arg or self.default_logical_device_id
+ logical_device_id = line.strip() or self.default_logical_device_id
if not logical_device_id:
raise Exception('<logical-device-id> parameter needed')
sub = LogicalDeviceCli(self.get_channel, logical_device_id)
sub.cmdloop()
- def do_debug(self, arg):
+ def device_ids(self, force_refresh=False):
+ if force_refresh or self.device_ids is None or \
+ (time() - self.device_ids_cache_ts) > 1:
+ self.device_ids_cache = [d.id for d in self.get_devices()]
+ self.device_ids_cache_ts = time()
+ return self.device_ids_cache
+
+ def logical_device_ids(self, force_refresh=False):
+ if force_refresh or self.logical_device_ids is None or \
+ (time() - self.logical_device_ids_cache_ts) > 1:
+ self.logical_device_ids_cache = [d.id for d
+ in self.get_logical_devices()]
+ self.logical_device_ids_cache_ts = time()
+ return self.logical_device_ids_cache
+
+ def complete_device(self, text, line, begidx, endidx):
+ if not text:
+ completions = self.device_ids()[:]
+ else:
+ completions = [d for d in self.device_ids() if d.startswith(text)]
+ return completions
+
+ def complete_logical_device(self, text, line, begidx, endidx):
+ if not text:
+ completions = self.logical_device_ids()[:]
+ else:
+ completions = [d for d in self.logical_device_ids()
+ if d.startswith(text)]
+ return completions
+
+ def do_pdb(self, line):
"""Launch PDB debug prompt in CLI (for CLI development)"""
from pdb import set_trace
set_trace()
- def do_health(self, arg):
+ def do_health(self, line):
"""Show connectivity status to Voltha status"""
stub = voltha_pb2.HealthServiceStub(self.get_channel())
res = stub.GetHealthStatus(Empty())
- print dumps(pb2dict(res), indent=4)
+ self.poutput(dumps(pb2dict(res), indent=4))
- def do_test(self, arg):
+ def do_test(self, line):
"""Enter test mode, which makes a bunch on new commands available"""
sub = TestCli(self.history, self.get_channel)
sub.cmdloop()
@@ -182,7 +264,7 @@
default='00:0c:e2:31:40:00'),
make_option('-i', '--ip-address', action='store', dest='ip_address'),
])
- def do_preprovision_olt(self, arg, opts):
+ def do_preprovision_olt(self, line, opts):
"""Preprovision a new OLT with given device type"""
stub = voltha_pb2.VolthaLocalServiceStub(self.get_channel())
kw = dict(type=opts.device_type)
@@ -194,16 +276,16 @@
raise Exception('Either IP address or Mac Address is needed')
device = voltha_pb2.Device(**kw)
device = stub.CreateDevice(device)
- print 'success (device id = {})'.format(device.id)
+ self.poutput('success (device id = {})'.format(device.id))
self.default_device_id = device.id
- def do_activate_olt(self, arg):
+ def do_activate_olt(self, line):
"""
Activate an OLT. If the <id> is not provided, it will be on the last
pre-provisioned OLT.
"""
- device_id = arg or self.default_device_id
- print 'activating', device_id
+ device_id = line or self.default_device_id
+ self.poutput('activating {}'.format(device_id))
stub = voltha_pb2.VolthaLocalServiceStub(self.get_channel())
stub.ActivateDevice(voltha_pb2.ID(id=device_id))
@@ -214,32 +296,36 @@
assert device.parent_id
self.default_logical_device_id = device.parent_id
break
- print 'waiting for device to be activated...'
- sleep(1)
- print 'success (logical device id = {})'.format(
- self.default_logical_device_id)
+ self.poutput('waiting for device to be activated...')
+ sleep(.5)
+ self.poutput('success (logical device id = {})'.format(
+ self.default_logical_device_id))
- def do_arrive_onus(self, arg):
+ complete_activate_olt = VolthaCli.complete_device
+
+ def do_arrive_onus(self, line):
"""
Simulate the arrival of ONUs
"""
- device_id = arg or self.default_device_id
+ device_id = line or self.default_device_id
requests.get('http://{}/devices/{}/detect_onus'.format(
self.voltha_sim_rest, device_id
))
- def do_install_eapol_flow(self, arg):
+ complete_arrive_onus = VolthaCli.complete_device
+
+ def do_install_eapol_flow(self, line):
"""
Install an EAPOL flow on the given logical device. If device is not
given, it will be applied to logical device of the last pre-provisioned
OLT device.
"""
- logical_device_id = arg or self.default_logical_device_id
+ logical_device_id = line or self.default_logical_device_id
update = FlowTableUpdate(
id=logical_device_id,
flow_mod = mk_simple_flow_mod(
priority=2000,
- match_fields=[in_port(101), eth_type(0x888e)],
+ match_fields=[in_port(241), eth_type(0x888e)],
actions=[
push_vlan(0x8100),
set_field(vlan_vid(4096 + 4000)),
@@ -249,18 +335,22 @@
)
stub = voltha_pb2.VolthaLocalServiceStub(self.get_channel())
res = stub.UpdateLogicalDeviceFlowTable(update)
- print 'success', res
+ self.poutput('success ({})'.format(res))
- def do_send_simulated_upstream_eapol(self, arg):
+ complete_install_eapol_flow = VolthaCli.complete_logical_device
+
+ def do_send_simulated_upstream_eapol(self, line):
"""
Send an EAPOL upstream from a simulated OLT
"""
- device_id = arg or self.default_device_id
+ device_id = line or self.default_device_id
requests.get('http://{}/devices/{}/test_eapol_in'.format(
self.voltha_sim_rest, device_id
))
- def do_inject_eapol_start(self, arg):
+ complete_send_simulated_upstream_eapol = VolthaCli.complete_device
+
+ def do_inject_eapol_start(self, line):
"""
Send out an an EAPOL start message into the given Unix interface
"""
@@ -269,6 +359,7 @@
if __name__ == '__main__':
c = VolthaCli()
+ c.poutput(banner)
c.load_history()
c.cmdloop()
c.save_history()
diff --git a/cli/table.py b/cli/table.py
new file mode 100644
index 0000000..3eb828a
--- /dev/null
+++ b/cli/table.py
@@ -0,0 +1,121 @@
+#
+# Copyright 2016 the original author or authors.
+#
+# 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.
+#
+import sys
+
+from google.protobuf.message import Message
+from termcolor import colored
+
+_printfn = lambda l: sys.stdout.write(l + '\n')
+
+
+class TablePrinter(object):
+ """Simple tabular data printer utility. For usage, see bottom of file"""
+
+ def __init__(self):
+ self.max_field_lengths = {}
+ self.field_names = {}
+ self.cell_values = {}
+
+ def add_cell(self, row_number, field_key, field_name, value):
+ if not isinstance(value, str):
+ value = str(value)
+ self._add_field_type(field_key, field_name)
+ row = self.cell_values.setdefault(row_number, {})
+ row[field_key] = value
+ self._update_max_length(field_key, value)
+
+ def print_table(self, header=None, printfn=_printfn, dividers=10):
+
+ if header is not None:
+ printfn(header)
+
+ field_keys = sorted(self.field_names.keys())
+
+ if not field_keys:
+ printfn('table empty')
+ return
+
+ def p_sep():
+ printfn('+' + '+'.join(
+ [(self.max_field_lengths[k] + 2) * '-'
+ for k in field_keys]) + '+')
+
+ p_sep()
+
+ printfn('| ' + ' | '.join(
+ '%%%ds' % self.max_field_lengths[k] % self.field_names[k]
+ for k in field_keys) + ' |')
+ p_sep()
+
+ for i in range(len(self.cell_values)):
+ row = self.cell_values[i]
+ printfn(colored('| ' + ' | '.join(
+ '%%%ds' % self.max_field_lengths[k] % row.get(k, '')
+ for k in field_keys
+ ) + ' |'))
+ if not ((i + 1) % dividers):
+ p_sep()
+
+ if (i + 1) % dividers:
+ p_sep()
+
+ def _update_max_length(self, field_key, string):
+ length = len(string)
+ if length > self.max_field_lengths.get(field_key, 0):
+ self.max_field_lengths[field_key] = length
+
+ def _add_field_type(self, field_key, field_name):
+ if field_key not in self.field_names:
+ self.field_names[field_key] = field_name
+ self._update_max_length(field_key, field_name)
+ else:
+ assert self.field_names[field_key] == field_name
+
+
+def print_pb_table(header, items, fields_to_omit=None, printfn=_printfn):
+ from cli.utils import pb2dict
+
+ t = TablePrinter()
+ for row, obj in enumerate(items):
+ assert isinstance(obj, Message)
+
+ def add(_row, pb, prefix='', number=0):
+ d = pb2dict(pb)
+ for field in pb._fields:
+ fname = prefix + field.name
+ if fname in fields_to_omit:
+ continue
+ value = getattr(pb, field.name)
+ if isinstance(value, Message):
+ add(_row, value, fname + '.',
+ 100 * (number + field.number))
+ else:
+ t.add_cell(_row, number + field.number, fname,
+ d.get(field.name))
+
+ add(row, obj)
+
+ t.print_table(header, printfn)
+
+
+if __name__ == '__main__':
+ import random
+ t = TablePrinter()
+ for row in range(10):
+ t.add_cell(row, 0, 'id', row + 100)
+ t.add_cell(row, 1, 'name', 'Joe Somebody')
+ t.add_cell(row, 2, 'ows', '${}'.format(random.randint(10, 100000)))
+ t.print_table()
diff --git a/cli/utils.py b/cli/utils.py
index 5e3b7dd..941ad9c 100644
--- a/cli/utils.py
+++ b/cli/utils.py
@@ -1,9 +1,28 @@
-import os
+#
+# Copyright 2016 the original author or authors.
+#
+# 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.
+#
+
import sys
-import requests
+
from google.protobuf.json_format import MessageToDict
from termcolor import cprint, colored
-from os.path import join as pjoin
+
+from cli.table import TablePrinter
+
+
+_printfn = lambda l: sys.stdout.write(l + '\n')
def pb2dict(pb_msg):
@@ -87,49 +106,22 @@
}
-def print_flows(what, id, type, flows, groups):
+def print_flows(what, id, type, flows, groups, printfn=_printfn):
- print
- print ''.join([
+ header = ''.join([
'{} '.format(what),
colored(id, color='green', attrs=['bold']),
' (type: ',
colored(type, color='blue'),
')'
- ])
- print 'Flows ({}):'.format(len(flows))
+ ]) + '\nFlows ({}):'.format(len(flows))
- max_field_lengths = {}
- field_names = {}
-
- def update_max_length(field_key, string):
- length = len(string)
- if length > max_field_lengths.get(field_key, 0):
- max_field_lengths[field_key] = length
-
- def add_field_type(field_key, field_name):
- if field_key not in field_names:
- field_names[field_key] = field_name
- update_max_length(field_key, field_name)
- else:
- assert field_names[field_key] == field_name
-
- cell_values = {}
-
- # preprocess data
- if not flows:
- return
+ table = TablePrinter()
for i, flow in enumerate(flows):
- def add_field(field_key, field_name, value):
- add_field_type(field_key, field_name)
- row = cell_values.setdefault(i, {})
- row[field_key] = value
- update_max_length(field_key, value)
-
- add_field(0, 'table_id', value=str(flow['table_id']))
- add_field(1, 'priority', value=str(flow['priority']))
- add_field(2, 'cookie', p_cookie(flow['cookie']))
+ table.add_cell(i, 0, 'table_id', value=str(flow['table_id']))
+ table.add_cell(i, 1, 'priority', value=str(flow['priority']))
+ table.add_cell(i, 2, 'cookie', p_cookie(flow['cookie']))
assert flow['match']['type'] == 'OFPMT_OXM'
for field in flow['match']['oxm_fields']:
@@ -137,40 +129,20 @@
ofb = field['ofb_field']
assert not ofb['has_mask'], 'masked match not handled yet' # TODO
type = ofb['type'][len('OFPXMT_OFB_'):]
- add_field(*field_printers[type](ofb))
+ table.add_cell(i, *field_printers[type](ofb))
for instruction in flow['instructions']:
if instruction['type'] == 4:
for action in instruction['actions']['actions']:
type = action['type'][len('OFPAT_'):]
- add_field(*action_printers[type](action))
+ table.add_cell(i, *action_printers[type](action))
- # print header
- field_keys = sorted(field_names.keys())
- def p_sep():
- print '+' + '+'.join(
- [(max_field_lengths[k] + 2) * '-' for k in field_keys]) + '+'
-
- p_sep()
- print '| ' + ' | '.join(
- '%%%ds' % max_field_lengths[k] % field_names[k]
- for k in field_keys) + ' |'
- p_sep()
-
- # print values
- for i in xrange(len(flows)):
- row = cell_values[i]
- cprint('| ' + ' | '.join(
- '%%%ds' % max_field_lengths[k] % row.get(k, '')
- for k in field_keys
- ) + ' |')
- if not ((i + 1) % 3):
- p_sep()
-
- if ((i + 1) % 3):
- p_sep()
+ table.print_table(header, printfn)
# TODO groups TBF
assert len(groups) == 0
+def dict2line(d):
+ assert isinstance(d, dict)
+ return ', '.join('{}: {}'.format(k, v) for k, v in sorted(d.items()))
diff --git a/common/frameio/frameio.py b/common/frameio/frameio.py
index 27aed12..b215da3 100644
--- a/common/frameio/frameio.py
+++ b/common/frameio/frameio.py
@@ -139,7 +139,12 @@
def _dispatch(self, frame):
log.debug('calling-publisher', frame=hexify(frame))
- self.callback(self, frame)
+ try:
+ self.callback(self, frame)
+ except Exception, e:
+ log.exception('callback-error',
+ explanation='Callback failed while processing frame',
+ e=e)
def recv(self):
"""Called on the select thread when a packet arrives"""
diff --git a/voltha/adapters/tibit_olt/tibit_olt.py b/voltha/adapters/tibit_olt/tibit_olt.py
index 6a0ff9e..45d610f 100644
--- a/voltha/adapters/tibit_olt/tibit_olt.py
+++ b/voltha/adapters/tibit_olt/tibit_olt.py
@@ -17,39 +17,38 @@
"""
Tibit OLT device adapter
"""
-import scapy
-import structlog
import json
-
from uuid import uuid4
-from scapy.layers.inet import ICMP, IP
+import structlog
+from scapy.fields import StrField
from scapy.layers.l2 import Ether, Dot1Q
-from twisted.internet.defer import DeferredQueue, inlineCallbacks
+from scapy.packet import Packet, bind_layers
from twisted.internet import reactor
-
+from twisted.internet.defer import DeferredQueue, inlineCallbacks
from zope.interface import implementer
-from common.utils.asleep import asleep
-
from common.frameio.frameio import BpfProgramFilter
-from voltha.registry import registry
from voltha.adapters.interface import IAdapterInterface
+from voltha.adapters.tibit_olt.EOAM import EOAMPayload, DPoEOpcode_SetRequest
+from voltha.adapters.tibit_olt.EOAM_TLV import DOLTObject, \
+ PortIngressRuleClauseMatchLength02, PortIngressRuleResultForward, \
+ PortIngressRuleResultSet, PortIngressRuleResultInsert, \
+ PortIngressRuleTerminator, AddPortIngressRule, CablelabsOUI
+from voltha.adapters.tibit_olt.EOAM_TLV import PortIngressRuleHeader
+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.common_pb2 import LogLevel, ConnectStatus
+from voltha.protos.common_pb2 import OperStatus, AdminState
from voltha.protos.device_pb2 import Device, Port
from voltha.protos.device_pb2 import DeviceType, DeviceTypes
from voltha.protos.health_pb2 import HealthStatus
-from voltha.protos.common_pb2 import LogLevel, ConnectStatus
-from voltha.protos.common_pb2 import OperStatus, AdminState
-
from voltha.protos.logical_device_pb2 import LogicalDevice, LogicalPort
from voltha.protos.openflow_13_pb2 import ofp_desc, ofp_port, OFPPF_10GB_FD, \
OFPPF_FIBER, OFPPS_LIVE, ofp_switch_features, OFPC_PORT_STATS, \
OFPC_GROUP_STATS, OFPC_TABLE_STATS, OFPC_FLOW_STATS
-
-from scapy.packet import Packet, bind_layers
-from scapy.fields import StrField
+from voltha.registry import registry
log = structlog.get_logger()
@@ -156,7 +155,8 @@
# if we got response, we can fill out the device info, mark the device
# reachable
- jdev = json.loads(response.data[5:])
+ # jdev = json.loads(response.data[5:])
+ jdev = json.loads(response.payload.payload.body.load)
device.root = True
device.vendor = 'Tibit Communications, Inc.'
device.model = jdev.get('results', {}).get('device', 'DEVICE_UNKNOWN')
@@ -255,7 +255,7 @@
if 1: # TODO check if it is really what we expect, and wait if not
break
- jdev = json.loads(response.data[5:])
+ jdev = json.loads(response.payload.payload.body.load)
tibit_mac = ''
for macid in jdev['results']:
if macid['macid'] is None:
@@ -342,8 +342,78 @@
raise NotImplementedError()
def update_flows_bulk(self, device, flows, groups):
- log.debug('bulk-flow-update', device_id=device.id,
- flows=flows, groups=groups)
+ log.info('bulk-flow-update', device_id=device.id,
+ flows=flows, groups=groups)
+
+ 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
+
+ precedence = 255 - min(flow.priority / 256, 255)
+
+ if in_port == 2:
+ # Downstream rule
+ pass # TODO still ignores
+
+ elif in_port == 1:
+ # Upstream rule
+ req = DOLTObject()
+ req /= PortIngressRuleHeader(precedence=precedence)
+
+ for field in get_ofb_fields(flow):
+ if field.type == ETH_TYPE:
+ _type = field.eth_type
+ req /= PortIngressRuleClauseMatchLength02(
+ fieldcode=3,
+ operator=1,
+ match0=(_type >> 8) & 0xff,
+ match1=_type & 0xff)
+ elif field.type == IP_PROTO:
+ pass
+ # TODO etc
+
+ for action in get_actions(flow):
+
+ if action.type == OUTPUT:
+ req /= PortIngressRuleResultForward()
+
+ elif action.type == PUSH_VLAN:
+ if action.push.ethertype != 0x8100:
+ log.error('unhandled-ether-type',
+ ethertype=action.push.ethertype)
+ req /= PortIngressRuleResultInsert(fieldcode=7)
+
+ 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:
+ req /= PortIngressRuleResultSet(
+ fieldcode=7, value=field.vlan_vid & 0xfff)
+ else:
+ log.error('unsupported-action-set-field-type',
+ field_type=field.type)
+
+ else:
+ log.error('unsupported-action-type',
+ action_type=action.type)
+
+ req /= PortIngressRuleTerminator()
+ req /= AddPortIngressRule()
+
+ msg = (
+ Ether(dst=device.mac_address) /
+ Dot1Q(vlan=TIBIT_MGMT_VLAN, prio=TIBIT_MGMT_PRIORITY) /
+ EOAMPayload(
+ body=CablelabsOUI() / DPoEOpcode_SetRequest() / req)
+ )
+
+ self.io_port.send(str(msg))
+
+ else:
+ raise Exception('Port should be 1 or 2 by our convention')
def update_flows_incrementally(self, device, flow_changes, group_changes):
raise NotImplementedError()