blob: 95768c867722256fa8ec2620b21f478eea867dac [file] [log] [blame]
khenaidoofdbad6e2018-11-06 22:26:38 -05001#!/usr/bin/env python
2#
3# Copyright 2018 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"""
19OpenOMCI level CLI commands
20"""
21from optparse import make_option
22from cmd2 import Cmd, options
23from datetime import datetime
24from google.protobuf.empty_pb2 import Empty
25from table import print_pb_list_as_table
Arun Aroraed4b7602019-04-02 18:42:37 +000026from voltha_protos import voltha_pb2
27from voltha_protos.omci_mib_db_pb2 import MibDeviceData, MibClassData, \
khenaidoofdbad6e2018-11-06 22:26:38 -050028 MibInstanceData
29from os import linesep
30
khenaidoofdbad6e2018-11-06 22:26:38 -050031
32
33class OmciCli(Cmd):
34 CREATED_KEY = 'created'
35 MODIFIED_KEY = 'modified'
36 MDS_KEY = 'mib_data_sync'
37 LAST_SYNC_KEY = 'last_mib_sync'
38 VERSION_KEY = 'version'
39 DEVICE_ID_KEY = 'device_id'
40 CLASS_ID_KEY = 'class_id'
41 INSTANCE_ID_KEY = 'instance_id'
42 ATTRIBUTES_KEY = 'attributes'
43 TIME_FORMAT = '%Y%m%d-%H%M%S.%f'
44 ME_KEY = 'managed_entities'
45 MSG_TYPE_KEY = 'message_types'
46
47 MSG_TYPE_TO_NAME = {
48 4: 'Create',
49 5: 'Create Complete',
50 6: 'Delete',
51 8: 'Set',
52 9: 'Get',
53 10: 'Get Complete',
54 11: 'Get All Alarms',
55 12: 'Get All Alarms Next',
56 13: 'Mib Upload',
57 14: 'Mib Upload Next',
58 15: 'Mib Reset',
59 16: 'Alarm Notification',
60 17: 'Attribute Value Change',
61 18: 'Test',
62 19: 'Start Software Download',
63 20: 'Download Section',
64 21: 'End Software Download',
65 22: 'Activate Software',
66 23: 'Commit Software',
67 24: 'Synchronize Time',
68 25: 'Reboot',
69 26: 'Get Next',
70 27: 'Test Result',
71 28: 'Get Current Data',
72 29: 'Set Table'
73 }
74
75 def __init__(self, device_id, get_stub):
76 Cmd.__init__(self)
77 self.get_stub = get_stub
78 self.device_id = device_id
79 self.prompt = '(' + self.colorize(
80 self.colorize('omci {}'.format(device_id), 'green'),
81 'bold') + ') '
82
83 def cmdloop(self, intro=None):
84 self._cmdloop()
85
86 do_exit = Cmd.do_quit
87
88 def do_quit(self, line):
89 return self._STOP_AND_EXIT
90
91 def get_device_mib(self, device_id, depth=-1):
92 stub = self.get_stub()
93
94 try:
95 res = stub.GetMibDeviceData(voltha_pb2.ID(id=device_id),
96 metadata=(('get-depth', str(depth)), ))
97 except Exception as e:
98 pass
99
100 return res
101
102 def help_show_mib(self):
103 self.poutput('show_mib [-d <device-id>] [-c <class-id> [-i <instance-id>]]' +
104 linesep + '-d: <device-id> ONU Device ID' +
105 linesep + '-c: <class-id> Managed Entity Class ID' +
106 linesep + '-i: <instance-id> ME Instance ID')
107
108 @options([
109 make_option('-d', '--device-id', action="store", dest='device_id', type='string',
110 help='ONU Device ID', default=None),
111 make_option('-c', '--class-id', action="store", dest='class_id',
112 type='int', help='Managed Entity Class ID', default=None),
113 make_option('-i', '--instance-id', action="store", dest='instance_id',
114 type='int', help='ME Instance ID', default=None)
115 ])
116 def do_show_mib(self, _line, opts):
117 """
118 Show OMCI MIB Database Information
119 """
120 device_id = opts.device_id or self.device_id
121
122 if opts.class_id is not None and not 1 <= opts.class_id <= 0xFFFF:
123 self.poutput(self.colorize('Error: ', 'red') +
124 self.colorize('Class ID must be 1..65535', 'blue'))
125 return
126
127 if opts.instance_id is not None and opts.class_id is None:
128 self.poutput(self.colorize('Error: ', 'red') +
129 self.colorize('Class ID required if specifying an Instance ID',
130 'blue'))
131 return
132
133 if opts.instance_id is not None and not 0 <= opts.instance_id <= 0xFFFF:
134 self.poutput(self.colorize('Error: ', 'red') +
135 self.colorize('Instance ID must be 0..65535', 'blue'))
136 return
137
138 try:
139 mib_db = self.get_device_mib(device_id, depth=-1)
140
141 except Exception: # UnboundLocalError if Device ID not found in DB
142 self.poutput(self.colorize('Failed to get MIB database for ONU {}'
143 .format(device_id), 'red'))
144 return
145
146 mib = self._device_to_dict(mib_db)
147
148 self.poutput('OpenOMCI MIB Database for ONU {}'.format(device_id))
149
150 if opts.class_id is None and opts.instance_id is None:
151 self.poutput('Version : {}'.format(mib[OmciCli.VERSION_KEY]))
152 self.poutput('Created : {}'.format(mib[OmciCli.CREATED_KEY]))
153 self.poutput('Last In-Sync Time : {}'.format(mib[OmciCli.LAST_SYNC_KEY]))
154 self.poutput('MIB Data Sync Value: {}'.format(mib[OmciCli.MDS_KEY]))
155
156 class_ids = [k for k in mib.iterkeys()
157 if isinstance(k, int) and
158 (opts.class_id is None or opts.class_id == k)]
159 class_ids.sort()
160
161 if len(class_ids) == 0 and opts.class_id is not None:
162 self.poutput(self.colorize('Class ID {} not found in MIB Database'
163 .format(opts.class_id), 'red'))
164 return
165
166 for cls_id in class_ids:
167 class_data = mib[cls_id]
168 self.poutput(' ----------------------------------------------')
169 self.poutput(' Class ID: {0} - ({0:#x})'.format(cls_id))
170
171 inst_ids = [k for k in class_data.iterkeys()
172 if isinstance(k, int) and
173 (opts.instance_id is None or opts.instance_id == k)]
174 inst_ids.sort()
175
176 if len(inst_ids) == 0 and opts.instance_id is not None:
177 self.poutput(self.colorize('Instance ID {} of Class ID {} not ' +
178 'found in MIB Database'.
179 format(opts.instance_id, opts.class_id),
180 'red'))
181 return
182
183 for inst_id in inst_ids:
184 inst_data = class_data[inst_id]
185 self.poutput(' Instance ID: {0} - ({0:#x})'.format(inst_id))
186 self.poutput(' Created : {}'.format(inst_data[OmciCli.CREATED_KEY]))
187 self.poutput(' Modified : {}'.format(inst_data[OmciCli.MODIFIED_KEY]))
188
189 attributes = inst_data[OmciCli.ATTRIBUTES_KEY]
190 attr_names = attributes.keys()
191 attr_names.sort()
192 max_len = max([len(attr) for attr in attr_names])
193
194 for attr in attr_names:
195 name = self._cleanup_attribute_name(attr).ljust(max_len)
196 value = attributes[attr]
197 try:
198 ivalue = int(value)
199 self.poutput(' {0}: {1} - ({1:#x})'.format(name, ivalue))
200
201 except ValueError:
202 self.poutput(' {}: {}'.format(name, value))
203
204 if inst_id is not inst_ids[-1]:
205 self.poutput(linesep)
206
207 def _cleanup_attribute_name(self, attr):
208 """Change underscore to space and capitalize first character"""
209 return ' '.join([v[0].upper() + v[1:] for v in attr.split('_')])
210
211 def _instance_to_dict(self, instance):
212 if not isinstance(instance, MibInstanceData):
213 raise TypeError('{} is not of type MibInstanceData'.format(type(instance)))
214
215 data = {
216 OmciCli.INSTANCE_ID_KEY: instance.instance_id,
217 OmciCli.CREATED_KEY: self._string_to_time(instance.created),
218 OmciCli.MODIFIED_KEY: self._string_to_time(instance.modified),
219 OmciCli.ATTRIBUTES_KEY: dict()
220 }
221 for attribute in instance.attributes:
222 data[OmciCli.ATTRIBUTES_KEY][attribute.name] = str(attribute.value)
223
224 return data
225
226 def _class_to_dict(self, val):
227 if not isinstance(val, MibClassData):
228 raise TypeError('{} is not of type MibClassData'.format(type(val)))
229
230 data = {
231 OmciCli.CLASS_ID_KEY: val.class_id,
232 }
233 for instance in val.instances:
234 data[instance.instance_id] = self._instance_to_dict(instance)
235 return data
236
237 def _device_to_dict(self, val):
238 if not isinstance(val, MibDeviceData):
239 raise TypeError('{} is not of type MibDeviceData'.format(type(val)))
240
241 data = {
242 OmciCli.DEVICE_ID_KEY: val.device_id,
243 OmciCli.CREATED_KEY: self._string_to_time(val.created),
244 OmciCli.LAST_SYNC_KEY: self._string_to_time(val.last_sync_time),
245 OmciCli.MDS_KEY: val.mib_data_sync,
246 OmciCli.VERSION_KEY: val.version,
247 OmciCli.ME_KEY: dict(),
248 OmciCli.MSG_TYPE_KEY: set()
249 }
250 for class_data in val.classes:
251 data[class_data.class_id] = self._class_to_dict(class_data)
252
253 for managed_entity in val.managed_entities:
254 data[OmciCli.ME_KEY][managed_entity.class_id] = managed_entity.name
255
256 for msg_type in val.message_types:
257 data[OmciCli.MSG_TYPE_KEY].add(msg_type.message_type)
258
259 return data
260
261 def _string_to_time(self, time):
262 return datetime.strptime(time, OmciCli.TIME_FORMAT) if len(time) else None
263
264 def help_show_me(self):
265 self.poutput('show_me [-d <device-id>]' +
266 linesep + '-d: <device-id> ONU Device ID')
267
268 @options([
269 make_option('-d', '--device-id', action="store", dest='device_id', type='string',
270 help='ONU Device ID', default=None),
271 ])
272 def do_show_me(self, _line, opts):
273 """ Show supported OMCI Managed Entities"""
274
275 device_id = opts.device_id or self.device_id
276
277 try:
278 mib_db = self.get_device_mib(device_id, depth=1)
279 mib = self._device_to_dict(mib_db)
280
281 except Exception: # UnboundLocalError if Device ID not found in DB
282 self.poutput(self.colorize('Failed to get supported ME information for ONU {}'
283 .format(device_id), 'red'))
284 return
285
286 class_ids = [class_id for class_id in mib[OmciCli.ME_KEY].keys()]
287 class_ids.sort()
288
289 self.poutput('Supported Managed Entities for ONU {}'.format(device_id))
290 for class_id in class_ids:
291 self.poutput(' {0} - ({0:#x}): {1}'.format(class_id,
292 mib[OmciCli.ME_KEY][class_id]))
293
294 def help_show_msg_types(self):
295 self.poutput('show_msg_types [-d <device-id>]' +
296 linesep + '-d: <device-id> ONU Device ID')
297
298 @options([
299 make_option('-d', '--device-id', action="store", dest='device_id', type='string',
300 help='ONU Device ID', default=None),
301 ])
302 def do_show_msg_types(self, _line, opts):
303 """ Show supported OMCI Message Types"""
304 device_id = opts.device_id or self.device_id
305
306 try:
307 mib_db = self.get_device_mib(device_id, depth=1)
308 mib = self._device_to_dict(mib_db)
309
310 except Exception: # UnboundLocalError if Device ID not found in DB
311 self.poutput(self.colorize('Failed to get supported Message Types for ONU {}'
312 .format(device_id), 'red'))
313 return
314
315 msg_types = [msg_type for msg_type in mib[OmciCli.MSG_TYPE_KEY]]
316 msg_types.sort()
317
318 self.poutput('Supported Message Types for ONU {}'.format(device_id))
319 for msg_type in msg_types:
320 self.poutput(' {0} - ({0:#x}): {1}'.
321 format(msg_type,
322 OmciCli.MSG_TYPE_TO_NAME.get(msg_type, 'Unknown')))
323
324 def get_devices(self):
325 stub = self.get_stub()
326 res = stub.ListDevices(Empty())
327 return res.items
328
329 def do_devices(self, line):
330 """List devices registered in Voltha reduced for OMCI menu"""
331 devices = self.get_devices()
332 omit_fields = {
333 'adapter',
334 'model',
335 'hardware_version',
336 'images',
337 'firmware_version',
338 'serial_number',
339 'vlan',
340 'root',
341 'extra_args',
342 'proxy_address',
343 }
344 print_pb_list_as_table('Devices:', devices, omit_fields, self.poutput)
345
346 def help_devices(self):
347 self.poutput('TODO: Provide some help')
348
349 def poutput(self, msg):
350 """Convenient shortcut for self.stdout.write(); adds newline if necessary."""
351 if msg:
352 self.stdout.write(msg)
353 if msg[-1] != '\n':
354 self.stdout.write('\n')