Skip to content

Commit

Permalink
Merge pull request #949 from digitalocean/develop
Browse files Browse the repository at this point in the history
Release v1.9.1
  • Loading branch information
jeremystretch authored Mar 8, 2017
2 parents ce26b56 + 094974d commit 097e0f3
Show file tree
Hide file tree
Showing 14 changed files with 130 additions and 104 deletions.
14 changes: 8 additions & 6 deletions netbox/dcim/api/views.py
Original file line number Diff line number Diff line change
Expand Up @@ -265,10 +265,11 @@ class DeviceListView(CustomFieldModelAPIView, generics.ListAPIView):
"""
List devices (filterable)
"""
queryset = Device.objects.select_related('device_type__manufacturer', 'device_role', 'tenant', 'platform',
'rack__site', 'parent_bay').prefetch_related('primary_ip4__nat_outside',
'primary_ip6__nat_outside',
'custom_field_values__field')
queryset = Device.objects.select_related(
'device_type__manufacturer', 'device_role', 'tenant', 'platform', 'site', 'rack', 'parent_bay'
).prefetch_related(
'primary_ip4__nat_outside', 'primary_ip6__nat_outside', 'custom_field_values__field'
)
serializer_class = serializers.DeviceSerializer
filter_class = filters.DeviceFilter
renderer_classes = api_settings.DEFAULT_RENDERER_CLASSES + [BINDZoneRenderer, FlatJSONRenderer]
Expand All @@ -278,8 +279,9 @@ class DeviceDetailView(CustomFieldModelAPIView, generics.RetrieveAPIView):
"""
Retrieve a single device
"""
queryset = Device.objects.select_related('device_type__manufacturer', 'device_role', 'tenant', 'platform',
'rack__site', 'parent_bay').prefetch_related('custom_field_values__field')
queryset = Device.objects.select_related(
'device_type__manufacturer', 'device_role', 'tenant', 'platform', 'site', 'rack', 'parent_bay'
).prefetch_related('custom_field_values__field')
serializer_class = serializers.DeviceSerializer


Expand Down
136 changes: 68 additions & 68 deletions netbox/dcim/forms.py
Original file line number Diff line number Diff line change
Expand Up @@ -934,28 +934,29 @@ def __init__(self, *args, **kwargs):
if not self.instance.pk:
raise RuntimeError("ConsolePortConnectionForm must be initialized with an existing ConsolePort instance.")

self.initial['site'] = self.instance.device.site
self.fields['rack'].queryset = Rack.objects.filter(site=self.instance.device.site)
self.fields['cs_port'].required = True
self.fields['connection_status'].choices = CONNECTION_STATUS_CHOICES

# Initialize console server choices
if self.is_bound and self.data.get('rack'):
self.fields['console_server'].queryset = Device.objects.filter(rack=self.data['rack'],
device_type__is_console_server=True)
elif self.initial.get('rack'):
self.fields['console_server'].queryset = Device.objects.filter(rack=self.initial['rack'],
device_type__is_console_server=True)
# Initialize rack choices if site is set
if self.initial.get('site'):
self.fields['rack'].queryset = Rack.objects.filter(site=self.initial['site'])
else:
self.fields['console_server'].queryset = Device.objects.filter(site=self.instance.device.site,
rack__isnull=True,
device_type__is_console_server=True)
self.fields['rack'].choices = []

# Initialize CS port choices
if self.is_bound:
self.fields['cs_port'].queryset = ConsoleServerPort.objects.filter(device__pk=self.data['console_server'])
elif self.initial.get('console_server', None):
self.fields['cs_port'].queryset = ConsoleServerPort.objects.filter(device__pk=self.initial['console_server'])
# Initialize console_server choices if rack or site is set
if self.initial.get('rack'):
self.fields['console_server'].queryset = Device.objects.filter(
rack=self.initial['rack'], device_type__is_console_server=True
)
elif self.initial.get('site'):
self.fields['console_server'].queryset = Device.objects.filter(
site=self.initial['site'], rack__isnull=True, device_type__is_console_server=True
)
else:
self.fields['console_server'].choices = []

# Initialize CS port choices if console_server is set
if self.initial.get('console_server'):
self.fields['cs_port'].queryset = ConsoleServerPort.objects.filter(
device=self.initial['console_server']
)
else:
self.fields['cs_port'].choices = []

Expand Down Expand Up @@ -1033,27 +1034,27 @@ class Meta:
'connection_status': 'Status',
}

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

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

self.initial['site'] = consoleserverport.device.site
self.fields['rack'].queryset = Rack.objects.filter(site=consoleserverport.device.site)
# Initialize rack choices if site is set
if self.initial.get('site'):
self.fields['rack'].queryset = Rack.objects.filter(site=self.initial['site'])
else:
self.fields['rack'].choices = []

