Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

TypeError thrown at some PDU devices #10236

Closed
baschdello opened this issue Sep 1, 2022 · 11 comments · Fixed by #10961
Closed

TypeError thrown at some PDU devices #10236

baschdello opened this issue Sep 1, 2022 · 11 comments · Fixed by #10961
Assignees
Labels
status: accepted This issue has been accepted for implementation type: bug A confirmed report of unexpected behavior in the application

Comments

@baschdello
Copy link

baschdello commented Sep 1, 2022

NetBox version

v3.3.0

Python version

3.9

Steps to Reproduce

  1. upgraded netbox to v3.3.0
  2. click at an affected pdu (device)

Expected Behavior

I expect that the pdu device is displayed.

Observed Behavior

An error page with "TypeError" is thrown. Not every pdu is affected although of same device type.

If deleting the cable of an affected pdu to the power feed, the pdu device is opened correctly. If I create the cable again, the type error page is present instead of the device page.
With option "DEBUG = True" in configuration.py the type error page displays some helpful information (attached at bottom). I think there is something wrong at the power utilization graph because of type mismatch.

Template error:
In template /opt/netbox/netbox/templates/dcim/device.html, error at line 252
   unsupported operand type(s) for /: 'str' and 'int'
   242 :                                         {% else %}
   243 :                                             <td class="text-muted">&mdash;</td>
   244 :                                             <td class="text-muted">&mdash;</td>
   245 :                                         {% endif %}
   246 :                                     </tr>
   247 :                                     {% for leg in utilization.legs %}
   248 :                                         <tr>
   249 :                                             <td style="padding-left: 20px">Leg {{ leg.name }}</td>
   250 :                                             <td>{{ leg.outlet_count }}</td>
   251 :                                             <td>{{ leg.allocated }}</td>
   252 :                                             <td> {{ powerfeed.available_power|divide:3 }} VA</td>
   253 :                                             {% with phase_available=powerfeed.available_power|divide:3 %}
   254 :                                                 <td>{% utilization_graph leg.allocated|percentage:phase_available %}</td>
   255 :                                             {% endwith %}
   256 :                                         </tr>
   257 :                                     {% endfor %}
   258 :                                 {% endwith %}
   259 :                             {% endfor %}
   260 :                         </table>
   261 :                     </div>
   262 :                 </div>


Traceback (most recent call last):
  File "/opt/netbox-3.3.0/venv/lib/python3.9/site-packages/django/core/handlers/exception.py", line 55, in inner
    response = get_response(request)
  File "/opt/netbox-3.3.0/venv/lib/python3.9/site-packages/django/core/handlers/base.py", line 197, in _get_response
    response = wrapped_callback(request, *callback_args, **callback_kwargs)
  File "/opt/netbox-3.3.0/venv/lib/python3.9/site-packages/django/views/generic/base.py", line 84, in view
    return self.dispatch(request, *args, **kwargs)
  File "/opt/netbox/netbox/utilities/views.py", line 90, in dispatch
    return super().dispatch(request, *args, **kwargs)
  File "/opt/netbox-3.3.0/venv/lib/python3.9/site-packages/django/views/generic/base.py", line 119, in dispatch
    return handler(request, *args, **kwargs)
  File "/opt/netbox/netbox/netbox/views/generic/object_views.py", line 67, in get
    return render(request, self.get_template_name(), {
  File "/opt/netbox-3.3.0/venv/lib/python3.9/site-packages/django/shortcuts.py", line 24, in render
    content = loader.render_to_string(template_name, context, request, using=using)
  File "/opt/netbox-3.3.0/venv/lib/python3.9/site-packages/django/template/loader.py", line 62, in render_to_string
    return template.render(context, request)
  File "/opt/netbox-3.3.0/venv/lib/python3.9/site-packages/django/template/backends/django.py", line 62, in render
    return self.template.render(context)
  File "/opt/netbox-3.3.0/venv/lib/python3.9/site-packages/django/template/base.py", line 175, in render
    return self._render(context)
  File "/opt/netbox-3.3.0/venv/lib/python3.9/site-packages/django/test/utils.py", line 111, in instrumented_test_render
    return self.nodelist.render(context)
  File "/opt/netbox-3.3.0/venv/lib/python3.9/site-packages/django/template/base.py", line 1000, in render
    return SafeString("".join([node.render_annotated(context) for node in self]))
  File "/opt/netbox-3.3.0/venv/lib/python3.9/site-packages/django/template/base.py", line 1000, in <listcomp>
    return SafeString("".join([node.render_annotated(context) for node in self]))
  File "/opt/netbox-3.3.0/venv/lib/python3.9/site-packages/django/template/base.py", line 958, in render_annotated
    return self.render(context)
  File "/opt/netbox-3.3.0/venv/lib/python3.9/site-packages/django/template/loader_tags.py", line 157, in render
    return compiled_parent._render(context)
  File "/opt/netbox-3.3.0/venv/lib/python3.9/site-packages/django/test/utils.py", line 111, in instrumented_test_render
    return self.nodelist.render(context)
  File "/opt/netbox-3.3.0/venv/lib/python3.9/site-packages/django/template/base.py", line 1000, in render
    return SafeString("".join([node.render_annotated(context) for node in self]))
  File "/opt/netbox-3.3.0/venv/lib/python3.9/site-packages/django/template/base.py", line 1000, in <listcomp>
    return SafeString("".join([node.render_annotated(context) for node in self]))
  File "/opt/netbox-3.3.0/venv/lib/python3.9/site-packages/django/template/base.py", line 958, in render_annotated
    return self.render(context)
  File "/opt/netbox-3.3.0/venv/lib/python3.9/site-packages/django/template/loader_tags.py", line 157, in render
    return compiled_parent._render(context)
  File "/opt/netbox-3.3.0/venv/lib/python3.9/site-packages/django/test/utils.py", line 111, in instrumented_test_render
    return self.nodelist.render(context)
  File "/opt/netbox-3.3.0/venv/lib/python3.9/site-packages/django/template/base.py", line 1000, in render
    return SafeString("".join([node.render_annotated(context) for node in self]))
  File "/opt/netbox-3.3.0/venv/lib/python3.9/site-packages/django/template/base.py", line 1000, in <listcomp>
    return SafeString("".join([node.render_annotated(context) for node in self]))
  File "/opt/netbox-3.3.0/venv/lib/python3.9/site-packages/django/template/base.py", line 958, in render_annotated
    return self.render(context)
  File "/opt/netbox-3.3.0/venv/lib/python3.9/site-packages/django/template/loader_tags.py", line 157, in render
    return compiled_parent._render(context)
  File "/opt/netbox-3.3.0/venv/lib/python3.9/site-packages/django/test/utils.py", line 111, in instrumented_test_render
    return self.nodelist.render(context)
  File "/opt/netbox-3.3.0/venv/lib/python3.9/site-packages/django/template/base.py", line 1000, in render
    return SafeString("".join([node.render_annotated(context) for node in self]))
  File "/opt/netbox-3.3.0/venv/lib/python3.9/site-packages/django/template/base.py", line 1000, in <listcomp>
    return SafeString("".join([node.render_annotated(context) for node in self]))
  File "/opt/netbox-3.3.0/venv/lib/python3.9/site-packages/django/template/base.py", line 958, in render_annotated
    return self.render(context)
  File "/opt/netbox-3.3.0/venv/lib/python3.9/site-packages/django/template/loader_tags.py", line 157, in render
    return compiled_parent._render(context)
  File "/opt/netbox-3.3.0/venv/lib/python3.9/site-packages/django/test/utils.py", line 111, in instrumented_test_render
    return self.nodelist.render(context)
  File "/opt/netbox-3.3.0/venv/lib/python3.9/site-packages/django/template/base.py", line 1000, in render
    return SafeString("".join([node.render_annotated(context) for node in self]))
  File "/opt/netbox-3.3.0/venv/lib/python3.9/site-packages/django/template/base.py", line 1000, in <listcomp>
    return SafeString("".join([node.render_annotated(context) for node in self]))
  File "/opt/netbox-3.3.0/venv/lib/python3.9/site-packages/django/template/base.py", line 958, in render_annotated
    return self.render(context)
  File "/opt/netbox-3.3.0/venv/lib/python3.9/site-packages/django/template/loader_tags.py", line 63, in render
    result = block.nodelist.render(context)
  File "/opt/netbox-3.3.0/venv/lib/python3.9/site-packages/django/template/base.py", line 1000, in render
    return SafeString("".join([node.render_annotated(context) for node in self]))
  File "/opt/netbox-3.3.0/venv/lib/python3.9/site-packages/django/template/base.py", line 1000, in <listcomp>
    return SafeString("".join([node.render_annotated(context) for node in self]))
  File "/opt/netbox-3.3.0/venv/lib/python3.9/site-packages/django/template/base.py", line 958, in render_annotated
    return self.render(context)
  File "/opt/netbox-3.3.0/venv/lib/python3.9/site-packages/django/template/loader_tags.py", line 63, in render
    result = block.nodelist.render(context)
  File "/opt/netbox-3.3.0/venv/lib/python3.9/site-packages/django/template/base.py", line 1000, in render
    return SafeString("".join([node.render_annotated(context) for node in self]))
  File "/opt/netbox-3.3.0/venv/lib/python3.9/site-packages/django/template/base.py", line 1000, in <listcomp>
    return SafeString("".join([node.render_annotated(context) for node in self]))
  File "/opt/netbox-3.3.0/venv/lib/python3.9/site-packages/django/template/base.py", line 958, in render_annotated
    return self.render(context)
  File "/opt/netbox-3.3.0/venv/lib/python3.9/site-packages/django/template/loader_tags.py", line 63, in render
    result = block.nodelist.render(context)
  File "/opt/netbox-3.3.0/venv/lib/python3.9/site-packages/django/template/base.py", line 1000, in render
    return SafeString("".join([node.render_annotated(context) for node in self]))
  File "/opt/netbox-3.3.0/venv/lib/python3.9/site-packages/django/template/base.py", line 1000, in <listcomp>
    return SafeString("".join([node.render_annotated(context) for node in self]))
  File "/opt/netbox-3.3.0/venv/lib/python3.9/site-packages/django/template/base.py", line 958, in render_annotated
    return self.render(context)
  File "/opt/netbox-3.3.0/venv/lib/python3.9/site-packages/django/template/defaulttags.py", line 322, in render
    return nodelist.render(context)
  File "/opt/netbox-3.3.0/venv/lib/python3.9/site-packages/django/template/base.py", line 1000, in render
    return SafeString("".join([node.render_annotated(context) for node in self]))
  File "/opt/netbox-3.3.0/venv/lib/python3.9/site-packages/django/template/base.py", line 1000, in <listcomp>
    return SafeString("".join([node.render_annotated(context) for node in self]))
  File "/opt/netbox-3.3.0/venv/lib/python3.9/site-packages/django/template/base.py", line 958, in render_annotated
    return self.render(context)
  File "/opt/netbox-3.3.0/venv/lib/python3.9/site-packages/django/template/defaulttags.py", line 238, in render
    nodelist.append(node.render_annotated(context))
  File "/opt/netbox-3.3.0/venv/lib/python3.9/site-packages/django/template/base.py", line 958, in render_annotated
    return self.render(context)
  File "/opt/netbox-3.3.0/venv/lib/python3.9/site-packages/django/template/defaulttags.py", line 542, in render
    return self.nodelist.render(context)
  File "/opt/netbox-3.3.0/venv/lib/python3.9/site-packages/django/template/base.py", line 1000, in render
    return SafeString("".join([node.render_annotated(context) for node in self]))
  File "/opt/netbox-3.3.0/venv/lib/python3.9/site-packages/django/template/base.py", line 1000, in <listcomp>
    return SafeString("".join([node.render_annotated(context) for node in self]))
  File "/opt/netbox-3.3.0/venv/lib/python3.9/site-packages/django/template/base.py", line 958, in render_annotated
    return self.render(context)
  File "/opt/netbox-3.3.0/venv/lib/python3.9/site-packages/django/template/defaulttags.py", line 238, in render
    nodelist.append(node.render_annotated(context))
  File "/opt/netbox-3.3.0/venv/lib/python3.9/site-packages/django/template/base.py", line 958, in render_annotated
    return self.render(context)
  File "/opt/netbox-3.3.0/venv/lib/python3.9/site-packages/django/template/base.py", line 1059, in render
    output = self.filter_expression.resolve(context)
  File "/opt/netbox-3.3.0/venv/lib/python3.9/site-packages/django/template/base.py", line 739, in resolve
    new_obj = func(obj, *arg_vals)
  File "/opt/netbox/netbox/utilities/templatetags/helpers.py", line 131, in divide
    return round(x / y)

Exception Type: TypeError at /dcim/devices/79/
Exception Value: unsupported operand type(s) for /: 'str' and 'int'
@baschdello baschdello added the type: bug A confirmed report of unexpected behavior in the application label Sep 1, 2022
@jeremystretch
Copy link
Member

  1. upgraded netbox to v3.3.0
  2. click at an affected pdu (device)

I'm afraid that's not going to be sufficient to reproduce the reported behavior. Please try to determine steps that someone else can follow to reliably reproduce the issue.

Exception Type: TypeError at /dcim/devices/79/
Exception Value: unsupported operand type(s) for /: 'str' and 'int'

This indicates that powerfeed.available_power is being passed as a string. I'm not sure how that could happen, as available_power is a required PositiveIntegerField and can never be anything other than an integer.

@jeremystretch jeremystretch added the status: revisions needed This issue requires additional information to be actionable label Sep 1, 2022
@baschdello
Copy link
Author

Ok, I suspected that. I will see what I can do. I may need some time to figure out how to reproduce this.

@jeremystretch
Copy link
Member

jeremystretch commented Sep 1, 2022

I'm curious what the value of that field is for the affected power feed. Can you inspect it using the shell? (Grab the numeric ID of the feed first; in the example below it's 19.)

$ ./manage.py nbshell
### NetBox interactive shell (jstretch-workstation)
### Python 3.8.13 | Django 4.0.7 | NetBox 3.3.2-dev
### lsmodels() will show available models. Use help(<model>) for more info.
>>> PowerFeed.objects.get(pk=19)
<PowerFeed: P1-10A>
>>> PowerFeed.objects.get(pk=19).available_power
3520

@baschdello
Copy link
Author

baschdello commented Sep 5, 2022

I've checked the PowerFeed but it seems that it is not a string:

./manage.py nbshell
### NetBox interactive shell (netbox-beta)
### Python 3.9.2 | Django 4.0.7 | NetBox 3.3.0
### lsmodels() will show available models. Use help(<model>) for more info.
>>> PowerFeed.objects.get(pk=49)
<PowerFeed: Feld01 - F04>
>>> PowerFeed.objects.get(pk=49).available_power
11085

So I've tried something other and played around with value "allocated draw" of the pdu's power port.
If opening page "Power Ports" of an affected pdu direct via url "/dcim/devices/[dev-id]/power-ports/" it seems that the power port has no allocated draw (displayed with a line). But if I edit this power port, adding for example 5 watts as allocated draw, the affected pdu is accessible without a type error page and device page is displayed like it should (except of this fictional allocated draw value).
I checked the nbshell in both cases:

  • case with non set allocated draw:
### NetBox interactive shell (netbox-beta)
### Python 3.9.2 | Django 4.0.7 | NetBox 3.3.0
### lsmodels() will show available models. Use help(<model>) for more info.
>>> d = Device.objects.get(pk=48)
>>> 
>>> p=d.powerports.get()
>>> p.get_power_draw()
{'allocated': 0, 'maximum': 5097, 'outlet_count': 24, 'legs': [{'name': 'A', 'allocated': 0, 'maximum': 1695, 'outlet_count': 8}, {'name': 'B', 'allocated': 0, 'maximum': 2002, 'outlet_count': 8}, {'name': 'C', 'allocated': 0, 'maximum': 1400, 'outlet_count': 8}]}
  • case with manually set allocated draw to 120W:
### NetBox interactive shell (netbox-beta)
### Python 3.9.2 | Django 4.0.7 | NetBox 3.3.0
### lsmodels() will show available models. Use help(<model>) for more info.
>>> d = Device.objects.get(pk=48)
>>> p=d.powerports.get()
>>> p.get_power_draw()
{'allocated': 120, 'maximum': 0, 'outlet_count': 24, 'legs': []}

In both cases I can not recognize any string values. I hope this information is helpful somehow.

@jeremystretch jeremystretch added status: under review Further discussion is needed to determine this issue's scope and/or implementation and removed status: revisions needed This issue requires additional information to be actionable labels Sep 6, 2022
@jaylik
Copy link

jaylik commented Sep 9, 2022

@jeremystretch - We hit this bug with three phase power feeds. It seems that this line has a bug,

{% with utilization=powerport.get_power_draw powerfeed=powerport.connected_endpoint %}

{% with power_port=powerfeed.connected_endpoint %}

{% if object.connected_endpoint %}
{{ object.connected_endpoint.device|linkify }} ({{ object.connected_endpoint }})
{% else %}
{{ ''|placeholder }}
{% endif %}
</td>
</tr>
<tr>
<th scope="row">Utilization (Allocated)</th>
{% with utilization=object.connected_endpoint.get_power_draw %}

We monkey patched this with ugly solution:

diff --git a/netbox/templates/dcim/device.html b/netbox/templates/dcim/device.html
index 6cc8597495f16fb7be6ec0cb280655e54b756650..95b9ddf151c29356f9a61403cec92b7a3782a633 100644
--- a/netbox/templates/dcim/device.html
+++ b/netbox/templates/dcim/device.html
@@ -229,7 +229,7 @@
                                 <th>Utilization</th>
                             </tr>
                             {% for powerport in object.powerports.all %}
-                                {% with utilization=powerport.get_power_draw powerfeed=powerport.connected_endpoint %}
+                                {% with utilization=powerport.get_power_draw powerfeed=powerport.connected_endpoints.0 %}
                                     <tr>
                                         <td>{{ powerport }}</td>
                                         <td>{{ utilization.outlet_count }}</td>
diff --git a/netbox/templates/dcim/powerfeed.html b/netbox/templates/dcim/powerfeed.html
index 584454df85e9b1ad963959b77970df48e81c9774..f6f201336c4187119cf76937051936a678d1ce02 100644
--- a/netbox/templates/dcim/powerfeed.html
+++ b/netbox/templates/dcim/powerfeed.html
@@ -41,16 +41,18 @@
                     <tr>
                         <th scope="row">Connected Device</th>
                         <td>
-                            {% if object.connected_endpoint %}
-                                {{ object.connected_endpoint.device|linkify }} ({{ object.connected_endpoint }})
+                            {% with connected_endpoint=object.connected_endpoints.0 %}
+                            {% if connected_endpoint %}
+                                {{ connected_endpoint.device|linkify }} ({{ connected_endpoint }})
                             {% else %}
                                 {{ ''|placeholder }}
                             {% endif %}
+                            {% endwith %}
                         </td>
                     </tr>
                     <tr>
                         <th scope="row">Utilization (Allocated)</th>
-                        {% with utilization=object.connected_endpoint.get_power_draw %}
+                        {% with utilization=object.connected_endpoints.0.get_power_draw %}
                             {% if utilization %}
                                 <td>
                                     {{ utilization.allocated }}VA / {{ object.available_power }}VA
diff --git a/netbox/templates/dcim/rack.html b/netbox/templates/dcim/rack.html
index 51e873ffaf70a8d56596f99cb8367644fd2018a2..87ece4169d9252535776c4b7860e0fbaa3f8dc54 100644
--- a/netbox/templates/dcim/rack.html
+++ b/netbox/templates/dcim/rack.html
@@ -173,7 +173,7 @@
                                 <td>{{ powerfeed|linkify }}</td>
                                 <td>{% badge powerfeed.get_status_display bg_color=powerfeed.get_status_color %}</td>
                                 <td>{% badge powerfeed.get_type_display bg_color=powerfeed.get_type_color %}</td>
-                                {% with power_port=powerfeed.connected_endpoint %}
+                                {% with power_port=powerfeed.connected_endpoints.0 %}
                                     {% if power_port %}
                                         <td>{% utilization_graph power_port.get_power_draw.allocated|percentage:powerfeed.available_power %}</td>
                                     {% else %}

@baschdello
Copy link
Author

@jaylik Thank's for your monkey patch. I tried your device.html template patch. Now affected PDUs displayed properly without error page (I didn't check it with all affected PDUs, but checked two of them).

