Pull to refresh

Построение графиков работы DHCP сервера

Reading time 10 min
Views 20K
На работе очень часто стоит вопрос — у нас DHCP сервер хорошо работает? Я работаю в internet провайдере, DHCP обеспечивает работу клиентской сети. Исторически сложилась следующая схема работы: DHCP серверов два, конфигурация генерируется на биллинговом сервере и с помощью rsync разливается на сервера. Используем Centos в качестве рабочей системы и ISC DHCP в качестве сервера. Никакого failover не настроено и не настраивалось — нет необходимости. Вполне достаточно, что сервера работают по идентичному конфигу. Используется привязка абонента к мак адресу, в случае смены оборудования у абонента есть возможность зайти в личный кабинет и указать новый мак. Генерация конфига выполняется раз в 5 минут, если у нового конфига изменилась md5 сумма — то сервис перестартовывает. Схема работает несколько лет, проблем нет.

Периодически возникали проблемы, что конфиг генерился синтаксически неправильным — и после рестарта сервис падал. Добавили в скрипт рестарта проверку синтаксиса (dhcpd -t), падения прекратились. Ну и со стороны билинга обвешано проверками — на наличие адреса, мака, и т.п.

Пару раз столкнулись с ситуацией, когда из сети приходят запросы, а ответы в сеть не доходят. Оказался виноват агрегатор, полечили. Все это время не хватало простого графического анализа — как работает DHCP сервер. Как показал опыт разборок с проблемами — банальным просмотром логов и оценкой, сколько каких сообщений принимает и отсылает сервер — можно уже примерно оценить наличие проблемы в сети. Ну и в принципе график можно показать ночному оператору с инструкцией — если здесь резкое изменение показателей — звони админам.

Итак, формулируем перед собой задачу. Согласно стандарту, в протоколе DHCP существуют следующие типы сообщений:

DHCPDISCOVER — запрос клиента на наличие адресов
DHCPOFFER — предложение сервера на получение адреса
DHCPREQUEST — запрос клиента на получение адреса (предложенного сервером в DHCPOFFER)
DHCPACK — подтверждение сервера о выдаче адреса
DHCPDECLINE — отказ клиента в получении предложенного адреса
DHCPNAK — отказ сервера в выдаче запрошенного адреса
DHCPRELEASE — уведомление клиента об освобождении адреса
DHCPINFORM — запрос клиента о дополнительных параметрах
Мы хотим построить график к-ва каждого типа сообщений.

В качестве источника информации можно использовать log файл сервера. Другого источника я не нашел, да и его не может быть особо — если предположить, что dhcp умеет где то у себя накапливать эту информацию — то во первых были бы средства извлечения такой статистики, А во вторых — постоянные рестарты процесса сводят ее полезность к нулю.

В нашей сети dhcp сервера — это отдельные виртуалки, машина, которая строит графики (называется mrtg) — тоже отдельная виртуалка. Т.е. нужен способ передачи информации между машинами.

В результате я реализовал вот такую схему. На dhcp сервере добавляем в конфиг:

log-facility local6


В syslog (эти виртуалки до сих пор работают на centos 5, аптайм машин год, было бы больше, если бы в нашем городе год назад свет не выключали на месяц) добавляем следующее:

*.info;mail.none;authpriv.none;cron.none;local6.none            /var/log/messages

local6.*                                                /var/log/dhcpd.log
local6.*                                                /var/log/dhcpd-stat.log


Т.е. запрещаем local6 выводится в общий файл /var/log/messages, а выводим его в два файла одновременно.

Следующим шагом учим mrtg ходить на dhcp по ключу без пароля. Ну и вот такой скриптик на mrtg:

скрипт обновления статистики
#!/bin/bash

for HOST in dhcp1 dhcp2
do
TARGET=/srv/www/noc.lds.net.ua/dhcp/$HOST
mkdir -p $TARGET
cd $TARGET
scp $HOST:/var/log/dhcpd-stat.log ./
ssh $HOST "echo -n > /var/log/dhcpd-stat.log"

