internlm/SWE-Fixer-Train-110K huggingface dataset:
Repository: netbox-community/netbox

Issue:
Add cumulated VM Resources to Cluster view
### NetBox version

v3.3.9

### Feature type

New functionality

### Proposed functionality

The cluster view should show a cumulated value for all assigned vm cpu/ram/disk.

### Use case

This is especially helpful for cluster size planning and get a quick overview over all assigned resources.

### Database changes

_No response_

### External dependencies

_No response_


Discussion:
In order for the developers not to come back to this several times, I would expand the request.
VMs already have base resource values:
![image](https://user-images.githubusercontent.com/84839985/208234693-65b0394b-3b26-47e0-b337-98208f992e3d.png)

It will be logical, that clusters also have same values as "resource pools" (similar to IP ranges/prefixes).
So, cluster view can show both available and assigned resources.


~~Devices also have CPU and RAM values, but I'd prefer them not be linked, since most of our "clusters" is either cloud resource pools or VMs with containers. So, they have defined resource limits, but have not devices to attach them.
Also, with "site requirement" limitations for clusters still in place, we can't attach devices to our physical clusters (since nodes located on different sites).~~
CPU cores and RAM amount for devices is our custom fields, sorry 😁

Resolving patch:
diff --git a/netbox/virtualization/views.py b/netbox/virtualization/views.py
--- a/netbox/virtualization/views.py
+++ b/netbox/virtualization/views.py
@@ -1,6 +1,6 @@
 from django.contrib import messages
 from django.db import transaction
-from django.db.models import Prefetch
+from django.db.models import Prefetch, Sum
 from django.shortcuts import get_object_or_404, redirect, render
 from django.urls import reverse
 from django.utils.translation import gettext as _
@@ -169,6 +169,9 @@ class ClusterListView(generic.ObjectListView):
 class ClusterView(generic.ObjectView):
     queryset = Cluster.objects.all()
 
+    def get_extra_context(self, request, instance):
+        return instance.virtual_machines.aggregate(vcpus_sum=Sum('vcpus'), memory_sum=Sum('memory'), disk_sum=Sum('disk'))
+
 
 @register_model_view(Cluster, 'virtualmachines', path='virtual-machines')
 class ClusterVirtualMachinesView(generic.ObjectChildrenView):


Test patch:
Repository: netbox-community/netbox

Issue:
VM Interface CSV Export have empty lines if two ip adresses assigned to VM Interface
### NetBox version

v3.4.1

### Python version

3.10

### Steps to Reproduce

VM Interface Export
open export.csv



### Expected Behavior

no empty lines should displayed
second lines should have the same information about device / interface as first line

### Observed Behavior

Between first interface Export with IP one and second Export IP three empty lines and second line only second IP Adress is displayed. no device / interface information in this line


Discussion:

Resolving patch:
diff --git a/netbox/dcim/tables/devices.py b/netbox/dcim/tables/devices.py
--- a/netbox/dcim/tables/devices.py
+++ b/netbox/dcim/tables/devices.py
@@ -506,6 +506,9 @@ class BaseInterfaceTable(NetBoxTable):
         verbose_name='Tagged VLANs'
     )
 
+    def value_ip_addresses(self, value):
+        return ",".join([str(obj.address) for obj in value.all()])
+
 
 class InterfaceTable(ModularDeviceComponentTable, BaseInterfaceTable, PathEndpointTable):
     device = tables.Column(
diff --git a/netbox/ipam/tables/fhrp.py b/netbox/ipam/tables/fhrp.py
--- a/netbox/ipam/tables/fhrp.py
+++ b/netbox/ipam/tables/fhrp.py
@@ -33,6 +33,9 @@ class FHRPGroupTable(NetBoxTable):
         url_name='ipam:fhrpgroup_list'
     )
 
