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