diff --git a/.travis.yml b/.travis.yml
index a7f9cda45..01fb25d8f 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -3,5 +3,6 @@ python:
- "2.7"
install:
- pip install -r requirements.txt
+ - pip install pep8
script:
- ./scripts/cibuild.sh
diff --git a/docs/getting-started.md b/docs/getting-started.md
index 7395fdace..6e9944cd3 100644
--- a/docs/getting-started.md
+++ b/docs/getting-started.md
@@ -210,7 +210,7 @@ If the test service does not run, or you cannot reach the NetBox home page, some
## Installation
-We'll set up a simple HTTP front end using [gunicorn](http://gunicorn.org/) for the purposes of this guide. For web servers, we have 2 configurations ready to go - we provide instructions for both [nginx](https://www.nginx.com/resources/wiki/)and [Apache](http://httpd.apache.org/docs/2.4). (You are of course free to use whichever combination of HTTP and WSGI services you'd like.) We'll also use [supervisord](http://supervisord.org/) for service persistence.
+We'll set up a simple HTTP front end using [gunicorn](http://gunicorn.org/) for the purposes of this guide. For web servers, we provide example configurations for both [nginx](https://www.nginx.com/resources/wiki/) and [Apache](http://httpd.apache.org/docs/2.4). (You are of course free to use whichever combination of HTTP and WSGI services you'd like.) We'll also use [supervisord](http://supervisord.org/) for service persistence.
```
# apt-get install gunicorn supervisor
@@ -264,34 +264,35 @@ Restart the nginx service to use the new configuration.
```
## Apache Configuration
-If you're feeling adventurous, or you already have Apache installed and can't run a dual-stack on your server - an Apache configuration has been created:
+If you're feeling adventurous, or you already have Apache installed and can't run a dual-stack on your server, the following configuration should work for Apache:
```
ProxyPreserveHost On
- ServerName netbox.totallycool.tld
+ ServerName netbox.example.com
- Alias /static/ /opt/netbox/static/static
+ Alias /static/ /opt/netbox/netbox/static
Options Indexes FollowSymLinks MultiViews
AllowOverride None
Order allow,deny
Allow from all
- #Require all granted [UNCOMMENT THIS IF RUNNING APACHE 2.4]
+ # Uncomment the line below if running Apache 2.4
+ #Require all granted
ProxyPass !
- ProxyPass / http://127.0.0.1:8001;
- ProxyPassReverse / http://127.0.0.1:8001;
+ ProxyPass / http://127.0.0.1:8001
+ ProxyPassReverse / http://127.0.0.1:8001
```
-Save the contents of the above example in `/etc/apache2/sites-available/netbox.conf`, add in the newly saved configuration and reload Apache:
+Save the contents of the above example in `/etc/apache2/sites-available/netbox.conf` and reload Apache:
```
# a2ensite netbox; service apache2 restart
@@ -329,4 +330,3 @@ Finally, restart the supervisor service to detect and run the gunicorn service:
At this point, you should be able to connect to the nginx HTTP service at the server name or IP address you provided. If you are unable to connect, check that the nginx service is running and properly configured. If you receive a 502 (bad gateway) error, this indicates that gunicorn is misconfigured or not running.
Please keep in mind that the configurations provided here are a bare minimum to get NetBox up and running. You will almost certainly want to make some changes to better suit your production environment.
-
diff --git a/docs/ipam.md b/docs/ipam.md
index d37a16319..53c5858f2 100644
--- a/docs/ipam.md
+++ b/docs/ipam.md
@@ -32,6 +32,8 @@ Additionally, you might define an aggregate for each large swath of public IPv4
Any prefixes you create in NetBox (discussed below) will be automatically organized under their respective aggregates. Any space within an aggregate which is not covered by an existing prefix will be annotated as available for allocation.
+Aggregates cannot overlap with one another; they can only exist in parallel. For instance, you cannot define both 10.0.0.0/8 and 10.16.0.0/16 as aggregates, because they overlap. 10.16.0.0/16 in this example would be created as a prefix.
+
### RIRs
Regional Internet Registries (RIRs) are responsible for the allocation of global address space. The five RIRs are ARIN, RIPE, APNIC, LACNIC, and AFRINIC. However, some address space has been set aside for private or internal use only, such as defined in RFCs 1918 and 6598. NetBox considers these RFCs as a sort of RIR as well; that is, an authority which "owns" certain address space.
diff --git a/netbox/dcim/forms.py b/netbox/dcim/forms.py
index 3b7c09ee6..776306a07 100644
--- a/netbox/dcim/forms.py
+++ b/netbox/dcim/forms.py
@@ -424,7 +424,7 @@ class DeviceFromCSVForm(forms.ModelForm):
'invalid_choice': 'Invalid site name.',
})
rack_name = forms.CharField()
- face = forms.ChoiceField(choices=[('front', 'Front'), ('rear', 'Rear')])
+ face = forms.ChoiceField(choices=[('Front', 'Front'), ('Rear', 'Rear')])
class Meta:
model = Device
@@ -1036,20 +1036,29 @@ class InterfaceConnectionImportForm(BulkImportForm, BootstrapMixin):
return
connection_list = []
+ occupied_interfaces = []
for i, record in enumerate(records, start=1):
form = self.fields['csv'].csv_form(data=record)
if form.is_valid():
interface_a = Interface.objects.get(device=form.cleaned_data['device_a'],
name=form.cleaned_data['interface_a'])
+ if interface_a in occupied_interfaces:
+ raise forms.ValidationError("{} {} found in multiple connections"
+ .format(interface_a.device.name, interface_a.name))
interface_b = Interface.objects.get(device=form.cleaned_data['device_b'],
name=form.cleaned_data['interface_b'])
+ if interface_b in occupied_interfaces:
+ raise forms.ValidationError("{} {} found in multiple connections"
+ .format(interface_b.device.name, interface_b.name))
connection = InterfaceConnection(interface_a=interface_a, interface_b=interface_b)
if form.cleaned_data['status'] == 'planned':
connection.connection_status = CONNECTION_STATUS_PLANNED
else:
connection.connection_status = CONNECTION_STATUS_CONNECTED
connection_list.append(connection)
+ occupied_interfaces.append(interface_a)
+ occupied_interfaces.append(interface_b)
else:
for field, errors in form.errors.items():
for e in errors:
diff --git a/netbox/dcim/tests/test_apis.py b/netbox/dcim/tests/test_apis.py
index ffb74cea2..f36fc4e84 100644
--- a/netbox/dcim/tests/test_apis.py
+++ b/netbox/dcim/tests/test_apis.py
@@ -47,7 +47,7 @@ class SiteTest(APITestCase):
graph_fields = [
'name',
'embed_url',
- 'link',
+ 'embed_link',
]
def test_get_list(self, endpoint='/api/dcim/sites/'):
diff --git a/netbox/dcim/tests/test_models.py b/netbox/dcim/tests/test_models.py
index ca841ea8f..2f3d8def6 100644
--- a/netbox/dcim/tests/test_models.py
+++ b/netbox/dcim/tests/test_models.py
@@ -64,7 +64,7 @@ class RackTestCase(TestCase):
rack=rack1,
position=10,
face=RACK_FACE_REAR,
- )
+ )
device1.save()
# Validate rack height
diff --git a/netbox/ipam/models.py b/netbox/ipam/models.py
index 91d04a09a..4dbfa801a 100644
--- a/netbox/ipam/models.py
+++ b/netbox/ipam/models.py
@@ -121,6 +121,12 @@ class Aggregate(CreatedUpdatedModel):
raise ValidationError("{} is already covered by an existing aggregate ({})"
.format(self.prefix, covering_aggregates[0]))
+ # Ensure that the aggregate being added does not cover an existing aggregate
+ covered_aggregates = Aggregate.objects.filter(prefix__net_contained=str(self.prefix))
+ if covered_aggregates:
+ raise ValidationError("{} is overlaps with an existing aggregate ({})"
+ .format(self.prefix, covered_aggregates[0]))
+
def save(self, *args, **kwargs):
if self.prefix:
# Infer address family from IPNetwork object
diff --git a/netbox/project-static/css/base.css b/netbox/project-static/css/base.css
index f44fd1a24..04bab2c63 100644
--- a/netbox/project-static/css/base.css
+++ b/netbox/project-static/css/base.css
@@ -9,7 +9,8 @@ body {
padding-top: 70px;
}
.container {
- width: 1340px;
+ width: auto;
+ max-width: 1340px;
}
.wrapper {
min-height: 100%;
diff --git a/netbox/project-static/js/forms.js b/netbox/project-static/js/forms.js
index 793a7c8e2..4ba61f13a 100644
--- a/netbox/project-static/js/forms.js
+++ b/netbox/project-static/js/forms.js
@@ -7,9 +7,9 @@ $(document).ready(function() {
// Slugify
function slugify(s, num_chars) {
- s = s.replace(/[^-\.\+\w\s]/g, ''); // Remove unneeded chars
+ s = s.replace(/[^\-\.\w\s]/g, ''); // Remove unneeded chars
s = s.replace(/^\s+|\s+$/g, ''); // Trim leading/trailing spaces
- s = s.replace(/[-\s]+/g, '-'); // Convert spaces to hyphens
+ s = s.replace(/[\-\.\s]+/g, '-'); // Convert spaces and decimals to hyphens
s = s.toLowerCase(); // Convert to lowercase
return s.substring(0, num_chars); // Trim to first num_chars chars
}
diff --git a/netbox/templates/dcim/interface_connections_import.html b/netbox/templates/dcim/interface_connections_import.html
index 79fce2eb2..6329e0680 100644
--- a/netbox/templates/dcim/interface_connections_import.html
+++ b/netbox/templates/dcim/interface_connections_import.html
@@ -8,6 +8,14 @@
Interface Connections Import
+ {% if form.non_field_errors %}
+
+
Errors
+
+ {{ form.non_field_errors }}
+
+
+ {% endif %}