cc1  v2.1
CC1 source code docs
 All Classes Namespaces Files Functions Variables Pages
monia.py
Go to the documentation of this file.
1 # -*- coding: utf-8 -*-
2 # @COPYRIGHT_begin
3 #
4 # Copyright [2010-2014] Institute of Nuclear Physics PAN, Krakow, Poland
5 #
6 # Licensed under the Apache License, Version 2.0 (the "License");
7 # you may not use this file except in compliance with the License.
8 # You may obtain a copy of the License at
9 #
10 # http://www.apache.org/licenses/LICENSE-2.0
11 #
12 # Unless required by applicable law or agreed to in writing, software
13 # distributed under the License is distributed on an "AS IS" BASIS,
14 # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15 # See the License for the specific language governing permissions and
16 # limitations under the License.
17 #
18 # @COPYRIGHT_end
19 
20 ##
21 # @package src.cm.utils.monia
22 #
23 # @author Tomek WojtoƄ
24 #
25 
26 import os.path
27 import tarfile
28 import time
29 
30 from cm import settings
31 from cm.utils import log
32 from cm.utils.exception import CMException
33 import rrdtool
34 
35 
37  # if not os.path.isfile(get_path(vm)):
38  # return 0
39  try:
40  rrdtool.info(get_path(vm))
41  except Exception, e:
42  log.error(0, 'stat_error %s %s' % (vm, e))
43  return 0
44  return 1
45 
46 
47 def get_path(vm):
48  path = settings.PATH_TO_RRD + vm + '.rrd'
49  return path
50 
51 
53  return settings.BACKUP_PATH + vm + '.tar.gz'
54 
55 
56 def stat_unit(stat_name):
57  d = {
58  'cpu_count': 'num',
59  'cpu_time': 'time',
60  'rd_req': 'ops',
61  'rd_bytes': 'bytes',
62  'wr_req': 'ops',
63  'wr_bytes': 'bytes',
64  'rx_bytes': 'bytes',
65  'rx_packets': 'ops',
66  'tx_bytes': 'bytes',
67  'tx_packets': 'ops',
68  }
69  return d[stat_name]
70 
71 
72 class RingBuffer(list):
73  def add(self, x):
74  if not x in self:
75  self.append(x)
76  log.debug(0, '%s added' % str(x))
77  else:
78  log.debug(0, '%s exists' % str(x))
79 
80  def clear(self):
81  self[:] = []
82 
83  def get(self):
84  try:
85  t = self.pop(0)
86  self.append(t)
87  return t
88  except Exception, e:
89  log.exception(0, e)
90 
91 
92 class RrdHandler():
93  def __init__(self, data=None):
94  if data:
95  self.vm = data
96  self.filepath = get_path(data['name'])
97  self.backuppath = get_backup_path(data['name'])
98 
99  ##
100  #
101  # Update rrd file, if exists. Otherwise create new rrd
102  #
103  def update(self):
104  if not self.vm:
105  raise Exception('No VM specified')
106  try:
107  filesize = os.path.getsize(self.filepath)
108  except Exception:
109  filesize = 0
110 
111  if(filesize == 0):
112  self.create()
113  else: # appropriate updating
114  cpu_percent = int(self.vm['cpu_time'] / self.vm['cpu_count'] / 10000 / 1000)
115  ret = rrdtool.update("%s" % (self.filepath), 'N:%d:%d:%d:%d:%d:%d:%d:%d:%d:%d' % (int(self.vm['cpu_count']),
116  int(self.vm['cpu_time']) / 100000000 / 10.0 / self.vm['cpu_count'],
117  int(self.vm['rd_req']),
118  int(self.vm['rd_bytes']),
119  int(self.vm['wr_req']),
120  int(self.vm['wr_bytes']),
121  int(self.vm['rx_bytes']),
122  int(self.vm['rx_packets']),
123  int(self.vm['tx_bytes']),
124  int(self.vm['tx_packets']),
125  ))
126  if ret:
127  log.error(0, 'update error: %s' % (rrdtool.error()))
128 
129  def create(self):
130  if not self.vm:
131  raise Exception('No VM specified')
132  # MonitorUtils.makefile(self.filepath)
133  rarg = ["%s" % (self.filepath), "--step", "%d" % settings.PERIOD,
134  "DS:cpu_count:GAUGE:%d:0:100000" % (settings.PERIOD * 2),
135  "DS:cpu_time:COUNTER:%d:0:100000" % (settings.PERIOD * 2),
136  "DS:rd_req:COUNTER:%d:0:1000000000" % (settings.PERIOD * 2),
137  "DS:rd_bytes:COUNTER:%d:0:1000000000" % (settings.PERIOD * 2),
138  "DS:wr_req:COUNTER:%d:0:1000000000" % (settings.PERIOD * 2),
139  "DS:wr_bytes:COUNTER:%d:0:1000000000" % (settings.PERIOD * 2),
140  "DS:rx_bytes:COUNTER:%d:0:1000000000" % (settings.PERIOD * 2),
141  "DS:rx_packets:COUNTER:%d:0:1000000000" % (settings.PERIOD * 2),
142  "DS:tx_bytes:COUNTER:%d:0:1000000000" % (settings.PERIOD * 2),
143  "DS:tx_packets:COUNTER:%d:0:1000000000" % (settings.PERIOD * 2),
144  ]
145  for s in settings.STATS:
146  rarg.append("RRA:AVERAGE:0.5:%d:%d" % (s[0], s[1]))
147 
148  try:
149  ret = rrdtool.create(rarg) # all data = 3,1MB
150  if ret:
151  log.error(0, 'update error: %s' % (rrdtool.error()))
152  except Exception, e:
153  log.exception(0, e)
154  log.info(0, 'created: %s' % (self.filepath))
155 
156  def remove(self):
157  if not self.vm:
158  return 0
159 
160  tar = tarfile.open(self.backuppath, "w:gz")
161  tar.add(self.filepath)
162  tar.close()
163 
164  os.remove(self.filepath)
165  log.info(0, 'removed: %s -> %s' % (self.filepath, self.backuppath))
166 
167  ##
168  #
169  # list vm stats with start and end times
170  #
171  def get_list(self):
172  f = os.listdir(settings.PATH_TO_RRD)
173 
174  rrds = {}
175  for rrd in f:
176  try:
177  t = []
178  t.append(rrdtool.first(settings.PATH_TO_RRD + rrd))
179  t.append(rrdtool.last(settings.PATH_TO_RRD + rrd))
180  rrds.update({os.path.splitext(rrd)[0]: t})
181  # rrds[{os.path.splitext(rrd)[0]: t}]
182  except Exception, e:
183  log.error(0, 'stat_error %s %s' % (rrd, e))
184  return rrds
185 
186  ##
187  #
188  # return information about steps, start & end time,
189  # and available stats
190  #
191  # time: 190us
192  # faster than regex
193  #
194  def get_vm_info(self, vm):
195  if not check_stat_exists(vm):
196  raise CMException('stat_not_exists')
197  filepath = get_path(vm)
198  try:
199  ds_info = rrdtool.info(filepath)
200  except Exception, e:
201  log.exception(0, e)
202  return 0
203 
204  stats = []
205  res = []
206  step = ds_info['step']
207 
208  for key in ds_info.keys():
209  if 'index' in key:
210  if key[0:2] == "ds":
211  ds_name = key[3:]
212  ds_name = ds_name[0: ds_name.find(']')]
213  stats.append(ds_name)
214  if 'pdp_per_row' in key:
215  res.append(ds_info[key] * step)
216  first = rrdtool.first(filepath)
217  last = rrdtool.last(filepath)
218 
219  return {'stats': stats, 'resolutions': res, 'first': first, 'last': last}
220 
221  def get_vm_stats(self, vm, names, start="-5min", end="now", resolution="10"):
222  if not check_stat_exists(vm):
223  raise CMException('stat_not_exists')
224 
225  res = []
226  filename = get_path(vm)
227  info, ds_rrd, data = rrdtool.fetch(filename, "AVERAGE", "--start", str(start), "--end", str(end), "--resolution", str(resolution))
228  start_rrd = info[0]
229  end_rrd = info[1]
230  step = info[2]
231  ts = start_rrd
232  total = self.get_vm_total(vm, names)
233  # ponizsze petle while mozna usunac zeby przsylac puste wartosci dla nieistniejacych danych
234  # while data and None in data[-1]:
235  # data.pop()
236  # while data and None in data[0]:
237  # data.pop(0)
238  # ts = ts + step
239 
240  now = int(time.time())
241 
242  ds_req = {}
243  ds_info = ['timestamp']
244  ds_n = []
245  ds_u = []
246  for i in range(len(ds_rrd)):
247  ds = ds_rrd[i]
248  if ds in names:
249  ds_req[i] = names.index(ds)
250  ds_n.append(ds)
251  ds_u.append(stat_unit(ds))
252  ds_info.append(ds_n) #
253  ds_info.append(ds_u) #
254  ds_info.append(end) #
255  ds_info.append(ts) #
256  ds_info.append(total.values()) #
257  res.append(ds_info)
258 
259  for row in data:
260  val = [None for i in names]
261  for i in range(len(row)):
262  if i in ds_req:
263  if row[i] == None:
264  val[ds_req[i]] = ''
265  else:
266  val[ds_req[i]] = row[i]
267  # val.insert(0, ts-time.timezone)
268  val.insert(0, ts)
269  res.append(val)
270  ts = ts + step
271  if ts > now - step:
272  break
273  if end_rrd > now + step:
274  res.append([ts + step, ''])
275  res.append([end_rrd, ''])
276  return res
277 
278  def get_vm_last(self, vm, res):
279  if not check_stat_exists(vm):
280  raise CMException('stat_not_exists')
281  vm_id = get_path(vm)
282  r = rrdtool.fetch("%s" % vm_id, 'AVERAGE', '-r', str(res), '-s', '-%ds' % (int(res) * 2), '-e', 'now')
283  i = {}
284  i['epoch'] = r[0][0]
285  i['labels'] = r[1]
286  i['data'] = map(lambda d: "" if d is None else d, r[2][0])
287  ret = dict(zip(i['labels'], i['data']))
288 
289  return ret
290 
291  def get_vm_total(self, vm, names=['cpu_time', 'rd_req', 'rd_bytes', 'wr_req', 'wr_bytes', 'rx_bytes', 'tx_bytes']):
292  if not check_stat_exists(vm):
293  raise CMException('stat_not_exists')
294  filename = get_path(vm)
295  ds_info = rrdtool.info(filename)
296 
297  ds_all = {}
298  for i in names:
299  ds_all[i] = ds_info['ds[%s].last_ds' % i]
300  return ds_all
301 
302  def cpu_load(self, vm, periods):
303  if not check_stat_exists(vm):
304  raise CMException('stat_not_exists')
305  r = {}
306  res = self.get_vm_info(vm)['resolutions']
307  min_res = min(res)
308  vm_id = get_path(vm)
309 
310  for p in periods:
311  try:
312  d = self.get_vm_stats(vm, ['cpu_time'], '-%ds' % (long(p) + min_res), 'now', min_res)
313  except Exception:
314  d = []
315 
316  if not d:
317  continue
318  cpus = [i[1] for i in d[1:] if i[1] != '']
319  if len(cpus):
320  r.update({str(p): "%.2f" % (sum(cpus) / float(len(cpus)))})
321  return r
322