Repository: nautobot/nautobot

Issue:
Power utilization not displaying correctly under Rack view
<!--
    NOTE: IF YOUR ISSUE DOES NOT FOLLOW THIS TEMPLATE, IT WILL BE CLOSED.

    This form is only for reporting reproducible bugs. If you need assistance
    with Nautobot installation, or if you have a general question, please start a
    discussion instead: https://github.com/nautobot/nautobot/discussions

    Please describe the environment in which you are running Nautobot. Be sure
    that you are running an unmodified instance of the latest stable release
    before submitting a bug report, and that any plugins have been disabled.
-->
### Environment
* Python version: 3.9.10
* Nautobot version: 1.3.8

<!--
    Describe in detail the exact steps that someone else can take to reproduce
    this bug using the current stable release of Nautobot. Begin with the
    creation of any necessary database objects and call out every operation
    being performed explicitly. If reporting a bug in the REST API, be sure to
    reconstruct the raw HTTP request(s) being made: Don't rely on a client
    library such as pynautobot.
-->
### Steps to Reproduce
1. Create Power Panel, Power Feed, and connect PDUs/Devices
2. Add Allocated Draw to a connected device
3. Power utilization is calculated correctly on the Power Feed view, but not on the Power Feed section on the appropriate rack

<!-- What did you expect to happen? -->
### Expected Behavior
1. Power utilization should be calculated on the Rack view under Power Feed, and on the Power Feed view itself

<!-- What happened instead? -->
### Observed Behavior
1. Power utilization was only calculated on the Power Feed view, not under the Rack view, even though these two should be equal.



Discussion:
Thanks for the report @josh9730. Can you provide more detailed steps to reproduce, including the connected cabling steps? We aren't able to reproduce the issue.

