blob: c0dbef6b00fc55999672ce5bfd0d29e25d88c0b0 [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
Stephane Barbarie4db8ca22017-04-24 10:30:20 -040020import sys
Zsolt Harasztid036b7e2016-12-23 15:36:01 -080021from optparse import make_option
Zsolt Haraszti80175202016-12-24 00:17:51 -080022from time import sleep, time
Zsolt Harasztid036b7e2016-12-23 15:36:01 -080023
Zsolt Harasztia133a452016-12-22 01:26:57 -080024import grpc
Zsolt Harasztid036b7e2016-12-23 15:36:01 -080025import requests
26from cmd2 import Cmd, options
Stephane Barbarie4db8ca22017-04-24 10:30:20 -040027from consul import Consul
Zsolt Harasztid036b7e2016-12-23 15:36:01 -080028from 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
Nikolay Titov89004ec2017-06-19 18:22:42 -040032from cli.xpon import XponCli
Stephane Barbarie4db8ca22017-04-24 10:30:20 -040033from cli.alarm_filters import AlarmFiltersCli
Zsolt Harasztid036b7e2016-12-23 15:36:01 -080034from cli.logical_device import LogicalDeviceCli
Stephane Barbarie4db8ca22017-04-24 10:30:20 -040035from cli.table import print_pb_list_as_table
Zsolt Harasztid036b7e2016-12-23 15:36:01 -080036from voltha.core.flow_decomposer import *
Zsolt Harasztia133a452016-12-22 01:26:57 -080037from voltha.protos import third_party
38from voltha.protos import voltha_pb2
Zsolt Haraszti85f12852016-12-24 08:30:58 -080039from voltha.protos.openflow_13_pb2 import FlowTableUpdate, FlowGroupTableUpdate
Zsolt Harasztid036b7e2016-12-23 15:36:01 -080040
Zsolt Harasztia133a452016-12-22 01:26:57 -080041_ = third_party
Stephane Barbarie4db8ca22017-04-24 10:30:20 -040042from cli.utils import pb2dict
Zsolt Haraszti9b485fb2016-12-26 23:11:15 -080043
44defs = dict(
45 # config=os.environ.get('CONFIG', './cli.yml'),
46 consul=os.environ.get('CONSUL', 'localhost:8500'),
47 voltha_grpc_endpoint=os.environ.get('VOLTHA_GRPC_ENDPOINT',
48 'localhost:50055'),
49 voltha_sim_rest_endpoint=os.environ.get('VOLTHA_SIM_REST_ENDPOINT',
50 'localhost:18880'),
khenaidoo108f05c2017-07-06 11:15:29 -040051 global_request=os.environ.get('GLOBAL_REQUEST', False)
Zsolt Haraszti9b485fb2016-12-26 23:11:15 -080052)
53
Zsolt Harasztid036b7e2016-12-23 15:36:01 -080054banner = """\
Zsolt Haraszti313c4be2016-12-27 11:06:53 -080055 _ _ _ ___ _ ___
56__ _____| | |_| |_ __ _ / __| | |_ _|
57\ V / _ \ | _| ' \/ _` | | (__| |__ | |
58 \_/\___/_|\__|_||_\__,_| \___|____|___|
Zsolt Haraszti80175202016-12-24 00:17:51 -080059(to exit type quit or hit Ctrl-D)
Zsolt Harasztid036b7e2016-12-23 15:36:01 -080060"""
Zsolt Harasztia133a452016-12-22 01:26:57 -080061
Zsolt Harasztia133a452016-12-22 01:26:57 -080062
Stephane Barbarie4db8ca22017-04-24 10:30:20 -040063class VolthaCli(Cmd):
Zsolt Harasztia133a452016-12-22 01:26:57 -080064 prompt = 'voltha'
65 history_file_name = '.voltha_cli_history'
Zsolt Harasztid036b7e2016-12-23 15:36:01 -080066
67 # Settable CLI parameters
68 voltha_grpc = 'localhost:50055'
69 voltha_sim_rest = 'localhost:18880'
khenaidoo108f05c2017-07-06 11:15:29 -040070 global_request = False
Zsolt Harasztia133a452016-12-22 01:26:57 -080071 max_history_lines = 500
Zsolt Harasztid036b7e2016-12-23 15:36:01 -080072 default_device_id = None
73 default_logical_device_id = None
Zsolt Harasztia133a452016-12-22 01:26:57 -080074
75 Cmd.settable.update(dict(
Zsolt Harasztid036b7e2016-12-23 15:36:01 -080076 voltha_grpc='Voltha GRPC endpoint in form of <host>:<port>',
77 voltha_sim_rest='Voltha simulation back door for testing in form '
78 'of <host>:<port>',
79 max_history_lines='Maximum number of history lines stored across '
80 'sessions',
81 default_device_id='Device id used when no device id is specified',
82 default_logical_device_id='Logical device id used when no device id '
83 'is specified',
Zsolt Harasztia133a452016-12-22 01:26:57 -080084 ))
85
Zsolt Haraszti9b485fb2016-12-26 23:11:15 -080086 # cleanup of superfluous commands from cmd2
Zsolt Haraszti80175202016-12-24 00:17:51 -080087 del Cmd.do_cmdenvironment
Steve Crooks05f24522017-02-27 13:32:27 -050088 del Cmd.do_load
Zsolt Haraszti80175202016-12-24 00:17:51 -080089 del Cmd.do__relative_load
Zsolt Haraszti80175202016-12-24 00:17:51 -080090
khenaidoo108f05c2017-07-06 11:15:29 -040091 def __init__(self, voltha_grpc, voltha_sim_rest, global_request=False):
Zsolt Haraszti9b485fb2016-12-26 23:11:15 -080092 VolthaCli.voltha_grpc = voltha_grpc
93 VolthaCli.voltha_sim_rest = voltha_sim_rest
khenaidoo108f05c2017-07-06 11:15:29 -040094 VolthaCli.global_request = global_request
Zsolt Haraszti9b485fb2016-12-26 23:11:15 -080095 Cmd.__init__(self)
Zsolt Harasztia133a452016-12-22 01:26:57 -080096 self.prompt = '(' + self.colorize(
Zsolt Haraszti80175202016-12-24 00:17:51 -080097 self.colorize(self.prompt, 'blue'), 'bold') + ') '
Zsolt Harasztia133a452016-12-22 01:26:57 -080098 self.channel = None
khenaidoo108f05c2017-07-06 11:15:29 -040099 self.stub = None
Zsolt Haraszti80175202016-12-24 00:17:51 -0800100 self.device_ids_cache = None
101 self.device_ids_cache_ts = time()
102 self.logical_device_ids_cache = None
103 self.logical_device_ids_cache_ts = time()
Zsolt Harasztia133a452016-12-22 01:26:57 -0800104
Zsolt Haraszti9b485fb2016-12-26 23:11:15 -0800105 # we override cmd2's method to avoid its optparse conflicting with our
106 # command line parsing
107 def cmdloop(self):
108 self._cmdloop()
109
Zsolt Harasztia133a452016-12-22 01:26:57 -0800110 def load_history(self):
111 """Load saved command history from local history file"""
112 try:
113 with file(self.history_file_name, 'r') as f:
114 for line in f.readlines():
115 stripped_line = line.strip()
116 self.history.append(stripped_line)
117 readline.add_history(stripped_line)
118 except IOError:
119 pass # ignore if file cannot be read
120
121 def save_history(self):
122 try:
Zsolt Haraszti80175202016-12-24 00:17:51 -0800123 with open(self.history_file_name, 'w') as f:
Zsolt Harasztia133a452016-12-22 01:26:57 -0800124 f.write('\n'.join(self.history[-self.max_history_lines:]))
Zsolt Haraszti80175202016-12-24 00:17:51 -0800125 except IOError as e:
126 self.perror('Could not save history in {}: {}'.format(
127 self.history_file_name, e))
Zsolt Harasztia133a452016-12-22 01:26:57 -0800128 else:
Zsolt Haraszti9b485fb2016-12-26 23:11:15 -0800129 self.poutput('History saved as {}'.format(
Zsolt Haraszti80175202016-12-24 00:17:51 -0800130 self.history_file_name))
131
132 def perror(self, errmsg, statement=None):
133 # Touch it up to make sure error is prefixed and colored
134 Cmd.perror(self, self.colorize('***ERROR: ', 'red') + errmsg,
135 statement)
Zsolt Harasztia133a452016-12-22 01:26:57 -0800136
137 def get_channel(self):
138 if self.channel is None:
139 self.channel = grpc.insecure_channel(self.voltha_grpc)
140 return self.channel
141
khenaidoo108f05c2017-07-06 11:15:29 -0400142 def get_stub(self):
143 if self.stub is None:
144 self.stub = \
145 voltha_pb2.VolthaGlobalServiceStub(self.get_channel()) \
146 if self.global_request else \
147 voltha_pb2.VolthaLocalServiceStub(self.get_channel())
148 return self.stub
149
Zsolt Haraszti80175202016-12-24 00:17:51 -0800150 # ~~~~~~~~~~~~~~~~~ ACTUAL COMMAND IMPLEMENTATIONS ~~~~~~~~~~~~~~~~~~~~~~~~
Zsolt Harasztid036b7e2016-12-23 15:36:01 -0800151
Zsolt Haraszti80175202016-12-24 00:17:51 -0800152 def do_reset_history(self, line):
Zsolt Harasztia133a452016-12-22 01:26:57 -0800153 """Reset CLI history"""
154 while self.history:
155 self.history.pop()
156
Zsolt Haraszti80175202016-12-24 00:17:51 -0800157 def do_launch(self, line):
Zsolt Harasztia133a452016-12-22 01:26:57 -0800158 """If Voltha is not running yet, launch it"""
Zsolt Haraszti80175202016-12-24 00:17:51 -0800159 raise NotImplementedError('not implemented yet')
Zsolt Harasztia133a452016-12-22 01:26:57 -0800160
Zsolt Haraszti80175202016-12-24 00:17:51 -0800161 def do_restart(self, line):
Zsolt Harasztia133a452016-12-22 01:26:57 -0800162 """Launch Voltha, but if it is already running, terminate it first"""
163 pass
164
Zsolt Haraszti80175202016-12-24 00:17:51 -0800165 def do_adapters(self, line):
166 """List loaded adapter"""
khenaidoo108f05c2017-07-06 11:15:29 -0400167 stub = self.get_stub()
Zsolt Haraszti80175202016-12-24 00:17:51 -0800168 res = stub.ListAdapters(Empty())
Sergio Slobodrian6e9fb692017-03-17 14:46:33 -0400169 omit_fields = {'config.log_level', 'logical_device_ids'}
Zsolt Haraszti85f12852016-12-24 08:30:58 -0800170 print_pb_list_as_table('Adapters:', res.items, omit_fields, self.poutput)
Zsolt Haraszti80175202016-12-24 00:17:51 -0800171
172 def get_devices(self):
khenaidoo108f05c2017-07-06 11:15:29 -0400173 stub = self.get_stub()
Zsolt Harasztia133a452016-12-22 01:26:57 -0800174 res = stub.ListDevices(Empty())
Zsolt Haraszti80175202016-12-24 00:17:51 -0800175 return res.items
Zsolt Harasztia133a452016-12-22 01:26:57 -0800176
Zsolt Haraszti80175202016-12-24 00:17:51 -0800177 def get_logical_devices(self):
khenaidoo108f05c2017-07-06 11:15:29 -0400178 stub = self.get_stub()
Zsolt Haraszti80175202016-12-24 00:17:51 -0800179 res = stub.ListLogicalDevices(Empty())
180 return res.items
181
182 def do_devices(self, line):
183 """List devices registered in Voltha"""
184 devices = self.get_devices()
185 omit_fields = {
186 'adapter',
187 'vendor',
188 'model',
189 'hardware_version',
ggowdru236bd952017-06-20 20:32:55 -0700190 'images',
Zsolt Haraszti80175202016-12-24 00:17:51 -0800191 'firmware_version',
Sergio Slobodriana95f99b2017-03-21 10:22:47 -0400192 'serial_number'
Zsolt Haraszti80175202016-12-24 00:17:51 -0800193 }
Zsolt Haraszti85f12852016-12-24 08:30:58 -0800194 print_pb_list_as_table('Devices:', devices, omit_fields, self.poutput)
Zsolt Haraszti80175202016-12-24 00:17:51 -0800195
196 def do_logical_devices(self, line):
Zsolt Harasztia133a452016-12-22 01:26:57 -0800197 """List logical devices in Voltha"""
khenaidoo108f05c2017-07-06 11:15:29 -0400198 stub = self.get_stub()
Zsolt Harasztia133a452016-12-22 01:26:57 -0800199 res = stub.ListLogicalDevices(Empty())
Zsolt Haraszti80175202016-12-24 00:17:51 -0800200 omit_fields = {
201 'desc.mfr_desc',
202 'desc.hw_desc',
203 'desc.sw_desc',
204 'desc.dp_desc',
205 'desc.serial_number',
Sergio Slobodriana95f99b2017-03-21 10:22:47 -0400206 'switch_features.capabilities'
Zsolt Haraszti80175202016-12-24 00:17:51 -0800207 }
Zsolt Haraszti85f12852016-12-24 08:30:58 -0800208 print_pb_list_as_table('Logical devices:', res.items, omit_fields,
209 self.poutput)
Zsolt Harasztia133a452016-12-22 01:26:57 -0800210
Zsolt Haraszti80175202016-12-24 00:17:51 -0800211 def do_device(self, line):
Zsolt Harasztia133a452016-12-22 01:26:57 -0800212 """Enter device level command mode"""
Zsolt Haraszti80175202016-12-24 00:17:51 -0800213 device_id = line.strip() or self.default_device_id
Zsolt Harasztid036b7e2016-12-23 15:36:01 -0800214 if not device_id:
215 raise Exception('<device-id> parameter needed')
khenaidoo108f05c2017-07-06 11:15:29 -0400216 sub = DeviceCli(device_id, self.get_stub)
Zsolt Harasztia133a452016-12-22 01:26:57 -0800217 sub.cmdloop()
218
Zsolt Haraszti80175202016-12-24 00:17:51 -0800219 def do_logical_device(self, line):
Zsolt Harasztia133a452016-12-22 01:26:57 -0800220 """Enter logical device level command mode"""
Zsolt Haraszti80175202016-12-24 00:17:51 -0800221 logical_device_id = line.strip() or self.default_logical_device_id
Zsolt Harasztid036b7e2016-12-23 15:36:01 -0800222 if not logical_device_id:
223 raise Exception('<logical-device-id> parameter needed')
khenaidoo108f05c2017-07-06 11:15:29 -0400224 sub = LogicalDeviceCli(logical_device_id, self.get_stub)
Zsolt Harasztia133a452016-12-22 01:26:57 -0800225 sub.cmdloop()
226
Zsolt Haraszti80175202016-12-24 00:17:51 -0800227 def device_ids(self, force_refresh=False):
228 if force_refresh or self.device_ids is None or \
Stephane Barbarie4db8ca22017-04-24 10:30:20 -0400229 (time() - self.device_ids_cache_ts) > 1:
Zsolt Haraszti80175202016-12-24 00:17:51 -0800230 self.device_ids_cache = [d.id for d in self.get_devices()]
231 self.device_ids_cache_ts = time()
232 return self.device_ids_cache
233
234 def logical_device_ids(self, force_refresh=False):
235 if force_refresh or self.logical_device_ids is None or \
Stephane Barbarie4db8ca22017-04-24 10:30:20 -0400236 (time() - self.logical_device_ids_cache_ts) > 1:
Zsolt Haraszti80175202016-12-24 00:17:51 -0800237 self.logical_device_ids_cache = [d.id for d
238 in self.get_logical_devices()]
239 self.logical_device_ids_cache_ts = time()
240 return self.logical_device_ids_cache
241
242 def complete_device(self, text, line, begidx, endidx):
243 if not text:
244 completions = self.device_ids()[:]
245 else:
246 completions = [d for d in self.device_ids() if d.startswith(text)]
247 return completions
248
249 def complete_logical_device(self, text, line, begidx, endidx):
250 if not text:
251 completions = self.logical_device_ids()[:]
252 else:
253 completions = [d for d in self.logical_device_ids()
254 if d.startswith(text)]
255 return completions
256
Nikolay Titov89004ec2017-06-19 18:22:42 -0400257 def do_xpon(self, line):
258 """xpon <optional> [device_ID] - Enter xpon level command mode"""
259 device_id = line.strip()
260 if not device_id:
261 sub = XponCli(self.get_channel, "")
262 else:
263 sub = XponCli(self.get_channel, device_id)
264 sub.cmdloop()
265
Zsolt Haraszti80175202016-12-24 00:17:51 -0800266 def do_pdb(self, line):
Zsolt Harasztia133a452016-12-22 01:26:57 -0800267 """Launch PDB debug prompt in CLI (for CLI development)"""
268 from pdb import set_trace
269 set_trace()
270
Zsolt Haraszti80175202016-12-24 00:17:51 -0800271 def do_health(self, line):
Zsolt Harasztia133a452016-12-22 01:26:57 -0800272 """Show connectivity status to Voltha status"""
273 stub = voltha_pb2.HealthServiceStub(self.get_channel())
274 res = stub.GetHealthStatus(Empty())
Zsolt Haraszti80175202016-12-24 00:17:51 -0800275 self.poutput(dumps(pb2dict(res), indent=4))
Zsolt Harasztia133a452016-12-22 01:26:57 -0800276
Zsolt Harasztid036b7e2016-12-23 15:36:01 -0800277 @options([
278 make_option('-t', '--device-type', action="store", dest='device_type',
Stephane Barbarie4db8ca22017-04-24 10:30:20 -0400279 help="Device type", default='simulated_olt'),
Zsolt Harasztid036b7e2016-12-23 15:36:01 -0800280 make_option('-m', '--mac-address', action='store', dest='mac_address',
281 default='00:0c:e2:31:40:00'),
282 make_option('-i', '--ip-address', action='store', dest='ip_address'),
Zsolt Haraszti656ecc62016-12-28 15:08:23 -0800283 make_option('-H', '--host_and_port', action='store',
284 dest='host_and_port'),
Zsolt Harasztid036b7e2016-12-23 15:36:01 -0800285 ])
Zsolt Haraszti80175202016-12-24 00:17:51 -0800286 def do_preprovision_olt(self, line, opts):
Zsolt Harasztid036b7e2016-12-23 15:36:01 -0800287 """Preprovision a new OLT with given device type"""
khenaidoo108f05c2017-07-06 11:15:29 -0400288 stub = self.get_stub()
Zsolt Harasztid036b7e2016-12-23 15:36:01 -0800289 kw = dict(type=opts.device_type)
Zsolt Haraszti656ecc62016-12-28 15:08:23 -0800290 if opts.host_and_port:
291 kw['host_and_port'] = opts.host_and_port
292 elif opts.ip_address:
Zsolt Harasztid036b7e2016-12-23 15:36:01 -0800293 kw['ipv4_address'] = opts.ip_address
294 elif opts.mac_address:
295 kw['mac_address'] = opts.mac_address
296 else:
297 raise Exception('Either IP address or Mac Address is needed')
Chip Boling90b224d2017-06-02 11:51:48 -0500298 # Pass any extra arguments past '--' to the device as custom arguments
299 kw['extra_args'] = line
300
Zsolt Harasztid036b7e2016-12-23 15:36:01 -0800301 device = voltha_pb2.Device(**kw)
302 device = stub.CreateDevice(device)
Zsolt Haraszti80175202016-12-24 00:17:51 -0800303 self.poutput('success (device id = {})'.format(device.id))
Zsolt Harasztid036b7e2016-12-23 15:36:01 -0800304 self.default_device_id = device.id
Zsolt Harasztia133a452016-12-22 01:26:57 -0800305
Khen Nursimulud068d812017-03-06 11:44:18 -0500306 def do_enable(self, line):
Zsolt Harasztid036b7e2016-12-23 15:36:01 -0800307 """
Khen Nursimulud068d812017-03-06 11:44:18 -0500308 Enable a device. If the <id> is not provided, it will be on the last
309 pre-provisioned device.
Zsolt Harasztid036b7e2016-12-23 15:36:01 -0800310 """
Zsolt Haraszti80175202016-12-24 00:17:51 -0800311 device_id = line or self.default_device_id
Khen Nursimulu29e75502017-03-07 17:26:50 -0500312 self.poutput('enabling {}'.format(device_id))
Khen Nursimuluc60afa12017-03-13 14:33:50 -0400313 try:
khenaidoo108f05c2017-07-06 11:15:29 -0400314 stub = self.get_stub()
Khen Nursimuluc60afa12017-03-13 14:33:50 -0400315 stub.EnableDevice(voltha_pb2.ID(id=device_id))
Zsolt Harasztia133a452016-12-22 01:26:57 -0800316
Khen Nursimuluc60afa12017-03-13 14:33:50 -0400317 while True:
318 device = stub.GetDevice(voltha_pb2.ID(id=device_id))
319 # If this is an OLT then acquire logical device id
320 if device.oper_status == voltha_pb2.OperStatus.ACTIVE:
321 if device.type.endswith('_olt'):
322 assert device.parent_id
323 self.default_logical_device_id = device.parent_id
324 self.poutput('success (logical device id = {})'.format(
325 self.default_logical_device_id))
326 else:
327 self.poutput('success (device id = {})'.format(device.id))
328 break
329 self.poutput('waiting for device to be enabled...')
330 sleep(.5)
331 except Exception, e:
332 self.poutput('Error enabling {}. Error:{}'.format(device_id, e))
Zsolt Harasztid036b7e2016-12-23 15:36:01 -0800333
Zsolt Haraszti50cae7d2017-01-08 22:27:07 -0800334 complete_activate_olt = complete_device
335
Khen Nursimulud068d812017-03-06 11:44:18 -0500336 def do_reboot(self, line):
337 """
338 Rebooting a device. ID of the device needs to be provided
339 """
340 device_id = line or self.default_device_id
341 self.poutput('rebooting {}'.format(device_id))
Khen Nursimuluc60afa12017-03-13 14:33:50 -0400342 try:
khenaidoo108f05c2017-07-06 11:15:29 -0400343 stub = self.get_stub()
Khen Nursimuluc60afa12017-03-13 14:33:50 -0400344 stub.RebootDevice(voltha_pb2.ID(id=device_id))
345 self.poutput('rebooted {}'.format(device_id))
346 except Exception, e:
347 self.poutput('Error rebooting {}. Error:{}'.format(device_id, e))
Khen Nursimulud068d812017-03-06 11:44:18 -0500348
sathishg5ae86222017-06-28 15:16:29 +0530349 def do_self_test(self, line):
350 """
351 Self Test a device. ID of the device needs to be provided
352 """
353 device_id = line or self.default_device_id
354 self.poutput('Self Testing {}'.format(device_id))
355 try:
khenaidoo108f05c2017-07-06 11:15:29 -0400356 stub = self.get_stub()
sathishg5ae86222017-06-28 15:16:29 +0530357 res = stub.SelfTest(voltha_pb2.ID(id=device_id))
358 self.poutput('Self Tested {}'.format(device_id))
359 self.poutput(dumps(pb2dict(res), indent=4))
360 except Exception, e:
361 self.poutput('Error in self test {}. Error:{}'.format(device_id, e))
362
Khen Nursimulud068d812017-03-06 11:44:18 -0500363 def do_delete(self, line):
364 """
365 Deleting a device. ID of the device needs to be provided
366 """
367 device_id = line or self.default_device_id
368 self.poutput('deleting {}'.format(device_id))
Khen Nursimuluc60afa12017-03-13 14:33:50 -0400369 try:
khenaidoo108f05c2017-07-06 11:15:29 -0400370 stub = self.get_stub()
Khen Nursimuluc60afa12017-03-13 14:33:50 -0400371 stub.DeleteDevice(voltha_pb2.ID(id=device_id))
372 self.poutput('deleted {}'.format(device_id))
373 except Exception, e:
374 self.poutput('Error deleting {}. Error:{}'.format(device_id, e))
Khen Nursimulud068d812017-03-06 11:44:18 -0500375
376 def do_disable(self, line):
377 """
378 Disable a device. ID of the device needs to be provided
379 """
380 device_id = line
381 self.poutput('disabling {}'.format(device_id))
Khen Nursimuluc60afa12017-03-13 14:33:50 -0400382 try:
khenaidoo108f05c2017-07-06 11:15:29 -0400383 stub = self.get_stub()
Khen Nursimuluc60afa12017-03-13 14:33:50 -0400384 stub.DisableDevice(voltha_pb2.ID(id=device_id))
Khen Nursimulud068d812017-03-06 11:44:18 -0500385
Khen Nursimuluc60afa12017-03-13 14:33:50 -0400386 # Do device query and verify that the device admin status is
387 # DISABLED and Operational Status is unknown
388 device = stub.GetDevice(voltha_pb2.ID(id=device_id))
389 if device.oper_status == voltha_pb2.OperStatus.UNKNOWN and \
390 device.admin_state == voltha_pb2.AdminState.DISABLED:
391 self.poutput('disabled successfully {}'.format(device_id))
392 else:
393 self.poutput('disabling failed {}. Admin State:{} '
394 'Operation State: {}'.format(device_id,
395 device.admin_state,
396 device.oper_status))
397 except Exception, e:
398 self.poutput('Error disabling {}. Error:{}'.format(device_id, e))
Khen Nursimulud068d812017-03-06 11:44:18 -0500399
Zsolt Haraszti50cae7d2017-01-08 22:27:07 -0800400 def do_test(self, line):
401 """Enter test mode, which makes a bunch on new commands available"""
khenaidoo108f05c2017-07-06 11:15:29 -0400402 sub = TestCli(self.history, self.voltha_grpc,
403 self.get_stub, self.voltha_sim_rest)
Zsolt Haraszti50cae7d2017-01-08 22:27:07 -0800404 sub.cmdloop()
405
Stephane Barbarie4db8ca22017-04-24 10:30:20 -0400406 def do_alarm_filters(self, line):
khenaidoo108f05c2017-07-06 11:15:29 -0400407 sub = AlarmFiltersCli(self.get_stub)
Stephane Barbarie4db8ca22017-04-24 10:30:20 -0400408 sub.cmdloop()
409
Zsolt Haraszti50cae7d2017-01-08 22:27:07 -0800410
411class TestCli(VolthaCli):
khenaidoo108f05c2017-07-06 11:15:29 -0400412 def __init__(self, history, voltha_grpc, get_stub, voltha_sim_rest):
Zsolt Haraszti50cae7d2017-01-08 22:27:07 -0800413 VolthaCli.__init__(self, voltha_grpc, voltha_sim_rest)
414 self.history = history
khenaidoo108f05c2017-07-06 11:15:29 -0400415 self.get_stub = get_stub
Zsolt Haraszti50cae7d2017-01-08 22:27:07 -0800416 self.prompt = '(' + self.colorize(self.colorize('test', 'cyan'),
Stephane Barbarie4db8ca22017-04-24 10:30:20 -0400417 'bold') + ') '
Zsolt Haraszti50cae7d2017-01-08 22:27:07 -0800418
419 def get_device(self, device_id, depth=0):
khenaidoo108f05c2017-07-06 11:15:29 -0400420 stub = self.get_stub()
Zsolt Haraszti50cae7d2017-01-08 22:27:07 -0800421 res = stub.GetDevice(voltha_pb2.ID(id=device_id),
Stephane Barbarie4db8ca22017-04-24 10:30:20 -0400422 metadata=(('get-depth', str(depth)),))
Zsolt Haraszti50cae7d2017-01-08 22:27:07 -0800423 return res
Zsolt Haraszti80175202016-12-24 00:17:51 -0800424
425 def do_arrive_onus(self, line):
Zsolt Harasztid036b7e2016-12-23 15:36:01 -0800426 """
Zsolt Haraszti85f12852016-12-24 08:30:58 -0800427 Simulate the arrival of ONUs (available only on simulated_olt)
Zsolt Harasztid036b7e2016-12-23 15:36:01 -0800428 """
Zsolt Haraszti80175202016-12-24 00:17:51 -0800429 device_id = line or self.default_device_id
Zsolt Haraszti85f12852016-12-24 08:30:58 -0800430
431 # verify that device is of type simulated_olt
432 device = self.get_device(device_id)
433 assert device.type == 'simulated_olt', (
434 'Cannot use it on this device type (only on simulated_olt type)')
435
Zsolt Harasztid036b7e2016-12-23 15:36:01 -0800436 requests.get('http://{}/devices/{}/detect_onus'.format(
437 self.voltha_sim_rest, device_id
438 ))
439
Zsolt Haraszti80175202016-12-24 00:17:51 -0800440 complete_arrive_onus = VolthaCli.complete_device
441
Zsolt Haraszti85f12852016-12-24 08:30:58 -0800442 def get_logical_ports(self, logical_device_id):
443 """
444 Return the NNI port number and the first usable UNI port of logical
445 device, and the vlan associated with the latter.
446 """
khenaidoo108f05c2017-07-06 11:15:29 -0400447 stub = self.get_stub()
Zsolt Haraszti85f12852016-12-24 08:30:58 -0800448 ports = stub.ListLogicalDevicePorts(
449 voltha_pb2.ID(id=logical_device_id)).items
Zsolt Harasztib9a5f752017-02-11 06:07:08 -0800450 nni = None
451 unis = []
Zsolt Haraszti85f12852016-12-24 08:30:58 -0800452 for port in ports:
Zsolt Harasztib9a5f752017-02-11 06:07:08 -0800453 if port.root_port:
454 assert nni is None, "There shall be only one root port"
Zsolt Haraszti85f12852016-12-24 08:30:58 -0800455 nni = port.ofp_port.port_no
Zsolt Harasztib9a5f752017-02-11 06:07:08 -0800456 else:
Zsolt Haraszti85f12852016-12-24 08:30:58 -0800457 uni = port.ofp_port.port_no
458 uni_device = self.get_device(port.device_id)
459 vlan = uni_device.vlan
Zsolt Harasztib9a5f752017-02-11 06:07:08 -0800460 unis.append((uni, vlan))
461
462 assert nni is not None, "No NNI port found"
463 assert unis, "Not a single UNI?"
464
465 return nni, unis
Zsolt Haraszti85f12852016-12-24 08:30:58 -0800466
Zsolt Haraszti80175202016-12-24 00:17:51 -0800467 def do_install_eapol_flow(self, line):
Zsolt Harasztid036b7e2016-12-23 15:36:01 -0800468 """
469 Install an EAPOL flow on the given logical device. If device is not
470 given, it will be applied to logical device of the last pre-provisioned
471 OLT device.
472 """
Zsolt Haraszti80175202016-12-24 00:17:51 -0800473 logical_device_id = line or self.default_logical_device_id
Zsolt Haraszti85f12852016-12-24 08:30:58 -0800474
475 # gather NNI and UNI port IDs
Zsolt Harasztib9a5f752017-02-11 06:07:08 -0800476 nni_port_no, unis = self.get_logical_ports(logical_device_id)
Zsolt Haraszti85f12852016-12-24 08:30:58 -0800477
478 # construct and push flow rule
khenaidoo108f05c2017-07-06 11:15:29 -0400479 stub = self.get_stub()
Zsolt Harasztib9a5f752017-02-11 06:07:08 -0800480 for uni_port_no, _ in unis:
481 update = FlowTableUpdate(
482 id=logical_device_id,
483 flow_mod=mk_simple_flow_mod(
484 priority=2000,
485 match_fields=[in_port(uni_port_no), eth_type(0x888e)],
486 actions=[
487 # push_vlan(0x8100),
488 # set_field(vlan_vid(4096 + 4000)),
489 output(ofp.OFPP_CONTROLLER)
490 ]
491 )
492 )
493 res = stub.UpdateLogicalDeviceFlowTable(update)
494 self.poutput('success for uni {} ({})'.format(uni_port_no, res))
Zsolt Harasztid036b7e2016-12-23 15:36:01 -0800495
Zsolt Haraszti80175202016-12-24 00:17:51 -0800496 complete_install_eapol_flow = VolthaCli.complete_logical_device
497
Zsolt Haraszti85f12852016-12-24 08:30:58 -0800498 def do_install_all_controller_bound_flows(self, line):
499 """
500 Install all flow rules for controller bound flows, including EAPOL,
501 IGMP and DHCP. If device is not given, it will be applied to logical
502 device of the last pre-provisioned OLT device.
503 """
504 logical_device_id = line or self.default_logical_device_id
505
506 # gather NNI and UNI port IDs
Zsolt Harasztib9a5f752017-02-11 06:07:08 -0800507 nni_port_no, unis = self.get_logical_ports(logical_device_id)
Zsolt Haraszti85f12852016-12-24 08:30:58 -0800508
509 # construct and push flow rules
khenaidoo108f05c2017-07-06 11:15:29 -0400510 stub = self.get_stub()
Zsolt Haraszti85f12852016-12-24 08:30:58 -0800511
Zsolt Harasztib9a5f752017-02-11 06:07:08 -0800512 for uni_port_no, _ in unis:
513 stub.UpdateLogicalDeviceFlowTable(FlowTableUpdate(
514 id=logical_device_id,
515 flow_mod=mk_simple_flow_mod(
516 priority=2000,
517 match_fields=[
518 in_port(uni_port_no),
519 eth_type(0x888e)
520 ],
521 actions=[output(ofp.OFPP_CONTROLLER)]
522 )
523 ))
524 stub.UpdateLogicalDeviceFlowTable(FlowTableUpdate(
525 id=logical_device_id,
526 flow_mod=mk_simple_flow_mod(
527 priority=1000,
528 match_fields=[
529 in_port(uni_port_no),
530 eth_type(0x800),
531 ip_proto(2)
532 ],
533 actions=[output(ofp.OFPP_CONTROLLER)]
534 )
535 ))
536 stub.UpdateLogicalDeviceFlowTable(FlowTableUpdate(
537 id=logical_device_id,
538 flow_mod=mk_simple_flow_mod(
539 priority=1000,
540 match_fields=[
541 in_port(uni_port_no),
542 eth_type(0x800),
543 ip_proto(17),
544 udp_dst(67)
545 ],
546 actions=[output(ofp.OFPP_CONTROLLER)]
547 )
548 ))
Zsolt Haraszti85f12852016-12-24 08:30:58 -0800549 self.poutput('success')
550
551 complete_install_all_controller_bound_flows = \
552 VolthaCli.complete_logical_device
553
554 def do_install_all_sample_flows(self, line):
555 """
556 Install all flows that are representative of the virtualized access
557 scenario in a PON network.
558 """
559 logical_device_id = line or self.default_logical_device_id
560
561 # gather NNI and UNI port IDs
Zsolt Harasztib9a5f752017-02-11 06:07:08 -0800562 nni_port_no, unis = self.get_logical_ports(logical_device_id)
Zsolt Haraszti85f12852016-12-24 08:30:58 -0800563
564 # construct and push flow rules
khenaidoo108f05c2017-07-06 11:15:29 -0400565 stub = self.get_stub()
Zsolt Haraszti85f12852016-12-24 08:30:58 -0800566
Zsolt Harasztib9a5f752017-02-11 06:07:08 -0800567 for uni_port_no, c_vid in unis:
Zsolt Harasztib9a5f752017-02-11 06:07:08 -0800568 # Controller-bound flows
569 stub.UpdateLogicalDeviceFlowTable(FlowTableUpdate(
570 id=logical_device_id,
571 flow_mod=mk_simple_flow_mod(
572 priority=2000,
573 match_fields=[in_port(uni_port_no), eth_type(0x888e)],
574 actions=[
575 # push_vlan(0x8100),
576 # set_field(vlan_vid(4096 + 4000)),
577 output(ofp.OFPP_CONTROLLER)
578 ]
579 )
580 ))
581 stub.UpdateLogicalDeviceFlowTable(FlowTableUpdate(
582 id=logical_device_id,
583 flow_mod=mk_simple_flow_mod(
584 priority=1000,
585 match_fields=[eth_type(0x800), ip_proto(2)],
586 actions=[output(ofp.OFPP_CONTROLLER)]
587 )
588 ))
589 stub.UpdateLogicalDeviceFlowTable(FlowTableUpdate(
590 id=logical_device_id,
591 flow_mod=mk_simple_flow_mod(
592 priority=1000,
593 match_fields=[eth_type(0x800), ip_proto(17), udp_dst(67)],
594 actions=[output(ofp.OFPP_CONTROLLER)]
595 )
596 ))
597
598 # Unicast flows:
599 # Downstream flow 1
600 stub.UpdateLogicalDeviceFlowTable(FlowTableUpdate(
601 id=logical_device_id,
602 flow_mod=mk_simple_flow_mod(
603 priority=500,
604 match_fields=[
605 in_port(nni_port_no),
606 vlan_vid(4096 + 1000),
607 metadata(c_vid) # here to mimic an ONOS artifact
608 ],
609 actions=[pop_vlan()],
610 next_table_id=1
611 )
612 ))
613 # Downstream flow 2
614 stub.UpdateLogicalDeviceFlowTable(FlowTableUpdate(
615 id=logical_device_id,
616 flow_mod=mk_simple_flow_mod(
617 priority=500,
618 table_id=1,
619 match_fields=[in_port(nni_port_no), vlan_vid(4096 + c_vid)],
620 actions=[set_field(vlan_vid(4096 + 0)), output(uni_port_no)]
621 )
622 ))
623 # Upstream flow 1 for 0-tagged case
624 stub.UpdateLogicalDeviceFlowTable(FlowTableUpdate(
625 id=logical_device_id,
626 flow_mod=mk_simple_flow_mod(
627 priority=500,
628 match_fields=[in_port(uni_port_no), vlan_vid(4096 + 0)],
629 actions=[set_field(vlan_vid(4096 + c_vid))],
630 next_table_id=1
631 )
632 ))
633 # Upstream flow 1 for untagged case
634 stub.UpdateLogicalDeviceFlowTable(FlowTableUpdate(
635 id=logical_device_id,
636 flow_mod=mk_simple_flow_mod(
637 priority=500,
638 match_fields=[in_port(uni_port_no), vlan_vid(0)],
639 actions=[push_vlan(0x8100), set_field(vlan_vid(4096 + c_vid))],
640 next_table_id=1
641 )
642 ))
643 # Upstream flow 2 for s-tag
644 stub.UpdateLogicalDeviceFlowTable(FlowTableUpdate(
645 id=logical_device_id,
646 flow_mod=mk_simple_flow_mod(
647 priority=500,
648 table_id=1,
649 match_fields=[in_port(uni_port_no), vlan_vid(4096 + c_vid)],
650 actions=[
651 push_vlan(0x8100),
652 set_field(vlan_vid(4096 + 1000)),
653 output(nni_port_no)
654 ]
655 )
656 ))
Zsolt Haraszti85f12852016-12-24 08:30:58 -0800657
658 # Push a few multicast flows
Zsolt Harasztib9a5f752017-02-11 06:07:08 -0800659 # 1st with one bucket for our uni 0
Zsolt Haraszti85f12852016-12-24 08:30:58 -0800660 stub.UpdateLogicalDeviceFlowGroupTable(FlowGroupTableUpdate(
661 id=logical_device_id,
662 group_mod=mk_multicast_group_mod(
663 group_id=1,
664 buckets=[
Zsolt Harasztib9a5f752017-02-11 06:07:08 -0800665 ofp.ofp_bucket(actions=[
666 pop_vlan(),
667 output(unis[0][0])
668 ])
Zsolt Haraszti85f12852016-12-24 08:30:58 -0800669 ]
670 )
671 ))
672 stub.UpdateLogicalDeviceFlowTable(FlowTableUpdate(
673 id=logical_device_id,
674 flow_mod=mk_simple_flow_mod(
675 priority=1000,
676 match_fields=[
677 in_port(nni_port_no),
678 eth_type(0x800),
679 vlan_vid(4096 + 140),
680 ipv4_dst(0xe4010101)
681 ],
682 actions=[group(1)]
683 )
684 ))
Zsolt Harasztib9a5f752017-02-11 06:07:08 -0800685
686 # 2nd with one bucket for uni 0 and 1
Zsolt Haraszti85f12852016-12-24 08:30:58 -0800687 stub.UpdateLogicalDeviceFlowGroupTable(FlowGroupTableUpdate(
688 id=logical_device_id,
689 group_mod=mk_multicast_group_mod(
690 group_id=2,
691 buckets=[
Nathan Knuth6b7b6ff2017-02-12 03:30:48 -0800692 ofp.ofp_bucket(actions=[pop_vlan(), output(unis[0][0])])
Stephane Barbarie4db8ca22017-04-24 10:30:20 -0400693 # ofp.ofp_bucket(actions=[pop_vlan(), output(unis[1][0])])
Zsolt Haraszti85f12852016-12-24 08:30:58 -0800694 ]
695 )
696 ))
697 stub.UpdateLogicalDeviceFlowTable(FlowTableUpdate(
698 id=logical_device_id,
699 flow_mod=mk_simple_flow_mod(
700 priority=1000,
701 match_fields=[
702 in_port(nni_port_no),
703 eth_type(0x800),
704 vlan_vid(4096 + 140),
705 ipv4_dst(0xe4020202)
706 ],
707 actions=[group(2)]
708 )
709 ))
Zsolt Harasztib9a5f752017-02-11 06:07:08 -0800710
Zsolt Haraszti85f12852016-12-24 08:30:58 -0800711 # 3rd with empty bucket
712 stub.UpdateLogicalDeviceFlowGroupTable(FlowGroupTableUpdate(
713 id=logical_device_id,
714 group_mod=mk_multicast_group_mod(
715 group_id=3,
716 buckets=[]
717 )
718 ))
719 stub.UpdateLogicalDeviceFlowTable(FlowTableUpdate(
720 id=logical_device_id,
721 flow_mod=mk_simple_flow_mod(
722 priority=1000,
723 match_fields=[
724 in_port(nni_port_no),
725 eth_type(0x800),
726 vlan_vid(4096 + 140),
727 ipv4_dst(0xe4030303)
728 ],
729 actions=[group(3)]
730 )
731 ))
732
733 self.poutput('success')
734
735 complete_install_all_sample_flows = VolthaCli.complete_logical_device
736
Nathan Knuth5f4163e2017-01-11 18:21:10 -0600737 def do_install_dhcp_flows(self, line):
738 """
739 Install all dhcp flows that are representative of the virtualized access
740 scenario in a PON network.
741 """
742 logical_device_id = line or self.default_logical_device_id
743
744 # gather NNI and UNI port IDs
Zsolt Harasztib9a5f752017-02-11 06:07:08 -0800745 nni_port_no, unis = self.get_logical_ports(logical_device_id)
Nathan Knuth5f4163e2017-01-11 18:21:10 -0600746
747 # construct and push flow rules
khenaidoo108f05c2017-07-06 11:15:29 -0400748 stub = self.get_stub()
Nathan Knuth5f4163e2017-01-11 18:21:10 -0600749
750 # Controller-bound flows
Zsolt Harasztib9a5f752017-02-11 06:07:08 -0800751 for uni_port_no, _ in unis:
752 stub.UpdateLogicalDeviceFlowTable(FlowTableUpdate(
753 id=logical_device_id,
754 flow_mod=mk_simple_flow_mod(
755 priority=1000,
756 match_fields=[
757 in_port(uni_port_no),
758 eth_type(0x800),
759 ip_proto(17),
760 udp_dst(67)
761 ],
762 actions=[output(ofp.OFPP_CONTROLLER)]
763 )
764 ))
Nathan Knuth5f4163e2017-01-11 18:21:10 -0600765
766 self.poutput('success')
767
768 complete_install_dhcp_flows = VolthaCli.complete_logical_device
769
Zsolt Haraszti85f12852016-12-24 08:30:58 -0800770 def do_delete_all_flows(self, line):
771 """
772 Remove all flows and flow groups from given logical device
773 """
774 logical_device_id = line or self.default_logical_device_id
khenaidoo108f05c2017-07-06 11:15:29 -0400775 stub = self.get_stub()
Zsolt Haraszti85f12852016-12-24 08:30:58 -0800776 stub.UpdateLogicalDeviceFlowTable(FlowTableUpdate(
777 id=logical_device_id,
778 flow_mod=ofp.ofp_flow_mod(
779 command=ofp.OFPFC_DELETE,
780 table_id=ofp.OFPTT_ALL,
781 cookie_mask=0,
782 out_port=ofp.OFPP_ANY,
783 out_group=ofp.OFPG_ANY
784 )
785 ))
786 stub.UpdateLogicalDeviceFlowGroupTable(FlowGroupTableUpdate(
787 id=logical_device_id,
788 group_mod=ofp.ofp_group_mod(
789 command=ofp.OFPGC_DELETE,
790 group_id=ofp.OFPG_ALL
791 )
792 ))
793 self.poutput('success')
794
795 complete_delete_all_flows = VolthaCli.complete_logical_device
796
Zsolt Haraszti80175202016-12-24 00:17:51 -0800797 def do_send_simulated_upstream_eapol(self, line):
Zsolt Harasztid036b7e2016-12-23 15:36:01 -0800798 """
799 Send an EAPOL upstream from a simulated OLT
800 """
Zsolt Haraszti80175202016-12-24 00:17:51 -0800801 device_id = line or self.default_device_id
Zsolt Harasztid036b7e2016-12-23 15:36:01 -0800802 requests.get('http://{}/devices/{}/test_eapol_in'.format(
803 self.voltha_sim_rest, device_id
804 ))
805
Zsolt Haraszti80175202016-12-24 00:17:51 -0800806 complete_send_simulated_upstream_eapol = VolthaCli.complete_device
807
808 def do_inject_eapol_start(self, line):
Zsolt Harasztid036b7e2016-12-23 15:36:01 -0800809 """
810 Send out an an EAPOL start message into the given Unix interface
811 """
812 pass
Zsolt Harasztia133a452016-12-22 01:26:57 -0800813
814
815if __name__ == '__main__':
Zsolt Haraszti9b485fb2016-12-26 23:11:15 -0800816
817 parser = argparse.ArgumentParser()
818
819 _help = '<hostname>:<port> to consul agent (default: %s)' % defs['consul']
820 parser.add_argument(
821 '-C', '--consul', action='store', default=defs['consul'], help=_help)
822
823 _help = 'Lookup Voltha endpoints based on service entries in Consul'
824 parser.add_argument(
825 '-L', '--lookup', action='store_true', help=_help)
826
khenaidoo108f05c2017-07-06 11:15:29 -0400827 _help = 'All requests to the Voltha gRPC service are global'
828 parser.add_argument(
829 '-G', '--global_request', action='store_true', help=_help)
830
Zsolt Haraszti9b485fb2016-12-26 23:11:15 -0800831 _help = '<hostname>:<port> of Voltha gRPC service (default={})'.format(
832 defs['voltha_grpc_endpoint'])
833 parser.add_argument('-g', '--grpc-endpoint', action='store',
834 default=defs['voltha_grpc_endpoint'], help=_help)
835
836 _help = '<hostname>:<port> of Voltha simulated adapter backend for ' \
837 'testing (default={})'.format(
838 defs['voltha_sim_rest_endpoint'])
839 parser.add_argument('-s', '--sim-rest-endpoint', action='store',
840 default=defs['voltha_sim_rest_endpoint'], help=_help)
841
842 args = parser.parse_args()
843
844 if args.lookup:
845 host = args.consul.split(':')[0].strip()
846 port = int(args.consul.split(':')[1].strip())
847 consul = Consul(host=host, port=port)
848
849 _, services = consul.catalog.service('voltha-grpc')
850 if not services:
851 print('No voltha-grpc service registered in consul; exiting')
852 sys.exit(1)
853 args.grpc_endpoint = '{}:{}'.format(services[0]['ServiceAddress'],
854 services[0]['ServicePort'])
855
856 _, services = consul.catalog.service('voltha-sim-rest')
857 if not services:
858 print('No voltha-sim-rest service registered in consul; exiting')
859 sys.exit(1)
860 args.sim_rest_endpoint = '{}:{}'.format(services[0]['ServiceAddress'],
861 services[0]['ServicePort'])
862
khenaidoo108f05c2017-07-06 11:15:29 -0400863 c = VolthaCli(args.grpc_endpoint, args.sim_rest_endpoint,
864 args.global_request)
Zsolt Haraszti80175202016-12-24 00:17:51 -0800865 c.poutput(banner)
Zsolt Harasztia133a452016-12-22 01:26:57 -0800866 c.load_history()
867 c.cmdloop()
868 c.save_history()