mirror of
https://github.com/netbox-community/netbox.git
synced 2025-12-25 06:37:46 -06:00
7961 CSV bulk update (#10715)
* 7961 add csv bulk update * temp checkin - blocked * 7961 bugfix and cleanup * 7961 change to id, add docs * 7961 add tests cases * 7961 fix does not exist validation error * 7961 fix does not exist validation error * 7961 update tests * 7961 update tests * 7961 update tests * 7961 update tests * 7961 update tests * 7961 update tests * 7961 update tests * 7961 update tests * 7961 update tests * 7961 make test cases more explicit * 7961 make test cases more explicit * 7961 make test cases more explicit * 7961 make test cases more explicit * 7961 make test cases more explicit * 7961 make test cases more explicit * 7961 make test cases more explicit * 7961 optimize loading csv test data * 7961 update tests remove redundant code * 7961 avoid MPTT issue in test cases
This commit is contained in:
@@ -50,6 +50,13 @@ class ProviderTestCase(ViewTestCases.PrimaryObjectViewTestCase):
|
||||
"Provider 6,provider-6",
|
||||
)
|
||||
|
||||
cls.csv_update_data = (
|
||||
"id,name,comments",
|
||||
f"{providers[0].pk},Provider 7,New comment7",
|
||||
f"{providers[1].pk},Provider 8,New comment8",
|
||||
f"{providers[2].pk},Provider 9,New comment9",
|
||||
)
|
||||
|
||||
cls.bulk_edit_data = {
|
||||
'account': '5678',
|
||||
'comments': 'New comments',
|
||||
@@ -62,11 +69,13 @@ class CircuitTypeTestCase(ViewTestCases.OrganizationalObjectViewTestCase):
|
||||
@classmethod
|
||||
def setUpTestData(cls):
|
||||
|
||||
CircuitType.objects.bulk_create([
|
||||
circuit_types = (
|
||||
CircuitType(name='Circuit Type 1', slug='circuit-type-1'),
|
||||
CircuitType(name='Circuit Type 2', slug='circuit-type-2'),
|
||||
CircuitType(name='Circuit Type 3', slug='circuit-type-3'),
|
||||
])
|
||||
)
|
||||
|
||||
CircuitType.objects.bulk_create(circuit_types)
|
||||
|
||||
tags = create_tags('Alpha', 'Bravo', 'Charlie')
|
||||
|
||||
@@ -84,6 +93,13 @@ class CircuitTypeTestCase(ViewTestCases.OrganizationalObjectViewTestCase):
|
||||
"Circuit Type 6,circuit-type-6",
|
||||
)
|
||||
|
||||
cls.csv_update_data = (
|
||||
"id,name,description",
|
||||
f"{circuit_types[0].pk},Circuit Type 7,New description7",
|
||||
f"{circuit_types[1].pk},Circuit Type 8,New description8",
|
||||
f"{circuit_types[2].pk},Circuit Type 9,New description9",
|
||||
)
|
||||
|
||||
cls.bulk_edit_data = {
|
||||
'description': 'Foo',
|
||||
}
|
||||
@@ -107,11 +123,13 @@ class CircuitTestCase(ViewTestCases.PrimaryObjectViewTestCase):
|
||||
)
|
||||
CircuitType.objects.bulk_create(circuittypes)
|
||||
|
||||
Circuit.objects.bulk_create([
|
||||
circuits = (
|
||||
Circuit(cid='Circuit 1', provider=providers[0], type=circuittypes[0]),
|
||||
Circuit(cid='Circuit 2', provider=providers[0], type=circuittypes[0]),
|
||||
Circuit(cid='Circuit 3', provider=providers[0], type=circuittypes[0]),
|
||||
])
|
||||
)
|
||||
|
||||
Circuit.objects.bulk_create(circuits)
|
||||
|
||||
tags = create_tags('Alpha', 'Bravo', 'Charlie')
|
||||
|
||||
@@ -136,6 +154,13 @@ class CircuitTestCase(ViewTestCases.PrimaryObjectViewTestCase):
|
||||
"Circuit 6,Provider 1,Circuit Type 1,active",
|
||||
)
|
||||
|
||||
cls.csv_update_data = (
|
||||
f"id,cid,description,status",
|
||||
f"{circuits[0].pk},Circuit 7,New description7,{CircuitStatusChoices.STATUS_DECOMMISSIONED}",
|
||||
f"{circuits[1].pk},Circuit 8,New description8,{CircuitStatusChoices.STATUS_DECOMMISSIONED}",
|
||||
f"{circuits[2].pk},Circuit 9,New description9,{CircuitStatusChoices.STATUS_DECOMMISSIONED}",
|
||||
)
|
||||
|
||||
cls.bulk_edit_data = {
|
||||
'provider': providers[1].pk,
|
||||
'type': circuittypes[1].pk,
|
||||
@@ -159,11 +184,13 @@ class ProviderNetworkTestCase(ViewTestCases.PrimaryObjectViewTestCase):
|
||||
)
|
||||
Provider.objects.bulk_create(providers)
|
||||
|
||||
ProviderNetwork.objects.bulk_create([
|
||||
provider_networks = (
|
||||
ProviderNetwork(name='Provider Network 1', provider=providers[0]),
|
||||
ProviderNetwork(name='Provider Network 2', provider=providers[0]),
|
||||
ProviderNetwork(name='Provider Network 3', provider=providers[0]),
|
||||
])
|
||||
)
|
||||
|
||||
ProviderNetwork.objects.bulk_create(provider_networks)
|
||||
|
||||
tags = create_tags('Alpha', 'Bravo', 'Charlie')
|
||||
|
||||
@@ -182,6 +209,13 @@ class ProviderNetworkTestCase(ViewTestCases.PrimaryObjectViewTestCase):
|
||||
"Provider Network 6,Provider 1,Baz",
|
||||
)
|
||||
|
||||
cls.csv_update_data = (
|
||||
"id,name,description",
|
||||
f"{provider_networks[0].pk},Provider Network 7,New description7",
|
||||
f"{provider_networks[1].pk},Provider Network 8,New description8",
|
||||
f"{provider_networks[2].pk},Provider Network 9,New description9",
|
||||
)
|
||||
|
||||
cls.bulk_edit_data = {
|
||||
'provider': providers[1].pk,
|
||||
'description': 'New description',
|
||||
|
||||
@@ -576,7 +576,7 @@ class PowerOutletCSVForm(NetBoxModelCSVForm):
|
||||
super().__init__(*args, **kwargs)
|
||||
|
||||
# Limit PowerPort choices to those belonging to this device (or VC master)
|
||||
if self.is_bound:
|
||||
if self.is_bound and 'device' in self.data:
|
||||
try:
|
||||
device = self.fields['device'].to_python(self.data['device'])
|
||||
except forms.ValidationError:
|
||||
@@ -711,7 +711,7 @@ class FrontPortCSVForm(NetBoxModelCSVForm):
|
||||
super().__init__(*args, **kwargs)
|
||||
|
||||
# Limit RearPort choices to those belonging to this device (or VC master)
|
||||
if self.is_bound:
|
||||
if self.is_bound and 'device' in self.data:
|
||||
try:
|
||||
device = self.fields['device'].to_python(self.data['device'])
|
||||
except forms.ValidationError:
|
||||
@@ -782,7 +782,7 @@ class DeviceBayCSVForm(NetBoxModelCSVForm):
|
||||
super().__init__(*args, **kwargs)
|
||||
|
||||
# Limit installed device choices to devices of the correct type and location
|
||||
if self.is_bound:
|
||||
if self.is_bound and 'device' in self.data:
|
||||
try:
|
||||
device = self.fields['device'].to_python(self.data['device'])
|
||||
except forms.ValidationError:
|
||||
|
||||
@@ -50,6 +50,13 @@ class RegionTestCase(ViewTestCases.OrganizationalObjectViewTestCase):
|
||||
"Region 6,region-6,Sixth region",
|
||||
)
|
||||
|
||||
cls.csv_update_data = (
|
||||
"id,name,description",
|
||||
f"{regions[0].pk},Region 7,Fourth region7",
|
||||
f"{regions[1].pk},Region 8,Fifth region8",
|
||||
f"{regions[2].pk},Region 0,Sixth region9",
|
||||
)
|
||||
|
||||
cls.bulk_edit_data = {
|
||||
'description': 'New description',
|
||||
}
|
||||
@@ -87,6 +94,13 @@ class SiteGroupTestCase(ViewTestCases.OrganizationalObjectViewTestCase):
|
||||
"Site Group 6,site-group-6,Sixth site group",
|
||||
)
|
||||
|
||||
cls.csv_update_data = (
|
||||
"id,name,description",
|
||||
f"{sitegroups[0].pk},Site Group 7,Fourth site group7",
|
||||
f"{sitegroups[1].pk},Site Group 8,Fifth site group8",
|
||||
f"{sitegroups[2].pk},Site Group 0,Sixth site group9",
|
||||
)
|
||||
|
||||
cls.bulk_edit_data = {
|
||||
'description': 'New description',
|
||||
}
|
||||
@@ -156,6 +170,13 @@ class SiteTestCase(ViewTestCases.PrimaryObjectViewTestCase):
|
||||
"Site 6,site-6,staging",
|
||||
)
|
||||
|
||||
cls.csv_update_data = (
|
||||
"id,name,status",
|
||||
f"{sites[0].pk},Site 7,staging",
|
||||
f"{sites[1].pk},Site 8,planned",
|
||||
f"{sites[2].pk},Site 9,active",
|
||||
)
|
||||
|
||||
cls.bulk_edit_data = {
|
||||
'status': SiteStatusChoices.STATUS_PLANNED,
|
||||
'region': regions[1].pk,
|
||||
@@ -202,6 +223,13 @@ class LocationTestCase(ViewTestCases.OrganizationalObjectViewTestCase):
|
||||
"Site 1,Tenant 1,Location 6,location-6,planned,Sixth location",
|
||||
)
|
||||
|
||||
cls.csv_update_data = (
|
||||
"id,name,description",
|
||||
f"{locations[0].pk},Location 7,Fourth location7",
|
||||
f"{locations[1].pk},Location 8,Fifth location8",
|
||||
f"{locations[2].pk},Location 0,Sixth location9",
|
||||
)
|
||||
|
||||
cls.bulk_edit_data = {
|
||||
'description': 'New description',
|
||||
}
|
||||
@@ -213,11 +241,12 @@ class RackRoleTestCase(ViewTestCases.OrganizationalObjectViewTestCase):
|
||||
@classmethod
|
||||
def setUpTestData(cls):
|
||||
|
||||
RackRole.objects.bulk_create([
|
||||
rack_roles = (
|
||||
RackRole(name='Rack Role 1', slug='rack-role-1'),
|
||||
RackRole(name='Rack Role 2', slug='rack-role-2'),
|
||||
RackRole(name='Rack Role 3', slug='rack-role-3'),
|
||||
])
|
||||
)
|
||||
RackRole.objects.bulk_create(rack_roles)
|
||||
|
||||
tags = create_tags('Alpha', 'Bravo', 'Charlie')
|
||||
|
||||
@@ -236,6 +265,13 @@ class RackRoleTestCase(ViewTestCases.OrganizationalObjectViewTestCase):
|
||||
"Rack Role 6,rack-role-6,0000ff",
|
||||
)
|
||||
|
||||
cls.csv_update_data = (
|
||||
"id,name,description",
|
||||
f"{rack_roles[0].pk},Rack Role 7,New description7",
|
||||
f"{rack_roles[1].pk},Rack Role 8,New description8",
|
||||
f"{rack_roles[2].pk},Rack Role 9,New description9",
|
||||
)
|
||||
|
||||
cls.bulk_edit_data = {
|
||||
'color': '00ff00',
|
||||
'description': 'New description',
|
||||
@@ -259,11 +295,12 @@ class RackReservationTestCase(ViewTestCases.PrimaryObjectViewTestCase):
|
||||
rack = Rack(name='Rack 1', site=site, location=location)
|
||||
rack.save()
|
||||
|
||||
RackReservation.objects.bulk_create([
|
||||
rack_reservations = (
|
||||
RackReservation(rack=rack, user=user2, units=[1, 2, 3], description='Reservation 1'),
|
||||
RackReservation(rack=rack, user=user2, units=[4, 5, 6], description='Reservation 2'),
|
||||
RackReservation(rack=rack, user=user2, units=[7, 8, 9], description='Reservation 3'),
|
||||
])
|
||||
)
|
||||
RackReservation.objects.bulk_create(rack_reservations)
|
||||
|
||||
tags = create_tags('Alpha', 'Bravo', 'Charlie')
|
||||
|
||||
@@ -283,6 +320,13 @@ class RackReservationTestCase(ViewTestCases.PrimaryObjectViewTestCase):
|
||||
'Site 1,Location 1,Rack 1,"16,17,18",Reservation 3',
|
||||
)
|
||||
|
||||
cls.csv_update_data = (
|
||||
'id,description',
|
||||
f'{rack_reservations[0].pk},New description1',
|
||||
f'{rack_reservations[1].pk},New description2',
|
||||
f'{rack_reservations[2].pk},New description3',
|
||||
)
|
||||
|
||||
cls.bulk_edit_data = {
|
||||
'user': user3.pk,
|
||||
'tenant': None,
|
||||
@@ -315,11 +359,12 @@ class RackTestCase(ViewTestCases.PrimaryObjectViewTestCase):
|
||||
)
|
||||
RackRole.objects.bulk_create(rackroles)
|
||||
|
||||
Rack.objects.bulk_create((
|
||||
racks = (
|
||||
Rack(name='Rack 1', site=sites[0]),
|
||||
Rack(name='Rack 2', site=sites[0]),
|
||||
Rack(name='Rack 3', site=sites[0]),
|
||||
))
|
||||
)
|
||||
Rack.objects.bulk_create(racks)
|
||||
|
||||
tags = create_tags('Alpha', 'Bravo', 'Charlie')
|
||||
|
||||
@@ -351,6 +396,13 @@ class RackTestCase(ViewTestCases.PrimaryObjectViewTestCase):
|
||||
"Site 2,Location 2,Rack 6,active,19,42",
|
||||
)
|
||||
|
||||
cls.csv_update_data = (
|
||||
"id,name,status",
|
||||
f"{racks[0].pk},Rack 7,{RackStatusChoices.STATUS_DEPRECATED}",
|
||||
f"{racks[1].pk},Rack 8,{RackStatusChoices.STATUS_DEPRECATED}",
|
||||
f"{racks[2].pk},Rack 9,{RackStatusChoices.STATUS_DEPRECATED}",
|
||||
)
|
||||
|
||||
cls.bulk_edit_data = {
|
||||
'site': sites[1].pk,
|
||||
'location': locations[1].pk,
|
||||
@@ -383,11 +435,12 @@ class ManufacturerTestCase(ViewTestCases.OrganizationalObjectViewTestCase):
|
||||
@classmethod
|
||||
def setUpTestData(cls):
|
||||
|
||||
Manufacturer.objects.bulk_create([
|
||||
manufacturers = (
|
||||
Manufacturer(name='Manufacturer 1', slug='manufacturer-1'),
|
||||
Manufacturer(name='Manufacturer 2', slug='manufacturer-2'),
|
||||
Manufacturer(name='Manufacturer 3', slug='manufacturer-3'),
|
||||
])
|
||||
)
|
||||
Manufacturer.objects.bulk_create(manufacturers)
|
||||
|
||||
tags = create_tags('Alpha', 'Bravo', 'Charlie')
|
||||
|
||||
@@ -405,6 +458,13 @@ class ManufacturerTestCase(ViewTestCases.OrganizationalObjectViewTestCase):
|
||||
"Manufacturer 6,manufacturer-6,Sixth manufacturer",
|
||||
)
|
||||
|
||||
cls.csv_update_data = (
|
||||
"id,name,description",
|
||||
f"{manufacturers[0].pk},Manufacturer 7,Fourth manufacturer7",
|
||||
f"{manufacturers[1].pk},Manufacturer 8,Fifth manufacturer8",
|
||||
f"{manufacturers[2].pk},Manufacturer 9,Sixth manufacturer9",
|
||||
)
|
||||
|
||||
cls.bulk_edit_data = {
|
||||
'description': 'New description',
|
||||
}
|
||||
@@ -1444,11 +1504,12 @@ class DeviceRoleTestCase(ViewTestCases.OrganizationalObjectViewTestCase):
|
||||
@classmethod
|
||||
def setUpTestData(cls):
|
||||
|
||||
DeviceRole.objects.bulk_create([
|
||||
device_roles = (
|
||||
DeviceRole(name='Device Role 1', slug='device-role-1'),
|
||||
DeviceRole(name='Device Role 2', slug='device-role-2'),
|
||||
DeviceRole(name='Device Role 3', slug='device-role-3'),
|
||||
])
|
||||
)
|
||||
DeviceRole.objects.bulk_create(device_roles)
|
||||
|
||||
tags = create_tags('Alpha', 'Bravo', 'Charlie')
|
||||
|
||||
@@ -1468,6 +1529,13 @@ class DeviceRoleTestCase(ViewTestCases.OrganizationalObjectViewTestCase):
|
||||
"Device Role 6,device-role-6,0000ff",
|
||||
)
|
||||
|
||||
cls.csv_update_data = (
|
||||
"id,name,description",
|
||||
f"{device_roles[0].pk},Device Role 7,New description7",
|
||||
f"{device_roles[1].pk},Device Role 8,New description8",
|
||||
f"{device_roles[2].pk},Device Role 9,New description9",
|
||||
)
|
||||
|
||||
cls.bulk_edit_data = {
|
||||
'color': '00ff00',
|
||||
'description': 'New description',
|
||||
@@ -1482,11 +1550,12 @@ class PlatformTestCase(ViewTestCases.OrganizationalObjectViewTestCase):
|
||||
|
||||
manufacturer = Manufacturer.objects.create(name='Manufacturer 1', slug='manufacturer-1')
|
||||
|
||||
Platform.objects.bulk_create([
|
||||
platforms = (
|
||||
Platform(name='Platform 1', slug='platform-1', manufacturer=manufacturer),
|
||||
Platform(name='Platform 2', slug='platform-2', manufacturer=manufacturer),
|
||||
Platform(name='Platform 3', slug='platform-3', manufacturer=manufacturer),
|
||||
])
|
||||
)
|
||||
Platform.objects.bulk_create(platforms)
|
||||
|
||||
tags = create_tags('Alpha', 'Bravo', 'Charlie')
|
||||
|
||||
@@ -1507,6 +1576,13 @@ class PlatformTestCase(ViewTestCases.OrganizationalObjectViewTestCase):
|
||||
"Platform 6,platform-6,Sixth platform",
|
||||
)
|
||||
|
||||
cls.csv_update_data = (
|
||||
"id,name,description",
|
||||
f"{platforms[0].pk},Platform 7,Fourth platform7",
|
||||
f"{platforms[1].pk},Platform 8,Fifth platform8",
|
||||
f"{platforms[2].pk},Platform 9,Sixth platform9",
|
||||
)
|
||||
|
||||
cls.bulk_edit_data = {
|
||||
'napalm_driver': 'ios',
|
||||
'description': 'New description',
|
||||
@@ -1554,11 +1630,12 @@ class DeviceTestCase(ViewTestCases.PrimaryObjectViewTestCase):
|
||||
)
|
||||
Platform.objects.bulk_create(platforms)
|
||||
|
||||
Device.objects.bulk_create([
|
||||
devices = (
|
||||
Device(name='Device 1', site=sites[0], rack=racks[0], device_type=devicetypes[0], device_role=deviceroles[0], platform=platforms[0]),
|
||||
Device(name='Device 2', site=sites[0], rack=racks[0], device_type=devicetypes[0], device_role=deviceroles[0], platform=platforms[0]),
|
||||
Device(name='Device 3', site=sites[0], rack=racks[0], device_type=devicetypes[0], device_role=deviceroles[0], platform=platforms[0]),
|
||||
])
|
||||
)
|
||||
Device.objects.bulk_create(devices)
|
||||
|
||||
tags = create_tags('Alpha', 'Bravo', 'Charlie')
|
||||
|
||||
@@ -1595,6 +1672,13 @@ class DeviceTestCase(ViewTestCases.PrimaryObjectViewTestCase):
|
||||
"Device Role 1,Manufacturer 1,Device Type 1,active,Device 6,Site 1,Location 1,Rack 1,30,front,Virtual Chassis 1,3,30",
|
||||
)
|
||||
|
||||
cls.csv_update_data = (
|
||||
"id,status",
|
||||
f"{devices[0].pk},{DeviceStatusChoices.STATUS_DECOMMISSIONING}",
|
||||
f"{devices[1].pk},{DeviceStatusChoices.STATUS_DECOMMISSIONING}",
|
||||
f"{devices[2].pk},{DeviceStatusChoices.STATUS_DECOMMISSIONING}",
|
||||
)
|
||||
|
||||
cls.bulk_edit_data = {
|
||||
'device_type': devicetypes[1].pk,
|
||||
'device_role': deviceroles[1].pk,
|
||||
@@ -1815,6 +1899,13 @@ class ModuleTestCase(
|
||||
"Device 2,Module Bay 3,Module Type 3,C,C",
|
||||
)
|
||||
|
||||
cls.csv_update_data = (
|
||||
"id,serial",
|
||||
f"{modules[0].pk},Serial 2",
|
||||
f"{modules[1].pk},Serial 3",
|
||||
f"{modules[2].pk},Serial 1",
|
||||
)
|
||||
|
||||
@override_settings(EXEMPT_VIEW_PERMISSIONS=['*'])
|
||||
def test_module_component_replication(self):
|
||||
self.add_permissions('dcim.add_module')
|
||||
@@ -1894,11 +1985,12 @@ class ConsolePortTestCase(ViewTestCases.DeviceComponentViewTestCase):
|
||||
def setUpTestData(cls):
|
||||
device = create_test_device('Device 1')
|
||||
|
||||
ConsolePort.objects.bulk_create([
|
||||
console_ports = (
|
||||
ConsolePort(device=device, name='Console Port 1'),
|
||||
ConsolePort(device=device, name='Console Port 2'),
|
||||
ConsolePort(device=device, name='Console Port 3'),
|
||||
])
|
||||
)
|
||||
ConsolePort.objects.bulk_create(console_ports)
|
||||
|
||||
tags = create_tags('Alpha', 'Bravo', 'Charlie')
|
||||
|
||||
@@ -1932,6 +2024,13 @@ class ConsolePortTestCase(ViewTestCases.DeviceComponentViewTestCase):
|
||||
"Device 1,Console Port 6",
|
||||
)
|
||||
|
||||
cls.csv_update_data = (
|
||||
"id,name,description",
|
||||
f"{console_ports[0].pk},Console Port 7,New description7",
|
||||
f"{console_ports[1].pk},Console Port 8,New description8",
|
||||
f"{console_ports[2].pk},Console Port 9,New description9",
|
||||
)
|
||||
|
||||
@override_settings(EXEMPT_VIEW_PERMISSIONS=['*'])
|
||||
def test_trace(self):
|
||||
consoleport = ConsolePort.objects.first()
|
||||
@@ -1953,11 +2052,12 @@ class ConsoleServerPortTestCase(ViewTestCases.DeviceComponentViewTestCase):
|
||||
def setUpTestData(cls):
|
||||
device = create_test_device('Device 1')
|
||||
|
||||
ConsoleServerPort.objects.bulk_create([
|
||||
console_server_ports = (
|
||||
ConsoleServerPort(device=device, name='Console Server Port 1'),
|
||||
ConsoleServerPort(device=device, name='Console Server Port 2'),
|
||||
ConsoleServerPort(device=device, name='Console Server Port 3'),
|
||||
])
|
||||
)
|
||||
ConsoleServerPort.objects.bulk_create(console_server_ports)
|
||||
|
||||
tags = create_tags('Alpha', 'Bravo', 'Charlie')
|
||||
|
||||
@@ -1989,6 +2089,13 @@ class ConsoleServerPortTestCase(ViewTestCases.DeviceComponentViewTestCase):
|
||||
"Device 1,Console Server Port 6",
|
||||
)
|
||||
|
||||
cls.csv_update_data = (
|
||||
"id,name,description",
|
||||
f"{console_server_ports[0].pk},Console Server Port 7,New description 7",
|
||||
f"{console_server_ports[1].pk},Console Server Port 8,New description 8",
|
||||
f"{console_server_ports[2].pk},Console Server Port 9,New description 9",
|
||||
)
|
||||
|
||||
@override_settings(EXEMPT_VIEW_PERMISSIONS=['*'])
|
||||
def test_trace(self):
|
||||
consoleserverport = ConsoleServerPort.objects.first()
|
||||
@@ -2010,11 +2117,12 @@ class PowerPortTestCase(ViewTestCases.DeviceComponentViewTestCase):
|
||||
def setUpTestData(cls):
|
||||
device = create_test_device('Device 1')
|
||||
|
||||
PowerPort.objects.bulk_create([
|
||||
power_ports = (
|
||||
PowerPort(device=device, name='Power Port 1'),
|
||||
PowerPort(device=device, name='Power Port 2'),
|
||||
PowerPort(device=device, name='Power Port 3'),
|
||||
])
|
||||
)
|
||||
PowerPort.objects.bulk_create(power_ports)
|
||||
|
||||
tags = create_tags('Alpha', 'Bravo', 'Charlie')
|
||||
|
||||
@@ -2052,6 +2160,13 @@ class PowerPortTestCase(ViewTestCases.DeviceComponentViewTestCase):
|
||||
"Device 1,Power Port 6",
|
||||
)
|
||||
|
||||
cls.csv_update_data = (
|
||||
"id,name,description",
|
||||
f"{power_ports[0].pk},Power Port 7,New description7",
|
||||
f"{power_ports[1].pk},Power Port 8,New description8",
|
||||
f"{power_ports[2].pk},Power Port 9,New description9",
|
||||
)
|
||||
|
||||
@override_settings(EXEMPT_VIEW_PERMISSIONS=['*'])
|
||||
def test_trace(self):
|
||||
powerport = PowerPort.objects.first()
|
||||
@@ -2079,11 +2194,12 @@ class PowerOutletTestCase(ViewTestCases.DeviceComponentViewTestCase):
|
||||
)
|
||||
PowerPort.objects.bulk_create(powerports)
|
||||
|
||||
PowerOutlet.objects.bulk_create([
|
||||
power_outlets = (
|
||||
PowerOutlet(device=device, name='Power Outlet 1', power_port=powerports[0]),
|
||||
PowerOutlet(device=device, name='Power Outlet 2', power_port=powerports[0]),
|
||||
PowerOutlet(device=device, name='Power Outlet 3', power_port=powerports[0]),
|
||||
])
|
||||
)
|
||||
PowerOutlet.objects.bulk_create(power_outlets)
|
||||
|
||||
tags = create_tags('Alpha', 'Bravo', 'Charlie')
|
||||
|
||||
@@ -2121,6 +2237,13 @@ class PowerOutletTestCase(ViewTestCases.DeviceComponentViewTestCase):
|
||||
"Device 1,Power Outlet 6",
|
||||
)
|
||||
|
||||
cls.csv_update_data = (
|
||||
"id,name,description",
|
||||
f"{power_outlets[0].pk},Power Outlet 7,New description7",
|
||||
f"{power_outlets[1].pk},Power Outlet 8,New description8",
|
||||
f"{power_outlets[2].pk},Power Outlet 9,New description9",
|
||||
)
|
||||
|
||||
@override_settings(EXEMPT_VIEW_PERMISSIONS=['*'])
|
||||
def test_trace(self):
|
||||
poweroutlet = PowerOutlet.objects.first()
|
||||
@@ -2247,6 +2370,13 @@ class InterfaceTestCase(ViewTestCases.DeviceComponentViewTestCase):
|
||||
f"Device 1,Interface 6,1000base-t,{vrfs[0].pk},pse,type1-ieee802.3af",
|
||||
)
|
||||
|
||||
cls.csv_update_data = (
|
||||
"id,name,description",
|
||||
f"{interfaces[0].pk},Interface 7,New description7",
|
||||
f"{interfaces[1].pk},Interface 8,New description8",
|
||||
f"{interfaces[2].pk},Interface 9,New description9",
|
||||
)
|
||||
|
||||
@override_settings(EXEMPT_VIEW_PERMISSIONS=['*'])
|
||||
def test_trace(self):
|
||||
interface1, interface2 = Interface.objects.all()[:2]
|
||||
@@ -2274,11 +2404,12 @@ class FrontPortTestCase(ViewTestCases.DeviceComponentViewTestCase):
|
||||
)
|
||||
RearPort.objects.bulk_create(rearports)
|
||||
|
||||
FrontPort.objects.bulk_create([
|
||||
front_ports = (
|
||||
FrontPort(device=device, name='Front Port 1', rear_port=rearports[0]),
|
||||
FrontPort(device=device, name='Front Port 2', rear_port=rearports[1]),
|
||||
FrontPort(device=device, name='Front Port 3', rear_port=rearports[2]),
|
||||
])
|
||||
)
|
||||
FrontPort.objects.bulk_create(front_ports)
|
||||
|
||||
tags = create_tags('Alpha', 'Bravo', 'Charlie')
|
||||
|
||||
@@ -2313,6 +2444,13 @@ class FrontPortTestCase(ViewTestCases.DeviceComponentViewTestCase):
|
||||
"Device 1,Front Port 6,8p8c,Rear Port 6,1",
|
||||
)
|
||||
|
||||
cls.csv_update_data = (
|
||||
"id,name,description",
|
||||
f"{front_ports[0].pk},Front Port 7,New description7",
|
||||
f"{front_ports[1].pk},Front Port 8,New description8",
|
||||
f"{front_ports[2].pk},Front Port 9,New description9",
|
||||
)
|
||||
|
||||
@override_settings(EXEMPT_VIEW_PERMISSIONS=['*'])
|
||||
def test_trace(self):
|
||||
frontport = FrontPort.objects.first()
|
||||
@@ -2334,11 +2472,12 @@ class RearPortTestCase(ViewTestCases.DeviceComponentViewTestCase):
|
||||
def setUpTestData(cls):
|
||||
device = create_test_device('Device 1')
|
||||
|
||||
RearPort.objects.bulk_create([
|
||||
rear_ports = (
|
||||
RearPort(device=device, name='Rear Port 1'),
|
||||
RearPort(device=device, name='Rear Port 2'),
|
||||
RearPort(device=device, name='Rear Port 3'),
|
||||
])
|
||||
)
|
||||
RearPort.objects.bulk_create(rear_ports)
|
||||
|
||||
tags = create_tags('Alpha', 'Bravo', 'Charlie')
|
||||
|
||||
@@ -2372,6 +2511,13 @@ class RearPortTestCase(ViewTestCases.DeviceComponentViewTestCase):
|
||||
"Device 1,Rear Port 6,8p8c,1",
|
||||
)
|
||||
|
||||
cls.csv_update_data = (
|
||||
"id,name,description",
|
||||
f"{rear_ports[0].pk},Rear Port 7,New description7",
|
||||
f"{rear_ports[1].pk},Rear Port 8,New description8",
|
||||
f"{rear_ports[2].pk},Rear Port 9,New description9",
|
||||
)
|
||||
|
||||
@override_settings(EXEMPT_VIEW_PERMISSIONS=['*'])
|
||||
def test_trace(self):
|
||||
rearport = RearPort.objects.first()
|
||||
@@ -2393,11 +2539,12 @@ class ModuleBayTestCase(ViewTestCases.DeviceComponentViewTestCase):
|
||||
def setUpTestData(cls):
|
||||
device = create_test_device('Device 1')
|
||||
|
||||
ModuleBay.objects.bulk_create([
|
||||
module_bays = (
|
||||
ModuleBay(device=device, name='Module Bay 1'),
|
||||
ModuleBay(device=device, name='Module Bay 2'),
|
||||
ModuleBay(device=device, name='Module Bay 3'),
|
||||
])
|
||||
)
|
||||
ModuleBay.objects.bulk_create(module_bays)
|
||||
|
||||
tags = create_tags('Alpha', 'Bravo', 'Charlie')
|
||||
|
||||
@@ -2426,6 +2573,13 @@ class ModuleBayTestCase(ViewTestCases.DeviceComponentViewTestCase):
|
||||
"Device 1,Module Bay 6",
|
||||
)
|
||||
|
||||
cls.csv_update_data = (
|
||||
"id,name,description",
|
||||
f"{module_bays[0].pk},Module Bay 7,New description7",
|
||||
f"{module_bays[1].pk},Module Bay 8,New description8",
|
||||
f"{module_bays[2].pk},Module Bay 9,New description9",
|
||||
)
|
||||
|
||||
|
||||
class DeviceBayTestCase(ViewTestCases.DeviceComponentViewTestCase):
|
||||
model = DeviceBay
|
||||
@@ -2438,11 +2592,12 @@ class DeviceBayTestCase(ViewTestCases.DeviceComponentViewTestCase):
|
||||
# Update the DeviceType subdevice role to allow adding DeviceBays
|
||||
DeviceType.objects.update(subdevice_role=SubdeviceRoleChoices.ROLE_PARENT)
|
||||
|
||||
DeviceBay.objects.bulk_create([
|
||||
device_bays = (
|
||||
DeviceBay(device=device, name='Device Bay 1'),
|
||||
DeviceBay(device=device, name='Device Bay 2'),
|
||||
DeviceBay(device=device, name='Device Bay 3'),
|
||||
])
|
||||
)
|
||||
DeviceBay.objects.bulk_create(device_bays)
|
||||
|
||||
tags = create_tags('Alpha', 'Bravo', 'Charlie')
|
||||
|
||||
@@ -2471,6 +2626,13 @@ class DeviceBayTestCase(ViewTestCases.DeviceComponentViewTestCase):
|
||||
"Device 1,Device Bay 6",
|
||||
)
|
||||
|
||||
cls.csv_update_data = (
|
||||
"id,name,description",
|
||||
f"{device_bays[0].pk},Device Bay 7,New description7",
|
||||
f"{device_bays[1].pk},Device Bay 8,New description8",
|
||||
f"{device_bays[2].pk},Device Bay 9,New description9",
|
||||
)
|
||||
|
||||
|
||||
class InventoryItemTestCase(ViewTestCases.DeviceComponentViewTestCase):
|
||||
model = InventoryItem
|
||||
@@ -2487,9 +2649,9 @@ class InventoryItemTestCase(ViewTestCases.DeviceComponentViewTestCase):
|
||||
)
|
||||
InventoryItemRole.objects.bulk_create(roles)
|
||||
|
||||
InventoryItem.objects.create(device=device, name='Inventory Item 1', role=roles[0], manufacturer=manufacturer)
|
||||
InventoryItem.objects.create(device=device, name='Inventory Item 2', role=roles[0], manufacturer=manufacturer)
|
||||
InventoryItem.objects.create(device=device, name='Inventory Item 3', role=roles[0], manufacturer=manufacturer)
|
||||
inventory_item1 = InventoryItem.objects.create(device=device, name='Inventory Item 1', role=roles[0], manufacturer=manufacturer)
|
||||
inventory_item2 = InventoryItem.objects.create(device=device, name='Inventory Item 2', role=roles[0], manufacturer=manufacturer)
|
||||
inventory_item3 = InventoryItem.objects.create(device=device, name='Inventory Item 3', role=roles[0], manufacturer=manufacturer)
|
||||
|
||||
tags = create_tags('Alpha', 'Bravo', 'Charlie')
|
||||
|
||||
@@ -2533,6 +2695,13 @@ class InventoryItemTestCase(ViewTestCases.DeviceComponentViewTestCase):
|
||||
"Device 1,Inventory Item 6,Inventory Item 3",
|
||||
)
|
||||
|
||||
cls.csv_update_data = (
|
||||
"id,name,description",
|
||||
f"{inventory_item1.pk},Inventory Item 7,New description7",
|
||||
f"{inventory_item2.pk},Inventory Item 8,New description8",
|
||||
f"{inventory_item3.pk},Inventory Item 9,New description9",
|
||||
)
|
||||
|
||||
|
||||
class InventoryItemRoleTestCase(ViewTestCases.OrganizationalObjectViewTestCase):
|
||||
model = InventoryItemRole
|
||||
@@ -2540,11 +2709,12 @@ class InventoryItemRoleTestCase(ViewTestCases.OrganizationalObjectViewTestCase):
|
||||
@classmethod
|
||||
def setUpTestData(cls):
|
||||
|
||||
InventoryItemRole.objects.bulk_create([
|
||||
inventory_item_roles = (
|
||||
InventoryItemRole(name='Inventory Item Role 1', slug='inventory-item-role-1'),
|
||||
InventoryItemRole(name='Inventory Item Role 2', slug='inventory-item-role-2'),
|
||||
InventoryItemRole(name='Inventory Item Role 3', slug='inventory-item-role-3'),
|
||||
])
|
||||
)
|
||||
InventoryItemRole.objects.bulk_create(inventory_item_roles)
|
||||
|
||||
tags = create_tags('Alpha', 'Bravo', 'Charlie')
|
||||
|
||||
@@ -2563,6 +2733,13 @@ class InventoryItemRoleTestCase(ViewTestCases.OrganizationalObjectViewTestCase):
|
||||
"Inventory Item Role 6,inventory-item-role-6,0000ff",
|
||||
)
|
||||
|
||||
cls.csv_update_data = (
|
||||
"id,name,description",
|
||||
f"{inventory_item_roles[0].pk},Inventory Item Role 7,New description7",
|
||||
f"{inventory_item_roles[1].pk},Inventory Item Role 8,New description8",
|
||||
f"{inventory_item_roles[2].pk},Inventory Item Role 9,New description9",
|
||||
)
|
||||
|
||||
cls.bulk_edit_data = {
|
||||
'color': '00ff00',
|
||||
'description': 'New description',
|
||||
@@ -2615,9 +2792,12 @@ class CableTestCase(
|
||||
)
|
||||
Interface.objects.bulk_create(interfaces)
|
||||
|
||||
Cable(a_terminations=[interfaces[0]], b_terminations=[interfaces[3]], type=CableTypeChoices.TYPE_CAT6).save()
|
||||
Cable(a_terminations=[interfaces[1]], b_terminations=[interfaces[4]], type=CableTypeChoices.TYPE_CAT6).save()
|
||||
Cable(a_terminations=[interfaces[2]], b_terminations=[interfaces[5]], type=CableTypeChoices.TYPE_CAT6).save()
|
||||
cable1 = Cable(a_terminations=[interfaces[0]], b_terminations=[interfaces[3]], type=CableTypeChoices.TYPE_CAT6)
|
||||
cable1.save()
|
||||
cable2 = Cable(a_terminations=[interfaces[1]], b_terminations=[interfaces[4]], type=CableTypeChoices.TYPE_CAT6)
|
||||
cable2.save()
|
||||
cable3 = Cable(a_terminations=[interfaces[2]], b_terminations=[interfaces[5]], type=CableTypeChoices.TYPE_CAT6)
|
||||
cable3.save()
|
||||
|
||||
tags = create_tags('Alpha', 'Bravo', 'Charlie')
|
||||
|
||||
@@ -2643,6 +2823,13 @@ class CableTestCase(
|
||||
"Device 3,dcim.interface,Interface 3,Device 4,dcim.interface,Interface 3",
|
||||
)
|
||||
|
||||
cls.csv_update_data = (
|
||||
"id,label,color",
|
||||
f"{cable1.pk},New label7,00ff00",
|
||||
f"{cable2.pk},New label8,00ff00",
|
||||
f"{cable3.pk},New label9,00ff00",
|
||||
)
|
||||
|
||||
cls.bulk_edit_data = {
|
||||
'type': CableTypeChoices.TYPE_CAT5E,
|
||||
'status': LinkStatusChoices.STATUS_CONNECTED,
|
||||
@@ -2726,6 +2913,13 @@ class VirtualChassisTestCase(ViewTestCases.PrimaryObjectViewTestCase):
|
||||
"VC6,Domain 6,Device 12",
|
||||
)
|
||||
|
||||
cls.csv_update_data = (
|
||||
"id,name,domain",
|
||||
f"{vc1.pk},VC7,Domain 7",
|
||||
f"{vc2.pk},VC8,Domain 8",
|
||||
f"{vc3.pk},VC9,Domain 9",
|
||||
)
|
||||
|
||||
cls.bulk_edit_data = {
|
||||
'domain': 'domain-x',
|
||||
}
|
||||
@@ -2750,11 +2944,12 @@ class PowerPanelTestCase(ViewTestCases.PrimaryObjectViewTestCase):
|
||||
for location in locations:
|
||||
location.save()
|
||||
|
||||
PowerPanel.objects.bulk_create((
|
||||
power_panels = (
|
||||
PowerPanel(site=sites[0], location=locations[0], name='Power Panel 1'),
|
||||
PowerPanel(site=sites[0], location=locations[0], name='Power Panel 2'),
|
||||
PowerPanel(site=sites[0], location=locations[0], name='Power Panel 3'),
|
||||
))
|
||||
)
|
||||
PowerPanel.objects.bulk_create(power_panels)
|
||||
|
||||
tags = create_tags('Alpha', 'Bravo', 'Charlie')
|
||||
|
||||
@@ -2772,6 +2967,13 @@ class PowerPanelTestCase(ViewTestCases.PrimaryObjectViewTestCase):
|
||||
"Site 1,Location 1,Power Panel 6",
|
||||
)
|
||||
|
||||
cls.csv_update_data = (
|
||||
"id,name",
|
||||
f"{power_panels[0].pk},Power Panel 7",
|
||||
f"{power_panels[1].pk},Power Panel 8",
|
||||
f"{power_panels[2].pk},Power Panel 9",
|
||||
)
|
||||
|
||||
cls.bulk_edit_data = {
|
||||
'site': sites[1].pk,
|
||||
'location': locations[1].pk,
|
||||
@@ -2798,11 +3000,12 @@ class PowerFeedTestCase(ViewTestCases.PrimaryObjectViewTestCase):
|
||||
)
|
||||
Rack.objects.bulk_create(racks)
|
||||
|
||||
PowerFeed.objects.bulk_create((
|
||||
power_feeds = (
|
||||
PowerFeed(name='Power Feed 1', power_panel=powerpanels[0], rack=racks[0]),
|
||||
PowerFeed(name='Power Feed 2', power_panel=powerpanels[0], rack=racks[0]),
|
||||
PowerFeed(name='Power Feed 3', power_panel=powerpanels[0], rack=racks[0]),
|
||||
))
|
||||
)
|
||||
PowerFeed.objects.bulk_create(power_feeds)
|
||||
|
||||
tags = create_tags('Alpha', 'Bravo', 'Charlie')
|
||||
|
||||
@@ -2828,6 +3031,13 @@ class PowerFeedTestCase(ViewTestCases.PrimaryObjectViewTestCase):
|
||||
"Site 1,Power Panel 1,Power Feed 6,active,primary,ac,single-phase,120,20,80",
|
||||
)
|
||||
|
||||
cls.csv_update_data = (
|
||||
"id,name,status",
|
||||
f"{power_feeds[0].pk},Power Feed 7,{PowerFeedStatusChoices.STATUS_PLANNED}",
|
||||
f"{power_feeds[1].pk},Power Feed 8,{PowerFeedStatusChoices.STATUS_PLANNED}",
|
||||
f"{power_feeds[2].pk},Power Feed 9,{PowerFeedStatusChoices.STATUS_PLANNED}",
|
||||
)
|
||||
|
||||
cls.bulk_edit_data = {
|
||||
'power_panel': powerpanels[1].pk,
|
||||
'rack': racks[1].pk,
|
||||
|
||||
@@ -48,6 +48,13 @@ class CustomFieldTestCase(ViewTestCases.PrimaryObjectViewTestCase):
|
||||
'field7,Field 7,object,dcim.site,dcim.region,100,4000,exact,,,,,read-write',
|
||||
)
|
||||
|
||||
cls.csv_update_data = (
|
||||
'id,label',
|
||||
f'{custom_fields[0].pk},New label 1',
|
||||
f'{custom_fields[1].pk},New label 2',
|
||||
f'{custom_fields[2].pk},New label 3',
|
||||
)
|
||||
|
||||
cls.bulk_edit_data = {
|
||||
'required': True,
|
||||
'weight': 200,
|
||||
@@ -86,6 +93,13 @@ class CustomLinkTestCase(ViewTestCases.PrimaryObjectViewTestCase):
|
||||
"Custom Link 6,dcim.site,False,100,blue,Link 6,http://exmaple.com/?6",
|
||||
)
|
||||
|
||||
cls.csv_update_data = (
|
||||
"id,name",
|
||||
f"{custom_links[0].pk},Custom Link 7",
|
||||
f"{custom_links[1].pk},Custom Link 8",
|
||||
f"{custom_links[2].pk},Custom Link 9",
|
||||
)
|
||||
|
||||
cls.bulk_edit_data = {
|
||||
'button_class': CustomLinkButtonClassChoices.CYAN,
|
||||
'enabled': False,
|
||||
@@ -123,6 +137,13 @@ class ExportTemplateTestCase(ViewTestCases.PrimaryObjectViewTestCase):
|
||||
f"Export Template 6,dcim.site,{TEMPLATE_CODE}",
|
||||
)
|
||||
|
||||
cls.csv_update_data = (
|
||||
"id,name",
|
||||
f"{export_templates[0].pk},Export Template 7",
|
||||
f"{export_templates[1].pk},Export Template 8",
|
||||
f"{export_templates[2].pk},Export Template 9",
|
||||
)
|
||||
|
||||
cls.bulk_edit_data = {
|
||||
'mime_type': 'text/html',
|
||||
'file_extension': 'html',
|
||||
@@ -165,6 +186,13 @@ class WebhookTestCase(ViewTestCases.PrimaryObjectViewTestCase):
|
||||
"Webhook 6,dcim.site,True,http://example.com/?6,GET,application/json",
|
||||
)
|
||||
|
||||
cls.csv_update_data = (
|
||||
"id,name",
|
||||
f"{webhooks[0].pk},Webhook 7",
|
||||
f"{webhooks[1].pk},Webhook 8",
|
||||
f"{webhooks[2].pk},Webhook 9",
|
||||
)
|
||||
|
||||
cls.bulk_edit_data = {
|
||||
'enabled': False,
|
||||
'type_create': False,
|
||||
@@ -180,11 +208,12 @@ class TagTestCase(ViewTestCases.OrganizationalObjectViewTestCase):
|
||||
@classmethod
|
||||
def setUpTestData(cls):
|
||||
|
||||
Tag.objects.bulk_create((
|
||||
tags = (
|
||||
Tag(name='Tag 1', slug='tag-1'),
|
||||
Tag(name='Tag 2', slug='tag-2'),
|
||||
Tag(name='Tag 3', slug='tag-3'),
|
||||
))
|
||||
)
|
||||
Tag.objects.bulk_create(tags)
|
||||
|
||||
cls.form_data = {
|
||||
'name': 'Tag X',
|
||||
@@ -200,6 +229,13 @@ class TagTestCase(ViewTestCases.OrganizationalObjectViewTestCase):
|
||||
"Tag 6,tag-6,0000ff,Sixth tag",
|
||||
)
|
||||
|
||||
cls.csv_update_data = (
|
||||
"id,name,description",
|
||||
f"{tags[0].pk},Tag 7,Fourth tag7",
|
||||
f"{tags[1].pk},Tag 8,Fifth tag8",
|
||||
f"{tags[2].pk},Tag 9,Sixth tag9",
|
||||
)
|
||||
|
||||
cls.bulk_edit_data = {
|
||||
'color': '00ff00',
|
||||
}
|
||||
|
||||
@@ -298,13 +298,13 @@ class IPAddressCSVForm(NetBoxModelCSVForm):
|
||||
def save(self, *args, **kwargs):
|
||||
|
||||
# Set interface assignment
|
||||
if self.cleaned_data['interface']:
|
||||
if self.cleaned_data.get('interface'):
|
||||
self.instance.assigned_object = self.cleaned_data['interface']
|
||||
|
||||
ipaddress = super().save(*args, **kwargs)
|
||||
|
||||
# Set as primary for device/VM
|
||||
if self.cleaned_data['is_primary']:
|
||||
if self.cleaned_data.get('is_primary'):
|
||||
parent = self.cleaned_data['device'] or self.cleaned_data['virtual_machine']
|
||||
if self.instance.address.version == 4:
|
||||
parent.primary_ip4 = ipaddress
|
||||
|
||||
@@ -60,6 +60,13 @@ class ASNTestCase(ViewTestCases.PrimaryObjectViewTestCase):
|
||||
"4200000002,RFC 6996",
|
||||
)
|
||||
|
||||
cls.csv_update_data = (
|
||||
"id,description",
|
||||
f"{asns[0].pk},New description1",
|
||||
f"{asns[1].pk},New description2",
|
||||
f"{asns[2].pk},New description3",
|
||||
)
|
||||
|
||||
cls.bulk_edit_data = {
|
||||
'rir': rirs[1].pk,
|
||||
'description': 'Next description',
|
||||
@@ -78,11 +85,12 @@ class VRFTestCase(ViewTestCases.PrimaryObjectViewTestCase):
|
||||
)
|
||||
Tenant.objects.bulk_create(tenants)
|
||||
|
||||
VRF.objects.bulk_create([
|
||||
vrfs = (
|
||||
VRF(name='VRF 1', rd='65000:1'),
|
||||
VRF(name='VRF 2', rd='65000:2'),
|
||||
VRF(name='VRF 3', rd='65000:3'),
|
||||
])
|
||||
)
|
||||
VRF.objects.bulk_create(vrfs)
|
||||
|
||||
tags = create_tags('Alpha', 'Bravo', 'Charlie')
|
||||
|
||||
@@ -102,6 +110,13 @@ class VRFTestCase(ViewTestCases.PrimaryObjectViewTestCase):
|
||||
"VRF 6",
|
||||
)
|
||||
|
||||
cls.csv_update_data = (
|
||||
"id,name",
|
||||
f"{vrfs[0].pk},VRF 7",
|
||||
f"{vrfs[1].pk},VRF 8",
|
||||
f"{vrfs[2].pk},VRF 9",
|
||||
)
|
||||
|
||||
cls.bulk_edit_data = {
|
||||
'tenant': tenants[1].pk,
|
||||
'enforce_unique': False,
|
||||
@@ -143,6 +158,13 @@ class RouteTargetTestCase(ViewTestCases.PrimaryObjectViewTestCase):
|
||||
"65000:1006,,No tenant",
|
||||
)
|
||||
|
||||
cls.csv_update_data = (
|
||||
"id,name,description",
|
||||
f"{route_targets[0].pk},65000:1007,New description1",
|
||||
f"{route_targets[1].pk},65000:1008,New description2",
|
||||
f"{route_targets[2].pk},65000:1009,New description3",
|
||||
)
|
||||
|
||||
cls.bulk_edit_data = {
|
||||
'tenant': tenants[1].pk,
|
||||
'description': 'New description',
|
||||
@@ -155,11 +177,12 @@ class RIRTestCase(ViewTestCases.OrganizationalObjectViewTestCase):
|
||||
@classmethod
|
||||
def setUpTestData(cls):
|
||||
|
||||
RIR.objects.bulk_create([
|
||||
rirs = (
|
||||
RIR(name='RIR 1', slug='rir-1'),
|
||||
RIR(name='RIR 2', slug='rir-2'),
|
||||
RIR(name='RIR 3', slug='rir-3'),
|
||||
])
|
||||
)
|
||||
RIR.objects.bulk_create(rirs)
|
||||
|
||||
tags = create_tags('Alpha', 'Bravo', 'Charlie')
|
||||
|
||||
@@ -178,6 +201,13 @@ class RIRTestCase(ViewTestCases.OrganizationalObjectViewTestCase):
|
||||
"RIR 6,rir-6,Sixth RIR",
|
||||
)
|
||||
|
||||
cls.csv_update_data = (
|
||||
"id,name,description",
|
||||
f"{rirs[0].pk},RIR 7,Fourth RIR7",
|
||||
f"{rirs[1].pk},RIR 8,Fifth RIR8",
|
||||
f"{rirs[2].pk},RIR 9,Sixth RIR9",
|
||||
)
|
||||
|
||||
cls.bulk_edit_data = {
|
||||
'description': 'New description',
|
||||
}
|
||||
@@ -195,11 +225,12 @@ class AggregateTestCase(ViewTestCases.PrimaryObjectViewTestCase):
|
||||
)
|
||||
RIR.objects.bulk_create(rirs)
|
||||
|
||||
Aggregate.objects.bulk_create([
|
||||
aggregates = (
|
||||
Aggregate(prefix=IPNetwork('10.1.0.0/16'), rir=rirs[0]),
|
||||
Aggregate(prefix=IPNetwork('10.2.0.0/16'), rir=rirs[0]),
|
||||
Aggregate(prefix=IPNetwork('10.3.0.0/16'), rir=rirs[0]),
|
||||
])
|
||||
)
|
||||
Aggregate.objects.bulk_create(aggregates)
|
||||
|
||||
tags = create_tags('Alpha', 'Bravo', 'Charlie')
|
||||
|
||||
@@ -218,6 +249,13 @@ class AggregateTestCase(ViewTestCases.PrimaryObjectViewTestCase):
|
||||
"10.6.0.0/16,RIR 1",
|
||||
)
|
||||
|
||||
cls.csv_update_data = (
|
||||
"id,description",
|
||||
f"{aggregates[0].pk},New description1",
|
||||
f"{aggregates[1].pk},New description2",
|
||||
f"{aggregates[2].pk},New description3",
|
||||
)
|
||||
|
||||
cls.bulk_edit_data = {
|
||||
'rir': rirs[1].pk,
|
||||
'date_added': datetime.date(2020, 1, 1),
|
||||
@@ -246,11 +284,12 @@ class RoleTestCase(ViewTestCases.OrganizationalObjectViewTestCase):
|
||||
@classmethod
|
||||
def setUpTestData(cls):
|
||||
|
||||
Role.objects.bulk_create([
|
||||
roles = (
|
||||
Role(name='Role 1', slug='role-1'),
|
||||
Role(name='Role 2', slug='role-2'),
|
||||
Role(name='Role 3', slug='role-3'),
|
||||
])
|
||||
)
|
||||
Role.objects.bulk_create(roles)
|
||||
|
||||
tags = create_tags('Alpha', 'Bravo', 'Charlie')
|
||||
|
||||
@@ -269,6 +308,13 @@ class RoleTestCase(ViewTestCases.OrganizationalObjectViewTestCase):
|
||||
"Role 6,role-6,1000",
|
||||
)
|
||||
|
||||
cls.csv_update_data = (
|
||||
"id,name,description",
|
||||
f"{roles[0].pk},Role 7,New description7",
|
||||
f"{roles[1].pk},Role 8,New description8",
|
||||
f"{roles[2].pk},Role 9,New description9",
|
||||
)
|
||||
|
||||
cls.bulk_edit_data = {
|
||||
'description': 'New description',
|
||||
}
|
||||
@@ -298,11 +344,12 @@ class PrefixTestCase(ViewTestCases.PrimaryObjectViewTestCase):
|
||||
)
|
||||
Role.objects.bulk_create(roles)
|
||||
|
||||
Prefix.objects.bulk_create([
|
||||
prefixes = (
|
||||
Prefix(prefix=IPNetwork('10.1.0.0/16'), vrf=vrfs[0], site=sites[0], role=roles[0]),
|
||||
Prefix(prefix=IPNetwork('10.2.0.0/16'), vrf=vrfs[0], site=sites[0], role=roles[0]),
|
||||
Prefix(prefix=IPNetwork('10.3.0.0/16'), vrf=vrfs[0], site=sites[0], role=roles[0]),
|
||||
])
|
||||
)
|
||||
Prefix.objects.bulk_create(prefixes)
|
||||
|
||||
tags = create_tags('Alpha', 'Bravo', 'Charlie')
|
||||
|
||||
@@ -326,6 +373,13 @@ class PrefixTestCase(ViewTestCases.PrimaryObjectViewTestCase):
|
||||
"VRF 1,10.6.0.0/16,active",
|
||||
)
|
||||
|
||||
cls.csv_update_data = (
|
||||
"id,description,status",
|
||||
f"{prefixes[0].pk},New description 7,{PrefixStatusChoices.STATUS_RESERVED}",
|
||||
f"{prefixes[1].pk},New description 8,{PrefixStatusChoices.STATUS_RESERVED}",
|
||||
f"{prefixes[2].pk},New description 9,{PrefixStatusChoices.STATUS_RESERVED}",
|
||||
)
|
||||
|
||||
cls.bulk_edit_data = {
|
||||
'site': sites[1].pk,
|
||||
'vrf': vrfs[1].pk,
|
||||
@@ -428,6 +482,13 @@ class IPRangeTestCase(ViewTestCases.PrimaryObjectViewTestCase):
|
||||
"VRF 1,10.3.0.1/16,10.3.9.254/16,active",
|
||||
)
|
||||
|
||||
cls.csv_update_data = (
|
||||
"id,description,status",
|
||||
f"{ip_ranges[0].pk},New description 7,{IPRangeStatusChoices.STATUS_RESERVED}",
|
||||
f"{ip_ranges[1].pk},New description 8,{IPRangeStatusChoices.STATUS_RESERVED}",
|
||||
f"{ip_ranges[2].pk},New description 9,{IPRangeStatusChoices.STATUS_RESERVED}",
|
||||
)
|
||||
|
||||
cls.bulk_edit_data = {
|
||||
'vrf': vrfs[1].pk,
|
||||
'tenant': None,
|
||||
@@ -467,11 +528,12 @@ class IPAddressTestCase(ViewTestCases.PrimaryObjectViewTestCase):
|
||||
)
|
||||
VRF.objects.bulk_create(vrfs)
|
||||
|
||||
IPAddress.objects.bulk_create([
|
||||
ipaddresses = (
|
||||
IPAddress(address=IPNetwork('192.0.2.1/24'), vrf=vrfs[0]),
|
||||
IPAddress(address=IPNetwork('192.0.2.2/24'), vrf=vrfs[0]),
|
||||
IPAddress(address=IPNetwork('192.0.2.3/24'), vrf=vrfs[0]),
|
||||
])
|
||||
)
|
||||
IPAddress.objects.bulk_create(ipaddresses)
|
||||
|
||||
tags = create_tags('Alpha', 'Bravo', 'Charlie')
|
||||
|
||||
@@ -494,6 +556,13 @@ class IPAddressTestCase(ViewTestCases.PrimaryObjectViewTestCase):
|
||||
"VRF 1,192.0.2.6/24,active",
|
||||
)
|
||||
|
||||
cls.csv_update_data = (
|
||||
"id,description,status",
|
||||
f"{ipaddresses[0].pk},New description 7,{IPAddressStatusChoices.STATUS_RESERVED}",
|
||||
f"{ipaddresses[1].pk},New description 8,{IPAddressStatusChoices.STATUS_RESERVED}",
|
||||
f"{ipaddresses[2].pk},New description 9,{IPAddressStatusChoices.STATUS_RESERVED}",
|
||||
)
|
||||
|
||||
cls.bulk_edit_data = {
|
||||
'vrf': vrfs[1].pk,
|
||||
'tenant': None,
|
||||
@@ -510,11 +579,12 @@ class FHRPGroupTestCase(ViewTestCases.PrimaryObjectViewTestCase):
|
||||
@classmethod
|
||||
def setUpTestData(cls):
|
||||
|
||||
FHRPGroup.objects.bulk_create((
|
||||
fhrp_groups = (
|
||||
FHRPGroup(protocol=FHRPGroupProtocolChoices.PROTOCOL_VRRP2, group_id=10, auth_type=FHRPGroupAuthTypeChoices.AUTHENTICATION_PLAINTEXT, auth_key='foobar123'),
|
||||
FHRPGroup(protocol=FHRPGroupProtocolChoices.PROTOCOL_VRRP3, group_id=20, auth_type=FHRPGroupAuthTypeChoices.AUTHENTICATION_MD5, auth_key='foobar123'),
|
||||
FHRPGroup(protocol=FHRPGroupProtocolChoices.PROTOCOL_HSRP, group_id=30),
|
||||
))
|
||||
)
|
||||
FHRPGroup.objects.bulk_create(fhrp_groups)
|
||||
|
||||
tags = create_tags('Alpha', 'Bravo', 'Charlie')
|
||||
|
||||
@@ -535,6 +605,13 @@ class FHRPGroupTestCase(ViewTestCases.PrimaryObjectViewTestCase):
|
||||
"hsrp,60,,,",
|
||||
)
|
||||
|
||||
cls.csv_update_data = (
|
||||
"id,name,description",
|
||||
f"{fhrp_groups[0].pk},FHRP Group 1,New description 1",
|
||||
f"{fhrp_groups[1].pk},FHRP Group 2,New description 2",
|
||||
f"{fhrp_groups[2].pk},FHRP Group 3,New description 3",
|
||||
)
|
||||
|
||||
cls.bulk_edit_data = {
|
||||
'protocol': FHRPGroupProtocolChoices.PROTOCOL_CARP,
|
||||
}
|
||||
@@ -552,11 +629,12 @@ class VLANGroupTestCase(ViewTestCases.OrganizationalObjectViewTestCase):
|
||||
)
|
||||
Site.objects.bulk_create(sites)
|
||||
|
||||
VLANGroup.objects.bulk_create([
|
||||
vlan_groups = (
|
||||
VLANGroup(name='VLAN Group 1', slug='vlan-group-1', scope=sites[0]),
|
||||
VLANGroup(name='VLAN Group 2', slug='vlan-group-2', scope=sites[0]),
|
||||
VLANGroup(name='VLAN Group 3', slug='vlan-group-3', scope=sites[0]),
|
||||
])
|
||||
)
|
||||
VLANGroup.objects.bulk_create(vlan_groups)
|
||||
|
||||
tags = create_tags('Alpha', 'Bravo', 'Charlie')
|
||||
|
||||
@@ -576,6 +654,13 @@ class VLANGroupTestCase(ViewTestCases.OrganizationalObjectViewTestCase):
|
||||
f"VLAN Group 6,vlan-group-6,dcim.site,{sites[1].pk},Sixth VLAN group",
|
||||
)
|
||||
|
||||
cls.csv_update_data = (
|
||||
f"id,name,description",
|
||||
f"{vlan_groups[0].pk},VLAN Group 7,Fourth VLAN group7",
|
||||
f"{vlan_groups[1].pk},VLAN Group 8,Fifth VLAN group8",
|
||||
f"{vlan_groups[2].pk},VLAN Group 9,Sixth VLAN group9",
|
||||
)
|
||||
|
||||
cls.bulk_edit_data = {
|
||||
'description': 'New description',
|
||||
}
|
||||
@@ -605,11 +690,12 @@ class VLANTestCase(ViewTestCases.PrimaryObjectViewTestCase):
|
||||
)
|
||||
Role.objects.bulk_create(roles)
|
||||
|
||||
VLAN.objects.bulk_create([
|
||||
vlans = (
|
||||
VLAN(group=vlangroups[0], vid=101, name='VLAN101', site=sites[0], role=roles[0]),
|
||||
VLAN(group=vlangroups[0], vid=102, name='VLAN102', site=sites[0], role=roles[0]),
|
||||
VLAN(group=vlangroups[0], vid=103, name='VLAN103', site=sites[0], role=roles[0]),
|
||||
])
|
||||
)
|
||||
VLAN.objects.bulk_create(vlans)
|
||||
|
||||
tags = create_tags('Alpha', 'Bravo', 'Charlie')
|
||||
|
||||
@@ -632,6 +718,13 @@ class VLANTestCase(ViewTestCases.PrimaryObjectViewTestCase):
|
||||
"106,VLAN106,active",
|
||||
)
|
||||
|
||||
cls.csv_update_data = (
|
||||
"id,name,description",
|
||||
f"{vlans[0].pk},VLAN107,New description 7",
|
||||
f"{vlans[1].pk},VLAN108,New description 8",
|
||||
f"{vlans[2].pk},VLAN109,New description 9",
|
||||
)
|
||||
|
||||
cls.bulk_edit_data = {
|
||||
'site': sites[1].pk,
|
||||
'group': vlangroups[1].pk,
|
||||
@@ -647,11 +740,12 @@ class ServiceTemplateTestCase(ViewTestCases.PrimaryObjectViewTestCase):
|
||||
|
||||
@classmethod
|
||||
def setUpTestData(cls):
|
||||
ServiceTemplate.objects.bulk_create([
|
||||
service_templates = (
|
||||
ServiceTemplate(name='Service Template 1', protocol=ServiceProtocolChoices.PROTOCOL_TCP, ports=[101]),
|
||||
ServiceTemplate(name='Service Template 2', protocol=ServiceProtocolChoices.PROTOCOL_TCP, ports=[102]),
|
||||
ServiceTemplate(name='Service Template 3', protocol=ServiceProtocolChoices.PROTOCOL_TCP, ports=[103]),
|
||||
])
|
||||
)
|
||||
ServiceTemplate.objects.bulk_create(service_templates)
|
||||
|
||||
tags = create_tags('Alpha', 'Bravo', 'Charlie')
|
||||
|
||||
@@ -670,6 +764,13 @@ class ServiceTemplateTestCase(ViewTestCases.PrimaryObjectViewTestCase):
|
||||
"Service Template 6,tcp,3,Third service template",
|
||||
)
|
||||
|
||||
cls.csv_update_data = (
|
||||
"id,name,description",
|
||||
f"{service_templates[0].pk},Service Template 7,First service template7",
|
||||
f"{service_templates[1].pk},Service Template 8,Second service template8",
|
||||
f"{service_templates[2].pk},Service Template 9,Third service template9",
|
||||
)
|
||||
|
||||
cls.bulk_edit_data = {
|
||||
'protocol': ServiceProtocolChoices.PROTOCOL_UDP,
|
||||
'ports': '106,107',
|
||||
@@ -689,11 +790,12 @@ class ServiceTestCase(ViewTestCases.PrimaryObjectViewTestCase):
|
||||
devicerole = DeviceRole.objects.create(name='Device Role 1', slug='device-role-1')
|
||||
device = Device.objects.create(name='Device 1', site=site, device_type=devicetype, device_role=devicerole)
|
||||
|
||||
Service.objects.bulk_create([
|
||||
services = (
|
||||
Service(device=device, name='Service 1', protocol=ServiceProtocolChoices.PROTOCOL_TCP, ports=[101]),
|
||||
Service(device=device, name='Service 2', protocol=ServiceProtocolChoices.PROTOCOL_TCP, ports=[102]),
|
||||
Service(device=device, name='Service 3', protocol=ServiceProtocolChoices.PROTOCOL_TCP, ports=[103]),
|
||||
])
|
||||
)
|
||||
Service.objects.bulk_create(services)
|
||||
|
||||
tags = create_tags('Alpha', 'Bravo', 'Charlie')
|
||||
|
||||
@@ -715,6 +817,13 @@ class ServiceTestCase(ViewTestCases.PrimaryObjectViewTestCase):
|
||||
"Device 1,Service 3,udp,3,Third service",
|
||||
)
|
||||
|
||||
cls.csv_update_data = (
|
||||
"id,name,description",
|
||||
f"{services[0].pk},Service 7,First service7",
|
||||
f"{services[1].pk},Service 8,Second service8",
|
||||
f"{services[2].pk},Service 9,Third service9",
|
||||
)
|
||||
|
||||
cls.bulk_edit_data = {
|
||||
'protocol': ServiceProtocolChoices.PROTOCOL_UDP,
|
||||
'ports': '106,107',
|
||||
@@ -751,14 +860,6 @@ class ServiceTestCase(ViewTestCases.PrimaryObjectViewTestCase):
|
||||
|
||||
class L2VPNTestCase(ViewTestCases.PrimaryObjectViewTestCase):
|
||||
model = L2VPN
|
||||
csv_data = (
|
||||
'name,slug,type,identifier',
|
||||
'L2VPN 5,l2vpn-5,vxlan,456',
|
||||
'L2VPN 6,l2vpn-6,vxlan,444',
|
||||
)
|
||||
bulk_edit_data = {
|
||||
'description': 'New Description',
|
||||
}
|
||||
|
||||
@classmethod
|
||||
def setUpTestData(cls):
|
||||
@@ -773,9 +874,24 @@ class L2VPNTestCase(ViewTestCases.PrimaryObjectViewTestCase):
|
||||
L2VPN(name='L2VPN 2', slug='l2vpn-2', type=L2VPNTypeChoices.TYPE_VXLAN, identifier='650002'),
|
||||
L2VPN(name='L2VPN 3', slug='l2vpn-3', type=L2VPNTypeChoices.TYPE_VXLAN, identifier='650003')
|
||||
)
|
||||
|
||||
L2VPN.objects.bulk_create(l2vpns)
|
||||
|
||||
cls.csv_data = (
|
||||
'name,slug,type,identifier',
|
||||
'L2VPN 5,l2vpn-5,vxlan,456',
|
||||
'L2VPN 6,l2vpn-6,vxlan,444',
|
||||
)
|
||||
|
||||
cls.csv_update_data = (
|
||||
'id,name,description',
|
||||
f'{l2vpns[0].pk},L2VPN 7,New description 7',
|
||||
f'{l2vpns[1].pk},L2VPN 8,New description 8',
|
||||
)
|
||||
|
||||
cls.bulk_edit_data = {
|
||||
'description': 'New Description',
|
||||
}
|
||||
|
||||
cls.form_data = {
|
||||
'name': 'L2VPN 8',
|
||||
'slug': 'l2vpn-8',
|
||||
@@ -804,7 +920,7 @@ class L2VPNTerminationTestCase(
|
||||
def setUpTestData(cls):
|
||||
device = create_test_device('Device 1')
|
||||
interface = Interface.objects.create(name='Interface 1', device=device, type='1000baset')
|
||||
l2vpn = L2VPN.objects.create(name='L2VPN 1', type=L2VPNTypeChoices.TYPE_VXLAN, identifier=650001)
|
||||
l2vpn = L2VPN.objects.create(name='L2VPN 1', slug='l2vpn-1', type=L2VPNTypeChoices.TYPE_VXLAN, identifier=650001)
|
||||
|
||||
vlans = (
|
||||
VLAN(name='Vlan 1', vid=1001),
|
||||
@@ -836,6 +952,13 @@ class L2VPNTerminationTestCase(
|
||||
"L2VPN 1,Vlan 6",
|
||||
)
|
||||
|
||||
cls.csv_update_data = (
|
||||
"id,l2vpn",
|
||||
f"{terminations[0].pk},L2VPN 2",
|
||||
f"{terminations[1].pk},L2VPN 2",
|
||||
f"{terminations[2].pk},L2VPN 2",
|
||||
)
|
||||
|
||||
cls.bulk_edit_data = {}
|
||||
|
||||
#
|
||||
|
||||
@@ -4,11 +4,11 @@ from copy import deepcopy
|
||||
|
||||
from django.contrib import messages
|
||||
from django.contrib.contenttypes.models import ContentType
|
||||
from django.core.exceptions import FieldDoesNotExist, ValidationError
|
||||
from django.core.exceptions import FieldDoesNotExist, ValidationError, ObjectDoesNotExist
|
||||
from django.db import transaction, IntegrityError
|
||||
from django.db.models import ManyToManyField, ProtectedError
|
||||
from django.db.models.fields.reverse_related import ManyToManyRel
|
||||
from django.forms import Form, ModelMultipleChoiceField, MultipleHiddenInput
|
||||
from django.forms import Form, ModelMultipleChoiceField, MultipleHiddenInput, model_to_dict
|
||||
from django.http import HttpResponse
|
||||
from django.shortcuts import get_object_or_404, redirect, render
|
||||
from django_tables2.export import TableExport
|
||||
@@ -321,13 +321,52 @@ class BulkImportView(GetReturnURLMixin, BaseMultiObjectView):
|
||||
|
||||
return ImportForm(*args, **kwargs)
|
||||
|
||||
def _create_objects(self, form, request):
|
||||
new_objs = []
|
||||
def _get_records(self, form, request):
|
||||
if request.FILES:
|
||||
headers, records = form.cleaned_data['csv_file']
|
||||
else:
|
||||
headers, records = form.cleaned_data['csv']
|
||||
|
||||
return headers, records
|
||||
|
||||
def _update_objects(self, form, request, headers, records):
|
||||
from utilities.forms import CSVModelChoiceField
|
||||
updated_objs = []
|
||||
|
||||
ids = [int(record["id"]) for record in records]
|
||||
qs = self.queryset.model.objects.filter(id__in=ids)
|
||||
objs = {}
|
||||
for obj in qs:
|
||||
objs[obj.id] = obj
|
||||
|
||||
for row, data in enumerate(records, start=1):
|
||||
if int(data["id"]) not in objs:
|
||||
form.add_error('csv', f'Row {row} id: {data["id"]} Does not exist')
|
||||
raise ValidationError("")
|
||||
|
||||
obj = objs[int(data["id"])]
|
||||
obj_form = self.model_form(data, headers=headers, instance=obj)
|
||||
|
||||
# The form should only contain fields that are in the CSV
|
||||
for name, field in list(obj_form.fields.items()):
|
||||
if name not in headers:
|
||||
del obj_form.fields[name]
|
||||
|
||||
restrict_form_fields(obj_form, request.user)
|
||||
|
||||
if obj_form.is_valid():
|
||||
obj = self._save_obj(obj_form, request)
|
||||
updated_objs.append(obj)
|
||||
else:
|
||||
for field, err in obj_form.errors.items():
|
||||
form.add_error('csv', f'Row {row} {field}: {err[0]}')
|
||||
raise ValidationError("")
|
||||
|
||||
return updated_objs
|
||||
|
||||
def _create_objects(self, form, request, headers, records):
|
||||
new_objs = []
|
||||
|
||||
for row, data in enumerate(records, start=1):
|
||||
obj_form = self.model_form(data, headers=headers)
|
||||
restrict_form_fields(obj_form, request.user)
|
||||
@@ -375,7 +414,11 @@ class BulkImportView(GetReturnURLMixin, BaseMultiObjectView):
|
||||
try:
|
||||
# Iterate through CSV data and bind each row to a new model form instance.
|
||||
with transaction.atomic():
|
||||
new_objs = self._create_objects(form, request)
|
||||
headers, records = self._get_records(form, request)
|
||||
if "id" in headers:
|
||||
new_objs = self._update_objects(form, request, headers, records)
|
||||
else:
|
||||
new_objs = self._create_objects(form, request, headers, records)
|
||||
|
||||
# Enforce object-level permissions
|
||||
if self.queryset.filter(pk__in=[obj.pk for obj in new_objs]).count() != len(new_objs):
|
||||
|
||||
@@ -32,6 +32,13 @@ class TenantGroupTestCase(ViewTestCases.OrganizationalObjectViewTestCase):
|
||||
"Tenant Group 6,tenant-group-6,Sixth tenant group",
|
||||
)
|
||||
|
||||
cls.csv_update_data = (
|
||||
"id,name,description",
|
||||
f"{tenant_groups[0].pk},Tenant Group 7,Fourth tenant group7",
|
||||
f"{tenant_groups[1].pk},Tenant Group 8,Fifth tenant group8",
|
||||
f"{tenant_groups[2].pk},Tenant Group 0,Sixth tenant group9",
|
||||
)
|
||||
|
||||
cls.bulk_edit_data = {
|
||||
'description': 'New description',
|
||||
}
|
||||
@@ -50,11 +57,12 @@ class TenantTestCase(ViewTestCases.PrimaryObjectViewTestCase):
|
||||
for tenanantgroup in tenant_groups:
|
||||
tenanantgroup.save()
|
||||
|
||||
Tenant.objects.bulk_create([
|
||||
tenants = (
|
||||
Tenant(name='Tenant 1', slug='tenant-1', group=tenant_groups[0]),
|
||||
Tenant(name='Tenant 2', slug='tenant-2', group=tenant_groups[0]),
|
||||
Tenant(name='Tenant 3', slug='tenant-3', group=tenant_groups[0]),
|
||||
])
|
||||
)
|
||||
Tenant.objects.bulk_create(tenants)
|
||||
|
||||
tags = create_tags('Alpha', 'Bravo', 'Charlie')
|
||||
|
||||
@@ -74,6 +82,13 @@ class TenantTestCase(ViewTestCases.PrimaryObjectViewTestCase):
|
||||
"Tenant 6,tenant-6",
|
||||
)
|
||||
|
||||
cls.csv_update_data = (
|
||||
"id,name,description",
|
||||
f"{tenants[0].pk},Tenant 7,New description 7",
|
||||
f"{tenants[1].pk},Tenant 8,New description 8",
|
||||
f"{tenants[2].pk},Tenant 9,New description 9",
|
||||
)
|
||||
|
||||
cls.bulk_edit_data = {
|
||||
'group': tenant_groups[1].pk,
|
||||
}
|
||||
@@ -109,6 +124,13 @@ class ContactGroupTestCase(ViewTestCases.OrganizationalObjectViewTestCase):
|
||||
"Contact Group 6,contact-group-6,Sixth contact group",
|
||||
)
|
||||
|
||||
cls.csv_update_data = (
|
||||
"id,name,description",
|
||||
f"{contact_groups[0].pk},Contact Group 7,Fourth contact group7",
|
||||
f"{contact_groups[1].pk},Contact Group 8,Fifth contact group8",
|
||||
f"{contact_groups[2].pk},Contact Group 0,Sixth contact group9",
|
||||
)
|
||||
|
||||
cls.bulk_edit_data = {
|
||||
'description': 'New description',
|
||||
}
|
||||
@@ -120,11 +142,12 @@ class ContactRoleTestCase(ViewTestCases.OrganizationalObjectViewTestCase):
|
||||
@classmethod
|
||||
def setUpTestData(cls):
|
||||
|
||||
ContactRole.objects.bulk_create([
|
||||
contact_roles = (
|
||||
ContactRole(name='Contact Role 1', slug='contact-role-1'),
|
||||
ContactRole(name='Contact Role 2', slug='contact-role-2'),
|
||||
ContactRole(name='Contact Role 3', slug='contact-role-3'),
|
||||
])
|
||||
)
|
||||
ContactRole.objects.bulk_create(contact_roles)
|
||||
|
||||
tags = create_tags('Alpha', 'Bravo', 'Charlie')
|
||||
|
||||
@@ -142,6 +165,13 @@ class ContactRoleTestCase(ViewTestCases.OrganizationalObjectViewTestCase):
|
||||
"Contact Role 6,contact-role-6",
|
||||
)
|
||||
|
||||
cls.csv_update_data = (
|
||||
"id,name,description",
|
||||
f"{contact_roles[0].pk},Contact Role 7,New description 7",
|
||||
f"{contact_roles[1].pk},Contact Role 8,New description 8",
|
||||
f"{contact_roles[2].pk},Contact Role 9,New description 9",
|
||||
)
|
||||
|
||||
cls.bulk_edit_data = {
|
||||
'description': 'New description',
|
||||
}
|
||||
@@ -160,11 +190,12 @@ class ContactTestCase(ViewTestCases.PrimaryObjectViewTestCase):
|
||||
for contactgroup in contact_groups:
|
||||
contactgroup.save()
|
||||
|
||||
Contact.objects.bulk_create([
|
||||
contacts = (
|
||||
Contact(name='Contact 1', group=contact_groups[0]),
|
||||
Contact(name='Contact 2', group=contact_groups[0]),
|
||||
Contact(name='Contact 3', group=contact_groups[0]),
|
||||
])
|
||||
)
|
||||
Contact.objects.bulk_create(contacts)
|
||||
|
||||
tags = create_tags('Alpha', 'Bravo', 'Charlie')
|
||||
|
||||
@@ -182,6 +213,13 @@ class ContactTestCase(ViewTestCases.PrimaryObjectViewTestCase):
|
||||
"Contact Group 1,Contact 6",
|
||||
)
|
||||
|
||||
cls.csv_update_data = (
|
||||
"id,name,comments",
|
||||
f"{contacts[0].pk},Contact Group 7,New comments 7",
|
||||
f"{contacts[1].pk},Contact Group 8,New comments 8",
|
||||
f"{contacts[2].pk},Contact Group 9,New comments 9",
|
||||
)
|
||||
|
||||
cls.bulk_edit_data = {
|
||||
'group': contact_groups[1].pk,
|
||||
}
|
||||
|
||||
@@ -220,7 +220,11 @@ def validate_csv(headers, fields, required_fields):
|
||||
if parsed csv data contains invalid headers or does not contain required headers.
|
||||
"""
|
||||
# Validate provided column headers
|
||||
is_update = False
|
||||
for field, to_field in headers.items():
|
||||
if field == "id":
|
||||
is_update = True
|
||||
continue
|
||||
if field not in fields:
|
||||
raise forms.ValidationError(f'Unexpected column header "{field}" found.')
|
||||
if to_field and not hasattr(fields[field], 'to_field_name'):
|
||||
@@ -228,7 +232,8 @@ def validate_csv(headers, fields, required_fields):
|
||||
if to_field and not hasattr(fields[field].queryset.model, to_field):
|
||||
raise forms.ValidationError(f'Invalid related object attribute for column "{field}": {to_field}')
|
||||
|
||||
# Validate required fields
|
||||
for f in required_fields:
|
||||
if f not in headers:
|
||||
raise forms.ValidationError(f'Required column header "{f}" not found.')
|
||||
# Validate required fields (if not an update)
|
||||
if not is_update:
|
||||
for f in required_fields:
|
||||
if f not in headers:
|
||||
raise forms.ValidationError(f'Required column header "{f}" not found.')
|
||||
|
||||
@@ -1,5 +1,8 @@
|
||||
import csv
|
||||
|
||||
from django.contrib.contenttypes.models import ContentType
|
||||
from django.core.exceptions import ObjectDoesNotExist
|
||||
from django.db.models import ForeignKey
|
||||
from django.test import override_settings
|
||||
from django.urls import reverse
|
||||
|
||||
@@ -19,6 +22,7 @@ __all__ = (
|
||||
# UI Tests
|
||||
#
|
||||
|
||||
|
||||
class ModelViewTestCase(ModelTestCase):
|
||||
"""
|
||||
Base TestCase for model views. Subclass to test individual views.
|
||||
@@ -546,6 +550,9 @@ class ViewTestCases:
|
||||
def _get_csv_data(self):
|
||||
return '\n'.join(self.csv_data)
|
||||
|
||||
def _get_update_csv_data(self):
|
||||
return self.csv_update_data, '\n'.join(self.csv_update_data)
|
||||
|
||||
def test_bulk_import_objects_without_permission(self):
|
||||
data = {
|
||||
'csv': self._get_csv_data(),
|
||||
@@ -583,6 +590,42 @@ class ViewTestCases:
|
||||
self.assertHttpStatus(self.client.post(self._get_url('import'), data), 200)
|
||||
self.assertEqual(self._get_queryset().count(), initial_count + len(self.csv_data) - 1)
|
||||
|
||||
@override_settings(EXEMPT_VIEW_PERMISSIONS=['*'])
|
||||
def test_bulk_update_objects_with_permission(self):
|
||||
if not hasattr(self, 'csv_update_data'):
|
||||
raise NotImplementedError("The test must define csv_update_data.")
|
||||
|
||||
initial_count = self._get_queryset().count()
|
||||
array, csv_data = self._get_update_csv_data()
|
||||
data = {
|
||||
'csv': csv_data,
|
||||
}
|
||||
|
||||
# Assign model-level permission
|
||||
obj_perm = ObjectPermission(
|
||||
name='Test permission',
|
||||
actions=['add']
|
||||
)
|
||||
obj_perm.save()
|
||||
obj_perm.users.add(self.user)
|
||||
obj_perm.object_types.add(ContentType.objects.get_for_model(self.model))
|
||||
|
||||
# Test POST with permission
|
||||
self.assertHttpStatus(self.client.post(self._get_url('import'), data), 200)
|
||||
self.assertEqual(initial_count, self._get_queryset().count())
|
||||
|
||||
reader = csv.DictReader(array, delimiter=',')
|
||||
check_data = list(reader)
|
||||
for line in check_data:
|
||||
obj = self.model.objects.get(id=line["id"])
|
||||
for attr, value in line.items():
|
||||
if attr != "id":
|
||||
field = self.model._meta.get_field(attr)
|
||||
value = getattr(obj, attr)
|
||||
# cannot verify FK fields as don't know what name the CSV maps to
|
||||
if value is not None and not isinstance(field, ForeignKey):
|
||||
self.assertEqual(value, value)
|
||||
|
||||
@override_settings(EXEMPT_VIEW_PERMISSIONS=['*'])
|
||||
def test_bulk_import_objects_with_constrained_permission(self):
|
||||
initial_count = self._get_queryset().count()
|
||||
|
||||
@@ -16,11 +16,12 @@ class ClusterGroupTestCase(ViewTestCases.OrganizationalObjectViewTestCase):
|
||||
@classmethod
|
||||
def setUpTestData(cls):
|
||||
|
||||
ClusterGroup.objects.bulk_create([
|
||||
cluster_groups = (
|
||||
ClusterGroup(name='Cluster Group 1', slug='cluster-group-1'),
|
||||
ClusterGroup(name='Cluster Group 2', slug='cluster-group-2'),
|
||||
ClusterGroup(name='Cluster Group 3', slug='cluster-group-3'),
|
||||
])
|
||||
)
|
||||
ClusterGroup.objects.bulk_create(cluster_groups)
|
||||
|
||||
tags = create_tags('Alpha', 'Bravo', 'Charlie')
|
||||
|
||||
@@ -38,6 +39,13 @@ class ClusterGroupTestCase(ViewTestCases.OrganizationalObjectViewTestCase):
|
||||
"Cluster Group 6,cluster-group-6,Sixth cluster group",
|
||||
)
|
||||
|
||||
cls.csv_update_data = (
|
||||
"id,name,description",
|
||||
f"{cluster_groups[0].pk},Cluster Group 7,Fourth cluster group7",
|
||||
f"{cluster_groups[1].pk},Cluster Group 8,Fifth cluster group8",
|
||||
f"{cluster_groups[2].pk},Cluster Group 9,Sixth cluster group9",
|
||||
)
|
||||
|
||||
cls.bulk_edit_data = {
|
||||
'description': 'New description',
|
||||
}
|
||||
@@ -49,11 +57,12 @@ class ClusterTypeTestCase(ViewTestCases.OrganizationalObjectViewTestCase):
|
||||
@classmethod
|
||||
def setUpTestData(cls):
|
||||
|
||||
ClusterType.objects.bulk_create([
|
||||
cluster_types = (
|
||||
ClusterType(name='Cluster Type 1', slug='cluster-type-1'),
|
||||
ClusterType(name='Cluster Type 2', slug='cluster-type-2'),
|
||||
ClusterType(name='Cluster Type 3', slug='cluster-type-3'),
|
||||
])
|
||||
)
|
||||
ClusterType.objects.bulk_create(cluster_types)
|
||||
|
||||
tags = create_tags('Alpha', 'Bravo', 'Charlie')
|
||||
|
||||
@@ -71,6 +80,13 @@ class ClusterTypeTestCase(ViewTestCases.OrganizationalObjectViewTestCase):
|
||||
"Cluster Type 6,cluster-type-6,Sixth cluster type",
|
||||
)
|
||||
|
||||
cls.csv_update_data = (
|
||||
"id,name,description",
|
||||
f"{cluster_types[0].pk},Cluster Type 7,Fourth cluster type7",
|
||||
f"{cluster_types[1].pk},Cluster Type 8,Fifth cluster type8",
|
||||
f"{cluster_types[2].pk},Cluster Type 9,Sixth cluster type9",
|
||||
)
|
||||
|
||||
cls.bulk_edit_data = {
|
||||
'description': 'New description',
|
||||
}
|
||||
@@ -100,11 +116,12 @@ class ClusterTestCase(ViewTestCases.PrimaryObjectViewTestCase):
|
||||
)
|
||||
ClusterType.objects.bulk_create(clustertypes)
|
||||
|
||||
Cluster.objects.bulk_create([
|
||||
clusters = (
|
||||
Cluster(name='Cluster 1', group=clustergroups[0], type=clustertypes[0], status=ClusterStatusChoices.STATUS_ACTIVE, site=sites[0]),
|
||||
Cluster(name='Cluster 2', group=clustergroups[0], type=clustertypes[0], status=ClusterStatusChoices.STATUS_ACTIVE, site=sites[0]),
|
||||
Cluster(name='Cluster 3', group=clustergroups[0], type=clustertypes[0], status=ClusterStatusChoices.STATUS_ACTIVE, site=sites[0]),
|
||||
])
|
||||
)
|
||||
Cluster.objects.bulk_create(clusters)
|
||||
|
||||
tags = create_tags('Alpha', 'Bravo', 'Charlie')
|
||||
|
||||
@@ -126,6 +143,13 @@ class ClusterTestCase(ViewTestCases.PrimaryObjectViewTestCase):
|
||||
"Cluster 6,Cluster Type 1,active",
|
||||
)
|
||||
|
||||
cls.csv_update_data = (
|
||||
"id,name,comments",
|
||||
f"{clusters[0].pk},Cluster 7,New comments 7",
|
||||
f"{clusters[1].pk},Cluster 8,New comments 8",
|
||||
f"{clusters[2].pk},Cluster 9,New comments 9",
|
||||
)
|
||||
|
||||
cls.bulk_edit_data = {
|
||||
'group': clustergroups[1].pk,
|
||||
'type': clustertypes[1].pk,
|
||||
@@ -187,11 +211,12 @@ class VirtualMachineTestCase(ViewTestCases.PrimaryObjectViewTestCase):
|
||||
create_test_device('device2', site=sites[1], cluster=clusters[1]),
|
||||
)
|
||||
|
||||
VirtualMachine.objects.bulk_create([
|
||||
virtual_machines = (
|
||||
VirtualMachine(name='Virtual Machine 1', site=sites[0], cluster=clusters[0], device=devices[0], role=deviceroles[0], platform=platforms[0]),
|
||||
VirtualMachine(name='Virtual Machine 2', site=sites[0], cluster=clusters[0], device=devices[0], role=deviceroles[0], platform=platforms[0]),
|
||||
VirtualMachine(name='Virtual Machine 3', site=sites[0], cluster=clusters[0], device=devices[0], role=deviceroles[0], platform=platforms[0]),
|
||||
])
|
||||
)
|
||||
VirtualMachine.objects.bulk_create(virtual_machines)
|
||||
|
||||
tags = create_tags('Alpha', 'Bravo', 'Charlie')
|
||||
|
||||
@@ -221,6 +246,13 @@ class VirtualMachineTestCase(ViewTestCases.PrimaryObjectViewTestCase):
|
||||
"Virtual Machine 6,active,Site 1,Cluster 1,",
|
||||
)
|
||||
|
||||
cls.csv_update_data = (
|
||||
"id,name,comments",
|
||||
f"{virtual_machines[0].pk},Virtual Machine 7,New comments 7",
|
||||
f"{virtual_machines[1].pk},Virtual Machine 8,New comments 8",
|
||||
f"{virtual_machines[2].pk},Virtual Machine 9,New comments 9",
|
||||
)
|
||||
|
||||
cls.bulk_edit_data = {
|
||||
'site': sites[1].pk,
|
||||
'cluster': clusters[1].pk,
|
||||
@@ -327,6 +359,13 @@ class VMInterfaceTestCase(ViewTestCases.DeviceComponentViewTestCase):
|
||||
f"Virtual Machine 2,Interface 6,{vrfs[0].pk}",
|
||||
)
|
||||
|
||||
cls.csv_update_data = (
|
||||
f"id,name,description",
|
||||
f"{interfaces[0].pk},Interface 7,New description 7",
|
||||
f"{interfaces[1].pk},Interface 8,New description 8",
|
||||
f"{interfaces[2].pk},Interface 9,New description 9",
|
||||
)
|
||||
|
||||
cls.bulk_edit_data = {
|
||||
'enabled': False,
|
||||
'mtu': 2000,
|
||||
|
||||
@@ -32,11 +32,18 @@ class WirelessLANGroupTestCase(ViewTestCases.OrganizationalObjectViewTestCase):
|
||||
|
||||
cls.csv_data = (
|
||||
"name,slug,description",
|
||||
"Wireles sLAN Group 4,wireless-lan-group-4,Fourth wireless LAN group",
|
||||
"Wireless LAN Group 4,wireless-lan-group-4,Fourth wireless LAN group",
|
||||
"Wireless LAN Group 5,wireless-lan-group-5,Fifth wireless LAN group",
|
||||
"Wireless LAN Group 6,wireless-lan-group-6,Sixth wireless LAN group",
|
||||
)
|
||||
|
||||
cls.csv_update_data = (
|
||||
"id,name,description",
|
||||
f"{groups[0].pk},Wireless LAN Group 7,Fourth wireless LAN group7",
|
||||
f"{groups[1].pk},Wireless LAN Group 8,Fifth wireless LAN group8",
|
||||
f"{groups[2].pk},Wireless LAN Group 0,Sixth wireless LAN group9",
|
||||
)
|
||||
|
||||
cls.bulk_edit_data = {
|
||||
'description': 'New description',
|
||||
}
|
||||
@@ -62,11 +69,12 @@ class WirelessLANTestCase(ViewTestCases.PrimaryObjectViewTestCase):
|
||||
for group in groups:
|
||||
group.save()
|
||||
|
||||
WirelessLAN.objects.bulk_create([
|
||||
wireless_lans = (
|
||||
WirelessLAN(group=groups[0], ssid='WLAN1', tenant=tenants[0]),
|
||||
WirelessLAN(group=groups[0], ssid='WLAN2', tenant=tenants[0]),
|
||||
WirelessLAN(group=groups[0], ssid='WLAN3', tenant=tenants[0]),
|
||||
])
|
||||
)
|
||||
WirelessLAN.objects.bulk_create(wireless_lans)
|
||||
|
||||
tags = create_tags('Alpha', 'Bravo', 'Charlie')
|
||||
|
||||
@@ -84,6 +92,13 @@ class WirelessLANTestCase(ViewTestCases.PrimaryObjectViewTestCase):
|
||||
f"Wireless LAN Group 2,WLAN6,{tenants[2].name}",
|
||||
)
|
||||
|
||||
cls.csv_update_data = (
|
||||
f"id,ssid",
|
||||
f"{wireless_lans[0].pk},WLAN7",
|
||||
f"{wireless_lans[1].pk},WLAN8",
|
||||
f"{wireless_lans[2].pk},WLAN9",
|
||||
)
|
||||
|
||||
cls.bulk_edit_data = {
|
||||
'description': 'New description',
|
||||
}
|
||||
@@ -115,9 +130,12 @@ class WirelessLinkTestCase(ViewTestCases.PrimaryObjectViewTestCase):
|
||||
]
|
||||
Interface.objects.bulk_create(interfaces)
|
||||
|
||||
WirelessLink(interface_a=interfaces[0], interface_b=interfaces[1], ssid='LINK1', tenant=tenants[0]).save()
|
||||
WirelessLink(interface_a=interfaces[2], interface_b=interfaces[3], ssid='LINK2', tenant=tenants[0]).save()
|
||||
WirelessLink(interface_a=interfaces[4], interface_b=interfaces[5], ssid='LINK3', tenant=tenants[0]).save()
|
||||
wirelesslink1 = WirelessLink(interface_a=interfaces[0], interface_b=interfaces[1], ssid='LINK1', tenant=tenants[0])
|
||||
wirelesslink1.save()
|
||||
wirelesslink2 = WirelessLink(interface_a=interfaces[2], interface_b=interfaces[3], ssid='LINK2', tenant=tenants[0])
|
||||
wirelesslink2.save()
|
||||
wirelesslink3 = WirelessLink(interface_a=interfaces[4], interface_b=interfaces[5], ssid='LINK3', tenant=tenants[0])
|
||||
wirelesslink3.save()
|
||||
|
||||
tags = create_tags('Alpha', 'Bravo', 'Charlie')
|
||||
|
||||
@@ -136,6 +154,13 @@ class WirelessLinkTestCase(ViewTestCases.PrimaryObjectViewTestCase):
|
||||
f"{interfaces[10].pk},{interfaces[11].pk},connected,{tenants[2].name}",
|
||||
)
|
||||
|
||||
cls.csv_update_data = (
|
||||
"id,ssid,description",
|
||||
f"{wirelesslink1.pk},LINK7,New decription 7",
|
||||
f"{wirelesslink2.pk},LINK8,New decription 8",
|
||||
f"{wirelesslink3.pk},LINK9,New decription 9",
|
||||
)
|
||||
|
||||
cls.bulk_edit_data = {
|
||||
'status': LinkStatusChoices.STATUS_PLANNED,
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user