Показаны сообщения с ярлыком rrdtool. Показать все сообщения
Показаны сообщения с ярлыком rrdtool. Показать все сообщения

10 мая 2012

Обновление rrd задней датой при помощи faketime.

Иногда скрипты, выполнение которых зависит от времени, не срабатывают. Менять время и запускать их повторно или менять код приложения неправильно. Что бы выполнить их задним числом есть утилита faketime.
Установка:
sudo apt-get install faketime

Пример использования:
faketime 'last Friday 5 pm' /bin/date
faketime '2008-12-24 08:15:42' /bin/date

Также работает с wine:
faketime '2006-09-20' wine myprogramm.exe

У меня есть скрипт на питоне который добавляет записи в rrd каждый день. Пересчитаем rrd за последние 3 дня.
faketime '2012-05-05 5 am' python updatemyrrd.py
faketime '2012-05-04 5 am' python updatemyrrd.py
faketime '2012-05-03 5 am' python updatemyrrd.py

11 апреля 2012

RRDTool пример использования в python

RRDtool (Round Robin Database). Обеспечивает хранение и отображение данных мониторинга (загрузка каналов, температура и любая другая зависящая от времени последовательность данных, которую можно получить некоторым способом). Задумывалась как повторная, но более правильная реализация MRTG. Объём хранимых данных не увеличивается со временем (ячейки хранения используются циклически). Использование различных функций консолидации данных позволяет охватывать большие интервалы времени без чрезмерного увеличения объема БД за счет снижения разрешающей способности. В отличие от mrtg, программа не упаковывает старые данные самостоятельно, это надо предусматривать при проектировании БД. Сбор информации и генерация HTML-кода также производится с помощью внешних средств. Более мощные средства генерации графиков. Все времена во внутреннем формате Unix (число секунд с 1 января 1970 в UTC). Значения счетчиков могут быть целыми или вещественными числами или специальным значением UNKNOWN. Лицензия - GPL (но автор не будет против, если в благодарность послать ему CD/DVD из указанного им списка; при установке rrdtool имитируется заказ CD на amazone.com ;-). Необходимо иметь perl 5 (без perl rrdtool соберется, но не будет модулей подержки perl) и обычный набор средств компиляции (make, gcc, autoconf, automake, libtool). Имеется также поддержка tcl и python. Нельзя использовать для учета и биллинга - слишком много он усредняет и сглаживает, используя достаточно сложные алгоритмы.
Очень хорошее описание http://www.bog.pp.ru/work/rrdtool.html#at

Создаем файл

rrd_file = "/usr/local/www/nagios/2week.rrd"
def rrd_create(rrd_file):
    if not os.path.isfile(rrd_file):
        rrdtool.create(rrd_file,
                        "--step", "86400",
                        "DS:available:GAUGE:172800:0.0:100.0",
                        "RRA:AVERAGE:0.5:1:366",
                       )

step - интервал через который будут обновляться значения. У нас 86400 сек - это 1 день т.е. ежедневно.
DS:available:GAUGE:172800:0:100 - источник данных
available - название источника
GAUGE - тип (в данном случае поддерживает числа с плавающей точкой)
172800 - значение которое должно быть 2*step :)
0:100 - интервал допустимых значений

RRA:AVERAGE:0.5:1:366 - архив значений
AVERAGE - тип (в данном случае среднее значение)
1:366 - означает что в архиве 366 записей с интервалом 1*step

Вносим данные

def rrd_update(rrd_file, value):
    rrdtool.update(rrd_file, "N:%s" % str(value))
N - означает на текущее время (Now)
через двоеточие указываются значения DS ( В случае нескольких DS могло быть так N:%s:%s:%s)

Создаем график в png

import tempfile
fd,path = tempfile.mkstemp('.png')
rrdtool.graph("/tmp/net.png",
            "--imgformat", "PNG",
            "--height", "250",
            "--width", "550",
            "--end", "now",
            "--start", "end-2w-1h",
            "--lower-limit", "99.3",
            "--upper-limit", "100.0",
            "--zoom", "1.5",
            "--y-grid", "0.05:1",
            "--x-grid=DAY:1:DAY:1:DAY:2:0:%a",
            "--right-axis", "1:0",
            "--right-axis-format", "%0.2lf%%",
            "--slope-mode",
            "--vertical-label=Up times/day",
            "DEF:avl=%s:available:AVERAGE" % rrd_file,
            "AREA:avl#00FF00:Up times(%)",
            'GPRINT:avl:AVERAGE:avg\: %2.2lf%%')
