blob: a57a33dce31686d20c5b5505f8d6306913999705 [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
Zsolt Harasztib9a5f752017-02-11 06:07:08 -0800359 nni = None
360 unis = []
Zsolt Haraszti85f12852016-12-24 08:30:58 -0800361 for port in ports:
Zsolt Harasztib9a5f752017-02-11 06:07:08 -0800362 if port.root_port:
363 assert nni is None, "There shall be only one root port"
Zsolt Haraszti85f12852016-12-24 08:30:58 -0800364 nni = port.ofp_port.port_no
Zsolt Harasztib9a5f752017-02-11 06:07:08 -0800365 else:
Zsolt Haraszti85f12852016-12-24 08:30:58 -0800366 uni = port.ofp_port.port_no
367 uni_device = self.get_device(port.device_id)
368 vlan = uni_device.vlan
Zsolt Harasztib9a5f752017-02-11 06:07:08 -0800369 unis.append((uni, vlan))
370
371 assert nni is not None, "No NNI port found"
372 assert unis, "Not a single UNI?"
373
374 return nni, unis
Zsolt Haraszti85f12852016-12-24 08:30:58 -0800375
Zsolt Haraszti80175202016-12-24 00:17:51 -0800376 def do_install_eapol_flow(self, line):
Zsolt Harasztid036b7e2016-12-23 15:36:01 -0800377 """
378 Install an EAPOL flow on the given logical device. If device is not
379 given, it will be applied to logical device of the last pre-provisioned
380 OLT device.
381 """
Zsolt Haraszti80175202016-12-24 00:17:51 -0800382 logical_device_id = line or self.default_logical_device_id
Zsolt Haraszti85f12852016-12-24 08:30:58 -0800383
384 # gather NNI and UNI port IDs
Zsolt Harasztib9a5f752017-02-11 06:07:08 -0800385 nni_port_no, unis = self.get_logical_ports(logical_device_id)
Zsolt Haraszti85f12852016-12-24 08:30:58 -0800386
387 # construct and push flow rule
Zsolt Harasztid036b7e2016-12-23 15:36:01 -0800388 stub = voltha_pb2.VolthaLocalServiceStub(self.get_channel())
Zsolt Harasztib9a5f752017-02-11 06:07:08 -0800389 for uni_port_no, _ in unis:
390 update = FlowTableUpdate(
391 id=logical_device_id,
392 flow_mod=mk_simple_flow_mod(
393 priority=2000,
394 match_fields=[in_port(uni_port_no), eth_type(0x888e)],
395 actions=[
396 # push_vlan(0x8100),
397 # set_field(vlan_vid(4096 + 4000)),
398 output(ofp.OFPP_CONTROLLER)
399 ]
400 )
401 )
402 res = stub.UpdateLogicalDeviceFlowTable(update)
403 self.poutput('success for uni {} ({})'.format(uni_port_no, res))
Zsolt Harasztid036b7e2016-12-23 15:36:01 -0800404
Zsolt Haraszti80175202016-12-24 00:17:51 -0800405 complete_install_eapol_flow = VolthaCli.complete_logical_device
406
Zsolt Haraszti85f12852016-12-24 08:30:58 -0800407 def do_install_all_controller_bound_flows(self, line):
408 """
409 Install all flow rules for controller bound flows, including EAPOL,
410 IGMP and DHCP. If device is not given, it will be applied to logical
411 device of the last pre-provisioned OLT device.
412 """
413 logical_device_id = line or self.default_logical_device_id
414
415 # gather NNI and UNI port IDs
Zsolt Harasztib9a5f752017-02-11 06:07:08 -0800416 nni_port_no, unis = self.get_logical_ports(logical_device_id)
Zsolt Haraszti85f12852016-12-24 08:30:58 -0800417
418 # construct and push flow rules
419 stub = voltha_pb2.VolthaLocalServiceStub(self.get_channel())
420
Zsolt Harasztib9a5f752017-02-11 06:07:08 -0800421 for uni_port_no, _ in unis:
422 stub.UpdateLogicalDeviceFlowTable(FlowTableUpdate(
423 id=logical_device_id,
424 flow_mod=mk_simple_flow_mod(
425 priority=2000,
426 match_fields=[
427 in_port(uni_port_no),
428 eth_type(0x888e)
429 ],
430 actions=[output(ofp.OFPP_CONTROLLER)]
431 )
432 ))
433 stub.UpdateLogicalDeviceFlowTable(FlowTableUpdate(
434 id=logical_device_id,
435 flow_mod=mk_simple_flow_mod(
436 priority=1000,
437 match_fields=[
438 in_port(uni_port_no),
439 eth_type(0x800),
440 ip_proto(2)
441 ],
442 actions=[output(ofp.OFPP_CONTROLLER)]
443 )
444 ))
445 stub.UpdateLogicalDeviceFlowTable(FlowTableUpdate(
446 id=logical_device_id,
447 flow_mod=mk_simple_flow_mod(
448 priority=1000,
449 match_fields=[
450 in_port(uni_port_no),
451 eth_type(0x800),
452 ip_proto(17),
453 udp_dst(67)
454 ],
455 actions=[output(ofp.OFPP_CONTROLLER)]
456 )
457 ))
Zsolt Haraszti85f12852016-12-24 08:30:58 -0800458 self.poutput('success')
459
460 complete_install_all_controller_bound_flows = \
461 VolthaCli.complete_logical_device
462
463 def do_install_all_sample_flows(self, line):
464 """
465 Install all flows that are representative of the virtualized access
466 scenario in a PON network.
467 """
468 logical_device_id = line or self.default_logical_device_id
469
470 # gather NNI and UNI port IDs
Zsolt Harasztib9a5f752017-02-11 06:07:08 -0800471 nni_port_no, unis = self.get_logical_ports(logical_device_id)
Zsolt Haraszti85f12852016-12-24 08:30:58 -0800472
473 # construct and push flow rules
474 stub = voltha_pb2.VolthaLocalServiceStub(self.get_channel())
475
Zsolt Harasztib9a5f752017-02-11 06:07:08 -0800476 for uni_port_no, c_vid in unis:
Zsolt Haraszti85f12852016-12-24 08:30:58 -0800477
Zsolt Harasztib9a5f752017-02-11 06:07:08 -0800478 # Controller-bound flows
479 stub.UpdateLogicalDeviceFlowTable(FlowTableUpdate(
480 id=logical_device_id,
481 flow_mod=mk_simple_flow_mod(
482 priority=2000,
483 match_fields=[in_port(uni_port_no), eth_type(0x888e)],
484 actions=[
485 # push_vlan(0x8100),
486 # set_field(vlan_vid(4096 + 4000)),
487 output(ofp.OFPP_CONTROLLER)
488 ]
489 )
490 ))
491 stub.UpdateLogicalDeviceFlowTable(FlowTableUpdate(
492 id=logical_device_id,
493 flow_mod=mk_simple_flow_mod(
494 priority=1000,
495 match_fields=[eth_type(0x800), ip_proto(2)],
496 actions=[output(ofp.OFPP_CONTROLLER)]
497 )
498 ))
499 stub.UpdateLogicalDeviceFlowTable(FlowTableUpdate(
500 id=logical_device_id,
501 flow_mod=mk_simple_flow_mod(
502 priority=1000,
503 match_fields=[eth_type(0x800), ip_proto(17), udp_dst(67)],
504 actions=[output(ofp.OFPP_CONTROLLER)]
505 )
506 ))
507
508 # Unicast flows:
509 # Downstream flow 1
510 stub.UpdateLogicalDeviceFlowTable(FlowTableUpdate(
511 id=logical_device_id,
512 flow_mod=mk_simple_flow_mod(
513 priority=500,
514 match_fields=[
515 in_port(nni_port_no),
516 vlan_vid(4096 + 1000),
517 metadata(c_vid) # here to mimic an ONOS artifact
518 ],
519 actions=[pop_vlan()],
520 next_table_id=1
521 )
522 ))
523 # Downstream flow 2
524 stub.UpdateLogicalDeviceFlowTable(FlowTableUpdate(
525 id=logical_device_id,
526 flow_mod=mk_simple_flow_mod(
527 priority=500,
528 table_id=1,
529 match_fields=[in_port(nni_port_no), vlan_vid(4096 + c_vid)],
530 actions=[set_field(vlan_vid(4096 + 0)), output(uni_port_no)]
531 )
532 ))
533 # Upstream flow 1 for 0-tagged case
534 stub.UpdateLogicalDeviceFlowTable(FlowTableUpdate(
535 id=logical_device_id,
536 flow_mod=mk_simple_flow_mod(
537 priority=500,
538 match_fields=[in_port(uni_port_no), vlan_vid(4096 + 0)],
539 actions=[set_field(vlan_vid(4096 + c_vid))],
540 next_table_id=1
541 )
542 ))
543 # Upstream flow 1 for untagged case
544 stub.UpdateLogicalDeviceFlowTable(FlowTableUpdate(
545 id=logical_device_id,
546 flow_mod=mk_simple_flow_mod(
547 priority=500,
548 match_fields=[in_port(uni_port_no), vlan_vid(0)],
549 actions=[push_vlan(0x8100), set_field(vlan_vid(4096 + c_vid))],
550 next_table_id=1
551 )
552 ))
553 # Upstream flow 2 for s-tag
554 stub.UpdateLogicalDeviceFlowTable(FlowTableUpdate(
555 id=logical_device_id,
556 flow_mod=mk_simple_flow_mod(
557 priority=500,
558 table_id=1,
559 match_fields=[in_port(uni_port_no), vlan_vid(4096 + c_vid)],
560 actions=[
561 push_vlan(0x8100),
562 set_field(vlan_vid(4096 + 1000)),
563 output(nni_port_no)
564 ]
565 )
566 ))
Zsolt Haraszti85f12852016-12-24 08:30:58 -0800567
568 # Push a few multicast flows
Zsolt Harasztib9a5f752017-02-11 06:07:08 -0800569 # 1st with one bucket for our uni 0
Zsolt Haraszti85f12852016-12-24 08:30:58 -0800570 stub.UpdateLogicalDeviceFlowGroupTable(FlowGroupTableUpdate(
571 id=logical_device_id,
572 group_mod=mk_multicast_group_mod(
573 group_id=1,
574 buckets=[
Zsolt Harasztib9a5f752017-02-11 06:07:08 -0800575 ofp.ofp_bucket(actions=[
576 pop_vlan(),
577 output(unis[0][0])
578 ])
Zsolt Haraszti85f12852016-12-24 08:30:58 -0800579 ]
580 )
581 ))
582 stub.UpdateLogicalDeviceFlowTable(FlowTableUpdate(
583 id=logical_device_id,
584 flow_mod=mk_simple_flow_mod(
585 priority=1000,
586 match_fields=[
587 in_port(nni_port_no),
588 eth_type(0x800),
589 vlan_vid(4096 + 140),
590 ipv4_dst(0xe4010101)
591 ],
592 actions=[group(1)]
593 )
594 ))
Zsolt Harasztib9a5f752017-02-11 06:07:08 -0800595
596 # 2nd with one bucket for uni 0 and 1
Zsolt Haraszti85f12852016-12-24 08:30:58 -0800597 stub.UpdateLogicalDeviceFlowGroupTable(FlowGroupTableUpdate(
598 id=logical_device_id,
599 group_mod=mk_multicast_group_mod(
600 group_id=2,
601 buckets=[
Nathan Knuth6b7b6ff2017-02-12 03:30:48 -0800602 ofp.ofp_bucket(actions=[pop_vlan(), output(unis[0][0])])
603# ofp.ofp_bucket(actions=[pop_vlan(), output(unis[1][0])])
Zsolt Haraszti85f12852016-12-24 08:30:58 -0800604 ]
605 )
606 ))
607 stub.UpdateLogicalDeviceFlowTable(FlowTableUpdate(
608 id=logical_device_id,
609 flow_mod=mk_simple_flow_mod(
610 priority=1000,
611 match_fields=[
612 in_port(nni_port_no),
613 eth_type(0x800),
614 vlan_vid(4096 + 140),
615 ipv4_dst(0xe4020202)
616 ],
617 actions=[group(2)]
618 )
619 ))
Zsolt Harasztib9a5f752017-02-11 06:07:08 -0800620
Zsolt Haraszti85f12852016-12-24 08:30:58 -0800621 # 3rd with empty bucket
622 stub.UpdateLogicalDeviceFlowGroupTable(FlowGroupTableUpdate(
623 id=logical_device_id,
624 group_mod=mk_multicast_group_mod(
625 group_id=3,
626 buckets=[]
627 )
628 ))
629 stub.UpdateLogicalDeviceFlowTable(FlowTableUpdate(
630 id=logical_device_id,
631 flow_mod=mk_simple_flow_mod(
632 priority=1000,
633 match_fields=[
634 in_port(nni_port_no),
635 eth_type(0x800),
636 vlan_vid(4096 + 140),
637 ipv4_dst(0xe4030303)
638 ],
639 actions=[group(3)]
640 )
641 ))
642
643 self.poutput('success')
644
645 complete_install_all_sample_flows = VolthaCli.complete_logical_device
646
Nathan Knuth5f4163e2017-01-11 18:21:10 -0600647 def do_install_dhcp_flows(self, line):
648 """
649 Install all dhcp flows that are representative of the virtualized access
650 scenario in a PON network.
651 """
652 logical_device_id = line or self.default_logical_device_id
653
654 # gather NNI and UNI port IDs
Zsolt Harasztib9a5f752017-02-11 06:07:08 -0800655 nni_port_no, unis = self.get_logical_ports(logical_device_id)
Nathan Knuth5f4163e2017-01-11 18:21:10 -0600656
657 # construct and push flow rules
658 stub = voltha_pb2.VolthaLocalServiceStub(self.get_channel())
659
660 # Controller-bound flows
Zsolt Harasztib9a5f752017-02-11 06:07:08 -0800661 for uni_port_no, _ in unis:
662 stub.UpdateLogicalDeviceFlowTable(FlowTableUpdate(
663 id=logical_device_id,
664 flow_mod=mk_simple_flow_mod(
665 priority=1000,
666 match_fields=[
667 in_port(uni_port_no),
668 eth_type(0x800),
669 ip_proto(17),
670 udp_dst(67)
671 ],
672 actions=[output(ofp.OFPP_CONTROLLER)]
673 )
674 ))
Nathan Knuth5f4163e2017-01-11 18:21:10 -0600675
676 self.poutput('success')
677
678 complete_install_dhcp_flows = VolthaCli.complete_logical_device
679
Zsolt Haraszti85f12852016-12-24 08:30:58 -0800680 def do_delete_all_flows(self, line):
681 """
682 Remove all flows and flow groups from given logical device
683 """
684 logical_device_id = line or self.default_logical_device_id
685 stub = voltha_pb2.VolthaLocalServiceStub(self.get_channel())
686 stub.UpdateLogicalDeviceFlowTable(FlowTableUpdate(
687 id=logical_device_id,
688 flow_mod=ofp.ofp_flow_mod(
689 command=ofp.OFPFC_DELETE,
690 table_id=ofp.OFPTT_ALL,
691 cookie_mask=0,
692 out_port=ofp.OFPP_ANY,
693 out_group=ofp.OFPG_ANY
694 )
695 ))
696 stub.UpdateLogicalDeviceFlowGroupTable(FlowGroupTableUpdate(
697 id=logical_device_id,
698 group_mod=ofp.ofp_group_mod(
699 command=ofp.OFPGC_DELETE,
700 group_id=ofp.OFPG_ALL
701 )
702 ))
703 self.poutput('success')
704
705 complete_delete_all_flows = VolthaCli.complete_logical_device
706
Zsolt Haraszti80175202016-12-24 00:17:51 -0800707 def do_send_simulated_upstream_eapol(self, line):
Zsolt Harasztid036b7e2016-12-23 15:36:01 -0800708 """
709 Send an EAPOL upstream from a simulated OLT
710 """
Zsolt Haraszti80175202016-12-24 00:17:51 -0800711 device_id = line or self.default_device_id
Zsolt Harasztid036b7e2016-12-23 15:36:01 -0800712 requests.get('http://{}/devices/{}/test_eapol_in'.format(
713 self.voltha_sim_rest, device_id
714 ))
715
Zsolt Haraszti80175202016-12-24 00:17:51 -0800716 complete_send_simulated_upstream_eapol = VolthaCli.complete_device
717
718 def do_inject_eapol_start(self, line):
Zsolt Harasztid036b7e2016-12-23 15:36:01 -0800719 """
720 Send out an an EAPOL start message into the given Unix interface
721 """
722 pass
Zsolt Harasztia133a452016-12-22 01:26:57 -0800723
724
725if __name__ == '__main__':
Zsolt Haraszti9b485fb2016-12-26 23:11:15 -0800726
727 parser = argparse.ArgumentParser()
728
729 _help = '<hostname>:<port> to consul agent (default: %s)' % defs['consul']
730 parser.add_argument(
731 '-C', '--consul', action='store', default=defs['consul'], help=_help)
732
733 _help = 'Lookup Voltha endpoints based on service entries in Consul'
734 parser.add_argument(
735 '-L', '--lookup', action='store_true', help=_help)
736
737 _help = '<hostname>:<port> of Voltha gRPC service (default={})'.format(
738 defs['voltha_grpc_endpoint'])
739 parser.add_argument('-g', '--grpc-endpoint', action='store',
740 default=defs['voltha_grpc_endpoint'], help=_help)
741
742 _help = '<hostname>:<port> of Voltha simulated adapter backend for ' \
743 'testing (default={})'.format(
744 defs['voltha_sim_rest_endpoint'])
745 parser.add_argument('-s', '--sim-rest-endpoint', action='store',
746 default=defs['voltha_sim_rest_endpoint'], help=_help)
747
748 args = parser.parse_args()
749
750 if args.lookup:
751 host = args.consul.split(':')[0].strip()
752 port = int(args.consul.split(':')[1].strip())
753 consul = Consul(host=host, port=port)
754
755 _, services = consul.catalog.service('voltha-grpc')
756 if not services:
757 print('No voltha-grpc service registered in consul; exiting')
758 sys.exit(1)
759 args.grpc_endpoint = '{}:{}'.format(services[0]['ServiceAddress'],
760 services[0]['ServicePort'])
761
762 _, services = consul.catalog.service('voltha-sim-rest')
763 if not services:
764 print('No voltha-sim-rest service registered in consul; exiting')
765 sys.exit(1)
766 args.sim_rest_endpoint = '{}:{}'.format(services[0]['ServiceAddress'],
767 services[0]['ServicePort'])
768
769 c = VolthaCli(args.grpc_endpoint, args.sim_rest_endpoint)
Zsolt Haraszti80175202016-12-24 00:17:51 -0800770 c.poutput(banner)
Zsolt Harasztia133a452016-12-22 01:26:57 -0800771 c.load_history()
772 c.cmdloop()
773 c.save_history()