mirror of
https://github.com/netbox-community/netbox.git
synced 2025-07-20 02:06:42 -06:00
commit
ac1e4b8e8f
26
CHANGELOG.md
26
CHANGELOG.md
@ -1,3 +1,29 @@
|
|||||||
|
v2.5.7 (2019-02-21)
|
||||||
|
|
||||||
|
## Enhancements
|
||||||
|
|
||||||
|
* [#2357](https://github.com/digitalocean/netbox/issues/2357) - Enable filtering of devices by rack face
|
||||||
|
* [#2638](https://github.com/digitalocean/netbox/issues/2638) - Add button to copy unlocked secret to clipboard
|
||||||
|
* [#2870](https://github.com/digitalocean/netbox/issues/2870) - Add Markdown rendering for provider NOC/admin contact fields
|
||||||
|
* [#2878](https://github.com/digitalocean/netbox/issues/2878) - Add cable types for OS1/OS2 singlemode fiber
|
||||||
|
* [#2890](https://github.com/digitalocean/netbox/issues/2890) - Add port types for APC fiber
|
||||||
|
* [#2898](https://github.com/digitalocean/netbox/issues/2898) - Enable filtering cables list by connection status
|
||||||
|
* [#2903](https://github.com/digitalocean/netbox/issues/2903) - Clarify purpose of tags field on interface edit form
|
||||||
|
|
||||||
|
## Bug Fixes
|
||||||
|
|
||||||
|
* [#2852](https://github.com/digitalocean/netbox/issues/2852) - Allow filtering devices by null rack position
|
||||||
|
* [#2884](https://github.com/digitalocean/netbox/issues/2884) - Don't display connect button for wireless interfaces
|
||||||
|
* [#2888](https://github.com/digitalocean/netbox/issues/2888) - Correct foreground color of device roles in rack elevations
|
||||||
|
* [#2893](https://github.com/digitalocean/netbox/issues/2893) - Remove duplicate display of VRF RD on IP address view
|
||||||
|
* [#2895](https://github.com/digitalocean/netbox/issues/2895) - Fix filtering of nullable character fields
|
||||||
|
* [#2901](https://github.com/digitalocean/netbox/issues/2901) - Fix ordering regions by site count
|
||||||
|
* [#2910](https://github.com/digitalocean/netbox/issues/2910) - Fix config context list and edit forms to use Select2 elements
|
||||||
|
* [#2912](https://github.com/digitalocean/netbox/issues/2912) - Cable type in filter form should be blank by default
|
||||||
|
* [#2913](https://github.com/digitalocean/netbox/issues/2913) - Fix assigned prefixes link on VRF view
|
||||||
|
* [#2914](https://github.com/digitalocean/netbox/issues/2914) - Fix empty connected circuit link on device interfaces list
|
||||||
|
* [#2915](https://github.com/digitalocean/netbox/issues/2915) - Fix bulk editing of pass-through ports
|
||||||
|
|
||||||
v2.5.6 (2019-02-13)
|
v2.5.6 (2019-02-13)
|
||||||
|
|
||||||
## Enhancements
|
## Enhancements
|
||||||
|
@ -128,4 +128,4 @@ Reports can be run on the CLI by invoking the management command:
|
|||||||
python3 manage.py runreport <module>
|
python3 manage.py runreport <module>
|
||||||
```
|
```
|
||||||
|
|
||||||
One or more report modules may be specified.
|
where ``<module>`` is the name of the python file in the ``reports`` directory without the ``.py`` extension. One or more report modules may be specified.
|
||||||
|
91
netbox/circuits/tests/test_views.py
Normal file
91
netbox/circuits/tests/test_views.py
Normal file
@ -0,0 +1,91 @@
|
|||||||
|
import urllib.parse
|
||||||
|
|
||||||
|
from django.test import Client, TestCase
|
||||||
|
from django.urls import reverse
|
||||||
|
|
||||||
|
from circuits.models import Circuit, CircuitType, Provider
|
||||||
|
|
||||||
|
|
||||||
|
class ProviderTestCase(TestCase):
|
||||||
|
|
||||||
|
def setUp(self):
|
||||||
|
|
||||||
|
self.client = Client()
|
||||||
|
|
||||||
|
Provider.objects.bulk_create([
|
||||||
|
Provider(name='Provider 1', slug='provider-1', asn=65001),
|
||||||
|
Provider(name='Provider 2', slug='provider-2', asn=65002),
|
||||||
|
Provider(name='Provider 3', slug='provider-3', asn=65003),
|
||||||
|
])
|
||||||
|
|
||||||
|
def test_provider_list(self):
|
||||||
|
|
||||||
|
url = reverse('circuits:provider_list')
|
||||||
|
params = {
|
||||||
|
"q": "test",
|
||||||
|
}
|
||||||
|
|
||||||
|
response = self.client.get('{}?{}'.format(url, urllib.parse.urlencode(params)))
|
||||||
|
self.assertEqual(response.status_code, 200)
|
||||||
|
|
||||||
|
def test_provider(self):
|
||||||
|
|
||||||
|
provider = Provider.objects.first()
|
||||||
|
response = self.client.get(provider.get_absolute_url())
|
||||||
|
self.assertEqual(response.status_code, 200)
|
||||||
|
|
||||||
|
|
||||||
|
class CircuitTypeTestCase(TestCase):
|
||||||
|
|
||||||
|
def setUp(self):
|
||||||
|
|
||||||
|
self.client = Client()
|
||||||
|
|
||||||
|
CircuitType.objects.bulk_create([
|
||||||
|
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'),
|
||||||
|
])
|
||||||
|
|
||||||
|
def test_circuittype_list(self):
|
||||||
|
|
||||||
|
url = reverse('circuits:circuittype_list')
|
||||||
|
|
||||||
|
response = self.client.get(url)
|
||||||
|
self.assertEqual(response.status_code, 200)
|
||||||
|
|
||||||
|
|
||||||
|
class CircuitTestCase(TestCase):
|
||||||
|
|
||||||
|
def setUp(self):
|
||||||
|
|
||||||
|
self.client = Client()
|
||||||
|
|
||||||
|
provider = Provider(name='Provider 1', slug='provider-1', asn=65001)
|
||||||
|
provider.save()
|
||||||
|
|
||||||
|
circuittype = CircuitType(name='Circuit Type 1', slug='circuit-type-1')
|
||||||
|
circuittype.save()
|
||||||
|
|
||||||
|
Circuit.objects.bulk_create([
|
||||||
|
Circuit(cid='Circuit 1', provider=provider, type=circuittype),
|
||||||
|
Circuit(cid='Circuit 2', provider=provider, type=circuittype),
|
||||||
|
Circuit(cid='Circuit 3', provider=provider, type=circuittype),
|
||||||
|
])
|
||||||
|
|
||||||
|
def test_circuit_list(self):
|
||||||
|
|
||||||
|
url = reverse('circuits:circuit_list')
|
||||||
|
params = {
|
||||||
|
"provider": Provider.objects.first().slug,
|
||||||
|
"type": CircuitType.objects.first().slug,
|
||||||
|
}
|
||||||
|
|
||||||
|
response = self.client.get('{}?{}'.format(url, urllib.parse.urlencode(params)))
|
||||||
|
self.assertEqual(response.status_code, 200)
|
||||||
|
|
||||||
|
def test_provider(self):
|
||||||
|
|
||||||
|
provider = Provider.objects.first()
|
||||||
|
response = self.client.get(provider.get_absolute_url())
|
||||||
|
self.assertEqual(response.status_code, 200)
|
@ -43,6 +43,12 @@ RACK_STATUS_CHOICES = [
|
|||||||
[RACK_STATUS_DEPRECATED, 'Deprecated'],
|
[RACK_STATUS_DEPRECATED, 'Deprecated'],
|
||||||
]
|
]
|
||||||
|
|
||||||
|
# Device rack position
|
||||||
|
DEVICE_POSITION_CHOICES = [
|
||||||
|
# Rack.u_height is limited to 100
|
||||||
|
(i, 'Unit {}'.format(i)) for i in range(1, 101)
|
||||||
|
]
|
||||||
|
|
||||||
# Parent/child device roles
|
# Parent/child device roles
|
||||||
SUBDEVICE_ROLE_PARENT = True
|
SUBDEVICE_ROLE_PARENT = True
|
||||||
SUBDEVICE_ROLE_CHILD = False
|
SUBDEVICE_ROLE_CHILD = False
|
||||||
@ -270,11 +276,14 @@ PORT_TYPE_8P8C = 1000
|
|||||||
PORT_TYPE_110_PUNCH = 1100
|
PORT_TYPE_110_PUNCH = 1100
|
||||||
PORT_TYPE_ST = 2000
|
PORT_TYPE_ST = 2000
|
||||||
PORT_TYPE_SC = 2100
|
PORT_TYPE_SC = 2100
|
||||||
|
PORT_TYPE_SC_APC = 2110
|
||||||
PORT_TYPE_FC = 2200
|
PORT_TYPE_FC = 2200
|
||||||
PORT_TYPE_LC = 2300
|
PORT_TYPE_LC = 2300
|
||||||
|
PORT_TYPE_LC_APC = 2310
|
||||||
PORT_TYPE_MTRJ = 2400
|
PORT_TYPE_MTRJ = 2400
|
||||||
PORT_TYPE_MPO = 2500
|
PORT_TYPE_MPO = 2500
|
||||||
PORT_TYPE_LSH = 2600
|
PORT_TYPE_LSH = 2600
|
||||||
|
PORT_TYPE_LSH_APC = 2610
|
||||||
PORT_TYPE_CHOICES = [
|
PORT_TYPE_CHOICES = [
|
||||||
[
|
[
|
||||||
'Copper',
|
'Copper',
|
||||||
@ -288,10 +297,13 @@ PORT_TYPE_CHOICES = [
|
|||||||
[
|
[
|
||||||
[PORT_TYPE_FC, 'FC'],
|
[PORT_TYPE_FC, 'FC'],
|
||||||
[PORT_TYPE_LC, 'LC'],
|
[PORT_TYPE_LC, 'LC'],
|
||||||
|
[PORT_TYPE_LC_APC, 'LC/APC'],
|
||||||
[PORT_TYPE_LSH, 'LSH'],
|
[PORT_TYPE_LSH, 'LSH'],
|
||||||
|
[PORT_TYPE_LSH_APC, 'LSH/APC'],
|
||||||
[PORT_TYPE_MPO, 'MPO'],
|
[PORT_TYPE_MPO, 'MPO'],
|
||||||
[PORT_TYPE_MTRJ, 'MTRJ'],
|
[PORT_TYPE_MTRJ, 'MTRJ'],
|
||||||
[PORT_TYPE_SC, 'SC'],
|
[PORT_TYPE_SC, 'SC'],
|
||||||
|
[PORT_TYPE_SC_APC, 'SC/APC'],
|
||||||
[PORT_TYPE_ST, 'ST'],
|
[PORT_TYPE_ST, 'ST'],
|
||||||
]
|
]
|
||||||
]
|
]
|
||||||
@ -355,11 +367,14 @@ CABLE_TYPE_CAT6A = 1610
|
|||||||
CABLE_TYPE_CAT7 = 1700
|
CABLE_TYPE_CAT7 = 1700
|
||||||
CABLE_TYPE_DAC_ACTIVE = 1800
|
CABLE_TYPE_DAC_ACTIVE = 1800
|
||||||
CABLE_TYPE_DAC_PASSIVE = 1810
|
CABLE_TYPE_DAC_PASSIVE = 1810
|
||||||
|
CABLE_TYPE_MMF = 3000
|
||||||
CABLE_TYPE_MMF_OM1 = 3010
|
CABLE_TYPE_MMF_OM1 = 3010
|
||||||
CABLE_TYPE_MMF_OM2 = 3020
|
CABLE_TYPE_MMF_OM2 = 3020
|
||||||
CABLE_TYPE_MMF_OM3 = 3030
|
CABLE_TYPE_MMF_OM3 = 3030
|
||||||
CABLE_TYPE_MMF_OM4 = 3040
|
CABLE_TYPE_MMF_OM4 = 3040
|
||||||
CABLE_TYPE_SMF = 3500
|
CABLE_TYPE_SMF = 3500
|
||||||
|
CABLE_TYPE_SMF_OS1 = 3510
|
||||||
|
CABLE_TYPE_SMF_OS2 = 3520
|
||||||
CABLE_TYPE_AOC = 3800
|
CABLE_TYPE_AOC = 3800
|
||||||
CABLE_TYPE_POWER = 5000
|
CABLE_TYPE_POWER = 5000
|
||||||
CABLE_TYPE_CHOICES = (
|
CABLE_TYPE_CHOICES = (
|
||||||
@ -377,11 +392,14 @@ CABLE_TYPE_CHOICES = (
|
|||||||
),
|
),
|
||||||
(
|
(
|
||||||
'Fiber', (
|
'Fiber', (
|
||||||
|
(CABLE_TYPE_MMF, 'Multimode Fiber'),
|
||||||
(CABLE_TYPE_MMF_OM1, 'Multimode Fiber (OM1)'),
|
(CABLE_TYPE_MMF_OM1, 'Multimode Fiber (OM1)'),
|
||||||
(CABLE_TYPE_MMF_OM2, 'Multimode Fiber (OM2)'),
|
(CABLE_TYPE_MMF_OM2, 'Multimode Fiber (OM2)'),
|
||||||
(CABLE_TYPE_MMF_OM3, 'Multimode Fiber (OM3)'),
|
(CABLE_TYPE_MMF_OM3, 'Multimode Fiber (OM3)'),
|
||||||
(CABLE_TYPE_MMF_OM4, 'Multimode Fiber (OM4)'),
|
(CABLE_TYPE_MMF_OM4, 'Multimode Fiber (OM4)'),
|
||||||
(CABLE_TYPE_SMF, 'Singlemode Fiber'),
|
(CABLE_TYPE_SMF, 'Singlemode Fiber'),
|
||||||
|
(CABLE_TYPE_SMF_OS1, 'Singlemode Fiber (OS1)'),
|
||||||
|
(CABLE_TYPE_SMF_OS2, 'Singlemode Fiber (OS2)'),
|
||||||
(CABLE_TYPE_AOC, 'Active Optical Cabling (AOC)'),
|
(CABLE_TYPE_AOC, 'Active Optical Cabling (AOC)'),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
@ -543,6 +543,10 @@ class DeviceFilter(CustomFieldFilterSet):
|
|||||||
queryset=Rack.objects.all(),
|
queryset=Rack.objects.all(),
|
||||||
label='Rack (ID)',
|
label='Rack (ID)',
|
||||||
)
|
)
|
||||||
|
position = django_filters.ChoiceFilter(
|
||||||
|
choices=DEVICE_POSITION_CHOICES,
|
||||||
|
null_label='Non-racked'
|
||||||
|
)
|
||||||
cluster_id = django_filters.ModelMultipleChoiceFilter(
|
cluster_id = django_filters.ModelMultipleChoiceFilter(
|
||||||
queryset=Cluster.objects.all(),
|
queryset=Cluster.objects.all(),
|
||||||
label='VM cluster (ID)',
|
label='VM cluster (ID)',
|
||||||
@ -602,7 +606,7 @@ class DeviceFilter(CustomFieldFilterSet):
|
|||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
model = Device
|
model = Device
|
||||||
fields = ['serial', 'position']
|
fields = ['serial', 'face']
|
||||||
|
|
||||||
def search(self, queryset, name, value):
|
def search(self, queryset, name, value):
|
||||||
if not value.strip():
|
if not value.strip():
|
||||||
|
@ -2362,7 +2362,7 @@ class FrontPortCreateForm(ComponentForm):
|
|||||||
|
|
||||||
class FrontPortBulkEditForm(BootstrapMixin, AddRemoveTagsForm, BulkEditForm):
|
class FrontPortBulkEditForm(BootstrapMixin, AddRemoveTagsForm, BulkEditForm):
|
||||||
pk = forms.ModelMultipleChoiceField(
|
pk = forms.ModelMultipleChoiceField(
|
||||||
queryset=Interface.objects.all(),
|
queryset=FrontPort.objects.all(),
|
||||||
widget=forms.MultipleHiddenInput()
|
widget=forms.MultipleHiddenInput()
|
||||||
)
|
)
|
||||||
type = forms.ChoiceField(
|
type = forms.ChoiceField(
|
||||||
@ -2436,7 +2436,7 @@ class RearPortCreateForm(ComponentForm):
|
|||||||
|
|
||||||
class RearPortBulkEditForm(BootstrapMixin, AddRemoveTagsForm, BulkEditForm):
|
class RearPortBulkEditForm(BootstrapMixin, AddRemoveTagsForm, BulkEditForm):
|
||||||
pk = forms.ModelMultipleChoiceField(
|
pk = forms.ModelMultipleChoiceField(
|
||||||
queryset=Interface.objects.all(),
|
queryset=RearPort.objects.all(),
|
||||||
widget=forms.MultipleHiddenInput()
|
widget=forms.MultipleHiddenInput()
|
||||||
)
|
)
|
||||||
type = forms.ChoiceField(
|
type = forms.ChoiceField(
|
||||||
@ -2753,10 +2753,15 @@ class CableFilterForm(BootstrapMixin, forms.Form):
|
|||||||
label='Search'
|
label='Search'
|
||||||
)
|
)
|
||||||
type = forms.MultipleChoiceField(
|
type = forms.MultipleChoiceField(
|
||||||
choices=CABLE_TYPE_CHOICES,
|
choices=add_blank_choice(CABLE_TYPE_CHOICES),
|
||||||
required=False,
|
required=False,
|
||||||
widget=StaticSelect2()
|
widget=StaticSelect2()
|
||||||
)
|
)
|
||||||
|
status = forms.ChoiceField(
|
||||||
|
required=False,
|
||||||
|
choices=add_blank_choice(CONNECTION_STATUS_CHOICES),
|
||||||
|
widget=StaticSelect2()
|
||||||
|
)
|
||||||
color = forms.CharField(
|
color = forms.CharField(
|
||||||
max_length=6,
|
max_length=6,
|
||||||
required=False,
|
required=False,
|
||||||
|
38
netbox/dcim/migrations/0069_deprecate_nullablecharfield.py
Normal file
38
netbox/dcim/migrations/0069_deprecate_nullablecharfield.py
Normal file
@ -0,0 +1,38 @@
|
|||||||
|
# Generated by Django 2.1.5 on 2019-02-14 14:26
|
||||||
|
|
||||||
|
from django.db import migrations, models
|
||||||
|
|
||||||
|
|
||||||
|
class Migration(migrations.Migration):
|
||||||
|
|
||||||
|
dependencies = [
|
||||||
|
('dcim', '0068_rack_new_fields'),
|
||||||
|
]
|
||||||
|
|
||||||
|
operations = [
|
||||||
|
migrations.AlterField(
|
||||||
|
model_name='device',
|
||||||
|
name='asset_tag',
|
||||||
|
field=models.CharField(blank=True, max_length=50, null=True, unique=True),
|
||||||
|
),
|
||||||
|
migrations.AlterField(
|
||||||
|
model_name='device',
|
||||||
|
name='name',
|
||||||
|
field=models.CharField(blank=True, max_length=64, null=True, unique=True),
|
||||||
|
),
|
||||||
|
migrations.AlterField(
|
||||||
|
model_name='inventoryitem',
|
||||||
|
name='asset_tag',
|
||||||
|
field=models.CharField(blank=True, max_length=50, null=True, unique=True),
|
||||||
|
),
|
||||||
|
migrations.AlterField(
|
||||||
|
model_name='rack',
|
||||||
|
name='asset_tag',
|
||||||
|
field=models.CharField(blank=True, max_length=50, null=True, unique=True),
|
||||||
|
),
|
||||||
|
migrations.AlterField(
|
||||||
|
model_name='rack',
|
||||||
|
name='facility_id',
|
||||||
|
field=models.CharField(blank=True, max_length=50, null=True),
|
||||||
|
),
|
||||||
|
]
|
@ -16,7 +16,7 @@ from taggit.managers import TaggableManager
|
|||||||
from timezone_field import TimeZoneField
|
from timezone_field import TimeZoneField
|
||||||
|
|
||||||
from extras.models import ConfigContextModel, CustomFieldModel, ObjectChange
|
from extras.models import ConfigContextModel, CustomFieldModel, ObjectChange
|
||||||
from utilities.fields import ColorField, NullableCharField
|
from utilities.fields import ColorField
|
||||||
from utilities.managers import NaturalOrderingManager
|
from utilities.managers import NaturalOrderingManager
|
||||||
from utilities.models import ChangeLoggedModel
|
from utilities.models import ChangeLoggedModel
|
||||||
from utilities.utils import serialize_object, to_meters
|
from utilities.utils import serialize_object, to_meters
|
||||||
@ -217,8 +217,7 @@ class Region(MPTTModel, ChangeLoggedModel):
|
|||||||
self.parent.name if self.parent else None,
|
self.parent.name if self.parent else None,
|
||||||
)
|
)
|
||||||
|
|
||||||
@property
|
def get_site_count(self):
|
||||||
def site_count(self):
|
|
||||||
return Site.objects.filter(
|
return Site.objects.filter(
|
||||||
Q(region=self) |
|
Q(region=self) |
|
||||||
Q(region__in=self.get_descendants())
|
Q(region__in=self.get_descendants())
|
||||||
@ -470,7 +469,7 @@ class Rack(ChangeLoggedModel, CustomFieldModel):
|
|||||||
name = models.CharField(
|
name = models.CharField(
|
||||||
max_length=50
|
max_length=50
|
||||||
)
|
)
|
||||||
facility_id = NullableCharField(
|
facility_id = models.CharField(
|
||||||
max_length=50,
|
max_length=50,
|
||||||
blank=True,
|
blank=True,
|
||||||
null=True,
|
null=True,
|
||||||
@ -511,7 +510,7 @@ class Rack(ChangeLoggedModel, CustomFieldModel):
|
|||||||
blank=True,
|
blank=True,
|
||||||
verbose_name='Serial number'
|
verbose_name='Serial number'
|
||||||
)
|
)
|
||||||
asset_tag = NullableCharField(
|
asset_tag = models.CharField(
|
||||||
max_length=50,
|
max_length=50,
|
||||||
blank=True,
|
blank=True,
|
||||||
null=True,
|
null=True,
|
||||||
@ -1354,7 +1353,7 @@ class Device(ChangeLoggedModel, ConfigContextModel, CustomFieldModel):
|
|||||||
blank=True,
|
blank=True,
|
||||||
null=True
|
null=True
|
||||||
)
|
)
|
||||||
name = NullableCharField(
|
name = models.CharField(
|
||||||
max_length=64,
|
max_length=64,
|
||||||
blank=True,
|
blank=True,
|
||||||
null=True,
|
null=True,
|
||||||
@ -1365,7 +1364,7 @@ class Device(ChangeLoggedModel, ConfigContextModel, CustomFieldModel):
|
|||||||
blank=True,
|
blank=True,
|
||||||
verbose_name='Serial number'
|
verbose_name='Serial number'
|
||||||
)
|
)
|
||||||
asset_tag = NullableCharField(
|
asset_tag = models.CharField(
|
||||||
max_length=50,
|
max_length=50,
|
||||||
blank=True,
|
blank=True,
|
||||||
null=True,
|
null=True,
|
||||||
@ -2389,7 +2388,7 @@ class InventoryItem(ComponentModel):
|
|||||||
verbose_name='Serial number',
|
verbose_name='Serial number',
|
||||||
blank=True
|
blank=True
|
||||||
)
|
)
|
||||||
asset_tag = NullableCharField(
|
asset_tag = models.CharField(
|
||||||
max_length=50,
|
max_length=50,
|
||||||
unique=True,
|
unique=True,
|
||||||
blank=True,
|
blank=True,
|
||||||
@ -2652,6 +2651,9 @@ class Cable(ChangeLoggedModel):
|
|||||||
self.length_unit,
|
self.length_unit,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
def get_status_class(self):
|
||||||
|
return 'success' if self.status else 'info'
|
||||||
|
|
||||||
def get_path_endpoints(self):
|
def get_path_endpoints(self):
|
||||||
"""
|
"""
|
||||||
Traverse both ends of a cable path and return its connected endpoints. Note that one or both endpoints may be
|
Traverse both ends of a cable path and return its connected endpoints. Note that one or both endpoints may be
|
||||||
|
@ -647,6 +647,9 @@ class CableTable(BaseTable):
|
|||||||
orderable=False,
|
orderable=False,
|
||||||
verbose_name=''
|
verbose_name=''
|
||||||
)
|
)
|
||||||
|
status = tables.TemplateColumn(
|
||||||
|
template_code=STATUS_LABEL
|
||||||
|
)
|
||||||
length = tables.TemplateColumn(
|
length = tables.TemplateColumn(
|
||||||
template_code=CABLE_LENGTH,
|
template_code=CABLE_LENGTH,
|
||||||
order_by='_abs_length'
|
order_by='_abs_length'
|
||||||
|
458
netbox/dcim/tests/test_views.py
Normal file
458
netbox/dcim/tests/test_views.py
Normal file
@ -0,0 +1,458 @@
|
|||||||
|
import urllib.parse
|
||||||
|
|
||||||
|
from django.contrib.auth import get_user_model
|
||||||
|
from django.test import Client, TestCase
|
||||||
|
from django.urls import reverse
|
||||||
|
|
||||||
|
from dcim.constants import CABLE_TYPE_CAT6, IFACE_FF_1GE_FIXED
|
||||||
|
from dcim.models import (
|
||||||
|
Cable, Device, DeviceRole, DeviceType, Interface, InventoryItem, Manufacturer, Platform, Rack, RackGroup,
|
||||||
|
RackReservation, RackRole, Site, Region, VirtualChassis,
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
class RegionTestCase(TestCase):
|
||||||
|
|
||||||
|
def setUp(self):
|
||||||
|
|
||||||
|
self.client = Client()
|
||||||
|
|
||||||
|
# Create three Regions
|
||||||
|
for i in range(1, 4):
|
||||||
|
Region(name='Region {}'.format(i), slug='region-{}'.format(i)).save()
|
||||||
|
|
||||||
|
def test_region_list(self):
|
||||||
|
|
||||||
|
url = reverse('dcim:region_list')
|
||||||
|
|
||||||
|
response = self.client.get(url)
|
||||||
|
self.assertEqual(response.status_code, 200)
|
||||||
|
|
||||||
|
|
||||||
|
class SiteTestCase(TestCase):
|
||||||
|
|
||||||
|
def setUp(self):
|
||||||
|
|
||||||
|
self.client = Client()
|
||||||
|
|
||||||
|
region = Region(name='Region 1', slug='region-1')
|
||||||
|
region.save()
|
||||||
|
|
||||||
|
Site.objects.bulk_create([
|
||||||
|
Site(name='Site 1', slug='site-1', region=region),
|
||||||
|
Site(name='Site 2', slug='site-2', region=region),
|
||||||
|
Site(name='Site 3', slug='site-3', region=region),
|
||||||
|
])
|
||||||
|
|
||||||
|
def test_site_list(self):
|
||||||
|
|
||||||
|
url = reverse('dcim:site_list')
|
||||||
|
params = {
|
||||||
|
"region": Region.objects.first().slug,
|
||||||
|
}
|
||||||
|
|
||||||
|
response = self.client.get('{}?{}'.format(url, urllib.parse.urlencode(params)))
|
||||||
|
self.assertEqual(response.status_code, 200)
|
||||||
|
|
||||||
|
def test_site(self):
|
||||||
|
|
||||||
|
site = Site.objects.first()
|
||||||
|
response = self.client.get(site.get_absolute_url())
|
||||||
|
self.assertEqual(response.status_code, 200)
|
||||||
|
|
||||||
|
|
||||||
|
class RackGroupTestCase(TestCase):
|
||||||
|
|
||||||
|
def setUp(self):
|
||||||
|
|
||||||
|
self.client = Client()
|
||||||
|
|
||||||
|
site = Site(name='Site 1', slug='site-1')
|
||||||
|
site.save()
|
||||||
|
|
||||||
|
RackGroup.objects.bulk_create([
|
||||||
|
RackGroup(name='Rack Group 1', slug='rack-group-1', site=site),
|
||||||
|
RackGroup(name='Rack Group 2', slug='rack-group-2', site=site),
|
||||||
|
RackGroup(name='Rack Group 3', slug='rack-group-3', site=site),
|
||||||
|
])
|
||||||
|
|
||||||
|
def test_rackgroup_list(self):
|
||||||
|
|
||||||
|
url = reverse('dcim:rackgroup_list')
|
||||||
|
|
||||||
|
response = self.client.get(url)
|
||||||
|
self.assertEqual(response.status_code, 200)
|
||||||
|
|
||||||
|
|
||||||
|
class RackTypeTestCase(TestCase):
|
||||||
|
|
||||||
|
def setUp(self):
|
||||||
|
|
||||||
|
self.client = Client()
|
||||||
|
|
||||||
|
RackRole.objects.bulk_create([
|
||||||
|
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'),
|
||||||
|
])
|
||||||
|
|
||||||
|
def test_rackrole_list(self):
|
||||||
|
|
||||||
|
url = reverse('dcim:rackrole_list')
|
||||||
|
|
||||||
|
response = self.client.get(url)
|
||||||
|
self.assertEqual(response.status_code, 200)
|
||||||
|
|
||||||
|
|
||||||
|
class RackReservationTestCase(TestCase):
|
||||||
|
|
||||||
|
def setUp(self):
|
||||||
|
|
||||||
|
self.client = Client()
|
||||||
|
|
||||||
|
User = get_user_model()
|
||||||
|
user = User(username='testuser', email='testuser@example.com')
|
||||||
|
user.save()
|
||||||
|
|
||||||
|
site = Site(name='Site 1', slug='site-1')
|
||||||
|
site.save()
|
||||||
|
|
||||||
|
rack = Rack(name='Rack 1', site=site)
|
||||||
|
rack.save()
|
||||||
|
|
||||||
|
RackReservation.objects.bulk_create([
|
||||||
|
RackReservation(rack=rack, user=user, units=[1, 2, 3], description='Reservation 1'),
|
||||||
|
RackReservation(rack=rack, user=user, units=[4, 5, 6], description='Reservation 2'),
|
||||||
|
RackReservation(rack=rack, user=user, units=[7, 8, 9], description='Reservation 3'),
|
||||||
|
])
|
||||||
|
|
||||||
|
def test_rackreservation_list(self):
|
||||||
|
|
||||||
|
url = reverse('dcim:rackreservation_list')
|
||||||
|
|
||||||
|
response = self.client.get(url)
|
||||||
|
self.assertEqual(response.status_code, 200)
|
||||||
|
|
||||||
|
|
||||||
|
class RackTestCase(TestCase):
|
||||||
|
|
||||||
|
def setUp(self):
|
||||||
|
|
||||||
|
self.client = Client()
|
||||||
|
|
||||||
|
site = Site(name='Site 1', slug='site-1')
|
||||||
|
site.save()
|
||||||
|
|
||||||
|
Rack.objects.bulk_create([
|
||||||
|
Rack(name='Rack 1', site=site),
|
||||||
|
Rack(name='Rack 2', site=site),
|
||||||
|
Rack(name='Rack 3', site=site),
|
||||||
|
])
|
||||||
|
|
||||||
|
def test_rack_list(self):
|
||||||
|
|
||||||
|
url = reverse('dcim:rack_list')
|
||||||
|
params = {
|
||||||
|
"site": Site.objects.first().slug,
|
||||||
|
}
|
||||||
|
|
||||||
|
response = self.client.get('{}?{}'.format(url, urllib.parse.urlencode(params)))
|
||||||
|
self.assertEqual(response.status_code, 200)
|
||||||
|
|
||||||
|
def test_rack(self):
|
||||||
|
|
||||||
|
rack = Rack.objects.first()
|
||||||
|
response = self.client.get(rack.get_absolute_url())
|
||||||
|
self.assertEqual(response.status_code, 200)
|
||||||
|
|
||||||
|
|
||||||
|
class ManufacturerTypeTestCase(TestCase):
|
||||||
|
|
||||||
|
def setUp(self):
|
||||||
|
|
||||||
|
self.client = Client()
|
||||||
|
|
||||||
|
Manufacturer.objects.bulk_create([
|
||||||
|
Manufacturer(name='Manufacturer 1', slug='manufacturer-1'),
|
||||||
|
Manufacturer(name='Manufacturer 2', slug='manufacturer-2'),
|
||||||
|
Manufacturer(name='Manufacturer 3', slug='manufacturer-3'),
|
||||||
|
])
|
||||||
|
|
||||||
|
def test_manufacturer_list(self):
|
||||||
|
|
||||||
|
url = reverse('dcim:manufacturer_list')
|
||||||
|
|
||||||
|
response = self.client.get(url)
|
||||||
|
self.assertEqual(response.status_code, 200)
|
||||||
|
|
||||||
|
|
||||||
|
class DeviceTypeTestCase(TestCase):
|
||||||
|
|
||||||
|
def setUp(self):
|
||||||
|
|
||||||
|
self.client = Client()
|
||||||
|
|
||||||
|
manufacturer = Manufacturer(name='Manufacturer 1', slug='manufacturer-1')
|
||||||
|
manufacturer.save()
|
||||||
|
|
||||||
|
DeviceType.objects.bulk_create([
|
||||||
|
DeviceType(model='Device Type 1', slug='device-type-1', manufacturer=manufacturer),
|
||||||
|
DeviceType(model='Device Type 2', slug='device-type-2', manufacturer=manufacturer),
|
||||||
|
DeviceType(model='Device Type 3', slug='device-type-3', manufacturer=manufacturer),
|
||||||
|
])
|
||||||
|
|
||||||
|
def test_devicetype_list(self):
|
||||||
|
|
||||||
|
url = reverse('dcim:devicetype_list')
|
||||||
|
params = {
|
||||||
|
"manufacturer": Manufacturer.objects.first().slug,
|
||||||
|
}
|
||||||
|
|
||||||
|
response = self.client.get('{}?{}'.format(url, urllib.parse.urlencode(params)))
|
||||||
|
self.assertEqual(response.status_code, 200)
|
||||||
|
|
||||||
|
def test_devicetype(self):
|
||||||
|
|
||||||
|
devicetype = DeviceType.objects.first()
|
||||||
|
response = self.client.get(devicetype.get_absolute_url())
|
||||||
|
self.assertEqual(response.status_code, 200)
|
||||||
|
|
||||||
|
|
||||||
|
class DeviceRoleTestCase(TestCase):
|
||||||
|
|
||||||
|
def setUp(self):
|
||||||
|
|
||||||
|
self.client = Client()
|
||||||
|
|
||||||
|
DeviceRole.objects.bulk_create([
|
||||||
|
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'),
|
||||||
|
])
|
||||||
|
|
||||||
|
def test_devicerole_list(self):
|
||||||
|
|
||||||
|
url = reverse('dcim:devicerole_list')
|
||||||
|
|
||||||
|
response = self.client.get(url)
|
||||||
|
self.assertEqual(response.status_code, 200)
|
||||||
|
|
||||||
|
|
||||||
|
class PlatformTestCase(TestCase):
|
||||||
|
|
||||||
|
def setUp(self):
|
||||||
|
|
||||||
|
self.client = Client()
|
||||||
|
|
||||||
|
Platform.objects.bulk_create([
|
||||||
|
Platform(name='Platform 1', slug='platform-1'),
|
||||||
|
Platform(name='Platform 2', slug='platform-2'),
|
||||||
|
Platform(name='Platform 3', slug='platform-3'),
|
||||||
|
])
|
||||||
|
|
||||||
|
def test_platform_list(self):
|
||||||
|
|
||||||
|
url = reverse('dcim:platform_list')
|
||||||
|
|
||||||
|
response = self.client.get(url)
|
||||||
|
self.assertEqual(response.status_code, 200)
|
||||||
|
|
||||||
|
|
||||||
|
class DeviceTestCase(TestCase):
|
||||||
|
|
||||||
|
def setUp(self):
|
||||||
|
|
||||||
|
self.client = Client()
|
||||||
|
|
||||||
|
site = Site(name='Site 1', slug='site-1')
|
||||||
|
site.save()
|
||||||
|
|
||||||
|
manufacturer = Manufacturer(name='Manufacturer 1', slug='manufacturer-1')
|
||||||
|
manufacturer.save()
|
||||||
|
|
||||||
|
devicetype = DeviceType(model='Device Type 1', manufacturer=manufacturer)
|
||||||
|
devicetype.save()
|
||||||
|
|
||||||
|
devicerole = DeviceRole(name='Device Role 1', slug='device-role-1')
|
||||||
|
devicerole.save()
|
||||||
|
|
||||||
|
Device.objects.bulk_create([
|
||||||
|
Device(name='Device 1', site=site, device_type=devicetype, device_role=devicerole),
|
||||||
|
Device(name='Device 2', site=site, device_type=devicetype, device_role=devicerole),
|
||||||
|
Device(name='Device 3', site=site, device_type=devicetype, device_role=devicerole),
|
||||||
|
])
|
||||||
|
|
||||||
|
def test_device_list(self):
|
||||||
|
|
||||||
|
url = reverse('dcim:device_list')
|
||||||
|
params = {
|
||||||
|
"device_type_id": DeviceType.objects.first().pk,
|
||||||
|
"role": DeviceRole.objects.first().slug,
|
||||||
|
}
|
||||||
|
|
||||||
|
response = self.client.get('{}?{}'.format(url, urllib.parse.urlencode(params)))
|
||||||
|
self.assertEqual(response.status_code, 200)
|
||||||
|
|
||||||
|
def test_device(self):
|
||||||
|
|
||||||
|
device = Device.objects.first()
|
||||||
|
response = self.client.get(device.get_absolute_url())
|
||||||
|
self.assertEqual(response.status_code, 200)
|
||||||
|
|
||||||
|
|
||||||
|
class InventoryItemTestCase(TestCase):
|
||||||
|
|
||||||
|
def setUp(self):
|
||||||
|
|
||||||
|
self.client = Client()
|
||||||
|
|
||||||
|
site = Site(name='Site 1', slug='site-1')
|
||||||
|
site.save()
|
||||||
|
|
||||||
|
manufacturer = Manufacturer(name='Manufacturer 1', slug='manufacturer-1')
|
||||||
|
manufacturer.save()
|
||||||
|
|
||||||
|
devicetype = DeviceType(model='Device Type 1', manufacturer=manufacturer)
|
||||||
|
devicetype.save()
|
||||||
|
|
||||||
|
devicerole = DeviceRole(name='Device Role 1', slug='device-role-1')
|
||||||
|
devicerole.save()
|
||||||
|
|
||||||
|
device = Device(name='Device 1', site=site, device_type=devicetype, device_role=devicerole)
|
||||||
|
device.save()
|
||||||
|
|
||||||
|
InventoryItem.objects.bulk_create([
|
||||||
|
InventoryItem(device=device, name='Inventory Item 1'),
|
||||||
|
InventoryItem(device=device, name='Inventory Item 2'),
|
||||||
|
InventoryItem(device=device, name='Inventory Item 3'),
|
||||||
|
])
|
||||||
|
|
||||||
|
def test_inventoryitem_list(self):
|
||||||
|
|
||||||
|
url = reverse('dcim:inventoryitem_list')
|
||||||
|
params = {
|
||||||
|
"device_id": Device.objects.first().pk,
|
||||||
|
}
|
||||||
|
|
||||||
|
response = self.client.get('{}?{}'.format(url, urllib.parse.urlencode(params)))
|
||||||
|
self.assertEqual(response.status_code, 200)
|
||||||
|
|
||||||
|
def test_inventoryitem(self):
|
||||||
|
|
||||||
|
inventoryitem = InventoryItem.objects.first()
|
||||||
|
response = self.client.get(inventoryitem.get_absolute_url())
|
||||||
|
self.assertEqual(response.status_code, 200)
|
||||||
|
|
||||||
|
|
||||||
|
class CableTestCase(TestCase):
|
||||||
|
|
||||||
|
def setUp(self):
|
||||||
|
|
||||||
|
self.client = Client()
|
||||||
|
|
||||||
|
site = Site(name='Site 1', slug='site-1')
|
||||||
|
site.save()
|
||||||
|
|
||||||
|
manufacturer = Manufacturer(name='Manufacturer 1', slug='manufacturer-1')
|
||||||
|
manufacturer.save()
|
||||||
|
|
||||||
|
devicetype = DeviceType(model='Device Type 1', manufacturer=manufacturer)
|
||||||
|
devicetype.save()
|
||||||
|
|
||||||
|
devicerole = DeviceRole(name='Device Role 1', slug='device-role-1')
|
||||||
|
devicerole.save()
|
||||||
|
|
||||||
|
device1 = Device(name='Device 1', site=site, device_type=devicetype, device_role=devicerole)
|
||||||
|
device1.save()
|
||||||
|
device2 = Device(name='Device 2', site=site, device_type=devicetype, device_role=devicerole)
|
||||||
|
device2.save()
|
||||||
|
|
||||||
|
iface1 = Interface(device=device1, name='Interface 1', form_factor=IFACE_FF_1GE_FIXED)
|
||||||
|
iface1.save()
|
||||||
|
iface2 = Interface(device=device1, name='Interface 2', form_factor=IFACE_FF_1GE_FIXED)
|
||||||
|
iface2.save()
|
||||||
|
iface3 = Interface(device=device1, name='Interface 3', form_factor=IFACE_FF_1GE_FIXED)
|
||||||
|
iface3.save()
|
||||||
|
iface4 = Interface(device=device2, name='Interface 1', form_factor=IFACE_FF_1GE_FIXED)
|
||||||
|
iface4.save()
|
||||||
|
iface5 = Interface(device=device2, name='Interface 2', form_factor=IFACE_FF_1GE_FIXED)
|
||||||
|
iface5.save()
|
||||||
|
iface6 = Interface(device=device2, name='Interface 3', form_factor=IFACE_FF_1GE_FIXED)
|
||||||
|
iface6.save()
|
||||||
|
|
||||||
|
Cable(termination_a=iface1, termination_b=iface4, type=CABLE_TYPE_CAT6).save()
|
||||||
|
Cable(termination_a=iface2, termination_b=iface5, type=CABLE_TYPE_CAT6).save()
|
||||||
|
Cable(termination_a=iface3, termination_b=iface6, type=CABLE_TYPE_CAT6).save()
|
||||||
|
|
||||||
|
def test_cable_list(self):
|
||||||
|
|
||||||
|
url = reverse('dcim:cable_list')
|
||||||
|
params = {
|
||||||
|
"type": CABLE_TYPE_CAT6,
|
||||||
|
}
|
||||||
|
|
||||||
|
response = self.client.get('{}?{}'.format(url, urllib.parse.urlencode(params)))
|
||||||
|
self.assertEqual(response.status_code, 200)
|
||||||
|
|
||||||
|
def test_cable(self):
|
||||||
|
|
||||||
|
cable = Cable.objects.first()
|
||||||
|
response = self.client.get(cable.get_absolute_url())
|
||||||
|
self.assertEqual(response.status_code, 200)
|
||||||
|
|
||||||
|
|
||||||
|
class VirtualMachineTestCase(TestCase):
|
||||||
|
|
||||||
|
def setUp(self):
|
||||||
|
|
||||||
|
self.client = Client()
|
||||||
|
|
||||||
|
site = Site.objects.create(name='Site 1', slug='site-1')
|
||||||
|
manufacturer = Manufacturer.objects.create(name='Manufacturer', slug='manufacturer-1')
|
||||||
|
device_type = DeviceType.objects.create(
|
||||||
|
manufacturer=manufacturer, model='Device Type 1', slug='device-type-1'
|
||||||
|
)
|
||||||
|
device_role = DeviceRole.objects.create(
|
||||||
|
name='Device Role', slug='device-role-1'
|
||||||
|
)
|
||||||
|
|
||||||
|
# Create 9 member Devices
|
||||||
|
device1 = Device.objects.create(
|
||||||
|
device_type=device_type, device_role=device_role, name='Device 1', site=site
|
||||||
|
)
|
||||||
|
device2 = Device.objects.create(
|
||||||
|
device_type=device_type, device_role=device_role, name='Device 2', site=site
|
||||||
|
)
|
||||||
|
device3 = Device.objects.create(
|
||||||
|
device_type=device_type, device_role=device_role, name='Device 3', site=site
|
||||||
|
)
|
||||||
|
device4 = Device.objects.create(
|
||||||
|
device_type=device_type, device_role=device_role, name='Device 4', site=site
|
||||||
|
)
|
||||||
|
device5 = Device.objects.create(
|
||||||
|
device_type=device_type, device_role=device_role, name='Device 5', site=site
|
||||||
|
)
|
||||||
|
device6 = Device.objects.create(
|
||||||
|
device_type=device_type, device_role=device_role, name='Device 6', site=site
|
||||||
|
)
|
||||||
|
|
||||||
|
# Create three VirtualChassis with two members each
|
||||||
|
vc1 = VirtualChassis.objects.create(master=device1, domain='test-domain-1')
|
||||||
|
Device.objects.filter(pk=device2.pk).update(virtual_chassis=vc1, vc_position=2)
|
||||||
|
vc2 = VirtualChassis.objects.create(master=device3, domain='test-domain-2')
|
||||||
|
Device.objects.filter(pk=device4.pk).update(virtual_chassis=vc2, vc_position=2)
|
||||||
|
vc3 = VirtualChassis.objects.create(master=device5, domain='test-domain-3')
|
||||||
|
Device.objects.filter(pk=device6.pk).update(virtual_chassis=vc3, vc_position=2)
|
||||||
|
|
||||||
|
def test_virtualchassis_list(self):
|
||||||
|
|
||||||
|
url = reverse('dcim:virtualchassis_list')
|
||||||
|
|
||||||
|
response = self.client.get(url)
|
||||||
|
self.assertEqual(response.status_code, 200)
|
||||||
|
|
||||||
|
def test_virtualchassis(self):
|
||||||
|
|
||||||
|
virtualchassis = VirtualChassis.objects.first()
|
||||||
|
response = self.client.get(virtualchassis.get_absolute_url())
|
||||||
|
self.assertEqual(response.status_code, 200)
|
@ -135,7 +135,13 @@ class BulkDisconnectView(GetReturnURLMixin, View):
|
|||||||
#
|
#
|
||||||
|
|
||||||
class RegionListView(ObjectListView):
|
class RegionListView(ObjectListView):
|
||||||
queryset = Region.objects.all()
|
queryset = Region.objects.add_related_count(
|
||||||
|
Region.objects.all(),
|
||||||
|
Site,
|
||||||
|
'region',
|
||||||
|
'site_count',
|
||||||
|
cumulative=True
|
||||||
|
)
|
||||||
filter = filters.RegionFilter
|
filter = filters.RegionFilter
|
||||||
filter_form = forms.RegionFilterForm
|
filter_form = forms.RegionFilterForm
|
||||||
table = tables.RegionTable
|
table = tables.RegionTable
|
||||||
|
@ -11,8 +11,8 @@ from taggit.models import Tag
|
|||||||
from dcim.models import DeviceRole, Platform, Region, Site
|
from dcim.models import DeviceRole, Platform, Region, Site
|
||||||
from tenancy.models import Tenant, TenantGroup
|
from tenancy.models import Tenant, TenantGroup
|
||||||
from utilities.forms import (
|
from utilities.forms import (
|
||||||
add_blank_choice, BootstrapMixin, BulkEditForm, BulkEditNullBooleanSelect, ContentTypeSelect, FilterChoiceField,
|
add_blank_choice, APISelectMultiple, BootstrapMixin, BulkEditForm, BulkEditNullBooleanSelect, ContentTypeSelect,
|
||||||
FilterTreeNodeMultipleChoiceField, LaxURLField, JSONField, SlugField,
|
FilterChoiceField, FilterTreeNodeMultipleChoiceField, LaxURLField, JSONField, SlugField,
|
||||||
)
|
)
|
||||||
from .constants import (
|
from .constants import (
|
||||||
CF_FILTER_DISABLED, CF_TYPE_BOOLEAN, CF_TYPE_DATE, CF_TYPE_INTEGER, CF_TYPE_SELECT, CF_TYPE_URL,
|
CF_FILTER_DISABLED, CF_TYPE_BOOLEAN, CF_TYPE_DATE, CF_TYPE_INTEGER, CF_TYPE_SELECT, CF_TYPE_URL,
|
||||||
@ -221,10 +221,6 @@ class TagFilterForm(BootstrapMixin, forms.Form):
|
|||||||
#
|
#
|
||||||
|
|
||||||
class ConfigContextForm(BootstrapMixin, forms.ModelForm):
|
class ConfigContextForm(BootstrapMixin, forms.ModelForm):
|
||||||
regions = TreeNodeMultipleChoiceField(
|
|
||||||
queryset=Region.objects.all(),
|
|
||||||
required=False
|
|
||||||
)
|
|
||||||
data = JSONField()
|
data = JSONField()
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
@ -233,6 +229,26 @@ class ConfigContextForm(BootstrapMixin, forms.ModelForm):
|
|||||||
'name', 'weight', 'description', 'is_active', 'regions', 'sites', 'roles', 'platforms', 'tenant_groups',
|
'name', 'weight', 'description', 'is_active', 'regions', 'sites', 'roles', 'platforms', 'tenant_groups',
|
||||||
'tenants', 'data',
|
'tenants', 'data',
|
||||||
]
|
]
|
||||||
|
widgets = {
|
||||||
|
'regions': APISelectMultiple(
|
||||||
|
api_url="/api/dcim/regions/"
|
||||||
|
),
|
||||||
|
'sites': APISelectMultiple(
|
||||||
|
api_url="/api/dcim/sites/"
|
||||||
|
),
|
||||||
|
'roles': APISelectMultiple(
|
||||||
|
api_url="/api/dcim/device-roles/"
|
||||||
|
),
|
||||||
|
'platforms': APISelectMultiple(
|
||||||
|
api_url="/api/dcim/platforms/"
|
||||||
|
),
|
||||||
|
'tenant_groups': APISelectMultiple(
|
||||||
|
api_url="/api/tenancy/tenant-groups/"
|
||||||
|
),
|
||||||
|
'tenants': APISelectMultiple(
|
||||||
|
api_url="/api/tenancy/tenants/"
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
class ConfigContextBulkEditForm(BootstrapMixin, BulkEditForm):
|
class ConfigContextBulkEditForm(BootstrapMixin, BulkEditForm):
|
||||||
@ -264,29 +280,53 @@ class ConfigContextFilterForm(BootstrapMixin, forms.Form):
|
|||||||
required=False,
|
required=False,
|
||||||
label='Search'
|
label='Search'
|
||||||
)
|
)
|
||||||
region = FilterTreeNodeMultipleChoiceField(
|
region = FilterChoiceField(
|
||||||
queryset=Region.objects.all(),
|
queryset=Region.objects.all(),
|
||||||
to_field_name='slug'
|
to_field_name='slug',
|
||||||
|
widget=APISelectMultiple(
|
||||||
|
api_url="/api/dcim/regions/",
|
||||||
|
value_field="slug",
|
||||||
|
)
|
||||||
)
|
)
|
||||||
site = FilterChoiceField(
|
site = FilterChoiceField(
|
||||||
queryset=Site.objects.all(),
|
queryset=Site.objects.all(),
|
||||||
to_field_name='slug'
|
to_field_name='slug',
|
||||||
|
widget=APISelectMultiple(
|
||||||
|
api_url="/api/dcim/sites/",
|
||||||
|
value_field="slug",
|
||||||
|
)
|
||||||
)
|
)
|
||||||
role = FilterChoiceField(
|
role = FilterChoiceField(
|
||||||
queryset=DeviceRole.objects.all(),
|
queryset=DeviceRole.objects.all(),
|
||||||
to_field_name='slug'
|
to_field_name='slug',
|
||||||
|
widget=APISelectMultiple(
|
||||||
|
api_url="/api/dcim/device-roles/",
|
||||||
|
value_field="slug",
|
||||||
|
)
|
||||||
)
|
)
|
||||||
platform = FilterChoiceField(
|
platform = FilterChoiceField(
|
||||||
queryset=Platform.objects.all(),
|
queryset=Platform.objects.all(),
|
||||||
to_field_name='slug'
|
to_field_name='slug',
|
||||||
|
widget=APISelectMultiple(
|
||||||
|
api_url="/api/dcim/platforms/",
|
||||||
|
value_field="slug",
|
||||||
|
)
|
||||||
)
|
)
|
||||||
tenant_group = FilterChoiceField(
|
tenant_group = FilterChoiceField(
|
||||||
queryset=TenantGroup.objects.all(),
|
queryset=TenantGroup.objects.all(),
|
||||||
to_field_name='slug'
|
to_field_name='slug',
|
||||||
|
widget=APISelectMultiple(
|
||||||
|
api_url="/api/tenancy/tenant-groups/",
|
||||||
|
value_field="slug",
|
||||||
|
)
|
||||||
)
|
)
|
||||||
tenant = FilterChoiceField(
|
tenant = FilterChoiceField(
|
||||||
queryset=Tenant.objects.all(),
|
queryset=Tenant.objects.all(),
|
||||||
to_field_name='slug'
|
to_field_name='slug',
|
||||||
|
widget=APISelectMultiple(
|
||||||
|
api_url="/api/tenancy/tenants/",
|
||||||
|
value_field="slug",
|
||||||
|
)
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
105
netbox/extras/tests/test_views.py
Normal file
105
netbox/extras/tests/test_views.py
Normal file
@ -0,0 +1,105 @@
|
|||||||
|
import urllib.parse
|
||||||
|
import uuid
|
||||||
|
|
||||||
|
from django.contrib.auth.models import User
|
||||||
|
from django.test import Client, TestCase
|
||||||
|
from django.urls import reverse
|
||||||
|
from taggit.models import Tag
|
||||||
|
|
||||||
|
from dcim.models import Site
|
||||||
|
from extras.models import ConfigContext, ObjectChange
|
||||||
|
|
||||||
|
|
||||||
|
class TagTestCase(TestCase):
|
||||||
|
|
||||||
|
def setUp(self):
|
||||||
|
|
||||||
|
self.client = Client()
|
||||||
|
|
||||||
|
Tag.objects.bulk_create([
|
||||||
|
Tag(name='Tag 1', slug='tag-1'),
|
||||||
|
Tag(name='Tag 2', slug='tag-2'),
|
||||||
|
Tag(name='Tag 3', slug='tag-3'),
|
||||||
|
])
|
||||||
|
|
||||||
|
def test_tag_list(self):
|
||||||
|
|
||||||
|
url = reverse('extras:tag_list')
|
||||||
|
params = {
|
||||||
|
"q": "tag",
|
||||||
|
}
|
||||||
|
|
||||||
|
response = self.client.get('{}?{}'.format(url, urllib.parse.urlencode(params)))
|
||||||
|
self.assertEqual(response.status_code, 200)
|
||||||
|
|
||||||
|
|
||||||
|
class ConfigContextTestCase(TestCase):
|
||||||
|
|
||||||
|
def setUp(self):
|
||||||
|
|
||||||
|
self.client = Client()
|
||||||
|
|
||||||
|
site = Site(name='Site 1', slug='site-1')
|
||||||
|
site.save()
|
||||||
|
|
||||||
|
# Create three ConfigContexts
|
||||||
|
for i in range(1, 4):
|
||||||
|
configcontext = ConfigContext(
|
||||||
|
name='Config Context {}'.format(i),
|
||||||
|
data='{{"foo": {}}}'.format(i)
|
||||||
|
)
|
||||||
|
configcontext.save()
|
||||||
|
configcontext.sites.add(site)
|
||||||
|
|
||||||
|
def test_configcontext_list(self):
|
||||||
|
|
||||||
|
url = reverse('extras:configcontext_list')
|
||||||
|
params = {
|
||||||
|
"q": "foo",
|
||||||
|
}
|
||||||
|
|
||||||
|
response = self.client.get('{}?{}'.format(url, urllib.parse.urlencode(params)))
|
||||||
|
self.assertEqual(response.status_code, 200)
|
||||||
|
|
||||||
|
def test_configcontext(self):
|
||||||
|
|
||||||
|
configcontext = ConfigContext.objects.first()
|
||||||
|
response = self.client.get(configcontext.get_absolute_url())
|
||||||
|
self.assertEqual(response.status_code, 200)
|
||||||
|
|
||||||
|
|
||||||
|
class ObjectChangeTestCase(TestCase):
|
||||||
|
|
||||||
|
def setUp(self):
|
||||||
|
|
||||||
|
self.client = Client()
|
||||||
|
|
||||||
|
user = User(username='testuser', email='testuser@example.com')
|
||||||
|
user.save()
|
||||||
|
|
||||||
|
site = Site(name='Site 1', slug='site-1')
|
||||||
|
site.save()
|
||||||
|
|
||||||
|
# Create three ObjectChanges
|
||||||
|
for i in range(1, 4):
|
||||||
|
site.log_change(
|
||||||
|
user=user,
|
||||||
|
request_id=uuid.uuid4(),
|
||||||
|
action=2
|
||||||
|
)
|
||||||
|
|
||||||
|
def test_objectchange_list(self):
|
||||||
|
|
||||||
|
url = reverse('extras:objectchange_list')
|
||||||
|
params = {
|
||||||
|
"user": User.objects.first(),
|
||||||
|
}
|
||||||
|
|
||||||
|
response = self.client.get('{}?{}'.format(url, urllib.parse.urlencode(params)))
|
||||||
|
self.assertEqual(response.status_code, 200)
|
||||||
|
|
||||||
|
def test_objectchange(self):
|
||||||
|
|
||||||
|
objectchange = ObjectChange.objects.first()
|
||||||
|
response = self.client.get(objectchange.get_absolute_url())
|
||||||
|
self.assertEqual(response.status_code, 200)
|
282
netbox/ipam/tests/test_views.py
Normal file
282
netbox/ipam/tests/test_views.py
Normal file
@ -0,0 +1,282 @@
|
|||||||
|
from netaddr import IPNetwork
|
||||||
|
import urllib.parse
|
||||||
|
|
||||||
|
from django.test import Client, TestCase
|
||||||
|
from django.urls import reverse
|
||||||
|
|
||||||
|
from dcim.models import Device, DeviceRole, DeviceType, Manufacturer, Site
|
||||||
|
from ipam.constants import IP_PROTOCOL_TCP
|
||||||
|
from ipam.models import Aggregate, IPAddress, Prefix, RIR, Role, Service, VLAN, VLANGroup, VRF
|
||||||
|
|
||||||
|
|
||||||
|
class VRFTestCase(TestCase):
|
||||||
|
|
||||||
|
def setUp(self):
|
||||||
|
|
||||||
|
self.client = Client()
|
||||||
|
|
||||||
|
VRF.objects.bulk_create([
|
||||||
|
VRF(name='VRF 1', rd='65000:1'),
|
||||||
|
VRF(name='VRF 2', rd='65000:2'),
|
||||||
|
VRF(name='VRF 3', rd='65000:3'),
|
||||||
|
])
|
||||||
|
|
||||||
|
def test_vrf_list(self):
|
||||||
|
|
||||||
|
url = reverse('ipam:vrf_list')
|
||||||
|
params = {
|
||||||
|
"q": "65000",
|
||||||
|
}
|
||||||
|
|
||||||
|
response = self.client.get('{}?{}'.format(url, urllib.parse.urlencode(params)))
|
||||||
|
self.assertEqual(response.status_code, 200)
|
||||||
|
|
||||||
|
def test_configcontext(self):
|
||||||
|
|
||||||
|
vrf = VRF.objects.first()
|
||||||
|
response = self.client.get(vrf.get_absolute_url())
|
||||||
|
self.assertEqual(response.status_code, 200)
|
||||||
|
|
||||||
|
|
||||||
|
class RIRTestCase(TestCase):
|
||||||
|
|
||||||
|
def setUp(self):
|
||||||
|
|
||||||
|
self.client = Client()
|
||||||
|
|
||||||
|
RIR.objects.bulk_create([
|
||||||
|
RIR(name='RIR 1', slug='rir-1'),
|
||||||
|
RIR(name='RIR 2', slug='rir-2'),
|
||||||
|
RIR(name='RIR 3', slug='rir-3'),
|
||||||
|
])
|
||||||
|
|
||||||
|
def test_rir_list(self):
|
||||||
|
|
||||||
|
url = reverse('ipam:rir_list')
|
||||||
|
|
||||||
|
response = self.client.get(url)
|
||||||
|
self.assertEqual(response.status_code, 200)
|
||||||
|
|
||||||
|
def test_rir(self):
|
||||||
|
|
||||||
|
rir = RIR.objects.first()
|
||||||
|
response = self.client.get(rir.get_absolute_url())
|
||||||
|
self.assertEqual(response.status_code, 200)
|
||||||
|
|
||||||
|
|
||||||
|
class AggregateTestCase(TestCase):
|
||||||
|
|
||||||
|
def setUp(self):
|
||||||
|
|
||||||
|
self.client = Client()
|
||||||
|
|
||||||
|
rir = RIR(name='RIR 1', slug='rir-1')
|
||||||
|
rir.save()
|
||||||
|
|
||||||
|
Aggregate.objects.bulk_create([
|
||||||
|
Aggregate(family=4, prefix=IPNetwork('10.1.0.0/16'), rir=rir),
|
||||||
|
Aggregate(family=4, prefix=IPNetwork('10.2.0.0/16'), rir=rir),
|
||||||
|
Aggregate(family=4, prefix=IPNetwork('10.3.0.0/16'), rir=rir),
|
||||||
|
])
|
||||||
|
|
||||||
|
def test_aggregate_list(self):
|
||||||
|
|
||||||
|
url = reverse('ipam:aggregate_list')
|
||||||
|
params = {
|
||||||
|
"rir": RIR.objects.first().slug,
|
||||||
|
}
|
||||||
|
|
||||||
|
response = self.client.get('{}?{}'.format(url, urllib.parse.urlencode(params)))
|
||||||
|
self.assertEqual(response.status_code, 200)
|
||||||
|
|
||||||
|
def test_aggregate(self):
|
||||||
|
|
||||||
|
aggregate = Aggregate.objects.first()
|
||||||
|
response = self.client.get(aggregate.get_absolute_url())
|
||||||
|
self.assertEqual(response.status_code, 200)
|
||||||
|
|
||||||
|
|
||||||
|
class RoleTestCase(TestCase):
|
||||||
|
|
||||||
|
def setUp(self):
|
||||||
|
|
||||||
|
self.client = Client()
|
||||||
|
|
||||||
|
Role.objects.bulk_create([
|
||||||
|
Role(name='Role 1', slug='role-1'),
|
||||||
|
Role(name='Role 2', slug='role-2'),
|
||||||
|
Role(name='Role 3', slug='role-3'),
|
||||||
|
])
|
||||||
|
|
||||||
|
def test_role_list(self):
|
||||||
|
|
||||||
|
url = reverse('ipam:role_list')
|
||||||
|
|
||||||
|
response = self.client.get(url)
|
||||||
|
self.assertEqual(response.status_code, 200)
|
||||||
|
|
||||||
|
|
||||||
|
class PrefixTestCase(TestCase):
|
||||||
|
|
||||||
|
def setUp(self):
|
||||||
|
|
||||||
|
self.client = Client()
|
||||||
|
|
||||||
|
site = Site(name='Site 1', slug='site-1')
|
||||||
|
site.save()
|
||||||
|
|
||||||
|
Prefix.objects.bulk_create([
|
||||||
|
Prefix(family=4, prefix=IPNetwork('10.1.0.0/16'), site=site),
|
||||||
|
Prefix(family=4, prefix=IPNetwork('10.2.0.0/16'), site=site),
|
||||||
|
Prefix(family=4, prefix=IPNetwork('10.3.0.0/16'), site=site),
|
||||||
|
])
|
||||||
|
|
||||||
|
def test_prefix_list(self):
|
||||||
|
|
||||||
|
url = reverse('ipam:prefix_list')
|
||||||
|
params = {
|
||||||
|
"site": Site.objects.first().slug,
|
||||||
|
}
|
||||||
|
|
||||||
|
response = self.client.get('{}?{}'.format(url, urllib.parse.urlencode(params)))
|
||||||
|
self.assertEqual(response.status_code, 200)
|
||||||
|
|
||||||
|
def test_prefix(self):
|
||||||
|
|
||||||
|
prefix = Prefix.objects.first()
|
||||||
|
response = self.client.get(prefix.get_absolute_url())
|
||||||
|
self.assertEqual(response.status_code, 200)
|
||||||
|
|
||||||
|
|
||||||
|
class IPAddressTestCase(TestCase):
|
||||||
|
|
||||||
|
def setUp(self):
|
||||||
|
|
||||||
|
self.client = Client()
|
||||||
|
|
||||||
|
vrf = VRF(name='VRF 1', rd='65000:1')
|
||||||
|
vrf.save()
|
||||||
|
|
||||||
|
IPAddress.objects.bulk_create([
|
||||||
|
IPAddress(family=4, address=IPNetwork('10.1.0.0/16'), vrf=vrf),
|
||||||
|
IPAddress(family=4, address=IPNetwork('10.2.0.0/16'), vrf=vrf),
|
||||||
|
IPAddress(family=4, address=IPNetwork('10.3.0.0/16'), vrf=vrf),
|
||||||
|
])
|
||||||
|
|
||||||
|
def test_ipaddress_list(self):
|
||||||
|
|
||||||
|
url = reverse('ipam:ipaddress_list')
|
||||||
|
params = {
|
||||||
|
"vrf": VRF.objects.first().rd,
|
||||||
|
}
|
||||||
|
|
||||||
|
response = self.client.get('{}?{}'.format(url, urllib.parse.urlencode(params)))
|
||||||
|
self.assertEqual(response.status_code, 200)
|
||||||
|
|
||||||
|
def test_ipaddress(self):
|
||||||
|
|
||||||
|
ipaddress = IPAddress.objects.first()
|
||||||
|
response = self.client.get(ipaddress.get_absolute_url())
|
||||||
|
self.assertEqual(response.status_code, 200)
|
||||||
|
|
||||||
|
|
||||||
|
class VLANGroupTestCase(TestCase):
|
||||||
|
|
||||||
|
def setUp(self):
|
||||||
|
|
||||||
|
self.client = Client()
|
||||||
|
|
||||||
|
site = Site(name='Site 1', slug='site-1')
|
||||||
|
site.save()
|
||||||
|
|
||||||
|
VLANGroup.objects.bulk_create([
|
||||||
|
VLANGroup(name='VLAN Group 1', slug='vlan-group-1', site=site),
|
||||||
|
VLANGroup(name='VLAN Group 2', slug='vlan-group-2', site=site),
|
||||||
|
VLANGroup(name='VLAN Group 3', slug='vlan-group-3', site=site),
|
||||||
|
])
|
||||||
|
|
||||||
|
def test_vlangroup_list(self):
|
||||||
|
|
||||||
|
url = reverse('ipam:vlangroup_list')
|
||||||
|
params = {
|
||||||
|
"site": Site.objects.first().slug,
|
||||||
|
}
|
||||||
|
|
||||||
|
response = self.client.get('{}?{}'.format(url, urllib.parse.urlencode(params)))
|
||||||
|
self.assertEqual(response.status_code, 200)
|
||||||
|
|
||||||
|
|
||||||
|
class VLANTestCase(TestCase):
|
||||||
|
|
||||||
|
def setUp(self):
|
||||||
|
|
||||||
|
self.client = Client()
|
||||||
|
|
||||||
|
vlangroup = VLANGroup(name='VLAN Group 1', slug='vlan-group-1')
|
||||||
|
vlangroup.save()
|
||||||
|
|
||||||
|
VLAN.objects.bulk_create([
|
||||||
|
VLAN(group=vlangroup, vid=101, name='VLAN101'),
|
||||||
|
VLAN(group=vlangroup, vid=102, name='VLAN102'),
|
||||||
|
VLAN(group=vlangroup, vid=103, name='VLAN103'),
|
||||||
|
])
|
||||||
|
|
||||||
|
def test_vlan_list(self):
|
||||||
|
|
||||||
|
url = reverse('ipam:vlan_list')
|
||||||
|
params = {
|
||||||
|
"group": VLANGroup.objects.first().slug,
|
||||||
|
}
|
||||||
|
|
||||||
|
response = self.client.get('{}?{}'.format(url, urllib.parse.urlencode(params)))
|
||||||
|
self.assertEqual(response.status_code, 200)
|
||||||
|
|
||||||
|
def test_vlan(self):
|
||||||
|
|
||||||
|
vlan = VLAN.objects.first()
|
||||||
|
response = self.client.get(vlan.get_absolute_url())
|
||||||
|
self.assertEqual(response.status_code, 200)
|
||||||
|
|
||||||
|
|
||||||
|
class ServiceTestCase(TestCase):
|
||||||
|
|
||||||
|
def setUp(self):
|
||||||
|
|
||||||
|
self.client = Client()
|
||||||
|
|
||||||
|
site = Site(name='Site 1', slug='site-1')
|
||||||
|
site.save()
|
||||||
|
|
||||||
|
manufacturer = Manufacturer(name='Manufacturer 1', slug='manufacturer-1')
|
||||||
|
manufacturer.save()
|
||||||
|
|
||||||
|
devicetype = DeviceType(manufacturer=manufacturer, model='Device Type 1')
|
||||||
|
devicetype.save()
|
||||||
|
|
||||||
|
devicerole = DeviceRole(name='Device Role 1', slug='device-role-1')
|
||||||
|
devicerole.save()
|
||||||
|
|
||||||
|
device = Device(name='Device 1', site=site, device_type=devicetype, device_role=devicerole)
|
||||||
|
device.save()
|
||||||
|
|
||||||
|
Service.objects.bulk_create([
|
||||||
|
Service(device=device, name='Service 1', protocol=IP_PROTOCOL_TCP, port=101),
|
||||||
|
Service(device=device, name='Service 2', protocol=IP_PROTOCOL_TCP, port=102),
|
||||||
|
Service(device=device, name='Service 3', protocol=IP_PROTOCOL_TCP, port=103),
|
||||||
|
])
|
||||||
|
|
||||||
|
def test_service_list(self):
|
||||||
|
|
||||||
|
url = reverse('ipam:service_list')
|
||||||
|
params = {
|
||||||
|
"device_id": Device.objects.first(),
|
||||||
|
}
|
||||||
|
|
||||||
|
response = self.client.get('{}?{}'.format(url, urllib.parse.urlencode(params)))
|
||||||
|
self.assertEqual(response.status_code, 200)
|
||||||
|
|
||||||
|
def test_service(self):
|
||||||
|
|
||||||
|
service = Service.objects.first()
|
||||||
|
response = self.client.get(service.get_absolute_url())
|
||||||
|
self.assertEqual(response.status_code, 200)
|
@ -22,7 +22,7 @@ except ImportError:
|
|||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
VERSION = '2.5.6'
|
VERSION = '2.5.7'
|
||||||
|
|
||||||
BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
|
BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
|
||||||
|
|
||||||
|
7
netbox/project-static/clipboard-2.0.4.min.js
vendored
Executable file
7
netbox/project-static/clipboard-2.0.4.min.js
vendored
Executable file
File diff suppressed because one or more lines are too long
@ -1,4 +1,6 @@
|
|||||||
$(document).ready(function() {
|
$(document).ready(function() {
|
||||||
|
// Instantiate ClipboardJS on all copy buttons
|
||||||
|
new ClipboardJS('button.copy-secret');
|
||||||
|
|
||||||
// Unlocking a secret
|
// Unlocking a secret
|
||||||
$('button.unlock-secret').click(function(event) {
|
$('button.unlock-secret').click(function(event) {
|
||||||
@ -45,6 +47,7 @@ $(document).ready(function() {
|
|||||||
console.log("Secret retrieved successfully");
|
console.log("Secret retrieved successfully");
|
||||||
$('#secret_' + secret_id).text(response.plaintext);
|
$('#secret_' + secret_id).text(response.plaintext);
|
||||||
$('button.unlock-secret[secret-id=' + secret_id + ']').hide();
|
$('button.unlock-secret[secret-id=' + secret_id + ']').hide();
|
||||||
|
$('button.copy-secret[secret-id=' + secret_id + ']').show();
|
||||||
$('button.lock-secret[secret-id=' + secret_id + ']').show();
|
$('button.lock-secret[secret-id=' + secret_id + ']').show();
|
||||||
} else {
|
} else {
|
||||||
console.log("Secret was not decrypted. Prompt user for private key.");
|
console.log("Secret was not decrypted. Prompt user for private key.");
|
||||||
@ -67,6 +70,7 @@ $(document).ready(function() {
|
|||||||
var secret_div = $('#secret_' + secret_id);
|
var secret_div = $('#secret_' + secret_id);
|
||||||
secret_div.html('********');
|
secret_div.html('********');
|
||||||
$('button.lock-secret[secret-id=' + secret_id + ']').hide();
|
$('button.lock-secret[secret-id=' + secret_id + ']').hide();
|
||||||
|
$('button.copy-secret[secret-id=' + secret_id + ']').hide();
|
||||||
$('button.unlock-secret[secret-id=' + secret_id + ']').show();
|
$('button.unlock-secret[secret-id=' + secret_id + ']').show();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
82
netbox/secrets/tests/test_views.py
Normal file
82
netbox/secrets/tests/test_views.py
Normal file
@ -0,0 +1,82 @@
|
|||||||
|
import urllib.parse
|
||||||
|
|
||||||
|
from django.contrib.auth import get_user_model
|
||||||
|
from django.test import Client, TestCase
|
||||||
|
from django.urls import reverse
|
||||||
|
|
||||||
|
from dcim.models import Device, DeviceRole, DeviceType, Manufacturer, Site
|
||||||
|
from secrets.models import Secret, SecretRole
|
||||||
|
|
||||||
|
|
||||||
|
class SecretRoleTestCase(TestCase):
|
||||||
|
|
||||||
|
def setUp(self):
|
||||||
|
|
||||||
|
TEST_USERNAME = 'testuser'
|
||||||
|
TEST_PASSWORD = 'testpassword'
|
||||||
|
|
||||||
|
User = get_user_model()
|
||||||
|
User.objects.create(username=TEST_USERNAME, email='testuser@example.com', password=TEST_PASSWORD)
|
||||||
|
|
||||||
|
self.client = Client()
|
||||||
|
self.client.login(username=TEST_USERNAME, password=TEST_PASSWORD)
|
||||||
|
|
||||||
|
SecretRole.objects.bulk_create([
|
||||||
|
SecretRole(name='Secret Role 1', slug='secret-role-1'),
|
||||||
|
SecretRole(name='Secret Role 2', slug='secret-role-2'),
|
||||||
|
SecretRole(name='Secret Role 3', slug='secret-role-3'),
|
||||||
|
])
|
||||||
|
|
||||||
|
def test_secretrole_list(self):
|
||||||
|
|
||||||
|
url = reverse('secrets:secret_list')
|
||||||
|
|
||||||
|
response = self.client.get(url, follow=True)
|
||||||
|
self.assertEqual(response.status_code, 200)
|
||||||
|
|
||||||
|
|
||||||
|
class SecretTestCase(TestCase):
|
||||||
|
|
||||||
|
def setUp(self):
|
||||||
|
|
||||||
|
self.client = Client()
|
||||||
|
|
||||||
|
site = Site(name='Site 1', slug='site-1')
|
||||||
|
site.save()
|
||||||
|
|
||||||
|
manufacturer = Manufacturer(name='Manufacturer 1', slug='manufacturer-1')
|
||||||
|
manufacturer.save()
|
||||||
|
|
||||||
|
devicetype = DeviceType(manufacturer=manufacturer, model='Device Type 1')
|
||||||
|
devicetype.save()
|
||||||
|
|
||||||
|
devicerole = DeviceRole(name='Device Role 1', slug='device-role-1')
|
||||||
|
devicerole.save()
|
||||||
|
|
||||||
|
device = Device(name='Device 1', site=site, device_type=devicetype, device_role=devicerole)
|
||||||
|
device.save()
|
||||||
|
|
||||||
|
secretrole = SecretRole(name='Secret Role 1', slug='secret-role-1')
|
||||||
|
secretrole.save()
|
||||||
|
|
||||||
|
Secret.objects.bulk_create([
|
||||||
|
Secret(device=device, role=secretrole, name='Secret 1', ciphertext=b'1234567890'),
|
||||||
|
Secret(device=device, role=secretrole, name='Secret 2', ciphertext=b'1234567890'),
|
||||||
|
Secret(device=device, role=secretrole, name='Secret 3', ciphertext=b'1234567890'),
|
||||||
|
])
|
||||||
|
|
||||||
|
def test_secret_list(self):
|
||||||
|
|
||||||
|
url = reverse('secrets:secret_list')
|
||||||
|
params = {
|
||||||
|
"role": SecretRole.objects.first().slug,
|
||||||
|
}
|
||||||
|
|
||||||
|
response = self.client.get('{}?{}'.format(url, urllib.parse.urlencode(params)), follow=True)
|
||||||
|
self.assertEqual(response.status_code, 200)
|
||||||
|
|
||||||
|
def test_configcontext(self):
|
||||||
|
|
||||||
|
secret = Secret.objects.first()
|
||||||
|
response = self.client.get(secret.get_absolute_url(), follow=True)
|
||||||
|
self.assertEqual(response.status_code, 200)
|
@ -69,6 +69,7 @@
|
|||||||
<script src="{% static 'jquery-ui-1.12.1/jquery-ui.min.js' %}"></script>
|
<script src="{% static 'jquery-ui-1.12.1/jquery-ui.min.js' %}"></script>
|
||||||
<script src="{% static 'bootstrap-3.3.7-dist/js/bootstrap.min.js' %}"></script>
|
<script src="{% static 'bootstrap-3.3.7-dist/js/bootstrap.min.js' %}"></script>
|
||||||
<script src="{% static 'select2-4.0.5/js/select2.min.js' %}"></script>
|
<script src="{% static 'select2-4.0.5/js/select2.min.js' %}"></script>
|
||||||
|
<script src="{% static 'clipboard-2.0.4.min.js' %}"></script>
|
||||||
<script src="{% static 'js/forms.js' %}?v{{ settings.VERSION }}"></script>
|
<script src="{% static 'js/forms.js' %}?v{{ settings.VERSION }}"></script>
|
||||||
<script type="text/javascript">
|
<script type="text/javascript">
|
||||||
var netbox_api_path = "/{{ settings.BASE_PATH }}api/";
|
var netbox_api_path = "/{{ settings.BASE_PATH }}api/";
|
||||||
|
@ -85,11 +85,11 @@
|
|||||||
</tr>
|
</tr>
|
||||||
<tr>
|
<tr>
|
||||||
<td>NOC Contact</td>
|
<td>NOC Contact</td>
|
||||||
<td>{{ provider.noc_contact|linebreaksbr|placeholder }}</td>
|
<td class="rendered-markdown">{{ provider.noc_contact|gfm|placeholder }}</td>
|
||||||
</tr>
|
</tr>
|
||||||
<tr>
|
<tr>
|
||||||
<td>Admin Contact</td>
|
<td>Admin Contact</td>
|
||||||
<td>{{ provider.admin_contact|linebreaksbr|placeholder }}</td>
|
<td class="rendered-markdown">{{ provider.admin_contact|gfm|placeholder }}</td>
|
||||||
</tr>
|
</tr>
|
||||||
<tr>
|
<tr>
|
||||||
<td>Circuits</td>
|
<td>Circuits</td>
|
||||||
|
@ -96,7 +96,7 @@
|
|||||||
{{ peer_termination.connected_endpoint.device }}
|
{{ peer_termination.connected_endpoint.device }}
|
||||||
</a><br/>
|
</a><br/>
|
||||||
<small>via <i class="fa fa-fw fa-globe" title="Circuit"></i>
|
<small>via <i class="fa fa-fw fa-globe" title="Circuit"></i>
|
||||||
<a href="{{ iface.connected_endpoint.circuit.get_absolure_url }}">
|
<a href="{{ iface.connected_endpoint.circuit.get_absolute_url }}">
|
||||||
{{ iface.connected_endpoint.circuit.provider }}
|
{{ iface.connected_endpoint.circuit.provider }}
|
||||||
{{ iface.connected_endpoint.circuit }}
|
{{ iface.connected_endpoint.circuit }}
|
||||||
</a>
|
</a>
|
||||||
@ -150,7 +150,7 @@
|
|||||||
{% if perms.dcim.change_interface %}
|
{% if perms.dcim.change_interface %}
|
||||||
{% if iface.cable %}
|
{% if iface.cable %}
|
||||||
{% include 'dcim/inc/cable_toggle_buttons.html' with cable=iface.cable %}
|
{% include 'dcim/inc/cable_toggle_buttons.html' with cable=iface.cable %}
|
||||||
{% elif not iface.is_virtual and perms.dcim.add_cable %}
|
{% elif iface.is_connectable and perms.dcim.add_cable %}
|
||||||
<a href="{% url 'dcim:interface_connect' termination_a_id=iface.pk %}?return_url={{ device.get_absolute_url }}" class="btn btn-success btn-xs" title="Connect">
|
<a href="{% url 'dcim:interface_connect' termination_a_id=iface.pk %}?return_url={{ device.get_absolute_url }}" class="btn btn-success btn-xs" title="Connect">
|
||||||
<i class="glyphicon glyphicon-resize-small" aria-hidden="true"></i>
|
<i class="glyphicon glyphicon-resize-small" aria-hidden="true"></i>
|
||||||
</a>
|
</a>
|
||||||
|
@ -25,7 +25,7 @@
|
|||||||
{% if u.device %}
|
{% if u.device %}
|
||||||
<li class="occupied h{{ u.device.device_type.u_height }}u"{% ifequal u.device.face face_id %} style="background-color: #{{ u.device.device_role.color }}"{% endifequal %}>
|
<li class="occupied h{{ u.device.device_type.u_height }}u"{% ifequal u.device.face face_id %} style="background-color: #{{ u.device.device_role.color }}"{% endifequal %}>
|
||||||
{% ifequal u.device.face face_id %}
|
{% ifequal u.device.face face_id %}
|
||||||
<a href="{% url 'dcim:device' pk=u.device.pk %}" data-toggle="popover" data-trigger="hover" data-container="body" data-html="true"
|
<a href="{% url 'dcim:device' pk=u.device.pk %}" style="color: {{ u.device.device_role.color|fgcolor }}" data-toggle="popover" data-trigger="hover" data-container="body" data-html="true"
|
||||||
data-content="{{ u.device.device_role }}<br />{{ u.device.device_type.display_name }} ({{ u.device.device_type.u_height }}U){% if u.device.asset_tag %}<br />{{ u.device.asset_tag }}{% endif %}{% if u.device.serial %}<br />{{ u.device.serial }}{% endif %}">
|
data-content="{{ u.device.device_role }}<br />{{ u.device.device_type.display_name }} ({{ u.device.device_type.u_height }}U){% if u.device.asset_tag %}<br />{{ u.device.asset_tag }}{% endif %}{% if u.device.serial %}<br />{{ u.device.serial }}{% endif %}">
|
||||||
{{ u.device }}
|
{{ u.device }}
|
||||||
{% if u.device.devicebay_count %}
|
{% if u.device.devicebay_count %}
|
||||||
|
@ -14,20 +14,29 @@
|
|||||||
{% render_field form.mgmt_only %}
|
{% render_field form.mgmt_only %}
|
||||||
{% render_field form.description %}
|
{% render_field form.description %}
|
||||||
{% render_field form.mode %}
|
{% render_field form.mode %}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="panel panel-default">
|
||||||
|
<div class="panel-heading"><strong>Tags</strong></div>
|
||||||
|
<div class="panel-body">
|
||||||
{% render_field form.tags %}
|
{% render_field form.tags %}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
{% if obj.mode %}
|
|
||||||
<div class="panel panel-default" id="vlans_panel">
|
<div class="panel panel-default" id="vlans_panel">
|
||||||
<div class="panel-heading"><strong>802.1Q VLANs</strong></div>
|
<div class="panel-heading"><strong>802.1Q VLANs</strong></div>
|
||||||
|
{% if obj.mode %}
|
||||||
{% include 'dcim/inc/interface_vlans_table.html' %}
|
{% include 'dcim/inc/interface_vlans_table.html' %}
|
||||||
<div class="panel-footer text-right">
|
<div class="panel-footer text-right">
|
||||||
<a href="{% url 'dcim:interface_assign_vlans' pk=obj.pk %}?return_url={% url 'dcim:interface_edit' pk=obj.pk %}" class="btn btn-primary btn-xs{% if form.instance.mode == 100 and form.instance.untagged_vlan %} disabled{% endif %}">
|
<a href="{% url 'dcim:interface_assign_vlans' pk=obj.pk %}?return_url={% url 'dcim:interface_edit' pk=obj.pk %}" class="btn btn-primary btn-xs{% if form.instance.mode == 100 and form.instance.untagged_vlan %} disabled{% endif %}">
|
||||||
<i class="glyphicon glyphicon-plus"></i> Add VLANs
|
<i class="glyphicon glyphicon-plus"></i> Add VLANs
|
||||||
</a>
|
</a>
|
||||||
</div>
|
</div>
|
||||||
|
{% else %}
|
||||||
|
<div class="panel-body text-center text-muted">
|
||||||
|
<p>802.1Q mode not set</p>
|
||||||
</div>
|
</div>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
|
</div>
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
|
||||||
{% block buttons %}
|
{% block buttons %}
|
||||||
|
@ -67,7 +67,7 @@
|
|||||||
<td>VRF</td>
|
<td>VRF</td>
|
||||||
<td>
|
<td>
|
||||||
{% if ipaddress.vrf %}
|
{% if ipaddress.vrf %}
|
||||||
<a href="{% url 'ipam:vrf' pk=ipaddress.vrf.pk %}">{{ ipaddress.vrf }}</a> ({{ ipaddress.vrf.rd }})
|
<a href="{% url 'ipam:vrf' pk=ipaddress.vrf.pk %}">{{ ipaddress.vrf }}</a>
|
||||||
{% else %}
|
{% else %}
|
||||||
<span>Global</span>
|
<span>Global</span>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
|
@ -87,7 +87,7 @@
|
|||||||
<tr>
|
<tr>
|
||||||
<td>Prefixes</td>
|
<td>Prefixes</td>
|
||||||
<td>
|
<td>
|
||||||
<a href="{% url 'ipam:prefix_list' %}?vrf={{ vrf.rd }}">{{ prefix_count }}</a>
|
<a href="{% url 'ipam:prefix_list' %}?vrf_id={{ vrf.pk }}">{{ prefix_count }}</a>
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
</table>
|
</table>
|
||||||
|
@ -8,6 +8,9 @@
|
|||||||
<button class="btn btn-xs btn-success unlock-secret" secret-id="{{ secret.pk }}">
|
<button class="btn btn-xs btn-success unlock-secret" secret-id="{{ secret.pk }}">
|
||||||
<i class="fa fa-lock"></i> Unlock
|
<i class="fa fa-lock"></i> Unlock
|
||||||
</button>
|
</button>
|
||||||
|
<button class="btn btn-xs btn-default copy-secret collapse" secret-id="{{ secret.pk }}" data-clipboard-target="#secret_{{ secret.pk }}">
|
||||||
|
<i class="fa fa-copy"></i> Copy
|
||||||
|
</button>
|
||||||
<button class="btn btn-xs btn-danger lock-secret collapse" secret-id="{{ secret.pk }}">
|
<button class="btn btn-xs btn-danger lock-secret collapse" secret-id="{{ secret.pk }}">
|
||||||
<i class="fa fa-unlock-alt"></i> Lock
|
<i class="fa fa-unlock-alt"></i> Lock
|
||||||
</button>
|
</button>
|
||||||
|
@ -77,11 +77,14 @@
|
|||||||
</form>
|
</form>
|
||||||
<div class="row">
|
<div class="row">
|
||||||
<div class="col-md-2">Secret</div>
|
<div class="col-md-2">Secret</div>
|
||||||
<div class="col-md-8" id="secret_{{ secret.pk }}">********</div>
|
<div class="col-md-6" id="secret_{{ secret.pk }}">********</div>
|
||||||
<div class="col-md-2 text-right">
|
<div class="col-md-4 text-right">
|
||||||
<button class="btn btn-xs btn-success unlock-secret" secret-id="{{ secret.pk }}">
|
<button class="btn btn-xs btn-success unlock-secret" secret-id="{{ secret.pk }}">
|
||||||
<i class="fa fa-lock"></i> Unlock
|
<i class="fa fa-lock"></i> Unlock
|
||||||
</button>
|
</button>
|
||||||
|
<button class="btn btn-xs btn-default copy-secret collapse" secret-id="{{ secret.pk }}" data-clipboard-target="#secret_{{ secret.pk }}">
|
||||||
|
<i class="fa fa-copy"></i> Copy
|
||||||
|
</button>
|
||||||
<button class="btn btn-xs btn-danger lock-secret collapse" secret-id="{{ secret.pk }}">
|
<button class="btn btn-xs btn-danger lock-secret collapse" secret-id="{{ secret.pk }}">
|
||||||
<i class="fa fa-unlock-alt"></i> Lock
|
<i class="fa fa-unlock-alt"></i> Lock
|
||||||
</button>
|
</button>
|
||||||
|
@ -4,6 +4,14 @@
|
|||||||
|
|
||||||
{% block content %}
|
{% block content %}
|
||||||
<h1>{% block title %}Editing {{ table.rows|length }} {{ obj_type_plural|bettertitle }}{% endblock %}</h1>
|
<h1>{% block title %}Editing {{ table.rows|length }} {{ obj_type_plural|bettertitle }}{% endblock %}</h1>
|
||||||
|
{% if form.errors %}
|
||||||
|
<div class="panel panel-danger">
|
||||||
|
<div class="panel-heading"><strong>Errors</strong></div>
|
||||||
|
<div class="panel-body">
|
||||||
|
{{ form.errors }}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
{% endif %}
|
||||||
<form action="" method="post" class="form form-horizontal">
|
<form action="" method="post" class="form form-horizontal">
|
||||||
{% csrf_token %}
|
{% csrf_token %}
|
||||||
{% if request.POST.return_url %}
|
{% if request.POST.return_url %}
|
||||||
|
58
netbox/tenancy/tests/test_views.py
Normal file
58
netbox/tenancy/tests/test_views.py
Normal file
@ -0,0 +1,58 @@
|
|||||||
|
import urllib.parse
|
||||||
|
|
||||||
|
from django.test import Client, TestCase
|
||||||
|
from django.urls import reverse
|
||||||
|
|
||||||
|
from tenancy.models import Tenant, TenantGroup
|
||||||
|
|
||||||
|
|
||||||
|
class TenantGroupTestCase(TestCase):
|
||||||
|
|
||||||
|
def setUp(self):
|
||||||
|
|
||||||
|
self.client = Client()
|
||||||
|
|
||||||
|
TenantGroup.objects.bulk_create([
|
||||||
|
TenantGroup(name='Tenant Group 1', slug='tenant-group-1'),
|
||||||
|
TenantGroup(name='Tenant Group 2', slug='tenant-group-2'),
|
||||||
|
TenantGroup(name='Tenant Group 3', slug='tenant-group-3'),
|
||||||
|
])
|
||||||
|
|
||||||
|
def test_tenantgroup_list(self):
|
||||||
|
|
||||||
|
url = reverse('tenancy:tenantgroup_list')
|
||||||
|
|
||||||
|
response = self.client.get(url, follow=True)
|
||||||
|
self.assertEqual(response.status_code, 200)
|
||||||
|
|
||||||
|
|
||||||
|
class TenantTestCase(TestCase):
|
||||||
|
|
||||||
|
def setUp(self):
|
||||||
|
|
||||||
|
self.client = Client()
|
||||||
|
|
||||||
|
tenantgroup = TenantGroup(name='Tenant Group 1', slug='tenant-group-1')
|
||||||
|
tenantgroup.save()
|
||||||
|
|
||||||
|
Tenant.objects.bulk_create([
|
||||||
|
Tenant(name='Tenant 1', slug='tenant-1', group=tenantgroup),
|
||||||
|
Tenant(name='Tenant 2', slug='tenant-2', group=tenantgroup),
|
||||||
|
Tenant(name='Tenant 3', slug='tenant-3', group=tenantgroup),
|
||||||
|
])
|
||||||
|
|
||||||
|
def test_tenant_list(self):
|
||||||
|
|
||||||
|
url = reverse('tenancy:tenant_list')
|
||||||
|
params = {
|
||||||
|
"group": TenantGroup.objects.first().slug,
|
||||||
|
}
|
||||||
|
|
||||||
|
response = self.client.get('{}?{}'.format(url, urllib.parse.urlencode(params)), follow=True)
|
||||||
|
self.assertEqual(response.status_code, 200)
|
||||||
|
|
||||||
|
def test_tenant(self):
|
||||||
|
|
||||||
|
tenant = Tenant.objects.first()
|
||||||
|
response = self.client.get(tenant.get_absolute_url(), follow=True)
|
||||||
|
self.assertEqual(response.status_code, 200)
|
@ -10,6 +10,8 @@ ColorValidator = RegexValidator(
|
|||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
# Deprecated: Retained only to ensure successful migration from early releases
|
||||||
|
# Use models.CharField(null=True) instead
|
||||||
class NullableCharField(models.CharField):
|
class NullableCharField(models.CharField):
|
||||||
description = "Stores empty values as NULL rather than ''"
|
description = "Stores empty values as NULL rather than ''"
|
||||||
|
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
import django_filters
|
import django_filters
|
||||||
|
from django.conf import settings
|
||||||
from django.db.models import Q
|
from django.db.models import Q
|
||||||
from taggit.models import Tag
|
from taggit.models import Tag
|
||||||
|
|
||||||
@ -14,12 +15,11 @@ class NullableCharFieldFilter(django_filters.CharFilter):
|
|||||||
"""
|
"""
|
||||||
Allow matching on null field values by passing a special string used to signify NULL.
|
Allow matching on null field values by passing a special string used to signify NULL.
|
||||||
"""
|
"""
|
||||||
null_value = 'NULL'
|
|
||||||
|
|
||||||
def filter(self, qs, value):
|
def filter(self, qs, value):
|
||||||
if value != self.null_value:
|
if value != settings.FILTERS_NULL_CHOICE_VALUE:
|
||||||
return super().filter(qs, value)
|
return super().filter(qs, value)
|
||||||
qs = self.get_method(qs)(**{'{}__isnull'.format(self.name): True})
|
qs = self.get_method(qs)(**{'{}__isnull'.format(self.field_name): True})
|
||||||
return qs.distinct() if self.distinct else qs
|
return qs.distinct() if self.distinct else qs
|
||||||
|
|
||||||
|
|
||||||
|
117
netbox/virtualization/tests/test_views.py
Normal file
117
netbox/virtualization/tests/test_views.py
Normal file
@ -0,0 +1,117 @@
|
|||||||
|
import urllib.parse
|
||||||
|
|
||||||
|
from django.test import Client, TestCase
|
||||||
|
from django.urls import reverse
|
||||||
|
|
||||||
|
from virtualization.models import Cluster, ClusterGroup, ClusterType, VirtualMachine
|
||||||
|
|
||||||
|
|
||||||
|
class ClusterGroupTestCase(TestCase):
|
||||||
|
|
||||||
|
def setUp(self):
|
||||||
|
|
||||||
|
self.client = Client()
|
||||||
|
|
||||||
|
ClusterGroup.objects.bulk_create([
|
||||||
|
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'),
|
||||||
|
])
|
||||||
|
|
||||||
|
def test_clustergroup_list(self):
|
||||||
|
|
||||||
|
url = reverse('virtualization:clustergroup_list')
|
||||||
|
|
||||||
|
response = self.client.get(url)
|
||||||
|
self.assertEqual(response.status_code, 200)
|
||||||
|
|
||||||
|
|
||||||
|
class ClusterTypeTestCase(TestCase):
|
||||||
|
|
||||||
|
def setUp(self):
|
||||||
|
|
||||||
|
self.client = Client()
|
||||||
|
|
||||||
|
ClusterType.objects.bulk_create([
|
||||||
|
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'),
|
||||||
|
])
|
||||||
|
|
||||||
|
def test_clustertype_list(self):
|
||||||
|
|
||||||
|
url = reverse('virtualization:clustertype_list')
|
||||||
|
|
||||||
|
response = self.client.get(url)
|
||||||
|
self.assertEqual(response.status_code, 200)
|
||||||
|
|
||||||
|
|
||||||
|
class ClusterTestCase(TestCase):
|
||||||
|
|
||||||
|
def setUp(self):
|
||||||
|
|
||||||
|
self.client = Client()
|
||||||
|
|
||||||
|
clustergroup = ClusterGroup(name='Cluster Group 1', slug='cluster-group-1')
|
||||||
|
clustergroup.save()
|
||||||
|
|
||||||
|
clustertype = ClusterType(name='Cluster Type 1', slug='cluster-type-1')
|
||||||
|
clustertype.save()
|
||||||
|
|
||||||
|
Cluster.objects.bulk_create([
|
||||||
|
Cluster(name='Cluster 1', group=clustergroup, type=clustertype),
|
||||||
|
Cluster(name='Cluster 2', group=clustergroup, type=clustertype),
|
||||||
|
Cluster(name='Cluster 3', group=clustergroup, type=clustertype),
|
||||||
|
])
|
||||||
|
|
||||||
|
def test_cluster_list(self):
|
||||||
|
|
||||||
|
url = reverse('virtualization:cluster_list')
|
||||||
|
params = {
|
||||||
|
"group": ClusterGroup.objects.first().slug,
|
||||||
|
"type": ClusterType.objects.first().slug,
|
||||||
|
}
|
||||||
|
|
||||||
|
response = self.client.get('{}?{}'.format(url, urllib.parse.urlencode(params)))
|
||||||
|
self.assertEqual(response.status_code, 200)
|
||||||
|
|
||||||
|
def test_cluster(self):
|
||||||
|
|
||||||
|
cluster = Cluster.objects.first()
|
||||||
|
response = self.client.get(cluster.get_absolute_url())
|
||||||
|
self.assertEqual(response.status_code, 200)
|
||||||
|
|
||||||
|
|
||||||
|
class VirtualMachineTestCase(TestCase):
|
||||||
|
|
||||||
|
def setUp(self):
|
||||||
|
|
||||||
|
self.client = Client()
|
||||||
|
|
||||||
|
clustertype = ClusterType(name='Cluster Type 1', slug='cluster-type-1')
|
||||||
|
clustertype.save()
|
||||||
|
|
||||||
|
cluster = Cluster(name='Cluster 1', type=clustertype)
|
||||||
|
cluster.save()
|
||||||
|
|
||||||
|
VirtualMachine.objects.bulk_create([
|
||||||
|
VirtualMachine(name='Virtual Machine 1', cluster=cluster),
|
||||||
|
VirtualMachine(name='Virtual Machine 2', cluster=cluster),
|
||||||
|
VirtualMachine(name='Virtual Machine 3', cluster=cluster),
|
||||||
|
])
|
||||||
|
|
||||||
|
def test_virtualmachine_list(self):
|
||||||
|
|
||||||
|
url = reverse('virtualization:virtualmachine_list')
|
||||||
|
params = {
|
||||||
|
"cluster_id": Cluster.objects.first().pk,
|
||||||
|
}
|
||||||
|
|
||||||
|
response = self.client.get('{}?{}'.format(url, urllib.parse.urlencode(params)))
|
||||||
|
self.assertEqual(response.status_code, 200)
|
||||||
|
|
||||||
|
def test_virtualmachine(self):
|
||||||
|
|
||||||
|
virtualmachine = VirtualMachine.objects.first()
|
||||||
|
response = self.client.get(virtualmachine.get_absolute_url())
|
||||||
|
self.assertEqual(response.status_code, 200)
|
Loading…
Reference in New Issue
Block a user