blob: 0adee5487cc709b7d84f9f7897f1d817723e6677 [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
327 def do_delete(self, line):
328 """
329 Deleting a device. ID of the device needs to be provided
330 """
331 device_id = line or self.default_device_id
332 self.poutput('deleting {}'.format(device_id))
Khen Nursimuluc60afa12017-03-13 14:33:50 -0400333 try:
334 stub = voltha_pb2.VolthaLocalServiceStub(self.get_channel())
335 stub.DeleteDevice(voltha_pb2.ID(id=device_id))
336 self.poutput('deleted {}'.format(device_id))
337 except Exception, e:
338 self.poutput('Error deleting {}. Error:{}'.format(device_id, e))
Khen Nursimulud068d812017-03-06 11:44:18 -0500339
340 def do_disable(self, line):
341 """
342 Disable a device. ID of the device needs to be provided
343 """
344 device_id = line
345 self.poutput('disabling {}'.format(device_id))
Khen Nursimuluc60afa12017-03-13 14:33:50 -0400346 try:
347 stub = voltha_pb2.VolthaLocalServiceStub(self.get_channel())
348 stub.DisableDevice(voltha_pb2.ID(id=device_id))
Khen Nursimulud068d812017-03-06 11:44:18 -0500349
Khen Nursimuluc60afa12017-03-13 14:33:50 -0400350 # Do device query and verify that the device admin status is
351 # DISABLED and Operational Status is unknown
352 device = stub.GetDevice(voltha_pb2.ID(id=device_id))
353 if device.oper_status == voltha_pb2.OperStatus.UNKNOWN and \
354 device.admin_state == voltha_pb2.AdminState.DISABLED:
355 self.poutput('disabled successfully {}'.format(device_id))
356 else:
357 self.poutput('disabling failed {}. Admin State:{} '
358 'Operation State: {}'.format(device_id,
359 device.admin_state,
360 device.oper_status))
361 except Exception, e:
362 self.poutput('Error disabling {}. Error:{}'.format(device_id, e))
Khen Nursimulud068d812017-03-06 11:44:18 -0500363
Zsolt Haraszti50cae7d2017-01-08 22:27:07 -0800364 def do_test(self, line):
365 """Enter test mode, which makes a bunch on new commands available"""
366 sub = TestCli(self.history, self.get_channel, self.voltha_grpc,
367 self.voltha_sim_rest)
368 sub.cmdloop()
369
Stephane Barbarie4db8ca22017-04-24 10:30:20 -0400370 def do_alarm_filters(self, line):
371 sub = AlarmFiltersCli(self.get_channel)
372 sub.cmdloop()
373
Zsolt Haraszti50cae7d2017-01-08 22:27:07 -0800374
375class TestCli(VolthaCli):
Zsolt Haraszti50cae7d2017-01-08 22:27:07 -0800376 def __init__(self, history, get_channel, voltha_grpc, voltha_sim_rest):
377 VolthaCli.__init__(self, voltha_grpc, voltha_sim_rest)
378 self.history = history
379 self.get_channel = get_channel
380 self.prompt = '(' + self.colorize(self.colorize('test', 'cyan'),
Stephane Barbarie4db8ca22017-04-24 10:30:20 -0400381 'bold') + ') '
Zsolt Haraszti50cae7d2017-01-08 22:27:07 -0800382
383 def get_device(self, device_id, depth=0):
384 stub = voltha_pb2.VolthaLocalServiceStub(self.get_channel())
385 res = stub.GetDevice(voltha_pb2.ID(id=device_id),
Stephane Barbarie4db8ca22017-04-24 10:30:20 -0400386 metadata=(('get-depth', str(depth)),))
Zsolt Haraszti50cae7d2017-01-08 22:27:07 -0800387 return res
Zsolt Haraszti80175202016-12-24 00:17:51 -0800388
389 def do_arrive_onus(self, line):
Zsolt Harasztid036b7e2016-12-23 15:36:01 -0800390 """
Zsolt Haraszti85f12852016-12-24 08:30:58 -0800391 Simulate the arrival of ONUs (available only on simulated_olt)
Zsolt Harasztid036b7e2016-12-23 15:36:01 -0800392 """
Zsolt Haraszti80175202016-12-24 00:17:51 -0800393 device_id = line or self.default_device_id
Zsolt Haraszti85f12852016-12-24 08:30:58 -0800394
395 # verify that device is of type simulated_olt
396 device = self.get_device(device_id)
397 assert device.type == 'simulated_olt', (
398 'Cannot use it on this device type (only on simulated_olt type)')
399
Zsolt Harasztid036b7e2016-12-23 15:36:01 -0800400 requests.get('http://{}/devices/{}/detect_onus'.format(
401 self.voltha_sim_rest, device_id
402 ))
403
Zsolt Haraszti80175202016-12-24 00:17:51 -0800404 complete_arrive_onus = VolthaCli.complete_device
405
Zsolt Haraszti85f12852016-12-24 08:30:58 -0800406 def get_logical_ports(self, logical_device_id):
407 """
408 Return the NNI port number and the first usable UNI port of logical
409 device, and the vlan associated with the latter.
410 """
411 stub = voltha_pb2.VolthaLocalServiceStub(self.get_channel())
412 ports = stub.ListLogicalDevicePorts(
413 voltha_pb2.ID(id=logical_device_id)).items
Zsolt Harasztib9a5f752017-02-11 06:07:08 -0800414 nni = None
415 unis = []
Zsolt Haraszti85f12852016-12-24 08:30:58 -0800416 for port in ports:
Zsolt Harasztib9a5f752017-02-11 06:07:08 -0800417 if port.root_port:
418 assert nni is None, "There shall be only one root port"
Zsolt Haraszti85f12852016-12-24 08:30:58 -0800419 nni = port.ofp_port.port_no
Zsolt Harasztib9a5f752017-02-11 06:07:08 -0800420 else:
Zsolt Haraszti85f12852016-12-24 08:30:58 -0800421 uni = port.ofp_port.port_no
422 uni_device = self.get_device(port.device_id)
423 vlan = uni_device.vlan
Zsolt Harasztib9a5f752017-02-11 06:07:08 -0800424 unis.append((uni, vlan))
425
426 assert nni is not None, "No NNI port found"
427 assert unis, "Not a single UNI?"
428
429 return nni, unis
Zsolt Haraszti85f12852016-12-24 08:30:58 -0800430
Zsolt Haraszti80175202016-12-24 00:17:51 -0800431 def do_install_eapol_flow(self, line):
Zsolt Harasztid036b7e2016-12-23 15:36:01 -0800432 """
433 Install an EAPOL flow on the given logical device. If device is not
434 given, it will be applied to logical device of the last pre-provisioned
435 OLT device.
436 """
Zsolt Haraszti80175202016-12-24 00:17:51 -0800437 logical_device_id = line or self.default_logical_device_id
Zsolt Haraszti85f12852016-12-24 08:30:58 -0800438
439 # gather NNI and UNI port IDs
Zsolt Harasztib9a5f752017-02-11 06:07:08 -0800440 nni_port_no, unis = self.get_logical_ports(logical_device_id)
Zsolt Haraszti85f12852016-12-24 08:30:58 -0800441
442 # construct and push flow rule
Zsolt Harasztid036b7e2016-12-23 15:36:01 -0800443 stub = voltha_pb2.VolthaLocalServiceStub(self.get_channel())
Zsolt Harasztib9a5f752017-02-11 06:07:08 -0800444 for uni_port_no, _ in unis:
445 update = FlowTableUpdate(
446 id=logical_device_id,
447 flow_mod=mk_simple_flow_mod(
448 priority=2000,
449 match_fields=[in_port(uni_port_no), eth_type(0x888e)],
450 actions=[
451 # push_vlan(0x8100),
452 # set_field(vlan_vid(4096 + 4000)),
453 output(ofp.OFPP_CONTROLLER)
454 ]
455 )
456 )
457 res = stub.UpdateLogicalDeviceFlowTable(update)
458 self.poutput('success for uni {} ({})'.format(uni_port_no, res))
Zsolt Harasztid036b7e2016-12-23 15:36:01 -0800459
Zsolt Haraszti80175202016-12-24 00:17:51 -0800460 complete_install_eapol_flow = VolthaCli.complete_logical_device
461
Zsolt Haraszti85f12852016-12-24 08:30:58 -0800462 def do_install_all_controller_bound_flows(self, line):
463 """
464 Install all flow rules for controller bound flows, including EAPOL,
465 IGMP and DHCP. If device is not given, it will be applied to logical
466 device of the last pre-provisioned OLT device.
467 """
468 logical_device_id = line or self.default_logical_device_id
469
470 # gather NNI and UNI port IDs
Zsolt Harasztib9a5f752017-02-11 06:07:08 -0800471 nni_port_no, unis = self.get_logical_ports(logical_device_id)
Zsolt Haraszti85f12852016-12-24 08:30:58 -0800472
473 # construct and push flow rules
474 stub = voltha_pb2.VolthaLocalServiceStub(self.get_channel())
475
Zsolt Harasztib9a5f752017-02-11 06:07:08 -0800476 for uni_port_no, _ in unis:
477 stub.UpdateLogicalDeviceFlowTable(FlowTableUpdate(
478 id=logical_device_id,
479 flow_mod=mk_simple_flow_mod(
480 priority=2000,
481 match_fields=[
482 in_port(uni_port_no),
483 eth_type(0x888e)
484 ],
485 actions=[output(ofp.OFPP_CONTROLLER)]
486 )
487 ))
488 stub.UpdateLogicalDeviceFlowTable(FlowTableUpdate(
489 id=logical_device_id,
490 flow_mod=mk_simple_flow_mod(
491 priority=1000,
492 match_fields=[
493 in_port(uni_port_no),
494 eth_type(0x800),
495 ip_proto(2)
496 ],
497 actions=[output(ofp.OFPP_CONTROLLER)]
498 )
499 ))
500 stub.UpdateLogicalDeviceFlowTable(FlowTableUpdate(
501 id=logical_device_id,
502 flow_mod=mk_simple_flow_mod(
503 priority=1000,
504 match_fields=[
505 in_port(uni_port_no),
506 eth_type(0x800),
507 ip_proto(17),
508 udp_dst(67)
509 ],
510 actions=[output(ofp.OFPP_CONTROLLER)]
511 )
512 ))
Zsolt Haraszti85f12852016-12-24 08:30:58 -0800513 self.poutput('success')
514
515 complete_install_all_controller_bound_flows = \
516 VolthaCli.complete_logical_device
517
518 def do_install_all_sample_flows(self, line):
519 """
520 Install all flows that are representative of the virtualized access
521 scenario in a PON network.
522 """
523 logical_device_id = line or self.default_logical_device_id
524
525 # gather NNI and UNI port IDs
Zsolt Harasztib9a5f752017-02-11 06:07:08 -0800526 nni_port_no, unis = self.get_logical_ports(logical_device_id)
Zsolt Haraszti85f12852016-12-24 08:30:58 -0800527
528 # construct and push flow rules
529 stub = voltha_pb2.VolthaLocalServiceStub(self.get_channel())
530
Zsolt Harasztib9a5f752017-02-11 06:07:08 -0800531 for uni_port_no, c_vid in unis:
Zsolt Harasztib9a5f752017-02-11 06:07:08 -0800532 # Controller-bound flows
533 stub.UpdateLogicalDeviceFlowTable(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 stub.UpdateLogicalDeviceFlowTable(FlowTableUpdate(
546 id=logical_device_id,
547 flow_mod=mk_simple_flow_mod(
548 priority=1000,
549 match_fields=[eth_type(0x800), ip_proto(2)],
550 actions=[output(ofp.OFPP_CONTROLLER)]
551 )
552 ))
553 stub.UpdateLogicalDeviceFlowTable(FlowTableUpdate(
554 id=logical_device_id,
555 flow_mod=mk_simple_flow_mod(
556 priority=1000,
557 match_fields=[eth_type(0x800), ip_proto(17), udp_dst(67)],
558 actions=[output(ofp.OFPP_CONTROLLER)]
559 )
560 ))
561
562 # Unicast flows:
563 # Downstream flow 1
564 stub.UpdateLogicalDeviceFlowTable(FlowTableUpdate(
565 id=logical_device_id,
566 flow_mod=mk_simple_flow_mod(
567 priority=500,
568 match_fields=[
569 in_port(nni_port_no),
570 vlan_vid(4096 + 1000),
571 metadata(c_vid) # here to mimic an ONOS artifact
572 ],
573 actions=[pop_vlan()],
574 next_table_id=1
575 )
576 ))
577 # Downstream flow 2
578 stub.UpdateLogicalDeviceFlowTable(FlowTableUpdate(
579 id=logical_device_id,
580 flow_mod=mk_simple_flow_mod(
581 priority=500,
582 table_id=1,
583 match_fields=[in_port(nni_port_no), vlan_vid(4096 + c_vid)],
584 actions=[set_field(vlan_vid(4096 + 0)), output(uni_port_no)]
585 )
586 ))
587 # Upstream flow 1 for 0-tagged case
588 stub.UpdateLogicalDeviceFlowTable(FlowTableUpdate(
589 id=logical_device_id,
590 flow_mod=mk_simple_flow_mod(
591 priority=500,
592 match_fields=[in_port(uni_port_no), vlan_vid(4096 + 0)],
593 actions=[set_field(vlan_vid(4096 + c_vid))],
594 next_table_id=1
595 )
596 ))
597 # Upstream flow 1 for untagged case
598 stub.UpdateLogicalDeviceFlowTable(FlowTableUpdate(
599 id=logical_device_id,
600 flow_mod=mk_simple_flow_mod(
601 priority=500,
602 match_fields=[in_port(uni_port_no), vlan_vid(0)],
603 actions=[push_vlan(0x8100), set_field(vlan_vid(4096 + c_vid))],
604 next_table_id=1
605 )
606 ))
607 # Upstream flow 2 for s-tag
608 stub.UpdateLogicalDeviceFlowTable(FlowTableUpdate(
609 id=logical_device_id,
610 flow_mod=mk_simple_flow_mod(
611 priority=500,
612 table_id=1,
613 match_fields=[in_port(uni_port_no), vlan_vid(4096 + c_vid)],
614 actions=[
615 push_vlan(0x8100),
616 set_field(vlan_vid(4096 + 1000)),
617 output(nni_port_no)
618 ]
619 )
620 ))
Zsolt Haraszti85f12852016-12-24 08:30:58 -0800621
622 # Push a few multicast flows
Zsolt Harasztib9a5f752017-02-11 06:07:08 -0800623 # 1st with one bucket for our uni 0
Zsolt Haraszti85f12852016-12-24 08:30:58 -0800624 stub.UpdateLogicalDeviceFlowGroupTable(FlowGroupTableUpdate(
625 id=logical_device_id,
626 group_mod=mk_multicast_group_mod(
627 group_id=1,
628 buckets=[
Zsolt Harasztib9a5f752017-02-11 06:07:08 -0800629 ofp.ofp_bucket(actions=[
630 pop_vlan(),
631 output(unis[0][0])
632 ])
Zsolt Haraszti85f12852016-12-24 08:30:58 -0800633 ]
634 )
635 ))
636 stub.UpdateLogicalDeviceFlowTable(FlowTableUpdate(
637 id=logical_device_id,
638 flow_mod=mk_simple_flow_mod(
639 priority=1000,
640 match_fields=[
641 in_port(nni_port_no),
642 eth_type(0x800),
643 vlan_vid(4096 + 140),
644 ipv4_dst(0xe4010101)
645 ],
646 actions=[group(1)]
647 )
648 ))
Zsolt Harasztib9a5f752017-02-11 06:07:08 -0800649
650 # 2nd with one bucket for uni 0 and 1
Zsolt Haraszti85f12852016-12-24 08:30:58 -0800651 stub.UpdateLogicalDeviceFlowGroupTable(FlowGroupTableUpdate(
652 id=logical_device_id,
653 group_mod=mk_multicast_group_mod(
654 group_id=2,
655 buckets=[
Nathan Knuth6b7b6ff2017-02-12 03:30:48 -0800656 ofp.ofp_bucket(actions=[pop_vlan(), output(unis[0][0])])
Stephane Barbarie4db8ca22017-04-24 10:30:20 -0400657 # ofp.ofp_bucket(actions=[pop_vlan(), output(unis[1][0])])
Zsolt Haraszti85f12852016-12-24 08:30:58 -0800658 ]
659 )
660 ))
661 stub.UpdateLogicalDeviceFlowTable(FlowTableUpdate(
662 id=logical_device_id,
663 flow_mod=mk_simple_flow_mod(
664 priority=1000,
665 match_fields=[
666 in_port(nni_port_no),
667 eth_type(0x800),
668 vlan_vid(4096 + 140),
669 ipv4_dst(0xe4020202)
670 ],
671 actions=[group(2)]
672 )
673 ))
Zsolt Harasztib9a5f752017-02-11 06:07:08 -0800674
Zsolt Haraszti85f12852016-12-24 08:30:58 -0800675 # 3rd with empty bucket
676 stub.UpdateLogicalDeviceFlowGroupTable(FlowGroupTableUpdate(
677 id=logical_device_id,
678 group_mod=mk_multicast_group_mod(
679 group_id=3,
680 buckets=[]
681 )
682 ))
683 stub.UpdateLogicalDeviceFlowTable(FlowTableUpdate(
684 id=logical_device_id,
685 flow_mod=mk_simple_flow_mod(
686 priority=1000,
687 match_fields=[
688 in_port(nni_port_no),
689 eth_type(0x800),
690 vlan_vid(4096 + 140),
691 ipv4_dst(0xe4030303)
692 ],
693 actions=[group(3)]
694 )
695 ))
696
697 self.poutput('success')
698
699 complete_install_all_sample_flows = VolthaCli.complete_logical_device
700
Nathan Knuth5f4163e2017-01-11 18:21:10 -0600701 def do_install_dhcp_flows(self, line):
702 """
703 Install all dhcp flows that are representative of the virtualized access
704 scenario in a PON network.
705 """
706 logical_device_id = line or self.default_logical_device_id
707
708 # gather NNI and UNI port IDs
Zsolt Harasztib9a5f752017-02-11 06:07:08 -0800709 nni_port_no, unis = self.get_logical_ports(logical_device_id)
Nathan Knuth5f4163e2017-01-11 18:21:10 -0600710
711 # construct and push flow rules
712 stub = voltha_pb2.VolthaLocalServiceStub(self.get_channel())
713
714 # Controller-bound flows
Zsolt Harasztib9a5f752017-02-11 06:07:08 -0800715 for uni_port_no, _ in unis:
716 stub.UpdateLogicalDeviceFlowTable(FlowTableUpdate(
717 id=logical_device_id,
718 flow_mod=mk_simple_flow_mod(
719 priority=1000,
720 match_fields=[
721 in_port(uni_port_no),
722 eth_type(0x800),
723 ip_proto(17),
724 udp_dst(67)
725 ],
726 actions=[output(ofp.OFPP_CONTROLLER)]
727 )
728 ))
Nathan Knuth5f4163e2017-01-11 18:21:10 -0600729
730 self.poutput('success')
731
732 complete_install_dhcp_flows = VolthaCli.complete_logical_device
733
Zsolt Haraszti85f12852016-12-24 08:30:58 -0800734 def do_delete_all_flows(self, line):
735 """
736 Remove all flows and flow groups from given logical device
737 """
738 logical_device_id = line or self.default_logical_device_id
739 stub = voltha_pb2.VolthaLocalServiceStub(self.get_channel())
740 stub.UpdateLogicalDeviceFlowTable(FlowTableUpdate(
741 id=logical_device_id,
742 flow_mod=ofp.ofp_flow_mod(
743 command=ofp.OFPFC_DELETE,
744 table_id=ofp.OFPTT_ALL,
745 cookie_mask=0,
746 out_port=ofp.OFPP_ANY,
747 out_group=ofp.OFPG_ANY
748 )
749 ))
750 stub.UpdateLogicalDeviceFlowGroupTable(FlowGroupTableUpdate(
751 id=logical_device_id,
752 group_mod=ofp.ofp_group_mod(
753 command=ofp.OFPGC_DELETE,
754 group_id=ofp.OFPG_ALL
755 )
756 ))
757 self.poutput('success')
758
759 complete_delete_all_flows = VolthaCli.complete_logical_device
760
Zsolt Haraszti80175202016-12-24 00:17:51 -0800761 def do_send_simulated_upstream_eapol(self, line):
Zsolt Harasztid036b7e2016-12-23 15:36:01 -0800762 """
763 Send an EAPOL upstream from a simulated OLT
764 """
Zsolt Haraszti80175202016-12-24 00:17:51 -0800765 device_id = line or self.default_device_id
Zsolt Harasztid036b7e2016-12-23 15:36:01 -0800766 requests.get('http://{}/devices/{}/test_eapol_in'.format(
767 self.voltha_sim_rest, device_id
768 ))
769
Zsolt Haraszti80175202016-12-24 00:17:51 -0800770 complete_send_simulated_upstream_eapol = VolthaCli.complete_device
771
772 def do_inject_eapol_start(self, line):
Zsolt Harasztid036b7e2016-12-23 15:36:01 -0800773 """
774 Send out an an EAPOL start message into the given Unix interface
775 """
776 pass
Zsolt Harasztia133a452016-12-22 01:26:57 -0800777
778
779if __name__ == '__main__':
Zsolt Haraszti9b485fb2016-12-26 23:11:15 -0800780
781 parser = argparse.ArgumentParser()
782
783 _help = '<hostname>:<port> to consul agent (default: %s)' % defs['consul']
784 parser.add_argument(
785 '-C', '--consul', action='store', default=defs['consul'], help=_help)
786
787 _help = 'Lookup Voltha endpoints based on service entries in Consul'
788 parser.add_argument(
789 '-L', '--lookup', action='store_true', help=_help)
790
791 _help = '<hostname>:<port> of Voltha gRPC service (default={})'.format(
792 defs['voltha_grpc_endpoint'])
793 parser.add_argument('-g', '--grpc-endpoint', action='store',
794 default=defs['voltha_grpc_endpoint'], help=_help)
795
796 _help = '<hostname>:<port> of Voltha simulated adapter backend for ' \
797 'testing (default={})'.format(
798 defs['voltha_sim_rest_endpoint'])
799 parser.add_argument('-s', '--sim-rest-endpoint', action='store',
800 default=defs['voltha_sim_rest_endpoint'], help=_help)
801
802 args = parser.parse_args()
803
804 if args.lookup:
805 host = args.consul.split(':')[0].strip()
806 port = int(args.consul.split(':')[1].strip())
807 consul = Consul(host=host, port=port)
808
809 _, services = consul.catalog.service('voltha-grpc')
810 if not services:
811 print('No voltha-grpc service registered in consul; exiting')
812 sys.exit(1)
813 args.grpc_endpoint = '{}:{}'.format(services[0]['ServiceAddress'],
814 services[0]['ServicePort'])
815
816 _, services = consul.catalog.service('voltha-sim-rest')
817 if not services:
818 print('No voltha-sim-rest service registered in consul; exiting')
819 sys.exit(1)
820 args.sim_rest_endpoint = '{}:{}'.format(services[0]['ServiceAddress'],
821 services[0]['ServicePort'])
822
823 c = VolthaCli(args.grpc_endpoint, args.sim_rest_endpoint)
Zsolt Haraszti80175202016-12-24 00:17:51 -0800824 c.poutput(banner)
Zsolt Harasztia133a452016-12-22 01:26:57 -0800825 c.load_history()
826 c.cmdloop()
827 c.save_history()