+    def value_ip_addresses(self, value):
+        return ",".join([str(obj.address) for obj in value.all()])
+
     class Meta(NetBoxTable.Meta):
         model = FHRPGroup
         fields = (


Test patch:
Repository: netbox-community/netbox

Issue:
Script Job duration is wrong
### NetBox version

v3.4.1

### Python version

3.9

### Steps to Reproduce

1. Schedule a Script to execute at any time
2. Wait for job to be run
3. Check duration of execution

### Expected Behavior

Script execution time should be counted from the scheduled time/started and completed at.


### Observed Behavior

Script execution time is being counted from creation time until completion:

![image](https://user-images.githubusercontent.com/91552302/209105797-14fbf569-c60a-4e32-92f7-b66ff790264d.png)

As you can see the script only ran for one minute, however if I go and check the script result it gives me 14 hours, which correlates to the time of creation.

![image](https://user-images.githubusercontent.com/91552302/209106111-c25862e2-1d9f-439e-92f5-74ffd42cdb59.png)

This may be happening to reports also, I don't have any to test with.


Discussion:

Resolving patch:
diff --git a/netbox/extras/models/models.py b/netbox/extras/models/models.py
--- a/netbox/extras/models/models.py
+++ b/netbox/extras/models/models.py
@@ -651,7 +651,12 @@ def duration(self):
         if not self.completed:
             return None
 
-        duration = self.completed - self.created
+        start_time = self.started or self.created
+
+        if not start_time:
+            return None
+
+        duration = self.completed - start_time
         minutes, seconds = divmod(duration.total_seconds(), 60)
 
         return f"{int(minutes)} minutes, {seconds:.2f} seconds"


Test patch:
Repository: netbox-community/netbox

Issue:
Elevations: Filter badge missing
### NetBox version

v3.4.1

### Python version

3.9

### Steps to Reproduce

1. Go to menu => Elevations
2. Click tab "Filters"
3. Type any search term and press Enter

### Expected Behavior

I would expect a filter badge and a save button to be displayed as in other views. If you have a look at the "Filters" tab you can see that there is a dropdown for "Saved Filter" which doesn't make sense if you are not able to save a filter.

### Observed Behavior

Neither filter badge nor save button is shown.


Discussion:
@jeremystretch You can assign this to me if you like.

Resolving patch:
diff --git a/netbox/dcim/views.py b/netbox/dcim/views.py
--- a/netbox/dcim/views.py
+++ b/netbox/dcim/views.py
@@ -691,6 +691,7 @@ def get(self, request):
             'sort_choices': ORDERING_CHOICES,
             'rack_face': rack_face,
             'filter_form': forms.RackElevationFilterForm(request.GET),
+            'model': self.queryset.model,
         })
 
 


Test patch:
Repository: netbox-community/netbox

Issue:
Scheduling a job in the past raises an exception
### NetBox version

v3.4.2

### Python version

3.10

### Steps to Reproduce

1. Create a script
2. Schedule it in the past

### Expected Behavior

Form validation error message

### Observed Behavior

```
Traceback (most recent call last):
  File "/home/main/devel/repos/netbox/venv/lib/python3.10/site-packages/django/core/handlers/exception.py", line 55, in inner
    response = get_response(request)
  File "/home/main/devel/repos/netbox/venv/lib/python3.10/site-packages/django/core/handlers/base.py", line 197, in _get_response
    response = wrapped_callback(request, *callback_args, **callback_kwargs)
  File "/home/main/devel/repos/netbox/venv/lib/python3.10/site-packages/django/views/generic/base.py", line 103, in view
    return self.dispatch(request, *args, **kwargs)
  File "/home/main/devel/repos/netbox/netbox/utilities/views.py", line 53, in dispatch
    return super().dispatch(request, *args, **kwargs)
  File "/home/main/devel/repos/netbox/venv/lib/python3.10/site-packages/django/views/generic/base.py", line 142, in dispatch
    return handler(request, *args, **kwargs)
  File "/home/main/devel/repos/netbox/netbox/extras/views.py", line 815, in post
    elif form.is_valid():
  File "/home/main/devel/repos/netbox/netbox/utilities/forms/forms.py", line 69, in is_valid
    is_valid = super().is_valid()
  File "/home/main/devel/repos/netbox/venv/lib/python3.10/site-packages/django/forms/forms.py", line 205, in is_valid
    return self.is_bound and not self.errors
  File "/home/main/devel/repos/netbox/venv/lib/python3.10/site-packages/django/forms/forms.py", line 200, in errors
    self.full_clean()
  File "/home/main/devel/repos/netbox/venv/lib/python3.10/site-packages/django/forms/forms.py", line 437, in full_clean
    self._clean_fields()
  File "/home/main/devel/repos/netbox/venv/lib/python3.10/site-packages/django/forms/forms.py", line 455, in _clean_fields
    self.add_error(name, e)
  File "/home/main/devel/repos/netbox/venv/lib/python3.10/site-packages/django/forms/forms.py", line 392, in add_error
    raise TypeError(
TypeError: The argument `field` must be `None` when the `error` argument contains errors for multiple fields.
```


Discussion:

Resolving patch:
diff --git a/netbox/extras/forms/scripts.py b/netbox/extras/forms/scripts.py
--- a/netbox/extras/forms/scripts.py
+++ b/netbox/extras/forms/scripts.py
@@ -48,9 +48,7 @@ def __init__(self, *args, **kwargs):
     def clean__schedule_at(self):
         scheduled_time = self.cleaned_data['_schedule_at']
         if scheduled_time and scheduled_time < timezone.now():
-            raise forms.ValidationError({
-                '_schedule_at': _('Scheduled time must be in the future.')
-            })
+            raise forms.ValidationError(_('Scheduled time must be in the future.'))
 
         return scheduled_time
 


Test patch:
Repository: netbox-community/netbox

Issue:
Scheduled tasks are not deleted when the JobResult is deleted when another queue is used (via. QUEUE_MAPPINGS)
### NetBox version

v3.4.2

### Python version

3.10

### Steps to Reproduce

1. Schedule a script with a scheduled datetime and an interval
2. Delete the JobResult before the script is executed

### Expected Behavior

The scheduled job should be deleted (can be view in admin -> background tasks).

### Observed Behavior

The scheduled job is not deleted.


Discussion:

Resolving patch:
diff --git a/netbox/extras/models/models.py b/netbox/extras/models/models.py
--- a/netbox/extras/models/models.py
+++ b/netbox/extras/models/models.py
@@ -634,7 +634,8 @@ def __str__(self):
     def delete(self, *args, **kwargs):
         super().delete(*args, **kwargs)
 
-        queue = django_rq.get_queue("default")
+        rq_queue_name = get_config().QUEUE_MAPPINGS.get(self.obj_type.name, RQ_QUEUE_DEFAULT)
+        queue = django_rq.get_queue(rq_queue_name)
         job = queue.fetch_job(str(self.job_id))
 
         if job:


Test patch:
Repository: netbox-community/netbox

Issue:
Missing pre-change data for device bay
### NetBox version

v3.3.10

### Python version

3.8

### Steps to Reproduce

1. Add a device to a device bay
2. Delete the device from device bay
3. Check change log for device bay

### Expected Behavior

Change Log and webhook should show previous installed device under pre-change data

### Observed Behavior

When deleting a device from device bay the pre-change data is null.

DeviceBayPopulateView and DeviceBayDepopulateView doesn't call snapshot before disassociating the device.


Discussion:

Resolving patch:
diff --git a/netbox/dcim/views.py b/netbox/dcim/views.py
--- a/netbox/dcim/views.py
+++ b/netbox/dcim/views.py
@@ -2820,7 +2820,7 @@ def post(self, request, pk):
         form = forms.PopulateDeviceBayForm(device_bay, request.POST)
 
         if form.is_valid():
-
+            device_bay.snapshot()
             device_bay.installed_device = form.cleaned_data['installed_device']
             device_bay.save()
             messages.success(request, "Added {} to {}.".format(device_bay.installed_device, device_bay))
@@ -2854,7 +2854,7 @@ def post(self, request, pk):
         form = ConfirmationForm(request.POST)
 
         if form.is_valid():
-
+            device_bay.snapshot()
             removed_device = device_bay.installed_device
             device_bay.installed_device = None
             device_bay.save()


Test patch:
Repository: netbox-community/netbox

Issue:
Disallow changing CustomField type after creation
### NetBox version

v3.4.2

### Feature type

Change to existing functionality

### Proposed functionality

Disallow changing CustomField type after creation.

### Use case

Currently we allow changing the type after creation, but if there's already custom field data saved on an object, in most cases it will cause exceptions in the UI and API when the custom field data is deserialized. Currently there is no logic to handle changing the type. Even if we created logic for converting between all types, it would be a very slow process and error prone as most types are not compatible (float -> int, int -> date etc.).

### Database changes

None

### External dependencies

None


Discussion:
Even renaming a custom field can cause problems. It sometimes times out before all objects have been updated....


Resolving patch:
diff --git a/netbox/extras/api/serializers.py b/netbox/extras/api/serializers.py
--- a/netbox/extras/api/serializers.py
+++ b/netbox/extras/api/serializers.py
@@ -97,6 +97,12 @@ class Meta:
             'validation_minimum', 'validation_maximum', 'validation_regex', 'choices', 'created', 'last_updated',
         ]
 
