mirror of
https://github.com/netbox-community/netbox.git
synced 2024-05-10 07:54:54 +00:00
Introduce the Cloud model
This commit is contained in:
@ -1,16 +1,29 @@
|
||||
from rest_framework import serializers
|
||||
|
||||
from circuits.models import Circuit, CircuitTermination, CircuitType, Provider
|
||||
from circuits.models import *
|
||||
from netbox.api import WritableNestedSerializer
|
||||
|
||||
__all__ = [
|
||||
'NestedCircuitSerializer',
|
||||
'NestedCircuitTerminationSerializer',
|
||||
'NestedCircuitTypeSerializer',
|
||||
'NestedCloudSerializer',
|
||||
'NestedProviderSerializer',
|
||||
]
|
||||
|
||||
|
||||
#
|
||||
# Clouds
|
||||
#
|
||||
|
||||
class NestedCloudSerializer(WritableNestedSerializer):
|
||||
url = serializers.HyperlinkedIdentityField(view_name='circuits-api:cloud-detail')
|
||||
|
||||
class Meta:
|
||||
model = Provider
|
||||
fields = ['id', 'url', 'display', 'name']
|
||||
|
||||
|
||||
#
|
||||
# Providers
|
||||
#
|
||||
|
@ -1,7 +1,7 @@
|
||||
from rest_framework import serializers
|
||||
|
||||
from circuits.choices import CircuitStatusChoices
|
||||
from circuits.models import Provider, Circuit, CircuitTermination, CircuitType
|
||||
from circuits.models import *
|
||||
from dcim.api.nested_serializers import NestedCableSerializer, NestedSiteSerializer
|
||||
from dcim.api.serializers import CableTerminationSerializer, ConnectedEndpointSerializer
|
||||
from netbox.api import ChoiceField
|
||||
@ -28,6 +28,22 @@ class ProviderSerializer(PrimaryModelSerializer):
|
||||
]
|
||||
|
||||
|
||||
#
|
||||
# Clouds
|
||||
#
|
||||
|
||||
class CloudSerializer(PrimaryModelSerializer):
|
||||
url = serializers.HyperlinkedIdentityField(view_name='circuits-api:cloud-detail')
|
||||
provider = NestedProviderSerializer()
|
||||
|
||||
class Meta:
|
||||
model = Cloud
|
||||
fields = [
|
||||
'id', 'url', 'display', 'provider', 'name', 'description', 'comments', 'tags', 'custom_fields', 'created',
|
||||
'last_updated',
|
||||
]
|
||||
|
||||
|
||||
#
|
||||
# Circuits
|
||||
#
|
||||
|
@ -13,5 +13,8 @@ router.register('circuit-types', views.CircuitTypeViewSet)
|
||||
router.register('circuits', views.CircuitViewSet)
|
||||
router.register('circuit-terminations', views.CircuitTerminationViewSet)
|
||||
|
||||
# Clouds
|
||||
router.register('clouds', views.CloudViewSet)
|
||||
|
||||
app_name = 'circuits-api'
|
||||
urlpatterns = router.urls
|
||||
|
@ -2,7 +2,7 @@ from django.db.models import Prefetch
|
||||
from rest_framework.routers import APIRootView
|
||||
|
||||
from circuits import filters
|
||||
from circuits.models import Provider, CircuitTermination, CircuitType, Circuit
|
||||
from circuits.models import *
|
||||
from dcim.api.views import PathEndpointMixin
|
||||
from extras.api.views import CustomFieldModelViewSet
|
||||
from netbox.api.views import ModelViewSet
|
||||
@ -66,3 +66,13 @@ class CircuitTerminationViewSet(PathEndpointMixin, ModelViewSet):
|
||||
serializer_class = serializers.CircuitTerminationSerializer
|
||||
filterset_class = filters.CircuitTerminationFilterSet
|
||||
brief_prefetch_fields = ['circuit']
|
||||
|
||||
|
||||
#
|
||||
# Clouds
|
||||
#
|
||||
|
||||
class CloudViewSet(CustomFieldModelViewSet):
|
||||
queryset = Cloud.objects.prefetch_related('tags')
|
||||
serializer_class = serializers.CloudSerializer
|
||||
filterset_class = filters.CloudFilterSet
|
||||
|
@ -9,12 +9,13 @@ from utilities.filters import (
|
||||
BaseFilterSet, NameSlugSearchFilterSet, TagFilter, TreeNodeMultipleChoiceFilter
|
||||
)
|
||||
from .choices import *
|
||||
from .models import Circuit, CircuitTermination, CircuitType, Provider
|
||||
from .models import *
|
||||
|
||||
__all__ = (
|
||||
'CircuitFilterSet',
|
||||
'CircuitTerminationFilterSet',
|
||||
'CircuitTypeFilterSet',
|
||||
'CloudFilterSet',
|
||||
'ProviderFilterSet',
|
||||
)
|
||||
|
||||
@ -79,6 +80,36 @@ class ProviderFilterSet(BaseFilterSet, CustomFieldModelFilterSet, CreatedUpdated
|
||||
)
|
||||
|
||||
|
||||
class CloudFilterSet(BaseFilterSet, CustomFieldModelFilterSet, CreatedUpdatedFilterSet):
|
||||
q = django_filters.CharFilter(
|
||||
method='search',
|
||||
label='Search',
|
||||
)
|
||||
provider_id = django_filters.ModelMultipleChoiceFilter(
|
||||
queryset=Provider.objects.all(),
|
||||
label='Provider (ID)',
|
||||
)
|
||||
provider = django_filters.ModelMultipleChoiceFilter(
|
||||
field_name='provider__slug',
|
||||
queryset=Provider.objects.all(),
|
||||
to_field_name='slug',
|
||||
label='Provider (slug)',
|
||||
)
|
||||
tag = TagFilter()
|
||||
|
||||
class Meta:
|
||||
model = Cloud
|
||||
fields = ['id', 'name']
|
||||
|
||||
def search(self, queryset, name, value):
|
||||
if not value.strip():
|
||||
return queryset
|
||||
return queryset.filter(
|
||||
Q(description__icontains=value) |
|
||||
Q(comments__icontains=value)
|
||||
).distinct()
|
||||
|
||||
|
||||
class CircuitTypeFilterSet(BaseFilterSet, NameSlugSearchFilterSet):
|
||||
|
||||
class Meta:
|
||||
|
@ -14,7 +14,7 @@ from utilities.forms import (
|
||||
StaticSelect2, StaticSelect2Multiple, TagFilterField,
|
||||
)
|
||||
from .choices import CircuitStatusChoices
|
||||
from .models import Circuit, CircuitTermination, CircuitType, Provider
|
||||
from .models import *
|
||||
|
||||
|
||||
#
|
||||
@ -128,6 +128,83 @@ class ProviderFilterForm(BootstrapMixin, CustomFieldFilterForm):
|
||||
tag = TagFilterField(model)
|
||||
|
||||
|
||||
#
|
||||
# Clouds
|
||||
#
|
||||
|
||||
class CloudForm(BootstrapMixin, CustomFieldModelForm):
|
||||
provider = DynamicModelChoiceField(
|
||||
queryset=Provider.objects.all()
|
||||
)
|
||||
comments = CommentField()
|
||||
tags = DynamicModelMultipleChoiceField(
|
||||
queryset=Tag.objects.all(),
|
||||
required=False
|
||||
)
|
||||
|
||||
class Meta:
|
||||
model = Cloud
|
||||
fields = [
|
||||
'provider', 'name', 'description', 'comments', 'tags',
|
||||
]
|
||||
fieldsets = (
|
||||
('Cloud', ('provider', 'name', 'description', 'tags')),
|
||||
)
|
||||
|
||||
|
||||
class CloudCSVForm(CustomFieldModelCSVForm):
|
||||
provider = CSVModelChoiceField(
|
||||
queryset=Provider.objects.all(),
|
||||
to_field_name='name',
|
||||
help_text='Assigned provider'
|
||||
)
|
||||
|
||||
class Meta:
|
||||
model = Cloud
|
||||
fields = [
|
||||
'provider', 'name', 'description', 'comments',
|
||||
]
|
||||
|
||||
|
||||
class CloudBulkEditForm(BootstrapMixin, AddRemoveTagsForm, CustomFieldBulkEditForm):
|
||||
pk = forms.ModelMultipleChoiceField(
|
||||
queryset=Cloud.objects.all(),
|
||||
widget=forms.MultipleHiddenInput
|
||||
)
|
||||
provider = DynamicModelChoiceField(
|
||||
queryset=Provider.objects.all(),
|
||||
required=False
|
||||
)
|
||||
description = forms.CharField(
|
||||
max_length=100,
|
||||
required=False
|
||||
)
|
||||
comments = CommentField(
|
||||
widget=SmallTextarea,
|
||||
label='Comments'
|
||||
)
|
||||
|
||||
class Meta:
|
||||
nullable_fields = [
|
||||
'description', 'comments',
|
||||
]
|
||||
|
||||
|
||||
class CloudFilterForm(BootstrapMixin, CustomFieldFilterForm):
|
||||
model = Cloud
|
||||
field_order = ['q', 'provider_id']
|
||||
q = forms.CharField(
|
||||
required=False,
|
||||
label=_('Search')
|
||||
)
|
||||
provider_id = DynamicModelMultipleChoiceField(
|
||||
queryset=Provider.objects.all(),
|
||||
required=False,
|
||||
label=_('Provider')
|
||||
)
|
||||
tag = TagFilterField(model)
|
||||
|
||||
|
||||
#
|
||||
# Circuit types
|
||||
#
|
||||
|
40
netbox/circuits/migrations/0027_cloud.py
Normal file
40
netbox/circuits/migrations/0027_cloud.py
Normal file
@ -0,0 +1,40 @@
|
||||
import django.core.serializers.json
|
||||
from django.db import migrations, models
|
||||
import django.db.models.deletion
|
||||
import taggit.managers
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('extras', '0058_journalentry'),
|
||||
('circuits', '0026_mark_connected'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.CreateModel(
|
||||
name='Cloud',
|
||||
fields=[
|
||||
('created', models.DateField(auto_now_add=True, null=True)),
|
||||
('last_updated', models.DateTimeField(auto_now=True, null=True)),
|
||||
('custom_field_data', models.JSONField(blank=True, default=dict, encoder=django.core.serializers.json.DjangoJSONEncoder)),
|
||||
('id', models.BigAutoField(primary_key=True, serialize=False)),
|
||||
('name', models.CharField(max_length=100)),
|
||||
('description', models.CharField(blank=True, max_length=200)),
|
||||
('comments', models.TextField(blank=True)),
|
||||
('provider', models.ForeignKey(on_delete=django.db.models.deletion.PROTECT, related_name='clouds', to='circuits.provider')),
|
||||
('tags', taggit.managers.TaggableManager(through='extras.TaggedItem', to='extras.Tag')),
|
||||
],
|
||||
options={
|
||||
'ordering': ('provider', 'name'),
|
||||
},
|
||||
),
|
||||
migrations.AddConstraint(
|
||||
model_name='cloud',
|
||||
constraint=models.UniqueConstraint(fields=('provider', 'name'), name='circuits_cloud_provider_name'),
|
||||
),
|
||||
migrations.AlterUniqueTogether(
|
||||
name='cloud',
|
||||
unique_together={('provider', 'name')},
|
||||
),
|
||||
]
|
@ -15,6 +15,7 @@ __all__ = (
|
||||
'Circuit',
|
||||
'CircuitTermination',
|
||||
'CircuitType',
|
||||
'Cloud',
|
||||
'Provider',
|
||||
)
|
||||
|
||||
@ -91,6 +92,59 @@ class Provider(PrimaryModel):
|
||||
)
|
||||
|
||||
|
||||
#
|
||||
# Clouds
|
||||
#
|
||||
|
||||
@extras_features('custom_fields', 'custom_links', 'export_templates', 'webhooks')
|
||||
class Cloud(PrimaryModel):
|
||||
name = models.CharField(
|
||||
max_length=100
|
||||
)
|
||||
provider = models.ForeignKey(
|
||||
to='circuits.Provider',
|
||||
on_delete=models.PROTECT,
|
||||
related_name='clouds'
|
||||
)
|
||||
description = models.CharField(
|
||||
max_length=200,
|
||||
blank=True
|
||||
)
|
||||
comments = models.TextField(
|
||||
blank=True
|
||||
)
|
||||
|
||||
csv_headers = [
|
||||
'provider', 'name', 'description', 'comments',
|
||||
]
|
||||
|
||||
objects = RestrictedQuerySet.as_manager()
|
||||
|
||||
class Meta:
|
||||
ordering = ('provider', 'name')
|
||||
constraints = (
|
||||
models.UniqueConstraint(
|
||||
fields=('provider', 'name'),
|
||||
name='circuits_cloud_provider_name'
|
||||
),
|
||||
)
|
||||
unique_together = ('provider', 'name')
|
||||
|
||||
def __str__(self):
|
||||
return self.name
|
||||
|
||||
def get_absolute_url(self):
|
||||
return reverse('circuits:cloud', args=[self.pk])
|
||||
|
||||
def to_csv(self):
|
||||
return (
|
||||
self.provider.name,
|
||||
self.name,
|
||||
self.description,
|
||||
self.comments,
|
||||
)
|
||||
|
||||
|
||||
@extras_features('custom_fields', 'export_templates', 'webhooks')
|
||||
class CircuitType(OrganizationalModel):
|
||||
"""
|
||||
|
@ -3,7 +3,7 @@ from django_tables2.utils import Accessor
|
||||
|
||||
from tenancy.tables import TenantColumn
|
||||
from utilities.tables import BaseTable, ButtonsColumn, ChoiceFieldColumn, TagColumn, ToggleColumn
|
||||
from .models import Circuit, CircuitType, Provider
|
||||
from .models import *
|
||||
|
||||
|
||||
#
|
||||
@ -29,6 +29,28 @@ class ProviderTable(BaseTable):
|
||||
default_columns = ('pk', 'name', 'asn', 'account', 'circuit_count')
|
||||
|
||||
|
||||
#
|
||||
# Clouds
|
||||
#
|
||||
|
||||
class CloudTable(BaseTable):
|
||||
pk = ToggleColumn()
|
||||
name = tables.Column(
|
||||
linkify=True
|
||||
)
|
||||
provider = tables.Column(
|
||||
linkify=True
|
||||
)
|
||||
tags = TagColumn(
|
||||
url_name='circuits:cloud_list'
|
||||
)
|
||||
|
||||
class Meta(BaseTable.Meta):
|
||||
model = Cloud
|
||||
fields = ('pk', 'name', 'provider', 'description', 'tags')
|
||||
default_columns = ('pk', 'name', 'provider', 'description')
|
||||
|
||||
|
||||
#
|
||||
# Circuit types
|
||||
#
|
||||
|
@ -1,7 +1,7 @@
|
||||
from django.urls import reverse
|
||||
|
||||
from circuits.choices import *
|
||||
from circuits.models import Circuit, CircuitTermination, CircuitType, Provider
|
||||
from circuits.models import *
|
||||
from dcim.models import Site
|
||||
from utilities.testing import APITestCase, APIViewTestCases
|
||||
|
||||
@ -178,3 +178,43 @@ class CircuitTerminationTest(APIViewTestCases.APIViewTestCase):
|
||||
cls.bulk_update_data = {
|
||||
'port_speed': 123456
|
||||
}
|
||||
|
||||
|
||||
class CloudTest(APIViewTestCases.APIViewTestCase):
|
||||
model = Cloud
|
||||
brief_fields = ['display', 'id', 'name', 'url']
|
||||
|
||||
@classmethod
|
||||
def setUpTestData(cls):
|
||||
providers = (
|
||||
Provider(name='Provider 1', slug='provider-1'),
|
||||
Provider(name='Provider 2', slug='provider-2'),
|
||||
)
|
||||
Provider.objects.bulk_create(providers)
|
||||
|
||||
clouds = (
|
||||
Cloud(name='Cloud 1', provider=providers[0]),
|
||||
Cloud(name='Cloud 2', provider=providers[0]),
|
||||
Cloud(name='Cloud 3', provider=providers[0]),
|
||||
)
|
||||
Cloud.objects.bulk_create(clouds)
|
||||
|
||||
cls.create_data = [
|
||||
{
|
||||
'name': 'Cloud 4',
|
||||
'provider': providers[0].pk,
|
||||
},
|
||||
{
|
||||
'name': 'Cloud 5',
|
||||
'provider': providers[0].pk,
|
||||
},
|
||||
{
|
||||
'name': 'Cloud 6',
|
||||
'provider': providers[0].pk,
|
||||
},
|
||||
]
|
||||
|
||||
cls.bulk_update_data = {
|
||||
'provider': providers[1].pk,
|
||||
'description': 'New description',
|
||||
}
|
||||
|
@ -2,7 +2,7 @@ from django.test import TestCase
|
||||
|
||||
from circuits.choices import *
|
||||
from circuits.filters import *
|
||||
from circuits.models import Circuit, CircuitTermination, CircuitType, Provider
|
||||
from circuits.models import *
|
||||
from dcim.models import Cable, Region, Site, SiteGroup
|
||||
from tenancy.models import Tenant, TenantGroup
|
||||
|
||||
@ -353,3 +353,40 @@ class CircuitTerminationTestCase(TestCase):
|
||||
self.assertEqual(self.filterset(params, self.queryset).qs.count(), 2)
|
||||
params = {'connected': False}
|
||||
self.assertEqual(self.filterset(params, self.queryset).qs.count(), 4)
|
||||
|
||||
|
||||
class CloudTestCase(TestCase):
|
||||
queryset = Cloud.objects.all()
|
||||
filterset = CloudFilterSet
|
||||
|
||||
@classmethod
|
||||
def setUpTestData(cls):
|
||||
|
||||
providers = (
|
||||
Provider(name='Provider 1', slug='provider-1'),
|
||||
Provider(name='Provider 2', slug='provider-2'),
|
||||
Provider(name='Provider 3', slug='provider-3'),
|
||||
)
|
||||
Provider.objects.bulk_create(providers)
|
||||
|
||||
clouds = (
|
||||
Cloud(name='Cloud 1', provider=providers[0]),
|
||||
Cloud(name='Cloud 2', provider=providers[1]),
|
||||
Cloud(name='Cloud 3', provider=providers[2]),
|
||||
)
|
||||
Cloud.objects.bulk_create(clouds)
|
||||
|
||||
def test_id(self):
|
||||
params = {'id': self.queryset.values_list('pk', flat=True)[:2]}
|
||||
self.assertEqual(self.filterset(params, self.queryset).qs.count(), 2)
|
||||
|
||||
def test_name(self):
|
||||
params = {'name': ['Cloud 1', 'Cloud 2']}
|
||||
self.assertEqual(self.filterset(params, self.queryset).qs.count(), 2)
|
||||
|
||||
def test_provider(self):
|
||||
providers = Provider.objects.all()[:2]
|
||||
params = {'provider_id': [providers[0].pk, providers[1].pk]}
|
||||
self.assertEqual(self.filterset(params, self.queryset).qs.count(), 2)
|
||||
params = {'provider': [providers[0].slug, providers[1].slug]}
|
||||
self.assertEqual(self.filterset(params, self.queryset).qs.count(), 2)
|
||||
|
@ -1,7 +1,7 @@
|
||||
import datetime
|
||||
|
||||
from circuits.choices import *
|
||||
from circuits.models import Circuit, CircuitType, Provider
|
||||
from circuits.models import *
|
||||
from utilities.testing import ViewTestCases
|
||||
|
||||
|
||||
@ -133,3 +133,45 @@ class CircuitTestCase(ViewTestCases.PrimaryObjectViewTestCase):
|
||||
'description': 'New description',
|
||||
'comments': 'New comments',
|
||||
}
|
||||
|
||||
|
||||
class CloudTestCase(ViewTestCases.PrimaryObjectViewTestCase):
|
||||
model = Cloud
|
||||
|
||||
@classmethod
|
||||
def setUpTestData(cls):
|
||||
|
||||
providers = (
|
||||
Provider(name='Provider 1', slug='provider-1'),
|
||||
Provider(name='Provider 2', slug='provider-2'),
|
||||
)
|
||||
Provider.objects.bulk_create(providers)
|
||||
|
||||
Cloud.objects.bulk_create([
|
||||
Cloud(name='Cloud 1', provider=providers[0]),
|
||||
Cloud(name='Cloud 2', provider=providers[0]),
|
||||
Cloud(name='Cloud 3', provider=providers[0]),
|
||||
])
|
||||
|
||||
tags = cls.create_tags('Alpha', 'Bravo', 'Charlie')
|
||||
|
||||
cls.form_data = {
|
||||
'name': 'Cloud X',
|
||||
'provider': providers[1].pk,
|
||||
'description': 'A new cloud',
|
||||
'comments': 'Longer description goes here',
|
||||
'tags': [t.pk for t in tags],
|
||||
}
|
||||
|
||||
cls.csv_data = (
|
||||
"name,provider,description",
|
||||
"Cloud 4,Provider 1,Foo",
|
||||
"Cloud 5,Provider 1,Bar",
|
||||
"Cloud 6,Provider 1,Baz",
|
||||
)
|
||||
|
||||
cls.bulk_edit_data = {
|
||||
'provider': providers[1].pk,
|
||||
'description': 'New description',
|
||||
'comments': 'New comments',
|
||||
}
|
||||
|
@ -3,7 +3,7 @@ from django.urls import path
|
||||
from dcim.views import CableCreateView, PathTraceView
|
||||
from extras.views import ObjectChangeLogView, ObjectJournalView
|
||||
from . import views
|
||||
from .models import Circuit, CircuitTermination, CircuitType, Provider
|
||||
from .models import *
|
||||
|
||||
app_name = 'circuits'
|
||||
urlpatterns = [
|
||||
@ -20,6 +20,18 @@ urlpatterns = [
|
||||
path('providers/<int:pk>/changelog/', ObjectChangeLogView.as_view(), name='provider_changelog', kwargs={'model': Provider}),
|
||||
path('providers/<int:pk>/journal/', ObjectJournalView.as_view(), name='provider_journal', kwargs={'model': Provider}),
|
||||
|
||||
# Clouds
|
||||
path('clouds/', views.CloudListView.as_view(), name='cloud_list'),
|
||||
path('clouds/add/', views.CloudEditView.as_view(), name='cloud_add'),
|
||||
path('clouds/import/', views.CloudBulkImportView.as_view(), name='cloud_import'),
|
||||
path('clouds/edit/', views.CloudBulkEditView.as_view(), name='cloud_bulk_edit'),
|
||||
path('clouds/delete/', views.CloudBulkDeleteView.as_view(), name='cloud_bulk_delete'),
|
||||
path('clouds/<int:pk>/', views.CloudView.as_view(), name='cloud'),
|
||||
path('clouds/<int:pk>/edit/', views.CloudEditView.as_view(), name='cloud_edit'),
|
||||
path('clouds/<int:pk>/delete/', views.CloudDeleteView.as_view(), name='cloud_delete'),
|
||||
path('clouds/<int:pk>/changelog/', ObjectChangeLogView.as_view(), name='cloud_changelog', kwargs={'model': Cloud}),
|
||||
path('clouds/<int:pk>/journal/', ObjectJournalView.as_view(), name='cloud_journal', kwargs={'model': Cloud}),
|
||||
|
||||
# Circuit types
|
||||
path('circuit-types/', views.CircuitTypeListView.as_view(), name='circuittype_list'),
|
||||
path('circuit-types/add/', views.CircuitTypeEditView.as_view(), name='circuittype_add'),
|
||||
|
@ -9,7 +9,7 @@ from utilities.paginator import EnhancedPaginator, get_paginate_count
|
||||
from utilities.utils import count_related
|
||||
from . import filters, forms, tables
|
||||
from .choices import CircuitTerminationSideChoices
|
||||
from .models import Circuit, CircuitTermination, CircuitType, Provider
|
||||
from .models import *
|
||||
|
||||
|
||||
#
|
||||
@ -81,6 +81,49 @@ class ProviderBulkDeleteView(generic.BulkDeleteView):
|
||||
table = tables.ProviderTable
|
||||
|
||||
|
||||
#
|
||||
# Clouds
|
||||
#
|
||||
|
||||
class CloudListView(generic.ObjectListView):
|
||||
queryset = Cloud.objects.all()
|
||||
filterset = filters.CloudFilterSet
|
||||
filterset_form = forms.CloudFilterForm
|
||||
table = tables.CloudTable
|
||||
|
||||
|
||||
class CloudView(generic.ObjectView):
|
||||
queryset = Cloud.objects.all()
|
||||
|
||||
|
||||
class CloudEditView(generic.ObjectEditView):
|
||||
queryset = Cloud.objects.all()
|
||||
model_form = forms.CloudForm
|
||||
|
||||
|
||||
class CloudDeleteView(generic.ObjectDeleteView):
|
||||
queryset = Cloud.objects.all()
|
||||
|
||||
|
||||
class CloudBulkImportView(generic.BulkImportView):
|
||||
queryset = Cloud.objects.all()
|
||||
model_form = forms.CloudCSVForm
|
||||
table = tables.CloudTable
|
||||
|
||||
|
||||
class CloudBulkEditView(generic.BulkEditView):
|
||||
queryset = Cloud.objects.all()
|
||||
filterset = filters.CloudFilterSet
|
||||
table = tables.CloudTable
|
||||
form = forms.CloudBulkEditForm
|
||||
|
||||
|
||||
class CloudBulkDeleteView(generic.BulkDeleteView):
|
||||
queryset = Cloud.objects.all()
|
||||
filterset = filters.CloudFilterSet
|
||||
table = tables.CloudTable
|
||||
|
||||
|
||||
#
|
||||
# Circuit Types
|
||||
#
|
||||
|
@ -1,10 +1,8 @@
|
||||
from collections import OrderedDict
|
||||
|
||||
from django.db.models import Count
|
||||
|
||||
from circuits.filters import CircuitFilterSet, ProviderFilterSet
|
||||
from circuits.models import Circuit, Provider
|
||||
from circuits.tables import CircuitTable, ProviderTable
|
||||
from circuits.filters import CircuitFilterSet, CloudFilterSet, ProviderFilterSet
|
||||
from circuits.models import Circuit, Cloud, Provider
|
||||
from circuits.tables import CircuitTable, CloudTable, ProviderTable
|
||||
from dcim.filters import (
|
||||
CableFilterSet, DeviceFilterSet, DeviceTypeFilterSet, PowerFeedFilterSet, RackFilterSet, LocationFilterSet,
|
||||
SiteFilterSet, VirtualChassisFilterSet,
|
||||
@ -47,6 +45,12 @@ SEARCH_TYPES = OrderedDict((
|
||||
'table': CircuitTable,
|
||||
'url': 'circuits:circuit_list',
|
||||
}),
|
||||
('cloud', {
|
||||
'queryset': Cloud.objects.prefetch_related('provider'),
|
||||
'filterset': CloudFilterSet,
|
||||
'table': CloudTable,
|
||||
'url': 'circuits:cloud_list',
|
||||
}),
|
||||
# DCIM
|
||||
('site', {
|
||||
'queryset': Site.objects.prefetch_related('region', 'tenant'),
|
||||
|
55
netbox/templates/circuits/cloud.html
Normal file
55
netbox/templates/circuits/cloud.html
Normal file
@ -0,0 +1,55 @@
|
||||
{% extends 'generic/object.html' %}
|
||||
{% load static %}
|
||||
{% load helpers %}
|
||||
{% load plugins %}
|
||||
|
||||
{% block breadcrumbs %}
|
||||
<li><a href="{% url 'circuits:cloud_list' %}">Clouds</a></li>
|
||||
<li><a href="{% url 'circuits:cloud_list' %}?provider_id={{ object.provider_id }}">{{ object.provider }}</a></li>
|
||||
<li>{{ object }}</li>
|
||||
{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
<div class="row">
|
||||
<div class="col-md-4">
|
||||
<div class="panel panel-default">
|
||||
<div class="panel-heading">
|
||||
<strong>Cloud</strong>
|
||||
</div>
|
||||
<table class="table table-hover panel-body attr-table">
|
||||
<tr>
|
||||
<td>Name</td>
|
||||
<td>{{ object.name }}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Description</td>
|
||||
<td>{{ object.description }}</td>
|
||||
</tr>
|
||||
</table>
|
||||
</div>
|
||||
{% include 'inc/custom_fields_panel.html' %}
|
||||
{% include 'extras/inc/tags_panel.html' with tags=object.tags.all url='circuits:cloud_list' %}
|
||||
{% plugin_left_page object %}
|
||||
</div>
|
||||
<div class="col-md-8">
|
||||
<div class="panel panel-default">
|
||||
<div class="panel-heading">
|
||||
<strong>Comments</strong>
|
||||
</div>
|
||||
<div class="panel-body rendered-markdown">
|
||||
{% if object.comments %}
|
||||
{{ object.comments|render_markdown }}
|
||||
{% else %}
|
||||
<span class="text-muted">None</span>
|
||||
{% endif %}
|
||||
</div>
|
||||
</div>
|
||||
{% plugin_right_page object %}
|
||||
</div>
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="col-md-12">
|
||||
{% plugin_full_width_page object %}
|
||||
</div>
|
||||
</div>
|
||||
{% endblock %}
|
@ -465,6 +465,14 @@
|
||||
</div>
|
||||
{% endif %}
|
||||
<a href="{% url 'circuits:provider_list' %}">Providers</a>
|
||||
<li{% if not perms.circuits.view_cloud %} class="disabled"{% endif %}>
|
||||
{% if perms.circuits.add_cloud %}
|
||||
<div class="buttons pull-right">
|
||||
<a href="{% url 'circuits:cloud_add' %}" class="btn btn-xs btn-success" title="Add"><i class="mdi mdi-plus-thick"></i></a>
|
||||
<a href="{% url 'circuits:cloud_import' %}" class="btn btn-xs btn-info" title="Import"><i class="mdi mdi-database-import-outline"></i></a>
|
||||
</div>
|
||||
{% endif %}
|
||||
<a href="{% url 'circuits:cloud_list' %}">Clouds</a>
|
||||
</li>
|
||||
</ul>
|
||||
</li>
|
||||
|
Reference in New Issue
Block a user