diff --git a/netbox/dcim/forms.py b/netbox/dcim/forms.py index 8399b0de6..a216bb75f 100644 --- a/netbox/dcim/forms.py +++ b/netbox/dcim/forms.py @@ -190,6 +190,27 @@ class RackGroupForm(BootstrapMixin, forms.ModelForm): fields = ['site', 'name', 'slug'] +class RackGroupCSVForm(forms.ModelForm): + site = forms.ModelChoiceField( + queryset=Site.objects.all(), + to_field_name='name', + help_text='Name of parent site', + error_messages={ + 'invalid_choice': 'Site not found.', + } + ) + + class Meta: + model = RackGroup + fields = [ + 'site', 'name', 'slug', + ] + help_texts = { + 'name': 'Name of rack group', + 'slug': 'URL-friendly slug', + } + + class RackGroupFilterForm(BootstrapMixin, forms.Form): site = FilterChoiceField(queryset=Site.objects.annotate(filter_count=Count('rack_groups')), to_field_name='slug') diff --git a/netbox/dcim/models.py b/netbox/dcim/models.py index af3387368..1cc948643 100644 --- a/netbox/dcim/models.py +++ b/netbox/dcim/models.py @@ -159,6 +159,10 @@ class RackGroup(models.Model): slug = models.SlugField() site = models.ForeignKey('Site', related_name='rack_groups', on_delete=models.CASCADE) + csv_headers = [ + 'site', 'name', 'slug', + ] + class Meta: ordering = ['site', 'name'] unique_together = [ @@ -172,6 +176,13 @@ class RackGroup(models.Model): def get_absolute_url(self): return "{}?group_id={}".format(reverse('dcim:rack_list'), self.pk) + def to_csv(self): + return csv_format([ + self.site, + self.name, + self.slug, + ]) + @python_2_unicode_compatible class RackRole(models.Model): diff --git a/netbox/dcim/urls.py b/netbox/dcim/urls.py index b03b7d030..12f8ebf86 100644 --- a/netbox/dcim/urls.py +++ b/netbox/dcim/urls.py @@ -32,6 +32,7 @@ urlpatterns = [ # Rack groups url(r'^rack-groups/$', views.RackGroupListView.as_view(), name='rackgroup_list'), url(r'^rack-groups/add/$', views.RackGroupCreateView.as_view(), name='rackgroup_add'), + url(r'^rack-groups/import/$', views.RackGroupBulkImportView.as_view(), name='rackgroup_import'), url(r'^rack-groups/delete/$', views.RackGroupBulkDeleteView.as_view(), name='rackgroup_bulk_delete'), url(r'^rack-groups/(?P\d+)/edit/$', views.RackGroupEditView.as_view(), name='rackgroup_edit'), diff --git a/netbox/dcim/views.py b/netbox/dcim/views.py index ca144afe8..c155774db 100644 --- a/netbox/dcim/views.py +++ b/netbox/dcim/views.py @@ -314,6 +314,13 @@ class RackGroupEditView(RackGroupCreateView): permission_required = 'dcim.change_rackgroup' +class RackGroupBulkImportView(PermissionRequiredMixin, BulkImportView): + permission_required = 'dcim.add_rackgroup' + model_form = forms.RackGroupCSVForm + table = tables.RackGroupTable + default_return_url = 'dcim:rackgroup_list' + + class RackGroupBulkDeleteView(PermissionRequiredMixin, BulkDeleteView): permission_required = 'dcim.delete_rackgroup' cls = RackGroup diff --git a/netbox/extras/constants.py b/netbox/extras/constants.py index 0ebd49af0..2c3918f75 100644 --- a/netbox/extras/constants.py +++ b/netbox/extras/constants.py @@ -37,7 +37,7 @@ GRAPH_TYPE_CHOICES = ( # Models which support export templates EXPORTTEMPLATE_MODELS = [ - 'site', 'region', 'rack', 'device', # DCIM + 'site', 'region', 'rack', 'rackgroup', 'device', # DCIM 'consoleport', 'powerport', 'interfaceconnection', # DCIM 'aggregate', 'prefix', 'ipaddress', 'vlan', # IPAM 'provider', 'circuit', # Circuits diff --git a/netbox/templates/_base.html b/netbox/templates/_base.html index 65cf39b30..ebc8bc680 100644 --- a/netbox/templates/_base.html +++ b/netbox/templates/_base.html @@ -68,6 +68,7 @@
  • Rack Groups
  • {% if perms.dcim.add_rackgroup %}
  • Add a Rack Group
  • +
  • Import Rack Groups
  • {% endif %}
  • Rack Roles
  • diff --git a/netbox/templates/dcim/rackgroup_list.html b/netbox/templates/dcim/rackgroup_list.html index dee6472fb..086a7df27 100644 --- a/netbox/templates/dcim/rackgroup_list.html +++ b/netbox/templates/dcim/rackgroup_list.html @@ -10,7 +10,12 @@ Add a rack group + + + Import rack groups + {% endif %} + {% include 'inc/export_button.html' with obj_type='rackgroups' %}

    Rack Groups