mirror of
https://github.com/netbox-community/netbox.git
synced 2025-07-18 13:06:30 -06:00
Merge branch 'develop' into api2
Conflicts: netbox/templates/users/_user.html netbox/users/urls.py
This commit is contained in:
commit
04aedcc056
1
.gitattributes
vendored
Normal file
1
.gitattributes
vendored
Normal file
@ -0,0 +1 @@
|
|||||||
|
*.sh text eol=lf
|
@ -680,13 +680,21 @@ class DeviceFromCSVForm(BaseDeviceFromCSVForm):
|
|||||||
|
|
||||||
|
|
||||||
class ChildDeviceFromCSVForm(BaseDeviceFromCSVForm):
|
class ChildDeviceFromCSVForm(BaseDeviceFromCSVForm):
|
||||||
parent = FlexibleModelChoiceField(queryset=Device.objects.all(), to_field_name='name', required=False,
|
parent = FlexibleModelChoiceField(
|
||||||
error_messages={'invalid_choice': 'Parent device not found.'})
|
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)
|
device_bay_name = forms.CharField(required=False)
|
||||||
|
|
||||||
class Meta(BaseDeviceFromCSVForm.Meta):
|
class Meta(BaseDeviceFromCSVForm.Meta):
|
||||||
fields = ['name', 'device_role', 'tenant', 'manufacturer', 'model_name', 'platform', 'serial', 'asset_tag',
|
fields = [
|
||||||
'parent', 'device_bay_name']
|
'name', 'device_role', 'tenant', 'manufacturer', 'model_name', 'platform', 'serial', 'asset_tag', 'parent',
|
||||||
|
'device_bay_name',
|
||||||
|
]
|
||||||
|
|
||||||
def clean(self):
|
def clean(self):
|
||||||
|
|
||||||
@ -733,7 +741,7 @@ class DeviceFilterForm(BootstrapMixin, CustomFieldFilterForm):
|
|||||||
model = Device
|
model = Device
|
||||||
q = forms.CharField(required=False, label='Search')
|
q = forms.CharField(required=False, label='Search')
|
||||||
site = FilterChoiceField(
|
site = FilterChoiceField(
|
||||||
queryset=Site.objects.annotate(filter_count=Count('racks__devices')),
|
queryset=Site.objects.annotate(filter_count=Count('devices')),
|
||||||
to_field_name='slug',
|
to_field_name='slug',
|
||||||
)
|
)
|
||||||
rack_group_id = FilterChoiceField(
|
rack_group_id = FilterChoiceField(
|
||||||
@ -1610,20 +1618,23 @@ class DeviceBayCreateForm(DeviceComponentForm):
|
|||||||
|
|
||||||
|
|
||||||
class PopulateDeviceBayForm(BootstrapMixin, forms.Form):
|
class PopulateDeviceBayForm(BootstrapMixin, forms.Form):
|
||||||
installed_device = forms.ModelChoiceField(queryset=Device.objects.all(), label='Child Device',
|
installed_device = forms.ModelChoiceField(
|
||||||
help_text="Child devices must first be created within the rack occupied "
|
queryset=Device.objects.all(),
|
||||||
"by the parent device. Then they can be assigned to a bay.")
|
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):
|
def __init__(self, device_bay, *args, **kwargs):
|
||||||
|
|
||||||
super(PopulateDeviceBayForm, self).__init__(*args, **kwargs)
|
super(PopulateDeviceBayForm, self).__init__(*args, **kwargs)
|
||||||
|
|
||||||
children_queryset = Device.objects.filter(rack=device_bay.device.rack,
|
self.fields['installed_device'].queryset = Device.objects.filter(
|
||||||
parent_bay__isnull=True,
|
site=device_bay.device.site,
|
||||||
device_type__u_height=0,
|
rack=device_bay.device.rack,
|
||||||
device_type__subdevice_role=SUBDEVICE_ROLE_CHILD)\
|
parent_bay__isnull=True,
|
||||||
.exclude(pk=device_bay.device.pk)
|
device_type__u_height=0,
|
||||||
self.fields['installed_device'].queryset = children_queryset
|
device_type__subdevice_role=SUBDEVICE_ROLE_CHILD
|
||||||
|
).exclude(pk=device_bay.device.pk)
|
||||||
|
|
||||||
|
|
||||||
#
|
#
|
||||||
|
@ -975,25 +975,26 @@ class Device(CreatedUpdatedModel, CustomFieldModel):
|
|||||||
# Child devices cannot be assigned to a rack face/unit
|
# Child devices cannot be assigned to a rack face/unit
|
||||||
if self.device_type.is_child_device and self.face is not None:
|
if self.device_type.is_child_device and self.face is not None:
|
||||||
raise ValidationError({
|
raise ValidationError({
|
||||||
'face': "Child device types cannot be assigned to a rack face. This is an attribute of the parent "
|
'face': "Child device types cannot be assigned to a rack face. This is an attribute of the "
|
||||||
"device."
|
"parent device."
|
||||||
})
|
})
|
||||||
if self.device_type.is_child_device and self.position:
|
if self.device_type.is_child_device and self.position:
|
||||||
raise ValidationError({
|
raise ValidationError({
|
||||||
'position': "Child device types cannot be assigned to a rack position. This is an attribute of the "
|
'position': "Child device types cannot be assigned to a rack position. This is an attribute of "
|
||||||
"parent device."
|
"the parent device."
|
||||||
})
|
})
|
||||||
|
|
||||||
# Validate rack space
|
# Validate rack space
|
||||||
rack_face = self.face if not self.device_type.is_full_depth else None
|
rack_face = self.face if not self.device_type.is_full_depth else None
|
||||||
exclude_list = [self.pk] if self.pk else []
|
exclude_list = [self.pk] if self.pk else []
|
||||||
try:
|
try:
|
||||||
available_units = self.rack.get_available_units(u_height=self.device_type.u_height, rack_face=rack_face,
|
available_units = self.rack.get_available_units(
|
||||||
exclude=exclude_list)
|
u_height=self.device_type.u_height, rack_face=rack_face, exclude=exclude_list
|
||||||
|
)
|
||||||
if self.position and self.position not in available_units:
|
if self.position and self.position not in available_units:
|
||||||
raise ValidationError({
|
raise ValidationError({
|
||||||
'position': "U{} is already occupied or does not have sufficient space to accommodate a(n) {} "
|
'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)
|
"{} ({}U).".format(self.position, self.device_type, self.device_type.u_height)
|
||||||
})
|
})
|
||||||
except Rack.DoesNotExist:
|
except Rack.DoesNotExist:
|
||||||
pass
|
pass
|
||||||
@ -1034,8 +1035,8 @@ class Device(CreatedUpdatedModel, CustomFieldModel):
|
|||||||
self.device_type.device_bay_templates.all()]
|
self.device_type.device_bay_templates.all()]
|
||||||
)
|
)
|
||||||
|
|
||||||
# Update Rack assignment for any child Devices
|
# Update Site and Rack assignment for any child Devices
|
||||||
Device.objects.filter(parent_bay__device=self).update(rack=self.rack)
|
Device.objects.filter(parent_bay__device=self).update(site=self.site, rack=self.rack)
|
||||||
|
|
||||||
def to_csv(self):
|
def to_csv(self):
|
||||||
return csv_format([
|
return csv_format([
|
||||||
@ -1059,8 +1060,10 @@ class Device(CreatedUpdatedModel, CustomFieldModel):
|
|||||||
return self.name
|
return self.name
|
||||||
elif self.position:
|
elif self.position:
|
||||||
return u"{} ({} U{})".format(self.device_type, self.rack.name, 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)
|
return u"{} ({})".format(self.device_type, self.rack.name)
|
||||||
|
else:
|
||||||
|
return u"{} ({})".format(self.device_type, self.site.name)
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def identifier(self):
|
def identifier(self):
|
||||||
|
@ -763,9 +763,12 @@ class ChildDeviceBulkImportView(PermissionRequiredMixin, BulkImportView):
|
|||||||
default_return_url = 'dcim:device_list'
|
default_return_url = 'dcim:device_list'
|
||||||
|
|
||||||
def save_obj(self, obj):
|
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.rack = obj.parent_bay.device.rack
|
||||||
obj.save()
|
obj.save()
|
||||||
|
|
||||||
# Save the reverse relation
|
# Save the reverse relation
|
||||||
device_bay = obj.parent_bay
|
device_bay = obj.parent_bay
|
||||||
device_bay.installed_device = obj
|
device_bay.installed_device = obj
|
||||||
|
@ -23,7 +23,7 @@ _patterns = [
|
|||||||
url(r'^ipam/', include('ipam.urls', namespace='ipam')),
|
url(r'^ipam/', include('ipam.urls', namespace='ipam')),
|
||||||
url(r'^secrets/', include('secrets.urls', namespace='secrets')),
|
url(r'^secrets/', include('secrets.urls', namespace='secrets')),
|
||||||
url(r'^tenancy/', include('tenancy.urls', namespace='tenancy')),
|
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
|
# API
|
||||||
url(r'^api/$', APIRootView.as_view()),
|
url(r'^api/$', APIRootView.as_view()),
|
||||||
|
@ -15,10 +15,10 @@ def userkey_required():
|
|||||||
uk = UserKey.objects.get(user=request.user)
|
uk = UserKey.objects.get(user=request.user)
|
||||||
except UserKey.DoesNotExist:
|
except UserKey.DoesNotExist:
|
||||||
messages.warning(request, u"This operation requires an active user key, but you don't have one.")
|
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():
|
if not uk.is_active():
|
||||||
messages.warning(request, u"This operation is not available. Your user key has not been activated.")
|
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 view(request, *args, **kwargs)
|
||||||
return wrapped_view
|
return wrapped_view
|
||||||
return _decorator
|
return _decorator
|
||||||
|
@ -245,7 +245,7 @@
|
|||||||
{% if request.user.is_staff %}
|
{% if request.user.is_staff %}
|
||||||
<li><a href="{% url 'admin:index' %}"><i class="fa fa-cogs" aria-hidden="true"></i> Admin</a></li>
|
<li><a href="{% url 'admin:index' %}"><i class="fa fa-cogs" aria-hidden="true"></i> Admin</a></li>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
<li><a href="{% url 'users:profile' %}"><i class="fa fa-user" aria-hidden="true"></i> {{ request.user }}</a></li>
|
<li><a href="{% url 'user:profile' %}"><i class="fa fa-user" aria-hidden="true"></i> {{ request.user }}</a></li>
|
||||||
<li><a href="{% url 'logout' %}"><i class="fa fa-sign-out" aria-hidden="true"></i> Log out</a></li>
|
<li><a href="{% url 'logout' %}"><i class="fa fa-sign-out" aria-hidden="true"></i> Log out</a></li>
|
||||||
{% else %}
|
{% else %}
|
||||||
<li><a href="{% url 'login' %}?next={{ request.path }}"><i class="fa fa-sign-in" aria-hidden="true"></i> Log in</a></li>
|
<li><a href="{% url 'login' %}?next={{ request.path }}"><i class="fa fa-sign-in" aria-hidden="true"></i> Log in</a></li>
|
||||||
|
@ -43,8 +43,10 @@
|
|||||||
<td>
|
<td>
|
||||||
{% if device.parent_bay %}
|
{% if device.parent_bay %}
|
||||||
{% with device.parent_bay.device as parent %}
|
{% with device.parent_bay.device as parent %}
|
||||||
<span>U{{ parent.position }} / {{ parent.get_face_display }}
|
<a href="{{ parent.get_absolute_url }}">{{ parent }}</a> <i class="fa fa-angle-right"></i> {{ device.parent_bay.name }}
|
||||||
(<a href="{{ parent.get_absolute_url }}">{{ parent }}</a> - {{ device.parent_bay.name }})</span>
|
{% if parent.position %}
|
||||||
|
(U{{ parent.position }} / {{ parent.get_face_display }})
|
||||||
|
{% endif %}
|
||||||
{% endwith %}
|
{% endwith %}
|
||||||
{% elif device.rack and device.position %}
|
{% elif device.rack and device.position %}
|
||||||
<span>U{{ device.position }} / {{ device.get_face_display }}</span>
|
<span>U{{ device.position }} / {{ device.get_face_display }}</span>
|
||||||
|
@ -10,19 +10,19 @@
|
|||||||
<div class="col-sm-3 col-md-2 col-md-offset-2">
|
<div class="col-sm-3 col-md-2 col-md-offset-2">
|
||||||
<ul class="nav nav-pills nav-stacked">
|
<ul class="nav nav-pills nav-stacked">
|
||||||
<li{% ifequal active_tab "profile" %} class="active"{% endifequal %}>
|
<li{% ifequal active_tab "profile" %} class="active"{% endifequal %}>
|
||||||
<a href="{% url 'users:profile' %}">Profile</a>
|
<a href="{% url 'user:profile' %}">Profile</a>
|
||||||
</li>
|
</li>
|
||||||
<li{% ifequal active_tab "change_password" %} class="active"{% endifequal %}>
|
<li{% ifequal active_tab "change_password" %} class="active"{% endifequal %}>
|
||||||
<a href="{% url 'users:change_password' %}">Change Password</a>
|
<a href="{% url 'user:change_password' %}">Change Password</a>
|
||||||
</li>
|
</li>
|
||||||
<li{% ifequal active_tab "api_tokens" %} class="active"{% endifequal %}>
|
<li{% ifequal active_tab "api_tokens" %} class="active"{% endifequal %}>
|
||||||
<a href="{% url 'users:token_list' %}">API Tokens</a>
|
<a href="{% url 'user:token_list' %}">API Tokens</a>
|
||||||
</li>
|
</li>
|
||||||
<li{% ifequal active_tab "userkey" %} class="active"{% endifequal %}>
|
<li{% ifequal active_tab "userkey" %} class="active"{% endifequal %}>
|
||||||
<a href="{% url 'users:userkey' %}">User Key</a>
|
<a href="{% url 'user:userkey' %}">User Key</a>
|
||||||
</li>
|
</li>
|
||||||
<li{% ifequal active_tab "recent_activity" %} class="active"{% endifequal %}>
|
<li{% ifequal active_tab "recent_activity" %} class="active"{% endifequal %}>
|
||||||
<a href="{% url 'users:recent_activity' %}">Recent Activity</a>
|
<a href="{% url 'user:recent_activity' %}">Recent Activity</a>
|
||||||
</li>
|
</li>
|
||||||
</ul>
|
</ul>
|
||||||
</div>
|
</div>
|
||||||
|
@ -24,7 +24,7 @@
|
|||||||
</div>
|
</div>
|
||||||
<div class="text-right">
|
<div class="text-right">
|
||||||
<button type="submit" name="_update" class="btn btn-primary">Update</button>
|
<button type="submit" name="_update" class="btn btn-primary">Update</button>
|
||||||
<a href="{% url 'users:profile' %}" class="btn btn-default">Cancel</a>
|
<a href="{% url 'user:profile' %}" class="btn btn-default">Cancel</a>
|
||||||
</div>
|
</div>
|
||||||
</form>
|
</form>
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
@ -15,7 +15,7 @@
|
|||||||
<p>Your public key is below.</p>
|
<p>Your public key is below.</p>
|
||||||
<pre>{{ userkey.public_key }}</pre>
|
<pre>{{ userkey.public_key }}</pre>
|
||||||
<div class="pull-right">
|
<div class="pull-right">
|
||||||
<a href="{% url 'users:userkey_edit' %}" class="btn btn-warning">
|
<a href="{% url 'user:userkey_edit' %}" class="btn btn-warning">
|
||||||
<span class="fa fa-pencil" aria-hidden="true"></span>
|
<span class="fa fa-pencil" aria-hidden="true"></span>
|
||||||
Edit user key
|
Edit user key
|
||||||
</a>
|
</a>
|
||||||
@ -24,7 +24,7 @@
|
|||||||
{% else %}
|
{% else %}
|
||||||
<p>You don't have a user key on file.</p>
|
<p>You don't have a user key on file.</p>
|
||||||
<p>
|
<p>
|
||||||
<a href="{% url 'users:userkey_edit' %}" class="btn btn-primary">
|
<a href="{% url 'user:userkey_edit' %}" class="btn btn-primary">
|
||||||
<span class="fa fa-plus" aria-hidden="true"></span>
|
<span class="fa fa-plus" aria-hidden="true"></span>
|
||||||
Create a User Key
|
Create a User Key
|
||||||
</a>
|
</a>
|
||||||
|
@ -23,7 +23,7 @@
|
|||||||
</div>
|
</div>
|
||||||
<div class="col-sm-6 col-md-6 text-right">
|
<div class="col-sm-6 col-md-6 text-right">
|
||||||
<button type="submit" name="_update" class="btn btn-primary">Save</button>
|
<button type="submit" name="_update" class="btn btn-primary">Save</button>
|
||||||
<a href="{% url 'users:userkey' %}" class="btn btn-default">Cancel</a>
|
<a href="{% url 'user:userkey' %}" class="btn btn-default">Cancel</a>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
@ -7,13 +7,13 @@ urlpatterns = [
|
|||||||
|
|
||||||
# User profiles
|
# User profiles
|
||||||
url(r'^profile/$', views.profile, name='profile'),
|
url(r'^profile/$', views.profile, name='profile'),
|
||||||
url(r'^profile/password/$', views.change_password, name='change_password'),
|
url(r'^password/$', views.change_password, name='change_password'),
|
||||||
url(r'^profile/api-tokens/$', views.TokenListView.as_view(), name='token_list'),
|
url(r'^api-tokens/$', views.TokenListView.as_view(), name='token_list'),
|
||||||
url(r'^profile/api-tokens/add/$', views.TokenEditView.as_view(), name='token_add'),
|
url(r'^api-tokens/add/$', views.TokenEditView.as_view(), name='token_add'),
|
||||||
url(r'^profile/api-tokens/(?P<pk>\d+)/edit/$', views.TokenEditView.as_view(), name='token_edit'),
|
url(r'^api-tokens/(?P<pk>\d+)/edit/$', views.TokenEditView.as_view(), name='token_edit'),
|
||||||
url(r'^profile/api-tokens/(?P<pk>\d+)/delete/$', views.TokenDeleteView.as_view(), name='token_delete'),
|
url(r'^api-tokens/(?P<pk>\d+)/delete/$', views.TokenDeleteView.as_view(), name='token_delete'),
|
||||||
url(r'^profile/user-key/$', views.userkey, name='userkey'),
|
url(r'^user-key/$', views.userkey, name='userkey'),
|
||||||
url(r'^profile/user-key/edit/$', views.userkey_edit, name='userkey_edit'),
|
url(r'^user-key/edit/$', views.userkey_edit, name='userkey_edit'),
|
||||||
url(r'^profile/recent-activity/$', views.recent_activity, name='recent_activity'),
|
url(r'^recent-activity/$', views.recent_activity, name='recent_activity'),
|
||||||
|
|
||||||
]
|
]
|
||||||
|
@ -72,7 +72,7 @@ def change_password(request):
|
|||||||
form.save()
|
form.save()
|
||||||
update_session_auth_hash(request, form.user)
|
update_session_auth_hash(request, form.user)
|
||||||
messages.success(request, u"Your password has been changed successfully.")
|
messages.success(request, u"Your password has been changed successfully.")
|
||||||
return redirect('users:profile')
|
return redirect('user:profile')
|
||||||
|
|
||||||
else:
|
else:
|
||||||
form = PasswordChangeForm(user=request.user)
|
form = PasswordChangeForm(user=request.user)
|
||||||
@ -112,7 +112,7 @@ def userkey_edit(request):
|
|||||||
uk.user = request.user
|
uk.user = request.user
|
||||||
uk.save()
|
uk.save()
|
||||||
messages.success(request, u"Your user key has been saved.")
|
messages.success(request, u"Your user key has been saved.")
|
||||||
return redirect('users:userkey')
|
return redirect('user:userkey')
|
||||||
|
|
||||||
else:
|
else:
|
||||||
form = UserKeyForm(instance=userkey)
|
form = UserKeyForm(instance=userkey)
|
||||||
|
Loading…
Reference in New Issue
Block a user