Skip to content

Commit

Permalink
Merge pull request #1614 from digitalocean/develop
Browse files Browse the repository at this point in the history
Release v2.2.2
  • Loading branch information
jeremystretch authored Oct 17, 2017
2 parents 2bda399 + 2afa6ed commit 7a64404
Show file tree
Hide file tree
Showing 15 changed files with 71 additions and 46 deletions.
5 changes: 2 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,5 @@ Please see [the documentation](http://netbox.readthedocs.io/en/stable/) for inst

## Alternative Installations

* [Docker container](https://github.com/digitalocean/netbox-docker)
* [Heroku deployment](https://heroku.com/deploy?template=https://github.com/BILDQUADRAT/netbox/tree/heroku) (via [@mraerino](https://github.com/BILDQUADRAT/netbox/tree/heroku))
* [Vagrant deployment](https://github.com/ryanmerolle/netbox-vagrant)
* [Docker container](https://github.com/ninech/netbox-docker) (via [@cimnine](https://github.com/cimnine))
* [Vagrant deployment](https://github.com/ryanmerolle/netbox-vagrant) (via [@ryanmerolle](https://github.com/ryanmerolle))
4 changes: 2 additions & 2 deletions docs/installation/ldap.md
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,7 @@ LDAP_IGNORE_CERT_ERRORS = True
## User Authentication

!!! info
When using Windows Server, `2012 AUTH_LDAP_USER_DN_TEMPLATE` should be set to None.
When using Windows Server 2012, `AUTH_LDAP_USER_DN_TEMPLATE` should be set to None.

```python
from django_auth_ldap.config import LDAPSearch
Expand All @@ -79,7 +79,7 @@ AUTH_LDAP_USER_ATTR_MAP = {

# User Groups for Permissions
!!! Info
When using Microsoft Active Directory, Support for nested Groups can be activated by using `GroupOfNamesType()` instead of `NestedGroupOfNamesType()` for AUTH_LDAP_GROUP_TYPE.
When using Microsoft Active Directory, Support for nested Groups can be activated by using `GroupOfNamesType()` instead of `NestedGroupOfNamesType()` for `AUTH_LDAP_GROUP_TYPE`.

```python
from django_auth_ldap.config import LDAPSearch, GroupOfNamesType
Expand Down
22 changes: 20 additions & 2 deletions netbox/dcim/forms.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
ExpandableNameField, FilterChoiceField, FlexibleModelChoiceField, Livesearch, SelectWithDisabled, SmallTextarea,
SlugField, FilterTreeNodeMultipleChoiceField,
)
from virtualization.models import Cluster
from .formfields import MACAddressFormField
from .models import (
DeviceBay, DeviceBayTemplate, CONNECTION_STATUS_CHOICES, CONNECTION_STATUS_CONNECTED, ConsolePort,
Expand Down Expand Up @@ -900,11 +901,20 @@ class DeviceCSVForm(BaseDeviceCSVForm):
required=False,
help_text='Mounted rack face'
)
cluster = forms.ModelChoiceField(
queryset=Cluster.objects.all(),
to_field_name='name',
required=False,
help_text='Virtualization cluster',
error_messages={
'invalid_choice': 'Invalid cluster name.',
}
)

class Meta(BaseDeviceCSVForm.Meta):
fields = [
'name', 'device_role', 'tenant', 'manufacturer', 'model_name', 'platform', 'serial', 'asset_tag', 'status',
'site', 'rack_group', 'rack_name', 'position', 'face',
'site', 'rack_group', 'rack_name', 'position', 'face', 'cluster',
]

def clean(self):
Expand Down Expand Up @@ -940,11 +950,19 @@ class ChildDeviceCSVForm(BaseDeviceCSVForm):
device_bay_name = forms.CharField(
help_text='Name of device bay',
)
cluster = forms.ModelChoiceField(
queryset=Cluster.objects.all(),
to_field_name='name',
help_text='Virtualization cluster',
error_messages={
'invalid_choice': 'Invalid cluster name.',
}
)

class Meta(BaseDeviceCSVForm.Meta):
fields = [
'name', 'device_role', 'tenant', 'manufacturer', 'model_name', 'platform', 'serial', 'asset_tag', 'status',
'parent', 'device_bay_name',
'parent', 'device_bay_name', 'cluster',
]

def clean(self):
Expand Down
26 changes: 0 additions & 26 deletions netbox/dcim/views.py
Original file line number Diff line number Diff line change
Expand Up @@ -34,32 +34,6 @@
)


EXPANSION_PATTERN = '\[(\d+-\d+)\]'


def xstr(s):
"""
Replace None with an empty string (for CSV export)
"""
return '' if s is None else str(s)


def expand_pattern(string):
"""
Expand a numeric pattern into a list of strings. Examples:
'ge-0/0/[0-3]' => ['ge-0/0/0', 'ge-0/0/1', 'ge-0/0/2', 'ge-0/0/3']
'xe-0/[0-3]/[0-7]' => ['xe-0/0/0', 'xe-0/0/1', 'xe-0/0/2', ... 'xe-0/3/5', 'xe-0/3/6', 'xe-0/3/7']
"""
lead, pattern, remnant = re.split(EXPANSION_PATTERN, string, maxsplit=1)
x, y = pattern.split('-')
for i in range(int(x), int(y) + 1):
if remnant:
for string in expand_pattern(remnant):
yield "{0}{1}{2}".format(lead, i, string)
else:
yield "{0}{1}".format(lead, i)


class BulkDisconnectView(View):
"""
An extendable view for disconnection console/power/interface components in bulk.
Expand Down
4 changes: 4 additions & 0 deletions netbox/extras/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -274,6 +274,7 @@ def render(self, img_format='png'):
# Construct the graph
graph = graphviz.Graph()
graph.graph_attr['ranksep'] = '1'
seen = set()
for i, device_set in enumerate(self.device_sets):

subgraph = graphviz.Graph(name='sg{}'.format(i))
Expand All @@ -288,6 +289,9 @@ def render(self, img_format='png'):
devices = []
for query in device_set.strip(';').split(';'): # Split regexes on semicolons
devices += Device.objects.filter(name__regex=query).select_related('device_role')
# Remove duplicate devices
devices = [d for d in devices if d.id not in seen]
seen.update([d.id for d in devices])
for d in devices:
bg_color = '#{}'.format(d.device_role.color)
fg_color = '#{}'.format(foreground_color(d.device_role.color))
Expand Down
13 changes: 12 additions & 1 deletion netbox/ipam/api/serializers.py
Original file line number Diff line number Diff line change
Expand Up @@ -240,12 +240,22 @@ class Meta:
# IP addresses
#

class IPAddressInterfaceSerializer(InterfaceSerializer):
virtual_machine = NestedVirtualMachineSerializer()

class Meta(InterfaceSerializer.Meta):
fields = [
'id', 'device', 'virtual_machine', 'name', 'form_factor', 'enabled', 'lag', 'mtu', 'mac_address',
'mgmt_only', 'description', 'is_connected', 'interface_connection', 'circuit_termination',
]


class IPAddressSerializer(CustomFieldModelSerializer):
vrf = NestedVRFSerializer()
tenant = NestedTenantSerializer()
status = ChoiceFieldSerializer(choices=IPADDRESS_STATUS_CHOICES)
role = ChoiceFieldSerializer(choices=IPADDRESS_ROLE_CHOICES)
interface = InterfaceSerializer()
interface = IPAddressInterfaceSerializer()

class Meta:
model = IPAddress
Expand All @@ -262,6 +272,7 @@ class Meta:
model = IPAddress
fields = ['id', 'url', 'family', 'address']


IPAddressSerializer._declared_fields['nat_inside'] = NestedIPAddressSerializer()
IPAddressSerializer._declared_fields['nat_outside'] = NestedIPAddressSerializer()

Expand Down
2 changes: 1 addition & 1 deletion netbox/ipam/api/views.py
Original file line number Diff line number Diff line change
Expand Up @@ -151,7 +151,7 @@ class IPAddressViewSet(WritableSerializerMixin, CustomFieldModelViewSet):
queryset = IPAddress.objects.select_related(
'vrf__tenant', 'tenant', 'nat_inside'
).prefetch_related(
'interface__device'
'interface__device', 'interface__virtual_machine'
)
serializer_class = serializers.IPAddressSerializer
write_serializer_class = serializers.WritableIPAddressSerializer
Expand Down
8 changes: 7 additions & 1 deletion netbox/ipam/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -440,7 +440,7 @@ def to_csv(self):
self.get_status_display(),
self.get_role_display(),
self.device.identifier if self.device else None,
self.virtual_machine.name if self.device else None,
self.virtual_machine.name if self.virtual_machine else None,
self.interface.name if self.interface else None,
is_primary,
self.description,
Expand All @@ -452,6 +452,12 @@ def device(self):
return self.interface.device
return None

@property
def virtual_machine(self):
if self.interface:
return self.interface.virtual_machine
return None

def get_status_class(self):
return STATUS_CHOICE_CLASSES[self.status]

Expand Down
4 changes: 4 additions & 0 deletions netbox/netbox/forms.py
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,10 @@
('Tenancy', (
('tenant', 'Tenants'),
)),
('Virtualization', (
('cluster', 'Clusters'),
('virtualmachine', 'Virtual machines'),
)),
)


Expand Down
2 changes: 1 addition & 1 deletion netbox/netbox/settings.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@
)


VERSION = '2.2.1'
VERSION = '2.2.2'

BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))

Expand Down
8 changes: 5 additions & 3 deletions netbox/netbox/views.py
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@
from tenancy.tables import TenantTable
from virtualization.filters import ClusterFilter, VirtualMachineFilter
from virtualization.models import Cluster, VirtualMachine
from virtualization.tables import ClusterTable, VirtualMachineTable
from virtualization.tables import ClusterTable, VirtualMachineDetailTable
from .forms import SearchForm


Expand Down Expand Up @@ -126,9 +126,11 @@
'url': 'virtualization:cluster_list',
}),
('virtualmachine', {
'queryset': VirtualMachine.objects.select_related('cluster', 'tenant', 'platform'),
'queryset': VirtualMachine.objects.select_related(
'cluster', 'tenant', 'platform', 'primary_ip4', 'primary_ip6',
),
'filter': VirtualMachineFilter,
'table': VirtualMachineTable,
'table': VirtualMachineDetailTable,
'url': 'virtualization:virtualmachine_list',
}),
))
Expand Down
7 changes: 6 additions & 1 deletion netbox/templates/virtualization/cluster_add_devices.html
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,7 @@ <h3>{% block title %}Add Devices to Cluster {{ cluster }}{% endblock %}</h3>
<script type="text/javascript">
$(document).ready(function() {
var device_list = $('#id_devices');
var disabled_indicator = device_list.attr('disabled-indicator');
$('#id_search').autocomplete({
source: function(request, response) {
$.ajax({
Expand All @@ -70,7 +71,11 @@ <h3>{% block title %}Add Devices to Cluster {{ cluster }}{% endblock %}</h3>
},
success: function(data) {
response($.map(data.results, function(item) {
device_list.append('<option value="' + item['id'] + '">' + item['display_name'] + '</option>');
var option = $("<option></option>").attr("value", item['id']).text(item['display_name']);
if (disabled_indicator && item[disabled_indicator]) {
option.attr("disabled", "disabled");
}
device_list.append(option);
}));
}
});
Expand Down
5 changes: 1 addition & 4 deletions netbox/virtualization/filters.py
Original file line number Diff line number Diff line change
Expand Up @@ -78,7 +78,7 @@ class VirtualMachineFilter(CustomFieldFilterSet):
label='Cluster group (ID)',
)
cluster_group = NullableModelMultipleChoiceFilter(
name='cluster__group__slug',
name='cluster__group',
queryset=ClusterGroup.objects.all(),
to_field_name='slug',
label='Cluster group (slug)',
Expand All @@ -88,12 +88,10 @@ class VirtualMachineFilter(CustomFieldFilterSet):
label='Cluster (ID)',
)
role_id = NullableModelMultipleChoiceFilter(
name='role_id',
queryset=DeviceRole.objects.all(),
label='Role (ID)',
)
role = NullableModelMultipleChoiceFilter(
name='role__slug',
queryset=DeviceRole.objects.all(),
to_field_name='slug',
label='Role (slug)',
Expand All @@ -112,7 +110,6 @@ class VirtualMachineFilter(CustomFieldFilterSet):
label='Platform (ID)',
)
platform = NullableModelMultipleChoiceFilter(
name='platform',
queryset=Platform.objects.all(),
to_field_name='slug',
label='Platform (slug)',
Expand Down
2 changes: 1 addition & 1 deletion netbox/virtualization/forms.py
Original file line number Diff line number Diff line change
Expand Up @@ -210,7 +210,7 @@ def clean(self):

# If the Cluster is assigned to a Site, all Devices must be assigned to that Site.
if self.cluster.site is not None:
for device in self.cleaned_data.get('devices'):
for device in self.cleaned_data.get('devices', []):
if device.site != self.cluster.site:
raise ValidationError({
'devices': "{} belongs to a different site ({}) than the cluster ({})".format(
Expand Down
5 changes: 5 additions & 0 deletions netbox/virtualization/tables.py
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,10 @@
<span class="label label-{{ record.get_status_class }}">{{ record.get_status_display }}</span>
"""

VIRTUALMACHINE_ROLE = """
<label class="label" style="background-color: #{{ record.role.color }}">{{ value }}</label>
"""

VIRTUALMACHINE_PRIMARY_IP = """
{{ record.primary_ip6.address.ip|default:"" }}
{% if record.primary_ip6 and record.primary_ip4 %}<br />{% endif %}
Expand Down Expand Up @@ -93,6 +97,7 @@ class VirtualMachineTable(BaseTable):
name = tables.LinkColumn()
status = tables.TemplateColumn(template_code=VIRTUALMACHINE_STATUS)
cluster = tables.LinkColumn('virtualization:cluster', args=[Accessor('cluster.pk')])
role = tables.TemplateColumn(VIRTUALMACHINE_ROLE)
tenant = tables.LinkColumn('tenancy:tenant', args=[Accessor('tenant.slug')])

class Meta(BaseTable.Meta):
Expand Down

0 comments on commit 7a64404

Please sign in to comment.