|
@@ -31,7 +31,7 @@ from .models import (
|
|
|
DeviceBay, DeviceBayTemplate, ConsolePort, ConsolePortTemplate, ConsoleServerPort, ConsoleServerPortTemplate,
|
|
|
Device, DeviceRole, DeviceType, Interface, InterfaceConnection, InterfaceTemplate, Manufacturer, InventoryItem,
|
|
|
Platform, PowerOutlet, PowerOutletTemplate, PowerPort, PowerPortTemplate, Rack, RackGroup, RackReservation,
|
|
|
- RackRole, Region, Site, VirtualChassis
|
|
|
+ RackRole, Region, Site, Facility, VirtualChassis
|
|
|
)
|
|
|
|
|
|
DEVICE_BY_PK_RE = '{\d+\}'
|
|
@@ -101,18 +101,17 @@ class RegionFilterForm(BootstrapMixin, forms.Form):
|
|
|
|
|
|
|
|
|
#
|
|
|
-# Sites
|
|
|
+# Facilities
|
|
|
#
|
|
|
|
|
|
-class SiteForm(BootstrapMixin, TenancyForm, CustomFieldForm):
|
|
|
- region = TreeNodeChoiceField(queryset=Region.objects.all(), required=False)
|
|
|
+class FacilityForm(BootstrapMixin, TenancyForm, CustomFieldForm):
|
|
|
slug = SlugField()
|
|
|
comments = CommentField()
|
|
|
|
|
|
class Meta:
|
|
|
- model = Site
|
|
|
+ model = Facility
|
|
|
fields = [
|
|
|
- 'name', 'slug', 'status', 'region', 'tenant_group', 'tenant', 'facility', 'asn', 'time_zone', 'description',
|
|
|
+ 'name', 'slug', 'tenant_group', 'tenant', 'description', 'peeringdb_id', 'city', 'latitude', 'longitude',
|
|
|
'physical_address', 'shipping_address', 'contact_name', 'contact_phone', 'contact_email', 'comments',
|
|
|
]
|
|
|
widgets = {
|
|
@@ -120,13 +119,72 @@ class SiteForm(BootstrapMixin, TenancyForm, CustomFieldForm):
|
|
|
'shipping_address': SmallTextarea(attrs={'rows': 3}),
|
|
|
}
|
|
|
help_texts = {
|
|
|
+ 'name': "Full name of the facility",
|
|
|
+ 'description': "Short description (will appear in facilities list)",
|
|
|
+ 'physical_address': "Physical location of the building (e.g. for GPS)",
|
|
|
+ 'shipping_address': "If different from the physical address"
|
|
|
+ }
|
|
|
+
|
|
|
+
|
|
|
+class FacilityCSVForm(forms.ModelForm):
|
|
|
+ tenant = forms.ModelChoiceField(
|
|
|
+ queryset=Tenant.objects.all(),
|
|
|
+ required=False,
|
|
|
+ to_field_name='name',
|
|
|
+ help_text='Name of assigned tenant',
|
|
|
+ error_messages={
|
|
|
+ 'invalid_choice': 'Tenant not found.',
|
|
|
+ }
|
|
|
+ )
|
|
|
+
|
|
|
+ class Meta:
|
|
|
+ model = Facility
|
|
|
+ fields = Facility.csv_headers
|
|
|
+ help_texts = {
|
|
|
+ 'name': 'Facility name',
|
|
|
+ 'slug': 'URL-friendly slug',
|
|
|
+ }
|
|
|
+
|
|
|
+
|
|
|
+class FacilityBulkEditForm(BootstrapMixin, CustomFieldBulkEditForm):
|
|
|
+ pk = forms.ModelMultipleChoiceField(queryset=Facility.objects.all(), widget=forms.MultipleHiddenInput)
|
|
|
+ tenant = forms.ModelChoiceField(queryset=Tenant.objects.all(), required=False)
|
|
|
+ description = forms.CharField(max_length=100, required=False)
|
|
|
+
|
|
|
+ class Meta:
|
|
|
+ nullable_fields = ['tenant', 'description']
|
|
|
+
|
|
|
+
|
|
|
+class FacilityFilterForm(BootstrapMixin, CustomFieldFilterForm):
|
|
|
+ model = Facility
|
|
|
+ q = forms.CharField(required=False, label='Search')
|
|
|
+ tenant = FilterChoiceField(
|
|
|
+ queryset=Tenant.objects.annotate(filter_count=Count('sites')),
|
|
|
+ to_field_name='slug',
|
|
|
+ null_label='-- None --'
|
|
|
+ )
|
|
|
+
|
|
|
+
|
|
|
+#
|
|
|
+# Sites
|
|
|
+#
|
|
|
+
|
|
|
+class SiteForm(BootstrapMixin, TenancyForm, CustomFieldForm):
|
|
|
+ region = TreeNodeChoiceField(queryset=Region.objects.all(), required=False)
|
|
|
+ slug = SlugField()
|
|
|
+ comments = CommentField()
|
|
|
+
|
|
|
+ class Meta:
|
|
|
+ model = Site
|
|
|
+ fields = [
|
|
|
+ 'name', 'slug', 'status', 'region', 'tenant_group', 'tenant', 'facility', 'asn', 'time_zone', 'description', 'comments',
|
|
|
+ ]
|
|
|
+ help_texts = {
|
|
|
'name': "Full name of the site",
|
|
|
- 'facility': "Data center provider and facility (e.g. Equinix NY7)",
|
|
|
+ 'facility': "Data center provider and facility",
|
|
|
'asn': "BGP autonomous system number",
|
|
|
'time_zone': "Local time zone",
|
|
|
'description': "Short description (will appear in sites list)",
|
|
|
- 'physical_address': "Physical location of the building (e.g. for GPS)",
|
|
|
- 'shipping_address': "If different from the physical address"
|
|
|
}
|
|
|
|
|
|
|
|
@@ -154,6 +212,15 @@ class SiteCSVForm(forms.ModelForm):
|
|
|
'invalid_choice': 'Tenant not found.',
|
|
|
}
|
|
|
)
|
|
|
+ facility = forms.ModelChoiceField(
|
|
|
+ queryset=Facility.objects.all(),
|
|
|
+ required=False,
|
|
|
+ to_field_name='name',
|
|
|
+ help_text='Name of facility',
|
|
|
+ error_messages={
|
|
|
+ 'invalid_choice': 'Facility not found.',
|
|
|
+ }
|
|
|
+ )
|
|
|
|
|
|
class Meta:
|
|
|
model = Site
|
|
@@ -170,12 +237,13 @@ class SiteBulkEditForm(BootstrapMixin, CustomFieldBulkEditForm):
|
|
|
status = forms.ChoiceField(choices=add_blank_choice(SITE_STATUS_CHOICES), required=False, initial='')
|
|
|
region = TreeNodeChoiceField(queryset=Region.objects.all(), required=False)
|
|
|
tenant = forms.ModelChoiceField(queryset=Tenant.objects.all(), required=False)
|
|
|
+ facility = forms.ModelChoiceField(queryset=Facility.objects.all(), required=False)
|
|
|
asn = forms.IntegerField(min_value=1, max_value=4294967295, required=False, label='ASN')
|
|
|
description = forms.CharField(max_length=100, required=False)
|
|
|
time_zone = TimeZoneFormField(required=False)
|
|
|
|
|
|
class Meta:
|
|
|
- nullable_fields = ['region', 'tenant', 'asn', 'description', 'time_zone']
|
|
|
+ nullable_fields = ['region', 'tenant', 'facility', 'asn', 'description', 'time_zone']
|
|
|
|
|
|
|
|
|
class SiteFilterForm(BootstrapMixin, CustomFieldFilterForm):
|
|
@@ -197,6 +265,11 @@ class SiteFilterForm(BootstrapMixin, CustomFieldFilterForm):
|
|
|
to_field_name='slug',
|
|
|
null_label='-- None --'
|
|
|
)
|
|
|
+ facility = FilterChoiceField(
|
|
|
+ queryset=Facility.objects.annotate(filter_count=Count('sites')),
|
|
|
+ to_field_name='slug',
|
|
|
+ null_label='-- None --'
|
|
|
+ )
|
|
|
|
|
|
|
|
|
#
|