blob: f5ba38bfd391bff507263c5510e4add4d77ea765 [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
Zsolt Haraszti80175202016-12-24 00:17:51 -0800281 def do_activate_olt(self, line):
Zsolt Harasztid036b7e2016-12-23 15:36:01 -0800282 """
283 Activate an OLT. If the <id> is not provided, it will be on the last
284 pre-provisioned OLT.
285 """
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())
289 stub.ActivateDevice(voltha_pb2.ID(id=device_id))
Zsolt Harasztia133a452016-12-22 01:26:57 -0800290
Zsolt Harasztid036b7e2016-12-23 15:36:01 -0800291 # try to acquire logical device id
292 while True:
293 device = stub.GetDevice(voltha_pb2.ID(id=device_id))
294 if device.oper_status == voltha_pb2.OperStatus.ACTIVE:
295 assert device.parent_id
296 self.default_logical_device_id = device.parent_id
297 break
Zsolt Haraszti80175202016-12-24 00:17:51 -0800298 self.poutput('waiting for device to be activated...')
299 sleep(.5)
300 self.poutput('success (logical device id = {})'.format(
301 self.default_logical_device_id))
Zsolt Harasztid036b7e2016-12-23 15:36:01 -0800302
Zsolt Haraszti50cae7d2017-01-08 22:27:07 -0800303 complete_activate_olt = complete_device
304
305 def do_test(self, line):
306 """Enter test mode, which makes a bunch on new commands available"""
307 sub = TestCli(self.history, self.get_channel, self.voltha_grpc,
308 self.voltha_sim_rest)
309 sub.cmdloop()
310
311
312class TestCli(VolthaCli):
313
314 def __init__(self, history, get_channel, voltha_grpc, voltha_sim_rest):
315 VolthaCli.__init__(self, voltha_grpc, voltha_sim_rest)
316 self.history = history
317 self.get_channel = get_channel
318 self.prompt = '(' + self.colorize(self.colorize('test', 'cyan'),
319 'bold') + ') '
320
321 def get_device(self, device_id, depth=0):
322 stub = voltha_pb2.VolthaLocalServiceStub(self.get_channel())
323 res = stub.GetDevice(voltha_pb2.ID(id=device_id),
324 metadata=(('get-depth', str(depth)), ))
325 return res
Zsolt Haraszti80175202016-12-24 00:17:51 -0800326
327 def do_arrive_onus(self, line):
Zsolt Harasztid036b7e2016-12-23 15:36:01 -0800328 """
Zsolt Haraszti85f12852016-12-24 08:30:58 -0800329 Simulate the arrival of ONUs (available only on simulated_olt)
Zsolt Harasztid036b7e2016-12-23 15:36:01 -0800330 """
Zsolt Haraszti80175202016-12-24 00:17:51 -0800331 device_id = line or self.default_device_id
Zsolt Haraszti85f12852016-12-24 08:30:58 -0800332
333 # verify that device is of type simulated_olt
334 device = self.get_device(device_id)
335 assert device.type == 'simulated_olt', (
336 'Cannot use it on this device type (only on simulated_olt type)')
337
Zsolt Harasztid036b7e2016-12-23 15:36:01 -0800338 requests.get('http://{}/devices/{}/detect_onus'.format(
339 self.voltha_sim_rest, device_id
340 ))
341
Zsolt Haraszti80175202016-12-24 00:17:51 -0800342 complete_arrive_onus = VolthaCli.complete_device
343
Zsolt Haraszti85f12852016-12-24 08:30:58 -0800344 def get_logical_ports(self, logical_device_id):
345 """
346 Return the NNI port number and the first usable UNI port of logical
347 device, and the vlan associated with the latter.
348 """
349 stub = voltha_pb2.VolthaLocalServiceStub(self.get_channel())
350 ports = stub.ListLogicalDevicePorts(
351 voltha_pb2.ID(id=logical_device_id)).items
Zsolt Harasztib9a5f752017-02-11 06:07:08 -0800352 nni = None
353 unis = []
Zsolt Haraszti85f12852016-12-24 08:30:58 -0800354 for port in ports:
Zsolt Harasztib9a5f752017-02-11 06:07:08 -0800355 if port.root_port:
356 assert nni is None, "There shall be only one root port"
Zsolt Haraszti85f12852016-12-24 08:30:58 -0800357 nni = port.ofp_port.port_no
Zsolt Harasztib9a5f752017-02-11 06:07:08 -0800358 else:
Zsolt Haraszti85f12852016-12-24 08:30:58 -0800359 uni = port.ofp_port.port_no
360 uni_device = self.get_device(port.device_id)
361 vlan = uni_device.vlan
Zsolt Harasztib9a5f752017-02-11 06:07:08 -0800362 unis.append((uni, vlan))
363
364 assert nni is not None, "No NNI port found"
365 assert unis, "Not a single UNI?"
366
367 return nni, unis
Zsolt Haraszti85f12852016-12-24 08:30:58 -0800368
Zsolt Haraszti80175202016-12-24 00:17:51 -0800369 def do_install_eapol_flow(self, line):
Zsolt Harasztid036b7e2016-12-23 15:36:01 -0800370 """
371 Install an EAPOL flow on the given logical device. If device is not
372 given, it will be applied to logical device of the last pre-provisioned
373 OLT device.
374 """
Zsolt Haraszti80175202016-12-24 00:17:51 -0800375 logical_device_id = line or self.default_logical_device_id
Zsolt Haraszti85f12852016-12-24 08:30:58 -0800376
377 # gather NNI and UNI port IDs
Zsolt Harasztib9a5f752017-02-11 06:07:08 -0800378 nni_port_no, unis = self.get_logical_ports(logical_device_id)
Zsolt Haraszti85f12852016-12-24 08:30:58 -0800379
380 # construct and push flow rule
Zsolt Harasztid036b7e2016-12-23 15:36:01 -0800381 stub = voltha_pb2.VolthaLocalServiceStub(self.get_channel())
Zsolt Harasztib9a5f752017-02-11 06:07:08 -0800382 for uni_port_no, _ in unis:
383 update = FlowTableUpdate(
384 id=logical_device_id,
385 flow_mod=mk_simple_flow_mod(
386 priority=2000,
387 match_fields=[in_port(uni_port_no), eth_type(0x888e)],
388 actions=[
389 # push_vlan(0x8100),
390 # set_field(vlan_vid(4096 + 4000)),
391 output(ofp.OFPP_CONTROLLER)
392 ]
393 )
394 )
395 res = stub.UpdateLogicalDeviceFlowTable(update)
396 self.poutput('success for uni {} ({})'.format(uni_port_no, res))
Zsolt Harasztid036b7e2016-12-23 15:36:01 -0800397
Zsolt Haraszti80175202016-12-24 00:17:51 -0800398 complete_install_eapol_flow = VolthaCli.complete_logical_device
399
Zsolt Haraszti85f12852016-12-24 08:30:58 -0800400 def do_install_all_controller_bound_flows(self, line):
401 """
402 Install all flow rules for controller bound flows, including EAPOL,
403 IGMP and DHCP. If device is not given, it will be applied to logical
404 device of the last pre-provisioned OLT device.
405 """
406 logical_device_id = line or self.default_logical_device_id
407
408 # gather NNI and UNI port IDs
Zsolt Harasztib9a5f752017-02-11 06:07:08 -0800409 nni_port_no, unis = self.get_logical_ports(logical_device_id)
Zsolt Haraszti85f12852016-12-24 08:30:58 -0800410
411 # construct and push flow rules
412 stub = voltha_pb2.VolthaLocalServiceStub(self.get_channel())
413
Zsolt Harasztib9a5f752017-02-11 06:07:08 -0800414 for uni_port_no, _ in unis:
415 stub.UpdateLogicalDeviceFlowTable(FlowTableUpdate(
416 id=logical_device_id,
417 flow_mod=mk_simple_flow_mod(
418 priority=2000,
419 match_fields=[
420 in_port(uni_port_no),
421 eth_type(0x888e)
422 ],
423 actions=[output(ofp.OFPP_CONTROLLER)]
424 )
425 ))
426 stub.UpdateLogicalDeviceFlowTable(FlowTableUpdate(
427 id=logical_device_id,
428 flow_mod=mk_simple_flow_mod(
429 priority=1000,
430 match_fields=[
431 in_port(uni_port_no),
432 eth_type(0x800),
433 ip_proto(2)
434 ],
435 actions=[output(ofp.OFPP_CONTROLLER)]
436 )
437 ))
438 stub.UpdateLogicalDeviceFlowTable(FlowTableUpdate(
439 id=logical_device_id,
440 flow_mod=mk_simple_flow_mod(
441 priority=1000,
442 match_fields=[
443 in_port(uni_port_no),
444 eth_type(0x800),
445 ip_proto(17),
446 udp_dst(67)
447 ],
448 actions=[output(ofp.OFPP_CONTROLLER)]
449 )
450 ))
Zsolt Haraszti85f12852016-12-24 08:30:58 -0800451 self.poutput('success')
452
453 complete_install_all_controller_bound_flows = \
454 VolthaCli.complete_logical_device
455
456 def do_install_all_sample_flows(self, line):
457 """
458 Install all flows that are representative of the virtualized access
459 scenario in a PON network.
460 """
461 logical_device_id = line or self.default_logical_device_id
462
463 # gather NNI and UNI port IDs
Zsolt Harasztib9a5f752017-02-11 06:07:08 -0800464 nni_port_no, unis = self.get_logical_ports(logical_device_id)
Zsolt Haraszti85f12852016-12-24 08:30:58 -0800465
466 # construct and push flow rules
467 stub = voltha_pb2.VolthaLocalServiceStub(self.get_channel())
468
Zsolt Harasztib9a5f752017-02-11 06:07:08 -0800469 for uni_port_no, c_vid in unis:
Zsolt Haraszti85f12852016-12-24 08:30:58 -0800470
Zsolt Harasztib9a5f752017-02-11 06:07:08 -0800471 # Controller-bound flows
472 stub.UpdateLogicalDeviceFlowTable(FlowTableUpdate(
473 id=logical_device_id,
474 flow_mod=mk_simple_flow_mod(
475 priority=2000,
476 match_fields=[in_port(uni_port_no), eth_type(0x888e)],
477 actions=[
478 # push_vlan(0x8100),
479 # set_field(vlan_vid(4096 + 4000)),
480 output(ofp.OFPP_CONTROLLER)
481 ]
482 )
483 ))
484 stub.UpdateLogicalDeviceFlowTable(FlowTableUpdate(
485 id=logical_device_id,
486 flow_mod=mk_simple_flow_mod(
487 priority=1000,
488 match_fields=[eth_type(0x800), ip_proto(2)],
489 actions=[output(ofp.OFPP_CONTROLLER)]
490 )
491 ))
492 stub.UpdateLogicalDeviceFlowTable(FlowTableUpdate(
493 id=logical_device_id,
494 flow_mod=mk_simple_flow_mod(
495 priority=1000,
496 match_fields=[eth_type(0x800), ip_proto(17), udp_dst(67)],
497 actions=[output(ofp.OFPP_CONTROLLER)]
498 )
499 ))
500
501 # Unicast flows:
502 # Downstream flow 1
503 stub.UpdateLogicalDeviceFlowTable(FlowTableUpdate(
504 id=logical_device_id,
505 flow_mod=mk_simple_flow_mod(
506 priority=500,
507 match_fields=[
508 in_port(nni_port_no),
509 vlan_vid(4096 + 1000),
510 metadata(c_vid) # here to mimic an ONOS artifact
511 ],
512 actions=[pop_vlan()],
513 next_table_id=1
514 )
515 ))
516 # Downstream flow 2
517 stub.UpdateLogicalDeviceFlowTable(FlowTableUpdate(
518 id=logical_device_id,
519 flow_mod=mk_simple_flow_mod(
520 priority=500,
521 table_id=1,
522 match_fields=[in_port(nni_port_no), vlan_vid(4096 + c_vid)],
523 actions=[set_field(vlan_vid(4096 + 0)), output(uni_port_no)]
524 )
525 ))
526 # Upstream flow 1 for 0-tagged case
527 stub.UpdateLogicalDeviceFlowTable(FlowTableUpdate(
528 id=logical_device_id,
529 flow_mod=mk_simple_flow_mod(
530 priority=500,
531 match_fields=[in_port(uni_port_no), vlan_vid(4096 + 0)],
532 actions=[set_field(vlan_vid(4096 + c_vid))],
533 next_table_id=1
534 )
535 ))
536 # Upstream flow 1 for untagged case
537 stub.UpdateLogicalDeviceFlowTable(FlowTableUpdate(
538 id=logical_device_id,
539 flow_mod=mk_simple_flow_mod(
540 priority=500,
541 match_fields=[in_port(uni_port_no), vlan_vid(0)],
542 actions=[push_vlan(0x8100), set_field(vlan_vid(4096 + c_vid))],
543 next_table_id=1
544 )
545 ))
546 # Upstream flow 2 for s-tag
547 stub.UpdateLogicalDeviceFlowTable(FlowTableUpdate(
548 id=logical_device_id,
549 flow_mod=mk_simple_flow_mod(
550 priority=500,
551 table_id=1,
552 match_fields=[in_port(uni_port_no), vlan_vid(4096 + c_vid)],
553 actions=[
554 push_vlan(0x8100),
555 set_field(vlan_vid(4096 + 1000)),
556 output(nni_port_no)
557 ]
558 )
559 ))
Zsolt Haraszti85f12852016-12-24 08:30:58 -0800560
561 # Push a few multicast flows
Zsolt Harasztib9a5f752017-02-11 06:07:08 -0800562 # 1st with one bucket for our uni 0
Zsolt Haraszti85f12852016-12-24 08:30:58 -0800563 stub.UpdateLogicalDeviceFlowGroupTable(FlowGroupTableUpdate(
564 id=logical_device_id,
565 group_mod=mk_multicast_group_mod(
566 group_id=1,
567 buckets=[
Zsolt Harasztib9a5f752017-02-11 06:07:08 -0800568 ofp.ofp_bucket(actions=[
569 pop_vlan(),
570 output(unis[0][0])
571 ])
Zsolt Haraszti85f12852016-12-24 08:30:58 -0800572 ]
573 )
574 ))
575 stub.UpdateLogicalDeviceFlowTable(FlowTableUpdate(
576 id=logical_device_id,
577 flow_mod=mk_simple_flow_mod(
578 priority=1000,
579 match_fields=[
580 in_port(nni_port_no),
581 eth_type(0x800),
582 vlan_vid(4096 + 140),
583 ipv4_dst(0xe4010101)
584 ],
585 actions=[group(1)]
586 )
587 ))
Zsolt Harasztib9a5f752017-02-11 06:07:08 -0800588
589 # 2nd with one bucket for uni 0 and 1
Zsolt Haraszti85f12852016-12-24 08:30:58 -0800590 stub.UpdateLogicalDeviceFlowGroupTable(FlowGroupTableUpdate(
591 id=logical_device_id,
592 group_mod=mk_multicast_group_mod(
593 group_id=2,
594 buckets=[
Nathan Knuth6b7b6ff2017-02-12 03:30:48 -0800595 ofp.ofp_bucket(actions=[pop_vlan(), output(unis[0][0])])
596# ofp.ofp_bucket(actions=[pop_vlan(), output(unis[1][0])])
Zsolt Haraszti85f12852016-12-24 08:30:58 -0800597 ]
598 )
599 ))
600 stub.UpdateLogicalDeviceFlowTable(FlowTableUpdate(
601 id=logical_device_id,
602 flow_mod=mk_simple_flow_mod(
603 priority=1000,
604 match_fields=[
605 in_port(nni_port_no),
606 eth_type(0x800),
607 vlan_vid(4096 + 140),
608 ipv4_dst(0xe4020202)
609 ],
610 actions=[group(2)]
611 )
612 ))
Zsolt Harasztib9a5f752017-02-11 06:07:08 -0800613
Zsolt Haraszti85f12852016-12-24 08:30:58 -0800614 # 3rd with empty bucket
615 stub.UpdateLogicalDeviceFlowGroupTable(FlowGroupTableUpdate(
616 id=logical_device_id,
617 group_mod=mk_multicast_group_mod(
618 group_id=3,
619 buckets=[]
620 )
621 ))
622 stub.UpdateLogicalDeviceFlowTable(FlowTableUpdate(
623 id=logical_device_id,
624 flow_mod=mk_simple_flow_mod(
625 priority=1000,
626 match_fields=[
627 in_port(nni_port_no),
628 eth_type(0x800),
629 vlan_vid(4096 + 140),
630 ipv4_dst(0xe4030303)
631 ],
632 actions=[group(3)]
633 )
634 ))
635
636 self.poutput('success')
637
638 complete_install_all_sample_flows = VolthaCli.complete_logical_device
639
Nathan Knuth5f4163e2017-01-11 18:21:10 -0600640 def do_install_dhcp_flows(self, line):
641 """
642 Install all dhcp flows that are representative of the virtualized access
643 scenario in a PON network.
644 """
645 logical_device_id = line or self.default_logical_device_id
646
647 # gather NNI and UNI port IDs
Zsolt Harasztib9a5f752017-02-11 06:07:08 -0800648 nni_port_no, unis = self.get_logical_ports(logical_device_id)
Nathan Knuth5f4163e2017-01-11 18:21:10 -0600649
650 # construct and push flow rules
651 stub = voltha_pb2.VolthaLocalServiceStub(self.get_channel())
652
653 # Controller-bound flows
Zsolt Harasztib9a5f752017-02-11 06:07:08 -0800654 for uni_port_no, _ in unis:
655 stub.UpdateLogicalDeviceFlowTable(FlowTableUpdate(
656 id=logical_device_id,
657 flow_mod=mk_simple_flow_mod(
658 priority=1000,
659 match_fields=[
660 in_port(uni_port_no),
661 eth_type(0x800),
662 ip_proto(17),
663 udp_dst(67)
664 ],
665 actions=[output(ofp.OFPP_CONTROLLER)]
666 )
667 ))
Nathan Knuth5f4163e2017-01-11 18:21:10 -0600668
669 self.poutput('success')
670
671 complete_install_dhcp_flows = VolthaCli.complete_logical_device
672
Zsolt Haraszti85f12852016-12-24 08:30:58 -0800673 def do_delete_all_flows(self, line):
674 """
675 Remove all flows and flow groups from given logical device
676 """
677 logical_device_id = line or self.default_logical_device_id
678 stub = voltha_pb2.VolthaLocalServiceStub(self.get_channel())
679 stub.UpdateLogicalDeviceFlowTable(FlowTableUpdate(
680 id=logical_device_id,
681 flow_mod=ofp.ofp_flow_mod(
682 command=ofp.OFPFC_DELETE,
683 table_id=ofp.OFPTT_ALL,
684 cookie_mask=0,
685 out_port=ofp.OFPP_ANY,
686 out_group=ofp.OFPG_ANY
687 )
688 ))
689 stub.UpdateLogicalDeviceFlowGroupTable(FlowGroupTableUpdate(
690 id=logical_device_id,
691 group_mod=ofp.ofp_group_mod(
692 command=ofp.OFPGC_DELETE,
693 group_id=ofp.OFPG_ALL
694 )
695 ))
696 self.poutput('success')
697
698 complete_delete_all_flows = VolthaCli.complete_logical_device
699
Zsolt Haraszti80175202016-12-24 00:17:51 -0800700 def do_send_simulated_upstream_eapol(self, line):
Zsolt Harasztid036b7e2016-12-23 15:36:01 -0800701 """
702 Send an EAPOL upstream from a simulated OLT
703 """
Zsolt Haraszti80175202016-12-24 00:17:51 -0800704 device_id = line or self.default_device_id
Zsolt Harasztid036b7e2016-12-23 15:36:01 -0800705 requests.get('http://{}/devices/{}/test_eapol_in'.format(
706 self.voltha_sim_rest, device_id
707 ))
708
Zsolt Haraszti80175202016-12-24 00:17:51 -0800709 complete_send_simulated_upstream_eapol = VolthaCli.complete_device
710
711 def do_inject_eapol_start(self, line):
Zsolt Harasztid036b7e2016-12-23 15:36:01 -0800712 """
713 Send out an an EAPOL start message into the given Unix interface
714 """
715 pass
Zsolt Harasztia133a452016-12-22 01:26:57 -0800716
717
718if __name__ == '__main__':
Zsolt Haraszti9b485fb2016-12-26 23:11:15 -0800719
720 parser = argparse.ArgumentParser()
721
722 _help = '<hostname>:<port> to consul agent (default: %s)' % defs['consul']
723 parser.add_argument(
724 '-C', '--consul', action='store', default=defs['consul'], help=_help)
725
726 _help = 'Lookup Voltha endpoints based on service entries in Consul'
727 parser.add_argument(
728 '-L', '--lookup', action='store_true', help=_help)
729
730 _help = '<hostname>:<port> of Voltha gRPC service (default={})'.format(
731 defs['voltha_grpc_endpoint'])
732 parser.add_argument('-g', '--grpc-endpoint', action='store',
733 default=defs['voltha_grpc_endpoint'], help=_help)
734
735 _help = '<hostname>:<port> of Voltha simulated adapter backend for ' \
736 'testing (default={})'.format(
737 defs['voltha_sim_rest_endpoint'])
738 parser.add_argument('-s', '--sim-rest-endpoint', action='store',
739 default=defs['voltha_sim_rest_endpoint'], help=_help)
740
741 args = parser.parse_args()
742
743 if args.lookup:
744 host = args.consul.split(':')[0].strip()
745 port = int(args.consul.split(':')[1].strip())
746 consul = Consul(host=host, port=port)
747
748 _, services = consul.catalog.service('voltha-grpc')
749 if not services:
750 print('No voltha-grpc service registered in consul; exiting')
751 sys.exit(1)
752 args.grpc_endpoint = '{}:{}'.format(services[0]['ServiceAddress'],
753 services[0]['ServicePort'])
754
755 _, services = consul.catalog.service('voltha-sim-rest')
756 if not services:
757 print('No voltha-sim-rest service registered in consul; exiting')
758 sys.exit(1)
759 args.sim_rest_endpoint = '{}:{}'.format(services[0]['ServiceAddress'],
760 services[0]['ServicePort'])
761
762 c = VolthaCli(args.grpc_endpoint, args.sim_rest_endpoint)
Zsolt Haraszti80175202016-12-24 00:17:51 -0800763 c.poutput(banner)
Zsolt Harasztia133a452016-12-22 01:26:57 -0800764 c.load_history()
765 c.cmdloop()
766 c.save_history()