blob: 41e04e800e345621467f760ff9807a8fb2fc2c1a [file] [log] [blame]
khenaidoofdbad6e2018-11-06 22:26:38 -05001#!/usr/bin/env python
2#
3# Copyright 2016 the original author or authors.
4#
5# Licensed under the Apache License, Version 2.0 (the "License");
6# you may not use this file except in compliance with the License.
7# You may obtain a copy of the License at
8#
9# http://www.apache.org/licenses/LICENSE-2.0
10#
11# Unless required by applicable law or agreed to in writing, software
12# distributed under the License is distributed on an "AS IS" BASIS,
13# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14# See the License for the specific language governing permissions and
15# limitations under the License.
16#
17import argparse
18import os
19import readline
20import sys
21from optparse import make_option
22from time import sleep, time
23
24import grpc
25import requests
26from cmd2 import Cmd, options
27from consul import Consul
28from google.protobuf.empty_pb2 import Empty
29from simplejson import dumps
30
31from device import DeviceCli
32from omci import OmciCli
33from alarm_filters import AlarmFiltersCli
34from logical_device import LogicalDeviceCli
35from table import print_pb_list_as_table
Arun Aroraed4b7602019-04-02 18:42:37 +000036from pyvoltha.common.openflow.utils import *
37from voltha_protos import voltha_pb2
38from voltha_protos import voltha_pb2_grpc
39from voltha_protos.openflow_13_pb2 import FlowTableUpdate, FlowGroupTableUpdate
khenaidoofdbad6e2018-11-06 22:26:38 -050040
khenaidoofdbad6e2018-11-06 22:26:38 -050041from python.cli.utils import pb2dict
42
43defs = dict(
44 # config=os.environ.get('CONFIG', './cli.yml'),
45 consul=os.environ.get('CONSUL', 'localhost:8500'),
46 voltha_grpc_endpoint=os.environ.get('VOLTHA_GRPC_ENDPOINT',
47 'localhost:50057'),
48 voltha_sim_rest_endpoint=os.environ.get('VOLTHA_SIM_REST_ENDPOINT',
49 'localhost:18880'),
50 global_request=os.environ.get('GLOBAL_REQUEST', False)
51)
52
53banner = """\
54 _ _ _ ___ _ ___
55__ _____| | |_| |_ __ _ / __| | |_ _|
56\ V / _ \ | _| ' \/ _` | | (__| |__ | |
57 \_/\___/_|\__|_||_\__,_| \___|____|___|
58(to exit type quit or hit Ctrl-D)
59"""
60
61
62class VolthaCli(Cmd):
63 prompt = 'voltha'
64 history_file_name = '.voltha_cli_history'
65
66 # Settable CLI parameters
67 voltha_grpc = 'localhost:50057'
68 voltha_sim_rest = 'localhost:18880'
69 global_request = False
70 max_history_lines = 500
71 default_device_id = None
72 default_logical_device_id = None
73
74 Cmd.settable.update(dict(
75 voltha_grpc='Voltha GRPC endpoint in form of <host>:<port>',
76 voltha_sim_rest='Voltha simulation back door for testing in form '
77 'of <host>:<port>',
78 max_history_lines='Maximum number of history lines stored across '
79 'sessions',
80 default_device_id='Device id used when no device id is specified',
81 default_logical_device_id='Logical device id used when no device id '
82 'is specified',
83 ))
84
85 # cleanup of superfluous commands from cmd2
86 del Cmd.do_cmdenvironment
87 del Cmd.do_load
88 del Cmd.do__relative_load
89
90 def __init__(self, voltha_grpc, voltha_sim_rest, global_request=False):
91
khenaidoo5aadea02018-11-07 14:30:11 -050092 VolthaCli.voltha_grpc = voltha_grpc
khenaidoofdbad6e2018-11-06 22:26:38 -050093 VolthaCli.voltha_sim_rest = voltha_sim_rest
94 VolthaCli.global_request = global_request
95 Cmd.__init__(self)
96 self.prompt = '(' + self.colorize(
97 self.colorize(self.prompt, 'blue'), 'bold') + ') '
98 self.channel = None
99 self.stub = None
100 self.device_ids_cache = None
101 self.device_ids_cache_ts = time()
102 self.logical_device_ids_cache = None
103 self.logical_device_ids_cache_ts = time()
104
105 # we override cmd2's method to avoid its optparse conflicting with our
106 # command line parsing
107 def cmdloop(self):
108 self._cmdloop()
109
110 def load_history(self):
111 """Load saved command history from local history file"""
112 try:
113 with file(self.history_file_name, 'r') as f:
114 for line in f.readlines():
115 stripped_line = line.strip()
116 self.history.append(stripped_line)
117 readline.add_history(stripped_line)
118 except IOError:
119 pass # ignore if file cannot be read
120
121 def save_history(self):
122 try:
123 with open(self.history_file_name, 'w') as f:
124 f.write('\n'.join(self.history[-self.max_history_lines:]))
125 except IOError as e:
126 self.perror('Could not save history in {}: {}'.format(
127 self.history_file_name, e))
128 else:
129 self.poutput('History saved as {}'.format(
130 self.history_file_name))
131
132 def perror(self, errmsg, statement=None):
133 # Touch it up to make sure error is prefixed and colored
134 Cmd.perror(self, self.colorize('***ERROR: ', 'red') + errmsg,
135 statement)
136
137 def get_channel(self):
138 if self.channel is None:
139 self.channel = grpc.insecure_channel(self.voltha_grpc)
140 return self.channel
141
142 def get_stub(self):
143 if self.stub is None:
Arun Aroraed4b7602019-04-02 18:42:37 +0000144 self.stub = voltha_pb2_grpc.VolthaServiceStub(self.get_channel())
khenaidoofdbad6e2018-11-06 22:26:38 -0500145 # self.stub = \
146 # voltha_pb2.VolthaGlobalServiceStub(self.get_channel()) \
147 # if self.global_request else \
148 # voltha_pb2.VolthaLocalServiceStub(self.get_channel())
149 return self.stub
150
151 # ~~~~~~~~~~~~~~~~~ ACTUAL COMMAND IMPLEMENTATIONS ~~~~~~~~~~~~~~~~~~~~~~~~
152
153 def do_reset_history(self, line):
154 """Reset CLI history"""
155 while self.history:
156 self.history.pop()
157
khenaidoo6f2fbe32019-01-18 16:16:50 -0500158 @options([
159 make_option('-p', '--package', action="store", dest='package',
160 help="Package Name"),
161 make_option('-l', '--level', action='store', dest='level'),
162 ])
163 def do_log(self, line, opts):
164
165 def logLevel(level):
166 switcher= {
167 "DEBUG": 0,
168 "INFO":1,
169 "WARNING":2,
170 "ERROR":3,
171 "CRITICAL":4,
172 "FATAL":5
173 }
174 return switcher.get(level, 3)
175
176 if opts.level is None:
177 return
178
179 stub = self.get_stub()
180 kw = dict()
181 if opts.package:
182 kw['package_name'] = "github.com/opencord/voltha-go/" + opts.package
183
184 kw['level'] = logLevel(opts.level.upper())
185
186 try:
187 logging = voltha_pb2.Logging(**kw)
188 stub.UpdateLogLevel(logging)
189 self.poutput('success')
190 except Exception as e:
191 self.poutput('Exception - {})'.format(e))
192
khenaidoofdbad6e2018-11-06 22:26:38 -0500193 def do_launch(self, line):
194 """If Voltha is not running yet, launch it"""
195 raise NotImplementedError('not implemented yet')
196
197 def do_restart(self, line):
198 """Launch Voltha, but if it is already running, terminate it first"""
199 pass
200
201 def do_adapters(self, line):
202 """List loaded adapter"""
203 stub = self.get_stub()
204 res = stub.ListAdapters(Empty())
205 omit_fields = {'config.log_level', 'logical_device_ids'}
206 print_pb_list_as_table('Adapters:', res.items, omit_fields, self.poutput)
207
208 def get_devices(self):
209 stub = self.get_stub()
210 res = stub.ListDevices(Empty())
211 return res.items
212
213 def get_logical_devices(self):
214 stub = self.get_stub()
215 res = stub.ListLogicalDevices(Empty())
216 return res.items
217
218 def do_devices(self, line):
219 """List devices registered in Voltha"""
220 devices = self.get_devices()
221 omit_fields = {
222 'adapter',
223 'vendor',
224 'model',
225 'hardware_version',
226 'images',
227 'firmware_version',
serkant.uluderyaee9755c2019-04-15 14:56:19 -0700228 'vendor_id',
229 'flows',
230 'pm_configs',
231 'proxy_address',
232 'ports'
khenaidoofdbad6e2018-11-06 22:26:38 -0500233 }
234 print_pb_list_as_table('Devices:', devices, omit_fields, self.poutput)
235
236 def do_logical_devices(self, line):
237 """List logical devices in Voltha"""
238 stub = self.get_stub()
239 res = stub.ListLogicalDevices(Empty())
240 omit_fields = {
241 'desc.mfr_desc',
242 'desc.hw_desc',
243 'desc.sw_desc',
244 'desc.dp_desc',
245 'desc.serial_number',
246 'switch_features.capabilities'
247 }
248 presfns = {
249 'datapath_id': lambda x: "{0:0{1}x}".format(int(x), 16)
250 }
251 print_pb_list_as_table('Logical devices:', res.items, omit_fields,
252 self.poutput, presfns=presfns)
253
254 def do_device(self, line):
255 """Enter device level command mode"""
256 device_id = line.strip() or self.default_device_id
257 if not device_id:
258 raise Exception('<device-id> parameter needed')
259 if device_id not in self.device_ids():
260 self.poutput( self.colorize('Error: ', 'red') +
261 'There is no such device')
262 raise Exception('<device-id> is not a valid one')
263 sub = DeviceCli(device_id, self.get_stub)
264 sub.cmdloop()
265
266 def do_logical_device(self, line):
267 """Enter logical device level command mode"""
268 logical_device_id = line.strip() or self.default_logical_device_id
269 if not logical_device_id:
270 raise Exception('<logical-device-id> parameter needed')
271 if logical_device_id not in self.logical_device_ids():
272 self.poutput( self.colorize('Error: ', 'red') +
273 'There is no such device')
274 raise Exception('<logical-device-id> is not a valid one')
275 sub = LogicalDeviceCli(logical_device_id, self.get_stub)
276 sub.cmdloop()
277
278 def device_ids(self, force_refresh=False):
279 if force_refresh or self.device_ids is None or \
280 (time() - self.device_ids_cache_ts) > 1:
281 self.device_ids_cache = [d.id for d in self.get_devices()]
282 self.device_ids_cache_ts = time()
283 return self.device_ids_cache
284
285 def logical_device_ids(self, force_refresh=False):
286 if force_refresh or self.logical_device_ids is None or \
287 (time() - self.logical_device_ids_cache_ts) > 1:
288 self.logical_device_ids_cache = [d.id for d
289 in self.get_logical_devices()]
290 self.logical_device_ids_cache_ts = time()
291 return self.logical_device_ids_cache
292
293 def complete_device(self, text, line, begidx, endidx):
294 if not text:
295 completions = self.device_ids()[:]
296 else:
297 completions = [d for d in self.device_ids() if d.startswith(text)]
298 return completions
299
300 def complete_logical_device(self, text, line, begidx, endidx):
301 if not text:
302 completions = self.logical_device_ids()[:]
303 else:
304 completions = [d for d in self.logical_device_ids()
305 if d.startswith(text)]
306 return completions
307
khenaidoofdbad6e2018-11-06 22:26:38 -0500308 def do_omci(self, line):
309 """omci <device_ID> - Enter OMCI level command mode"""
310
311 device_id = line.strip() or self.default_device_id
312 if not device_id:
313 raise Exception('<device-id> parameter needed')
314 sub = OmciCli(device_id, self.get_stub)
315 sub.cmdloop()
316
317 def do_pdb(self, line):
318 """Launch PDB debug prompt in CLI (for CLI development)"""
319 from pdb import set_trace
320 set_trace()
321
322 def do_version(self, line):
323 """Show the VOLTHA core version"""
324 stub = self.get_stub()
325 voltha = stub.GetVoltha(Empty())
326 self.poutput('{}'.format(voltha.version))
327
328 def do_health(self, line):
329 """Show connectivity status to Voltha status"""
330 stub = voltha_pb2.HealthServiceStub(self.get_channel())
331 res = stub.GetHealthStatus(Empty())
332 self.poutput(dumps(pb2dict(res), indent=4))
333
334 @options([
335 make_option('-t', '--device-type', action="store", dest='device_type',
336 help="Device type", default='simulated_olt'),
337 make_option('-m', '--mac-address', action='store', dest='mac_address',
338 default='00:0c:e2:31:40:00'),
339 make_option('-i', '--ip-address', action='store', dest='ip_address'),
340 make_option('-H', '--host_and_port', action='store',
341 dest='host_and_port'),
342 ])
343 def do_preprovision_olt(self, line, opts):
344 """Preprovision a new OLT with given device type"""
345 stub = self.get_stub()
346 kw = dict(type=opts.device_type)
347 if opts.host_and_port:
348 kw['host_and_port'] = opts.host_and_port
349 elif opts.ip_address:
350 kw['ipv4_address'] = opts.ip_address
351 elif opts.mac_address:
352 kw['mac_address'] = opts.mac_address.lower()
353 else:
354 raise Exception('Either IP address or Mac Address is needed')
355 # Pass any extra arguments past '--' to the device as custom arguments
356 kw['extra_args'] = line
357
358 device = voltha_pb2.Device(**kw)
359 device = stub.CreateDevice(device)
360 self.poutput('success (device id = {})'.format(device.id))
361 self.default_device_id = device.id
362
363 def do_enable(self, line):
364 """
365 Enable a device. If the <id> is not provided, it will be on the last
366 pre-provisioned device.
367 """
368 device_id = line or self.default_device_id
369 if device_id not in self.device_ids():
370 self.poutput('Error: There is no such preprovisioned device')
371 return
372
373 try:
374 stub = self.get_stub()
375 device = stub.GetDevice(voltha_pb2.ID(id=device_id))
376 if device.admin_state == voltha_pb2.AdminState.ENABLED:
377 if device.oper_status != voltha_pb2.OperStatus.ACTIVATING:
378 self.poutput('Error: Device is already enabled')
379 return
380 else:
381 stub.EnableDevice(voltha_pb2.ID(id=device_id))
382 self.poutput('enabling {}'.format(device_id))
383
384 while True:
385 device = stub.GetDevice(voltha_pb2.ID(id=device_id))
386 # If this is an OLT then acquire logical device id
387 if device.oper_status == voltha_pb2.OperStatus.ACTIVE:
388 if device.type.endswith('_olt'):
389 assert device.parent_id
390 self.default_logical_device_id = device.parent_id
391 self.poutput('success (logical device id = {})'.format(
392 self.default_logical_device_id))
393 else:
394 self.poutput('success (device id = {})'.format(device.id))
395 break
396 self.poutput('waiting for device to be enabled...')
397 sleep(.5)
398 except Exception as e:
399 self.poutput('Error enabling {}. Error:{}'.format(device_id, e))
400
401 complete_activate_olt = complete_device
402
403 def do_reboot(self, line):
404 """
405 Rebooting a device. ID of the device needs to be provided
406 """
407 device_id = line or self.default_device_id
408 self.poutput('rebooting {}'.format(device_id))
409 try:
410 stub = self.get_stub()
411 stub.RebootDevice(voltha_pb2.ID(id=device_id))
412 self.poutput('rebooted {}'.format(device_id))
413 except Exception as e:
414 self.poutput('Error rebooting {}. Error:{}'.format(device_id, e))
415
416 def do_self_test(self, line):
417 """
418 Self Test a device. ID of the device needs to be provided
419 """
420 device_id = line or self.default_device_id
421 self.poutput('Self Testing {}'.format(device_id))
422 try:
423 stub = self.get_stub()
424 res = stub.SelfTest(voltha_pb2.ID(id=device_id))
425 self.poutput('Self Tested {}'.format(device_id))
426 self.poutput(dumps(pb2dict(res), indent=4))
427 except Exception as e:
428 self.poutput('Error in self test {}. Error:{}'.format(device_id, e))
429
430 def do_delete(self, line):
431 """
432 Deleting a device. ID of the device needs to be provided
433 """
434 device_id = line or self.default_device_id
435 self.poutput('deleting {}'.format(device_id))
436 try:
437 stub = self.get_stub()
438 stub.DeleteDevice(voltha_pb2.ID(id=device_id))
439 self.poutput('deleted {}'.format(device_id))
440 except Exception as e:
441 self.poutput('Error deleting {}. Error:{}'.format(device_id, e))
442
443 def do_disable(self, line):
444 """
445 Disable a device. ID of the device needs to be provided
446 """
447 device_id = line
448 if device_id not in self.device_ids():
449 self.poutput('Error: There is no such device')
450 return
451 try:
452 stub = self.get_stub()
453 device = stub.GetDevice(voltha_pb2.ID(id=device_id))
454 if device.admin_state == voltha_pb2.AdminState.DISABLED:
455 self.poutput('Error: Device is already disabled')
456 return
457 stub.DisableDevice(voltha_pb2.ID(id=device_id))
458 self.poutput('disabling {}'.format(device_id))
459
460 # Do device query and verify that the device admin status is
461 # DISABLED and Operational Status is unknown
462 device = stub.GetDevice(voltha_pb2.ID(id=device_id))
463 if device.admin_state == voltha_pb2.AdminState.DISABLED:
464 self.poutput('disabled successfully {}'.format(device_id))
465 else:
466 self.poutput('disabling failed {}. Admin State:{} '
467 'Operation State: {}'.format(device_id,
468 device.admin_state,
469 device.oper_status))
470 except Exception as e:
471 self.poutput('Error disabling {}. Error:{}'.format(device_id, e))
472
473 def do_test(self, line):
474 """Enter test mode, which makes a bunch on new commands available"""
475 sub = TestCli(self.history, self.voltha_grpc,
476 self.get_stub, self.voltha_sim_rest)
477 sub.cmdloop()
478
479 def do_alarm_filters(self, line):
480 sub = AlarmFiltersCli(self.get_stub)
481 sub.cmdloop()
482
483
484class TestCli(VolthaCli):
485 def __init__(self, history, voltha_grpc, get_stub, voltha_sim_rest):
486 VolthaCli.__init__(self, voltha_grpc, voltha_sim_rest)
487 self.history = history
488 self.get_stub = get_stub
489 self.prompt = '(' + self.colorize(self.colorize('test', 'cyan'),
490 'bold') + ') '
491
492 def get_device(self, device_id, depth=0):
493 stub = self.get_stub()
494 res = stub.GetDevice(voltha_pb2.ID(id=device_id),
495 metadata=(('get-depth', str(depth)),))
496 return res
497
498 def do_arrive_onus(self, line):
499 """
500 Simulate the arrival of ONUs (available only on simulated_olt)
501 """
502 device_id = line or self.default_device_id
503
504 # verify that device is of type simulated_olt
505 device = self.get_device(device_id)
506 assert device.type == 'simulated_olt', (
507 'Cannot use it on this device type (only on simulated_olt type)')
508
509 requests.get('http://{}/devices/{}/detect_onus'.format(
510 self.voltha_sim_rest, device_id
511 ))
512
513 complete_arrive_onus = VolthaCli.complete_device
514
515 def get_logical_ports(self, logical_device_id):
516 """
517 Return the NNI port number and the first usable UNI port of logical
518 device, and the vlan associated with the latter.
519 """
520 stub = self.get_stub()
521 ports = stub.ListLogicalDevicePorts(
522 voltha_pb2.ID(id=logical_device_id)).items
523 nni = None
524 unis = []
525 for port in ports:
526 if port.root_port:
527 assert nni is None, "There shall be only one root port"
528 nni = port.ofp_port.port_no
529 else:
530 uni = port.ofp_port.port_no
531 uni_device = self.get_device(port.device_id)
532 vlan = uni_device.vlan
533 unis.append((uni, vlan))
534
535 assert nni is not None, "No NNI port found"
536 assert unis, "Not a single UNI?"
537
538 return nni, unis
539
540 def do_install_eapol_flow(self, line):
541 """
542 Install an EAPOL flow on the given logical device. If device is not
543 given, it will be applied to logical device of the last pre-provisioned
544 OLT device.
545 """
546
547 logical_device_id = line or self.default_logical_device_id
548
549 # gather NNI and UNI port IDs
550 nni_port_no, unis = self.get_logical_ports(logical_device_id)
551
552 # construct and push flow rule
553 stub = self.get_stub()
554 print "I am now here", unis
555 for uni_port_no, _ in unis:
556 update = FlowTableUpdate(
557 id=logical_device_id,
558 flow_mod=mk_simple_flow_mod(
559 priority=2000,
560 match_fields=[in_port(uni_port_no), eth_type(0x888e)],
561 actions=[
562 # push_vlan(0x8100),
563 # set_field(vlan_vid(4096 + 4000)),
564 output(ofp.OFPP_CONTROLLER)
565 ]
566 )
567 )
568 print "I am now here"
569 res = stub.UpdateLogicalDeviceFlowTable(update)
570 self.poutput('success for uni {} ({})'.format(uni_port_no, res))
571
572 complete_install_eapol_flow = VolthaCli.complete_logical_device
573
574 def do_install_all_controller_bound_flows(self, line):
575 """
576 Install all flow rules for controller bound flows, including EAPOL,
577 IGMP and DHCP. If device is not given, it will be applied to logical
578 device of the last pre-provisioned OLT device.
579 """
580 logical_device_id = line or self.default_logical_device_id
581
582 # gather NNI and UNI port IDs
583 nni_port_no, unis = self.get_logical_ports(logical_device_id)
584
585 # construct and push flow rules
586 stub = self.get_stub()
587
588 for uni_port_no, _ in unis:
589 stub.UpdateLogicalDeviceFlowTable(FlowTableUpdate(
590 id=logical_device_id,
591 flow_mod=mk_simple_flow_mod(
592 priority=2000,
593 match_fields=[
594 in_port(uni_port_no),
595 eth_type(0x888e)
596 ],
597 actions=[output(ofp.OFPP_CONTROLLER)]
598 )
599 ))
600 stub.UpdateLogicalDeviceFlowTable(FlowTableUpdate(
601 id=logical_device_id,
602 flow_mod=mk_simple_flow_mod(
603 priority=1000,
604 match_fields=[
605 in_port(uni_port_no),
606 eth_type(0x800),
607 ip_proto(2)
608 ],
609 actions=[output(ofp.OFPP_CONTROLLER)]
610 )
611 ))
612 stub.UpdateLogicalDeviceFlowTable(FlowTableUpdate(
613 id=logical_device_id,
614 flow_mod=mk_simple_flow_mod(
615 priority=1000,
616 match_fields=[
617 in_port(uni_port_no),
618 eth_type(0x800),
619 ip_proto(17),
620 udp_dst(67)
621 ],
622 actions=[output(ofp.OFPP_CONTROLLER)]
623 )
624 ))
625 self.poutput('success')
626
627 complete_install_all_controller_bound_flows = \
628 VolthaCli.complete_logical_device
629
630 def do_install_all_sample_flows(self, line):
631 """
632 Install all flows that are representative of the virtualized access
633 scenario in a PON network.
634 """
635 logical_device_id = line or self.default_logical_device_id
636
637 # gather NNI and UNI port IDs
638 nni_port_no, unis = self.get_logical_ports(logical_device_id)
639
640 # construct and push flow rules
641 stub = self.get_stub()
642
643 for uni_port_no, c_vid in unis:
644 # Controller-bound flows
645 stub.UpdateLogicalDeviceFlowTable(FlowTableUpdate(
646 id=logical_device_id,
647 flow_mod=mk_simple_flow_mod(
648 priority=2000,
649 match_fields=[in_port(uni_port_no), eth_type(0x888e)],
650 actions=[
651 # push_vlan(0x8100),
652 # set_field(vlan_vid(4096 + 4000)),
653 output(ofp.OFPP_CONTROLLER)
654 ]
655 )
656 ))
657 stub.UpdateLogicalDeviceFlowTable(FlowTableUpdate(
658 id=logical_device_id,
659 flow_mod=mk_simple_flow_mod(
660 priority=1000,
661 match_fields=[eth_type(0x800), ip_proto(2)],
662 actions=[output(ofp.OFPP_CONTROLLER)]
663 )
664 ))
665 stub.UpdateLogicalDeviceFlowTable(FlowTableUpdate(
666 id=logical_device_id,
667 flow_mod=mk_simple_flow_mod(
668 priority=1000,
669 match_fields=[eth_type(0x800), ip_proto(17), udp_dst(67)],
670 actions=[output(ofp.OFPP_CONTROLLER)]
671 )
672 ))
673
674 # Unicast flows:
675 # Downstream flow 1
676 stub.UpdateLogicalDeviceFlowTable(FlowTableUpdate(
677 id=logical_device_id,
678 flow_mod=mk_simple_flow_mod(
679 priority=500,
680 match_fields=[
681 in_port(nni_port_no),
682 vlan_vid(4096 + 1000),
683 metadata(c_vid) # here to mimic an ONOS artifact
684 ],
685 actions=[pop_vlan()],
686 next_table_id=1
687 )
688 ))
689 # Downstream flow 2
690 stub.UpdateLogicalDeviceFlowTable(FlowTableUpdate(
691 id=logical_device_id,
692 flow_mod=mk_simple_flow_mod(
693 priority=500,
694 table_id=1,
695 match_fields=[in_port(nni_port_no), vlan_vid(4096 + c_vid)],
696 actions=[set_field(vlan_vid(4096 + 0)), output(uni_port_no)]
697 )
698 ))
699 # Upstream flow 1 for 0-tagged case
700 stub.UpdateLogicalDeviceFlowTable(FlowTableUpdate(
701 id=logical_device_id,
702 flow_mod=mk_simple_flow_mod(
703 priority=500,
704 match_fields=[in_port(uni_port_no), vlan_vid(4096 + 0)],
705 actions=[set_field(vlan_vid(4096 + c_vid))],
706 next_table_id=1
707 )
708 ))
709 # Upstream flow 1 for untagged case
710 stub.UpdateLogicalDeviceFlowTable(FlowTableUpdate(
711 id=logical_device_id,
712 flow_mod=mk_simple_flow_mod(
713 priority=500,
714 match_fields=[in_port(uni_port_no), vlan_vid(0)],
715 actions=[push_vlan(0x8100), set_field(vlan_vid(4096 + c_vid))],
716 next_table_id=1
717 )
718 ))
719 # Upstream flow 2 for s-tag
720 stub.UpdateLogicalDeviceFlowTable(FlowTableUpdate(
721 id=logical_device_id,
722 flow_mod=mk_simple_flow_mod(
723 priority=500,
724 table_id=1,
725 match_fields=[in_port(uni_port_no), vlan_vid(4096 + c_vid)],
726 actions=[
727 push_vlan(0x8100),
728 set_field(vlan_vid(4096 + 1000)),
729 output(nni_port_no)
730 ]
731 )
732 ))
733
734 # Push a few multicast flows
735 # 1st with one bucket for our uni 0
736 stub.UpdateLogicalDeviceFlowGroupTable(FlowGroupTableUpdate(
737 id=logical_device_id,
738 group_mod=mk_multicast_group_mod(
739 group_id=1,
740 buckets=[
741 ofp.ofp_bucket(actions=[
742 pop_vlan(),
743 output(unis[0][0])
744 ])
745 ]
746 )
747 ))
748 stub.UpdateLogicalDeviceFlowTable(FlowTableUpdate(
749 id=logical_device_id,
750 flow_mod=mk_simple_flow_mod(
751 priority=1000,
752 match_fields=[
753 in_port(nni_port_no),
754 eth_type(0x800),
755 vlan_vid(4096 + 140),
756 ipv4_dst(0xe4010101)
757 ],
758 actions=[group(1)]
759 )
760 ))
761
762 # 2nd with one bucket for uni 0 and 1
763 stub.UpdateLogicalDeviceFlowGroupTable(FlowGroupTableUpdate(
764 id=logical_device_id,
765 group_mod=mk_multicast_group_mod(
766 group_id=2,
767 buckets=[
768 ofp.ofp_bucket(actions=[pop_vlan(), output(unis[0][0])])
769 # ofp.ofp_bucket(actions=[pop_vlan(), output(unis[1][0])])
770 ]
771 )
772 ))
773 stub.UpdateLogicalDeviceFlowTable(FlowTableUpdate(
774 id=logical_device_id,
775 flow_mod=mk_simple_flow_mod(
776 priority=1000,
777 match_fields=[
778 in_port(nni_port_no),
779 eth_type(0x800),
780 vlan_vid(4096 + 140),
781 ipv4_dst(0xe4020202)
782 ],
783 actions=[group(2)]
784 )
785 ))
786
787 # 3rd with empty bucket
788 stub.UpdateLogicalDeviceFlowGroupTable(FlowGroupTableUpdate(
789 id=logical_device_id,
790 group_mod=mk_multicast_group_mod(
791 group_id=3,
792 buckets=[]
793 )
794 ))
795 stub.UpdateLogicalDeviceFlowTable(FlowTableUpdate(
796 id=logical_device_id,
797 flow_mod=mk_simple_flow_mod(
798 priority=1000,
799 match_fields=[
800 in_port(nni_port_no),
801 eth_type(0x800),
802 vlan_vid(4096 + 140),
803 ipv4_dst(0xe4030303)
804 ],
805 actions=[group(3)]
806 )
807 ))
808
809 self.poutput('success')
810
811 complete_install_all_sample_flows = VolthaCli.complete_logical_device
812
813 def do_install_dhcp_flows(self, line):
814 """
815 Install all dhcp flows that are representative of the virtualized access
816 scenario in a PON network.
817 """
818 logical_device_id = line or self.default_logical_device_id
819
820 # gather NNI and UNI port IDs
821 nni_port_no, unis = self.get_logical_ports(logical_device_id)
822
823 # construct and push flow rules
824 stub = self.get_stub()
825
826 # Controller-bound flows
827 for uni_port_no, _ in unis:
828 stub.UpdateLogicalDeviceFlowTable(FlowTableUpdate(
829 id=logical_device_id,
830 flow_mod=mk_simple_flow_mod(
831 priority=1000,
832 match_fields=[
833 in_port(uni_port_no),
834 eth_type(0x800),
835 ip_proto(17),
836 udp_dst(67)
837 ],
838 actions=[output(ofp.OFPP_CONTROLLER)]
839 )
840 ))
841
842 self.poutput('success')
843
844 complete_install_dhcp_flows = VolthaCli.complete_logical_device
845
846 def do_delete_all_flows(self, line):
847 """
848 Remove all flows and flow groups from given logical device
849 """
850 logical_device_id = line or self.default_logical_device_id
851 stub = self.get_stub()
852 stub.UpdateLogicalDeviceFlowTable(FlowTableUpdate(
853 id=logical_device_id,
854 flow_mod=ofp.ofp_flow_mod(
855 command=ofp.OFPFC_DELETE,
856 table_id=ofp.OFPTT_ALL,
857 cookie_mask=0,
858 out_port=ofp.OFPP_ANY,
859 out_group=ofp.OFPG_ANY
860 )
861 ))
862 stub.UpdateLogicalDeviceFlowGroupTable(FlowGroupTableUpdate(
863 id=logical_device_id,
864 group_mod=ofp.ofp_group_mod(
865 command=ofp.OFPGC_DELETE,
866 group_id=ofp.OFPG_ALL
867 )
868 ))
869 self.poutput('success')
870
871 complete_delete_all_flows = VolthaCli.complete_logical_device
872
873 def do_send_simulated_upstream_eapol(self, line):
874 """
875 Send an EAPOL upstream from a simulated OLT
876 """
877 device_id = line or self.default_device_id
878 requests.get('http://{}/devices/{}/test_eapol_in'.format(
879 self.voltha_sim_rest, device_id
880 ))
881
882 complete_send_simulated_upstream_eapol = VolthaCli.complete_device
883
884 def do_inject_eapol_start(self, line):
885 """
886 Send out an an EAPOL start message into the given Unix interface
887 """
888 pass
889
890
891if __name__ == '__main__':
892
893 parser = argparse.ArgumentParser()
894
895 _help = '<hostname>:<port> to consul agent (default: %s)' % defs['consul']
896 parser.add_argument(
897 '-C', '--consul', action='store', default=defs['consul'], help=_help)
898
899 _help = 'Lookup Voltha endpoints based on service entries in Consul'
900 parser.add_argument(
901 '-L', '--lookup', action='store_true', help=_help)
902
903 _help = 'All requests to the Voltha gRPC service are global'
904 parser.add_argument(
905 '-G', '--global_request', action='store_true', help=_help)
906
907 _help = '<hostname>:<port> of Voltha gRPC service (default={})'.format(
908 defs['voltha_grpc_endpoint'])
909 parser.add_argument('-g', '--grpc-endpoint', action='store',
910 default=defs['voltha_grpc_endpoint'], help=_help)
911
912 _help = '<hostname>:<port> of Voltha simulated adapter backend for ' \
913 'testing (default={})'.format(
914 defs['voltha_sim_rest_endpoint'])
915 parser.add_argument('-s', '--sim-rest-endpoint', action='store',
916 default=defs['voltha_sim_rest_endpoint'], help=_help)
917
918 args = parser.parse_args()
919
920 if args.lookup:
921 host = args.consul.split(':')[0].strip()
922 port = int(args.consul.split(':')[1].strip())
923 consul = Consul(host=host, port=port)
924
925 _, services = consul.catalog.service('voltha-grpc')
926 if not services:
927 print('No voltha-grpc service registered in consul; exiting')
928 sys.exit(1)
929 args.grpc_endpoint = '{}:{}'.format(services[0]['ServiceAddress'],
930 services[0]['ServicePort'])
931
932 _, services = consul.catalog.service('voltha-sim-rest')
933 if not services:
934 print('No voltha-sim-rest service registered in consul; exiting')
935 sys.exit(1)
936 args.sim_rest_endpoint = '{}:{}'.format(services[0]['ServiceAddress'],
937 services[0]['ServicePort'])
938
939 c = VolthaCli(args.grpc_endpoint, args.sim_rest_endpoint,
940 args.global_request)
941 c.poutput(banner)
942 c.load_history()
943 c.cmdloop()
944 c.save_history()