Merge pull request #4564 from netbox-community/3147-csv-import-fields

Closes #3147: Allow dynamic access to related objects during CSV import
This commit is contained in:
Jeremy Stretch
2020-05-06 10:15:00 -04:00
committed by GitHub
17 changed files with 706 additions and 879 deletions

File diff suppressed because it is too large Load Diff

View File

@@ -180,12 +180,14 @@ class Site(ChangeLoggedModel, CustomFieldModel):
)
facility = models.CharField(
max_length=50,
blank=True
blank=True,
help_text='Local facility ID or description'
)
asn = ASNField(
blank=True,
null=True,
verbose_name='ASN'
verbose_name='ASN',
help_text='32-bit autonomous system number'
)
time_zone = TimeZoneField(
blank=True
@@ -206,13 +208,15 @@ class Site(ChangeLoggedModel, CustomFieldModel):
max_digits=8,
decimal_places=6,
blank=True,
null=True
null=True,
help_text='GPS coordinate (latitude)'
)
longitude = models.DecimalField(
max_digits=9,
decimal_places=6,
blank=True,
null=True
null=True,
help_text='GPS coordinate (longitude)'
)
contact_name = models.CharField(
max_length=50,
@@ -419,7 +423,8 @@ class Rack(ChangeLoggedModel, CustomFieldModel):
max_length=50,
blank=True,
null=True,
verbose_name='Facility ID'
verbose_name='Facility ID',
help_text='Locally-assigned identifier'
)
site = models.ForeignKey(
to='dcim.Site',
@@ -431,7 +436,8 @@ class Rack(ChangeLoggedModel, CustomFieldModel):
on_delete=models.SET_NULL,
related_name='racks',
blank=True,
null=True
null=True,
help_text='Assigned group'
)
tenant = models.ForeignKey(
to='tenancy.Tenant',
@@ -450,7 +456,8 @@ class Rack(ChangeLoggedModel, CustomFieldModel):
on_delete=models.PROTECT,
related_name='racks',
blank=True,
null=True
null=True,
help_text='Functional role'
)
serial = models.CharField(
max_length=50,
@@ -480,7 +487,8 @@ class Rack(ChangeLoggedModel, CustomFieldModel):
u_height = models.PositiveSmallIntegerField(
default=RACK_U_HEIGHT_DEFAULT,
verbose_name='Height (U)',
validators=[MinValueValidator(1), MaxValueValidator(100)]
validators=[MinValueValidator(1), MaxValueValidator(100)],
help_text='Height in rack units'
)
desc_units = models.BooleanField(
default=False,
@@ -489,11 +497,13 @@ class Rack(ChangeLoggedModel, CustomFieldModel):
)
outer_width = models.PositiveSmallIntegerField(
blank=True,
null=True
null=True,
help_text='Outer dimension of rack (width)'
)
outer_depth = models.PositiveSmallIntegerField(
blank=True,
null=True
null=True,
help_text='Outer dimension of rack (depth)'
)
outer_unit = models.CharField(
max_length=50,
@@ -514,7 +524,7 @@ class Rack(ChangeLoggedModel, CustomFieldModel):
tags = TaggableManager(through=TaggedItem)
csv_headers = [
'site', 'group_name', 'name', 'facility_id', 'tenant', 'status', 'role', 'type', 'serial', 'asset_tag', 'width',
'site', 'group', 'name', 'facility_id', 'tenant', 'status', 'role', 'type', 'serial', 'asset_tag', 'width',
'u_height', 'desc_units', 'outer_width', 'outer_depth', 'outer_unit', 'comments',
]
clone_fields = [
@@ -821,7 +831,7 @@ class RackReservation(ChangeLoggedModel):
def clean(self):
if self.units:
if hasattr(self, 'rack') and self.units:
# Validate that all specified units exist in the Rack.
invalid_units = [u for u in self.units if u not in self.rack.units]
@@ -1415,7 +1425,7 @@ class Device(ChangeLoggedModel, ConfigContextModel, CustomFieldModel):
tags = TaggableManager(through=TaggedItem)
csv_headers = [
'name', 'device_role', 'tenant', 'manufacturer', 'model_name', 'platform', 'serial', 'asset_tag', 'status',
'name', 'device_role', 'tenant', 'manufacturer', 'device_type', 'platform', 'serial', 'asset_tag', 'status',
'site', 'rack_group', 'rack_name', 'position', 'face', 'comments',
]
clone_fields = [
@@ -1798,7 +1808,7 @@ class PowerPanel(ChangeLoggedModel):
max_length=50
)
csv_headers = ['site', 'rack_group_name', 'name']
csv_headers = ['site', 'rack_group', 'name']
class Meta:
ordering = ['site', 'name']
@@ -1905,7 +1915,7 @@ class PowerFeed(ChangeLoggedModel, CableTermination, CustomFieldModel):
tags = TaggableManager(through=TaggedItem)
csv_headers = [
'site', 'panel_name', 'rack_group', 'rack_name', 'name', 'status', 'type', 'supply', 'phase', 'voltage',
'site', 'power_panel', 'rack_group', 'rack', 'name', 'status', 'type', 'supply', 'phase', 'voltage',
'amperage', 'max_utilization', 'comments',
]
clone_fields = [

View File

@@ -239,7 +239,8 @@ class ConsolePort(CableTermination, ComponentModel):
type = models.CharField(
max_length=50,
choices=ConsolePortTypeChoices,
blank=True
blank=True,
help_text='Physical port type'
)
connected_endpoint = models.OneToOneField(
to='dcim.ConsoleServerPort',
@@ -300,7 +301,8 @@ class ConsoleServerPort(CableTermination, ComponentModel):
type = models.CharField(
max_length=50,
choices=ConsolePortTypeChoices,
blank=True
blank=True,
help_text='Physical port type'
)
connection_status = models.NullBooleanField(
choices=CONNECTION_STATUS_CHOICES,
@@ -354,7 +356,8 @@ class PowerPort(CableTermination, ComponentModel):
type = models.CharField(
max_length=50,
choices=PowerPortTypeChoices,
blank=True
blank=True,
help_text='Physical port type'
)
maximum_draw = models.PositiveSmallIntegerField(
blank=True,
@@ -516,7 +519,8 @@ class PowerOutlet(CableTermination, ComponentModel):
type = models.CharField(
max_length=50,
choices=PowerOutletTypeChoices,
blank=True
blank=True,
help_text='Physical port type'
)
power_port = models.ForeignKey(
to='dcim.PowerPort',
@@ -653,7 +657,7 @@ class Interface(CableTermination, ComponentModel):
mode = models.CharField(
max_length=50,
choices=InterfaceModeChoices,
blank=True,
blank=True
)
untagged_vlan = models.ForeignKey(
to='ipam.VLAN',
@@ -1083,7 +1087,8 @@ class InventoryItem(ComponentModel):
part_id = models.CharField(
max_length=50,
verbose_name='Part ID',
blank=True
blank=True,
help_text='Manufacturer-assigned part identifier'
)
serial = models.CharField(
max_length=50,
@@ -1100,7 +1105,7 @@ class InventoryItem(ComponentModel):
)
discovered = models.BooleanField(
default=False,
verbose_name='Discovered'
help_text='This item was automatically discovered'
)
tags = TaggableManager(through=TaggedItem)

View File

@@ -184,7 +184,10 @@ class RackReservationTestCase(ViewTestCases.PrimaryObjectViewTestCase):
site = Site.objects.create(name='Site 1', slug='site-1')
rack = Rack(name='Rack 1', site=site)
rack_group = RackGroup(name='Rack Group 1', slug='rack-group-1', site=site)
rack_group.save()
rack = Rack(name='Rack 1', site=site, group=rack_group)
rack.save()
RackReservation.objects.bulk_create([
@@ -202,10 +205,10 @@ class RackReservationTestCase(ViewTestCases.PrimaryObjectViewTestCase):
}
cls.csv_data = (
'site,rack_name,units,description',
'Site 1,Rack 1,"10,11,12",Reservation 1',
'Site 1,Rack 1,"13,14,15",Reservation 2',
'Site 1,Rack 1,"16,17,18",Reservation 3',
'site,rack_group,rack,units,description',
'Site 1,Rack Group 1,Rack 1,"10,11,12",Reservation 1',
'Site 1,Rack Group 1,Rack 1,"13,14,15",Reservation 2',
'Site 1,Rack Group 1,Rack 1,"16,17,18",Reservation 3',
)
cls.bulk_edit_data = {
@@ -268,10 +271,10 @@ class RackTestCase(ViewTestCases.PrimaryObjectViewTestCase):
}
cls.csv_data = (
"site,name,width,u_height",
"Site 1,Rack 4,19,42",
"Site 1,Rack 5,19,42",
"Site 1,Rack 6,19,42",
"site,group,name,width,u_height",
"Site 1,,Rack 4,19,42",
"Site 1,Rack Group 1,Rack 5,19,42",
"Site 2,Rack Group 2,Rack 6,19,42",
)
cls.bulk_edit_data = {
@@ -890,8 +893,11 @@ class DeviceTestCase(ViewTestCases.PrimaryObjectViewTestCase):
)
Site.objects.bulk_create(sites)
rack_group = RackGroup(site=sites[0], name='Rack Group 1', slug='rack-group-1')
rack_group.save()
racks = (
Rack(name='Rack 1', site=sites[0]),
Rack(name='Rack 1', site=sites[0], group=rack_group),
Rack(name='Rack 2', site=sites[1]),
)
Rack.objects.bulk_create(racks)
@@ -947,10 +953,10 @@ class DeviceTestCase(ViewTestCases.PrimaryObjectViewTestCase):
}
cls.csv_data = (
"device_role,manufacturer,model_name,status,site,name",
"Device Role 1,Manufacturer 1,Device Type 1,Active,Site 1,Device 4",
"Device Role 1,Manufacturer 1,Device Type 1,Active,Site 1,Device 5",
"Device Role 1,Manufacturer 1,Device Type 1,Active,Site 1,Device 6",
"device_role,manufacturer,device_type,status,name,site,rack_group,rack,position,face",
"Device Role 1,Manufacturer 1,Device Type 1,Active,Device 4,Site 1,Rack Group 1,Rack 1,10,Front",
"Device Role 1,Manufacturer 1,Device Type 1,Active,Device 5,Site 1,Rack Group 1,Rack 1,20,Front",
"Device Role 1,Manufacturer 1,Device Type 1,Active,Device 6,Site 1,Rack Group 1,Rack 1,30,Front",
)
cls.bulk_edit_data = {
@@ -1586,7 +1592,7 @@ class PowerPanelTestCase(ViewTestCases.PrimaryObjectViewTestCase):
}
cls.csv_data = (
"site,rack_group_name,name",
"site,rack_group,name",
"Site 1,Rack Group 1,Power Panel 4",
"Site 1,Rack Group 1,Power Panel 5",
"Site 1,Rack Group 1,Power Panel 6",
@@ -1645,7 +1651,7 @@ class PowerFeedTestCase(ViewTestCases.PrimaryObjectViewTestCase):
}
cls.csv_data = (
"site,panel_name,name,voltage,amperage,max_utilization",
"site,power_panel,name,voltage,amperage,max_utilization",
"Site 1,Power Panel 1,Power Feed 4,120,20,80",
"Site 1,Power Panel 1,Power Feed 5,120,20,80",
"Site 1,Power Panel 1,Power Feed 6,120,20,80",