Описание всех параметров есть в ссылке приведенной выше.


Backup и Restore

Фаил rrd платформо зависим и может не заработать на другом железе или ОС(например из 32бит в 64бит), поэтому принято хранить бэкапы в xml.
rrdtool dump файл.rrd > бэкап.xml

XML можно править вручную и залить обратно в rrd
rrdtool restore бэкап.xml > файл.rrd

Результат:
rrdtool пример графика


Для обучения лучше выбрать меньший интервал чем 1 день, например 2 мин(step = 120).

21 декабря 2011

upd: Режем пики в rrd

Обновления в скрипте Срезаем пики в RRD при помощи python. В предыдущем варианте скрипт получал предельное значение и если любое значение в rrd файле его превышало, то оно обнулялось. Это неудобно когда для каждого datasource нужно задавать свой предел. Ниже пример где несколько строк, в каждой строке 6 значений.

                         1.2531404050e+041.1760614140e+031.0547667362e+010.0000000000e+000.0000000000e+000.0000000000e+00
                         6.2535788107e+036.5077305135e+025.9844172295e+000.0000000000e+000.0000000000e+000.0000000000e+00
                         7.4349123852e+037.2086196285e+026.7862914917e+000.0000000000e+000.0000000000e+000.0000000000e+00
                         6.6044470194e+036.1316221529e+026.2081965076e+000.0000000000e+000.0000000000e+000.0000000000e+00
                         1.6377377735e+042.8213539887e+031.4662221016e+010.0000000000e+000.0000000000e+000.0000000000e+00
                         1.4991881969e+041.9741971231e+031.3208469357e+010.0000000000e+000.0000000000e+000.0000000000e+00
...

Намного юзабильней когда можно задать для каждого из этих 6 значений своё ограничение. Код и примеры брать здесь https://github.com/uralbash/rrd_killerpeak

09 декабря 2011

Срезаем пики в RRD при помощи python

В некоторых случаях на rrd графиках появляются пики, как например после перезагрузки сетевой карты
пики в rrd
Для их удаления нам нужно воспользоваться утилитой dump из rrdtool, которая переводит файл rrd в формат xml. Затем обнулить значение пиков и записать изменения обратно в rrd файл при помощи утилиты restore. На оф. сайте rrdtool можно найти скрипт на perl removespikes и на php для cacti spikekill. removespike работает не всегда корректно, поэтому рассмотрим как можно обрезать графики при помощи скрипта на python
#!/usr/bin/env python
import os
import shutil
from xml.etree import ElementTree as ET
from optparse import OptionParser

__version__ = '0.1'

def get_configuration():
    parser = OptionParser(version="%prog v" + __version__,
                          usage="%prog ")
    parser.add_option("-v", "--verbose", action="count", default=0,
                      help="produce more output")
    parser.add_option('-b', '--border', action ='store', default=10**10,
                 help='Min value of peak. Default 100000')
    options, args = parser.parse_args()

    if not os.path.isfile(args[0]):
        parser.error("Not found file")

    return [ options ] + args

def main():
    options, filename = get_configuration()
    try:
        rrd_dump = os.system("rrdtool dump '%s' > 'old.xml'" % (filename))
    except:
        print "Error RRD file type"
        raise

    if not os.path.exists('rrd_bak'):
        os.makedirs('rrd_bak')
    shutil.copy2(filename, 'rrd_bak')

    tree = ET.parse('old.xml')

    def iterparent(tree):
        for parent in tree.getiterator():
            for child in parent:
                yield parent, child

    for parent, child in iterparent(tree):
        if child.tag == 'v':
            if float(child.text) > options.border:
                print 'peak detect in %s' % filename
                child.text = 'NaN'

    tree.write('new.xml')
    os.unlink(filename)
    os.system("rrdtool restore 'new.xml' '%s'" % filename)
    os.unlink('new.xml')
    os.unlink('old.xml')

if __name__ == '__main__':
    import sys
    sys.exit(main())

Для того что бы парсить параметры командной строки воспользуемся модулем optparse
def get_configuration():
    parser = OptionParser(version="%prog v" + __version__,
                          usage="%prog ")
    parser.add_option("-v", "--verbose", action="count", default=0,
                      help="produce more output")
    parser.add_option('-b', '--border', action ='store', default=10**10,
                 help='Min value of peak. Default 100000')
    options, args = parser.parse_args()

    if not os.path.isfile(args[0]):
        parser.error("Not found file")

    return [ options ] + args
