Browse Source

Merge branch 'develop' into v2-develop

Conflicts:
	netbox/circuits/models.py
	netbox/netbox/settings.py
	upgrade.sh
Jeremy Stretch 8 years ago
parent
commit
f73693206f

+ 11 - 1
docs/installation/netbox.md

@@ -6,6 +6,7 @@ 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
 ```
 
 Python 2:
@@ -22,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:
@@ -84,6 +86,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
 ```
@@ -173,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:

+ 3 - 2
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
 ```
 

+ 21 - 0
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'),
+        ),
+    ]

+ 5 - 3
netbox/circuits/models.py

@@ -151,11 +151,13 @@ class CircuitTermination(models.Model):
     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, on_delete=models.CASCADE
+        '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)')
 

+ 2 - 3
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)))

+ 8 - 5
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):

+ 1 - 1
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'
 

+ 10 - 2
netbox/templates/ipam/ipaddress_bulk_add.html

@@ -10,13 +10,21 @@
 
 {% block form %}
     <div class="panel panel-default">
-        <div class="panel-heading"><strong>IP Address</strong></div>
+        <div class="panel-heading"><strong>IP Addresses</strong></div>
         <div class="panel-body">
-            {% 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 %}
         </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 %}
 {% endblock %}

+ 13 - 16
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),
         })
 

+ 6 - 2
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
 
@@ -24,7 +28,7 @@ eval $COMMAND
 ./netbox/manage.py migrate
 
 # Collect static files
-./netbox/manage.py collectstatic --noinput
+./netbox/manage.py collectstatic --no-input
 
 # Delete old bytecode
 find . -name "*.pyc" -delete