#### Create Power Panel and associate with site/rack group
![Screen Shot 2022-07-26 at 3 11 53 PM](https://user-images.githubusercontent.com/49131939/181121164-6c85e3ab-12fd-45e2-b8b6-175e83723f88.png)

#### Create power feed and associate with power panel/rack
![Screen Shot 2022-07-26 at 3 13 05 PM](https://user-images.githubusercontent.com/49131939/181121315-ef8e332e-52d6-4154-a9a5-b849f63665d8.png)

#### Create PDU device in the same rack as the feed, link to power feed 
![Screen Shot 2022-07-26 at 3 13 45 PM](https://user-images.githubusercontent.com/49131939/181121391-f150f17e-f03f-4282-b527-dcfe71e9d0f1.png)
 
![Screen Shot 2022-07-26 at 3 14 10 PM](https://user-images.githubusercontent.com/49131939/181121436-da02aa0b-5bb4-4261-b5a2-5a83416e3ba4.png)

#### Connect PDU outlets to device drawing power
![Screen Shot 2022-07-26 at 3 15 35 PM](https://user-images.githubusercontent.com/49131939/181121603-ef92ffb0-34c7-4c90-978f-3520e33fb1e7.png)

![Screen Shot 2022-07-26 at 3 16 06 PM](https://user-images.githubusercontent.com/49131939/181121672-0a9ba80f-a7f0-4c8c-b9fa-0401a040260d.png)

#### Add allocated draw to device
![Screen Shot 2022-07-26 at 3 16 19 PM](https://user-images.githubusercontent.com/49131939/181121694-90add8db-8903-430e-8418-e287d9ecb73b.png)

#### Power calculation correct on Feed view, but not on Rack view
![Screen Shot 2022-07-26 at 3 19 20 PM](https://user-images.githubusercontent.com/49131939/181122080-0ca5d9d9-d8b2-47a7-ae57-66dd62a0ee39.png)

![Screen Shot 2022-07-26 at 3 19 39 PM](https://user-images.githubusercontent.com/49131939/181122120-44a92df0-efb5-4251-a38e-9f8f732b9ef0.png)


Thanks for the report @josh9730! Can confirm the issue now. It looks like the rack view calculation is using the allocation on the power ports exclusively, instead of deriving them from the devices connected to associated power outlets.

Resolving patch:
diff --git a/nautobot/dcim/models/racks.py b/nautobot/dcim/models/racks.py
--- a/nautobot/dcim/models/racks.py
+++ b/nautobot/dcim/models/racks.py
@@ -604,6 +604,7 @@ def get_power_utilization(self):
             _cable_peer_type=ContentType.objects.get_for_model(PowerFeed),
             _cable_peer_id__in=powerfeeds.values_list("id", flat=True),
         )
+        direct_allocated_draw = pf_powerports.aggregate(Sum("allocated_draw"))["allocated_draw__sum"] or 0
         poweroutlets = PowerOutlet.objects.filter(power_port_id__in=pf_powerports)
         allocated_draw_total = (
             PowerPort.objects.filter(
@@ -612,6 +613,7 @@ def get_power_utilization(self):
             ).aggregate(Sum("allocated_draw"))["allocated_draw__sum"]
             or 0
         )
+        allocated_draw_total += direct_allocated_draw
 
         return UtilizationData(numerator=allocated_draw_total, denominator=available_power_total)
 


Test patch:
diff --git a/nautobot/dcim/tests/test_views.py b/nautobot/dcim/tests/test_views.py
--- a/nautobot/dcim/tests/test_views.py
+++ b/nautobot/dcim/tests/test_views.py
@@ -614,26 +614,74 @@ def test_powerports(self):
         # Create Power Port for device
         powerport1 = PowerPort.objects.create(device=devices[0], name="Power Port 11")
         powerfeed1 = PowerFeed.objects.create(
-            power_panel=self.powerpanels[0], name="Power Feed 11", phase="three-phase"
+            power_panel=self.powerpanels[0],
+            name="Power Feed 11",
+            phase="single-phase",
+            voltage=240,
+            amperage=20,
+            rack=self.racks[0],
+        )
+        powerfeed2 = PowerFeed.objects.create(
+            power_panel=self.powerpanels[0],
+            name="Power Feed 12",
+            phase="single-phase",
+            voltage=240,
+            amperage=20,
+            rack=self.racks[0],
         )
 
         # Create power outlet to the power port
-        poweroutlet1 = PowerOutlet.objects.create(device=devices[0], name="Power Outlet 11")
+        poweroutlet1 = PowerOutlet.objects.create(device=devices[0], name="Power Outlet 11", power_port=powerport1)
 
-        # connect power port to power feed (3 phase)
+        # connect power port to power feed (single-phase)
         cable1 = Cable(termination_a=powerfeed1, termination_b=powerport1, status=self.cable_connected)
         cable1.save()
 
         # Create power port for 2nd device
-        powerport2 = PowerPort.objects.create(device=devices[1], name="Power Port 12")
+        powerport2 = PowerPort.objects.create(device=devices[1], name="Power Port 12", allocated_draw=1200)
 
         # Connect power port to power outlet (dev1)
         cable2 = Cable(termination_a=powerport2, termination_b=poweroutlet1, status=self.cable_connected)
         cable2.save()
 
+        # Create another power port for 2nd device and directly connect to the second PowerFeed.
+        powerport3 = PowerPort.objects.create(device=devices[1], name="Power Port 13", allocated_draw=2400)
+        cable3 = Cable(termination_a=powerfeed2, termination_b=powerport3, status=self.cable_connected)
+        cable3.save()
+
         # Test the view
         response = self.client.get(reverse("dcim:rack", args=[self.racks[0].pk]))
         self.assertHttpStatus(response, 200)
+        # Validate Power Utilization for PowerFeed 11 is displaying correctly on Rack View.
+        power_feed_11_html = """
+        <td><div title="Used: 1200&#13;Count: 3840" class="progress text-center">
+            <div class="progress-bar progress-bar-success"
+                role="progressbar" aria-valuenow="31" aria-valuemin="0" aria-valuemax="100" style="width: 31%">
+                31%
+            </div>
+        </div></td>
+        """
+        self.assertContains(response, power_feed_11_html, html=True)
+        # Validate Power Utilization for PowerFeed12 is displaying correctly on Rack View.
+        power_feed_12_html = """
+        <td><div title="Used: 2400&#13;Count: 3840" class="progress text-center">
+            <div class="progress-bar progress-bar-success"
+                role="progressbar" aria-valuenow="62" aria-valuemin="0" aria-valuemax="100" style="width: 62%">
+                62%
+            </div>
+        </div></td>
+        """
+        self.assertContains(response, power_feed_12_html, html=True)
+        # Validate Rack Power Utilization for Combined powerfeeds is displaying correctly on the Rack View
+        total_utilization_html = """
+        <td><div title="Used: 3600&#13;Count: 7680" class="progress text-center">
+            <div class="progress-bar progress-bar-success"
+                role="progressbar" aria-valuenow="46" aria-valuemin="0" aria-valuemax="100" style="width: 46%">
+                46%
+            </div>
+        </div></td>
+        """
+        self.assertContains(response, total_utilization_html, html=True)
 
 
 class ManufacturerTestCase(ViewTestCases.OrganizationalObjectViewTestCase):