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 v2.2.2 #1614

Merged
merged 17 commits into from
Oct 17, 2017
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
17 commits
Select commit Hold shift + click to select a range
a01c9ff
Minor LDAP documentation formatting cleanup
mdlayher Sep 29, 2017
881fdbe
Post-release version bump
jeremystretch Oct 12, 2017
40f555a
Merge pull request #1545 from digitalocean/mdl-ldap-docs-formatting
jeremystretch Oct 13, 2017
4cb0be4
Fixes #1582: Add virtual_machine attribute to IPAddress
jeremystretch Oct 13, 2017
6c27e6c
Fixes #1584: Colorized virtual machine role column
jeremystretch Oct 13, 2017
17493ff
Closes #1587: Add primary IP column for virtual machines in global se…
jeremystretch Oct 13, 2017
d00cab0
Merge branch 'develop' of github.com:digitalocean/netbox into develop
jeremystretch Oct 13, 2017
023ff68
Designated new Docker build repo; removed stale Heroku build repo
jeremystretch Oct 13, 2017
60b4f1f
Fixes #1585: Fixed slug-based filtering of virtual machines
jeremystretch Oct 13, 2017
91b6ebb
Closes #1580: Allow cluster assignment when bulk importing devices
jeremystretch Oct 13, 2017
34259d5
Removed deprecated xstr and expand_pattern functions
jeremystretch Oct 13, 2017
5fc3eac
Avoid creating repeated graph nodes where device matches multiple reg…
Oct 16, 2017
047f22e
Fixes #1605: Added clusters and virtual machines to object list for g…
jeremystretch Oct 16, 2017
6ec9d1d
Merge pull request #1598 from candlerb/candlerb/1498
jeremystretch Oct 16, 2017
6f2f869
Fixes #1609: Added missing virtual_machine field to IP address interf…
jeremystretch Oct 17, 2017
34f1a9e
Fixes #1579: Devices already assigned to a cluster cannot be added to…
jeremystretch Oct 17, 2017
2afa6ed
Release v2.2.2
jeremystretch Oct 17, 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
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