Blog

Решил поиграться с графитом. Цель была- понять, можно ли механизмом активаторов собирать и слать данные во внешнюю систему в больших объемах. Все выродилось в два 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'

 

 

 

Связь NOC с Zabbix на уровне БД.
Представляю Вашему вниманию вариант организации связи NOC с Zabbix на уровне триггеров базы данных.
 
Версия PostgreSQL от 9.1
База Zabbix может быть как PostgreSQL так и MySQL.
Связь реализована на основе Foreign Data Wrappers (FDW) http://wiki.postgresql.org/wiki/Foreign_data_wrappers
 
Версия Zabbix 2.2.0, но это не принципиально.
Необходимо изменить только SQL с созданием внешних таблиц соответственно вашей версии Zabbix,
а в коде pgsql используются только id и переменные ROWTYPE.
 
Версия NOC значения не имеет, поскольку используются всего две таблицы:
- sa_managedobject поля NAME, ADDRESS
- sa_managedobjectattribute поля key (с содержимым 'platform'), value.
 
Порядок действий:
-----------------
Zabbix:
-----------------
1. Импортировать шаблоны. Хотя на самом деле можно использовать любые
только нужно заполнить таблицу соответствия в NOC (см. ниже).
2. Изменить тип индекса на уникальный для имени хоста ( runme_zabbix.sql ).
 

NOC:
-----------------
1. Пользователь noc должен быть суперюзером на время исполнения runme_noc.sql
Потом можно лишить его этой привилегии.
2. Перед тем как исполнить runme_noc.sql почитайте комментарии в скриптах.

В итоге при добавлении, изменении или удалении хоста в NOC
соответствующие изменения автоматически произойдут и в заббиксе.
Триггерная функция написана так, что при возникновении ошибки
транзакция в NOC безусловно произойдёт.

 

TAR c SQL скриптами и шаблонами для Zabbix доступен  http://yadi.sk/d/hhrJv1T6E5vGd

 

 

 

Сейчас пока нет морды для вот такой простой вещи

from noc.sa.models import ManagedObject
from lib.scheduler import utils
dev=ManagedObject.objects.filter(name__icontains='server')
for i in dev:
    utils.refresh_schedule("inv.discovery", "config_discovery", i.id)

этот кусочек позволяет пересчитать когда будет запущено задание.

Фактически переводить сбор на NOW.

в 4-ой строке можно задать какие именно железяки будут затронуты

все варианты аргументов.

Choices are: activator, address, address_set, administrative_domain, config, config_diff_filter_rule, config_filter_rule, config_validation_rule, description, id, is_configuration_managed, is_managed, managedobjectattribute, maptask, max_scripts, name, object_profile, password, port, profile_name, remote_path, repo_path, scheme, shape, snmp_ro, snmp_rw, super_password, tags, trap_community, trap_source_ip, user, vc_domain, vrf

использовать так

dev=ManagedObject.objects.filter(profile_name='Cisco.IOS')
Branched development

NOC's development had followed single-branch development model from a very start. It was single default branch, result of streamlined path of development. Releases were just a tags - simple milestones on the road, a points of relative stability. Simplicity always has own benefits:

  • Easy to understood
  • Easy to commit: Just test you changes and push to the repos
  • Easy to choose: Wait on stable milestone or dance over bleeding edge of tip

But sometimes, simplicity is worse than theft. Drawback of simple approach are obvious:

  • No hotfix releases. In order to fix critical bug you must leave the stable milestone
  • Feature development blocks release. You must freeze the development of new features when going into release process. Long-term feature development blocks release for a long time.
  • Hard to test single feature, they are all bundled in single branch
  • Expirements are expensive, it is hard to clean up results of unsuccessive experiment from main branch
  • Hard to share early work and demonstrate incomplete features
  • Hard to split development and Q&A

DCVSes offer the branches as the method of splitting development into semi-independed streams. Mercurial, used by NOC, has all necessary tools. But branching itself is not enough. Without strict policy branching will lead to greater mess, so we need a really good one.

