blob: c31000c46c350321926e846fb85f90a5a94d4b7c [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
Zsolt Harasztid036b7e2016-12-23 15:36:01 -080020from optparse import make_option
Zsolt Haraszti80175202016-12-24 00:17:51 -080021from time import sleep, time
Zsolt Harasztid036b7e2016-12-23 15:36:01 -080022
Zsolt Haraszti9b485fb2016-12-26 23:11:15 -080023import sys
24from consul import Consul
Zsolt Harasztia133a452016-12-22 01:26:57 -080025import grpc
Zsolt Harasztid036b7e2016-12-23 15:36:01 -080026import requests
27from cmd2 import Cmd, options
28from 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
32from cli.logical_device import LogicalDeviceCli
Zsolt Haraszti85f12852016-12-24 08:30:58 -080033from cli.table import TablePrinter, print_pb_list_as_table
Zsolt Harasztid036b7e2016-12-23 15:36:01 -080034from voltha.core.flow_decomposer import *
Zsolt Harasztia133a452016-12-22 01:26:57 -080035from voltha.protos import third_party
36from voltha.protos import voltha_pb2
Zsolt Haraszti85f12852016-12-24 08:30:58 -080037from voltha.protos.openflow_13_pb2 import FlowTableUpdate, FlowGroupTableUpdate
Zsolt Harasztid036b7e2016-12-23 15:36:01 -080038
Zsolt Harasztia133a452016-12-22 01:26:57 -080039_ = third_party
Zsolt Haraszti80175202016-12-24 00:17:51 -080040from cli.utils import pb2dict, dict2line
Zsolt Harasztia133a452016-12-22 01:26:57 -080041
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
60class VolthaCli(Cmd):
61
62 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 Slobodrianb5ef3482017-03-17 16:56:16 -0400179 'serial_number',
180 'parent_port_no',
181 'vlan',
182 'ports',
183 'reason',
184 'root',
185 'parent_id'
Zsolt Haraszti80175202016-12-24 00:17:51 -0800186 }
Zsolt Haraszti85f12852016-12-24 08:30:58 -0800187 print_pb_list_as_table('Devices:', devices, omit_fields, self.poutput)
Zsolt Haraszti80175202016-12-24 00:17:51 -0800188
189 def do_logical_devices(self, line):
Zsolt Harasztia133a452016-12-22 01:26:57 -0800190 """List logical devices in Voltha"""
191 stub = voltha_pb2.VolthaLocalServiceStub(self.get_channel())
192 res = stub.ListLogicalDevices(Empty())
Zsolt Haraszti80175202016-12-24 00:17:51 -0800193 omit_fields = {
194 'desc.mfr_desc',
195 'desc.hw_desc',
196 'desc.sw_desc',
197 'desc.dp_desc',
198 'desc.serial_number',
Sergio Slobodrianb5ef3482017-03-17 16:56:16 -0400199 'desc.serial_num',
200 'switch_features.n_buffers',
201 'switch_features.n_tables',
202 'switch_features.auxiliary_id',
203 'switch_features.datapath_id',
204 'switch_features.capabilities',
205 'ports'
Zsolt Haraszti80175202016-12-24 00:17:51 -0800206 }
Zsolt Haraszti85f12852016-12-24 08:30:58 -0800207 print_pb_list_as_table('Logical devices:', res.items, omit_fields,
208 self.poutput)
Zsolt Harasztia133a452016-12-22 01:26:57 -0800209
Zsolt Haraszti80175202016-12-24 00:17:51 -0800210 def do_device(self, line):
Zsolt Harasztia133a452016-12-22 01:26:57 -0800211 """Enter device level command mode"""
Zsolt Haraszti80175202016-12-24 00:17:51 -0800212 device_id = line.strip() or self.default_device_id
Zsolt Harasztid036b7e2016-12-23 15:36:01 -0800213 if not device_id:
214 raise Exception('<device-id> parameter needed')
215 sub = DeviceCli(self.get_channel, device_id)
Zsolt Harasztia133a452016-12-22 01:26:57 -0800216 sub.cmdloop()
217
Zsolt Haraszti80175202016-12-24 00:17:51 -0800218 def do_logical_device(self, line):
Zsolt Harasztia133a452016-12-22 01:26:57 -0800219 """Enter logical device level command mode"""
Zsolt Haraszti80175202016-12-24 00:17:51 -0800220 logical_device_id = line.strip() or self.default_logical_device_id
Zsolt Harasztid036b7e2016-12-23 15:36:01 -0800221 if not logical_device_id:
222 raise Exception('<logical-device-id> parameter needed')
223 sub = LogicalDeviceCli(self.get_channel, logical_device_id)
Zsolt Harasztia133a452016-12-22 01:26:57 -0800224 sub.cmdloop()
225
Zsolt Haraszti80175202016-12-24 00:17:51 -0800226 def device_ids(self, force_refresh=False):
227 if force_refresh or self.device_ids is None or \
228 (time() - self.device_ids_cache_ts) > 1:
229 self.device_ids_cache = [d.id for d in self.get_devices()]
230 self.device_ids_cache_ts = time()
231 return self.device_ids_cache
232
233 def logical_device_ids(self, force_refresh=False):
234 if force_refresh or self.logical_device_ids is None or \
235 (time() - self.logical_device_ids_cache_ts) > 1:
236 self.logical_device_ids_cache = [d.id for d
237 in self.get_logical_devices()]
238 self.logical_device_ids_cache_ts = time()
239 return self.logical_device_ids_cache
240
241 def complete_device(self, text, line, begidx, endidx):
242 if not text:
243 completions = self.device_ids()[:]
244 else:
245 completions = [d for d in self.device_ids() if d.startswith(text)]
246 return completions
247
248 def complete_logical_device(self, text, line, begidx, endidx):
249 if not text:
250 completions = self.logical_device_ids()[:]
251 else:
252 completions = [d for d in self.logical_device_ids()
253 if d.startswith(text)]
254 return completions
255
256 def do_pdb(self, line):
Zsolt Harasztia133a452016-12-22 01:26:57 -0800257 """Launch PDB debug prompt in CLI (for CLI development)"""
258 from pdb import set_trace
259 set_trace()
260
Zsolt Haraszti80175202016-12-24 00:17:51 -0800261 def do_health(self, line):
Zsolt Harasztia133a452016-12-22 01:26:57 -0800262 """Show connectivity status to Voltha status"""
263 stub = voltha_pb2.HealthServiceStub(self.get_channel())
264 res = stub.GetHealthStatus(Empty())
Zsolt Haraszti80175202016-12-24 00:17:51 -0800265 self.poutput(dumps(pb2dict(res), indent=4))
Zsolt Harasztia133a452016-12-22 01:26:57 -0800266
Zsolt Harasztid036b7e2016-12-23 15:36:01 -0800267 @options([
268 make_option('-t', '--device-type', action="store", dest='device_type',
269 help="Device type", default='simulated_olt'),
270 make_option('-m', '--mac-address', action='store', dest='mac_address',
271 default='00:0c:e2:31:40:00'),
272 make_option('-i', '--ip-address', action='store', dest='ip_address'),
Zsolt Haraszti656ecc62016-12-28 15:08:23 -0800273 make_option('-H', '--host_and_port', action='store',
274 dest='host_and_port'),
Zsolt Harasztid036b7e2016-12-23 15:36:01 -0800275 ])
Zsolt Haraszti80175202016-12-24 00:17:51 -0800276 def do_preprovision_olt(self, line, opts):
Zsolt Harasztid036b7e2016-12-23 15:36:01 -0800277 """Preprovision a new OLT with given device type"""
Zsolt Harasztia133a452016-12-22 01:26:57 -0800278 stub = voltha_pb2.VolthaLocalServiceStub(self.get_channel())
Zsolt Harasztid036b7e2016-12-23 15:36:01 -0800279 kw = dict(type=opts.device_type)
Zsolt Haraszti656ecc62016-12-28 15:08:23 -0800280 if opts.host_and_port:
281 kw['host_and_port'] = opts.host_and_port
282 elif opts.ip_address:
Zsolt Harasztid036b7e2016-12-23 15:36:01 -0800283 kw['ipv4_address'] = opts.ip_address
284 elif opts.mac_address:
285 kw['mac_address'] = opts.mac_address
286 else:
287 raise Exception('Either IP address or Mac Address is needed')
288 device = voltha_pb2.Device(**kw)
289 device = stub.CreateDevice(device)
Zsolt Haraszti80175202016-12-24 00:17:51 -0800290 self.poutput('success (device id = {})'.format(device.id))
Zsolt Harasztid036b7e2016-12-23 15:36:01 -0800291 self.default_device_id = device.id
Zsolt Harasztia133a452016-12-22 01:26:57 -0800292
Khen Nursimulud068d812017-03-06 11:44:18 -0500293 def do_enable(self, line):
Zsolt Harasztid036b7e2016-12-23 15:36:01 -0800294 """
Khen Nursimulud068d812017-03-06 11:44:18 -0500295 Enable a device. If the <id> is not provided, it will be on the last
296 pre-provisioned device.
Zsolt Harasztid036b7e2016-12-23 15:36:01 -0800297 """
Zsolt Haraszti80175202016-12-24 00:17:51 -0800298 device_id = line or self.default_device_id
Khen Nursimulu29e75502017-03-07 17:26:50 -0500299 self.poutput('enabling {}'.format(device_id))
Khen Nursimuluc60afa12017-03-13 14:33:50 -0400300 try:
301 stub = voltha_pb2.VolthaLocalServiceStub(self.get_channel())
302 stub.EnableDevice(voltha_pb2.ID(id=device_id))
Zsolt Harasztia133a452016-12-22 01:26:57 -0800303
Khen Nursimuluc60afa12017-03-13 14:33:50 -0400304 while True:
305 device = stub.GetDevice(voltha_pb2.ID(id=device_id))
306 # If this is an OLT then acquire logical device id
307 if device.oper_status == voltha_pb2.OperStatus.ACTIVE:
308 if device.type.endswith('_olt'):
309 assert device.parent_id
310 self.default_logical_device_id = device.parent_id
311 self.poutput('success (logical device id = {})'.format(
312 self.default_logical_device_id))
313 else:
314 self.poutput('success (device id = {})'.format(device.id))
315 break
316 self.poutput('waiting for device to be enabled...')
317 sleep(.5)
318 except Exception, e:
319 self.poutput('Error enabling {}. Error:{}'.format(device_id, e))
Zsolt Harasztid036b7e2016-12-23 15:36:01 -0800320
Zsolt Haraszti50cae7d2017-01-08 22:27:07 -0800321 complete_activate_olt = complete_device
322
Khen Nursimulud068d812017-03-06 11:44:18 -0500323 def do_reboot(self, line):
324 """
325 Rebooting a device. ID of the device needs to be provided
326 """
327 device_id = line or self.default_device_id
328 self.poutput('rebooting {}'.format(device_id))
Khen Nursimuluc60afa12017-03-13 14:33:50 -0400329 try:
330 stub = voltha_pb2.VolthaLocalServiceStub(self.get_channel())
331 stub.RebootDevice(voltha_pb2.ID(id=device_id))
332 self.poutput('rebooted {}'.format(device_id))
333 except Exception, e:
334 self.poutput('Error rebooting {}. Error:{}'.format(device_id, e))
Khen Nursimulud068d812017-03-06 11:44:18 -0500335
336 def do_delete(self, line):
337 """
338 Deleting a device. ID of the device needs to be provided
339 """
340 device_id = line or self.default_device_id
341 self.poutput('deleting {}'.format(device_id))
Khen Nursimuluc60afa12017-03-13 14:33:50 -0400342 try:
343 stub = voltha_pb2.VolthaLocalServiceStub(self.get_channel())
344 stub.DeleteDevice(voltha_pb2.ID(id=device_id))
345 self.poutput('deleted {}'.format(device_id))
346 except Exception, e:
347 self.poutput('Error deleting {}. Error:{}'.format(device_id, e))
Khen Nursimulud068d812017-03-06 11:44:18 -0500348
349 def do_disable(self, line):
350 """
351 Disable a device. ID of the device needs to be provided
352 """
353 device_id = line
354 self.poutput('disabling {}'.format(device_id))
Khen Nursimuluc60afa12017-03-13 14:33:50 -0400355 try:
356 stub = voltha_pb2.VolthaLocalServiceStub(self.get_channel())
357 stub.DisableDevice(voltha_pb2.ID(id=device_id))
Khen Nursimulud068d812017-03-06 11:44:18 -0500358
Khen Nursimuluc60afa12017-03-13 14:33:50 -0400359 # Do device query and verify that the device admin status is
360 # DISABLED and Operational Status is unknown
361 device = stub.GetDevice(voltha_pb2.ID(id=device_id))
362 if device.oper_status == voltha_pb2.OperStatus.UNKNOWN and \
363 device.admin_state == voltha_pb2.AdminState.DISABLED:
364 self.poutput('disabled successfully {}'.format(device_id))
365 else:
366 self.poutput('disabling failed {}. Admin State:{} '
367 'Operation State: {}'.format(device_id,
368 device.admin_state,
369 device.oper_status))
370 except Exception, e:
371 self.poutput('Error disabling {}. Error:{}'.format(device_id, e))
Khen Nursimulud068d812017-03-06 11:44:18 -0500372
Zsolt Haraszti50cae7d2017-01-08 22:27:07 -0800373 def do_test(self, line):
374 """Enter test mode, which makes a bunch on new commands available"""
375 sub = TestCli(self.history, self.get_channel, self.voltha_grpc,
376 self.voltha_sim_rest)
377 sub.cmdloop()
378
379
380class TestCli(VolthaCli):
381
382 def __init__(self, history, get_channel, voltha_grpc, voltha_sim_rest):
383 VolthaCli.__init__(self, voltha_grpc, voltha_sim_rest)
384 self.history = history
385 self.get_channel = get_channel
386 self.prompt = '(' + self.colorize(self.colorize('test', 'cyan'),
387 'bold') + ') '
388
389 def get_device(self, device_id, depth=0):
390 stub = voltha_pb2.VolthaLocalServiceStub(self.get_channel())
391 res = stub.GetDevice(voltha_pb2.ID(id=device_id),
392 metadata=(('get-depth', str(depth)), ))
393 return res
Zsolt Haraszti80175202016-12-24 00:17:51 -0800394
395 def do_arrive_onus(self, line):
Zsolt Harasztid036b7e2016-12-23 15:36:01 -0800396 """
Zsolt Haraszti85f12852016-12-24 08:30:58 -0800397 Simulate the arrival of ONUs (available only on simulated_olt)
Zsolt Harasztid036b7e2016-12-23 15:36:01 -0800398 """
Zsolt Haraszti80175202016-12-24 00:17:51 -0800399 device_id = line or self.default_device_id
Zsolt Haraszti85f12852016-12-24 08:30:58 -0800400
401 # verify that device is of type simulated_olt
402 device = self.get_device(device_id)
403 assert device.type == 'simulated_olt', (
404 'Cannot use it on this device type (only on simulated_olt type)')
405
Zsolt Harasztid036b7e2016-12-23 15:36:01 -0800406 requests.get('http://{}/devices/{}/detect_onus'.format(
407 self.voltha_sim_rest, device_id
408 ))
409
Zsolt Haraszti80175202016-12-24 00:17:51 -0800410 complete_arrive_onus = VolthaCli.complete_device
411
Zsolt Haraszti85f12852016-12-24 08:30:58 -0800412 def get_logical_ports(self, logical_device_id):
413 """
414 Return the NNI port number and the first usable UNI port of logical
415 device, and the vlan associated with the latter.
416 """
417 stub = voltha_pb2.VolthaLocalServiceStub(self.get_channel())
418 ports = stub.ListLogicalDevicePorts(
419 voltha_pb2.ID(id=logical_device_id)).items
Zsolt Harasztib9a5f752017-02-11 06:07:08 -0800420 nni = None
421 unis = []
Zsolt Haraszti85f12852016-12-24 08:30:58 -0800422 for port in ports:
Zsolt Harasztib9a5f752017-02-11 06:07:08 -0800423 if port.root_port:
424 assert nni is None, "There shall be only one root port"
Zsolt Haraszti85f12852016-12-24 08:30:58 -0800425 nni = port.ofp_port.port_no
Zsolt Harasztib9a5f752017-02-11 06:07:08 -0800426 else:
Zsolt Haraszti85f12852016-12-24 08:30:58 -0800427 uni = port.ofp_port.port_no
428 uni_device = self.get_device(port.device_id)
429 vlan = uni_device.vlan
Zsolt Harasztib9a5f752017-02-11 06:07:08 -0800430 unis.append((uni, vlan))
431
432 assert nni is not None, "No NNI port found"
433 assert unis, "Not a single UNI?"
434
435 return nni, unis
Zsolt Haraszti85f12852016-12-24 08:30:58 -0800436
Zsolt Haraszti80175202016-12-24 00:17:51 -0800437 def do_install_eapol_flow(self, line):
Zsolt Harasztid036b7e2016-12-23 15:36:01 -0800438 """
439 Install an EAPOL flow on the given logical device. If device is not
440 given, it will be applied to logical device of the last pre-provisioned
441 OLT device.
442 """
Zsolt Haraszti80175202016-12-24 00:17:51 -0800443 logical_device_id = line or self.default_logical_device_id
Zsolt Haraszti85f12852016-12-24 08:30:58 -0800444
445 # gather NNI and UNI port IDs
Zsolt Harasztib9a5f752017-02-11 06:07:08 -0800446 nni_port_no, unis = self.get_logical_ports(logical_device_id)
Zsolt Haraszti85f12852016-12-24 08:30:58 -0800447
448 # construct and push flow rule
Zsolt Harasztid036b7e2016-12-23 15:36:01 -0800449 stub = voltha_pb2.VolthaLocalServiceStub(self.get_channel())
Zsolt Harasztib9a5f752017-02-11 06:07:08 -0800450 for uni_port_no, _ in unis:
451 update = FlowTableUpdate(
452 id=logical_device_id,
453 flow_mod=mk_simple_flow_mod(
454 priority=2000,
455 match_fields=[in_port(uni_port_no), eth_type(0x888e)],
456 actions=[
457 # push_vlan(0x8100),
458 # set_field(vlan_vid(4096 + 4000)),
459 output(ofp.OFPP_CONTROLLER)
460 ]
461 )
462 )
463 res = stub.UpdateLogicalDeviceFlowTable(update)
464 self.poutput('success for uni {} ({})'.format(uni_port_no, res))
Zsolt Harasztid036b7e2016-12-23 15:36:01 -0800465
Zsolt Haraszti80175202016-12-24 00:17:51 -0800466 complete_install_eapol_flow = VolthaCli.complete_logical_device
467
Zsolt Haraszti85f12852016-12-24 08:30:58 -0800468 def do_install_all_controller_bound_flows(self, line):
469 """
470 Install all flow rules for controller bound flows, including EAPOL,
471 IGMP and DHCP. If device is not given, it will be applied to logical
472 device of the last pre-provisioned OLT device.
473 """
474 logical_device_id = line or self.default_logical_device_id
475
476 # gather NNI and UNI port IDs
Zsolt Harasztib9a5f752017-02-11 06:07:08 -0800477 nni_port_no, unis = self.get_logical_ports(logical_device_id)
Zsolt Haraszti85f12852016-12-24 08:30:58 -0800478
479 # construct and push flow rules
480 stub = voltha_pb2.VolthaLocalServiceStub(self.get_channel())
481
Zsolt Harasztib9a5f752017-02-11 06:07:08 -0800482 for uni_port_no, _ in unis:
483 stub.UpdateLogicalDeviceFlowTable(FlowTableUpdate(
484 id=logical_device_id,
485 flow_mod=mk_simple_flow_mod(
486 priority=2000,
487 match_fields=[
488 in_port(uni_port_no),
489 eth_type(0x888e)
490 ],
491 actions=[output(ofp.OFPP_CONTROLLER)]
492 )
493 ))
494 stub.UpdateLogicalDeviceFlowTable(FlowTableUpdate(
495 id=logical_device_id,
496 flow_mod=mk_simple_flow_mod(
497 priority=1000,
498 match_fields=[
499 in_port(uni_port_no),
500 eth_type(0x800),
501 ip_proto(2)
502 ],
503 actions=[output(ofp.OFPP_CONTROLLER)]
504 )
505 ))
506 stub.UpdateLogicalDeviceFlowTable(FlowTableUpdate(
507 id=logical_device_id,
508 flow_mod=mk_simple_flow_mod(
509 priority=1000,
510 match_fields=[
511 in_port(uni_port_no),
512 eth_type(0x800),
513 ip_proto(17),
514 udp_dst(67)
515 ],
516 actions=[output(ofp.OFPP_CONTROLLER)]
517 )
518 ))
Zsolt Haraszti85f12852016-12-24 08:30:58 -0800519 self.poutput('success')
520
521 complete_install_all_controller_bound_flows = \
522 VolthaCli.complete_logical_device
523
524 def do_install_all_sample_flows(self, line):
525 """
526 Install all flows that are representative of the virtualized access
527 scenario in a PON network.
528 """
529 logical_device_id = line or self.default_logical_device_id
530
531 # gather NNI and UNI port IDs
Zsolt Harasztib9a5f752017-02-11 06:07:08 -0800532 nni_port_no, unis = self.get_logical_ports(logical_device_id)
Zsolt Haraszti85f12852016-12-24 08:30:58 -0800533
534 # construct and push flow rules
535 stub = voltha_pb2.VolthaLocalServiceStub(self.get_channel())
536
Zsolt Harasztib9a5f752017-02-11 06:07:08 -0800537 for uni_port_no, c_vid in unis:
Zsolt Haraszti85f12852016-12-24 08:30:58 -0800538
Zsolt Harasztib9a5f752017-02-11 06:07:08 -0800539 # Controller-bound flows
540 stub.UpdateLogicalDeviceFlowTable(FlowTableUpdate(
541 id=logical_device_id,
542 flow_mod=mk_simple_flow_mod(
543 priority=2000,
544 match_fields=[in_port(uni_port_no), eth_type(0x888e)],
545 actions=[
546 # push_vlan(0x8100),
547 # set_field(vlan_vid(4096 + 4000)),
548 output(ofp.OFPP_CONTROLLER)
549 ]
550 )
551 ))
552 stub.UpdateLogicalDeviceFlowTable(FlowTableUpdate(
553 id=logical_device_id,
554 flow_mod=mk_simple_flow_mod(
555 priority=1000,
556 match_fields=[eth_type(0x800), ip_proto(2)],
557 actions=[output(ofp.OFPP_CONTROLLER)]
558 )
559 ))
560 stub.UpdateLogicalDeviceFlowTable(FlowTableUpdate(
561 id=logical_device_id,
562 flow_mod=mk_simple_flow_mod(
563 priority=1000,
564 match_fields=[eth_type(0x800), ip_proto(17), udp_dst(67)],
565 actions=[output(ofp.OFPP_CONTROLLER)]
566 )
567 ))
568
569 # Unicast flows:
570 # Downstream flow 1
571 stub.UpdateLogicalDeviceFlowTable(FlowTableUpdate(
572 id=logical_device_id,
573 flow_mod=mk_simple_flow_mod(
574 priority=500,
575 match_fields=[
576 in_port(nni_port_no),
577 vlan_vid(4096 + 1000),
578 metadata(c_vid) # here to mimic an ONOS artifact
579 ],
580 actions=[pop_vlan()],
581 next_table_id=1
582 )
583 ))
584 # Downstream flow 2
585 stub.UpdateLogicalDeviceFlowTable(FlowTableUpdate(
586 id=logical_device_id,
587 flow_mod=mk_simple_flow_mod(
588 priority=500,
589 table_id=1,
590 match_fields=[in_port(nni_port_no), vlan_vid(4096 + c_vid)],
591 actions=[set_field(vlan_vid(4096 + 0)), output(uni_port_no)]
592 )
593 ))
594 # Upstream flow 1 for 0-tagged 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(4096 + 0)],
600 actions=[set_field(vlan_vid(4096 + c_vid))],
601 next_table_id=1
602 )
603 ))
604 # Upstream flow 1 for untagged case
605 stub.UpdateLogicalDeviceFlowTable(FlowTableUpdate(
606 id=logical_device_id,
607 flow_mod=mk_simple_flow_mod(
608 priority=500,
609 match_fields=[in_port(uni_port_no), vlan_vid(0)],
610 actions=[push_vlan(0x8100), set_field(vlan_vid(4096 + c_vid))],
611 next_table_id=1
612 )
613 ))
614 # Upstream flow 2 for s-tag
615 stub.UpdateLogicalDeviceFlowTable(FlowTableUpdate(
616 id=logical_device_id,
617 flow_mod=mk_simple_flow_mod(
618 priority=500,
619 table_id=1,
620 match_fields=[in_port(uni_port_no), vlan_vid(4096 + c_vid)],
621 actions=[
622 push_vlan(0x8100),
623 set_field(vlan_vid(4096 + 1000)),
624 output(nni_port_no)
625 ]
626 )
627 ))
Zsolt Haraszti85f12852016-12-24 08:30:58 -0800628
629 # Push a few multicast flows
Zsolt Harasztib9a5f752017-02-11 06:07:08 -0800630 # 1st with one bucket for our uni 0
Zsolt Haraszti85f12852016-12-24 08:30:58 -0800631 stub.UpdateLogicalDeviceFlowGroupTable(FlowGroupTableUpdate(
632 id=logical_device_id,
633 group_mod=mk_multicast_group_mod(
634 group_id=1,
635 buckets=[
Zsolt Harasztib9a5f752017-02-11 06:07:08 -0800636 ofp.ofp_bucket(actions=[
637 pop_vlan(),
638 output(unis[0][0])
639 ])
Zsolt Haraszti85f12852016-12-24 08:30:58 -0800640 ]
641 )
642 ))
643 stub.UpdateLogicalDeviceFlowTable(FlowTableUpdate(
644 id=logical_device_id,
645 flow_mod=mk_simple_flow_mod(
646 priority=1000,
647 match_fields=[
648 in_port(nni_port_no),
649 eth_type(0x800),
650 vlan_vid(4096 + 140),
651 ipv4_dst(0xe4010101)
652 ],
653 actions=[group(1)]
654 )
655 ))
Zsolt Harasztib9a5f752017-02-11 06:07:08 -0800656
657 # 2nd with one bucket for uni 0 and 1
Zsolt Haraszti85f12852016-12-24 08:30:58 -0800658 stub.UpdateLogicalDeviceFlowGroupTable(FlowGroupTableUpdate(
659 id=logical_device_id,
660 group_mod=mk_multicast_group_mod(
661 group_id=2,
662 buckets=[
Nathan Knuth6b7b6ff2017-02-12 03:30:48 -0800663 ofp.ofp_bucket(actions=[pop_vlan(), output(unis[0][0])])
664# ofp.ofp_bucket(actions=[pop_vlan(), output(unis[1][0])])
Zsolt Haraszti85f12852016-12-24 08:30:58 -0800665 ]
666 )
667 ))
668 stub.UpdateLogicalDeviceFlowTable(FlowTableUpdate(
669 id=logical_device_id,
670 flow_mod=mk_simple_flow_mod(
671 priority=1000,
672 match_fields=[
673 in_port(nni_port_no),
674 eth_type(0x800),
675 vlan_vid(4096 + 140),
676 ipv4_dst(0xe4020202)
677 ],
678 actions=[group(2)]
679 )
680 ))
Zsolt Harasztib9a5f752017-02-11 06:07:08 -0800681
Zsolt Haraszti85f12852016-12-24 08:30:58 -0800682 # 3rd with empty bucket
683 stub.UpdateLogicalDeviceFlowGroupTable(FlowGroupTableUpdate(
684 id=logical_device_id,
685 group_mod=mk_multicast_group_mod(
686 group_id=3,
687 buckets=[]
688 )
689 ))
690 stub.UpdateLogicalDeviceFlowTable(FlowTableUpdate(
691 id=logical_device_id,
692 flow_mod=mk_simple_flow_mod(
693 priority=1000,
694 match_fields=[
695 in_port(nni_port_no),
696 eth_type(0x800),
697 vlan_vid(4096 + 140),
698 ipv4_dst(0xe4030303)
699 ],
700 actions=[group(3)]
701 )
702 ))
703
704 self.poutput('success')
705
706 complete_install_all_sample_flows = VolthaCli.complete_logical_device
707
Nathan Knuth5f4163e2017-01-11 18:21:10 -0600708 def do_install_dhcp_flows(self, line):
709 """
710 Install all dhcp flows that are representative of the virtualized access
711 scenario in a PON network.
712 """
713 logical_device_id = line or self.default_logical_device_id
714
715 # gather NNI and UNI port IDs
Zsolt Harasztib9a5f752017-02-11 06:07:08 -0800716 nni_port_no, unis = self.get_logical_ports(logical_device_id)
Nathan Knuth5f4163e2017-01-11 18:21:10 -0600717
718 # construct and push flow rules
719 stub = voltha_pb2.VolthaLocalServiceStub(self.get_channel())
720
721 # Controller-bound flows
Zsolt Harasztib9a5f752017-02-11 06:07:08 -0800722 for uni_port_no, _ in unis:
723 stub.UpdateLogicalDeviceFlowTable(FlowTableUpdate(
724 id=logical_device_id,
725 flow_mod=mk_simple_flow_mod(
726 priority=1000,
727 match_fields=[
728 in_port(uni_port_no),
729 eth_type(0x800),
730 ip_proto(17),
731 udp_dst(67)
732 ],
733 actions=[output(ofp.OFPP_CONTROLLER)]
734 )
735 ))
Nathan Knuth5f4163e2017-01-11 18:21:10 -0600736
737 self.poutput('success')
738
739 complete_install_dhcp_flows = VolthaCli.complete_logical_device
740
Zsolt Haraszti85f12852016-12-24 08:30:58 -0800741 def do_delete_all_flows(self, line):
742 """
743 Remove all flows and flow groups from given logical device
744 """
745 logical_device_id = line or self.default_logical_device_id
746 stub = voltha_pb2.VolthaLocalServiceStub(self.get_channel())
747 stub.UpdateLogicalDeviceFlowTable(FlowTableUpdate(
748 id=logical_device_id,
749 flow_mod=ofp.ofp_flow_mod(
750 command=ofp.OFPFC_DELETE,
751 table_id=ofp.OFPTT_ALL,
752 cookie_mask=0,
753 out_port=ofp.OFPP_ANY,
754 out_group=ofp.OFPG_ANY
755 )
756 ))
757 stub.UpdateLogicalDeviceFlowGroupTable(FlowGroupTableUpdate(
758 id=logical_device_id,
759 group_mod=ofp.ofp_group_mod(
760 command=ofp.OFPGC_DELETE,
761 group_id=ofp.OFPG_ALL
762 )
763 ))
764 self.poutput('success')
765
766 complete_delete_all_flows = VolthaCli.complete_logical_device
767
Zsolt Haraszti80175202016-12-24 00:17:51 -0800768 def do_send_simulated_upstream_eapol(self, line):
Zsolt Harasztid036b7e2016-12-23 15:36:01 -0800769 """
770 Send an EAPOL upstream from a simulated OLT
771 """
Zsolt Haraszti80175202016-12-24 00:17:51 -0800772 device_id = line or self.default_device_id
Zsolt Harasztid036b7e2016-12-23 15:36:01 -0800773 requests.get('http://{}/devices/{}/test_eapol_in'.format(
774 self.voltha_sim_rest, device_id
775 ))
776
Zsolt Haraszti80175202016-12-24 00:17:51 -0800777 complete_send_simulated_upstream_eapol = VolthaCli.complete_device
778
779 def do_inject_eapol_start(self, line):
Zsolt Harasztid036b7e2016-12-23 15:36:01 -0800780 """
781 Send out an an EAPOL start message into the given Unix interface
782 """
783 pass
Zsolt Harasztia133a452016-12-22 01:26:57 -0800784
785
786if __name__ == '__main__':
Zsolt Haraszti9b485fb2016-12-26 23:11:15 -0800787
788 parser = argparse.ArgumentParser()
789
790 _help = '<hostname>:<port> to consul agent (default: %s)' % defs['consul']
791 parser.add_argument(
792 '-C', '--consul', action='store', default=defs['consul'], help=_help)
793
794 _help = 'Lookup Voltha endpoints based on service entries in Consul'
795 parser.add_argument(
796 '-L', '--lookup', action='store_true', help=_help)
797
798 _help = '<hostname>:<port> of Voltha gRPC service (default={})'.format(
799 defs['voltha_grpc_endpoint'])
800 parser.add_argument('-g', '--grpc-endpoint', action='store',
801 default=defs['voltha_grpc_endpoint'], help=_help)
802
803 _help = '<hostname>:<port> of Voltha simulated adapter backend for ' \
804 'testing (default={})'.format(
805 defs['voltha_sim_rest_endpoint'])
806 parser.add_argument('-s', '--sim-rest-endpoint', action='store',
807 default=defs['voltha_sim_rest_endpoint'], help=_help)
808
809 args = parser.parse_args()
810
811 if args.lookup:
812 host = args.consul.split(':')[0].strip()
813 port = int(args.consul.split(':')[1].strip())
814 consul = Consul(host=host, port=port)
815
816 _, services = consul.catalog.service('voltha-grpc')
817 if not services:
818 print('No voltha-grpc service registered in consul; exiting')
819 sys.exit(1)
820 args.grpc_endpoint = '{}:{}'.format(services[0]['ServiceAddress'],
821 services[0]['ServicePort'])
822
823 _, services = consul.catalog.service('voltha-sim-rest')
824 if not services:
825 print('No voltha-sim-rest service registered in consul; exiting')
826 sys.exit(1)
827 args.sim_rest_endpoint = '{}:{}'.format(services[0]['ServiceAddress'],
828 services[0]['ServicePort'])
829
830 c = VolthaCli(args.grpc_endpoint, args.sim_rest_endpoint)
Zsolt Haraszti80175202016-12-24 00:17:51 -0800831 c.poutput(banner)
Zsolt Harasztia133a452016-12-22 01:26:57 -0800832 c.load_history()
833 c.cmdloop()
834 c.save_history()