blob: 0f5c618b567157938415eb21f4a2a2e14b7f886c [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',
228 'vendor_id'
229 }
230 print_pb_list_as_table('Devices:', devices, omit_fields, self.poutput)
231
232 def do_logical_devices(self, line):
233 """List logical devices in Voltha"""
234 stub = self.get_stub()
235 res = stub.ListLogicalDevices(Empty())
236 omit_fields = {
237 'desc.mfr_desc',
238 'desc.hw_desc',
239 'desc.sw_desc',
240 'desc.dp_desc',
241 'desc.serial_number',
242 'switch_features.capabilities'
243 }
244 presfns = {
245 'datapath_id': lambda x: "{0:0{1}x}".format(int(x), 16)
246 }
247 print_pb_list_as_table('Logical devices:', res.items, omit_fields,
248 self.poutput, presfns=presfns)
249
250 def do_device(self, line):
251 """Enter device level command mode"""
252 device_id = line.strip() or self.default_device_id
253 if not device_id:
254 raise Exception('<device-id> parameter needed')
255 if device_id not in self.device_ids():
256 self.poutput( self.colorize('Error: ', 'red') +
257 'There is no such device')
258 raise Exception('<device-id> is not a valid one')
259 sub = DeviceCli(device_id, self.get_stub)
260 sub.cmdloop()
261
262 def do_logical_device(self, line):
263 """Enter logical device level command mode"""
264 logical_device_id = line.strip() or self.default_logical_device_id
265 if not logical_device_id:
266 raise Exception('<logical-device-id> parameter needed')
267 if logical_device_id not in self.logical_device_ids():
268 self.poutput( self.colorize('Error: ', 'red') +
269 'There is no such device')
270 raise Exception('<logical-device-id> is not a valid one')
271 sub = LogicalDeviceCli(logical_device_id, self.get_stub)
272 sub.cmdloop()
273
274 def device_ids(self, force_refresh=False):
275 if force_refresh or self.device_ids is None or \
276 (time() - self.device_ids_cache_ts) > 1:
277 self.device_ids_cache = [d.id for d in self.get_devices()]
278 self.device_ids_cache_ts = time()
279 return self.device_ids_cache
280
281 def logical_device_ids(self, force_refresh=False):
282 if force_refresh or self.logical_device_ids is None or \
283 (time() - self.logical_device_ids_cache_ts) > 1:
284 self.logical_device_ids_cache = [d.id for d
285 in self.get_logical_devices()]
286 self.logical_device_ids_cache_ts = time()
287 return self.logical_device_ids_cache
288
289 def complete_device(self, text, line, begidx, endidx):
290 if not text:
291 completions = self.device_ids()[:]
292 else:
293 completions = [d for d in self.device_ids() if d.startswith(text)]
294 return completions
295
296 def complete_logical_device(self, text, line, begidx, endidx):
297 if not text:
298 completions = self.logical_device_ids()[:]
299 else:
300 completions = [d for d in self.logical_device_ids()
301 if d.startswith(text)]
302 return completions
303
khenaidoofdbad6e2018-11-06 22:26:38 -0500304 def do_omci(self, line):
305 """omci <device_ID> - Enter OMCI level command mode"""
306
307 device_id = line.strip() or self.default_device_id
308 if not device_id:
309 raise Exception('<device-id> parameter needed')
310 sub = OmciCli(device_id, self.get_stub)
311 sub.cmdloop()
312
313 def do_pdb(self, line):
314 """Launch PDB debug prompt in CLI (for CLI development)"""
315 from pdb import set_trace
316 set_trace()
317
318 def do_version(self, line):
319 """Show the VOLTHA core version"""
320 stub = self.get_stub()
321 voltha = stub.GetVoltha(Empty())
322 self.poutput('{}'.format(voltha.version))
323
324 def do_health(self, line):
325 """Show connectivity status to Voltha status"""
326 stub = voltha_pb2.HealthServiceStub(self.get_channel())
327 res = stub.GetHealthStatus(Empty())
328 self.poutput(dumps(pb2dict(res), indent=4))
329
330 @options([
331 make_option('-t', '--device-type', action="store", dest='device_type',
332 help="Device type", default='simulated_olt'),
333 make_option('-m', '--mac-address', action='store', dest='mac_address',
334 default='00:0c:e2:31:40:00'),
335 make_option('-i', '--ip-address', action='store', dest='ip_address'),
336 make_option('-H', '--host_and_port', action='store',
337 dest='host_and_port'),
338 ])
339 def do_preprovision_olt(self, line, opts):
340 """Preprovision a new OLT with given device type"""
341 stub = self.get_stub()
342 kw = dict(type=opts.device_type)
343 if opts.host_and_port:
344 kw['host_and_port'] = opts.host_and_port
345 elif opts.ip_address:
346 kw['ipv4_address'] = opts.ip_address
347 elif opts.mac_address:
348 kw['mac_address'] = opts.mac_address.lower()
349 else:
350 raise Exception('Either IP address or Mac Address is needed')
351 # Pass any extra arguments past '--' to the device as custom arguments
352 kw['extra_args'] = line
353
354 device = voltha_pb2.Device(**kw)
355 device = stub.CreateDevice(device)
356 self.poutput('success (device id = {})'.format(device.id))
357 self.default_device_id = device.id
358
359 def do_enable(self, line):
360 """
361 Enable a device. If the <id> is not provided, it will be on the last
362 pre-provisioned device.
363 """
364 device_id = line or self.default_device_id
365 if device_id not in self.device_ids():
366 self.poutput('Error: There is no such preprovisioned device')
367 return
368
369 try:
370 stub = self.get_stub()
371 device = stub.GetDevice(voltha_pb2.ID(id=device_id))
372 if device.admin_state == voltha_pb2.AdminState.ENABLED:
373 if device.oper_status != voltha_pb2.OperStatus.ACTIVATING:
374 self.poutput('Error: Device is already enabled')
375 return
376 else:
377 stub.EnableDevice(voltha_pb2.ID(id=device_id))
378 self.poutput('enabling {}'.format(device_id))
379
380 while True:
381 device = stub.GetDevice(voltha_pb2.ID(id=device_id))
382 # If this is an OLT then acquire logical device id
383 if device.oper_status == voltha_pb2.OperStatus.ACTIVE:
384 if device.type.endswith('_olt'):
385 assert device.parent_id
386 self.default_logical_device_id = device.parent_id
387 self.poutput('success (logical device id = {})'.format(
388 self.default_logical_device_id))
389 else:
390 self.poutput('success (device id = {})'.format(device.id))
391 break
392 self.poutput('waiting for device to be enabled...')
393 sleep(.5)
394 except Exception as e:
395 self.poutput('Error enabling {}. Error:{}'.format(device_id, e))
396
397 complete_activate_olt = complete_device
398
399 def do_reboot(self, line):
400 """
401 Rebooting a device. ID of the device needs to be provided
402 """
403 device_id = line or self.default_device_id
404 self.poutput('rebooting {}'.format(device_id))
405 try:
406 stub = self.get_stub()
407 stub.RebootDevice(voltha_pb2.ID(id=device_id))
408 self.poutput('rebooted {}'.format(device_id))
409 except Exception as e:
410 self.poutput('Error rebooting {}. Error:{}'.format(device_id, e))
411
412 def do_self_test(self, line):
413 """
414 Self Test a device. ID of the device needs to be provided
415 """
416 device_id = line or self.default_device_id
417 self.poutput('Self Testing {}'.format(device_id))
418 try:
419 stub = self.get_stub()
420 res = stub.SelfTest(voltha_pb2.ID(id=device_id))
421 self.poutput('Self Tested {}'.format(device_id))
422 self.poutput(dumps(pb2dict(res), indent=4))
423 except Exception as e:
424 self.poutput('Error in self test {}. Error:{}'.format(device_id, e))
425
426 def do_delete(self, line):
427 """
428 Deleting a device. ID of the device needs to be provided
429 """
430 device_id = line or self.default_device_id
431 self.poutput('deleting {}'.format(device_id))
432 try:
433 stub = self.get_stub()
434 stub.DeleteDevice(voltha_pb2.ID(id=device_id))
435 self.poutput('deleted {}'.format(device_id))
436 except Exception as e:
437 self.poutput('Error deleting {}. Error:{}'.format(device_id, e))
438
439 def do_disable(self, line):
440 """
441 Disable a device. ID of the device needs to be provided
442 """
443 device_id = line
444 if device_id not in self.device_ids():
445 self.poutput('Error: There is no such device')
446 return
447 try:
448 stub = self.get_stub()
449 device = stub.GetDevice(voltha_pb2.ID(id=device_id))
450 if device.admin_state == voltha_pb2.AdminState.DISABLED:
451 self.poutput('Error: Device is already disabled')
452 return
453 stub.DisableDevice(voltha_pb2.ID(id=device_id))
454 self.poutput('disabling {}'.format(device_id))
455
456 # Do device query and verify that the device admin status is
457 # DISABLED and Operational Status is unknown
458 device = stub.GetDevice(voltha_pb2.ID(id=device_id))
459 if device.admin_state == voltha_pb2.AdminState.DISABLED:
460 self.poutput('disabled successfully {}'.format(device_id))
461 else:
462 self.poutput('disabling failed {}. Admin State:{} '
463 'Operation State: {}'.format(device_id,
464 device.admin_state,
465 device.oper_status))
466 except Exception as e:
467 self.poutput('Error disabling {}. Error:{}'.format(device_id, e))
468
469 def do_test(self, line):
470 """Enter test mode, which makes a bunch on new commands available"""
471 sub = TestCli(self.history, self.voltha_grpc,
472 self.get_stub, self.voltha_sim_rest)
473 sub.cmdloop()
474
475 def do_alarm_filters(self, line):
476 sub = AlarmFiltersCli(self.get_stub)
477 sub.cmdloop()
478
479
480class TestCli(VolthaCli):
481 def __init__(self, history, voltha_grpc, get_stub, voltha_sim_rest):
482 VolthaCli.__init__(self, voltha_grpc, voltha_sim_rest)
483 self.history = history
484 self.get_stub = get_stub
485 self.prompt = '(' + self.colorize(self.colorize('test', 'cyan'),
486 'bold') + ') '
487
488 def get_device(self, device_id, depth=0):
489 stub = self.get_stub()
490 res = stub.GetDevice(voltha_pb2.ID(id=device_id),
491 metadata=(('get-depth', str(depth)),))
492 return res
493
494 def do_arrive_onus(self, line):
495 """
496 Simulate the arrival of ONUs (available only on simulated_olt)
497 """
498 device_id = line or self.default_device_id
499
500 # verify that device is of type simulated_olt
501 device = self.get_device(device_id)
502 assert device.type == 'simulated_olt', (
503 'Cannot use it on this device type (only on simulated_olt type)')
504
505 requests.get('http://{}/devices/{}/detect_onus'.format(
506 self.voltha_sim_rest, device_id
507 ))
508
509 complete_arrive_onus = VolthaCli.complete_device
510
511 def get_logical_ports(self, logical_device_id):
512 """
513 Return the NNI port number and the first usable UNI port of logical
514 device, and the vlan associated with the latter.
515 """
516 stub = self.get_stub()
517 ports = stub.ListLogicalDevicePorts(
518 voltha_pb2.ID(id=logical_device_id)).items
519 nni = None
520 unis = []
521 for port in ports:
522 if port.root_port:
523 assert nni is None, "There shall be only one root port"
524 nni = port.ofp_port.port_no
525 else:
526 uni = port.ofp_port.port_no
527 uni_device = self.get_device(port.device_id)
528 vlan = uni_device.vlan
529 unis.append((uni, vlan))
530
531 assert nni is not None, "No NNI port found"
532 assert unis, "Not a single UNI?"
533
534 return nni, unis
535
536 def do_install_eapol_flow(self, line):
537 """
538 Install an EAPOL flow on the given logical device. If device is not
539 given, it will be applied to logical device of the last pre-provisioned
540 OLT device.
541 """
542
543 logical_device_id = line or self.default_logical_device_id
544
545 # gather NNI and UNI port IDs
546 nni_port_no, unis = self.get_logical_ports(logical_device_id)
547
548 # construct and push flow rule
549 stub = self.get_stub()
550 print "I am now here", unis
551 for uni_port_no, _ in unis:
552 update = FlowTableUpdate(
553 id=logical_device_id,
554 flow_mod=mk_simple_flow_mod(
555 priority=2000,
556 match_fields=[in_port(uni_port_no), eth_type(0x888e)],
557 actions=[
558 # push_vlan(0x8100),
559 # set_field(vlan_vid(4096 + 4000)),
560 output(ofp.OFPP_CONTROLLER)
561 ]
562 )
563 )
564 print "I am now here"
565 res = stub.UpdateLogicalDeviceFlowTable(update)
566 self.poutput('success for uni {} ({})'.format(uni_port_no, res))
567
568 complete_install_eapol_flow = VolthaCli.complete_logical_device
569
570 def do_install_all_controller_bound_flows(self, line):
571 """
572 Install all flow rules for controller bound flows, including EAPOL,
573 IGMP and DHCP. If device is not given, it will be applied to logical
574 device of the last pre-provisioned OLT device.
575 """
576 logical_device_id = line or self.default_logical_device_id
577
578 # gather NNI and UNI port IDs
579 nni_port_no, unis = self.get_logical_ports(logical_device_id)
580
581 # construct and push flow rules
582 stub = self.get_stub()
583
584 for uni_port_no, _ in unis:
585 stub.UpdateLogicalDeviceFlowTable(FlowTableUpdate(
586 id=logical_device_id,
587 flow_mod=mk_simple_flow_mod(
588 priority=2000,
589 match_fields=[
590 in_port(uni_port_no),
591 eth_type(0x888e)
592 ],
593 actions=[output(ofp.OFPP_CONTROLLER)]
594 )
595 ))
596 stub.UpdateLogicalDeviceFlowTable(FlowTableUpdate(
597 id=logical_device_id,
598 flow_mod=mk_simple_flow_mod(
599 priority=1000,
600 match_fields=[
601 in_port(uni_port_no),
602 eth_type(0x800),
603 ip_proto(2)
604 ],
605 actions=[output(ofp.OFPP_CONTROLLER)]
606 )
607 ))
608 stub.UpdateLogicalDeviceFlowTable(FlowTableUpdate(
609 id=logical_device_id,
610 flow_mod=mk_simple_flow_mod(
611 priority=1000,
612 match_fields=[
613 in_port(uni_port_no),
614 eth_type(0x800),
615 ip_proto(17),
616 udp_dst(67)
617 ],
618 actions=[output(ofp.OFPP_CONTROLLER)]
619 )
620 ))
621 self.poutput('success')
622
623 complete_install_all_controller_bound_flows = \
624 VolthaCli.complete_logical_device
625
626 def do_install_all_sample_flows(self, line):
627 """
628 Install all flows that are representative of the virtualized access
629 scenario in a PON network.
630 """
631 logical_device_id = line or self.default_logical_device_id
632
633 # gather NNI and UNI port IDs
634 nni_port_no, unis = self.get_logical_ports(logical_device_id)
635
636 # construct and push flow rules
637 stub = self.get_stub()
638
639 for uni_port_no, c_vid in unis:
640 # Controller-bound flows
641 stub.UpdateLogicalDeviceFlowTable(FlowTableUpdate(
642 id=logical_device_id,
643 flow_mod=mk_simple_flow_mod(
644 priority=2000,
645 match_fields=[in_port(uni_port_no), eth_type(0x888e)],
646 actions=[
647 # push_vlan(0x8100),
648 # set_field(vlan_vid(4096 + 4000)),
649 output(ofp.OFPP_CONTROLLER)
650 ]
651 )
652 ))
653 stub.UpdateLogicalDeviceFlowTable(FlowTableUpdate(
654 id=logical_device_id,
655 flow_mod=mk_simple_flow_mod(
656 priority=1000,
657 match_fields=[eth_type(0x800), ip_proto(2)],
658 actions=[output(ofp.OFPP_CONTROLLER)]
659 )
660 ))
661 stub.UpdateLogicalDeviceFlowTable(FlowTableUpdate(
662 id=logical_device_id,
663 flow_mod=mk_simple_flow_mod(
664 priority=1000,
665 match_fields=[eth_type(0x800), ip_proto(17), udp_dst(67)],
666 actions=[output(ofp.OFPP_CONTROLLER)]
667 )
668 ))
669
670 # Unicast flows:
671 # Downstream flow 1
672 stub.UpdateLogicalDeviceFlowTable(FlowTableUpdate(
673 id=logical_device_id,
674 flow_mod=mk_simple_flow_mod(
675 priority=500,
676 match_fields=[
677 in_port(nni_port_no),
678 vlan_vid(4096 + 1000),
679 metadata(c_vid) # here to mimic an ONOS artifact
680 ],
681 actions=[pop_vlan()],
682 next_table_id=1
683 )
684 ))
685 # Downstream flow 2
686 stub.UpdateLogicalDeviceFlowTable(FlowTableUpdate(
687 id=logical_device_id,
688 flow_mod=mk_simple_flow_mod(
689 priority=500,
690 table_id=1,
691 match_fields=[in_port(nni_port_no), vlan_vid(4096 + c_vid)],
692 actions=[set_field(vlan_vid(4096 + 0)), output(uni_port_no)]
693 )
694 ))
695 # Upstream flow 1 for 0-tagged case
696 stub.UpdateLogicalDeviceFlowTable(FlowTableUpdate(
697 id=logical_device_id,
698 flow_mod=mk_simple_flow_mod(
699 priority=500,
700 match_fields=[in_port(uni_port_no), vlan_vid(4096 + 0)],
701 actions=[set_field(vlan_vid(4096 + c_vid))],
702 next_table_id=1
703 )
704 ))
705 # Upstream flow 1 for untagged case
706 stub.UpdateLogicalDeviceFlowTable(FlowTableUpdate(
707 id=logical_device_id,
708 flow_mod=mk_simple_flow_mod(
709 priority=500,
710 match_fields=[in_port(uni_port_no), vlan_vid(0)],
711 actions=[push_vlan(0x8100), set_field(vlan_vid(4096 + c_vid))],
712 next_table_id=1
713 )
714 ))
715 # Upstream flow 2 for s-tag
716 stub.UpdateLogicalDeviceFlowTable(FlowTableUpdate(
717 id=logical_device_id,
718 flow_mod=mk_simple_flow_mod(
719 priority=500,
720 table_id=1,
721 match_fields=[in_port(uni_port_no), vlan_vid(4096 + c_vid)],
722 actions=[
723 push_vlan(0x8100),
724 set_field(vlan_vid(4096 + 1000)),
725 output(nni_port_no)
726 ]
727 )
728 ))
729
730 # Push a few multicast flows
731 # 1st with one bucket for our uni 0
732 stub.UpdateLogicalDeviceFlowGroupTable(FlowGroupTableUpdate(
733 id=logical_device_id,
734 group_mod=mk_multicast_group_mod(
735 group_id=1,
736 buckets=[
737 ofp.ofp_bucket(actions=[
738 pop_vlan(),
739 output(unis[0][0])
740 ])
741 ]
742 )
743 ))
744 stub.UpdateLogicalDeviceFlowTable(FlowTableUpdate(
745 id=logical_device_id,
746 flow_mod=mk_simple_flow_mod(
747 priority=1000,
748 match_fields=[
749 in_port(nni_port_no),
750 eth_type(0x800),
751 vlan_vid(4096 + 140),
752 ipv4_dst(0xe4010101)
753 ],
754 actions=[group(1)]
755 )
756 ))
757
758 # 2nd with one bucket for uni 0 and 1
759 stub.UpdateLogicalDeviceFlowGroupTable(FlowGroupTableUpdate(
760 id=logical_device_id,
761 group_mod=mk_multicast_group_mod(
762 group_id=2,
763 buckets=[
764 ofp.ofp_bucket(actions=[pop_vlan(), output(unis[0][0])])
765 # ofp.ofp_bucket(actions=[pop_vlan(), output(unis[1][0])])
766 ]
767 )
768 ))
769 stub.UpdateLogicalDeviceFlowTable(FlowTableUpdate(
770 id=logical_device_id,
771 flow_mod=mk_simple_flow_mod(
772 priority=1000,
773 match_fields=[
774 in_port(nni_port_no),
775 eth_type(0x800),
776 vlan_vid(4096 + 140),
777 ipv4_dst(0xe4020202)
778 ],
779 actions=[group(2)]
780 )
781 ))
782
783 # 3rd with empty bucket
784 stub.UpdateLogicalDeviceFlowGroupTable(FlowGroupTableUpdate(
785 id=logical_device_id,
786 group_mod=mk_multicast_group_mod(
787 group_id=3,
788 buckets=[]
789 )
790 ))
791 stub.UpdateLogicalDeviceFlowTable(FlowTableUpdate(
792 id=logical_device_id,
793 flow_mod=mk_simple_flow_mod(
794 priority=1000,
795 match_fields=[
796 in_port(nni_port_no),
797 eth_type(0x800),
798 vlan_vid(4096 + 140),
799 ipv4_dst(0xe4030303)
800 ],
801 actions=[group(3)]
802 )
803 ))
804
805 self.poutput('success')
806
807 complete_install_all_sample_flows = VolthaCli.complete_logical_device
808
809 def do_install_dhcp_flows(self, line):
810 """
811 Install all dhcp flows that are representative of the virtualized access
812 scenario in a PON network.
813 """
814 logical_device_id = line or self.default_logical_device_id
815
816 # gather NNI and UNI port IDs
817 nni_port_no, unis = self.get_logical_ports(logical_device_id)
818
819 # construct and push flow rules
820 stub = self.get_stub()
821
822 # Controller-bound flows
823 for uni_port_no, _ in unis:
824 stub.UpdateLogicalDeviceFlowTable(FlowTableUpdate(
825 id=logical_device_id,
826 flow_mod=mk_simple_flow_mod(
827 priority=1000,
828 match_fields=[
829 in_port(uni_port_no),
830 eth_type(0x800),
831 ip_proto(17),
832 udp_dst(67)
833 ],
834 actions=[output(ofp.OFPP_CONTROLLER)]
835 )
836 ))
837
838 self.poutput('success')
839
840 complete_install_dhcp_flows = VolthaCli.complete_logical_device
841
842 def do_delete_all_flows(self, line):
843 """
844 Remove all flows and flow groups from given logical device
845 """
846 logical_device_id = line or self.default_logical_device_id
847 stub = self.get_stub()
848 stub.UpdateLogicalDeviceFlowTable(FlowTableUpdate(
849 id=logical_device_id,
850 flow_mod=ofp.ofp_flow_mod(
851 command=ofp.OFPFC_DELETE,
852 table_id=ofp.OFPTT_ALL,
853 cookie_mask=0,
854 out_port=ofp.OFPP_ANY,
855 out_group=ofp.OFPG_ANY
856 )
857 ))
858 stub.UpdateLogicalDeviceFlowGroupTable(FlowGroupTableUpdate(
859 id=logical_device_id,
860 group_mod=ofp.ofp_group_mod(
861 command=ofp.OFPGC_DELETE,
862 group_id=ofp.OFPG_ALL
863 )
864 ))
865 self.poutput('success')
866
867 complete_delete_all_flows = VolthaCli.complete_logical_device
868
869 def do_send_simulated_upstream_eapol(self, line):
870 """
871 Send an EAPOL upstream from a simulated OLT
872 """
873 device_id = line or self.default_device_id
874 requests.get('http://{}/devices/{}/test_eapol_in'.format(
875 self.voltha_sim_rest, device_id
876 ))
877
878 complete_send_simulated_upstream_eapol = VolthaCli.complete_device
879
880 def do_inject_eapol_start(self, line):
881 """
882 Send out an an EAPOL start message into the given Unix interface
883 """
884 pass
885
886
887if __name__ == '__main__':
888
889 parser = argparse.ArgumentParser()
890
891 _help = '<hostname>:<port> to consul agent (default: %s)' % defs['consul']
892 parser.add_argument(
893 '-C', '--consul', action='store', default=defs['consul'], help=_help)
894
895 _help = 'Lookup Voltha endpoints based on service entries in Consul'
896 parser.add_argument(
897 '-L', '--lookup', action='store_true', help=_help)
898
899 _help = 'All requests to the Voltha gRPC service are global'
900 parser.add_argument(
901 '-G', '--global_request', action='store_true', help=_help)
902
903 _help = '<hostname>:<port> of Voltha gRPC service (default={})'.format(
904 defs['voltha_grpc_endpoint'])
905 parser.add_argument('-g', '--grpc-endpoint', action='store',
906 default=defs['voltha_grpc_endpoint'], help=_help)
907
908 _help = '<hostname>:<port> of Voltha simulated adapter backend for ' \
909 'testing (default={})'.format(
910 defs['voltha_sim_rest_endpoint'])
911 parser.add_argument('-s', '--sim-rest-endpoint', action='store',
912 default=defs['voltha_sim_rest_endpoint'], help=_help)
913
914 args = parser.parse_args()
915
916 if args.lookup:
917 host = args.consul.split(':')[0].strip()
918 port = int(args.consul.split(':')[1].strip())
919 consul = Consul(host=host, port=port)
920
921 _, services = consul.catalog.service('voltha-grpc')
922 if not services:
923 print('No voltha-grpc service registered in consul; exiting')
924 sys.exit(1)
925 args.grpc_endpoint = '{}:{}'.format(services[0]['ServiceAddress'],
926 services[0]['ServicePort'])
927
928 _, services = consul.catalog.service('voltha-sim-rest')
929 if not services:
930 print('No voltha-sim-rest service registered in consul; exiting')
931 sys.exit(1)
932 args.sim_rest_endpoint = '{}:{}'.format(services[0]['ServiceAddress'],
933 services[0]['ServicePort'])
934
935 c = VolthaCli(args.grpc_endpoint, args.sim_rest_endpoint,
936 args.global_request)
937 c.poutput(banner)
938 c.load_history()
939 c.cmdloop()
940 c.save_history()