From 86cd044a68fe4c0310be82c67b27888d25b39393 Mon Sep 17 00:00:00 2001 From: Jeremy Stretch Date: Wed, 7 Aug 2019 17:47:44 -0400 Subject: [PATCH 1/3] Fixes #3405: Move device component creation logic into template models --- netbox/dcim/models.py | 114 +++++++++++++++++++++++++++++++----------- 1 file changed, 85 insertions(+), 29 deletions(-) diff --git a/netbox/dcim/models.py b/netbox/dcim/models.py index 0a1b52979..4c22c9549 100644 --- a/netbox/dcim/models.py +++ b/netbox/dcim/models.py @@ -31,6 +31,12 @@ class ComponentTemplateModel(models.Model): class Meta: abstract = True + def instantiate(self, device): + """ + Instantiate a new component on the specified Device. + """ + raise NotImplementedError() + def log_change(self, user, request_id, action): """ Log an ObjectChange including the parent DeviceType. @@ -1010,6 +1016,12 @@ class ConsolePortTemplate(ComponentTemplateModel): def __str__(self): return self.name + def instantiate(self, device): + return ConsolePort( + device=device, + name=self.name + ) + class ConsoleServerPortTemplate(ComponentTemplateModel): """ @@ -1033,6 +1045,12 @@ class ConsoleServerPortTemplate(ComponentTemplateModel): def __str__(self): return self.name + def instantiate(self, device): + return ConsoleServerPort( + device=device, + name=self.name + ) + class PowerPortTemplate(ComponentTemplateModel): """ @@ -1068,6 +1086,14 @@ class PowerPortTemplate(ComponentTemplateModel): def __str__(self): return self.name + def instantiate(self, device): + return PowerPort( + device=device, + name=self.name, + maximum_draw=self.maximum_draw, + allocated_draw=self.allocated_draw + ) + class PowerOutletTemplate(ComponentTemplateModel): """ @@ -1112,6 +1138,18 @@ class PowerOutletTemplate(ComponentTemplateModel): "Parent power port ({}) must belong to the same device type".format(self.power_port) ) + def instantiate(self, device): + if self.power_port: + power_port = PowerPort.objects.get(device=device, name=self.power_port.name) + else: + power_port = None + return PowerOutlet( + device=device, + name=self.name, + power_port=power_port, + feed_leg=self.feed_leg + ) + class InterfaceTemplate(ComponentTemplateModel): """ @@ -1159,6 +1197,14 @@ class InterfaceTemplate(ComponentTemplateModel): """ self.type = value + def instantiate(self, device): + return Interface( + device=device, + name=self.name, + type=self.type, + mgmt_only=self.mgmt_only + ) + class FrontPortTemplate(ComponentTemplateModel): """ @@ -1213,6 +1259,19 @@ class FrontPortTemplate(ComponentTemplateModel): ) ) + def instantiate(self, device): + if self.rear_port: + rear_port = RearPort.objects.get(device=device, name=self.rear_port.name) + else: + rear_port = None + return FrontPort( + device=device, + name=self.name, + type=self.type, + rear_port=rear_port, + rear_port_position=self.rear_port_position + ) + class RearPortTemplate(ComponentTemplateModel): """ @@ -1243,6 +1302,14 @@ class RearPortTemplate(ComponentTemplateModel): def __str__(self): return self.name + def instantiate(self, device): + return RearPort( + device=device, + name=self.name, + type=self.type, + positions=self.positions + ) + class DeviceBayTemplate(ComponentTemplateModel): """ @@ -1266,6 +1333,12 @@ class DeviceBayTemplate(ComponentTemplateModel): def __str__(self): return self.name + def instantiate(self, device): + return DeviceBay( + device=device, + name=self.name + ) + # # Devices @@ -1640,45 +1713,28 @@ class Device(ChangeLoggedModel, ConfigContextModel, CustomFieldModel): # If this is a new Device, instantiate all of the related components per the DeviceType definition if is_new: ConsolePort.objects.bulk_create( - [ConsolePort(device=self, name=template.name) for template in - self.device_type.consoleport_templates.all()] + [x.instantiate(self) for x in self.device_type.consoleport_templates.all()] ) ConsoleServerPort.objects.bulk_create( - [ConsoleServerPort(device=self, name=template.name) for template in - self.device_type.consoleserverport_templates.all()] + [x.instantiate(self) for x in self.device_type.consoleserverport_templates.all()] ) PowerPort.objects.bulk_create( - [PowerPort(device=self, name=template.name) for template in - self.device_type.powerport_templates.all()] + [x.instantiate(self) for x in self.device_type.powerport_templates.all()] ) PowerOutlet.objects.bulk_create( - [PowerOutlet(device=self, name=template.name) for template in - self.device_type.poweroutlet_templates.all()] + [x.instantiate(self) for x in self.device_type.poweroutlet_templates.all()] ) Interface.objects.bulk_create( - [Interface(device=self, name=template.name, type=template.type, - mgmt_only=template.mgmt_only) for template in self.device_type.interface_templates.all()] + [x.instantiate(self) for x in self.device_type.interface_templates.all()] + ) + RearPort.objects.bulk_create( + [x.instantiate(self) for x in self.device_type.rearport_templates.all()] + ) + FrontPort.objects.bulk_create( + [x.instantiate(self) for x in self.device_type.frontport_templates.all()] ) - RearPort.objects.bulk_create([ - RearPort( - device=self, - name=template.name, - type=template.type, - positions=template.positions - ) for template in self.device_type.rearport_templates.all() - ]) - FrontPort.objects.bulk_create([ - FrontPort( - device=self, - name=template.name, - type=template.type, - rear_port=RearPort.objects.get(device=self, name=template.rear_port.name), - rear_port_position=template.rear_port_position, - ) for template in self.device_type.frontport_templates.all() - ]) DeviceBay.objects.bulk_create( - [DeviceBay(device=self, name=template.name) for template in - self.device_type.device_bay_templates.all()] + [x.instantiate(self) for x in self.device_type.device_bay_templates.all()] ) # Update Site and Rack assignment for any child Devices From 605be30fb2d0107b9536f8a1f99716f58417ccc7 Mon Sep 17 00:00:00 2001 From: Jeremy Stretch Date: Wed, 7 Aug 2019 17:48:12 -0400 Subject: [PATCH 2/3] Add test for device component creation --- netbox/dcim/tests/test_models.py | 132 ++++++++++++++++++++++++++++++- 1 file changed, 131 insertions(+), 1 deletion(-) diff --git a/netbox/dcim/tests/test_models.py b/netbox/dcim/tests/test_models.py index e0af86b20..2135aba66 100644 --- a/netbox/dcim/tests/test_models.py +++ b/netbox/dcim/tests/test_models.py @@ -1,6 +1,5 @@ from django.test import TestCase -from dcim.constants import * from dcim.models import * @@ -152,6 +151,137 @@ class RackTestCase(TestCase): self.assertTrue(pdu) +class DeviceTestCase(TestCase): + + def setUp(self): + + self.site = Site.objects.create(name='Test Site 1', slug='test-site-1') + manufacturer = Manufacturer.objects.create(name='Test Manufacturer 1', slug='test-manufacturer-1') + self.device_type = DeviceType.objects.create( + manufacturer=manufacturer, model='Test Device Type 1', slug='test-device-type-1' + ) + self.device_role = DeviceRole.objects.create( + name='Test Device Role 1', slug='test-device-role-1', color='ff0000' + ) + + # Create DeviceType components + ConsolePortTemplate( + device_type=self.device_type, + name='Console Port 1' + ).save() + + ConsoleServerPortTemplate( + device_type=self.device_type, + name='Console Server Port 1' + ).save() + + ppt = PowerPortTemplate( + device_type=self.device_type, + name='Power Port 1', + maximum_draw=1000, + allocated_draw=500 + ) + ppt.save() + + PowerOutletTemplate( + device_type=self.device_type, + name='Power Outlet 1', + power_port=ppt, + feed_leg=POWERFEED_LEG_A + ).save() + + InterfaceTemplate( + device_type=self.device_type, + name='Interface 1', + type=IFACE_TYPE_1GE_FIXED, + mgmt_only=True + ).save() + + rpt = RearPortTemplate( + device_type=self.device_type, + name='Rear Port 1', + type=PORT_TYPE_8P8C, + positions=8 + ) + rpt.save() + + FrontPortTemplate( + device_type=self.device_type, + name='Front Port 1', + type=PORT_TYPE_8P8C, + rear_port=rpt, + rear_port_position=2 + ).save() + + DeviceBayTemplate( + device_type=self.device_type, + name='Device Bay 1' + ).save() + + def test_device_creation(self): + """ + Ensure that all Device components are copied automatically from the DeviceType. + """ + d = Device( + site=self.site, + device_type=self.device_type, + device_role=self.device_role, + name='Test Device 1' + ) + d.save() + + ConsolePort.objects.get( + device=d, + name='Console Port 1' + ) + + ConsoleServerPort.objects.get( + device=d, + name='Console Server Port 1' + ) + + pp = PowerPort.objects.get( + device=d, + name='Power Port 1', + maximum_draw=1000, + allocated_draw=500 + ) + + PowerOutlet.objects.get( + device=d, + name='Power Outlet 1', + power_port=pp, + feed_leg=POWERFEED_LEG_A + ) + + Interface.objects.get( + device=d, + name='Interface 1', + type=IFACE_TYPE_1GE_FIXED, + mgmt_only=True + ) + + rp = RearPort.objects.get( + device=d, + name='Rear Port 1', + type=PORT_TYPE_8P8C, + positions=8 + ) + + FrontPort.objects.get( + device=d, + name='Front Port 1', + type=PORT_TYPE_8P8C, + rear_port=rp, + rear_port_position=2 + ) + + DeviceBay.objects.get( + device=d, + name='Device Bay 1' + ) + + class CableTestCase(TestCase): def setUp(self): From 0516aecb0314046f130c0907a619f14640e99113 Mon Sep 17 00:00:00 2001 From: Jeremy Stretch Date: Wed, 7 Aug 2019 17:49:54 -0400 Subject: [PATCH 3/3] Changelog for #3405 --- CHANGELOG.md | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 96d5aa674..eaf8cd930 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,11 @@ +v2.6.3 (FUTURE) + +## Bug Fixes + +* [#3405](https://github.com/netbox-community/netbox/issues/3405) - Fix population of power port/outlet details on device creation + +--- + v2.6.2 (2019-08-02) ## Enhancements