Browse Source

Implemented a view for adding individual devices to an existing virtual chassis

Jeremy Stretch 7 years ago
parent
commit
8d1676db54
4 changed files with 102 additions and 4 deletions
  1. 43 4
      netbox/dcim/forms.py
  2. 1 0
      netbox/dcim/urls.py
  3. 53 0
      netbox/dcim/views.py
  4. 5 0
      netbox/templates/dcim/device.html

+ 43 - 4
netbox/dcim/forms.py

@@ -22,9 +22,10 @@ from utilities.forms import (
 )
 )
 from virtualization.models import Cluster
 from virtualization.models import Cluster
 from .constants import (
 from .constants import (
-    CONNECTION_STATUS_CHOICES, CONNECTION_STATUS_CONNECTED, IFACE_FF_CHOICES, IFACE_FF_LAG, IFACE_ORDERING_CHOICES,
-    RACK_FACE_CHOICES, RACK_TYPE_CHOICES, RACK_WIDTH_CHOICES, RACK_WIDTH_19IN, RACK_WIDTH_23IN, STATUS_CHOICES,
-    SUBDEVICE_ROLE_CHILD, SUBDEVICE_ROLE_PARENT, SUBDEVICE_ROLE_CHOICES,
+    CONNECTION_STATUS_CHOICES, CONNECTION_STATUS_CONNECTED, IFACE_FF_CHOICES, IFACE_FF_LAG, IFACE_MODE_ACCESS,
+    IFACE_MODE_CHOICES, IFACE_MODE_TAGGED_ALL, IFACE_ORDERING_CHOICES, RACK_FACE_CHOICES, RACK_TYPE_CHOICES,
+    RACK_WIDTH_CHOICES, RACK_WIDTH_19IN, RACK_WIDTH_23IN, STATUS_CHOICES, SUBDEVICE_ROLE_CHILD, SUBDEVICE_ROLE_PARENT,
+    SUBDEVICE_ROLE_CHOICES,
 )
 )
 from .formfields import MACAddressFormField
 from .formfields import MACAddressFormField
 from .models import (
 from .models import (
@@ -33,7 +34,6 @@ from .models import (
     Platform, PowerOutlet, PowerOutletTemplate, PowerPort, PowerPortTemplate, Rack, RackGroup, RackReservation,
     Platform, PowerOutlet, PowerOutletTemplate, PowerPort, PowerPortTemplate, Rack, RackGroup, RackReservation,
     RackRole, Region, Site, VCMembership, VirtualChassis
     RackRole, Region, Site, VCMembership, VirtualChassis
 )
 )
-from .constants import *
 
 
 DEVICE_BY_PK_RE = '{\d+\}'
 DEVICE_BY_PK_RE = '{\d+\}'
 
 
@@ -2253,3 +2253,42 @@ class VCMembershipForm(BootstrapMixin, forms.ModelForm):
     class Meta:
     class Meta:
         model = VCMembership
         model = VCMembership
         fields = ['position', 'priority']
         fields = ['position', 'priority']
+
+
+class VCMembershipCreateForm(BootstrapMixin, ChainedFieldsMixin, forms.ModelForm):
+    site = forms.ModelChoiceField(
+        queryset=Site.objects.all(),
+        label='Site',
+        required=False,
+        widget=forms.Select(
+            attrs={'filter-for': 'rack'}
+        )
+    )
+    rack = ChainedModelChoiceField(
+        queryset=Rack.objects.all(),
+        chains=(
+            ('site', 'site'),
+        ),
+        label='Rack',
+        required=False,
+        widget=APISelect(
+            api_url='/api/dcim/racks/?site_id={{site}}',
+            attrs={'filter-for': 'device', 'nullable': 'true'}
+        )
+    )
+    device = ChainedModelChoiceField(
+        queryset=Device.objects.all(),
+        chains=(
+            ('site', 'site'),
+            ('rack', 'rack'),
+        ),
+        label='Device',
+        widget=APISelect(
+            api_url='/api/dcim/devices/?site_id={{site}}&rack_id={{rack}}',
+            display_field='display_name'
+        )
+    )
+
+    class Meta:
+        model = VCMembership
+        fields = ['site', 'rack', 'device', 'position', 'priority']

+ 1 - 0
netbox/dcim/urls.py

@@ -216,6 +216,7 @@ urlpatterns = [
     url(r'^virtual-chassis/add/$', views.VirtualChassisCreateView.as_view(), name='virtualchassis_add'),
     url(r'^virtual-chassis/add/$', views.VirtualChassisCreateView.as_view(), name='virtualchassis_add'),
     url(r'^virtual-chassis/(?P<pk>\d+)/edit/$', views.VirtualChassisEditView.as_view(), name='virtualchassis_edit'),
     url(r'^virtual-chassis/(?P<pk>\d+)/edit/$', views.VirtualChassisEditView.as_view(), name='virtualchassis_edit'),
     url(r'^virtual-chassis/(?P<pk>\d+)/delete/$', views.VirtualChassisDeleteView.as_view(), name='virtualchassis_delete'),
     url(r'^virtual-chassis/(?P<pk>\d+)/delete/$', views.VirtualChassisDeleteView.as_view(), name='virtualchassis_delete'),
+    url(r'^virtual-chassis/(?P<pk>\d+)/add-member/$', views.VirtualChassisAddMemberView.as_view(), name='virtualchassis_add_member'),
 
 
     # VC memberships
     # VC memberships
     url(r'^vc-memberships/(?P<pk>\d+)/edit/$', views.VCMembershipEditView.as_view(), name='vcmembership_edit'),
     url(r'^vc-memberships/(?P<pk>\d+)/edit/$', views.VCMembershipEditView.as_view(), name='vcmembership_edit'),

+ 53 - 0
netbox/dcim/views.py

@@ -2099,6 +2099,59 @@ class VirtualChassisDeleteView(PermissionRequiredMixin, ObjectDeleteView):
     default_return_url = 'dcim:device_list'
     default_return_url = 'dcim:device_list'
 
 
 
 
+class VirtualChassisAddMemberView(GetReturnURLMixin, View):
+    """
+    Create a new VCMembership tying a Device to the VirtualChassis.
+    """
+    template_name = 'utilities/obj_edit.html'
+
+    def get(self, request, pk):
+
+        virtual_chassis = get_object_or_404(VirtualChassis, pk=pk)
+        obj = VCMembership(virtual_chassis=virtual_chassis)
+
+        initial_data = {k: request.GET[k] for k in request.GET}
+        form = forms.VCMembershipCreateForm(instance=obj, initial=initial_data)
+
+        return render(request, self.template_name, {
+            'obj': obj,
+            'obj_type': VCMembership._meta.verbose_name,
+            'form': form,
+            'return_url': self.get_return_url(request, obj),
+        })
+
+    def post(self, request, pk):
+
+        virtual_chassis = get_object_or_404(VirtualChassis, pk=pk)
+        obj = VCMembership(virtual_chassis=virtual_chassis)
+
+        form = forms.VCMembershipCreateForm(request.POST, instance=obj)
+
+        if form.is_valid():
+
+            obj = form.save()
+
+            msg = 'Added member <a href="{}">{}</a>'.format(obj.device.get_absolute_url(), escape(obj.device))
+            messages.success(request, mark_safe(msg))
+            UserAction.objects.log_create(request.user, obj, msg)
+
+            if '_addanother' in request.POST:
+                return redirect(request.get_full_path())
+
+            return_url = form.cleaned_data.get('return_url')
+            if return_url is not None and is_safe_url(url=return_url, host=request.get_host()):
+                return redirect(return_url)
+            else:
+                return redirect(self.get_return_url(request, obj))
+
+        return render(request, self.template_name, {
+            'obj': obj,
+            'obj_type': VCMembership._meta.verbose_name,
+            'form': form,
+            'return_url': self.get_return_url(request, obj),
+        })
+
+
 #
 #
 # VC memberships
 # VC memberships
 #
 #

+ 5 - 0
netbox/templates/dcim/device.html

@@ -122,6 +122,11 @@
                     {% endfor %}
                     {% endfor %}
                 </table>
                 </table>
                 <div class="panel-footer text-right">
                 <div class="panel-footer text-right">
+                    {% if perms.dcim.add_vcmembership %}
+                        <a href="{% url 'dcim:virtualchassis_add_member' pk=device.virtual_chassis.pk %}?site={{ device.site.pk }}&rack={{ device.rack.pk }}&return_url={{ device.get_absolute_url }}" class="btn btn-primary btn-xs">
+                            <span class="glyphicon glyphicon-plus" aria-hidden="true"></span> Add Member
+                        </a>
+                    {% endif %}
                     {% if perms.dcim.change_virtualchassis %}
                     {% if perms.dcim.change_virtualchassis %}
                         <a href="{% url 'dcim:virtualchassis_edit' pk=device.virtual_chassis.pk %}?return_url={{ device.get_absolute_url }}" class="btn btn-warning btn-xs">
                         <a href="{% url 'dcim:virtualchassis_edit' pk=device.virtual_chassis.pk %}?return_url={{ device.get_absolute_url }}" class="btn btn-warning btn-xs">
                             <span class="glyphicon glyphicon-pencil" aria-hidden="true"></span> Edit Virtual Chassis
                             <span class="glyphicon glyphicon-pencil" aria-hidden="true"></span> Edit Virtual Chassis