blob: 70db5b2a60f295c27abf4111f36e698c8e4f559e [file] [log] [blame]
Matteo Scandoloeb0d11c2017-08-08 13:05:26 -07001
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
rdudyalab086cf32016-08-11 00:07:45 -040017#
18# Copyright 2013 NEC Corporation. All rights reserved.
19#
20# Licensed under the Apache License, Version 2.0 (the "License"); you may
21# not use this file except in compliance with the License. You may obtain
22# a copy of the License at
23#
24# http://www.apache.org/licenses/LICENSE-2.0
25#
26# Unless required by applicable law or agreed to in writing, software
27# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
28# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
29# License for the specific language governing permissions and limitations
30# under the License.
31from oslo_utils import timeutils
32import six
33from six import moves
34from six.moves.urllib import parse as urlparse
35
36from ceilometer.i18n import _
37from ceilometer.network.statistics import driver
38from ceilometer.network.statistics.onos import client
39from ceilometer.openstack.common import log
40from ceilometer import utils
41
42
43LOG = log.getLogger(__name__)
44
45
46def _get_properties(properties, prefix='properties'):
47 resource_meta = {}
48 if properties is not None:
49 for k, v in six.iteritems(properties):
50 value = v['value']
51 key = prefix + '_' + k
52 if 'name' in v:
53 key += '_' + v['name']
54 resource_meta[key] = value
55 return resource_meta
56
57
58def _get_int_sample(key, statistic, resource_id, resource_meta):
59 if key not in statistic:
60 return None
61 return int(statistic[key]), resource_id, resource_meta
62
63
64class ONOSDriver(driver.Driver):
65 """Driver of network info collector from ONOS.
66
67 This driver uses resources in "pipeline.yaml".
68 Resource requires below conditions:
69
70 * resource is url
71 * scheme is "onos"
72
73 This driver can be configured via query parameters.
74 Supported parameters:
75
76 * scheme:
77 The scheme of request url to ONOS REST API endpoint.
78 (default http)
79 * auth:
80 Auth strategy of http.
81 This parameter can be set basic and digest.(default None)
82 * user:
83 This is username that is used by auth.(default None)
84 * password:
85 This is password that is used by auth.(default None)
86 * container_name:
87 Name of container of ONOS.(default "default")
88 This parameter allows multi vaues.
89
90 e.g.::
91
92 onos://127.0.0.1:8181/onos/v1?auth=basic&user=admin&password=admin&scheme=http
93
94 In this case, the driver send request to below URLs:
95
96 http://127.0.0.1:8181/onos/v1/flows
97 """
98 @staticmethod
99 def _prepare_cache(endpoint, params, cache):
100
101 if 'network.statistics.onos' in cache:
102 return cache['network.statistics.onos']
103
104 data = {}
105
106 container_names = params.get('container_name', ['default'])
107
108 onos_params = {}
109 if 'auth' in params:
110 onos_params['auth'] = params['auth'][0]
111 if 'user' in params:
112 onos_params['user'] = params['user'][0]
113 if 'password' in params:
114 onos_params['password'] = params['password'][0]
115 cs = client.Client(endpoint, onos_params)
116
117 for container_name in container_names:
118 try:
119 container_data = {}
120
121 # get flow statistics
122 container_data['flow'] = cs.rest_client.get_flow_statistics(
123 container_name)
124
125 # get port statistics
126 container_data['port'] = cs.rest_client.get_port_statistics(
127 container_name)
128
129 # get table statistics
130 container_data['table'] = cs.rest_client.get_table_statistics(
131 container_name)
132
133 # get topology
134 #container_data['topology'] = cs.topology.get_topology(
135 # container_name)
136
137 # get switch informations
138 container_data['switch'] = cs.rest_client.get_devices(
139 container_name)
140
141 container_data['timestamp'] = timeutils.isotime()
142
143 data[container_name] = container_data
144 except Exception:
145 LOG.exception(_('Request failed to connect to ONOS'
146 ' with NorthBound REST API'))
147
148 cache['network.statistics.onos'] = data
149
150 return data
151
152 def get_sample_data(self, meter_name, parse_url, params, cache):
153
154 extractor = self._get_extractor(meter_name)
155 if extractor is None:
156 # The way to getting meter is not implemented in this driver or
157 # ONOS REST API has not api to getting meter.
158 return None
159
160 iter = self._get_iter(meter_name)
161 if iter is None:
162 # The way to getting meter is not implemented in this driver or
163 # ONOS REST API has not api to getting meter.
164 return None
165
166 parts = urlparse.ParseResult(params.get('scheme', ['http'])[0],
167 parse_url.netloc,
168 parse_url.path,
169 None,
170 None,
171 None)
172 endpoint = urlparse.urlunparse(parts)
173
174 data = self._prepare_cache(endpoint, params, cache)
175
176 samples = []
177 for name, value in six.iteritems(data):
178 timestamp = value['timestamp']
179 for sample in iter(extractor, value):
180 if sample is not None:
181 # set controller name and container name
182 # to resource_metadata
183 sample[2]['controller'] = 'ONOS'
184 sample[2]['container'] = name
185
186 samples.append(sample + (timestamp, ))
187
188 return samples
189
190 def _get_iter(self, meter_name):
191 if meter_name == 'switch':
192 return self._iter_switch
193 elif meter_name.startswith('switch.flow'):
194 return self._iter_flow
195 elif meter_name.startswith('switch.table'):
196 return self._iter_table
197 elif meter_name.startswith('switch.port'):
198 return self._iter_port
199
200 def _get_extractor(self, meter_name):
201 method_name = '_' + meter_name.replace('.', '_')
202 return getattr(self, method_name, None)
203
204 @staticmethod
205 def _iter_switch(extractor, data):
206 for switch in data['switch']['devices']:
207 yield extractor(switch, switch['id'], {})
208
209 @staticmethod
210 def _switch(statistic, resource_id, resource_meta):
211
212 for key in ['mfr','hw','sw','available']:
213 resource_meta[key] = statistic[key]
214 for key in ['protocol','channelId']:
215 resource_meta[key] = statistic['annotations'][key]
216
217 return 1, resource_id, resource_meta
218
219 @staticmethod
220 def _iter_port(extractor, data):
221 for statistic in data['port']['statistics']:
222 for port_statistic in statistic['ports']:
223 resource_meta = {'port': port_statistic['port']}
224 yield extractor(port_statistic, statistic['device'],
225 resource_meta, data)
226
227 @staticmethod
228 def _switch_port(statistic, resource_id, resource_meta, data):
229 return 1, resource_id, resource_meta
230
231 @staticmethod
232 def _switch_port_receive_packets(statistic, resource_id,
233 resource_meta, data):
234 return _get_int_sample('packetsReceived', statistic, resource_id,
235 resource_meta)
236
237 @staticmethod
238 def _switch_port_transmit_packets(statistic, resource_id,
239 resource_meta, data):
240 return _get_int_sample('packetsSent', statistic, resource_id,
241 resource_meta)
242
243 @staticmethod
244 def _switch_port_receive_bytes(statistic, resource_id,
245 resource_meta, data):
246 return _get_int_sample('bytesReceived', statistic, resource_id,
247 resource_meta)
248
249 @staticmethod
250 def _switch_port_transmit_bytes(statistic, resource_id,
251 resource_meta, data):
252 return _get_int_sample('bytesSent', statistic, resource_id,
253 resource_meta)
254
255 @staticmethod
256 def _switch_port_receive_drops(statistic, resource_id,
257 resource_meta, data):
258 return _get_int_sample('packetsRxDropped', statistic, resource_id,
259 resource_meta)
260
261 @staticmethod
262 def _switch_port_transmit_drops(statistic, resource_id,
263 resource_meta, data):
264 return _get_int_sample('packetsTxDropped', statistic, resource_id,
265 resource_meta)
266
267 @staticmethod
268 def _switch_port_receive_errors(statistic, resource_id,
269 resource_meta, data):
270 return _get_int_sample('packetsRxErrors', statistic, resource_id,
271 resource_meta)
272
273 @staticmethod
274 def _switch_port_transmit_errors(statistic, resource_id,
275 resource_meta, data):
276 return _get_int_sample('packetsTxErrors', statistic, resource_id,
277 resource_meta)
278
279 @staticmethod
280 def _switch_port_receive_frame_error(statistic, resource_id,
281 resource_meta, data):
282 #return _get_int_sample('receiveFrameError', statistic, resource_id,
283 # resource_meta)
284 return 0, resource_id, resource_meta
285
286 @staticmethod
287 def _switch_port_receive_overrun_error(statistic, resource_id,
288 resource_meta, data):
289 #return _get_int_sample('receiveOverRunError', statistic, resource_id,
290 # resource_meta)
291 return 0, resource_id, resource_meta
292
293 @staticmethod
294 def _switch_port_receive_crc_error(statistic, resource_id,
295 resource_meta, data):
296 #return _get_int_sample('receiveCrcError', statistic, resource_id,
297 # resource_meta)
298 return 0, resource_id, resource_meta
299
300 @staticmethod
301 def _switch_port_collision_count(statistic, resource_id,
302 resource_meta, data):
303 #return _get_int_sample('collisionCount', statistic, resource_id,
304 # resource_meta)
305 return 0, resource_id, resource_meta
306
307 @staticmethod
308 def _iter_table(extractor, data):
309 for statistic in data['table']['statistics']:
310 for table_statistic in statistic['table']:
311 resource_meta = {'table_id': table_statistic['tableId']}
312 yield extractor(table_statistic,
313 statistic['device'],
314 resource_meta)
315
316 @staticmethod
317 def _switch_table(statistic, resource_id, resource_meta):
318 return 1, resource_id, resource_meta
319
320 @staticmethod
321 def _switch_table_active_entries(statistic, resource_id,
322 resource_meta):
323 return _get_int_sample('activeEntries', statistic, resource_id,
324 resource_meta)
325
326 @staticmethod
327 def _switch_table_lookup_packets(statistic, resource_id,
328 resource_meta):
329 return _get_int_sample('packetsLookedUp', statistic, resource_id,
330 resource_meta)
331
332 @staticmethod
333 def _switch_table_matched_packets(statistic, resource_id,
334 resource_meta):
335 return _get_int_sample('packetsMathced', statistic, resource_id,
336 resource_meta)
337
338 @staticmethod
339 def _iter_flow(extractor, data):
340 for flow_statistic in data['flow']['flows']:
341 resource_meta = {'flow_id': flow_statistic['id'],
342 'table_id': flow_statistic['tableId'],
343 'priority': flow_statistic['priority'],
344 'state': flow_statistic['state']}
345 yield extractor(flow_statistic,
346 flow_statistic['deviceId'],
347 resource_meta)
348
349 @staticmethod
350 def _switch_flow(statistic, resource_id, resource_meta):
351 return 1, resource_id, resource_meta
352
353 @staticmethod
354 def _switch_flow_duration_seconds(statistic, resource_id,
355 resource_meta):
356 if 'life' not in statistic:
357 return None
358 return int(statistic['life']/1000), resource_id, resource_meta
359
360 @staticmethod
361 def _switch_flow_duration_nanoseconds(statistic, resource_id,
362 resource_meta):
363 if 'life' not in statistic:
364 return None
365 return int(statistic['life']*1000), resource_id, resource_meta
366
367 @staticmethod
368 def _switch_flow_packets(statistic, resource_id, resource_meta):
369 return _get_int_sample('packets', statistic, resource_id,
370 resource_meta)
371
372 @staticmethod
373 def _switch_flow_bytes(statistic, resource_id, resource_meta):
374 return _get_int_sample('bytes', statistic, resource_id,
375 resource_meta)