Дампим rrd файл в xml
try:
        rrd_dump = os.system("rrdtool dump '%s' > 'old.xml'" % (filename))
    except:
        print "Error RRD file type"
        raise
На всякий случай копируем старый rrd в папку rrd_bak
if not os.path.exists('rrd_bak'):
        os.makedirs('rrd_bak')
    shutil.copy2(filename, 'rrd_bak')
Функция для получения списока всех элементов из xml
def iterparent(tree):
        for parent in tree.getiterator():
            for child in parent:
                yield parent, child
Записи в xml выглядят так

                         1.2531404050e+041.1760614140e+031.0547667362e+010.0000000000e+000.0000000000e+000.0000000000e+00
                         6.2535788107e+036.5077305135e+025.9844172295e+000.0000000000e+000.0000000000e+000.0000000000e+00
                         7.4349123852e+037.2086196285e+026.7862914917e+000.0000000000e+000.0000000000e+000.0000000000e+00
                         6.6044470194e+036.1316221529e+026.2081965076e+000.0000000000e+000.0000000000e+000.0000000000e+00
                         1.6377377735e+042.8213539887e+031.4662221016e+010.0000000000e+000.0000000000e+000.0000000000e+00
                         1.4991881969e+041.9741971231e+031.3208469357e+010.0000000000e+000.0000000000e+000.0000000000e+00
...

Пробегаем по всем тегам и ищем значения которые больше порогового 10 в степени 10 и записываем нулевое значение(NaN)

for parent, child in iterparent(tree):
        if child.tag == 'v':
            if float(child.text) > options.border:
                print 'peak detect in %s' % filename
                child.text = 'NaN'
Сохраняем xml и импортируем изменения в rrd
tree.write('new.xml')
    os.system("rrdtool restore 'new.xml' '%s'" % filename)
Полученный результат
rrd график без пиков
Скрипт на bash для удаления пиков во всей директории
#!/bin/bash
if [ -a $1 ]
then
    for file in $1*
    do
        echo $file
        python killerpeak.py $file
    done
fi
echo "Don't forget delete directory rrd_bak if all is ok"
exit 0
Запуск скрипта killerpeak.py
sudo ./killerpeak.py data/rrdfile/7890.rrd
peak detect in data/rrdfile/7890.rrd
peak detect in data/rrdfile/7890.rrd
peak detect in data/rrdfile/7890.rrd
peak detect in data/rrdfile/7890.rrd
Запуск скрипта на bash killpeak.sh
./killpeak.sh data/rrdfile

Взять код или поправить можно на github https://github.com/uralbash/rrd_killerpeak

13 ноября 2011

Красивые графики javascriptRRD + Float

В продолжение статьи Python + Tornado + RRDTool: температура на улице и в серверной рассмотрим как можно рисовать rrd используя только javascript на стороне клиента. Результат будет такой:
Используем библиотеку javascriptrrd. Качаем и добавляем следующие файлы из нее в наш шаблон в <head>
<script type="text/javascript" src="media/js/lib/binaryXHR.js"></script>
    <script type="text/javascript" src="media/js/lib/rrdFile.js"></script>

    <!-- rrdFlot class needs the following four include files !-->
    <script type="text/javascript" src="media/js/lib/rrdFlotSupport.js"></script>

    <script type="text/javascript" src="media/js/lib/rrdFlot.js"></script>
    <script type="text/javascript" src="media/flot/jquery.js"></script>
    <script type="text/javascript" src="media/flot/jquery.flot.js"></script>
    <script type="text/javascript" src="media/flot/jquery.flot.selection.js"></script>
    <script type="text/javascript" src="media/flot/jquery.flot.tooltip.js"></script>

