Compare commits

...

22 Commits

Author SHA1 Message Date
Jeremy Stretch
fa7b7288c9 Merge pull request #1398 from digitalocean/develop
Release v2.1.2
2017-08-04 10:54:29 -04:00
Jeremy Stretch
a7d5fb5006 Release v2.1.2 2017-08-04 10:52:06 -04:00
Jeremy Stretch
dadfcd4f81 Tweaked navigation menu styling 2017-08-03 17:49:22 -04:00
Jeremy Stretch
236229ea7e Closes #992: Allow the creation of multiple services per device with the same protocol and port 2017-08-03 15:40:52 -04:00
Jeremy Stretch
6f0f3cc115 Fixes #1390: Fixed IndexError when viewing available IPs within large IPv6 prefixes 2017-08-03 10:09:37 -04:00
Jeremy Stretch
3aa072b437 Fixes #1388: Fixed server error when searching globally for IPs/prefixes (rolled back #1379) 2017-08-03 09:26:28 -04:00
Jeremy Stretch
9cc03aaa9a Merge pull request #1387 from digitalocean/develop
Release v2.1.1
2017-08-02 14:22:30 -04:00
Jeremy Stretch
930b15ae09 Post-release version bump 2017-08-02 14:20:43 -04:00
Jeremy Stretch
1c1f4068cd Release v2.1.1 2017-08-02 14:20:20 -04:00
Jeremy Stretch
b6690e0012 Closes #1368: Render reservations in rack elevations view 2017-08-02 13:33:44 -04:00
Jeremy Stretch
52e7f670f3 Deleted obsolete template 2017-08-02 13:17:34 -04:00
Jeremy Stretch
12d643694f Cleaned up title blocks 2017-08-02 13:15:28 -04:00
Jeremy Stretch
3876a96b67 Tweaked IP address layout 2017-08-02 12:55:21 -04:00
Jeremy Stretch
e50f234ba3 Closes #1379: Allow searching devices by interface MAC address in global search 2017-08-02 11:54:53 -04:00
Jeremy Stretch
fd6df8e52a Fixes #1385: Connected device API endpoint no longer requires authentication if LOGIN_REQUIRED=False 2017-08-02 11:17:57 -04:00
Jeremy Stretch
28225c7cbd Merge pull request #1386 from bdlamprecht/patch-1
Updated ldap.md
2017-08-02 10:56:41 -04:00
bdlamprecht
cab2929c10 Updated ldap.md
Add mapping of `email` field to `mail` value in LDAP.  Also remove duplicate entries of such mapping information.
2017-08-02 08:45:00 -06:00
Jeremy Stretch
52a490bf5d Fixes #461: Display a validation error when attempting to assigning a new child device to a rack face/position 2017-07-28 10:05:51 -04:00
Jeremy Stretch
f2c16fbf3c Closes #893: Allow filtering by null values for NullCharacterFields (e.g. return only unnamed devices) 2017-07-27 09:53:41 -04:00
Jeremy Stretch
65a633f42d Closes #1374: Added NAPALM_ARGS and NAPALM_TIMEOUT configiuration parameters 2017-07-26 11:47:59 -04:00
Jeremy Stretch
d2c6d79589 Closes #1375: Renamed NETBOX_USERNAME and NETBOX_PASSWORD configuration parameters to NAPALM_USERNAME and NAPALM_PASSWORD 2017-07-26 11:24:19 -04:00
Jeremy Stretch
f4a873745f Post-release version bump 2017-07-25 11:26:50 -04:00
70 changed files with 322 additions and 341 deletions

View File

