blob: 6b9f28181c803538af0f6319b574c75e9c7789ac [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
Sergio Slobodrian901bf4e2017-03-17 12:54:39 -040033from voltha.protos.device_pb2 import PmConfigs, PmConfig, PmGroupConfig
34from google.protobuf.json_format import MessageToDict
Zsolt Harasztid036b7e2016-12-23 15:36:01 -080035
Sergio Slobodrian901bf4e2017-03-17 12:54:39 -040036# Since proto3 won't send fields that are set to 0/false/"" any object that
37# might have those values set in them needs to be replicated here such that the
Sergio Slobodrian3ba3d562017-04-21 10:07:56 -040038# fields can be adequately
Zsolt Harasztid036b7e2016-12-23 15:36:01 -080039
40class DeviceCli(Cmd):
41
khenaidoo108f05c2017-07-06 11:15:29 -040042 def __init__(self, device_id, get_stub):
Zsolt Harasztid036b7e2016-12-23 15:36:01 -080043 Cmd.__init__(self)
khenaidoo108f05c2017-07-06 11:15:29 -040044 self.get_stub = get_stub
Zsolt Harasztid036b7e2016-12-23 15:36:01 -080045 self.device_id = device_id
46 self.prompt = '(' + self.colorize(
47 self.colorize('device {}'.format(device_id), 'red'), 'bold') + ') '
Sergio Slobodrian901bf4e2017-03-17 12:54:39 -040048 self.pm_config_last = None
49 self.pm_config_dirty = False
Zsolt Harasztid036b7e2016-12-23 15:36:01 -080050
Zsolt Haraszti9b485fb2016-12-26 23:11:15 -080051 def cmdloop(self):
52 self._cmdloop()
53
Zsolt Harasztid036b7e2016-12-23 15:36:01 -080054 def get_device(self, depth=0):
khenaidoo108f05c2017-07-06 11:15:29 -040055 stub = self.get_stub()
Zsolt Harasztid036b7e2016-12-23 15:36:01 -080056 res = stub.GetDevice(voltha_pb2.ID(id=self.device_id),
57 metadata=(('get-depth', str(depth)), ))
58 return res
59
Zsolt Haraszti80175202016-12-24 00:17:51 -080060 do_exit = Cmd.do_quit
Zsolt Harasztid036b7e2016-12-23 15:36:01 -080061
Sergio Slobodrian6e9fb692017-03-17 14:46:33 -040062 def do_quit(self, line):
63 if self.pm_config_dirty:
64 self.poutput("Uncommited changes for " + \
65 self.colorize(
66 self.colorize("perf_config,", "blue"),
67 "bold") + " please either " + self.colorize(
68 self.colorize("commit", "blue"), "bold") + \
69 " or " + self.colorize(
70 self.colorize("reset", "blue"), "bold") + \
71 " your changes using " + \
72 self.colorize(
73 self.colorize("perf_config", "blue"), "bold"))
74 return False
75 else:
76 return self._STOP_AND_EXIT
77
Zsolt Haraszti80175202016-12-24 00:17:51 -080078 def do_show(self, line):
79 """Show detailed device information"""
Zsolt Haraszti85f12852016-12-24 08:30:58 -080080 print_pb_as_table('Device {}'.format(self.device_id),
Sergio Slobodriana95f99b2017-03-21 10:22:47 -040081 self.get_device(depth=-1))
Zsolt Haraszti85f12852016-12-24 08:30:58 -080082
83 def do_ports(self, line):
84 """Show ports of device"""
85 device = self.get_device(depth=-1)
86 omit_fields = {
87 }
88 print_pb_list_as_table('Device ports:', device.ports,
89 omit_fields, self.poutput)
Zsolt Haraszti80175202016-12-24 00:17:51 -080090
Sergio Slobodrian3ba3d562017-04-21 10:07:56 -040091 def complete_perf_config(self, text, line, begidx, endidx):
92 sub_cmds = {"show", "set", "commit", "reset"}
93 sub_opts = {"-f", "-e", "-d", "-o"}
94 # Help the interpreter complete the paramters.
95 completions = []
96 if not self.pm_config_last:
97 device = self.get_device(depth=-1)
98 self.pm_config_last = device.pm_configs
99 m_names = [d.name for d in self.pm_config_last.metrics]
100 cur_cmd = line.strip().split(" ")
101 try:
102 if not text and len(cur_cmd) == 1:
103 completions = ("show", "set", "commit", "reset")
104 elif len(cur_cmd) == 2:
105 if "set" == cur_cmd[1]:
106 completions = [d for d in sub_opts]
107 else:
108 completions = [d for d in sub_cmds if d.startswith(text)]
109 elif len(cur_cmd) > 2 and cur_cmd[1] == "set":
110 if cur_cmd[len(cur_cmd)-1] == "-":
111 completions = [list(d)[1] for d in sub_opts]
112 elif cur_cmd[len(cur_cmd)-1] == "-f":
113 completions = ("\255","Please enter a sampling frequency in 10ths of a second")
114 elif cur_cmd[len(cur_cmd)-2] == "-f":
115 completions = [d for d in sub_opts]
116 elif cur_cmd[len(cur_cmd)-1] in {"-e","-d","-o"}:
117 if self.pm_config_last.grouped:
118 pass
119 else:
120 completions = [d.name for d in self.pm_config_last.metrics]
121 elif cur_cmd[len(cur_cmd)-2] in {"-e","-d"}:
122 if text and text not in m_names:
123 completions = [d for d in m_names if d.startswith(text)]
124 else:
125 completions = [d for d in sub_opts]
126 elif cur_cmd[len(cur_cmd)-2] == "-o":
127 if cur_cmd[len(cur_cmd)-1] in [d.name for d in self.pm_config_last.metrics]:
128 completions = ("\255","Please enter a sampling frequency in 10ths of a second")
129 else:
130 completions = [d for d in m_names if d.startswith(text)]
131 elif cur_cmd[len(cur_cmd)-3] == "-o":
132 completions = [d for d in sub_opts]
133 except:
134 e = sys.exc_info()
135 print(e)
136 return completions
137
138
Sergio Slobodrian6e9fb692017-03-17 14:46:33 -0400139 def help_perf_config(self):
140 self.poutput(
141'''
Sergio Slobodrian57979ec2017-03-21 22:32:17 -0400142perf_config [show | set | commit | reset] [-f <default frequency>] [{-e <metric/group
143 name>}] [{-d <metric/group name>}] [{-o <metric/group name> <override
144 frequency>}]
Sergio Slobodrian038bd3c2017-03-22 15:53:25 -0400145
Sergio Slobodrian3fb99b32017-03-22 22:18:21 -0400146show: displays the performance configuration of the device
Sergio Slobodrian57979ec2017-03-21 22:32:17 -0400147set: changes the parameters specified with -e, -d, and -o
148reset: reverts any changes made since the last commit
149commit: commits any changes made which applies them to the device.
150
151-e: enable collection of the specified metric, more than one -e may be
152 specified.
Sergio Slobodrian3fb99b32017-03-22 22:18:21 -0400153-d: disable collection of the specified metric, more than on -d may be
Sergio Slobodrian57979ec2017-03-21 22:32:17 -0400154 specified.
Sergio Slobodrianec6e3912017-04-02 11:46:55 -0400155-o: override the collection frequency of the specified metric, more than one -o
Sergio Slobodrian57979ec2017-03-21 22:32:17 -0400156 may be specified. Note that -o isn't valid unless
Sergio Slobodrian3fb99b32017-03-22 22:18:21 -0400157 frequency_override is set to True for the device.
Sergio Slobodrian57979ec2017-03-21 22:32:17 -0400158
Sergio Slobodrian6e9fb692017-03-17 14:46:33 -0400159Changes made by set are held locally until a commit or reset command is issued.
160A commit command will write the configuration to the device and it takes effect
161immediately. The reset command will undo any changes sinc the start of the
162device session.
163
Sergio Slobodrian4236ade2017-03-17 22:01:20 -0400164If grouped is true then the -d, -e and -o commands refer to groups and not
Sergio Slobodrian6e9fb692017-03-17 14:46:33 -0400165individual metrics.
166'''
167 )
168
Sergio Slobodrian901bf4e2017-03-17 12:54:39 -0400169 @options([
170 make_option('-f', '--default_freq', action="store", dest='default_freq',
171 type='long', default=None),
172 make_option('-e', '--enable', action='append', dest='enable',
173 default=None),
174 make_option('-d', '--disable', action='append', dest='disable',
175 default=None),
Sergio Slobodrian4236ade2017-03-17 22:01:20 -0400176 make_option('-o', '--overried', action='append', dest='override',
Sergio Slobodrian6e9fb692017-03-17 14:46:33 -0400177 nargs=2, default=None, type='string'),
Sergio Slobodrian901bf4e2017-03-17 12:54:39 -0400178 ])
179 def do_perf_config(self, line, opts):
Sergio Slobodrian4236ade2017-03-17 22:01:20 -0400180 #print(line)
Sergio Slobodrian901bf4e2017-03-17 12:54:39 -0400181 """Show and set the performance monitoring configuration of the device"""
182
183 device = self.get_device(depth=-1)
184 if not self.pm_config_last:
185 self.pm_config_last = device.pm_configs
186
187 # Ensure that a valid sub-command was provided
188 if line.strip() not in {"set", "show", "commit", "reset", ""}:
189 self.poutput(self.colorize('Error: ', 'red') + \
190 self.colorize(self.colorize(line.strip(), 'blue'),
191 'bold') + ' is not recognized')
192 return
193
194 # Ensure no options are provided when requesting to view the config
195 if line.strip() == "show" or line.strip() == "":
196 if opts.default_freq or opts.enable or opts.disable:
197 self.poutput(opts.disable)
198 self.poutput(self.colorize('Error: ', 'red') + 'use ' + \
199 self.colorize(self.colorize('"set"', 'blue'),
200 'bold') + ' to change settings')
201 return
202
203 if line.strip() == "set": # Set the supplied values
204 # The defualt frequency
205 if opts.default_freq:
206 self.pm_config_last.default_freq = opts.default_freq
207 self.pm_config_dirty = True
208
Sergio Slobodrian4236ade2017-03-17 22:01:20 -0400209 # Field or group visibility
Sergio Slobodrian901bf4e2017-03-17 12:54:39 -0400210 if self.pm_config_last.grouped:
211 for g in self.pm_config_last.groups:
212 if opts.enable:
213 if g.group_name in opts.enable:
214 g.enabled = True
215 self.pm_config_dirty = True
216 for g in self.pm_config_last.groups:
217 if opts.disable:
218 if g.group_name in opts.disable:
219 g.enabled = False
220 self.pm_config_dirty = True
221 else:
222 for m in self.pm_config_last.metrics:
223 if opts.enable:
224 if m.name in opts.enable:
225 m.enabled = True
226 self.pm_config_dirty = True
227 for m in self.pm_config_last.metrics:
228 if opts.disable:
229 if m.name in opts.disable:
230 m.enabled = False
231 self.pm_config_dirty = True
Sergio Slobodrian4236ade2017-03-17 22:01:20 -0400232
233 #Frequency overrides.
234 if opts.override:
235 if self.pm_config_last.freq_override:
236 oo = dict()
237 for o in opts.override:
238 oo[o[0]] = o[1]
239 if self.pm_config_last.grouped:
240 for g in self.pm_config_last.groups:
241 if g.group_name in oo:
242 try:
243 g.group_freq = int(oo[g.group_name])
244 except ValueError:
245 self.poutput(self.colorize('Warning: ',
246 'yellow') + \
247 self.colorize(oo[m.name],
248 'blue') +\
249 " is not an integer... ignored")
250 del oo[g.group_name]
251 self.pm_config_dirty = True
252 else:
253 for m in self.pm_config_last.metrics:
254 if m.name in oo:
255 try:
256 m.sample_freq = int(oo[m.name])
257 except ValueError:
258 self.poutput(self.colorize('Warning: ',
259 'yellow') + \
260 self.colorize(oo[m.name],
261 'blue') +\
262 " is not an integer... ignored")
263 del oo[m.name]
264 self.pm_config_dirty = True
265
266 # If there's anything left the input was typoed
267 if self.pm_config_last.grouped:
268 field = 'group'
269 else:
270 field = 'metric'
271 for o in oo:
272 self.poutput(self.colorize('Warning: ', 'yellow') + \
273 'the parameter' + ' ' + \
274 self.colorize(o, 'blue') + ' is not ' + \
275 'a ' + field + ' name... ignored')
276 if oo:
277 return
278
279 else: # Frequency overrides not enabled
280 self.poutput(self.colorize('Error: ', 'red') + \
281 'Individual overrides are only ' + \
282 'supported if ' + \
283 self.colorize('freq_override', 'blue') + \
284 ' is set to ' + self.colorize('True', 'blue'))
285 return
286 self.poutput("Success")
287 return
288
Sergio Slobodrian901bf4e2017-03-17 12:54:39 -0400289 elif line.strip() == "commit" and self.pm_config_dirty:
khenaidoo108f05c2017-07-06 11:15:29 -0400290 stub = self.get_stub()
Sergio Slobodrian901bf4e2017-03-17 12:54:39 -0400291 stub.UpdateDevicePmConfigs(self.pm_config_last)
292 self.pm_config_last = self.get_device(depth=-1).pm_configs
293 self.pm_config_dirty = False
294 elif line.strip() == "reset" and self.pm_config_dirty:
295 self.pm_config_last = self.get_device(depth=-1).pm_configs
296 self.pm_config_dirty = False
297
Sergio Slobodrian4236ade2017-03-17 22:01:20 -0400298
Sergio Slobodrian901bf4e2017-03-17 12:54:39 -0400299 omit_fields = {'groups', 'metrics', 'id'}
300 print_pb_as_table('PM Config:', self.pm_config_last, omit_fields,
Sergio Slobodriana95f99b2017-03-21 10:22:47 -0400301 self.poutput,show_nulls=True)
Sergio Slobodrian901bf4e2017-03-17 12:54:39 -0400302 if self.pm_config_last.grouped:
303 #self.poutput("Supported metric groups:")
304 for g in self.pm_config_last.groups:
305 if self.pm_config_last.freq_override:
306 omit_fields = {'metrics'}
307 else:
308 omit_fields = {'group_freq','metrics'}
Sergio Slobodriana95f99b2017-03-21 10:22:47 -0400309 print_pb_as_table('', g, omit_fields, self.poutput,
310 show_nulls=True)
Sergio Slobodrian901bf4e2017-03-17 12:54:39 -0400311 if g.enabled:
312 state = 'enabled'
313 else:
314 state = 'disabled'
315 print_pb_list_as_table(
Sergio Slobodriana4b89c02017-04-03 12:48:36 -0400316 'Metric group {} is {}'.format(g.group_name,state),
Sergio Slobodrian901bf4e2017-03-17 12:54:39 -0400317 g.metrics, {'enabled', 'sample_freq'}, self.poutput,
Sergio Slobodriana95f99b2017-03-21 10:22:47 -0400318 dividers=100, show_nulls=True)
Sergio Slobodrian901bf4e2017-03-17 12:54:39 -0400319 else:
320 if self.pm_config_last.freq_override:
321 omit_fields = {}
322 else:
323 omit_fields = {'sample_freq'}
324 print_pb_list_as_table('Supported metrics:', self.pm_config_last.metrics,
Sergio Slobodrian038bd3c2017-03-22 15:53:25 -0400325 omit_fields, self.poutput, dividers=100,
326 show_nulls=True)
Sergio Slobodrian901bf4e2017-03-17 12:54:39 -0400327
328
Zsolt Haraszti80175202016-12-24 00:17:51 -0800329 def do_flows(self, line):
Zsolt Harasztid036b7e2016-12-23 15:36:01 -0800330 """Show flow table for device"""
331 device = pb2dict(self.get_device(-1))
332 print_flows(
333 'Device',
334 self.device_id,
335 type=device['type'],
336 flows=device['flows']['items'],
337 groups=device['flow_groups']['items']
338 )
339
ggowdru236bd952017-06-20 20:32:55 -0700340 def do_images(self, line):
341 """Show software images on the device"""
342 device = self.get_device(depth=-1)
343 omit_fields = {}
344 print_pb_list_as_table('Software Images:', device.images.image,
345 omit_fields, self.poutput, show_nulls=True)
346
Lydia Fang01f2e852017-06-28 17:24:58 -0700347 @options([
348 make_option('-u', '--url', action='store', dest='url',
349 help="URL to get sw image"),
350 make_option('-n', '--name', action='store', dest='name',
351 help="Image name"),
352 make_option('-c', '--crc', action='store', dest='crc',
353 help="CRC code to verify with", default=0),
354 make_option('-v', '--version', action='store', dest='version',
355 help="Image version", default=0),
356 ])
357 def do_img_dnld_request(self, line, opts):
358 """
359 Request image download to a device
360 """
361 device = self.get_device(depth=-1)
362 self.poutput('device_id {}'.format(device.id))
363 self.poutput('name {}'.format(opts.name))
364 self.poutput('url {}'.format(opts.url))
365 self.poutput('crc {}'.format(opts.crc))
366 self.poutput('version {}'.format(opts.version))
367 try:
368 device_id = device.id
369 if device_id and opts.name and opts.url:
370 kw = dict(id=device_id)
371 kw['name'] = opts.name
372 kw['url'] = opts.url
373 else:
374 self.poutput('Device ID and URL are needed')
375 raise Exception('Device ID and URL are needed')
376 except Exception as e:
377 self.poutput('Error request img dnld {}. Error:{}'.format(device_id, e))
378 return
379 kw['crc'] = long(opts.crc)
380 kw['image_version'] = opts.version
381 response = None
382 try:
383 request = voltha_pb2.ImageDownload(**kw)
384 stub = self.get_stub()
385 response = stub.DownloadImage(request)
386 except Exception as e:
387 self.poutput('Error download image {}. Error:{}'.format(kw['id'], e))
388 return
389 name = enum2name(common_pb2.OperationResp,
390 'OperationReturnCode', response.code)
391 self.poutput('response: {}'.format(name))
392 self.poutput('{}'.format(response))
393
394 @options([
395 make_option('-n', '--name', action='store', dest='name',
396 help="Image name"),
397 ])
398 def do_img_dnld_status(self, line, opts):
399 """
400 Get a image download status
401 """
402 device = self.get_device(depth=-1)
403 self.poutput('device_id {}'.format(device.id))
404 self.poutput('name {}'.format(opts.name))
405 try:
406 device_id = device.id
407 if device_id and opts.name:
408 kw = dict(id=device_id)
409 kw['name'] = opts.name
410 else:
411 self.poutput('Device ID, Image Name are needed')
412 raise Exception('Device ID, Image Name are needed')
413 except Exception as e:
414 self.poutput('Error get img dnld status {}. Error:{}'.format(device_id, e))
415 return
416 status = None
417 try:
418 img_dnld = voltha_pb2.ImageDownload(**kw)
419 stub = self.get_stub()
420 status = stub.GetImageDownloadStatus(img_dnld)
421 except Exception as e:
422 self.poutput('Error get img dnld status {}. Error:{}'.format(device_id, e))
423 return
424 fields_to_omit = {
425 'crc',
426 'local_dir',
427 }
428 try:
429 print_pb_as_table('ImageDownload Status:', status, fields_to_omit, self.poutput)
430 except Exception, e:
431 self.poutput('Error {}. Error:{}'.format(device_id, e))
432
433 def do_img_dnld_list(self, line):
434 """
435 List all image download records for a given device
436 """
437 device = self.get_device(depth=-1)
438 device_id = device.id
439 self.poutput('Get all img dnld records {}'.format(device_id))
440 try:
441 stub = self.get_stub()
442 img_dnlds = stub.ListImageDownloads(voltha_pb2.ID(id=device_id))
443 except Exception, e:
444 self.poutput('Error list img dnlds {}. Error:{}'.format(device_id, e))
445 return
446 fields_to_omit = {
447 'crc',
448 'local_dir',
449 }
450 try:
451 print_pb_list_as_table('ImageDownloads:', img_dnlds.items, fields_to_omit, self.poutput)
452 except Exception, e:
453 self.poutput('Error {}. Error:{}'.format(device_id, e))
454
455
456 @options([
457 make_option('-n', '--name', action='store', dest='name',
458 help="Image name"),
459 ])
460 def do_img_dnld_cancel(self, line, opts):
461 """
462 Cancel a requested image download
463 """
464 device = self.get_device(depth=-1)
465 self.poutput('device_id {}'.format(device.id))
466 self.poutput('name {}'.format(opts.name))
467 device_id = device.id
468 try:
469 if device_id and opts.name:
470 kw = dict(id=device_id)
471 kw['name'] = opts.name
472 else:
473 self.poutput('Device ID, Image Name are needed')
474 raise Exception('Device ID, Image Name are needed')
475 except Exception as e:
476 self.poutput('Error cancel sw dnld {}. Error:{}'.format(device_id, e))
477 return
478 response = None
479 try:
480 img_dnld = voltha_pb2.ImageDownload(**kw)
481 stub = self.get_stub()
482 img_dnld = stub.GetImageDownload(img_dnld)
483 response = stub.CancelImageDownload(img_dnld)
484 except Exception as e:
485 self.poutput('Error cancel sw dnld {}. Error:{}'.format(device_id, e))
486 return
487 name = enum2name(common_pb2.OperationResp,
488 'OperationReturnCode', response.code)
489 self.poutput('response: {}'.format(name))
490 self.poutput('{}'.format(response))
491
492 @options([
493 make_option('-n', '--name', action='store', dest='name',
494 help="Image name"),
495 make_option('-s', '--save', action='store', dest='save_config',
496 help="Save Config", default="True"),
497 make_option('-d', '--dir', action='store', dest='local_dir',
498 help="Image on device location"),
499 ])
500 def do_img_activate(self, line, opts):
501 """
502 Activate an image update on device
503 """
504 device = self.get_device(depth=-1)
505 device_id = device.id
506 try:
507 if device_id and opts.name and opts.local_dir:
508 kw = dict(id=device_id)
509 kw['name'] = opts.name
510 kw['local_dir'] = opts.local_dir
511 else:
512 self.poutput('Device ID, Image Name, and Location are needed')
513 raise Exception('Device ID, Image Name, and Location are needed')
514 except Exception as e:
515 self.poutput('Error activate image {}. Error:{}'.format(device_id, e))
516 return
517 kw['save_config'] = json.loads(opts.save_config.lower())
518 self.poutput('activate image update {} {} {} {}'.format( \
519 kw['id'], kw['name'],
520 kw['local_dir'], kw['save_config']))
521 response = None
522 try:
523 img_dnld = voltha_pb2.ImageDownload(**kw)
524 stub = self.get_stub()
525 img_dnld = stub.GetImageDownload(img_dnld)
526 response = stub.ActivateImageUpdate(img_dnld)
527 except Exception as e:
528 self.poutput('Error activate image {}. Error:{}'.format(kw['id'], e))
529 return
530 name = enum2name(common_pb2.OperationResp,
531 'OperationReturnCode', response.code)
532 self.poutput('response: {}'.format(name))
533 self.poutput('{}'.format(response))
534
535 @options([
536 make_option('-n', '--name', action='store', dest='name',
537 help="Image name"),
538 make_option('-s', '--save', action='store', dest='save_config',
539 help="Save Config", default="True"),
540 make_option('-d', '--dir', action='store', dest='local_dir',
541 help="Image on device location"),
542 ])
543 def do_img_revert(self, line, opts):
544 """
545 Revert an image update on device
546 """
547 device = self.get_device(depth=-1)
548 device_id = device.id
549 try:
550 if device_id and opts.name and opts.local_dir:
551 kw = dict(id=device_id)
552 kw['name'] = opts.name
553 kw['local_dir'] = opts.local_dir
554 else:
555 self.poutput('Device ID, Image Name, and Location are needed')
556 raise Exception('Device ID, Image Name, and Location are needed')
557 except Exception as e:
558 self.poutput('Error revert image {}. Error:{}'.format(device_id, e))
559 return
560 kw['save_config'] = json.loads(opts.save_config.lower())
561 self.poutput('revert image update {} {} {} {}'.format( \
562 kw['id'], kw['name'],
563 kw['local_dir'], kw['save_config']))
564 response = None
565 try:
566 img_dnld = voltha_pb2.ImageDownload(**kw)
567 stub = self.get_stub()
568 img_dnld = stub.GetImageDownload(img_dnld)
569 response = stub.RevertImageUpdate(img_dnld)
570 except Exception as e:
571 self.poutput('Error revert image {}. Error:{}'.format(kw['id'], e))
572 return
573 name = enum2name(common_pb2.OperationResp,
574 'OperationReturnCode', response.code)
575 self.poutput('response: {}'.format(name))
576 self.poutput('{}'.format(response))