if [ ! -f dhcpqueries.rrd ]
then
 /usr/bin/rrdtool create dhcpqueries.rrd \
    DS:request:GAUGE:600:0:10000000 \
    DS:ack:GAUGE:600:0:10000000 \
    DS:decline:GAUGE:600:0:10000000 \
    DS:discover:GAUGE:600:0:10000000 \
    DS:release:GAUGE:600:0:10000000 \
    DS:nak:GAUGE:600:0:10000000 \
    DS:info:GAUGE:600:0:10000000 \
    DS:offer:GAUGE:600:0:10000000 \
    RRA:AVERAGE:0.5:1:800 \
    RRA:AVERAGE:0.5:6:800 \
    RRA:AVERAGE:0.5:24:800 \
    RRA:AVERAGE:0.5:288:800 \
    RRA:MAX:0.5:1:800 \
    RRA:MAX:0.5:6:800 \
    RRA:MAX:0.5:24:800 \
    RRA:MAX:0.5:288:800
fi

out=$(awk '
    BEGIN       {request=0;ack=0;decline=0;discover=0;release=0;nak=0;info=0;offer=0}
    {
        if($6 == "DHCPREQUEST")     {request = request + 1}
        if($6 == "DHCPACK") {ack = ack + 1}
        if($6 == "DHCPDECLINE")    {decline = decline + 1}
        if($6 == "DHCPDISCOVER")  {discover = discover + 1}
        if($6 == "DHCPRELEASE")   {release = release + 1}
        if($6 == "DHCPNAK")    {nak = nak + 1}
        if($6 == "DHCPINFORM")   {info = info + 1}
        if($6 == "DHCPOFFER")   {offer = offer + 1}
    }
    END         {print request ":" ack ":" decline ":" discover ":" release ":" nak ":" info ":" offer}
' dhcpd-stat.log)

/usr/bin/rrdtool update dhcpqueries.rrd --template \
    request:ack:decline:discover:release:nak:info:offer \
    N:$out

/usr/bin/rrdtool graph dhcprequest.png \
    -v "Requests/Minute" \
    --rigid \
    -l 0 \
    --start -86400 \
    DEF:a=dhcpqueries.rrd:request:AVERAGE \
    DEF:b=dhcpqueries.rrd:ack:AVERAGE \
    DEF:c=dhcpqueries.rrd:decline:AVERAGE \
    DEF:d=dhcpqueries.rrd:discover:AVERAGE \
    DEF:e=dhcpqueries.rrd:release:AVERAGE \
    DEF:f=dhcpqueries.rrd:nak:AVERAGE \
    DEF:g=dhcpqueries.rrd:info:AVERAGE \
    DEF:h=dhcpqueries.rrd:offer:AVERAGE \
    CDEF:cdefa=a,5,/ \
    CDEF:cdefb=b,5,/ \
    CDEF:cdefc=c,5,/ \
    CDEF:cdefd=d,5,/ \
    CDEF:cdefe=e,5,/ \
    CDEF:cdeff=f,5,/ \
    CDEF:cdefg=g,5,/ \
    CDEF:cdefh=h,5,/ \
    LINE1:cdefa#9C7BBD:DHCPREQUEST \
    GPRINT:cdefa:LAST:"   Cur\:%8.1lf" \
    GPRINT:cdefa:AVERAGE:"Ave\:%8.1lf" \
    GPRINT:cdefa:MAX:"Max\:%8.1lf\n" \
    LINE1:cdefb#3152A5:DHCPACK \
    GPRINT:cdefb:LAST:"       Cur\:%8.1lf" \
    GPRINT:cdefb:AVERAGE:"Ave\:%8.1lf" \
    GPRINT:cdefb:MAX:"Max\:%8.1lf\n" \
    LINE1:cdefc#750F7D:DHCPDECLINE \
    GPRINT:cdefc:LAST:"   Cur\:%8.1lf" \
    GPRINT:cdefc:AVERAGE:"Ave\:%8.1lf" \
    GPRINT:cdefc:MAX:"Max\:%8.1lf\n" \
    LINE1:cdefd#157419:DHCPDISCOVER \
    GPRINT:cdefd:LAST:"  Cur\:%8.1lf" \
    GPRINT:cdefd:AVERAGE:"Ave\:%8.1lf" \
    GPRINT:cdefd:MAX:"Max\:%8.1lf\n" \
    LINE1:cdefe#6DC8FE:DHCPRELEASE \
    GPRINT:cdefe:LAST:"   Cur\:%8.1lf" \
    GPRINT:cdefe:AVERAGE:"Ave\:%8.1lf" \
    GPRINT:cdefe:MAX:"Max\:%8.1lf\n" \
    LINE1:cdeff#FFAB00:DHCPNAK \
    GPRINT:cdeff:LAST:"       Cur\:%8.1lf" \
    GPRINT:cdeff:AVERAGE:"Ave\:%8.1lf" \
    GPRINT:cdeff:MAX:"Max\:%8.1lf\n" \
    LINE1:cdefg#FF0000:DHCPINFORM \
    GPRINT:cdefg:LAST:"    Cur\:%8.1lf" \
    GPRINT:cdefg:AVERAGE:"Ave\:%8.1lf" \
    GPRINT:cdefg:MAX:"Max\:%8.1lf\n" \
    LINE1:cdefh#00FF00:DHCPOFFER \
    GPRINT:cdefh:LAST:"     Cur\:%8.1lf" \
    GPRINT:cdefh:AVERAGE:"Ave\:%8.1lf" \
    GPRINT:cdefh:MAX:"Max\:%8.1lf\n" \

/usr/bin/rrdtool graph dhcprequest-weekly.png \
    -v "Requests/Minute" \
    --rigid \
    -l 0 \
    --start -604800 \
    DEF:a=dhcpqueries.rrd:request:AVERAGE \
    DEF:b=dhcpqueries.rrd:ack:AVERAGE \
    DEF:c=dhcpqueries.rrd:decline:AVERAGE \
    DEF:d=dhcpqueries.rrd:discover:AVERAGE \
    DEF:e=dhcpqueries.rrd:release:AVERAGE \
    DEF:f=dhcpqueries.rrd:nak:AVERAGE \
    DEF:g=dhcpqueries.rrd:info:AVERAGE \
    DEF:h=dhcpqueries.rrd:offer:AVERAGE \
    CDEF:cdefa=a,5,/ \
    CDEF:cdefb=b,5,/ \
    CDEF:cdefc=c,5,/ \
    CDEF:cdefd=d,5,/ \
    CDEF:cdefe=e,5,/ \
    CDEF:cdeff=f,5,/ \
    CDEF:cdefg=g,5,/ \
    CDEF:cdefh=h,5,/ \
    LINE1:cdefa#9C7BBD:DHCPREQUEST \
    GPRINT:cdefa:LAST:"   Cur\:%8.1lf" \
    GPRINT:cdefa:AVERAGE:"Ave\:%8.1lf" \
    GPRINT:cdefa:MAX:"Max\:%8.1lf\n" \
    LINE1:cdefb#3152A5:DHCPACK \
    GPRINT:cdefb:LAST:"       Cur\:%8.1lf" \
    GPRINT:cdefb:AVERAGE:"Ave\:%8.1lf" \
    GPRINT:cdefb:MAX:"Max\:%8.1lf\n" \
    LINE1:cdefc#750F7D:DHCPDECLINE \
    GPRINT:cdefc:LAST:"   Cur\:%8.1lf" \
    GPRINT:cdefc:AVERAGE:"Ave\:%8.1lf" \
    GPRINT:cdefc:MAX:"Max\:%8.1lf\n" \
    LINE1:cdefd#157419:DHCPDISCOVER \
    GPRINT:cdefd:LAST:"  Cur\:%8.1lf" \
    GPRINT:cdefd:AVERAGE:"Ave\:%8.1lf" \
    GPRINT:cdefd:MAX:"Max\:%8.1lf\n" \
    LINE1:cdefe#6DC8FE:DHCPRELEASE \
    GPRINT:cdefe:LAST:"   Cur\:%8.1lf" \
    GPRINT:cdefe:AVERAGE:"Ave\:%8.1lf" \
    GPRINT:cdefe:MAX:"Max\:%8.1lf\n" \
    LINE1:cdeff#FFAB00:DHCPNAK \
    GPRINT:cdeff:LAST:"       Cur\:%8.1lf" \
    GPRINT:cdeff:AVERAGE:"Ave\:%8.1lf" \
    GPRINT:cdeff:MAX:"Max\:%8.1lf\n" \
    LINE1:cdefg#FF0000:DHCPINFORM \
    GPRINT:cdefg:LAST:"    Cur\:%8.1lf" \
    GPRINT:cdefg:AVERAGE:"Ave\:%8.1lf" \
    GPRINT:cdefg:MAX:"Max\:%8.1lf\n" \
    LINE1:cdefh#00FF00:DHCPOFFER \
    GPRINT:cdefh:LAST:"     Cur\:%8.1lf" \
    GPRINT:cdefh:AVERAGE:"Ave\:%8.1lf" \
    GPRINT:cdefh:MAX:"Max\:%8.1lf\n" \

/usr/bin/rrdtool graph dhcprequest-monthly.png \
    -v "Requests/Minute" \
    --rigid \
    -l 0 \
    --start -2592000 \
    DEF:a=dhcpqueries.rrd:request:AVERAGE \
    DEF:b=dhcpqueries.rrd:ack:AVERAGE \
    DEF:c=dhcpqueries.rrd:decline:AVERAGE \
    DEF:d=dhcpqueries.rrd:discover:AVERAGE \
    DEF:e=dhcpqueries.rrd:release:AVERAGE \
    DEF:f=dhcpqueries.rrd:nak:AVERAGE \
    DEF:g=dhcpqueries.rrd:info:AVERAGE \
    DEF:h=dhcpqueries.rrd:offer:AVERAGE \
    CDEF:cdefa=a,5,/ \
    CDEF:cdefb=b,5,/ \
    CDEF:cdefc=c,5,/ \
    CDEF:cdefd=d,5,/ \
    CDEF:cdefe=e,5,/ \
    CDEF:cdeff=f,5,/ \
    CDEF:cdefg=g,5,/ \
    CDEF:cdefh=h,5,/ \
    LINE1:cdefa#9C7BBD:DHCPREQUEST \
    GPRINT:cdefa:LAST:"   Cur\:%8.1lf" \
    GPRINT:cdefa:AVERAGE:"Ave\:%8.1lf" \
    GPRINT:cdefa:MAX:"Max\:%8.1lf\n" \
    LINE1:cdefb#3152A5:DHCPACK \
    GPRINT:cdefb:LAST:"       Cur\:%8.1lf" \
    GPRINT:cdefb:AVERAGE:"Ave\:%8.1lf" \
    GPRINT:cdefb:MAX:"Max\:%8.1lf\n" \
    LINE1:cdefc#750F7D:DHCPDECLINE \
    GPRINT:cdefc:LAST:"   Cur\:%8.1lf" \
    GPRINT:cdefc:AVERAGE:"Ave\:%8.1lf" \
    GPRINT:cdefc:MAX:"Max\:%8.1lf\n" \
    LINE1:cdefd#157419:DHCPDISCOVER \
    GPRINT:cdefd:LAST:"  Cur\:%8.1lf" \
    GPRINT:cdefd:AVERAGE:"Ave\:%8.1lf" \
    GPRINT:cdefd:MAX:"Max\:%8.1lf\n" \
    LINE1:cdefe#6DC8FE:DHCPRELEASE \
    GPRINT:cdefe:LAST:"   Cur\:%8.1lf" \
    GPRINT:cdefe:AVERAGE:"Ave\:%8.1lf" \
    GPRINT:cdefe:MAX:"Max\:%8.1lf\n" \
    LINE1:cdeff#FFAB00:DHCPNAK \
    GPRINT:cdeff:LAST:"       Cur\:%8.1lf" \
    GPRINT:cdeff:AVERAGE:"Ave\:%8.1lf" \
    GPRINT:cdeff:MAX:"Max\:%8.1lf\n" \
    LINE1:cdefg#FF0000:DHCPINFORM \
    GPRINT:cdefg:LAST:"    Cur\:%8.1lf" \
    GPRINT:cdefg:AVERAGE:"Ave\:%8.1lf" \
    GPRINT:cdefg:MAX:"Max\:%8.1lf\n" \
    LINE1:cdefh#00FF00:DHCPOFFER \
    GPRINT:cdefh:LAST:"     Cur\:%8.1lf" \
    GPRINT:cdefh:AVERAGE:"Ave\:%8.1lf" \
    GPRINT:cdefh:MAX:"Max\:%8.1lf\n" \

/usr/bin/rrdtool graph dhcprequest-yearly.png \
    -v "Requests/Minute" \
    --rigid \
    -l 0 \
    --start -31536000 \
    DEF:a=dhcpqueries.rrd:request:AVERAGE \
    DEF:b=dhcpqueries.rrd:ack:AVERAGE \
    DEF:c=dhcpqueries.rrd:decline:AVERAGE \
    DEF:d=dhcpqueries.rrd:discover:AVERAGE \
    DEF:e=dhcpqueries.rrd:release:AVERAGE \
    DEF:f=dhcpqueries.rrd:nak:AVERAGE \
    DEF:g=dhcpqueries.rrd:info:AVERAGE \
    DEF:h=dhcpqueries.rrd:offer:AVERAGE \
    CDEF:cdefa=a,5,/ \
    CDEF:cdefb=b,5,/ \
    CDEF:cdefc=c,5,/ \
    CDEF:cdefd=d,5,/ \
    CDEF:cdefe=e,5,/ \
    CDEF:cdeff=f,5,/ \
    CDEF:cdefg=g,5,/ \
    CDEF:cdefh=h,5,/ \
    LINE1:cdefa#9C7BBD:DHCPREQUEST \
   GPRINT:cdefa:LAST:"   Cur\:%8.1lf" \
    GPRINT:cdefa:AVERAGE:"Ave\:%8.1lf" \
    GPRINT:cdefa:MAX:"Max\:%8.1lf\n" \
    LINE1:cdefb#3152A5:DHCPACK \
    GPRINT:cdefb:LAST:"       Cur\:%8.1lf" \
    GPRINT:cdefb:AVERAGE:"Ave\:%8.1lf" \
    GPRINT:cdefb:MAX:"Max\:%8.1lf\n" \
    LINE1:cdefc#750F7D:DHCPDECLINE \
    GPRINT:cdefc:LAST:"   Cur\:%8.1lf" \
    GPRINT:cdefc:AVERAGE:"Ave\:%8.1lf" \
    GPRINT:cdefc:MAX:"Max\:%8.1lf\n" \
    LINE1:cdefd#157419:DHCPDISCOVER \
    GPRINT:cdefd:LAST:"  Cur\:%8.1lf" \
    GPRINT:cdefd:AVERAGE:"Ave\:%8.1lf" \
    GPRINT:cdefd:MAX:"Max\:%8.1lf\n" \
    LINE1:cdefe#6DC8FE:DHCPRELEASE \
    GPRINT:cdefe:LAST:"   Cur\:%8.1lf" \
    GPRINT:cdefe:AVERAGE:"Ave\:%8.1lf" \
    GPRINT:cdefe:MAX:"Max\:%8.1lf\n" \
    LINE1:cdeff#FFAB00:DHCPNAK \
    GPRINT:cdeff:LAST:"       Cur\:%8.1lf" \
    GPRINT:cdeff:AVERAGE:"Ave\:%8.1lf" \
    GPRINT:cdeff:MAX:"Max\:%8.1lf\n" \
    LINE1:cdefg#FF0000:DHCPINFORM \
    GPRINT:cdefg:LAST:"    Cur\:%8.1lf" \
    GPRINT:cdefg:AVERAGE:"Ave\:%8.1lf" \
    GPRINT:cdefg:MAX:"Max\:%8.1lf\n" \
    LINE1:cdefh#00FF00:DHCPOFFER \
    GPRINT:cdefh:LAST:"     Cur\:%8.1lf" \
    GPRINT:cdefh:AVERAGE:"Ave\:%8.1lf" \
    GPRINT:cdefh:MAX:"Max\:%8.1lf\n" \

if [ ! -f index.shtml ]
then

cat > ./index.shtml << END
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<HTML>
<HEAD>
    <TITLE>$HOST</TITLE>
    <META HTTP-EQUIV="Refresh" CONTENT="300">
    <META HTTP-EQUIV="Cache-Control" content="no-cache">
    <META HTTP-EQUIV="Pragma" CONTENT="no-cache">
    <META HTTP-EQUIV="Expires" CONTENT="Mon, 04 Feb 2008 16:28:46 GMT">

<style type="text/css">
</style>
</HEAD>

<BODY bgcolor="#ffffff" text="#000000" link="#000000" vlink="#000000" alink="#000000">
<!--#include virtual="/menu.shtml" -->
<CENTER><H1><B>$HOST</B></H1></CENTER>

<TABLE BORDER=0 CELLPADDING=0 CELLSPACING=0 align="center">
  <tbody>
   <tr><td align="center"><b><h2>Query types</h2></b></td></tr>
    <tr>
      <td align="center">
       <b>daily</B><br>
       <IMG BORDER=0 SRC="dhcprequest.png"><br>
      </td>
    </tr>
    <tr>
      <td align="center">
       <b>weekly</B><br>
       <IMG BORDER=0 SRC="dhcprequest-weekly.png"><br>
      </td>
    </tr>
    <tr>
      <td align="center">
       <b>monthly</B><br>
       <IMG BORDER=0 SRC="dhcprequest-monthly.png"><br>
      </td>
    </tr>
    <tr>
      <td align="center">
       <b>yearly</B><br>
       <IMG BORDER=0 SRC="dhcprequest-yearly.png"><br>
      </td>
    </tr>

 </tbody>
 </table>
</BODY>
</HTML>
END

fi

done



Расскажу, как этот скрипт работает.

Для каждого dhcp сервера создается отдельный каталог на веб сервере. Затем, мы по scp забираем текущий dhcpd-stat.log с сервера, и следующей командой его очищаем. Для того, чтобы в следующий раз забрать лог файл за прошедшее время работы. Так как скрипт вызывается из крона каждые 5 минут, то наша статистика обрабатывает логи за 5 минут работы. Я более чем уверен, что за то время, которое проходит между командами копирования и очистки — в лог попадают какие то данные, которые я теряю. Но думаю, что несколько строк погоды не делают.

Заодно отвечу на вопрос — почему логи dhcp выводятся в два файла. Первый — это обычный лог, он ротейтится стандартным logrotate, не переписывается постоянно, предназначен для человека — если надо будет его почитать. Второй — для скрипта, каждые 5 минут очищается.
Ну а дальше в скрипте в принципе все должно быть понятно: инициализируем rrd базу, при ее отсуствии, с помощью awk считаем количество каждого типа сообщений, заносим в rrd базу, затем строим стандартную 4-ку картинок — дневной график, недельный график, месячный график, годовой график. Ну и создаем index.shtml, если его нету.

Скриптик начал работать, графики рисуются. Теперь видно сразу — работает dhcp, или нет…

Вот как выглядит график:

Tags:
Hubs:
+13
Comments 32
Comments Comments 32

Articles