Blog

Fault Managment и поддержка GNU/Linux

1. Введение.

Есть очень много способов реализовать журналирование событий в GNU/Linux. Рассмотрим один из лучших, с возможностью предварительной фильтрации журналируемых событий и отправкой их по сети на syslog сервер NOC.

Предварительная фильтрация журналируемых событий необходима для предотвращения перегрузки сетевого канала передаваемыми событиями и DDOS сервера NOC.

Чтобы реализовать аудит будем использовать средства ядра Linux, также дополнительные утилиты: iptables, .... для настройки правил отлова событий в сети.  В качестве сервиса журналирования - syslog-ng.

2. Включение аудита выполняемых на сервере команд (Security | Audit | Command).

2.1 Настройка ядра Linux.

На исходные тексты ядра необходимо предварительно наложить патчи grsecurity.net после чего, как минимум, включить следующие опции:

CONFIG_GRKERNSEC_AUDIT_GROUP=y
CONFIG_GRKERNSEC_AUDIT_GID=65111
CONFIG_GRKERNSEC_EXECLOG=y
CONFIG_GRKERNSEC_AUDIT_CHDIR=y
2.2 Настройка системы.

Создаём группу для аудита:

# groupadd -g 65111 audit

И добавляем в неё необходимых пользователей:

# usermod -a -G audit noc
# usermod -a -G audit ...

Теперь ядро будет слать в системный журнал все выполняемые пользователями команды. Будьте осторожны, некоторые пользователи сервисов могут выполнять огромное количество команд. Добавляйте только необходимых пользователей!

3. Настройки защиты ядра Linux (Security | Attack | Crack Attemption Detected).

Включаем следующие опции настройки ядра:

CONFIG_PAX_NOEXEC=y
CONFIG_PAX_PAGEEXEC=y
CONFIG_PAX_MPROTECT=y
CONFIG_PAX_ASLR=y
CONFIG_PAX_RANDKSTACK=y
CONFIG_PAX_RANDUSTACK=y
CONFIG_PAX_RANDMMAP=y
CONFIG_PAX_MEMORY_SANITIZE=y
CONFIG_PAX_MEMORY_STACKLEAK=y
CONFIG_PAX_REFCOUNT=y
CONFIG_PAX_USERCOPY=y
CONFIG_GRKERNSEC_KMEM=y
CONFIG_GRKERNSEC_IO=y
CONFIG_GRKERNSEC_BPF_HARDEN=y
CONFIG_GRKERNSEC_PERF_HARDEN=y
CONFIG_GRKERNSEC_RAND_THREADSTACK=y
CONFIG_GRKERNSEC_PROC_MEMMAP=y
CONFIG_GRKERNSEC_KSTACKOVERFLOW=y
CONFIG_GRKERNSEC_BRUTE=y
CONFIG_GRKERNSEC_MODHARDEN=y
CONFIG_GRKERNSEC_HIDESYM=y
CONFIG_GRKERNSEC_RANDSTRUCT=y
CONFIG_GRKERNSEC_KERN_LOCKOUT=y
CONFIG_GRKERNSEC_BLACKHOLE=y
CONFIG_GRKERNSEC_FLOODTIME=60
CONFIG_GRKERNSEC_FLOODBURST=3

Бинарные файлы должны собираться с PIE/SSP (CFLAGS="-fPIE -fstack-protector-all -D_FORTIFY_SOURCE=2" LDFLAGS="-Wl,-z,now -Wl,-z,relro"). Теперь ядро способно определять некоторые виды популярных атак, предотвращать их распространение и информировать нас о произошедшей попытке взлома. Можно отметить в этом разделе больше опций или меньше... Некоторые опции сильно влияют на производительность, которая в свою очередь зависит от аппаратной платформы и поддерживаемых процессором инструкций.

4. Настройки iptables, ... и прочих средств списков контроля доступа (Security | ACL).

Необходимо создать специальные правила журналирования пакетов в iptables со специальными метками. Например для журналирования отброшенных пакетов необходимо последними в цепочках INPUT, FORWARD, OUTPUT добавить соответственно следующие правила:

# iptables -t filter -A INPUT -p ALL -m limit --limit 5/h --limit-burst 3 -j LOG --log-prefix 'iptables INPUT '
# iptables -t filter -A FORWARD -p ALL -m limit --limit 5/h --limit-burst 3 -j LOG --log-prefix 'iptables FORWARD '
# iptables -t filter -A OUTPUT -p ALL -m limit --limit 5/h --limit-burst 3 -j LOG --log-prefix 'iptables OUTPUT ' --log-uid

Количество сетевых событий также может быть огромным, фильтруйте только необходимые, например атака ssh heartbeat:

# iptables -t filter -A INPUT -p tcp --dport 22 -m u32 --u32 "52=0x18030000:0x1803FFFF" -j LOG --log-prefix 'iptables SSH HEARTBEAT '

5. Настройка lm_sensors (Environment)

