blob: bb3fa6974bbd0edf52c5e07783b5be6843b453b5 [file] [log] [blame]
Scott Baker9828eb82018-09-13 14:13:34 -07001#!/usr/bin/env python
2# Copyright 2017-present Open Networking Foundation
3#
4# Licensed under the Apache License, Version 2.0 (the "License");
5# you may not use this file except in compliance with the License.
6# You may obtain a copy of the License at
7#
8# http://www.apache.org/licenses/LICENSE-2.0
9#
10# Unless required by applicable law or agreed to in writing, software
11# distributed under the License is distributed on an "AS IS" BASIS,
12# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13# See the License for the specific language governing permissions and
14# limitations under the License.
15
16"""
17Alarm-generator is a process that sends a stream of simulate_alarm requrests to VOLTHA. It will automatically pick
18between a variety of ONU-related alarms. The number of alarms per second is configurable (for example, 0.01 = one
19alarm per hundred seconds), and the duration of the alarm from RAISE to CLEAR is also configurable.
20
21By default, if not device is specified, then VOLTHA will be queried for the first device.
22
23Example:
24 # Generate 1 request per second, each request a duration of 2 seconds. Use whatever OLT device id exists in
25 # VOLTHA. Use intf_id =1234 and onu_device_id=5678
26 main.py -C consul:8500 -g voltha:50555 -G -r 1 -u 2 -i 1234 -o 5678
27
28 # Generate 1 request per second, each request a duration of 2 seconds. Use whatever OLT and ONUs exist in
29 # VOLTHA.
30 main.py -C consul:8500 -g voltha:50555 -G -r 1 -u 2
31
32"""
33import argparse
34import copy
35import functools
36import os
37import random
38import structlog
39import sys
40import time
41from threading import Timer
42
43import grpc
44from consul import Consul
45from simplejson import dumps
46
47from google.protobuf.empty_pb2 import Empty
48from voltha.protos import third_party
Scott Bakerd865fa22018-11-07 11:45:28 -080049from voltha.protos import voltha_pb2, voltha_pb2_grpc, common_pb2
Scott Baker9828eb82018-09-13 14:13:34 -070050
51defs = dict(
52 # config=os.environ.get('CONFIG', './cli.yml'),
53 consul=os.environ.get('CONSUL', 'localhost:8500'),
54 voltha_grpc_endpoint=os.environ.get('VOLTHA_GRPC_ENDPOINT',
55 'localhost:50055'),
56 global_request=os.environ.get('GLOBAL_REQUEST', False),
57 device_id = None,
58 intf_id = "1234",
59 onu_id = None,
60 rate = 0.1,
61 duration = 1,
62)
63
64def enum2name(msg_obj, enum_type, enum_value):
65 descriptor = msg_obj.DESCRIPTOR.enum_types_by_name[enum_type]
66 name = descriptor.values_by_number[enum_value].name
67 return name
68
69banner = """\
70 _ _ _
71__ _____| | |_| |_ __ _
72\ V / _ \ | _| ' \/ _` | Alarm Generator
73 \_/\___/_|\__|_||_\__,_|
74"""
75
76class VOLTHAClient(object):
77 def __init__(self, voltha_grpc, global_request=False):
78 self.voltha_grpc = voltha_grpc
79 self.global_request = global_request
80 self.channel = None
81 self.stub = None
82 self.log = structlog.get_logger()
83 self.stdout = sys.stdout
84
85 def get_channel(self):
86 if self.channel is None:
87 self.channel = grpc.insecure_channel(self.voltha_grpc)
88 return self.channel
89
90 def get_stub(self):
91 if self.stub is None:
92 self.stub = \
Scott Bakerd865fa22018-11-07 11:45:28 -080093 voltha_pb2_grpc.VolthaGlobalServiceStub(self.get_channel()) \
Scott Baker9828eb82018-09-13 14:13:34 -070094 if self.global_request else \
Scott Bakerd865fa22018-11-07 11:45:28 -080095 voltha_pb2_grpc.VolthaLocalServiceStub(self.get_channel())
Scott Baker9828eb82018-09-13 14:13:34 -070096 return self.stub
97
98 def get_first_olt_device_id(self):
99 """ Query VOLTHA and pick some olt device that we can use for alarms. """
100
101 stub = self.get_stub()
102 logical_devices = stub.ListLogicalDevices(Empty())
103 logical_device_ids = [x.id for x in logical_devices.items]
104
105 devices = stub.ListDevices(Empty())
106
107 for device in devices.items:
108 # If the parent_id is notempty and no a logical_device then it must not be an OLT.
109 if (device.parent_id) and (device.parent_id not in logical_device_ids):
110 print device.parent_id, logical_device_ids
111 continue
112
113 return device. id
114
115 raise Exception("No OLT devices in VOLTHA to choose from")
116
117 def get_onus(self, olt_id):
118 """ Query VOLTHA to get a list of ONUs attached to a particular OLT """
119
120 onus = []
121
122 stub = self.get_stub()
123 devices = stub.ListDevices(Empty())
124 for device in devices.items:
125 if device.parent_id != olt_id:
126 continue
127 if device.parent_port_no is None:
128 continue
129
130 onus.append({"id": olt_id,
131 "onu_device_id": device.id,
132 "intf_id": str(device.parent_port_no & 0x0F)})
133
134 return onus
135
136
137class AlarmGenerator(VOLTHAClient):
138 def __init__(self, voltha_grpc, global_request, onus=[], rate=0.1, duration=1):
139 super(AlarmGenerator, self).__init__(voltha_grpc, global_request)
140 self.onus = onus
141 self.rate = rate
142 self.duration = duration
143 self.raised_alarms = []
144
145 def pick_alarm(self):
146 eligible_indicators = ["dying_gasp", "onu_los", "onu_lopc_miss", "onu_lopc_mic", "onu_lob"]
147 while True:
148 onu = random.choice(self.onus)
149 indicator = random.choice(eligible_indicators)
150
151 alarm = copy.copy(onu)
152 alarm["indicator"] = indicator
153
154 # Make sure we don't already have this alarm raised on this ONU. If so, the loop around and try to pick
155 # some other alarm. If all possible alarms are currently raised, eventually we'll keep looping until some
156 # alarm is free.
157 if alarm in self.raised_alarms:
158 time.sleep(0.01) # Avoid tying up the CPU if all alarms are raised for an extended time.
159 continue
160
161 return alarm
162
163 def clear_alarm(self, kw):
164 if kw in self.raised_alarms:
165 self.raised_alarms.remove(kw)
166
167 kw["operation"] = voltha_pb2.SimulateAlarmRequest.CLEAR
168
169 response = None
170 try:
171 simulate_alarm = voltha_pb2.SimulateAlarmRequest(**kw)
172 stub = self.get_stub()
173 response = stub.SimulateAlarm(simulate_alarm)
174 except Exception as e:
175 self.log.error('Error simulate alarm {}. Error:{}'.format(kw['id'], e), kw=kw)
176 return
177 name = enum2name(common_pb2.OperationResp,
178 'OperationReturnCode', response.code)
179 self.log.info("Cleared Alarm", alarm=kw, response=name)
180
181 def generate_alarm(self):
182 kw = self.pick_alarm()
183
184 response = None
185 try:
186 simulate_alarm = voltha_pb2.SimulateAlarmRequest(**kw)
187 stub = self.get_stub()
188 response = stub.SimulateAlarm(simulate_alarm)
189 except Exception as e:
190 self.log.error('Error simulate alarm {}. Error:{}'.format(kw['id'], e), kw=kw)
191 return
192 name = enum2name(common_pb2.OperationResp,
193 'OperationReturnCode', response.code)
194 self.log.info("Generated Alarm", alarm=kw, response=name)
195
196 self.raised_alarms.append(kw)
197
198 Timer(self.duration, functools.partial(self.clear_alarm, kw)).start()
199
200 def run(self):
201 while True:
202 time.sleep(1/self.rate)
203 self.generate_alarm()
204
205
206def main():
207
208 parser = argparse.ArgumentParser()
209
210 _help = '<hostname>:<port> to consul agent (default: %s)' % defs['consul']
211 parser.add_argument(
212 '-C', '--consul', action='store', default=defs['consul'], help=_help)
213
214 _help = 'Lookup Voltha endpoints based on service entries in Consul'
215 parser.add_argument(
216 '-L', '--lookup', action='store_true', help=_help)
217
218 _help = 'All requests to the Voltha gRPC service are global'
219 parser.add_argument(
220 '-G', '--global_request', action='store_true', help=_help)
221
222 _help = '<hostname>:<port> of Voltha gRPC service (default={})'.format(
223 defs['voltha_grpc_endpoint'])
224 parser.add_argument('-g', '--grpc-endpoint', action='store',
225 default=defs['voltha_grpc_endpoint'], help=_help)
226
227 _help = 'device id, if None will query VOLTHA for the first device (default %s)' % defs['device_id']
228 parser.add_argument('-d', '--device_id', action='store',
229 default=defs['device_id'], help=_help)
230
231 _help = 'onu id, if None will query VOLTHA for a set of ONUs (default: %s)' % defs['onu_id']
232 parser.add_argument('-o', '--onu_id', action='store',
233 default=defs['onu_id'], help=_help)
234
235 _help = 'intf id (default: %s)' % defs['intf_id']
236 parser.add_argument('-i', '--intf_id', action='store',
237 default=defs['intf_id'], help=_help)
238
239 _help = 'rate in alarms per second (default: %s)' % defs['rate']
240 parser.add_argument('-r', '--rate', action='store', type=float,
241 default=defs['rate'], help=_help)
242
243 _help = 'duration between raise and clear in seconds (default: %s)' % defs['duration']
244 parser.add_argument('-u', '--duration', action='store', type=int,
245 default=defs['duration'], help=_help)
246
247 args = parser.parse_args()
248
249 if args.lookup:
250 host = args.consul.split(':')[0].strip()
251 port = int(args.consul.split(':')[1].strip())
252 consul = Consul(host=host, port=port)
253
254 _, services = consul.catalog.service('voltha-grpc')
255 if not services:
256 print('No voltha-grpc service registered in consul; exiting')
257 sys.exit(1)
258 args.grpc_endpoint = '{}:{}'.format(services[0]['ServiceAddress'],
259 services[0]['ServicePort'])
260
261 _, services = consul.catalog.service('voltha-sim-rest')
262 if not services:
263 print('No voltha-sim-rest service registered in consul; exiting')
264 sys.exit(1)
265 args.sim_rest_endpoint = '{}:{}'.format(services[0]['ServiceAddress'],
266 services[0]['ServicePort'])
267
268 device_id = args.device_id
269 if not device_id:
270 device_id = VOLTHAClient(args.grpc_endpoint, args.global_request).get_first_olt_device_id()
271
272 if args.onu_id:
273 onus = [{"id": device_id,
274 "onu_device_id": args.onu_id,
275 "intf_id": args.intf_id}]
276 else:
277 onus = VOLTHAClient(args.grpc_endpoint, args.global_request).get_onus(device_id)
278 if not onus:
279 raise Exception("Found no valid ONUs in VOLTHA on olt %s. Perhaps use -i and -o options?" % device_id)
280
281 print banner
282
283 g = AlarmGenerator(args.grpc_endpoint,
284 args.global_request,
285 onus = onus,
286 rate=args.rate,
287 duration=args.duration)
288 g.run()
289
290if __name__ == "__main__":
291 main()