What I also tried without patching something: If I connect the power port of an PDU with a three-phase power feed, problem appears. Now if edit the power feed and switch to a single-phase type, the PDU is displayed without error page. As @jaylik already said, it seems that this problem appears at three-phase power feeds. What I can say, we mainly use three-phase feeds but I don't have this bug with all of our three-phase feeded PDUs.
Another try without patching something: if connecting the power port of affected PDU with two power feeds (possible since v3.3.0 because of the new cable model) the PDU is displayed properly. Both feeds are three-phase types.
Because this bug comes with upgrade to v3.3.0 I think it has something to do with the new cable model.

@baschdello
Copy link
Author

baschdello commented Oct 13, 2022

What I can say, we mainly use three-phase feeds but I don't have this bug with all of our three-phase feeded PDUs.

I missed the fact, that our non-affected PDUs have a manually set allocated draw. If deleting the allocated draw of PDU power port, the exception page is present.

@arthanson arthanson self-assigned this Oct 18, 2022
@arthanson
Copy link
Collaborator

@baschdello do you have a repro scenario for this? I tried using three-phase power feed and wasn't able to get it to repro. A step-by-step repro would be greatly appreciated.

@baschdello
Copy link
Author

I will try this on monday with a fresh installation of v3.3.0 and give feedback.

