mirror of
https://github.com/netbox-community/netbox.git
synced 2025-08-15 20:18:17 -06:00
Merge branch 'develop' of https://github.com/digitalocean/netbox into 2902-systemd
This commit is contained in:
commit
2dae2e60d6
60
CHANGELOG.md
60
CHANGELOG.md
@ -1,18 +1,76 @@
|
|||||||
v2.5.7 (FUTURE)
|
v2.5.9 (FUTURE)
|
||||||
|
|
||||||
|
## Enhancements
|
||||||
|
|
||||||
|
* [#3011](https://github.com/digitalocean/netbox/issues/3011) - Add SSL support for django-rq (requires django-rq v1.3.1+)
|
||||||
|
|
||||||
|
## Bug Fixes
|
||||||
|
|
||||||
|
* [#2207](https://github.com/digitalocean/netbox/issues/2207) - Fixes Deterministic Ordering of Interfaces
|
||||||
|
* [#2577](https://github.com/digitalocean/netbox/issues/2577) - Clarification of wording in API regarding filtering
|
||||||
|
* [#2924](https://github.com/digitalocean/netbox/issues/2924) - Add interface type for QSFP28 50GE
|
||||||
|
* [#2936](https://github.com/digitalocean/netbox/issues/2936) - Fix device role selection showing duplicate first entry
|
||||||
|
* [#2998](https://github.com/digitalocean/netbox/issues/2998) - Limit device query to non-racked devices if no rack selected when creating a cable
|
||||||
|
* [#3014](https://github.com/digitalocean/netbox/issues/3014) - Fixes VM Role filtering
|
||||||
|
|
||||||
|
v2.5.8 (2019-03-11)
|
||||||
|
|
||||||
|
## Enhancements
|
||||||
|
|
||||||
|
* [#2435](https://github.com/digitalocean/netbox/issues/2435) - Printer friendly CSS
|
||||||
|
|
||||||
|
## Bug Fixes
|
||||||
|
|
||||||
|
* [#2065](https://github.com/digitalocean/netbox/issues/2065) - Correct documentation for VM interface serializer
|
||||||
|
* [#2705](https://github.com/digitalocean/netbox/issues/2705) - Fix endpoint grouping in API docs
|
||||||
|
* [#2781](https://github.com/digitalocean/netbox/issues/2781) - Fix filtering of sites/devices/VMs by multiple regions
|
||||||
|
* [#2923](https://github.com/digitalocean/netbox/issues/2923) - Provider filter form's site field should be blank by default
|
||||||
|
* [#2938](https://github.com/digitalocean/netbox/issues/2938) - Enforce deterministic ordering of device components returned by API
|
||||||
|
* [#2939](https://github.com/digitalocean/netbox/issues/2939) - Exclude circuit terminations from API interface connections endpoint
|
||||||
|
* [#2940](https://github.com/digitalocean/netbox/issues/2940) - Allow CSV import of prefixes/IPs to VRF without an RD assigned
|
||||||
|
* [#2944](https://github.com/digitalocean/netbox/issues/2944) - Record the deletion of an IP address in the changelog of its parent interface (if any)
|
||||||
|
* [#2952](https://github.com/digitalocean/netbox/issues/2952) - Added the `slug` field to the Tenant filter for use in the API and search function
|
||||||
|
* [#2954](https://github.com/digitalocean/netbox/issues/2954) - Remove trailing slashes to fix root/template paths on Windows
|
||||||
|
* [#2961](https://github.com/digitalocean/netbox/issues/2961) - Prevent exception when exporting inventory items belonging to unnamed devices
|
||||||
|
* [#2962](https://github.com/digitalocean/netbox/issues/2962) - Increase ExportTemplate `mime_type` field length
|
||||||
|
* [#2966](https://github.com/digitalocean/netbox/issues/2966) - Accept `null` cable length_unit via API
|
||||||
|
* [#2972](https://github.com/digitalocean/netbox/issues/2972) - Improve ContentTypeField serializer to elegantly handle invalid data
|
||||||
|
* [#2976](https://github.com/digitalocean/netbox/issues/2976) - Add delete button to tag view
|
||||||
|
* [#2980](https://github.com/digitalocean/netbox/issues/2980) - Improve rendering time for API docs
|
||||||
|
* [#2982](https://github.com/digitalocean/netbox/issues/2982) - Correct CSS class assignment on color picker
|
||||||
|
* [#2984](https://github.com/digitalocean/netbox/issues/2984) - Fix logging of unlabeled cable ID on cable deletion
|
||||||
|
* [#2985](https://github.com/digitalocean/netbox/issues/2985) - Fix pagination page length for rack elevations
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
v2.5.7 (2019-02-21)
|
||||||
|
|
||||||
## Enhancements
|
## Enhancements
|
||||||
|
|
||||||
* [#2902](https://github.com/digitalocean/netbox/issues/2902) - Replace supervisord with systemd
|
* [#2902](https://github.com/digitalocean/netbox/issues/2902) - Replace supervisord with systemd
|
||||||
* [#2357](https://github.com/digitalocean/netbox/issues/2357) - Enable filtering of devices by rack face
|
* [#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
|
* [#2903](https://github.com/digitalocean/netbox/issues/2903) - Clarify purpose of tags field on interface edit form
|
||||||
|
|
||||||
## Bug Fixes
|
## 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
|
* [#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
|
* [#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
|
* [#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
|
* [#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
|
* [#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)
|
||||||
|
|
||||||
|
@ -45,13 +45,13 @@ and run `upgrade.sh`.
|
|||||||
|
|
||||||
## Supported SDK
|
## Supported SDK
|
||||||
|
|
||||||
- [pynetbox](https://github.com/digitalocean/pynetbox) Python API client library for Netbox.
|
- [pynetbox](https://github.com/digitalocean/pynetbox) - A Python API client library for Netbox
|
||||||
|
|
||||||
## Community SDK
|
## Community SDK
|
||||||
|
|
||||||
- [netbox-client-ruby](https://github.com/ninech/netbox-client-ruby) A ruby client library for Netbox v2.
|
- [netbox-client-ruby](https://github.com/ninech/netbox-client-ruby) - A Ruby client library for Netbox
|
||||||
|
- [powerbox](https://github.com/BatmanAMA/powerbox) - A PowerShell library for Netbox
|
||||||
|
|
||||||
## Ansible Inventory
|
## Ansible Inventory
|
||||||
|
|
||||||
- [netbox-as-ansible-inventory](https://github.com/AAbouZaid/netbox-as-ansible-inventory) Ansible dynamic inventory script for Netbox.
|
- [netbox-as-ansible-inventory](https://github.com/AAbouZaid/netbox-as-ansible-inventory) - Ansible dynamic inventory script for Netbox
|
||||||
|
|
||||||
|
@ -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.
|
||||||
|
@ -261,7 +261,7 @@ A list of objects retrieved via the API can be filtered by passing one or more q
|
|||||||
GET /api/ipam/prefixes/?status=1
|
GET /api/ipam/prefixes/?status=1
|
||||||
```
|
```
|
||||||
|
|
||||||
The same filter can be incldued multiple times. These will effect a logical OR and return objects matching any of the given values. For example, the following will return all active and reserved prefixes:
|
Certain filters can be included multiple times within a single request. These will effect a logical OR and return objects matching any of the given values. For example, the following will return all active and reserved prefixes:
|
||||||
|
|
||||||
```
|
```
|
||||||
GET /api/ipam/prefixes/?status=1&status=2
|
GET /api/ipam/prefixes/?status=1&status=2
|
||||||
|
@ -283,6 +283,7 @@ REDIS = {
|
|||||||
'PASSWORD': '',
|
'PASSWORD': '',
|
||||||
'DATABASE': 0,
|
'DATABASE': 0,
|
||||||
'DEFAULT_TIMEOUT': 300,
|
'DEFAULT_TIMEOUT': 300,
|
||||||
|
'SSL': False,
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
@ -315,3 +316,9 @@ The TCP port to use when connecting to the Redis server.
|
|||||||
Default: None
|
Default: None
|
||||||
|
|
||||||
The password to use when authenticating to the Redis server (optional).
|
The password to use when authenticating to the Redis server (optional).
|
||||||
|
|
||||||
|
### SSL
|
||||||
|
|
||||||
|
Default: False
|
||||||
|
|
||||||
|
Use secure sockets layer to encrypt the connections to the Redis server.
|
||||||
|
@ -107,7 +107,7 @@ class ProviderFilterForm(BootstrapMixin, CustomFieldFilterForm):
|
|||||||
site = FilterChoiceField(
|
site = FilterChoiceField(
|
||||||
queryset=Site.objects.all(),
|
queryset=Site.objects.all(),
|
||||||
to_field_name='slug',
|
to_field_name='slug',
|
||||||
widget=APISelect(
|
widget=APISelectMultiple(
|
||||||
api_url="/api/dcim/sites/",
|
api_url="/api/dcim/sites/",
|
||||||
value_field="slug",
|
value_field="slug",
|
||||||
)
|
)
|
||||||
|
@ -59,7 +59,7 @@ class CircuitTypeTable(BaseTable):
|
|||||||
name = tables.LinkColumn()
|
name = tables.LinkColumn()
|
||||||
circuit_count = tables.Column(verbose_name='Circuits')
|
circuit_count = tables.Column(verbose_name='Circuits')
|
||||||
actions = tables.TemplateColumn(
|
actions = tables.TemplateColumn(
|
||||||
template_code=CIRCUITTYPE_ACTIONS, attrs={'td': {'class': 'text-right'}}, verbose_name=''
|
template_code=CIRCUITTYPE_ACTIONS, attrs={'td': {'class': 'text-right noprint'}}, verbose_name=''
|
||||||
)
|
)
|
||||||
|
|
||||||
class Meta(BaseTable.Meta):
|
class Meta(BaseTable.Meta):
|
||||||
|
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)
|
@ -507,7 +507,7 @@ class CableSerializer(ValidatedModelSerializer):
|
|||||||
termination_a = serializers.SerializerMethodField(read_only=True)
|
termination_a = serializers.SerializerMethodField(read_only=True)
|
||||||
termination_b = serializers.SerializerMethodField(read_only=True)
|
termination_b = serializers.SerializerMethodField(read_only=True)
|
||||||
status = ChoiceField(choices=CONNECTION_STATUS_CHOICES, required=False)
|
status = ChoiceField(choices=CONNECTION_STATUS_CHOICES, required=False)
|
||||||
length_unit = ChoiceField(choices=CABLE_LENGTH_UNIT_CHOICES, required=False)
|
length_unit = ChoiceField(choices=CABLE_LENGTH_UNIT_CHOICES, required=False, allow_null=True)
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
model = Cable
|
model = Cable
|
||||||
|
@ -496,11 +496,11 @@ class PowerConnectionViewSet(ListModelMixin, GenericViewSet):
|
|||||||
|
|
||||||
class InterfaceConnectionViewSet(ListModelMixin, GenericViewSet):
|
class InterfaceConnectionViewSet(ListModelMixin, GenericViewSet):
|
||||||
queryset = Interface.objects.select_related(
|
queryset = Interface.objects.select_related(
|
||||||
'device', '_connected_interface', '_connected_circuittermination'
|
'device', '_connected_interface__device'
|
||||||
).filter(
|
).filter(
|
||||||
# Avoid duplicate connections by only selecting the lower PK in a connected pair
|
# Avoid duplicate connections by only selecting the lower PK in a connected pair
|
||||||
Q(_connected_interface__isnull=False, pk__lt=F('_connected_interface')) |
|
_connected_interface__isnull=False,
|
||||||
Q(_connected_circuittermination__isnull=False)
|
pk__lt=F('_connected_interface')
|
||||||
)
|
)
|
||||||
serializer_class = serializers.InterfaceConnectionSerializer
|
serializer_class = serializers.InterfaceConnectionSerializer
|
||||||
filterset_class = filters.InterfaceConnectionFilter
|
filterset_class = filters.InterfaceConnectionFilter
|
||||||
|
@ -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
|
||||||
@ -77,6 +83,7 @@ IFACE_FF_10GE_XENPAK = 1310
|
|||||||
IFACE_FF_10GE_X2 = 1320
|
IFACE_FF_10GE_X2 = 1320
|
||||||
IFACE_FF_25GE_SFP28 = 1350
|
IFACE_FF_25GE_SFP28 = 1350
|
||||||
IFACE_FF_40GE_QSFP_PLUS = 1400
|
IFACE_FF_40GE_QSFP_PLUS = 1400
|
||||||
|
IFACE_FF_50GE_QSFP28 = 1420
|
||||||
IFACE_FF_100GE_CFP = 1500
|
IFACE_FF_100GE_CFP = 1500
|
||||||
IFACE_FF_100GE_CFP2 = 1510
|
IFACE_FF_100GE_CFP2 = 1510
|
||||||
IFACE_FF_100GE_CFP4 = 1520
|
IFACE_FF_100GE_CFP4 = 1520
|
||||||
@ -158,6 +165,7 @@ IFACE_FF_CHOICES = [
|
|||||||
[IFACE_FF_10GE_X2, 'X2 (10GE)'],
|
[IFACE_FF_10GE_X2, 'X2 (10GE)'],
|
||||||
[IFACE_FF_25GE_SFP28, 'SFP28 (25GE)'],
|
[IFACE_FF_25GE_SFP28, 'SFP28 (25GE)'],
|
||||||
[IFACE_FF_40GE_QSFP_PLUS, 'QSFP+ (40GE)'],
|
[IFACE_FF_40GE_QSFP_PLUS, 'QSFP+ (40GE)'],
|
||||||
|
[IFACE_FF_50GE_QSFP28, 'QSFP28 (50GE)'],
|
||||||
[IFACE_FF_100GE_CFP, 'CFP (100GE)'],
|
[IFACE_FF_100GE_CFP, 'CFP (100GE)'],
|
||||||
[IFACE_FF_100GE_CFP2, 'CFP2 (100GE)'],
|
[IFACE_FF_100GE_CFP2, 'CFP2 (100GE)'],
|
||||||
[IFACE_FF_200GE_CFP2, 'CFP2 (200GE)'],
|
[IFACE_FF_200GE_CFP2, 'CFP2 (200GE)'],
|
||||||
@ -270,11 +278,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 +299,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 +369,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 +394,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)'),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
@ -1,6 +1,5 @@
|
|||||||
import django_filters
|
import django_filters
|
||||||
from django.contrib.auth.models import User
|
from django.contrib.auth.models import User
|
||||||
from django.core.exceptions import ObjectDoesNotExist
|
|
||||||
from django.db.models import Q
|
from django.db.models import Q
|
||||||
from netaddr import EUI
|
from netaddr import EUI
|
||||||
from netaddr.core import AddrFormatError
|
from netaddr.core import AddrFormatError
|
||||||
@ -8,7 +7,9 @@ from netaddr.core import AddrFormatError
|
|||||||
from extras.filters import CustomFieldFilterSet
|
from extras.filters import CustomFieldFilterSet
|
||||||
from tenancy.models import Tenant
|
from tenancy.models import Tenant
|
||||||
from utilities.constants import COLOR_CHOICES
|
from utilities.constants import COLOR_CHOICES
|
||||||
from utilities.filters import NameSlugSearchFilterSet, NullableCharFieldFilter, NumericInFilter, TagFilter
|
from utilities.filters import (
|
||||||
|
NameSlugSearchFilterSet, NullableCharFieldFilter, NumericInFilter, TagFilter, TreeNodeMultipleChoiceFilter
|
||||||
|
)
|
||||||
from virtualization.models import Cluster
|
from virtualization.models import Cluster
|
||||||
from .constants import *
|
from .constants import *
|
||||||
from .models import (
|
from .models import (
|
||||||
@ -49,14 +50,15 @@ class SiteFilter(CustomFieldFilterSet, django_filters.FilterSet):
|
|||||||
choices=SITE_STATUS_CHOICES,
|
choices=SITE_STATUS_CHOICES,
|
||||||
null_value=None
|
null_value=None
|
||||||
)
|
)
|
||||||
region_id = django_filters.NumberFilter(
|
region_id = TreeNodeMultipleChoiceFilter(
|
||||||
method='filter_region',
|
queryset=Region.objects.all(),
|
||||||
field_name='pk',
|
field_name='region__in',
|
||||||
label='Region (ID)',
|
label='Region (ID)',
|
||||||
)
|
)
|
||||||
region = django_filters.CharFilter(
|
region = TreeNodeMultipleChoiceFilter(
|
||||||
method='filter_region',
|
queryset=Region.objects.all(),
|
||||||
field_name='slug',
|
field_name='region__in',
|
||||||
|
to_field_name='slug',
|
||||||
label='Region (slug)',
|
label='Region (slug)',
|
||||||
)
|
)
|
||||||
tenant_id = django_filters.ModelMultipleChoiceFilter(
|
tenant_id = django_filters.ModelMultipleChoiceFilter(
|
||||||
@ -95,16 +97,6 @@ class SiteFilter(CustomFieldFilterSet, django_filters.FilterSet):
|
|||||||
pass
|
pass
|
||||||
return queryset.filter(qs_filter)
|
return queryset.filter(qs_filter)
|
||||||
|
|
||||||
def filter_region(self, queryset, name, value):
|
|
||||||
try:
|
|
||||||
region = Region.objects.get(**{name: value})
|
|
||||||
except ObjectDoesNotExist:
|
|
||||||
return queryset.none()
|
|
||||||
return queryset.filter(
|
|
||||||
Q(region=region) |
|
|
||||||
Q(region__in=region.get_descendants())
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
class RackGroupFilter(NameSlugSearchFilterSet):
|
class RackGroupFilter(NameSlugSearchFilterSet):
|
||||||
site_id = django_filters.ModelMultipleChoiceFilter(
|
site_id = django_filters.ModelMultipleChoiceFilter(
|
||||||
@ -513,14 +505,15 @@ class DeviceFilter(CustomFieldFilterSet):
|
|||||||
)
|
)
|
||||||
name = NullableCharFieldFilter()
|
name = NullableCharFieldFilter()
|
||||||
asset_tag = NullableCharFieldFilter()
|
asset_tag = NullableCharFieldFilter()
|
||||||
region_id = django_filters.NumberFilter(
|
region_id = TreeNodeMultipleChoiceFilter(
|
||||||
method='filter_region',
|
queryset=Region.objects.all(),
|
||||||
field_name='pk',
|
field_name='site__region__in',
|
||||||
label='Region (ID)',
|
label='Region (ID)',
|
||||||
)
|
)
|
||||||
region = django_filters.CharFilter(
|
region = TreeNodeMultipleChoiceFilter(
|
||||||
method='filter_region',
|
queryset=Region.objects.all(),
|
||||||
field_name='slug',
|
field_name='site__region__in',
|
||||||
|
to_field_name='slug',
|
||||||
label='Region (slug)',
|
label='Region (slug)',
|
||||||
)
|
)
|
||||||
site_id = django_filters.ModelMultipleChoiceFilter(
|
site_id = django_filters.ModelMultipleChoiceFilter(
|
||||||
@ -543,6 +536,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 +599,7 @@ class DeviceFilter(CustomFieldFilterSet):
|
|||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
model = Device
|
model = Device
|
||||||
fields = ['serial', 'position', 'face']
|
fields = ['serial', 'face']
|
||||||
|
|
||||||
def search(self, queryset, name, value):
|
def search(self, queryset, name, value):
|
||||||
if not value.strip():
|
if not value.strip():
|
||||||
@ -615,16 +612,6 @@ class DeviceFilter(CustomFieldFilterSet):
|
|||||||
Q(comments__icontains=value)
|
Q(comments__icontains=value)
|
||||||
).distinct()
|
).distinct()
|
||||||
|
|
||||||
def filter_region(self, queryset, name, value):
|
|
||||||
try:
|
|
||||||
region = Region.objects.get(**{name: value})
|
|
||||||
except ObjectDoesNotExist:
|
|
||||||
return queryset.none()
|
|
||||||
return queryset.filter(
|
|
||||||
Q(site__region=region) |
|
|
||||||
Q(site__region__in=region.get_descendants())
|
|
||||||
)
|
|
||||||
|
|
||||||
def _mac_address(self, queryset, name, value):
|
def _mac_address(self, queryset, name, value):
|
||||||
value = value.strip()
|
value = value.strip()
|
||||||
if not value:
|
if not value:
|
||||||
|
@ -1700,7 +1700,6 @@ class DeviceFilterForm(BootstrapMixin, CustomFieldFilterForm):
|
|||||||
widget=APISelectMultiple(
|
widget=APISelectMultiple(
|
||||||
api_url="/api/dcim/device-roles/",
|
api_url="/api/dcim/device-roles/",
|
||||||
value_field="slug",
|
value_field="slug",
|
||||||
null_option=True,
|
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
tenant = FilterChoiceField(
|
tenant = FilterChoiceField(
|
||||||
@ -2362,7 +2361,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 +2435,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 +2752,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,
|
||||||
|
@ -27,7 +27,7 @@ class DeviceComponentManager(Manager):
|
|||||||
select={
|
select={
|
||||||
'name_padded': sql.format(table_name, table_name),
|
'name_padded': sql.format(table_name, table_name),
|
||||||
}
|
}
|
||||||
).order_by('name_padded')
|
).order_by('name_padded', 'pk')
|
||||||
|
|
||||||
|
|
||||||
class InterfaceQuerySet(QuerySet):
|
class InterfaceQuerySet(QuerySet):
|
||||||
@ -64,11 +64,15 @@ class InterfaceManager(Manager):
|
|||||||
|
|
||||||
The original `name` field is considered in its entirety to serve as a fallback in the event interfaces do not
|
The original `name` field is considered in its entirety to serve as a fallback in the event interfaces do not
|
||||||
match any of the prescribed fields.
|
match any of the prescribed fields.
|
||||||
|
|
||||||
|
The `id` field is included to enforce deterministic ordering of interfaces in similar vein of other device
|
||||||
|
components.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
sql_col = '{}.name'.format(self.model._meta.db_table)
|
sql_col = '{}.name'.format(self.model._meta.db_table)
|
||||||
ordering = [
|
ordering = [
|
||||||
'_slot', '_subslot', '_position', '_subposition', '_type', '_id', '_channel', '_vc', 'name',
|
'_slot', '_subslot', '_position', '_subposition', '_type', '_id', '_channel', '_vc', 'name', 'pk'
|
||||||
|
|
||||||
]
|
]
|
||||||
|
|
||||||
fields = {
|
fields = {
|
||||||
|
@ -2423,7 +2423,7 @@ class InventoryItem(ComponentModel):
|
|||||||
|
|
||||||
def to_csv(self):
|
def to_csv(self):
|
||||||
return (
|
return (
|
||||||
self.device.name or '{' + self.device.pk + '}',
|
self.device.name or '{{{}}}'.format(self.device.pk),
|
||||||
self.name,
|
self.name,
|
||||||
self.manufacturer.name if self.manufacturer else None,
|
self.manufacturer.name if self.manufacturer else None,
|
||||||
self.part_id,
|
self.part_id,
|
||||||
@ -2557,16 +2557,15 @@ class Cable(ChangeLoggedModel):
|
|||||||
('termination_b_type', 'termination_b_id'),
|
('termination_b_type', 'termination_b_id'),
|
||||||
)
|
)
|
||||||
|
|
||||||
def __init__(self, *args, **kwargs):
|
|
||||||
|
|
||||||
super().__init__(*args, **kwargs)
|
|
||||||
|
|
||||||
# Create an ID string for use by __str__(). We have to save a copy of pk since it's nullified after .delete()
|
|
||||||
# is called.
|
|
||||||
self.id_string = '#{}'.format(self.pk)
|
|
||||||
|
|
||||||
def __str__(self):
|
def __str__(self):
|
||||||
return self.label or self.id_string
|
if self.label:
|
||||||
|
return self.label
|
||||||
|
|
||||||
|
# Save a copy of the PK on the instance since it's nullified if .delete() is called
|
||||||
|
if not hasattr(self, 'id_string'):
|
||||||
|
self.id_string = '#{}'.format(self.pk)
|
||||||
|
|
||||||
|
return self.id_string
|
||||||
|
|
||||||
def get_absolute_url(self):
|
def get_absolute_url(self):
|
||||||
return reverse('dcim:cable', args=[self.pk])
|
return reverse('dcim:cable', args=[self.pk])
|
||||||
@ -2651,6 +2650,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
|
||||||
|
@ -196,7 +196,7 @@ class RegionTable(BaseTable):
|
|||||||
slug = tables.Column(verbose_name='Slug')
|
slug = tables.Column(verbose_name='Slug')
|
||||||
actions = tables.TemplateColumn(
|
actions = tables.TemplateColumn(
|
||||||
template_code=REGION_ACTIONS,
|
template_code=REGION_ACTIONS,
|
||||||
attrs={'td': {'class': 'text-right'}},
|
attrs={'td': {'class': 'text-right noprint'}},
|
||||||
verbose_name=''
|
verbose_name=''
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -239,7 +239,7 @@ class RackGroupTable(BaseTable):
|
|||||||
slug = tables.Column()
|
slug = tables.Column()
|
||||||
actions = tables.TemplateColumn(
|
actions = tables.TemplateColumn(
|
||||||
template_code=RACKGROUP_ACTIONS,
|
template_code=RACKGROUP_ACTIONS,
|
||||||
attrs={'td': {'class': 'text-right'}},
|
attrs={'td': {'class': 'text-right noprint'}},
|
||||||
verbose_name=''
|
verbose_name=''
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -258,7 +258,7 @@ class RackRoleTable(BaseTable):
|
|||||||
rack_count = tables.Column(verbose_name='Racks')
|
rack_count = tables.Column(verbose_name='Racks')
|
||||||
color = tables.TemplateColumn(COLOR_LABEL, verbose_name='Color')
|
color = tables.TemplateColumn(COLOR_LABEL, verbose_name='Color')
|
||||||
slug = tables.Column(verbose_name='Slug')
|
slug = tables.Column(verbose_name='Slug')
|
||||||
actions = tables.TemplateColumn(template_code=RACKROLE_ACTIONS, attrs={'td': {'class': 'text-right'}},
|
actions = tables.TemplateColumn(template_code=RACKROLE_ACTIONS, attrs={'td': {'class': 'text-right noprint'}},
|
||||||
verbose_name='')
|
verbose_name='')
|
||||||
|
|
||||||
class Meta(BaseTable.Meta):
|
class Meta(BaseTable.Meta):
|
||||||
@ -309,7 +309,7 @@ class RackReservationTable(BaseTable):
|
|||||||
rack = tables.LinkColumn('dcim:rack', args=[Accessor('rack.pk')])
|
rack = tables.LinkColumn('dcim:rack', args=[Accessor('rack.pk')])
|
||||||
unit_list = tables.Column(orderable=False, verbose_name='Units')
|
unit_list = tables.Column(orderable=False, verbose_name='Units')
|
||||||
actions = tables.TemplateColumn(
|
actions = tables.TemplateColumn(
|
||||||
template_code=RACKRESERVATION_ACTIONS, attrs={'td': {'class': 'text-right'}}, verbose_name=''
|
template_code=RACKRESERVATION_ACTIONS, attrs={'td': {'class': 'text-right noprint'}}, verbose_name=''
|
||||||
)
|
)
|
||||||
|
|
||||||
class Meta(BaseTable.Meta):
|
class Meta(BaseTable.Meta):
|
||||||
@ -327,7 +327,7 @@ class ManufacturerTable(BaseTable):
|
|||||||
devicetype_count = tables.Column(verbose_name='Device Types')
|
devicetype_count = tables.Column(verbose_name='Device Types')
|
||||||
platform_count = tables.Column(verbose_name='Platforms')
|
platform_count = tables.Column(verbose_name='Platforms')
|
||||||
slug = tables.Column(verbose_name='Slug')
|
slug = tables.Column(verbose_name='Slug')
|
||||||
actions = tables.TemplateColumn(template_code=MANUFACTURER_ACTIONS, attrs={'td': {'class': 'text-right'}},
|
actions = tables.TemplateColumn(template_code=MANUFACTURER_ACTIONS, attrs={'td': {'class': 'text-right noprint'}},
|
||||||
verbose_name='')
|
verbose_name='')
|
||||||
|
|
||||||
class Meta(BaseTable.Meta):
|
class Meta(BaseTable.Meta):
|
||||||
@ -463,7 +463,7 @@ class DeviceRoleTable(BaseTable):
|
|||||||
slug = tables.Column(verbose_name='Slug')
|
slug = tables.Column(verbose_name='Slug')
|
||||||
actions = tables.TemplateColumn(
|
actions = tables.TemplateColumn(
|
||||||
template_code=DEVICEROLE_ACTIONS,
|
template_code=DEVICEROLE_ACTIONS,
|
||||||
attrs={'td': {'class': 'text-right'}},
|
attrs={'td': {'class': 'text-right noprint'}},
|
||||||
verbose_name=''
|
verbose_name=''
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -492,7 +492,7 @@ class PlatformTable(BaseTable):
|
|||||||
)
|
)
|
||||||
actions = tables.TemplateColumn(
|
actions = tables.TemplateColumn(
|
||||||
template_code=PLATFORM_ACTIONS,
|
template_code=PLATFORM_ACTIONS,
|
||||||
attrs={'td': {'class': 'text-right'}},
|
attrs={'td': {'class': 'text-right noprint'}},
|
||||||
verbose_name=''
|
verbose_name=''
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -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'
|
||||||
@ -776,7 +779,7 @@ class VirtualChassisTable(BaseTable):
|
|||||||
member_count = tables.Column(verbose_name='Members')
|
member_count = tables.Column(verbose_name='Members')
|
||||||
actions = tables.TemplateColumn(
|
actions = tables.TemplateColumn(
|
||||||
template_code=VIRTUALCHASSIS_ACTIONS,
|
template_code=VIRTUALCHASSIS_ACTIONS,
|
||||||
attrs={'td': {'class': 'text-right'}},
|
attrs={'td': {'class': 'text-right noprint'}},
|
||||||
verbose_name=''
|
verbose_name=''
|
||||||
)
|
)
|
||||||
|
|
||||||
|
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)
|
@ -1,5 +1,6 @@
|
|||||||
import re
|
import re
|
||||||
|
|
||||||
|
from django.conf import settings
|
||||||
from django.contrib import messages
|
from django.contrib import messages
|
||||||
from django.contrib.auth.mixins import PermissionRequiredMixin
|
from django.contrib.auth.mixins import PermissionRequiredMixin
|
||||||
from django.core.paginator import EmptyPage, PageNotAnInteger
|
from django.core.paginator import EmptyPage, PageNotAnInteger
|
||||||
@ -353,8 +354,9 @@ class RackElevationListView(View):
|
|||||||
total_count = racks.count()
|
total_count = racks.count()
|
||||||
|
|
||||||
# Pagination
|
# Pagination
|
||||||
paginator = EnhancedPaginator(racks, 25)
|
per_page = request.GET.get('per_page', settings.PAGINATE_COUNT)
|
||||||
page_number = request.GET.get('page', 1)
|
page_number = request.GET.get('page', 1)
|
||||||
|
paginator = EnhancedPaginator(racks, per_page)
|
||||||
try:
|
try:
|
||||||
page = paginator.page(page_number)
|
page = paginator.page(page_number)
|
||||||
except PageNotAnInteger:
|
except PageNotAnInteger:
|
||||||
|
@ -22,6 +22,7 @@ class ExtrasConfig(AppConfig):
|
|||||||
port=settings.REDIS_PORT,
|
port=settings.REDIS_PORT,
|
||||||
db=settings.REDIS_DATABASE,
|
db=settings.REDIS_DATABASE,
|
||||||
password=settings.REDIS_PASSWORD or None,
|
password=settings.REDIS_PASSWORD or None,
|
||||||
|
ssl=settings.REDIS_SSL,
|
||||||
)
|
)
|
||||||
rs.ping()
|
rs.ping()
|
||||||
except redis.exceptions.ConnectionError:
|
except redis.exceptions.ConnectionError:
|
||||||
|
@ -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",
|
||||||
|
)
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
@ -29,7 +29,11 @@ def cache_changed_object(instance, **kwargs):
|
|||||||
|
|
||||||
def _record_object_deleted(request, instance, **kwargs):
|
def _record_object_deleted(request, instance, **kwargs):
|
||||||
|
|
||||||
# Record that the object was deleted.
|
# Force resolution of request.user in case it's still a SimpleLazyObject. This seems to happen
|
||||||
|
# occasionally during tests, but haven't been able to determine why.
|
||||||
|
assert request.user.is_authenticated
|
||||||
|
|
||||||
|
# Record that the object was deleted
|
||||||
if hasattr(instance, 'log_change'):
|
if hasattr(instance, 'log_change'):
|
||||||
instance.log_change(request.user, request.id, OBJECTCHANGE_ACTION_DELETE)
|
instance.log_change(request.user, request.id, OBJECTCHANGE_ACTION_DELETE)
|
||||||
|
|
||||||
|
@ -0,0 +1,18 @@
|
|||||||
|
# Generated by Django 2.1.7 on 2019-03-05 18:07
|
||||||
|
|
||||||
|
from django.db import migrations, models
|
||||||
|
|
||||||
|
|
||||||
|
class Migration(migrations.Migration):
|
||||||
|
|
||||||
|
dependencies = [
|
||||||
|
('extras', '0016_exporttemplate_add_cable'),
|
||||||
|
]
|
||||||
|
|
||||||
|
operations = [
|
||||||
|
migrations.AlterField(
|
||||||
|
model_name='exporttemplate',
|
||||||
|
name='mime_type',
|
||||||
|
field=models.CharField(blank=True, max_length=50),
|
||||||
|
),
|
||||||
|
]
|
@ -357,7 +357,7 @@ class ExportTemplate(models.Model):
|
|||||||
)
|
)
|
||||||
template_code = models.TextField()
|
template_code = models.TextField()
|
||||||
mime_type = models.CharField(
|
mime_type = models.CharField(
|
||||||
max_length=15,
|
max_length=50,
|
||||||
blank=True
|
blank=True
|
||||||
)
|
)
|
||||||
file_extension = models.CharField(
|
file_extension = models.CharField(
|
||||||
|
@ -68,7 +68,7 @@ class TagTable(BaseTable):
|
|||||||
)
|
)
|
||||||
actions = tables.TemplateColumn(
|
actions = tables.TemplateColumn(
|
||||||
template_code=TAG_ACTIONS,
|
template_code=TAG_ACTIONS,
|
||||||
attrs={'td': {'class': 'text-right'}},
|
attrs={'td': {'class': 'text-right noprint'}},
|
||||||
verbose_name=''
|
verbose_name=''
|
||||||
)
|
)
|
||||||
|
|
||||||
|
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)
|
@ -349,11 +349,11 @@ class PrefixForm(BootstrapMixin, TenancyForm, CustomFieldForm):
|
|||||||
|
|
||||||
|
|
||||||
class PrefixCSVForm(forms.ModelForm):
|
class PrefixCSVForm(forms.ModelForm):
|
||||||
vrf = forms.ModelChoiceField(
|
vrf = FlexibleModelChoiceField(
|
||||||
queryset=VRF.objects.all(),
|
queryset=VRF.objects.all(),
|
||||||
required=False,
|
|
||||||
to_field_name='rd',
|
to_field_name='rd',
|
||||||
help_text='Route distinguisher of parent VRF',
|
required=False,
|
||||||
|
help_text='Route distinguisher of parent VRF (or {ID})',
|
||||||
error_messages={
|
error_messages={
|
||||||
'invalid_choice': 'VRF not found.',
|
'invalid_choice': 'VRF not found.',
|
||||||
}
|
}
|
||||||
@ -764,11 +764,11 @@ class IPAddressBulkAddForm(BootstrapMixin, TenancyForm, CustomFieldForm):
|
|||||||
|
|
||||||
|
|
||||||
class IPAddressCSVForm(forms.ModelForm):
|
class IPAddressCSVForm(forms.ModelForm):
|
||||||
vrf = forms.ModelChoiceField(
|
vrf = FlexibleModelChoiceField(
|
||||||
queryset=VRF.objects.all(),
|
queryset=VRF.objects.all(),
|
||||||
required=False,
|
|
||||||
to_field_name='rd',
|
to_field_name='rd',
|
||||||
help_text='Route distinguisher of the assigned VRF',
|
required=False,
|
||||||
|
help_text='Route distinguisher of parent VRF (or {ID})',
|
||||||
error_messages={
|
error_messages={
|
||||||
'invalid_choice': 'VRF not found.',
|
'invalid_choice': 'VRF not found.',
|
||||||
}
|
}
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
import netaddr
|
import netaddr
|
||||||
from django.conf import settings
|
from django.conf import settings
|
||||||
from django.contrib.contenttypes.fields import GenericRelation
|
from django.contrib.contenttypes.fields import GenericRelation
|
||||||
from django.core.exceptions import ValidationError
|
from django.core.exceptions import ValidationError, ObjectDoesNotExist
|
||||||
from django.core.validators import MaxValueValidator, MinValueValidator
|
from django.core.validators import MaxValueValidator, MinValueValidator
|
||||||
from django.db import models
|
from django.db import models
|
||||||
from django.db.models import Q
|
from django.db.models import Q
|
||||||
@ -10,8 +10,9 @@ from django.urls import reverse
|
|||||||
from taggit.managers import TaggableManager
|
from taggit.managers import TaggableManager
|
||||||
|
|
||||||
from dcim.models import Interface
|
from dcim.models import Interface
|
||||||
from extras.models import CustomFieldModel
|
from extras.models import CustomFieldModel, ObjectChange
|
||||||
from utilities.models import ChangeLoggedModel
|
from utilities.models import ChangeLoggedModel
|
||||||
|
from utilities.utils import serialize_object
|
||||||
from .constants import *
|
from .constants import *
|
||||||
from .fields import IPNetworkField, IPAddressField
|
from .fields import IPNetworkField, IPAddressField
|
||||||
from .querysets import PrefixQuerySet
|
from .querysets import PrefixQuerySet
|
||||||
@ -629,6 +630,27 @@ class IPAddress(ChangeLoggedModel, CustomFieldModel):
|
|||||||
self.family = self.address.version
|
self.family = self.address.version
|
||||||
super().save(*args, **kwargs)
|
super().save(*args, **kwargs)
|
||||||
|
|
||||||
|
def log_change(self, user, request_id, action):
|
||||||
|
"""
|
||||||
|
Include the connected Interface (if any).
|
||||||
|
"""
|
||||||
|
|
||||||
|
# It's possible that an IPAddress can be deleted _after_ its parent Interface, in which case trying to resolve
|
||||||
|
# the interface will raise DoesNotExist.
|
||||||
|
try:
|
||||||
|
parent_obj = self.interface
|
||||||
|
except ObjectDoesNotExist:
|
||||||
|
parent_obj = None
|
||||||
|
|
||||||
|
ObjectChange(
|
||||||
|
user=user,
|
||||||
|
request_id=request_id,
|
||||||
|
changed_object=self,
|
||||||
|
related_object=parent_obj,
|
||||||
|
action=action,
|
||||||
|
object_data=serialize_object(self)
|
||||||
|
).save()
|
||||||
|
|
||||||
def to_csv(self):
|
def to_csv(self):
|
||||||
|
|
||||||
# Determine if this IP is primary for a Device
|
# Determine if this IP is primary for a Device
|
||||||
|
@ -203,7 +203,7 @@ class RIRTable(BaseTable):
|
|||||||
name = tables.LinkColumn(verbose_name='Name')
|
name = tables.LinkColumn(verbose_name='Name')
|
||||||
is_private = BooleanColumn(verbose_name='Private')
|
is_private = BooleanColumn(verbose_name='Private')
|
||||||
aggregate_count = tables.Column(verbose_name='Aggregates')
|
aggregate_count = tables.Column(verbose_name='Aggregates')
|
||||||
actions = tables.TemplateColumn(template_code=RIR_ACTIONS, attrs={'td': {'class': 'text-right'}}, verbose_name='')
|
actions = tables.TemplateColumn(template_code=RIR_ACTIONS, attrs={'td': {'class': 'text-right noprint'}}, verbose_name='')
|
||||||
|
|
||||||
class Meta(BaseTable.Meta):
|
class Meta(BaseTable.Meta):
|
||||||
model = RIR
|
model = RIR
|
||||||
@ -288,7 +288,7 @@ class RoleTable(BaseTable):
|
|||||||
orderable=False,
|
orderable=False,
|
||||||
verbose_name='VLANs'
|
verbose_name='VLANs'
|
||||||
)
|
)
|
||||||
actions = tables.TemplateColumn(template_code=ROLE_ACTIONS, attrs={'td': {'class': 'text-right'}}, verbose_name='')
|
actions = tables.TemplateColumn(template_code=ROLE_ACTIONS, attrs={'td': {'class': 'text-right noprint'}}, verbose_name='')
|
||||||
|
|
||||||
class Meta(BaseTable.Meta):
|
class Meta(BaseTable.Meta):
|
||||||
model = Role
|
model = Role
|
||||||
@ -392,7 +392,7 @@ class VLANGroupTable(BaseTable):
|
|||||||
site = tables.LinkColumn('dcim:site', args=[Accessor('site.slug')], verbose_name='Site')
|
site = tables.LinkColumn('dcim:site', args=[Accessor('site.slug')], verbose_name='Site')
|
||||||
vlan_count = tables.Column(verbose_name='VLANs')
|
vlan_count = tables.Column(verbose_name='VLANs')
|
||||||
slug = tables.Column(verbose_name='Slug')
|
slug = tables.Column(verbose_name='Slug')
|
||||||
actions = tables.TemplateColumn(template_code=VLANGROUP_ACTIONS, attrs={'td': {'class': 'text-right'}},
|
actions = tables.TemplateColumn(template_code=VLANGROUP_ACTIONS, attrs={'td': {'class': 'text-right noprint'}},
|
||||||
verbose_name='')
|
verbose_name='')
|
||||||
|
|
||||||
class Meta(BaseTable.Meta):
|
class Meta(BaseTable.Meta):
|
||||||
@ -437,7 +437,7 @@ class VLANMemberTable(BaseTable):
|
|||||||
)
|
)
|
||||||
actions = tables.TemplateColumn(
|
actions = tables.TemplateColumn(
|
||||||
template_code=VLAN_MEMBER_ACTIONS,
|
template_code=VLAN_MEMBER_ACTIONS,
|
||||||
attrs={'td': {'class': 'text-right'}},
|
attrs={'td': {'class': 'text-right noprint'}},
|
||||||
verbose_name=''
|
verbose_name=''
|
||||||
)
|
)
|
||||||
|
|
||||||
|
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)
|
@ -132,6 +132,7 @@ REDIS = {
|
|||||||
'PASSWORD': '',
|
'PASSWORD': '',
|
||||||
'DATABASE': 0,
|
'DATABASE': 0,
|
||||||
'DEFAULT_TIMEOUT': 300,
|
'DEFAULT_TIMEOUT': 300,
|
||||||
|
'SSL': False,
|
||||||
}
|
}
|
||||||
|
|
||||||
# The file path where custom reports will be stored. A trailing slash is not needed. Note that the default value of
|
# The file path where custom reports will be stored. A trailing slash is not needed. Note that the default value of
|
||||||
|
@ -22,7 +22,7 @@ except ImportError:
|
|||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
VERSION = '2.5.7-dev'
|
VERSION = '2.5.9-dev'
|
||||||
|
|
||||||
BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
|
BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
|
||||||
|
|
||||||
@ -131,6 +131,7 @@ REDIS_PORT = REDIS.get('PORT', 6379)
|
|||||||
REDIS_PASSWORD = REDIS.get('PASSWORD', '')
|
REDIS_PASSWORD = REDIS.get('PASSWORD', '')
|
||||||
REDIS_DATABASE = REDIS.get('DATABASE', 0)
|
REDIS_DATABASE = REDIS.get('DATABASE', 0)
|
||||||
REDIS_DEFAULT_TIMEOUT = REDIS.get('DEFAULT_TIMEOUT', 300)
|
REDIS_DEFAULT_TIMEOUT = REDIS.get('DEFAULT_TIMEOUT', 300)
|
||||||
|
REDIS_SSL = REDIS.get('SSL', False)
|
||||||
|
|
||||||
# Email
|
# Email
|
||||||
EMAIL_HOST = EMAIL.get('SERVER')
|
EMAIL_HOST = EMAIL.get('SERVER')
|
||||||
@ -197,7 +198,7 @@ ROOT_URLCONF = 'netbox.urls'
|
|||||||
TEMPLATES = [
|
TEMPLATES = [
|
||||||
{
|
{
|
||||||
'BACKEND': 'django.template.backends.django.DjangoTemplates',
|
'BACKEND': 'django.template.backends.django.DjangoTemplates',
|
||||||
'DIRS': [BASE_DIR + '/templates/'],
|
'DIRS': [BASE_DIR + '/templates'],
|
||||||
'APP_DIRS': True,
|
'APP_DIRS': True,
|
||||||
'OPTIONS': {
|
'OPTIONS': {
|
||||||
'context_processors': [
|
'context_processors': [
|
||||||
@ -223,7 +224,7 @@ USE_I18N = True
|
|||||||
USE_TZ = True
|
USE_TZ = True
|
||||||
|
|
||||||
# Static files (CSS, JavaScript, Images)
|
# Static files (CSS, JavaScript, Images)
|
||||||
STATIC_ROOT = BASE_DIR + '/static/'
|
STATIC_ROOT = BASE_DIR + '/static'
|
||||||
STATIC_URL = '/{}static/'.format(BASE_PATH)
|
STATIC_URL = '/{}static/'.format(BASE_PATH)
|
||||||
STATICFILES_DIRS = (
|
STATICFILES_DIRS = (
|
||||||
os.path.join(BASE_DIR, "project-static"),
|
os.path.join(BASE_DIR, "project-static"),
|
||||||
@ -291,6 +292,7 @@ RQ_QUEUES = {
|
|||||||
'DB': REDIS_DATABASE,
|
'DB': REDIS_DATABASE,
|
||||||
'PASSWORD': REDIS_PASSWORD,
|
'PASSWORD': REDIS_PASSWORD,
|
||||||
'DEFAULT_TIMEOUT': REDIS_DEFAULT_TIMEOUT,
|
'DEFAULT_TIMEOUT': REDIS_DEFAULT_TIMEOUT,
|
||||||
|
'SSL': REDIS_SSL,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -315,6 +317,7 @@ SWAGGER_SETTINGS = {
|
|||||||
'utilities.custom_inspectors.IdInFilterInspector',
|
'utilities.custom_inspectors.IdInFilterInspector',
|
||||||
'drf_yasg.inspectors.CoreAPICompatInspector',
|
'drf_yasg.inspectors.CoreAPICompatInspector',
|
||||||
],
|
],
|
||||||
|
'DEFAULT_MODEL_DEPTH': 1,
|
||||||
'DEFAULT_PAGINATOR_INSPECTORS': [
|
'DEFAULT_PAGINATOR_INSPECTORS': [
|
||||||
'utilities.custom_inspectors.NullablePaginatorInspector',
|
'utilities.custom_inspectors.NullablePaginatorInspector',
|
||||||
'drf_yasg.inspectors.DjangoRestResponsePagination',
|
'drf_yasg.inspectors.DjangoRestResponsePagination',
|
||||||
|
@ -267,6 +267,7 @@ class SearchView(View):
|
|||||||
class APIRootView(APIView):
|
class APIRootView(APIView):
|
||||||
_ignore_model_permissions = True
|
_ignore_model_permissions = True
|
||||||
exclude_from_schema = True
|
exclude_from_schema = True
|
||||||
|
swagger_schema = None
|
||||||
|
|
||||||
def get_view_name(self):
|
def get_view_name(self):
|
||||||
return "API Root"
|
return "API Root"
|
||||||
|
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
@ -49,6 +49,19 @@ footer p {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Printer friendly CSS class and various fixes for printing. */
|
||||||
|
@media print {
|
||||||
|
body {
|
||||||
|
padding-top: 0px;
|
||||||
|
}
|
||||||
|
a[href]:after {
|
||||||
|
content: none !important;
|
||||||
|
}
|
||||||
|
.noprint {
|
||||||
|
display: none !important;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/* Collapse the nav menu on displays less than 960px wide */
|
/* Collapse the nav menu on displays less than 960px wide */
|
||||||
@media (max-width: 959px) {
|
@media (max-width: 959px) {
|
||||||
.navbar-header {
|
.navbar-header {
|
||||||
@ -575,4 +588,4 @@ td .progress {
|
|||||||
}
|
}
|
||||||
textarea {
|
textarea {
|
||||||
font-family: Consolas, Lucida Console, monospace;
|
font-family: Consolas, Lucida Console, monospace;
|
||||||
}
|
}
|
||||||
|
@ -90,6 +90,10 @@ $(document).ready(function() {
|
|||||||
// Assign color picker selection classes
|
// Assign color picker selection classes
|
||||||
function colorPickerClassCopy(data, container) {
|
function colorPickerClassCopy(data, container) {
|
||||||
if (data.element) {
|
if (data.element) {
|
||||||
|
// Remove any existing color-selection classes
|
||||||
|
$(container).attr('class', function(i, c) {
|
||||||
|
return c.replace(/(^|\s)color-selection-\S+/g, '');
|
||||||
|
});
|
||||||
$(container).addClass($(data.element).attr("class"));
|
$(container).addClass($(data.element).attr("class"));
|
||||||
}
|
}
|
||||||
return data.text;
|
return data.text;
|
||||||
@ -151,10 +155,13 @@ $(document).ready(function() {
|
|||||||
|
|
||||||
filter_for_elements.each(function(index, filter_for_element) {
|
filter_for_elements.each(function(index, filter_for_element) {
|
||||||
var param_name = $(filter_for_element).attr(attr_name);
|
var param_name = $(filter_for_element).attr(attr_name);
|
||||||
|
var is_nullable = $(filter_for_element).attr("nullable");
|
||||||
var value = $(filter_for_element).val();
|
var value = $(filter_for_element).val();
|
||||||
|
|
||||||
if (param_name && value) {
|
if (param_name && value) {
|
||||||
parameters[param_name] = value;
|
parameters[param_name] = value;
|
||||||
|
} else if (param_name && is_nullable) {
|
||||||
|
parameters[param_name] = "null";
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -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();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -23,7 +23,7 @@ class SecretRoleTable(BaseTable):
|
|||||||
secret_count = tables.Column(verbose_name='Secrets')
|
secret_count = tables.Column(verbose_name='Secrets')
|
||||||
slug = tables.Column(verbose_name='Slug')
|
slug = tables.Column(verbose_name='Slug')
|
||||||
actions = tables.TemplateColumn(
|
actions = tables.TemplateColumn(
|
||||||
template_code=SECRETROLE_ACTIONS, attrs={'td': {'class': 'text-right'}}, verbose_name=''
|
template_code=SECRETROLE_ACTIONS, attrs={'td': {'class': 'text-right noprint'}}, verbose_name=''
|
||||||
)
|
)
|
||||||
|
|
||||||
class Meta(BaseTable.Meta):
|
class Meta(BaseTable.Meta):
|
||||||
|
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)
|
@ -54,7 +54,7 @@
|
|||||||
<div class="col-xs-4 text-center">
|
<div class="col-xs-4 text-center">
|
||||||
<p class="text-muted">{% now 'Y-m-d H:i:s T' %}</p>
|
<p class="text-muted">{% now 'Y-m-d H:i:s T' %}</p>
|
||||||
</div>
|
</div>
|
||||||
<div class="col-xs-4 text-right">
|
<div class="col-xs-4 text-right noprint">
|
||||||
<p class="text-muted">
|
<p class="text-muted">
|
||||||
<i class="fa fa-fw fa-book text-primary"></i> <a href="http://netbox.readthedocs.io/">Docs</a> ·
|
<i class="fa fa-fw fa-book text-primary"></i> <a href="http://netbox.readthedocs.io/">Docs</a> ·
|
||||||
<i class="fa fa-fw fa-cloud text-primary"></i> <a href="{% url 'api_docs' %}">API</a> ·
|
<i class="fa fa-fw fa-cloud text-primary"></i> <a href="{% url 'api_docs' %}">API</a> ·
|
||||||
@ -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/";
|
||||||
|
@ -4,7 +4,7 @@
|
|||||||
{% block title %}{{ circuit }}{% endblock %}
|
{% block title %}{{ circuit }}{% endblock %}
|
||||||
|
|
||||||
{% block header %}
|
{% block header %}
|
||||||
<div class="row">
|
<div class="row noprint">
|
||||||
<div class="col-sm-8 col-md-9">
|
<div class="col-sm-8 col-md-9">
|
||||||
<ol class="breadcrumb">
|
<ol class="breadcrumb">
|
||||||
<li><a href="{% url 'circuits:circuit_list' %}">Circuits</a></li>
|
<li><a href="{% url 'circuits:circuit_list' %}">Circuits</a></li>
|
||||||
@ -25,7 +25,7 @@
|
|||||||
</form>
|
</form>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="pull-right">
|
<div class="pull-right noprint">
|
||||||
{% if perms.circuits.change_circuit %}
|
{% if perms.circuits.change_circuit %}
|
||||||
<a href="{% url 'circuits:circuit_edit' pk=circuit.pk %}" class="btn btn-warning">
|
<a href="{% url 'circuits:circuit_edit' pk=circuit.pk %}" class="btn btn-warning">
|
||||||
<span class="fa fa-pencil" aria-hidden="true"></span>
|
<span class="fa fa-pencil" aria-hidden="true"></span>
|
||||||
|
@ -2,7 +2,7 @@
|
|||||||
{% load buttons %}
|
{% load buttons %}
|
||||||
|
|
||||||
{% block content %}
|
{% block content %}
|
||||||
<div class="pull-right">
|
<div class="pull-right noprint">
|
||||||
{% if perms.circuits.add_circuit %}
|
{% if perms.circuits.add_circuit %}
|
||||||
{% add_button 'circuits:circuit_add' %}
|
{% add_button 'circuits:circuit_add' %}
|
||||||
{% import_button 'circuits:circuit_import' %}
|
{% import_button 'circuits:circuit_import' %}
|
||||||
@ -14,7 +14,7 @@
|
|||||||
<div class="col-md-9">
|
<div class="col-md-9">
|
||||||
{% include 'utilities/obj_table.html' with bulk_edit_url='circuits:circuit_bulk_edit' bulk_delete_url='circuits:circuit_bulk_delete' %}
|
{% include 'utilities/obj_table.html' with bulk_edit_url='circuits:circuit_bulk_edit' bulk_delete_url='circuits:circuit_bulk_delete' %}
|
||||||
</div>
|
</div>
|
||||||
<div class="col-md-3">
|
<div class="col-md-3 noprint">
|
||||||
{% include 'inc/search_panel.html' %}
|
{% include 'inc/search_panel.html' %}
|
||||||
{% include 'inc/tags_panel.html' %}
|
{% include 'inc/tags_panel.html' %}
|
||||||
</div>
|
</div>
|
||||||
|
@ -2,7 +2,7 @@
|
|||||||
{% load buttons %}
|
{% load buttons %}
|
||||||
|
|
||||||
{% block content %}
|
{% block content %}
|
||||||
<div class="pull-right">
|
<div class="pull-right noprint">
|
||||||
{% if perms.circuits.add_circuittype %}
|
{% if perms.circuits.add_circuittype %}
|
||||||
{% add_button 'circuits:circuittype_add' %}
|
{% add_button 'circuits:circuittype_add' %}
|
||||||
{% import_button 'circuits:circuittype_import' %}
|
{% import_button 'circuits:circuittype_import' %}
|
||||||
|
@ -5,7 +5,7 @@
|
|||||||
{% block title %}{{ provider }}{% endblock %}
|
{% block title %}{{ provider }}{% endblock %}
|
||||||
|
|
||||||
{% block header %}
|
{% block header %}
|
||||||
<div class="row">
|
<div class="row noprint">
|
||||||
<div class="col-sm-8 col-md-9">
|
<div class="col-sm-8 col-md-9">
|
||||||
<ol class="breadcrumb">
|
<ol class="breadcrumb">
|
||||||
<li><a href="{% url 'circuits:provider_list' %}">Providers</a></li>
|
<li><a href="{% url 'circuits:provider_list' %}">Providers</a></li>
|
||||||
@ -25,7 +25,7 @@
|
|||||||
</form>
|
</form>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="pull-right">
|
<div class="pull-right noprint">
|
||||||
{% if show_graphs %}
|
{% if show_graphs %}
|
||||||
<button type="button" class="btn btn-primary" data-toggle="modal" data-target="#graphs_modal" data-obj="{{ provider.name }}" data-url="{% url 'circuits-api:provider-graphs' pk=provider.pk %}" title="Show graphs">
|
<button type="button" class="btn btn-primary" data-toggle="modal" data-target="#graphs_modal" data-obj="{{ provider.name }}" data-url="{% url 'circuits-api:provider-graphs' pk=provider.pk %}" title="Show graphs">
|
||||||
<i class="fa fa-signal" aria-hidden="true"></i>
|
<i class="fa fa-signal" aria-hidden="true"></i>
|
||||||
@ -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>
|
||||||
@ -172,7 +172,7 @@
|
|||||||
{% endfor %}
|
{% endfor %}
|
||||||
</table>
|
</table>
|
||||||
{% if perms.circuits.add_circuit %}
|
{% if perms.circuits.add_circuit %}
|
||||||
<div class="panel-footer text-right">
|
<div class="panel-footer text-right noprint">
|
||||||
<a href="{% url 'circuits:circuit_add' %}?provider={{ provider.pk }}" class="btn btn-xs btn-primary">
|
<a href="{% url 'circuits:circuit_add' %}?provider={{ provider.pk }}" class="btn btn-xs btn-primary">
|
||||||
<span class="glyphicon glyphicon-plus" aria-hidden="true"></span> Add circuit
|
<span class="glyphicon glyphicon-plus" aria-hidden="true"></span> Add circuit
|
||||||
</a>
|
</a>
|
||||||
|
@ -2,7 +2,7 @@
|
|||||||
{% load buttons %}
|
{% load buttons %}
|
||||||
|
|
||||||
{% block content %}
|
{% block content %}
|
||||||
<div class="pull-right">
|
<div class="pull-right noprint">
|
||||||
{% if perms.circuits.add_provider %}
|
{% if perms.circuits.add_provider %}
|
||||||
{% add_button 'circuits:provider_add' %}
|
{% add_button 'circuits:provider_add' %}
|
||||||
{% import_button 'circuits:provider_import' %}
|
{% import_button 'circuits:provider_import' %}
|
||||||
@ -14,7 +14,7 @@
|
|||||||
<div class="col-md-9">
|
<div class="col-md-9">
|
||||||
{% include 'utilities/obj_table.html' with bulk_edit_url='circuits:provider_bulk_edit' bulk_delete_url='circuits:provider_bulk_delete' %}
|
{% include 'utilities/obj_table.html' with bulk_edit_url='circuits:provider_bulk_edit' bulk_delete_url='circuits:provider_bulk_delete' %}
|
||||||
</div>
|
</div>
|
||||||
<div class="col-md-3">
|
<div class="col-md-3 noprint">
|
||||||
{% include 'inc/search_panel.html' %}
|
{% include 'inc/search_panel.html' %}
|
||||||
{% include 'inc/tags_panel.html' %}
|
{% include 'inc/tags_panel.html' %}
|
||||||
</div>
|
</div>
|
||||||
|
@ -2,7 +2,7 @@
|
|||||||
{% load helpers %}
|
{% load helpers %}
|
||||||
|
|
||||||
{% block header %}
|
{% block header %}
|
||||||
<div class="row">
|
<div class="row noprint">
|
||||||
<div class="col-md-12">
|
<div class="col-md-12">
|
||||||
<ol class="breadcrumb">
|
<ol class="breadcrumb">
|
||||||
<li><a href="{% url 'dcim:cable_list' %}">Cables</a></li>
|
<li><a href="{% url 'dcim:cable_list' %}">Cables</a></li>
|
||||||
@ -10,7 +10,7 @@
|
|||||||
</ol>
|
</ol>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="pull-right">
|
<div class="pull-right noprint">
|
||||||
{% if perms.dcim.change_cable %}
|
{% if perms.dcim.change_cable %}
|
||||||
<a href="{% url 'dcim:cable_edit' pk=cable.pk %}" class="btn btn-warning">
|
<a href="{% url 'dcim:cable_edit' pk=cable.pk %}" class="btn btn-warning">
|
||||||
<span class="fa fa-pencil" aria-hidden="true"></span> Edit this cable
|
<span class="fa fa-pencil" aria-hidden="true"></span> Edit this cable
|
||||||
|
@ -2,7 +2,7 @@
|
|||||||
{% load buttons %}
|
{% load buttons %}
|
||||||
|
|
||||||
{% block content %}
|
{% block content %}
|
||||||
<div class="pull-right">
|
<div class="pull-right noprint">
|
||||||
{% if perms.dcim.add_cable %}
|
{% if perms.dcim.add_cable %}
|
||||||
{% import_button 'dcim:cable_import' %}
|
{% import_button 'dcim:cable_import' %}
|
||||||
{% endif %}
|
{% endif %}
|
||||||
@ -13,7 +13,7 @@
|
|||||||
<div class="col-md-9">
|
<div class="col-md-9">
|
||||||
{% include 'utilities/obj_table.html' with bulk_edit_url='dcim:cable_bulk_edit' bulk_delete_url='dcim:cable_bulk_delete' %}
|
{% include 'utilities/obj_table.html' with bulk_edit_url='dcim:cable_bulk_edit' bulk_delete_url='dcim:cable_bulk_delete' %}
|
||||||
</div>
|
</div>
|
||||||
<div class="col-md-3">
|
<div class="col-md-3 noprint">
|
||||||
{% include 'inc/search_panel.html' %}
|
{% include 'inc/search_panel.html' %}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
@ -2,7 +2,7 @@
|
|||||||
{% load buttons %}
|
{% load buttons %}
|
||||||
|
|
||||||
{% block content %}
|
{% block content %}
|
||||||
<div class="pull-right">
|
<div class="pull-right noprint">
|
||||||
{% export_button content_type %}
|
{% export_button content_type %}
|
||||||
</div>
|
</div>
|
||||||
<h1>{% block title %}Console Connections{% endblock %}</h1>
|
<h1>{% block title %}Console Connections{% endblock %}</h1>
|
||||||
@ -11,7 +11,7 @@
|
|||||||
{% include 'responsive_table.html' %}
|
{% include 'responsive_table.html' %}
|
||||||
{% include 'inc/paginator.html' with paginator=table.paginator page=table.page %}
|
{% include 'inc/paginator.html' with paginator=table.paginator page=table.page %}
|
||||||
</div>
|
</div>
|
||||||
<div class="col-md-3">
|
<div class="col-md-3 noprint">
|
||||||
{% include 'inc/search_panel.html' %}
|
{% include 'inc/search_panel.html' %}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
@ -5,7 +5,7 @@
|
|||||||
{% block title %}{{ device }}{% endblock %}
|
{% block title %}{{ device }}{% endblock %}
|
||||||
|
|
||||||
{% block header %}
|
{% block header %}
|
||||||
<div class="row">
|
<div class="row noprint">
|
||||||
<div class="col-sm-8 col-md-9">
|
<div class="col-sm-8 col-md-9">
|
||||||
<ol class="breadcrumb">
|
<ol class="breadcrumb">
|
||||||
<li><a href="{% url 'dcim:site' slug=device.site.slug %}">{{ device.site }}</a></li>
|
<li><a href="{% url 'dcim:site' slug=device.site.slug %}">{{ device.site }}</a></li>
|
||||||
@ -33,7 +33,7 @@
|
|||||||
</form>
|
</form>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="pull-right">
|
<div class="pull-right noprint">
|
||||||
{% if perms.dcim.change_device %}
|
{% if perms.dcim.change_device %}
|
||||||
<div class="btn-group">
|
<div class="btn-group">
|
||||||
<button type="button" class="btn btn-primary dropdown-toggle" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false">
|
<button type="button" class="btn btn-primary dropdown-toggle" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false">
|
||||||
@ -199,7 +199,7 @@
|
|||||||
</tr>
|
</tr>
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
</table>
|
</table>
|
||||||
<div class="panel-footer text-right">
|
<div class="panel-footer text-right noprint">
|
||||||
{% if perms.dcim.change_virtualchassis %}
|
{% if perms.dcim.change_virtualchassis %}
|
||||||
<a href="{% url 'dcim:virtualchassis_add_member' pk=device.virtual_chassis.pk %}?site={{ device.site.pk }}&rack={{ device.rack.pk }}&return_url={{ device.get_absolute_url }}" class="btn btn-primary btn-xs">
|
<a href="{% url 'dcim:virtualchassis_add_member' pk=device.virtual_chassis.pk %}?site={{ device.site.pk }}&rack={{ device.rack.pk }}&return_url={{ device.get_absolute_url }}" class="btn btn-primary btn-xs">
|
||||||
<span class="glyphicon glyphicon-plus" aria-hidden="true"></span> Add Member
|
<span class="glyphicon glyphicon-plus" aria-hidden="true"></span> Add Member
|
||||||
@ -317,7 +317,7 @@
|
|||||||
{% endfor %}
|
{% endfor %}
|
||||||
</table>
|
</table>
|
||||||
{% if perms.dcim.add_interface or perms.dcim.add_consoleport or perms.dcim.add_powerport %}
|
{% if perms.dcim.add_interface or perms.dcim.add_consoleport or perms.dcim.add_powerport %}
|
||||||
<div class="panel-footer text-right">
|
<div class="panel-footer text-right noprint">
|
||||||
{% if perms.dcim.add_consoleport %}
|
{% if perms.dcim.add_consoleport %}
|
||||||
<a href="{% url 'dcim:consoleport_add' pk=device.pk %}" class="btn btn-xs btn-primary">
|
<a href="{% url 'dcim:consoleport_add' pk=device.pk %}" class="btn btn-xs btn-primary">
|
||||||
<span class="glyphicon glyphicon-plus" aria-hidden="true"></span> Add console port
|
<span class="glyphicon glyphicon-plus" aria-hidden="true"></span> Add console port
|
||||||
@ -352,7 +352,7 @@
|
|||||||
<form id="secret_form">
|
<form id="secret_form">
|
||||||
{% csrf_token %}
|
{% csrf_token %}
|
||||||
</form>
|
</form>
|
||||||
<div class="panel-footer text-right">
|
<div class="panel-footer text-right noprint">
|
||||||
<a href="{% url 'dcim:device_addsecret' pk=device.pk %}" class="btn btn-xs btn-primary">
|
<a href="{% url 'dcim:device_addsecret' pk=device.pk %}" class="btn btn-xs btn-primary">
|
||||||
<span class="glyphicon glyphicon-plus" aria-hidden="true"></span>
|
<span class="glyphicon glyphicon-plus" aria-hidden="true"></span>
|
||||||
Add secret
|
Add secret
|
||||||
@ -377,7 +377,7 @@
|
|||||||
</div>
|
</div>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
{% if perms.ipam.add_service %}
|
{% if perms.ipam.add_service %}
|
||||||
<div class="panel-footer text-right">
|
<div class="panel-footer text-right noprint">
|
||||||
<a href="{% url 'dcim:device_service_assign' device=device.pk %}" class="btn btn-xs btn-primary">
|
<a href="{% url 'dcim:device_service_assign' device=device.pk %}" class="btn btn-xs btn-primary">
|
||||||
<span class="glyphicon glyphicon-plus" aria-hidden="true"></span> Assign service
|
<span class="glyphicon glyphicon-plus" aria-hidden="true"></span> Assign service
|
||||||
</a>
|
</a>
|
||||||
@ -390,7 +390,7 @@
|
|||||||
</div>
|
</div>
|
||||||
{% include 'inc/image_attachments.html' with images=device.images.all %}
|
{% include 'inc/image_attachments.html' with images=device.images.all %}
|
||||||
{% if perms.extras.add_imageattachment %}
|
{% if perms.extras.add_imageattachment %}
|
||||||
<div class="panel-footer text-right">
|
<div class="panel-footer text-right noprint">
|
||||||
<a href="{% url 'dcim:device_add_image' object_id=device.pk %}" class="btn btn-primary btn-xs">
|
<a href="{% url 'dcim:device_add_image' object_id=device.pk %}" class="btn btn-primary btn-xs">
|
||||||
<span class="glyphicon glyphicon-plus" aria-hidden="true"></span>
|
<span class="glyphicon glyphicon-plus" aria-hidden="true"></span>
|
||||||
Attach an image
|
Attach an image
|
||||||
@ -398,7 +398,7 @@
|
|||||||
</div>
|
</div>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
</div>
|
</div>
|
||||||
<div class="panel panel-default">
|
<div class="panel panel-default noprint">
|
||||||
<div class="panel-heading">
|
<div class="panel-heading">
|
||||||
<strong>Related Devices</strong>
|
<strong>Related Devices</strong>
|
||||||
</div>
|
</div>
|
||||||
@ -459,7 +459,7 @@
|
|||||||
{% endfor %}
|
{% endfor %}
|
||||||
</tbody>
|
</tbody>
|
||||||
</table>
|
</table>
|
||||||
<div class="panel-footer">
|
<div class="panel-footer noprint">
|
||||||
{% if device_bays and perms.dcim.change_devicebay %}
|
{% if device_bays and perms.dcim.change_devicebay %}
|
||||||
<button type="submit" name="_rename" formaction="{% url 'dcim:devicebay_bulk_rename' %}?return_url={{ device.get_absolute_url }}" class="btn btn-warning btn-xs">
|
<button type="submit" name="_rename" formaction="{% url 'dcim:devicebay_bulk_rename' %}?return_url={{ device.get_absolute_url }}" class="btn btn-warning btn-xs">
|
||||||
<span class="glyphicon glyphicon-pencil" aria-hidden="true"></span> Rename
|
<span class="glyphicon glyphicon-pencil" aria-hidden="true"></span> Rename
|
||||||
@ -493,7 +493,7 @@
|
|||||||
<div class="panel panel-default">
|
<div class="panel panel-default">
|
||||||
<div class="panel-heading">
|
<div class="panel-heading">
|
||||||
<strong>Interfaces</strong>
|
<strong>Interfaces</strong>
|
||||||
<div class="pull-right">
|
<div class="pull-right noprint">
|
||||||
<button class="btn btn-default btn-xs toggle-ips" selected="selected">
|
<button class="btn btn-default btn-xs toggle-ips" selected="selected">
|
||||||
<span class="glyphicon glyphicon-check" aria-hidden="true"></span> Show IPs
|
<span class="glyphicon glyphicon-check" aria-hidden="true"></span> Show IPs
|
||||||
</button>
|
</button>
|
||||||
@ -521,7 +521,7 @@
|
|||||||
{% endfor %}
|
{% endfor %}
|
||||||
</tbody>
|
</tbody>
|
||||||
</table>
|
</table>
|
||||||
<div class="panel-footer">
|
<div class="panel-footer noprint">
|
||||||
{% if interfaces and perms.dcim.change_interface %}
|
{% if interfaces and perms.dcim.change_interface %}
|
||||||
<button type="submit" name="_rename" formaction="{% url 'dcim:interface_bulk_rename' %}?return_url={{ device.get_absolute_url }}" class="btn btn-warning btn-xs">
|
<button type="submit" name="_rename" formaction="{% url 'dcim:interface_bulk_rename' %}?return_url={{ device.get_absolute_url }}" class="btn btn-warning btn-xs">
|
||||||
<span class="glyphicon glyphicon-pencil" aria-hidden="true"></span> Rename
|
<span class="glyphicon glyphicon-pencil" aria-hidden="true"></span> Rename
|
||||||
@ -581,7 +581,7 @@
|
|||||||
{% endfor %}
|
{% endfor %}
|
||||||
</tbody>
|
</tbody>
|
||||||
</table>
|
</table>
|
||||||
<div class="panel-footer">
|
<div class="panel-footer noprint">
|
||||||
{% if consoleserverports and perms.dcim.change_consoleport %}
|
{% if consoleserverports and perms.dcim.change_consoleport %}
|
||||||
<button type="submit" name="_rename" formaction="{% url 'dcim:consoleserverport_bulk_rename' %}?return_url={{ device.get_absolute_url }}" class="btn btn-warning btn-xs">
|
<button type="submit" name="_rename" formaction="{% url 'dcim:consoleserverport_bulk_rename' %}?return_url={{ device.get_absolute_url }}" class="btn btn-warning btn-xs">
|
||||||
<span class="glyphicon glyphicon-pencil" aria-hidden="true"></span> Rename
|
<span class="glyphicon glyphicon-pencil" aria-hidden="true"></span> Rename
|
||||||
@ -636,7 +636,7 @@
|
|||||||
{% endfor %}
|
{% endfor %}
|
||||||
</tbody>
|
</tbody>
|
||||||
</table>
|
</table>
|
||||||
<div class="panel-footer">
|
<div class="panel-footer noprint">
|
||||||
{% if poweroutlets and perms.dcim.change_powerport %}
|
{% if poweroutlets and perms.dcim.change_powerport %}
|
||||||
<button type="submit" name="_rename" formaction="{% url 'dcim:poweroutlet_bulk_rename' %}?return_url={{ device.get_absolute_url }}" class="btn btn-warning btn-xs">
|
<button type="submit" name="_rename" formaction="{% url 'dcim:poweroutlet_bulk_rename' %}?return_url={{ device.get_absolute_url }}" class="btn btn-warning btn-xs">
|
||||||
<span class="glyphicon glyphicon-pencil" aria-hidden="true"></span> Rename
|
<span class="glyphicon glyphicon-pencil" aria-hidden="true"></span> Rename
|
||||||
@ -693,7 +693,7 @@
|
|||||||
{% endfor %}
|
{% endfor %}
|
||||||
</tbody>
|
</tbody>
|
||||||
</table>
|
</table>
|
||||||
<div class="panel-footer">
|
<div class="panel-footer noprint">
|
||||||
{% if front_ports and perms.dcim.change_frontport %}
|
{% if front_ports and perms.dcim.change_frontport %}
|
||||||
<button type="submit" name="_rename" formaction="{% url 'dcim:frontport_bulk_rename' %}?return_url={{ device.get_absolute_url }}" class="btn btn-warning btn-xs">
|
<button type="submit" name="_rename" formaction="{% url 'dcim:frontport_bulk_rename' %}?return_url={{ device.get_absolute_url }}" class="btn btn-warning btn-xs">
|
||||||
<span class="glyphicon glyphicon-pencil" aria-hidden="true"></span> Rename
|
<span class="glyphicon glyphicon-pencil" aria-hidden="true"></span> Rename
|
||||||
@ -750,7 +750,7 @@
|
|||||||
{% endfor %}
|
{% endfor %}
|
||||||
</tbody>
|
</tbody>
|
||||||
</table>
|
</table>
|
||||||
<div class="panel-footer">
|
<div class="panel-footer noprint">
|
||||||
{% if rear_ports and perms.dcim.change_rearport %}
|
{% if rear_ports and perms.dcim.change_rearport %}
|
||||||
<button type="submit" name="_rename" formaction="{% url 'dcim:rearport_bulk_rename' %}?return_url={{ device.get_absolute_url }}" class="btn btn-warning btn-xs">
|
<button type="submit" name="_rename" formaction="{% url 'dcim:rearport_bulk_rename' %}?return_url={{ device.get_absolute_url }}" class="btn btn-warning btn-xs">
|
||||||
<span class="glyphicon glyphicon-pencil" aria-hidden="true"></span> Rename
|
<span class="glyphicon glyphicon-pencil" aria-hidden="true"></span> Rename
|
||||||
|
@ -53,7 +53,7 @@
|
|||||||
</tbody>
|
</tbody>
|
||||||
</table>
|
</table>
|
||||||
{% if perms.dcim.add_inventoryitem %}
|
{% if perms.dcim.add_inventoryitem %}
|
||||||
<div class="panel-footer text-right">
|
<div class="panel-footer text-right noprint">
|
||||||
<a href="{% url 'dcim:inventoryitem_add' device=device.pk %}" class="btn btn-primary btn-xs">
|
<a href="{% url 'dcim:inventoryitem_add' device=device.pk %}" class="btn btn-primary btn-xs">
|
||||||
<span class="fa fa-plus" aria-hidden="true"></span> Add Inventory Item
|
<span class="fa fa-plus" aria-hidden="true"></span> Add Inventory Item
|
||||||
</a>
|
</a>
|
||||||
|
@ -2,7 +2,7 @@
|
|||||||
{% load buttons %}
|
{% load buttons %}
|
||||||
|
|
||||||
{% block content %}
|
{% block content %}
|
||||||
<div class="pull-right">
|
<div class="pull-right noprint">
|
||||||
{% if perms.dcim.add_device %}
|
{% if perms.dcim.add_device %}
|
||||||
{% add_button 'dcim:device_add' %}
|
{% add_button 'dcim:device_add' %}
|
||||||
{% import_button 'dcim:device_import' %}
|
{% import_button 'dcim:device_import' %}
|
||||||
@ -14,7 +14,7 @@
|
|||||||
<div class="col-md-9">
|
<div class="col-md-9">
|
||||||
{% include 'dcim/inc/device_table.html' with bulk_edit_url='dcim:device_bulk_edit' bulk_delete_url='dcim:device_bulk_delete' %}
|
{% include 'dcim/inc/device_table.html' with bulk_edit_url='dcim:device_bulk_edit' bulk_delete_url='dcim:device_bulk_delete' %}
|
||||||
</div>
|
</div>
|
||||||
<div class="col-md-3">
|
<div class="col-md-3 noprint">
|
||||||
{% include 'inc/search_panel.html' %}
|
{% include 'inc/search_panel.html' %}
|
||||||
{% include 'inc/tags_panel.html' %}
|
{% include 'inc/tags_panel.html' %}
|
||||||
</div>
|
</div>
|
||||||
|
@ -2,7 +2,7 @@
|
|||||||
{% load buttons %}
|
{% load buttons %}
|
||||||
|
|
||||||
{% block content %}
|
{% block content %}
|
||||||
<div class="pull-right">
|
<div class="pull-right noprint">
|
||||||
{% if perms.dcim.add_devicerole %}
|
{% if perms.dcim.add_devicerole %}
|
||||||
{% add_button 'dcim:devicerole_add' %}
|
{% add_button 'dcim:devicerole_add' %}
|
||||||
{% import_button 'dcim:devicerole_import' %}
|
{% import_button 'dcim:devicerole_import' %}
|
||||||
|
@ -4,7 +4,7 @@
|
|||||||
{% block title %}{{ devicetype.manufacturer }} {{ devicetype.model }}{% endblock %}
|
{% block title %}{{ devicetype.manufacturer }} {{ devicetype.model }}{% endblock %}
|
||||||
|
|
||||||
{% block header %}
|
{% block header %}
|
||||||
<div class="row">
|
<div class="row noprint">
|
||||||
<div class="col-md-12">
|
<div class="col-md-12">
|
||||||
<ol class="breadcrumb">
|
<ol class="breadcrumb">
|
||||||
<li><a href="{% url 'dcim:devicetype_list' %}">Device Types</a></li>
|
<li><a href="{% url 'dcim:devicetype_list' %}">Device Types</a></li>
|
||||||
@ -14,7 +14,7 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
{% if perms.dcim.change_devicetype or perms.dcim.delete_devicetype %}
|
{% if perms.dcim.change_devicetype or perms.dcim.delete_devicetype %}
|
||||||
<div class="pull-right">
|
<div class="pull-right noprint">
|
||||||
{% if perms.dcim.change_devicetype %}
|
{% if perms.dcim.change_devicetype %}
|
||||||
<div class="btn-group">
|
<div class="btn-group">
|
||||||
<button type="button" class="btn btn-primary dropdown-toggle" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false">
|
<button type="button" class="btn btn-primary dropdown-toggle" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false">
|
||||||
|
@ -2,7 +2,7 @@
|
|||||||
{% load buttons %}
|
{% load buttons %}
|
||||||
|
|
||||||
{% block content %}
|
{% block content %}
|
||||||
<div class="pull-right">
|
<div class="pull-right noprint">
|
||||||
{% if perms.dcim.add_devicetype %}
|
{% if perms.dcim.add_devicetype %}
|
||||||
{% add_button 'dcim:devicetype_add' %}
|
{% add_button 'dcim:devicetype_add' %}
|
||||||
{% import_button 'dcim:devicetype_import' %}
|
{% import_button 'dcim:devicetype_import' %}
|
||||||
@ -14,7 +14,7 @@
|
|||||||
<div class="col-md-9">
|
<div class="col-md-9">
|
||||||
{% include 'utilities/obj_table.html' with bulk_edit_url='dcim:devicetype_bulk_edit' bulk_delete_url='dcim:devicetype_bulk_delete' %}
|
{% include 'utilities/obj_table.html' with bulk_edit_url='dcim:devicetype_bulk_edit' bulk_delete_url='dcim:devicetype_bulk_delete' %}
|
||||||
</div>
|
</div>
|
||||||
<div class="col-md-3">
|
<div class="col-md-3 noprint">
|
||||||
{% include 'inc/search_panel.html' %}
|
{% include 'inc/search_panel.html' %}
|
||||||
{% include 'inc/tags_panel.html' %}
|
{% include 'inc/tags_panel.html' %}
|
||||||
</div>
|
</div>
|
||||||
|
@ -29,7 +29,7 @@
|
|||||||
{% endif %}
|
{% endif %}
|
||||||
|
|
||||||
{# Actions #}
|
{# Actions #}
|
||||||
<td class="text-right">
|
<td class="text-right noprint">
|
||||||
{% if cp.cable %}
|
{% if cp.cable %}
|
||||||
{% include 'dcim/inc/cable_toggle_buttons.html' with cable=cp.cable %}
|
{% include 'dcim/inc/cable_toggle_buttons.html' with cable=cp.cable %}
|
||||||
{% elif perms.dcim.add_cable %}
|
{% elif perms.dcim.add_cable %}
|
||||||
|
@ -36,7 +36,7 @@
|
|||||||
{% endif %}
|
{% endif %}
|
||||||
|
|
||||||
{# Actions #}
|
{# Actions #}
|
||||||
<td class="text-right">
|
<td class="text-right noprint">
|
||||||
{% if csp.cable %}
|
{% if csp.cable %}
|
||||||
{% include 'dcim/inc/cable_toggle_buttons.html' with cable=csp.cable %}
|
{% include 'dcim/inc/cable_toggle_buttons.html' with cable=csp.cable %}
|
||||||
{% elif perms.dcim.add_cable %}
|
{% elif perms.dcim.add_cable %}
|
||||||
|
@ -23,7 +23,7 @@
|
|||||||
<span class="text-muted">Vacant</span>
|
<span class="text-muted">Vacant</span>
|
||||||
</td>
|
</td>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
<td class="text-right">
|
<td class="text-right noprint">
|
||||||
{% if perms.dcim.change_devicebay %}
|
{% if perms.dcim.change_devicebay %}
|
||||||
{% if devicebay.installed_device %}
|
{% if devicebay.installed_device %}
|
||||||
<a href="{% url 'dcim:devicebay_depopulate' pk=devicebay.pk %}" class="btn btn-danger btn-xs">
|
<a href="{% url 'dcim:devicebay_depopulate' pk=devicebay.pk %}" class="btn btn-danger btn-xs">
|
||||||
|
@ -6,7 +6,7 @@
|
|||||||
<strong>{{ title }}</strong>
|
<strong>{{ title }}</strong>
|
||||||
</div>
|
</div>
|
||||||
{% include 'responsive_table.html' %}
|
{% include 'responsive_table.html' %}
|
||||||
<div class="panel-footer">
|
<div class="panel-footer noprint">
|
||||||
{% if table.rows %}
|
{% if table.rows %}
|
||||||
{% if edit_url %}
|
{% if edit_url %}
|
||||||
<button type="submit" name="_edit" formaction="{% url edit_url pk=devicetype.pk %}?return_url={{ devicetype.get_absolute_url }}" class="btn btn-xs btn-warning">
|
<button type="submit" name="_edit" formaction="{% url edit_url pk=devicetype.pk %}?return_url={{ devicetype.get_absolute_url }}" class="btn btn-xs btn-warning">
|
||||||
|
@ -54,7 +54,7 @@
|
|||||||
{% endif %}
|
{% endif %}
|
||||||
|
|
||||||
{# Actions #}
|
{# Actions #}
|
||||||
<td class="text-right">
|
<td class="text-right noprint">
|
||||||
{% if frontport.cable %}
|
{% if frontport.cable %}
|
||||||
{% include 'dcim/inc/cable_toggle_buttons.html' with cable=frontport.cable %}
|
{% include 'dcim/inc/cable_toggle_buttons.html' with cable=frontport.cable %}
|
||||||
{% elif perms.dcim.add_cable %}
|
{% elif perms.dcim.add_cable %}
|
||||||
|
@ -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>
|
||||||
@ -134,7 +134,7 @@
|
|||||||
{% endif %}
|
{% endif %}
|
||||||
|
|
||||||
{# Buttons #}
|
{# Buttons #}
|
||||||
<td class="text-right text-nowrap">
|
<td class="text-right text-nowrap noprint">
|
||||||
{% if show_graphs %}
|
{% if show_graphs %}
|
||||||
{% if iface.connected_endpoint %}
|
{% if iface.connected_endpoint %}
|
||||||
<button type="button" class="btn btn-primary btn-xs" data-toggle="modal" data-target="#graphs_modal" data-obj="{{ device.name }} - {{ iface.name }}" data-url="{% url 'dcim-api:interface-graphs' pk=iface.pk %}" title="Show graphs">
|
<button type="button" class="btn btn-primary btn-xs" data-toggle="modal" data-target="#graphs_modal" data-obj="{{ device.name }} - {{ iface.name }}" data-url="{% url 'dcim-api:interface-graphs' pk=iface.pk %}" title="Show graphs">
|
||||||
@ -231,7 +231,7 @@
|
|||||||
</td>
|
</td>
|
||||||
|
|
||||||
{# Buttons #}
|
{# Buttons #}
|
||||||
<td class="text-right text-nowrap">
|
<td class="text-right text-nowrap noprint">
|
||||||
{% if perms.ipam.change_ipaddress %}
|
{% if perms.ipam.change_ipaddress %}
|
||||||
<a href="{% url 'ipam:ipaddress_edit' pk=ip.pk %}?return_url={{ device.get_absolute_url }}" class="btn btn-info btn-xs">
|
<a href="{% url 'ipam:ipaddress_edit' pk=ip.pk %}?return_url={{ device.get_absolute_url }}" class="btn btn-info btn-xs">
|
||||||
<i class="glyphicon glyphicon-pencil" aria-hidden="true" title="Edit IP address"></i>
|
<i class="glyphicon glyphicon-pencil" aria-hidden="true" title="Edit IP address"></i>
|
||||||
|
@ -6,7 +6,7 @@
|
|||||||
<td>{{ item.serial }}</td>
|
<td>{{ item.serial }}</td>
|
||||||
<td>{{ item.asset_tag|default:"" }}</td>
|
<td>{{ item.asset_tag|default:"" }}</td>
|
||||||
<td>{{ item.description }}</td>
|
<td>{{ item.description }}</td>
|
||||||
<td class="text-right">
|
<td class="text-right noprint">
|
||||||
{% if perms.dcim.change_inventoryitem %}
|
{% if perms.dcim.change_inventoryitem %}
|
||||||
<a href="{% url 'dcim:inventoryitem_edit' pk=item.pk %}" class="btn btn-xs btn-warning"><span class="glyphicon glyphicon-pencil" aria-hidden="true"></span></a>
|
<a href="{% url 'dcim:inventoryitem_edit' pk=item.pk %}" class="btn btn-xs btn-warning"><span class="glyphicon glyphicon-pencil" aria-hidden="true"></span></a>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
|
@ -36,7 +36,7 @@
|
|||||||
{% endif %}
|
{% endif %}
|
||||||
|
|
||||||
{# Actions #}
|
{# Actions #}
|
||||||
<td class="text-right">
|
<td class="text-right noprint">
|
||||||
{% if po.cable %}
|
{% if po.cable %}
|
||||||
{% include 'dcim/inc/cable_toggle_buttons.html' with cable=po.cable %}
|
{% include 'dcim/inc/cable_toggle_buttons.html' with cable=po.cable %}
|
||||||
{% elif perms.dcim.add_cable %}
|
{% elif perms.dcim.add_cable %}
|
||||||
|
@ -29,7 +29,7 @@
|
|||||||
{% endif %}
|
{% endif %}
|
||||||
|
|
||||||
{# Actions #}
|
{# Actions #}
|
||||||
<td class="text-right">
|
<td class="text-right noprint">
|
||||||
{% if pp.cable %}
|
{% if pp.cable %}
|
||||||
{% include 'dcim/inc/cable_toggle_buttons.html' with cable=pp.cable %}
|
{% include 'dcim/inc/cable_toggle_buttons.html' with cable=pp.cable %}
|
||||||
{% elif perms.dcim.add_cable %}
|
{% elif perms.dcim.add_cable %}
|
||||||
|
@ -53,7 +53,7 @@
|
|||||||
{% endif %}
|
{% endif %}
|
||||||
|
|
||||||
{# Actions #}
|
{# Actions #}
|
||||||
<td class="text-right">
|
<td class="text-right noprint">
|
||||||
{% if rearport.cable %}
|
{% if rearport.cable %}
|
||||||
{% include 'dcim/inc/cable_toggle_buttons.html' with cable=rearport.cable %}
|
{% include 'dcim/inc/cable_toggle_buttons.html' with cable=rearport.cable %}
|
||||||
{% elif perms.dcim.add_cable %}
|
{% elif perms.dcim.add_cable %}
|
||||||
|
@ -2,7 +2,7 @@
|
|||||||
{% load helpers %}
|
{% load helpers %}
|
||||||
|
|
||||||
{% block header %}
|
{% block header %}
|
||||||
<div class="row">
|
<div class="row noprint">
|
||||||
<div class="col-md-12">
|
<div class="col-md-12">
|
||||||
<ol class="breadcrumb">
|
<ol class="breadcrumb">
|
||||||
{% if interface.device %}
|
{% if interface.device %}
|
||||||
@ -15,7 +15,7 @@
|
|||||||
</ol>
|
</ol>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="pull-right">
|
<div class="pull-right noprint">
|
||||||
{% if perms.dcim.change_interface %}
|
{% if perms.dcim.change_interface %}
|
||||||
<a href="{% if interface.device %}{% url 'dcim:interface_edit' pk=interface.pk %}{% else %}{% url 'virtualization:interface_edit' pk=interface.pk %}{% endif %}" class="btn btn-warning">
|
<a href="{% if interface.device %}{% url 'dcim:interface_edit' pk=interface.pk %}{% else %}{% url 'virtualization:interface_edit' pk=interface.pk %}{% endif %}" class="btn btn-warning">
|
||||||
<span class="fa fa-pencil" aria-hidden="true"></span> Edit this interface
|
<span class="fa fa-pencil" aria-hidden="true"></span> Edit this interface
|
||||||
|
@ -2,7 +2,7 @@
|
|||||||
{% load buttons %}
|
{% load buttons %}
|
||||||
|
|
||||||
{% block content %}
|
{% block content %}
|
||||||
<div class="pull-right">
|
<div class="pull-right noprint">
|
||||||
{% export_button content_type %}
|
{% export_button content_type %}
|
||||||
</div>
|
</div>
|
||||||
<h1>{% block title %}Interface Connections{% endblock %}</h1>
|
<h1>{% block title %}Interface Connections{% endblock %}</h1>
|
||||||
@ -11,7 +11,7 @@
|
|||||||
{% include 'responsive_table.html' %}
|
{% include 'responsive_table.html' %}
|
||||||
{% include 'inc/paginator.html' with paginator=table.paginator page=table.page %}
|
{% include 'inc/paginator.html' with paginator=table.paginator page=table.page %}
|
||||||
</div>
|
</div>
|
||||||
<div class="col-md-3">
|
<div class="col-md-3 noprint">
|
||||||
{% include 'inc/search_panel.html' %}
|
{% include 'inc/search_panel.html' %}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
@ -3,7 +3,7 @@
|
|||||||
{% load helpers %}
|
{% load helpers %}
|
||||||
|
|
||||||
{% block content %}
|
{% block content %}
|
||||||
<div class="pull-right">
|
<div class="pull-right noprint">
|
||||||
{% if perms.dcim.add_devicetype %}
|
{% if perms.dcim.add_devicetype %}
|
||||||
{% import_button 'dcim:inventoryitem_import' %}
|
{% import_button 'dcim:inventoryitem_import' %}
|
||||||
{% endif %}
|
{% endif %}
|
||||||
@ -14,7 +14,7 @@
|
|||||||
<div class="col-md-9">
|
<div class="col-md-9">
|
||||||
{% include 'utilities/obj_table.html' with bulk_edit_url='dcim:inventoryitem_bulk_edit' bulk_delete_url='dcim:inventoryitem_bulk_delete' %}
|
{% include 'utilities/obj_table.html' with bulk_edit_url='dcim:inventoryitem_bulk_edit' bulk_delete_url='dcim:inventoryitem_bulk_delete' %}
|
||||||
</div>
|
</div>
|
||||||
<div class="col-md-3">
|
<div class="col-md-3 noprint">
|
||||||
{% include 'inc/search_panel.html' %}
|
{% include 'inc/search_panel.html' %}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
@ -2,7 +2,7 @@
|
|||||||
{% load buttons %}
|
{% load buttons %}
|
||||||
|
|
||||||
{% block content %}
|
{% block content %}
|
||||||
<div class="pull-right">
|
<div class="pull-right noprint">
|
||||||
{% if perms.dcim.add_manufacturer %}
|
{% if perms.dcim.add_manufacturer %}
|
||||||
{% add_button 'dcim:manufacturer_add' %}
|
{% add_button 'dcim:manufacturer_add' %}
|
||||||
{% import_button 'dcim:manufacturer_import' %}
|
{% import_button 'dcim:manufacturer_import' %}
|
||||||
|
@ -2,7 +2,7 @@
|
|||||||
{% load buttons %}
|
{% load buttons %}
|
||||||
|
|
||||||
{% block content %}
|
{% block content %}
|
||||||
<div class="pull-right">
|
<div class="pull-right noprint">
|
||||||
{% if perms.dcim.add_platform %}
|
{% if perms.dcim.add_platform %}
|
||||||
{% add_button 'dcim:platform_add' %}
|
{% add_button 'dcim:platform_add' %}
|
||||||
{% import_button 'dcim:platform_import' %}
|
{% import_button 'dcim:platform_import' %}
|
||||||
|
@ -2,7 +2,7 @@
|
|||||||
{% load buttons %}
|
{% load buttons %}
|
||||||
|
|
||||||
{% block content %}
|
{% block content %}
|
||||||
<div class="pull-right">
|
<div class="pull-right noprint">
|
||||||
{% export_button content_type %}
|
{% export_button content_type %}
|
||||||
</div>
|
</div>
|
||||||
<h1>{% block title %}Power Connections{% endblock %}</h1>
|
<h1>{% block title %}Power Connections{% endblock %}</h1>
|
||||||
@ -11,7 +11,7 @@
|
|||||||
{% include 'responsive_table.html' %}
|
{% include 'responsive_table.html' %}
|
||||||
{% include 'inc/paginator.html' with paginator=table.paginator page=table.page %}
|
{% include 'inc/paginator.html' with paginator=table.paginator page=table.page %}
|
||||||
</div>
|
</div>
|
||||||
<div class="col-md-3">
|
<div class="col-md-3 noprint">
|
||||||
{% include 'inc/search_panel.html' %}
|
{% include 'inc/search_panel.html' %}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
@ -2,7 +2,7 @@
|
|||||||
{% load helpers %}
|
{% load helpers %}
|
||||||
|
|
||||||
{% block header %}
|
{% block header %}
|
||||||
<div class="row">
|
<div class="row noprint">
|
||||||
<div class="col-sm-8 col-md-9">
|
<div class="col-sm-8 col-md-9">
|
||||||
<ol class="breadcrumb">
|
<ol class="breadcrumb">
|
||||||
<li><a href="{% url 'dcim:rack_list' %}">Racks</a></li>
|
<li><a href="{% url 'dcim:rack_list' %}">Racks</a></li>
|
||||||
@ -23,7 +23,7 @@
|
|||||||
</form>
|
</form>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="pull-right">
|
<div class="pull-right noprint">
|
||||||
<a {% if prev_rack %}href="{% url 'dcim:rack' pk=prev_rack.pk %}"{% else %}disabled="disabled"{% endif %} class="btn btn-primary">
|
<a {% if prev_rack %}href="{% url 'dcim:rack' pk=prev_rack.pk %}"{% else %}disabled="disabled"{% endif %} class="btn btn-primary">
|
||||||
<span class="fa fa-chevron-left" aria-hidden="true"></span> Previous Rack
|
<span class="fa fa-chevron-left" aria-hidden="true"></span> Previous Rack
|
||||||
</a>
|
</a>
|
||||||
@ -223,7 +223,7 @@
|
|||||||
<div class="panel-body text-muted">None</div>
|
<div class="panel-body text-muted">None</div>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
{% if perms.dcim.add_device %}
|
{% if perms.dcim.add_device %}
|
||||||
<div class="panel-footer text-right">
|
<div class="panel-footer text-right noprint">
|
||||||
<a href="{% url 'dcim:device_add' %}?site={{ rack.site.pk }}&rack={{ rack.pk }}" class="btn btn-primary btn-xs">
|
<a href="{% url 'dcim:device_add' %}?site={{ rack.site.pk }}&rack={{ rack.pk }}" class="btn btn-primary btn-xs">
|
||||||
<span class="glyphicon glyphicon-plus" aria-hidden="true"></span>
|
<span class="glyphicon glyphicon-plus" aria-hidden="true"></span>
|
||||||
Add a non-racked device
|
Add a non-racked device
|
||||||
@ -237,7 +237,7 @@
|
|||||||
</div>
|
</div>
|
||||||
{% include 'inc/image_attachments.html' with images=rack.images.all %}
|
{% include 'inc/image_attachments.html' with images=rack.images.all %}
|
||||||
{% if perms.extras.add_imageattachment %}
|
{% if perms.extras.add_imageattachment %}
|
||||||
<div class="panel-footer text-right">
|
<div class="panel-footer text-right noprint">
|
||||||
<a href="{% url 'dcim:rack_add_image' object_id=rack.pk %}" class="btn btn-primary btn-xs">
|
<a href="{% url 'dcim:rack_add_image' object_id=rack.pk %}" class="btn btn-primary btn-xs">
|
||||||
<span class="glyphicon glyphicon-plus" aria-hidden="true"></span>
|
<span class="glyphicon glyphicon-plus" aria-hidden="true"></span>
|
||||||
Attach an image
|
Attach an image
|
||||||
@ -271,7 +271,7 @@
|
|||||||
{{ resv.description }}<br />
|
{{ resv.description }}<br />
|
||||||
<small>{{ resv.user }} · {{ resv.created }}</small>
|
<small>{{ resv.user }} · {{ resv.created }}</small>
|
||||||
</td>
|
</td>
|
||||||
<td class="text-right">
|
<td class="text-right noprint">
|
||||||
{% if perms.dcim.change_rackreservation %}
|
{% if perms.dcim.change_rackreservation %}
|
||||||
<a href="{% url 'dcim:rackreservation_edit' pk=resv.pk %}" class="btn btn-warning btn-xs" title="Edit reservation">
|
<a href="{% url 'dcim:rackreservation_edit' pk=resv.pk %}" class="btn btn-warning btn-xs" title="Edit reservation">
|
||||||
<i class="glyphicon glyphicon-pencil" aria-hidden="true"></i>
|
<i class="glyphicon glyphicon-pencil" aria-hidden="true"></i>
|
||||||
@ -290,7 +290,7 @@
|
|||||||
<div class="panel-body text-muted">None</div>
|
<div class="panel-body text-muted">None</div>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
{% if perms.dcim.add_rackreservation %}
|
{% if perms.dcim.add_rackreservation %}
|
||||||
<div class="panel-footer text-right">
|
<div class="panel-footer text-right noprint">
|
||||||
<a href="{% url 'dcim:rack_add_reservation' rack=rack.pk %}" class="btn btn-primary btn-xs">
|
<a href="{% url 'dcim:rack_add_reservation' rack=rack.pk %}" class="btn btn-primary btn-xs">
|
||||||
<span class="glyphicon glyphicon-plus" aria-hidden="true"></span>
|
<span class="glyphicon glyphicon-plus" aria-hidden="true"></span>
|
||||||
Add a reservation
|
Add a reservation
|
||||||
|
@ -2,7 +2,7 @@
|
|||||||
{% load helpers %}
|
{% load helpers %}
|
||||||
|
|
||||||
{% block content %}
|
{% block content %}
|
||||||
<div class="btn-group pull-right" role="group">
|
<div class="btn-group pull-right noprint" role="group">
|
||||||
<a href="{% url 'dcim:rack_elevation_list' %}{% querystring request face=0 %}" class="btn btn-default{% if request.GET.face != '1' %} active{% endif %}">Front</a>
|
<a href="{% url 'dcim:rack_elevation_list' %}{% querystring request face=0 %}" class="btn btn-default{% if request.GET.face != '1' %} active{% endif %}">Front</a>
|
||||||
<a href="{% url 'dcim:rack_elevation_list' %}{% querystring request face=1 %}" class="btn btn-default{% if request.GET.face == '1' %} active{% endif %}">Rear</a>
|
<a href="{% url 'dcim:rack_elevation_list' %}{% querystring request face=1 %}" class="btn btn-default{% if request.GET.face == '1' %} active{% endif %}">Rear</a>
|
||||||
</div>
|
</div>
|
||||||
@ -38,7 +38,7 @@
|
|||||||
<p>No racks found</p>
|
<p>No racks found</p>
|
||||||
</div>
|
</div>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
<div class="col-md-3">
|
<div class="col-md-3 noprint">
|
||||||
{% include 'inc/search_panel.html' %}
|
{% include 'inc/search_panel.html' %}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
@ -2,7 +2,7 @@
|
|||||||
{% load buttons %}
|
{% load buttons %}
|
||||||
|
|
||||||
{% block content %}
|
{% block content %}
|
||||||
<div class="pull-right">
|
<div class="pull-right noprint">
|
||||||
{% if perms.dcim.add_rack %}
|
{% if perms.dcim.add_rack %}
|
||||||
{% add_button 'dcim:rack_add' %}
|
{% add_button 'dcim:rack_add' %}
|
||||||
{% import_button 'dcim:rack_import' %}
|
{% import_button 'dcim:rack_import' %}
|
||||||
@ -14,7 +14,7 @@
|
|||||||
<div class="col-md-9">
|
<div class="col-md-9">
|
||||||
{% include 'utilities/obj_table.html' with bulk_edit_url='dcim:rack_bulk_edit' bulk_delete_url='dcim:rack_bulk_delete' %}
|
{% include 'utilities/obj_table.html' with bulk_edit_url='dcim:rack_bulk_edit' bulk_delete_url='dcim:rack_bulk_delete' %}
|
||||||
</div>
|
</div>
|
||||||
<div class="col-md-3">
|
<div class="col-md-3 noprint">
|
||||||
{% include 'inc/search_panel.html' %}
|
{% include 'inc/search_panel.html' %}
|
||||||
{% include 'inc/tags_panel.html' %}
|
{% include 'inc/tags_panel.html' %}
|
||||||
</div>
|
</div>
|
||||||
|
@ -2,7 +2,7 @@
|
|||||||
{% load buttons %}
|
{% load buttons %}
|
||||||
|
|
||||||
{% block content %}
|
{% block content %}
|
||||||
<div class="pull-right">
|
<div class="pull-right noprint">
|
||||||
{% if perms.dcim.add_rackgroup %}
|
{% if perms.dcim.add_rackgroup %}
|
||||||
{% add_button 'dcim:rackgroup_add' %}
|
{% add_button 'dcim:rackgroup_add' %}
|
||||||
{% import_button 'dcim:rackgroup_import' %}
|
{% import_button 'dcim:rackgroup_import' %}
|
||||||
@ -14,7 +14,7 @@
|
|||||||
<div class="col-md-9">
|
<div class="col-md-9">
|
||||||
{% include 'utilities/obj_table.html' with bulk_delete_url='dcim:rackgroup_bulk_delete' %}
|
{% include 'utilities/obj_table.html' with bulk_delete_url='dcim:rackgroup_bulk_delete' %}
|
||||||
</div>
|
</div>
|
||||||
<div class="col-md-3">
|
<div class="col-md-3 noprint">
|
||||||
{% include 'inc/search_panel.html' %}
|
{% include 'inc/search_panel.html' %}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
@ -7,7 +7,7 @@
|
|||||||
<div class="col-md-9">
|
<div class="col-md-9">
|
||||||
{% include 'utilities/obj_table.html' with bulk_edit_url='dcim:rackreservation_bulk_edit' bulk_delete_url='dcim:rackreservation_bulk_delete' %}
|
{% include 'utilities/obj_table.html' with bulk_edit_url='dcim:rackreservation_bulk_edit' bulk_delete_url='dcim:rackreservation_bulk_delete' %}
|
||||||
</div>
|
</div>
|
||||||
<div class="col-md-3">
|
<div class="col-md-3 noprint">
|
||||||
{% include 'inc/search_panel.html' %}
|
{% include 'inc/search_panel.html' %}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
@ -2,7 +2,7 @@
|
|||||||
{% load buttons %}
|
{% load buttons %}
|
||||||
|
|
||||||
{% block content %}
|
{% block content %}
|
||||||
<div class="pull-right">
|
<div class="pull-right noprint">
|
||||||
{% if perms.dcim.add_rackrole %}
|
{% if perms.dcim.add_rackrole %}
|
||||||
{% add_button 'dcim:rackrole_add' %}
|
{% add_button 'dcim:rackrole_add' %}
|
||||||
{% import_button 'dcim:rackrole_import' %}
|
{% import_button 'dcim:rackrole_import' %}
|
||||||
|
@ -2,7 +2,7 @@
|
|||||||
{% load buttons %}
|
{% load buttons %}
|
||||||
|
|
||||||
{% block content %}
|
{% block content %}
|
||||||
<div class="pull-right">
|
<div class="pull-right noprint">
|
||||||
{% if perms.dcim.add_region %}
|
{% if perms.dcim.add_region %}
|
||||||
{% add_button 'dcim:region_add' %}
|
{% add_button 'dcim:region_add' %}
|
||||||
{% import_button 'dcim:region_import' %}
|
{% import_button 'dcim:region_import' %}
|
||||||
@ -14,7 +14,7 @@
|
|||||||
<div class="col-md-9">
|
<div class="col-md-9">
|
||||||
{% include 'utilities/obj_table.html' with bulk_delete_url='dcim:region_bulk_delete' %}
|
{% include 'utilities/obj_table.html' with bulk_delete_url='dcim:region_bulk_delete' %}
|
||||||
</div>
|
</div>
|
||||||
<div class="col-md-3">
|
<div class="col-md-3 noprint">
|
||||||
{% include 'inc/search_panel.html' %}
|
{% include 'inc/search_panel.html' %}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
@ -4,7 +4,7 @@
|
|||||||
{% load helpers %}
|
{% load helpers %}
|
||||||
|
|
||||||
{% block header %}
|
{% block header %}
|
||||||
<div class="row">
|
<div class="row noprint">
|
||||||
<div class="col-sm-8 col-md-9">
|
<div class="col-sm-8 col-md-9">
|
||||||
<ol class="breadcrumb">
|
<ol class="breadcrumb">
|
||||||
<li><a href="{% url 'dcim:site_list' %}">Sites</a></li>
|
<li><a href="{% url 'dcim:site_list' %}">Sites</a></li>
|
||||||
@ -30,7 +30,7 @@
|
|||||||
</form>
|
</form>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="pull-right">
|
<div class="pull-right noprint">
|
||||||
{% if show_graphs %}
|
{% if show_graphs %}
|
||||||
<button type="button" class="btn btn-primary" data-toggle="modal" data-target="#graphs_modal" data-obj="{{ site.name }}" data-url="{% url 'dcim-api:site-graphs' pk=site.pk %}" title="Show graphs">
|
<button type="button" class="btn btn-primary" data-toggle="modal" data-target="#graphs_modal" data-obj="{{ site.name }}" data-url="{% url 'dcim-api:site-graphs' pk=site.pk %}" title="Show graphs">
|
||||||
<i class="fa fa-signal" aria-hidden="true"></i>
|
<i class="fa fa-signal" aria-hidden="true"></i>
|
||||||
@ -138,7 +138,7 @@
|
|||||||
<td>Physical Address</td>
|
<td>Physical Address</td>
|
||||||
<td>
|
<td>
|
||||||
{% if site.physical_address %}
|
{% if site.physical_address %}
|
||||||
<div class="pull-right">
|
<div class="pull-right noprint">
|
||||||
<a href="http://maps.google.com/?q={{ site.physical_address|oneline|urlencode }}" target="_blank" class="btn btn-primary btn-xs">
|
<a href="http://maps.google.com/?q={{ site.physical_address|oneline|urlencode }}" target="_blank" class="btn btn-primary btn-xs">
|
||||||
<i class="glyphicon glyphicon-map-marker"></i> Map it
|
<i class="glyphicon glyphicon-map-marker"></i> Map it
|
||||||
</a>
|
</a>
|
||||||
@ -157,7 +157,7 @@
|
|||||||
<td>GPS Coordinates</td>
|
<td>GPS Coordinates</td>
|
||||||
<td>
|
<td>
|
||||||
{% if site.latitude and site.longitude %}
|
{% if site.latitude and site.longitude %}
|
||||||
<div class="pull-right">
|
<div class="pull-right noprint">
|
||||||
<a href="http://maps.google.com/?q={{ site.latitude }},{{ site.longitude }}" target="_blank" class="btn btn-primary btn-xs">
|
<a href="http://maps.google.com/?q={{ site.latitude }},{{ site.longitude }}" target="_blank" class="btn btn-primary btn-xs">
|
||||||
<i class="glyphicon glyphicon-map-marker"></i> Map it
|
<i class="glyphicon glyphicon-map-marker"></i> Map it
|
||||||
</a>
|
</a>
|
||||||
@ -251,7 +251,7 @@
|
|||||||
<tr>
|
<tr>
|
||||||
<td><i class="fa fa-fw fa-folder-o"></i> <a href="{{ rg.get_absolute_url }}">{{ rg }}</a></td>
|
<td><i class="fa fa-fw fa-folder-o"></i> <a href="{{ rg.get_absolute_url }}">{{ rg }}</a></td>
|
||||||
<td>{{ rg.rack_count }}</td>
|
<td>{{ rg.rack_count }}</td>
|
||||||
<td class="text-right">
|
<td class="text-right noprint">
|
||||||
<a href="{% url 'dcim:rack_elevation_list' %}?group_id={{ rg.pk }}" class="btn btn-xs btn-primary" title="View elevations">
|
<a href="{% url 'dcim:rack_elevation_list' %}?group_id={{ rg.pk }}" class="btn btn-xs btn-primary" title="View elevations">
|
||||||
<i class="fa fa-eye"></i>
|
<i class="fa fa-eye"></i>
|
||||||
</a>
|
</a>
|
||||||
@ -271,7 +271,7 @@
|
|||||||
</div>
|
</div>
|
||||||
{% include 'inc/image_attachments.html' with images=site.images.all %}
|
{% include 'inc/image_attachments.html' with images=site.images.all %}
|
||||||
{% if perms.extras.add_imageattachment %}
|
{% if perms.extras.add_imageattachment %}
|
||||||
<div class="panel-footer text-right">
|
<div class="panel-footer text-right noprint">
|
||||||
<a href="{% url 'dcim:site_add_image' object_id=site.pk %}" class="btn btn-primary btn-xs">
|
<a href="{% url 'dcim:site_add_image' object_id=site.pk %}" class="btn btn-primary btn-xs">
|
||||||
<span class="glyphicon glyphicon-plus" aria-hidden="true"></span>
|
<span class="glyphicon glyphicon-plus" aria-hidden="true"></span>
|
||||||
Attach an image
|
Attach an image
|
||||||
|
@ -2,7 +2,7 @@
|
|||||||
{% load buttons %}
|
{% load buttons %}
|
||||||
|
|
||||||
{% block content %}
|
{% block content %}
|
||||||
<div class="pull-right">
|
<div class="pull-right noprint">
|
||||||
{% if perms.dcim.add_site %}
|
{% if perms.dcim.add_site %}
|
||||||
{% add_button 'dcim:site_add' %}
|
{% add_button 'dcim:site_add' %}
|
||||||
{% import_button 'dcim:site_import' %}
|
{% import_button 'dcim:site_import' %}
|
||||||
@ -14,7 +14,7 @@
|
|||||||
<div class="col-md-9">
|
<div class="col-md-9">
|
||||||
{% include 'utilities/obj_table.html' with bulk_edit_url='dcim:site_bulk_edit' %}
|
{% include 'utilities/obj_table.html' with bulk_edit_url='dcim:site_bulk_edit' %}
|
||||||
</div>
|
</div>
|
||||||
<div class="col-md-3">
|
<div class="col-md-3 noprint">
|
||||||
{% include 'inc/search_panel.html' %}
|
{% include 'inc/search_panel.html' %}
|
||||||
{% include 'inc/tags_panel.html' %}
|
{% include 'inc/tags_panel.html' %}
|
||||||
</div>
|
</div>
|
||||||
|
@ -3,7 +3,7 @@
|
|||||||
{% load helpers %}
|
{% load helpers %}
|
||||||
|
|
||||||
{% block content %}
|
{% block content %}
|
||||||
<div class="pull-right">
|
<div class="pull-right noprint">
|
||||||
{% export_button content_type %}
|
{% export_button content_type %}
|
||||||
</div>
|
</div>
|
||||||
<h1>{% block title %}Virtual Chassis{% endblock %}</h1>
|
<h1>{% block title %}Virtual Chassis{% endblock %}</h1>
|
||||||
@ -11,7 +11,7 @@
|
|||||||
<div class="col-md-9">
|
<div class="col-md-9">
|
||||||
{% include 'utilities/obj_table.html' %}
|
{% include 'utilities/obj_table.html' %}
|
||||||
</div>
|
</div>
|
||||||
<div class="col-md-3">
|
<div class="col-md-3 noprint">
|
||||||
{% include 'inc/search_panel.html' %}
|
{% include 'inc/search_panel.html' %}
|
||||||
{% include 'inc/tags_panel.html' %}
|
{% include 'inc/tags_panel.html' %}
|
||||||
</div>
|
</div>
|
||||||
|
@ -2,7 +2,7 @@
|
|||||||
{% load helpers %}
|
{% load helpers %}
|
||||||
|
|
||||||
{% block header %}
|
{% block header %}
|
||||||
<div class="row">
|
<div class="row noprint">
|
||||||
<div class="col-sm-8 col-md-9">
|
<div class="col-sm-8 col-md-9">
|
||||||
<ol class="breadcrumb">
|
<ol class="breadcrumb">
|
||||||
<li><a href="{% url 'extras:configcontext_list' %}">Config Contexts</a></li>
|
<li><a href="{% url 'extras:configcontext_list' %}">Config Contexts</a></li>
|
||||||
@ -22,7 +22,7 @@
|
|||||||
</form>
|
</form>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="pull-right">
|
<div class="pull-right noprint">
|
||||||
{% if perms.extras.change_configcontext %}
|
{% if perms.extras.change_configcontext %}
|
||||||
<a href="{% url 'extras:configcontext_edit' pk=configcontext.pk %}" class="btn btn-warning">
|
<a href="{% url 'extras:configcontext_edit' pk=configcontext.pk %}" class="btn btn-warning">
|
||||||
<span class="glyphicon glyphicon-pencil" aria-hidden="true"></span>
|
<span class="glyphicon glyphicon-pencil" aria-hidden="true"></span>
|
||||||
|
@ -2,7 +2,7 @@
|
|||||||
{% load buttons %}
|
{% load buttons %}
|
||||||
|
|
||||||
{% block content %}
|
{% block content %}
|
||||||
<div class="pull-right">
|
<div class="pull-right noprint">
|
||||||
{% if perms.extras.add_configcontext %}
|
{% if perms.extras.add_configcontext %}
|
||||||
{% add_button 'extras:configcontext_add' %}
|
{% add_button 'extras:configcontext_add' %}
|
||||||
{% endif %}
|
{% endif %}
|
||||||
@ -12,7 +12,7 @@
|
|||||||
<div class="col-md-9">
|
<div class="col-md-9">
|
||||||
{% include 'utilities/obj_table.html' with bulk_edit_url='extras:configcontext_bulk_edit' bulk_delete_url='extras:configcontext_bulk_delete' %}
|
{% include 'utilities/obj_table.html' with bulk_edit_url='extras:configcontext_bulk_edit' bulk_delete_url='extras:configcontext_bulk_delete' %}
|
||||||
</div>
|
</div>
|
||||||
<div class="col-md-3">
|
<div class="col-md-3 noprint">
|
||||||
{% include 'inc/search_panel.html' %}
|
{% include 'inc/search_panel.html' %}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
@ -4,7 +4,7 @@
|
|||||||
{% block title %}{{ objectchange }}{% endblock %}
|
{% block title %}{{ objectchange }}{% endblock %}
|
||||||
|
|
||||||
{% block header %}
|
{% block header %}
|
||||||
<div class="row">
|
<div class="row noprint">
|
||||||
<div class="col-sm-8 col-md-9">
|
<div class="col-sm-8 col-md-9">
|
||||||
<ol class="breadcrumb">
|
<ol class="breadcrumb">
|
||||||
<li><a href="{% url 'extras:objectchange_list' %}">Changelog</a></li>
|
<li><a href="{% url 'extras:objectchange_list' %}">Changelog</a></li>
|
||||||
@ -97,7 +97,7 @@
|
|||||||
</div>
|
</div>
|
||||||
<div class="row">
|
<div class="row">
|
||||||
<div class="col-md-12">
|
<div class="col-md-12">
|
||||||
{% include 'panel_table.html' with table=related_changes_table heading='Related Changes' %}
|
{% include 'panel_table.html' with table=related_changes_table heading='Related Changes' panel_class='noprint' %}
|
||||||
{% if related_changes_count > related_changes_table.rows|length %}
|
{% if related_changes_count > related_changes_table.rows|length %}
|
||||||
<div class="pull-right">
|
<div class="pull-right">
|
||||||
<a href="{% url 'extras:objectchange_list' %}?request_id={{ objectchange.request_id }}" class="btn btn-primary">See all {{ related_changes_count|add:"1" }} changes</a>
|
<a href="{% url 'extras:objectchange_list' %}?request_id={{ objectchange.request_id }}" class="btn btn-primary">See all {{ related_changes_count|add:"1" }} changes</a>
|
||||||
|
@ -2,7 +2,7 @@
|
|||||||
{% load buttons %}
|
{% load buttons %}
|
||||||
|
|
||||||
{% block content %}
|
{% block content %}
|
||||||
<div class="pull-right">
|
<div class="pull-right noprint">
|
||||||
{% export_button content_type %}
|
{% export_button content_type %}
|
||||||
</div>
|
</div>
|
||||||
<h1>{% block title %}Changelog{% endblock %}</h1>
|
<h1>{% block title %}Changelog{% endblock %}</h1>
|
||||||
@ -10,7 +10,7 @@
|
|||||||
<div class="col-md-9">
|
<div class="col-md-9">
|
||||||
{% include 'utilities/obj_table.html' %}
|
{% include 'utilities/obj_table.html' %}
|
||||||
</div>
|
</div>
|
||||||
<div class="col-md-3">
|
<div class="col-md-3 noprint">
|
||||||
{% include 'inc/search_panel.html' %}
|
{% include 'inc/search_panel.html' %}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
@ -4,7 +4,7 @@
|
|||||||
{% block title %}{{ report.name }}{% endblock %}
|
{% block title %}{{ report.name }}{% endblock %}
|
||||||
|
|
||||||
{% block content %}
|
{% block content %}
|
||||||
<div class="row">
|
<div class="row noprint">
|
||||||
<div class="col-md-12">
|
<div class="col-md-12">
|
||||||
<ol class="breadcrumb">
|
<ol class="breadcrumb">
|
||||||
<li><a href="{% url 'extras:report_list' %}">Reports</a></li>
|
<li><a href="{% url 'extras:report_list' %}">Reports</a></li>
|
||||||
@ -14,7 +14,7 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
{% if perms.extras.add_reportresult %}
|
{% if perms.extras.add_reportresult %}
|
||||||
<div class="pull-right">
|
<div class="pull-right noprint">
|
||||||
<form action="{% url 'extras:report_run' name=report.full_name %}" method="post">
|
<form action="{% url 'extras:report_run' name=report.full_name %}" method="post">
|
||||||
{% csrf_token %}
|
{% csrf_token %}
|
||||||
{{ run_form }}
|
{{ run_form }}
|
||||||
|
@ -29,6 +29,12 @@
|
|||||||
Edit this tag
|
Edit this tag
|
||||||
</a>
|
</a>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
|
{% if perms.taggit.delete_tag %}
|
||||||
|
<a href="{% url 'extras:tag_delete' slug=tag.slug %}" class="btn btn-danger">
|
||||||
|
<span class="glyphicon glyphicon-trash" aria-hidden="true"></span>
|
||||||
|
Delete this tag
|
||||||
|
</a>
|
||||||
|
{% endif %}
|
||||||
</div>
|
</div>
|
||||||
<h1>{% block title %}Tag: {{ tag }}{% endblock %}</h1>
|
<h1>{% block title %}Tag: {{ tag }}{% endblock %}</h1>
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
@ -14,7 +14,7 @@
|
|||||||
</td>
|
</td>
|
||||||
<td>{{ attachment.size|filesizeformat }}</td>
|
<td>{{ attachment.size|filesizeformat }}</td>
|
||||||
<td>{{ attachment.created }}</td>
|
<td>{{ attachment.created }}</td>
|
||||||
<td class="text-right">
|
<td class="text-right noprint">
|
||||||
{% if perms.extras.change_imageattachment %}
|
{% if perms.extras.change_imageattachment %}
|
||||||
<a href="{% url 'extras:imageattachment_edit' pk=attachment.pk %}" class="btn btn-warning btn-xs" title="Edit image">
|
<a href="{% url 'extras:imageattachment_edit' pk=attachment.pk %}" class="btn btn-warning btn-xs" title="Edit image">
|
||||||
<i class="glyphicon glyphicon-pencil" aria-hidden="true"></i>
|
<i class="glyphicon glyphicon-pencil" aria-hidden="true"></i>
|
||||||
|
@ -26,7 +26,7 @@
|
|||||||
{% endif %}
|
{% endif %}
|
||||||
</div>
|
</div>
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
<div class="text-right">
|
<div class="text-right noprint">
|
||||||
<button type="submit" class="btn btn-primary">
|
<button type="submit" class="btn btn-primary">
|
||||||
<span class="fa fa-search" aria-hidden="true"></span> Apply
|
<span class="fa fa-search" aria-hidden="true"></span> Apply
|
||||||
</button>
|
</button>
|
||||||
|
@ -2,7 +2,7 @@
|
|||||||
{% load helpers %}
|
{% load helpers %}
|
||||||
|
|
||||||
{% block header %}
|
{% block header %}
|
||||||
<div class="row">
|
<div class="row noprint">
|
||||||
<div class="col-sm-8 col-md-9">
|
<div class="col-sm-8 col-md-9">
|
||||||
<ol class="breadcrumb">
|
<ol class="breadcrumb">
|
||||||
<li><a href="{% url 'ipam:aggregate_list' %}">Aggregates</a></li>
|
<li><a href="{% url 'ipam:aggregate_list' %}">Aggregates</a></li>
|
||||||
@ -23,7 +23,7 @@
|
|||||||
</form>
|
</form>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="pull-right">
|
<div class="pull-right noprint">
|
||||||
{% if perms.ipam.change_aggregate %}
|
{% if perms.ipam.change_aggregate %}
|
||||||
<a href="{% url 'ipam:aggregate_edit' pk=aggregate.pk %}" class="btn btn-warning">
|
<a href="{% url 'ipam:aggregate_edit' pk=aggregate.pk %}" class="btn btn-warning">
|
||||||
<span class="fa fa-pencil" aria-hidden="true"></span>
|
<span class="fa fa-pencil" aria-hidden="true"></span>
|
||||||
|
@ -3,7 +3,7 @@
|
|||||||
{% load humanize %}
|
{% load humanize %}
|
||||||
|
|
||||||
{% block content %}
|
{% block content %}
|
||||||
<div class="pull-right">
|
<div class="pull-right noprint">
|
||||||
{% if perms.ipam.add_aggregate %}
|
{% if perms.ipam.add_aggregate %}
|
||||||
{% add_button 'ipam:aggregate_add' %}
|
{% add_button 'ipam:aggregate_add' %}
|
||||||
{% import_button 'ipam:aggregate_import' %}
|
{% import_button 'ipam:aggregate_import' %}
|
||||||
@ -15,7 +15,7 @@
|
|||||||
<div class="col-md-9">
|
<div class="col-md-9">
|
||||||
{% include 'utilities/obj_table.html' with bulk_edit_url='ipam:aggregate_bulk_edit' bulk_delete_url='ipam:aggregate_bulk_delete' %}
|
{% include 'utilities/obj_table.html' with bulk_edit_url='ipam:aggregate_bulk_edit' bulk_delete_url='ipam:aggregate_bulk_delete' %}
|
||||||
</div>
|
</div>
|
||||||
<div class="col-md-3">
|
<div class="col-md-3 noprint">
|
||||||
{% include 'inc/search_panel.html' %}
|
{% include 'inc/search_panel.html' %}
|
||||||
{% include 'inc/tags_panel.html' %}
|
{% include 'inc/tags_panel.html' %}
|
||||||
<div class="panel panel-default">
|
<div class="panel panel-default">
|
||||||
|
@ -13,7 +13,7 @@
|
|||||||
{% endfor %}
|
{% endfor %}
|
||||||
</td>
|
</td>
|
||||||
<td>{{ service.description }}</td>
|
<td>{{ service.description }}</td>
|
||||||
<td class="text-right">
|
<td class="text-right noprint">
|
||||||
<a href="{% url 'ipam:service_changelog' pk=service.pk %}" class="btn btn-default btn-xs" title="Changelog">
|
<a href="{% url 'ipam:service_changelog' pk=service.pk %}" class="btn btn-default btn-xs" title="Changelog">
|
||||||
<i class="fa fa-history"></i>
|
<i class="fa fa-history"></i>
|
||||||
</a>
|
</a>
|
||||||
|
@ -2,7 +2,7 @@
|
|||||||
{% load helpers %}
|
{% load helpers %}
|
||||||
|
|
||||||
{% block header %}
|
{% block header %}
|
||||||
<div class="row">
|
<div class="row noprint">
|
||||||
<div class="col-sm-8 col-md-9">
|
<div class="col-sm-8 col-md-9">
|
||||||
<ol class="breadcrumb">
|
<ol class="breadcrumb">
|
||||||
<li><a href="{% url 'ipam:ipaddress_list' %}">IP Addresses</a></li>
|
<li><a href="{% url 'ipam:ipaddress_list' %}">IP Addresses</a></li>
|
||||||
@ -25,7 +25,7 @@
|
|||||||
</form>
|
</form>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="pull-right">
|
<div class="pull-right noprint">
|
||||||
{% if perms.ipam.change_ipaddress %}
|
{% if perms.ipam.change_ipaddress %}
|
||||||
<a href="{% url 'ipam:ipaddress_edit' pk=ipaddress.pk %}" class="btn btn-warning">
|
<a href="{% url 'ipam:ipaddress_edit' pk=ipaddress.pk %}" class="btn btn-warning">
|
||||||
<span class="fa fa-pencil" aria-hidden="true"></span>
|
<span class="fa fa-pencil" aria-hidden="true"></span>
|
||||||
@ -150,7 +150,7 @@
|
|||||||
{% if duplicate_ips_table.rows %}
|
{% if duplicate_ips_table.rows %}
|
||||||
{% include 'panel_table.html' with table=duplicate_ips_table heading='Duplicate IP Addresses' panel_class='danger' %}
|
{% include 'panel_table.html' with table=duplicate_ips_table heading='Duplicate IP Addresses' panel_class='danger' %}
|
||||||
{% endif %}
|
{% endif %}
|
||||||
{% include 'panel_table.html' with table=related_ips_table heading='Related IP Addresses' panel_class='default' %}
|
{% include 'panel_table.html' with table=related_ips_table heading='Related IP Addresses' panel_class='default noprint' %}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
@ -2,7 +2,7 @@
|
|||||||
{% load buttons %}
|
{% load buttons %}
|
||||||
|
|
||||||
{% block content %}
|
{% block content %}
|
||||||
<div class="pull-right">
|
<div class="pull-right noprint">
|
||||||
{% if perms.ipam.add_ipaddress %}
|
{% if perms.ipam.add_ipaddress %}
|
||||||
{% add_button 'ipam:ipaddress_add' %}
|
{% add_button 'ipam:ipaddress_add' %}
|
||||||
{% import_button 'ipam:ipaddress_import' %}
|
{% import_button 'ipam:ipaddress_import' %}
|
||||||
@ -14,7 +14,7 @@
|
|||||||
<div class="col-md-9">
|
<div class="col-md-9">
|
||||||
{% include 'utilities/obj_table.html' with bulk_edit_url='ipam:ipaddress_bulk_edit' bulk_delete_url='ipam:ipaddress_bulk_delete' %}
|
{% include 'utilities/obj_table.html' with bulk_edit_url='ipam:ipaddress_bulk_edit' bulk_delete_url='ipam:ipaddress_bulk_delete' %}
|
||||||
</div>
|
</div>
|
||||||
<div class="col-md-3">
|
<div class="col-md-3 noprint">
|
||||||
{% include 'inc/search_panel.html' %}
|
{% include 'inc/search_panel.html' %}
|
||||||
{% include 'inc/tags_panel.html' %}
|
{% include 'inc/tags_panel.html' %}
|
||||||
</div>
|
</div>
|
||||||
|
@ -2,7 +2,7 @@
|
|||||||
{% load helpers %}
|
{% load helpers %}
|
||||||
|
|
||||||
{% block header %}
|
{% block header %}
|
||||||
<div class="row">
|
<div class="row noprint">
|
||||||
<div class="col-sm-8 col-md-9">
|
<div class="col-sm-8 col-md-9">
|
||||||
<ol class="breadcrumb">
|
<ol class="breadcrumb">
|
||||||
<li><a href="{% url 'ipam:prefix_list' %}">Prefixes</a></li>
|
<li><a href="{% url 'ipam:prefix_list' %}">Prefixes</a></li>
|
||||||
@ -25,7 +25,7 @@
|
|||||||
</form>
|
</form>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="pull-right">
|
<div class="pull-right noprint">
|
||||||
{% if perms.ipam.add_prefix and active_tab == 'prefixes' and first_available_prefix %}
|
{% if perms.ipam.add_prefix and active_tab == 'prefixes' and first_available_prefix %}
|
||||||
<a href="{% url 'ipam:prefix_add' %}?prefix={{ first_available_prefix }}&vrf={{ prefix.vrf.pk }}&site={{ prefix.site.pk }}&tenant_group={{ prefix.tenant.group.pk }}&tenant={{ prefix.tenant.pk }}" class="btn btn-success">
|
<a href="{% url 'ipam:prefix_add' %}?prefix={{ first_available_prefix }}&vrf={{ prefix.vrf.pk }}&site={{ prefix.site.pk }}&tenant_group={{ prefix.tenant.group.pk }}&tenant={{ prefix.tenant.pk }}" class="btn btn-success">
|
||||||
<i class="fa fa-plus" aria-hidden="true"></i> Add Child Prefix
|
<i class="fa fa-plus" aria-hidden="true"></i> Add Child Prefix
|
||||||
|
@ -3,7 +3,7 @@
|
|||||||
{% load helpers %}
|
{% load helpers %}
|
||||||
|
|
||||||
{% block content %}
|
{% block content %}
|
||||||
<div class="pull-right">
|
<div class="pull-right noprint">
|
||||||
<div class="btn-group" role="group">
|
<div class="btn-group" role="group">
|
||||||
<a href="{% url 'ipam:prefix_list' %}{% querystring request expand=None page=1 %}" class="btn btn-default{% if not request.GET.expand %} active{% endif %}">Collapse</a>
|
<a href="{% url 'ipam:prefix_list' %}{% querystring request expand=None page=1 %}" class="btn btn-default{% if not request.GET.expand %} active{% endif %}">Collapse</a>
|
||||||
<a href="{% url 'ipam:prefix_list' %}{% querystring request expand='on' page=1 %}" class="btn btn-default{% if request.GET.expand %} active{% endif %}">Expand</a>
|
<a href="{% url 'ipam:prefix_list' %}{% querystring request expand='on' page=1 %}" class="btn btn-default{% if request.GET.expand %} active{% endif %}">Expand</a>
|
||||||
@ -19,7 +19,7 @@
|
|||||||
<div class="col-md-9">
|
<div class="col-md-9">
|
||||||
{% include 'utilities/obj_table.html' with bulk_edit_url='ipam:prefix_bulk_edit' bulk_delete_url='ipam:prefix_bulk_delete' %}
|
{% include 'utilities/obj_table.html' with bulk_edit_url='ipam:prefix_bulk_edit' bulk_delete_url='ipam:prefix_bulk_delete' %}
|
||||||
</div>
|
</div>
|
||||||
<div class="col-md-3">
|
<div class="col-md-3 noprint">
|
||||||
{% include 'inc/search_panel.html' %}
|
{% include 'inc/search_panel.html' %}
|
||||||
{% include 'inc/tags_panel.html' %}
|
{% include 'inc/tags_panel.html' %}
|
||||||
</div>
|
</div>
|
||||||
|
@ -3,7 +3,7 @@
|
|||||||
{% load humanize %}
|
{% load humanize %}
|
||||||
|
|
||||||
{% block content %}
|
{% block content %}
|
||||||
<div class="pull-right">
|
<div class="pull-right noprint">
|
||||||
{% if request.GET.family == '6' %}
|
{% if request.GET.family == '6' %}
|
||||||
<a href="{% url 'ipam:rir_list' %}" class="btn btn-default">
|
<a href="{% url 'ipam:rir_list' %}" class="btn btn-default">
|
||||||
<span class="fa fa-table" aria-hidden="true"></span>
|
<span class="fa fa-table" aria-hidden="true"></span>
|
||||||
@ -29,7 +29,7 @@
|
|||||||
<div class="alert alert-info pull-right"><strong>Note:</strong> Numbers shown indicate /64 prefixes.</div>
|
<div class="alert alert-info pull-right"><strong>Note:</strong> Numbers shown indicate /64 prefixes.</div>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
</div>
|
</div>
|
||||||
<div class="col-md-3">
|
<div class="col-md-3 noprint">
|
||||||
{% include 'inc/search_panel.html' %}
|
{% include 'inc/search_panel.html' %}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
@ -2,7 +2,7 @@
|
|||||||
{% load buttons %}
|
{% load buttons %}
|
||||||
|
|
||||||
{% block content %}
|
{% block content %}
|
||||||
<div class="pull-right">
|
<div class="pull-right noprint">
|
||||||
{% if perms.ipam.add_role %}
|
{% if perms.ipam.add_role %}
|
||||||
{% add_button 'ipam:role_add' %}
|
{% add_button 'ipam:role_add' %}
|
||||||
{% import_button 'ipam:role_import' %}
|
{% import_button 'ipam:role_import' %}
|
||||||
|
@ -2,7 +2,7 @@
|
|||||||
{% load helpers %}
|
{% load helpers %}
|
||||||
|
|
||||||
{% block content %}
|
{% block content %}
|
||||||
<div class="row">
|
<div class="row noprint">
|
||||||
<div class="col-sm-8 col-md-9">
|
<div class="col-sm-8 col-md-9">
|
||||||
<ol class="breadcrumb">
|
<ol class="breadcrumb">
|
||||||
<li><a href="{% url 'ipam:service_list' %}">Services</a></li>
|
<li><a href="{% url 'ipam:service_list' %}">Services</a></li>
|
||||||
|
@ -2,7 +2,7 @@
|
|||||||
{% load buttons %}
|
{% load buttons %}
|
||||||
|
|
||||||
{% block content %}
|
{% block content %}
|
||||||
<div class="pull-right">
|
<div class="pull-right noprint">
|
||||||
{% export_button content_type %}
|
{% export_button content_type %}
|
||||||
</div>
|
</div>
|
||||||
<h1>{% block title %}Services{% endblock %}</h1>
|
<h1>{% block title %}Services{% endblock %}</h1>
|
||||||
@ -10,7 +10,7 @@
|
|||||||
<div class="col-md-9">
|
<div class="col-md-9">
|
||||||
{% include 'utilities/obj_table.html' with bulk_edit_url='ipam:service_bulk_edit' bulk_delete_url='ipam:service_bulk_delete' %}
|
{% include 'utilities/obj_table.html' with bulk_edit_url='ipam:service_bulk_edit' bulk_delete_url='ipam:service_bulk_delete' %}
|
||||||
</div>
|
</div>
|
||||||
<div class="col-md-3">
|
<div class="col-md-3 noprint">
|
||||||
{% include 'inc/search_panel.html' %}
|
{% include 'inc/search_panel.html' %}
|
||||||
{% include 'inc/tags_panel.html' %}
|
{% include 'inc/tags_panel.html' %}
|
||||||
</div>
|
</div>
|
||||||
|
@ -2,7 +2,7 @@
|
|||||||
{% load helpers %}
|
{% load helpers %}
|
||||||
|
|
||||||
{% block header %}
|
{% block header %}
|
||||||
<div class="row">
|
<div class="row noprint">
|
||||||
<div class="col-sm-8 col-md-9">
|
<div class="col-sm-8 col-md-9">
|
||||||
<ol class="breadcrumb">
|
<ol class="breadcrumb">
|
||||||
<li><a href="{% url 'ipam:vlan_list' %}">VLANs</a></li>
|
<li><a href="{% url 'ipam:vlan_list' %}">VLANs</a></li>
|
||||||
@ -28,7 +28,7 @@
|
|||||||
</form>
|
</form>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="pull-right">
|
<div class="pull-right noprint">
|
||||||
{% if perms.ipam.change_vlan %}
|
{% if perms.ipam.change_vlan %}
|
||||||
<a href="{% url 'ipam:vlan_edit' pk=vlan.pk %}" class="btn btn-warning">
|
<a href="{% url 'ipam:vlan_edit' pk=vlan.pk %}" class="btn btn-warning">
|
||||||
<span class="fa fa-pencil" aria-hidden="true"></span>
|
<span class="fa fa-pencil" aria-hidden="true"></span>
|
||||||
@ -143,7 +143,7 @@
|
|||||||
</div>
|
</div>
|
||||||
{% include 'responsive_table.html' with table=prefix_table %}
|
{% include 'responsive_table.html' with table=prefix_table %}
|
||||||
{% if perms.ipam.add_prefix %}
|
{% if perms.ipam.add_prefix %}
|
||||||
<div class="panel-footer text-right">
|
<div class="panel-footer text-right noprint">
|
||||||
<a href="{% url 'ipam:prefix_add' %}?{% if vlan.tenant %}tenant={{ vlan.tenant.pk }}&{% endif %}site={{ vlan.site.pk }}&vlan={{ vlan.pk }}" class="btn btn-primary btn-xs">
|
<a href="{% url 'ipam:prefix_add' %}?{% if vlan.tenant %}tenant={{ vlan.tenant.pk }}&{% endif %}site={{ vlan.site.pk }}&vlan={{ vlan.pk }}" class="btn btn-primary btn-xs">
|
||||||
<span class="glyphicon glyphicon-plus" aria-hidden="true"></span>
|
<span class="glyphicon glyphicon-plus" aria-hidden="true"></span>
|
||||||
Add a prefix
|
Add a prefix
|
||||||
|
@ -2,7 +2,7 @@
|
|||||||
{% load buttons %}
|
{% load buttons %}
|
||||||
|
|
||||||
{% block content %}
|
{% block content %}
|
||||||
<div class="pull-right">
|
<div class="pull-right noprint">
|
||||||
{% if perms.ipam.add_vlan %}
|
{% if perms.ipam.add_vlan %}
|
||||||
{% add_button 'ipam:vlan_add' %}
|
{% add_button 'ipam:vlan_add' %}
|
||||||
{% import_button 'ipam:vlan_import' %}
|
{% import_button 'ipam:vlan_import' %}
|
||||||
@ -14,7 +14,7 @@
|
|||||||
<div class="col-md-9">
|
<div class="col-md-9">
|
||||||
{% include 'utilities/obj_table.html' with bulk_edit_url='ipam:vlan_bulk_edit' bulk_delete_url='ipam:vlan_bulk_delete' %}
|
{% include 'utilities/obj_table.html' with bulk_edit_url='ipam:vlan_bulk_edit' bulk_delete_url='ipam:vlan_bulk_delete' %}
|
||||||
</div>
|
</div>
|
||||||
<div class="col-md-3">
|
<div class="col-md-3 noprint">
|
||||||
{% include 'inc/search_panel.html' %}
|
{% include 'inc/search_panel.html' %}
|
||||||
{% include 'inc/tags_panel.html' %}
|
{% include 'inc/tags_panel.html' %}
|
||||||
</div>
|
</div>
|
||||||
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user