Устанавливаем в систему пакет lm_sensors с поддержкой sensord. В опции запуска sensord можно добавить "-i 0", так он будет журналировать только тревожные сообщения. Запускаем sensord и добавляем его в вашу систему инициализации.

6. Настройка syslog-ng

Настройка этого сервиса предельно понятна и удобна. Необходимо указать 4 главных параметра: источник событий (source), место назначения событий (destination), фильтр событий (filter) и дать команду журналировать (log). Фильтр событьй должен выбирать только события классифицируемые в NOC! Приведём рабочий пример файла настройки /etc/syslog-ng/syslog-ng.conf, локальные журналы с него удалены:

@version: 3.7
# $Id$
#
# Syslog-ng default configuration file for Gentoo Linux

# https://bugs.gentoo.org/show_bug.cgi?id=426814
@include "scl.conf"

options {
threaded(yes);
chain_hostnames(no);

# The default action of syslog-ng is to log a STATS line
# to the file every 10 minutes. That's pretty ugly after a while.
# Change it to every 12 hours so you get a nice daily update of
# how many messages syslog-ng missed (0).
stats_freq(43200);
# The default action of syslog-ng is to log a MARK line
# to the file every 20 minutes. That's seems high for most
# people so turn it down to once an hour. Set it to zero
# if you don't want the functionality at all.
mark_freq(3600);
};

source src {
system();
internal();
};

destination NOC { udp("1.1.1.1" port(514)); };

filter Environment { facility(daemon) and program("sensord") and level(alert); };
filter Network_Link { facility(kern) and message("^.*: (link up|link down)$"); };
filter Network_LAG { facility(kern) and message("^.*: (Adding slave|Removing slave)"); };
filter Network_NTP { facility(daemon) and program("^ntpd$") and (
message("(now valid|now invalid)$")
or message("adjusting local clock by")
or message("clock is now synced$")
); };
filter Security_ACL { facility(kern) and (
                message("^iptables .*")
                or message("^ebtables .*")
                ); };
filter Security_Attack_Hardened { facility(kern) and (
                message("^PAX: terminating task: ")
                or message("^grsec: banning user with uid ")
                or message("^grsec: bruteforce prevention initiated ")
                or message("^grsec: denied resource overstep ")
                or message("^grsec: denied RWX mmap ")
               or message("^grsec: denied untrusted exec ")
                or message("^grsec: From ")
                ); };
filter Security_Audit_Command { facility(kern) and (
                message("^grsec: chdir to") and not message("^.*uid/euid:0/0 gid/egid:0/0, parent /usr/sbin/(run-crons|cron)")
                or message("^grsec: exec of") and not message("^.*uid/euid:0/0 gid/egid:0/0, parent /usr/sbin/(run-crons|cron)")
                ); };
filter Security_Audit_Cron { facility(cron) and message("^.* CMD .*$") and not message("^.*/usr/sbin/run-crons.*$"); };
filter Security_Authentication { facility(authpriv) and message("^(.*|)pam_unix.*$"); };
filter System { program("^init$") and message("^(Entering|Switching to) runlevel: "); };
filter NOC { filter(Environment)
                or filter(Network_Link)
                or filter(Network_LAG)
or filter(Network_NTP)
                or filter(Security_ACL)
                or filter(Security_Attack_Hardened)
                or filter(Security_Audit_Command)
                or filter(Security_Audit_Cron)
                or filter(Security_Authentication)
                or filter(System)
                ; };
log { source(src); filter(NOC); destination(NOC); };

7. NOC

Теперь необходимо ваш GNU/Linux сервер добавить в NOC Service Activation -> Managed Object, при добавлении выбрать профиль OS.Linux и указать в Trap Source IP адрес сервера.

Если решили добавить новые события с сервера GNU/Linux, то напишите ещё фильтры, у файле настройки /etc/syslog-ng/syslog-ng.conf, выбирающие только необходимые вам события и добавьте их через or в агрегирующий фильтр NOC. Выбранные вами события появятся в Fault Management -> Events как Unknown | Syslog. Далее вам необходимо написать правила классификации событий.

 

Ишью как модераторы-то джируют!

Или рассказ о модерировании Issues в JIRA.

Назрела потребность добавить порядка в задачах в трекере. 

Собственно трекер: https://bt.nocproject.org/secure/Dashboard.jspa

Пара модераторов: Егор и Сергей.

Права на редактирования задачи и добавляем инфы в поля Component/s & Labels.

Сейчас добавляем в Component/s:

в Labels:

  • профиль, если задача по конкретному профилю, например Cisco.IOS;
  • unknown-event — по необработанным FM-сообщениям;
  • patch — при наличии в задаче патча;
  • security — все про безопасность в ноке;
  • request-sa-script — запрашивается создание скрипта в профиле SA;
  • search — проблемы поиска и одноимённого приложения;
  • new-sa-profile — новый профиль устройств;
  • beefCanned Beef;
  • crash — баг ведет в трейсбэку;
  • get_ — имя скрипта, о котором задача;
  • json — есть json-данные.

Примеры поисковых запросов:

