netbox-community / netbox

The premier source of truth powering network automation. Open source under Apache 2. Try NetBox Cloud free: https://netboxlabs.com/free-netbox-cloud/
http://netboxlabs.com/oss/netbox/
Apache License 2.0
16.1k stars 2.58k forks source link

Bulk edit fails when modifying URL to expand search parameters #16871

Closed einhirn closed 2 months ago

einhirn commented 3 months ago

Deployment Type

Self-hosted

NetBox Version

v4.0.6

Python Version

3.10

Steps to Reproduce

  1. Click "Devices"->"Device Components"->"Interfaces"
  2. Try to search for a number of interfaces on a number of switches that all need to be configured the same way
  3. Fail using the UI, modify URL to contain multiple mentions of "name" and "device" like so: https://netbox.selfhosted/dcim/interfaces/?device=dc-l1-3-leaf1&device=dc-l1-3-leaf2&name=1/21&name=1/22&name=1/23&name=1/24
  4. Successfully be presented with required list of Ports across multiple switches and select all of them
  5. Click on "Edit selected"
  6. Receive a 500 server error

Expected Behavior

A Bulk edit page should have opened for the selected ports

Observed Behavior

<class 'ValueError'>

Field 'id' expected a number but got 'dc-l1-3-leaf2'.

is reported

Additional Context

The request contains the list of pks for the selected interfaces as "Form data".

Inspecting the "Edit selected" button, I noticed that formaction for _rename Button doesn't contain parameters from the URL, while formaction for _edit (and _delete) Button contains full URL including manually added parameters:

<button type="submit" name="_edit" formaction="/dcim/interfaces/edit/?device=dc-l1-3-leaf1&amp;device=dc-l1-3-leaf2&amp;name=1%2F21&amp;name=1%2F22&amp;name=1%2F23&amp;name=1%2F24" class="btn btn-yellow">
    <i class="mdi mdi-pencil" aria-hidden="true"></i> Edit Selected
  </button>
<button type="submit" name="_rename" formaction="/dcim/interfaces/rename/" class="btn btn-outline-warning">
          <i class="mdi mdi-pencil-outline" aria-hidden="true"></i> Rename Selected
        </button>
<button type="submit" name="_delete" formaction="/dcim/interfaces/delete/?device=dc-l1-3-leaf1&amp;device=dc-l1-3-leaf2&amp;name=1%2F21&amp;name=1%2F22&amp;name=1%2F23&amp;name=1%2F24" class="btn btn-red">
    <i class="mdi mdi-trash-can-outline" aria-hidden="true"></i> Delete Selected
  </button>

I modified the form action on the fly to remove the additional parameters and the button worked as expected - a bulk edit form was opened and the changes were applied to all selected interfaces. I also was returned to the search result page/URL with additional parameters as expected, so I could see the changes on the selected interfaces

arthanson commented 3 months ago

Just the url given with https://netbox.selfhosted/dcim/interfaces/?device=dc-l1-3-leaf1 (or appropriate device) will show the error. Not sure if the bulk edit should work with any crafter URL outside the UI, but it shouldn't crash in this case:

Traceback (most recent call last):
  File "/Users/arthurhanson/dev/work/netbox/venv/lib/python3.11/site-packages/django/core/handlers/exception.py", line 55, in inner
    response = get_response(request)
               ^^^^^^^^^^^^^^^^^^^^^
  File "/Users/arthurhanson/dev/work/netbox/venv/lib/python3.11/site-packages/django/core/handlers/base.py", line 197, in _get_response
    response = wrapped_callback(request, *callback_args, **callback_kwargs)
               ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/Users/arthurhanson/dev/work/netbox/venv/lib/python3.11/site-packages/django/views/generic/base.py", line 104, in view
    return self.dispatch(request, *args, **kwargs)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/Users/arthurhanson/dev/work/netbox/netbox/netbox/views/generic/base.py", line 77, in dispatch
    return super().dispatch(request, *args, **kwargs)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/Users/arthurhanson/dev/work/netbox/netbox/utilities/views.py", line 113, in dispatch
    return super().dispatch(request, *args, **kwargs)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/Users/arthurhanson/dev/work/netbox/venv/lib/python3.11/site-packages/django/views/generic/base.py", line 143, in dispatch
    return handler(request, *args, **kwargs)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/Users/arthurhanson/dev/work/netbox/netbox/netbox/views/generic/bulk_views.py", line 665, in post
    form = self.form(initial=initial_data)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/Users/arthurhanson/dev/work/netbox/netbox/dcim/forms/bulk_edit.py", line 1418, in __init__
    super().__init__(*args, **kwargs)
  File "/Users/arthurhanson/dev/work/netbox/netbox/dcim/forms/bulk_edit.py", line 1196, in __init__
    device = Device.objects.filter(pk=self.initial['device']).first()
             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/Users/arthurhanson/dev/work/netbox/venv/lib/python3.11/site-packages/django/db/models/manager.py", line 87, in manager_method
    return getattr(self.get_queryset(), name)(*args, **kwargs)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/Users/arthurhanson/dev/work/netbox/venv/lib/python3.11/site-packages/django/db/models/query.py", line 1476, in filter
    return self._filter_or_exclude(False, args, kwargs)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/Users/arthurhanson/dev/work/netbox/venv/lib/python3.11/site-packages/django/db/models/query.py", line 1494, in _filter_or_exclude
    clone._filter_or_exclude_inplace(negate, args, kwargs)
  File "/Users/arthurhanson/dev/work/netbox/venv/lib/python3.11/site-packages/django/db/models/query.py", line 1501, in _filter_or_exclude_inplace
    self._query.add_q(Q(*args, **kwargs))
  File "/Users/arthurhanson/dev/work/netbox/venv/lib/python3.11/site-packages/django/db/models/sql/query.py", line 1613, in add_q
    clause, _ = self._add_q(q_object, self.used_aliases)
                ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/Users/arthurhanson/dev/work/netbox/venv/lib/python3.11/site-packages/django/db/models/sql/query.py", line 1645, in _add_q
    child_clause, needed_inner = self.build_filter(
                                 ^^^^^^^^^^^^^^^^^^
  File "/Users/arthurhanson/dev/work/netbox/venv/lib/python3.11/site-packages/django/db/models/sql/query.py", line 1559, in build_filter
    condition = self.build_lookup(lookups, col, value)
                ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/Users/arthurhanson/dev/work/netbox/venv/lib/python3.11/site-packages/django/db/models/sql/query.py", line 1389, in build_lookup
    lookup = lookup_class(lhs, rhs)
             ^^^^^^^^^^^^^^^^^^^^^^
  File "/Users/arthurhanson/dev/work/netbox/venv/lib/python3.11/site-packages/django/db/models/lookups.py", line 30, in __init__
    self.rhs = self.get_prep_lookup()
               ^^^^^^^^^^^^^^^^^^^^^^
  File "/Users/arthurhanson/dev/work/netbox/venv/lib/python3.11/site-packages/django/db/models/lookups.py", line 364, in get_prep_lookup
    return super().get_prep_lookup()
           ^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/Users/arthurhanson/dev/work/netbox/venv/lib/python3.11/site-packages/django/db/models/lookups.py", line 88, in get_prep_lookup
    return self.lhs.output_field.get_prep_value(self.rhs)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/Users/arthurhanson/dev/work/netbox/venv/lib/python3.11/site-packages/django/db/models/fields/__init__.py", line 2119, in get_prep_value
    raise e.__class__(
ValueError: Field 'id' expected a number but got 'dmi01-akron-rtr01'.