Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Release v1.9.6 #1094

Merged
merged 21 commits into from
Apr 21, 2017
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
21 commits
Select commit Hold shift + click to select a range
abc51fd
Post-release version bump
jeremystretch Apr 6, 2017
d3b16ba
Fixes #1057: Corrected VLAN validation during prefix import
jeremystretch Apr 7, 2017
105e9da
XSS flaw bugfix
Apr 10, 2017
3b48a27
Merge pull request #1062 from asteinhauser/develop
jeremystretch Apr 10, 2017
cf5be85
Closes #1061: Escape all messages by default (complements #1062)
jeremystretch Apr 10, 2017
ba1a4f0
Replace tabs with spaces
jeremystretch Apr 10, 2017
5312912
Python3 fixes for CentOS/RHEL
bellwood Apr 12, 2017
6dcc5a1
Merge pull request #1070 from bellwood/patch-1
jeremystretch Apr 12, 2017
7cbea49
Fixes #1072: Order LAG interfaces naturally on bulk interface edit form
jeremystretch Apr 12, 2017
b42dab3
Differentiate between LAG and virtual interfaces in device interface …
jeremystretch Apr 12, 2017
d5c3f9e
#878: Show assigned IP addresses in device interfaces list
jeremystretch Apr 13, 2017
f70f0f8
Improved handling of return_url for object edit/delete views; removed…
jeremystretch Apr 13, 2017
09000ad
Closes #1001: Merged IP interface assignment into ipam.IPAddressForm
jeremystretch Apr 13, 2017
610b412
#878: Layout tweaks
jeremystretch Apr 13, 2017
f9a33bf
Fixes #1074: Require ncclient 0.5.3 (Python 3 fix)
jeremystretch Apr 13, 2017
599e1bb
Fixes #1071: Protect assigned circuit termination when an interface i…
jeremystretch Apr 19, 2017
401357b
Closes #1084: Include custom fields when creating IP addresses in bulk
jeremystretch Apr 19, 2017
38d826d
Fixes #1092: Increase randomness in SECRET_KEY generation tool
jeremystretch Apr 21, 2017
697866d
#1090: Tweaked docs for Python3 on Ubuntu
jeremystretch Apr 21, 2017
5c0614d
#1090: Python3 tweaks for installation on CentOS
jeremystretch Apr 21, 2017
5037046
Release v1.9.6
jeremystretch Apr 21, 2017
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
15 changes: 13 additions & 2 deletions docs/installation/netbox.md
Original file line number Diff line number Diff line change
Expand Up @@ -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:
Expand All @@ -20,7 +21,9 @@ Python 3:

