1
0
mirror of https://github.com/netbox-community/netbox.git synced 2024-05-10 07:54:54 +00: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:
Arthur Hanson
2022-10-27 10:10:18 -07:00
committed by GitHub
parent d773f4e62a
commit cb815ede60
13 changed files with 715 additions and 117 deletions

View File

@ -20,12 +20,14 @@ To create a new object in NetBox, find the object type in the navigation menu an
## Bulk Import (CSV/YAML)
NetBox supports the bulk import of new objects using CSV-formatted data. This method can be ideal for importing spreadsheet data, which is very easy to convert to CSV data. CSV data can be imported either as raw text using the form field, or by uploading a properly formatted CSV file.
NetBox supports the bulk import of new objects, and updating of existing objects using CSV-formatted data. This method can be ideal for importing spreadsheet data, which is very easy to convert to CSV data. CSV data can be imported either as raw text using the form field, or by uploading a properly formatted CSV file.
When viewing the CSV import form for an object type, you'll notice that the headers for the required columns have been pre-populated. Each form has a table beneath it titled "CSV Field Options," which lists _all_ supported columns for your reference. (Generally, these map to the fields you see in the corresponding creation form for individual objects.)
<!-- TODO: Screenshot -->
If an "id" field is added the data will be used to update existing records instead of importing new objects.
Note that some models (namely device types and module types) do not support CSV import. Instead, they accept YAML-formatted data to facilitate the import of both the parent object as well as child components.
## Scripting

View File

@ -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',

View File

@ -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:

View File

@ -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,

View File

@ -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',
}

View File

@ -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

View File

@ -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 = {}
#

View File

@ -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):

View File

@ -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,
}

View File

@ -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.')

View File

@ -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()

View File

@ -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,

View File

@ -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,
}