labels = sa AND component IS NULL
component IS NULL AND text ~event_class__name
component IS NULL AND text ~"SNMP Trap" AND status != RESOLVED
labels IS NULL AND text ~dlink AND status NOT IN (RESOLVED, CLOSED)
labels IS NULL AND text ~cisco AND status NOT IN (RESOLVED, CLOSED)
project = NOC AND labels IS NULL AND text ~cisco AND status NOT IN (RESOLVED, CLOSED)
project = NOC AND labels IS NULL AND component IS NULL AND status NOT IN (RESOLVED, CLOSED)
project = NOC AND reporter =  'Ilya Zakharov' AND labels IS NULL AND component IS NULL AND status NOT IN (RESOLVED, CLOSED)

 

Веллком в комменты с предложениями по Component/s & Labels.

Повседневный:

project = NOC AND (labels IS NULL OR component IS NULL) AND status NOT IN (RESOLVED, CLOSED, DONE)

Старые баги:

project = NOC AND status not in (RESOLVED, CLOSED, DONE, FEEDBACK) AND createdDate < 2012-01-01 AND type = Bug

вот еще:

project = NOC AND status not in (RESOLVED, CLOSED, DONE, FEEDBACK) AND type = Bug AND (labels is EMPTY OR component is EMPTY) ORDER BY updatedDate

 

 
Записки новичка: перезалить скрипт

Бывает, что нужно поправить файло, а с обновлением потом "непонятки",

или и вовсе я случайно грохнул нужный скрипт sa/profiles/Cisco/IOS/get_oam_status.py

возвращаем файл к состоянию, зафиксированному в репозитории:

под noc'ом

hg revert sa/profiles/Cisco/IOS/get_oam_status.py

 

или точнее:

8[14:38]    zi_rus: TSergey: у тебя неправильное описание, реверт не метит, а вытягирает и репо текущую версию взамен той что лежит на диске. 
            если локальный файл отличается от репозитарного, то hg не может по-нормальному его обновить, вернее она могла бы его зам
8[14:38]    zi_rus: а когда ты ревертишь у тебя файл становится одинаковым
8[14:38]    zi_rus: и апгрейд может нормально дальше пройти
Записки новичка: удаляем конфиг МО

Как-то так:

./noc shell
%cpaste
from noc.lib.gridvcs.gridvcs import *
from noc.lib.gridvcs.manager import *
from noc.sa.models import *
CLEAR_CONFIG_MO = {'MO1','MO2',...}
mo = ManagedObject.objects.filter()
for m in mo:
    if m.name in CLEAR_CONFIG_MO:
        print "*************************************"
        print m.name
        m.config.get_gridvcs().delete(m.id)
--
quit()

Понадобилось при работе над get_config, для удаления черновых конфигов.

Записки новичка: консервируем МО

У нас нередко коммутатор получает "вторую жизнь".

По каким-либо причинам мы его демонтируем, помещаем на склад, а спустя некоторое время он возвращается в сеть, как новый объект.

