diff options
Diffstat (limited to 'dashboard/src/workflow/forms.py')
-rw-r--r-- | dashboard/src/workflow/forms.py | 535 |
1 files changed, 254 insertions, 281 deletions
diff --git a/dashboard/src/workflow/forms.py b/dashboard/src/workflow/forms.py index 726e7dd..ee44ecd 100644 --- a/dashboard/src/workflow/forms.py +++ b/dashboard/src/workflow/forms.py @@ -9,41 +9,37 @@ import django.forms as forms -from django.forms import widgets +from django.forms import widgets, ValidationError from django.utils.safestring import mark_safe from django.template.loader import render_to_string from django.forms.widgets import NumberInput +import json + from account.models import Lab from account.models import UserProfile from resource_inventory.models import ( - GenericResourceBundle, - ConfigBundle, OPNFVRole, - Image, Installer, - Scenario + Scenario, ) +from booking.lib import get_user_items, get_user_field_opts class SearchableSelectMultipleWidget(widgets.SelectMultiple): template_name = 'dashboard/searchable_select_multiple.html' def __init__(self, attrs=None): - self.items = attrs['set'] + self.items = attrs['items'] self.show_from_noentry = attrs['show_from_noentry'] self.show_x_results = attrs['show_x_results'] - self.results_scrollable = attrs['scrollable'] + self.results_scrollable = attrs['results_scrollable'] self.selectable_limit = attrs['selectable_limit'] self.placeholder = attrs['placeholder'] self.name = attrs['name'] - self.initial = attrs.get("initial", "") - self.default_entry = attrs.get("default_entry", "") - self.edit = attrs.get("edit", False) - self.wf_type = attrs.get("wf_type") - self.incompatible = attrs.get("incompatible", "false") + self.initial = attrs.get("initial", []) - super(SearchableSelectMultipleWidget, self).__init__(attrs) + super(SearchableSelectMultipleWidget, self).__init__() def render(self, name, value, attrs=None, renderer=None): @@ -60,124 +56,160 @@ class SearchableSelectMultipleWidget(widgets.SelectMultiple): 'selectable_limit': self.selectable_limit, 'placeholder': self.placeholder, 'initial': self.initial, - 'default_entry': self.default_entry, - 'edit': self.edit, - 'wf_type': self.wf_type, - 'incompatible': self.incompatible } -class ResourceSelectorForm(forms.Form): +class SearchableSelectMultipleField(forms.Field): + def __init__(self, *args, required=True, widget=None, label=None, disabled=False, + items=None, queryset=None, show_from_noentry=True, show_x_results=-1, + results_scrollable=False, selectable_limit=-1, placeholder="search here", + name="searchable_select", initial=[], **kwargs): + """from the documentation: + # required -- Boolean that specifies whether the field is required. + # True by default. + # widget -- A Widget class, or instance of a Widget class, that should + # be used for this Field when displaying it. Each Field has a + # default Widget that it'll use if you don't specify this. In + # most cases, the default widget is TextInput. + # label -- A verbose name for this field, for use in displaying this + # field in a form. By default, Django will use a "pretty" + # version of the form field name, if the Field is part of a + # Form. + # initial -- A value to use in this Field's initial display. This value + # is *not* used as a fallback if data isn't given. + # help_text -- An optional string to use as "help text" for this Field. + # error_messages -- An optional dictionary to override the default + # messages that the field will raise. + # show_hidden_initial -- Boolean that specifies if it is needed to render a + # hidden widget with initial value after widget. + # validators -- List of additional validators to use + # localize -- Boolean that specifies if the field should be localized. + # disabled -- Boolean that specifies whether the field is disabled, that + # is its widget is shown in the form but not editable. + # label_suffix -- Suffix to be added to the label. Overrides + # form's label_suffix. + """ + + self.widget = widget + if self.widget is None: + self.widget = SearchableSelectMultipleWidget( + attrs={ + 'items': items, + 'initial': [obj.id for obj in initial], + 'show_from_noentry': show_from_noentry, + 'show_x_results': show_x_results, + 'results_scrollable': results_scrollable, + 'selectable_limit': selectable_limit, + 'placeholder': placeholder, + 'name': name, + 'disabled': disabled + } + ) + self.disabled = disabled + self.queryset = queryset + self.selectable_limit = selectable_limit - def __init__(self, data=None, **kwargs): - chosen_resource = "" - bundle = None - edit = False - if "chosen_resource" in kwargs: - chosen_resource = kwargs.pop("chosen_resource") - if "bundle" in kwargs: - bundle = kwargs.pop("bundle") - if "edit" in kwargs: - edit = kwargs.pop("edit") - super(ResourceSelectorForm, self).__init__(data=data, **kwargs) - queryset = GenericResourceBundle.objects.select_related("owner").all() - if data and 'user' in data: - queryset = queryset.filter(owner=data['user']) + super().__init__(disabled=disabled, **kwargs) - attrs = self.build_search_widget_attrs(chosen_resource, bundle, edit, queryset) + self.required = required - self.fields['generic_resource_bundle'] = forms.CharField( - widget=SearchableSelectMultipleWidget(attrs=attrs) + def clean(self, data): + data = data[0] + if not data: + if self.required: + raise ValidationError("Nothing was selected") + else: + return [] + data_as_list = json.loads(data) + if self.selectable_limit != -1: + if len(data_as_list) > self.selectable_limit: + raise ValidationError("Too many items were selected") + + items = [] + for elem in data_as_list: + items.append(self.queryset.get(id=elem)) + + return items + + +class SearchableSelectAbstractForm(forms.Form): + def __init__(self, *args, queryset=None, initial=[], **kwargs): + self.queryset = queryset + items = self.generate_items(self.queryset) + options = self.generate_options() + + super(SearchableSelectAbstractForm, self).__init__(*args, **kwargs) + self.fields['searchable_select'] = SearchableSelectMultipleField( + initial=initial, + items=items, + queryset=self.queryset, + **options ) - def build_search_widget_attrs(self, chosen_resource, bundle, edit, queryset): - resources = {} - for res in queryset: - displayable = {} - displayable['small_name'] = res.name - if res.owner: - displayable['expanded_name'] = res.owner.username - else: - displayable['expanded_name'] = "" - displayable['string'] = res.description - displayable['id'] = res.id - resources[res.id] = displayable - - attrs = { - 'set': resources, - 'show_from_noentry': "true", + def get_validated_bundle(self): + bundles = self.cleaned_data['searchable_select'] + if len(bundles) < 1: # don't need to check for >1, as field does that for us + raise ValidationError("No bundle was selected") + return bundles[0] + + def generate_items(self, queryset): + raise Exception("SearchableSelectAbstractForm does not implement concrete generate_items()") + + def generate_options(self, disabled=False): + return { + 'show_from_noentry': True, 'show_x_results': -1, - 'scrollable': "true", + 'results_scrollable': True, 'selectable_limit': 1, - 'name': "generic_resource_bundle", - 'placeholder': "resource", - 'initial': chosen_resource, - 'edit': edit, - 'wf_type': 1 + 'placeholder': 'Search for a Bundle', + 'name': 'searchable_select', + 'disabled': False } - return attrs -class SWConfigSelectorForm(forms.Form): +class SWConfigSelectorForm(SearchableSelectAbstractForm): + def generate_items(self, queryset): + items = {} - def __init__(self, *args, **kwargs): - chosen_software = "" - bundle = None - edit = False - resource = None - if "chosen_software" in kwargs: - chosen_software = kwargs.pop("chosen_software") - - if "bundle" in kwargs: - bundle = kwargs.pop("bundle") - if "edit" in kwargs: - edit = kwargs.pop("edit") - if "resource" in kwargs: - resource = kwargs.pop("resource") - super(SWConfigSelectorForm, self).__init__(*args, **kwargs) - attrs = self.build_search_widget_attrs(chosen_software, bundle, edit, resource) - self.fields['software_bundle'] = forms.CharField( - widget=SearchableSelectMultipleWidget(attrs=attrs) - ) + for bundle in queryset: + items[bundle.id] = { + 'expanded_name': bundle.name, + 'small_name': bundle.owner.username, + 'string': bundle.description, + 'id': bundle.id + } + + return items - def build_search_widget_attrs(self, chosen, bundle, edit, resource): - configs = {} - queryset = ConfigBundle.objects.select_related('owner').all() - if resource: - queryset = queryset.filter(bundle=resource) + +class OPNFVSelectForm(SearchableSelectAbstractForm): + def generate_items(self, queryset): + items = {} for config in queryset: - displayable = {} - displayable['small_name'] = config.name - displayable['expanded_name'] = config.owner.username - displayable['string'] = config.description - displayable['id'] = config.id - configs[config.id] = displayable - - incompatible_choice = "false" - if bundle and bundle.id not in configs: - displayable = {} - displayable['small_name'] = bundle.name - displayable['expanded_name'] = bundle.owner.username - displayable['string'] = bundle.description - displayable['id'] = bundle.id - configs[bundle.id] = displayable - incompatible_choice = "true" - - attrs = { - 'set': configs, - 'show_from_noentry': "true", - 'show_x_results': -1, - 'scrollable': "true", - 'selectable_limit': 1, - 'name': "software_bundle", - 'placeholder': "config", - 'initial': chosen, - 'edit': edit, - 'wf_type': 2, - 'incompatible': incompatible_choice - } - return attrs + items[config.id] = { + 'expanded_name': config.name, + 'small_name': config.bundle.owner.username, + 'string': config.description, + 'id': config.id + } + + return items + + +class ResourceSelectorForm(SearchableSelectAbstractForm): + def generate_items(self, queryset): + items = {} + + for bundle in queryset: + items[bundle.id] = { + 'expanded_name': bundle.name, + 'small_name': bundle.owner.username, + 'string': bundle.description, + 'id': bundle.id + } + + return items class BookingMetaForm(forms.Form): @@ -186,194 +218,125 @@ class BookingMetaForm(forms.Form): widget=NumberInput( attrs={ "type": "range", - 'min': "0", + 'min': "1", "max": "21", - "value": "0" + "value": "1" } ) ) purpose = forms.CharField(max_length=1000) project = forms.CharField(max_length=400) info_file = forms.CharField(max_length=1000, required=False) + deploy_opnfv = forms.BooleanField(required=False) - def __init__(self, data=None, *args, **kwargs): - chosen_users = [] - if "default_user" in kwargs: - default_user = kwargs.pop("default_user") - else: - default_user = "you" - self.default_user = default_user - if "chosen_users" in kwargs: - chosen_users = kwargs.pop("chosen_users") - elif data and "users" in data: - chosen_users = data.getlist("users") - else: - pass - - super(BookingMetaForm, self).__init__(data=data, **kwargs) + def __init__(self, *args, user_initial=[], owner=None, **kwargs): + super(BookingMetaForm, self).__init__(**kwargs) - self.fields['users'] = forms.CharField( - widget=SearchableSelectMultipleWidget( - attrs=self.build_search_widget_attrs(chosen_users, default_user=default_user) - ), - required=False + self.fields['users'] = SearchableSelectMultipleField( + queryset=UserProfile.objects.select_related('user').exclude(user=owner), + initial=user_initial, + items=get_user_items(exclude=owner), + required=False, + **get_user_field_opts() ) - def build_user_list(self): - """ - returns a mapping of UserProfile ids to displayable objects expected by - searchable multiple select widget - """ - try: - users = {} - d_qset = UserProfile.objects.select_related('user').all().exclude(user__username=self.default_user) - for userprofile in d_qset: - user = { - 'id': userprofile.user.id, - 'expanded_name': userprofile.full_name, - 'small_name': userprofile.user.username, - 'string': userprofile.email_addr - } - - users[userprofile.user.id] = user - - return users - except Exception: - pass - - def build_search_widget_attrs(self, chosen_users, default_user="you"): - - attrs = { - 'set': self.build_user_list(), - 'show_from_noentry': "false", - 'show_x_results': 10, - 'scrollable': "false", - 'selectable_limit': -1, - 'name': "users", - 'placeholder': "username", - 'initial': chosen_users, - 'edit': False - } - return attrs - class MultipleSelectFilterWidget(forms.Widget): - def __init__(self, attrs=None): - super(MultipleSelectFilterWidget, self).__init__(attrs) - self.attrs = attrs + def __init__(self, *args, display_objects=None, filter_items=None, neighbors=None, **kwargs): + super(MultipleSelectFilterWidget, self).__init__(*args, **kwargs) + self.display_objects = display_objects + self.filter_items = filter_items + self.neighbors = neighbors self.template_name = "dashboard/multiple_select_filter_widget.html" def render(self, name, value, attrs=None, renderer=None): - attrs = self.attrs - self.context = self.get_context(name, value, attrs) - html = render_to_string(self.template_name, context=self.context) + context = self.get_context(name, value, attrs) + html = render_to_string(self.template_name, context=context) return mark_safe(html) def get_context(self, name, value, attrs): - return attrs + return { + 'display_objects': self.display_objects, + 'neighbors': self.neighbors, + 'filter_items': self.filter_items, + 'initial_value': value + } class MultipleSelectFilterField(forms.Field): - def __init__(self, required=True, widget=None, label=None, initial=None, - help_text='', error_messages=None, show_hidden_initial=False, - validators=(), localize=False, disabled=False, label_suffix=None): - """from the documentation: - # required -- Boolean that specifies whether the field is required. - # True by default. - # widget -- A Widget class, or instance of a Widget class, that should - # be used for this Field when displaying it. Each Field has a - # default Widget that it'll use if you don't specify this. In - # most cases, the default widget is TextInput. - # label -- A verbose name for this field, for use in displaying this - # field in a form. By default, Django will use a "pretty" - # version of the form field name, if the Field is part of a - # Form. - # initial -- A value to use in this Field's initial display. This value - # is *not* used as a fallback if data isn't given. - # help_text -- An optional string to use as "help text" for this Field. - # error_messages -- An optional dictionary to override the default - # messages that the field will raise. - # show_hidden_initial -- Boolean that specifies if it is needed to render a - # hidden widget with initial value after widget. - # validators -- List of additional validators to use - # localize -- Boolean that specifies if the field should be localized. - # disabled -- Boolean that specifies whether the field is disabled, that - # is its widget is shown in the form but not editable. - # label_suffix -- Suffix to be added to the label. Overrides - # form's label_suffix. - """ - # this is bad, but django forms are annoying - self.widget = widget - if self.widget is None: - self.widget = MultipleSelectFilterWidget() - super(MultipleSelectFilterField, self).__init__( - required=required, - widget=self.widget, - label=label, - initial=None, - help_text=help_text, - error_messages=error_messages, - show_hidden_initial=show_hidden_initial, - validators=validators, - localize=localize, - disabled=disabled, - label_suffix=label_suffix - ) + def __init__(self, **kwargs): + self.initial = kwargs.get("initial") + super().__init__(**kwargs) - def clean(data): - """ - This method will raise a django.forms.ValidationError or return clean data - """ - return data + def to_python(self, value): + return json.loads(value) class FormUtils: @staticmethod - def getLabData(multiple_selectable_hosts): + def getLabData(multiple_hosts=False): """ Gets all labs and thier host profiles and returns a serialized version the form can understand. Should be rewritten with a related query to make it faster - Should be moved outside of global scope """ + # javascript truthy variables + true = 1 + false = 0 + if multiple_hosts: + multiple_hosts = true + else: + multiple_hosts = false labs = {} hosts = {} items = {} - mapping = {} + neighbors = {} for lab in Lab.objects.all(): - slab = {} - slab['id'] = "lab_" + str(lab.lab_user.id) - slab['name'] = lab.name - slab['description'] = lab.description - slab['selected'] = 0 - slab['selectable'] = 1 - slab['follow'] = 1 - slab['multiple'] = 0 - items[slab['id']] = slab - mapping[slab['id']] = [] - labs[slab['id']] = slab + lab_node = { + 'id': "lab_" + str(lab.lab_user.id), + 'model_id': lab.lab_user.id, + 'name': lab.name, + 'description': lab.description, + 'selected': false, + 'selectable': true, + 'follow': false, + 'multiple': false, + 'class': 'lab' + } + if multiple_hosts: + # "follow" this lab node to discover more hosts if allowed + lab_node['follow'] = true + items[lab_node['id']] = lab_node + neighbors[lab_node['id']] = [] + labs[lab_node['id']] = lab_node + for host in lab.hostprofiles.all(): - shost = {} - shost['forms'] = [{"name": "host_name", "type": "text", "placeholder": "hostname"}] - shost['id'] = "host_" + str(host.id) - shost['name'] = host.name - shost['description'] = host.description - shost['selected'] = 0 - shost['selectable'] = 1 - shost['follow'] = 0 - shost['multiple'] = multiple_selectable_hosts - items[shost['id']] = shost - mapping[slab['id']].append(shost['id']) - if shost['id'] not in mapping: - mapping[shost['id']] = [] - mapping[shost['id']].append(slab['id']) - hosts[shost['id']] = shost - - filter_objects = [("labs", labs.values()), ("hosts", hosts.values())] + host_node = { + 'form': {"name": "host_name", "type": "text", "placeholder": "hostname"}, + 'id': "host_" + str(host.id), + 'model_id': host.id, + 'name': host.name, + 'description': host.description, + 'selected': false, + 'selectable': true, + 'follow': false, + 'multiple': multiple_hosts, + 'class': 'host' + } + if multiple_hosts: + host_node['values'] = [] # place to store multiple values + items[host_node['id']] = host_node + neighbors[lab_node['id']].append(host_node['id']) + if host_node['id'] not in neighbors: + neighbors[host_node['id']] = [] + neighbors[host_node['id']].append(lab_node['id']) + hosts[host_node['id']] = host_node + + display_objects = [("lab", labs.values()), ("host", hosts.values())] context = { - 'filter_objects': filter_objects, - 'mapping': mapping, + 'display_objects': display_objects, + 'neighbors': neighbors, 'filter_items': items } return context @@ -382,14 +345,10 @@ class FormUtils: class HardwareDefinitionForm(forms.Form): def __init__(self, *args, **kwargs): - selection_data = kwargs.pop("selection_data", False) super(HardwareDefinitionForm, self).__init__(*args, **kwargs) - attrs = FormUtils.getLabData(1) - attrs['selection_data'] = selection_data + attrs = FormUtils.getLabData(multiple_hosts=True) self.fields['filter_field'] = MultipleSelectFilterField( - widget=MultipleSelectFilterWidget( - attrs=attrs - ) + widget=MultipleSelectFilterWidget(**attrs) ) @@ -422,20 +381,14 @@ class NetworkConfigurationForm(forms.Form): class HostSoftwareDefinitionForm(forms.Form): - fields = ["host_name", "role", "image"] host_name = forms.CharField(max_length=200, disabled=True, required=False) - role = forms.ModelChoiceField(queryset=OPNFVRole.objects.all()) - image = forms.ModelChoiceField(queryset=Image.objects.all()) - + headnode = forms.BooleanField(required=False, widget=forms.HiddenInput) -class SoftwareConfigurationForm(forms.Form): - - name = forms.CharField(max_length=200) - description = forms.CharField(widget=forms.Textarea) - opnfv = forms.BooleanField(disabled=True, required=False) - installer = forms.ModelChoiceField(queryset=Installer.objects.all(), disabled=True, required=False) - scenario = forms.ModelChoiceField(queryset=Scenario.objects.all(), disabled=True, required=False) + def __init__(self, *args, **kwargs): + imageQS = kwargs.pop("imageQS") + super(HostSoftwareDefinitionForm, self).__init__(*args, **kwargs) + self.fields['image'] = forms.ModelChoiceField(queryset=imageQS) class WorkflowSelectionForm(forms.Form): @@ -459,7 +412,7 @@ class SnapshotHostSelectForm(forms.Form): host = forms.CharField() -class SnapshotMetaForm(forms.Form): +class BasicMetaForm(forms.Form): name = forms.CharField() description = forms.CharField(widget=forms.Textarea) @@ -473,3 +426,23 @@ class ConfirmationForm(forms.Form): (False, "Cancel") ) ) + + +class OPNFVSelectionForm(forms.Form): + installer = forms.ModelChoiceField(queryset=Installer.objects.all(), required=True) + scenario = forms.ModelChoiceField(queryset=Scenario.objects.all(), required=True) + + +class OPNFVNetworkRoleForm(forms.Form): + role = forms.CharField(max_length=200, disabled=True, required=False) + + def __init__(self, *args, config_bundle, **kwargs): + super(OPNFVNetworkRoleForm, self).__init__(*args, **kwargs) + self.fields['network'] = forms.ModelChoiceField( + queryset=config_bundle.bundle.networks.all() + ) + + +class OPNFVHostRoleForm(forms.Form): + host_name = forms.CharField(max_length=200, disabled=True, required=False) + role = forms.ModelChoiceField(queryset=OPNFVRole.objects.all().order_by("name").distinct("name")) |