78 lines
3.4 KiB
Python
78 lines
3.4 KiB
Python
import re
|
||
|
||
from dcim.models.devices import Device
|
||
from extras.models.tags import Tag
|
||
from extras.scripts import ObjectVar, Script
|
||
from dcim.models import VirtualChassis, Interface
|
||
from netbox.choices import ColorChoices
|
||
from utilities.exceptions import AbortScript
|
||
|
||
|
||
class Vchassis_checker(Script):
|
||
TAGS = 'sync:vchas_interface'
|
||
IFACE_RE = re.compile(r'^(?P<prefix>[A-Za-z]+)\s?(?:(?P<slot>\d+)/)?(?P<rest>\d+/\d+)$')
|
||
|
||
class Meta(Script.Meta):
|
||
commit_default = False
|
||
name = 'Переопределение интерфейсов'
|
||
description = 'Переопределение интерфейсов у стекированных коммутаторов'
|
||
|
||
virtual_chassis = ObjectVar(
|
||
model=VirtualChassis,
|
||
query_params={'tags': None},
|
||
)
|
||
|
||
def run(self, data, commit):
|
||
if Tag.objects.filter(name=self.TAGS).exists():
|
||
tag = Tag.objects.get(name=self.TAGS)
|
||
else:
|
||
self.log_warning(f'Отсутствует tag {self.TAGS}')
|
||
tag_data = {
|
||
'color': ColorChoices.COLOR_LIGHT_BLUE,
|
||
'name': self.TAGS,
|
||
'slug': self.TAGS.replace(':', '_'),
|
||
'description': 'тег для автоматического назначения на синхронизированные интерфейсы vchassis',
|
||
}
|
||
tag = Tag(**tag_data)
|
||
if commit:
|
||
tag.save()
|
||
self.log_success(f'Создан тег: {self.TAGS}')
|
||
vc = data['virtual_chassis']
|
||
devices: list[Device] = list(vc.members.all().order_by('vc_position'))
|
||
if not vc._state.adding and vc.master and vc.master not in devices:
|
||
raise AbortScript(f'В стеке {vc.name} отсутствует мастер')
|
||
for device in devices:
|
||
interfaces = Interface.objects.filter(device=device)
|
||
for iface in interfaces:
|
||
m = self.IFACE_RE.match(iface)
|
||
|
||
# Все интерфейсы всех устройств стека одним запросом
|
||
device_ids = [d.pk for d in devices]
|
||
interfaces_qs = Interface.objects.filter(device_id__in=device_ids)
|
||
|
||
if commit:
|
||
# Вариант 1: цикл с .save() — вызываются clean(), save(), сигналы и change log (ObjectChange)
|
||
for iface in interfaces_qs:
|
||
iface.description = f'{iface.device.name}:{iface.name}' # пример
|
||
iface.save()
|
||
|
||
# Вариант 2: массовый UPDATE без change log (быстро, но без аудита):
|
||
# interfaces_qs.update(description=...) # одно значение для всех
|
||
# Или bulk_update для разных значений по объектам:
|
||
# to_update = []
|
||
# for iface in interfaces_qs:
|
||
# iface.description = f'{iface.device.name}:{iface.name}'
|
||
# to_update.append(iface)
|
||
# Interface.objects.bulk_update(to_update, ['description'])
|
||
else:
|
||
count = interfaces_qs.count()
|
||
self.log_success(f'Будет обновлено интерфейсов: {count}')
|
||
|
||
|
||
# vchassis = VirtualChassis.objects.all()
|
||
# for vc in vchassis:
|
||
# interfaces = Interface.objects.filter(device=vc)
|
||
# for interface in interfaces:
|
||
# interface.name = f'{vc.name}-{interface.name}'
|
||
# interface.save()
|