blob: f1256347e2f32659abf8dad13325c8ad66cd3282 [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())
156 omit_fields = {}
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',
179 'serial_number'
180 }
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',
193 'switch_features.capabilities'
194 }
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 \
216 (time() - self.device_ids_cache_ts) > 1:
217 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 \
223 (time() - self.logical_device_ids_cache_ts) > 1:
224 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',
257 help="Device type", default='simulated_olt'),
258 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
367
368class TestCli(VolthaCli):
369
370 def __init__(self, history, get_channel, voltha_grpc, voltha_sim_rest):
371 VolthaCli.__init__(self, voltha_grpc, voltha_sim_rest)
372 self.history = history
373 self.get_channel = get_channel
374 self.prompt = '(' + self.colorize(self.colorize('test', 'cyan'),
375 'bold') + ') '
376
377 def get_device(self, device_id, depth=0):
378 stub = voltha_pb2.VolthaLocalServiceStub(self.get_channel())
379 res = stub.GetDevice(voltha_pb2.ID(id=device_id),
380 metadata=(('get-depth', str(depth)), ))
381 return res
Zsolt Haraszti80175202016-12-24 00:17:51 -0800382
383 def do_arrive_onus(self, line):
Zsolt Harasztid036b7e2016-12-23 15:36:01 -0800384 """
Zsolt Haraszti85f12852016-12-24 08:30:58 -0800385 Simulate the arrival of ONUs (available only on simulated_olt)
Zsolt Harasztid036b7e2016-12-23 15:36:01 -0800386 """
Zsolt Haraszti80175202016-12-24 00:17:51 -0800387 device_id = line or self.default_device_id
Zsolt Haraszti85f12852016-12-24 08:30:58 -0800388
389 # verify that device is of type simulated_olt
390 device = self.get_device(device_id)
391 assert device.type == 'simulated_olt', (
392 'Cannot use it on this device type (only on simulated_olt type)')
393
Zsolt Harasztid036b7e2016-12-23 15:36:01 -0800394 requests.get('http://{}/devices/{}/detect_onus'.format(
395 self.voltha_sim_rest, device_id
396 ))
397
Zsolt Haraszti80175202016-12-24 00:17:51 -0800398 complete_arrive_onus = VolthaCli.complete_device
399
Zsolt Haraszti85f12852016-12-24 08:30:58 -0800400 def get_logical_ports(self, logical_device_id):
401 """
402 Return the NNI port number and the first usable UNI port of logical
403 device, and the vlan associated with the latter.
404 """
405 stub = voltha_pb2.VolthaLocalServiceStub(self.get_channel())
406 ports = stub.ListLogicalDevicePorts(
407 voltha_pb2.ID(id=logical_device_id)).items
Zsolt Harasztib9a5f752017-02-11 06:07:08 -0800408 nni = None
409 unis = []
Zsolt Haraszti85f12852016-12-24 08:30:58 -0800410 for port in ports:
Zsolt Harasztib9a5f752017-02-11 06:07:08 -0800411 if port.root_port:
412 assert nni is None, "There shall be only one root port"
Zsolt Haraszti85f12852016-12-24 08:30:58 -0800413 nni = port.ofp_port.port_no
Zsolt Harasztib9a5f752017-02-11 06:07:08 -0800414 else:
Zsolt Haraszti85f12852016-12-24 08:30:58 -0800415 uni = port.ofp_port.port_no
416 uni_device = self.get_device(port.device_id)
417 vlan = uni_device.vlan
Zsolt Harasztib9a5f752017-02-11 06:07:08 -0800418 unis.append((uni, vlan))
419
420 assert nni is not None, "No NNI port found"
421 assert unis, "Not a single UNI?"
422
423 return nni, unis
Zsolt Haraszti85f12852016-12-24 08:30:58 -0800424
Zsolt Haraszti80175202016-12-24 00:17:51 -0800425 def do_install_eapol_flow(self, line):
Zsolt Harasztid036b7e2016-12-23 15:36:01 -0800426 """
427 Install an EAPOL flow on the given logical device. If device is not
428 given, it will be applied to logical device of the last pre-provisioned
429 OLT device.
430 """
Zsolt Haraszti80175202016-12-24 00:17:51 -0800431 logical_device_id = line or self.default_logical_device_id
Zsolt Haraszti85f12852016-12-24 08:30:58 -0800432
433 # gather NNI and UNI port IDs
Zsolt Harasztib9a5f752017-02-11 06:07:08 -0800434 nni_port_no, unis = self.get_logical_ports(logical_device_id)
Zsolt Haraszti85f12852016-12-24 08:30:58 -0800435
436 # construct and push flow rule
Zsolt Harasztid036b7e2016-12-23 15:36:01 -0800437 stub = voltha_pb2.VolthaLocalServiceStub(self.get_channel())
Zsolt Harasztib9a5f752017-02-11 06:07:08 -0800438 for uni_port_no, _ in unis:
439 update = FlowTableUpdate(
440 id=logical_device_id,
441 flow_mod=mk_simple_flow_mod(
442 priority=2000,
443 match_fields=[in_port(uni_port_no), eth_type(0x888e)],
444 actions=[
445 # push_vlan(0x8100),
446 # set_field(vlan_vid(4096 + 4000)),
447 output(ofp.OFPP_CONTROLLER)
448 ]
449 )
450 )
451 res = stub.UpdateLogicalDeviceFlowTable(update)
452 self.poutput('success for uni {} ({})'.format(uni_port_no, res))
Zsolt Harasztid036b7e2016-12-23 15:36:01 -0800453
Zsolt Haraszti80175202016-12-24 00:17:51 -0800454 complete_install_eapol_flow = VolthaCli.complete_logical_device
455
Zsolt Haraszti85f12852016-12-24 08:30:58 -0800456 def do_install_all_controller_bound_flows(self, line):
457 """
458 Install all flow rules for controller bound flows, including EAPOL,
459 IGMP and DHCP. If device is not given, it will be applied to logical
460 device of the last pre-provisioned OLT device.
461 """
462 logical_device_id = line or self.default_logical_device_id
463
464 # gather NNI and UNI port IDs
Zsolt Harasztib9a5f752017-02-11 06:07:08 -0800465 nni_port_no, unis = self.get_logical_ports(logical_device_id)
Zsolt Haraszti85f12852016-12-24 08:30:58 -0800466
467 # construct and push flow rules
468 stub = voltha_pb2.VolthaLocalServiceStub(self.get_channel())
469
Zsolt Harasztib9a5f752017-02-11 06:07:08 -0800470 for uni_port_no, _ in unis:
471 stub.UpdateLogicalDeviceFlowTable(FlowTableUpdate(
472 id=logical_device_id,
473 flow_mod=mk_simple_flow_mod(
474 priority=2000,
475 match_fields=[
476 in_port(uni_port_no),
477 eth_type(0x888e)
478 ],
479 actions=[output(ofp.OFPP_CONTROLLER)]
480 )
481 ))
482 stub.UpdateLogicalDeviceFlowTable(FlowTableUpdate(
483 id=logical_device_id,
484 flow_mod=mk_simple_flow_mod(
485 priority=1000,
486 match_fields=[
487 in_port(uni_port_no),
488 eth_type(0x800),
489 ip_proto(2)
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(17),
502 udp_dst(67)
503 ],
504 actions=[output(ofp.OFPP_CONTROLLER)]
505 )
506 ))
Zsolt Haraszti85f12852016-12-24 08:30:58 -0800507 self.poutput('success')
508
509 complete_install_all_controller_bound_flows = \
510 VolthaCli.complete_logical_device
511
512 def do_install_all_sample_flows(self, line):
513 """
514 Install all flows that are representative of the virtualized access
515 scenario in a PON network.
516 """
517 logical_device_id = line or self.default_logical_device_id
518
519 # gather NNI and UNI port IDs
Zsolt Harasztib9a5f752017-02-11 06:07:08 -0800520 nni_port_no, unis = self.get_logical_ports(logical_device_id)
Zsolt Haraszti85f12852016-12-24 08:30:58 -0800521
522 # construct and push flow rules
523 stub = voltha_pb2.VolthaLocalServiceStub(self.get_channel())
524
Zsolt Harasztib9a5f752017-02-11 06:07:08 -0800525 for uni_port_no, c_vid in unis:
Zsolt Haraszti85f12852016-12-24 08:30:58 -0800526
Zsolt Harasztib9a5f752017-02-11 06:07:08 -0800527 # Controller-bound flows
528 stub.UpdateLogicalDeviceFlowTable(FlowTableUpdate(
529 id=logical_device_id,
530 flow_mod=mk_simple_flow_mod(
531 priority=2000,
532 match_fields=[in_port(uni_port_no), eth_type(0x888e)],
533 actions=[
534 # push_vlan(0x8100),
535 # set_field(vlan_vid(4096 + 4000)),
536 output(ofp.OFPP_CONTROLLER)
537 ]
538 )
539 ))
540 stub.UpdateLogicalDeviceFlowTable(FlowTableUpdate(
541 id=logical_device_id,
542 flow_mod=mk_simple_flow_mod(
543 priority=1000,
544 match_fields=[eth_type(0x800), ip_proto(2)],
545 actions=[output(ofp.OFPP_CONTROLLER)]
546 )
547 ))
548 stub.UpdateLogicalDeviceFlowTable(FlowTableUpdate(
549 id=logical_device_id,
550 flow_mod=mk_simple_flow_mod(
551 priority=1000,
552 match_fields=[eth_type(0x800), ip_proto(17), udp_dst(67)],
553 actions=[output(ofp.OFPP_CONTROLLER)]
554 )
555 ))
556
557 # Unicast flows:
558 # Downstream flow 1
559 stub.UpdateLogicalDeviceFlowTable(FlowTableUpdate(
560 id=logical_device_id,
561 flow_mod=mk_simple_flow_mod(
562 priority=500,
563 match_fields=[
564 in_port(nni_port_no),
565 vlan_vid(4096 + 1000),
566 metadata(c_vid) # here to mimic an ONOS artifact
567 ],
568 actions=[pop_vlan()],
569 next_table_id=1
570 )
571 ))
572 # Downstream flow 2
573 stub.UpdateLogicalDeviceFlowTable(FlowTableUpdate(
574 id=logical_device_id,
575 flow_mod=mk_simple_flow_mod(
576 priority=500,
577 table_id=1,
578 match_fields=[in_port(nni_port_no), vlan_vid(4096 + c_vid)],
579 actions=[set_field(vlan_vid(4096 + 0)), output(uni_port_no)]
580 )
581 ))
582 # Upstream flow 1 for 0-tagged case
583 stub.UpdateLogicalDeviceFlowTable(FlowTableUpdate(
584 id=logical_device_id,
585 flow_mod=mk_simple_flow_mod(
586 priority=500,
587 match_fields=[in_port(uni_port_no), vlan_vid(4096 + 0)],
588 actions=[set_field(vlan_vid(4096 + c_vid))],
589 next_table_id=1
590 )
591 ))
592 # Upstream flow 1 for untagged case
593 stub.UpdateLogicalDeviceFlowTable(FlowTableUpdate(
594 id=logical_device_id,
595 flow_mod=mk_simple_flow_mod(
596 priority=500,
597 match_fields=[in_port(uni_port_no), vlan_vid(0)],
598 actions=[push_vlan(0x8100), set_field(vlan_vid(4096 + c_vid))],
599 next_table_id=1
600 )
601 ))
602 # Upstream flow 2 for s-tag
603 stub.UpdateLogicalDeviceFlowTable(FlowTableUpdate(
604 id=logical_device_id,
605 flow_mod=mk_simple_flow_mod(
606 priority=500,
607 table_id=1,
608 match_fields=[in_port(uni_port_no), vlan_vid(4096 + c_vid)],
609 actions=[
610 push_vlan(0x8100),
611 set_field(vlan_vid(4096 + 1000)),
612 output(nni_port_no)
613 ]
614 )
615 ))
Zsolt Haraszti85f12852016-12-24 08:30:58 -0800616
617 # Push a few multicast flows
Zsolt Harasztib9a5f752017-02-11 06:07:08 -0800618 # 1st with one bucket for our uni 0
Zsolt Haraszti85f12852016-12-24 08:30:58 -0800619 stub.UpdateLogicalDeviceFlowGroupTable(FlowGroupTableUpdate(
620 id=logical_device_id,
621 group_mod=mk_multicast_group_mod(
622 group_id=1,
623 buckets=[
Zsolt Harasztib9a5f752017-02-11 06:07:08 -0800624 ofp.ofp_bucket(actions=[
625 pop_vlan(),
626 output(unis[0][0])
627 ])
Zsolt Haraszti85f12852016-12-24 08:30:58 -0800628 ]
629 )
630 ))
631 stub.UpdateLogicalDeviceFlowTable(FlowTableUpdate(
632 id=logical_device_id,
633 flow_mod=mk_simple_flow_mod(
634 priority=1000,
635 match_fields=[
636 in_port(nni_port_no),
637 eth_type(0x800),
638 vlan_vid(4096 + 140),
639 ipv4_dst(0xe4010101)
640 ],
641 actions=[group(1)]
642 )
643 ))
Zsolt Harasztib9a5f752017-02-11 06:07:08 -0800644
645 # 2nd with one bucket for uni 0 and 1
Zsolt Haraszti85f12852016-12-24 08:30:58 -0800646 stub.UpdateLogicalDeviceFlowGroupTable(FlowGroupTableUpdate(
647 id=logical_device_id,
648 group_mod=mk_multicast_group_mod(
649 group_id=2,
650 buckets=[
Nathan Knuth6b7b6ff2017-02-12 03:30:48 -0800651 ofp.ofp_bucket(actions=[pop_vlan(), output(unis[0][0])])
652# ofp.ofp_bucket(actions=[pop_vlan(), output(unis[1][0])])
Zsolt Haraszti85f12852016-12-24 08:30:58 -0800653 ]
654 )
655 ))
656 stub.UpdateLogicalDeviceFlowTable(FlowTableUpdate(
657 id=logical_device_id,
658 flow_mod=mk_simple_flow_mod(
659 priority=1000,
660 match_fields=[
661 in_port(nni_port_no),
662 eth_type(0x800),
663 vlan_vid(4096 + 140),
664 ipv4_dst(0xe4020202)
665 ],
666 actions=[group(2)]
667 )
668 ))
Zsolt Harasztib9a5f752017-02-11 06:07:08 -0800669
Zsolt Haraszti85f12852016-12-24 08:30:58 -0800670 # 3rd with empty bucket
671 stub.UpdateLogicalDeviceFlowGroupTable(FlowGroupTableUpdate(
672 id=logical_device_id,
673 group_mod=mk_multicast_group_mod(
674 group_id=3,
675 buckets=[]
676 )
677 ))
678 stub.UpdateLogicalDeviceFlowTable(FlowTableUpdate(
679 id=logical_device_id,
680 flow_mod=mk_simple_flow_mod(
681 priority=1000,
682 match_fields=[
683 in_port(nni_port_no),
684 eth_type(0x800),
685 vlan_vid(4096 + 140),
686 ipv4_dst(0xe4030303)
687 ],
688 actions=[group(3)]
689 )
690 ))
691
692 self.poutput('success')
693
694 complete_install_all_sample_flows = VolthaCli.complete_logical_device
695
Nathan Knuth5f4163e2017-01-11 18:21:10 -0600696 def do_install_dhcp_flows(self, line):
697 """
698 Install all dhcp flows that are representative of the virtualized access
699 scenario in a PON network.
700 """
701 logical_device_id = line or self.default_logical_device_id
702
703 # gather NNI and UNI port IDs
Zsolt Harasztib9a5f752017-02-11 06:07:08 -0800704 nni_port_no, unis = self.get_logical_ports(logical_device_id)
Nathan Knuth5f4163e2017-01-11 18:21:10 -0600705
706 # construct and push flow rules
707 stub = voltha_pb2.VolthaLocalServiceStub(self.get_channel())
708
709 # Controller-bound flows
Zsolt Harasztib9a5f752017-02-11 06:07:08 -0800710 for uni_port_no, _ in unis:
711 stub.UpdateLogicalDeviceFlowTable(FlowTableUpdate(
712 id=logical_device_id,
713 flow_mod=mk_simple_flow_mod(
714 priority=1000,
715 match_fields=[
716 in_port(uni_port_no),
717 eth_type(0x800),
718 ip_proto(17),
719 udp_dst(67)
720 ],
721 actions=[output(ofp.OFPP_CONTROLLER)]
722 )
723 ))
Nathan Knuth5f4163e2017-01-11 18:21:10 -0600724
725 self.poutput('success')
726
727 complete_install_dhcp_flows = VolthaCli.complete_logical_device
728
Zsolt Haraszti85f12852016-12-24 08:30:58 -0800729 def do_delete_all_flows(self, line):
730 """
731 Remove all flows and flow groups from given logical device
732 """
733 logical_device_id = line or self.default_logical_device_id
734 stub = voltha_pb2.VolthaLocalServiceStub(self.get_channel())
735 stub.UpdateLogicalDeviceFlowTable(FlowTableUpdate(
736 id=logical_device_id,
737 flow_mod=ofp.ofp_flow_mod(
738 command=ofp.OFPFC_DELETE,
739 table_id=ofp.OFPTT_ALL,
740 cookie_mask=0,
741 out_port=ofp.OFPP_ANY,
742 out_group=ofp.OFPG_ANY
743 )
744 ))
745 stub.UpdateLogicalDeviceFlowGroupTable(FlowGroupTableUpdate(
746 id=logical_device_id,
747 group_mod=ofp.ofp_group_mod(
748 command=ofp.OFPGC_DELETE,
749 group_id=ofp.OFPG_ALL
750 )
751 ))
752 self.poutput('success')
753
754 complete_delete_all_flows = VolthaCli.complete_logical_device
755
Zsolt Haraszti80175202016-12-24 00:17:51 -0800756 def do_send_simulated_upstream_eapol(self, line):
Zsolt Harasztid036b7e2016-12-23 15:36:01 -0800757 """
758 Send an EAPOL upstream from a simulated OLT
759 """
Zsolt Haraszti80175202016-12-24 00:17:51 -0800760 device_id = line or self.default_device_id
Zsolt Harasztid036b7e2016-12-23 15:36:01 -0800761 requests.get('http://{}/devices/{}/test_eapol_in'.format(
762 self.voltha_sim_rest, device_id
763 ))
764
Zsolt Haraszti80175202016-12-24 00:17:51 -0800765 complete_send_simulated_upstream_eapol = VolthaCli.complete_device
766
767 def do_inject_eapol_start(self, line):
Zsolt Harasztid036b7e2016-12-23 15:36:01 -0800768 """
769 Send out an an EAPOL start message into the given Unix interface
770 """
771 pass
Zsolt Harasztia133a452016-12-22 01:26:57 -0800772
773
774if __name__ == '__main__':
Zsolt Haraszti9b485fb2016-12-26 23:11:15 -0800775
776 parser = argparse.ArgumentParser()
777
778 _help = '<hostname>:<port> to consul agent (default: %s)' % defs['consul']
779 parser.add_argument(
780 '-C', '--consul', action='store', default=defs['consul'], help=_help)
781
782 _help = 'Lookup Voltha endpoints based on service entries in Consul'
783 parser.add_argument(
784 '-L', '--lookup', action='store_true', help=_help)
785
786 _help = '<hostname>:<port> of Voltha gRPC service (default={})'.format(
787 defs['voltha_grpc_endpoint'])
788 parser.add_argument('-g', '--grpc-endpoint', action='store',
789 default=defs['voltha_grpc_endpoint'], help=_help)
790
791 _help = '<hostname>:<port> of Voltha simulated adapter backend for ' \
792 'testing (default={})'.format(
793 defs['voltha_sim_rest_endpoint'])
794 parser.add_argument('-s', '--sim-rest-endpoint', action='store',
795 default=defs['voltha_sim_rest_endpoint'], help=_help)
796
797 args = parser.parse_args()
798
799 if args.lookup:
800 host = args.consul.split(':')[0].strip()
801 port = int(args.consul.split(':')[1].strip())
802 consul = Consul(host=host, port=port)
803
804 _, services = consul.catalog.service('voltha-grpc')
805 if not services:
806 print('No voltha-grpc service registered in consul; exiting')
807 sys.exit(1)
808 args.grpc_endpoint = '{}:{}'.format(services[0]['ServiceAddress'],
809 services[0]['ServicePort'])
810
811 _, services = consul.catalog.service('voltha-sim-rest')
812 if not services:
813 print('No voltha-sim-rest service registered in consul; exiting')
814 sys.exit(1)
815 args.sim_rest_endpoint = '{}:{}'.format(services[0]['ServiceAddress'],
816 services[0]['ServicePort'])
817
818 c = VolthaCli(args.grpc_endpoint, args.sim_rest_endpoint)
Zsolt Haraszti80175202016-12-24 00:17:51 -0800819 c.poutput(banner)
Zsolt Harasztia133a452016-12-22 01:26:57 -0800820 c.load_history()
821 c.cmdloop()
822 c.save_history()