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[A-Za-z]+)\s?(?:(?P\d+)/)?(?P\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.full_clean() tag.save() self.log_success( f'Создан тег: {self.TAGS}', obj=tag, ) 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) prefix = m.group('prefix') slot = m.group('slot') rest = m.group('rest') # Все интерфейсы всех устройств стека одним запросом 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}')