Для NOC`а, однако, это не совсем новый объект, некоторые его свойства он помнит для его предыдущей реинкарнации.

Я правлю это так:

./noc shell
from noc.inv.models.discoveryid import DiscoveryID
from noc.inv.models import *
from noc.sa.models import *
MO = {'MO1', 'MO2'}
for m in MO:
    id = ManagedObject.objects.get(name = m).id
    try:
        chassis = DiscoveryID.objects.get(object = id)
        print "chassis:", chassis.object, chassis.chassis_mac, chassis.id         
        # delete chassis 
        chassis.delete()
        chassis.save()
    except:
        print "chassis not found:", m
    for i in Interface.objects.filter(managed_object = id):
        if i.link:
            try:
                l = Link.objects.get(interfaces = i)
                print "link:", i.link
                # delete link
                l.delete()
                l.save()
            except:
                print "link not found:", i
        # delete interface
        i.delete()
        i.save()
quit()

Для добавления железок используются ип адреса из address space managment-assignet addresses. Там же нужно проставить tag. Вот сам скрипт

 

from noc.sa.models import *
from noc.inv.models import *
from noc.ip.models import *
from noc.main.models import SystemNotification
import string
import commands
for i in range(1,10):                          #задаем диапазон сеток
  prf=Prefix.objects.filter(prefix='10.10.'+str(i)+'.0/24',vrf='1') #делим диапазон на сети /24 (10.10.1.0/24 по 10.10.10.0/24)
  for p in prf:
    if p.tags:
      tags1= ','.join(str(v) for v in p.tags)     #ищем тег сетки 
      adrs=p.nested_address_set                   #поиск найденных адресов
      print tags1
      for a in adrs:
        addpr=False
        mo=ManagedObject.objects.filter(address=a.address)  #проверка на наличие mo
        if mo:
           for o in mo:
            print o.name
            addpr=False
        else:
           print a.address
           ring="ring"+str(int(str(a.address).split('.')[3])//20)                          #доп тег
           cmd="snmpget -v 2c -c public " + str(a.address) + " 1.3.6.1.2.1.1.2.0 -O Uvqn"  #вендор
           cmd2="snmpget -v 2c -c public " + str(a.address) + " 1.3.6.1.2.1.1.5.0 -O Uvqn" #hostname - будущее имя mo
           x=str(commands.getoutput(cmd))
           name=commands.getoutput(cmd2).replace('"', '')
           if x == ".1.3.6.1.4.1.2011.2.23.92":               #различные вендоры
            type1="Huawei.VRP"#s2326
            type2="s2300"
            addpr=True
           if x == ".1.3.6.1.4.1.259.6.10.94":
            type1="EdgeCore.ES"
            type2="edge"
            addpr=True
           if x == ".1.3.6.1.4.1.2011.2.23.96":            #железку не добавляем
            type1="Huawei.VRP"
            addpr=False
            continue
           if x == ".1.3.6.1.4.1.2011.2.23.229":
            type1="Huawei.VRP" #s2328
            type2="s2300"
            addpr=True
           if x.startswith("Timeout"):
            addpr=False
            continue
           if addpr:
            tags2 = [tags1,ring,type2]
            print tags2
            print addpr
            print name
            m = ManagedObject(name=name.format(a.address),is_managed=True,administrative_domain=AdministrativeDomain.objects.get(name="default"),activator = Activator.objects.get(name='default'),profile_name = type1, tags = tags2, snmp_ro = 'public', object_profile = ManagedObjectProfile.objects.get(name='test1'), scheme = 0, address = a.address, user = 'user', password = 'pass')
            m.save()                           #запись нового mo
            a.managed_object = m  

Работает через ./noc shell. На выходе куча железок в sa-mo, в которых проставлены вендоры, теги и тд, те сразу начинается сбор статистики.

Код далек от идеала. Например, выдет ошибку, если у мо 2 ип (это поправимо).

  1. Как запихнуть этот код в пируль?
  2. При переносе железки с изменением ип, хотелось бы старый мо удалить и создать новый.
  3. Как применить несколько snmp community?

(Canned BeefNOC-1429)

Есть у меня в использовании несколько коммутаторов DLink DGS-3100-24TG.

Они чуть более исключительные, чем остальные DES-DGS, и для них сделан отдельный профиль DGS3100.

Я допилил для указанного профиля обработку портчэналов, доработка коснулась скриптов get_portchannel и get_interfaces.

Для того, что бы поместить изменения в проект, нужно протестировать скрипты — действительно ли я все учел.

В NOC'е для этого реализован механизм работы с "тушенкой", возможность собрать данные с коммутатора и затем тестировать работу скриптов в отсутствии самого железа.

Обновим "тушенку" из публичного репо:

noc@noc:/opt/noc$ ./noc beef --pull
[sa_public]
pulling from https://bitbucket.org/nocproject/noc-sa-beef-public
searching for changes
no changes found

Тестируем скрипты:

noc@noc:/opt/noc$ ./noc test --beef-filter=DLink.DGS3100.get_portchannel noc.stop
Ran 0 tests in 0.000s
OK
noc@noc:/opt/noc$ ./noc test --beef-filter=DLink.DGS3100.get_interfaces noc.stop
Ran 0 tests in 0.000s
OK

Ноль тестов? Проверяем, есть ли "тушенка" для наших скриптов.

Данные лежат в /opt/noc/local/

noc@noc:/opt/noc/local/repos/sa/sa_public/DLink/DGS3100$ ls -l
drwxrwxr-x 2 noc noc 4096 Oct 20 14:41 get_chassis_id
drwxrwxr-x 2 noc noc 4096 Oct 20 14:41 get_dom_status
drwxrwxr-x 2 noc noc 4096 Oct 20 14:41 get_interface_status
drwxrwxr-x 2 noc noc 4096 Oct 20 14:41 get_lldp_neighbors
drwxrwxr-x 2 noc noc 4096 Oct 20 14:41 get_mac_address_table
drwxrwxr-x 2 noc noc 4096 Oct 20 14:41 get_version
drwxrwxr-x 2 noc noc 4096 Oct 20 14:41 get_vlans

Видим, что для get_portchannel и get_interfaces нет каталогов.

Значит сделаем локальный репо и протестируем на своих данных.

Пишем где будет наш репо:

etc/beef.conf

[my_repo]
enabled = true
type = sa
private = true
repo = /srv/noc/my_repo

Проверяем:

noc@noc:/opt/noc$ ./noc beef --list-repo
E P T  Name                 Repo
+ N sa sa_public            https://bitbucket.org/nocproject/noc-sa-beef-public
+ N sa my_repo              /srv/noc/my_repo

Готовим "тушенку":

./noc debug-script get_portchannel router1 -o output.json
./noc debug-script get_interfaces  router1 -o output.json
 я делал это так

(DLink DGS-3100 — предварительно сделанный селектор)

 

./noc shell
from noc.sa.models import *
SELECTOR = "DLink DGS-3100"
mos = ManagedObjectSelector.objects.filter(name=SELECTOR)[0]
if mos is not None:
    for m in mos.managed_objects:
        #print m.name, m.platform, m.get_attr("version","")
        txt  = "./noc debug-script get_portchannel " 
        txt += m.name + " -o /home/noc/" 
        txt += m.name.split(".")[0] + "_get_portchannel_" 
        txt += m.get_attr("version","") + ".json"
        print txt
if mos is not None:
    for m in mos.managed_objects:
        txt  = "./noc debug-script get_interfaces " 
        txt += m.name + " -o /home/noc/" 
        txt += m.name.split(".")[0] + "_get_interfaces_" 
        txt += m.get_attr("version","") + ".json"
        print txt
quit()

получаю строки вида:
./noc debug-script get_portchannel router1 -o /home/noc/s2-5-8_get_portchannel_3.60.44.json
./noc debug-script get_interfaces router1 -o /home/noc/s2-5-8_get_interfaces_3.60.44.json

затем кучей вываливаю их в консоль и через некоторое время получаю "тушенку" в /home/noc/

 


Делаем каталоги  get_portchannel и get_interfaces в /srv/noc/my_repo, копируем в них полученные файлы json. (json в репо помещается не совсем так, буду корректировать)

Тестируем на наших данных:

noc@noc:/opt/noc$ ./noc test --beef=/srv/noc/my_repo/ noc.stop
Ran 28 tests in 0.014s
OK

Или так:

./noc test --beef=/srv/noc/my_repo/ --beef-filter=DLink.DGS3100.get_portchannel noc.stop
./noc test --beef=/srv/noc/my_repo/ --beef-filter=DLink.DGS3100.get_interfaces noc.stop

Или так, на всей доступной тушенке: и нашей и публичной:

./noc test --beef-filter=DLink.DGS3100.get_portchannel noc.stop
./noc test --beef-filter=DLink.DGS3100.get_interfaces noc.stop

 

Ура, скрипты корректно работают на всех DGS-3100, которые есть у меня в NOC`е.

 