# Initialize device choices
if self.is_bound and self.data.get('rack'):
self.fields['device'].queryset = Device.objects.filter(rack=self.data['rack'])
elif self.initial.get('rack', None):
# Initialize device choices if rack or site is set
if self.initial.get('rack'):
self.fields['device'].queryset = Device.objects.filter(rack=self.initial['rack'])
elif self.initial.get('site'):
self.fields['device'].queryset = Device.objects.filter(site=self.initial['site'], rack__isnull=True)
else:
self.fields['device'].queryset = Device.objects.filter(site=consoleserverport.device.site,
rack__isnull=True)
self.fields['device'].choices = []

# Initialize port choices
if self.is_bound:
self.fields['port'].queryset = ConsolePort.objects.filter(device__pk=self.data['device'])
elif self.initial.get('device', None):
self.fields['port'].queryset = ConsolePort.objects.filter(device_pk=self.initial['device'])
# Initialize port choices if device is set
if self.initial.get('device'):
self.fields['port'].queryset = ConsolePort.objects.filter(device=self.initial['device'])
else:
self.fields['port'].choices = []

Expand Down Expand Up @@ -1201,28 +1202,27 @@ def __init__(self, *args, **kwargs):
if not self.instance.pk:
raise RuntimeError("PowerPortConnectionForm must be initialized with an existing PowerPort instance.")

self.initial['site'] = self.instance.device.site
self.fields['rack'].queryset = Rack.objects.filter(site=self.instance.device.site)
self.fields['power_outlet'].required = True
self.fields['connection_status'].choices = CONNECTION_STATUS_CHOICES

# Initialize PDU choices
if self.is_bound and self.data.get('rack'):
self.fields['pdu'].queryset = Device.objects.filter(rack=self.data['rack'],
device_type__is_pdu=True)
elif self.initial.get('rack', None):
self.fields['pdu'].queryset = Device.objects.filter(rack=self.initial['rack'],
device_type__is_pdu=True)
# Initialize rack choices if site is set
if self.initial.get('site'):
self.fields['rack'].queryset = Rack.objects.filter(site=self.initial['site'])
else:
self.fields['pdu'].queryset = Device.objects.filter(site=self.instance.device.site,
rack__isnull=True,
device_type__is_pdu=True)
self.fields['rack'].choices = []

# Initialize power outlet choices
if self.is_bound:
self.fields['power_outlet'].queryset = PowerOutlet.objects.filter(device__pk=self.data['pdu'])
elif self.initial.get('pdu', None):
self.fields['power_outlet'].queryset = PowerOutlet.objects.filter(device__pk=self.initial['pdu'])
# Initialize pdu choices if rack or site is set
if self.initial.get('rack'):
self.fields['pdu'].queryset = Device.objects.filter(
rack=self.initial['rack'], device_type__is_pdu=True
)
elif self.initial.get('site'):
self.fields['pdu'].queryset = Device.objects.filter(
site=self.initial['site'], rack__isnull=True, device_type__is_pdu=True
)
else:
self.fields['pdu'].choices = []

# Initialize power outlet choices if pdu is set
if self.initial.get('pdu'):
self.fields['power_outlet'].queryset = PowerOutlet.objects.filter(device=self.initial['pdu'])
else:
self.fields['power_outlet'].choices = []

Expand Down Expand Up @@ -1300,27 +1300,27 @@ class Meta:
'connection_status': 'Status',
}

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

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

self.initial['site'] = poweroutlet.device.site
self.fields['rack'].queryset = Rack.objects.filter(site=poweroutlet.device.site)
# Initialize rack choices if site is set
if self.initial.get('site'):
self.fields['rack'].queryset = Rack.objects.filter(site=self.initial['site'])
else:
self.fields['rack'].choices = []

# Initialize device choices
if self.is_bound and self.data.get('rack'):
self.fields['device'].queryset = Device.objects.filter(rack=self.data['rack'])
elif self.initial.get('rack', None):
# Initialize device choices if rack or site is set
if self.initial.get('rack'):
self.fields['device'].queryset = Device.objects.filter(rack=self.initial['rack'])
elif self.initial.get('site'):
self.fields['device'].queryset = Device.objects.filter(site=self.initial['site'], rack__isnull=True)
else:
self.fields['device'].queryset = Device.objects.filter(site=poweroutlet.device.site,
rack__isnull=True)
self.fields['device'].choices = []

# Initialize port choices
if self.is_bound:
self.fields['port'].queryset = PowerPort.objects.filter(device__pk=self.data['device'])
elif self.initial.get('device', None):
self.fields['port'].queryset = PowerPort.objects.filter(device_pk=self.initial['device'])
# Initialize port choices if device is set
if self.initial.get('device'):
self.fields['port'].queryset = PowerPort.objects.filter(device=self.initial['device'])
else:
self.fields['port'].choices = []

