blob: 4456a4c10a80824c20a8bcd30d089102cbf6fd8e [file] [log] [blame]
Zsolt Harasztid036b7e2016-12-23 15:36:01 -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#
17
18"""
19Device level CLI commands
20"""
Sergio Slobodrian901bf4e2017-03-17 12:54:39 -040021from optparse import make_option
22from cmd2 import Cmd, options
Zsolt Harasztid036b7e2016-12-23 15:36:01 -080023from simplejson import dumps
24
Zsolt Haraszti85f12852016-12-24 08:30:58 -080025from cli.table import print_pb_as_table, print_pb_list_as_table
Lydia Fang01f2e852017-06-28 17:24:58 -070026from cli.utils import print_flows, pb2dict, enum2name
Zsolt Harasztid036b7e2016-12-23 15:36:01 -080027from voltha.protos import third_party
28
29_ = third_party
Lydia Fang01f2e852017-06-28 17:24:58 -070030from voltha.protos import voltha_pb2, common_pb2
Sergio Slobodrian3ba3d562017-04-21 10:07:56 -040031import sys
Lydia Fang01f2e852017-06-28 17:24:58 -070032import json
ntoorchi9cb00a72019-07-17 10:43:54 -070033import unicodedata
34import ast
Sergio Slobodrian901bf4e2017-03-17 12:54:39 -040035from google.protobuf.json_format import MessageToDict
Zsolt Harasztid036b7e2016-12-23 15:36:01 -080036
Sergio Slobodrian901bf4e2017-03-17 12:54:39 -040037# Since proto3 won't send fields that are set to 0/false/"" any object that
38# might have those values set in them needs to be replicated here such that the
Sergio Slobodrian3ba3d562017-04-21 10:07:56 -040039# fields can be adequately
Zsolt Harasztid036b7e2016-12-23 15:36:01 -080040
ntoorchi9cb00a72019-07-17 10:43:54 -070041FLOW_ID_INFO_PATH = '{}/{}/flow_id_info/{}'
42FLOW_IDS_PATH = '{}/{}/flow_ids'
43technology = 'xgspon'
44PATH_PREFIX0 = 'service/voltha/resource_manager/{}'
45PATH_PREFIX = PATH_PREFIX0.format(technology)
46PATH = '{}/{}'
47
Chip Boling825764b2018-08-17 16:17:07 -050048
Zsolt Harasztid036b7e2016-12-23 15:36:01 -080049class DeviceCli(Cmd):
50
ntoorchi9cb00a72019-07-17 10:43:54 -070051 def __init__(self, device_id, get_stub, etcd):
Zsolt Harasztid036b7e2016-12-23 15:36:01 -080052 Cmd.__init__(self)
khenaidoo108f05c2017-07-06 11:15:29 -040053 self.get_stub = get_stub
Zsolt Harasztid036b7e2016-12-23 15:36:01 -080054 self.device_id = device_id
55 self.prompt = '(' + self.colorize(
56 self.colorize('device {}'.format(device_id), 'red'), 'bold') + ') '
Sergio Slobodrian901bf4e2017-03-17 12:54:39 -040057 self.pm_config_last = None
58 self.pm_config_dirty = False
ntoorchi9cb00a72019-07-17 10:43:54 -070059 self.etcd = etcd
Zsolt Harasztid036b7e2016-12-23 15:36:01 -080060
Zsolt Haraszti9b485fb2016-12-26 23:11:15 -080061 def cmdloop(self):
62 self._cmdloop()
63
Zsolt Harasztid036b7e2016-12-23 15:36:01 -080064 def get_device(self, depth=0):
khenaidoo108f05c2017-07-06 11:15:29 -040065 stub = self.get_stub()
Zsolt Harasztid036b7e2016-12-23 15:36:01 -080066 res = stub.GetDevice(voltha_pb2.ID(id=self.device_id),
67 metadata=(('get-depth', str(depth)), ))
68 return res
69
Zsolt Haraszti80175202016-12-24 00:17:51 -080070 do_exit = Cmd.do_quit
Zsolt Harasztid036b7e2016-12-23 15:36:01 -080071
Sergio Slobodrian6e9fb692017-03-17 14:46:33 -040072 def do_quit(self, line):
73 if self.pm_config_dirty:
74 self.poutput("Uncommited changes for " + \
75 self.colorize(
76 self.colorize("perf_config,", "blue"),
77 "bold") + " please either " + self.colorize(
78 self.colorize("commit", "blue"), "bold") + \
79 " or " + self.colorize(
80 self.colorize("reset", "blue"), "bold") + \
81 " your changes using " + \
82 self.colorize(
83 self.colorize("perf_config", "blue"), "bold"))
84 return False
85 else:
86 return self._STOP_AND_EXIT
87
Zsolt Haraszti80175202016-12-24 00:17:51 -080088 def do_show(self, line):
89 """Show detailed device information"""
Zsolt Haraszti85f12852016-12-24 08:30:58 -080090 print_pb_as_table('Device {}'.format(self.device_id),
Sergio Slobodriana95f99b2017-03-21 10:22:47 -040091 self.get_device(depth=-1))
Zsolt Haraszti85f12852016-12-24 08:30:58 -080092
93 def do_ports(self, line):
94 """Show ports of device"""
95 device = self.get_device(depth=-1)
96 omit_fields = {
97 }
98 print_pb_list_as_table('Device ports:', device.ports,
99 omit_fields, self.poutput)
Zsolt Haraszti80175202016-12-24 00:17:51 -0800100
Sergio Slobodrian3ba3d562017-04-21 10:07:56 -0400101 def complete_perf_config(self, text, line, begidx, endidx):
102 sub_cmds = {"show", "set", "commit", "reset"}
103 sub_opts = {"-f", "-e", "-d", "-o"}
104 # Help the interpreter complete the paramters.
105 completions = []
106 if not self.pm_config_last:
107 device = self.get_device(depth=-1)
108 self.pm_config_last = device.pm_configs
109 m_names = [d.name for d in self.pm_config_last.metrics]
110 cur_cmd = line.strip().split(" ")
111 try:
112 if not text and len(cur_cmd) == 1:
113 completions = ("show", "set", "commit", "reset")
114 elif len(cur_cmd) == 2:
115 if "set" == cur_cmd[1]:
116 completions = [d for d in sub_opts]
117 else:
118 completions = [d for d in sub_cmds if d.startswith(text)]
119 elif len(cur_cmd) > 2 and cur_cmd[1] == "set":
120 if cur_cmd[len(cur_cmd)-1] == "-":
121 completions = [list(d)[1] for d in sub_opts]
122 elif cur_cmd[len(cur_cmd)-1] == "-f":
123 completions = ("\255","Please enter a sampling frequency in 10ths of a second")
124 elif cur_cmd[len(cur_cmd)-2] == "-f":
125 completions = [d for d in sub_opts]
126 elif cur_cmd[len(cur_cmd)-1] in {"-e","-d","-o"}:
127 if self.pm_config_last.grouped:
128 pass
129 else:
130 completions = [d.name for d in self.pm_config_last.metrics]
131 elif cur_cmd[len(cur_cmd)-2] in {"-e","-d"}:
132 if text and text not in m_names:
133 completions = [d for d in m_names if d.startswith(text)]
134 else:
135 completions = [d for d in sub_opts]
136 elif cur_cmd[len(cur_cmd)-2] == "-o":
137 if cur_cmd[len(cur_cmd)-1] in [d.name for d in self.pm_config_last.metrics]:
138 completions = ("\255","Please enter a sampling frequency in 10ths of a second")
139 else:
140 completions = [d for d in m_names if d.startswith(text)]
141 elif cur_cmd[len(cur_cmd)-3] == "-o":
142 completions = [d for d in sub_opts]
143 except:
144 e = sys.exc_info()
145 print(e)
146 return completions
147
148
Sergio Slobodrian6e9fb692017-03-17 14:46:33 -0400149 def help_perf_config(self):
150 self.poutput(
151'''
Sergio Slobodrian57979ec2017-03-21 22:32:17 -0400152perf_config [show | set | commit | reset] [-f <default frequency>] [{-e <metric/group
153 name>}] [{-d <metric/group name>}] [{-o <metric/group name> <override
154 frequency>}]
Sergio Slobodrian038bd3c2017-03-22 15:53:25 -0400155
Sergio Slobodrian3fb99b32017-03-22 22:18:21 -0400156show: displays the performance configuration of the device
Sergio Slobodrian57979ec2017-03-21 22:32:17 -0400157set: changes the parameters specified with -e, -d, and -o
158reset: reverts any changes made since the last commit
159commit: commits any changes made which applies them to the device.
160
161-e: enable collection of the specified metric, more than one -e may be
162 specified.
Sergio Slobodrian3fb99b32017-03-22 22:18:21 -0400163-d: disable collection of the specified metric, more than on -d may be
Sergio Slobodrian57979ec2017-03-21 22:32:17 -0400164 specified.
Sergio Slobodrianec6e3912017-04-02 11:46:55 -0400165-o: override the collection frequency of the specified metric, more than one -o
Sergio Slobodrian57979ec2017-03-21 22:32:17 -0400166 may be specified. Note that -o isn't valid unless
Sergio Slobodrian3fb99b32017-03-22 22:18:21 -0400167 frequency_override is set to True for the device.
Sergio Slobodrian57979ec2017-03-21 22:32:17 -0400168
Sergio Slobodrian6e9fb692017-03-17 14:46:33 -0400169Changes made by set are held locally until a commit or reset command is issued.
170A commit command will write the configuration to the device and it takes effect
Chip Boling825764b2018-08-17 16:17:07 -0500171immediately. The reset command will undo any changes since the start of the
Sergio Slobodrian6e9fb692017-03-17 14:46:33 -0400172device session.
173
Sergio Slobodrian4236ade2017-03-17 22:01:20 -0400174If grouped is true then the -d, -e and -o commands refer to groups and not
Sergio Slobodrian6e9fb692017-03-17 14:46:33 -0400175individual metrics.
176'''
177 )
178
Sergio Slobodrian901bf4e2017-03-17 12:54:39 -0400179 @options([
180 make_option('-f', '--default_freq', action="store", dest='default_freq',
181 type='long', default=None),
182 make_option('-e', '--enable', action='append', dest='enable',
183 default=None),
184 make_option('-d', '--disable', action='append', dest='disable',
185 default=None),
Chip Boling825764b2018-08-17 16:17:07 -0500186 make_option('-o', '--override', action='append', dest='override',
Sergio Slobodrian6e9fb692017-03-17 14:46:33 -0400187 nargs=2, default=None, type='string'),
Sergio Slobodrian901bf4e2017-03-17 12:54:39 -0400188 ])
189 def do_perf_config(self, line, opts):
Sergio Slobodrian901bf4e2017-03-17 12:54:39 -0400190 """Show and set the performance monitoring configuration of the device"""
191
192 device = self.get_device(depth=-1)
193 if not self.pm_config_last:
194 self.pm_config_last = device.pm_configs
195
196 # Ensure that a valid sub-command was provided
197 if line.strip() not in {"set", "show", "commit", "reset", ""}:
Chip Boling825764b2018-08-17 16:17:07 -0500198 self.poutput(self.colorize('Error: ', 'red') +
Sergio Slobodrian901bf4e2017-03-17 12:54:39 -0400199 self.colorize(self.colorize(line.strip(), 'blue'),
200 'bold') + ' is not recognized')
201 return
202
203 # Ensure no options are provided when requesting to view the config
204 if line.strip() == "show" or line.strip() == "":
205 if opts.default_freq or opts.enable or opts.disable:
206 self.poutput(opts.disable)
Chip Boling825764b2018-08-17 16:17:07 -0500207 self.poutput(self.colorize('Error: ', 'red') + 'use ' +
Sergio Slobodrian901bf4e2017-03-17 12:54:39 -0400208 self.colorize(self.colorize('"set"', 'blue'),
209 'bold') + ' to change settings')
210 return
211
Chip Boling825764b2018-08-17 16:17:07 -0500212 if line.strip() == "set": # Set the supplied values
213 metric_list = set()
214 if opts.enable is not None:
215 metric_list |= {metric for metric in opts.enable}
216 if opts.disable is not None:
217 metric_list |= {metric for metric in opts.disable}
218 if opts.override is not None:
219 metric_list |= {metric for metric, _ in opts.override}
220
221 # The default frequency
Sergio Slobodrian901bf4e2017-03-17 12:54:39 -0400222 if opts.default_freq:
223 self.pm_config_last.default_freq = opts.default_freq
224 self.pm_config_dirty = True
225
Sergio Slobodrian4236ade2017-03-17 22:01:20 -0400226 # Field or group visibility
Sergio Slobodrian901bf4e2017-03-17 12:54:39 -0400227 if self.pm_config_last.grouped:
228 for g in self.pm_config_last.groups:
229 if opts.enable:
230 if g.group_name in opts.enable:
231 g.enabled = True
232 self.pm_config_dirty = True
Chip Boling825764b2018-08-17 16:17:07 -0500233 metric_list.discard(g.group_name)
Sergio Slobodrian901bf4e2017-03-17 12:54:39 -0400234 for g in self.pm_config_last.groups:
235 if opts.disable:
236 if g.group_name in opts.disable:
237 g.enabled = False
238 self.pm_config_dirty = True
Chip Boling825764b2018-08-17 16:17:07 -0500239 metric_list.discard(g.group_name)
Sergio Slobodrian901bf4e2017-03-17 12:54:39 -0400240 else:
241 for m in self.pm_config_last.metrics:
242 if opts.enable:
243 if m.name in opts.enable:
244 m.enabled = True
245 self.pm_config_dirty = True
Chip Boling825764b2018-08-17 16:17:07 -0500246 metric_list.discard(m.name)
Sergio Slobodrian901bf4e2017-03-17 12:54:39 -0400247 for m in self.pm_config_last.metrics:
248 if opts.disable:
249 if m.name in opts.disable:
250 m.enabled = False
251 self.pm_config_dirty = True
Chip Boling825764b2018-08-17 16:17:07 -0500252 metric_list.discard(m.name)
Sergio Slobodrian4236ade2017-03-17 22:01:20 -0400253
Chip Boling825764b2018-08-17 16:17:07 -0500254 # Frequency overrides.
Sergio Slobodrian4236ade2017-03-17 22:01:20 -0400255 if opts.override:
256 if self.pm_config_last.freq_override:
257 oo = dict()
258 for o in opts.override:
259 oo[o[0]] = o[1]
260 if self.pm_config_last.grouped:
261 for g in self.pm_config_last.groups:
262 if g.group_name in oo:
263 try:
264 g.group_freq = int(oo[g.group_name])
265 except ValueError:
266 self.poutput(self.colorize('Warning: ',
Chip Boling825764b2018-08-17 16:17:07 -0500267 'yellow') +
268 self.colorize(oo[g.group_name],
269 'blue') +
Sergio Slobodrian4236ade2017-03-17 22:01:20 -0400270 " is not an integer... ignored")
271 del oo[g.group_name]
272 self.pm_config_dirty = True
Chip Boling825764b2018-08-17 16:17:07 -0500273 metric_list.discard(g.group_name)
Sergio Slobodrian4236ade2017-03-17 22:01:20 -0400274 else:
275 for m in self.pm_config_last.metrics:
276 if m.name in oo:
277 try:
278 m.sample_freq = int(oo[m.name])
279 except ValueError:
280 self.poutput(self.colorize('Warning: ',
Chip Boling825764b2018-08-17 16:17:07 -0500281 'yellow') +
Sergio Slobodrian4236ade2017-03-17 22:01:20 -0400282 self.colorize(oo[m.name],
Chip Boling825764b2018-08-17 16:17:07 -0500283 'blue') +
Sergio Slobodrian4236ade2017-03-17 22:01:20 -0400284 " is not an integer... ignored")
285 del oo[m.name]
286 self.pm_config_dirty = True
Chip Boling825764b2018-08-17 16:17:07 -0500287 metric_list.discard(m.name)
Sergio Slobodrian4236ade2017-03-17 22:01:20 -0400288
289 # If there's anything left the input was typoed
290 if self.pm_config_last.grouped:
291 field = 'group'
292 else:
293 field = 'metric'
294 for o in oo:
Chip Boling825764b2018-08-17 16:17:07 -0500295 self.poutput(self.colorize('Warning: ', 'yellow') +
296 'the parameter' + ' ' +
297 self.colorize(o, 'blue') + ' is not ' +
Sergio Slobodrian4236ade2017-03-17 22:01:20 -0400298 'a ' + field + ' name... ignored')
299 if oo:
300 return
301
Chip Boling825764b2018-08-17 16:17:07 -0500302 else: # Frequency overrides not enabled
303 self.poutput(self.colorize('Error: ', 'red') +
304 'Individual overrides are only ' +
305 'supported if ' +
306 self.colorize('freq_override', 'blue') +
Sergio Slobodrian4236ade2017-03-17 22:01:20 -0400307 ' is set to ' + self.colorize('True', 'blue'))
308 return
Chip Boling825764b2018-08-17 16:17:07 -0500309
310 if len(metric_list):
311 metric_name_list = ", ".join(str(metric) for metric in metric_list)
312 self.poutput(self.colorize('Error: ', 'red') +
313 'Metric/Metric Group{} '.format('s' if len(metric_list) > 1 else '') +
314 self.colorize(metric_name_list, 'blue') +
315 ' {} not found'.format('were' if len(metric_list) > 1 else 'was'))
316 return
317
Sergio Slobodrian4236ade2017-03-17 22:01:20 -0400318 self.poutput("Success")
319 return
320
Sergio Slobodrian901bf4e2017-03-17 12:54:39 -0400321 elif line.strip() == "commit" and self.pm_config_dirty:
khenaidoo108f05c2017-07-06 11:15:29 -0400322 stub = self.get_stub()
Sergio Slobodrian901bf4e2017-03-17 12:54:39 -0400323 stub.UpdateDevicePmConfigs(self.pm_config_last)
324 self.pm_config_last = self.get_device(depth=-1).pm_configs
325 self.pm_config_dirty = False
Chip Boling825764b2018-08-17 16:17:07 -0500326
Sergio Slobodrian901bf4e2017-03-17 12:54:39 -0400327 elif line.strip() == "reset" and self.pm_config_dirty:
328 self.pm_config_last = self.get_device(depth=-1).pm_configs
329 self.pm_config_dirty = False
330
331 omit_fields = {'groups', 'metrics', 'id'}
332 print_pb_as_table('PM Config:', self.pm_config_last, omit_fields,
Sergio Slobodriana95f99b2017-03-21 10:22:47 -0400333 self.poutput,show_nulls=True)
Sergio Slobodrian901bf4e2017-03-17 12:54:39 -0400334 if self.pm_config_last.grouped:
335 #self.poutput("Supported metric groups:")
336 for g in self.pm_config_last.groups:
337 if self.pm_config_last.freq_override:
338 omit_fields = {'metrics'}
339 else:
340 omit_fields = {'group_freq','metrics'}
Sergio Slobodriana95f99b2017-03-21 10:22:47 -0400341 print_pb_as_table('', g, omit_fields, self.poutput,
342 show_nulls=True)
Sergio Slobodrian901bf4e2017-03-17 12:54:39 -0400343 if g.enabled:
344 state = 'enabled'
345 else:
346 state = 'disabled'
347 print_pb_list_as_table(
Sergio Slobodriana4b89c02017-04-03 12:48:36 -0400348 'Metric group {} is {}'.format(g.group_name,state),
Sergio Slobodrian901bf4e2017-03-17 12:54:39 -0400349 g.metrics, {'enabled', 'sample_freq'}, self.poutput,
Sergio Slobodriana95f99b2017-03-21 10:22:47 -0400350 dividers=100, show_nulls=True)
Sergio Slobodrian901bf4e2017-03-17 12:54:39 -0400351 else:
352 if self.pm_config_last.freq_override:
353 omit_fields = {}
354 else:
355 omit_fields = {'sample_freq'}
356 print_pb_list_as_table('Supported metrics:', self.pm_config_last.metrics,
Sergio Slobodrian038bd3c2017-03-22 15:53:25 -0400357 omit_fields, self.poutput, dividers=100,
358 show_nulls=True)
Sergio Slobodrian901bf4e2017-03-17 12:54:39 -0400359
ntoorchi9cb00a72019-07-17 10:43:54 -0700360 def get_flow_id(self, id_str):
361 # original representation of the flow id
362 # there is a mask for upstream flow ids of openolt adapter as 0x1 << 15 | flow_id
363 # ponsim and other flow does not need any modification
364 if int(id_str) >= 0x1 << 15:
365 flow_id = int(id_str) ^ 0x1 << 15
366 else:
367 flow_id = int(id_str)
368
369 return flow_id
370
371 def flow_exist(self, pon_intf_onu_id, flow_id):
372 # checks whether the flow still exists in ETCD or not
373 flow_ids_path = FLOW_IDS_PATH.format(self.device_id, pon_intf_onu_id)
374 path_to_flow_ids = PATH.format(PATH_PREFIX, flow_ids_path)
375 (flow_ids, _) = self.etcd.get(path_to_flow_ids)
376 if flow_ids is None:
377 return False
378 else:
379 if flow_id in eval(flow_ids):
380 return True
381 else:
382 return False
383
384 def update_repeated_ids_dict(self,flow_id, repeated_ids):
385 # updates how many times an id is seen
386 if str(flow_id) in repeated_ids:
387 repeated_ids[str(flow_id)] += 1
388 else:
389 repeated_ids[str(flow_id)] = 1
390 return repeated_ids
391
392 def get_flow_index(self,flow, flow_info_all):
393 if 'flow_store_cookie' in flow:
394 for i, flow_info in enumerate(flow_info_all):
395 if unicodedata.normalize('NFKD', flow['flow_store_cookie']).encode('ascii', 'ignore') == flow_info['flow_store_cookie']:
396 return i
397 return None
398 else: #only one flow or those flows that are not added to device
399 return 0
400
Zsolt Haraszti80175202016-12-24 00:17:51 -0800401 def do_flows(self, line):
Zsolt Harasztid036b7e2016-12-23 15:36:01 -0800402 """Show flow table for device"""
403 device = pb2dict(self.get_device(-1))
ntoorchi9cb00a72019-07-17 10:43:54 -0700404 flows_info = list()
405 flows = device['flows']['items']
406 for i, flow in enumerate(flows):
407 flow_info = dict()
408 if unicodedata.normalize('NFKD', device['type']).encode('ascii', 'ignore') == 'openolt':
409 flow_id = self.get_flow_id(flow['id'])
410 else:
411 flow_id = int(flow['id'])
412
413 flow_info.update({'flow_id' : str(flow_id)})
414
415 if 'intf_tuple' in flow and len(flow['intf_tuple']) > 0: # we have extra flow info in ETCD!!!
416 pon_intf_onu_id = unicodedata.normalize('NFKD', flow['intf_tuple'][0]).encode('ascii', 'ignore')
417 flow_info.update({'pon_intf_onu_id': pon_intf_onu_id})
418
419 # check if the flow info still exists in ETCD
420 if self.flow_exist(pon_intf_onu_id, flow_id):
421 flow_id_info_path = FLOW_ID_INFO_PATH.format(self.device_id, pon_intf_onu_id, flow_id)
422 path_to_flow_info = PATH.format(PATH_PREFIX, flow_id_info_path)
423 (flow_info_all, _) = self.etcd.get(path_to_flow_info)
424 flow_info_all = ast.literal_eval(flow_info_all)
425 flow_info_index = self.get_flow_index(flow, flow_info_all)
426
427 if flow_info_index is not None:
428 flow_info_all = flow_info_all[flow_info_index]
429 if 'gemport_id' in flow_info_all:
430 flow_info.update({'gemport_id': flow_info_all['gemport_id']})
431
432 if 'alloc_id' in flow_info_all:
433 flow_info.update({'alloc_id': flow_info_all['alloc_id']})
434
435 if 'flow_type' in flow_info_all:
436 flow_info.update({'flow_type': flow_info_all['flow_type']})
437
438 if 'o_pbits' in flow_info_all['classifier']:
439 flow_info.update({'o_pbits': flow_info_all['classifier']['o_pbits']})
440
441 if 'flow_category' in flow_info_all:
442 flow_info.update({'flow_category': flow_info_all['flow_category']})
443
444 flows_info.append(flow_info)
Zsolt Harasztid036b7e2016-12-23 15:36:01 -0800445 print_flows(
446 'Device',
447 self.device_id,
448 type=device['type'],
449 flows=device['flows']['items'],
ntoorchi9cb00a72019-07-17 10:43:54 -0700450 groups=device['flow_groups']['items'],
451 flows_info=flows_info,
452 fields_to_omit = ['table_id', 'goto-table']
Zsolt Harasztid036b7e2016-12-23 15:36:01 -0800453 )
454
ggowdru236bd952017-06-20 20:32:55 -0700455 def do_images(self, line):
456 """Show software images on the device"""
457 device = self.get_device(depth=-1)
458 omit_fields = {}
459 print_pb_list_as_table('Software Images:', device.images.image,
460 omit_fields, self.poutput, show_nulls=True)
461
Lydia Fang01f2e852017-06-28 17:24:58 -0700462 @options([
463 make_option('-u', '--url', action='store', dest='url',
464 help="URL to get sw image"),
465 make_option('-n', '--name', action='store', dest='name',
466 help="Image name"),
467 make_option('-c', '--crc', action='store', dest='crc',
468 help="CRC code to verify with", default=0),
469 make_option('-v', '--version', action='store', dest='version',
470 help="Image version", default=0),
lcui33d6a8e2018-08-28 12:51:38 -0700471 make_option('-d', '--dir', action='store', dest='dir',
472 help="local directory"),
Lydia Fang01f2e852017-06-28 17:24:58 -0700473 ])
474 def do_img_dnld_request(self, line, opts):
475 """
476 Request image download to a device
477 """
478 device = self.get_device(depth=-1)
479 self.poutput('device_id {}'.format(device.id))
480 self.poutput('name {}'.format(opts.name))
481 self.poutput('url {}'.format(opts.url))
482 self.poutput('crc {}'.format(opts.crc))
483 self.poutput('version {}'.format(opts.version))
lcui33d6a8e2018-08-28 12:51:38 -0700484 self.poutput('local dir {}'.format(opts.dir))
Lydia Fang01f2e852017-06-28 17:24:58 -0700485 try:
486 device_id = device.id
487 if device_id and opts.name and opts.url:
488 kw = dict(id=device_id)
489 kw['name'] = opts.name
490 kw['url'] = opts.url
491 else:
492 self.poutput('Device ID and URL are needed')
493 raise Exception('Device ID and URL are needed')
lcui33d6a8e2018-08-28 12:51:38 -0700494 if opts.dir:
495 kw['local_dir'] = opts.dir
Lydia Fang01f2e852017-06-28 17:24:58 -0700496 except Exception as e:
497 self.poutput('Error request img dnld {}. Error:{}'.format(device_id, e))
498 return
499 kw['crc'] = long(opts.crc)
500 kw['image_version'] = opts.version
501 response = None
502 try:
503 request = voltha_pb2.ImageDownload(**kw)
504 stub = self.get_stub()
505 response = stub.DownloadImage(request)
506 except Exception as e:
507 self.poutput('Error download image {}. Error:{}'.format(kw['id'], e))
508 return
509 name = enum2name(common_pb2.OperationResp,
510 'OperationReturnCode', response.code)
511 self.poutput('response: {}'.format(name))
512 self.poutput('{}'.format(response))
513
514 @options([
515 make_option('-n', '--name', action='store', dest='name',
516 help="Image name"),
517 ])
518 def do_img_dnld_status(self, line, opts):
519 """
520 Get a image download status
521 """
522 device = self.get_device(depth=-1)
523 self.poutput('device_id {}'.format(device.id))
524 self.poutput('name {}'.format(opts.name))
525 try:
526 device_id = device.id
527 if device_id and opts.name:
528 kw = dict(id=device_id)
529 kw['name'] = opts.name
530 else:
531 self.poutput('Device ID, Image Name are needed')
532 raise Exception('Device ID, Image Name are needed')
533 except Exception as e:
534 self.poutput('Error get img dnld status {}. Error:{}'.format(device_id, e))
535 return
536 status = None
537 try:
538 img_dnld = voltha_pb2.ImageDownload(**kw)
539 stub = self.get_stub()
540 status = stub.GetImageDownloadStatus(img_dnld)
541 except Exception as e:
542 self.poutput('Error get img dnld status {}. Error:{}'.format(device_id, e))
543 return
544 fields_to_omit = {
545 'crc',
546 'local_dir',
547 }
548 try:
549 print_pb_as_table('ImageDownload Status:', status, fields_to_omit, self.poutput)
550 except Exception, e:
551 self.poutput('Error {}. Error:{}'.format(device_id, e))
552
553 def do_img_dnld_list(self, line):
554 """
555 List all image download records for a given device
556 """
557 device = self.get_device(depth=-1)
558 device_id = device.id
559 self.poutput('Get all img dnld records {}'.format(device_id))
560 try:
561 stub = self.get_stub()
562 img_dnlds = stub.ListImageDownloads(voltha_pb2.ID(id=device_id))
563 except Exception, e:
564 self.poutput('Error list img dnlds {}. Error:{}'.format(device_id, e))
565 return
566 fields_to_omit = {
567 'crc',
568 'local_dir',
569 }
570 try:
571 print_pb_list_as_table('ImageDownloads:', img_dnlds.items, fields_to_omit, self.poutput)
572 except Exception, e:
573 self.poutput('Error {}. Error:{}'.format(device_id, e))
574
575
576 @options([
577 make_option('-n', '--name', action='store', dest='name',
578 help="Image name"),
579 ])
580 def do_img_dnld_cancel(self, line, opts):
581 """
582 Cancel a requested image download
583 """
584 device = self.get_device(depth=-1)
585 self.poutput('device_id {}'.format(device.id))
586 self.poutput('name {}'.format(opts.name))
587 device_id = device.id
588 try:
589 if device_id and opts.name:
590 kw = dict(id=device_id)
591 kw['name'] = opts.name
592 else:
593 self.poutput('Device ID, Image Name are needed')
594 raise Exception('Device ID, Image Name are needed')
595 except Exception as e:
596 self.poutput('Error cancel sw dnld {}. Error:{}'.format(device_id, e))
597 return
598 response = None
599 try:
600 img_dnld = voltha_pb2.ImageDownload(**kw)
601 stub = self.get_stub()
602 img_dnld = stub.GetImageDownload(img_dnld)
603 response = stub.CancelImageDownload(img_dnld)
604 except Exception as e:
605 self.poutput('Error cancel sw dnld {}. Error:{}'.format(device_id, e))
606 return
607 name = enum2name(common_pb2.OperationResp,
608 'OperationReturnCode', response.code)
609 self.poutput('response: {}'.format(name))
610 self.poutput('{}'.format(response))
611
Scott Bakerd3190952018-09-04 15:47:28 -0700612 def help_simulate_alarm(self):
613 self.poutput(
614'''
615simulate_alarm <alarm_name> [-b <bit rate>] [-c] [-d <drift>] [-e <eqd>]
616 [-i <interface id>] [-o <onu device id>] [-p <port type name>]
617
618<name> is the name of the alarm to raise. Other rguments are alarm specific
619and only have meaning in the context of a particular alarm. Below is a list
620of the alarms that may be raised:
621
622simulate_alarm los -i <interface_id> -p <port_type_name>
623simulate_alarm dying_gasp -i <interface_id> -o <onu_device_id>
624simulate_alarm onu_los -i <interface_id> -o <onu_device_id>
625simulate_alarm onu_lopc_miss -i <interface_id> -o <onu_device_id>
626simulate_alarm onu_lopc_mic -i <interface_id> -o <onu_device_id>
627simulate_alarm onu_lob -i <interface_id> -o <onu_device_id>
628simulate_alarm onu_signal_degrade -i <interface_id> -o <onu_device_id>
629 -b <bit_rate>
630simulate_alarm onu_drift_of_window -i <interface_id>
631 -o <onu_device_id> -d <drift> -e <eqd>
632simulate_alarm onu_signal_fail -i <interface_id> -o <onu_device_id>
633 -b <bit_rate>
634simulate_alarm onu_activation -i <interface_id> -o <onu_device_id>
635simulate_alarm onu_startup -i <interface_id> -o <onu_device_id>
636simulate_alarm onu_discovery -i <interface_id> -s <onu_serial_number>
637
638If the -c option is specified then the alarm will be cleared. By default,
639it will be raised. Note that only some alarms can be cleared.
640'''
641 )
642
643 @options([
644 make_option('-c', '--clear', action='store_true', default=False,
645 help="Clear alarm instead of raising"),
646 make_option('-b', '--inverse_bit_error_rate', action='store', dest='inverse_bit_error_rate',
647 help="Inverse bit error rate", default=0, type="int"),
648 make_option('-d', '--drift', action='store', dest='drift',
649 help="Drift", default=0, type="int"),
650 make_option('-e', '--new_eqd', action='store', dest='new_eqd',
651 help="New EQD", default=0, type="int"),
652 make_option('-i', '--intf_id', action='store', dest='intf_id',
653 help="Interface ID", default=""),
654 make_option('-o', '--onu_device_id', action='store', dest='onu_device_id',
655 help="ONU device ID", default=""),
656 make_option('-p', '--port_type_name', action='store', dest='port_type_name',
657 help="Port type name", default=""),
658 make_option('-s', '--onu_serial_number', action='store', dest='onu_serial_number',
659 help="ONU Serial Number", default=""),
660 ])
661 def do_simulate_alarm(self, line, opts):
662 indicator = line
663 device = self.get_device(depth=-1)
664 device_id = device.id
665
666 alarm_args = {"los": ["intf_id", "port_type_name"],
667 "dying_gasp": ["intf_id", "onu_device_id"],
668 "onu_los": ["intf_id", "onu_device_id"],
669 "onu_lopc_miss": ["intf_id", "onu_device_id"],
670 "onu_lopc_mic": ["intf_id", "onu_device_id"],
671 "onu_lob": ["intf_id", "onu_device_id"],
672 "onu_signal_degrade": ["intf_id", "onu_device_id", "inverse_bit_error_rate"],
673 "onu_drift_of_window": ["intf_id", "onu_device_id", "drift", "new_eqd"],
674 "onu_signal_fail": ["intf_id", "onu_device_id", "inverse_bit_error_rate"],
675 "onu_activation": ["intf_id", "onu_device_id"],
676 "onu_startup": ["intf_id", "onu_device_id"],
677 "onu_discovery": ["intf_id", "onu_serial_number"]
678 }
679 try:
680 if indicator not in alarm_args:
681 self.poutput("Unknown alarm indicator %s. Valid choices are %s." % (indicator,
682 ", ".join(alarm_args.keys())))
683 raise Exception("Unknown alarm indicator %s" % indicator)
684
685 for arg_name in alarm_args[indicator]:
686 if not getattr(opts, arg_name):
687 self.poutput("Option %s is required for alarm %s. See help." % (arg_name, indicator))
688 raise Exception("Option %s is required for alarm %s" % (arg_name, indicator))
689
690 # TODO: check for required arguments
691 kw = dict(id=device_id)
692
693 kw["indicator"] = indicator
694 kw["intf_id"] = opts.intf_id
695 kw["onu_device_id"] = opts.onu_device_id
696 kw["port_type_name"] = opts.port_type_name
697 kw["inverse_bit_error_rate"] = opts.inverse_bit_error_rate
698 kw["drift"] = opts.drift
699 kw["new_eqd"] = opts.new_eqd
700 kw["onu_serial_number"] = opts.onu_serial_number
701
702 if opts.clear:
703 kw["operation"] = voltha_pb2.SimulateAlarmRequest.CLEAR
704 else:
705 kw["operation"] = voltha_pb2.SimulateAlarmRequest.RAISE
706 except Exception as e:
707 self.poutput('Error simulate alarm {}. Error:{}'.format(device_id, e))
708 return
709 response = None
710 try:
711 simulate_alarm = voltha_pb2.SimulateAlarmRequest(**kw)
712 stub = self.get_stub()
713 response = stub.SimulateAlarm(simulate_alarm)
714 except Exception as e:
715 self.poutput('Error simulate alarm {}. Error:{}'.format(kw['id'], e))
716 return
717 name = enum2name(common_pb2.OperationResp,
718 'OperationReturnCode', response.code)
719 self.poutput('response: {}'.format(name))
720 self.poutput('{}'.format(response))
721
Lydia Fang01f2e852017-06-28 17:24:58 -0700722 @options([
723 make_option('-n', '--name', action='store', dest='name',
724 help="Image name"),
725 make_option('-s', '--save', action='store', dest='save_config',
726 help="Save Config", default="True"),
727 make_option('-d', '--dir', action='store', dest='local_dir',
728 help="Image on device location"),
729 ])
730 def do_img_activate(self, line, opts):
731 """
732 Activate an image update on device
733 """
734 device = self.get_device(depth=-1)
735 device_id = device.id
736 try:
737 if device_id and opts.name and opts.local_dir:
738 kw = dict(id=device_id)
739 kw['name'] = opts.name
740 kw['local_dir'] = opts.local_dir
741 else:
742 self.poutput('Device ID, Image Name, and Location are needed')
743 raise Exception('Device ID, Image Name, and Location are needed')
744 except Exception as e:
745 self.poutput('Error activate image {}. Error:{}'.format(device_id, e))
746 return
747 kw['save_config'] = json.loads(opts.save_config.lower())
748 self.poutput('activate image update {} {} {} {}'.format( \
749 kw['id'], kw['name'],
750 kw['local_dir'], kw['save_config']))
751 response = None
752 try:
753 img_dnld = voltha_pb2.ImageDownload(**kw)
754 stub = self.get_stub()
755 img_dnld = stub.GetImageDownload(img_dnld)
756 response = stub.ActivateImageUpdate(img_dnld)
757 except Exception as e:
758 self.poutput('Error activate image {}. Error:{}'.format(kw['id'], e))
759 return
760 name = enum2name(common_pb2.OperationResp,
761 'OperationReturnCode', response.code)
762 self.poutput('response: {}'.format(name))
763 self.poutput('{}'.format(response))
764
765 @options([
766 make_option('-n', '--name', action='store', dest='name',
767 help="Image name"),
768 make_option('-s', '--save', action='store', dest='save_config',
769 help="Save Config", default="True"),
770 make_option('-d', '--dir', action='store', dest='local_dir',
771 help="Image on device location"),
772 ])
773 def do_img_revert(self, line, opts):
774 """
775 Revert an image update on device
776 """
777 device = self.get_device(depth=-1)
778 device_id = device.id
779 try:
780 if device_id and opts.name and opts.local_dir:
781 kw = dict(id=device_id)
782 kw['name'] = opts.name
783 kw['local_dir'] = opts.local_dir
784 else:
785 self.poutput('Device ID, Image Name, and Location are needed')
786 raise Exception('Device ID, Image Name, and Location are needed')
787 except Exception as e:
788 self.poutput('Error revert image {}. Error:{}'.format(device_id, e))
789 return
790 kw['save_config'] = json.loads(opts.save_config.lower())
791 self.poutput('revert image update {} {} {} {}'.format( \
792 kw['id'], kw['name'],
793 kw['local_dir'], kw['save_config']))
794 response = None
795 try:
796 img_dnld = voltha_pb2.ImageDownload(**kw)
797 stub = self.get_stub()
798 img_dnld = stub.GetImageDownload(img_dnld)
799 response = stub.RevertImageUpdate(img_dnld)
800 except Exception as e:
801 self.poutput('Error revert image {}. Error:{}'.format(kw['id'], e))
802 return
803 name = enum2name(common_pb2.OperationResp,
804 'OperationReturnCode', response.code)
805 self.poutput('response: {}'.format(name))
806 self.poutput('{}'.format(response))