blob: b3d012daeafda7c2f1a793fa5ba54919726ed48b [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',
177 'software_version',
178 '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')
276 device = voltha_pb2.Device(**kw)
277 device = stub.CreateDevice(device)
Zsolt Haraszti80175202016-12-24 00:17:51 -0800278 self.poutput('success (device id = {})'.format(device.id))
Zsolt Harasztid036b7e2016-12-23 15:36:01 -0800279 self.default_device_id = device.id
Zsolt Harasztia133a452016-12-22 01:26:57 -0800280
Khen Nursimulud068d812017-03-06 11:44:18 -0500281 def do_enable(self, line):
Zsolt Harasztid036b7e2016-12-23 15:36:01 -0800282 """
Khen Nursimulud068d812017-03-06 11:44:18 -0500283 Enable a device. If the <id> is not provided, it will be on the last
284 pre-provisioned device.
Zsolt Harasztid036b7e2016-12-23 15:36:01 -0800285 """
Zsolt Haraszti80175202016-12-24 00:17:51 -0800286 device_id = line or self.default_device_id
Khen Nursimulu29e75502017-03-07 17:26:50 -0500287 self.poutput('enabling {}'.format(device_id))
Khen Nursimuluc60afa12017-03-13 14:33:50 -0400288 try:
289 stub = voltha_pb2.VolthaLocalServiceStub(self.get_channel())
290 stub.EnableDevice(voltha_pb2.ID(id=device_id))
Zsolt Harasztia133a452016-12-22 01:26:57 -0800291
Khen Nursimuluc60afa12017-03-13 14:33:50 -0400292 while True:
293 device = stub.GetDevice(voltha_pb2.ID(id=device_id))
294 # If this is an OLT then acquire logical device id
295 if device.oper_status == voltha_pb2.OperStatus.ACTIVE:
296 if device.type.endswith('_olt'):
297 assert device.parent_id
298 self.default_logical_device_id = device.parent_id
299 self.poutput('success (logical device id = {})'.format(
300 self.default_logical_device_id))
301 else:
302 self.poutput('success (device id = {})'.format(device.id))
303 break
304 self.poutput('waiting for device to be enabled...')
305 sleep(.5)
306 except Exception, e:
307 self.poutput('Error enabling {}. Error:{}'.format(device_id, e))
Zsolt Harasztid036b7e2016-12-23 15:36:01 -0800308
Zsolt Haraszti50cae7d2017-01-08 22:27:07 -0800309 complete_activate_olt = complete_device
310
Khen Nursimulud068d812017-03-06 11:44:18 -0500311 def do_reboot(self, line):
312 """
313 Rebooting a device. ID of the device needs to be provided
314 """
315 device_id = line or self.default_device_id
316 self.poutput('rebooting {}'.format(device_id))
Khen Nursimuluc60afa12017-03-13 14:33:50 -0400317 try:
318 stub = voltha_pb2.VolthaLocalServiceStub(self.get_channel())
319 stub.RebootDevice(voltha_pb2.ID(id=device_id))
320 self.poutput('rebooted {}'.format(device_id))
321 except Exception, e:
322 self.poutput('Error rebooting {}. Error:{}'.format(device_id, e))
Khen Nursimulud068d812017-03-06 11:44:18 -0500323
324 def do_delete(self, line):
325 """
326 Deleting a device. ID of the device needs to be provided
327 """
328 device_id = line or self.default_device_id
329 self.poutput('deleting {}'.format(device_id))
Khen Nursimuluc60afa12017-03-13 14:33:50 -0400330 try:
331 stub = voltha_pb2.VolthaLocalServiceStub(self.get_channel())
332 stub.DeleteDevice(voltha_pb2.ID(id=device_id))
333 self.poutput('deleted {}'.format(device_id))
334 except Exception, e:
335 self.poutput('Error deleting {}. Error:{}'.format(device_id, e))
Khen Nursimulud068d812017-03-06 11:44:18 -0500336
337 def do_disable(self, line):
338 """
339 Disable a device. ID of the device needs to be provided
340 """
341 device_id = line
342 self.poutput('disabling {}'.format(device_id))
Khen Nursimuluc60afa12017-03-13 14:33:50 -0400343 try:
344 stub = voltha_pb2.VolthaLocalServiceStub(self.get_channel())
345 stub.DisableDevice(voltha_pb2.ID(id=device_id))
Khen Nursimulud068d812017-03-06 11:44:18 -0500346
Khen Nursimuluc60afa12017-03-13 14:33:50 -0400347 # Do device query and verify that the device admin status is
348 # DISABLED and Operational Status is unknown
349 device = stub.GetDevice(voltha_pb2.ID(id=device_id))
350 if device.oper_status == voltha_pb2.OperStatus.UNKNOWN and \
351 device.admin_state == voltha_pb2.AdminState.DISABLED:
352 self.poutput('disabled successfully {}'.format(device_id))
353 else:
354 self.poutput('disabling failed {}. Admin State:{} '
355 'Operation State: {}'.format(device_id,
356 device.admin_state,
357 device.oper_status))
358 except Exception, e:
359 self.poutput('Error disabling {}. Error:{}'.format(device_id, e))
Khen Nursimulud068d812017-03-06 11:44:18 -0500360
Zsolt Haraszti50cae7d2017-01-08 22:27:07 -0800361 def do_test(self, line):
362 """Enter test mode, which makes a bunch on new commands available"""
363 sub = TestCli(self.history, self.get_channel, self.voltha_grpc,
364 self.voltha_sim_rest)
365 sub.cmdloop()
366
Stephane Barbarie4db8ca22017-04-24 10:30:20 -0400367 def do_alarm_filters(self, line):
368 sub = AlarmFiltersCli(self.get_channel)
369 sub.cmdloop()
370
Zsolt Haraszti50cae7d2017-01-08 22:27:07 -0800371
372class TestCli(VolthaCli):
Zsolt Haraszti50cae7d2017-01-08 22:27:07 -0800373 def __init__(self, history, get_channel, voltha_grpc, voltha_sim_rest):
374 VolthaCli.__init__(self, voltha_grpc, voltha_sim_rest)
375 self.history = history
376 self.get_channel = get_channel
377 self.prompt = '(' + self.colorize(self.colorize('test', 'cyan'),
Stephane Barbarie4db8ca22017-04-24 10:30:20 -0400378 'bold') + ') '
Zsolt Haraszti50cae7d2017-01-08 22:27:07 -0800379
380 def get_device(self, device_id, depth=0):
381 stub = voltha_pb2.VolthaLocalServiceStub(self.get_channel())
382 res = stub.GetDevice(voltha_pb2.ID(id=device_id),
Stephane Barbarie4db8ca22017-04-24 10:30:20 -0400383 metadata=(('get-depth', str(depth)),))
Zsolt Haraszti50cae7d2017-01-08 22:27:07 -0800384 return res
Zsolt Haraszti80175202016-12-24 00:17:51 -0800385
386 def do_arrive_onus(self, line):
Zsolt Harasztid036b7e2016-12-23 15:36:01 -0800387 """
Zsolt Haraszti85f12852016-12-24 08:30:58 -0800388 Simulate the arrival of ONUs (available only on simulated_olt)
Zsolt Harasztid036b7e2016-12-23 15:36:01 -0800389 """
Zsolt Haraszti80175202016-12-24 00:17:51 -0800390 device_id = line or self.default_device_id
Zsolt Haraszti85f12852016-12-24 08:30:58 -0800391
392 # verify that device is of type simulated_olt
393 device = self.get_device(device_id)
394 assert device.type == 'simulated_olt', (
395 'Cannot use it on this device type (only on simulated_olt type)')
396
Zsolt Harasztid036b7e2016-12-23 15:36:01 -0800397 requests.get('http://{}/devices/{}/detect_onus'.format(
398 self.voltha_sim_rest, device_id
399 ))
400
Zsolt Haraszti80175202016-12-24 00:17:51 -0800401 complete_arrive_onus = VolthaCli.complete_device
402
Zsolt Haraszti85f12852016-12-24 08:30:58 -0800403 def get_logical_ports(self, logical_device_id):
404 """
405 Return the NNI port number and the first usable UNI port of logical
406 device, and the vlan associated with the latter.
407 """
408 stub = voltha_pb2.VolthaLocalServiceStub(self.get_channel())
409 ports = stub.ListLogicalDevicePorts(
410 voltha_pb2.ID(id=logical_device_id)).items
Zsolt Harasztib9a5f752017-02-11 06:07:08 -0800411 nni = None
412 unis = []
Zsolt Haraszti85f12852016-12-24 08:30:58 -0800413 for port in ports:
Zsolt Harasztib9a5f752017-02-11 06:07:08 -0800414 if port.root_port:
415 assert nni is None, "There shall be only one root port"
Zsolt Haraszti85f12852016-12-24 08:30:58 -0800416 nni = port.ofp_port.port_no
Zsolt Harasztib9a5f752017-02-11 06:07:08 -0800417 else:
Zsolt Haraszti85f12852016-12-24 08:30:58 -0800418 uni = port.ofp_port.port_no
419 uni_device = self.get_device(port.device_id)
420 vlan = uni_device.vlan
Zsolt Harasztib9a5f752017-02-11 06:07:08 -0800421 unis.append((uni, vlan))
422
423 assert nni is not None, "No NNI port found"
424 assert unis, "Not a single UNI?"
425
426 return nni, unis
Zsolt Haraszti85f12852016-12-24 08:30:58 -0800427
Zsolt Haraszti80175202016-12-24 00:17:51 -0800428 def do_install_eapol_flow(self, line):
Zsolt Harasztid036b7e2016-12-23 15:36:01 -0800429 """
430 Install an EAPOL flow on the given logical device. If device is not
431 given, it will be applied to logical device of the last pre-provisioned
432 OLT device.
433 """
Zsolt Haraszti80175202016-12-24 00:17:51 -0800434 logical_device_id = line or self.default_logical_device_id
Zsolt Haraszti85f12852016-12-24 08:30:58 -0800435
436 # gather NNI and UNI port IDs
Zsolt Harasztib9a5f752017-02-11 06:07:08 -0800437 nni_port_no, unis = self.get_logical_ports(logical_device_id)
Zsolt Haraszti85f12852016-12-24 08:30:58 -0800438
439 # construct and push flow rule
Zsolt Harasztid036b7e2016-12-23 15:36:01 -0800440 stub = voltha_pb2.VolthaLocalServiceStub(self.get_channel())
Zsolt Harasztib9a5f752017-02-11 06:07:08 -0800441 for uni_port_no, _ in unis:
442 update = FlowTableUpdate(
443 id=logical_device_id,
444 flow_mod=mk_simple_flow_mod(
445 priority=2000,
446 match_fields=[in_port(uni_port_no), eth_type(0x888e)],
447 actions=[
448 # push_vlan(0x8100),
449 # set_field(vlan_vid(4096 + 4000)),
450 output(ofp.OFPP_CONTROLLER)
451 ]
452 )
453 )
454 res = stub.UpdateLogicalDeviceFlowTable(update)
455 self.poutput('success for uni {} ({})'.format(uni_port_no, res))
Zsolt Harasztid036b7e2016-12-23 15:36:01 -0800456
Zsolt Haraszti80175202016-12-24 00:17:51 -0800457 complete_install_eapol_flow = VolthaCli.complete_logical_device
458
Zsolt Haraszti85f12852016-12-24 08:30:58 -0800459 def do_install_all_controller_bound_flows(self, line):
460 """
461 Install all flow rules for controller bound flows, including EAPOL,
462 IGMP and DHCP. If device is not given, it will be applied to logical
463 device of the last pre-provisioned OLT device.
464 """
465 logical_device_id = line or self.default_logical_device_id
466
467 # gather NNI and UNI port IDs
Zsolt Harasztib9a5f752017-02-11 06:07:08 -0800468 nni_port_no, unis = self.get_logical_ports(logical_device_id)
Zsolt Haraszti85f12852016-12-24 08:30:58 -0800469
470 # construct and push flow rules
471 stub = voltha_pb2.VolthaLocalServiceStub(self.get_channel())
472
Zsolt Harasztib9a5f752017-02-11 06:07:08 -0800473 for uni_port_no, _ in unis:
474 stub.UpdateLogicalDeviceFlowTable(FlowTableUpdate(
475 id=logical_device_id,
476 flow_mod=mk_simple_flow_mod(
477 priority=2000,
478 match_fields=[
479 in_port(uni_port_no),
480 eth_type(0x888e)
481 ],
482 actions=[output(ofp.OFPP_CONTROLLER)]
483 )
484 ))
485 stub.UpdateLogicalDeviceFlowTable(FlowTableUpdate(
486 id=logical_device_id,
487 flow_mod=mk_simple_flow_mod(
488 priority=1000,
489 match_fields=[
490 in_port(uni_port_no),
491 eth_type(0x800),
492 ip_proto(2)
493 ],
494 actions=[output(ofp.OFPP_CONTROLLER)]
495 )
496 ))
497 stub.UpdateLogicalDeviceFlowTable(FlowTableUpdate(
498 id=logical_device_id,
499 flow_mod=mk_simple_flow_mod(
500 priority=1000,
501 match_fields=[
502 in_port(uni_port_no),
503 eth_type(0x800),
504 ip_proto(17),
505 udp_dst(67)
506 ],
507 actions=[output(ofp.OFPP_CONTROLLER)]
508 )
509 ))
Zsolt Haraszti85f12852016-12-24 08:30:58 -0800510 self.poutput('success')
511
512 complete_install_all_controller_bound_flows = \
513 VolthaCli.complete_logical_device
514
515 def do_install_all_sample_flows(self, line):
516 """
517 Install all flows that are representative of the virtualized access
518 scenario in a PON network.
519 """
520 logical_device_id = line or self.default_logical_device_id
521
522 # gather NNI and UNI port IDs
Zsolt Harasztib9a5f752017-02-11 06:07:08 -0800523 nni_port_no, unis = self.get_logical_ports(logical_device_id)
Zsolt Haraszti85f12852016-12-24 08:30:58 -0800524
525 # construct and push flow rules
526 stub = voltha_pb2.VolthaLocalServiceStub(self.get_channel())
527
Zsolt Harasztib9a5f752017-02-11 06:07:08 -0800528 for uni_port_no, c_vid in unis:
Zsolt Harasztib9a5f752017-02-11 06:07:08 -0800529 # Controller-bound flows
530 stub.UpdateLogicalDeviceFlowTable(FlowTableUpdate(
531 id=logical_device_id,
532 flow_mod=mk_simple_flow_mod(
533 priority=2000,
534 match_fields=[in_port(uni_port_no), eth_type(0x888e)],
535 actions=[
536 # push_vlan(0x8100),
537 # set_field(vlan_vid(4096 + 4000)),
538 output(ofp.OFPP_CONTROLLER)
539 ]
540 )
541 ))
542 stub.UpdateLogicalDeviceFlowTable(FlowTableUpdate(
543 id=logical_device_id,
544 flow_mod=mk_simple_flow_mod(
545 priority=1000,
546 match_fields=[eth_type(0x800), ip_proto(2)],
547 actions=[output(ofp.OFPP_CONTROLLER)]
548 )
549 ))
550 stub.UpdateLogicalDeviceFlowTable(FlowTableUpdate(
551 id=logical_device_id,
552 flow_mod=mk_simple_flow_mod(
553 priority=1000,
554 match_fields=[eth_type(0x800), ip_proto(17), udp_dst(67)],
555 actions=[output(ofp.OFPP_CONTROLLER)]
556 )
557 ))
558
559 # Unicast flows:
560 # Downstream flow 1
561 stub.UpdateLogicalDeviceFlowTable(FlowTableUpdate(
562 id=logical_device_id,
563 flow_mod=mk_simple_flow_mod(
564 priority=500,
565 match_fields=[
566 in_port(nni_port_no),
567 vlan_vid(4096 + 1000),
568 metadata(c_vid) # here to mimic an ONOS artifact
569 ],
570 actions=[pop_vlan()],
571 next_table_id=1
572 )
573 ))
574 # Downstream flow 2
575 stub.UpdateLogicalDeviceFlowTable(FlowTableUpdate(
576 id=logical_device_id,
577 flow_mod=mk_simple_flow_mod(
578 priority=500,
579 table_id=1,
580 match_fields=[in_port(nni_port_no), vlan_vid(4096 + c_vid)],
581 actions=[set_field(vlan_vid(4096 + 0)), output(uni_port_no)]
582 )
583 ))
584 # Upstream flow 1 for 0-tagged case
585 stub.UpdateLogicalDeviceFlowTable(FlowTableUpdate(
586 id=logical_device_id,
587 flow_mod=mk_simple_flow_mod(
588 priority=500,
589 match_fields=[in_port(uni_port_no), vlan_vid(4096 + 0)],
590 actions=[set_field(vlan_vid(4096 + c_vid))],
591 next_table_id=1
592 )
593 ))
594 # Upstream flow 1 for untagged case
595 stub.UpdateLogicalDeviceFlowTable(FlowTableUpdate(
596 id=logical_device_id,
597 flow_mod=mk_simple_flow_mod(
598 priority=500,
599 match_fields=[in_port(uni_port_no), vlan_vid(0)],
600 actions=[push_vlan(0x8100), set_field(vlan_vid(4096 + c_vid))],
601 next_table_id=1
602 )
603 ))
604 # Upstream flow 2 for s-tag
605 stub.UpdateLogicalDeviceFlowTable(FlowTableUpdate(
606 id=logical_device_id,
607 flow_mod=mk_simple_flow_mod(
608 priority=500,
609 table_id=1,
610 match_fields=[in_port(uni_port_no), vlan_vid(4096 + c_vid)],
611 actions=[
612 push_vlan(0x8100),
613 set_field(vlan_vid(4096 + 1000)),
614 output(nni_port_no)
615 ]
616 )
617 ))
Zsolt Haraszti85f12852016-12-24 08:30:58 -0800618
619 # Push a few multicast flows
Zsolt Harasztib9a5f752017-02-11 06:07:08 -0800620 # 1st with one bucket for our uni 0
Zsolt Haraszti85f12852016-12-24 08:30:58 -0800621 stub.UpdateLogicalDeviceFlowGroupTable(FlowGroupTableUpdate(
622 id=logical_device_id,
623 group_mod=mk_multicast_group_mod(
624 group_id=1,
625 buckets=[
Zsolt Harasztib9a5f752017-02-11 06:07:08 -0800626 ofp.ofp_bucket(actions=[
627 pop_vlan(),
628 output(unis[0][0])
629 ])
Zsolt Haraszti85f12852016-12-24 08:30:58 -0800630 ]
631 )
632 ))
633 stub.UpdateLogicalDeviceFlowTable(FlowTableUpdate(
634 id=logical_device_id,
635 flow_mod=mk_simple_flow_mod(
636 priority=1000,
637 match_fields=[
638 in_port(nni_port_no),
639 eth_type(0x800),
640 vlan_vid(4096 + 140),
641 ipv4_dst(0xe4010101)
642 ],
643 actions=[group(1)]
644 )
645 ))
Zsolt Harasztib9a5f752017-02-11 06:07:08 -0800646
647 # 2nd with one bucket for uni 0 and 1
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=2,
652 buckets=[
Nathan Knuth6b7b6ff2017-02-12 03:30:48 -0800653 ofp.ofp_bucket(actions=[pop_vlan(), output(unis[0][0])])
Stephane Barbarie4db8ca22017-04-24 10:30:20 -0400654 # ofp.ofp_bucket(actions=[pop_vlan(), output(unis[1][0])])
Zsolt Haraszti85f12852016-12-24 08:30:58 -0800655 ]
656 )
657 ))
658 stub.UpdateLogicalDeviceFlowTable(FlowTableUpdate(
659 id=logical_device_id,
660 flow_mod=mk_simple_flow_mod(
661 priority=1000,
662 match_fields=[
663 in_port(nni_port_no),
664 eth_type(0x800),
665 vlan_vid(4096 + 140),
666 ipv4_dst(0xe4020202)
667 ],
668 actions=[group(2)]
669 )
670 ))
Zsolt Harasztib9a5f752017-02-11 06:07:08 -0800671
Zsolt Haraszti85f12852016-12-24 08:30:58 -0800672 # 3rd with empty bucket
673 stub.UpdateLogicalDeviceFlowGroupTable(FlowGroupTableUpdate(
674 id=logical_device_id,
675 group_mod=mk_multicast_group_mod(
676 group_id=3,
677 buckets=[]
678 )
679 ))
680 stub.UpdateLogicalDeviceFlowTable(FlowTableUpdate(
681 id=logical_device_id,
682 flow_mod=mk_simple_flow_mod(
683 priority=1000,
684 match_fields=[
685 in_port(nni_port_no),
686 eth_type(0x800),
687 vlan_vid(4096 + 140),
688 ipv4_dst(0xe4030303)
689 ],
690 actions=[group(3)]
691 )
692 ))
693
694 self.poutput('success')
695
696 complete_install_all_sample_flows = VolthaCli.complete_logical_device
697
Nathan Knuth5f4163e2017-01-11 18:21:10 -0600698 def do_install_dhcp_flows(self, line):
699 """
700 Install all dhcp flows that are representative of the virtualized access
701 scenario in a PON network.
702 """
703 logical_device_id = line or self.default_logical_device_id
704
705 # gather NNI and UNI port IDs
Zsolt Harasztib9a5f752017-02-11 06:07:08 -0800706 nni_port_no, unis = self.get_logical_ports(logical_device_id)
Nathan Knuth5f4163e2017-01-11 18:21:10 -0600707
708 # construct and push flow rules
709 stub = voltha_pb2.VolthaLocalServiceStub(self.get_channel())
710
711 # Controller-bound flows
Zsolt Harasztib9a5f752017-02-11 06:07:08 -0800712 for uni_port_no, _ in unis:
713 stub.UpdateLogicalDeviceFlowTable(FlowTableUpdate(
714 id=logical_device_id,
715 flow_mod=mk_simple_flow_mod(
716 priority=1000,
717 match_fields=[
718 in_port(uni_port_no),
719 eth_type(0x800),
720 ip_proto(17),
721 udp_dst(67)
722 ],
723 actions=[output(ofp.OFPP_CONTROLLER)]
724 )
725 ))
Nathan Knuth5f4163e2017-01-11 18:21:10 -0600726
727 self.poutput('success')
728
729 complete_install_dhcp_flows = VolthaCli.complete_logical_device
730
Zsolt Haraszti85f12852016-12-24 08:30:58 -0800731 def do_delete_all_flows(self, line):
732 """
733 Remove all flows and flow groups from given logical device
734 """
735 logical_device_id = line or self.default_logical_device_id
736 stub = voltha_pb2.VolthaLocalServiceStub(self.get_channel())
737 stub.UpdateLogicalDeviceFlowTable(FlowTableUpdate(
738 id=logical_device_id,
739 flow_mod=ofp.ofp_flow_mod(
740 command=ofp.OFPFC_DELETE,
741 table_id=ofp.OFPTT_ALL,
742 cookie_mask=0,
743 out_port=ofp.OFPP_ANY,
744 out_group=ofp.OFPG_ANY
745 )
746 ))
747 stub.UpdateLogicalDeviceFlowGroupTable(FlowGroupTableUpdate(
748 id=logical_device_id,
749 group_mod=ofp.ofp_group_mod(
750 command=ofp.OFPGC_DELETE,
751 group_id=ofp.OFPG_ALL
752 )
753 ))
754 self.poutput('success')
755
756 complete_delete_all_flows = VolthaCli.complete_logical_device
757
Zsolt Haraszti80175202016-12-24 00:17:51 -0800758 def do_send_simulated_upstream_eapol(self, line):
Zsolt Harasztid036b7e2016-12-23 15:36:01 -0800759 """
760 Send an EAPOL upstream from a simulated OLT
761 """
Zsolt Haraszti80175202016-12-24 00:17:51 -0800762 device_id = line or self.default_device_id
Zsolt Harasztid036b7e2016-12-23 15:36:01 -0800763 requests.get('http://{}/devices/{}/test_eapol_in'.format(
764 self.voltha_sim_rest, device_id
765 ))
766
Zsolt Haraszti80175202016-12-24 00:17:51 -0800767 complete_send_simulated_upstream_eapol = VolthaCli.complete_device
768
769 def do_inject_eapol_start(self, line):
Zsolt Harasztid036b7e2016-12-23 15:36:01 -0800770 """
771 Send out an an EAPOL start message into the given Unix interface
772 """
773 pass
Zsolt Harasztia133a452016-12-22 01:26:57 -0800774
775
776if __name__ == '__main__':
Zsolt Haraszti9b485fb2016-12-26 23:11:15 -0800777
778 parser = argparse.ArgumentParser()
779
780 _help = '<hostname>:<port> to consul agent (default: %s)' % defs['consul']
781 parser.add_argument(
782 '-C', '--consul', action='store', default=defs['consul'], help=_help)
783
784 _help = 'Lookup Voltha endpoints based on service entries in Consul'
785 parser.add_argument(
786 '-L', '--lookup', action='store_true', help=_help)
787
788 _help = '<hostname>:<port> of Voltha gRPC service (default={})'.format(
789 defs['voltha_grpc_endpoint'])
790 parser.add_argument('-g', '--grpc-endpoint', action='store',
791 default=defs['voltha_grpc_endpoint'], help=_help)
792
793 _help = '<hostname>:<port> of Voltha simulated adapter backend for ' \
794 'testing (default={})'.format(
795 defs['voltha_sim_rest_endpoint'])
796 parser.add_argument('-s', '--sim-rest-endpoint', action='store',
797 default=defs['voltha_sim_rest_endpoint'], help=_help)
798
799 args = parser.parse_args()
800
801 if args.lookup:
802 host = args.consul.split(':')[0].strip()
803 port = int(args.consul.split(':')[1].strip())
804 consul = Consul(host=host, port=port)
805
806 _, services = consul.catalog.service('voltha-grpc')
807 if not services:
808 print('No voltha-grpc service registered in consul; exiting')
809 sys.exit(1)
810 args.grpc_endpoint = '{}:{}'.format(services[0]['ServiceAddress'],
811 services[0]['ServicePort'])
812
813 _, services = consul.catalog.service('voltha-sim-rest')
814 if not services:
815 print('No voltha-sim-rest service registered in consul; exiting')
816 sys.exit(1)
817 args.sim_rest_endpoint = '{}:{}'.format(services[0]['ServiceAddress'],
818 services[0]['ServicePort'])
819
820 c = VolthaCli(args.grpc_endpoint, args.sim_rest_endpoint)
Zsolt Haraszti80175202016-12-24 00:17:51 -0800821 c.poutput(banner)
Zsolt Harasztia133a452016-12-22 01:26:57 -0800822 c.load_history()
823 c.cmdloop()
824 c.save_history()