The number of members in virtual-chasis is always 1 #10916

Closed
opened 2025-12-29 21:37:40 +01:00 by adam · 7 comments
Owner

Originally created by @Wythetells on GitHub (Mar 19, 2025).

Deployment Type

Self-hosted

NetBox Version

v4.2.3

Python Version

3.11

Steps to Reproduce

The number of members in virtual-chasis is always 1

Image

Image

Image

Expected Behavior

The number of members in virtual-chasis should is 0

Observed Behavior

The number of members in virtual-chasis is always 1

Originally created by @Wythetells on GitHub (Mar 19, 2025). ### Deployment Type Self-hosted ### NetBox Version v4.2.3 ### Python Version 3.11 ### Steps to Reproduce The number of members in virtual-chasis is always 1 ![Image](https://github.com/user-attachments/assets/421430c2-060b-4f8a-bd91-14c3f9357722) ![Image](https://github.com/user-attachments/assets/f676eb1f-6b01-42df-a7a5-fd41686bb7c5) ![Image](https://github.com/user-attachments/assets/7ce5c328-cf3a-4b45-ba82-d056b3a72667) ### Expected Behavior The number of members in virtual-chasis should is 0 ### Observed Behavior The number of members in virtual-chasis is always 1
adam added the netbox label 2025-12-29 21:37:40 +01:00
adam closed this issue 2025-12-29 21:37:41 +01:00
Author
Owner

@bctiemann commented on GitHub (Mar 19, 2025):

@Wythetells I can't reproduce this:

Image Image

These two virtual chassis have 2 and 0 members respectively, as is correct. Can you provide details on how to reproduce your results?

@bctiemann commented on GitHub (Mar 19, 2025): @Wythetells I can't reproduce this: <img width="1100" alt="Image" src="https://github.com/user-attachments/assets/85600c75-63f8-4d7e-87d8-f31a541cb3a7" /> <img width="1089" alt="Image" src="https://github.com/user-attachments/assets/bbb098ba-b8ba-4eb5-a32f-cacab5598325" /> These two virtual chassis have 2 and 0 members respectively, as is correct. Can you provide details on how to reproduce your results?
Author
Owner

@Wythetells commented on GitHub (Mar 20, 2025):

ok,I installed it using the Git method and then used a custom script to update the stack information. I extracted the stack name from the device name. For example, stack_name:A corresponds to the stack group stack_name. All the stack groups created and associated using the script have this problem。

The script is as follows:

from dcim.models import Device, VirtualChassis, DeviceRole
from extras.scripts import Script, BooleanVar, StringVar
from django.db import transaction


class StackDetection(Script):
    class Meta:
        name = "堆叠检测维护(VirtualChassis)"
        description = "根据设备名称自动创建虚拟堆叠"

    def run(self, data, commit):
        # 创建虚拟堆叠的缓存字典 {base_name: (vc_object, members)}
        stack_groups = {}

        # 第一步:扫描所有设备并分类
        for device in Device.objects.prefetch_related('virtual_chassis'):
            if not device.name:
                continue
            if ':' in device.name:
                base_name, suffix = device.name.rsplit(':', 1)
                suffix = suffix.upper()

                # 验证命名规范
                if len(suffix) == 1 and suffix.isalpha():
                    stack_groups.setdefault(base_name, {'vc': None, 'members': {}})
                    stack_groups[base_name]['members'][suffix] = device

        # 第二步:处理每个堆叠组
        created_vc = 0
        updated_devices = 0

        for base_name, group_data in stack_groups.items():
            members = group_data['members']
            sorted_suffixes = sorted(members.keys())

            # 有效性检查
            if not sorted_suffixes:
                continue

            # 确定主设备(:A 作为master)
            master = members.get('A') or members[sorted_suffixes[0]]

            try:
                with transaction.atomic():
                    # 创建或获取VirtualChassis
                    vc, created = VirtualChassis.objects.get_or_create(
                        name=base_name,
                        defaults={
                            'master': master,
                            'domain': f'jd.local',
                        }
                    )

                    if created:
                        self.log_success(f"创建虚拟堆叠: {base_name}")
                        created_vc += 1
                    else:
                        # 更新现有堆叠的主设备
                        if vc.master != master:
                            vc.master = master
                            vc.save()

                    # 关联成员设备
                    for idx, suffix in enumerate(sorted_suffixes, start=1):
                        device = members[suffix]
                        update_fields = {}

                        # 检查是否需要更新
                        if device.virtual_chassis != vc:
                            update_fields['virtual_chassis'] = vc

                        if device.vc_position != idx:
                            update_fields['vc_position'] = idx

                        if update_fields:
                            if commit:
                                Device.objects.filter(pk=device.pk).update(**update_fields)
                            updated_devices += 1
                            self.log_info(f"已关联到 {base_name} 堆叠组 {idx}",obj=device)

            except Exception as e:
                self.log_failure(f"处理 {base_name} 失败: {str(e)}")

        # 结果汇总
        self.log_success(f"操作完成!创建 {created_vc} 个虚拟堆叠,更新 {updated_devices} 台设备")