P.S.

Бывает, что "test" ругается на данные, а мы видим, что скрипт корректно отдает данные. В моем случае ругалось на ответ скрипта "[]".

Корректируем данные так:

./noc test --beef=/srv/noc/my_repo/ --fixed-beef-base=/srv/noc/fixed-beef yes

в /srv/noc/fixed-beef будут сформированы файлы, в которых найдена и поправлена ошибка. Копируем эти файлы в репо (нужно угадать какие файлы были ошибочные) и снова тестируемся.


Файлы "тушенки" отправляем на beef@nocproject.org и если все корректно, то их разместят в публичном репо.

 

 

Записки новичка: закрытие аварий

Как подсказал zi_rus:

 

09:36 misak, noc shell в помощь
09:36 from noc.fm.models import *
09:36 ac = AlarmClass.objects.get(name='Network | EIGRP | Neighbor Down')
09:36 alarms = ActiveAlarm.objects.filter(alarm_class=ac)
09:36 
09:36 for a in alarms:
09:36 a.clear_alarm("close")
09:36 from noc.fm.models import *
09:36 alarms = ActiveAlarm.objects.filter(vars__peer__in=['195.98.32.86'])
09:36 for a in alarms:
09:36 a.clear_alarm("close")
09:36  или так
09:36 from noc.fm.models import *
09:36 from noc.sa.models import *
09:36 alarms = ActiveAlarm.objects.filter(managed_object=ManagedObject.objects.get(name="bb-zmile").id)
09:36 for a in alarms:
09:36 a.clear_alarm("close")
09:36 5 строк

Закроем такие:

# закрыть 'Network | IMPB | Unauthenticated IP-MAC' с IP = '0.0.0.0'
from noc.fm.models import *
ac = AlarmClass.objects.get(name='Network | IMPB | Unauthenticated IP-MAC')
alarms = ActiveAlarm.objects.filter(alarm_class=ac)
for a in alarms:
    if a['vars']['ip']=='0.0.0.0':
        a.clear_alarm("close")

Решил поиграться с графитом. Цель была- понять, можно ли механизмом активаторов собирать и слать данные во внешнюю систему в больших объемах. Все выродилось в два pyRule:

  1. IReduceTask, именно он собранные данные шлет наружу - в графит по простому текстовому протоколу. Мне кажется, проблема моей ДНК где-то в этом коде. Именно отсюда растут тормоза.
