blob: d8b8334107ab3695dcd9f89ddaf24000bdb961bc [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
26from python.protos import third_party
27from python.protos import voltha_pb2
28from python.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(' ----------------------------------------------')
171 self.poutput(' Class ID: {0} - ({0:#x})'.format(cls_id))
172
173 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()
193 attr_names.sort()
194 max_len = max([len(attr) for attr in attr_names])
195
196 for attr in attr_names:
197 name = self._cleanup_attribute_name(attr).ljust(max_len)
198 value = attributes[attr]
199 try:
200 ivalue = int(value)
201 self.poutput(' {0}: {1} - ({1:#x})'.format(name, ivalue))
202
203 except ValueError:
204 self.poutput(' {}: {}'.format(name, value))
205
206 if inst_id is not inst_ids[-1]:
207 self.poutput(linesep)
208
209 def _cleanup_attribute_name(self, attr):
210 """Change underscore to space and capitalize first character"""
211 return ' '.join([v[0].upper() + v[1:] for v in attr.split('_')])
212
213 def _instance_to_dict(self, instance):
214 if not isinstance(instance, MibInstanceData):
215 raise TypeError('{} is not of type MibInstanceData'.format(type(instance)))
216
217 data = {
218 OmciCli.INSTANCE_ID_KEY: instance.instance_id,
219 OmciCli.CREATED_KEY: self._string_to_time(instance.created),
220 OmciCli.MODIFIED_KEY: self._string_to_time(instance.modified),
221 OmciCli.ATTRIBUTES_KEY: dict()
222 }
223 for attribute in instance.attributes:
224 data[OmciCli.ATTRIBUTES_KEY][attribute.name] = str(attribute.value)
225
226 return data
227
228 def _class_to_dict(self, val):
229 if not isinstance(val, MibClassData):
230 raise TypeError('{} is not of type MibClassData'.format(type(val)))
231
232 data = {
233 OmciCli.CLASS_ID_KEY: val.class_id,
234 }
235 for instance in val.instances:
236 data[instance.instance_id] = self._instance_to_dict(instance)
237 return data
238
239 def _device_to_dict(self, val):
240 if not isinstance(val, MibDeviceData):
241 raise TypeError('{} is not of type MibDeviceData'.format(type(val)))
242
243 data = {
244 OmciCli.DEVICE_ID_KEY: val.device_id,
245 OmciCli.CREATED_KEY: self._string_to_time(val.created),
246 OmciCli.LAST_SYNC_KEY: self._string_to_time(val.last_sync_time),
247 OmciCli.MDS_KEY: val.mib_data_sync,
248 OmciCli.VERSION_KEY: val.version,
249 OmciCli.ME_KEY: dict(),
250 OmciCli.MSG_TYPE_KEY: set()
251 }
252 for class_data in val.classes:
253 data[class_data.class_id] = self._class_to_dict(class_data)
254
255 for managed_entity in val.managed_entities:
256 data[OmciCli.ME_KEY][managed_entity.class_id] = managed_entity.name
257
258 for msg_type in val.message_types:
259 data[OmciCli.MSG_TYPE_KEY].add(msg_type.message_type)
260
261 return data
262
263 def _string_to_time(self, time):
264 return datetime.strptime(time, OmciCli.TIME_FORMAT) if len(time) else None
265
266 def help_show_me(self):
267 self.poutput('show_me [-d <device-id>]' +
268 linesep + '-d: <device-id> ONU Device ID')
269
270 @options([
271 make_option('-d', '--device-id', action="store", dest='device_id', type='string',
272 help='ONU Device ID', default=None),
273 ])
274 def do_show_me(self, _line, opts):
275 """ Show supported OMCI Managed Entities"""
276
277 device_id = opts.device_id or self.device_id
278
279 try:
280 mib_db = self.get_device_mib(device_id, depth=1)
281 mib = self._device_to_dict(mib_db)
282
283 except Exception: # UnboundLocalError if Device ID not found in DB
284 self.poutput(self.colorize('Failed to get supported ME information for ONU {}'
285 .format(device_id), 'red'))
286 return
287
288 class_ids = [class_id for class_id in mib[OmciCli.ME_KEY].keys()]
289 class_ids.sort()
290
291 self.poutput('Supported Managed Entities for ONU {}'.format(device_id))
292 for class_id in class_ids:
293 self.poutput(' {0} - ({0:#x}): {1}'.format(class_id,
294 mib[OmciCli.ME_KEY][class_id]))
295
296 def help_show_msg_types(self):
297 self.poutput('show_msg_types [-d <device-id>]' +
298 linesep + '-d: <device-id> ONU Device ID')
299
300 @options([
301 make_option('-d', '--device-id', action="store", dest='device_id', type='string',
302 help='ONU Device ID', default=None),
303 ])
304 def do_show_msg_types(self, _line, opts):
305 """ Show supported OMCI Message Types"""
306 device_id = opts.device_id or self.device_id
307
308 try:
309 mib_db = self.get_device_mib(device_id, depth=1)
310 mib = self._device_to_dict(mib_db)
311
312 except Exception: # UnboundLocalError if Device ID not found in DB
313 self.poutput(self.colorize('Failed to get supported Message Types for ONU {}'
314 .format(device_id), 'red'))
315 return
316
317 msg_types = [msg_type for msg_type in mib[OmciCli.MSG_TYPE_KEY]]
318 msg_types.sort()
319
320 self.poutput('Supported Message Types for ONU {}'.format(device_id))
321 for msg_type in msg_types:
322 self.poutput(' {0} - ({0:#x}): {1}'.
323 format(msg_type,
324 OmciCli.MSG_TYPE_TO_NAME.get(msg_type, 'Unknown')))
325
326 def get_devices(self):
327 stub = self.get_stub()
328 res = stub.ListDevices(Empty())
329 return res.items
330
331 def do_devices(self, line):
332 """List devices registered in Voltha reduced for OMCI menu"""
333 devices = self.get_devices()
334 omit_fields = {
335 'adapter',
336 'model',
337 'hardware_version',
338 'images',
339 'firmware_version',
340 'serial_number',
341 'vlan',
342 'root',
343 'extra_args',
344 'proxy_address',
345 }
346 print_pb_list_as_table('Devices:', devices, omit_fields, self.poutput)
347
348 def help_devices(self):
349 self.poutput('TODO: Provide some help')
350
351 def poutput(self, msg):
352 """Convenient shortcut for self.stdout.write(); adds newline if necessary."""
353 if msg:
354 self.stdout.write(msg)
355 if msg[-1] != '\n':
356 self.stdout.write('\n')