@Wythetells commented on GitHub (Mar 20, 2025): ok,I installed it using the Git method and then used a custom script to update the stack information. I extracted the stack name from the device name. For example, stack_name:A corresponds to the stack group stack_name. All the stack groups created and associated using the script have this problem。 The script is as follows: ``` from dcim.models import Device, VirtualChassis, DeviceRole from extras.scripts import Script, BooleanVar, StringVar from django.db import transaction class StackDetection(Script): class Meta: name = "堆叠检测维护(VirtualChassis)" description = "根据设备名称自动创建虚拟堆叠" def run(self, data, commit): # 创建虚拟堆叠的缓存字典 {base_name: (vc_object, members)} stack_groups = {} # 第一步:扫描所有设备并分类 for device in Device.objects.prefetch_related('virtual_chassis'): if not device.name: continue if ':' in device.name: base_name, suffix = device.name.rsplit(':', 1) suffix = suffix.upper() # 验证命名规范 if len(suffix) == 1 and suffix.isalpha(): stack_groups.setdefault(base_name, {'vc': None, 'members': {}}) stack_groups[base_name]['members'][suffix] = device # 第二步:处理每个堆叠组 created_vc = 0 updated_devices = 0 for base_name, group_data in stack_groups.items(): members = group_data['members'] sorted_suffixes = sorted(members.keys()) # 有效性检查 if not sorted_suffixes: continue # 确定主设备(:A 作为master) master = members.get('A') or members[sorted_suffixes[0]] try: with transaction.atomic(): # 创建或获取VirtualChassis vc, created = VirtualChassis.objects.get_or_create( name=base_name, defaults={ 'master': master, 'domain': f'jd.local', } ) if created: self.log_success(f"创建虚拟堆叠: {base_name}") created_vc += 1 else: # 更新现有堆叠的主设备 if vc.master != master: vc.master = master vc.save() # 关联成员设备 for idx, suffix in enumerate(sorted_suffixes, start=1): device = members[suffix] update_fields = {} # 检查是否需要更新 if device.virtual_chassis != vc: update_fields['virtual_chassis'] = vc if device.vc_position != idx: update_fields['vc_position'] = idx if update_fields: if commit: Device.objects.filter(pk=device.pk).update(**update_fields) updated_devices += 1 self.log_info(f"已关联到 {base_name} 堆叠组 {idx}",obj=device) except Exception as e: self.log_failure(f"处理 {base_name} 失败: {str(e)}") # 结果汇总 self.log_success(f"操作完成!创建 {created_vc} 个虚拟堆叠,更新 {updated_devices} 台设备") ```
Author
Owner

@jeremystretch commented on GitHub (Apr 9, 2025):

@Wythetells It's very possible that your custom script is not properly validating or saving model. Please provide step-by-step instructions for reproducing the reported problem exclusively using the UI or REST API.

