From d68b34cefeeff72576478eb0920956064e8d8559 Mon Sep 17 00:00:00 2001 From: Peter Russell Date: Wed, 8 Mar 2017 13:30:20 +0000 Subject: [PATCH 1/7] Force Unix line endings on shell scripts When following the quickstart docker instructions on Windows (using Docker for Windows), an error is encountered when running the netbox container. This is caused by git converting the line endings of the docker-entrypoint.sh script to Windows format, which are then copied into the container image. By setting .gitattributes, we force LF rather than CRLF line endings on shell scripts on Windows. Other files are left as is. --- .gitattributes | 1 + 1 file changed, 1 insertion(+) create mode 100644 .gitattributes diff --git a/.gitattributes b/.gitattributes new file mode 100644 index 000000000..dfdb8b771 --- /dev/null +++ b/.gitattributes @@ -0,0 +1 @@ +*.sh text eol=lf From 87ff433ef8f68d9f111569ee3def40a324d17b88 Mon Sep 17 00:00:00 2001 From: Jeremy Stretch Date: Mon, 13 Mar 2017 10:06:32 -0400 Subject: [PATCH 2/7] Fixes #956: Correct bug affecting unnamed rackless devices --- netbox/dcim/models.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/netbox/dcim/models.py b/netbox/dcim/models.py index 03d5b2587..b7a0b4492 100644 --- a/netbox/dcim/models.py +++ b/netbox/dcim/models.py @@ -1059,8 +1059,10 @@ class Device(CreatedUpdatedModel, CustomFieldModel): return self.name elif self.position: return u"{} ({} U{})".format(self.device_type, self.rack.name, self.position) - else: + elif self.rack: return u"{} ({})".format(self.device_type, self.rack.name) + else: + return u"{} ({})".format(self.device_type, self.site.name) @property def identifier(self): From 6a2a2d5d11708af275f605e6a0a78d7e55fca845 Mon Sep 17 00:00:00 2001 From: Jeremy Stretch Date: Mon, 13 Mar 2017 10:13:04 -0400 Subject: [PATCH 3/7] Fixes #957: Correct device site filter count to include unracked devices --- netbox/dcim/forms.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/netbox/dcim/forms.py b/netbox/dcim/forms.py index dc2b296ee..9bfe8e84d 100644 --- a/netbox/dcim/forms.py +++ b/netbox/dcim/forms.py @@ -733,7 +733,7 @@ class DeviceFilterForm(BootstrapMixin, CustomFieldFilterForm): model = Device q = forms.CharField(required=False, label='Search') site = FilterChoiceField( - queryset=Site.objects.annotate(filter_count=Count('racks__devices')), + queryset=Site.objects.annotate(filter_count=Count('devices')), to_field_name='slug', ) rack_group_id = FilterChoiceField( From f661c233be6144a943aed1310367bf38207373dc Mon Sep 17 00:00:00 2001 From: Jeremy Stretch Date: Mon, 13 Mar 2017 11:18:33 -0400 Subject: [PATCH 4/7] Fixes #950: Fix site_id error on child device import --- netbox/dcim/forms.py | 16 ++++++++++++---- netbox/dcim/models.py | 21 +++++++++++---------- netbox/dcim/views.py | 5 ++++- 3 files changed, 27 insertions(+), 15 deletions(-) diff --git a/netbox/dcim/forms.py b/netbox/dcim/forms.py index 9bfe8e84d..abe98e51a 100644 --- a/netbox/dcim/forms.py +++ b/netbox/dcim/forms.py @@ -680,13 +680,21 @@ class DeviceFromCSVForm(BaseDeviceFromCSVForm): class ChildDeviceFromCSVForm(BaseDeviceFromCSVForm): - parent = FlexibleModelChoiceField(queryset=Device.objects.all(), to_field_name='name', required=False, - error_messages={'invalid_choice': 'Parent device not found.'}) + parent = FlexibleModelChoiceField( + queryset=Device.objects.all(), + to_field_name='name', + required=False, + error_messages={ + 'invalid_choice': 'Parent device not found.' + } + ) device_bay_name = forms.CharField(required=False) class Meta(BaseDeviceFromCSVForm.Meta): - fields = ['name', 'device_role', 'tenant', 'manufacturer', 'model_name', 'platform', 'serial', 'asset_tag', - 'parent', 'device_bay_name'] + fields = [ + 'name', 'device_role', 'tenant', 'manufacturer', 'model_name', 'platform', 'serial', 'asset_tag', 'parent', + 'device_bay_name', + ] def clean(self): diff --git a/netbox/dcim/models.py b/netbox/dcim/models.py index b7a0b4492..73678ae1c 100644 --- a/netbox/dcim/models.py +++ b/netbox/dcim/models.py @@ -975,25 +975,26 @@ class Device(CreatedUpdatedModel, CustomFieldModel): # Child devices cannot be assigned to a rack face/unit if self.device_type.is_child_device and self.face is not None: raise ValidationError({ - 'face': "Child device types cannot be assigned to a rack face. This is an attribute of the parent " - "device." + 'face': "Child device types cannot be assigned to a rack face. This is an attribute of the " + "parent device." }) if self.device_type.is_child_device and self.position: raise ValidationError({ - 'position': "Child device types cannot be assigned to a rack position. This is an attribute of the " - "parent device." + 'position': "Child device types cannot be assigned to a rack position. This is an attribute of " + "the parent device." }) # Validate rack space rack_face = self.face if not self.device_type.is_full_depth else None exclude_list = [self.pk] if self.pk else [] try: - available_units = self.rack.get_available_units(u_height=self.device_type.u_height, rack_face=rack_face, - exclude=exclude_list) + available_units = self.rack.get_available_units( + u_height=self.device_type.u_height, rack_face=rack_face, exclude=exclude_list + ) if self.position and self.position not in available_units: raise ValidationError({ - 'position': "U{} is already occupied or does not have sufficient space to accommodate a(n) {} " - "({}U).".format(self.position, self.device_type, self.device_type.u_height) + 'position': "U{} is already occupied or does not have sufficient space to accommodate a(n) " + "{} ({}U).".format(self.position, self.device_type, self.device_type.u_height) }) except Rack.DoesNotExist: pass @@ -1034,8 +1035,8 @@ class Device(CreatedUpdatedModel, CustomFieldModel): self.device_type.device_bay_templates.all()] ) - # Update Rack assignment for any child Devices - Device.objects.filter(parent_bay__device=self).update(rack=self.rack) + # Update Site and Rack assignment for any child Devices + Device.objects.filter(parent_bay__device=self).update(site=self.site, rack=self.rack) def to_csv(self): return csv_format([ diff --git a/netbox/dcim/views.py b/netbox/dcim/views.py index 73de16e53..cb307324e 100644 --- a/netbox/dcim/views.py +++ b/netbox/dcim/views.py @@ -763,9 +763,12 @@ class ChildDeviceBulkImportView(PermissionRequiredMixin, BulkImportView): default_return_url = 'dcim:device_list' def save_obj(self, obj): - # Inherent rack from parent device + + # Inherit site and rack from parent device + obj.site = obj.parent_bay.device.site obj.rack = obj.parent_bay.device.rack obj.save() + # Save the reverse relation device_bay = obj.parent_bay device_bay.installed_device = obj From cd97b2fb96a5c0d70a9648d2f2d7bb8d4375ec31 Mon Sep 17 00:00:00 2001 From: Jeremy Stretch Date: Mon, 13 Mar 2017 11:25:06 -0400 Subject: [PATCH 5/7] Fix parent device position display --- netbox/templates/dcim/device.html | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/netbox/templates/dcim/device.html b/netbox/templates/dcim/device.html index 081397774..d38f60cb3 100644 --- a/netbox/templates/dcim/device.html +++ b/netbox/templates/dcim/device.html @@ -43,8 +43,10 @@ {% if device.parent_bay %} {% with device.parent_bay.device as parent %} - U{{ parent.position }} / {{ parent.get_face_display }} - ({{ parent }} - {{ device.parent_bay.name }}) + {{ parent }} {{ device.parent_bay.name }} + {% if parent.position %} + (U{{ parent.position }} / {{ parent.get_face_display }}) + {% endif %} {% endwith %} {% elif device.rack and device.position %} U{{ device.position }} / {{ device.get_face_display }} From f3fd82a24a0c7ccb68455d810b4fa4ddc0d9322b Mon Sep 17 00:00:00 2001 From: Jeremy Stretch Date: Mon, 13 Mar 2017 11:31:28 -0400 Subject: [PATCH 6/7] Allow assigning child devices to rackless parents --- netbox/dcim/forms.py | 21 ++++++++++++--------- 1 file changed, 12 insertions(+), 9 deletions(-) diff --git a/netbox/dcim/forms.py b/netbox/dcim/forms.py index abe98e51a..c79f65d53 100644 --- a/netbox/dcim/forms.py +++ b/netbox/dcim/forms.py @@ -1618,20 +1618,23 @@ class DeviceBayCreateForm(DeviceComponentForm): class PopulateDeviceBayForm(BootstrapMixin, forms.Form): - installed_device = forms.ModelChoiceField(queryset=Device.objects.all(), label='Child Device', - help_text="Child devices must first be created within the rack occupied " - "by the parent device. Then they can be assigned to a bay.") + installed_device = forms.ModelChoiceField( + queryset=Device.objects.all(), + label='Child Device', + help_text="Child devices must first be created and assigned to the site/rack of the parent device." + ) def __init__(self, device_bay, *args, **kwargs): super(PopulateDeviceBayForm, self).__init__(*args, **kwargs) - children_queryset = Device.objects.filter(rack=device_bay.device.rack, - parent_bay__isnull=True, - device_type__u_height=0, - device_type__subdevice_role=SUBDEVICE_ROLE_CHILD)\ - .exclude(pk=device_bay.device.pk) - self.fields['installed_device'].queryset = children_queryset + self.fields['installed_device'].queryset = Device.objects.filter( + site=device_bay.device.site, + rack=device_bay.device.rack, + parent_bay__isnull=True, + device_type__u_height=0, + device_type__subdevice_role=SUBDEVICE_ROLE_CHILD + ).exclude(pk=device_bay.device.pk) # From d9f1bcbf153d9e4a781d12cc166091ce453189dd Mon Sep 17 00:00:00 2001 From: Jeremy Stretch Date: Tue, 14 Mar 2017 12:36:44 -0400 Subject: [PATCH 7/7] Renamed user URL namespace --- netbox/netbox/urls.py | 2 +- netbox/secrets/decorators.py | 4 ++-- netbox/templates/_base.html | 2 +- netbox/templates/users/_user.html | 8 ++++---- netbox/templates/users/change_password.html | 2 +- netbox/templates/users/userkey.html | 4 ++-- netbox/templates/users/userkey_edit.html | 2 +- netbox/users/urls.py | 8 ++++---- netbox/users/views.py | 4 ++-- 9 files changed, 18 insertions(+), 18 deletions(-) diff --git a/netbox/netbox/urls.py b/netbox/netbox/urls.py index bbfdee58d..9b1f81dad 100644 --- a/netbox/netbox/urls.py +++ b/netbox/netbox/urls.py @@ -23,7 +23,7 @@ _patterns = [ url(r'^ipam/', include('ipam.urls', namespace='ipam')), url(r'^secrets/', include('secrets.urls', namespace='secrets')), url(r'^tenancy/', include('tenancy.urls', namespace='tenancy')), - url(r'^profile/', include('users.urls', namespace='users')), + url(r'^user/', include('users.urls', namespace='user')), # API url(r'^api/circuits/', include('circuits.api.urls', namespace='circuits-api')), diff --git a/netbox/secrets/decorators.py b/netbox/secrets/decorators.py index 41af204da..683805124 100644 --- a/netbox/secrets/decorators.py +++ b/netbox/secrets/decorators.py @@ -15,10 +15,10 @@ def userkey_required(): uk = UserKey.objects.get(user=request.user) except UserKey.DoesNotExist: messages.warning(request, u"This operation requires an active user key, but you don't have one.") - return redirect('users:userkey') + return redirect('user:userkey') if not uk.is_active(): messages.warning(request, u"This operation is not available. Your user key has not been activated.") - return redirect('users:userkey') + return redirect('user:userkey') return view(request, *args, **kwargs) return wrapped_view return _decorator diff --git a/netbox/templates/_base.html b/netbox/templates/_base.html index d1090a5fb..6f0dfced6 100644 --- a/netbox/templates/_base.html +++ b/netbox/templates/_base.html @@ -245,7 +245,7 @@ {% if request.user.is_staff %}
  • Admin
  • {% endif %} -
  • {{ request.user }}
  • +
  • {{ request.user }}
  • Log out
  • {% else %}
  • Log in
  • diff --git a/netbox/templates/users/_user.html b/netbox/templates/users/_user.html index 01978a59e..a2e18daed 100644 --- a/netbox/templates/users/_user.html +++ b/netbox/templates/users/_user.html @@ -9,10 +9,10 @@
    diff --git a/netbox/templates/users/change_password.html b/netbox/templates/users/change_password.html index 80528c981..700bf682d 100644 --- a/netbox/templates/users/change_password.html +++ b/netbox/templates/users/change_password.html @@ -24,7 +24,7 @@
    - Cancel + Cancel
    {% endblock %} diff --git a/netbox/templates/users/userkey.html b/netbox/templates/users/userkey.html index 08c519c2d..df5e55be9 100644 --- a/netbox/templates/users/userkey.html +++ b/netbox/templates/users/userkey.html @@ -15,7 +15,7 @@

    Your public key is below.

    {{ userkey.public_key }}
    - + Edit user key @@ -24,7 +24,7 @@ {% else %}

    You don't have a user key on file.

    - + Create a User Key diff --git a/netbox/templates/users/userkey_edit.html b/netbox/templates/users/userkey_edit.html index 550ade4c2..45bac1938 100644 --- a/netbox/templates/users/userkey_edit.html +++ b/netbox/templates/users/userkey_edit.html @@ -23,7 +23,7 @@

    - Cancel + Cancel
    diff --git a/netbox/users/urls.py b/netbox/users/urls.py index d33d14beb..a9f7a1d7f 100644 --- a/netbox/users/urls.py +++ b/netbox/users/urls.py @@ -7,9 +7,9 @@ urlpatterns = [ # User profiles url(r'^profile/$', views.profile, name='profile'), - url(r'^profile/password/$', views.change_password, name='change_password'), - url(r'^profile/user-key/$', views.userkey, name='userkey'), - url(r'^profile/user-key/edit/$', views.userkey_edit, name='userkey_edit'), - url(r'^profile/recent-activity/$', views.recent_activity, name='recent_activity'), + url(r'^password/$', views.change_password, name='change_password'), + url(r'^user-key/$', views.userkey, name='userkey'), + url(r'^user-key/edit/$', views.userkey_edit, name='userkey_edit'), + url(r'^recent-activity/$', views.recent_activity, name='recent_activity'), ] diff --git a/netbox/users/views.py b/netbox/users/views.py index 95f5f3bfc..12a714696 100644 --- a/netbox/users/views.py +++ b/netbox/users/views.py @@ -69,7 +69,7 @@ def change_password(request): form.save() update_session_auth_hash(request, form.user) messages.success(request, u"Your password has been changed successfully.") - return redirect('users:profile') + return redirect('user:profile') else: form = PasswordChangeForm(user=request.user) @@ -109,7 +109,7 @@ def userkey_edit(request): uk.user = request.user uk.save() messages.success(request, u"Your user key has been saved.") - return redirect('users:userkey') + return redirect('user:userkey') else: form = UserKeyForm(instance=userkey)