class RelationshipForm(BootstrapMixin, forms.ModelForm): slug = SlugField() source_type = forms.ModelChoiceField( queryset=ContentType.objects.filter( FeatureQuery("relationships").get_query()).order_by( "app_label", "model"), help_text="The source object type to which this relationship applies.", ) source_filter = JSONField( required=False, help_text= "Queryset filter matching the applicable source objects of the selected type.<br>" 'Enter in <a href="https://json.org/">JSON</a> format.', ) destination_type = forms.ModelChoiceField( queryset=ContentType.objects.filter( FeatureQuery("relationships").get_query()).order_by( "app_label", "model"), help_text= "The destination object type to which this relationship applies.", ) destination_filter = JSONField( required=False, help_text= "Queryset filter matching the applicable destination objects of the selected type.<br>" 'Enter in <a href="https://json.org/">JSON</a> format.', ) class Meta: model = Relationship fields = [ "name", "slug", "description", "type", "source_type", "source_label", "source_hidden", "source_filter", "destination_type", "destination_label", "destination_hidden", "destination_filter", ] def save(self, commit=True): # TODO add support for owner when a CR is created in the UI obj = super().save(commit) return obj
class SecretForm(BootstrapMixin, CustomFieldModelForm, RelationshipModelForm): """Create/update form for `Secret` objects.""" slug = SlugField() provider = forms.ChoiceField(choices=provider_choices, widget=StaticSelect2()) parameters = JSONField( help_text= 'Enter parameters in <a href="https://json.org/">JSON</a> format.') tags = DynamicModelMultipleChoiceField(queryset=Tag.objects.all(), required=False) class Meta: model = Secret fields = [ "name", "slug", "description", "provider", "parameters", "tags", ]
class ConfigContextForm(BootstrapMixin, forms.ModelForm): regions = DynamicModelMultipleChoiceField(queryset=Region.objects.all(), required=False) sites = DynamicModelMultipleChoiceField(queryset=Site.objects.all(), required=False) roles = DynamicModelMultipleChoiceField(queryset=DeviceRole.objects.all(), required=False) device_types = DynamicModelMultipleChoiceField(queryset=DeviceType.objects.all(), required=False) platforms = DynamicModelMultipleChoiceField(queryset=Platform.objects.all(), required=False) cluster_groups = DynamicModelMultipleChoiceField(queryset=ClusterGroup.objects.all(), required=False) clusters = DynamicModelMultipleChoiceField(queryset=Cluster.objects.all(), required=False) tenant_groups = DynamicModelMultipleChoiceField(queryset=TenantGroup.objects.all(), required=False) tenants = DynamicModelMultipleChoiceField(queryset=Tenant.objects.all(), required=False) tags = DynamicModelMultipleChoiceField(queryset=Tag.objects.all(), required=False) data = JSONField(label="") class Meta: model = ConfigContext fields = ( "name", "weight", "description", "schema", "is_active", "regions", "sites", "roles", "device_types", "platforms", "cluster_groups", "clusters", "tenant_groups", "tenants", "tags", "data", )
class ConfigContextSchemaForm(BootstrapMixin, forms.ModelForm): data_schema = JSONField(label="") slug = SlugField() class Meta: model = ConfigContextSchema fields = ( "name", "slug", "description", "data_schema", )
class LocalContextModelForm(forms.ModelForm): local_context_schema = DynamicModelChoiceField( queryset=ConfigContextSchema.objects.all(), required=False) local_context_data = JSONField(required=False, label="")
class VirtualMachineForm(BootstrapMixin, TenancyForm, CustomFieldModelForm, RelationshipModelForm): cluster_group = DynamicModelChoiceField( queryset=ClusterGroup.objects.all(), required=False, null_option="None", initial_params={"clusters": "$cluster"}, ) cluster = DynamicModelChoiceField( queryset=Cluster.objects.all(), query_params={"group_id": "$cluster_group"}) role = DynamicModelChoiceField( queryset=DeviceRole.objects.all(), required=False, query_params={"vm_role": "True"}, ) platform = DynamicModelChoiceField(queryset=Platform.objects.all(), required=False) local_context_data = JSONField(required=False, label="") tags = DynamicModelMultipleChoiceField(queryset=Tag.objects.all(), required=False) class Meta: model = VirtualMachine fields = [ "name", "status", "cluster_group", "cluster", "role", "tenant_group", "tenant", "platform", "primary_ip4", "primary_ip6", "vcpus", "memory", "disk", "comments", "tags", "local_context_data", ] help_texts = { "local_context_data": "Local config context data overwrites all sources contexts in the final rendered " "config context", } widgets = { "primary_ip4": StaticSelect2(), "primary_ip6": StaticSelect2(), } def __init__(self, *args, **kwargs): super().__init__(*args, **kwargs) if self.instance.present_in_database: # Compile list of choices for primary IPv4 and IPv6 addresses for family in [4, 6]: ip_choices = [(None, "---------")] # Gather PKs of all interfaces belonging to this VM interface_ids = self.instance.interfaces.values_list("pk", flat=True) # Collect interface IPs interface_ips = IPAddress.objects.ip_family(family).filter( assigned_object_type=ContentType.objects.get_for_model( VMInterface), assigned_object_id__in=interface_ids, ) if interface_ips: ip_list = [(ip.id, f"{ip.address} ({ip.assigned_object})") for ip in interface_ips] ip_choices.append(("Interface IPs", ip_list)) # Collect NAT IPs nat_ips = (IPAddress.objects.prefetch_related( "nat_inside").ip_family(family).filter( nat_inside__assigned_object_type=ContentType.objects. get_for_model(VMInterface), nat_inside__assigned_object_id__in=interface_ids, )) if nat_ips: ip_list = [(ip.id, f"{ip.address} (NAT)") for ip in nat_ips] ip_choices.append(("NAT IPs", ip_list)) self.fields["primary_ip{}".format(family)].choices = ip_choices else: # An object that doesn't exist yet can't have any IPs assigned to it self.fields["primary_ip4"].choices = [] self.fields["primary_ip4"].widget.attrs["readonly"] = True self.fields["primary_ip6"].choices = [] self.fields["primary_ip6"].widget.attrs["readonly"] = True
def to_form_field(self, set_initial=True, enforce_required=True, for_csv_import=False, simple_json_filter=False): """ Return a form field suitable for setting a CustomField's value for an object. set_initial: Set initial date for the field. This should be False when generating a field for bulk editing. enforce_required: Honor the value of CustomField.required. Set to False for filtering/bulk editing. for_csv_import: Return a form field suitable for bulk import of objects in CSV format. simple_json_filter: Return a TextInput widget for JSON filtering instead of the default TextArea widget. """ initial = self.default if set_initial else None required = self.required if enforce_required else False # Integer if self.type == CustomFieldTypeChoices.TYPE_INTEGER: field = forms.IntegerField( required=required, initial=initial, min_value=self.validation_minimum, max_value=self.validation_maximum, ) # Boolean elif self.type == CustomFieldTypeChoices.TYPE_BOOLEAN: choices = ( (None, "---------"), (True, "True"), (False, "False"), ) field = forms.NullBooleanField( required=required, initial=initial, widget=StaticSelect2(choices=choices), ) # Date elif self.type == CustomFieldTypeChoices.TYPE_DATE: field = forms.DateField(required=required, initial=initial, widget=DatePicker()) # URL elif self.type == CustomFieldTypeChoices.TYPE_URL: field = LaxURLField(required=required, initial=initial) # Text elif self.type == CustomFieldTypeChoices.TYPE_TEXT: field = forms.CharField(max_length=255, required=required, initial=initial) if self.validation_regex: field.validators = [ RegexValidator( regex=self.validation_regex, message=mark_safe( f"Values must match this regex: <code>{self.validation_regex}</code>" ), ) ] # JSON elif self.type == CustomFieldTypeChoices.TYPE_JSON: if simple_json_filter: field = JSONField(encoder=DjangoJSONEncoder, required=required, initial=None, widget=TextInput) else: field = JSONField(encoder=DjangoJSONEncoder, required=required, initial=initial) # Select or Multi-select else: choices = [(cfc.value, cfc.value) for cfc in self.choices.all()] default_choice = self.choices.filter(value=self.default).first() if not required or default_choice is None: choices = add_blank_choice(choices) # Set the initial value to the first available choice (if any) if set_initial and default_choice: initial = default_choice.value if self.type == CustomFieldTypeChoices.TYPE_SELECT: field_class = CSVChoiceField if for_csv_import else forms.ChoiceField field = field_class( choices=choices, required=required, initial=initial, widget=StaticSelect2(), ) else: field_class = CSVMultipleChoiceField if for_csv_import else forms.MultipleChoiceField field = field_class(choices=choices, required=required, initial=initial, widget=StaticSelect2Multiple()) field.model = self field.label = str(self) if self.description: # Avoid script injection and similar attacks! Output HTML but only accept Markdown as input field.help_text = render_markdown(self.description) return field