@jeremystretch commented on GitHub (Apr 9, 2025): @Wythetells It's very possible that your custom script is not properly validating or saving model. Please provide step-by-step instructions for reproducing the reported problem exclusively using the UI or REST API.
Author
Owner

@github-actions[bot] commented on GitHub (Apr 17, 2025):

This is a reminder that additional information is needed in order to further triage this issue. If the requested details are not provided, the issue will soon be closed automatically.

@github-actions[bot] commented on GitHub (Apr 17, 2025): This is a reminder that additional information is needed in order to further triage this issue. If the requested details are not provided, the issue will soon be closed automatically.
Author
Owner

@Wythetells commented on GitHub (Apr 17, 2025):

ok,

  1. I installed it using the Git method

  2. Create two hosts, one with the hostname test: A, One for test:B, Other parameters are arbitrary

3.Create custom scripts,The script name doesn't matter, it doesn't affect,the specific code of the script is as follows

class StackDetection(Script):
    class Meta:
        name = "堆叠检测维护(VirtualChassis)"
        description = "根据设备名称自动创建虚拟堆叠"

    def run(self, data, commit):
        # 创建虚拟堆叠的缓存字典 {base_name: (vc_object, members)}
        stack_groups = {}

        # 第一步:扫描所有设备并分类
        for device in Device.objects.prefetch_related('virtual_chassis'):
            if not device.name:
                continue
            if ':' in device.name:
                base_name, suffix = device.name.rsplit(':', 1)
                suffix = suffix.upper()

                # 验证命名规范
                if len(suffix) == 1 and suffix.isalpha():
                    stack_groups.setdefault(base_name, {'vc': None, 'members': {}})
                    stack_groups[base_name]['members'][suffix] = device

        # 第二步:处理每个堆叠组
        created_vc = 0
        updated_devices = 0

        for base_name, group_data in stack_groups.items():
            members = group_data['members']
            sorted_suffixes = sorted(members.keys())

            # 有效性检查
            if not sorted_suffixes:
                continue

            # 确定主设备(:A 作为master)
            master = members.get('A') or members[sorted_suffixes[0]]

            try:
                with transaction.atomic():
                    # 创建或获取VirtualChassis
                    vc, created = VirtualChassis.objects.get_or_create(
                        name=base_name,
                        defaults={
                            'master': master,
                            'domain': f'jd.local',
                        }
                    )

                    if created:
                        self.log_success(f"创建虚拟堆叠: {base_name}")
                        created_vc += 1
                    else:
                        # 更新现有堆叠的主设备
                        if vc.master != master:
                            vc.master = master
                            vc.save()

                    # 关联成员设备
                    for idx, suffix in enumerate(sorted_suffixes, start=1):
                        device = members[suffix]
                        update_fields = {}

                        # 检查是否需要更新
                        if device.virtual_chassis != vc:
                            update_fields['virtual_chassis'] = vc

                        if device.vc_position != idx:
                            update_fields['vc_position'] = idx

                        if update_fields:
                            if commit:
                                Device.objects.filter(pk=device.pk).update(**update_fields)
                            updated_devices += 1
                            self.log_info(f"已关联到 {base_name} 堆叠组 {idx}",obj=device)

            except Exception as e:
                self.log_failure(f"处理 {base_name} 失败: {str(e)}")

        # 结果汇总
        self.log_success(f"操作完成!创建 {created_vc} 个虚拟堆叠,更新 {updated_devices} 台设备")
  1. Run the script, note that there is a bug here https://github.com/netbox-community/netbox/issues/18965
    The script must be run on the script details page, and running on the script list page will not take effect.

Image

Image

5.Inspection results

Expected results
The script will create a virtual assistants named 'test' with two devices and 2 members
Actual results
The script created a virtual assistants named 'test' with two devices and 1 member