```no-highlight
# yum install -y epel-release
# yum install -y gcc python3 python3-devel python3-pip libxml2-devel libxslt-devel libffi-devel graphviz openssl-devel
# 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:
Expand Down Expand Up @@ -83,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
```
Expand Down Expand Up @@ -172,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:
Expand Down
5 changes: 3 additions & 2 deletions docs/installation/postgresql.md
Original file line number Diff line number Diff line change
Expand Up @@ -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
```

Expand Down
Original file line number Diff line number Diff line change
@@ -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'),
),
]
10 changes: 7 additions & 3 deletions netbox/circuits/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -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)')

Expand Down
6 changes: 2 additions & 4 deletions netbox/circuits/views.py
Original file line number Diff line number Diff line change
Expand Up @@ -95,7 +95,7 @@ class CircuitTypeEditView(PermissionRequiredMixin, ObjectEditView):
model = CircuitType
form_class = forms.CircuitTypeForm

def get_return_url(self, obj):
def get_return_url(self, request, obj):
return reverse('circuits:circuittype_list')


Expand Down Expand Up @@ -142,7 +142,6 @@ class CircuitEditView(PermissionRequiredMixin, ObjectEditView):
permission_required = 'circuits.change_circuit'
model = Circuit
form_class = forms.CircuitForm
fields_initial = ['provider']
template_name = 'circuits/circuit_edit.html'
default_return_url = 'circuits:circuit_list'

Expand Down Expand Up @@ -230,15 +229,14 @@ class CircuitTerminationEditView(PermissionRequiredMixin, ObjectEditView):
permission_required = 'circuits.change_circuittermination'
model = CircuitTermination
form_class = forms.CircuitTerminationForm
fields_initial = ['term_side']
template_name = 'circuits/circuittermination_edit.html'

def alter_obj(self, obj, request, url_args, url_kwargs):
if 'circuit' in url_kwargs:
obj.circuit = get_object_or_404(Circuit, pk=url_kwargs['circuit'])
return obj

def get_return_url(self, obj):
def get_return_url(self, request, obj):
return obj.circuit.get_absolute_url()


Expand Down
41 changes: 9 additions & 32 deletions netbox/dcim/forms.py
Original file line number Diff line number Diff line change
Expand Up @@ -1422,9 +1422,16 @@ def __init__(self, *args, **kwargs):
super(InterfaceBulkEditForm, self).__init__(*args, **kwargs)

# Limit LAG choices to interfaces which belong to the parent device.
device = None
if self.initial.get('device'):
self.fields['lag'].queryset = Interface.objects.filter(
device=self.initial['device'], form_factor=IFACE_FF_LAG
try:
device = Device.objects.get(pk=self.initial.get('device'))
except Device.DoesNotExist:
pass
if device is not None:
interface_ordering = device.device_type.interface_ordering
self.fields['lag'].queryset = Interface.objects.order_naturally(method=interface_ordering).filter(
device=device, form_factor=IFACE_FF_LAG
)
else:
self.fields['lag'].choices = []
Expand Down Expand Up @@ -1684,36 +1691,6 @@ class InterfaceConnectionFilterForm(BootstrapMixin, forms.Form):
device = forms.CharField(required=False, label='Device name')


#
# IP addresses
#

class IPAddressForm(BootstrapMixin, CustomFieldForm):
set_as_primary = forms.BooleanField(label='Set as primary IP for device', required=False)

class Meta:
model = IPAddress
fields = ['address', 'vrf', 'tenant', 'status', 'interface', 'description']

def __init__(self, device, *args, **kwargs):

super(IPAddressForm, self).__init__(*args, **kwargs)

self.fields['vrf'].empty_label = 'Global'

interfaces = device.interfaces.all()
self.fields['interface'].queryset = interfaces
self.fields['interface'].required = True

# If this device has only one interface, select it by default.
if len(interfaces) == 1:
self.fields['interface'].initial = interfaces[0]

# If this device does not have any IP addresses assigned, default to setting the first IP as its primary.
if not IPAddress.objects.filter(interface__device=device).count():
self.fields['set_as_primary'].initial = True


#
# Modules
#
Expand Down
1 change: 0 additions & 1 deletion netbox/dcim/urls.py
Original file line number Diff line number Diff line change
Expand Up @@ -116,7 +116,6 @@
url(r'^devices/(?P<pk>\d+)/delete/$', views.DeviceDeleteView.as_view(), name='device_delete'),
url(r'^devices/(?P<pk>\d+)/inventory/$', views.device_inventory, name='device_inventory'),
url(r'^devices/(?P<pk>\d+)/lldp-neighbors/$', views.device_lldp_neighbors, name='device_lldp_neighbors'),
url(r'^devices/(?P<pk>\d+)/ip-addresses/assign/$', views.ipaddress_assign, name='ipaddress_assign'),
url(r'^devices/(?P<pk>\d+)/add-secret/$', secret_add, name='device_addsecret'),
url(r'^devices/(?P<device>\d+)/services/assign/$', ServiceEditView.as_view(), name='service_assign'),

Expand Down
73 changes: 13 additions & 60 deletions netbox/dcim/views.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@
from django.utils.http import urlencode
from django.views.generic import View

from ipam.models import Prefix, IPAddress, Service, VLAN
from ipam.models import Prefix, Service, VLAN
from circuits.models import Circuit
from extras.models import Graph, TopologyMap, GRAPH_TYPE_INTERFACE, GRAPH_TYPE_SITE
from utilities.forms import ConfirmationForm
Expand Down Expand Up @@ -124,13 +124,13 @@ def post(self, request, pk):

class ComponentEditView(ObjectEditView):

def get_return_url(self, obj):
def get_return_url(self, request, obj):
return obj.device.get_absolute_url()


class ComponentDeleteView(ObjectDeleteView):

def get_return_url(self, obj):
def get_return_url(self, request, obj):
return obj.device.get_absolute_url()


Expand All @@ -149,7 +149,7 @@ class RegionEditView(PermissionRequiredMixin, ObjectEditView):
model = Region
form_class = forms.RegionForm

def get_return_url(self, obj):
def get_return_url(self, request, obj):
return reverse('dcim:region_list')


Expand Down Expand Up @@ -242,7 +242,7 @@ class RackGroupEditView(PermissionRequiredMixin, ObjectEditView):
model = RackGroup
form_class = forms.RackGroupForm

def get_return_url(self, obj):
def get_return_url(self, request, obj):
return reverse('dcim:rackgroup_list')


Expand All @@ -268,7 +268,7 @@ class RackRoleEditView(PermissionRequiredMixin, ObjectEditView):
model = RackRole
form_class = forms.RackRoleForm

def get_return_url(self, obj):
def get_return_url(self, request, obj):
return reverse('dcim:rackrole_list')


Expand Down Expand Up @@ -379,15 +379,15 @@ def alter_obj(self, obj, request, args, kwargs):
obj.user = request.user
return obj

def get_return_url(self, obj):
def get_return_url(self, request, obj):
return obj.rack.get_absolute_url()


class RackReservationDeleteView(PermissionRequiredMixin, ObjectDeleteView):
permission_required = 'dcim.delete_rackreservation'
model = RackReservation

def get_return_url(self, obj):
def get_return_url(self, request, obj):
return obj.rack.get_absolute_url()


Expand All @@ -412,7 +412,7 @@ class ManufacturerEditView(PermissionRequiredMixin, ObjectEditView):
model = Manufacturer
form_class = forms.ManufacturerForm

def get_return_url(self, obj):
def get_return_url(self, request, obj):
return reverse('dcim:manufacturer_list')


Expand Down Expand Up @@ -632,7 +632,7 @@ class DeviceRoleEditView(PermissionRequiredMixin, ObjectEditView):
model = DeviceRole
form_class = forms.DeviceRoleForm

def get_return_url(self, obj):
def get_return_url(self, request, obj):
return reverse('dcim:devicerole_list')


Expand All @@ -657,7 +657,7 @@ class PlatformEditView(PermissionRequiredMixin, ObjectEditView):
model = Platform
form_class = forms.PlatformForm

def get_return_url(self, obj):
def get_return_url(self, request, obj):
return reverse('dcim:platform_list')


Expand Down Expand Up @@ -700,19 +700,15 @@ def device(request, pk):
interfaces = Interface.objects.order_naturally(device.device_type.interface_ordering)\
.filter(device=device, mgmt_only=False)\
.select_related('connected_as_a__interface_b__device', 'connected_as_b__interface_a__device',
'circuit_termination__circuit')
'circuit_termination__circuit').prefetch_related('ip_addresses')
mgmt_interfaces = Interface.objects.order_naturally(device.device_type.interface_ordering)\
.filter(device=device, mgmt_only=True)\
.select_related('connected_as_a__interface_b__device', 'connected_as_b__interface_a__device',
'circuit_termination__circuit')
'circuit_termination__circuit').prefetch_related('ip_addresses')
device_bays = natsorted(
DeviceBay.objects.filter(device=device).select_related('installed_device__device_type__manufacturer'),
key=attrgetter('name')
)

# Gather relevant device objects
ip_addresses = IPAddress.objects.filter(interface__device=device).select_related('interface', 'vrf')\
.order_by('address')
services = Service.objects.filter(device=device)
secrets = device.secrets.all()

Expand Down Expand Up @@ -743,7 +739,6 @@ def device(request, pk):
'interfaces': interfaces,
'mgmt_interfaces': mgmt_interfaces,
'device_bays': device_bays,
'ip_addresses': ip_addresses,
'services': services,
'secrets': secrets,
'related_devices': related_devices,
Expand All @@ -755,7 +750,6 @@ class DeviceEditView(PermissionRequiredMixin, ObjectEditView):
permission_required = 'dcim.change_device'
model = Device
form_class = forms.DeviceForm
fields_initial = ['site', 'rack', 'position', 'face', 'device_bay']
template_name = 'dcim/device_edit.html'
default_return_url = 'dcim:device_list'

Expand Down Expand Up @@ -1567,47 +1561,6 @@ class InterfaceConnectionsListView(ObjectListView):
template_name = 'dcim/interface_connections_list.html'


#
# IP addresses
#

@permission_required(['dcim.change_device', 'ipam.add_ipaddress'])
def ipaddress_assign(request, pk):

device = get_object_or_404(Device, pk=pk)

if request.method == 'POST':
form = forms.IPAddressForm(device, request.POST)
if form.is_valid():

ipaddress = form.save(commit=False)
ipaddress.interface = form.cleaned_data['interface']
ipaddress.save()
form.save_custom_fields()
messages.success(request, u"Added new IP address {} to interface {}.".format(ipaddress, ipaddress.interface))

if form.cleaned_data['set_as_primary']:
if ipaddress.family == 4:
device.primary_ip4 = ipaddress
elif ipaddress.family == 6:
device.primary_ip6 = ipaddress
device.save()

if '_addanother' in request.POST:
return redirect('dcim:ipaddress_assign', pk=device.pk)
else:
return redirect('dcim:device', pk=device.pk)

else:
form = forms.IPAddressForm(device)

return render(request, 'dcim/ipaddress_assign.html', {
'device': device,
'form': form,
'return_url': reverse('dcim:device', kwargs={'pk': device.pk}),
})


#
# Modules
#
Expand Down
5 changes: 2 additions & 3 deletions netbox/generate_secret_key.py
Original file line number Diff line number Diff line change
@@ -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)))
Loading