# -*- coding: utf-8 -*-
##----------------------------------------------------------------------
## ResultReport
##----------------------------------------------------------------------
## INTERFACE: IReduceTask
##----------------------------------------------------------------------
## DESCRIPTION:
## Display reduce task result
##----------------------------------------------------------------------
## Copyright (C) 2007-2010 The NOC Project
## See LICENSE for details
##----------------------------------------------------------------------
import pprint
##
## 
##
@pyrule
def result_report_graphite(task):
    out=""
    import time
    from noc.lib.nbsocket import *
    sf=SocketFactory()
    gp=sf.connect_tcp('graphite.host',2003,ConnectedTCPSocket)
    sf.loop()
    gp.create_socket()
    sf.loop()
    gp.handle_connect()
    sf.loop()
    for mt in task.maptask_set.all():
      if mt.script_result:
        for k,v in mt.script_result:
            t=int(time.time())
            out+="crc.%s.%s %s %s\n"%(mt.managed_object.address, k.split(".")[-1],v,t)
            gp.write("crc.%s.%s %s %s\n"%(mt.managed_object.address, k.split(".")[-1],v,t))
            sf.loop()
  #          for i in xrange(10):
   #          sf.loop()
            #gp.close(True)
#    out+=["<TR><TD>%s</TD><TD>%s</TD><TD><PRE>%s</PRE></TD></TR>"%(mt.managed_object.name,mt.status,pprint.pformat(mt.script_result))\
#        for mt in task.maptask_set.all()]
 #   out+=["</tbody>","</table>"]
    sf.shutdown()
    return out

2. IPeriodicTask - собственно, пируль, который запускает сбор данных. Просто для всех железяк опрашивается ноковским getnext ветка CRCErrors

# -*- coding: utf-8 -*-
from noc.sa.models import *
from noc.inv.models import *
from noc.ip.models import *
from noc.main.models import SystemNotification
import string


@pyrule
def SendCRCtoGraphite(timeout= None):
 #mo=managed object to get counters from
 mo=ManagedObjectSelector.objects.get(name='All-DLINK')
 t=ReduceTask.create_task(mo,"pyrule:result_report_graphite","","get_snmp_getnext",{'oid':'1.3.6.1.2.1.16.1.1.1.8'},900)
 z=t.get_result()
 SystemNotification.notify(name="sa.version_inventory", subject="Managed Object discovered", body="Discovered %s"%z)
 return True

Для одного хоста укладывался секунд в 10 вместе с отправкой всех данных в графит. Для двух тысяч наступает понимание - что-то тут не так... Если будут сочувствующие, умные и вообще неплохие люди - могу поэкспериментировать(до среды или с 5 октября).

Да, код-ревью устраивать не надо,это черновик.

Записки новичка: грохнуть порты
./noc shell
# delete mo interface 
from noc.inv.models import *
mo = ManagedObject.objects.get(name='MO')
for i in Interface.objects.filter(managed_object=mo.id ): #, type='physical' , type='SVI' , name='1:9'
    print i.name, i.type, i.managed_object
    i.delete()
    i.save()
quit()

Революция, о необходимости которой так долго гооврили большевики, таки свершилась: NOC, как и подобает любой нормальной OSS, обзавелся своей адресной базой. Роль адресной базы часто ошибочно недооценивают, но тем не менее она является одним из краеугольных камней в фундаменте OSS. Без качественной адресной базы крайне сложно нормально организовать процессы капитального строительства и обслуживания массового сегмента (PSTN, ШПД, ЦТВ, КТВ и др), то есть почти все услуги фиксированной связи, оказываемые в жесткой привязке к конкретному адресу.

Структура и идеология адресной базы возникла не на ровном месте, а как обобщение многолетнего опыта работы в сфере OSS и анализе достоинств и недостатков различных подходов, применяющихся в различных компаниях.На мой взгляд, структура получилась с одной стороны простой до идиотизма, с другой - достаточно гибкой для применения во многих областях. Итак, вкратце об основной концепции базы.

 