И саму js функцию для отрисовки
<br><h2>javascriptRRD пример</h2>
    <table id="infotable" border=1>
        <tr><td colspan="21"><b>Javascript needed for this page to work</b></td></tr>
 <tr><td><b>RRD file</b></td><td id="fname" colspan="5">None</td></tr>
    </table>

    <div id="mygraph"></div>

    <script type="text/javascript">

      // Remove the Javascript warning
      document.getElementById("infotable").deleteRow(0);

      // fname and rrd_data are the global variable used by all the functions below
      fname="media/temperature.rrd";
      rrd_data=undefined;

      // This function updates the Web Page with the data from the RRD archive header
      // when a new file is selected
      function update_fname() {
        // Finally, update the file name and enable the update button
        document.getElementById("fname").firstChild.data=fname;

        var graph_opts={legend: { noColumns:4}};
        var ds_graph_opts={'Oscilator':{ color: "#ff8000", 
                                         lines: { show: true, fill: true, fillColor:"#ffff80"} },
                           'Idle':{ label: 'IdleJobs', color: "#00c0c0", 
                                    lines: { show: true, fill: true} },
                           'Running':{color: "#000000",yaxis:2}};

        

        // the rrdFlot object creates and handles the graph
        var f=new rrdFlot("mygraph",rrd_data,graph_opts,ds_graph_opts);
      }
 
      // This is the callback function that,
      // given a binary file object,
      // verifies that it is a valid RRD archive
      // and performs the update of the Web page
      function update_fname_handler(bf) {
          var i_rrd_data=undefined;
          try {
            var i_rrd_data=new RRDFile(bf);            
          } catch(err) {
            alert("File "+fname+" is not a valid RRD archive!");
          }
          if (i_rrd_data!=undefined) {
            rrd_data=i_rrd_data;
            update_fname()
          }
      }

      // this function is invoked when the RRD file name changes
      function fname_update() {
        fname="media/temperature.rrd";
        try {
          FetchBinaryURLAsync(fname,update_fname_handler);
        } catch (err) {
           alert("Failed loading "+fname+"\n"+err);
        }
      }

    </script>

Для автоматического запуска функции при загрузки страницы добавим событие onLoad в body
<body bgcolor="white" text="black" onLoad="fname_update()">
Всё, радуемся результату.

Python + Tornado + RRDTool: температура на улице и в серверной

Рассмотрим как можно мониторить температуру при помощи сервера. Алгоритм довольно простой: подключаем датчик температуры к компу, пишем скрипт который снимает данные с датчика, записываем данные и отдаем по запросу пользователю. Результат будет выглядеть так:

Датчик температуры можно выбрать любой. Я сделал для COM порта по схеме "Электронный термометр на DS-18B20". Стоимось вышла около 450р., без кожуха и разъемов раза в 2 дешевле.

Сам датчик температуры(DS-18B20) можно соединять параллельно с другими(до 100шт.) и он будет нормально работать на одной шине длиной до 300м. После подключения скорей всего он определится на последовательном порту /dev/ttyS0. Для снятия показателей нужна программа digitemp.
sudo apt-get install digitemp
Чтобы получить данные введем
/usr/bin/digitemp_DS9097 -t 0 -s /dev/ttyS0 -q -o "%.2C"
-7.62
При обращении к датчику он блокируется и если в этот момент запустить еще раз команду digitemp то она вернет пустое значение. Для избежания этой ситуации напишем скрипт который по крону каждую минуту будет снимать данные и записывать в файл
#!/bin/bash
echo `/usr/bin/digitemp_DS9097 -t 0 -s /dev/ttyS0 -q -o "%.2C"` > /projects/scripts/temp.cashe
Сейчас напишем скрипт который будет формировать rrd базу и добавлять в нее данные из temp.cashe каждую минуту.
from rrdcontroller import RRDController
import time

def update_rrd(rrd):
    f = open('temp.cashe')
    temp = f.read()
    rrd.update(temperature=temp)

    rrd.graph(period='day')
    rrd.graph(period='week')
    rrd.graph(period='month')
    rrd.graph(period='year')

if __name__ == '__main__':
    rrdfile = 'media/temperature.rrd'
    imgdir = 'media'
    rrd = RRDController(rrdfile=rrdfile, static_path = imgdir)
    rrd.create()

    while(1):
        update_rrd(rrd)
        time.sleep(60)

И файл rrdcontroller.py
from pyrrd.rrd import RRD, RRA, DS
from pyrrd.graph import DEF, CDEF, VDEF
from pyrrd.graph import LINE, AREA, GPRINT
from pyrrd.graph import ColorAttributes, Graph
import os, logging, time