Vincent Driessen's model is good starting point (See "A successful Git branching model"). The common idea is to define possible branch categories and define a policy of possible merging. Model define following branch types:

  • default - stable release branch. Releases denoted by tags. Direct commit into default branch are strictly prohibited
  • develop - contains tested features and bug fixes. develop is relatively stable and used as the base to start the feature branches. All changes from default must be merged into develop too. Although simple patches can be commited directly in develop, greater changes must be developed and tested in the dedicated feature/* branches.
  • release/* - when develop collects worth amount of features, the release process begun. First, separate release/* branch forked from develop one, and Q&A process begins. No new features may be commited into open release/* branch. When release/* branch is well-tested and polished, it is marked by release tag, merged into default and develop, then release/* branch closed
  • hotfix/* - Can be used to fix bugs leaked to release. hotfix/* branch forked from the point of release on default branch, after bug has been fixed hotfix got release tag and all changes return back to default and develop, then hotfix/* branch closed
  • feature/* - forked from develop. Most of development work done in feature/* branches, single branch per feature. Stable changes merged back to develop

Comparison table:

BranchNumberForked fromDirect commitsMerged toClosed
defaultone

-

NeverdevelopNever
developone-Small fixes and featuresfeature/*Never
release/*one per releasedevelopLast-minute fixes, release notes

default

develop

On release
hotfix/*one per hotfix releasedefaultPost-release fixes

default

develop

On release
feature/*one per featuredevelopNew features, improvements, large fixesdevelopOn feature complete

Upgrade process

Choose appropriative branch to follow:

  1. default - to receive stable releases and hot fixes for production use
  2. develop - to access latest tested features for early access testing
  3. feature/<name> - to get access to particular feature in development

Change etc/upgrade.conf and set BRANCH variable to appropriative branch. i. e.

BRANCH=feature/extjs-4.2.1

Then upgrade as usually

/opt/noc# ./scripts/upgrade

Developer's tools

hgflow extension used to maintain branch workflow.

To install extension grab latest hgflow release from download section and unpack distribution. Then enable hgflow in ~/.hgrc

[extensions]
....
flow = <path to hgflow.py>
 
[flow]
autoshelve = true

 

hg flow subcommand provides access to the extensions.

Common tasks:

Check, which is the name of active branch:

 
/opt/noc$ hg branch
develop

Get list of open feature branches 

/opt/noc$ hg flow feature list
flow: Open <feature> branches:
flow:   feature/NOC-1036
flow:   feature/OAM
flow:   feature/extjs-4.2.1
flow:   feature/fm-new-ui
flow:   feature/ip.show_maintained
flow:   feature/notification-center
flow:   feature/ranged-chassis-id

Switch to develop

/opt/noc$ hg flow develop

 

Start new feature branch 

/opt/noc$ hg flow develop
/opt/noc$ hg flow feature start myfeature

 

Switch to another feature branch

/opt/noc$ hg flow feature other feature

Push feature branch changes to repo

/opt/noc$ hg flow feature push
Массовое изменение параметров МО

Появилась задача - изменить/добавить параметры RO Community в настройках объекта.

За основу был взят пост Небольшие сниппеты, которые могут пригодиться

проконсультировавшись с автором получил следующее

from noc.sa.models import *
var = ManagedObjectSelector.objects.get(name='SL')
for mo in var.managed_objects:
    mo.snmp_ro = "public"
    mo.save()

 

Сначала выбираем все MO из селектора, в данном случае SL

Затем проставляем нужный нам комьюнити для snmp и сохраняем результат.

Все параметры которые можно изменять, и их называние, можно посмотреть в /opt/noc/sa/models/managedobject.py

 


Не претендуя на оригинальность... 

Вчера, абсолютно неожиданно и без объявления войны, перестала работать база mongo. И как следствие перестал работать noc. 

Почитав форумы и следуя разным инструкциям по ремонту баз - так не достиг желаемого. Возможно, имея больше времени, базу удалось бы поднять, но времени как раз и не было. Поступил проще - удалил все файлы из /var/lib/mongodb/. Затем запустил 

#./scripts/post-update.

Только после этого запустился noc. За ночь discovery вытянул все интерфейсы и нашел все линки. Можно было успокоиться, но в Network Map пришлось бы расставлять все узлы по новой. А это долго и откровенно лень. Тем более, что картой сети могло дело и не ограничиться. 

Помня что имеется механизм резервного копирования, решил этим воспользоваться. 

<mikevlz> mongorestore читал?
<mikevlz> читай, запускай. Оно принимает путь к каталогу из него может восстановить базу

 

Собственно при помощи mongorestore будем восстанавливать данные 

если коротко, то имеем такие опции

--help — вызов справки
-v [ --verbose ] – выводить больше информации (например, для большей детализации необходимо использовать -vvvvv)
-h [ --host ] – хост mongo для соединения («left,right», если используется replica+sets)
--port – порт сервера, также возможно использовать –host hostname:port
-d [ --db ] – указать базу данных
-c [ --collection ] – указать используемые таблицы
-u [ --username ] – имя пользователя
-p [ --password ] – пароль
--ipv6 – включить поддержку IPv6 (по умолчанию отключена)
--dbpath – прямой доступ к файлам базы mongod по указанному пути, вместо конекта к серверу mongod. Необходима блокировка директории с данными, поэтому не может быть использована если монго-сервер использует базу по указнному пути
--directoryperdb – если задан аргумент dbpath, то каждая БД находится в отдельной директории
--objcheck - проверить обьект на валидность перед вставкой
--filter — использовать фильтр перед вставкой
--drop — удалить таблицу перед импортом
--indexesLast — подождать для добавления индекса (происходит быстрее, если данные вставляются не по порядку индекса)

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

backup # mongorestore --verbose --db noc /var/backup/noc

 /var/backup/noc - путь к уже распакованным  копиям.

 

 Можно проверять результат. Я еще перезапустил mongodb и noc-launcher, на всякий случай.

Данный восстановились, но так как ночью работал дискавери, то все данные по интерфейсам и линкам продублировались. 

Снова читаем доку по mongorestore

--drop
Modifies the restoration procedure to drop every collection from the target database before restoring the collection from the dumped backup.

backup # mongorestore --drop --verbose --db noc /var/backup/noc

Проверяем.

 

 

 

Fault Management - Оповещение об авариях

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

Создаём нужные шаблоны в Main - Setup - Templates
Например такой:
Name: Ping Failed (Core)
Subject: Потеряна связь с узлом ядра {{alarm.managed_object.name}}
Body: В {{alarm.timestamp}} потеряна связь с улом ядра {{alarm.managed_object.name}}
Или такой:
Name: Alarm Cleared
Subject: Авария на {{alarm.managed_object.name}} устранена
Body: Авария <{{alarm.alarm_class.name}}> закрыта по событию <{{event.event_class.name}}> в {{event.timestamp}}

Создаём группы по интересам в Main - Setup - Notification Groups
Например, группу Admins, в Users добавляем нужных пользователей из NOC-а, в Other опционально - адреса вида телефон@sms.mtslife.ru для рассылки sms

Создаём селектор в Service Activation - Setup - Object Selectors
Например, селектор Core, где выбираем объекты по тегу "Core".

Пишем pyRule в Main - Setup - PyRule для автоматической подписки заинтересованных лиц на аварии.
В данном примере 4 и 5 - id пользователей, которых мы подписываем на уведомления.



##----------------------------------------------------------------------
## subscribe to alarm
##----------------------------------------------------------------------
## INTERFACE: IAlarmTrigger
##----------------------------------------------------------------------
## Copyright (C) 2007-2013 The NOC Project
## See LICENSE for details
##----------------------------------------------------------------------
@pyrule
def subscribe_admins_to_alarm(alarm):
	if not [4,5] in alarm.subscribers:
		alarm.subscribers += [4,5]
		alarm.save()

 

Далее настраиваем тиггеры в Fault Management - Setup - Alarm Triggers
Name: Ping Failed (Core)
Alarm Class RE: Ping Failed$
Time Pattern: Any
Managed Object Selector: Core
Notification Group: Admins
Template: Ping Failed (Core)
pyRule: subscribe_admins_to_alarm

Теперь наверное, самое неправильное: патчим коррелятор. Это нужно для уведомления подписчиков аварии об её закрытии.



diff -r cf00ba8b7614 fm/correlator/daemon.py
--- a/fm/correlator/daemon.py Sat Feb 09 00:50:44 2013 +0400
+++ b/fm/correlator/daemon.py Wed Mar 20 09:44:39 2013 +0700
@@ -28,7 +28,8 @@
 from noc.main.models import PrefixTable, PrefixTablePrefix
 from noc.lib.version import get_version
 from noc.lib.debug import format_frames, get_traceback_frames, error_report
-
+# my test
+from noc.main.models import Notification, Template, UserProfile
	class Correlator(Daemon):
		daemon_name = "noc-correlator"
@@ -257,6 +258,21 @@
			r.u_name, str(e.id), e.event_class.name,
			str(a.id), a.alarm_class.name))
		a.clear_alarm("Cleared by disposition rule '%s'" % r.u_name)
+			#
+			# my test patch - notify subscribers
+			#
+			tpl = Template.objects.get(name="Alarm Cleared")
+			for s in a.subscribers:
+				usr_prof = UserProfile.objects.get(user=s)
+				for ac in usr_prof.active_contacts:
+					Notification(
+						notification_method=ac[0],
+						notification_params=ac[1],
+						subject=tpl.render_subject(alarm=a),
+						body=tpl.render_body(alarm=a,event=e),
+						link=None
+ 					).save()
+			# end my test patch

		def get_delayed_event(self, r, e):
			"""

Как минимум, надо бы этот патч перенести в метод clear_alarm класса ActiveAlarm - тогда будет уведомление не только об авариях, закрытых коррелятором, но и о закрытых пользователем. Но тогда нужно переделывать, т.к. там нет информации о событии, закрывающем аварию.

 

Вообще, как уже было озвучено в IRC, было бы неплохо привязать шаблоны уведомлений к классам аварий, тогда не придётся городить в корреляторе поиск нужного шаблона. Потом, нужно иметь возможность подписывать на аварии целые группы, а не отдельных пользователей. тогда генерация уведомлений ещё немного упрощается и можно будет без особых проблем распихать вызов Notification по всем остальным изменениям состояния аварии (например, найдена новая причина, изменён приоритет или добавлен комментарий пользователя).

Кстати, если будут проблемы с подпиской, посмотрите  NOC-961 - Getting issue details... STATUS  

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

Шел мужик по пустыне. Давно уже шел, устал, еле полз, можно сказать. Хотел очень сильно пить, немножко хотел есть. Пить хотел сильнее... И тут он натыкается на древнюю бутылку... Открывает, а оттуда вылазит джинн.

  • Говори, что хочешь - сказал джинн.
  • Домой хочу - сказал мужик.
  • Пошли - ответил джинн.
  • Я быстро хочу - возразил мужик
  • Тогда побежали - усмехнулся джинн...

К чему все это? Ах да, ускорить NOC. Если внимательно посмотреть гугл, то можно найти джанго-документацию. Там нам расскажут, что Django, в которой сделано много чего из NOC, поддерживает кеширование данных. Мидлварь джанги умеет класть в кеш сгенерированные страницы, при новом запросе, если нифига не устарело для тех же параметров - возвращаем из кеша.

В ходе отладки кода, который строит путь, я был в шоке. NetworkX, которая анализирует граф связности и ищет пути в нем, давала результат ну очень долго. Мне так казалось. Firebug говорил, что данные в JSON возвращались сервером через минуту. Коллеги мне сказали, что это дофига. Черт с тобой, золотая рыбка, сказал я и воткнул memcached на 64МБ. Что я получил? Правильно, можно было обновлять алармы и ивенты до посинения, их было строго фиксированное количество. Нет, процессы все работали, а вот вебморда гнала лажу.

Читаем доку дальше, натыкаемся на то, что кешем можно пользоваться в коде. Отлично. К этому моменту код уже обернут профайлером, в котором видна безрадостная картина: куча вызовов функций из яиц pymongo и psycopg2. Не, есть еще совсем уже дикое число вызовов стандартной copy, но это уже из кода доступа к БД дергалось. Суммарно там как раз набегало 60 секунд. В чатиках dvolodin уже привычно объясняет мне, что я наркоман, а кеширование нам не нужно, т.к. монго спасет нас. На робкое возражение, что из памяти кеш быстрее достанет, чем монга и постгрес с диска, Дмитрий говорит, что mmap это не совсем диск, и все не так однозначно. Я же все-таки упорный(или упоротый, кому как удобнее), правлю код, сохраняя граф в кеше на час.Делаю два прохода, на первом данные про путь между двумя узлами отдаются через минуту(да, такой хреновый сервер, все в одном)...Второй запрос, путь между другими узлами сети - 1.5 секунды. Не трогаем ничего больше часа, ура, опять минута на первый запрос, все очень быстро на второй и последующие.

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

    def make_link_graph(self):
        if cache.get('full_graph'):
            return cache.get('full_graph')
        G = nx.Graph()
        for l in Link.objects.all():
            G.add_edge(l.interfaces[0].id,l.interfaces[1].id)
            G.add_edge(l.interfaces[0].id,l.interfaces[0].managed_object.id)
            G.add_edge(l.interfaces[1].id,l.interfaces[1].managed_object.id)
        cache.set('full_graph',G,3600)
        return G

Да, в чатике прозвучала мысль от Дмитрия, что целесообразно в случае поиска путей бить сеть на сегменты и раздельно искать пути внутри доменов VLAN и внутри ядра. Это получается по графу на каждый домен + граф на ядро. Да, есть в этом смысл, если ограничиваться только поиском путей. Меньше железок надо дернуть в граф, меньше интерфейсов в него закинуть, меньше запросов, быстрее выполнится. А если надо коррелировать  аларм со связностью? Или выполнить задачу по пути(ребутнуть последовательно железки для обновления софта или еще что-нить)? Тут тоже вроде можно обойтись доменами...

В общем, повод для дискуссии есть, комменты открыты...

Для сравнения - профайлинг без кеша(или при устаревшем кеше) и с кешем:

noc# python stat_prof.py link-trace-view-20130319T095657.prof
         3135074 function calls (2960180 primitive calls) in 56.285 seconds

   Ordered by: internal time, call count
   List reduced from 388 to 20 due to restriction <20>

   ncalls  tottime  percall  cumtime  percall filename:lineno(function)
344255/202132    5.590    0.000   11.309    0.000 /usr/local/lib/python2.7/copy.py:145(deepcopy)
     7306    4.760    0.001    4.760    0.001 build/bdist.freebsd-8.2-RELEASE-p9-amd64/egg/pymongo/mongo_client.py:858(__receive_data_on_socket)
     3625    3.597    0.001    4.211    0.001 /usr/local/noc/contrib/lib/django/db/backends/postgresql_psycopg2/base.py:50(execute)
     7250    3.311    0.000    3.981    0.001 /usr/local/noc/contrib/lib/django/db/models/sql/compiler.py:832(<lambda>)
   290054    2.030    0.000    2.030    0.000 /usr/local/lib/python2.7/copy.py:267(_keep_alive)
     3625    1.591    0.000    3.281    0.001 /usr/local/noc/contrib/lib/django/db/models/sql/compiler.py:248(get_default_columns)
   109351    1.381    0.000    3.031    0.000 /usr/local/noc/contrib/lib/mongoengine-0.6.20-py2.7.egg/mongoengine/base.py:823(__setattr__)
5435/5434    1.265    0.000    5.170    0.001 /usr/local/noc/contrib/lib/mongoengine-0.6.20-py2.7.egg/mongoengine/base.py:787(__init__)
10812/3604    1.214    0.000    5.724    0.002 build/bdist.freebsd-8.2-RELEASE-p9-amd64/egg/pymongo/cursor.py:843(__deepcopy)
   112135    1.015    0.000    1.015    0.000 /usr/local/noc/contrib/lib/django/db/backends/postgresql_psycopg2/operations.py:69(quote_name)
5435/5434    0.873    0.000    6.743    0.001 /usr/local/noc/contrib/lib/mongoengine-0.6.20-py2.7.egg/mongoengine/base.py:938(_from_son)
   169930    0.814    0.000    0.814    0.000 /usr/local/lib/python2.7/copy.py:198(_deepcopy_atomic)
    77462    0.813    0.000    1.439    0.000 /usr/local/noc/contrib/lib/mongoengine-0.6.20-py2.7.egg/mongoengine/base.py:197(__set__)
    84012    0.755    0.000    0.755    0.000 /usr/local/noc/contrib/lib/mongoengine-0.6.20-py2.7.egg/mongoengine/base.py:988(_mark_as_changed)
   115775    0.710    0.000    0.800    0.000 /usr/local/noc/contrib/lib/django/db/models/sql/compiler.py:37(quote_name_unless_alias)
     3604    0.655    0.000    7.304    0.002 build/bdist.freebsd-8.2-RELEASE-p9-amd64/egg/pymongo/cursor.py:175(__clone)
    90100    0.622    0.000    0.622    0.000 build/bdist.freebsd-8.2-RELEASE-p9-amd64/egg/pymongo/cursor.py:185(<genexpr>)
25478/14538    0.614    0.000    3.736    0.000 /usr/local/lib/python2.7/copy.py:234(_deepcopy_tuple)
   104870    0.614    0.000    0.614    0.000 <string>:8(__new__)
     7285    0.554    0.000    8.026    0.001 /usr/local/noc/contrib/lib/django/db/models/sql/query.py:235(clone)

noc# python stat_prof.py link-trace-view-20130319T100928.prof
         94353 function calls (91608 primitive calls) in 1.744 seconds

   Ordered by: internal time, call count
   List reduced from 296 to 20 due to restriction <20>

   ncalls  tottime  percall  cumtime  percall filename:lineno(function)
        2    0.621    0.310    0.884    0.442 build/bdist.freebsd-8.2-RELEASE-p9-amd64/egg/memcache.py:965(_recv_value)
    43160    0.177    0.000    0.177    0.000 build/bdist.freebsd-8.2-RELEASE-p9-amd64/egg/bson/objectid.py:282(__hash__)
        1    0.135    0.135    0.225    0.225 /usr/local/lib/python2.7/site-packages/networkx/algorithms/shortest_paths/unweighted.py:295(predecessor)
       39    0.069    0.002    0.073    0.002 /usr/local/noc/contrib/lib/django/db/backends/postgresql_psycopg2/base.py:50(execute)
        2    0.056    0.028    0.056    0.028 build/bdist.freebsd-8.2-RELEASE-p9-amd64/egg/memcache.py:1142(recv)
     7174    0.052    0.000    0.052    0.000 /usr/local/lib/python2.7/copy_reg.py:46(_reconstructor)
3536/1268    0.051    0.000    0.109    0.000 /usr/local/lib/python2.7/copy.py:145(deepcopy)
      100    0.040    0.000    0.040    0.000 build/bdist.freebsd-8.2-RELEASE-p9-amd64/egg/pymongo/mongo_client.py:858(__receive_data_on_socket)
     7184    0.037    0.000    0.037    0.000 build/bdist.freebsd-8.2-RELEASE-p9-amd64/egg/bson/objectid.py:229(__setstate__)
        5    0.034    0.007    0.259    0.052 /usr/local/lib/python2.7/site-packages/networkx/algorithms/shortest_paths/generic.py:279(all_shortest_paths)
     3570    0.032    0.000    0.042    0.000 /usr/local/lib/python2.7/site-packages/networkx/classes/graph.py:294(__getitem__)
     1023    0.021    0.000    0.022    0.000 /usr/local/noc/contrib/lib/django/db/models/sql/compiler.py:37(quote_name_unless_alias)
     3139    0.016    0.000    0.016    0.000 /usr/local/lib/python2.7/copy.py:267(_keep_alive)
        2    0.016    0.008    0.901    0.451 build/bdist.freebsd-8.2-RELEASE-p9-amd64/egg/memcache.py:818(_unsafe_get)
        1    0.014    0.014    0.916    0.916 /usr/local/noc/inv/apps/linktrace/views.py:60(make_link_graph)
        5    0.014    0.003    0.062    0.012 /usr/local/noc/inv/apps/linktrace/views.py:38(get_object_shape)
       39    0.014    0.000    0.039    0.001 /usr/local/noc/contrib/lib/django/db/models/sql/compiler.py:248(get_default_columns)
       83    0.013    0.000    0.083    0.001 build/bdist.freebsd-8.2-RELEASE-p9-amd64/egg/pymongo/cursor.py:741(_refresh)
        1    0.012    0.012    1.744    1.744 /usr/local/noc/inv/apps/linktrace/views.py:71(api_find_paths)
       55    0.009    0.000    0.041    0.001 /usr/local/noc/contrib/lib/mongoengine-0.6.20-py2.7.egg/mongoengine/base.py:938(_from_son)

Есть такая работа - VLAN-ы прокидывать

Да, есть такая работа. Вот сейчас у меня база на 2 тысячи линков(ну, если честно, то 1770), больше половины из которых собрано автоматически(lldp - вещь!). Чуть меньше половины - мне понадобилось только подтвердить эти линки, их нашла написанная мной периодика, которая сравнивает VLAN-ны на интерфейсах. Совсем чуть-чуть - это роутеры на IronWare с протоколом FDP(тож сам дописал). На всей этой куче линков есть куча вланов. К управлению вланами я еще вернусь, пока дела земные надо утрясти, т.к. управление вланами неполноценно без автопроброски.

Я тут уже на досуге игрался с замечательной библиотекой NetworkX. Скажу прямо - отличная библиотека, только версия нужна посвежее, не ниже 1.7. Я уже нарисовал техническое приложение, которое выводит все простые пути между двумя железками. Только выводит оно в виде массива. Вот сижу теперь и думаю, а как бы организовать выбор собственно маршрута?

Объясняю в чем сложность. Нельзя просто так взять и показать маршрут. Во-первых, для нормального построения топологии надо выгрести из базы линки, создать граф и начать в него напихивать в качестве узлов ИНТЕРФЕЙСЫ железок. Во-вторых, чтоб топология была полной, надо засунуть в граф железки, к которым привязываются интерфейсы. В итоге, получается, что путь кроме естественных хопов в виде железок будет иметь еще и искусственные железки в виде интерфейсов, но мне это нравится, т.к. для проброса вланов недостаточно знать, через какую железку его гонят, гораздо важнее знать, через какой интерфейс он пойдет. Соответственно, нужна подсказка, как отобразить в виде опций для выбора путь с описанием интерфейсов? Желательно с учетом того, что путей может быть не один десяток, длина пути(даже если убрать входящий интерфейс и ManagedObject из описания пути) может быть очень длинным.

Сам я думал в сторону "мастера", уточняющего маршрут. Например, между двумя узлами до 4 маршрутов. Это количество обусловлено тем, что карта физики примерно такая(ASCII-art, да) O1=O2-O3-O4=O5. На прокладке влана между O1 и О5 все богатство выбора сведется выбору порта в начале пути и в конце. А может быть топология, где маршрут может быть определен уже первым же портом. Хотя может быть и так, что придется отщелкать два десятка железок и останется еще 30 вариантов маршрута. Есть еще вариант графического отображения, но это еще бОльший ужас, как по мне. Для начала надо освоить возможность авторасстановки узлов у mxGraph, потом этой все расставить и показать разные маршруты разным цветом. А если маршруты должны пройти по одному единственному каналу между городами(не надо говорить, что это плохо, все-равно будут такие места) - каким цветом рисовать? Вобщем, я в тоске-печали. Да, может быть строить маршрут надо не простыми путями(которые на самом деле ВСЕ возможные пути), а использовать all_shortest_paths(shortest_path для тех, кто feeling lucky ), но и там, судя по описанию, все уныло - тот же генератор маршрутов(всего один маршрут, самый короткий), надо выбирать и не факт, что этот путь оптимален. Пропишем кому-нить влан корпората с потреблением под 5 Гбит/с через дохлый радиомост(резерв из прошлого на 150Мбит), будет весело, да.

Жду мыслей в комментах, тем, кто хочет идти в ногу - рекомендую проверить, чтоб set_switchport поддерживался для вашего железа. Без него будет плохо.

sample-network вот для такой сети мы получаем примерно такой вывод путей:

Path: [ [u'fi4-2-42k1000', u'fi4-2-42k1000: 1/1', u'fi4-42k1000: 1/8', u'fi4-42k1000', u'fi4-42k1000: 1/4', u'bi8-akras: 5/7', u'bi8-akras', u'bi8-akras: 2/16', u'rx8-akras: 1/3', u'rx8-akras', u'rx8-akras: 1/2', u'bi8-dia: 1/1', u'bi8-dia'] ]

И это не самый длинный. А есть пути и подлиннее(схема-то неполная)