@@ -135,11 +135,53 @@ An API consumer can request an arbitrary number of objects by appending the "lim
---
## NETBOX_USERNAME
## NAPALM_USERNAME
## NETBOX_PASSWORD
## NAPALM_PASSWORD
If provided, NetBox will use these credentials to authenticate against devices when collecting data.
NetBox will use these credentials when authenticating to remote devices via the [NAPALM library](https://napalm-automation.net/), if installed. Both parameters are optional.
Note: If SSH public key authentication has been set up for the system account under which NetBox runs, these parameters are not needed.
---
## NAPALM_ARGS
A dictionary of optional arguments to pass to NAPALM when instantiating a network driver. See the NAPALM documentation for a [complete list of optional arguments](http://napalm.readthedocs.io/en/latest/support/#optional-arguments). An example:
```
NAPALM_ARGS = {
'api_key': '472071a93b60a1bd1fafb401d9f8ef41',
'port': 2222,
}
```
Note: Some platforms (e.g. Cisco IOS) require an argument named `secret` to be passed in addition to the normal password. If desired, you can use the configured `NAPALM_PASSWORD` as the value for this argument:
```
NAPALM_USERNAME = 'username'
NAPALM_PASSWORD = 'MySecretPassword'
NAPALM_ARGS = {
'secret': NAPALM_PASSWORD,
# Include any additional args here
}
```
---
## NAPALM_TIMEOUT
Default: 30 seconds
The amount of time (in seconds) to wait for NAPALM to connect to a device.
---
## NETBOX_USERNAME (Deprecated)
## NETBOX_PASSWORD (Deprecated)
These settings have been deprecated and will be removed in NetBox v2.2. Please use `NAPALM_USERNAME` and `NAPALM_PASSWORD` instead.
---

View File

@@ -72,7 +72,8 @@ AUTH_LDAP_USER_DN_TEMPLATE = "uid=%(user)s,ou=users,dc=example,dc=com"
# You can map user attributes to Django attributes as so.
AUTH_LDAP_USER_ATTR_MAP = {
"first_name": "givenName",
"last_name": "sn"
"last_name": "sn",
"email": "mail"
}
```
@@ -108,12 +109,3 @@ AUTH_LDAP_GROUP_CACHE_TIMEOUT = 3600
* `is_active` - All users must be mapped to at least this group to enable authentication. Without this, users cannot log in.
* `is_staff` - Users mapped to this group are enabled for access to the administration tools; this is the equivalent of checking the "staff status" box on a manually created user. This doesn't grant any specific permissions.
* `is_superuser` - Users mapped to this group will be granted superuser status. Superusers are implicitly granted all permissions.
It is also possible map user attributes to Django attributes:
```python
AUTH_LDAP_USER_ATTR_MAP = {
"first_name": "givenName",
"last_name": "sn",
}
```

View File

@@ -3,7 +3,6 @@ from collections import OrderedDict
from rest_framework.decorators import detail_route
from rest_framework.mixins import ListModelMixin
from rest_framework.permissions import IsAuthenticated
from rest_framework.response import Response
from rest_framework.viewsets import GenericViewSet, ModelViewSet, ViewSet
@@ -21,7 +20,7 @@ from dcim import filters
from extras.api.serializers import RenderedGraphSerializer
from extras.api.views import CustomFieldModelViewSet
from extras.models import Graph, GRAPH_TYPE_INTERFACE, GRAPH_TYPE_SITE
from utilities.api import ServiceUnavailable, WritableSerializerMixin
from utilities.api import IsAuthenticatedOrLoginNotRequired, ServiceUnavailable, WritableSerializerMixin
from .exceptions import MissingFilterException
from . import serializers
@@ -272,15 +271,17 @@ class DeviceViewSet(WritableSerializerMixin, CustomFieldModelViewSet):
ip_address = str(device.primary_ip.address.ip)
d = driver(
hostname=ip_address,
username=settings.NETBOX_USERNAME,
password=settings.NETBOX_PASSWORD
username=settings.NAPALM_USERNAME,
password=settings.NAPALM_PASSWORD,
timeout=settings.NAPALM_TIMEOUT,
optional_args=settings.NAPALM_ARGS
)
try:
d.open()
for method in napalm_methods:
response[method] = getattr(d, method)()
except Exception as e:
raise ServiceUnavailable("Error connecting to the device: {}".format(e))
raise ServiceUnavailable("Error connecting to the device at {}: {}".format(ip_address, e))
d.close()
return Response(response)
@@ -385,7 +386,7 @@ class ConnectedDeviceViewSet(ViewSet):
* `peer-device`: The name of the peer device
* `peer-interface`: The name of the peer interface
"""
permission_classes = [IsAuthenticated]
permission_classes = [IsAuthenticatedOrLoginNotRequired]
def get_view_name(self):
return "Connected Device Locator"

View File

@@ -1,6 +1,7 @@
from __future__ import unicode_literals
import django_filters
from netaddr import EUI
from netaddr.core import AddrFormatError
from django.contrib.auth.models import User
@@ -8,7 +9,7 @@ from django.db.models import Q
from extras.filters import CustomFieldFilterSet
from tenancy.models import Tenant
from utilities.filters import NullableModelMultipleChoiceFilter, NumericInFilter
from utilities.filters import NullableCharFieldFilter, NullableModelMultipleChoiceFilter, NumericInFilter
from .models import (
ConsolePort, ConsolePortTemplate, ConsoleServerPort, ConsoleServerPortTemplate, Device, DeviceBay,
DeviceBayTemplate, DeviceRole, DeviceType, STATUS_CHOICES, IFACE_FF_LAG, Interface, InterfaceConnection,
@@ -113,6 +114,7 @@ class RackFilter(CustomFieldFilterSet, django_filters.FilterSet):
method='search',
label='Search',
)
facility_id = NullableCharFieldFilter()
site_id = django_filters.ModelMultipleChoiceFilter(
queryset=Site.objects.all(),
label='Site (ID)',
@@ -156,7 +158,7 @@ class RackFilter(CustomFieldFilterSet, django_filters.FilterSet):
class Meta:
model = Rack
fields = ['facility_id', 'type', 'width', 'u_height', 'desc_units']
fields = ['type', 'width', 'u_height', 'desc_units']
def search(self, queryset, name, value):
if not value.strip():
@@ -383,6 +385,8 @@ class DeviceFilter(CustomFieldFilterSet, django_filters.FilterSet):
to_field_name='slug',
label='Platform (slug)',
)
name = NullableCharFieldFilter()
asset_tag = NullableCharFieldFilter()
site_id = django_filters.ModelMultipleChoiceFilter(
queryset=Site.objects.all(),
label='Site (ID)',
@@ -439,7 +443,7 @@ class DeviceFilter(CustomFieldFilterSet, django_filters.FilterSet):
class Meta:
model = Device
fields = ['name', 'serial', 'asset_tag']
fields = ['serial']
def search(self, queryset, name, value):
if not value.strip():
@@ -457,7 +461,8 @@ class DeviceFilter(CustomFieldFilterSet, django_filters.FilterSet):
if not value:
return queryset
try:
return queryset.filter(interfaces__mac_address=value).distinct()
mac = EUI(value.strip())
return queryset.filter(interfaces__mac_address=mac).distinct()
except AddrFormatError:
return queryset.none()
@@ -569,7 +574,8 @@ class InterfaceFilter(django_filters.FilterSet):
if not value:
return queryset
try:
return queryset.filter(mac_address=value)
mac = EUI(value.strip())
return queryset.filter(mac_address=mac)
except AddrFormatError:
return queryset.none()
@@ -596,10 +602,11 @@ class InventoryItemFilter(DeviceComponentFilterSet):
to_field_name='slug',
label='Manufacturer (slug)',
)
asset_tag = NullableCharFieldFilter()
class Meta:
model = InventoryItem
fields = ['name', 'part_id', 'serial', 'asset_tag', 'discovered']
fields = ['name', 'part_id', 'serial', 'discovered']
class ConsoleConnectionFilter(django_filters.FilterSet):

View File

@@ -357,6 +357,16 @@ class Rack(CreatedUpdatedModel, CustomFieldModel):
return list(reversed(available_units))
def get_reserved_units(self):
"""
Return a dictionary mapping all reserved units within the rack to their reservation.
"""
reserved_units = {}
for r in self.reservations.all():
for u in r.units:
reserved_units[u] = r
return reserved_units
def get_0u_devices(self):
return self.devices.filter(position=0)

View File

@@ -417,15 +417,10 @@ class RackView(View):
prev_rack = Rack.objects.filter(site=rack.site, name__lt=rack.name).order_by('-name').first()
reservations = RackReservation.objects.filter(rack=rack)
reserved_units = {}
for r in reservations:
for u in r.units:
reserved_units[u] = r
return render(request, 'dcim/rack.html', {
'rack': rack,
'reservations': reservations,
'reserved_units': reserved_units,
'nonracked_devices': nonracked_devices,
'next_rack': next_rack,
'prev_rack': prev_rack,

View File

@@ -13,8 +13,8 @@ from dcim.models import Device, InventoryItem, Site, STATUS_ACTIVE
class Command(BaseCommand):
help = "Update inventory information for specified devices"
username = settings.NETBOX_USERNAME
password = settings.NETBOX_PASSWORD
username = settings.NAPALM_USERNAME
password = settings.NAPALM_PASSWORD
def add_arguments(self, parser):
parser.add_argument('-u', '--username', dest='username', help="Specify the username to use")

View File

@@ -115,7 +115,11 @@ class PrefixViewSet(WritableSerializerMixin, CustomFieldModelViewSet):
limit = min(limit, settings.MAX_PAGE_SIZE)
# Calculate available IPs within the prefix
ip_list = list(prefix.get_available_ips())[:limit]
ip_list = []
for index, ip in enumerate(prefix.get_available_ips(), start=1):
ip_list.append(ip)
if index == limit:
break
serializer = serializers.AvailableIPSerializer(ip_list, many=True, context={
'request': request,
'prefix': prefix.prefix,

View File

@@ -0,0 +1,19 @@
# -*- coding: utf-8 -*-
# Generated by Django 1.11.3 on 2017-08-03 19:37
from __future__ import unicode_literals
from django.db import migrations
class Migration(migrations.Migration):
dependencies = [
('ipam', '0017_ipaddress_roles'),
]
operations = [
migrations.AlterUniqueTogether(
name='service',
unique_together=set([]),
),
]

View File

@@ -600,7 +600,6 @@ class Service(CreatedUpdatedModel):
class Meta:
ordering = ['device', 'protocol', 'port']
unique_together = ['device', 'protocol', 'port']
def __str__(self):
return '{} ({}/{})'.format(self.name, self.port, self.get_protocol_display())

View File

@@ -60,8 +60,8 @@ BASE_PATH = os.environ.get('BASE_PATH', '')
MAINTENANCE_MODE = os.environ.get('MAINTENANCE_MODE', False)
# Credentials that NetBox will use to access live devices.
NETBOX_USERNAME = os.environ.get('NETBOX_USERNAME', '')
NETBOX_PASSWORD = os.environ.get('NETBOX_PASSWORD', '')
NAPALM_USERNAME = os.environ.get('NAPALM_USERNAME', '')
NAPALM_PASSWORD = os.environ.get('NAPALM_PASSWORD', '')
# Determine how many objects to display per page within a list. (Default: 50)
PAGINATE_COUNT = os.environ.get('PAGINATE_COUNT', 50)

View File

@@ -93,9 +93,16 @@ MAINTENANCE_MODE = False
# all objects by specifying "?limit=0".
MAX_PAGE_SIZE = 1000
# Credentials that NetBox will use to access live devices (future use).
NETBOX_USERNAME = ''
NETBOX_PASSWORD = ''
# Credentials that NetBox will uses to authenticate to devices when connecting via NAPALM.
NAPALM_USERNAME = ''
NAPALM_PASSWORD = ''
# NAPALM timeout (in seconds). (Default: 30)
NAPALM_TIMEOUT = 30
# NAPALM optional arguments (see http://napalm.readthedocs.io/en/latest/support/#optional-arguments). Arguments must
# be provided as a dictionary.
NAPALM_ARGS = {}
# Determine how many objects to display per page within a list. (Default: 50)
PAGINATE_COUNT = 50

View File

@@ -13,7 +13,7 @@ except ImportError:
)
VERSION = '2.1.0'
VERSION = '2.1.2'
# Import required configuration parameters
ALLOWED_HOSTS = DATABASE = SECRET_KEY = None
@@ -46,8 +46,12 @@ MAINTENANCE_MODE = getattr(configuration, 'MAINTENANCE_MODE', False)
MAX_PAGE_SIZE = getattr(configuration, 'MAX_PAGE_SIZE', 1000)
PAGINATE_COUNT = getattr(configuration, 'PAGINATE_COUNT', 50)
PREFER_IPV4 = getattr(configuration, 'PREFER_IPV4', False)
NETBOX_USERNAME = getattr(configuration, 'NETBOX_USERNAME', '')
NETBOX_PASSWORD = getattr(configuration, 'NETBOX_PASSWORD', '')
NAPALM_USERNAME = getattr(configuration, 'NAPALM_USERNAME', '')
NAPALM_PASSWORD = getattr(configuration, 'NAPALM_PASSWORD', '')
NAPALM_TIMEOUT = getattr(configuration, 'NAPALM_TIMEOUT', 30)
NAPALM_ARGS = getattr(configuration, 'NAPALM_ARGS', {})
NETBOX_USERNAME = getattr(configuration, 'NETBOX_USERNAME', '') # Deprecated
NETBOX_PASSWORD = getattr(configuration, 'NETBOX_PASSWORD', '') # Deprecated
SHORT_DATE_FORMAT = getattr(configuration, 'SHORT_DATE_FORMAT', 'Y-m-d')
SHORT_DATETIME_FORMAT = getattr(configuration, 'SHORT_DATETIME_FORMAT', 'Y-m-d H:i')
SHORT_TIME_FORMAT = getattr(configuration, 'SHORT_TIME_FORMAT', 'H:i:s')
@@ -56,6 +60,19 @@ TIME_ZONE = getattr(configuration, 'TIME_ZONE', 'UTC')
CSRF_TRUSTED_ORIGINS = ALLOWED_HOSTS
# Check for deprecated configuration parameters
config_logger = logging.getLogger('configuration')
config_logger.addHandler(logging.StreamHandler())
config_logger.setLevel(logging.WARNING)
if NETBOX_USERNAME:
config_logger.warning('NETBOX_USERNAME is deprecated and will be removed in v2.2. Please use NAPALM_USERNAME instead.')
if not NAPALM_USERNAME:
NAPALM_USERNAME = NETBOX_USERNAME
if NETBOX_PASSWORD:
config_logger.warning('NETBOX_PASSWORD is deprecated and will be removed in v2.2. Please use NAPALM_PASSWORD instead.')
if not NAPALM_PASSWORD:
NAPALM_PASSWORD = NETBOX_PASSWORD
# Attempt to import LDAP configuration if it has been defined
LDAP_IGNORE_CERT_ERRORS = False
try:
@@ -78,9 +95,9 @@ if LDAP_CONFIGURED:
if LDAP_IGNORE_CERT_ERRORS:
ldap.set_option(ldap.OPT_X_TLS_REQUIRE_CERT, ldap.OPT_X_TLS_NEVER)
# Enable logging for django_auth_ldap
logger = logging.getLogger('django_auth_ldap')
logger.addHandler(logging.StreamHandler())
logger.setLevel(logging.DEBUG)
ldap_logger = logging.getLogger('django_auth_ldap')
ldap_logger.addHandler(logging.StreamHandler())
ldap_logger.setLevel(logging.DEBUG)
except ImportError:
raise ImproperlyConfigured(
"LDAP authentication has been configured, but django-auth-ldap is not installed. You can remove "

View File

@@ -81,6 +81,11 @@ footer p {
}
}
/* Navigation menu */
li.subnav > a {
padding-left: 30px;
}
/* Forms */
label {
font-weight: normal;

View File

@@ -31,197 +31,197 @@
<li class="dropdown{% if request.path|contains:'/dcim/sites/,/dcim/regions/,/tenancy/' %} active{% endif %}">
<a href="#" class="dropdown-toggle" data-toggle="dropdown" role="button" aria-haspopup="true" aria-expanded="false">Organization <span class="caret"></span></a>
<ul class="dropdown-menu">
<li><a href="{% url 'dcim:site_list' %}"><i class="fa fa-search" aria-hidden="true"></i> Sites</a></li>
<li><a href="{% url 'dcim:site_list' %}"><strong>Sites</strong></a></li>
{% if perms.dcim.add_site %}
<li><a href="{% url 'dcim:site_add' %}"><i class="fa fa-plus" aria-hidden="true"></i> Add a Site</a></li>
<li><a href="{% url 'dcim:site_import' %}"><i class="fa fa-download" aria-hidden="true"></i> Import Sites</a></li>
<li class="subnav"><a href="{% url 'dcim:site_add' %}"><i class="fa fa-plus"></i> Add a Site</a></li>
<li class="subnav"><a href="{% url 'dcim:site_import' %}"><i class="fa fa-download"></i> Import Sites</a></li>
{% endif %}
<li class="divider"></li>
<li><a href="{% url 'dcim:region_list' %}"><i class="fa fa-search" aria-hidden="true"></i> Regions</a></li>
<li><a href="{% url 'dcim:region_list' %}"><strong>Regions</strong></a></li>
{% if perms.dcim.add_region %}
<li><a href="{% url 'dcim:region_add' %}"><i class="fa fa-plus" aria-hidden="true"></i> Add a Region</a></li>
<li class="subnav"><a href="{% url 'dcim:region_add' %}"><i class="fa fa-plus"></i> Add a Region</a></li>
{% endif %}
<li class="divider"></li>
<li><a href="{% url 'tenancy:tenant_list' %}"><i class="fa fa-search" aria-hidden="true"></i> Tenants</a></li>
<li><a href="{% url 'tenancy:tenant_list' %}"><strong>Tenants</strong></a></li>
{% if perms.tenancy.add_tenant %}
<li><a href="{% url 'tenancy:tenant_add' %}"><i class="fa fa-plus" aria-hidden="true"></i> Add a Tenant</a></li>
<li><a href="{% url 'tenancy:tenant_import' %}"><i class="fa fa-download" aria-hidden="true"></i> Import Tenants</a></li>
<li class="subnav"><a href="{% url 'tenancy:tenant_add' %}"><i class="fa fa-plus"></i> Add a Tenant</a></li>
<li class="subnav"><a href="{% url 'tenancy:tenant_import' %}"><i class="fa fa-download"></i> Import Tenants</a></li>
{% endif %}
<li class="divider"></li>
<li><a href="{% url 'tenancy:tenantgroup_list' %}"><i class="fa fa-search" aria-hidden="true"></i> Tenant Groups</a></li>
<li><a href="{% url 'tenancy:tenantgroup_list' %}"><strong>Tenant Groups</strong></a></li>
{% if perms.tenancy.add_tenantgroup %}
<li><a href="{% url 'tenancy:tenantgroup_add' %}"><i class="fa fa-plus" aria-hidden="true"></i> Add a Tenant Group</a></li>
<li class="subnav"><a href="{% url 'tenancy:tenantgroup_add' %}"><i class="fa fa-plus"></i> Add a Tenant Group</a></li>
{% endif %}
</ul>
</li>
<li class="dropdown{% if request.path|contains:'/dcim/rack' %} active{% endif %}">
<a href="#" class="dropdown-toggle" data-toggle="dropdown" role="button" aria-haspopup="true" aria-expanded="false">Racks <span class="caret"></span></a>
<ul class="dropdown-menu">
<li><a href="{% url 'dcim:rack_list' %}"><i class="fa fa-search" aria-hidden="true"></i> Racks</a></li>
<li><a href="{% url 'dcim:rack_elevation_list' %}"><i class="fa fa-bars" aria-hidden="true"></i> Rack Elevations</a></li>
<li><a href="{% url 'dcim:rack_list' %}"><strong>Racks</strong></a></li>
{% if perms.dcim.add_rack %}
<li><a href="{% url 'dcim:rack_add' %}"><i class="fa fa-plus" aria-hidden="true"></i> Add a Rack</a></li>
<li><a href="{% url 'dcim:rack_import' %}"><i class="fa fa-download" aria-hidden="true"></i> Import Racks</a></li>
<li class="subnav"><a href="{% url 'dcim:rack_add' %}"><i class="fa fa-plus"></i> Add a Rack</a></li>
<li class="subnav"><a href="{% url 'dcim:rack_import' %}"><i class="fa fa-download"></i> Import Racks</a></li>
{% endif %}
<li><a href="{% url 'dcim:rack_elevation_list' %}"><strong>Rack Elevations</strong></a></li>
<li class="divider"></li>
<li><a href="{% url 'dcim:rackgroup_list' %}"><i class="fa fa-search" aria-hidden="true"></i> Rack Groups</a></li>
<li><a href="{% url 'dcim:rackgroup_list' %}"><strong>Rack Groups</strong></a></li>
{% if perms.dcim.add_rackgroup %}
<li><a href="{% url 'dcim:rackgroup_add' %}"><i class="fa fa-plus" aria-hidden="true"></i> Add a Rack Group</a></li>
<li class="subnav"><a href="{% url 'dcim:rackgroup_add' %}"><i class="fa fa-plus"></i> Add a Rack Group</a></li>
{% endif %}
<li class="divider"></li>
<li><a href="{% url 'dcim:rackrole_list' %}"><i class="fa fa-search" aria-hidden="true"></i> Rack Roles</a></li>
<li><a href="{% url 'dcim:rackrole_list' %}"><strong>Rack Roles</strong></a></li>
{% if perms.dcim.add_rackrole %}
<li><a href="{% url 'dcim:rackrole_add' %}"><i class="fa fa-plus" aria-hidden="true"></i> Add a Rack Role</a></li>
<li class="subnav"><a href="{% url 'dcim:rackrole_add' %}"><i class="fa fa-plus"></i> Add a Rack Role</a></li>
{% endif %}
<li class="divider"></li>
<li><a href="{% url 'dcim:rackreservation_list' %}"><i class="fa fa-search" aria-hidden="true"></i> Rack Reservations</a></li>
<li><a href="{% url 'dcim:rackreservation_list' %}"><strong>Rack Reservations</strong></a></li>
</ul>
</li>
<li class="dropdown{% if request.path|contains:'/dcim/device,/dcim/manufacturers/,/dcim/platforms/' %} active{% endif %}">
<a href="#" class="dropdown-toggle" data-toggle="dropdown" role="button" aria-haspopup="true" aria-expanded="false">Devices <span class="caret"></span></a>
<ul class="dropdown-menu">
<li><a href="{% url 'dcim:device_list' %}"><i class="fa fa-search" aria-hidden="true"></i> Devices</a></li>
<li><a href="{% url 'dcim:device_list' %}"><strong>Devices</strong></a></li>
{% if perms.dcim.add_device %}
<li><a href="{% url 'dcim:device_add' %}"><i class="fa fa-plus" aria-hidden="true"></i> Add a Device</a></li>
<li><a href="{% url 'dcim:device_import' %}"><i class="fa fa-download" aria-hidden="true"></i> Import Devices</a></li>
<li class="subnav"><a href="{% url 'dcim:device_add' %}"><i class="fa fa-plus"></i> Add a Device</a></li>
<li class="subnav"><a href="{% url 'dcim:device_import' %}"><i class="fa fa-download"></i> Import Devices</a></li>
{% endif %}
{% if perms.ipam.add_device or perms.ipam.add_devicetype %}
<li class="divider"></li>
{% endif %}
<li><a href="{% url 'dcim:devicetype_list' %}"><i class="fa fa-search" aria-hidden="true"></i> Device Types</a></li>
<li><a href="{% url 'dcim:devicetype_list' %}"><strong>Device Types</strong></a></li>
{% if perms.dcim.add_devicetype %}
<li><a href="{% url 'dcim:devicetype_add' %}"><i class="fa fa-plus" aria-hidden="true"></i> Add a Device Type</a></li>
<li class="subnav"><a href="{% url 'dcim:devicetype_add' %}"><i class="fa fa-plus"></i> Add a Device Type</a></li>
{% endif %}
<li class="divider"></li>
<li><a href="{% url 'dcim:devicerole_list' %}"><i class="fa fa-search" aria-hidden="true"></i> Device Roles</a></li>
<li><a href="{% url 'dcim:devicerole_list' %}"><strong>Device Roles</strong></a></li>
{% if perms.dcim.add_devicerole %}
<li><a href="{% url 'dcim:devicerole_add' %}"><i class="fa fa-plus" aria-hidden="true"></i> Add a Device Role</a></li>
<li class="subnav"><a href="{% url 'dcim:devicerole_add' %}"><i class="fa fa-plus"></i> Add a Device Role</a></li>
{% endif %}
{% if perms.dcim.add_devicerole or perms.dcim.add_manufacturer %}
<li class="divider"></li>
{% endif %}
<li><a href="{% url 'dcim:manufacturer_list' %}"><i class="fa fa-search" aria-hidden="true"></i> Manufacturers</a></li>
<li><a href="{% url 'dcim:manufacturer_list' %}"><strong>Manufacturers</strong></a></li>
{% if perms.dcim.add_manufacturer %}
<li><a href="{% url 'dcim:manufacturer_add' %}"><i class="fa fa-plus" aria-hidden="true"></i> Add a Manufacturer</a></li>
<li class="subnav"><a href="{% url 'dcim:manufacturer_add' %}"><i class="fa fa-plus"></i> Add a Manufacturer</a></li>
{% endif %}
{% if perms.dcim.add_manufacturer or perms.dcim.add_platform %}
<li class="divider"></li>
{% endif %}
<li><a href="{% url 'dcim:platform_list' %}"><i class="fa fa-search" aria-hidden="true"></i> Platforms</a></li>
<li><a href="{% url 'dcim:platform_list' %}"><strong>Platforms</strong></a></li>
{% if perms.dcim.add_platform %}
<li><a href="{% url 'dcim:platform_add' %}"><i class="fa fa-plus" aria-hidden="true"></i> Add a Platform</a></li>
<li class="subnav"><a href="{% url 'dcim:platform_add' %}"><i class="fa fa-plus"></i> Add a Platform</a></li>
{% endif %}
</ul>
</li>
<li class="dropdown{% if request.path|contains:'/dcim/console-connections/,/dcim/power-connections/,/dcim/interface-connections/' %} active{% endif %}">
<a href="#" class="dropdown-toggle" data-toggle="dropdown" role="button" aria-haspopup="true" aria-expanded="false">Connections <span class="caret"></span></a>
<ul class="dropdown-menu">
<li><a href="{% url 'dcim:console_connections_list' %}"><i class="fa fa-search" aria-hidden="true"></i> Console Connections</a></li>
<li><a href="{% url 'dcim:console_connections_list' %}"><strong>Console Connections</strong></a></li>
{% if perms.dcim.change_consoleport %}
<li><a href="{% url 'dcim:console_connections_import' %}"><i class="fa fa-download" aria-hidden="true"></i> Import Console Connections</a></li>
<li class="subnav"><a href="{% url 'dcim:console_connections_import' %}"><i class="fa fa-download"></i> Import Console Connections</a></li>
{% endif %}
{% if perms.ipam.change_consoleport or perms.ipam.change_powerport %}
<li class="divider"></li>
{% endif %}
<li><a href="{% url 'dcim:power_connections_list' %}"><i class="fa fa-search" aria-hidden="true"></i> Power Connections</a></li>
<li><a href="{% url 'dcim:power_connections_list' %}"><strong>Power Connections</strong></a></li>
{% if perms.dcim.change_powerport %}
<li><a href="{% url 'dcim:power_connections_import' %}"><i class="fa fa-download" aria-hidden="true"></i> Import Power Connections</a></li>
<li class="subnav"><a href="{% url 'dcim:power_connections_import' %}"><i class="fa fa-download"></i> Import Power Connections</a></li>
{% endif %}
{% if perms.ipam.change_powerport or perms.ipam.add_interfaceconnection %}
<li class="divider"></li>
{% endif %}
<li><a href="{% url 'dcim:interface_connections_list' %}"><i class="fa fa-search" aria-hidden="true"></i> Interface Connections</a></li>
<li><a href="{% url 'dcim:interface_connections_list' %}"><strong>Interface Connections</strong></a></li>
{% if perms.dcim.add_interfaceconnection %}
<li><a href="{% url 'dcim:interface_connections_import' %}"><i class="fa fa-download" aria-hidden="true"></i> Import Interface Connections</a></li>
<li class="subnav"><a href="{% url 'dcim:interface_connections_import' %}"><i class="fa fa-download"></i> Import Interface Connections</a></li>
{% endif %}
</ul>
</li>
<li class="dropdown{% if request.path|contains:'/ipam/' and not request.path|contains:'/ipam/vlan' %} active{% endif %}">
<a href="#" class="dropdown-toggle" data-toggle="dropdown" role="button" aria-haspopup="true" aria-expanded="false">IP Space <span class="caret"></span></a>
<ul class="dropdown-menu">
<li><a href="{% url 'ipam:ipaddress_list' %}"><i class="fa fa-search" aria-hidden="true"></i> IP Addresses</a></li>
<li><a href="{% url 'ipam:ipaddress_list' %}"><strong>IP Addresses</strong></a></li>
{% if perms.ipam.add_ipaddress %}
<li><a href="{% url 'ipam:ipaddress_add' %}"><i class="fa fa-plus" aria-hidden="true"></i> Add an IP</a></li>
<li><a href="{% url 'ipam:ipaddress_import' %}"><i class="fa fa-download" aria-hidden="true"></i> Import IPs</a></li>
<li class="subnav"><a href="{% url 'ipam:ipaddress_add' %}"><i class="fa fa-plus"></i> Add an IP</a></li>
<li class="subnav"><a href="{% url 'ipam:ipaddress_import' %}"><i class="fa fa-download"></i> Import IPs</a></li>
{% endif %}
{% if perms.ipam.add_ipaddress or perms.ipam.add_prefix %}
<li class="divider"></li>
{% endif %}
<li><a href="{% url 'ipam:prefix_list' %}"><i class="fa fa-search" aria-hidden="true"></i> Prefixes</a></li>
<li><a href="{% url 'ipam:prefix_list' %}"><strong>Prefixes</strong></a></li>
{% if perms.ipam.add_prefix %}
<li><a href="{% url 'ipam:prefix_add' %}"><i class="fa fa-plus" aria-hidden="true"></i> Add a Prefix</a></li>
<li><a href="{% url 'ipam:prefix_import' %}"><i class="fa fa-download" aria-hidden="true"></i> Import Prefixes</a></li>
<li class="subnav"><a href="{% url 'ipam:prefix_add' %}"><i class="fa fa-plus"></i> Add a Prefix</a></li>
<li class="subnav"><a href="{% url 'ipam:prefix_import' %}"><i class="fa fa-download"></i> Import Prefixes</a></li>
{% endif %}
{% if perms.ipam.add_prefix or perms.ipam.add_aggregate %}
<li class="divider"></li>
{% endif %}
<li><a href="{% url 'ipam:aggregate_list' %}"><i class="fa fa-search" aria-hidden="true"></i> Aggregates</a></li>
<li><a href="{% url 'ipam:aggregate_list' %}"><strong>Aggregates</strong></a></li>
{% if perms.ipam.add_aggregate %}
<li><a href="{% url 'ipam:aggregate_add' %}"><i class="fa fa-plus" aria-hidden="true"></i> Add an Aggregate</a></li>
<li><a href="{% url 'ipam:aggregate_import' %}"><i class="fa fa-download" aria-hidden="true"></i> Import Aggregates</a></li>
<li class="subnav"><a href="{% url 'ipam:aggregate_add' %}"><i class="fa fa-plus"></i> Add an Aggregate</a></li>
<li class="subnav"><a href="{% url 'ipam:aggregate_import' %}"><i class="fa fa-download"></i> Import Aggregates</a></li>
{% endif %}
{% if perms.ipam.add_aggregate or perms.ipam.add_vrf %}
<li class="divider"></li>
{% endif %}
<li><a href="{% url 'ipam:vrf_list' %}"><i class="fa fa-search" aria-hidden="true"></i> VRFs</a></li>
<li><a href="{% url 'ipam:vrf_list' %}"><strong>VRFs</strong></a></li>
{% if perms.ipam.add_vrf %}
<li><a href="{% url 'ipam:vrf_add' %}"><i class="fa fa-plus" aria-hidden="true"></i> Add a VRF</a></li>
<li><a href="{% url 'ipam:vrf_import' %}"><i class="fa fa-download" aria-hidden="true"></i> Import VRFs</a></li>
<li class="subnav"><a href="{% url 'ipam:vrf_add' %}"><i class="fa fa-plus"></i> Add a VRF</a></li>
<li class="subnav"><a href="{% url 'ipam:vrf_import' %}"><i class="fa fa-download"></i> Import VRFs</a></li>
{% endif %}
<li class="divider"></li>
<li><a href="{% url 'ipam:rir_list' %}"><i class="fa fa-search" aria-hidden="true"></i> RIRs</a></li>
<li><a href="{% url 'ipam:rir_list' %}"><strong>RIRs</strong></a></li>
{% if perms.ipam.add_rir %}
<li><a href="{% url 'ipam:rir_add' %}"><i class="fa fa-plus" aria-hidden="true"></i> Add a RIR</a></li>
<li class="subnav"><a href="{% url 'ipam:rir_add' %}"><i class="fa fa-plus"></i> Add a RIR</a></li>
{% endif %}
{% if perms.ipam.add_rir or perms.ipam.add_role %}
<li class="divider"></li>
{% endif %}
<li><a href="{% url 'ipam:role_list' %}"><i class="fa fa-search" aria-hidden="true"></i> Prefix Roles</a></li>
<li><a href="{% url 'ipam:role_list' %}"><strong>Prefix Roles</strong></a></li>
{% if perms.ipam.add_role %}
<li><a href="{% url 'ipam:role_add' %}"><i class="fa fa-plus" aria-hidden="true"></i> Add a Role</a></li>
<li class="subnav"><a href="{% url 'ipam:role_add' %}"><i class="fa fa-plus"></i> Add a Role</a></li>
{% endif %}
</ul>
</li>
<li class="dropdown{% if request.path|contains:'/ipam/vlan' %} active{% endif %}">
<a href="#" class="dropdown-toggle" data-toggle="dropdown" role="button" aria-haspopup="true" aria-expanded="false">VLANs <span class="caret"></span></a>
<ul class="dropdown-menu">
<li><a href="{% url 'ipam:vlan_list' %}"><i class="fa fa-search" aria-hidden="true"></i> VLANs</a></li>
<li><a href="{% url 'ipam:vlan_list' %}"><strong>VLANs</strong></a></li>
{% if perms.ipam.add_vlan %}
<li><a href="{% url 'ipam:vlan_add' %}"><i class="fa fa-plus" aria-hidden="true"></i> Add a VLAN</a></li>
<li><a href="{% url 'ipam:vlan_import' %}"><i class="fa fa-download" aria-hidden="true"></i> Import VLANs</a></li>
<li class="subnav"><a href="{% url 'ipam:vlan_add' %}"><i class="fa fa-plus"></i> Add a VLAN</a></li>
<li class="subnav"><a href="{% url 'ipam:vlan_import' %}"><i class="fa fa-download"></i> Import VLANs</a></li>
{% endif %}
<li class="divider"></li>
<li><a href="{% url 'ipam:vlangroup_list' %}"><i class="fa fa-search" aria-hidden="true"></i> VLAN Groups</a></li>
<li><a href="{% url 'ipam:vlangroup_list' %}"><strong>VLAN Groups</strong></a></li>
{% if perms.ipam.add_vlangroup %}
<li><a href="{% url 'ipam:vlangroup_add' %}"><i class="fa fa-plus" aria-hidden="true"></i> Add a VLAN Group</a></li>
<li class="subnav"><a href="{% url 'ipam:vlangroup_add' %}"><i class="fa fa-plus"></i> Add a VLAN Group</a></li>
{% endif %}
<li class="divider"></li>
<li><a href="{% url 'ipam:role_list' %}"><i class="fa fa-search" aria-hidden="true"></i> VLAN Roles</a></li>
<li><a href="{% url 'ipam:role_list' %}"><strong>VLAN Roles</strong></a></li>
{% if perms.ipam.add_role %}
<li><a href="{% url 'ipam:role_add' %}"><i class="fa fa-plus" aria-hidden="true"></i> Add a Role</a></li>
<li class="subnav"><a href="{% url 'ipam:role_add' %}"><i class="fa fa-plus"></i> Add a Role</a></li>
{% endif %}
</ul>
</li>
<li class="dropdown{% if request.path|contains:'/circuits/' %} active{% endif %}">
<a href="#" class="dropdown-toggle" data-toggle="dropdown" role="button" aria-haspopup="true" aria-expanded="false">Circuits <span class="caret"></span></a>
<ul class="dropdown-menu">
<li><a href="{% url 'circuits:provider_list' %}"><i class="fa fa-search" aria-hidden="true"></i> Providers</a></li>
<li><a href="{% url 'circuits:provider_list' %}"><strong>Providers</strong></a></li>
{% if perms.circuits.add_provider %}
<li><a href="{% url 'circuits:provider_add' %}"><i class="fa fa-plus" aria-hidden="true"></i> Add a Provider</a></li>
<li><a href="{% url 'circuits:provider_import' %}"><i class="fa fa-download" aria-hidden="true"></i> Import Providers</a></li>
<li class="subnav"><a href="{% url 'circuits:provider_add' %}"><i class="fa fa-plus"></i> Add a Provider</a></li>
<li class="subnav"><a href="{% url 'circuits:provider_import' %}"><i class="fa fa-download"></i> Import Providers</a></li>
{% endif %}
{% if perms.circuits.add_circuit or perms.circuits.add_provider %}
<li class="divider"></li>
{% endif %}
<li><a href="{% url 'circuits:circuit_list' %}"><i class="fa fa-search" aria-hidden="true"></i> Circuits</a></li>
<li><a href="{% url 'circuits:circuit_list' %}"><strong>Circuits</strong></a></li>
{% if perms.circuits.add_circuit %}
<li><a href="{% url 'circuits:circuit_add' %}"><i class="fa fa-plus" aria-hidden="true"></i> Add a Circuit</a></li>
<li><a href="{% url 'circuits:circuit_import' %}"><i class="fa fa-download" aria-hidden="true"></i> Import Circuits</a></li>
<li class="subnav"><a href="{% url 'circuits:circuit_add' %}"><i class="fa fa-plus"></i> Add a Circuit</a></li>
<li class="subnav"><a href="{% url 'circuits:circuit_import' %}"><i class="fa fa-download"></i> Import Circuits</a></li>
{% endif %}
<li class="divider"></li>
<li><a href="{% url 'circuits:circuittype_list' %}"><i class="fa fa-search" aria-hidden="true"></i> Circuit Types</a></li>
<li><a href="{% url 'circuits:circuittype_list' %}"><strong>Circuit Types</strong></a></li>
{% if perms.circuits.add_circuittype %}
<li><a href="{% url 'circuits:circuittype_add' %}"><i class="fa fa-plus" aria-hidden="true"></i> Add a Circuit Type</a></li>
<li class="subnav"><a href="{% url 'circuits:circuittype_add' %}"><i class="fa fa-plus"></i> Add a Circuit Type</a></li>
{% endif %}
</ul>
</li>
@@ -229,14 +229,14 @@
<li class="dropdown{% if request.path|contains:'/secrets/' %} active{% endif %}">
<a href="#" class="dropdown-toggle" data-toggle="dropdown" role="button" aria-haspopup="true" aria-expanded="false">Secrets <span class="caret"></span></a>
<ul class="dropdown-menu">
<li><a href="{% url 'secrets:secret_list' %}"><i class="fa fa-search" aria-hidden="true"></i> Secrets</a></li>
<li><a href="{% url 'secrets:secret_list' %}"><strong>Secrets</strong></a></li>
{% if perms.secrets.add_secret %}
<li><a href="{% url 'secrets:secret_import' %}"><i class="fa fa-download" aria-hidden="true"></i> Import Secrets</a></li>
<li class="subnav"><a href="{% url 'secrets:secret_import' %}"><i class="fa fa-download"></i> Import Secrets</a></li>
{% endif %}
<li class="divider"></li>
<li><a href="{% url 'secrets:secretrole_list' %}"><i class="fa fa-search" aria-hidden="true"></i> Secret Roles</a></li>
<li><a href="{% url 'secrets:secretrole_list' %}"><strong>Secret Roles</strong></a></li>
{% if perms.secrets.add_secretrole %}
<li><a href="{% url 'secrets:secretrole_add' %}"><i class="fa fa-plus" aria-hidden="true"></i> Add a Secret Role</a></li>
<li class="subnav"><a href="{% url 'secrets:secretrole_add' %}"><i class="fa fa-plus"></i> Add a Secret Role</a></li>
{% endif %}
</ul>
</li>
@@ -250,16 +250,16 @@
{{ request.user|truncatechars:"30" }} <span class="caret"></span>
</a>
<ul class="dropdown-menu">
<li><a href="{% url 'user:profile' %}"><i class="fa fa-user" aria-hidden="true"></i> Profile</a></li>
<li><a href="{% url 'user:profile' %}"><i class="fa fa-user"></i> Profile</a></li>
{% if request.user.is_staff %}
<li><a href="{% url 'admin:index' %}"><i class="fa fa-cogs" aria-hidden="true"></i> Admin</a></li>
<li><a href="{% url 'admin:index' %}"><i class="fa fa-cogs"></i> Admin</a></li>
{% endif %}
<li class="divider"></li>
<li><a href="{% url 'logout' %}"><i class="fa fa-sign-out" aria-hidden="true"></i> Log out</a></li>
<li><a href="{% url 'logout' %}"><i class="fa fa-sign-out"></i> Log out</a></li>
</ul>
</li>
{% else %}
<li><a href="{% url 'login' %}?next={{ request.path }}"><i class="fa fa-sign-in" aria-hidden="true"></i> Log in</a></li>
<li><a href="{% url 'login' %}?next={{ request.path }}"><i class="fa fa-sign-in"></i> Log in</a></li>
{% endif %}
</ul>
<form action="{% url 'search' %}" method="get" class="navbar-form navbar-right" id="navbar_search" role="search">
@@ -267,7 +267,7 @@
<input type="text" name="q" class="form-control" placeholder="Search">
<span class="input-group-btn">
<button type="submit" class="btn btn-primary">
<i class="fa fa-search" aria-hidden="true"></i>
<i class="fa fa-search"></i>
</button>
</span>
</div>
@@ -290,7 +290,7 @@
{% for message in messages %}
<div class="alert alert-{{ message.tags }} alert-dismissable" role="alert">
<button type="button" class="close" data-dismiss="alert" aria-label="Close">
<span aria-hidden="true">&times;</span>
<span>&times;</span>
</button>
{{ message }}
</div>

View File

@@ -1,8 +1,6 @@
{% extends '_base.html' %}
{% load helpers %}
{% block title %}{{ circuit.provider }} - {{ circuit.cid }}{% endblock %}
{% block content %}
<div class="row">
<div class="col-sm-8 col-md-9">
@@ -39,7 +37,7 @@
</a>
{% endif %}
</div>
<h1>{{ circuit.provider }} - {{ circuit.cid }}</h1>
<h1>{% block title %}{{ circuit.provider }} - {{ circuit.cid }}{% endblock %}</h1>
{% include 'inc/created_updated.html' with obj=circuit %}
<div class="row">
<div class="col-md-6">

View File

@@ -1,8 +1,6 @@
{% extends '_base.html' %}
{% load helpers %}
{% block title %}Circuits{% endblock %}
{% block content %}
<div class="pull-right">
{% if perms.circuits.add_circuit %}
@@ -17,7 +15,7 @@
{% endif %}
{% include 'inc/export_button.html' with obj_type='circuits' %}
</div>
<h1>Circuits</h1>
<h1>{% block title %}Circuits{% endblock %}</h1>
<div class="row">
<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' %}

View File

@@ -2,10 +2,6 @@
{% load staticfiles %}
{% load form_helpers %}
{% block title %}
Circuit {{ obj.circuit }} - Side {{ form.term_side.value }}
{% endblock %}
{% block content %}
<form action="." method="post" class="form form-horizontal">
{% csrf_token %}
@@ -14,7 +10,7 @@
{% endfor %}
<div class="row">
<div class="col-md-6 col-md-offset-3">
<h3>Circuit {{ obj.circuit }} - Side {{ form.term_side.value }}</h3>
<h3>{% block title %}Circuit {{ obj.circuit }} - {{ form.term_side.value }} Side{% endblock %}</h3>
{% if form.non_field_errors %}
<div class="panel panel-danger">
<div class="panel-heading"><strong>Errors</strong></div>

View File

@@ -1,8 +1,6 @@
{% extends '_base.html' %}
{% load helpers %}
{% block title %}Circuit Types{% endblock %}
{% block content %}
<div class="pull-right">
{% if perms.circuits.add_circuittype %}
@@ -12,7 +10,7 @@
</a>
{% endif %}
</div>
<h1>Circuit Types</h1>
<h1>{% block title %}Circuit Types{% endblock %}</h1>
<div class="row">
<div class="col-md-12">
{% include 'utilities/obj_table.html' with bulk_delete_url='circuits:circuittype_bulk_delete' %}

View File

@@ -2,8 +2,6 @@
{% load static from staticfiles %}
{% load helpers %}
{% block title %}{{ provider }}{% endblock %}
{% block content %}
<div class="row">
<div class="col-sm-8 col-md-9">
@@ -45,7 +43,7 @@
</a>
{% endif %}
</div>
<h1>{{ provider }}</h1>
<h1>{% block title %}{{ provider }}{% endblock %}</h1>
{% include 'inc/created_updated.html' with obj=provider %}
<div class="row">
<div class="col-md-4">

View File

@@ -1,7 +1,5 @@
{% extends '_base.html' %}
{% block title %}Providers{% endblock %}
{% block content %}
<div class="pull-right">
{% if perms.circuits.add_provider %}
@@ -16,7 +14,7 @@
{% endif %}
{% include 'inc/export_button.html' with obj_type='providers' %}
</div>
<h1>Providers</h1>
<h1>{% block title %}Providers{% endblock %}</h1>
<div class="row">
<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' %}

View File

@@ -1,7 +1,5 @@
{% extends '_base.html' %}
{% block title %}Console Connections{% endblock %}
{% block content %}
<div class="pull-right">
{% if perms.dcim.change_consoleport %}
@@ -12,7 +10,7 @@
{% endif %}
{% include 'inc/export_button.html' with obj_type='connections' %}
</div>
<h1>Console Connections</h1>
<h1>{% block title %}Console Connections{% endblock %}</h1>
<div class="row">
<div class="col-md-9">
{% include 'responsive_table.html' %}

View File

@@ -2,8 +2,6 @@
{% load static from staticfiles %}
{% load form_helpers %}
{% block title %}Connect {{ consoleport.device }} {{ consoleport }}{% endblock %}
{% block content %}
<form action="." method="post" class="form form-horizontal">
{% csrf_token %}
@@ -21,7 +19,7 @@
</div>
{% endif %}
<div class="panel panel-default">
<div class="panel-heading">Connect {{ consoleport.device }} {{ consoleport }}</div>
<div class="panel-heading">{% block title %}Connect {{ consoleport.device }} {{ consoleport }}{% endblock %}</div>
<div class="panel-body">
<ul class="nav nav-tabs" role="tablist">
<li role="presentation" class="active"><a href="#search" aria-controls="search" role="tab" data-toggle="tab">Search</a></li>

View File

@@ -2,8 +2,6 @@
{% load static from staticfiles %}
{% load form_helpers %}
{% block title %}Connect {{ consoleserverport.device }} {{ consoleserverport }}{% endblock %}
{% block content %}
<form action="." method="post" class="form form-horizontal">
{% csrf_token %}
@@ -21,7 +19,7 @@
</div>
{% endif %}
<div class="panel panel-default">
<div class="panel-heading">Connect {{ consoleserverport.device }} {{ consoleserverport }}</div>
<div class="panel-heading">{% block title %}Connect {{ consoleserverport.device }} {{ consoleserverport }}{% endblock %}</div>
<div class="panel-body">
<ul class="nav nav-tabs" role="tablist">
<li role="presentation" class="active"><a href="#search" aria-controls="search" role="tab" data-toggle="tab">Search</a></li>

View File

@@ -45,7 +45,7 @@
</p>
</div>
</div>
{% elif not obj.device_type.is_child_device %}
{% else %}
{% render_field form.face %}
{% render_field form.position %}
{% endif %}

View File

@@ -1,8 +1,6 @@
{% extends '_base.html' %}
{% load helpers %}
{% block title %}Devices{% endblock %}
{% block content %}
<div class="pull-right">
{% if perms.dcim.add_device %}
@@ -17,7 +15,7 @@
{% endif %}
{% include 'inc/export_button.html' with obj_type='devices' %}
</div>
<h1>Devices</h1>
<h1>{% block title %}Devices{% endblock %}</h1>
<div class="row">
<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' %}

View File

@@ -1,8 +1,6 @@
{% extends '_base.html' %}
{% load form_helpers %}
{% block title %}Populate {{ device_bay }}{% endblock %}
{% block content %}
<form action="." method="post" class="form form-horizontal">
{% csrf_token %}
@@ -17,7 +15,7 @@
</div>
{% endif %}
<div class="panel panel-default">
<div class="panel-heading">Populate {{ device_bay }}</div>
<div class="panel-heading">{% block title %}Populate {{ device_bay }}{% endblock %}</div>
<div class="panel-body">
<div class="form-group">
<label class="col-md-3 control-label required">Parent Device</label>

View File

@@ -1,8 +1,6 @@
{% extends '_base.html' %}
{% load helpers %}
{% block title %}Device Roles{% endblock %}
{% block content %}
<div class="pull-right">
{% if perms.dcim.add_devicerole %}
@@ -12,7 +10,7 @@
</a>
{% endif %}
</div>
<h1>Device Roles</h1>
<h1>{% block title %}Device Roles{% endblock %}</h1>
<div class="row">
<div class="col-md-12">
{% include 'utilities/obj_table.html' with bulk_delete_url='dcim:devicerole_bulk_delete' %}

View File

@@ -1,8 +1,6 @@
{% extends '_base.html' %}
{% load helpers %}
{% block title %}{{ devicetype.manufacturer }} {{ devicetype.model }}{% endblock %}
{% block content %}
<div class="row">
<div class="col-md-12">
@@ -31,7 +29,7 @@
</div>
{% endif %}
<h1>{{ devicetype.manufacturer }} {{ devicetype.model }}</h1>
<h1>{% block title %}{{ devicetype.manufacturer }} {{ devicetype.model }}{% endblock %}</h1>
<div class="row">
<div class="col-md-5">
<div class="panel panel-default">

View File

@@ -1,8 +1,6 @@
{% extends '_base.html' %}
{% load helpers %}
{% block title %}Device Types{% endblock %}
{% block content %}
<div class="pull-right">
{% if perms.dcim.add_devicetype %}
@@ -12,7 +10,7 @@
</a>
{% endif %}
</div>
<h1>Device Types</h1>
<h1>{% block title %}Device Types{% endblock %}</h1>
<div class="row">
<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' %}

View File

@@ -1,7 +1,5 @@
{% extends '_base.html' %}
{% block title %}Interface Connections{% endblock %}
{% block content %}
<div class="pull-right">
{% if perms.dcim.add_interfaceconnection %}
@@ -12,7 +10,7 @@
{% endif %}
{% include 'inc/export_button.html' with obj_type='connections' %}
</div>
<h1>Interface Connections</h1>
<h1>{% block title %}Interface Connections{% endblock %}</h1>
<div class="row">
<div class="col-md-9">
{% include 'responsive_table.html' %}

View File

@@ -1,64 +0,0 @@
{% extends '_base.html' %}
{% load form_helpers %}
{% block title %}Assign a New IP Address{% endblock %}
{% block content %}
<form action="." method="post" class="form form-horizontal">
{% csrf_token %}
<div class="row">
<div class="col-md-6 col-md-offset-3">
{% if form.non_field_errors %}
<div class="panel panel-danger">
<div class="panel-heading"><strong>Errors</strong></div>
<div class="panel-body">
{{ form.non_field_errors }}
</div>
</div>
{% endif %}
<div class="panel panel-default">
<div class="panel-heading">
<strong>IP Address</strong>
</div>
<div class="panel-body">
{% render_field form.address %}
{% render_field form.vrf %}
{% render_field form.tenant %}
{% render_field form.status %}
{% render_field form.description %}
</div>
</div>
<div class="panel panel-default">
<div class="panel-heading">
<strong>Interface Assignment</strong>
</div>
<div class="panel-body">
<div class="form-group">
<label class="col-md-3 control-label">Device</label>
<div class="col-md-9">
<p class="form-control-static">{{ device }}</p>
</div>
</div>
{% render_field form.interface %}
{% render_field form.set_as_primary %}
</div>
</div>
{% if form.custom_fields %}
<div class="panel panel-default">
<div class="panel-heading"><strong>Custom Fields</strong></div>
<div class="panel-body">
{% render_custom_fields form %}
</div>
</div>
{% endif %}
<div class="form-group">
<div class="col-md-9 col-md-offset-3">
<button type="submit" name="_create" class="btn btn-primary">Create</button>
<button type="submit" name="_addanother" class="btn btn-primary">Create and Add More</button>
<a href="{{ return_url }}" class="btn btn-default">Cancel</a>
</div>
</div>
</div>
</div>
</form>
{% endblock %}

View File

@@ -1,8 +1,6 @@
{% extends '_base.html' %}
{% load helpers %}
{% block title %}Manufacturers{% endblock %}
{% block content %}
<div class="pull-right">
{% if perms.dcim.add_manufacturer %}
@@ -12,7 +10,7 @@
</a>
{% endif %}
</div>
<h1>Manufacturers</h1>
<h1>{% block title %}Manufacturers{% endblock %}</h1>
<div class="row">
<div class="col-md-12">
{% include 'utilities/obj_table.html' with bulk_delete_url='dcim:manufacturer_bulk_delete' %}

View File

@@ -1,8 +1,6 @@
{% extends '_base.html' %}
{% load helpers %}
{% block title %}Platforms{% endblock %}
{% block content %}
<div class="pull-right">
{% if perms.dcim.add_platform %}
@@ -12,7 +10,7 @@
</a>
{% endif %}
</div>
<h1>Platforms</h1>
<h1>{% block title %}Platforms{% endblock %}</h1>
<div class="row">
<div class="col-md-12">
{% include 'utilities/obj_table.html' with bulk_delete_url='dcim:platform_bulk_delete' %}

View File

@@ -1,7 +1,5 @@
{% extends '_base.html' %}
{% block title %}Power Connections{% endblock %}
{% block content %}
<div class="pull-right">
{% if perms.dcim.change_powerport %}
@@ -12,7 +10,7 @@
{% endif %}
{% include 'inc/export_button.html' with obj_type='connections' %}
</div>
<h1>Power Connections</h1>
<h1>{% block title %}Power Connections{% endblock %}</h1>
<div class="row">
<div class="col-md-9">
{% include 'responsive_table.html' %}

View File

@@ -2,8 +2,6 @@
{% load static from staticfiles %}
{% load form_helpers %}
{% block title %}Connect {{ poweroutlet.device }} {{ poweroutlet }}{% endblock %}
{% block content %}
<form action="." method="post" class="form form-horizontal">
{% csrf_token %}
@@ -21,7 +19,7 @@
</div>
{% endif %}
<div class="panel panel-default">
<div class="panel-heading">Connect {{ poweroutlet.device }} {{ poweroutlet }}</div>
<div class="panel-heading">{% block title %}Connect {{ poweroutlet.device }} {{ poweroutlet }}{% endblock %}</div>
<div class="panel-body">
<ul class="nav nav-tabs" role="tablist">
<li role="presentation" class="active"><a href="#search" aria-controls="search" role="tab" data-toggle="tab">Search</a></li>

View File

@@ -2,8 +2,6 @@
{% load static from staticfiles %}
{% load form_helpers %}
{% block title %}Connect {{ powerport.device }} {{ powerport }}{% endblock %}
{% block content %}
<form action="." method="post" class="form form-horizontal">
{% csrf_token %}
@@ -21,7 +19,7 @@
</div>
{% endif %}
<div class="panel panel-default">
<div class="panel-heading">Connect {{ powerport.device }} {{ powerport }}</div>
<div class="panel-heading">{% block title %}Connect {{ powerport.device }} {{ powerport }}{% endblock %}</div>
<div class="panel-body">
<ul class="nav nav-tabs" role="tablist">
<li role="presentation" class="active"><a href="#search" aria-controls="search" role="tab" data-toggle="tab">Search</a></li>

View File

@@ -1,8 +1,6 @@
{% extends '_base.html' %}
{% load helpers %}
{% block title %}{{ rack.site }} - Rack {{ rack.name }}{% endblock %}
{% block content %}
<div class="row">
<div class="col-sm-8 col-md-9">
@@ -51,7 +49,7 @@
</a>
{% endif %}
</div>
<h1>Rack {{ rack.name }}</h1>
<h1>{% block title %}Rack {{ rack.name }}{% endblock %}</h1>
{% include 'inc/created_updated.html' with obj=rack %}
<div class="row">
<div class="col-md-6">
@@ -261,13 +259,13 @@
<div class="rack_header">
<h4>Front</h4>
</div>
{% include 'dcim/inc/rack_elevation.html' with primary_face=front_elevation secondary_face=rear_elevation face_id=0 %}
{% include 'dcim/inc/rack_elevation.html' with primary_face=front_elevation secondary_face=rear_elevation face_id=0 reserved_units=rack.get_reserved_units %}
</div>
<div class="col-md-6 col-sm-6 col-xs-12">
<div class="rack_header">
<h4>Rear</h4>
</div>
{% include 'dcim/inc/rack_elevation.html' with primary_face=rear_elevation secondary_face=front_elevation face_id=1 %}
{% include 'dcim/inc/rack_elevation.html' with primary_face=rear_elevation secondary_face=front_elevation face_id=1 reserved_units=rack.get_reserved_units %}
</div>
</div>
</div>

View File

@@ -18,9 +18,9 @@
<p><small class="text-muted">{{ rack.facility_id|truncatechars:"30" }}</small></p>
</div>
{% if face_id %}
{% include 'dcim/inc/rack_elevation.html' with primary_face=rack.get_rear_elevation secondary_face=rack.get_front_elevation face_id=1 %}
{% include 'dcim/inc/rack_elevation.html' with primary_face=rack.get_rear_elevation secondary_face=rack.get_front_elevation face_id=1 reserved_units=rack.get_reserved_units %}
{% else %}
{% include 'dcim/inc/rack_elevation.html' with primary_face=rack.get_front_elevation secondary_face=rack.get_rear_elevation face_id=0 %}
{% include 'dcim/inc/rack_elevation.html' with primary_face=rack.get_front_elevation secondary_face=rack.get_rear_elevation face_id=0 reserved_units=rack.get_reserved_units %}
{% endif %}
<div class="clearfix"></div>
<div class="rack_header">

View File

@@ -1,8 +1,6 @@
{% extends '_base.html' %}
{% load helpers %}
{% block title %}Racks{% endblock %}
{% block content %}
<div class="pull-right">
{% if perms.dcim.add_rack %}
@@ -17,7 +15,7 @@
{% endif %}
{% include 'inc/export_button.html' with obj_type='racks' %}
</div>
<h1>Racks</h1>
<h1>{% block title %}Racks{% endblock %}</h1>
<div class="row">
<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' %}

View File

@@ -1,8 +1,6 @@
{% extends '_base.html' %}
{% load helpers %}
{% block title %}Rack Groups{% endblock %}
{% block content %}
<div class="pull-right">
{% if perms.dcim.add_rackgroup %}
@@ -12,7 +10,7 @@
</a>
{% endif %}
</div>
<h1>Rack Groups</h1>
<h1>{% block title %}Rack Groups{% endblock %}</h1>
<div class="row">
<div class="col-md-9">
{% include 'utilities/obj_table.html' with bulk_delete_url='dcim:rackgroup_bulk_delete' %}

View File

@@ -1,8 +1,6 @@
{% extends '_base.html' %}
{% load helpers %}
{% block title %}Rack Role{% endblock %}
{% block content %}
<div class="pull-right">
{% if perms.dcim.add_rackrole %}
@@ -12,7 +10,7 @@
</a>
{% endif %}
</div>
<h1>Rack Roles</h1>
<h1>{% block title %}Rack Roles{% endblock %}</h1>
<div class="row">
<div class="col-md-12">
{% include 'utilities/obj_table.html' with bulk_delete_url='dcim:rackrole_bulk_delete' %}

View File

@@ -1,8 +1,6 @@
{% extends '_base.html' %}
{% load helpers %}
{% block title %}Regions{% endblock %}
{% block content %}
<div class="pull-right">
{% if perms.dcim.add_region %}
@@ -12,7 +10,7 @@
</a>
{% endif %}
</div>
<h1>{{ block.title }}</h1>
<h1>{% block title %}Regions{% endblock %}</h1>
<div class="row">
<div class="col-md-12">
{% include 'utilities/obj_table.html' with bulk_delete_url='dcim:region_bulk_delete' %}

View File

@@ -2,8 +2,6 @@
{% load static from staticfiles %}
{% load helpers %}
{% block title %}{{ site }}{% endblock %}
{% block content %}
<div class="row">
<div class="col-sm-8 col-md-9">
@@ -50,7 +48,7 @@
</a>
{% endif %}
</div>
<h1>{{ site.name }}</h1>
<h1>{% block title %}{{ site }}{% endblock %}</h1>
{% include 'inc/created_updated.html' with obj=site %}
<div class="row">
<div class="col-md-7">

View File

@@ -1,7 +1,5 @@
{% extends '_base.html' %}
{% block title %}Sites{% endblock %}
{% block content %}
<div class="pull-right">
{% if perms.dcim.add_site %}
@@ -16,7 +14,7 @@
{% endif %}
{% include 'inc/export_button.html' with obj_type='sites' %}
</div>
<h1>Sites</h1>
<h1>{% block title %}Sites{% endblock %}</h1>
<div class="row">
<div class="col-md-9">
{% include 'utilities/obj_table.html' with bulk_edit_url='dcim:site_bulk_edit' %}

View File

@@ -1,6 +1,14 @@
{% extends '_base.html' %}
{% block content %}
{% if settings.NETBOX_USERNAME or settings.NETBOX_PASSWORD %}
<div class="alert alert-warning alert-dismissable" role="alert">
<button type="button" class="close" data-dismiss="alert" aria-label="Close">
<span aria-hidden="true">&times;</span>
</button>
<strong>Warning:</strong> The <code>NETBOX_USERNAME</code> and <code>NETBOX_PASSWORD</code> configuration parameters have been deprecated. Please replace them in configuration.py with <code>NAPALM_USERNAME</code> and <code>NAPALM_PASSWORD</code>.
</div>
{% endif %}
{% include 'search_form.html' %}
<div class="row">
<div class="col-sm-6 col-md-4">

View File

@@ -1,7 +1,5 @@
{% extends '_base.html' %}
{% block title %}Aggregate: {{ aggregate }}{% endblock %}
{% block content %}
<div class="row">
<div class="col-sm-8 col-md-9">
@@ -38,7 +36,7 @@
</a>
{% endif %}
</div>
<h1>{{ aggregate }}</h1>
<h1>{% block title %}{{ aggregate }}{% endblock %}</h1>
{% include 'inc/created_updated.html' with obj=aggregate %}
<div class="row">
<div class="col-md-6">

View File

@@ -2,8 +2,6 @@
{% load humanize %}
{% load helpers %}
{% block title %}Aggregates{% endblock %}
{% block content %}
<div class="pull-right">
{% if perms.ipam.add_aggregate %}
@@ -18,7 +16,7 @@
{% endif %}
{% include 'inc/export_button.html' with obj_type='aggregates' %}
</div>
<h1>Aggregates</h1>
<h1>{% block title %}Aggregates{% endblock %}</h1>
<div class="row">
<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' %}

View File

@@ -1,7 +1,5 @@
{% extends '_base.html' %}
{% block title %}{{ ipaddress }}{% endblock %}
{% block content %}
<div class="row">
<div class="col-sm-8 col-md-9">
@@ -40,10 +38,10 @@
</a>
{% endif %}
</div>
<h1>{{ ipaddress }}</h1>
<h1>{% block title %}{{ ipaddress }}{% endblock %}</h1>
{% include 'inc/created_updated.html' with obj=ipaddress %}
<div class="row">
<div class="col-md-6">
<div class="col-md-4">
<div class="panel panel-default">
<div class="panel-heading">
<strong>IP Address</strong>
@@ -137,7 +135,7 @@
{% include 'inc/custom_fields_panel.html' %}
{% endwith %}
</div>
<div class="col-md-6">
<div class="col-md-8">
{% include 'panel_table.html' with table=parent_prefixes_table heading='Parent Prefixes' %}
{% if duplicate_ips_table.rows %}
{% include 'panel_table.html' with table=duplicate_ips_table heading='Duplicate IP Addresses' panel_class='danger' %}

View File

@@ -2,8 +2,6 @@
{% load static from staticfiles %}
{% load form_helpers %}
{% block title %}Assign an IP Address{% endblock %}
{% block content %}
<form action="." method="post" class="form form-horizontal">
{% csrf_token %}
@@ -19,7 +17,7 @@
{% endif %}
<div class="panel panel-default">
<div class="panel-heading">
<strong>Assign an IP Address</strong>
<strong>{% block title %}Assign an IP Address{% endblock %}</strong>
</div>
<div class="panel-body">
<div class="form-group">

View File

@@ -1,8 +1,6 @@
{% extends '_base.html' %}
{% load helpers %}
{% block title %}IP Addresses{% endblock %}
{% block content %}
<div class="pull-right">
{% if perms.ipam.add_ipaddress %}
@@ -17,7 +15,7 @@
{% endif %}
{% include 'inc/export_button.html' with obj_type='IPs' %}
</div>
<h1>IP Addresses</h1>
<h1>{% block title %}IP Addresses{% endblock %}</h1>
<div class="row">
<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' %}

View File

@@ -1,6 +1,6 @@
{% extends '_base.html' %}
{% block title %}{{ prefix }}{% endblock %}
{% block title %}{{ prefix }} - IP Addresses{% endblock %}
{% block content %}
{% include 'ipam/inc/prefix_header.html' with active_tab='ip-addresses' %}

View File

@@ -2,8 +2,6 @@
{% load helpers %}
{% load form_helpers %}
{% block title %}Prefixes{% endblock %}
{% block content %}
<div class="pull-right">
<div class="btn-group" role="group">
@@ -22,7 +20,7 @@
{% endif %}
{% include 'inc/export_button.html' with obj_type='prefixes' %}
</div>
<h1>Prefixes</h1>
<h1>{% block title %}Prefixes{% endblock %}</h1>
<div class="row">
<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' %}

View File

@@ -2,8 +2,6 @@
{% load humanize %}
{% load helpers %}
{% block title %}RIRs{% endblock %}
{% block content %}
<div class="pull-right">
{% if request.GET.family == '6' %}
@@ -24,7 +22,7 @@
</a>
{% endif %}
</div>
<h1>RIRs</h1>
<h1>{% block title %}RIRs{% endblock %}</h1>
<div class="row">
<div class="col-md-9">
{% include 'utilities/obj_table.html' with bulk_delete_url='ipam:rir_bulk_delete' %}

View File

@@ -1,8 +1,6 @@
{% extends '_base.html' %}
{% load helpers %}
{% block title %}Prefix/VLAN Roles{% endblock %}
{% block content %}
<div class="pull-right">
{% if perms.dcim.add_devicerole %}
@@ -12,7 +10,7 @@
</a>
{% endif %}
</div>
<h1>Prefix/VLAN Roles</h1>
<h1>{% block title %}Prefix/VLAN Roles{% endblock %}</h1>
<div class="row">
<div class="col-md-12">
{% include 'utilities/obj_table.html' with bulk_delete_url='ipam:role_bulk_delete' %}

View File

@@ -1,7 +1,5 @@
{% extends '_base.html' %}
{% block title %}VLAN {{ vlan.display_name }}{% endblock %}
{% block content %}
<div class="row">
<div class="col-sm-8 col-md-9">
@@ -43,7 +41,7 @@
</a>
{% endif %}
</div>
<h1>VLAN {{ vlan.display_name }}</h1>
<h1>{% block title %}VLAN {{ vlan.display_name }}{% endblock %}</h1>
{% include 'inc/created_updated.html' with obj=vlan %}
<div class="row">
<div class="col-md-6">

View File

@@ -2,8 +2,6 @@
{% load helpers %}
{% load form_helpers %}
{% block title %}VLANs{% endblock %}
{% block content %}
<div class="pull-right">
{% if perms.ipam.add_vlan %}
@@ -18,7 +16,7 @@
{% endif %}
{% include 'inc/export_button.html' with obj_type='VLANs' %}
</div>
<h1>VLANs</h1>
<h1>{% block title %}VLANs{% endblock %}</h1>
<div class="row">
<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' %}

View File

@@ -1,8 +1,6 @@
{% extends '_base.html' %}
{% load helpers %}
{% block title %}VLAN Groups{% endblock %}
{% block content %}
<div class="pull-right">
{% if perms.ipam.add_vlangroup %}
@@ -12,7 +10,7 @@
</a>
{% endif %}
</div>
<h1>VLAN Groups</h1>
<h1>{% block title %}VLAN Groups{% endblock %}</h1>
<div class="row">
<div class="col-md-9">
{% include 'utilities/obj_table.html' with bulk_delete_url='ipam:vlangroup_bulk_delete' %}

View File

@@ -1,7 +1,5 @@
{% extends '_base.html' %}
{% block title %}VRF {{ vrf }}{% endblock %}
{% block content %}
<div class="row">
<div class="col-sm-8 col-md-9">
@@ -37,7 +35,7 @@
</a>
{% endif %}
</div>
<h1>{{ vrf }}</h1>
<h1>{% block title %}VRF {{ vrf }}{% endblock %}</h1>
{% include 'inc/created_updated.html' with obj=vrf %}
<div class="row">
<div class="col-md-6">

View File

@@ -2,8 +2,6 @@
{% load helpers %}
{% load form_helpers %}
{% block title %}VRFs{% endblock %}
{% block content %}
<div class="pull-right">
{% if perms.ipam.add_vrf %}
@@ -18,7 +16,7 @@
{% endif %}
{% include 'inc/export_button.html' with obj_type='VRFs' %}
</div>
<h1>VRFs</h1>
<h1>{% block title %}VRFs{% endblock %}</h1>
<div class="row">
<div class="col-md-9">
{% include 'utilities/obj_table.html' with bulk_edit_url='ipam:vrf_bulk_edit' bulk_delete_url='ipam:vrf_bulk_delete' %}

View File

@@ -2,8 +2,6 @@
{% load static from staticfiles %}
{% load secret_helpers %}
{% block title %}Secret: {{ secret }}{% endblock %}
{% block content %}
<div class="row">
<div class="col-md-12">
@@ -28,7 +26,7 @@
</a>
{% endif %}
</div>
<h1>{{ secret }}</h1>
<h1>{% block title %}{{ secret }}{% endblock %}</h1>
{% include 'inc/created_updated.html' with obj=secret %}
<div class="row">
<div class="col-md-6">

View File

@@ -2,15 +2,13 @@
{% load static from staticfiles %}
{% load form_helpers %}
{% block title %}{% if secret.pk %}Editing {{ secret }}{% else %}Add a Secret{% endif %}{% endblock %}
{% block content %}
<form action="." method="post" class="form form-horizontal">
{% csrf_token %}
{{ form.private_key }}
<div class="row">
<div class="col-md-6 col-md-offset-3">
<h3>{% if secret.pk %}Editing {{ secret }}{% else %}Add a Secret{% endif %}</h3>
<h3>{% block title %}{% if secret.pk %}Editing {{ secret }}{% else %}Add a Secret{% endif %}{% endblock %}</h3>
{% if form.non_field_errors %}
<div class="panel panel-danger">
<div class="panel-heading"><strong>Errors</strong></div>

View File

@@ -2,10 +2,8 @@
{% load static from staticfiles %}
{% load form_helpers %}
{% block title %}Secret Import{% endblock %}
{% block content %}
<h1>Secret Import</h1>
<h1>{% block title %}Secret Import{% endblock %}</h1>
<div class="row">
<div class="col-md-6">
{% if form.non_field_errors %}

View File

@@ -1,8 +1,6 @@
{% extends '_base.html' %}
{% load helpers %}
{% block title %}Secrets{% endblock %}
{% block content %}
<div class="pull-right">
{% if perms.secrets.add_secret %}
@@ -12,7 +10,7 @@
</a>
{% endif %}
</div>
<h1>Secrets</h1>
<h1>{% block title %}Secrets{% endblock %}</h1>
<div class="row">
<div class="col-md-9">
{% include 'utilities/obj_table.html' with bulk_edit_url='secrets:secret_bulk_edit' bulk_delete_url='secrets:secret_bulk_delete' %}

View File

@@ -1,8 +1,6 @@
{% extends '_base.html' %}
{% load helpers %}
{% block title %}Secret Roles{% endblock %}
{% block content %}
<div class="pull-right">
{% if perms.dcim.add_devicerole %}
@@ -12,7 +10,7 @@
</a>
{% endif %}
</div>
<h1>Secret Roles</h1>
<h1>{% block title %}Secret Roles{% endblock %}</h1>
<div class="row">
<div class="col-md-12">
{% include 'utilities/obj_table.html' with bulk_delete_url='secrets:secretrole_bulk_delete' %}

View File

@@ -1,8 +1,6 @@
{% extends '_base.html' %}
{% load helpers %}
{% block title %}{{ tenant }}{% endblock %}
{% block content %}
<div class="row">
<div class="col-sm-8 col-md-9">
@@ -41,7 +39,7 @@
</a>
{% endif %}
</div>
<h1>{{ tenant }}</h1>
<h1>{% block title %}{{ tenant }}{% endblock %}</h1>
{% include 'inc/created_updated.html' with obj=tenant %}
<div class="row">
<div class="col-md-7">

View File

@@ -1,8 +1,6 @@
{% extends '_base.html' %}
{% load helpers %}
{% block title %}Tenants{% endblock %}
{% block content %}
<div class="pull-right">
{% if perms.tenancy.add_tenant %}
@@ -17,7 +15,7 @@
{% endif %}
{% include 'inc/export_button.html' with obj_type='tenants' %}
</div>
<h1>Tenants</h1>
<h1>{% block title %}Tenants{% endblock %}</h1>
<div class="row">
<div class="col-md-9">
{% include 'utilities/obj_table.html' with bulk_edit_url='tenancy:tenant_bulk_edit' bulk_delete_url='tenancy:tenant_bulk_delete' %}

View File

@@ -1,8 +1,6 @@
{% extends '_base.html' %}
{% load helpers %}
{% block title %}Tenant Groups{% endblock %}
{% block content %}
<div class="pull-right">
{% if perms.tenancy.add_tenantgroup %}
@@ -12,7 +10,7 @@
</a>
{% endif %}
</div>
<h1>Tenant Groups</h1>
<h1>{% block title %}Tenant Groups{% endblock %}</h1>
<div class="row">
<div class="col-md-12">
{% include 'utilities/obj_table.html' with bulk_delete_url='tenancy:tenantgroup_bulk_delete' %}

View File

@@ -4,9 +4,10 @@ from django.conf import settings
from django.contrib.contenttypes.models import ContentType
from rest_framework import authentication, exceptions
from rest_framework.compat import is_authenticated
from rest_framework.exceptions import APIException
from rest_framework.pagination import LimitOffsetPagination
from rest_framework.permissions import DjangoModelPermissions, SAFE_METHODS
from rest_framework.permissions import BasePermission, DjangoModelPermissions, SAFE_METHODS
from rest_framework.serializers import Field, ValidationError
from users.models import Token
@@ -20,6 +21,10 @@ class ServiceUnavailable(APIException):
default_detail = "Service temporarily unavailable, please try again later."
#
# Authentication
#
class TokenAuthentication(authentication.TokenAuthentication):
"""
A custom authentication scheme which enforces Token expiration times.
@@ -61,6 +66,20 @@ class TokenPermissions(DjangoModelPermissions):
return super(TokenPermissions, self).has_permission(request, view)
class IsAuthenticatedOrLoginNotRequired(BasePermission):
"""
Returns True if the user is authenticated or LOGIN_REQUIRED is False.
"""
def has_permission(self, request, view):
if not settings.LOGIN_REQUIRED:
return True
return request.user and is_authenticated(request.user)
#
# Serializers
#
class ChoiceFieldSerializer(Field):
"""
Represent a ChoiceField as {'value': <DB value>, 'label': <string>}.
@@ -98,6 +117,10 @@ class ContentTypeFieldSerializer(Field):
raise ValidationError("Invalid content type")
#
# Mixins
#
class ModelValidationMixin(object):
"""
Enforce a model's validation through clean() when validating serializer data. This is necessary to ensure we're
@@ -119,6 +142,10 @@ class WritableSerializerMixin(object):
return self.serializer_class
#
# Pagination
#
class OptionalLimitOffsetPagination(LimitOffsetPagination):
"""
Override the stock paginator to allow setting limit=0 to disable pagination for a request. This returns all objects

View File

@@ -19,6 +19,16 @@ class NumericInFilter(django_filters.BaseInFilter, django_filters.NumberFilter):
pass
class NullableCharFieldFilter(django_filters.CharFilter):
null_value = 'NULL'
def filter(self, qs, value):
if value != self.null_value:
return super(NullableCharFieldFilter, self).filter(qs, value)
qs = self.get_method(qs)(**{'{}__isnull'.format(self.name): True})
return qs.distinct() if self.distinct else qs
class NullableModelMultipleChoiceField(forms.ModelMultipleChoiceField):
"""
This field operates like a normal ModelMultipleChoiceField except that it allows for one additional choice which is