+    def validate_type(self, value):
+        if self.instance and self.instance.type != value:
+            raise serializers.ValidationError('Changing the type of custom fields is not supported.')
+
+        return value
+
     def get_data_type(self, obj):
         types = CustomFieldTypeChoices
         if obj.type == types.TYPE_INTEGER:
diff --git a/netbox/extras/forms/model_forms.py b/netbox/extras/forms/model_forms.py
--- a/netbox/extras/forms/model_forms.py
+++ b/netbox/extras/forms/model_forms.py
@@ -63,6 +63,13 @@ class Meta:
             'ui_visibility': StaticSelect(),
         }
 
+    def __init__(self, *args, **kwargs):
+        super().__init__(*args, **kwargs)
+
+        # Disable changing the type of a CustomField as it almost universally causes errors if custom field data is already present.
+        if self.instance.pk:
+            self.fields['type'].disabled = True
+
 
 class CustomLinkForm(BootstrapMixin, forms.ModelForm):
     content_types = ContentTypeMultipleChoiceField(


Test patch:
diff --git a/netbox/extras/tests/test_api.py b/netbox/extras/tests/test_api.py
--- a/netbox/extras/tests/test_api.py
+++ b/netbox/extras/tests/test_api.py
@@ -106,6 +106,11 @@ class CustomFieldTest(APIViewTestCases.APIViewTestCase):
     bulk_update_data = {
         'description': 'New description',
     }
+    update_data = {
+        'content_types': ['dcim.device'],
+        'name': 'New_Name',
+        'description': 'New description',
+    }
 
     @classmethod
     def setUpTestData(cls):
Repository: netbox-community/netbox

Issue:
Error on non-regex search for invalid regex string
### NetBox version

v3.4.2

### Python version

3.8

### Steps to Reproduce

1. Create a *Site* with Name = *"Foo (Bar)"* and Slug = *"foo-bar"*
2. Perform a default global search or explicit *Partial match* search for the string *"(Bar"*
3. Receive error

### Expected Behavior

The *Site* should have been found and the string appropriately highlighted in the result. I believe this is caused by the use of `re.split` in `utilities.utils.highlight_string`, as I could not find where the highlight string is being escaped for non-regex searches.

### Observed Behavior

The following error is received:

```
<class 're.error'>

missing ), unterminated subpattern at position 0

Python version: 3.8.10
NetBox version: 3.4.2
```


Discussion:

Resolving patch:
diff --git a/netbox/utilities/utils.py b/netbox/utilities/utils.py
--- a/netbox/utilities/utils.py
+++ b/netbox/utilities/utils.py
@@ -517,7 +517,7 @@ def highlight_string(value, highlight, trim_pre=None, trim_post=None, trim_place
     """
     # Split value on highlight string
     try:
-        pre, match, post = re.split(fr'({highlight})', value, maxsplit=1, flags=re.IGNORECASE)
+        pre, match, post = re.split(fr'({re.escape(highlight)})', value, maxsplit=1, flags=re.IGNORECASE)
     except ValueError:
         # Match not found
         return escape(value)


Test patch:
Repository: netbox-community/netbox

Issue:
IP Range search not working as expected
### NetBox version

v3.4.3

### Python version

3.11

### Steps to Reproduce

1. Log into https://demo.netbox.dev
2. [Go to IP-Ranges](https://demo.netbox.dev/ipam/ip-ranges/)
3. Search for one of the IP ranges, eg. `192.168.1.50`, or simply `192`

### Expected Behavior

All IP ranges with a matching substring of the search shows, kinda like `?q=` works on `ip_addresses`. (It's the same for the API and pynetbox)

### Observed Behavior

No results found.
Search in GUI and `?q=`  does not show any results at all.
`?contains=` seems to work when searching for an IP (with /cidr), and URL encodes the `/`. `192.168.1.66/22` does not work, but `192.168.1.66%2f22` works. But I can't get contains to work via pynetbox.


Discussion:
@zacho112 - do you mean "Quick search" instead of "Search"?
Or do you mean that "192.168.1.66" does not work in "Search"?

Quick search of "192" shows 0 results. 
| --- |
| <img width="663" alt="image" src="https://user-images.githubusercontent.com/1142651/215170342-a4f6c72e-01ac-4ea8-93f9-e0bdde9d9101.png"> |

Quick search of "Future" shows 4 results.
| --- |
| <img width="906" alt="image" src="https://user-images.githubusercontent.com/1142651/215171190-0017de06-261f-49cb-8f25-360ef51aa401.png"> |

"Search" of "192" shows 9 results
| --- |
| <img width="877" alt="image" src="https://user-images.githubusercontent.com/1142651/215171381-e29bef2b-0daa-4e35-82c1-d3f17fa43514.png">
 | 
Yes, my bad, quick search it is. Searching for part of ip in the range. And also the 'query' ('?q=') via API ofc

Resolving patch:
diff --git a/netbox/ipam/filtersets.py b/netbox/ipam/filtersets.py
--- a/netbox/ipam/filtersets.py
+++ b/netbox/ipam/filtersets.py
@@ -441,9 +441,9 @@ class Meta:
     def search(self, queryset, name, value):
         if not value.strip():
             return queryset
-        qs_filter = Q(description__icontains=value)
+        qs_filter = Q(description__icontains=value) | Q(start_address__contains=value) | Q(end_address__contains=value)
         try:
-            ipaddress = str(netaddr.IPNetwork(value.strip()).cidr)
+            ipaddress = str(netaddr.IPNetwork(value.strip()))
             qs_filter |= Q(start_address=ipaddress)
             qs_filter |= Q(end_address=ipaddress)
         except (AddrFormatError, ValueError):


Test patch:
Repository: netbox-community/netbox

Issue:
CSV import for custom fields of Multiple Object type does not work
### NetBox version

v3.4.4

### Python version

3.10

### Steps to Reproduce

1. Create a CircuitType and Circuit, get the ID of the circuit
2. Create a customfield of multiple object type named cf_tenantmulti
3. Try to import the following:

```
name,slug,cf_tenantmulti
test1233dsdsfdsfdsffdsf21,testdsfsdfdsfddsfdsfsfsdfsdf,1
```

### Expected Behavior

Import succeed

### Observed Behavior

`Row 1 cf_tenantmulti: Enter a list of values`


Discussion:

Resolving patch:
diff --git a/netbox/extras/models/customfields.py b/netbox/extras/models/customfields.py
--- a/netbox/extras/models/customfields.py
+++ b/netbox/extras/models/customfields.py
@@ -21,7 +21,7 @@
 from netbox.search import FieldTypes
 from utilities import filters
 from utilities.forms import (
-    CSVChoiceField, CSVMultipleChoiceField, DatePicker, DynamicModelChoiceField, DynamicModelMultipleChoiceField,
+    CSVChoiceField, CSVModelMultipleChoiceField, CSVMultipleChoiceField, DatePicker, DynamicModelChoiceField, DynamicModelMultipleChoiceField,
     JSONField, LaxURLField, StaticSelectMultiple, StaticSelect, add_blank_choice,
 )
 from utilities.querysets import RestrictedQuerySet
@@ -422,10 +422,12 @@ def to_form_field(self, set_initial=True, enforce_required=True, enforce_visibil
         # Multiple objects
         elif self.type == CustomFieldTypeChoices.TYPE_MULTIOBJECT:
             model = self.object_type.model_class()
-            field = DynamicModelMultipleChoiceField(
+            field_class = CSVModelMultipleChoiceField if for_csv_import else DynamicModelMultipleChoiceField
+
+            field = field_class(
                 queryset=model.objects.all(),
                 required=required,
-                initial=initial
+                initial=initial,
             )
 
         # Text


Test patch:
Repository: netbox-community/netbox

Issue:
Webhook condition for having a primary IP doesn't appear to be working
### NetBox version

v3.4.2

### Python version

3.10

### Steps to Reproduce

1. Create a webhook, content type 'DCIM > Device', all events, with the condition:
`{
    "and": [
        {
            "attr": "primary_ip",
            "value": "",
            "negate": true
        }
    ]
}`
2. Create a site, all defaults.
3. Create a device role, all defaults.
4. Create a manufacturer, all defaults.
5. Create a device type, all defaults.
6. Create a device.
7. Go to the admin panel, check the jobs in the default queue.

### Expected Behavior

The webhook does not fire due to the condition of requiring a primary IP.

### Observed Behavior

The webhook fires (despite the condition).


Discussion:
Seems to be similar/related to #10519 . We see this issue on our prod instance as well (v3.3.5, python 3.8).

Resolving patch:
diff --git a/netbox/extras/conditions.py b/netbox/extras/conditions.py
--- a/netbox/extras/conditions.py
+++ b/netbox/extras/conditions.py
@@ -44,7 +44,8 @@ class Condition:
         bool: (EQ, CONTAINS),
         int: (EQ, GT, GTE, LT, LTE, CONTAINS),
         float: (EQ, GT, GTE, LT, LTE, CONTAINS),
-        list: (EQ, IN, CONTAINS)
+        list: (EQ, IN, CONTAINS),
+        type(None): (EQ,)
     }
 
     def __init__(self, attr, value, op=EQ, negate=False):


Test patch:
diff --git a/netbox/extras/tests/test_conditions.py b/netbox/extras/tests/test_conditions.py
--- a/netbox/extras/tests/test_conditions.py
+++ b/netbox/extras/tests/test_conditions.py
@@ -126,6 +126,16 @@ def test_invalid_logic(self):
         with self.assertRaises(ValueError):
             ConditionSet({'foo': []})
 
+    def test_null_value(self):
+        cs = ConditionSet({
+            'and': [
+                {'attr': 'a', 'value': None, 'op': 'eq', 'negate': True},
+            ]
+        })
+        self.assertFalse(cs.eval({'a': None}))
+        self.assertTrue(cs.eval({'a': "string"}))
+        self.assertTrue(cs.eval({'a': {"key": "value"}}))
+
     def test_and_single_depth(self):
         cs = ConditionSet({
             'and': [