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