Child pages
  • Продвинутое использование питона в сниппетах

Versions Compared

Key

  • This line was added.
  • This line was removed.
  • Formatting was changed.
Comment: Migrated to Confluence 5.3

  В момент появления поддержки тега {% pyhotn %}, тут была блогозапись как этим пользоваться, но очень слабый пример, только поверхностно затронута мощь этого инструмента. На сколько я понимаю определение сниппета, это механизм позволяющий с помощью шаблона выполнять автоматически однообразные действия. Для меня это настройка клиентских портов под интернет, очень много однотипных действий, поэтому взялся написать сниппет это автоматизирующий. Собственно хочу его представить

Code Block
linenumberstrue
{% load python %}
{% var cmd internal %}
{% var Client str %}
{% var po str %}
{% var id str %}
{% var speed str %}
{% python %}
from noc.inv.models import *
from noc.sa.models import *
from noc.ip.models import *
from noc.lib.ip import IP
from noc.lib.text import split_alnum

i = Interface.objects.filter(managed_object=context["object"].id, type="physical")
free_interface=""
for interface in sorted(i, key=lambda x: split_alnum(x.name)):
    if interface.description==None or interface.description.split(" ")[0]=="was":
        free_interface=interface.name
        iface=interface
        break

if free_interface=="":
    raise Exception('No free interface found')

p = Prefix.objects.filter(vrf=12, Prefix_group="Clients-p2p", afi=4)
free = []
for pref in p:
    for free_preffix in pref.iter_free():
        free += {free_preffix}  
    try:
        if free[0]:
            break
    except:
        pass

ip_prefix = str(free[0]).split('/')[0].split('.')
ip_gw = str(ip_prefix[0]) + "." + str(ip_prefix[1]) + "." + str(ip_prefix[2]) + "." + str(int(ip_prefix[3])+1)
new_prefix=str(free[0]).split('/')[0]+"/30"
domain_name=context["object"].name + "-" + "-".join("-".join(free_interface.split("/")).split(" ")) + ".a.ru"
iface.description=context["Client"]

iface.profile=InterfaceProfile.objects.get(name="UNI").id 
Prefix(prefix=new_prefix, vrf=VRF.objects.get(id=12), description=context["Client"], Project=int(context["po"]), Channel_ID=int(context["id"])).save()
Address(address=ip_gw, fqdn=domain_name, vrf=VRF.objects.get(id=12)).save()
iface.save()

context["cmd"]="configure terminal\n"
context["cmd"]+="interface " + free_interface + "\n"
context["cmd"]+=" description " + context["Client"] + " po-" + context["po"] + " id-" + context["id"] + "\n"
context["cmd"]+=" no switchport\n no shutdown\n"
context["cmd"]+=" ip access-group fromclient in\n"
context["cmd"]+=" ip address " + ip_gw + " 255.255.255.252\n"
context["cmd"]+=" no snmp trap link-status\n"
if not " " in context["speed"]:
    context["cmd"]+=" service-policy input " + context["speed"] + "-in\n"
    context["cmd"]+=" service-policy output " + context["speed"] + "-out\n"
context["cmd"]+="end\n"
context["cmd"]+="write\n"
context["cmd"]+="show running-config interface " + free_interface
{% endpython %}
{{cmd}}

Получилось полсотни строк.

Некоторые пояснения.

14 - 20 строки ищем свободный интерфейс. то есть сначала загружаем в i все интерфейсы у выбранного МО, потом идем в цикле в поисках первого где  дескрипшен будет пустой или начинаться со слова "was" (у нас это значит что какой-то клиент раньше был на этом порту но уже отключился), как только нашли интерфейс, то записываем его и прекращаем это безобразие.

25 - 34 - задачка посложнее была, но тоже решилось, здесь мы ищем свободный префикс /30. У меня все большие сети откуда выдаются клиентам адреса помещены в группу Clients-p2p (это мой custom field), хотел фильтровать по тегу, но что-то не заработало. а дальше с помощью iter_free ищем что у нас не занято. Работает несколько неоптимально, можно было бы выходить из цикла когда нашел первый свободный, а не продолжать выбирать все из префикса, но я не захотел ломать то что уже заработало.

дальше идут некоторые преобразования чтобы получить нужные параметры

ip_prefix - это сам найденый свободный префикс без маски, приведенный к списку октетов (этого наверное не стоило делать, 
да и вообще тут большое поле для оптимизаций)
ip_gw - ip который мы настраиваем на интерфейсе

new_prefix - тут дело вот в чем, свободные префиксы ищутся с максимально короткой маской, поэтому найденую маску приходится срезать и жестко приделывать /30

domain_name - ипользуется для сохранения в ипам адреса который мы настроили

Далее сохраняем все в БД, прописываем дескрипшен для интерфейса в инвентори, чтобы не занять интерфейс второй раз и не ждать пока сработает дискавери, сохраняем префикс в ипам (думаю не надо объяснять зачем мы это делаем), и собственно адрес, адрес я сохраняю потому, что дискавери и fqdn_template очень страшно генерят fqdn. Неудобство есть небольшое, запись в БД происходит в момент выполнения питона и если где-то произошла опечатка, то даже если еще не подтвердил выполнение команд на железе, данные в ИПАМ и Инвентори уже попали

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

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