class RRDController(object):
    def __init__(self, rrdfile, static_path):
        self.rrdfile = rrdfile
        self.static_path = static_path

    def delete(self):
        os.unlink(self.rrdfile)

    def create(self):
        if os.path.exists(self.rrdfile):
            self.rrd = RRD(self.rrdfile)
            return

        dss = []
        ds1 = DS(dsName="temperature", dsType="GAUGE", heartbeat=120,
                 minval=-10000000.0, maxval=100000000.0)
        dss.append(ds1)

        day = 24 * 60 * 60
        week = 7 * day
        month = day * 30
        quarter = month * 3
        half = 365 * day / 2
        year = 365 * day
        step = 60

        rras = []
        rra1 = RRA(cf="AVERAGE", xff=0.5, steps=1, rows=day/step)
        rra2 = RRA(cf="AVERAGE", xff=0.5, steps=30, rows=week/(30*step))
        rra3 = RRA(cf="AVERAGE", xff=0.5, steps=120, rows=month/(120*step))
        rra4 = RRA(cf="AVERAGE", xff=0.5, steps=720, rows=year/(720*step))
        rras.extend([rra1, rra2, rra3, rra4])
        rras.append(rra1)

        self.rrd = RRD(self.rrdfile, step=step, ds=dss, rra=rras)
        self.rrd.create(debug=False)
        time.sleep(2)

    def update(self, temperature):
        print temperature
        self.rrd.bufferValue(time.time(), temperature)
        self.rrd.update()

    def graph_temperature(self, period='day'):
        def1 = DEF(rrdfile=self.rrdfile, vname='temp', dsName="temperature",
                   cdef="AVERAGE")

        # TOTAL
        vdef1 = VDEF(vname='max', rpn='temp,MAXIMUM')
        vdef2 = VDEF(vname='avg', rpn='temp,AVERAGE')
        vdef3 = VDEF(vname='last', rpn='temp,LAST')
        vdef4 = VDEF(vname='min', rpn='temp,MINIMUM')

        line1 = LINE(1, defObj=def1, color='#FF2222', legend='Total')
        gprint1 = GPRINT(vdef1, "Max\\: %5.1lf %S")
        gprint2 = GPRINT(vdef2, "Avg\\: %5.1lf %S")
        gprint3 = GPRINT(vdef3, "Current\\: %5.1lf %S")
        gprint4 = GPRINT(vdef4, "Min\\: %5.1lf %S\\n")

        ca = ColorAttributes()
        ca.back = '#FFFFFF'
        ca.canvas = '#FFFFFF'
        ca.shadea = '#000000'
        ca.shadeb = '#111111'
        ca.mgrid = '#CCCCCC'
        ca.axis = '#333333'
        ca.frame = '#AAAAAA'
        ca.font = '#333333'
        ca.arrow = '#333333'

        img = "temperature-%s.png" % period
        imgname = self.static_path +"/"+ img
        start = '-1'+period

        g = Graph(imgname, imgformat='PNG', step=start,
                  vertical_label='temperature', color=ca,
                  width=700, height=150
                  )
        g.data.extend([def1, vdef1, vdef2, vdef3, vdef4, line1, gprint1,
                       gprint2, gprint3, gprint4])
        g.write()

    def graph(self, period='day'):

        self.graph_temperature(period)
После запуска скрипта он создаст rrd базу если ее еще нет, начнет каждые 60 сек добавлять значения и отрисовывать графики. Осталось только отдать эти графики по запросу. Для этого используем фреймворк Tornado.
Создадим файл weather_srv.py в той же директории где папка media с нашими графиками
#!/usr/bin/python
#coding: utf-8
import tornado.ioloop
import tornado.web
import os

class MainHandler(tornado.web.RequestHandler):
    def get(self):

        f = open('temp.cashe')
        temp = f.read()
 self.render("index.html", temp=temp)

application = tornado.web.Application([
    (r"/", MainHandler),
    (r"/media/(.*)", tornado.web.StaticFileHandler, {"path": os.path.join(os.path.dirname(__file__), "media")}),
])

if __name__ == "__main__":
    application.listen(8009)
    tornado.ioloop.IOLoop.instance().start()
Шаблон index.html
<html>
<head>
<title>Температура на улице</title>
</head>
<body bgcolor="white" text="black">
<center><font size="5">{{ escape(temp) }}</b>C°</font>
<br><img src="media/temperature-day.png" title="температура за день">
</center>
</body>
</html>

Запускаем weather_srv.py и радуемся графикам.
Описание по измерению температуры termal.me
Пример как это выглядит 19216801.ru
Дополнительно для прочтения:
Перевод документации RRDTool
Пример с хабра
nginx-monitor похожий пример для nginx