From 670d264c9d9f639ad425d1ecfafb76e4c75b9e89 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 571bf53735f43bdd8cda1b08dded4aaa6c78ff9e 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 0a6c8ee907b0174be19cb4478cca1f9815c447f1 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 0a82c7ac59e66ca9e1f884689d7a5df2863b1a87 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 08ee992c59a749c65c43d2a05e8e53eb671755e8 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 f5a1b3683c3c85f4682127b47f56a11e06522147 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 7ea7bcf03de06fb81d4e4e41576b8d6445d15366 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 556be4a9c6304ee179fd8c625abd0ed3214390d2 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 ab71de1f3552aa8abbd43a7bf69dd31418ddd51c 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 806dc6af66c2a47086c15878b55910c2b9796302 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 9b8c56114268dfa113bdecd88d82ae4994b32856 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