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