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