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