class SubmissionForm(forms.ModelForm): answers = SimpleArrayField(SimpleArrayField(forms.CharField()), delimiter='|') class Meta: model = Submission fields = '__all__'
class RackReservationCSVForm(CustomFieldModelCSVForm): site = CSVModelChoiceField(queryset=Site.objects.all(), to_field_name='name', help_text='Parent site') location = CSVModelChoiceField(queryset=Location.objects.all(), to_field_name='name', required=False, help_text="Rack's location (if any)") rack = CSVModelChoiceField(queryset=Rack.objects.all(), to_field_name='name', help_text='Rack') units = SimpleArrayField( base_field=forms.IntegerField(), required=True, help_text='Comma-separated list of individual unit numbers') tenant = CSVModelChoiceField(queryset=Tenant.objects.all(), required=False, to_field_name='name', help_text='Assigned tenant') class Meta: model = RackReservation fields = ('site', 'location', 'rack', 'units', 'tenant', 'description') def __init__(self, data=None, *args, **kwargs): super().__init__(data, *args, **kwargs) if data: # Limit location queryset by assigned site params = { f"site__{self.fields['site'].to_field_name}": data.get('site') } self.fields['location'].queryset = self.fields[ 'location'].queryset.filter(**params) # Limit rack queryset by assigned site and group params = { f"site__{self.fields['site'].to_field_name}": data.get('site'), f"location__{self.fields['location'].to_field_name}": data.get('location'), } self.fields['rack'].queryset = self.fields['rack'].queryset.filter( **params)
class CloneForm(forms.Form): """Clone storage/interface form.""" source = NodeChoiceField( label="Source", queryset=Machine.objects.all(), required=True, initial=None, help_text="The source machine to clone from.", ) destinations = SimpleArrayField( NodeChoiceField(queryset=Machine.objects.all()), label="Destinations", min_length=1, error_messages={ "item_invalid": "Machine %(nth)s in the array did not validate:" }, help_text="The destinations to clone to.", ) storage = forms.BooleanField( label="Storage", required=False, help_text="Clone the storage configuration.", ) interfaces = forms.BooleanField( label="Interfaces", required=False, help_text="Clone the interfaces configuration.", ) def __init__(self, user, **kwargs): self.user = user super().__init__(**kwargs) self.fields["source"].queryset = Machine.objects.get_nodes( self.user, NodePermission.view) self.fields[ "destinations"].base_field.queryset = Machine.objects.get_nodes( self.user, NodePermission.admin, from_nodes=Machine.objects.filter( status__in={NODE_STATUS.READY, NODE_STATUS.FAILED_TESTING }), ) def clean(self): """Validate that the form is valid and that the destinations can accept the storage and/or interfaces configuration from the source.""" cleaned_data = super().clean() source = self.cleaned_data.get("source") if not source: # Django should be placing this automatically, but it does not # occur. So we force the setting of this error here. set_form_error(self, "source", "This field is required.") destinations = self.cleaned_data.get("destinations") destination_field = self.fields["destinations"] item_invalid = destination_field.error_messages["item_invalid"] storage = self.cleaned_data.get("storage", False) interfaces = self.cleaned_data.get("interfaces", False) if source and destinations: for index, dest in enumerate(destinations, 1): if source == dest: error = prefix_validation_error( ValidationError( "Source machine cannot be a destination machine."), prefix=item_invalid, code="item_invalid", params={"nth": index}, ) set_form_error(self, "destinations", error) else: if storage: try: dest._get_storage_mapping_between_nodes(source) except ValidationError as exc: error = prefix_validation_error( exc, prefix=item_invalid, code="item_invalid", params={"nth": index}, ) set_form_error(self, "destinations", error) if interfaces: try: dest._get_interface_mapping_between_nodes(source) except ValidationError as exc: error = prefix_validation_error( exc, prefix=item_invalid, code="item_invalid", params={"nth": index}, ) set_form_error(self, "destinations", error) if not storage and not interfaces: set_form_error(self, "__all__", "Either storage or interfaces must be true.") return cleaned_data def save(self): """Clone the storage and/or interfaces configuration to the destinations.""" source = self.cleaned_data.get("source") destinations = self.cleaned_data.get("destinations") storage = self.cleaned_data.get("storage", False) interfaces = self.cleaned_data.get("interfaces", False) for dest in destinations: if storage: dest.set_storage_configuration_from_node(source) if interfaces: dest.set_networking_configuration_from_node(source)
class CloneForm(forms.Form): """Clone storage/interface form.""" source = NodeChoiceField( label="Source", queryset=Machine.objects.all(), required=True, initial=None, help_text="The source machine to clone from.", ) destinations = SimpleArrayField( NodeChoiceField(queryset=Machine.objects.all()), label="Destinations", min_length=1, error_messages={ "item_invalid": "Machine %(nth)s is invalid:", "is-source": "Source machine %(machine)s cannot be a destination machine.", "storage": "%(machine)s is invalid:", "networking": "%(machine)s is invalid:", }, help_text="The destinations to clone to.", ) storage = forms.BooleanField( label="Storage", required=False, help_text="Clone the storage configuration.", ) interfaces = forms.BooleanField( label="Interfaces", required=False, help_text="Clone the interfaces configuration.", ) def __init__(self, user, **kwargs): self.user = user super().__init__(**kwargs) self.fields["source"].queryset = Machine.objects.get_nodes( self.user, NodePermission.view) self.fields[ "destinations"].base_field.queryset = Machine.objects.get_nodes( self.user, NodePermission.admin, from_nodes=Machine.objects.filter( status__in={NODE_STATUS.READY, NODE_STATUS.FAILED_TESTING }), ) def clean(self): """Validate that the form is valid and that the destinations can accept the storage and/or interfaces configuration from the source.""" cleaned_data = super().clean() source = self.cleaned_data.get("source") if not source: # Django should be placing this automatically, but it does not # occur. So we force the setting of this error here. self.add_error("source", "This field is required.") destinations = self.cleaned_data.get("destinations") destination_field = self.fields["destinations"] storage = self.cleaned_data.get("storage", False) interfaces = self.cleaned_data.get("interfaces", False) if source and destinations: for dest in destinations: if source == dest: error = ValidationError( destination_field.error_messages["is-source"], code="is-source", params={ "machine": str(dest), "system_id": dest.system_id, }, ) self.add_error("destinations", error) else: if storage: try: dest._get_storage_mapping_between_nodes(source) except ValidationError as exc: error = prefix_validation_error( exc, prefix=destination_field. error_messages["storage"], code="storage", params={ "machine": str(dest), "system_id": dest.system_id, }, ) self.add_error("destinations", error) if interfaces: try: dest._get_interface_mapping_between_nodes(source) except ValidationError as exc: error = prefix_validation_error( exc, prefix=destination_field. error_messages["networking"], code="networking", params={ "machine": str(dest), "system_id": dest.system_id, }, ) self.add_error("destinations", error) if not storage and not interfaces: self.add_error( "__all__", ValidationError( "Either storage or interfaces must be true.", code="required", ), ) return cleaned_data def strip_failed_destinations(self): """Remove destinations that have errors.""" if "destinations" not in self.cleaned_data: submitted_destinations = self.data.get("destinations") # Don't try and manipulate empty submission if not submitted_destinations: return errors = self.errors.as_data() bogus_system_ids = { error.params["system_id"] for error in errors["destinations"] } return [ dest for dest in submitted_destinations if dest not in bogus_system_ids ] def save(self): """Clone the storage and/or interfaces configuration to the destinations.""" source = self.cleaned_data.get("source") destinations = self.cleaned_data.get("destinations", []) storage = self.cleaned_data.get("storage", False) interfaces = self.cleaned_data.get("interfaces", False) for dest in destinations: if storage: dest.set_storage_configuration_from_node(source) if interfaces: dest.set_networking_configuration_from_node(source)