@Wythetells commented on GitHub (Apr 17, 2025): ok, 1. I installed it using the Git method 2. Create two hosts, one with the hostname test: A, One for test:B, Other parameters are arbitrary 3.Create custom scripts,The script name doesn't matter, it doesn't affect,the specific code of the script is as follows ``` class StackDetection(Script): class Meta: name = "堆叠检测维护(VirtualChassis)" description = "根据设备名称自动创建虚拟堆叠" def run(self, data, commit): # 创建虚拟堆叠的缓存字典 {base_name: (vc_object, members)} stack_groups = {} # 第一步:扫描所有设备并分类 for device in Device.objects.prefetch_related('virtual_chassis'): if not device.name: continue if ':' in device.name: base_name, suffix = device.name.rsplit(':', 1) suffix = suffix.upper() # 验证命名规范 if len(suffix) == 1 and suffix.isalpha(): stack_groups.setdefault(base_name, {'vc': None, 'members': {}}) stack_groups[base_name]['members'][suffix] = device # 第二步:处理每个堆叠组 created_vc = 0 updated_devices = 0 for base_name, group_data in stack_groups.items(): members = group_data['members'] sorted_suffixes = sorted(members.keys()) # 有效性检查 if not sorted_suffixes: continue # 确定主设备(:A 作为master) master = members.get('A') or members[sorted_suffixes[0]] try: with transaction.atomic(): # 创建或获取VirtualChassis vc, created = VirtualChassis.objects.get_or_create( name=base_name, defaults={ 'master': master, 'domain': f'jd.local', } ) if created: self.log_success(f"创建虚拟堆叠: {base_name}") created_vc += 1 else: # 更新现有堆叠的主设备 if vc.master != master: vc.master = master vc.save() # 关联成员设备 for idx, suffix in enumerate(sorted_suffixes, start=1): device = members[suffix] update_fields = {} # 检查是否需要更新 if device.virtual_chassis != vc: update_fields['virtual_chassis'] = vc if device.vc_position != idx: update_fields['vc_position'] = idx if update_fields: if commit: Device.objects.filter(pk=device.pk).update(**update_fields) updated_devices += 1 self.log_info(f"已关联到 {base_name} 堆叠组 {idx}",obj=device) except Exception as e: self.log_failure(f"处理 {base_name} 失败: {str(e)}") # 结果汇总 self.log_success(f"操作完成!创建 {created_vc} 个虚拟堆叠,更新 {updated_devices} 台设备") ``` 4. Run the script, note that there is a bug here https://github.com/netbox-community/netbox/issues/18965 The script must be run on the script details page, and running on the script list page will not take effect. ![Image](https://github.com/user-attachments/assets/63fbf586-3a6c-4eb2-b8fa-dfe38f51b690) ![Image](https://github.com/user-attachments/assets/35dbe066-2d2a-4cc3-be7e-550850dc3dbc) 5.Inspection results **Expected results** The script will create a virtual assistants named 'test' with two devices and **2 members** **Actual results** The script created a virtual assistants named 'test' with two devices and **1 member**
Author
Owner

@Wythetells commented on GitHub (Apr 17, 2025):

I guess that when updating through a custom script, the update to the total number of Members was not triggered. When a device is added again from the UI to the virtual assistants named test, the number of devices in the test is 3, but the members of the test will be+1, resulting in a result of 2

@Wythetells commented on GitHub (Apr 17, 2025): I guess that when updating through a custom script, the update to the total number of Members was not triggered. When a device is added again from the UI to the virtual assistants named test, the number of devices in the test is 3, but the members of the test will be+1, resulting in a result of 2
Author
Owner

@jeremystretch commented on GitHub (Apr 17, 2025):

Again, this is likely an issue with your custom script. Please open a discussion if you need help troubleshooting.

I'm going to close this issue as it does not appear to be a NetBox bug.

@jeremystretch commented on GitHub (Apr 17, 2025): Again, this is likely an issue with your custom script. Please open a [discussion](https://github.com/netbox-community/netbox/discussions) if you need help troubleshooting. I'm going to close this issue as it does not appear to be a NetBox bug.
Sign in to join this conversation.
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: starred/netbox#10916