class NetworkSerializer(serializers.Serializer): id = serializers.CharField(read_only=True) url = CustomHyperlinkedIdentityField(view_name='network-detail', lookup_field='id', lookup_url_kwarg='pk', parent_url_kwargs=['cloud_pk']) name = serializers.CharField() state = serializers.CharField(read_only=True) cidr_block = serializers.CharField(read_only=True) subnets = CustomHyperlinkedIdentityField(view_name='subnet-list', lookup_field='id', lookup_url_kwarg='network_pk', parent_url_kwargs=['cloud_pk']) def create(self, validated_data): provider = view_helpers.get_cloud_provider(self.context.get('view')) return provider.network.create(name=validated_data.get('name')) def update(self, instance, validated_data): try: if instance.name != validated_data.get('name'): instance.name = validated_data.get('name') return instance except Exception as e: raise serializers.ValidationError("{0}".format(e))
class CloudSerializer(serializers.ModelSerializer): slug = serializers.CharField(read_only=True) compute = CustomHyperlinkedIdentityField(view_name='compute-list', lookup_field='slug', lookup_url_kwarg='cloud_pk') security = CustomHyperlinkedIdentityField(view_name='security-list', lookup_field='slug', lookup_url_kwarg='cloud_pk') block_store = CustomHyperlinkedIdentityField(view_name='block_store-list', lookup_field='slug', lookup_url_kwarg='cloud_pk') object_store = CustomHyperlinkedIdentityField( view_name='object_store-list', lookup_field='slug', lookup_url_kwarg='cloud_pk') networks = CustomHyperlinkedIdentityField(view_name='network-list', lookup_field='slug', lookup_url_kwarg='cloud_pk') region_name = serializers.SerializerMethodField() def get_region_name(self, obj): if hasattr(obj, 'aws'): return obj.aws.compute.ec2_region_name elif hasattr(obj, 'openstack'): return obj.openstack.region_name else: return "Cloud provider not recognized" class Meta: model = models.Cloud exclude = ('kind', )
class CredentialsSerializer(serializers.Serializer): aws = CustomHyperlinkedIdentityField(view_name='awscredentials-list') openstack = CustomHyperlinkedIdentityField( view_name='openstackcredentials-list') azure = CustomHyperlinkedIdentityField( view_name='azurecredentials-list') gce = CustomHyperlinkedIdentityField( view_name='gcecredentials-list')
class ComputeSerializer(serializers.Serializer): machine_images = CustomHyperlinkedIdentityField( view_name='machine_image-list', parent_url_kwargs=['cloud_pk']) instance_types = CustomHyperlinkedIdentityField( view_name='instance_type-list', parent_url_kwargs=['cloud_pk']) instances = CustomHyperlinkedIdentityField(view_name='instance-list', parent_url_kwargs=['cloud_pk']) regions = CustomHyperlinkedIdentityField(view_name='region-list', parent_url_kwargs=['cloud_pk'])
class RegionSerializer(serializers.Serializer): id = serializers.CharField(read_only=True) url = CustomHyperlinkedIdentityField(view_name='region-detail', lookup_field='id', lookup_url_kwarg='pk', parent_url_kwargs=['cloud_pk']) name = serializers.CharField() zones = CustomHyperlinkedIdentityField(view_name='zone-list', lookup_field='id', lookup_url_kwarg='region_pk', parent_url_kwargs=['cloud_pk'])
class SecurityGroupRuleSerializer(serializers.Serializer): ip_protocol = serializers.CharField(label="IP protocol", allow_blank=True) from_port = serializers.CharField(allow_blank=True) to_port = serializers.CharField(allow_blank=True) cidr_ip = serializers.CharField(label="CIDR IP", allow_blank=True) group = ProviderPKRelatedField(label="Source group", queryset='security.security_groups', display_fields=['name', 'id'], display_format="{0} (ID: {1})", required=False, allow_null=True) url = CustomHyperlinkedIdentityField( view_name='security_group_rule-detail', lookup_field='id', lookup_url_kwarg='pk', parent_url_kwargs=['cloud_pk', 'security_group_pk']) def create(self, validated_data): view = self.context.get('view') provider = view_helpers.get_cloud_provider(view) sg_pk = view.kwargs.get('security_group_pk') if sg_pk: sg = provider.security.security_groups.get(sg_pk) if sg and validated_data.get('group'): return sg.add_rule(src_group=validated_data.get('group')) elif sg: return sg.add_rule(validated_data.get('ip_protocol'), validated_data.get('from_port'), validated_data.get('to_port'), validated_data.get('cidr_ip')) return None
class UserSerializer(UserDetailsSerializer): credentials = CustomHyperlinkedIdentityField( view_name='credentialsroute-list', lookup_field=None) aws_creds = serializers.SerializerMethodField() openstack_creds = serializers.SerializerMethodField() def get_aws_creds(self, obj): """ Include a URL for listing this bucket's contents """ try: creds = obj.userprofile.credentials.filter( awscredentials__isnull=False).select_subclasses() return AWSCredsSerializer(instance=creds, many=True, context=self.context).data except models.UserProfile.DoesNotExist: return "" def get_openstack_creds(self, obj): """ Include a URL for listing this bucket's contents """ try: creds = obj.userprofile.credentials.filter( openstackcredentials__isnull=False).select_subclasses() return OpenstackCredsSerializer(instance=creds, many=True, context=self.context).data except models.UserProfile.DoesNotExist: return "" class Meta(UserDetailsSerializer.Meta): fields = UserDetailsSerializer.Meta.fields + \ ('aws_creds', 'openstack_creds', 'credentials')
class MachineImageSerializer(serializers.Serializer): id = serializers.CharField(read_only=True) url = CustomHyperlinkedIdentityField(view_name='machine_image-detail', lookup_field='id', lookup_url_kwarg='pk', parent_url_kwargs=['cloud_pk']) name = serializers.CharField() description = serializers.CharField()
class BucketSerializer(serializers.Serializer): id = serializers.CharField(read_only=True) url = CustomHyperlinkedIdentityField(view_name='bucket-detail', lookup_field='id', lookup_url_kwarg='pk', parent_url_kwargs=['cloud_pk']) name = serializers.CharField() objects = CustomHyperlinkedIdentityField(view_name='bucketobject-list', lookup_field='id', lookup_url_kwarg='bucket_pk', parent_url_kwargs=['cloud_pk']) def create(self, validated_data): provider = view_helpers.get_cloud_provider(self.context.get('view')) try: return provider.object_store.create(validated_data.get('name')) except Exception as e: raise serializers.ValidationError("{0}".format(e))
class SecurityGroupSerializer(serializers.Serializer): id = serializers.CharField(read_only=True) url = CustomHyperlinkedIdentityField(view_name='security_group-detail', lookup_field='id', lookup_url_kwarg='pk', parent_url_kwargs=['cloud_pk']) name = serializers.CharField() # Technically, the description is required but when wanting to reuse an # existing security group with a different resource (eg, creating an # instance), we need to be able to call this serializer w/o it. description = serializers.CharField(required=False) rules = CustomHyperlinkedIdentityField(view_name='security_group_rule-list', lookup_field='id', lookup_url_kwarg='security_group_pk', parent_url_kwargs=['cloud_pk']) def create(self, validated_data): provider = view_helpers.get_cloud_provider(self.context.get('view')) return provider.security.security_groups.create( validated_data.get('name'), validated_data.get('description'))
class KeyPairSerializer(serializers.Serializer): id = serializers.CharField(read_only=True) url = CustomHyperlinkedIdentityField(view_name='keypair-detail', lookup_field='id', lookup_url_kwarg='pk', parent_url_kwargs=['cloud_pk']) name = serializers.CharField() material = serializers.CharField(read_only=True) def create(self, validated_data): provider = view_helpers.get_cloud_provider(self.context.get('view')) return provider.security.key_pairs.create(validated_data.get('name'))
class AttachmentInfoSerializer(serializers.Serializer): device = serializers.CharField(read_only=True) instance_id = ProviderPKRelatedField(label="Instance ID", queryset='compute.instances', display_fields=['name', 'id'], display_format="{0} (ID: {1})", required=False, allow_null=True) instance = CustomHyperlinkedIdentityField(view_name='instance-detail', lookup_field='instance_id', lookup_url_kwarg='pk', parent_url_kwargs=['cloud_pk'])
class InstanceTypeSerializer(serializers.Serializer): id = serializers.CharField(read_only=True) url = CustomHyperlinkedIdentityField(view_name='instance_type-detail', lookup_field='pk', parent_url_kwargs=['cloud_pk']) name = serializers.CharField() family = serializers.CharField() vcpus = serializers.CharField() ram = serializers.CharField() size_root_disk = serializers.CharField() size_ephemeral_disks = serializers.CharField() num_ephemeral_disks = serializers.CharField() size_total_disk = serializers.CharField() extra_data = serializers.DictField(serializers.CharField())
class VolumeSerializer(serializers.Serializer): id = serializers.CharField(read_only=True) url = CustomHyperlinkedIdentityField(view_name='volume-detail', lookup_field='id', lookup_url_kwarg='pk', parent_url_kwargs=['cloud_pk']) name = serializers.CharField() description = serializers.CharField(allow_blank=True) size = serializers.IntegerField(min_value=0) create_time = serializers.CharField(read_only=True) zone_id = PlacementZonePKRelatedField(label="Zone", queryset='non_empty_value', display_fields=[ 'id'], display_format="{0}", required=True) state = serializers.CharField(read_only=True) snapshot_id = ProviderPKRelatedField(label="Snapshot ID", queryset='block_store.snapshots', display_fields=[ 'name', 'id', 'size'], display_format="{0} (ID: {1}," " Size: {2} GB)", write_only=True, required=False, allow_null=True) attachments = AttachmentInfoSerializer() def create(self, validated_data): provider = view_helpers.get_cloud_provider(self.context.get('view')) try: return provider.block_store.volumes.create( validated_data.get('name'), validated_data.get('size'), validated_data.get('zone_id'), description=validated_data.get('description'), snapshot=validated_data.get('snapshot_id')) except Exception as e: raise serializers.ValidationError("{0}".format(e)) def update(self, instance, validated_data): try: if instance.name != validated_data.get('name'): instance.name = validated_data.get('name') if instance.description != validated_data.get('description'): instance.description = validated_data.get('description') return instance except Exception as e: raise serializers.ValidationError("{0}".format(e))
class SubnetSerializer(serializers.Serializer): id = serializers.CharField(read_only=True) url = CustomHyperlinkedIdentityField(view_name='subnet-detail', lookup_field='id', lookup_url_kwarg='pk', parent_url_kwargs=['cloud_pk', 'network_pk']) name = serializers.CharField(allow_blank=True) cidr_block = serializers.CharField() network_id = serializers.CharField(read_only=True) def create(self, validated_data): provider = view_helpers.get_cloud_provider(self.context.get('view')) net_id = self.context.get('view').kwargs.get('network_pk') return provider.network.subnets.create( net_id, validated_data.get('cidr_block'), name=validated_data.get('name'))
class BucketObjectSerializer(serializers.Serializer): id = serializers.CharField(read_only=True) name = serializers.CharField(allow_blank=True) size = serializers.IntegerField(read_only=True) last_modified = serializers.CharField(read_only=True) url = CustomHyperlinkedIdentityField( view_name='bucketobject-detail', lookup_field='id', lookup_url_kwarg='pk', parent_url_kwargs=['cloud_pk', 'bucket_pk']) download_url = serializers.SerializerMethodField() upload_content = serializers.FileField(write_only=True) def get_download_url(self, obj): """Create a URL for accessing a single instance.""" kwargs = self.context['view'].kwargs.copy() kwargs.update({'pk': obj.id}) obj_url = reverse('bucketobject-detail', kwargs=kwargs, request=self.context['request']) return urllib.parse.urljoin(obj_url, '?format=binary') def create(self, validated_data): provider = view_helpers.get_cloud_provider(self.context.get('view')) bucket_id = self.context.get('view').kwargs.get('bucket_pk') bucket = provider.object_store.get(bucket_id) try: name = validated_data.get('name') content = validated_data.get('upload_content') if name: object = bucket.create_object(name) else: object = bucket.create_object(content.name) if content: object.upload(content.file.getvalue()) return object except Exception as e: raise serializers.ValidationError("{0}".format(e)) def update(self, instance, validated_data): try: instance.upload( validated_data.get('upload_content').file.getvalue()) return instance except Exception as e: raise serializers.ValidationError("{0}".format(e))
class SnapshotSerializer(serializers.Serializer): id = serializers.CharField(read_only=True) url = CustomHyperlinkedIdentityField(view_name='snapshot-detail', lookup_field='id', lookup_url_kwarg='pk', parent_url_kwargs=['cloud_pk']) name = serializers.CharField() description = serializers.CharField() state = serializers.CharField(read_only=True) volume_id = ProviderPKRelatedField(label="Volume ID", queryset='block_store.volumes', display_fields=[ 'name', 'id', 'size'], display_format="{0} (ID: {1}," " Size: {2} GB)", required=True) create_time = serializers.CharField(read_only=True) size = serializers.IntegerField(min_value=0, read_only=True) def create(self, validated_data): provider = view_helpers.get_cloud_provider(self.context.get('view')) try: return provider.block_store.snapshots.create( validated_data.get('name'), validated_data.get('volume_id'), description=validated_data.get('description')) except Exception as e: raise serializers.ValidationError("{0}".format(e)) def update(self, instance, validated_data): try: if instance.name != validated_data.get('name'): instance.name = validated_data.get('name') if instance.description != validated_data.get('description'): instance.description = validated_data.get('description') return instance except Exception as e: raise serializers.ValidationError("{0}".format(e))
class BlockStoreSerializer(serializers.Serializer): volumes = CustomHyperlinkedIdentityField(view_name='volume-list', parent_url_kwargs=['cloud_pk']) snapshots = CustomHyperlinkedIdentityField(view_name='snapshot-list', parent_url_kwargs=['cloud_pk'])
class SecuritySerializer(serializers.Serializer): keypairs = CustomHyperlinkedIdentityField(view_name='keypair-list', parent_url_kwargs=['cloud_pk']) security_groups = CustomHyperlinkedIdentityField( view_name='security_group-list', parent_url_kwargs=['cloud_pk'])
class CloudSerializer(serializers.ModelSerializer): slug = serializers.CharField(read_only=True) compute = CustomHyperlinkedIdentityField(view_name='compute-list', lookup_field='slug', lookup_url_kwarg='cloud_pk') security = CustomHyperlinkedIdentityField(view_name='security-list', lookup_field='slug', lookup_url_kwarg='cloud_pk') block_store = CustomHyperlinkedIdentityField(view_name='block_store-list', lookup_field='slug', lookup_url_kwarg='cloud_pk') object_store = CustomHyperlinkedIdentityField( view_name='object_store-list', lookup_field='slug', lookup_url_kwarg='cloud_pk') networks = CustomHyperlinkedIdentityField(view_name='network-list', lookup_field='slug', lookup_url_kwarg='cloud_pk') static_ips = CustomHyperlinkedIdentityField(view_name='static_ip-list', lookup_field='slug', lookup_url_kwarg='cloud_pk') cloudman = CustomHyperlinkedIdentityField(view_name='cloudman-list', lookup_field='slug', lookup_url_kwarg='cloud_pk') region_name = serializers.SerializerMethodField() cloud_type = serializers.SerializerMethodField() extra_data = serializers.SerializerMethodField() def get_region_name(self, obj): if hasattr(obj, 'aws'): return obj.aws.compute.ec2_region_name elif hasattr(obj, 'openstack'): return obj.openstack.region_name elif hasattr(obj, 'azure'): return obj.azure.region_name else: return "Cloud provider not recognized" def get_cloud_type(self, obj): if hasattr(obj, 'aws'): return 'aws' elif hasattr(obj, 'openstack'): return 'openstack' elif hasattr(obj, 'azure'): return 'azure' else: return 'unknown' def get_extra_data(self, obj): if hasattr(obj, 'aws'): aws = obj.aws extra_data = {} if aws.compute: compute = aws.compute extra_data['ec2_region_name'] = compute.ec2_region_name extra_data['ec2_region_endpoint'] = compute.ec2_region_endpoint extra_data['ec2_conn_path'] = compute.ec2_conn_path extra_data['ec2_port'] = compute.ec2_port extra_data['ec2_is_secure'] = compute.ec2_is_secure if aws.object_store: s3 = aws.object_store extra_data['s3_host'] = s3.s3_host extra_data['s3_conn_path'] = s3.s3_conn_path extra_data['s3_port'] = s3.s3_port extra_data['s3_is_secure'] = s3.s3_is_secure return extra_data elif hasattr(obj, 'openstack'): os = obj.openstack return { 'auth_url': os.auth_url, 'region_name': os.region_name, 'identity_api_version': os.identity_api_version } elif hasattr(obj, 'azure'): azure = obj.azure return { 'region_name': azure.region_name, 'resource_group': azure.resource_group, 'storage_account': azure.storage_account, 'azure_vm_default_user_name': azure.azure_vm_default_user_name } else: return {} class Meta: model = models.Cloud exclude = ('kind', )
class DeploymentSerializer(serializers.ModelSerializer): owner = serializers.CharField(read_only=True) name = serializers.CharField(required=True) provider_settings = serializers.CharField(read_only=True) application_config = StoredJSONField(read_only=True) application = serializers.CharField(write_only=True, required=True) config_app = serializers.JSONField(write_only=True, required=False) app_version_details = DeploymentAppVersionSerializer(source="application_version", read_only=True) latest_task = serializers.SerializerMethodField() tasks = CustomHyperlinkedIdentityField(view_name='deployment_task-list', lookup_field='id', lookup_url_kwarg='deployment_pk') class Meta: model = models.ApplicationDeployment fields = ('id','name', 'application', 'application_version', 'target_cloud', 'provider_settings', 'application_config', 'added', 'updated', 'owner', 'config_app', 'app_version_details', 'tasks', 'latest_task') def get_latest_task(self, obj): """Provide task info about the most recenly updated deployment task.""" try: task = models.ApplicationDeploymentTask.objects.filter( deployment=obj.id).latest('updated') return DeploymentTaskSerializer( task, context={'request': self.context['request'], 'deployment_pk': obj.id}).data except models.ApplicationDeploymentTask.DoesNotExist: return None def to_internal_value(self, data): application = data.get('application') version = data.get('application_version') if version: version = models.ApplicationVersion.objects.get(application=application, version=version) data['application_version'] = version.id return super(DeploymentSerializer, self).to_internal_value(data) def create(self, validated_data): """ Create a new ApplicationDeployment object. Called automatically by the DRF following a POST request. """ name = validated_data.get("name") cloud = validated_data.get("target_cloud") version = validated_data.get("application_version") cloud_version_config = models.ApplicationVersionCloudConfig.objects.get( application_version=version.id, cloud=cloud.slug) default_combined_config = cloud_version_config.compute_merged_config() request = self.context.get('view').request provider = view_helpers.get_cloud_provider( self.context.get('view'), cloud_id=cloud.slug) credentials = view_helpers.get_credentials(cloud, request) try: handler = util.import_class(version.backend_component_name)() app_config = validated_data.get("config_app", {}) merged_config = jsonmerge.merge(default_combined_config, app_config) cloud_config = util.serialize_cloud_config(cloud_version_config) final_ud_config = handler.process_app_config( provider, name, cloud_config, merged_config) sanitised_app_config = handler.sanitise_app_config(merged_config) async_result = tasks.launch_appliance.delay( name, cloud_version_config, credentials, merged_config, final_ud_config) del validated_data['application'] del validated_data['config_app'] validated_data['owner_id'] = request.user.id validated_data['application_config'] = json.dumps(merged_config) app_deployment = super(DeploymentSerializer, self).create(validated_data) self.log_usage(cloud_version_config, app_deployment, sanitised_app_config, request.user) models.ApplicationDeploymentTask.objects.create( action=models.ApplicationDeploymentTask.LAUNCH, deployment=app_deployment, celery_id=async_result.task_id) return app_deployment except serializers.ValidationError as ve: raise ve except Exception as e: raise serializers.ValidationError({ "error" : str(e) }) def log_usage(self, app_version_cloud_config, app_deployment, sanitised_app_config, user): u = models.Usage(app_version_cloud_config=app_version_cloud_config, app_deployment=app_deployment, app_config=sanitised_app_config, user=user) u.save()
class ObjectStoreSerializer(serializers.Serializer): buckets = CustomHyperlinkedIdentityField(view_name='bucket-list', parent_url_kwargs=['cloud_pk'])
class InstanceSerializer(serializers.Serializer): id = serializers.CharField(read_only=True) url = CustomHyperlinkedIdentityField(view_name='instance-detail', lookup_field='id', lookup_url_kwarg='pk', parent_url_kwargs=['cloud_pk']) name = serializers.CharField() public_ips = serializers.ListField(serializers.IPAddressField()) private_ips = serializers.ListField(serializers.IPAddressField()) instance_type_id = ProviderPKRelatedField( label="Instance Type", queryset='compute.instance_types', display_fields=['name'], display_format="{0}", required=True) instance_type_url = CustomHyperlinkedIdentityField( view_name='instance_type-detail', lookup_field='instance_type_id', lookup_url_kwarg='pk', parent_url_kwargs=['cloud_pk']) image_id = ProviderPKRelatedField(label="Image", queryset='compute.images', display_fields=['name', 'id'], display_format="{0} ({1})", required=True) image_id_url = CustomHyperlinkedIdentityField( view_name='machine_image-detail', lookup_field='image_id', lookup_url_kwarg='pk', parent_url_kwargs=['cloud_pk']) key_pair_name = ProviderPKRelatedField(label="Keypair Name", queryset='security.key_pairs', display_fields=['id'], display_format="{0}", required=True) zone_id = PlacementZonePKRelatedField(label="Placement Zone", queryset='non_empty_value', display_fields=['id'], display_format="{0}", required=True) security_group_ids = ProviderPKRelatedField( label="Security Groups", queryset='security.security_groups', display_fields=['name'], display_format="{0}", many=True) user_data = serializers.CharField(write_only=True, style={'base_template': 'textarea.html'}) def create(self, validated_data): provider = view_helpers.get_cloud_provider(self.context.get('view')) name = validated_data.get('name') image_id = validated_data.get('image_id') instance_type = validated_data.get('instance_type_id') kp_name = validated_data.get('key_pair_name') zone_id = validated_data.get('zone_id') security_group_ids = validated_data.get('security_group_ids') user_data = validated_data.get('user_data') try: return provider.compute.instances.create( name, image_id, instance_type, zone=zone_id, key_pair=kp_name, security_groups=security_group_ids, user_data=user_data) except Exception as e: raise serializers.ValidationError("{0}".format(e)) def update(self, instance, validated_data): try: if instance.name != validated_data.get('name'): instance.name = validated_data.get('name') return instance except Exception as e: raise serializers.ValidationError("{0}".format(e))
class DeploymentTaskSerializer(serializers.ModelSerializer): url = CustomHyperlinkedIdentityField(view_name='deployment_task-detail', lookup_field='id', lookup_url_kwarg='pk', parent_url_kwargs=['deployment_pk', 'deployment_task_pk']) deployment = serializers.CharField(read_only=True) celery_id = serializers.CharField(read_only=True) action = serializers.ChoiceField( choices=models.ApplicationDeploymentTask.ACTION_CHOICES, initial=models.ApplicationDeploymentTask.HEALTH_CHECK) status = serializers.CharField(read_only=True) result = serializers.CharField(read_only=True) traceback = serializers.CharField(read_only=True) class Meta: model = models.ApplicationDeploymentTask exclude = ('deployment', '_result', '_status') def create(self, validated_data): """ Fire off a new task for the supplied action. Called automatically by the DRF following a POST request. :type validated_data: ``dict`` :param validated_data: Dict containing action the task should perform. Valid actions are `HEALTH_CHECK`, `DELETE`. """ print("deployment task data: %s" % validated_data) action = getattr(models.ApplicationDeploymentTask, validated_data.get( 'action', models.ApplicationDeploymentTask.HEALTH_CHECK)) request = self.context.get('view').request dpk = self.context['view'].kwargs.get('deployment_pk') dpl = models.ApplicationDeployment.objects.get(id=dpk) credentials = view_helpers.get_credentials(dpl.target_cloud, request) try: if action == models.ApplicationDeploymentTask.HEALTH_CHECK: async_result = tasks.health_check.delay(dpl, credentials) elif action == models.ApplicationDeploymentTask.RESTART: async_result = tasks.manage_appliance.delay('restart', dpl, credentials) elif action == models.ApplicationDeploymentTask.DELETE: async_result = tasks.manage_appliance.delay('delete', dpl, credentials) return models.ApplicationDeploymentTask.objects.create( action=action, deployment=dpl, celery_id=async_result.task_id) except serializers.ValidationError as ve: raise ve except Exception as e: raise serializers.ValidationError({"error": str(e)}) def validate_action(self, value): """Make sure only one LAUNCH task exists per deployment.""" if value == models.ApplicationDeploymentTask.LAUNCH: dpk = self.context['view'].kwargs.get('deployment_pk') dpl = models.ApplicationDeployment.objects.get(id=dpk) if models.ApplicationDeploymentTask.objects.filter( deployment=dpl, action=models.ApplicationDeploymentTask.LAUNCH): raise serializers.ValidationError( "Duplicate LAUNCH action for deployment %s" % dpl.name) return value.upper()