blob: 327f122a8bb1421962e02f942ae702377cda2eaa [file] [log] [blame]
Zsolt Harasztia133a452016-12-22 01:26:57 -08001#!/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#
Zsolt Haraszti9b485fb2016-12-26 23:11:15 -080017import argparse
18import os
Zsolt Harasztia133a452016-12-22 01:26:57 -080019import readline
Stephane Barbarie4db8ca22017-04-24 10:30:20 -040020import sys
Zsolt Harasztid036b7e2016-12-23 15:36:01 -080021from optparse import make_option
Zsolt Haraszti80175202016-12-24 00:17:51 -080022from time import sleep, time
Zsolt Harasztid036b7e2016-12-23 15:36:01 -080023
Zsolt Harasztia133a452016-12-22 01:26:57 -080024import grpc
Zsolt Harasztid036b7e2016-12-23 15:36:01 -080025import requests
26from cmd2 import Cmd, options
Stephane Barbarie4db8ca22017-04-24 10:30:20 -040027from consul import Consul
Zsolt Harasztid036b7e2016-12-23 15:36:01 -080028from google.protobuf.empty_pb2 import Empty
Zsolt Harasztia133a452016-12-22 01:26:57 -080029from simplejson import dumps
30
Zsolt Harasztid036b7e2016-12-23 15:36:01 -080031from cli.device import DeviceCli
Stephane Barbarie4db8ca22017-04-24 10:30:20 -040032from cli.alarm_filters import AlarmFiltersCli
Zsolt Harasztid036b7e2016-12-23 15:36:01 -080033from cli.logical_device import LogicalDeviceCli
Stephane Barbarie4db8ca22017-04-24 10:30:20 -040034from cli.table import print_pb_list_as_table
Zsolt Harasztid036b7e2016-12-23 15:36:01 -080035from voltha.core.flow_decomposer import *
Zsolt Harasztia133a452016-12-22 01:26:57 -080036from voltha.protos import third_party
37from voltha.protos import voltha_pb2
Zsolt Haraszti85f12852016-12-24 08:30:58 -080038from voltha.protos.openflow_13_pb2 import FlowTableUpdate, FlowGroupTableUpdate
Zsolt Harasztid036b7e2016-12-23 15:36:01 -080039
Zsolt Harasztia133a452016-12-22 01:26:57 -080040_ = third_party
Stephane Barbarie4db8ca22017-04-24 10:30:20 -040041from cli.utils import pb2dict
Zsolt Haraszti9b485fb2016-12-26 23:11:15 -080042
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:50055'),
48 voltha_sim_rest_endpoint=os.environ.get('VOLTHA_SIM_REST_ENDPOINT',
49 'localhost:18880'),
50)
51
Zsolt Harasztid036b7e2016-12-23 15:36:01 -080052banner = """\
Zsolt Haraszti313c4be2016-12-27 11:06:53 -080053 _ _ _ ___ _ ___
54__ _____| | |_| |_ __ _ / __| | |_ _|
55\ V / _ \ | _| ' \/ _` | | (__| |__ | |
56 \_/\___/_|\__|_||_\__,_| \___|____|___|
Zsolt Haraszti80175202016-12-24 00:17:51 -080057(to exit type quit or hit Ctrl-D)
Zsolt Harasztid036b7e2016-12-23 15:36:01 -080058"""
Zsolt Harasztia133a452016-12-22 01:26:57 -080059
Zsolt Harasztia133a452016-12-22 01:26:57 -080060
Stephane Barbarie4db8ca22017-04-24 10:30:20 -040061class VolthaCli(Cmd):
Zsolt Harasztia133a452016-12-22 01:26:57 -080062 prompt = 'voltha'
63 history_file_name = '.voltha_cli_history'
Zsolt Harasztid036b7e2016-12-23 15:36:01 -080064
65 # Settable CLI parameters
66 voltha_grpc = 'localhost:50055'
67 voltha_sim_rest = 'localhost:18880'
Zsolt Harasztia133a452016-12-22 01:26:57 -080068 max_history_lines = 500
Zsolt Harasztid036b7e2016-12-23 15:36:01 -080069 default_device_id = None
70 default_logical_device_id = None
Zsolt Harasztia133a452016-12-22 01:26:57 -080071
72 Cmd.settable.update(dict(
Zsolt Harasztid036b7e2016-12-23 15:36:01 -080073 voltha_grpc='Voltha GRPC endpoint in form of <host>:<port>',
74 voltha_sim_rest='Voltha simulation back door for testing in form '
75 'of <host>:<port>',
76 max_history_lines='Maximum number of history lines stored across '
77 'sessions',
78 default_device_id='Device id used when no device id is specified',
79 default_logical_device_id='Logical device id used when no device id '
80 'is specified',
Zsolt Harasztia133a452016-12-22 01:26:57 -080081 ))
82
Zsolt Haraszti9b485fb2016-12-26 23:11:15 -080083 # cleanup of superfluous commands from cmd2
Zsolt Haraszti80175202016-12-24 00:17:51 -080084 del Cmd.do_cmdenvironment
Steve Crooks05f24522017-02-27 13:32:27 -050085 del Cmd.do_load
Zsolt Haraszti80175202016-12-24 00:17:51 -080086 del Cmd.do__relative_load
Zsolt Haraszti80175202016-12-24 00:17:51 -080087
Zsolt Haraszti9b485fb2016-12-26 23:11:15 -080088 def __init__(self, voltha_grpc, voltha_sim_rest):
89 VolthaCli.voltha_grpc = voltha_grpc
90 VolthaCli.voltha_sim_rest = voltha_sim_rest
91 Cmd.__init__(self)
Zsolt Harasztia133a452016-12-22 01:26:57 -080092 self.prompt = '(' + self.colorize(
Zsolt Haraszti80175202016-12-24 00:17:51 -080093 self.colorize(self.prompt, 'blue'), 'bold') + ') '
Zsolt Harasztia133a452016-12-22 01:26:57 -080094 self.channel = None
Zsolt Haraszti80175202016-12-24 00:17:51 -080095 self.device_ids_cache = None
96 self.device_ids_cache_ts = time()
97 self.logical_device_ids_cache = None
98 self.logical_device_ids_cache_ts = time()
Zsolt Harasztia133a452016-12-22 01:26:57 -080099
Zsolt Haraszti9b485fb2016-12-26 23:11:15 -0800100 # we override cmd2's method to avoid its optparse conflicting with our
101 # command line parsing
102 def cmdloop(self):
103 self._cmdloop()
104
Zsolt Harasztia133a452016-12-22 01:26:57 -0800105 def load_history(self):
106 """Load saved command history from local history file"""
107 try:
108 with file(self.history_file_name, 'r') as f:
109 for line in f.readlines():
110 stripped_line = line.strip()
111 self.history.append(stripped_line)
112 readline.add_history(stripped_line)
113 except IOError:
114 pass # ignore if file cannot be read
115
116 def save_history(self):
117 try:
Zsolt Haraszti80175202016-12-24 00:17:51 -0800118 with open(self.history_file_name, 'w') as f:
Zsolt Harasztia133a452016-12-22 01:26:57 -0800119 f.write('\n'.join(self.history[-self.max_history_lines:]))
Zsolt Haraszti80175202016-12-24 00:17:51 -0800120 except IOError as e:
121 self.perror('Could not save history in {}: {}'.format(
122 self.history_file_name, e))
Zsolt Harasztia133a452016-12-22 01:26:57 -0800123 else:
Zsolt Haraszti9b485fb2016-12-26 23:11:15 -0800124 self.poutput('History saved as {}'.format(
Zsolt Haraszti80175202016-12-24 00:17:51 -0800125 self.history_file_name))
126
127 def perror(self, errmsg, statement=None):
128 # Touch it up to make sure error is prefixed and colored
129 Cmd.perror(self, self.colorize('***ERROR: ', 'red') + errmsg,
130 statement)
Zsolt Harasztia133a452016-12-22 01:26:57 -0800131
132 def get_channel(self):
133 if self.channel is None:
134 self.channel = grpc.insecure_channel(self.voltha_grpc)
135 return self.channel
136
Zsolt Haraszti80175202016-12-24 00:17:51 -0800137 # ~~~~~~~~~~~~~~~~~ ACTUAL COMMAND IMPLEMENTATIONS ~~~~~~~~~~~~~~~~~~~~~~~~
Zsolt Harasztid036b7e2016-12-23 15:36:01 -0800138
Zsolt Haraszti80175202016-12-24 00:17:51 -0800139 def do_reset_history(self, line):
Zsolt Harasztia133a452016-12-22 01:26:57 -0800140 """Reset CLI history"""
141 while self.history:
142 self.history.pop()
143
Zsolt Haraszti80175202016-12-24 00:17:51 -0800144 def do_launch(self, line):
Zsolt Harasztia133a452016-12-22 01:26:57 -0800145 """If Voltha is not running yet, launch it"""
Zsolt Haraszti80175202016-12-24 00:17:51 -0800146 raise NotImplementedError('not implemented yet')
Zsolt Harasztia133a452016-12-22 01:26:57 -0800147
Zsolt Haraszti80175202016-12-24 00:17:51 -0800148 def do_restart(self, line):
Zsolt Harasztia133a452016-12-22 01:26:57 -0800149 """Launch Voltha, but if it is already running, terminate it first"""
150 pass
151
Zsolt Haraszti80175202016-12-24 00:17:51 -0800152 def do_adapters(self, line):
153 """List loaded adapter"""
154 stub = voltha_pb2.VolthaLocalServiceStub(self.get_channel())
155 res = stub.ListAdapters(Empty())
Sergio Slobodrian6e9fb692017-03-17 14:46:33 -0400156 omit_fields = {'config.log_level', 'logical_device_ids'}
Zsolt Haraszti85f12852016-12-24 08:30:58 -0800157 print_pb_list_as_table('Adapters:', res.items, omit_fields, self.poutput)
Zsolt Haraszti80175202016-12-24 00:17:51 -0800158
159 def get_devices(self):
Zsolt Harasztia133a452016-12-22 01:26:57 -0800160 stub = voltha_pb2.VolthaLocalServiceStub(self.get_channel())
161 res = stub.ListDevices(Empty())
Zsolt Haraszti80175202016-12-24 00:17:51 -0800162 return res.items
Zsolt Harasztia133a452016-12-22 01:26:57 -0800163
Zsolt Haraszti80175202016-12-24 00:17:51 -0800164 def get_logical_devices(self):
165 stub = voltha_pb2.VolthaLocalServiceStub(self.get_channel())
166 res = stub.ListLogicalDevices(Empty())
167 return res.items
168
169 def do_devices(self, line):
170 """List devices registered in Voltha"""
171 devices = self.get_devices()
172 omit_fields = {
173 'adapter',
174 'vendor',
175 'model',
176 'hardware_version',
ggowdru236bd952017-06-20 20:32:55 -0700177 'images',
Zsolt Haraszti80175202016-12-24 00:17:51 -0800178 'firmware_version',
Sergio Slobodriana95f99b2017-03-21 10:22:47 -0400179 'serial_number'
Zsolt Haraszti80175202016-12-24 00:17:51 -0800180 }
Zsolt Haraszti85f12852016-12-24 08:30:58 -0800181 print_pb_list_as_table('Devices:', devices, omit_fields, self.poutput)
Zsolt Haraszti80175202016-12-24 00:17:51 -0800182
183 def do_logical_devices(self, line):
Zsolt Harasztia133a452016-12-22 01:26:57 -0800184 """List logical devices in Voltha"""
185 stub = voltha_pb2.VolthaLocalServiceStub(self.get_channel())
186 res = stub.ListLogicalDevices(Empty())
Zsolt Haraszti80175202016-12-24 00:17:51 -0800187 omit_fields = {
188 'desc.mfr_desc',
189 'desc.hw_desc',
190 'desc.sw_desc',
191 'desc.dp_desc',
192 'desc.serial_number',
Sergio Slobodriana95f99b2017-03-21 10:22:47 -0400193 'switch_features.capabilities'
Zsolt Haraszti80175202016-12-24 00:17:51 -0800194 }
Zsolt Haraszti85f12852016-12-24 08:30:58 -0800195 print_pb_list_as_table('Logical devices:', res.items, omit_fields,
196 self.poutput)
Zsolt Harasztia133a452016-12-22 01:26:57 -0800197
Zsolt Haraszti80175202016-12-24 00:17:51 -0800198 def do_device(self, line):
Zsolt Harasztia133a452016-12-22 01:26:57 -0800199 """Enter device level command mode"""
Zsolt Haraszti80175202016-12-24 00:17:51 -0800200 device_id = line.strip() or self.default_device_id
Zsolt Harasztid036b7e2016-12-23 15:36:01 -0800201 if not device_id:
202 raise Exception('<device-id> parameter needed')
203 sub = DeviceCli(self.get_channel, device_id)
Zsolt Harasztia133a452016-12-22 01:26:57 -0800204 sub.cmdloop()
205
Zsolt Haraszti80175202016-12-24 00:17:51 -0800206 def do_logical_device(self, line):
Zsolt Harasztia133a452016-12-22 01:26:57 -0800207 """Enter logical device level command mode"""
Zsolt Haraszti80175202016-12-24 00:17:51 -0800208 logical_device_id = line.strip() or self.default_logical_device_id
Zsolt Harasztid036b7e2016-12-23 15:36:01 -0800209 if not logical_device_id:
210 raise Exception('<logical-device-id> parameter needed')
211 sub = LogicalDeviceCli(self.get_channel, logical_device_id)
Zsolt Harasztia133a452016-12-22 01:26:57 -0800212 sub.cmdloop()
213
Zsolt Haraszti80175202016-12-24 00:17:51 -0800214 def device_ids(self, force_refresh=False):
215 if force_refresh or self.device_ids is None or \
Stephane Barbarie4db8ca22017-04-24 10:30:20 -0400216 (time() - self.device_ids_cache_ts) > 1:
Zsolt Haraszti80175202016-12-24 00:17:51 -0800217 self.device_ids_cache = [d.id for d in self.get_devices()]
218 self.device_ids_cache_ts = time()
219 return self.device_ids_cache
220
221 def logical_device_ids(self, force_refresh=False):
222 if force_refresh or self.logical_device_ids is None or \
Stephane Barbarie4db8ca22017-04-24 10:30:20 -0400223 (time() - self.logical_device_ids_cache_ts) > 1:
Zsolt Haraszti80175202016-12-24 00:17:51 -0800224 self.logical_device_ids_cache = [d.id for d
225 in self.get_logical_devices()]
226 self.logical_device_ids_cache_ts = time()
227 return self.logical_device_ids_cache
228
229 def complete_device(self, text, line, begidx, endidx):
230 if not text:
231 completions = self.device_ids()[:]
232 else:
233 completions = [d for d in self.device_ids() if d.startswith(text)]
234 return completions
235
236 def complete_logical_device(self, text, line, begidx, endidx):
237 if not text:
238 completions = self.logical_device_ids()[:]
239 else:
240 completions = [d for d in self.logical_device_ids()
241 if d.startswith(text)]
242 return completions
243
244 def do_pdb(self, line):
Zsolt Harasztia133a452016-12-22 01:26:57 -0800245 """Launch PDB debug prompt in CLI (for CLI development)"""
246 from pdb import set_trace
247 set_trace()
248
Zsolt Haraszti80175202016-12-24 00:17:51 -0800249 def do_health(self, line):
Zsolt Harasztia133a452016-12-22 01:26:57 -0800250 """Show connectivity status to Voltha status"""
251 stub = voltha_pb2.HealthServiceStub(self.get_channel())
252 res = stub.GetHealthStatus(Empty())
Zsolt Haraszti80175202016-12-24 00:17:51 -0800253 self.poutput(dumps(pb2dict(res), indent=4))
Zsolt Harasztia133a452016-12-22 01:26:57 -0800254
Zsolt Harasztid036b7e2016-12-23 15:36:01 -0800255 @options([
256 make_option('-t', '--device-type', action="store", dest='device_type',
Stephane Barbarie4db8ca22017-04-24 10:30:20 -0400257 help="Device type", default='simulated_olt'),
Zsolt Harasztid036b7e2016-12-23 15:36:01 -0800258 make_option('-m', '--mac-address', action='store', dest='mac_address',
259 default='00:0c:e2:31:40:00'),
260 make_option('-i', '--ip-address', action='store', dest='ip_address'),
Zsolt Haraszti656ecc62016-12-28 15:08:23 -0800261 make_option('-H', '--host_and_port', action='store',
262 dest='host_and_port'),
Zsolt Harasztid036b7e2016-12-23 15:36:01 -0800263 ])
Zsolt Haraszti80175202016-12-24 00:17:51 -0800264 def do_preprovision_olt(self, line, opts):
Zsolt Harasztid036b7e2016-12-23 15:36:01 -0800265 """Preprovision a new OLT with given device type"""
Zsolt Harasztia133a452016-12-22 01:26:57 -0800266 stub = voltha_pb2.VolthaLocalServiceStub(self.get_channel())
Zsolt Harasztid036b7e2016-12-23 15:36:01 -0800267 kw = dict(type=opts.device_type)
Zsolt Haraszti656ecc62016-12-28 15:08:23 -0800268 if opts.host_and_port:
269 kw['host_and_port'] = opts.host_and_port
270 elif opts.ip_address:
Zsolt Harasztid036b7e2016-12-23 15:36:01 -0800271 kw['ipv4_address'] = opts.ip_address
272 elif opts.mac_address:
273 kw['mac_address'] = opts.mac_address
274 else:
275 raise Exception('Either IP address or Mac Address is needed')
Chip Boling90b224d2017-06-02 11:51:48 -0500276 # Pass any extra arguments past '--' to the device as custom arguments
277 kw['extra_args'] = line
278
Zsolt Harasztid036b7e2016-12-23 15:36:01 -0800279 device = voltha_pb2.Device(**kw)
280 device = stub.CreateDevice(device)
Zsolt Haraszti80175202016-12-24 00:17:51 -0800281 self.poutput('success (device id = {})'.format(device.id))
Zsolt Harasztid036b7e2016-12-23 15:36:01 -0800282 self.default_device_id = device.id
Zsolt Harasztia133a452016-12-22 01:26:57 -0800283
Khen Nursimulud068d812017-03-06 11:44:18 -0500284 def do_enable(self, line):
Zsolt Harasztid036b7e2016-12-23 15:36:01 -0800285 """
Khen Nursimulud068d812017-03-06 11:44:18 -0500286 Enable a device. If the <id> is not provided, it will be on the last
287 pre-provisioned device.
Zsolt Harasztid036b7e2016-12-23 15:36:01 -0800288 """
Zsolt Haraszti80175202016-12-24 00:17:51 -0800289 device_id = line or self.default_device_id
Khen Nursimulu29e75502017-03-07 17:26:50 -0500290 self.poutput('enabling {}'.format(device_id))
Khen Nursimuluc60afa12017-03-13 14:33:50 -0400291 try:
292 stub = voltha_pb2.VolthaLocalServiceStub(self.get_channel())
293 stub.EnableDevice(voltha_pb2.ID(id=device_id))
Zsolt Harasztia133a452016-12-22 01:26:57 -0800294
Khen Nursimuluc60afa12017-03-13 14:33:50 -0400295 while True:
296 device = stub.GetDevice(voltha_pb2.ID(id=device_id))
297 # If this is an OLT then acquire logical device id
298 if device.oper_status == voltha_pb2.OperStatus.ACTIVE:
299 if device.type.endswith('_olt'):
300 assert device.parent_id
301 self.default_logical_device_id = device.parent_id
302 self.poutput('success (logical device id = {})'.format(
303 self.default_logical_device_id))
304 else:
305 self.poutput('success (device id = {})'.format(device.id))
306 break
307 self.poutput('waiting for device to be enabled...')
308 sleep(.5)
309 except Exception, e:
310 self.poutput('Error enabling {}. Error:{}'.format(device_id, e))
Zsolt Harasztid036b7e2016-12-23 15:36:01 -0800311
Zsolt Haraszti50cae7d2017-01-08 22:27:07 -0800312 complete_activate_olt = complete_device
313
Khen Nursimulud068d812017-03-06 11:44:18 -0500314 def do_reboot(self, line):
315 """
316 Rebooting a device. ID of the device needs to be provided
317 """
318 device_id = line or self.default_device_id
319 self.poutput('rebooting {}'.format(device_id))
Khen Nursimuluc60afa12017-03-13 14:33:50 -0400320 try:
321 stub = voltha_pb2.VolthaLocalServiceStub(self.get_channel())
322 stub.RebootDevice(voltha_pb2.ID(id=device_id))
323 self.poutput('rebooted {}'.format(device_id))
324 except Exception, e:
325 self.poutput('Error rebooting {}. Error:{}'.format(device_id, e))
Khen Nursimulud068d812017-03-06 11:44:18 -0500326
sathishg5ae86222017-06-28 15:16:29 +0530327 def do_self_test(self, line):
328 """
329 Self Test a device. ID of the device needs to be provided
330 """
331 device_id = line or self.default_device_id
332 self.poutput('Self Testing {}'.format(device_id))
333 try:
334 stub = voltha_pb2.VolthaLocalServiceStub(self.get_channel())
335 res = stub.SelfTest(voltha_pb2.ID(id=device_id))
336 self.poutput('Self Tested {}'.format(device_id))
337 self.poutput(dumps(pb2dict(res), indent=4))
338 except Exception, e:
339 self.poutput('Error in self test {}. Error:{}'.format(device_id, e))
340
Khen Nursimulud068d812017-03-06 11:44:18 -0500341 def do_delete(self, line):
342 """
343 Deleting a device. ID of the device needs to be provided
344 """
345 device_id = line or self.default_device_id
346 self.poutput('deleting {}'.format(device_id))
Khen Nursimuluc60afa12017-03-13 14:33:50 -0400347 try:
348 stub = voltha_pb2.VolthaLocalServiceStub(self.get_channel())
349 stub.DeleteDevice(voltha_pb2.ID(id=device_id))
350 self.poutput('deleted {}'.format(device_id))
351 except Exception, e:
352 self.poutput('Error deleting {}. Error:{}'.format(device_id, e))
Khen Nursimulud068d812017-03-06 11:44:18 -0500353
354 def do_disable(self, line):
355 """
356 Disable a device. ID of the device needs to be provided
357 """
358 device_id = line
359 self.poutput('disabling {}'.format(device_id))
Khen Nursimuluc60afa12017-03-13 14:33:50 -0400360 try:
361 stub = voltha_pb2.VolthaLocalServiceStub(self.get_channel())
362 stub.DisableDevice(voltha_pb2.ID(id=device_id))
Khen Nursimulud068d812017-03-06 11:44:18 -0500363
Khen Nursimuluc60afa12017-03-13 14:33:50 -0400364 # Do device query and verify that the device admin status is
365 # DISABLED and Operational Status is unknown
366 device = stub.GetDevice(voltha_pb2.ID(id=device_id))
367 if device.oper_status == voltha_pb2.OperStatus.UNKNOWN and \
368 device.admin_state == voltha_pb2.AdminState.DISABLED:
369 self.poutput('disabled successfully {}'.format(device_id))
370 else:
371 self.poutput('disabling failed {}. Admin State:{} '
372 'Operation State: {}'.format(device_id,
373 device.admin_state,
374 device.oper_status))
375 except Exception, e:
376 self.poutput('Error disabling {}. Error:{}'.format(device_id, e))
Khen Nursimulud068d812017-03-06 11:44:18 -0500377
Zsolt Haraszti50cae7d2017-01-08 22:27:07 -0800378 def do_test(self, line):
379 """Enter test mode, which makes a bunch on new commands available"""
380 sub = TestCli(self.history, self.get_channel, self.voltha_grpc,
381 self.voltha_sim_rest)
382 sub.cmdloop()
383
Stephane Barbarie4db8ca22017-04-24 10:30:20 -0400384 def do_alarm_filters(self, line):
385 sub = AlarmFiltersCli(self.get_channel)
386 sub.cmdloop()
387
Zsolt Haraszti50cae7d2017-01-08 22:27:07 -0800388
389class TestCli(VolthaCli):
Zsolt Haraszti50cae7d2017-01-08 22:27:07 -0800390 def __init__(self, history, get_channel, voltha_grpc, voltha_sim_rest):
391 VolthaCli.__init__(self, voltha_grpc, voltha_sim_rest)
392 self.history = history
393 self.get_channel = get_channel
394 self.prompt = '(' + self.colorize(self.colorize('test', 'cyan'),
Stephane Barbarie4db8ca22017-04-24 10:30:20 -0400395 'bold') + ') '
Zsolt Haraszti50cae7d2017-01-08 22:27:07 -0800396
397 def get_device(self, device_id, depth=0):
398 stub = voltha_pb2.VolthaLocalServiceStub(self.get_channel())
399 res = stub.GetDevice(voltha_pb2.ID(id=device_id),
Stephane Barbarie4db8ca22017-04-24 10:30:20 -0400400 metadata=(('get-depth', str(depth)),))
Zsolt Haraszti50cae7d2017-01-08 22:27:07 -0800401 return res
Zsolt Haraszti80175202016-12-24 00:17:51 -0800402
403 def do_arrive_onus(self, line):
Zsolt Harasztid036b7e2016-12-23 15:36:01 -0800404 """
Zsolt Haraszti85f12852016-12-24 08:30:58 -0800405 Simulate the arrival of ONUs (available only on simulated_olt)
Zsolt Harasztid036b7e2016-12-23 15:36:01 -0800406 """
Zsolt Haraszti80175202016-12-24 00:17:51 -0800407 device_id = line or self.default_device_id
Zsolt Haraszti85f12852016-12-24 08:30:58 -0800408
409 # verify that device is of type simulated_olt
410 device = self.get_device(device_id)
411 assert device.type == 'simulated_olt', (
412 'Cannot use it on this device type (only on simulated_olt type)')
413
Zsolt Harasztid036b7e2016-12-23 15:36:01 -0800414 requests.get('http://{}/devices/{}/detect_onus'.format(
415 self.voltha_sim_rest, device_id
416 ))
417
Zsolt Haraszti80175202016-12-24 00:17:51 -0800418 complete_arrive_onus = VolthaCli.complete_device
419
Zsolt Haraszti85f12852016-12-24 08:30:58 -0800420 def get_logical_ports(self, logical_device_id):
421 """
422 Return the NNI port number and the first usable UNI port of logical
423 device, and the vlan associated with the latter.
424 """
425 stub = voltha_pb2.VolthaLocalServiceStub(self.get_channel())
426 ports = stub.ListLogicalDevicePorts(
427 voltha_pb2.ID(id=logical_device_id)).items
Zsolt Harasztib9a5f752017-02-11 06:07:08 -0800428 nni = None
429 unis = []
Zsolt Haraszti85f12852016-12-24 08:30:58 -0800430 for port in ports:
Zsolt Harasztib9a5f752017-02-11 06:07:08 -0800431 if port.root_port:
432 assert nni is None, "There shall be only one root port"
Zsolt Haraszti85f12852016-12-24 08:30:58 -0800433 nni = port.ofp_port.port_no
Zsolt Harasztib9a5f752017-02-11 06:07:08 -0800434 else:
Zsolt Haraszti85f12852016-12-24 08:30:58 -0800435 uni = port.ofp_port.port_no
436 uni_device = self.get_device(port.device_id)
437 vlan = uni_device.vlan
Zsolt Harasztib9a5f752017-02-11 06:07:08 -0800438 unis.append((uni, vlan))
439
440 assert nni is not None, "No NNI port found"
441 assert unis, "Not a single UNI?"
442
443 return nni, unis
Zsolt Haraszti85f12852016-12-24 08:30:58 -0800444
Zsolt Haraszti80175202016-12-24 00:17:51 -0800445 def do_install_eapol_flow(self, line):
Zsolt Harasztid036b7e2016-12-23 15:36:01 -0800446 """
447 Install an EAPOL flow on the given logical device. If device is not
448 given, it will be applied to logical device of the last pre-provisioned
449 OLT device.
450 """
Zsolt Haraszti80175202016-12-24 00:17:51 -0800451 logical_device_id = line or self.default_logical_device_id
Zsolt Haraszti85f12852016-12-24 08:30:58 -0800452
453 # gather NNI and UNI port IDs
Zsolt Harasztib9a5f752017-02-11 06:07:08 -0800454 nni_port_no, unis = self.get_logical_ports(logical_device_id)
Zsolt Haraszti85f12852016-12-24 08:30:58 -0800455
456 # construct and push flow rule
Zsolt Harasztid036b7e2016-12-23 15:36:01 -0800457 stub = voltha_pb2.VolthaLocalServiceStub(self.get_channel())
Zsolt Harasztib9a5f752017-02-11 06:07:08 -0800458 for uni_port_no, _ in unis:
459 update = FlowTableUpdate(
460 id=logical_device_id,
461 flow_mod=mk_simple_flow_mod(
462 priority=2000,
463 match_fields=[in_port(uni_port_no), eth_type(0x888e)],
464 actions=[
465 # push_vlan(0x8100),
466 # set_field(vlan_vid(4096 + 4000)),
467 output(ofp.OFPP_CONTROLLER)
468 ]
469 )
470 )
471 res = stub.UpdateLogicalDeviceFlowTable(update)
472 self.poutput('success for uni {} ({})'.format(uni_port_no, res))
Zsolt Harasztid036b7e2016-12-23 15:36:01 -0800473
Zsolt Haraszti80175202016-12-24 00:17:51 -0800474 complete_install_eapol_flow = VolthaCli.complete_logical_device
475
Zsolt Haraszti85f12852016-12-24 08:30:58 -0800476 def do_install_all_controller_bound_flows(self, line):
477 """
478 Install all flow rules for controller bound flows, including EAPOL,
479 IGMP and DHCP. If device is not given, it will be applied to logical
480 device of the last pre-provisioned OLT device.
481 """
482 logical_device_id = line or self.default_logical_device_id
483
484 # gather NNI and UNI port IDs
Zsolt Harasztib9a5f752017-02-11 06:07:08 -0800485 nni_port_no, unis = self.get_logical_ports(logical_device_id)
Zsolt Haraszti85f12852016-12-24 08:30:58 -0800486
487 # construct and push flow rules
488 stub = voltha_pb2.VolthaLocalServiceStub(self.get_channel())
489
Zsolt Harasztib9a5f752017-02-11 06:07:08 -0800490 for uni_port_no, _ in unis:
491 stub.UpdateLogicalDeviceFlowTable(FlowTableUpdate(
492 id=logical_device_id,
493 flow_mod=mk_simple_flow_mod(
494 priority=2000,
495 match_fields=[
496 in_port(uni_port_no),
497 eth_type(0x888e)
498 ],
499 actions=[output(ofp.OFPP_CONTROLLER)]
500 )
501 ))
502 stub.UpdateLogicalDeviceFlowTable(FlowTableUpdate(
503 id=logical_device_id,
504 flow_mod=mk_simple_flow_mod(
505 priority=1000,
506 match_fields=[
507 in_port(uni_port_no),
508 eth_type(0x800),
509 ip_proto(2)
510 ],
511 actions=[output(ofp.OFPP_CONTROLLER)]
512 )
513 ))
514 stub.UpdateLogicalDeviceFlowTable(FlowTableUpdate(
515 id=logical_device_id,
516 flow_mod=mk_simple_flow_mod(
517 priority=1000,
518 match_fields=[
519 in_port(uni_port_no),
520 eth_type(0x800),
521 ip_proto(17),
522 udp_dst(67)
523 ],
524 actions=[output(ofp.OFPP_CONTROLLER)]
525 )
526 ))
Zsolt Haraszti85f12852016-12-24 08:30:58 -0800527 self.poutput('success')
528
529 complete_install_all_controller_bound_flows = \
530 VolthaCli.complete_logical_device
531
532 def do_install_all_sample_flows(self, line):
533 """
534 Install all flows that are representative of the virtualized access
535 scenario in a PON network.
536 """
537 logical_device_id = line or self.default_logical_device_id
538
539 # gather NNI and UNI port IDs
Zsolt Harasztib9a5f752017-02-11 06:07:08 -0800540 nni_port_no, unis = self.get_logical_ports(logical_device_id)
Zsolt Haraszti85f12852016-12-24 08:30:58 -0800541
542 # construct and push flow rules
543 stub = voltha_pb2.VolthaLocalServiceStub(self.get_channel())
544
Zsolt Harasztib9a5f752017-02-11 06:07:08 -0800545 for uni_port_no, c_vid in unis:
Zsolt Harasztib9a5f752017-02-11 06:07:08 -0800546 # Controller-bound flows
547 stub.UpdateLogicalDeviceFlowTable(FlowTableUpdate(
548 id=logical_device_id,
549 flow_mod=mk_simple_flow_mod(
550 priority=2000,
551 match_fields=[in_port(uni_port_no), eth_type(0x888e)],
552 actions=[
553 # push_vlan(0x8100),
554 # set_field(vlan_vid(4096 + 4000)),
555 output(ofp.OFPP_CONTROLLER)
556 ]
557 )
558 ))
559 stub.UpdateLogicalDeviceFlowTable(FlowTableUpdate(
560 id=logical_device_id,
561 flow_mod=mk_simple_flow_mod(
562 priority=1000,
563 match_fields=[eth_type(0x800), ip_proto(2)],
564 actions=[output(ofp.OFPP_CONTROLLER)]
565 )
566 ))
567 stub.UpdateLogicalDeviceFlowTable(FlowTableUpdate(
568 id=logical_device_id,
569 flow_mod=mk_simple_flow_mod(
570 priority=1000,
571 match_fields=[eth_type(0x800), ip_proto(17), udp_dst(67)],
572 actions=[output(ofp.OFPP_CONTROLLER)]
573 )
574 ))
575
576 # Unicast flows:
577 # Downstream flow 1
578 stub.UpdateLogicalDeviceFlowTable(FlowTableUpdate(
579 id=logical_device_id,
580 flow_mod=mk_simple_flow_mod(
581 priority=500,
582 match_fields=[
583 in_port(nni_port_no),
584 vlan_vid(4096 + 1000),
585 metadata(c_vid) # here to mimic an ONOS artifact
586 ],
587 actions=[pop_vlan()],
588 next_table_id=1
589 )
590 ))
591 # Downstream flow 2
592 stub.UpdateLogicalDeviceFlowTable(FlowTableUpdate(
593 id=logical_device_id,
594 flow_mod=mk_simple_flow_mod(
595 priority=500,
596 table_id=1,
597 match_fields=[in_port(nni_port_no), vlan_vid(4096 + c_vid)],
598 actions=[set_field(vlan_vid(4096 + 0)), output(uni_port_no)]
599 )
600 ))
601 # Upstream flow 1 for 0-tagged case
602 stub.UpdateLogicalDeviceFlowTable(FlowTableUpdate(
603 id=logical_device_id,
604 flow_mod=mk_simple_flow_mod(
605 priority=500,
606 match_fields=[in_port(uni_port_no), vlan_vid(4096 + 0)],
607 actions=[set_field(vlan_vid(4096 + c_vid))],
608 next_table_id=1
609 )
610 ))
611 # Upstream flow 1 for untagged case
612 stub.UpdateLogicalDeviceFlowTable(FlowTableUpdate(
613 id=logical_device_id,
614 flow_mod=mk_simple_flow_mod(
615 priority=500,
616 match_fields=[in_port(uni_port_no), vlan_vid(0)],
617 actions=[push_vlan(0x8100), set_field(vlan_vid(4096 + c_vid))],
618 next_table_id=1
619 )
620 ))
621 # Upstream flow 2 for s-tag
622 stub.UpdateLogicalDeviceFlowTable(FlowTableUpdate(
623 id=logical_device_id,
624 flow_mod=mk_simple_flow_mod(
625 priority=500,
626 table_id=1,
627 match_fields=[in_port(uni_port_no), vlan_vid(4096 + c_vid)],
628 actions=[
629 push_vlan(0x8100),
630 set_field(vlan_vid(4096 + 1000)),
631 output(nni_port_no)
632 ]
633 )
634 ))
Zsolt Haraszti85f12852016-12-24 08:30:58 -0800635
636 # Push a few multicast flows
Zsolt Harasztib9a5f752017-02-11 06:07:08 -0800637 # 1st with one bucket for our uni 0
Zsolt Haraszti85f12852016-12-24 08:30:58 -0800638 stub.UpdateLogicalDeviceFlowGroupTable(FlowGroupTableUpdate(
639 id=logical_device_id,
640 group_mod=mk_multicast_group_mod(
641 group_id=1,
642 buckets=[
Zsolt Harasztib9a5f752017-02-11 06:07:08 -0800643 ofp.ofp_bucket(actions=[
644 pop_vlan(),
645 output(unis[0][0])
646 ])
Zsolt Haraszti85f12852016-12-24 08:30:58 -0800647 ]
648 )
649 ))
650 stub.UpdateLogicalDeviceFlowTable(FlowTableUpdate(
651 id=logical_device_id,
652 flow_mod=mk_simple_flow_mod(
653 priority=1000,
654 match_fields=[
655 in_port(nni_port_no),
656 eth_type(0x800),
657 vlan_vid(4096 + 140),
658 ipv4_dst(0xe4010101)
659 ],
660 actions=[group(1)]
661 )
662 ))
Zsolt Harasztib9a5f752017-02-11 06:07:08 -0800663
664 # 2nd with one bucket for uni 0 and 1
Zsolt Haraszti85f12852016-12-24 08:30:58 -0800665 stub.UpdateLogicalDeviceFlowGroupTable(FlowGroupTableUpdate(
666 id=logical_device_id,
667 group_mod=mk_multicast_group_mod(
668 group_id=2,
669 buckets=[
Nathan Knuth6b7b6ff2017-02-12 03:30:48 -0800670 ofp.ofp_bucket(actions=[pop_vlan(), output(unis[0][0])])
Stephane Barbarie4db8ca22017-04-24 10:30:20 -0400671 # ofp.ofp_bucket(actions=[pop_vlan(), output(unis[1][0])])
Zsolt Haraszti85f12852016-12-24 08:30:58 -0800672 ]
673 )
674 ))
675 stub.UpdateLogicalDeviceFlowTable(FlowTableUpdate(
676 id=logical_device_id,
677 flow_mod=mk_simple_flow_mod(
678 priority=1000,
679 match_fields=[
680 in_port(nni_port_no),
681 eth_type(0x800),
682 vlan_vid(4096 + 140),
683 ipv4_dst(0xe4020202)
684 ],
685 actions=[group(2)]
686 )
687 ))
Zsolt Harasztib9a5f752017-02-11 06:07:08 -0800688
Zsolt Haraszti85f12852016-12-24 08:30:58 -0800689 # 3rd with empty bucket
690 stub.UpdateLogicalDeviceFlowGroupTable(FlowGroupTableUpdate(
691 id=logical_device_id,
692 group_mod=mk_multicast_group_mod(
693 group_id=3,
694 buckets=[]
695 )
696 ))
697 stub.UpdateLogicalDeviceFlowTable(FlowTableUpdate(
698 id=logical_device_id,
699 flow_mod=mk_simple_flow_mod(
700 priority=1000,
701 match_fields=[
702 in_port(nni_port_no),
703 eth_type(0x800),
704 vlan_vid(4096 + 140),
705 ipv4_dst(0xe4030303)
706 ],
707 actions=[group(3)]
708 )
709 ))
710
711 self.poutput('success')
712
713 complete_install_all_sample_flows = VolthaCli.complete_logical_device
714
Nathan Knuth5f4163e2017-01-11 18:21:10 -0600715 def do_install_dhcp_flows(self, line):
716 """
717 Install all dhcp flows that are representative of the virtualized access
718 scenario in a PON network.
719 """
720 logical_device_id = line or self.default_logical_device_id
721
722 # gather NNI and UNI port IDs
Zsolt Harasztib9a5f752017-02-11 06:07:08 -0800723 nni_port_no, unis = self.get_logical_ports(logical_device_id)
Nathan Knuth5f4163e2017-01-11 18:21:10 -0600724
725 # construct and push flow rules
726 stub = voltha_pb2.VolthaLocalServiceStub(self.get_channel())
727
728 # Controller-bound flows
Zsolt Harasztib9a5f752017-02-11 06:07:08 -0800729 for uni_port_no, _ in unis:
730 stub.UpdateLogicalDeviceFlowTable(FlowTableUpdate(
731 id=logical_device_id,
732 flow_mod=mk_simple_flow_mod(
733 priority=1000,
734 match_fields=[
735 in_port(uni_port_no),
736 eth_type(0x800),
737 ip_proto(17),
738 udp_dst(67)
739 ],
740 actions=[output(ofp.OFPP_CONTROLLER)]
741 )
742 ))
Nathan Knuth5f4163e2017-01-11 18:21:10 -0600743
744 self.poutput('success')
745
746 complete_install_dhcp_flows = VolthaCli.complete_logical_device
747
Zsolt Haraszti85f12852016-12-24 08:30:58 -0800748 def do_delete_all_flows(self, line):
749 """
750 Remove all flows and flow groups from given logical device
751 """
752 logical_device_id = line or self.default_logical_device_id
753 stub = voltha_pb2.VolthaLocalServiceStub(self.get_channel())
754 stub.UpdateLogicalDeviceFlowTable(FlowTableUpdate(
755 id=logical_device_id,
756 flow_mod=ofp.ofp_flow_mod(
757 command=ofp.OFPFC_DELETE,
758 table_id=ofp.OFPTT_ALL,
759 cookie_mask=0,
760 out_port=ofp.OFPP_ANY,
761 out_group=ofp.OFPG_ANY
762 )
763 ))
764 stub.UpdateLogicalDeviceFlowGroupTable(FlowGroupTableUpdate(
765 id=logical_device_id,
766 group_mod=ofp.ofp_group_mod(
767 command=ofp.OFPGC_DELETE,
768 group_id=ofp.OFPG_ALL
769 )
770 ))
771 self.poutput('success')
772
773 complete_delete_all_flows = VolthaCli.complete_logical_device
774
Zsolt Haraszti80175202016-12-24 00:17:51 -0800775 def do_send_simulated_upstream_eapol(self, line):
Zsolt Harasztid036b7e2016-12-23 15:36:01 -0800776 """
777 Send an EAPOL upstream from a simulated OLT
778 """
Zsolt Haraszti80175202016-12-24 00:17:51 -0800779 device_id = line or self.default_device_id
Zsolt Harasztid036b7e2016-12-23 15:36:01 -0800780 requests.get('http://{}/devices/{}/test_eapol_in'.format(
781 self.voltha_sim_rest, device_id
782 ))
783
Zsolt Haraszti80175202016-12-24 00:17:51 -0800784 complete_send_simulated_upstream_eapol = VolthaCli.complete_device
785
786 def do_inject_eapol_start(self, line):
Zsolt Harasztid036b7e2016-12-23 15:36:01 -0800787 """
788 Send out an an EAPOL start message into the given Unix interface
789 """
790 pass
Zsolt Harasztia133a452016-12-22 01:26:57 -0800791
792
793if __name__ == '__main__':
Zsolt Haraszti9b485fb2016-12-26 23:11:15 -0800794
795 parser = argparse.ArgumentParser()
796
797 _help = '<hostname>:<port> to consul agent (default: %s)' % defs['consul']
798 parser.add_argument(
799 '-C', '--consul', action='store', default=defs['consul'], help=_help)
800
801 _help = 'Lookup Voltha endpoints based on service entries in Consul'
802 parser.add_argument(
803 '-L', '--lookup', action='store_true', help=_help)
804
805 _help = '<hostname>:<port> of Voltha gRPC service (default={})'.format(
806 defs['voltha_grpc_endpoint'])
807 parser.add_argument('-g', '--grpc-endpoint', action='store',
808 default=defs['voltha_grpc_endpoint'], help=_help)
809
810 _help = '<hostname>:<port> of Voltha simulated adapter backend for ' \
811 'testing (default={})'.format(
812 defs['voltha_sim_rest_endpoint'])
813 parser.add_argument('-s', '--sim-rest-endpoint', action='store',
814 default=defs['voltha_sim_rest_endpoint'], help=_help)
815
816 args = parser.parse_args()
817
818 if args.lookup:
819 host = args.consul.split(':')[0].strip()
820 port = int(args.consul.split(':')[1].strip())
821 consul = Consul(host=host, port=port)
822
823 _, services = consul.catalog.service('voltha-grpc')
824 if not services:
825 print('No voltha-grpc service registered in consul; exiting')
826 sys.exit(1)
827 args.grpc_endpoint = '{}:{}'.format(services[0]['ServiceAddress'],
828 services[0]['ServicePort'])
829
830 _, services = consul.catalog.service('voltha-sim-rest')
831 if not services:
832 print('No voltha-sim-rest service registered in consul; exiting')
833 sys.exit(1)
834 args.sim_rest_endpoint = '{}:{}'.format(services[0]['ServiceAddress'],
835 services[0]['ServicePort'])
836
837 c = VolthaCli(args.grpc_endpoint, args.sim_rest_endpoint)
Zsolt Haraszti80175202016-12-24 00:17:51 -0800838 c.poutput(banner)
Zsolt Harasztia133a452016-12-22 01:26:57 -0800839 c.load_history()
840 c.cmdloop()
841 c.save_history()