blob: a05041a2f5866368246ed0dbac96b3985d6066f5 [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
Nikolay Titov89004ec2017-06-19 18:22:42 -040032from cli.xpon import XponCli
Stephane Barbarie4db8ca22017-04-24 10:30:20 -040033from cli.alarm_filters import AlarmFiltersCli
Zsolt Harasztid036b7e2016-12-23 15:36:01 -080034from cli.logical_device import LogicalDeviceCli
Stephane Barbarie4db8ca22017-04-24 10:30:20 -040035from cli.table import print_pb_list_as_table
Zsolt Harasztid036b7e2016-12-23 15:36:01 -080036from voltha.core.flow_decomposer import *
Zsolt Harasztia133a452016-12-22 01:26:57 -080037from voltha.protos import third_party
38from voltha.protos import voltha_pb2
Zsolt Haraszti85f12852016-12-24 08:30:58 -080039from voltha.protos.openflow_13_pb2 import FlowTableUpdate, FlowGroupTableUpdate
Zsolt Harasztid036b7e2016-12-23 15:36:01 -080040
Zsolt Harasztia133a452016-12-22 01:26:57 -080041_ = third_party
Stephane Barbarie4db8ca22017-04-24 10:30:20 -040042from cli.utils import pb2dict
Zsolt Haraszti9b485fb2016-12-26 23:11:15 -080043
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:50055'),
49 voltha_sim_rest_endpoint=os.environ.get('VOLTHA_SIM_REST_ENDPOINT',
50 'localhost:18880'),
51)
52
Zsolt Harasztid036b7e2016-12-23 15:36:01 -080053banner = """\
Zsolt Haraszti313c4be2016-12-27 11:06:53 -080054 _ _ _ ___ _ ___
55__ _____| | |_| |_ __ _ / __| | |_ _|
56\ V / _ \ | _| ' \/ _` | | (__| |__ | |
57 \_/\___/_|\__|_||_\__,_| \___|____|___|
Zsolt Haraszti80175202016-12-24 00:17:51 -080058(to exit type quit or hit Ctrl-D)
Zsolt Harasztid036b7e2016-12-23 15:36:01 -080059"""
Zsolt Harasztia133a452016-12-22 01:26:57 -080060
Zsolt Harasztia133a452016-12-22 01:26:57 -080061
Stephane Barbarie4db8ca22017-04-24 10:30:20 -040062class VolthaCli(Cmd):
Zsolt Harasztia133a452016-12-22 01:26:57 -080063 prompt = 'voltha'
64 history_file_name = '.voltha_cli_history'
Zsolt Harasztid036b7e2016-12-23 15:36:01 -080065
66 # Settable CLI parameters
67 voltha_grpc = 'localhost:50055'
68 voltha_sim_rest = 'localhost:18880'
Zsolt Harasztia133a452016-12-22 01:26:57 -080069 max_history_lines = 500
Zsolt Harasztid036b7e2016-12-23 15:36:01 -080070 default_device_id = None
71 default_logical_device_id = None
Zsolt Harasztia133a452016-12-22 01:26:57 -080072
73 Cmd.settable.update(dict(
Zsolt Harasztid036b7e2016-12-23 15:36:01 -080074 voltha_grpc='Voltha GRPC endpoint in form of <host>:<port>',
75 voltha_sim_rest='Voltha simulation back door for testing in form '
76 'of <host>:<port>',
77 max_history_lines='Maximum number of history lines stored across '
78 'sessions',
79 default_device_id='Device id used when no device id is specified',
80 default_logical_device_id='Logical device id used when no device id '
81 'is specified',
Zsolt Harasztia133a452016-12-22 01:26:57 -080082 ))
83
Zsolt Haraszti9b485fb2016-12-26 23:11:15 -080084 # cleanup of superfluous commands from cmd2
Zsolt Haraszti80175202016-12-24 00:17:51 -080085 del Cmd.do_cmdenvironment
Steve Crooks05f24522017-02-27 13:32:27 -050086 del Cmd.do_load
Zsolt Haraszti80175202016-12-24 00:17:51 -080087 del Cmd.do__relative_load
Zsolt Haraszti80175202016-12-24 00:17:51 -080088
Zsolt Haraszti9b485fb2016-12-26 23:11:15 -080089 def __init__(self, voltha_grpc, voltha_sim_rest):
90 VolthaCli.voltha_grpc = voltha_grpc
91 VolthaCli.voltha_sim_rest = voltha_sim_rest
92 Cmd.__init__(self)
Zsolt Harasztia133a452016-12-22 01:26:57 -080093 self.prompt = '(' + self.colorize(
Zsolt Haraszti80175202016-12-24 00:17:51 -080094 self.colorize(self.prompt, 'blue'), 'bold') + ') '
Zsolt Harasztia133a452016-12-22 01:26:57 -080095 self.channel = None
Zsolt Haraszti80175202016-12-24 00:17:51 -080096 self.device_ids_cache = None
97 self.device_ids_cache_ts = time()
98 self.logical_device_ids_cache = None
99 self.logical_device_ids_cache_ts = time()
Zsolt Harasztia133a452016-12-22 01:26:57 -0800100
Zsolt Haraszti9b485fb2016-12-26 23:11:15 -0800101 # we override cmd2's method to avoid its optparse conflicting with our
102 # command line parsing
103 def cmdloop(self):
104 self._cmdloop()
105
Zsolt Harasztia133a452016-12-22 01:26:57 -0800106 def load_history(self):
107 """Load saved command history from local history file"""
108 try:
109 with file(self.history_file_name, 'r') as f:
110 for line in f.readlines():
111 stripped_line = line.strip()
112 self.history.append(stripped_line)
113 readline.add_history(stripped_line)
114 except IOError:
115 pass # ignore if file cannot be read
116
117 def save_history(self):
118 try:
Zsolt Haraszti80175202016-12-24 00:17:51 -0800119 with open(self.history_file_name, 'w') as f:
Zsolt Harasztia133a452016-12-22 01:26:57 -0800120 f.write('\n'.join(self.history[-self.max_history_lines:]))
Zsolt Haraszti80175202016-12-24 00:17:51 -0800121 except IOError as e:
122 self.perror('Could not save history in {}: {}'.format(
123 self.history_file_name, e))
Zsolt Harasztia133a452016-12-22 01:26:57 -0800124 else:
Zsolt Haraszti9b485fb2016-12-26 23:11:15 -0800125 self.poutput('History saved as {}'.format(
Zsolt Haraszti80175202016-12-24 00:17:51 -0800126 self.history_file_name))
127
128 def perror(self, errmsg, statement=None):
129 # Touch it up to make sure error is prefixed and colored
130 Cmd.perror(self, self.colorize('***ERROR: ', 'red') + errmsg,
131 statement)
Zsolt Harasztia133a452016-12-22 01:26:57 -0800132
133 def get_channel(self):
134 if self.channel is None:
135 self.channel = grpc.insecure_channel(self.voltha_grpc)
136 return self.channel
137
Zsolt Haraszti80175202016-12-24 00:17:51 -0800138 # ~~~~~~~~~~~~~~~~~ ACTUAL COMMAND IMPLEMENTATIONS ~~~~~~~~~~~~~~~~~~~~~~~~
Zsolt Harasztid036b7e2016-12-23 15:36:01 -0800139
Zsolt Haraszti80175202016-12-24 00:17:51 -0800140 def do_reset_history(self, line):
Zsolt Harasztia133a452016-12-22 01:26:57 -0800141 """Reset CLI history"""
142 while self.history:
143 self.history.pop()
144
Zsolt Haraszti80175202016-12-24 00:17:51 -0800145 def do_launch(self, line):
Zsolt Harasztia133a452016-12-22 01:26:57 -0800146 """If Voltha is not running yet, launch it"""
Zsolt Haraszti80175202016-12-24 00:17:51 -0800147 raise NotImplementedError('not implemented yet')
Zsolt Harasztia133a452016-12-22 01:26:57 -0800148
Zsolt Haraszti80175202016-12-24 00:17:51 -0800149 def do_restart(self, line):
Zsolt Harasztia133a452016-12-22 01:26:57 -0800150 """Launch Voltha, but if it is already running, terminate it first"""
151 pass
152
Zsolt Haraszti80175202016-12-24 00:17:51 -0800153 def do_adapters(self, line):
154 """List loaded adapter"""
155 stub = voltha_pb2.VolthaLocalServiceStub(self.get_channel())
156 res = stub.ListAdapters(Empty())
Sergio Slobodrian6e9fb692017-03-17 14:46:33 -0400157 omit_fields = {'config.log_level', 'logical_device_ids'}
Zsolt Haraszti85f12852016-12-24 08:30:58 -0800158 print_pb_list_as_table('Adapters:', res.items, omit_fields, self.poutput)
Zsolt Haraszti80175202016-12-24 00:17:51 -0800159
160 def get_devices(self):
Zsolt Harasztia133a452016-12-22 01:26:57 -0800161 stub = voltha_pb2.VolthaLocalServiceStub(self.get_channel())
162 res = stub.ListDevices(Empty())
Zsolt Haraszti80175202016-12-24 00:17:51 -0800163 return res.items
Zsolt Harasztia133a452016-12-22 01:26:57 -0800164
Zsolt Haraszti80175202016-12-24 00:17:51 -0800165 def get_logical_devices(self):
166 stub = voltha_pb2.VolthaLocalServiceStub(self.get_channel())
167 res = stub.ListLogicalDevices(Empty())
168 return res.items
169
170 def do_devices(self, line):
171 """List devices registered in Voltha"""
172 devices = self.get_devices()
173 omit_fields = {
174 'adapter',
175 'vendor',
176 'model',
177 'hardware_version',
ggowdru236bd952017-06-20 20:32:55 -0700178 'images',
Zsolt Haraszti80175202016-12-24 00:17:51 -0800179 'firmware_version',
Sergio Slobodriana95f99b2017-03-21 10:22:47 -0400180 'serial_number'
Zsolt Haraszti80175202016-12-24 00:17:51 -0800181 }
Zsolt Haraszti85f12852016-12-24 08:30:58 -0800182 print_pb_list_as_table('Devices:', devices, omit_fields, self.poutput)
Zsolt Haraszti80175202016-12-24 00:17:51 -0800183
184 def do_logical_devices(self, line):
Zsolt Harasztia133a452016-12-22 01:26:57 -0800185 """List logical devices in Voltha"""
186 stub = voltha_pb2.VolthaLocalServiceStub(self.get_channel())
187 res = stub.ListLogicalDevices(Empty())
Zsolt Haraszti80175202016-12-24 00:17:51 -0800188 omit_fields = {
189 'desc.mfr_desc',
190 'desc.hw_desc',
191 'desc.sw_desc',
192 'desc.dp_desc',
193 'desc.serial_number',
Sergio Slobodriana95f99b2017-03-21 10:22:47 -0400194 'switch_features.capabilities'
Zsolt Haraszti80175202016-12-24 00:17:51 -0800195 }
Zsolt Haraszti85f12852016-12-24 08:30:58 -0800196 print_pb_list_as_table('Logical devices:', res.items, omit_fields,
197 self.poutput)
Zsolt Harasztia133a452016-12-22 01:26:57 -0800198
Zsolt Haraszti80175202016-12-24 00:17:51 -0800199 def do_device(self, line):
Zsolt Harasztia133a452016-12-22 01:26:57 -0800200 """Enter device level command mode"""
Zsolt Haraszti80175202016-12-24 00:17:51 -0800201 device_id = line.strip() or self.default_device_id
Zsolt Harasztid036b7e2016-12-23 15:36:01 -0800202 if not device_id:
203 raise Exception('<device-id> parameter needed')
204 sub = DeviceCli(self.get_channel, device_id)
Zsolt Harasztia133a452016-12-22 01:26:57 -0800205 sub.cmdloop()
206
Zsolt Haraszti80175202016-12-24 00:17:51 -0800207 def do_logical_device(self, line):
Zsolt Harasztia133a452016-12-22 01:26:57 -0800208 """Enter logical device level command mode"""
Zsolt Haraszti80175202016-12-24 00:17:51 -0800209 logical_device_id = line.strip() or self.default_logical_device_id
Zsolt Harasztid036b7e2016-12-23 15:36:01 -0800210 if not logical_device_id:
211 raise Exception('<logical-device-id> parameter needed')
212 sub = LogicalDeviceCli(self.get_channel, logical_device_id)
Zsolt Harasztia133a452016-12-22 01:26:57 -0800213 sub.cmdloop()
214
Zsolt Haraszti80175202016-12-24 00:17:51 -0800215 def device_ids(self, force_refresh=False):
216 if force_refresh or self.device_ids is None or \
Stephane Barbarie4db8ca22017-04-24 10:30:20 -0400217 (time() - self.device_ids_cache_ts) > 1:
Zsolt Haraszti80175202016-12-24 00:17:51 -0800218 self.device_ids_cache = [d.id for d in self.get_devices()]
219 self.device_ids_cache_ts = time()
220 return self.device_ids_cache
221
222 def logical_device_ids(self, force_refresh=False):
223 if force_refresh or self.logical_device_ids is None or \
Stephane Barbarie4db8ca22017-04-24 10:30:20 -0400224 (time() - self.logical_device_ids_cache_ts) > 1:
Zsolt Haraszti80175202016-12-24 00:17:51 -0800225 self.logical_device_ids_cache = [d.id for d
226 in self.get_logical_devices()]
227 self.logical_device_ids_cache_ts = time()
228 return self.logical_device_ids_cache
229
230 def complete_device(self, text, line, begidx, endidx):
231 if not text:
232 completions = self.device_ids()[:]
233 else:
234 completions = [d for d in self.device_ids() if d.startswith(text)]
235 return completions
236
237 def complete_logical_device(self, text, line, begidx, endidx):
238 if not text:
239 completions = self.logical_device_ids()[:]
240 else:
241 completions = [d for d in self.logical_device_ids()
242 if d.startswith(text)]
243 return completions
244
Nikolay Titov89004ec2017-06-19 18:22:42 -0400245 def do_xpon(self, line):
246 """xpon <optional> [device_ID] - Enter xpon level command mode"""
247 device_id = line.strip()
248 if not device_id:
249 sub = XponCli(self.get_channel, "")
250 else:
251 sub = XponCli(self.get_channel, device_id)
252 sub.cmdloop()
253
Zsolt Haraszti80175202016-12-24 00:17:51 -0800254 def do_pdb(self, line):
Zsolt Harasztia133a452016-12-22 01:26:57 -0800255 """Launch PDB debug prompt in CLI (for CLI development)"""
256 from pdb import set_trace
257 set_trace()
258
Zsolt Haraszti80175202016-12-24 00:17:51 -0800259 def do_health(self, line):
Zsolt Harasztia133a452016-12-22 01:26:57 -0800260 """Show connectivity status to Voltha status"""
261 stub = voltha_pb2.HealthServiceStub(self.get_channel())
262 res = stub.GetHealthStatus(Empty())
Zsolt Haraszti80175202016-12-24 00:17:51 -0800263 self.poutput(dumps(pb2dict(res), indent=4))
Zsolt Harasztia133a452016-12-22 01:26:57 -0800264
Zsolt Harasztid036b7e2016-12-23 15:36:01 -0800265 @options([
266 make_option('-t', '--device-type', action="store", dest='device_type',
Stephane Barbarie4db8ca22017-04-24 10:30:20 -0400267 help="Device type", default='simulated_olt'),
Zsolt Harasztid036b7e2016-12-23 15:36:01 -0800268 make_option('-m', '--mac-address', action='store', dest='mac_address',
269 default='00:0c:e2:31:40:00'),
270 make_option('-i', '--ip-address', action='store', dest='ip_address'),
Zsolt Haraszti656ecc62016-12-28 15:08:23 -0800271 make_option('-H', '--host_and_port', action='store',
272 dest='host_and_port'),
Zsolt Harasztid036b7e2016-12-23 15:36:01 -0800273 ])
Zsolt Haraszti80175202016-12-24 00:17:51 -0800274 def do_preprovision_olt(self, line, opts):
Zsolt Harasztid036b7e2016-12-23 15:36:01 -0800275 """Preprovision a new OLT with given device type"""
Zsolt Harasztia133a452016-12-22 01:26:57 -0800276 stub = voltha_pb2.VolthaLocalServiceStub(self.get_channel())
Zsolt Harasztid036b7e2016-12-23 15:36:01 -0800277 kw = dict(type=opts.device_type)
Zsolt Haraszti656ecc62016-12-28 15:08:23 -0800278 if opts.host_and_port:
279 kw['host_and_port'] = opts.host_and_port
280 elif opts.ip_address:
Zsolt Harasztid036b7e2016-12-23 15:36:01 -0800281 kw['ipv4_address'] = opts.ip_address
282 elif opts.mac_address:
283 kw['mac_address'] = opts.mac_address
284 else:
285 raise Exception('Either IP address or Mac Address is needed')
Chip Boling90b224d2017-06-02 11:51:48 -0500286 # Pass any extra arguments past '--' to the device as custom arguments
287 kw['extra_args'] = line
288
Zsolt Harasztid036b7e2016-12-23 15:36:01 -0800289 device = voltha_pb2.Device(**kw)
290 device = stub.CreateDevice(device)
Zsolt Haraszti80175202016-12-24 00:17:51 -0800291 self.poutput('success (device id = {})'.format(device.id))
Zsolt Harasztid036b7e2016-12-23 15:36:01 -0800292 self.default_device_id = device.id
Zsolt Harasztia133a452016-12-22 01:26:57 -0800293
Khen Nursimulud068d812017-03-06 11:44:18 -0500294 def do_enable(self, line):
Zsolt Harasztid036b7e2016-12-23 15:36:01 -0800295 """
Khen Nursimulud068d812017-03-06 11:44:18 -0500296 Enable a device. If the <id> is not provided, it will be on the last
297 pre-provisioned device.
Zsolt Harasztid036b7e2016-12-23 15:36:01 -0800298 """
Zsolt Haraszti80175202016-12-24 00:17:51 -0800299 device_id = line or self.default_device_id
Khen Nursimulu29e75502017-03-07 17:26:50 -0500300 self.poutput('enabling {}'.format(device_id))
Khen Nursimuluc60afa12017-03-13 14:33:50 -0400301 try:
302 stub = voltha_pb2.VolthaLocalServiceStub(self.get_channel())
303 stub.EnableDevice(voltha_pb2.ID(id=device_id))
Zsolt Harasztia133a452016-12-22 01:26:57 -0800304
Khen Nursimuluc60afa12017-03-13 14:33:50 -0400305 while True:
306 device = stub.GetDevice(voltha_pb2.ID(id=device_id))
307 # If this is an OLT then acquire logical device id
308 if device.oper_status == voltha_pb2.OperStatus.ACTIVE:
309 if device.type.endswith('_olt'):
310 assert device.parent_id
311 self.default_logical_device_id = device.parent_id
312 self.poutput('success (logical device id = {})'.format(
313 self.default_logical_device_id))
314 else:
315 self.poutput('success (device id = {})'.format(device.id))
316 break
317 self.poutput('waiting for device to be enabled...')
318 sleep(.5)
319 except Exception, e:
320 self.poutput('Error enabling {}. Error:{}'.format(device_id, e))
Zsolt Harasztid036b7e2016-12-23 15:36:01 -0800321
Zsolt Haraszti50cae7d2017-01-08 22:27:07 -0800322 complete_activate_olt = complete_device
323
Khen Nursimulud068d812017-03-06 11:44:18 -0500324 def do_reboot(self, line):
325 """
326 Rebooting a device. ID of the device needs to be provided
327 """
328 device_id = line or self.default_device_id
329 self.poutput('rebooting {}'.format(device_id))
Khen Nursimuluc60afa12017-03-13 14:33:50 -0400330 try:
331 stub = voltha_pb2.VolthaLocalServiceStub(self.get_channel())
332 stub.RebootDevice(voltha_pb2.ID(id=device_id))
333 self.poutput('rebooted {}'.format(device_id))
334 except Exception, e:
335 self.poutput('Error rebooting {}. Error:{}'.format(device_id, e))
Khen Nursimulud068d812017-03-06 11:44:18 -0500336
sathishg5ae86222017-06-28 15:16:29 +0530337 def do_self_test(self, line):
338 """
339 Self Test a device. ID of the device needs to be provided
340 """
341 device_id = line or self.default_device_id
342 self.poutput('Self Testing {}'.format(device_id))
343 try:
344 stub = voltha_pb2.VolthaLocalServiceStub(self.get_channel())
345 res = stub.SelfTest(voltha_pb2.ID(id=device_id))
346 self.poutput('Self Tested {}'.format(device_id))
347 self.poutput(dumps(pb2dict(res), indent=4))
348 except Exception, e:
349 self.poutput('Error in self test {}. Error:{}'.format(device_id, e))
350
Khen Nursimulud068d812017-03-06 11:44:18 -0500351 def do_delete(self, line):
352 """
353 Deleting a device. ID of the device needs to be provided
354 """
355 device_id = line or self.default_device_id
356 self.poutput('deleting {}'.format(device_id))
Khen Nursimuluc60afa12017-03-13 14:33:50 -0400357 try:
358 stub = voltha_pb2.VolthaLocalServiceStub(self.get_channel())
359 stub.DeleteDevice(voltha_pb2.ID(id=device_id))
360 self.poutput('deleted {}'.format(device_id))
361 except Exception, e:
362 self.poutput('Error deleting {}. Error:{}'.format(device_id, e))
Khen Nursimulud068d812017-03-06 11:44:18 -0500363
364 def do_disable(self, line):
365 """
366 Disable a device. ID of the device needs to be provided
367 """
368 device_id = line
369 self.poutput('disabling {}'.format(device_id))
Khen Nursimuluc60afa12017-03-13 14:33:50 -0400370 try:
371 stub = voltha_pb2.VolthaLocalServiceStub(self.get_channel())
372 stub.DisableDevice(voltha_pb2.ID(id=device_id))
Khen Nursimulud068d812017-03-06 11:44:18 -0500373
Khen Nursimuluc60afa12017-03-13 14:33:50 -0400374 # Do device query and verify that the device admin status is
375 # DISABLED and Operational Status is unknown
376 device = stub.GetDevice(voltha_pb2.ID(id=device_id))
377 if device.oper_status == voltha_pb2.OperStatus.UNKNOWN and \
378 device.admin_state == voltha_pb2.AdminState.DISABLED:
379 self.poutput('disabled successfully {}'.format(device_id))
380 else:
381 self.poutput('disabling failed {}. Admin State:{} '
382 'Operation State: {}'.format(device_id,
383 device.admin_state,
384 device.oper_status))
385 except Exception, e:
386 self.poutput('Error disabling {}. Error:{}'.format(device_id, e))
Khen Nursimulud068d812017-03-06 11:44:18 -0500387
Zsolt Haraszti50cae7d2017-01-08 22:27:07 -0800388 def do_test(self, line):
389 """Enter test mode, which makes a bunch on new commands available"""
390 sub = TestCli(self.history, self.get_channel, self.voltha_grpc,
391 self.voltha_sim_rest)
392 sub.cmdloop()
393
Stephane Barbarie4db8ca22017-04-24 10:30:20 -0400394 def do_alarm_filters(self, line):
395 sub = AlarmFiltersCli(self.get_channel)
396 sub.cmdloop()
397
Zsolt Haraszti50cae7d2017-01-08 22:27:07 -0800398
399class TestCli(VolthaCli):
Zsolt Haraszti50cae7d2017-01-08 22:27:07 -0800400 def __init__(self, history, get_channel, voltha_grpc, voltha_sim_rest):
401 VolthaCli.__init__(self, voltha_grpc, voltha_sim_rest)
402 self.history = history
403 self.get_channel = get_channel
404 self.prompt = '(' + self.colorize(self.colorize('test', 'cyan'),
Stephane Barbarie4db8ca22017-04-24 10:30:20 -0400405 'bold') + ') '
Zsolt Haraszti50cae7d2017-01-08 22:27:07 -0800406
407 def get_device(self, device_id, depth=0):
408 stub = voltha_pb2.VolthaLocalServiceStub(self.get_channel())
409 res = stub.GetDevice(voltha_pb2.ID(id=device_id),
Stephane Barbarie4db8ca22017-04-24 10:30:20 -0400410 metadata=(('get-depth', str(depth)),))
Zsolt Haraszti50cae7d2017-01-08 22:27:07 -0800411 return res
Zsolt Haraszti80175202016-12-24 00:17:51 -0800412
413 def do_arrive_onus(self, line):
Zsolt Harasztid036b7e2016-12-23 15:36:01 -0800414 """
Zsolt Haraszti85f12852016-12-24 08:30:58 -0800415 Simulate the arrival of ONUs (available only on simulated_olt)
Zsolt Harasztid036b7e2016-12-23 15:36:01 -0800416 """
Zsolt Haraszti80175202016-12-24 00:17:51 -0800417 device_id = line or self.default_device_id
Zsolt Haraszti85f12852016-12-24 08:30:58 -0800418
419 # verify that device is of type simulated_olt
420 device = self.get_device(device_id)
421 assert device.type == 'simulated_olt', (
422 'Cannot use it on this device type (only on simulated_olt type)')
423
Zsolt Harasztid036b7e2016-12-23 15:36:01 -0800424 requests.get('http://{}/devices/{}/detect_onus'.format(
425 self.voltha_sim_rest, device_id
426 ))
427
Zsolt Haraszti80175202016-12-24 00:17:51 -0800428 complete_arrive_onus = VolthaCli.complete_device
429
Zsolt Haraszti85f12852016-12-24 08:30:58 -0800430 def get_logical_ports(self, logical_device_id):
431 """
432 Return the NNI port number and the first usable UNI port of logical
433 device, and the vlan associated with the latter.
434 """
435 stub = voltha_pb2.VolthaLocalServiceStub(self.get_channel())
436 ports = stub.ListLogicalDevicePorts(
437 voltha_pb2.ID(id=logical_device_id)).items
Zsolt Harasztib9a5f752017-02-11 06:07:08 -0800438 nni = None
439 unis = []
Zsolt Haraszti85f12852016-12-24 08:30:58 -0800440 for port in ports:
Zsolt Harasztib9a5f752017-02-11 06:07:08 -0800441 if port.root_port:
442 assert nni is None, "There shall be only one root port"
Zsolt Haraszti85f12852016-12-24 08:30:58 -0800443 nni = port.ofp_port.port_no
Zsolt Harasztib9a5f752017-02-11 06:07:08 -0800444 else:
Zsolt Haraszti85f12852016-12-24 08:30:58 -0800445 uni = port.ofp_port.port_no
446 uni_device = self.get_device(port.device_id)
447 vlan = uni_device.vlan
Zsolt Harasztib9a5f752017-02-11 06:07:08 -0800448 unis.append((uni, vlan))
449
450 assert nni is not None, "No NNI port found"
451 assert unis, "Not a single UNI?"
452
453 return nni, unis
Zsolt Haraszti85f12852016-12-24 08:30:58 -0800454
Zsolt Haraszti80175202016-12-24 00:17:51 -0800455 def do_install_eapol_flow(self, line):
Zsolt Harasztid036b7e2016-12-23 15:36:01 -0800456 """
457 Install an EAPOL flow on the given logical device. If device is not
458 given, it will be applied to logical device of the last pre-provisioned
459 OLT device.
460 """
Zsolt Haraszti80175202016-12-24 00:17:51 -0800461 logical_device_id = line or self.default_logical_device_id
Zsolt Haraszti85f12852016-12-24 08:30:58 -0800462
463 # gather NNI and UNI port IDs
Zsolt Harasztib9a5f752017-02-11 06:07:08 -0800464 nni_port_no, unis = self.get_logical_ports(logical_device_id)
Zsolt Haraszti85f12852016-12-24 08:30:58 -0800465
466 # construct and push flow rule
Zsolt Harasztid036b7e2016-12-23 15:36:01 -0800467 stub = voltha_pb2.VolthaLocalServiceStub(self.get_channel())
Zsolt Harasztib9a5f752017-02-11 06:07:08 -0800468 for uni_port_no, _ in unis:
469 update = FlowTableUpdate(
470 id=logical_device_id,
471 flow_mod=mk_simple_flow_mod(
472 priority=2000,
473 match_fields=[in_port(uni_port_no), eth_type(0x888e)],
474 actions=[
475 # push_vlan(0x8100),
476 # set_field(vlan_vid(4096 + 4000)),
477 output(ofp.OFPP_CONTROLLER)
478 ]
479 )
480 )
481 res = stub.UpdateLogicalDeviceFlowTable(update)
482 self.poutput('success for uni {} ({})'.format(uni_port_no, res))
Zsolt Harasztid036b7e2016-12-23 15:36:01 -0800483
Zsolt Haraszti80175202016-12-24 00:17:51 -0800484 complete_install_eapol_flow = VolthaCli.complete_logical_device
485
Zsolt Haraszti85f12852016-12-24 08:30:58 -0800486 def do_install_all_controller_bound_flows(self, line):
487 """
488 Install all flow rules for controller bound flows, including EAPOL,
489 IGMP and DHCP. If device is not given, it will be applied to logical
490 device of the last pre-provisioned OLT device.
491 """
492 logical_device_id = line or self.default_logical_device_id
493
494 # gather NNI and UNI port IDs
Zsolt Harasztib9a5f752017-02-11 06:07:08 -0800495 nni_port_no, unis = self.get_logical_ports(logical_device_id)
Zsolt Haraszti85f12852016-12-24 08:30:58 -0800496
497 # construct and push flow rules
498 stub = voltha_pb2.VolthaLocalServiceStub(self.get_channel())
499
Zsolt Harasztib9a5f752017-02-11 06:07:08 -0800500 for uni_port_no, _ in unis:
501 stub.UpdateLogicalDeviceFlowTable(FlowTableUpdate(
502 id=logical_device_id,
503 flow_mod=mk_simple_flow_mod(
504 priority=2000,
505 match_fields=[
506 in_port(uni_port_no),
507 eth_type(0x888e)
508 ],
509 actions=[output(ofp.OFPP_CONTROLLER)]
510 )
511 ))
512 stub.UpdateLogicalDeviceFlowTable(FlowTableUpdate(
513 id=logical_device_id,
514 flow_mod=mk_simple_flow_mod(
515 priority=1000,
516 match_fields=[
517 in_port(uni_port_no),
518 eth_type(0x800),
519 ip_proto(2)
520 ],
521 actions=[output(ofp.OFPP_CONTROLLER)]
522 )
523 ))
524 stub.UpdateLogicalDeviceFlowTable(FlowTableUpdate(
525 id=logical_device_id,
526 flow_mod=mk_simple_flow_mod(
527 priority=1000,
528 match_fields=[
529 in_port(uni_port_no),
530 eth_type(0x800),
531 ip_proto(17),
532 udp_dst(67)
533 ],
534 actions=[output(ofp.OFPP_CONTROLLER)]
535 )
536 ))
Zsolt Haraszti85f12852016-12-24 08:30:58 -0800537 self.poutput('success')
538
539 complete_install_all_controller_bound_flows = \
540 VolthaCli.complete_logical_device
541
542 def do_install_all_sample_flows(self, line):
543 """
544 Install all flows that are representative of the virtualized access
545 scenario in a PON network.
546 """
547 logical_device_id = line or self.default_logical_device_id
548
549 # gather NNI and UNI port IDs
Zsolt Harasztib9a5f752017-02-11 06:07:08 -0800550 nni_port_no, unis = self.get_logical_ports(logical_device_id)
Zsolt Haraszti85f12852016-12-24 08:30:58 -0800551
552 # construct and push flow rules
553 stub = voltha_pb2.VolthaLocalServiceStub(self.get_channel())
554
Zsolt Harasztib9a5f752017-02-11 06:07:08 -0800555 for uni_port_no, c_vid in unis:
Zsolt Harasztib9a5f752017-02-11 06:07:08 -0800556 # Controller-bound flows
557 stub.UpdateLogicalDeviceFlowTable(FlowTableUpdate(
558 id=logical_device_id,
559 flow_mod=mk_simple_flow_mod(
560 priority=2000,
561 match_fields=[in_port(uni_port_no), eth_type(0x888e)],
562 actions=[
563 # push_vlan(0x8100),
564 # set_field(vlan_vid(4096 + 4000)),
565 output(ofp.OFPP_CONTROLLER)
566 ]
567 )
568 ))
569 stub.UpdateLogicalDeviceFlowTable(FlowTableUpdate(
570 id=logical_device_id,
571 flow_mod=mk_simple_flow_mod(
572 priority=1000,
573 match_fields=[eth_type(0x800), ip_proto(2)],
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=[eth_type(0x800), ip_proto(17), udp_dst(67)],
582 actions=[output(ofp.OFPP_CONTROLLER)]
583 )
584 ))
585
586 # Unicast flows:
587 # Downstream flow 1
588 stub.UpdateLogicalDeviceFlowTable(FlowTableUpdate(
589 id=logical_device_id,
590 flow_mod=mk_simple_flow_mod(
591 priority=500,
592 match_fields=[
593 in_port(nni_port_no),
594 vlan_vid(4096 + 1000),
595 metadata(c_vid) # here to mimic an ONOS artifact
596 ],
597 actions=[pop_vlan()],
598 next_table_id=1
599 )
600 ))
601 # Downstream flow 2
602 stub.UpdateLogicalDeviceFlowTable(FlowTableUpdate(
603 id=logical_device_id,
604 flow_mod=mk_simple_flow_mod(
605 priority=500,
606 table_id=1,
607 match_fields=[in_port(nni_port_no), vlan_vid(4096 + c_vid)],
608 actions=[set_field(vlan_vid(4096 + 0)), output(uni_port_no)]
609 )
610 ))
611 # Upstream flow 1 for 0-tagged 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(4096 + 0)],
617 actions=[set_field(vlan_vid(4096 + c_vid))],
618 next_table_id=1
619 )
620 ))
621 # Upstream flow 1 for untagged case
622 stub.UpdateLogicalDeviceFlowTable(FlowTableUpdate(
623 id=logical_device_id,
624 flow_mod=mk_simple_flow_mod(
625 priority=500,
626 match_fields=[in_port(uni_port_no), vlan_vid(0)],
627 actions=[push_vlan(0x8100), set_field(vlan_vid(4096 + c_vid))],
628 next_table_id=1
629 )
630 ))
631 # Upstream flow 2 for s-tag
632 stub.UpdateLogicalDeviceFlowTable(FlowTableUpdate(
633 id=logical_device_id,
634 flow_mod=mk_simple_flow_mod(
635 priority=500,
636 table_id=1,
637 match_fields=[in_port(uni_port_no), vlan_vid(4096 + c_vid)],
638 actions=[
639 push_vlan(0x8100),
640 set_field(vlan_vid(4096 + 1000)),
641 output(nni_port_no)
642 ]
643 )
644 ))
Zsolt Haraszti85f12852016-12-24 08:30:58 -0800645
646 # Push a few multicast flows
Zsolt Harasztib9a5f752017-02-11 06:07:08 -0800647 # 1st with one bucket for our uni 0
Zsolt Haraszti85f12852016-12-24 08:30:58 -0800648 stub.UpdateLogicalDeviceFlowGroupTable(FlowGroupTableUpdate(
649 id=logical_device_id,
650 group_mod=mk_multicast_group_mod(
651 group_id=1,
652 buckets=[
Zsolt Harasztib9a5f752017-02-11 06:07:08 -0800653 ofp.ofp_bucket(actions=[
654 pop_vlan(),
655 output(unis[0][0])
656 ])
Zsolt Haraszti85f12852016-12-24 08:30:58 -0800657 ]
658 )
659 ))
660 stub.UpdateLogicalDeviceFlowTable(FlowTableUpdate(
661 id=logical_device_id,
662 flow_mod=mk_simple_flow_mod(
663 priority=1000,
664 match_fields=[
665 in_port(nni_port_no),
666 eth_type(0x800),
667 vlan_vid(4096 + 140),
668 ipv4_dst(0xe4010101)
669 ],
670 actions=[group(1)]
671 )
672 ))
Zsolt Harasztib9a5f752017-02-11 06:07:08 -0800673
674 # 2nd with one bucket for uni 0 and 1
Zsolt Haraszti85f12852016-12-24 08:30:58 -0800675 stub.UpdateLogicalDeviceFlowGroupTable(FlowGroupTableUpdate(
676 id=logical_device_id,
677 group_mod=mk_multicast_group_mod(
678 group_id=2,
679 buckets=[
Nathan Knuth6b7b6ff2017-02-12 03:30:48 -0800680 ofp.ofp_bucket(actions=[pop_vlan(), output(unis[0][0])])
Stephane Barbarie4db8ca22017-04-24 10:30:20 -0400681 # ofp.ofp_bucket(actions=[pop_vlan(), output(unis[1][0])])
Zsolt Haraszti85f12852016-12-24 08:30:58 -0800682 ]
683 )
684 ))
685 stub.UpdateLogicalDeviceFlowTable(FlowTableUpdate(
686 id=logical_device_id,
687 flow_mod=mk_simple_flow_mod(
688 priority=1000,
689 match_fields=[
690 in_port(nni_port_no),
691 eth_type(0x800),
692 vlan_vid(4096 + 140),
693 ipv4_dst(0xe4020202)
694 ],
695 actions=[group(2)]
696 )
697 ))
Zsolt Harasztib9a5f752017-02-11 06:07:08 -0800698
Zsolt Haraszti85f12852016-12-24 08:30:58 -0800699 # 3rd with empty bucket
700 stub.UpdateLogicalDeviceFlowGroupTable(FlowGroupTableUpdate(
701 id=logical_device_id,
702 group_mod=mk_multicast_group_mod(
703 group_id=3,
704 buckets=[]
705 )
706 ))
707 stub.UpdateLogicalDeviceFlowTable(FlowTableUpdate(
708 id=logical_device_id,
709 flow_mod=mk_simple_flow_mod(
710 priority=1000,
711 match_fields=[
712 in_port(nni_port_no),
713 eth_type(0x800),
714 vlan_vid(4096 + 140),
715 ipv4_dst(0xe4030303)
716 ],
717 actions=[group(3)]
718 )
719 ))
720
721 self.poutput('success')
722
723 complete_install_all_sample_flows = VolthaCli.complete_logical_device
724
Nathan Knuth5f4163e2017-01-11 18:21:10 -0600725 def do_install_dhcp_flows(self, line):
726 """
727 Install all dhcp flows that are representative of the virtualized access
728 scenario in a PON network.
729 """
730 logical_device_id = line or self.default_logical_device_id
731
732 # gather NNI and UNI port IDs
Zsolt Harasztib9a5f752017-02-11 06:07:08 -0800733 nni_port_no, unis = self.get_logical_ports(logical_device_id)
Nathan Knuth5f4163e2017-01-11 18:21:10 -0600734
735 # construct and push flow rules
736 stub = voltha_pb2.VolthaLocalServiceStub(self.get_channel())
737
738 # Controller-bound flows
Zsolt Harasztib9a5f752017-02-11 06:07:08 -0800739 for uni_port_no, _ in unis:
740 stub.UpdateLogicalDeviceFlowTable(FlowTableUpdate(
741 id=logical_device_id,
742 flow_mod=mk_simple_flow_mod(
743 priority=1000,
744 match_fields=[
745 in_port(uni_port_no),
746 eth_type(0x800),
747 ip_proto(17),
748 udp_dst(67)
749 ],
750 actions=[output(ofp.OFPP_CONTROLLER)]
751 )
752 ))
Nathan Knuth5f4163e2017-01-11 18:21:10 -0600753
754 self.poutput('success')
755
756 complete_install_dhcp_flows = VolthaCli.complete_logical_device
757
Zsolt Haraszti85f12852016-12-24 08:30:58 -0800758 def do_delete_all_flows(self, line):
759 """
760 Remove all flows and flow groups from given logical device
761 """
762 logical_device_id = line or self.default_logical_device_id
763 stub = voltha_pb2.VolthaLocalServiceStub(self.get_channel())
764 stub.UpdateLogicalDeviceFlowTable(FlowTableUpdate(
765 id=logical_device_id,
766 flow_mod=ofp.ofp_flow_mod(
767 command=ofp.OFPFC_DELETE,
768 table_id=ofp.OFPTT_ALL,
769 cookie_mask=0,
770 out_port=ofp.OFPP_ANY,
771 out_group=ofp.OFPG_ANY
772 )
773 ))
774 stub.UpdateLogicalDeviceFlowGroupTable(FlowGroupTableUpdate(
775 id=logical_device_id,
776 group_mod=ofp.ofp_group_mod(
777 command=ofp.OFPGC_DELETE,
778 group_id=ofp.OFPG_ALL
779 )
780 ))
781 self.poutput('success')
782
783 complete_delete_all_flows = VolthaCli.complete_logical_device
784
Zsolt Haraszti80175202016-12-24 00:17:51 -0800785 def do_send_simulated_upstream_eapol(self, line):
Zsolt Harasztid036b7e2016-12-23 15:36:01 -0800786 """
787 Send an EAPOL upstream from a simulated OLT
788 """
Zsolt Haraszti80175202016-12-24 00:17:51 -0800789 device_id = line or self.default_device_id
Zsolt Harasztid036b7e2016-12-23 15:36:01 -0800790 requests.get('http://{}/devices/{}/test_eapol_in'.format(
791 self.voltha_sim_rest, device_id
792 ))
793
Zsolt Haraszti80175202016-12-24 00:17:51 -0800794 complete_send_simulated_upstream_eapol = VolthaCli.complete_device
795
796 def do_inject_eapol_start(self, line):
Zsolt Harasztid036b7e2016-12-23 15:36:01 -0800797 """
798 Send out an an EAPOL start message into the given Unix interface
799 """
800 pass
Zsolt Harasztia133a452016-12-22 01:26:57 -0800801
802
803if __name__ == '__main__':
Zsolt Haraszti9b485fb2016-12-26 23:11:15 -0800804
805 parser = argparse.ArgumentParser()
806
807 _help = '<hostname>:<port> to consul agent (default: %s)' % defs['consul']
808 parser.add_argument(
809 '-C', '--consul', action='store', default=defs['consul'], help=_help)
810
811 _help = 'Lookup Voltha endpoints based on service entries in Consul'
812 parser.add_argument(
813 '-L', '--lookup', action='store_true', help=_help)
814
815 _help = '<hostname>:<port> of Voltha gRPC service (default={})'.format(
816 defs['voltha_grpc_endpoint'])
817 parser.add_argument('-g', '--grpc-endpoint', action='store',
818 default=defs['voltha_grpc_endpoint'], help=_help)
819
820 _help = '<hostname>:<port> of Voltha simulated adapter backend for ' \
821 'testing (default={})'.format(
822 defs['voltha_sim_rest_endpoint'])
823 parser.add_argument('-s', '--sim-rest-endpoint', action='store',
824 default=defs['voltha_sim_rest_endpoint'], help=_help)
825
826 args = parser.parse_args()
827
828 if args.lookup:
829 host = args.consul.split(':')[0].strip()
830 port = int(args.consul.split(':')[1].strip())
831 consul = Consul(host=host, port=port)
832
833 _, services = consul.catalog.service('voltha-grpc')
834 if not services:
835 print('No voltha-grpc service registered in consul; exiting')
836 sys.exit(1)
837 args.grpc_endpoint = '{}:{}'.format(services[0]['ServiceAddress'],
838 services[0]['ServicePort'])
839
840 _, services = consul.catalog.service('voltha-sim-rest')
841 if not services:
842 print('No voltha-sim-rest service registered in consul; exiting')
843 sys.exit(1)
844 args.sim_rest_endpoint = '{}:{}'.format(services[0]['ServiceAddress'],
845 services[0]['ServicePort'])
846
847 c = VolthaCli(args.grpc_endpoint, args.sim_rest_endpoint)
Zsolt Haraszti80175202016-12-24 00:17:51 -0800848 c.poutput(banner)
Zsolt Harasztia133a452016-12-22 01:26:57 -0800849 c.load_history()
850 c.cmdloop()
851 c.save_history()