From 599e1bb220dd9a02f4500cff4148fb08d66c4b1e Mon Sep 17 00:00:00 2001 From: Jeremy Stretch Date: Wed, 19 Apr 2017 13:19:30 -0400 Subject: [PATCH 1/7] Fixes #1071: Protect assigned circuit termination when an interface is deleted --- ...termination_interface_protect_on_delete.py | 21 +++++++++++++++++++ netbox/circuits/models.py | 10 ++++++--- 2 files changed, 28 insertions(+), 3 deletions(-) create mode 100644 netbox/circuits/migrations/0008_circuittermination_interface_protect_on_delete.py diff --git a/netbox/circuits/migrations/0008_circuittermination_interface_protect_on_delete.py b/netbox/circuits/migrations/0008_circuittermination_interface_protect_on_delete.py new file mode 100644 index 000000000..14ee6686d --- /dev/null +++ b/netbox/circuits/migrations/0008_circuittermination_interface_protect_on_delete.py @@ -0,0 +1,21 @@ +# -*- coding: utf-8 -*- +# Generated by Django 1.11 on 2017-04-19 17:17 +from __future__ import unicode_literals + +from django.db import migrations, models +import django.db.models.deletion + + +class Migration(migrations.Migration): + + dependencies = [ + ('circuits', '0007_circuit_add_description'), + ] + + operations = [ + migrations.AlterField( + model_name='circuittermination', + name='interface', + field=models.OneToOneField(blank=True, null=True, on_delete=django.db.models.deletion.PROTECT, related_name='circuit_termination', to='dcim.Interface'), + ), + ] diff --git a/netbox/circuits/models.py b/netbox/circuits/models.py index 6a0380dd5..caa91d1a3 100644 --- a/netbox/circuits/models.py +++ b/netbox/circuits/models.py @@ -150,10 +150,14 @@ class CircuitTermination(models.Model): circuit = models.ForeignKey('Circuit', related_name='terminations', on_delete=models.CASCADE) term_side = models.CharField(max_length=1, choices=TERM_SIDE_CHOICES, verbose_name='Termination') site = models.ForeignKey('dcim.Site', related_name='circuit_terminations', on_delete=models.PROTECT) - interface = models.OneToOneField('dcim.Interface', related_name='circuit_termination', blank=True, null=True) + interface = models.OneToOneField( + 'dcim.Interface', related_name='circuit_termination', blank=True, null=True, on_delete=models.PROTECT + ) port_speed = models.PositiveIntegerField(verbose_name='Port speed (Kbps)') - upstream_speed = models.PositiveIntegerField(blank=True, null=True, verbose_name='Upstream speed (Kbps)', - help_text='Upstream speed, if different from port speed') + upstream_speed = models.PositiveIntegerField( + blank=True, null=True, verbose_name='Upstream speed (Kbps)', + help_text='Upstream speed, if different from port speed' + ) xconnect_id = models.CharField(max_length=50, blank=True, verbose_name='Cross-connect ID') pp_info = models.CharField(max_length=100, blank=True, verbose_name='Patch panel/port(s)') From 401357b8cbffcea68fa0f60eb367626d587449e7 Mon Sep 17 00:00:00 2001 From: Jeremy Stretch Date: Wed, 19 Apr 2017 14:50:58 -0400 Subject: [PATCH 2/7] Closes #1084: Include custom fields when creating IP addresses in bulk --- netbox/ipam/forms.py | 13 +++++---- netbox/ipam/views.py | 2 +- netbox/templates/ipam/ipaddress_bulk_add.html | 12 ++++++-- netbox/utilities/views.py | 29 +++++++++---------- 4 files changed, 32 insertions(+), 24 deletions(-) diff --git a/netbox/ipam/forms.py b/netbox/ipam/forms.py index c9701f60b..6aa5347da 100644 --- a/netbox/ipam/forms.py +++ b/netbox/ipam/forms.py @@ -418,12 +418,15 @@ class IPAddressForm(BootstrapMixin, ReturnURLForm, CustomFieldForm): self.fields['nat_inside'].choices = [] -class IPAddressBulkAddForm(BootstrapMixin, forms.Form): - address = ExpandableIPAddressField() +class IPAddressBulkAddForm(BootstrapMixin, CustomFieldForm): + address_pattern = ExpandableIPAddressField(label='Address Pattern') vrf = forms.ModelChoiceField(queryset=VRF.objects.all(), required=False, label='VRF', empty_label='Global') - tenant = forms.ModelChoiceField(queryset=Tenant.objects.all(), required=False) - status = forms.ChoiceField(choices=IPADDRESS_STATUS_CHOICES) - description = forms.CharField(max_length=100, required=False) + + pattern_map = ('address_pattern', 'address') + + class Meta: + model = IPAddress + fields = ['address_pattern', 'vrf', 'tenant', 'status', 'description'] class IPAddressAssignForm(BootstrapMixin, forms.Form): diff --git a/netbox/ipam/views.py b/netbox/ipam/views.py index 0c53b8bc9..0754e0fa7 100644 --- a/netbox/ipam/views.py +++ b/netbox/ipam/views.py @@ -588,7 +588,7 @@ class IPAddressDeleteView(PermissionRequiredMixin, ObjectDeleteView): class IPAddressBulkAddView(PermissionRequiredMixin, BulkAddView): permission_required = 'ipam.add_ipaddress' form = forms.IPAddressBulkAddForm - model = IPAddress + model_form = forms.IPAddressForm template_name = 'ipam/ipaddress_bulk_add.html' default_return_url = 'ipam:ipaddress_list' diff --git a/netbox/templates/ipam/ipaddress_bulk_add.html b/netbox/templates/ipam/ipaddress_bulk_add.html index 1599ee900..d53f73bd5 100644 --- a/netbox/templates/ipam/ipaddress_bulk_add.html +++ b/netbox/templates/ipam/ipaddress_bulk_add.html @@ -10,13 +10,21 @@ {% block form %}
-
IP Address
+
IP Addresses
- {% render_field form.address %} + {% render_field form.address_pattern %} {% render_field form.vrf %} {% render_field form.tenant %} {% render_field form.status %} {% render_field form.description %}
+ {% if form.custom_fields %} +
+
Custom Fields
+
+ {% render_custom_fields form %} +
+
+ {% endif %} {% endblock %} diff --git a/netbox/utilities/views.py b/netbox/utilities/views.py index 0f867e16b..71e385a6e 100644 --- a/netbox/utilities/views.py +++ b/netbox/utilities/views.py @@ -296,12 +296,12 @@ class BulkAddView(View): Create new objects in bulk. form: Form class - model: The model of the objects being created + model_form: The ModelForm used to create individual objects template_name: The name of the template default_return_url: Name of the URL to which the user is redirected after creating the objects """ form = None - model = None + model_form = None template_name = None default_return_url = 'home' @@ -310,47 +310,44 @@ class BulkAddView(View): form = self.form() return render(request, self.template_name, { - 'obj_type': self.model._meta.verbose_name, + 'obj_type': self.model_form._meta.model._meta.verbose_name, 'form': form, 'return_url': reverse(self.default_return_url), }) def post(self, request): + model = self.model_form._meta.model form = self.form(request.POST) if form.is_valid(): - # The first field will be used as the pattern - field_names = list(form.fields.keys()) - pattern_field = field_names[0] + # Read the pattern field and target from the form's pattern_map + pattern_field, pattern_target = form.pattern_map pattern = form.cleaned_data[pattern_field] - - # All other fields will be copied as object attributes - kwargs = {k: form.cleaned_data[k] for k in field_names[1:]} + model_form_data = form.cleaned_data new_objs = [] try: with transaction.atomic(): for value in pattern: - obj = self.model(**kwargs) - setattr(obj, pattern_field, value) - obj.full_clean() - obj.save() + model_form_data[pattern_target] = value + model_form = self.model_form(model_form_data) + obj = model_form.save() new_objs.append(obj) except ValidationError as e: form.add_error(None, e) if not form.errors: - msg = u"Added {} {}".format(len(new_objs), self.model._meta.verbose_name_plural) + msg = u"Added {} {}".format(len(new_objs), model._meta.verbose_name_plural) messages.success(request, msg) - UserAction.objects.log_bulk_create(request.user, ContentType.objects.get_for_model(self.model), msg) + UserAction.objects.log_bulk_create(request.user, ContentType.objects.get_for_model(model), msg) if '_addanother' in request.POST: return redirect(request.path) return redirect(self.default_return_url) return render(request, self.template_name, { 'form': form, - 'obj_type': self.model._meta.verbose_name, + 'obj_type': model._meta.verbose_name, 'return_url': reverse(self.default_return_url), }) From 38d826d152b5c5f0e78df1f0da146c7e67d98566 Mon Sep 17 00:00:00 2001 From: Jeremy Stretch Date: Fri, 21 Apr 2017 10:32:10 -0400 Subject: [PATCH 3/7] Fixes #1092: Increase randomness in SECRET_KEY generation tool --- netbox/generate_secret_key.py | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/netbox/generate_secret_key.py b/netbox/generate_secret_key.py index 0e0214dc4..3c88aa710 100755 --- a/netbox/generate_secret_key.py +++ b/netbox/generate_secret_key.py @@ -1,8 +1,7 @@ #!/usr/bin/env python # This script will generate a random 50-character string suitable for use as a SECRET_KEY. -import os import random charset = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!@#$%^&*(-_=+)' -random.seed = (os.urandom(2048)) -print(''.join(random.choice(charset) for c in range(50))) +secure_random = random.SystemRandom() +print(''.join(secure_random.sample(charset, 50))) From 697866d1bae79de337c09f91b7edf269359fa4c4 Mon Sep 17 00:00:00 2001 From: Jeremy Stretch Date: Fri, 21 Apr 2017 13:30:18 -0400 Subject: [PATCH 4/7] #1090: Tweaked docs for Python3 on Ubuntu --- docs/installation/netbox.md | 15 +++++++++++++++ docs/installation/postgresql.md | 5 +++-- upgrade.sh | 6 +++++- 3 files changed, 23 insertions(+), 3 deletions(-) diff --git a/docs/installation/netbox.md b/docs/installation/netbox.md index 0e116a29f..507cd2522 100644 --- a/docs/installation/netbox.md +++ b/docs/installation/netbox.md @@ -8,6 +8,13 @@ Python 3: # apt-get install -y python3 python3-dev python3-pip libxml2-dev libxslt1-dev libffi-dev graphviz libpq-dev libssl-dev ``` +!!! warning + On Ubuntu, installing only Python3 will not create a symlink for the interpreter at `/usr/bin/python`: scripts need to be called using the `python3` binary explicitly. As a workaround, you can create this symlink using the `update-alternatives` utility: + + # update-alternatives --install /usr/bin/python python /usr/bin/python3 1 + + This will allow calling the `python3` binary as `python`. + Python 2: ```no-highlight @@ -84,6 +91,14 @@ Checking connectivity... done. Install the required Python packages using pip. (If you encounter any compilation errors during this step, ensure that you've installed all of the system dependencies listed above.) +Python 3: + +```no-highlight +# pip3 install -r requirements.txt +``` + +Python 2: + ```no-highlight # pip install -r requirements.txt ``` diff --git a/docs/installation/postgresql.md b/docs/installation/postgresql.md index 39a8f05cb..543a0a2cf 100644 --- a/docs/installation/postgresql.md +++ b/docs/installation/postgresql.md @@ -5,13 +5,14 @@ NetBox requires a PostgreSQL database to store data. (Please note that MySQL is **Debian/Ubuntu** ```no-highlight -# apt-get install -y postgresql libpq-dev python-psycopg2 +# apt-get update +# apt-get install -y postgresql libpq-dev ``` **CentOS/RHEL** ```no-highlight -# yum install -y postgresql postgresql-server postgresql-devel python-psycopg2 +# yum install -y postgresql postgresql-server postgresql-devel # postgresql-setup initdb ``` diff --git a/upgrade.sh b/upgrade.sh index f0d17e61b..1582409e7 100755 --- a/upgrade.sh +++ b/upgrade.sh @@ -15,8 +15,12 @@ if [ "$(whoami)" = "root" ]; then PREFIX="" fi +# Fall back to pip3 if pip is missing +PIP="pip" +type $PIP >/dev/null 2>&1 || PIP="pip3" + # Install any new Python packages -COMMAND="${PREFIX}pip install -r requirements.txt --upgrade" +COMMAND="${PREFIX}${PIP} install -r requirements.txt --upgrade" echo "Updating required Python packages ($COMMAND)..." eval $COMMAND From 5c0614d65699f47618966b8979c460e9eb0dc0c6 Mon Sep 17 00:00:00 2001 From: Jeremy Stretch Date: Fri, 21 Apr 2017 14:37:47 -0400 Subject: [PATCH 5/7] #1090: Python3 tweaks for installation on CentOS --- docs/installation/netbox.md | 11 +++-------- upgrade.sh | 2 +- 2 files changed, 4 insertions(+), 9 deletions(-) diff --git a/docs/installation/netbox.md b/docs/installation/netbox.md index 507cd2522..7f0046231 100644 --- a/docs/installation/netbox.md +++ b/docs/installation/netbox.md @@ -6,15 +6,9 @@ Python 3: ```no-highlight # apt-get install -y python3 python3-dev python3-pip libxml2-dev libxslt1-dev libffi-dev graphviz libpq-dev libssl-dev +# update-alternatives --install /usr/bin/python python /usr/bin/python3 1 ``` -!!! warning - On Ubuntu, installing only Python3 will not create a symlink for the interpreter at `/usr/bin/python`: scripts need to be called using the `python3` binary explicitly. As a workaround, you can create this symlink using the `update-alternatives` utility: - - # update-alternatives --install /usr/bin/python python /usr/bin/python3 1 - - This will allow calling the `python3` binary as `python`. - Python 2: ```no-highlight @@ -29,6 +23,7 @@ Python 3: # yum install -y epel-release # yum install -y gcc python34 python34-devel python34-setuptools libxml2-devel libxslt-devel libffi-devel graphviz openssl-devel # easy_install-3.4 pip +# ln -s -f python3.4 /usr/bin/python ``` Python 2: @@ -188,7 +183,7 @@ Superuser created successfully. # Collect Static Files ```no-highlight -# ./manage.py collectstatic +# ./manage.py collectstatic --no-input You have requested to collect static files at the destination location as specified in your settings: diff --git a/upgrade.sh b/upgrade.sh index 1582409e7..1fa46233e 100755 --- a/upgrade.sh +++ b/upgrade.sh @@ -28,4 +28,4 @@ eval $COMMAND ./netbox/manage.py migrate # Collect static files -./netbox/manage.py collectstatic --noinput +./netbox/manage.py collectstatic --no-input From 5037046624845b8a3a8cb106fea49b078a619949 Mon Sep 17 00:00:00 2001 From: Jeremy Stretch Date: Fri, 21 Apr 2017 14:47:31 -0400 Subject: [PATCH 6/7] Release v1.9.6 --- netbox/netbox/settings.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/netbox/netbox/settings.py b/netbox/netbox/settings.py index 57dcdd275..e76f71e73 100644 --- a/netbox/netbox/settings.py +++ b/netbox/netbox/settings.py @@ -12,7 +12,7 @@ except ImportError: "the documentation.") -VERSION = '1.9.6-dev' +VERSION = '1.9.6' # Import local configuration for setting in ['ALLOWED_HOSTS', 'DATABASE', 'SECRET_KEY']: From 861c8b29c0df07d2015fca0cd46bf1a6259ea48e Mon Sep 17 00:00:00 2001 From: Jeremy Stretch Date: Fri, 21 Apr 2017 14:56:36 -0400 Subject: [PATCH 7/7] Post-release version bump --- netbox/netbox/settings.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/netbox/netbox/settings.py b/netbox/netbox/settings.py index e76f71e73..cfde69d9e 100644 --- a/netbox/netbox/settings.py +++ b/netbox/netbox/settings.py @@ -12,7 +12,7 @@ except ImportError: "the documentation.") -VERSION = '1.9.6' +VERSION = '1.9.7-dev' # Import local configuration for setting in ['ALLOWED_HOSTS', 'DATABASE', 'SECRET_KEY']: