blob: 9b11d3272ae8daabdd507b716a89bfe679ee470e [file] [log] [blame]
Chip Boling69abce82018-06-18 09:56:23 -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 cli.table import print_pb_list_as_table
26from voltha.protos import third_party
27from voltha.protos import voltha_pb2
28from voltha.protos.omci_mib_db_pb2 import MibDeviceData, MibClassData, \
29 MibInstanceData
Chip Boling5584d202018-11-09 15:17:40 -060030from voltha.protos.omci_alarm_db_pb2 import AlarmDeviceData, AlarmClassData, \
31 AlarmInstanceData
Chip Boling69abce82018-06-18 09:56:23 -050032from os import linesep
33
34_ = third_party
35
36
37class OmciCli(Cmd):
38 CREATED_KEY = 'created'
39 MODIFIED_KEY = 'modified'
40 MDS_KEY = 'mib_data_sync'
41 LAST_SYNC_KEY = 'last_mib_sync'
42 VERSION_KEY = 'version'
43 DEVICE_ID_KEY = 'device_id'
44 CLASS_ID_KEY = 'class_id'
45 INSTANCE_ID_KEY = 'instance_id'
46 ATTRIBUTES_KEY = 'attributes'
47 TIME_FORMAT = '%Y%m%d-%H%M%S.%f'
48 ME_KEY = 'managed_entities'
49 MSG_TYPE_KEY = 'message_types'
50
51 MSG_TYPE_TO_NAME = {
52 4: 'Create',
53 5: 'Create Complete',
54 6: 'Delete',
55 8: 'Set',
56 9: 'Get',
57 10: 'Get Complete',
58 11: 'Get All Alarms',
59 12: 'Get All Alarms Next',
60 13: 'Mib Upload',
61 14: 'Mib Upload Next',
62 15: 'Mib Reset',
63 16: 'Alarm Notification',
64 17: 'Attribute Value Change',
65 18: 'Test',
66 19: 'Start Software Download',
67 20: 'Download Section',
68 21: 'End Software Download',
69 22: 'Activate Software',
70 23: 'Commit Software',
71 24: 'Synchronize Time',
72 25: 'Reboot',
73 26: 'Get Next',
74 27: 'Test Result',
75 28: 'Get Current Data',
76 29: 'Set Table'
77 }
78
79 def __init__(self, device_id, get_stub):
80 Cmd.__init__(self)
81 self.get_stub = get_stub
82 self.device_id = device_id
83 self.prompt = '(' + self.colorize(
84 self.colorize('omci {}'.format(device_id), 'green'),
85 'bold') + ') '
86
87 def cmdloop(self, intro=None):
88 self._cmdloop()
89
90 do_exit = Cmd.do_quit
91
92 def do_quit(self, line):
93 return self._STOP_AND_EXIT
94
95 def get_device_mib(self, device_id, depth=-1):
96 stub = self.get_stub()
97
98 try:
99 res = stub.GetMibDeviceData(voltha_pb2.ID(id=device_id),
100 metadata=(('get-depth', str(depth)), ))
Chip Boling5584d202018-11-09 15:17:40 -0600101 except Exception as _e:
102 res = None
Chip Boling69abce82018-06-18 09:56:23 -0500103
104 return res
105
106 def help_show_mib(self):
107 self.poutput('show_mib [-d <device-id>] [-c <class-id> [-i <instance-id>]]' +
108 linesep + '-d: <device-id> ONU Device ID' +
109 linesep + '-c: <class-id> Managed Entity Class ID' +
110 linesep + '-i: <instance-id> ME Instance ID')
111
112 @options([
113 make_option('-d', '--device-id', action="store", dest='device_id', type='string',
114 help='ONU Device ID', default=None),
115 make_option('-c', '--class-id', action="store", dest='class_id',
116 type='int', help='Managed Entity Class ID', default=None),
117 make_option('-i', '--instance-id', action="store", dest='instance_id',
118 type='int', help='ME Instance ID', default=None)
119 ])
120 def do_show_mib(self, _line, opts):
121 """
122 Show OMCI MIB Database Information
123 """
124 device_id = opts.device_id or self.device_id
125
126 if opts.class_id is not None and not 1 <= opts.class_id <= 0xFFFF:
127 self.poutput(self.colorize('Error: ', 'red') +
128 self.colorize('Class ID must be 1..65535', 'blue'))
129 return
130
131 if opts.instance_id is not None and opts.class_id is None:
132 self.poutput(self.colorize('Error: ', 'red') +
133 self.colorize('Class ID required if specifying an Instance ID',
134 'blue'))
135 return
136
137 if opts.instance_id is not None and not 0 <= opts.instance_id <= 0xFFFF:
138 self.poutput(self.colorize('Error: ', 'red') +
139 self.colorize('Instance ID must be 0..65535', 'blue'))
140 return
141
142 try:
143 mib_db = self.get_device_mib(device_id, depth=-1)
144
145 except Exception: # UnboundLocalError if Device ID not found in DB
146 self.poutput(self.colorize('Failed to get MIB database for ONU {}'
147 .format(device_id), 'red'))
148 return
149
Chip Boling5584d202018-11-09 15:17:40 -0600150 if mib_db is None:
151 self.poutput(self.colorize('MIB database for ONU {} is not currently available'
152 .format(device_id), 'red'))
153 return
154
Chip Boling69abce82018-06-18 09:56:23 -0500155 mib = self._device_to_dict(mib_db)
156
157 self.poutput('OpenOMCI MIB Database for ONU {}'.format(device_id))
158
159 if opts.class_id is None and opts.instance_id is None:
160 self.poutput('Version : {}'.format(mib[OmciCli.VERSION_KEY]))
161 self.poutput('Created : {}'.format(mib[OmciCli.CREATED_KEY]))
162 self.poutput('Last In-Sync Time : {}'.format(mib[OmciCli.LAST_SYNC_KEY]))
163 self.poutput('MIB Data Sync Value: {}'.format(mib[OmciCli.MDS_KEY]))
164
165 class_ids = [k for k in mib.iterkeys()
166 if isinstance(k, int) and
167 (opts.class_id is None or opts.class_id == k)]
168 class_ids.sort()
169
170 if len(class_ids) == 0 and opts.class_id is not None:
171 self.poutput(self.colorize('Class ID {} not found in MIB Database'
172 .format(opts.class_id), 'red'))
173 return
174
175 for cls_id in class_ids:
176 class_data = mib[cls_id]
177 self.poutput(' ----------------------------------------------')
Chip Boling07912c32018-09-28 14:13:31 -0500178 self.poutput(' Class ID : {0} - ({0:#x}): {1}'.
179 format(cls_id, mib[OmciCli.ME_KEY].get(cls_id, 'Unknown')))
Chip Boling69abce82018-06-18 09:56:23 -0500180 inst_ids = [k for k in class_data.iterkeys()
181 if isinstance(k, int) and
182 (opts.instance_id is None or opts.instance_id == k)]
183 inst_ids.sort()
184
185 if len(inst_ids) == 0 and opts.instance_id is not None:
186 self.poutput(self.colorize('Instance ID {} of Class ID {} not ' +
187 'found in MIB Database'.
188 format(opts.instance_id, opts.class_id),
189 'red'))
190 return
191
192 for inst_id in inst_ids:
193 inst_data = class_data[inst_id]
194 self.poutput(' Instance ID: {0} - ({0:#x})'.format(inst_id))
195 self.poutput(' Created : {}'.format(inst_data[OmciCli.CREATED_KEY]))
196 self.poutput(' Modified : {}'.format(inst_data[OmciCli.MODIFIED_KEY]))
197
198 attributes = inst_data[OmciCli.ATTRIBUTES_KEY]
199 attr_names = attributes.keys()
Chip Boling07912c32018-09-28 14:13:31 -0500200 if len(attr_names):
201 attr_names.sort()
202 max_len = max([len(attr) for attr in attr_names])
Chip Boling69abce82018-06-18 09:56:23 -0500203
Chip Boling07912c32018-09-28 14:13:31 -0500204 for attr in attr_names:
205 name = self._cleanup_attribute_name(attr).ljust(max_len)
206 value = attributes[attr]
207 try:
208 ivalue = int(value)
209 self.poutput(' {0}: {1} - ({1:#x})'.format(name, ivalue))
Chip Boling69abce82018-06-18 09:56:23 -0500210
Chip Boling07912c32018-09-28 14:13:31 -0500211 except ValueError:
212 self.poutput(' {}: {}'.format(name, value))
Chip Boling69abce82018-06-18 09:56:23 -0500213
Chip Boling07912c32018-09-28 14:13:31 -0500214 if inst_id is not inst_ids[-1]:
215 self.poutput(linesep)
Chip Boling69abce82018-06-18 09:56:23 -0500216
217 def _cleanup_attribute_name(self, attr):
218 """Change underscore to space and capitalize first character"""
219 return ' '.join([v[0].upper() + v[1:] for v in attr.split('_')])
220
221 def _instance_to_dict(self, instance):
Chip Boling5584d202018-11-09 15:17:40 -0600222 if not isinstance(instance, (MibInstanceData, AlarmInstanceData)):
223 raise TypeError('{} is not of type MIB/Alarm Instance Data'.format(type(instance)))
Chip Boling69abce82018-06-18 09:56:23 -0500224
225 data = {
226 OmciCli.INSTANCE_ID_KEY: instance.instance_id,
227 OmciCli.CREATED_KEY: self._string_to_time(instance.created),
228 OmciCli.MODIFIED_KEY: self._string_to_time(instance.modified),
229 OmciCli.ATTRIBUTES_KEY: dict()
230 }
231 for attribute in instance.attributes:
232 data[OmciCli.ATTRIBUTES_KEY][attribute.name] = str(attribute.value)
233
234 return data
235
236 def _class_to_dict(self, val):
Chip Boling5584d202018-11-09 15:17:40 -0600237 if not isinstance(val, (MibClassData, AlarmClassData)):
238 raise TypeError('{} is not of type MIB/Alarm Class Data'.format(type(val)))
Chip Boling69abce82018-06-18 09:56:23 -0500239
240 data = {
241 OmciCli.CLASS_ID_KEY: val.class_id,
242 }
243 for instance in val.instances:
244 data[instance.instance_id] = self._instance_to_dict(instance)
245 return data
246
247 def _device_to_dict(self, val):
248 if not isinstance(val, MibDeviceData):
Chip Boling5584d202018-11-09 15:17:40 -0600249 raise TypeError('{} is not of type MIB Device Data'.format(type(val)))
Chip Boling69abce82018-06-18 09:56:23 -0500250
251 data = {
252 OmciCli.DEVICE_ID_KEY: val.device_id,
253 OmciCli.CREATED_KEY: self._string_to_time(val.created),
254 OmciCli.LAST_SYNC_KEY: self._string_to_time(val.last_sync_time),
255 OmciCli.MDS_KEY: val.mib_data_sync,
256 OmciCli.VERSION_KEY: val.version,
257 OmciCli.ME_KEY: dict(),
258 OmciCli.MSG_TYPE_KEY: set()
259 }
260 for class_data in val.classes:
261 data[class_data.class_id] = self._class_to_dict(class_data)
262
263 for managed_entity in val.managed_entities:
264 data[OmciCli.ME_KEY][managed_entity.class_id] = managed_entity.name
265
266 for msg_type in val.message_types:
267 data[OmciCli.MSG_TYPE_KEY].add(msg_type.message_type)
268
269 return data
270
271 def _string_to_time(self, time):
272 return datetime.strptime(time, OmciCli.TIME_FORMAT) if len(time) else None
273
274 def help_show_me(self):
275 self.poutput('show_me [-d <device-id>]' +
276 linesep + '-d: <device-id> ONU Device ID')
277
278 @options([
279 make_option('-d', '--device-id', action="store", dest='device_id', type='string',
280 help='ONU Device ID', default=None),
281 ])
282 def do_show_me(self, _line, opts):
283 """ Show supported OMCI Managed Entities"""
284
285 device_id = opts.device_id or self.device_id
286
287 try:
288 mib_db = self.get_device_mib(device_id, depth=1)
Chip Boling5584d202018-11-09 15:17:40 -0600289 if mib_db is None:
290 self.poutput(self.colorize('Supported ME information for ONU {} is not currently available'
291 .format(device_id), 'red'))
292 return
Chip Boling69abce82018-06-18 09:56:23 -0500293 mib = self._device_to_dict(mib_db)
294
295 except Exception: # UnboundLocalError if Device ID not found in DB
296 self.poutput(self.colorize('Failed to get supported ME information for ONU {}'
297 .format(device_id), 'red'))
298 return
299
300 class_ids = [class_id for class_id in mib[OmciCli.ME_KEY].keys()]
301 class_ids.sort()
302
303 self.poutput('Supported Managed Entities for ONU {}'.format(device_id))
304 for class_id in class_ids:
305 self.poutput(' {0} - ({0:#x}): {1}'.format(class_id,
306 mib[OmciCli.ME_KEY][class_id]))
307
308 def help_show_msg_types(self):
309 self.poutput('show_msg_types [-d <device-id>]' +
310 linesep + '-d: <device-id> ONU Device ID')
311
312 @options([
313 make_option('-d', '--device-id', action="store", dest='device_id', type='string',
314 help='ONU Device ID', default=None),
315 ])
316 def do_show_msg_types(self, _line, opts):
317 """ Show supported OMCI Message Types"""
318 device_id = opts.device_id or self.device_id
319
320 try:
321 mib_db = self.get_device_mib(device_id, depth=1)
Chip Boling5584d202018-11-09 15:17:40 -0600322 if mib_db is None:
323 self.poutput(self.colorize('Message Types for ONU {} are not currently available'
324 .format(device_id), 'red'))
325 return
326
Chip Boling69abce82018-06-18 09:56:23 -0500327 mib = self._device_to_dict(mib_db)
328
329 except Exception: # UnboundLocalError if Device ID not found in DB
330 self.poutput(self.colorize('Failed to get supported Message Types for ONU {}'
331 .format(device_id), 'red'))
332 return
333
334 msg_types = [msg_type for msg_type in mib[OmciCli.MSG_TYPE_KEY]]
335 msg_types.sort()
336
337 self.poutput('Supported Message Types for ONU {}'.format(device_id))
338 for msg_type in msg_types:
339 self.poutput(' {0} - ({0:#x}): {1}'.
340 format(msg_type,
341 OmciCli.MSG_TYPE_TO_NAME.get(msg_type, 'Unknown')))
342
343 def get_devices(self):
344 stub = self.get_stub()
345 res = stub.ListDevices(Empty())
346 return res.items
347
348 def do_devices(self, line):
349 """List devices registered in Voltha reduced for OMCI menu"""
350 devices = self.get_devices()
351 omit_fields = {
352 'adapter',
353 'model',
354 'hardware_version',
355 'images',
356 'firmware_version',
357 'serial_number',
358 'vlan',
359 'root',
360 'extra_args',
361 'proxy_address',
362 }
363 print_pb_list_as_table('Devices:', devices, omit_fields, self.poutput)
364
365 def help_devices(self):
Chip Boling5584d202018-11-09 15:17:40 -0600366 self.poutput('List devices registered in Voltha')
Chip Boling69abce82018-06-18 09:56:23 -0500367
368 def poutput(self, msg):
369 """Convenient shortcut for self.stdout.write(); adds newline if necessary."""
370 if msg:
371 self.stdout.write(msg)
372 if msg[-1] != '\n':
373 self.stdout.write('\n')
Chip Boling08463302018-11-06 13:54:47 -0600374
375 def do_show(self, _):
376 """Show detailed omci information"""
377 self.poutput('Use show_mib, show_alarms, show_me, show_msg_types for detailed OMCI information')
Chip Boling5584d202018-11-09 15:17:40 -0600378
379 def get_alarm_table(self, device_id, depth=-1):
380 stub = self.get_stub()
381
382 try:
383 res = stub.GetAlarmDeviceData(voltha_pb2.ID(id=device_id),
384 metadata=(('get-depth', str(depth)), ))
385 except Exception as _e:
386 res = None
387
388 return res
389
390 def _alarms_to_dict(self, val):
391 if not isinstance(val, AlarmDeviceData):
392 raise TypeError('{} is not of type Alarm Device Data'.format(type(val)))
393
394 data = {
395 OmciCli.DEVICE_ID_KEY: val.device_id,
396 OmciCli.CREATED_KEY: self._string_to_time(val.created),
397 OmciCli.VERSION_KEY: val.version
398 }
399 for class_data in val.classes:
400 data[class_data.class_id] = self._class_to_dict(class_data)
401
402 return data
403
404 def help_show_alarms(self):
405 self.poutput('show_alarms [-d <device-id>]' +
406 linesep + '-d: <device-id> ONU Device ID')
407
408 @options([
409 make_option('-d', '--device-id', action="store", dest='device_id', type='string',
410 help='ONU Device ID', default=None),
411 ])
412 def do_show_alarms(self, _line, opts):
413 """ Show contents of the alarm table"""
414 device_id = opts.device_id or self.device_id
415
416 try:
417 alarm_db = self.get_alarm_table(device_id, depth=-1)
418 if alarm_db is None:
419 self.poutput(self.colorize('Alarm Table for ONU {} is not currently available'
420 .format(device_id), 'red'))
421 return
422
423 except Exception: # UnboundLocalError if Device ID not found in DB
424 self.poutput(self.colorize('Failed to get Alarm Table for ONU {}'
425 .format(device_id), 'red'))
426 return
427
428 alarms = self._alarms_to_dict(alarm_db)
429 self.poutput('OpenOMCI Alarm Table for ONU {}'.format(device_id))
430 self.poutput('Version : {}'.format(alarms[OmciCli.VERSION_KEY]))
431 self.poutput('Created : {}'.format(alarms[OmciCli.CREATED_KEY]))
432
433 class_ids = [k for k in alarms.iterkeys() if isinstance(k, int)]
434 class_ids.sort()
435
436 if len(class_ids) == 0:
437 self.poutput('No active alarms')
438 return
439
440 for cls_id in class_ids:
441 from omci_alarm_info import _alarm_info
442 class_data = alarms[cls_id]
443 info = _alarm_info.get(cls_id)
444
445 self.poutput(' ----------------------------------------------')
446 self.poutput(' Class ID: {0} - ({0:#x}): {1}'.
447 format(cls_id,
448 info.get('name') if info is not None else 'Unknown Class ID'))
449
450 inst_ids = [k for k in class_data.iterkeys() if isinstance(k, int)]
451 inst_ids.sort()
452
453 for inst_id in inst_ids:
454 inst_data = class_data[inst_id]
455 self.poutput(' Instance ID : {0} - ({0:#x})'.format(inst_id))
456 self.poutput(' Created : {}'.format(inst_data[OmciCli.CREATED_KEY]))
457 self.poutput(' Modified : {}'.format(inst_data[OmciCli.MODIFIED_KEY]))
458
459 try:
460 alarm_value = int(inst_data[OmciCli.ATTRIBUTES_KEY]['alarm_bit_map'])
461 except ValueError:
462 alarm_value = 0
463
464 if alarm_value == 0:
465 self.poutput(' Active Alarms: No Active Alarms')
466
467 else:
468 padding = ' Active Alarms:'
469 for alarm_no in xrange(0, 224):
470 if (1 << (223 - alarm_no)) & alarm_value:
471 if info is None:
472 txt = 'Unknown alarm number'
473 else:
474 txt = info.get(alarm_no, 'Unknown alarm number')
475
476 self.poutput('{} {}: {}'.format(padding, alarm_no, txt))
477 padding = ' '
478
479 if inst_id is not inst_ids[-1]:
480 self.poutput(linesep)