@baschdello
Copy link
Author

I've reproduced this issue with a fresh installation of v3.3.0. Here are steps to reproduce:

  1. create site "dc1"
  2. create power-panel "usv-a"
  3. create rack "rack1" and assign it to site "dc1"
  4. create power-feed "feed1" with following details:
    Power Panel "usv-a"
    Rack "rack1"
    Phase "three-phase"
  5. create manufacturer "manufacturer1"
  6. create device-type "pdu" with manufacturer "manufacturer1"
  7. add power-port "Power" to device-type "pdu" with plug type "3P+N+E 6H"
  8. add power-outlets (one for each leg) "out1", "out2" and "out 3" with options:
    type "C13"
    power port "Power"
    feed leg "out1" => leg A, "out2" => leg B, "out 3" => leg C
  9. create devicerole "pdu"
  10. at our "rack1" create a pdu as non-racked device with the devicerole "pdu" and choose device-type "pdu"
  11. connect pdu power-port to power-feed and choose:
    Power Panel "USV-A"
    Power Feed "feed1"
  12. open the created device "pdu" and you will see the TypeError-page.

Best regards
baschdello

@jeremystretch jeremystretch added status: accepted This issue has been accepted for implementation and removed status: under review Further discussion is needed to determine this issue's scope and/or implementation labels Nov 18, 2022
jeremystretch added a commit that referenced this issue Nov 18, 2022
@fabi125
Copy link
Contributor

fabi125 commented Nov 28, 2022

I don't think #10961 fully fixed the issue.

Here powerfeed is set using the removed connected_endpoint: https://github.com/netbox-community/netbox/pull/10961/files#diff-244731798e872e98b04c335c35679f920c3e5c90e8bf2340471f7910bd0449cdL232
Which means it will always be None.

I think the proper fix would be to change it to use connected_endpoints (plural).

There are also two other locations where connected_endpoint is still referenced:

{% with peer=object.connected_endpoint %}

{% with power_port=powerfeed.connected_endpoint %}

@github-actions github-actions bot locked as resolved and limited conversation to collaborators Feb 27, 2023
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
status: accepted This issue has been accepted for implementation type: bug A confirmed report of unexpected behavior in the application
Projects
None yet
Development

Successfully merging a pull request may close this issue.

5 participants