Expand Down
2 changes: 1 addition & 1 deletion netbox/dcim/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -293,7 +293,7 @@ def count_racks(self):

@property
def count_devices(self):
return Device.objects.filter(rack__site=self).count()
return Device.objects.filter(site=self).count()

@property
def count_circuits(self):
Expand Down
4 changes: 2 additions & 2 deletions netbox/dcim/tables.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,11 +12,11 @@

REGION_LINK = """
{% if record.get_children %}
<span style="padding-left: {{ record.get_ancestors|length }}0px "><i class="fa fa-caret-right"></i></a>
<span style="padding-left: {{ record.get_ancestors|length }}0px "><i class="fa fa-caret-right"></i>
{% else %}
<span style="padding-left: {{ record.get_ancestors|length }}9px">
{% endif %}
{{ record.name }}
<a href="{% url 'dcim:site_list' %}?region={{ record.slug }}">{{ record.name }}</a>
</span>
"""

Expand Down
28 changes: 21 additions & 7 deletions netbox/dcim/views.py
Original file line number Diff line number Diff line change
Expand Up @@ -171,7 +171,7 @@ def site(request, slug):
site = get_object_or_404(Site.objects.select_related('region', 'tenant__group'), slug=slug)
stats = {
'rack_count': Rack.objects.filter(site=site).count(),
'device_count': Device.objects.filter(rack__site=site).count(),
'device_count': Device.objects.filter(site=site).count(),
'prefix_count': Prefix.objects.filter(site=site).count(),
'vlan_count': VLAN.objects.filter(site=site).count(),
'circuit_count': Circuit.objects.filter(terminations__site=site).count(),
Expand Down Expand Up @@ -844,7 +844,9 @@ def consoleport_connect(request, pk):

else:
form = forms.ConsolePortConnectionForm(instance=consoleport, initial={
'rack': consoleport.device.rack,
'site': request.GET.get('site', consoleport.device.site),
'rack': request.GET.get('rack', None),
'console_server': request.GET.get('console_server', None),
'connection_status': CONNECTION_STATUS_CONNECTED,
})

Expand Down Expand Up @@ -927,7 +929,7 @@ def consoleserverport_connect(request, pk):
consoleserverport = get_object_or_404(ConsoleServerPort, pk=pk)

if request.method == 'POST':
form = forms.ConsoleServerPortConnectionForm(consoleserverport, request.POST)
form = forms.ConsoleServerPortConnectionForm(request.POST)
if form.is_valid():
consoleport = form.cleaned_data['port']
consoleport.cs_port = consoleserverport
Expand All @@ -942,7 +944,12 @@ def consoleserverport_connect(request, pk):
return redirect('dcim:device', pk=consoleserverport.device.pk)

else:
form = forms.ConsoleServerPortConnectionForm(consoleserverport, initial={'rack': consoleserverport.device.rack})
form = forms.ConsoleServerPortConnectionForm(initial={
'site': request.GET.get('site', consoleserverport.device.site),
'rack': request.GET.get('rack', None),
'device': request.GET.get('device', None),
'connection_status': CONNECTION_STATUS_CONNECTED,
})

return render(request, 'dcim/consoleserverport_connect.html', {
'consoleserverport': consoleserverport,
Expand Down Expand Up @@ -1030,7 +1037,9 @@ def powerport_connect(request, pk):

else:
form = forms.PowerPortConnectionForm(instance=powerport, initial={
'rack': powerport.device.rack,
'site': request.GET.get('site', powerport.device.site),
'rack': request.GET.get('rack', None),
'pdu': request.GET.get('pdu', None),
'connection_status': CONNECTION_STATUS_CONNECTED,
})

Expand Down Expand Up @@ -1113,7 +1122,7 @@ def poweroutlet_connect(request, pk):
poweroutlet = get_object_or_404(PowerOutlet, pk=pk)

if request.method == 'POST':
form = forms.PowerOutletConnectionForm(poweroutlet, request.POST)
form = forms.PowerOutletConnectionForm(request.POST)
if form.is_valid():
powerport = form.cleaned_data['port']
powerport.power_outlet = poweroutlet
Expand All @@ -1128,7 +1137,12 @@ def poweroutlet_connect(request, pk):
return redirect('dcim:device', pk=poweroutlet.device.pk)

else:
form = forms.PowerOutletConnectionForm(poweroutlet, initial={'rack': poweroutlet.device.rack})
form = forms.PowerOutletConnectionForm(initial={
'site': request.GET.get('site', poweroutlet.device.site),
'rack': request.GET.get('rack', None),
'device': request.GET.get('device', None),
'connection_status': CONNECTION_STATUS_CONNECTED,
})

return render(request, 'dcim/poweroutlet_connect.html', {
'poweroutlet': poweroutlet,
Expand Down
2 changes: 1 addition & 1 deletion netbox/extras/management/commands/run_inventory.py
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,7 @@ def create_modules(modules, parent=None):
self.stdout.write("Running inventory for these sites: {}".format(', '.join(site_names)))
else:
raise CommandError("One or more sites specified but none found.")
device_list = device_list.filter(rack__site__in=sites)
device_list = device_list.filter(site__in=sites)

# --name: Filter devices by name matching a regex
if options['name']:
Expand Down
4 changes: 2 additions & 2 deletions netbox/ipam/fields.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
from .formfields import IPFormField
from .lookups import (
EndsWith, IEndsWith, IRegex, IStartsWith, NetContained, NetContainedOrEqual, NetContains, NetContainsOrEquals,
NetHost, NetMaskLength, Regex, StartsWith,
NetHost, NetHostContained, NetMaskLength, Regex, StartsWith,
)


Expand Down Expand Up @@ -66,7 +66,6 @@ def db_type(self, connection):
IPNetworkField.register_lookup(NetContainedOrEqual)
IPNetworkField.register_lookup(NetContains)
IPNetworkField.register_lookup(NetContainsOrEquals)
IPNetworkField.register_lookup(NetHost)
IPNetworkField.register_lookup(NetMaskLength)


Expand All @@ -91,4 +90,5 @@ def db_type(self, connection):
IPAddressField.register_lookup(NetContains)
IPAddressField.register_lookup(NetContainsOrEquals)
IPAddressField.register_lookup(NetHost)
IPAddressField.register_lookup(NetHostContained)
IPAddressField.register_lookup(NetMaskLength)
2 changes: 1 addition & 1 deletion netbox/ipam/filters.py
Original file line number Diff line number Diff line change
Expand Up @@ -254,7 +254,7 @@ def search_by_parent(self, queryset, name, value):
return queryset
try:
query = str(IPNetwork(value.strip()).cidr)
return queryset.filter(address__net_contained_or_equal=query)
return queryset.filter(address__net_host_contained=query)
except AddrFormatError:
return queryset.none()

Expand Down
10 changes: 6 additions & 4 deletions netbox/ipam/forms.py
Original file line number Diff line number Diff line change
Expand Up @@ -333,19 +333,21 @@ def __init__(self, *args, **kwargs):
self.initial['nat_site'] = self.instance.nat_inside.interface.device.site.pk
self.initial['nat_device'] = self.instance.nat_inside.interface.device.pk
self.fields['nat_device'].queryset = Device.objects.filter(
rack__site=nat_inside.interface.device.site)
site=nat_inside.interface.device.site
)
self.fields['nat_inside'].queryset = IPAddress.objects.filter(
interface__device=nat_inside.interface.device)
interface__device=nat_inside.interface.device
)
else:
self.fields['nat_inside'].queryset = IPAddress.objects.filter(pk=nat_inside.pk)

else:

# Initialize nat_device choices if nat_site is set
if self.is_bound and self.data.get('nat_site'):
self.fields['nat_device'].queryset = Device.objects.filter(rack__site__pk=self.data['nat_site'])
self.fields['nat_device'].queryset = Device.objects.filter(site__pk=self.data['nat_site'])
elif self.initial.get('nat_site'):
self.fields['nat_device'].queryset = Device.objects.filter(rack__site=self.initial['nat_site'])
self.fields['nat_device'].queryset = Device.objects.filter(site=self.initial['nat_site'])
else:
self.fields['nat_device'].choices = []

Expand Down
14 changes: 14 additions & 0 deletions netbox/ipam/lookups.py
Original file line number Diff line number Diff line change
Expand Up @@ -89,6 +89,20 @@ def as_sql(self, qn, connection):
return 'HOST(%s) = %s' % (lhs, rhs), params


class NetHostContained(Lookup):
"""
Check for the host portion of an IP address without regard to its mask. This allows us to find e.g. 192.0.2.1/24
when specifying a parent prefix of 192.0.2.0/26.
"""
lookup_name = 'net_host_contained'

def as_sql(self, qn, connection):
lhs, lhs_params = self.process_lhs(qn, connection)
rhs, rhs_params = self.process_rhs(qn, connection)
params = lhs_params + rhs_params
return 'CAST(HOST(%s) AS INET) << %s' % (lhs, rhs), params


class NetMaskLength(Transform):
lookup_name = 'net_mask_length'
function = 'MASKLEN'
Expand Down
Loading

0 comments on commit 097e0f3

Please sign in to comment.