blob: 8f7c06477a36ae355e783ba6c859d11cb896fbba [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
85 # del Cmd.do_eof
Zsolt Haraszti80175202016-12-24 00:17:51 -080086 del Cmd.do_q
87 del Cmd.do_hi
88 del Cmd.do_l
89 del Cmd.do_li
90 del Cmd.do_r
91 del Cmd.do__load
92 del Cmd.do__relative_load
93 Cmd.do_edit = Cmd.do_ed
94
Zsolt Haraszti9b485fb2016-12-26 23:11:15 -080095 def __init__(self, voltha_grpc, voltha_sim_rest):
96 VolthaCli.voltha_grpc = voltha_grpc
97 VolthaCli.voltha_sim_rest = voltha_sim_rest
98 Cmd.__init__(self)
Zsolt Harasztia133a452016-12-22 01:26:57 -080099 self.prompt = '(' + self.colorize(
Zsolt Haraszti80175202016-12-24 00:17:51 -0800100 self.colorize(self.prompt, 'blue'), 'bold') + ') '
Zsolt Harasztia133a452016-12-22 01:26:57 -0800101 self.channel = None
Zsolt Haraszti80175202016-12-24 00:17:51 -0800102 self.device_ids_cache = None
103 self.device_ids_cache_ts = time()
104 self.logical_device_ids_cache = None
105 self.logical_device_ids_cache_ts = time()
Zsolt Harasztia133a452016-12-22 01:26:57 -0800106
Zsolt Haraszti9b485fb2016-12-26 23:11:15 -0800107 # we override cmd2's method to avoid its optparse conflicting with our
108 # command line parsing
109 def cmdloop(self):
110 self._cmdloop()
111
Zsolt Harasztia133a452016-12-22 01:26:57 -0800112 def load_history(self):
113 """Load saved command history from local history file"""
114 try:
115 with file(self.history_file_name, 'r') as f:
116 for line in f.readlines():
117 stripped_line = line.strip()
118 self.history.append(stripped_line)
119 readline.add_history(stripped_line)
120 except IOError:
121 pass # ignore if file cannot be read
122
123 def save_history(self):
124 try:
Zsolt Haraszti80175202016-12-24 00:17:51 -0800125 with open(self.history_file_name, 'w') as f:
Zsolt Harasztia133a452016-12-22 01:26:57 -0800126 f.write('\n'.join(self.history[-self.max_history_lines:]))
Zsolt Haraszti80175202016-12-24 00:17:51 -0800127 except IOError as e:
128 self.perror('Could not save history in {}: {}'.format(
129 self.history_file_name, e))
Zsolt Harasztia133a452016-12-22 01:26:57 -0800130 else:
Zsolt Haraszti9b485fb2016-12-26 23:11:15 -0800131 self.poutput('History saved as {}'.format(
Zsolt Haraszti80175202016-12-24 00:17:51 -0800132 self.history_file_name))
133
134 def perror(self, errmsg, statement=None):
135 # Touch it up to make sure error is prefixed and colored
136 Cmd.perror(self, self.colorize('***ERROR: ', 'red') + errmsg,
137 statement)
Zsolt Harasztia133a452016-12-22 01:26:57 -0800138
139 def get_channel(self):
140 if self.channel is None:
141 self.channel = grpc.insecure_channel(self.voltha_grpc)
142 return self.channel
143
Zsolt Haraszti80175202016-12-24 00:17:51 -0800144 # ~~~~~~~~~~~~~~~~~ ACTUAL COMMAND IMPLEMENTATIONS ~~~~~~~~~~~~~~~~~~~~~~~~
Zsolt Harasztid036b7e2016-12-23 15:36:01 -0800145
Zsolt Haraszti80175202016-12-24 00:17:51 -0800146 def do_reset_history(self, line):
Zsolt Harasztia133a452016-12-22 01:26:57 -0800147 """Reset CLI history"""
148 while self.history:
149 self.history.pop()
150
Zsolt Haraszti80175202016-12-24 00:17:51 -0800151 def do_launch(self, line):
Zsolt Harasztia133a452016-12-22 01:26:57 -0800152 """If Voltha is not running yet, launch it"""
Zsolt Haraszti80175202016-12-24 00:17:51 -0800153 raise NotImplementedError('not implemented yet')
Zsolt Harasztia133a452016-12-22 01:26:57 -0800154
Zsolt Haraszti80175202016-12-24 00:17:51 -0800155 def do_restart(self, line):
Zsolt Harasztia133a452016-12-22 01:26:57 -0800156 """Launch Voltha, but if it is already running, terminate it first"""
157 pass
158
Zsolt Haraszti80175202016-12-24 00:17:51 -0800159 def do_adapters(self, line):
160 """List loaded adapter"""
161 stub = voltha_pb2.VolthaLocalServiceStub(self.get_channel())
162 res = stub.ListAdapters(Empty())
163 omit_fields = {}
Zsolt Haraszti85f12852016-12-24 08:30:58 -0800164 print_pb_list_as_table('Adapters:', res.items, omit_fields, self.poutput)
Zsolt Haraszti80175202016-12-24 00:17:51 -0800165
166 def get_devices(self):
Zsolt Harasztia133a452016-12-22 01:26:57 -0800167 stub = voltha_pb2.VolthaLocalServiceStub(self.get_channel())
168 res = stub.ListDevices(Empty())
Zsolt Haraszti80175202016-12-24 00:17:51 -0800169 return res.items
Zsolt Harasztia133a452016-12-22 01:26:57 -0800170
Zsolt Haraszti80175202016-12-24 00:17:51 -0800171 def get_logical_devices(self):
172 stub = voltha_pb2.VolthaLocalServiceStub(self.get_channel())
173 res = stub.ListLogicalDevices(Empty())
174 return res.items
175
176 def do_devices(self, line):
177 """List devices registered in Voltha"""
178 devices = self.get_devices()
179 omit_fields = {
180 'adapter',
181 'vendor',
182 'model',
183 'hardware_version',
184 'software_version',
185 'firmware_version',
186 'serial_number'
187 }
Zsolt Haraszti85f12852016-12-24 08:30:58 -0800188 print_pb_list_as_table('Devices:', devices, omit_fields, self.poutput)
Zsolt Haraszti80175202016-12-24 00:17:51 -0800189
190 def do_logical_devices(self, line):
Zsolt Harasztia133a452016-12-22 01:26:57 -0800191 """List logical devices in Voltha"""
192 stub = voltha_pb2.VolthaLocalServiceStub(self.get_channel())
193 res = stub.ListLogicalDevices(Empty())
Zsolt Haraszti80175202016-12-24 00:17:51 -0800194 omit_fields = {
195 'desc.mfr_desc',
196 'desc.hw_desc',
197 'desc.sw_desc',
198 'desc.dp_desc',
199 'desc.serial_number',
200 'switch_features.capabilities'
201 }
Zsolt Haraszti85f12852016-12-24 08:30:58 -0800202 print_pb_list_as_table('Logical devices:', res.items, omit_fields,
203 self.poutput)
Zsolt Harasztia133a452016-12-22 01:26:57 -0800204
Zsolt Haraszti80175202016-12-24 00:17:51 -0800205 def do_device(self, line):
Zsolt Harasztia133a452016-12-22 01:26:57 -0800206 """Enter device level command mode"""
Zsolt Haraszti80175202016-12-24 00:17:51 -0800207 device_id = line.strip() or self.default_device_id
Zsolt Harasztid036b7e2016-12-23 15:36:01 -0800208 if not device_id:
209 raise Exception('<device-id> parameter needed')
210 sub = DeviceCli(self.get_channel, device_id)
Zsolt Harasztia133a452016-12-22 01:26:57 -0800211 sub.cmdloop()
212
Zsolt Haraszti80175202016-12-24 00:17:51 -0800213 def do_logical_device(self, line):
Zsolt Harasztia133a452016-12-22 01:26:57 -0800214 """Enter logical device level command mode"""
Zsolt Haraszti80175202016-12-24 00:17:51 -0800215 logical_device_id = line.strip() or self.default_logical_device_id
Zsolt Harasztid036b7e2016-12-23 15:36:01 -0800216 if not logical_device_id:
217 raise Exception('<logical-device-id> parameter needed')
218 sub = LogicalDeviceCli(self.get_channel, logical_device_id)
Zsolt Harasztia133a452016-12-22 01:26:57 -0800219 sub.cmdloop()
220
Zsolt Haraszti80175202016-12-24 00:17:51 -0800221 def device_ids(self, force_refresh=False):
222 if force_refresh or self.device_ids is None or \
223 (time() - self.device_ids_cache_ts) > 1:
224 self.device_ids_cache = [d.id for d in self.get_devices()]
225 self.device_ids_cache_ts = time()
226 return self.device_ids_cache
227
228 def logical_device_ids(self, force_refresh=False):
229 if force_refresh or self.logical_device_ids is None or \
230 (time() - self.logical_device_ids_cache_ts) > 1:
231 self.logical_device_ids_cache = [d.id for d
232 in self.get_logical_devices()]
233 self.logical_device_ids_cache_ts = time()
234 return self.logical_device_ids_cache
235
236 def complete_device(self, text, line, begidx, endidx):
237 if not text:
238 completions = self.device_ids()[:]
239 else:
240 completions = [d for d in self.device_ids() if d.startswith(text)]
241 return completions
242
243 def complete_logical_device(self, text, line, begidx, endidx):
244 if not text:
245 completions = self.logical_device_ids()[:]
246 else:
247 completions = [d for d in self.logical_device_ids()
248 if d.startswith(text)]
249 return completions
250
251 def do_pdb(self, line):
Zsolt Harasztia133a452016-12-22 01:26:57 -0800252 """Launch PDB debug prompt in CLI (for CLI development)"""
253 from pdb import set_trace
254 set_trace()
255
Zsolt Haraszti80175202016-12-24 00:17:51 -0800256 def do_health(self, line):
Zsolt Harasztia133a452016-12-22 01:26:57 -0800257 """Show connectivity status to Voltha status"""
258 stub = voltha_pb2.HealthServiceStub(self.get_channel())
259 res = stub.GetHealthStatus(Empty())
Zsolt Haraszti80175202016-12-24 00:17:51 -0800260 self.poutput(dumps(pb2dict(res), indent=4))
Zsolt Harasztia133a452016-12-22 01:26:57 -0800261
Zsolt Harasztid036b7e2016-12-23 15:36:01 -0800262 @options([
263 make_option('-t', '--device-type', action="store", dest='device_type',
264 help="Device type", default='simulated_olt'),
265 make_option('-m', '--mac-address', action='store', dest='mac_address',
266 default='00:0c:e2:31:40:00'),
267 make_option('-i', '--ip-address', action='store', dest='ip_address'),
Zsolt Haraszti656ecc62016-12-28 15:08:23 -0800268 make_option('-H', '--host_and_port', action='store',
269 dest='host_and_port'),
Zsolt Harasztid036b7e2016-12-23 15:36:01 -0800270 ])
Zsolt Haraszti80175202016-12-24 00:17:51 -0800271 def do_preprovision_olt(self, line, opts):
Zsolt Harasztid036b7e2016-12-23 15:36:01 -0800272 """Preprovision a new OLT with given device type"""
Zsolt Harasztia133a452016-12-22 01:26:57 -0800273 stub = voltha_pb2.VolthaLocalServiceStub(self.get_channel())
Zsolt Harasztid036b7e2016-12-23 15:36:01 -0800274 kw = dict(type=opts.device_type)
Zsolt Haraszti656ecc62016-12-28 15:08:23 -0800275 if opts.host_and_port:
276 kw['host_and_port'] = opts.host_and_port
277 elif opts.ip_address:
Zsolt Harasztid036b7e2016-12-23 15:36:01 -0800278 kw['ipv4_address'] = opts.ip_address
279 elif opts.mac_address:
280 kw['mac_address'] = opts.mac_address
281 else:
282 raise Exception('Either IP address or Mac Address is needed')
283 device = voltha_pb2.Device(**kw)
284 device = stub.CreateDevice(device)
Zsolt Haraszti80175202016-12-24 00:17:51 -0800285 self.poutput('success (device id = {})'.format(device.id))
Zsolt Harasztid036b7e2016-12-23 15:36:01 -0800286 self.default_device_id = device.id
Zsolt Harasztia133a452016-12-22 01:26:57 -0800287
Zsolt Haraszti80175202016-12-24 00:17:51 -0800288 def do_activate_olt(self, line):
Zsolt Harasztid036b7e2016-12-23 15:36:01 -0800289 """
290 Activate an OLT. If the <id> is not provided, it will be on the last
291 pre-provisioned OLT.
292 """
Zsolt Haraszti80175202016-12-24 00:17:51 -0800293 device_id = line or self.default_device_id
294 self.poutput('activating {}'.format(device_id))
Zsolt Harasztid036b7e2016-12-23 15:36:01 -0800295 stub = voltha_pb2.VolthaLocalServiceStub(self.get_channel())
296 stub.ActivateDevice(voltha_pb2.ID(id=device_id))
Zsolt Harasztia133a452016-12-22 01:26:57 -0800297
Zsolt Harasztid036b7e2016-12-23 15:36:01 -0800298 # try to acquire logical device id
299 while True:
300 device = stub.GetDevice(voltha_pb2.ID(id=device_id))
301 if device.oper_status == voltha_pb2.OperStatus.ACTIVE:
302 assert device.parent_id
303 self.default_logical_device_id = device.parent_id
304 break
Zsolt Haraszti80175202016-12-24 00:17:51 -0800305 self.poutput('waiting for device to be activated...')
306 sleep(.5)
307 self.poutput('success (logical device id = {})'.format(
308 self.default_logical_device_id))
Zsolt Harasztid036b7e2016-12-23 15:36:01 -0800309
Zsolt Haraszti50cae7d2017-01-08 22:27:07 -0800310 complete_activate_olt = complete_device
311
312 def do_test(self, line):
313 """Enter test mode, which makes a bunch on new commands available"""
314 sub = TestCli(self.history, self.get_channel, self.voltha_grpc,
315 self.voltha_sim_rest)
316 sub.cmdloop()
317
318
319class TestCli(VolthaCli):
320
321 def __init__(self, history, get_channel, voltha_grpc, voltha_sim_rest):
322 VolthaCli.__init__(self, voltha_grpc, voltha_sim_rest)
323 self.history = history
324 self.get_channel = get_channel
325 self.prompt = '(' + self.colorize(self.colorize('test', 'cyan'),
326 'bold') + ') '
327
328 def get_device(self, device_id, depth=0):
329 stub = voltha_pb2.VolthaLocalServiceStub(self.get_channel())
330 res = stub.GetDevice(voltha_pb2.ID(id=device_id),
331 metadata=(('get-depth', str(depth)), ))
332 return res
Zsolt Haraszti80175202016-12-24 00:17:51 -0800333
334 def do_arrive_onus(self, line):
Zsolt Harasztid036b7e2016-12-23 15:36:01 -0800335 """
Zsolt Haraszti85f12852016-12-24 08:30:58 -0800336 Simulate the arrival of ONUs (available only on simulated_olt)
Zsolt Harasztid036b7e2016-12-23 15:36:01 -0800337 """
Zsolt Haraszti80175202016-12-24 00:17:51 -0800338 device_id = line or self.default_device_id
Zsolt Haraszti85f12852016-12-24 08:30:58 -0800339
340 # verify that device is of type simulated_olt
341 device = self.get_device(device_id)
342 assert device.type == 'simulated_olt', (
343 'Cannot use it on this device type (only on simulated_olt type)')
344
Zsolt Harasztid036b7e2016-12-23 15:36:01 -0800345 requests.get('http://{}/devices/{}/detect_onus'.format(
346 self.voltha_sim_rest, device_id
347 ))
348
Zsolt Haraszti80175202016-12-24 00:17:51 -0800349 complete_arrive_onus = VolthaCli.complete_device
350
Zsolt Haraszti85f12852016-12-24 08:30:58 -0800351 def get_logical_ports(self, logical_device_id):
352 """
353 Return the NNI port number and the first usable UNI port of logical
354 device, and the vlan associated with the latter.
355 """
356 stub = voltha_pb2.VolthaLocalServiceStub(self.get_channel())
357 ports = stub.ListLogicalDevicePorts(
358 voltha_pb2.ID(id=logical_device_id)).items
359 nni = uni = vlan = None
360 for port in ports:
361 if nni is None and port.root_port:
362 nni = port.ofp_port.port_no
363 if uni is None and not port.root_port:
364 uni = port.ofp_port.port_no
365 uni_device = self.get_device(port.device_id)
366 vlan = uni_device.vlan
367 if nni is not None and uni is not None:
368 return nni, uni, vlan
369 raise Exception('No valid port pair found (no ONUs yet?)')
370
Zsolt Haraszti80175202016-12-24 00:17:51 -0800371 def do_install_eapol_flow(self, line):
Zsolt Harasztid036b7e2016-12-23 15:36:01 -0800372 """
373 Install an EAPOL flow on the given logical device. If device is not
374 given, it will be applied to logical device of the last pre-provisioned
375 OLT device.
376 """
Zsolt Haraszti80175202016-12-24 00:17:51 -0800377 logical_device_id = line or self.default_logical_device_id
Zsolt Haraszti85f12852016-12-24 08:30:58 -0800378
379 # gather NNI and UNI port IDs
Zsolt Haraszti01bbe882016-12-27 10:43:18 -0800380 nni_port_no, uni_port_no, _ = self.get_logical_ports(logical_device_id)
Zsolt Haraszti85f12852016-12-24 08:30:58 -0800381
382 # construct and push flow rule
Zsolt Harasztid036b7e2016-12-23 15:36:01 -0800383 update = FlowTableUpdate(
384 id=logical_device_id,
Zsolt Haraszti85f12852016-12-24 08:30:58 -0800385 flow_mod=mk_simple_flow_mod(
Zsolt Harasztid036b7e2016-12-23 15:36:01 -0800386 priority=2000,
Zsolt Haraszti85f12852016-12-24 08:30:58 -0800387 match_fields=[in_port(uni_port_no), eth_type(0x888e)],
Zsolt Harasztid036b7e2016-12-23 15:36:01 -0800388 actions=[
Zsolt Haraszti85f12852016-12-24 08:30:58 -0800389 # push_vlan(0x8100),
390 # set_field(vlan_vid(4096 + 4000)),
Zsolt Harasztid036b7e2016-12-23 15:36:01 -0800391 output(ofp.OFPP_CONTROLLER)
392 ]
393 )
Zsolt Harasztia133a452016-12-22 01:26:57 -0800394 )
Zsolt Harasztid036b7e2016-12-23 15:36:01 -0800395 stub = voltha_pb2.VolthaLocalServiceStub(self.get_channel())
396 res = stub.UpdateLogicalDeviceFlowTable(update)
Zsolt Haraszti80175202016-12-24 00:17:51 -0800397 self.poutput('success ({})'.format(res))
Zsolt Harasztid036b7e2016-12-23 15:36:01 -0800398
Zsolt Haraszti80175202016-12-24 00:17:51 -0800399 complete_install_eapol_flow = VolthaCli.complete_logical_device
400
Zsolt Haraszti85f12852016-12-24 08:30:58 -0800401 def do_install_all_controller_bound_flows(self, line):
402 """
403 Install all flow rules for controller bound flows, including EAPOL,
404 IGMP and DHCP. If device is not given, it will be applied to logical
405 device of the last pre-provisioned OLT device.
406 """
407 logical_device_id = line or self.default_logical_device_id
408
409 # gather NNI and UNI port IDs
Zsolt Haraszti01bbe882016-12-27 10:43:18 -0800410 nni_port_no, uni_port_no, _ = self.get_logical_ports(logical_device_id)
Zsolt Haraszti85f12852016-12-24 08:30:58 -0800411
412 # construct and push flow rules
413 stub = voltha_pb2.VolthaLocalServiceStub(self.get_channel())
414
415 stub.UpdateLogicalDeviceFlowTable(FlowTableUpdate(
416 id=logical_device_id,
417 flow_mod=mk_simple_flow_mod(
418 priority=2000,
419 match_fields=[in_port(uni_port_no), eth_type(0x888e)],
420 actions=[
421 # push_vlan(0x8100),
422 # set_field(vlan_vid(4096 + 4000)),
423 output(ofp.OFPP_CONTROLLER)
424 ]
425 )
426 ))
427 stub.UpdateLogicalDeviceFlowTable(FlowTableUpdate(
428 id=logical_device_id,
429 flow_mod=mk_simple_flow_mod(
430 priority=1000,
431 match_fields=[eth_type(0x800), ip_proto(2)],
432 actions=[output(ofp.OFPP_CONTROLLER)]
433 )
434 ))
435 stub.UpdateLogicalDeviceFlowTable(FlowTableUpdate(
436 id=logical_device_id,
437 flow_mod=mk_simple_flow_mod(
438 priority=1000,
439 match_fields=[eth_type(0x800), ip_proto(17), udp_dst(67)],
440 actions=[output(ofp.OFPP_CONTROLLER)]
441 )
442 ))
443
444 self.poutput('success')
445
446 complete_install_all_controller_bound_flows = \
447 VolthaCli.complete_logical_device
448
449 def do_install_all_sample_flows(self, line):
450 """
451 Install all flows that are representative of the virtualized access
452 scenario in a PON network.
453 """
454 logical_device_id = line or self.default_logical_device_id
455
456 # gather NNI and UNI port IDs
457 nni_port_no, uni_port_no, c_vid = \
458 self.get_logical_ports(logical_device_id)
459
460 # construct and push flow rules
461 stub = voltha_pb2.VolthaLocalServiceStub(self.get_channel())
462
463 # Controller-bound flows
464 stub.UpdateLogicalDeviceFlowTable(FlowTableUpdate(
465 id=logical_device_id,
466 flow_mod=mk_simple_flow_mod(
467 priority=2000,
468 match_fields=[in_port(uni_port_no), eth_type(0x888e)],
469 actions=[
470 # push_vlan(0x8100),
471 # set_field(vlan_vid(4096 + 4000)),
472 output(ofp.OFPP_CONTROLLER)
473 ]
474 )
475 ))
476 stub.UpdateLogicalDeviceFlowTable(FlowTableUpdate(
477 id=logical_device_id,
478 flow_mod=mk_simple_flow_mod(
479 priority=1000,
480 match_fields=[eth_type(0x800), ip_proto(2)],
481 actions=[output(ofp.OFPP_CONTROLLER)]
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(17), udp_dst(67)],
489 actions=[output(ofp.OFPP_CONTROLLER)]
490 )
491 ))
492
493 # Unicast flows:
494 # Downstream flow 1
495 stub.UpdateLogicalDeviceFlowTable(FlowTableUpdate(
496 id=logical_device_id,
497 flow_mod=mk_simple_flow_mod(
498 priority=500,
499 match_fields=[
500 in_port(nni_port_no),
Zsolt Haraszti6a5107c2017-01-09 23:42:41 -0800501 vlan_vid(4096 + 1000),
502 metadata(40) # here to mimic an ONOS artifact
Zsolt Haraszti85f12852016-12-24 08:30:58 -0800503 ],
504 actions=[pop_vlan()],
505 next_table_id=1
506 )
507 ))
508 # Downstream flow 2
509 stub.UpdateLogicalDeviceFlowTable(FlowTableUpdate(
510 id=logical_device_id,
511 flow_mod=mk_simple_flow_mod(
512 priority=500,
513 match_fields=[in_port(nni_port_no), vlan_vid(4096 + c_vid)],
514 actions=[set_field(vlan_vid(4096 + 0)), output(uni_port_no)]
515 )
516 ))
517 # Upstream flow 1 for 0-tagged case
518 stub.UpdateLogicalDeviceFlowTable(FlowTableUpdate(
519 id=logical_device_id,
520 flow_mod=mk_simple_flow_mod(
521 priority=500,
522 match_fields=[in_port(uni_port_no), vlan_vid(4096 + 0)],
523 actions=[set_field(vlan_vid(4096 + c_vid))],
524 next_table_id=1
525 )
526 ))
527 # Upstream flow 1 for untagged case
528 stub.UpdateLogicalDeviceFlowTable(FlowTableUpdate(
529 id=logical_device_id,
530 flow_mod=mk_simple_flow_mod(
531 priority=500,
532 match_fields=[in_port(uni_port_no), vlan_vid(0)],
533 actions=[push_vlan(0x8100), set_field(vlan_vid(4096 + c_vid))],
534 next_table_id=1
535 )
536 ))
537 # Upstream flow 2 for s-tag
538 stub.UpdateLogicalDeviceFlowTable(FlowTableUpdate(
539 id=logical_device_id,
540 flow_mod=mk_simple_flow_mod(
541 priority=500,
542 match_fields=[in_port(uni_port_no), vlan_vid(4096 + c_vid)],
543 actions=[
544 push_vlan(0x8100),
545 set_field(vlan_vid(4096 + 1000)),
546 output(nni_port_no)
547 ]
548 )
549 ))
550
551 # Push a few multicast flows
552 # 1st with one bucket for our uni
553 stub.UpdateLogicalDeviceFlowGroupTable(FlowGroupTableUpdate(
554 id=logical_device_id,
555 group_mod=mk_multicast_group_mod(
556 group_id=1,
557 buckets=[
558 ofp.ofp_bucket(actions=[pop_vlan(), output(uni_port_no)])
559 ]
560 )
561 ))
562 stub.UpdateLogicalDeviceFlowTable(FlowTableUpdate(
563 id=logical_device_id,
564 flow_mod=mk_simple_flow_mod(
565 priority=1000,
566 match_fields=[
567 in_port(nni_port_no),
568 eth_type(0x800),
569 vlan_vid(4096 + 140),
570 ipv4_dst(0xe4010101)
571 ],
572 actions=[group(1)]
573 )
574 ))
575 # 2st with one bucket for our uni
576 stub.UpdateLogicalDeviceFlowGroupTable(FlowGroupTableUpdate(
577 id=logical_device_id,
578 group_mod=mk_multicast_group_mod(
579 group_id=2,
580 buckets=[
581 ofp.ofp_bucket(actions=[pop_vlan(), output(uni_port_no)])
582 ]
583 )
584 ))
585 stub.UpdateLogicalDeviceFlowTable(FlowTableUpdate(
586 id=logical_device_id,
587 flow_mod=mk_simple_flow_mod(
588 priority=1000,
589 match_fields=[
590 in_port(nni_port_no),
591 eth_type(0x800),
592 vlan_vid(4096 + 140),
593 ipv4_dst(0xe4020202)
594 ],
595 actions=[group(2)]
596 )
597 ))
598 # 3rd with empty bucket
599 stub.UpdateLogicalDeviceFlowGroupTable(FlowGroupTableUpdate(
600 id=logical_device_id,
601 group_mod=mk_multicast_group_mod(
602 group_id=3,
603 buckets=[]
604 )
605 ))
606 stub.UpdateLogicalDeviceFlowTable(FlowTableUpdate(
607 id=logical_device_id,
608 flow_mod=mk_simple_flow_mod(
609 priority=1000,
610 match_fields=[
611 in_port(nni_port_no),
612 eth_type(0x800),
613 vlan_vid(4096 + 140),
614 ipv4_dst(0xe4030303)
615 ],
616 actions=[group(3)]
617 )
618 ))
619
620 self.poutput('success')
621
622 complete_install_all_sample_flows = VolthaCli.complete_logical_device
623
Nathan Knuth5f4163e2017-01-11 18:21:10 -0600624 def do_install_dhcp_flows(self, line):
625 """
626 Install all dhcp flows that are representative of the virtualized access
627 scenario in a PON network.
628 """
629 logical_device_id = line or self.default_logical_device_id
630
631 # gather NNI and UNI port IDs
632 nni_port_no, uni_port_no, c_vid = \
633 self.get_logical_ports(logical_device_id)
634
635 # construct and push flow rules
636 stub = voltha_pb2.VolthaLocalServiceStub(self.get_channel())
637
638 # Controller-bound flows
639 stub.UpdateLogicalDeviceFlowTable(FlowTableUpdate(
640 id=logical_device_id,
641 flow_mod=mk_simple_flow_mod(
642 priority=1000,
643 match_fields=[eth_type(0x800), ip_proto(17), udp_dst(67)],
644 actions=[output(ofp.OFPP_CONTROLLER)]
645 )
646 ))
647
648
649 self.poutput('success')
650
651 complete_install_dhcp_flows = VolthaCli.complete_logical_device
652
Zsolt Haraszti85f12852016-12-24 08:30:58 -0800653 def do_delete_all_flows(self, line):
654 """
655 Remove all flows and flow groups from given logical device
656 """
657 logical_device_id = line or self.default_logical_device_id
658 stub = voltha_pb2.VolthaLocalServiceStub(self.get_channel())
659 stub.UpdateLogicalDeviceFlowTable(FlowTableUpdate(
660 id=logical_device_id,
661 flow_mod=ofp.ofp_flow_mod(
662 command=ofp.OFPFC_DELETE,
663 table_id=ofp.OFPTT_ALL,
664 cookie_mask=0,
665 out_port=ofp.OFPP_ANY,
666 out_group=ofp.OFPG_ANY
667 )
668 ))
669 stub.UpdateLogicalDeviceFlowGroupTable(FlowGroupTableUpdate(
670 id=logical_device_id,
671 group_mod=ofp.ofp_group_mod(
672 command=ofp.OFPGC_DELETE,
673 group_id=ofp.OFPG_ALL
674 )
675 ))
676 self.poutput('success')
677
678 complete_delete_all_flows = VolthaCli.complete_logical_device
679
Zsolt Haraszti80175202016-12-24 00:17:51 -0800680 def do_send_simulated_upstream_eapol(self, line):
Zsolt Harasztid036b7e2016-12-23 15:36:01 -0800681 """
682 Send an EAPOL upstream from a simulated OLT
683 """
Zsolt Haraszti80175202016-12-24 00:17:51 -0800684 device_id = line or self.default_device_id
Zsolt Harasztid036b7e2016-12-23 15:36:01 -0800685 requests.get('http://{}/devices/{}/test_eapol_in'.format(
686 self.voltha_sim_rest, device_id
687 ))
688
Zsolt Haraszti80175202016-12-24 00:17:51 -0800689 complete_send_simulated_upstream_eapol = VolthaCli.complete_device
690
691 def do_inject_eapol_start(self, line):
Zsolt Harasztid036b7e2016-12-23 15:36:01 -0800692 """
693 Send out an an EAPOL start message into the given Unix interface
694 """
695 pass
Zsolt Harasztia133a452016-12-22 01:26:57 -0800696
697
698if __name__ == '__main__':
Zsolt Haraszti9b485fb2016-12-26 23:11:15 -0800699
700 parser = argparse.ArgumentParser()
701
702 _help = '<hostname>:<port> to consul agent (default: %s)' % defs['consul']
703 parser.add_argument(
704 '-C', '--consul', action='store', default=defs['consul'], help=_help)
705
706 _help = 'Lookup Voltha endpoints based on service entries in Consul'
707 parser.add_argument(
708 '-L', '--lookup', action='store_true', help=_help)
709
710 _help = '<hostname>:<port> of Voltha gRPC service (default={})'.format(
711 defs['voltha_grpc_endpoint'])
712 parser.add_argument('-g', '--grpc-endpoint', action='store',
713 default=defs['voltha_grpc_endpoint'], help=_help)
714
715 _help = '<hostname>:<port> of Voltha simulated adapter backend for ' \
716 'testing (default={})'.format(
717 defs['voltha_sim_rest_endpoint'])
718 parser.add_argument('-s', '--sim-rest-endpoint', action='store',
719 default=defs['voltha_sim_rest_endpoint'], help=_help)
720
721 args = parser.parse_args()
722
723 if args.lookup:
724 host = args.consul.split(':')[0].strip()
725 port = int(args.consul.split(':')[1].strip())
726 consul = Consul(host=host, port=port)
727
728 _, services = consul.catalog.service('voltha-grpc')
729 if not services:
730 print('No voltha-grpc service registered in consul; exiting')
731 sys.exit(1)
732 args.grpc_endpoint = '{}:{}'.format(services[0]['ServiceAddress'],
733 services[0]['ServicePort'])
734
735 _, services = consul.catalog.service('voltha-sim-rest')
736 if not services:
737 print('No voltha-sim-rest service registered in consul; exiting')
738 sys.exit(1)
739 args.sim_rest_endpoint = '{}:{}'.format(services[0]['ServiceAddress'],
740 services[0]['ServicePort'])
741
742 c = VolthaCli(args.grpc_endpoint, args.sim_rest_endpoint)
Zsolt Haraszti80175202016-12-24 00:17:51 -0800743 c.poutput(banner)
Zsolt Harasztia133a452016-12-22 01:26:57 -0800744 c.load_history()
745 c.cmdloop()
746 c.save_history()