This commit cleans up the python directory to ensure the adapters
and the cli runs properly.
Change-Id: Ic68a3ecd1f16a5af44296e3c020c808b185f4c18
diff --git a/python/cli/device.py b/python/cli/device.py
new file mode 100644
index 0000000..38ea835
--- /dev/null
+++ b/python/cli/device.py
@@ -0,0 +1,597 @@
+#!/usr/bin/env python
+#
+# Copyright 2016 the original author or authors.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+"""
+Device level CLI commands
+"""
+from optparse import make_option
+from cmd2 import Cmd, options
+from simplejson import dumps
+
+from table import print_pb_as_table, print_pb_list_as_table
+from utils import print_flows, pb2dict, enum2name
+from python.protos import third_party
+
+_ = third_party
+from python.protos import voltha_pb2, common_pb2
+import sys
+import json
+from google.protobuf.json_format import MessageToDict
+
+# Since proto3 won't send fields that are set to 0/false/"" any object that
+# might have those values set in them needs to be replicated here such that the
+# fields can be adequately
+
+
+class DeviceCli(Cmd):
+
+ def __init__(self, device_id, get_stub):
+ Cmd.__init__(self)
+ self.get_stub = get_stub
+ self.device_id = device_id
+ self.prompt = '(' + self.colorize(
+ self.colorize('device {}'.format(device_id), 'red'), 'bold') + ') '
+ self.pm_config_last = None
+ self.pm_config_dirty = False
+
+ def cmdloop(self):
+ self._cmdloop()
+
+ def get_device(self, depth=0):
+ stub = self.get_stub()
+ res = stub.GetDevice(voltha_pb2.ID(id=self.device_id),
+ metadata=(('get-depth', str(depth)), ))
+ return res
+
+ do_exit = Cmd.do_quit
+
+ def do_quit(self, line):
+ if self.pm_config_dirty:
+ self.poutput("Uncommited changes for " + \
+ self.colorize(
+ self.colorize("perf_config,", "blue"),
+ "bold") + " please either " + self.colorize(
+ self.colorize("commit", "blue"), "bold") + \
+ " or " + self.colorize(
+ self.colorize("reset", "blue"), "bold") + \
+ " your changes using " + \
+ self.colorize(
+ self.colorize("perf_config", "blue"), "bold"))
+ return False
+ else:
+ return self._STOP_AND_EXIT
+
+ def do_show(self, line):
+ """Show detailed device information"""
+ print_pb_as_table('Device {}'.format(self.device_id),
+ self.get_device(depth=-1))
+
+ def do_ports(self, line):
+ """Show ports of device"""
+ device = self.get_device(depth=-1)
+ omit_fields = {
+ }
+ print_pb_list_as_table('Device ports:', device.ports,
+ omit_fields, self.poutput)
+
+ def complete_perf_config(self, text, line, begidx, endidx):
+ sub_cmds = {"show", "set", "commit", "reset"}
+ sub_opts = {"-f", "-e", "-d", "-o"}
+ # Help the interpreter complete the paramters.
+ completions = []
+ if not self.pm_config_last:
+ device = self.get_device(depth=-1)
+ self.pm_config_last = device.pm_configs
+ m_names = [d.name for d in self.pm_config_last.metrics]
+ cur_cmd = line.strip().split(" ")
+ try:
+ if not text and len(cur_cmd) == 1:
+ completions = ("show", "set", "commit", "reset")
+ elif len(cur_cmd) == 2:
+ if "set" == cur_cmd[1]:
+ completions = [d for d in sub_opts]
+ else:
+ completions = [d for d in sub_cmds if d.startswith(text)]
+ elif len(cur_cmd) > 2 and cur_cmd[1] == "set":
+ if cur_cmd[len(cur_cmd)-1] == "-":
+ completions = [list(d)[1] for d in sub_opts]
+ elif cur_cmd[len(cur_cmd)-1] == "-f":
+ completions = ("\255","Please enter a sampling frequency in 10ths of a second")
+ elif cur_cmd[len(cur_cmd)-2] == "-f":
+ completions = [d for d in sub_opts]
+ elif cur_cmd[len(cur_cmd)-1] in {"-e","-d","-o"}:
+ if self.pm_config_last.grouped:
+ pass
+ else:
+ completions = [d.name for d in self.pm_config_last.metrics]
+ elif cur_cmd[len(cur_cmd)-2] in {"-e","-d"}:
+ if text and text not in m_names:
+ completions = [d for d in m_names if d.startswith(text)]
+ else:
+ completions = [d for d in sub_opts]
+ elif cur_cmd[len(cur_cmd)-2] == "-o":
+ if cur_cmd[len(cur_cmd)-1] in [d.name for d in self.pm_config_last.metrics]:
+ completions = ("\255","Please enter a sampling frequency in 10ths of a second")
+ else:
+ completions = [d for d in m_names if d.startswith(text)]
+ elif cur_cmd[len(cur_cmd)-3] == "-o":
+ completions = [d for d in sub_opts]
+ except:
+ e = sys.exc_info()
+ print(e)
+ return completions
+
+
+ def help_perf_config(self):
+ self.poutput(
+'''
+perf_config [show | set | commit | reset] [-f <default frequency>] [{-e <metric/group
+ name>}] [{-d <metric/group name>}] [{-o <metric/group name> <override
+ frequency>}]
+
+show: displays the performance configuration of the device
+set: changes the parameters specified with -e, -d, and -o
+reset: reverts any changes made since the last commit
+commit: commits any changes made which applies them to the device.
+
+-e: enable collection of the specified metric, more than one -e may be
+ specified.
+-d: disable collection of the specified metric, more than on -d may be
+ specified.
+-o: override the collection frequency of the specified metric, more than one -o
+ may be specified. Note that -o isn't valid unless
+ frequency_override is set to True for the device.
+
+Changes made by set are held locally until a commit or reset command is issued.
+A commit command will write the configuration to the device and it takes effect
+immediately. The reset command will undo any changes since the start of the
+device session.
+
+If grouped is true then the -d, -e and -o commands refer to groups and not
+individual metrics.
+'''
+ )
+
+ @options([
+ make_option('-f', '--default_freq', action="store", dest='default_freq',
+ type='long', default=None),
+ make_option('-e', '--enable', action='append', dest='enable',
+ default=None),
+ make_option('-d', '--disable', action='append', dest='disable',
+ default=None),
+ make_option('-o', '--override', action='append', dest='override',
+ nargs=2, default=None, type='string'),
+ ])
+ def do_perf_config(self, line, opts):
+ """Show and set the performance monitoring configuration of the device"""
+
+ device = self.get_device(depth=-1)
+ if not self.pm_config_last:
+ self.pm_config_last = device.pm_configs
+
+ # Ensure that a valid sub-command was provided
+ if line.strip() not in {"set", "show", "commit", "reset", ""}:
+ self.poutput(self.colorize('Error: ', 'red') +
+ self.colorize(self.colorize(line.strip(), 'blue'),
+ 'bold') + ' is not recognized')
+ return
+
+ # Ensure no options are provided when requesting to view the config
+ if line.strip() == "show" or line.strip() == "":
+ if opts.default_freq or opts.enable or opts.disable:
+ self.poutput(opts.disable)
+ self.poutput(self.colorize('Error: ', 'red') + 'use ' +
+ self.colorize(self.colorize('"set"', 'blue'),
+ 'bold') + ' to change settings')
+ return
+
+ if line.strip() == "set": # Set the supplied values
+ metric_list = set()
+ if opts.enable is not None:
+ metric_list |= {metric for metric in opts.enable}
+ if opts.disable is not None:
+ metric_list |= {metric for metric in opts.disable}
+ if opts.override is not None:
+ metric_list |= {metric for metric, _ in opts.override}
+
+ # The default frequency
+ if opts.default_freq:
+ self.pm_config_last.default_freq = opts.default_freq
+ self.pm_config_dirty = True
+
+ # Field or group visibility
+ if self.pm_config_last.grouped:
+ for g in self.pm_config_last.groups:
+ if opts.enable:
+ if g.group_name in opts.enable:
+ g.enabled = True
+ self.pm_config_dirty = True
+ metric_list.discard(g.group_name)
+ for g in self.pm_config_last.groups:
+ if opts.disable:
+ if g.group_name in opts.disable:
+ g.enabled = False
+ self.pm_config_dirty = True
+ metric_list.discard(g.group_name)
+ else:
+ for m in self.pm_config_last.metrics:
+ if opts.enable:
+ if m.name in opts.enable:
+ m.enabled = True
+ self.pm_config_dirty = True
+ metric_list.discard(m.name)
+ for m in self.pm_config_last.metrics:
+ if opts.disable:
+ if m.name in opts.disable:
+ m.enabled = False
+ self.pm_config_dirty = True
+ metric_list.discard(m.name)
+
+ # Frequency overrides.
+ if opts.override:
+ if self.pm_config_last.freq_override:
+ oo = dict()
+ for o in opts.override:
+ oo[o[0]] = o[1]
+ if self.pm_config_last.grouped:
+ for g in self.pm_config_last.groups:
+ if g.group_name in oo:
+ try:
+ g.group_freq = int(oo[g.group_name])
+ except ValueError:
+ self.poutput(self.colorize('Warning: ',
+ 'yellow') +
+ self.colorize(oo[g.group_name],
+ 'blue') +
+ " is not an integer... ignored")
+ del oo[g.group_name]
+ self.pm_config_dirty = True
+ metric_list.discard(g.group_name)
+ else:
+ for m in self.pm_config_last.metrics:
+ if m.name in oo:
+ try:
+ m.sample_freq = int(oo[m.name])
+ except ValueError:
+ self.poutput(self.colorize('Warning: ',
+ 'yellow') +
+ self.colorize(oo[m.name],
+ 'blue') +
+ " is not an integer... ignored")
+ del oo[m.name]
+ self.pm_config_dirty = True
+ metric_list.discard(m.name)
+
+ # If there's anything left the input was typoed
+ if self.pm_config_last.grouped:
+ field = 'group'
+ else:
+ field = 'metric'
+ for o in oo:
+ self.poutput(self.colorize('Warning: ', 'yellow') +
+ 'the parameter' + ' ' +
+ self.colorize(o, 'blue') + ' is not ' +
+ 'a ' + field + ' name... ignored')
+ if oo:
+ return
+
+ else: # Frequency overrides not enabled
+ self.poutput(self.colorize('Error: ', 'red') +
+ 'Individual overrides are only ' +
+ 'supported if ' +
+ self.colorize('freq_override', 'blue') +
+ ' is set to ' + self.colorize('True', 'blue'))
+ return
+
+ if len(metric_list):
+ metric_name_list = ", ".join(str(metric) for metric in metric_list)
+ self.poutput(self.colorize('Error: ', 'red') +
+ 'Metric/Metric Group{} '.format('s' if len(metric_list) > 1 else '') +
+ self.colorize(metric_name_list, 'blue') +
+ ' {} not found'.format('were' if len(metric_list) > 1 else 'was'))
+ return
+
+ self.poutput("Success")
+ return
+
+ elif line.strip() == "commit" and self.pm_config_dirty:
+ stub = self.get_stub()
+ stub.UpdateDevicePmConfigs(self.pm_config_last)
+ self.pm_config_last = self.get_device(depth=-1).pm_configs
+ self.pm_config_dirty = False
+
+ elif line.strip() == "reset" and self.pm_config_dirty:
+ self.pm_config_last = self.get_device(depth=-1).pm_configs
+ self.pm_config_dirty = False
+
+ omit_fields = {'groups', 'metrics', 'id'}
+ print_pb_as_table('PM Config:', self.pm_config_last, omit_fields,
+ self.poutput,show_nulls=True)
+ if self.pm_config_last.grouped:
+ #self.poutput("Supported metric groups:")
+ for g in self.pm_config_last.groups:
+ if self.pm_config_last.freq_override:
+ omit_fields = {'metrics'}
+ else:
+ omit_fields = {'group_freq','metrics'}
+ print_pb_as_table('', g, omit_fields, self.poutput,
+ show_nulls=True)
+ if g.enabled:
+ state = 'enabled'
+ else:
+ state = 'disabled'
+ print_pb_list_as_table(
+ 'Metric group {} is {}'.format(g.group_name,state),
+ g.metrics, {'enabled', 'sample_freq'}, self.poutput,
+ dividers=100, show_nulls=True)
+ else:
+ if self.pm_config_last.freq_override:
+ omit_fields = {}
+ else:
+ omit_fields = {'sample_freq'}
+ print_pb_list_as_table('Supported metrics:', self.pm_config_last.metrics,
+ omit_fields, self.poutput, dividers=100,
+ show_nulls=True)
+
+ def do_flows(self, line):
+ """Show flow table for device"""
+ device = pb2dict(self.get_device(-1))
+ print_flows(
+ 'Device',
+ self.device_id,
+ type=device['type'],
+ flows=device['flows']['items'],
+ groups=device['flow_groups']['items']
+ )
+
+ def do_images(self, line):
+ """Show software images on the device"""
+ device = self.get_device(depth=-1)
+ omit_fields = {}
+ print_pb_list_as_table('Software Images:', device.images.image,
+ omit_fields, self.poutput, show_nulls=True)
+
+ @options([
+ make_option('-u', '--url', action='store', dest='url',
+ help="URL to get sw image"),
+ make_option('-n', '--name', action='store', dest='name',
+ help="Image name"),
+ make_option('-c', '--crc', action='store', dest='crc',
+ help="CRC code to verify with", default=0),
+ make_option('-v', '--version', action='store', dest='version',
+ help="Image version", default=0),
+ ])
+ def do_img_dnld_request(self, line, opts):
+ """
+ Request image download to a device
+ """
+ device = self.get_device(depth=-1)
+ self.poutput('device_id {}'.format(device.id))
+ self.poutput('name {}'.format(opts.name))
+ self.poutput('url {}'.format(opts.url))
+ self.poutput('crc {}'.format(opts.crc))
+ self.poutput('version {}'.format(opts.version))
+ try:
+ device_id = device.id
+ if device_id and opts.name and opts.url:
+ kw = dict(id=device_id)
+ kw['name'] = opts.name
+ kw['url'] = opts.url
+ else:
+ self.poutput('Device ID and URL are needed')
+ raise Exception('Device ID and URL are needed')
+ except Exception as e:
+ self.poutput('Error request img dnld {}. Error:{}'.format(device_id, e))
+ return
+ kw['crc'] = long(opts.crc)
+ kw['image_version'] = opts.version
+ response = None
+ try:
+ request = voltha_pb2.ImageDownload(**kw)
+ stub = self.get_stub()
+ response = stub.DownloadImage(request)
+ except Exception as e:
+ self.poutput('Error download image {}. Error:{}'.format(kw['id'], e))
+ return
+ name = enum2name(common_pb2.OperationResp,
+ 'OperationReturnCode', response.code)
+ self.poutput('response: {}'.format(name))
+ self.poutput('{}'.format(response))
+
+ @options([
+ make_option('-n', '--name', action='store', dest='name',
+ help="Image name"),
+ ])
+ def do_img_dnld_status(self, line, opts):
+ """
+ Get a image download status
+ """
+ device = self.get_device(depth=-1)
+ self.poutput('device_id {}'.format(device.id))
+ self.poutput('name {}'.format(opts.name))
+ try:
+ device_id = device.id
+ if device_id and opts.name:
+ kw = dict(id=device_id)
+ kw['name'] = opts.name
+ else:
+ self.poutput('Device ID, Image Name are needed')
+ raise Exception('Device ID, Image Name are needed')
+ except Exception as e:
+ self.poutput('Error get img dnld status {}. Error:{}'.format(device_id, e))
+ return
+ status = None
+ try:
+ img_dnld = voltha_pb2.ImageDownload(**kw)
+ stub = self.get_stub()
+ status = stub.GetImageDownloadStatus(img_dnld)
+ except Exception as e:
+ self.poutput('Error get img dnld status {}. Error:{}'.format(device_id, e))
+ return
+ fields_to_omit = {
+ 'crc',
+ 'local_dir',
+ }
+ try:
+ print_pb_as_table('ImageDownload Status:', status, fields_to_omit, self.poutput)
+ except Exception, e:
+ self.poutput('Error {}. Error:{}'.format(device_id, e))
+
+ def do_img_dnld_list(self, line):
+ """
+ List all image download records for a given device
+ """
+ device = self.get_device(depth=-1)
+ device_id = device.id
+ self.poutput('Get all img dnld records {}'.format(device_id))
+ try:
+ stub = self.get_stub()
+ img_dnlds = stub.ListImageDownloads(voltha_pb2.ID(id=device_id))
+ except Exception, e:
+ self.poutput('Error list img dnlds {}. Error:{}'.format(device_id, e))
+ return
+ fields_to_omit = {
+ 'crc',
+ 'local_dir',
+ }
+ try:
+ print_pb_list_as_table('ImageDownloads:', img_dnlds.items, fields_to_omit, self.poutput)
+ except Exception, e:
+ self.poutput('Error {}. Error:{}'.format(device_id, e))
+
+
+ @options([
+ make_option('-n', '--name', action='store', dest='name',
+ help="Image name"),
+ ])
+ def do_img_dnld_cancel(self, line, opts):
+ """
+ Cancel a requested image download
+ """
+ device = self.get_device(depth=-1)
+ self.poutput('device_id {}'.format(device.id))
+ self.poutput('name {}'.format(opts.name))
+ device_id = device.id
+ try:
+ if device_id and opts.name:
+ kw = dict(id=device_id)
+ kw['name'] = opts.name
+ else:
+ self.poutput('Device ID, Image Name are needed')
+ raise Exception('Device ID, Image Name are needed')
+ except Exception as e:
+ self.poutput('Error cancel sw dnld {}. Error:{}'.format(device_id, e))
+ return
+ response = None
+ try:
+ img_dnld = voltha_pb2.ImageDownload(**kw)
+ stub = self.get_stub()
+ img_dnld = stub.GetImageDownload(img_dnld)
+ response = stub.CancelImageDownload(img_dnld)
+ except Exception as e:
+ self.poutput('Error cancel sw dnld {}. Error:{}'.format(device_id, e))
+ return
+ name = enum2name(common_pb2.OperationResp,
+ 'OperationReturnCode', response.code)
+ self.poutput('response: {}'.format(name))
+ self.poutput('{}'.format(response))
+
+ @options([
+ make_option('-n', '--name', action='store', dest='name',
+ help="Image name"),
+ make_option('-s', '--save', action='store', dest='save_config',
+ help="Save Config", default="True"),
+ make_option('-d', '--dir', action='store', dest='local_dir',
+ help="Image on device location"),
+ ])
+ def do_img_activate(self, line, opts):
+ """
+ Activate an image update on device
+ """
+ device = self.get_device(depth=-1)
+ device_id = device.id
+ try:
+ if device_id and opts.name and opts.local_dir:
+ kw = dict(id=device_id)
+ kw['name'] = opts.name
+ kw['local_dir'] = opts.local_dir
+ else:
+ self.poutput('Device ID, Image Name, and Location are needed')
+ raise Exception('Device ID, Image Name, and Location are needed')
+ except Exception as e:
+ self.poutput('Error activate image {}. Error:{}'.format(device_id, e))
+ return
+ kw['save_config'] = json.loads(opts.save_config.lower())
+ self.poutput('activate image update {} {} {} {}'.format( \
+ kw['id'], kw['name'],
+ kw['local_dir'], kw['save_config']))
+ response = None
+ try:
+ img_dnld = voltha_pb2.ImageDownload(**kw)
+ stub = self.get_stub()
+ img_dnld = stub.GetImageDownload(img_dnld)
+ response = stub.ActivateImageUpdate(img_dnld)
+ except Exception as e:
+ self.poutput('Error activate image {}. Error:{}'.format(kw['id'], e))
+ return
+ name = enum2name(common_pb2.OperationResp,
+ 'OperationReturnCode', response.code)
+ self.poutput('response: {}'.format(name))
+ self.poutput('{}'.format(response))
+
+ @options([
+ make_option('-n', '--name', action='store', dest='name',
+ help="Image name"),
+ make_option('-s', '--save', action='store', dest='save_config',
+ help="Save Config", default="True"),
+ make_option('-d', '--dir', action='store', dest='local_dir',
+ help="Image on device location"),
+ ])
+ def do_img_revert(self, line, opts):
+ """
+ Revert an image update on device
+ """
+ device = self.get_device(depth=-1)
+ device_id = device.id
+ try:
+ if device_id and opts.name and opts.local_dir:
+ kw = dict(id=device_id)
+ kw['name'] = opts.name
+ kw['local_dir'] = opts.local_dir
+ else:
+ self.poutput('Device ID, Image Name, and Location are needed')
+ raise Exception('Device ID, Image Name, and Location are needed')
+ except Exception as e:
+ self.poutput('Error revert image {}. Error:{}'.format(device_id, e))
+ return
+ kw['save_config'] = json.loads(opts.save_config.lower())
+ self.poutput('revert image update {} {} {} {}'.format( \
+ kw['id'], kw['name'],
+ kw['local_dir'], kw['save_config']))
+ response = None
+ try:
+ img_dnld = voltha_pb2.ImageDownload(**kw)
+ stub = self.get_stub()
+ img_dnld = stub.GetImageDownload(img_dnld)
+ response = stub.RevertImageUpdate(img_dnld)
+ except Exception as e:
+ self.poutput('Error revert image {}. Error:{}'.format(kw['id'], e))
+ return
+ name = enum2name(common_pb2.OperationResp,
+ 'OperationReturnCode', response.code)
+ self.poutput('response: {}'.format(name))
+ self.poutput('{}'.format(response))