From 2080abc6c3d84457ef7242ebd364460834a2cc2b Mon Sep 17 00:00:00 2001 From: Jeremy Stretch Date: Mon, 27 Jun 2016 23:56:39 -0400 Subject: [PATCH 01/11] Corrected SiteTest to account for earlier Graph model change --- netbox/dcim/tests/test_apis.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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/'): From c5d498ac148a0c41b1864804e37a9023e5d2fcfc Mon Sep 17 00:00:00 2001 From: Matt Layher Date: Mon, 27 Jun 2016 16:58:00 -0400 Subject: [PATCH 02/11] Run tests in CI --- scripts/cibuild.sh | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/scripts/cibuild.sh b/scripts/cibuild.sh index 91a847c37..b3f50152e 100755 --- a/scripts/cibuild.sh +++ b/scripts/cibuild.sh @@ -21,6 +21,20 @@ if [[ ! -z $SYNTAX ]]; then EXIT=1 fi +# Prepare configuration file for use in CI +CONFIG="netbox/netbox/configuration.py" +cp netbox/netbox/configuration.example.py $CONFIG +sed -i -e "s/ALLOWED_HOSTS = \[\]/ALLOWED_HOSTS = \['*'\]/g" $CONFIG +sed -i -e "s/SECRET_KEY = ''/SECRET_KEY = 'netboxci'/g" $CONFIG + +# Run NetBox tests +./netbox/manage.py test netbox/ +RC=$? +if [[ $RC != 0 ]]; then + echo -e "\n$(info) one or more tests failed, failing build." + EXIT=$RC +fi + # Show build duration END=$(date +%s) echo "$(info) exiting with code $EXIT after $(($END - $START)) seconds." From 5181c97281e964da0b89176a50d476819039d658 Mon Sep 17 00:00:00 2001 From: Matt Layher Date: Tue, 28 Jun 2016 00:25:12 -0400 Subject: [PATCH 03/11] Fix PEP 8 error in DCIM tests --- netbox/dcim/tests/test_models.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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 From b392aa4a4a7a968f1de5956f5c98727856fb7637 Mon Sep 17 00:00:00 2001 From: Jeremy Stretch Date: Tue, 28 Jun 2016 09:39:55 -0400 Subject: [PATCH 04/11] Fixes #45: Strip plus signs during slugification --- netbox/project-static/js/forms.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) 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 } From 4dac43c1c932d44915c64301f6294b818a21e157 Mon Sep 17 00:00:00 2001 From: Jeremy Stretch Date: Tue, 28 Jun 2016 09:50:00 -0400 Subject: [PATCH 05/11] Fixes #48: Set .container to auto with a max width --- netbox/project-static/css/base.css | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) 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%; From 6848a3dc810758d267a5f4a1bc4e6c08340c466e Mon Sep 17 00:00:00 2001 From: Jeremy Stretch Date: Tue, 28 Jun 2016 10:04:03 -0400 Subject: [PATCH 06/11] Fixes #67: Improved Aggregate validation; extended aggregate documentation --- docs/ipam.md | 2 ++ netbox/ipam/models.py | 6 ++++++ 2 files changed, 8 insertions(+) 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/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 From f1857dd189442211c4a9a377ed1ee853a4424309 Mon Sep 17 00:00:00 2001 From: Matt Layher Date: Tue, 28 Jun 2016 00:20:02 -0400 Subject: [PATCH 07/11] Add CI check for PEP 8 compliance --- .travis.yml | 1 + scripts/cibuild.sh | 10 ++++++++++ 2 files changed, 11 insertions(+) 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/scripts/cibuild.sh b/scripts/cibuild.sh index b3f50152e..4f4fe1ca3 100755 --- a/scripts/cibuild.sh +++ b/scripts/cibuild.sh @@ -21,6 +21,16 @@ if [[ ! -z $SYNTAX ]]; then EXIT=1 fi +# Check all python source files for PEP 8 compliance, but explicitly +# ignore: +# - E501: line greater than 80 characters in length +pep8 --ignore=E501 netbox/ +RC=$? +if [[ $RC != 0 ]]; then + echo -e "\n$(info) one or more PEP 8 errors detected, failing build." + EXIT=$RC +fi + # Prepare configuration file for use in CI CONFIG="netbox/netbox/configuration.py" cp netbox/netbox/configuration.example.py $CONFIG From 98febf3979c3c128ad36fb05a9509f9b1cd012c0 Mon Sep 17 00:00:00 2001 From: Jeremy Stretch Date: Tue, 28 Jun 2016 11:11:53 -0400 Subject: [PATCH 08/11] Fixes #72: Check for re-used interfaces when importing interface connections --- netbox/dcim/forms.py | 9 +++++++++ netbox/templates/dcim/interface_connections_import.html | 8 ++++++++ 2 files changed, 17 insertions(+) diff --git a/netbox/dcim/forms.py b/netbox/dcim/forms.py index 3b7c09ee6..81b75791e 100644 --- a/netbox/dcim/forms.py +++ b/netbox/dcim/forms.py @@ -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/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 %}
{% csrf_token %} {% render_form form %} From 374702927b2aaace53a37e30846fc959a384c34e Mon Sep 17 00:00:00 2001 From: Jeremy Stretch Date: Tue, 28 Jun 2016 11:38:09 -0400 Subject: [PATCH 09/11] Fixes #80: Correct rack face (lowercase) to be consistent with export behavior (uppercase) --- netbox/dcim/forms.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/netbox/dcim/forms.py b/netbox/dcim/forms.py index 81b75791e..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 From b37503ed8fec7ee3d88e6cca7924fd10c08e5705 Mon Sep 17 00:00:00 2001 From: Jeremy Stretch Date: Tue, 28 Jun 2016 11:50:25 -0400 Subject: [PATCH 10/11] Corrected typos in the Apache config; cleaned up grammar --- docs/getting-started.md | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/docs/getting-started.md b/docs/getting-started.md index 7395fdace..744e8bf2c 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,7 +264,7 @@ 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: ``` @@ -279,19 +279,20 @@ If you're feeling adventurous, or you already have Apache installed and can't ru 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. - From cce6c8981031c8ac9cd0f224781c2a788464fef1 Mon Sep 17 00:00:00 2001 From: Jeremy Stretch Date: Tue, 28 Jun 2016 11:57:44 -0400 Subject: [PATCH 11/11] Corrected static path in Apache config --- docs/getting-started.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/getting-started.md b/docs/getting-started.md index 744e8bf2c..6e9944cd3 100644 --- a/docs/getting-started.md +++ b/docs/getting-started.md @@ -270,9 +270,9 @@ If you're feeling adventurous, or you already have Apache installed and can't ru 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