1 ##############################################################################
2 # Copyright (c) 2018 Sawyer Bergeron, Parker Berberian, and others.
3 # Copyright (c) 2020 Sawyer Bergeron, Sean Smith, and others.
5 # All rights reserved. This program and the accompanying materials
6 # are made available under the terms of the Apache License, Version 2.0
7 # which accompanies this distribution, and is available at
8 # http://www.apache.org/licenses/LICENSE-2.0
9 ##############################################################################
12 import django.forms as forms
13 from django.forms import widgets, ValidationError
14 from django.utils.safestring import mark_safe
15 from django.template.loader import render_to_string
16 from django.forms.widgets import NumberInput
21 from account.models import Lab
22 from account.models import UserProfile
23 from resource_inventory.models import (
28 from resource_inventory.resource_manager import ResourceManager
29 from booking.lib import get_user_items, get_user_field_opts
32 class SearchableSelectMultipleWidget(widgets.SelectMultiple):
33 template_name = 'dashboard/searchable_select_multiple.html'
35 def __init__(self, attrs=None):
36 self.items = attrs['items']
37 self.show_from_noentry = attrs['show_from_noentry']
38 self.show_x_results = attrs['show_x_results']
39 self.results_scrollable = attrs['results_scrollable']
40 self.selectable_limit = attrs['selectable_limit']
41 self.placeholder = attrs['placeholder']
42 self.name = attrs['name']
43 self.initial = attrs.get("initial", [])
45 super(SearchableSelectMultipleWidget, self).__init__()
47 def render(self, name, value, attrs=None, renderer=None):
49 context = self.get_context(attrs)
50 return mark_safe(render_to_string(self.template_name, context))
52 def get_context(self, attrs):
56 'show_from_noentry': self.show_from_noentry,
57 'show_x_results': self.show_x_results,
58 'results_scrollable': self.results_scrollable,
59 'selectable_limit': self.selectable_limit,
60 'placeholder': self.placeholder,
61 'initial': self.initial,
65 class SearchableSelectMultipleField(forms.Field):
66 def __init__(self, *args, required=True, widget=None, label=None, disabled=False,
67 items=None, queryset=None, show_from_noentry=True, show_x_results=-1,
68 results_scrollable=False, selectable_limit=-1, placeholder="search here",
69 name="searchable_select", initial=[], **kwargs):
71 From the documentation.
73 # required -- Boolean that specifies whether the field is required.
75 # widget -- A Widget class, or instance of a Widget class, that should
76 # be used for this Field when displaying it. Each Field has a
77 # default Widget that it'll use if you don't specify this. In
78 # most cases, the default widget is TextInput.
79 # label -- A verbose name for this field, for use in displaying this
80 # field in a form. By default, Django will use a "pretty"
81 # version of the form field name, if the Field is part of a
83 # initial -- A value to use in this Field's initial display. This value
84 # is *not* used as a fallback if data isn't given.
85 # help_text -- An optional string to use as "help text" for this Field.
86 # error_messages -- An optional dictionary to override the default
87 # messages that the field will raise.
88 # show_hidden_initial -- Boolean that specifies if it is needed to render a
89 # hidden widget with initial value after widget.
90 # validators -- List of additional validators to use
91 # localize -- Boolean that specifies if the field should be localized.
92 # disabled -- Boolean that specifies whether the field is disabled, that
93 # is its widget is shown in the form but not editable.
94 # label_suffix -- Suffix to be added to the label. Overrides
95 # form's label_suffix.
98 if self.widget is None:
99 self.widget = SearchableSelectMultipleWidget(
102 'initial': [obj.id for obj in initial],
103 'show_from_noentry': show_from_noentry,
104 'show_x_results': show_x_results,
105 'results_scrollable': results_scrollable,
106 'selectable_limit': selectable_limit,
107 'placeholder': placeholder,
112 self.disabled = disabled
113 self.queryset = queryset
114 self.selectable_limit = selectable_limit
116 super().__init__(disabled=disabled, **kwargs)
118 self.required = required
120 def clean(self, data):
124 raise ValidationError("Nothing was selected")
128 data_as_list = json.loads(data)
129 except json.decoder.JSONDecodeError:
132 raise ValidationError("Contents Not JSON")
133 if self.selectable_limit != -1:
134 if len(data_as_list) > self.selectable_limit:
135 raise ValidationError("Too many items were selected")
138 for elem in data_as_list:
139 items.append(self.queryset.get(id=elem))
144 class SearchableSelectAbstractForm(forms.Form):
145 def __init__(self, *args, queryset=None, initial=[], **kwargs):
146 self.queryset = queryset
147 items = self.generate_items(self.queryset)
148 options = self.generate_options()
150 super(SearchableSelectAbstractForm, self).__init__(*args, **kwargs)
151 self.fields['searchable_select'] = SearchableSelectMultipleField(
154 queryset=self.queryset,
158 def get_validated_bundle(self):
159 bundles = self.cleaned_data['searchable_select']
160 if len(bundles) < 1: # don't need to check for >1, as field does that for us
161 raise ValidationError("No bundle was selected")
164 def generate_items(self, queryset):
165 raise Exception("SearchableSelectAbstractForm does not implement concrete generate_items()")
167 def generate_options(self, disabled=False):
169 'show_from_noentry': True,
170 'show_x_results': -1,
171 'results_scrollable': True,
172 'selectable_limit': 1,
173 'placeholder': 'Search for a Bundle',
174 'name': 'searchable_select',
179 class SWConfigSelectorForm(SearchableSelectAbstractForm):
180 def generate_items(self, queryset):
183 for bundle in queryset:
185 'expanded_name': bundle.name,
186 'small_name': bundle.owner.username,
187 'string': bundle.description,
194 class OPNFVSelectForm(SearchableSelectAbstractForm):
195 def generate_items(self, queryset):
198 for config in queryset:
200 'expanded_name': config.name,
201 'small_name': config.bundle.owner.username,
202 'string': config.description,
209 class ResourceSelectorForm(SearchableSelectAbstractForm):
210 def generate_items(self, queryset):
213 for bundle in queryset:
215 'expanded_name': bundle.name,
216 'small_name': bundle.owner.username,
217 'string': bundle.description,
224 class BookingMetaForm(forms.Form):
225 # Django Form class for Book a Pod
226 length = forms.IntegerField(
236 purpose = forms.CharField(max_length=1000)
237 project = forms.CharField(max_length=400)
238 info_file = forms.CharField(max_length=1000, required=False)
239 deploy_opnfv = forms.BooleanField(required=False)
241 def __init__(self, *args, user_initial=[], owner=None, **kwargs):
242 super(BookingMetaForm, self).__init__(**kwargs)
244 self.fields['users'] = SearchableSelectMultipleField(
245 queryset=UserProfile.objects.select_related('user').exclude(user=owner),
246 initial=user_initial,
247 items=get_user_items(exclude=owner),
249 **get_user_field_opts()
253 class MultipleSelectFilterWidget(forms.Widget):
254 def __init__(self, *args, display_objects=None, filter_items=None, neighbors=None, **kwargs):
255 super(MultipleSelectFilterWidget, self).__init__(*args, **kwargs)
256 self.display_objects = display_objects
257 self.filter_items = filter_items
258 self.neighbors = neighbors
259 self.template_name = "dashboard/multiple_select_filter_widget.html"
261 def render(self, name, value, attrs=None, renderer=None):
262 context = self.get_context(name, value, attrs)
263 html = render_to_string(self.template_name, context=context)
264 return mark_safe(html)
266 def get_context(self, name, value, attrs):
268 'display_objects': self.display_objects,
269 'neighbors': self.neighbors,
270 'filter_items': self.filter_items,
271 'initial_value': value
275 class MultipleSelectFilterField(forms.Field):
277 def __init__(self, **kwargs):
278 self.initial = kwargs.get("initial")
279 super().__init__(**kwargs)
281 def to_python(self, value):
283 return json.loads(value)
284 except json.decoder.JSONDecodeError:
286 raise ValidationError("content is not valid JSON")
291 def getLabData(multiple_hosts=False, user=None):
293 Get all labs and thier host profiles, returns a serialized version the form can understand.
295 Could be rewritten with a related query to make it faster
297 # javascript truthy variables
301 multiple_hosts = true
303 multiple_hosts = false
308 for lab in Lab.objects.all():
310 'id': "lab_" + str(lab.lab_user.id),
311 'model_id': lab.lab_user.id,
313 'description': lab.description,
316 'follow': multiple_hosts,
319 'available_resources': json.dumps(lab.get_available_resources())
322 items[lab_node['id']] = lab_node
323 neighbors[lab_node['id']] = []
324 labs[lab_node['id']] = lab_node
326 for template in ResourceManager.getInstance().getAvailableResourceTemplates(lab, user):
328 'form': {"name": "host_name", "type": "text", "placeholder": "hostname"},
329 'id': "resource_" + str(template.id),
330 'model_id': template.id,
331 'name': template.name,
332 'description': template.description,
336 'multiple': multiple_hosts,
338 'required_resources': json.dumps(template.get_required_resources())
342 resource_node['values'] = [] # place to store multiple values
344 items[resource_node['id']] = resource_node
345 neighbors[lab_node['id']].append(resource_node['id'])
347 if resource_node['id'] not in neighbors:
348 neighbors[resource_node['id']] = []
350 neighbors[resource_node['id']].append(lab_node['id'])
351 resources[resource_node['id']] = resource_node
353 display_objects = [("lab", labs.values()), ("resource", resources.values())]
356 'display_objects': display_objects,
357 'neighbors': neighbors,
358 'filter_items': items
364 class HardwareDefinitionForm(forms.Form):
366 def __init__(self, user, *args, **kwargs):
367 super(HardwareDefinitionForm, self).__init__(*args, **kwargs)
368 attrs = FormUtils.getLabData(multiple_hosts=True, user=user)
369 self.fields['filter_field'] = MultipleSelectFilterField(
370 widget=MultipleSelectFilterWidget(**attrs)
374 class PodDefinitionForm(forms.Form):
377 xml = forms.CharField()
380 class ResourceMetaForm(forms.Form):
382 bundle_name = forms.CharField(label="POD Name")
383 bundle_description = forms.CharField(label="POD Description", widget=forms.Textarea, max_length=1000)
386 class GenericHostMetaForm(forms.Form):
388 host_profile = forms.CharField(label="Host Type", disabled=True, required=False)
389 host_name = forms.CharField(label="Host Name")
392 class NetworkDefinitionForm(forms.Form):
393 def __init__(self, *args, **kwargs):
394 super(NetworkDefinitionForm, self).__init__(**kwargs)
397 class NetworkConfigurationForm(forms.Form):
398 def __init__(self, *args, **kwargs):
399 super(NetworkConfigurationForm).__init__(**kwargs)
402 class HostSoftwareDefinitionForm(forms.Form):
403 # Django Form class for Design a Pod
404 host_name = forms.CharField(
409 headnode = forms.BooleanField(required=False, widget=forms.HiddenInput)
411 def __init__(self, *args, **kwargs):
412 imageQS = kwargs.pop("imageQS")
413 super(HostSoftwareDefinitionForm, self).__init__(*args, **kwargs)
414 self.fields['image'] = forms.ModelChoiceField(queryset=imageQS)
417 class WorkflowSelectionForm(forms.Form):
418 fields = ['workflow']
420 empty_permitted = False
422 workflow = forms.ChoiceField(
425 (1, 'Resource Bundle'),
426 (2, 'Software Configuration')
428 label="Choose Workflow",
434 class SnapshotHostSelectForm(forms.Form):
435 host = forms.CharField()
438 class BasicMetaForm(forms.Form):
439 name = forms.CharField()
440 description = forms.CharField(widget=forms.Textarea)
443 class ConfirmationForm(forms.Form):
446 confirm = forms.ChoiceField(
454 def validate_step(value):
455 if value not in ["prev", "next", "current"]:
456 raise ValidationError(str(value) + " is not allowed")
459 def validate_step_form(value):
461 urllib.parse.unquote_plus(value)
463 raise ValidationError("Value is not url encoded data")
466 class ManagerForm(forms.Form):
467 step = forms.CharField(widget=forms.widgets.HiddenInput, validators=[validate_step])
468 step_form = forms.CharField(widget=forms.widgets.HiddenInput, validators=[validate_step_form])
472 class OPNFVSelectionForm(forms.Form):
473 installer = forms.ModelChoiceField(queryset=Installer.objects.all(), required=True)
474 scenario = forms.ModelChoiceField(queryset=Scenario.objects.all(), required=True)
477 class OPNFVNetworkRoleForm(forms.Form):
478 role = forms.CharField(max_length=200, disabled=True, required=False)
480 def __init__(self, *args, config_bundle, **kwargs):
481 super(OPNFVNetworkRoleForm, self).__init__(*args, **kwargs)
482 self.fields['network'] = forms.ModelChoiceField(
483 queryset=config_bundle.bundle.networks.all()
487 class OPNFVHostRoleForm(forms.Form):
488 host_name = forms.CharField(max_length=200, disabled=True, required=False)
489 role = forms.ModelChoiceField(queryset=OPNFVRole.objects.all().order_by("name").distinct("name"))