blob: 3d9de586d1e98ffef88d499470f9081dc4cc1fe2 [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
287 self.poutput('activating {}'.format(device_id))
Zsolt Harasztid036b7e2016-12-23 15:36:01 -0800288 stub = voltha_pb2.VolthaLocalServiceStub(self.get_channel())
Khen Nursimulud068d812017-03-06 11:44:18 -0500289 stub.EnableDevice(voltha_pb2.ID(id=device_id))
Zsolt Harasztia133a452016-12-22 01:26:57 -0800290
Zsolt Harasztid036b7e2016-12-23 15:36:01 -0800291 while True:
292 device = stub.GetDevice(voltha_pb2.ID(id=device_id))
Khen Nursimulud068d812017-03-06 11:44:18 -0500293 # If this is an OLT then acquire logical device id
Zsolt Harasztid036b7e2016-12-23 15:36:01 -0800294 if device.oper_status == voltha_pb2.OperStatus.ACTIVE:
Khen Nursimulud068d812017-03-06 11:44:18 -0500295 if device.type.endswith('_olt'):
296 assert device.parent_id
297 self.default_logical_device_id = device.parent_id
298 self.poutput('success (logical device id = {})'.format(
299 self.default_logical_device_id))
300 else:
301 self.poutput('success (device id = {})'.format(device.id))
Zsolt Harasztid036b7e2016-12-23 15:36:01 -0800302 break
Khen Nursimulud068d812017-03-06 11:44:18 -0500303 self.poutput('waiting for device to be enabled...')
Zsolt Haraszti80175202016-12-24 00:17:51 -0800304 sleep(.5)
Zsolt Harasztid036b7e2016-12-23 15:36:01 -0800305
Zsolt Haraszti50cae7d2017-01-08 22:27:07 -0800306 complete_activate_olt = complete_device
307
Khen Nursimulud068d812017-03-06 11:44:18 -0500308 def do_reboot(self, line):
309 """
310 Rebooting a device. ID of the device needs to be provided
311 """
312 device_id = line or self.default_device_id
313 self.poutput('rebooting {}'.format(device_id))
314 stub = voltha_pb2.VolthaLocalServiceStub(self.get_channel())
315 stub.RebootDevice(voltha_pb2.ID(id=device_id))
316 self.poutput('rebooted {}'.format(device_id))
317
318 def do_delete(self, line):
319 """
320 Deleting a device. ID of the device needs to be provided
321 """
322 device_id = line or self.default_device_id
323 self.poutput('deleting {}'.format(device_id))
324 stub = voltha_pb2.VolthaLocalServiceStub(self.get_channel())
325 stub.DeleteDevice(voltha_pb2.ID(id=device_id))
326 self.poutput('deleted {}'.format(device_id))
327
328 def do_disable(self, line):
329 """
330 Disable a device. ID of the device needs to be provided
331 """
332 device_id = line
333 self.poutput('disabling {}'.format(device_id))
334 stub = voltha_pb2.VolthaLocalServiceStub(self.get_channel())
335 stub.DisableDevice(voltha_pb2.ID(id=device_id))
336
337 # Do device query and verify that the device admin status is
338 # DISABLED and Operational Status is unknown
339 device = stub.GetDevice(voltha_pb2.ID(id=device_id))
340 if device.oper_status == voltha_pb2.OperStatus.UNKNOWN and \
341 device.admin_state == voltha_pb2.AdminState.DISABLED:
342 self.poutput('disabled successfully {}'.format(device_id))
343 else:
344 self.poutput('disabling failed {}. Admin State:{} '
345 'Operation State: {}'.format(device_id,
346 device.admin_state,
347 device.oper_status))
348
Zsolt Haraszti50cae7d2017-01-08 22:27:07 -0800349 def do_test(self, line):
350 """Enter test mode, which makes a bunch on new commands available"""
351 sub = TestCli(self.history, self.get_channel, self.voltha_grpc,
352 self.voltha_sim_rest)
353 sub.cmdloop()
354
355
356class TestCli(VolthaCli):
357
358 def __init__(self, history, get_channel, voltha_grpc, voltha_sim_rest):
359 VolthaCli.__init__(self, voltha_grpc, voltha_sim_rest)
360 self.history = history
361 self.get_channel = get_channel
362 self.prompt = '(' + self.colorize(self.colorize('test', 'cyan'),
363 'bold') + ') '
364
365 def get_device(self, device_id, depth=0):
366 stub = voltha_pb2.VolthaLocalServiceStub(self.get_channel())
367 res = stub.GetDevice(voltha_pb2.ID(id=device_id),
368 metadata=(('get-depth', str(depth)), ))
369 return res
Zsolt Haraszti80175202016-12-24 00:17:51 -0800370
371 def do_arrive_onus(self, line):
Zsolt Harasztid036b7e2016-12-23 15:36:01 -0800372 """
Zsolt Haraszti85f12852016-12-24 08:30:58 -0800373 Simulate the arrival of ONUs (available only on simulated_olt)
Zsolt Harasztid036b7e2016-12-23 15:36:01 -0800374 """
Zsolt Haraszti80175202016-12-24 00:17:51 -0800375 device_id = line or self.default_device_id
Zsolt Haraszti85f12852016-12-24 08:30:58 -0800376
377 # verify that device is of type simulated_olt
378 device = self.get_device(device_id)
379 assert device.type == 'simulated_olt', (
380 'Cannot use it on this device type (only on simulated_olt type)')
381
Zsolt Harasztid036b7e2016-12-23 15:36:01 -0800382 requests.get('http://{}/devices/{}/detect_onus'.format(
383 self.voltha_sim_rest, device_id
384 ))
385
Zsolt Haraszti80175202016-12-24 00:17:51 -0800386 complete_arrive_onus = VolthaCli.complete_device
387
Zsolt Haraszti85f12852016-12-24 08:30:58 -0800388 def get_logical_ports(self, logical_device_id):
389 """
390 Return the NNI port number and the first usable UNI port of logical
391 device, and the vlan associated with the latter.
392 """
393 stub = voltha_pb2.VolthaLocalServiceStub(self.get_channel())
394 ports = stub.ListLogicalDevicePorts(
395 voltha_pb2.ID(id=logical_device_id)).items
Zsolt Harasztib9a5f752017-02-11 06:07:08 -0800396 nni = None
397 unis = []
Zsolt Haraszti85f12852016-12-24 08:30:58 -0800398 for port in ports:
Zsolt Harasztib9a5f752017-02-11 06:07:08 -0800399 if port.root_port:
400 assert nni is None, "There shall be only one root port"
Zsolt Haraszti85f12852016-12-24 08:30:58 -0800401 nni = port.ofp_port.port_no
Zsolt Harasztib9a5f752017-02-11 06:07:08 -0800402 else:
Zsolt Haraszti85f12852016-12-24 08:30:58 -0800403 uni = port.ofp_port.port_no
404 uni_device = self.get_device(port.device_id)
405 vlan = uni_device.vlan
Zsolt Harasztib9a5f752017-02-11 06:07:08 -0800406 unis.append((uni, vlan))
407
408 assert nni is not None, "No NNI port found"
409 assert unis, "Not a single UNI?"
410
411 return nni, unis
Zsolt Haraszti85f12852016-12-24 08:30:58 -0800412
Zsolt Haraszti80175202016-12-24 00:17:51 -0800413 def do_install_eapol_flow(self, line):
Zsolt Harasztid036b7e2016-12-23 15:36:01 -0800414 """
415 Install an EAPOL flow on the given logical device. If device is not
416 given, it will be applied to logical device of the last pre-provisioned
417 OLT device.
418 """
Zsolt Haraszti80175202016-12-24 00:17:51 -0800419 logical_device_id = line or self.default_logical_device_id
Zsolt Haraszti85f12852016-12-24 08:30:58 -0800420
421 # gather NNI and UNI port IDs
Zsolt Harasztib9a5f752017-02-11 06:07:08 -0800422 nni_port_no, unis = self.get_logical_ports(logical_device_id)
Zsolt Haraszti85f12852016-12-24 08:30:58 -0800423
424 # construct and push flow rule
Zsolt Harasztid036b7e2016-12-23 15:36:01 -0800425 stub = voltha_pb2.VolthaLocalServiceStub(self.get_channel())
Zsolt Harasztib9a5f752017-02-11 06:07:08 -0800426 for uni_port_no, _ in unis:
427 update = FlowTableUpdate(
428 id=logical_device_id,
429 flow_mod=mk_simple_flow_mod(
430 priority=2000,
431 match_fields=[in_port(uni_port_no), eth_type(0x888e)],
432 actions=[
433 # push_vlan(0x8100),
434 # set_field(vlan_vid(4096 + 4000)),
435 output(ofp.OFPP_CONTROLLER)
436 ]
437 )
438 )
439 res = stub.UpdateLogicalDeviceFlowTable(update)
440 self.poutput('success for uni {} ({})'.format(uni_port_no, res))
Zsolt Harasztid036b7e2016-12-23 15:36:01 -0800441
Zsolt Haraszti80175202016-12-24 00:17:51 -0800442 complete_install_eapol_flow = VolthaCli.complete_logical_device
443
Zsolt Haraszti85f12852016-12-24 08:30:58 -0800444 def do_install_all_controller_bound_flows(self, line):
445 """
446 Install all flow rules for controller bound flows, including EAPOL,
447 IGMP and DHCP. If device is not given, it will be applied to logical
448 device of the last pre-provisioned OLT device.
449 """
450 logical_device_id = line or self.default_logical_device_id
451
452 # gather NNI and UNI port IDs
Zsolt Harasztib9a5f752017-02-11 06:07:08 -0800453 nni_port_no, unis = self.get_logical_ports(logical_device_id)
Zsolt Haraszti85f12852016-12-24 08:30:58 -0800454
455 # construct and push flow rules
456 stub = voltha_pb2.VolthaLocalServiceStub(self.get_channel())
457
Zsolt Harasztib9a5f752017-02-11 06:07:08 -0800458 for uni_port_no, _ in unis:
459 stub.UpdateLogicalDeviceFlowTable(FlowTableUpdate(
460 id=logical_device_id,
461 flow_mod=mk_simple_flow_mod(
462 priority=2000,
463 match_fields=[
464 in_port(uni_port_no),
465 eth_type(0x888e)
466 ],
467 actions=[output(ofp.OFPP_CONTROLLER)]
468 )
469 ))
470 stub.UpdateLogicalDeviceFlowTable(FlowTableUpdate(
471 id=logical_device_id,
472 flow_mod=mk_simple_flow_mod(
473 priority=1000,
474 match_fields=[
475 in_port(uni_port_no),
476 eth_type(0x800),
477 ip_proto(2)
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(17),
490 udp_dst(67)
491 ],
492 actions=[output(ofp.OFPP_CONTROLLER)]
493 )
494 ))
Zsolt Haraszti85f12852016-12-24 08:30:58 -0800495 self.poutput('success')
496
497 complete_install_all_controller_bound_flows = \
498 VolthaCli.complete_logical_device
499
500 def do_install_all_sample_flows(self, line):
501 """
502 Install all flows that are representative of the virtualized access
503 scenario in a PON network.
504 """
505 logical_device_id = line or self.default_logical_device_id
506
507 # gather NNI and UNI port IDs
Zsolt Harasztib9a5f752017-02-11 06:07:08 -0800508 nni_port_no, unis = self.get_logical_ports(logical_device_id)
Zsolt Haraszti85f12852016-12-24 08:30:58 -0800509
510 # construct and push flow rules
511 stub = voltha_pb2.VolthaLocalServiceStub(self.get_channel())
512
Zsolt Harasztib9a5f752017-02-11 06:07:08 -0800513 for uni_port_no, c_vid in unis:
Zsolt Haraszti85f12852016-12-24 08:30:58 -0800514
Zsolt Harasztib9a5f752017-02-11 06:07:08 -0800515 # Controller-bound flows
516 stub.UpdateLogicalDeviceFlowTable(FlowTableUpdate(
517 id=logical_device_id,
518 flow_mod=mk_simple_flow_mod(
519 priority=2000,
520 match_fields=[in_port(uni_port_no), eth_type(0x888e)],
521 actions=[
522 # push_vlan(0x8100),
523 # set_field(vlan_vid(4096 + 4000)),
524 output(ofp.OFPP_CONTROLLER)
525 ]
526 )
527 ))
528 stub.UpdateLogicalDeviceFlowTable(FlowTableUpdate(
529 id=logical_device_id,
530 flow_mod=mk_simple_flow_mod(
531 priority=1000,
532 match_fields=[eth_type(0x800), ip_proto(2)],
533 actions=[output(ofp.OFPP_CONTROLLER)]
534 )
535 ))
536 stub.UpdateLogicalDeviceFlowTable(FlowTableUpdate(
537 id=logical_device_id,
538 flow_mod=mk_simple_flow_mod(
539 priority=1000,
540 match_fields=[eth_type(0x800), ip_proto(17), udp_dst(67)],
541 actions=[output(ofp.OFPP_CONTROLLER)]
542 )
543 ))
544
545 # Unicast flows:
546 # Downstream flow 1
547 stub.UpdateLogicalDeviceFlowTable(FlowTableUpdate(
548 id=logical_device_id,
549 flow_mod=mk_simple_flow_mod(
550 priority=500,
551 match_fields=[
552 in_port(nni_port_no),
553 vlan_vid(4096 + 1000),
554 metadata(c_vid) # here to mimic an ONOS artifact
555 ],
556 actions=[pop_vlan()],
557 next_table_id=1
558 )
559 ))
560 # Downstream flow 2
561 stub.UpdateLogicalDeviceFlowTable(FlowTableUpdate(
562 id=logical_device_id,
563 flow_mod=mk_simple_flow_mod(
564 priority=500,
565 table_id=1,
566 match_fields=[in_port(nni_port_no), vlan_vid(4096 + c_vid)],
567 actions=[set_field(vlan_vid(4096 + 0)), output(uni_port_no)]
568 )
569 ))
570 # Upstream flow 1 for 0-tagged case
571 stub.UpdateLogicalDeviceFlowTable(FlowTableUpdate(
572 id=logical_device_id,
573 flow_mod=mk_simple_flow_mod(
574 priority=500,
575 match_fields=[in_port(uni_port_no), vlan_vid(4096 + 0)],
576 actions=[set_field(vlan_vid(4096 + c_vid))],
577 next_table_id=1
578 )
579 ))
580 # Upstream flow 1 for untagged case
581 stub.UpdateLogicalDeviceFlowTable(FlowTableUpdate(
582 id=logical_device_id,
583 flow_mod=mk_simple_flow_mod(
584 priority=500,
585 match_fields=[in_port(uni_port_no), vlan_vid(0)],
586 actions=[push_vlan(0x8100), set_field(vlan_vid(4096 + c_vid))],
587 next_table_id=1
588 )
589 ))
590 # Upstream flow 2 for s-tag
591 stub.UpdateLogicalDeviceFlowTable(FlowTableUpdate(
592 id=logical_device_id,
593 flow_mod=mk_simple_flow_mod(
594 priority=500,
595 table_id=1,
596 match_fields=[in_port(uni_port_no), vlan_vid(4096 + c_vid)],
597 actions=[
598 push_vlan(0x8100),
599 set_field(vlan_vid(4096 + 1000)),
600 output(nni_port_no)
601 ]
602 )
603 ))
Zsolt Haraszti85f12852016-12-24 08:30:58 -0800604
605 # Push a few multicast flows
Zsolt Harasztib9a5f752017-02-11 06:07:08 -0800606 # 1st with one bucket for our uni 0
Zsolt Haraszti85f12852016-12-24 08:30:58 -0800607 stub.UpdateLogicalDeviceFlowGroupTable(FlowGroupTableUpdate(
608 id=logical_device_id,
609 group_mod=mk_multicast_group_mod(
610 group_id=1,
611 buckets=[
Zsolt Harasztib9a5f752017-02-11 06:07:08 -0800612 ofp.ofp_bucket(actions=[
613 pop_vlan(),
614 output(unis[0][0])
615 ])
Zsolt Haraszti85f12852016-12-24 08:30:58 -0800616 ]
617 )
618 ))
619 stub.UpdateLogicalDeviceFlowTable(FlowTableUpdate(
620 id=logical_device_id,
621 flow_mod=mk_simple_flow_mod(
622 priority=1000,
623 match_fields=[
624 in_port(nni_port_no),
625 eth_type(0x800),
626 vlan_vid(4096 + 140),
627 ipv4_dst(0xe4010101)
628 ],
629 actions=[group(1)]
630 )
631 ))
Zsolt Harasztib9a5f752017-02-11 06:07:08 -0800632
633 # 2nd with one bucket for uni 0 and 1
Zsolt Haraszti85f12852016-12-24 08:30:58 -0800634 stub.UpdateLogicalDeviceFlowGroupTable(FlowGroupTableUpdate(
635 id=logical_device_id,
636 group_mod=mk_multicast_group_mod(
637 group_id=2,
638 buckets=[
Nathan Knuth6b7b6ff2017-02-12 03:30:48 -0800639 ofp.ofp_bucket(actions=[pop_vlan(), output(unis[0][0])])
640# ofp.ofp_bucket(actions=[pop_vlan(), output(unis[1][0])])
Zsolt Haraszti85f12852016-12-24 08:30:58 -0800641 ]
642 )
643 ))
644 stub.UpdateLogicalDeviceFlowTable(FlowTableUpdate(
645 id=logical_device_id,
646 flow_mod=mk_simple_flow_mod(
647 priority=1000,
648 match_fields=[
649 in_port(nni_port_no),
650 eth_type(0x800),
651 vlan_vid(4096 + 140),
652 ipv4_dst(0xe4020202)
653 ],
654 actions=[group(2)]
655 )
656 ))
Zsolt Harasztib9a5f752017-02-11 06:07:08 -0800657
Zsolt Haraszti85f12852016-12-24 08:30:58 -0800658 # 3rd with empty bucket
659 stub.UpdateLogicalDeviceFlowGroupTable(FlowGroupTableUpdate(
660 id=logical_device_id,
661 group_mod=mk_multicast_group_mod(
662 group_id=3,
663 buckets=[]
664 )
665 ))
666 stub.UpdateLogicalDeviceFlowTable(FlowTableUpdate(
667 id=logical_device_id,
668 flow_mod=mk_simple_flow_mod(
669 priority=1000,
670 match_fields=[
671 in_port(nni_port_no),
672 eth_type(0x800),
673 vlan_vid(4096 + 140),
674 ipv4_dst(0xe4030303)
675 ],
676 actions=[group(3)]
677 )
678 ))
679
680 self.poutput('success')
681
682 complete_install_all_sample_flows = VolthaCli.complete_logical_device
683
Nathan Knuth5f4163e2017-01-11 18:21:10 -0600684 def do_install_dhcp_flows(self, line):
685 """
686 Install all dhcp flows that are representative of the virtualized access
687 scenario in a PON network.
688 """
689 logical_device_id = line or self.default_logical_device_id
690
691 # gather NNI and UNI port IDs
Zsolt Harasztib9a5f752017-02-11 06:07:08 -0800692 nni_port_no, unis = self.get_logical_ports(logical_device_id)
Nathan Knuth5f4163e2017-01-11 18:21:10 -0600693
694 # construct and push flow rules
695 stub = voltha_pb2.VolthaLocalServiceStub(self.get_channel())
696
697 # Controller-bound flows
Zsolt Harasztib9a5f752017-02-11 06:07:08 -0800698 for uni_port_no, _ in unis:
699 stub.UpdateLogicalDeviceFlowTable(FlowTableUpdate(
700 id=logical_device_id,
701 flow_mod=mk_simple_flow_mod(
702 priority=1000,
703 match_fields=[
704 in_port(uni_port_no),
705 eth_type(0x800),
706 ip_proto(17),
707 udp_dst(67)
708 ],
709 actions=[output(ofp.OFPP_CONTROLLER)]
710 )
711 ))
Nathan Knuth5f4163e2017-01-11 18:21:10 -0600712
713 self.poutput('success')
714
715 complete_install_dhcp_flows = VolthaCli.complete_logical_device
716
Zsolt Haraszti85f12852016-12-24 08:30:58 -0800717 def do_delete_all_flows(self, line):
718 """
719 Remove all flows and flow groups from given logical device
720 """
721 logical_device_id = line or self.default_logical_device_id
722 stub = voltha_pb2.VolthaLocalServiceStub(self.get_channel())
723 stub.UpdateLogicalDeviceFlowTable(FlowTableUpdate(
724 id=logical_device_id,
725 flow_mod=ofp.ofp_flow_mod(
726 command=ofp.OFPFC_DELETE,
727 table_id=ofp.OFPTT_ALL,
728 cookie_mask=0,
729 out_port=ofp.OFPP_ANY,
730 out_group=ofp.OFPG_ANY
731 )
732 ))
733 stub.UpdateLogicalDeviceFlowGroupTable(FlowGroupTableUpdate(
734 id=logical_device_id,
735 group_mod=ofp.ofp_group_mod(
736 command=ofp.OFPGC_DELETE,
737 group_id=ofp.OFPG_ALL
738 )
739 ))
740 self.poutput('success')
741
742 complete_delete_all_flows = VolthaCli.complete_logical_device
743
Zsolt Haraszti80175202016-12-24 00:17:51 -0800744 def do_send_simulated_upstream_eapol(self, line):
Zsolt Harasztid036b7e2016-12-23 15:36:01 -0800745 """
746 Send an EAPOL upstream from a simulated OLT
747 """
Zsolt Haraszti80175202016-12-24 00:17:51 -0800748 device_id = line or self.default_device_id
Zsolt Harasztid036b7e2016-12-23 15:36:01 -0800749 requests.get('http://{}/devices/{}/test_eapol_in'.format(
750 self.voltha_sim_rest, device_id
751 ))
752
Zsolt Haraszti80175202016-12-24 00:17:51 -0800753 complete_send_simulated_upstream_eapol = VolthaCli.complete_device
754
755 def do_inject_eapol_start(self, line):
Zsolt Harasztid036b7e2016-12-23 15:36:01 -0800756 """
757 Send out an an EAPOL start message into the given Unix interface
758 """
759 pass
Zsolt Harasztia133a452016-12-22 01:26:57 -0800760
761
762if __name__ == '__main__':
Zsolt Haraszti9b485fb2016-12-26 23:11:15 -0800763
764 parser = argparse.ArgumentParser()
765
766 _help = '<hostname>:<port> to consul agent (default: %s)' % defs['consul']
767 parser.add_argument(
768 '-C', '--consul', action='store', default=defs['consul'], help=_help)
769
770 _help = 'Lookup Voltha endpoints based on service entries in Consul'
771 parser.add_argument(
772 '-L', '--lookup', action='store_true', help=_help)
773
774 _help = '<hostname>:<port> of Voltha gRPC service (default={})'.format(
775 defs['voltha_grpc_endpoint'])
776 parser.add_argument('-g', '--grpc-endpoint', action='store',
777 default=defs['voltha_grpc_endpoint'], help=_help)
778
779 _help = '<hostname>:<port> of Voltha simulated adapter backend for ' \
780 'testing (default={})'.format(
781 defs['voltha_sim_rest_endpoint'])
782 parser.add_argument('-s', '--sim-rest-endpoint', action='store',
783 default=defs['voltha_sim_rest_endpoint'], help=_help)
784
785 args = parser.parse_args()
786
787 if args.lookup:
788 host = args.consul.split(':')[0].strip()
789 port = int(args.consul.split(':')[1].strip())
790 consul = Consul(host=host, port=port)
791
792 _, services = consul.catalog.service('voltha-grpc')
793 if not services:
794 print('No voltha-grpc service registered in consul; exiting')
795 sys.exit(1)
796 args.grpc_endpoint = '{}:{}'.format(services[0]['ServiceAddress'],
797 services[0]['ServicePort'])
798
799 _, services = consul.catalog.service('voltha-sim-rest')
800 if not services:
801 print('No voltha-sim-rest service registered in consul; exiting')
802 sys.exit(1)
803 args.sim_rest_endpoint = '{}:{}'.format(services[0]['ServiceAddress'],
804 services[0]['ServicePort'])
805
806 c = VolthaCli(args.grpc_endpoint, args.sim_rest_endpoint)
Zsolt Haraszti80175202016-12-24 00:17:51 -0800807 c.poutput(banner)
Zsolt Harasztia133a452016-12-22 01:26:57 -0800808 c.load_history()
809 c.cmdloop()
810 c.save_history()