База описывает четыре основных сущности:

  • Дом, как объект недвижимости. Дома представляют для нас особый интерес в первую очередь тем, что в них имеют привычку селиться люди. Некоторые из этих замечательных людей являются нашими клиентами и платят за услуги фиксированной связи. Другие, не менее замечательные – спят и видят, как мы их подключим и сделаем своими клиентами, устранив тем самым вселенскую несправедливость. В любом случае, при работе на массовом сегменте, подключение разбивается на два независимых этапа – подключение собственно дома (капитальное строительство, создание технической возможности), и подключение жильца или конторы в доме. Чем больше мы знаем про подключенные дома, тем лучше. Чем больше мы знаем про дома, которые можно подключить - тем еще лучше.
  • Если сильно упрощать, дома имеют привычку сбиваться в кучки и организовываться в различные образования. Совсем маленькие кучки становятся хуторами, чуть более крупные - деревнями и селами, если домов сбежалось слишком много, то появляются поселки и города. Когда домов в городе становится слишком много, их начинают разбивать на кучки помельче и возникают городские районы. Когда город разрастается до неприличных размеров, то районы начинают группировать в более крупные формации, вроде городских округов. На более крупном уровне возникают группировки в области и федеральные округа, а еще выше – в страны. Возникает строго иерархическая система деления - от страны к федеральным округам, далее - к областям, потом идут районы, сельсоветы и многочисленные деревни Гадюкино. Так как подобное разбиение обусловленно, в основном, административными причинами и осуществляется по принципу территорий, его называют Административно-территориальное деление, а его элементы - административно-территориальным образованием (АТО). ATO является не единственным делением, параллельно могут существовать деления по участкам обслуживания, финансовой привлекательности, и другим причинам. Общее у них то, что на самом нижнем уровне идет Дом
  • Далее возникает вопрос, как отличать один дом от другого. Когда домов в деревне с десяток, и различать нечего, в общем: вот тот на краю - деда Пахома, в соседнем Прасковьи Петровны, далее, с покосившейся крышей, Марфы Захаровной, которая, дура, в позапрошлом году в город уехала. С ростом деревни, Санта-Барбара начинает крепчать и пухнуть на глазах, появляются дома на выселках за Новонедоделовкой, которая через речку от Старододелова, где в позапрошлом году два порося в луже утопилось, пока Марфа, дура, в городе гужевалась, а при царе Горохе мельница была. В общем, мозг от насилия нужно как-то спасать и от местного колорита избавляться. Решили, ту тропинку, по которой дед Пахом на дрожке за дровами через всю деревню в лес чешет, а барин Игнат Савельевич на черном крузаке распахивает, и где по весне лужи по колено, в которых два порося потонуло, торжественно назвать улицей, а лучше прешпектом. А прешпект, соответсвенно, назвать Поросячим. И закипело дело: дом деда Пахома стал первым домом по Поросячему прешпекту, Петровны - вторым, а лучше сразу третьим, Марфа - хоть и дура, пятая по счету и все это в селе Старододелове, что в Моршанском уезде Тамбовской губернии. Повесил дед Пахом табличку 1 на дом, глянул на двор и закручинился: на сортир табличку уже и не повесишь, отдельное строение. Почесал дед Пахом лысину и решил - быть сортиру домом 1 строением 1. А пристройке летней у дома быть корпусом 2. Увидел, что это хорошо, а значит так тому и быть. Испытал нужду большую и пошел по адресу Поросячий п-т, д 1 к 1, там его облегчение и настигло.
    А мы в результате получили две новые проблемы – улицы и адреса, как номера домов по улицам. Относятся ли улицы к АТО? На самом деле и да и нет. С одной стороны, улица может проходить через несколько районов и при этом иметь сквозную нумерацию домов. С другой – обычно улица все-таки связана с населенным пунктом. Как связаны дома с улицами: с одной стороны - дом имеет какой-то номер по улице. С другой – если вокруг дома деда Пахома кабаны тропы протоптали, а бары колею раскатали, то быть ему угловым сразу на всех четырех улицах и везде под разными номерами. Таким образом - у дома может быть несколько адресов. Адреса также имеют склонность к ветренности и имеют привычку внезапно меняться, доводя ситуацию до полного абсурда, как описано по ссылке.

 

Таким образом, если мы хотим устранить цифровое неравенство в сортире деда Пахома, наша адресная база должна работать с четыремя основными элементами: Дом (сортир), административным деленим (Тамбовская губерния, Моршанский уезд, село Старододелово, в котором расположен сортир), улицей (Поросячий п-т) и адресом (д. 1. к. 1 по Поросячему прешпекту).

А как жить с этим богатством, мы обсудим в следующей статье

Отчет по топологии своими рукам

Понадобилось тут проверить, а что за железки выпали из дерева топологии. Естественно, у задачи есть минимум два решения. А был бы ерл вместо питона - было бы по пять вариантов написания каждого из двух решений и одно решение в виде ASCII-art. Ну да ладно. Не о том речь. Речь о том, до чего именно я докатился в своем падении.

Сейчас можно сделать отчет тремя путями:

  1. сгенерировать скелет отчета с помощью механизма newapp у консольного приложения noc.
  2. скопировать любой понравившийся отчет в новый каталоги, удалить лишнее, исправить код
  3. взять карту местности, на которой отмечен крестиком ближайший лес... Ой, то есть сделать типовое приложение Ext на базе нужного класса с нуля.

 

Я не стал выпендриваться с приложением с нуля. Лазить по коду и вспоминать ключи(Дима, привет, хорошо что в хелпе консольных команд есть пример использования, там же все очевидно) командной строки.. Я тупо скопировал отчет, подрихтовал его под себя и все у меня теперь работает.

