community/front-end/ofe/website/ghpcfe/forms.py (826 lines of code) (raw):

# Copyright 2022 Google LLC # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. """ forms.py """ import logging from django import forms from django.contrib.auth.forms import ( UserChangeForm as BaseUserChangeForm, UserCreationForm as BaseUserCreationForm, ) from django.db.models import Q from django.forms import ValidationError from django.utils.safestring import mark_safe from .cluster_manager import cloud_info from .cluster_manager import validate_credential # If we have a model, it has a form - pretty much from .models import * # pylint: disable=wildcard-import,unused-wildcard-import logger = logging.getLogger(__name__) class UserCreationForm(BaseUserCreationForm): """Custom UserCreationForm""" class Meta(BaseUserCreationForm): model = User fields = ("email",) class UserUpdateForm(BaseUserChangeForm): """Custom form for updating user account""" password = None class Meta: model = User fields = ("email",) widgets = { "email": forms.TextInput(attrs={"class": "form-control"}), } class UserAdminUpdateForm(forms.ModelForm): """Custom form for Admin update of users""" class Meta: model = User fields = ( "username", "email", "roles", "quota_type", "quota_amount", ) widgets = { "username": forms.TextInput(attrs={"class": "form-control"}), "email": forms.TextInput(attrs={"class": "form-control"}), "roles": forms.SelectMultiple(attrs={"class": "form-control"}), "quota_type": forms.Select( attrs={"class": "form-control", "disabled": False} ), "quota_amount": forms.NumberInput(attrs={"class": "form-control"}), } class CredentialForm(forms.ModelForm): """Custom form for Credential model implementing additional validation""" class Meta: model = Credential fields = ("name", "detail") widgets = { "name": forms.TextInput(attrs={"class": "form-control"}), "detail": forms.Textarea(attrs={"class": "form-control"}), } def clean(self): super().clean() # validate the credential details with cloud platform detail = self.cleaned_data["detail"] validated = validate_credential.validate_credential("GCP", detail) if not validated: raise ValidationError("Credential cannot be validated.") class ClusterForm(forms.ModelForm): """Custom form for Cluster model implementing option filtering""" def _get_creds(self, kwargs): # We do this, because on Create views, there isn't an instance, so we # set the creds via the 'initial' data field. On Updates, there is # an object, so pull from there if "cloud_credential" in kwargs["initial"]: creds = kwargs["initial"]["cloud_credential"] else: creds = self.instance.cloud_credential return creds def __init__(self, *args, **kwargs): super().__init__(*args, **kwargs) # For machine types, will use JS to get valid types dependent on # cloud zone. So bypass cleaning and choices def prep_dynamic_select(field, value): self.fields[field].widget.choices = [ ( value, value ) ] self.fields[field].clean = lambda value: value prep_dynamic_select( "controller_instance_type", self.instance.controller_instance_type ) prep_dynamic_select( "controller_disk_type", self.instance.controller_disk_type ) prep_dynamic_select( "login_node_instance_type", self.instance.login_node_instance_type ) prep_dynamic_select( "login_node_disk_type", self.instance.login_node_disk_type ) # If cluster is running make some of form field ready only. if self.instance.status == "r": logger.info("Cluster is running making some fields ready only") # Define a list of field names you want to set as readonly fields_to_make_readonly = ['cloud_credential', 'name', 'subnet', 'cloud_region', 'cloud_zone'] # Loop through the fields and set the 'readonly' attribute for field_name in fields_to_make_readonly: self.fields[field_name].widget = forms.TextInput(attrs={'class': 'form-control'}) self.fields[field_name].widget.attrs['readonly'] = True class Meta: model = Cluster fields = ( "cloud_credential", "name", "subnet", "cloud_region", "cloud_zone", "authorised_users", "spackdir", "controller_instance_type", "controller_disk_type", "controller_disk_size", "num_login_nodes", "login_node_instance_type", "login_node_disk_type", "login_node_disk_size", "login_node_image", "controller_node_image", "use_cloudsql", "use_bigquery", ) widgets = { "name": forms.TextInput(attrs={"class": "form-control"}), "cloud_credential": forms.Select( attrs={"class": "form-control"} ), "subnet": forms.Select(attrs={"class": "form-control"}), "cloud_region": forms.Select(attrs={"class": "form-control", "readonly": "readonly"}), "cloud_zone": forms.Select(attrs={"class": "form-control"}), "authorised_users": forms.SelectMultiple(attrs={"class": "form-control"}), "spackdir": forms.TextInput(attrs={"class": "form-control"}), "controller_instance_type": forms.Select( attrs={"class": "form-control machine_type_select"} ), "controller_disk_size": forms.NumberInput( attrs={"class": "form-control"} ), "controller_disk_type": forms.Select( attrs={"class": "form-control disk_type_select"} ), "login_node_instance_type": forms.Select( attrs={"class": "form-control machine_type_select"} ), "login_node_disk_size": forms.NumberInput( attrs={"class": "form-control"} ), "login_node_disk_type": forms.Select( attrs={"class": "form-control disk_type_select"} ), "num_login_nodes": forms.NumberInput( attrs={"class": "form-control"} ), "login_node_image": forms.Select(attrs={"class": "form-control", "id": "login-node-image", "name": "login_node_image", "value": "",}), "controller_node_image": forms.Select(attrs={"class": "form-control", "id": "controller-node-image", "name": "controller_node_image", "value": "",}), "use_cloudsql": forms.CheckboxInput(attrs={"class": "required checkbox"}), "use_bigquery": forms.CheckboxInput(attrs={"class": "required checkbox"}), } class ClusterMountPointForm(forms.ModelForm): """Form for Cluster Mount points""" class Meta: model = MountPoint fields = ("export", "mount_order", "mount_path", "mount_options") def __init__(self, *args, **kwargs): super().__init__(*args, **kwargs) for field in self.fields: self.fields[field].widget.attrs.update({"class": "form-control"}) class ClusterPartitionForm(forms.ModelForm): """Form for Cluster Partitions""" machine_type = forms.ChoiceField(widget=forms.Select()) GPU_type = forms.ChoiceField(widget=forms.Select()) # pylint: disable=invalid-name class Meta: model = ClusterPartition fields = ( "name", "machine_type", "image", "dynamic_node_count", "static_node_count", "reservation_name", "exclusive", "enable_placement", "enable_hyperthreads", "enable_tier1_networking", "enable_node_reuse", "GPU_type", "GPU_per_node", "boot_disk_type", "boot_disk_size", "additional_disk_type", "additional_disk_count", "additional_disk_size", "additional_disk_auto_delete" ) def __init__(self, *args, **kwargs): super().__init__(*args, **kwargs) for field in self.fields: self.fields[field].widget.attrs.update({"class": "form-control"}) if self.fields[field].help_text: self.fields[field].widget.attrs.update( {"title": self.fields[field].help_text} ) self.fields["boot_disk_type"].widget = forms.Select(attrs={"class": "form-control disk_type_select"}) self.fields["additional_disk_type"].widget = forms.Select(attrs={"class": "form-control disk_type_select"}) self.fields["machine_type"].widget.attrs[ "class" ] += " machine_type_select" def prep_dynamic_select(field, value): self.fields[field].widget.choices = [ ( value, value ) ] self.fields[field].clean = lambda value: value prep_dynamic_select( "boot_disk_type", self.instance.boot_disk_type ) prep_dynamic_select( "additional_disk_type", self.instance.additional_disk_type ) prep_dynamic_select( "machine_type", self.instance.machine_type ) prep_dynamic_select( "GPU_type", self.instance.GPU_type ) # Mark 'reservation_name' as optional self.fields["reservation_name"].widget.attrs.update({"placeholder": "Optional"}) def clean(self): cleaned_data = super().clean() if cleaned_data["enable_placement"] and cleaned_data[ "machine_type" ].split("-")[0] in ["e2", "t2d", "n1", "t2a", "m1", "m2", "m3"]: raise ValidationError( "SlurmGCP does not support Placement Groups for selected instance type" # pylint: disable=line-too-long ) # schedmd-slurm-gcp-v6-partition/outputs.tf if cleaned_data["dynamic_node_count"] > 0 and not cleaned_data[ "exclusive" ]: raise ValidationError( "If any non-static nodesets have enable placement set to true, exclusive must be true." ) if cleaned_data["static_node_count"] > 0 and cleaned_data[ "exclusive" ]: raise ValidationError( "Can't use static nodes within partition with exclusive set to true." ) # schedmd-slurm-gcp-v6-nodeset/outputs.tf if cleaned_data["reservation_name"] and cleaned_data[ "enable_placement" ]: raise ValidationError("If a reservation is specified, placement must be false.") if cleaned_data["enable_placement"] and cleaned_data[ "static_node_count" ] > 0 and cleaned_data[ "dynamic_node_count" ] > 0: raise ValidationError( "Cannot use placement with static and auto-scaling nodes in the same node set." ) # Reservation validation logic reservation_name = cleaned_data.get("reservation_name") if reservation_name: try: cluster = cleaned_data.get('cluster') cloud_credential = cluster.cloud_credential.detail cloud_zone = cluster.cloud_zone # logger.info(f"Cluster: {cluster}") # logger.info(f"Cloud Credential: {cloud_credential}") # logger.info(f"Cloud Zone: {cloud_zone}") reservations = cloud_info.get_vm_reservations("GCP", cloud_credential, None, cloud_zone) if not reservations: raise ValidationError("No reservations found for the specified zone.") matching_reservation = reservations.get(reservation_name) if not matching_reservation: raise ValidationError( f"Reservation {reservation_name} does not exist in the specified zone." ) if matching_reservation[ "instanceProperties" ][ "machineType" ] != cleaned_data["machine_type"]: raise ValidationError( f"Reservation {reservation_name} does not support the specified machine type. " f"Machine type: {cleaned_data['machine_type']}." ) total_requested_nodes = cleaned_data["dynamic_node_count"] + cleaned_data["static_node_count"] available_nodes = matching_reservation.get("instanceProperties", {}).get("availableCount", 0) if total_requested_nodes > available_nodes: raise ValidationError( f"Reservation {reservation_name} does not have enough available nodes." f"Requested: {total_requested_nodes}, Available: {available_nodes}" ) specific_reservation = matching_reservation.get("specificReservationRequired") if specific_reservation == False: raise ValidationError( f"You must use a 'specific' reservation type." f"Please read the following URL for more information about setting up reservations:" f"https://cloud.google.com/compute/docs/instances/reservations-overview#how-reservations-work" ) except Exception as e: logger.error(f"Error validating reservation: {reservation_name}. Exception: {e}") raise ValidationError( f"Error validating reservation: {reservation_name}. Exception: {str(e)}" ) return cleaned_data class WorkbenchForm(forms.ModelForm): """Custom form for Workbench model implementing option filtering""" def clean(self): cleaned_data = super().clean() subnet = cleaned_data.get("subnet") if subnet.cloud_region not in self.workbench_zones: validation_error_message = ( f"Network {subnet.vpc.cloud_id} has an invalid region & zone " "for Vertex AI Workbenches: {subnet.cloud_region}. Please see " '<a href="https://cloud.google.com/vertex-ai/docs/general/' 'locations#vertex-ai-workbench-locations" target="_blank"> ' "Workbench Documentation</a> for more information on region " "availability." ) raise forms.ValidationError(mark_safe(validation_error_message)) user = cleaned_data.get("trusted_user") # check user is associated with a social login account try: if user.socialaccount_set.first().uid: pass except: raise forms.ValidationError( # pylint: disable=raise-missing-from "User not associated with a required Social ID " ) def __init__(self, user, *args, **kwargs): has_creds = "cloud_credential" in kwargs if has_creds: credential = kwargs.pop("cloud_credential") kwargs["initial"]["cloud_credential"] = credential super().__init__(*args, **kwargs) if not has_creds: credential = self.instance.cloud_credential zone_choices = None if "zone_choices" in kwargs: zone_choices = kwargs.pop("zone_choices") if self.instance.id: for field in self.fields: if field != "name": self.fields[field].disabled = True self.fields["subnet"].queryset = VirtualSubnet.objects.filter( cloud_credential=credential ).filter(Q(cloud_state="i") | Q(cloud_state="m")) if zone_choices: # We set this on the widget, because we will be changing the # widget's field in the template via javascript self.fields["cloud_zone"].widget.choices = zone_choices if "n" not in self.instance.cloud_state: # Need to disable certain widgets self.fields["subnet"].disabled = True self.fields["cloud_zone"].disabled = True self.fields["attached_cluster"].disabled = True self.workbench_zones = cloud_info.get_gcp_workbench_region_zone_info( credential.detail ) self.fields["trusted_user"].queryset = ( User.objects.exclude(socialaccount__isnull=True) ) # Pull instance types from cloud_info instance_types = cloud_info.get_machine_types( "GCP", credential.detail, "europe-west4", "europe-west4-a" ) # set variables for retrieving instance types for dropdown menu choices_list = [] instance_list = [] category = "" # Populate dropdown menu with preset instance_types from # WorkbenchPresets for preset in WorkbenchPreset.objects.order_by("category").values(): # if category variable has changed from last loop then append # instances to overall choices list as tuple and clear instance_list if category != preset["category"]: if category: choices_list.append((category, tuple(instance_list))) instance_list = [] # set category to current value and append preset to dropdown menu # list category = preset["category"] instance_list.append((preset["machine_type"], preset["name"])) # append final preset instance type from loop choices_list.append((category, tuple(instance_list))) category = "" if Role.CLUSTERADMIN in [x.id for x in user.roles.all()]: for instance_type in sorted(instance_types): # if family variable has changed from last loop then append # instances to overall choices list as tuple and clear # instance_list if category != instance_types[instance_type]["family"]: if category: choices_list.append((category, tuple(instance_list))) instance_list = [] # save family of current instance category = instance_types[instance_type]["family"] # create instance string for displaying to user instance_string = ( instance_types[instance_type]["name"] + " - " + str(instance_types[instance_type]["vCPU"]) + "x " + instance_types[instance_type]["arch"] + " vCPUs with " + str(instance_types[instance_type]["memory"]) + " Memory" ) # append tuple to instance list instance_list.append( (instance_types[instance_type]["name"], instance_string) ) # append final preset instance type from loop choices_list.append((category, tuple(instance_list))) self.fields["machine_type"].widget.choices = choices_list self.fields["attached_cluster"].queryset= Cluster.objects.filter( cloud_state="m" ) class Meta: model = Workbench fields = ( "name", "subnet", "cloud_zone", "cloud_credential", "trusted_user", "machine_type", "boot_disk_type", "boot_disk_capacity", "image_family", "attached_cluster", ) widgets = { "name": forms.TextInput(attrs={"class": "form-control"}), "cloud_credential": forms.Select( attrs={"class": "form-control", "disabled": True} ), "subnet": forms.Select(attrs={"class": "form-control"}), "machine_type": forms.Select(attrs={"class": "form-control"}), "cloud_zone": forms.Select(attrs={"class": "form-control"}), "trusted_user": forms.Select(attrs={"class": "form-control"}), "attached_cluster": forms.Select(attrs={"class": "form-control"}), } class ApplicationEditForm(forms.ModelForm): """Custom form for application model""" class Meta: model = Application fields = ("name", "description") widgets = { "name": forms.TextInput(attrs={"class": "form-control"}), "description": forms.Textarea(attrs={"class": "form-control"}), "load_command": forms.TextInput(attrs={"class": "form-control"}), } class ApplicationForm(forms.ModelForm): """Custom form for application model""" installation_path = forms.CharField( widget=forms.TextInput(attrs={"class": "form-control"}), help_text="Path where application was installed.", ) class Meta: model = Application fields = ( "cluster", "name", "version", "description", "load_command", "installed_architecture", "compiler", "mpi", ) widgets = { "cluster": forms.Select( attrs={"class": "form-control", "disabled": True} ), "name": forms.TextInput(attrs={"class": "form-control"}), "version": forms.TextInput(attrs={"class": "form-control"}), "description": forms.Textarea(attrs={"class": "form-control"}), "load_command": forms.TextInput(attrs={"class": "form-control"}), "installed_architecture": forms.TextInput( attrs={"class": "form-control"} ), "compiler": forms.TextInput(attrs={"class": "form-control"}), "mpi": forms.TextInput(attrs={"class": "form-control"}), } def __init__(self, *args, **kwargs): super().__init__(*args, **kwargs) class CustomInstallationApplicationForm(forms.ModelForm): """Form to collect custom app installation details""" install_loc = forms.CharField( widget=forms.TextInput(attrs={"class": "form-control"}), help_text="Path where application will be installed.", ) class Meta: model = CustomInstallationApplication fields = ( "cluster", "name", "version", "description", "install_partition", "install_script", "module_name", "module_script", ) widgets = { "cluster": forms.Select( attrs={"class": "form-control", "disabled": True} ), "name": forms.TextInput(attrs={"class": "form-control"}), "version": forms.TextInput(attrs={"class": "form-control"}), "description": forms.Textarea(attrs={"class": "form-control"}), "install_partition": forms.Select(attrs={"class": "form-control"}), "install_script": forms.URLInput(attrs={"class": "form-control"}), "module_name": forms.TextInput(attrs={"class": "form-control"}), "module_script": forms.Textarea(attrs={"class": "form-control"}), } def __init__(self, *args, **kwargs): super().__init__(*args, **kwargs) cluster = kwargs["initial"]["cluster"] self.fields["install_partition"].queryset = cluster.partitions class SpackApplicationForm(forms.ModelForm): """Custom form for application model""" class Meta: model = SpackApplication fields = ( "cluster", "spack_name", "name", "version", "spack_spec", "description", "install_partition", ) widgets = { "cluster": forms.Select( attrs={"class": "form-control", "disabled": True} ), "spack_name": forms.TextInput(attrs={"class": "form-control"}), "name": forms.TextInput(attrs={"class": "form-control"}), "spack_spec": forms.TextInput(attrs={"class": "form-control"}), "version": forms.Select(attrs={"class": "form-control"}), "description": forms.Textarea(attrs={"class": "form-control"}), "install_partition": forms.Select(attrs={"class": "form-control"}), } def __init__(self, *args, **kwargs): super().__init__(*args, **kwargs) cluster = kwargs["initial"]["cluster"] self.fields["install_partition"].queryset = cluster.partitions class JobForm(forms.ModelForm): """Custom form for job model""" class Meta: model = Job fields = ( "name", "application", "cluster", "partition", "number_of_nodes", "ranks_per_node", "threads_per_rank", "wall_clock_time_limit", "run_script", "cleanup_choice", "input_data", "result_data", "benchmark", ) widgets = { "name": forms.TextInput(attrs={"class": "form-control"}), "application": forms.HiddenInput(), "cluster": forms.Select( attrs={"class": "form-control", "disabled": True} ), "partition": forms.Select(attrs={"class": "form-control"}), "number_of_nodes": forms.NumberInput( attrs={"class": "form-control", "min": "1"} ), "ranks_per_node": forms.NumberInput( attrs={"class": "form-control", "min": "1"} ), "threads_per_rank": forms.NumberInput( attrs={"class": "form-control", "min": "1", "readonly": True} ), "wall_clock_time_limit": forms.NumberInput( attrs={"class": "form-control", "min": "1"} ), "run_script": forms.URLInput(attrs={"class": "form-control"}), "input_data": forms.URLInput(attrs={"class": "form-control"}), "result_data": forms.URLInput(attrs={"class": "form-control"}), "cleanup_choice": forms.Select(attrs={"class": "form-control"}), "benchmark": forms.Select(attrs={"class": "form-control"}), } def __init__(self, *args, **kwargs): super().__init__(*args, **kwargs) cluster = kwargs["initial"]["cluster"] self.fields["partition"].queryset = cluster.partitions class BenchmarkForm(forms.ModelForm): """Custom form for benchmark model""" class Meta: model = Benchmark fields = ("name", "description") widgets = { "name": forms.TextInput(attrs={"class": "form-control"}), "description": forms.Textarea(attrs={"class": "form-control"}), } class VPCForm(forms.ModelForm): """Custom form for VPC model implementing option filtering""" subnets = forms.MultipleChoiceField( widget=forms.SelectMultiple(attrs={"class": "form-control"}), help_text="Available Subnets", required=False, ) def __init__(self, *args, **kwargs): super().__init__(*args, **kwargs) self.fields["cloud_region"].widget.choices = [ (x, x) for x in kwargs["initial"]["regions"] ] self.fields["subnets"].choices = kwargs["initial"].get( "available_subnets", [] ) class Meta: model = VirtualNetwork fields = ("name", "cloud_credential", "cloud_region") widgets = { "name": forms.TextInput(attrs={"class": "form-control"}), "cloud_region": forms.Select(attrs={"class": "form-control"}), "cloud_credential": forms.Select( attrs={"class": "form-control", "disabled": True} ), } class VPCImportForm(forms.ModelForm): """Form for importing externally created VPCs""" subnets = forms.MultipleChoiceField( widget=forms.SelectMultiple( attrs={"class": "form-control", "disabled": True} ) ) vpc = forms.ChoiceField( widget=forms.Select( attrs={"class": "form-control", "onchange": "vpcSelected()"} ) ) def __init__(self, *args, **kwargs): super().__init__(*args, **kwargs) self.fields["subnets"].choices = kwargs["initial"]["subnets"] self.fields["vpc"].choices = kwargs["initial"]["vpc"] class Meta: model = VirtualNetwork fields = ("name", "cloud_credential") widgets = { "name": forms.TextInput(attrs={"class": "form-control"}), "cloud_credential": forms.Select( attrs={"class": "form-control", "disabled": True} ), } class VirtualSubnetForm(forms.ModelForm): """Form for VirtualSubnet model to be embedded""" class Meta: model = VirtualSubnet fields = ("name", "cidr", "cloud_region") widgets = { "name": forms.TextInput(attrs={"class": "form-control"}), "cidr": forms.TextInput(attrs={"class": "form-control"}), "cloud_region": forms.Select(attrs={"class": "form-control"}), } class FilesystemImportForm(forms.ModelForm): """Form to import externally managed filesystems""" share_name = forms.CharField( label="Export Name", help_text="Mount point from this filesystem (ie: /shared)", widget=forms.TextInput(attrs={"class": "form-control"}), validators=[ RegexValidator( regex="^/[-a-zA-Z0-9_]{1,63}", message=( "Share name must start with a '/' and be no more than 64 " "characters long, with no spaces" ), ), ], ) class Meta: model = Filesystem fields = ("name", "vpc", "cloud_zone", "hostname_or_ip", "fstype") widgets = { "name": forms.TextInput(attrs={"class": "form-control"}), "cloud_credential": forms.Select( attrs={"class": "form-control", "disabled": True} ), "vpc": forms.Select(attrs={"class": "form-control"}), "cloud_zone": forms.Select(attrs={"class": "form-control"}), "hostname_or_ip": forms.TextInput(attrs={"class": "form-control"}), "fstype": forms.Select(attrs={"class": "form-control"}), } def _get_creds(self, kwargs): # We do this, because on Create views, there isn't an instance, so we # set the creds via the 'initial' data field. On Updates, there is # an object, so pull from there if "cloud_credential" in kwargs["initial"]: creds = kwargs["initial"]["cloud_credential"] else: creds = self.instance.cloud_credential return creds def __init__(self, *args, **kwargs): super().__init__(*args, **kwargs) creds = self._get_creds(kwargs) self.fields["vpc"].queryset = VirtualNetwork.objects.filter( cloud_credential=creds ).filter(Q(cloud_state="i") | Q(cloud_state="m")) region_info = cloud_info.get_region_zone_info("GCP", creds.detail) self.fields["cloud_zone"].widget.choices = [ (r, [(z, z) for z in rz]) for r, rz in region_info.items() ] class FilestoreForm(forms.ModelForm): """Custom form for GCP Filestoremodel implementing option filtering""" share_name = forms.CharField( label="Export Name", max_length=16, validators=[ RegexValidator( regex="^/[-a-zA-Z0-9_]{1,16}", message=( "Share name must start with a '/' and be no more than 16 " "characters long, with no spaces" ), ), ], ) def _get_creds(self, kwargs): # We do this, because on Create views, there isn't an instance, so we # set the creds via the 'initial' data field. On Updates, there is # an object, so pull from there if "cloud_credential" in kwargs["initial"]: creds = kwargs["initial"]["cloud_credential"] else: creds = self.instance.cloud_credential return creds def __init__(self, *args, **kwargs): zone_choices = None if "zone_choices" in kwargs: zone_choices = kwargs.pop("zone_choices") super().__init__(*args, **kwargs) creds = self._get_creds(kwargs) self.fields["vpc"].queryset = VirtualNetwork.objects.filter( cloud_credential=creds ).filter(Q(cloud_state="i") | Q(cloud_state="m")) region_info = cloud_info.get_region_zone_info("GCP", creds.detail) self.fields["cloud_zone"].widget.choices = [ (r, [(z, z) for z in rz]) for r, rz in region_info.items() ] if zone_choices: # We set this on the widget, because we will be changing the # widget's field in the template via javascript self.fields["cloud_zone"].widget.choices = zone_choices if "n" not in self.instance.cloud_state: # Need to disable certain widgets self.fields["vpc"].disabled = True self.fields["cloud_zone"].disabled = True self.fields["share_name"].disabled = True self.fields["performance_tier"].disabled = True class Meta: model = GCPFilestoreFilesystem fields = ( "name", "vpc", "cloud_zone", "cloud_credential", "capacity", "performance_tier", ) widgets = { "name": forms.TextInput(attrs={"class": "form-control"}), "cloud_credential": forms.Select( attrs={"class": "form-control", "disabled": True} ), "vpc": forms.Select(attrs={"class": "form-control"}), "capacity": forms.NumberInput(attrs={"min": 2660, "default": 2660}), "share_name": forms.TextInput(attrs={"class": "form-control"}), "cloud_zone": forms.Select(attrs={"class": "form-control"}), } class WorkbenchMountPointForm(forms.ModelForm): """Form for Cluster Mount points""" class Meta: model = WorkbenchMountPoint fields = ("workbench", "export", "mount_order", "mount_path") def __init__(self, *args, **kwargs): super().__init__(*args, **kwargs) for field in self.fields: self.fields[field].widget.attrs.update({"class": "form-control"}) class StartupScriptForm(forms.ModelForm): """ Custom form for handling data input and validation for the StartupScript model. This form class extends the `forms.ModelForm` class and is designed to work with the `StartupScript` model, which represents a script executed during the startup phase of a node. Form Fields: - "name": A text input field for providing a name for the startup script. - "description": A textarea input field for adding a description of the script. - "type": A select input field for choosing the type or category of the script. - "content": A file input field for uploading the content of the startup script. Form Validation: The form automatically validates the input data based on the model field definitions and any additional constraints defined in the model. """ class Meta: model = StartupScript fields = ( "name", "description", "type", "content", ) widgets = { "name": forms.TextInput(attrs={"class": "form-control"}), "description": forms.Textarea(attrs={"class": "form-control"}), "type": forms.Select(attrs={"class": "form-control"}), "content": forms.ClearableFileInput(attrs={"class": "form-control"}), } class ImageForm(forms.ModelForm): """Custom form for Image model""" class Meta: model = Image fields = ( "cloud_credential", "name", "family", "cloud_region", "cloud_zone", "source_image_project", "source_image_family", "startup_script", "enable_os_login", "block_project_ssh_keys", "authorised_users" ) widgets = { "cloud_credential": forms.Select(attrs={"class": "form-control"}), "name": forms.TextInput(attrs={"class": "form-control"}), "family": forms.TextInput(attrs={"class": "form-control"}), "cloud_region": forms.Select(attrs={"class": "form-control"}), "cloud_zone": forms.Select(attrs={"class": "form-control"}), "source_image_project": forms.TextInput(attrs={"class": "form-control"}), "source_image_family": forms.TextInput(attrs={"class": "form-control"}), "startup_script": forms.SelectMultiple(attrs={"class": "form-control"}), "enable_os_login": forms.RadioSelect(), "block_project_ssh_keys": forms.RadioSelect(), "authorised_users": forms.SelectMultiple(attrs={"class": "form-control"}), } def __init__(self, *args, **kwargs): user = kwargs.pop("user", None) super().__init__(*args, **kwargs) self.fields["startup_script"].queryset = self.get_startup_scripts(user) def get_startup_scripts(self, user): # Retrieve startup scripts owned by the user owned_scripts = StartupScript.objects.filter(owner=user) # Retrieve startup scripts authorized for the user authorized_scripts = StartupScript.objects.filter(authorised_users=user) # Combine the owned and authorized scripts startup_scripts = owned_scripts | authorized_scripts return startup_scripts class ImageImportForm(forms.ModelForm): """Custom form for Image model when importing an image""" class Meta: model = Image fields = ( "cloud_credential", #"name", #"family", "startup_script", "enable_os_login", "block_project_ssh_keys", "authorised_users" ) widgets = { "cloud_credential": forms.Select(attrs={"class": "form-control"}), #"name": forms.HiddenInput,#TextInput(attrs={"class": "form-control"}), #"family": forms.HiddenInput,#(attrs={"class": "form-control"}), "startup_script": forms.SelectMultiple(attrs={"class": "form-control"}), "enable_os_login": forms.RadioSelect(), "block_project_ssh_keys": forms.RadioSelect(), "authorised_users": forms.SelectMultiple(attrs={"class": "form-control"}), } help_texts = { #"name": "Enter the existing image name", #"family": "Enter the family of the existing image" } def __init__(self, *args, **kwargs): user = kwargs.pop("user", None) super().__init__(*args, **kwargs) self.fields["startup_script"].queryset = self.get_startup_scripts(user) def get_startup_scripts(self, user): # Retrieve startup scripts owned by the user owned_scripts = StartupScript.objects.filter(owner=user) # Retrieve startup scripts authorized for the user authorized_scripts = StartupScript.objects.filter(authorised_users=user) # Combine the owned and authorized scripts startup_scripts = owned_scripts | authorized_scripts return startup_scripts