Решения:

  1. Взять базу объектов, базу линков, ящик водки... Заправить базовым алгоритмом A*, ну или чем-то похожим... Если бы меня приперло, я бы и такое родил. Но я так делать не стал
  2. Установить пакет networkx. Версию не ниже 1.7, использовать написанный умными людьми код.

 

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

  • железки нет в графе(она однозначно выпала из дерева топологии
  • железка есть в графе, но нет пути, который бы соединял железку с центром сети.

Код отчета прилагаю ниже, там все относительно просто(файл views.py):

# -*- coding: utf-8 -*-
##----------------------------------------------------------------------
## inv.reportobjectsummary
##----------------------------------------------------------------------
## Copyright (C) 2007-2013 The NOC Project
## See LICENSE for details
##----------------------------------------------------------------------
## Python modules
from collections import defaultdict
import networkx as nx
## NOC modules
from noc.lib.app.simplereport import SimpleReport, TableColumn
from noc.inv.models import *
from noc.sa.models import *
class ReportTopoFailsApplication(SimpleReport):
    title = "Objects not in Topology tree"
    def gen_mgraph(self):
        G = nx.MultiGraph()
        for l in Link.objects.all():
            G.add_edge(l.interfaces[0].managed_object.id,l.interfaces[1].managed_object.id)
        return G
    def get_data(self, **kwargs):
        data = defaultdict(int)
        #Generate graph to search orphans
        G = self.gen_mgraph()
        for mo in ManagedObject.objects.all():
            if not mo.id in G.nodes():
                data[mo]=0
            else:
                if not nx.has_path(G,mo.id,664):
                    for nei in nx.all_neighbors(G,mo.id):
                        data[mo]+=1
        data = sorted(((k, data[k]) for k in data), key=lambda x: -x[1])
        return self.from_dataset(
            title=self.title,
            columns=[
                "Object",
                TableColumn("Neighbors Count", format="numeric", align="right")
            ],
            data=data
        )

Изменение порта web сервера NOC

Приветствую,

NOC Version 0.8dev9319 OS Ubuntu 12.4 Server

 

по умолчанию NOC работает на 80 порту, который потребовалось изменить

заходим в cd /etc/nginx/sites-enabled

Редактируем файл noc.conf

sudo nano noc.conf

 

Изменяем в разделе server в строке порт через который будем ходить --> listen 8383;

 

После этого рестрат noc и nginx

 

 

my "Hello world"

c "Божьей"  помощью, вот что получилось. Я очень очень буду рада комментариям, но убедительно прошу не сильно пинать ногами, т.к. мой опыт программирования равен 1 неделе, опыт программирования на python и того меньше.  Первая попытка, т.к. цель не достигнута код буду очень сильно модифицировать и часто. 

 

1
intname = "fastethernet0/45"
aclname = "BLOCK-" + intname
blockstring = "deny any any"
aclseek = False
import re
f = open('/srv/noc/repo/config/cisco1845conf', 'r')
m = re.search(r'(?P<int>interface (?P<intname>' + intname + ')(.+?)\!)', f.read(), re.IGNORECASE | re.DOTALL)
if m:
    interfaceconf = m.group('int')
    intname = m.group('intname')
    m = re.search(r'.+access-group (?P<aclname>.+) .+', interfaceconf)
    if m:
        aclname = m.group('aclname')
        aclseek = True
cmd = ""
acl = []
acl.append(blockstring)
if aclseek:
    f.seek(0)
    while True:
        line = f.readline()
        if not line: break
        m = re.search(r'access-list ' + aclname + ' (?P<acl>.+)', line)
        if m:
            cmd = "interface " + intname + "\n no access-group " + aclname + " in\n!\n"
            cmd = cmd + "no access-list " + aclname + "\n"
            acl.append(m.group('acl'))
f.close()
for line in acl:
    cmd = cmd + "access-list " + aclname + " " + line + "\n"
cmd = cmd + "!\ninterface " + intname + "\n" \
                                        " access-group " + aclname + " in\n"
print(cmd)
Записки новичка: Pyrule

По мотивам крайне полезного пируля, чуть его доработал:

# -*- coding: utf-8 -*-
##----------------------------------------------------------------------
## set interfaces profile
##----------------------------------------------------------------------
## INTERFACE: IPeriodicTask
##----------------------------------------------------------------------
## DESCRIPTION:
## set NNI or UNI inventory\interfaces profile
##----------------------------------------------------------------------
## Copyright (C) 2007-2014 The NOC Project
## See LICENSE for details
##----------------------------------------------------------------------
## http://kb.nocproject.org/pages/viewpage.action?pageId=11108392
import logging
from noc.inv.models import *
@pyrule
def set_interface_profile(timeout = None):
    current_profile_name = "default"
    new_profile_name = "default"
    interface = Interface.objects.filter(type="physical")
    for i in interface:
        current_profile_name = i.profile.name
        new_profile_name = current_profile_name
        untagged_vlan = SubInterface.objects.get(interface=i).untagged_vlan
        if i.link:
            new_profile_name = "NNI"
        else:
           if not i.description:
               new_profile_name = "default"
           elif i.profile.name == InterfaceProfile.objects.get(name="default").name:
               new_profile_name = "UNI"
        if untagged_vlan == 1:
               new_profile_name = "NNI" 
        if new_profile_name != current_profile_name:
            i.profile = InterfaceProfile.objects.get(name=new_profile_name).id
            i.save()
            logging.info("[pyrule.set_interfaces_profile] set interface.profile %s '%s'->'%s'" % (i, current_profile_name, new_profile_name))
    return True 

 

В лог noc-scheduler.log попадают записи об изменении профиля порта, примерно так

2014-03-07 09:13:57,080 [pyrule.set_interfaces_profile] set interface.profile s558.intt: 1:9 'default'->'NNI'