def get_s3_connection():

    if settings.S3_ACCESS_KEY and settings.S3_SECRET_KEY and settings.S3_HOST:
        log.debug('Connecting to {}, with secure connection is {}'.
                  format(settings.S3_HOST, settings.S3_SECURE_CONNECTION))
        return boto.connect_s3(
            aws_access_key_id=settings.S3_ACCESS_KEY,
            aws_secret_access_key=settings.S3_SECRET_KEY,
            host=settings.S3_HOST,
            is_secure=settings.S3_SECURE_CONNECTION,
            calling_format=boto.s3.connection.OrdinaryCallingFormat())
    return None def get_or_create_bucket(s3_connection):
    bucket = s3_connection.get_bucket(settings.S3_BUCKET_NAME)
    if bucket is None:
        bucket = s3_connection.create_bucket(settings.S3_BUCKET_NAME)
    return bucket class Project(models.Model):
    name = models.CharField(_('Name'), max_length=128, unique=True)

    def __str__(self):
        return 'Project: {0}'.format(self.name) class Settings(models.Model):
    project = models.ForeignKey(Project, related_name='settings')
    key = models.CharField(max_length=255)
    value = models.TextField()

    def __str__(self):
        return "{}={}".format(self.key, self.value) @celery.task(time_limit=43200, bind=True)
def launch_process(self, cmd, task_type=None, env={}):
    pid = None

    def sigterm_handler(signum, frame):
        log.debug('Get "{}", starting handler'.format(signum))
        if pid is None:
            log.warn("Pid is None, nothing to kill...")
            return
        kill_proc_tree(pid)

    signal.signal(signal.SIGTERM, sigterm_handler)

    start = datetime.datetime.now()
    cmd = cmd.replace('\n', ';').replace('\r', '')
    result = {
        'cmd': cmd,
        'env': env,
        'stdout': None,
        'stderr': None,
        'return_code': 0,
    }
    cwd = '/tmp/'
    if 'WORKSPACE' in env:
        cwd = env['WORKSPACE']
    try:
        cmd = subprocess.Popen(['bash', '-c', cmd], stdout=subprocess.PIPE,
                               stderr=subprocess.PIPE, env=env, cwd=cwd,
                               universal_newlines=False)
        pid = cmd.pid
        result['stdout'], result['stderr'] = cmd.communicate()
        result['return_code'] = cmd.returncode
        # If INIT_SCRIPT task returns non-zero code we finalize launch
        # and raise Ignore exception to force the worker to ignore
        # current task and all tasks in its callback
        # http://docs.celeryproject.org/en/3.1/userguide/tasks.html#ignore
        if result['return_code'] != 0 and task_type == INIT_SCRIPT:
            self.update_state(state=states.FAILURE, meta=result)
            finalize_launch(launch_id=env['LAUNCH_ID'])
            raise Ignore()
    except subprocess.CalledProcessError as e:
        result['stdout'] = e.output
        result['return_code'] = e.returncode
    except OSError as e:
        result['stderr'] = e.strerror
        result['return_code'] = 127
    except SoftTimeLimitExceeded as e:
        result['stderr'] = 'Soft timeout limit exceeded. {}'.format(e)
        result['return_code'] = 1
    end = datetime.datetime.now()
    result['start'] = start.isoformat()
    result['end'] = end.isoformat()
    result['delta'] = (end - start).total_seconds()

    return result @celery.task()
def create_environment(environment_vars, json_file):
    workspace_path = environment_vars['WORKSPACE']
    # Create workspace directory
    if not os.path.exists(workspace_path):
        os.makedirs(workspace_path)
    os.chmod(workspace_path,
             stat.S_IRWXU | stat.S_IRWXG | stat.S_IROTH | stat.S_IXOTH)

    # Write json file
    json_file_path = os.path.join(workspace_path, 'file.json')
    with open(json_file_path, 'w+') as f:
        f.write(json.dumps(json_file))

    # Write environment file
    env_file_path = os.path.join(workspace_path, 'environments.sh')
    output = ''
    for key, value in iter(environment_vars.items()):
        output += 'export {key}="{value}"\n'.format(key=key, value=value)
    with open(env_file_path, 'w+') as f:
        f.write(output) class ProjectSerializer(serializers.ModelSerializer):
    settings = SettingsSerializer(many=True, read_only=True)

    class Meta:
        model = Project
        fields = ('id', 'name', 'settings') class LaunchSerializer(serializers.ModelSerializer):
    counts = serializers.ReadOnlyField()
    tasks = TasksResultField(source='get_tasks', read_only=True)
    parameters = serializers.ReadOnlyField(source='get_parameters')
    build = BuildSerializer(read_only=True)

    class Meta:
        model = Launch
        fields = ('id', 'test_plan', 'created', 'counts', 'tasks',
                  'state', 'started_by', 'created', 'finished', 'parameters',
                  'duration', 'build') class LaunchItemSerializer(serializers.ModelSerializer):
    class Meta:
        model = LaunchItem class TestResultSerializer(serializers.ModelSerializer):
    class Meta:
        model = TestResult class TestPlanSerializer(serializers.ModelSerializer):
    class Meta:
        model = TestPlan class AsyncResultSerializer(serializers.Serializer):
    id = serializers.CharField()
    result = TaskResultField()
    status = serializers.CharField()

    def update(self, instance, validated_data):
        log.info("Update: {}".format(validated_data))

    def create(self, validated_data):
        log.info("Create: {}".format(validated_data)) class CommentSerializer(serializers.ModelSerializer):
    user_data = AccountSerializer(source='user', read_only=True)

    class Meta:
        model = Comment
        fields = ('id', 'comment', 'submit_date', 'content_type', 'object_pk',
                  'user', 'user_data') class BugSerializer(serializers.ModelSerializer):
    status = serializers.ReadOnlyField(source='get_state')

    class Meta:
        model = Bug
        fields = ('id', 'externalId', 'name', 'status', 'regexp', 'updated') class StageSerializer(serializers.ModelSerializer):
    class Meta:
        model = Stage
        fields = ('id', 'name', 'text', 'state',
                  'link', 'project', 'updated', 'weight') class MetricSerializer(serializers.ModelSerializer):
    schedule = serializers.ReadOnlyField(source='get_schedule_as_cron')

    class Meta:
        model = Metric class MetricValueSerializer(serializers.ModelSerializer):
    class Meta:
        model = MetricValue class TestPlan(models.Model):
    name = models.CharField(_('Name'), max_length=256)
    project = models.ForeignKey(Project)
    main = models.BooleanField(_('Show in short statistic'),
                               blank=True, null=False, default=False)
    hidden = models.BooleanField(blank=False, null=False, default=True)
    owner = models.ForeignKey(User, default=1)
    filter = models.TextField(_('Started by filter'), default='',
                              blank=True, null=False, max_length=128)
    description = models.TextField(_('Description'), default='',
                                   blank=True, null=False)
    variable_name = models.TextField(_('Environment variable name'),
                                     default='', blank=True,
                                     null=False, max_length=128)
    variable_value_regexp = models.CharField(_('Regexp for variable value'),
                                             max_length=255, default='',
                                             blank=True)
    show_in_summary = models.BooleanField(
        _('Consider in summary calculation'),
        blank=True, null=False, default=False)

    show_in_twodays = models.BooleanField(
        _('Consider in statistic for last two days'),
        blank=True, null=False, default=False)

    def __str__(self):
        return '{0} -> TestPlan: {1}'.format(self.project, self.name) class Launch(models.Model):
    test_plan = models.ForeignKey(TestPlan)
    counts_cache = models.TextField(blank=True, null=True, default=None)
    started_by = models.URLField(_('Started by'), blank=True, null=True,
                                 default=None)
    created = models.DateTimeField(_('Created'), auto_now_add=True)
    finished = models.DateTimeField(_('Finished'), default=None, blank=True,
                                    null=True)
    state = models.IntegerField(_('State'), default=FINISHED)
    tasks = models.TextField(_('Tasks'), default='')
    parameters = models.TextField(_('Parameters'), default='{}')
    duration = models.FloatField(_('Duration time'), null=True, default=None)

    def is_finished(self):
        return self.state == FINISHED

    @property
    def counts(self):
        if self.counts_cache is None or self.state == INITIALIZED:
            self.calculate_counts()
        return json.loads(self.counts_cache)

    def calculate_counts(self):
        data = {
            'passed': len(self.passed),
            'failed': len(self.failed),
            'skipped': len(self.skipped),
            'blocked': len(self.blocked),
            'total': 0
        }
        for name, count in data.items():
            if name != 'total':
                data['total'] += count
        self.counts_cache = json.dumps(data)
        self.save()

    @property
    def failed(self):
        return self.testresult_set.filter(state=FAILED)

    @property
    def skipped(self):
        return self.testresult_set.filter(state=SKIPPED)

    @property
    def passed(self):
        return self.testresult_set.filter(state=PASSED)

    @property
    def blocked(self):
        return self.testresult_set.filter(state=BLOCKED)

    def get_tasks(self):
        if self.tasks == '' or self.tasks is None:
            self.tasks = '{}'
        return json.loads(self.tasks)

    def set_tasks(self, tasks):
        self.tasks = json.dumps(tasks)

    def get_parameters(self):
        if self.parameters == '' or self.parameters is None:
            self.parameters = '{}'
        return json.loads(self.parameters)

    def set_parameters(self, parameters):
        self.parameters = json.dumps(parameters)

    def __str__(self):
        return '{0} -> Launch: {1}'.format(self.test_plan, self.pk) class Build(models.Model):
    launch = models.OneToOneField(Launch, related_name='build')
    version = models.CharField(
        max_length=16, default=None, null=True, blank=True)
    hash = models.CharField(
        max_length=64, default=None, null=True, blank=True)
    branch = models.CharField(
        max_length=128, default=None, null=True, blank=True)
    commit_author = models.CharField(
        max_length=16, default=None, null=True, blank=True)
    commit_message = models.CharField(
        max_length=128, default=None, null=True, blank=True)
    last_commits = models.TextField(default=None, null=True, blank=True)

    def get_last_commits(self):
        if self.last_commits == '""' or self.last_commits is None:
            self.last_commits = '[]'
        return json.loads(self.last_commits)

    def set_last_commits(self, last_commits):
        self.last_commits = json.dumps(last_commits)

    def __str__(self):
        return '{0} -> LaunchBuild: {1}/{2}/{3}'.format(
            self.launch, self.version, self.hash, self. branch) class TestResult(models.Model):
    launch = models.ForeignKey(Launch)
    name = models.CharField(_('Name'), max_length=128, db_index=True)
    suite = models.CharField(_('TestSuite'), max_length=256)
    state = models.IntegerField(_('State'), default=BLOCKED)
    failure_reason = models.TextField(_('Failure Reason'), default=None,
                                      blank=True, null=True)
    duration = models.FloatField(_('Duration time'), default=0.0)
    launch_item_id = models.IntegerField(blank=True, default=None, null=True)

    def __str__(self):
        return '{0} -> TestResult: {1}/{2}'.format(
            self.launch, self.suite, self.name) class LaunchItem(models.Model):
    test_plan = models.ForeignKey(TestPlan)
    name = models.CharField(
        max_length=128, default=None, null=True, blank=True)
    command = models.TextField()
    timeout = models.IntegerField(default=300)
    type = models.IntegerField(default=ASYNC_CALL)

    def __str__(self):
        return '{0} -> {1}'.format(self.test_plan.name, self.name) class Bug(models.Model):
    externalId = models.CharField(max_length=255, blank=False)
    name = models.CharField(max_length=255, default='', blank=True)
    regexp = models.CharField(max_length=255, default='', blank=False)
    state = models.CharField(max_length=32, default='', blank=True)
    updated = models.DateTimeField(auto_now=True)

    def get_state(self):
        return self.state

    def __str__(self):
        return ':'.join((self.externalId, self.name)) TEST_STATES = (PASSED, FAILED, SKIPPED, BLOCKED) = (0, 1, 2, 3)
LAUNCH_STATES = (INITIALIZED, IN_PROGRESS, FINISHED, STOPPED) = (0, 1, 2, 3)
LAUNCH_TYPES = (ASYNC_CALL, INIT_SCRIPT, CONCLUSIVE) = (0, 1, 2)
CELERY_FINISHED_STATES = (states.SUCCESS, states.FAILURE)
RESULT_PREVIEW_CHOICES = (
    ('head', 'Show test result head'),
    ('tail', 'Show test result tail')
)
class ExtUser(models.Model):
class TestPlan(models.Model):
class Launch(models.Model):
class Build(models.Model):
class TestResult(models.Model):
class LaunchItem(models.Model):
class Bug(models.Model):
    def get_dashboards(self):
    def set_dashboards(self, dashboards):
    def __str__(self):
    def is_finished(self):
    def counts(self):
    def calculate_counts(self):
    def failed(self):
    def skipped(self):
    def passed(self):
    def blocked(self):
    def get_tasks(self):
    def set_tasks(self, tasks):
    def get_parameters(self):
    def set_parameters(self, parameters):
    def __str__(self):
    def get_last_commits(self):
    def set_last_commits(self, last_commits):
    def __str__(self):
    def __str__(self):
    def __str__(self):
    def get_state(self):
    def __str__(self):
def get_issue_fields_from_bts(externalId):
def _get_bug(bug_id): TEST_STATES = (PASSED, FAILED, SKIPPED, BLOCKED) = (0, 1, 2, 3)
LAUNCH_STATES = (INITIALIZED, IN_PROGRESS, FINISHED, STOPPED) = (0, 1, 2, 3)
LAUNCH_TYPES = (ASYNC_CALL, INIT_SCRIPT, CONCLUSIVE) = (0, 1, 2)
CELERY_FINISHED_STATES = (states.SUCCESS, states.FAILURE)
RESULT_PREVIEW_CHOICES = (
    ('head', 'Show test result head'),
    ('tail', 'Show test result tail')
)
class ExtUser(models.Model):
class TestPlan(models.Model):
class Launch(models.Model):
class Build(models.Model):
class TestResult(models.Model):
class LaunchItem(models.Model):
class Bug(models.Model):
    def get_dashboards(self):
    def set_dashboards(self, dashboards):
    def __str__(self):
    def is_finished(self):
    def counts(self):
    def calculate_counts(self):
    def failed(self):
    def skipped(self):
    def passed(self):
    def blocked(self):
    def get_tasks(self):
    def set_tasks(self, tasks):
    def get_parameters(self):
    def set_parameters(self, parameters):
    def __str__(self):
    def get_last_commits(self):
    def set_last_commits(self, last_commits):
    def __str__(self):
    def __str__(self):
    def __str__(self):
    def get_state(self):
    def __str__(self):
def get_issue_fields_from_bts(externalId):
def _get_bug(bug_id): def get_issue_fields_from_bts(externalId):
    log.debug('Get fields for bug {}'.format(externalId))
    res = _get_bug(externalId)
    if 'fields' in res:
        return res['fields']
    return res class Stage(models.Model):
    name = models.CharField(max_length=128)
    project = models.ForeignKey(Project)
    text = models.TextField(default='', blank=True)
    link = models.URLField(default=None, blank=True, null=True)
    state = models.CharField(max_length=24, default='info', blank=True)
    weight = models.IntegerField(default=0)
    updated = models.DateTimeField(default=datetime.datetime.now())

    def save(self, *args, **kwargs):
        self.updated = datetime.datetime.now()
        return super(Stage, self).save(*args, **kwargs)

    def __str__(self):
        return self.name class Metric(models.Model):
    project = models.ForeignKey(Project)
    name = models.CharField(max_length=128)
    query = models.TextField(default='', blank=True)
    schedule = models.ForeignKey(PeriodicTask)
    handler = models.CharField(max_length=128, choices=HANDLER_CHOICES)
    error = models.TextField(default='', blank=True)
    weight = models.IntegerField(default=1)

    def __str__(self):
        return '{} -> Metric: {}'.format(self.project, self.name)

    def get_schedule_as_cron(self):
        cron = self.schedule.crontab
        return '{} {} {} {} {}'.format(cron.minute, cron.hour,
                                       cron.day_of_month, cron.month_of_year,
                                       cron.day_of_week) class MetricValue(models.Model):
    metric = models.ForeignKey(Metric)
    value = models.FloatField(default=None,
                              validators=[MinValueValidator(0.0)])
    created = models.DateTimeField()

    def __str__(self):
        return self.value

    def save(self, force_insert=False, force_update=False, using=None,
             update_fields=None):
        if self.created is None:
            self.created = timezone.now()
        super().save(force_insert, force_update, using, update_fields) HANDLER_CHOICES = (
    ('count', 'Calculate count value'),
    ('cycletime', 'Calculate cycle time'),
    ('leadtime', 'Calculate lead time')
) @celery.task()
def restore_metric_values(metric_id, query, step, handler, handler_field):
    expand = None
    if handler in ['cycletime', 'leadtime']:
        expand = ['changelog']

    if settings.JIRA_INTEGRATION:
        try:
            method = getattr(handlers, handler)
            data = request_jira_api(
                query=query,
                fields=['created', 'resolutiondate'],
                expand=expand)

            results = group_issues_by_step(data, int(step), handler_field)

        except Exception as e:
            raise e

        for date, group in results.items():
            log.info('Handling selected data started')
            try:
                value = method(group)  # handler value
                log.info('Handling selected data finished')
            except Exception as e:
                log.error(
                    'Selected data "{}" can not be handle by {}-handler: {}'.
                    format(data, handler, e))
                raise e
            MetricValue.objects.create(metric_id=metric_id, value=value,
                                       created=date)
    else:
        log.info('Jira integration is off. '
                 'If you want to use this feature, turn it on.') @celery.task()
def finalize_launch(launch_id, state=FINISHED, timeout=30, tries=5):
    log.info("Finalize launch {}".format(launch_id))
    launch = Launch.objects.get(pk=launch_id)
    log.info("Current launch: {}".format(launch.__dict__))
    launch.finished = datetime.now()
    launch.calculate_counts()
    launch.state = state
    log.info("Launch for update: {}".format(launch.__dict__))
    launch.save(force_update=True)
    if state != STOPPED:
        for i in range(0, tries):
            log.info("Waiting for {} seconds, before next try".format(timeout))
            sleep(timeout)
            launch = Launch.objects.get(pk=launch_id)
            if launch.state == state:
                break
            log.info("Launch state not finished, try to save again.")
            launch.finished = datetime.now()
            launch.state = state
            launch.save()
    log.info(
        "Updated launch: {}".format(Launch.objects.get(pk=launch_id).__dict__)) @celery.task(bind=True)
def parse_xml(self, xunit_format, launch_id, params, s3_conn=False,
              s3_key_name=None, file_content=None):
    try:
        if s3_conn:
            s3_connection = get_s3_connection()
            log.debug('Trying to get file from {}'.format(settings.S3_HOST))
            file_content = \
                get_file_from_storage(s3_connection, s3_key_name).read()
            log.debug('Getting file is successful')

        log.debug('Start parsing xml {}'.format(s3_key_name))
        xml_parser_func(format=xunit_format,
                        file_content=file_content,
                        launch_id=launch_id,
                        params=params)
        log.debug('Xml parsed successful')
    except ConnectionRefusedError as e:
        log.error(e)
        comment = 'There are some problems with ' \
                  'connection to {}: "{}". '.format(settings.S3_HOST, e)

        if parse_xml.request.retries < settings.S3_MAX_RETRIES:
            comment += 'Next try in {} min.'.\
                format(int(settings.S3_COUNTDOWN / 60))

            add_comment_to_launch(launch_id, comment)
            return self.retry(countdown=settings.S3_COUNTDOWN,
                              throw=False, exc=e)

        comment += 'Please, try to send your xml later.'
        add_comment_to_launch(launch_id=launch_id, comment=comment)
    except Exception as e:
        log.error(e)

        comment = 'During xml parsing the ' \
                  'following error is received: "{}"'.format(e)
        add_comment_to_launch(launch_id, comment)

    if s3_conn:
        finalize_launch(launch_id=launch_id, tries=0)
        delete_file_from_storage(s3_connection, s3_key_name)
        log.debug('Xml file "{}" deleted'.format(s3_key_name))
    else:
        launch = Launch.objects.get(pk=launch_id)
        launch.calculate_counts() class Comment(models.Model):
    """
    A user comment about some object.
    """
    content_type = models.ForeignKey(
        ContentType,
        verbose_name=_('content type'),
        related_name="content_type_set_for_%(class)s")
    object_pk = models.TextField(_('object ID'))

    content_object = generic.GenericForeignKey(ct_field="content_type",
                                               fk_field="object_pk")
    # Who posted this comment? If ``user`` is set then it was an authenticated
    # user; otherwise at least user_name should have been set and the comment
    # was posted by a non-authenticated user.
    user = models.ForeignKey(settings.AUTH_USER_MODEL, verbose_name=_('user'),
                             blank=True, null=True,
                             related_name="%(class)s_comments")
    comment = models.TextField(_('comment'), max_length=COMMENT_MAX_LENGTH)

    # Metadata about the comment
    submit_date = models.DateTimeField(_('date/time submitted'), default=None,
                                       blank=True)

    # Manager
    objects = CommentManager()

    class Meta:
        ordering = ('submit_date',)
        permissions = [("can_moderate", "Can moderate comments")]
        verbose_name = _('comment')
        verbose_name_plural = _('comments')

    def __str__(self):
        return "{0}: {1}...".format(self.user, self.comment[:50])

    def save(self, *args, **kwargs):
        if self.submit_date is None:
            self.submit_date = timezone.now()
        super(Comment, self).save(*args, **kwargs) def debug_task(self):
from rest_framework import viewsets
from rest_framework import status
from rest_framework import mixins
from rest_framework_xml.renderers import XMLRenderer
from rest_framework.parsers import BaseParser, FileUploadParser
from defusedxml import ElementTree
from rest_framework.exceptions import ParseError
from rest_framework.permissions import IsAuthenticatedOrReadOnly
from rest_framework.permissions import DjangoModelPermissionsOrAnonReadOnly
from rest_framework.response import Response
from rest_framework.filters import DjangoFilterBackend
from rest_framework.filters import OrderingFilter
from rest_framework.filters import SearchFilter
from rest_framework.decorators import detail_route, list_route
from rest_framework.views import APIView
from rest_framework.authentication import SessionAuthentication
from rest_framework.authentication import BasicAuthentication
from django.core.exceptions import ObjectDoesNotExist, MultipleObjectsReturned
from django.core.files.uploadedfile import InMemoryUploadedFile
from rest_framework_bulk import ListBulkCreateAPIView
from common.storage import get_s3_connection, get_or_create_bucket
from common.models import Project, Settings
from common.tasks import launch_process
from testreport.tasks import create_environment
from cdws_api.serializers import ProjectSerializer
from cdws_api.serializers import LaunchSerializer
from cdws_api.serializers import LaunchItemSerializer
from cdws_api.serializers import TestResultSerializer
from cdws_api.serializers import TestPlanSerializer
from cdws_api.serializers import AsyncResultSerializer
from cdws_api.serializers import CommentSerializer
from cdws_api.serializers import BugSerializer
from cdws_api.serializers import StageSerializer
from cdws_api.serializers import MetricSerializer, MetricValueSerializer
from testreport.models import TestPlan
from testreport.models import Launch
from testreport.models import Build
from testreport.models import TestResult
from testreport.models import LaunchItem
from testreport.models import Bug
from testreport.models import INITIALIZED, ASYNC_CALL, INIT_SCRIPT, CONCLUSIVE
from testreport.models import STOPPED, IN_PROGRESS, FINISHED
from testreport.models import get_issue_fields_from_bts
from stages.models import Stage
from metrics.models import Metric, MetricValue
from metrics.handlers import HANDLER_CHOICES
from metrics.tasks import restore_metric_values
from testreport.tasks import finalize_launch
from testreport.tasks import parse_xml
from django.contrib.contenttypes.models import ContentType
from django.utils import timezone
from django.db.models import Q, Count
from comments.models import Comment
from djcelery.models import TaskMeta, CrontabSchedule, PeriodicTask
from celery.utils import uuid
from django.conf import settings
from pycd.celery import app
import datetime
import logging
import celery
import copy
import os
import socket
    model = Project

    filter_backends = (DjangoFilterBackend, )
    filter_fields = ('name', )

    def find_duplicate(self, serializer):
        return Project.objects.get(name=serializer.initial_data['name'])

    @detail_route(methods=['post'],
                  permission_classes=[DjangoModelPermissionsOrAnonReadOnly],
                  url_path='settings')
    def set_settings(self, request, pk=None):
        (settings, new) = Settings.objects.get_or_create(
            project=Project.objects.get(id=pk), key=request.data['key'])
        settings.value = request.data['value']
        settings.save()
        return Response(status=status.HTTP_201_CREATED, data={'message': 'ok'})

    @detail_route(methods=['post'],
                  permission_classes=[DjangoModelPermissionsOrAnonReadOnly],
                  url_path='settings/delete')
    def delete_settings(self, request, pk=None):
        settings = Settings.objects.filter(
            project=Project.objects.get(id=pk),
            key=request.data['key'], value=request.data['value'])
        settings.delete()
        return Response(status=status.HTTP_200_OK, data={'message': 'ok'})


class TestPlanViewSet(GetOrCreateViewSet):
    queryset = TestPlan.objects.all()
    serializer_class = TestPlanSerializer
    model = TestPlan

    filter_backends = (DjangoFilterBackend, )
    filter_fields = ('id', 'project', 'name')

    @list_route(methods=['get'])
    def custom_list(self, request, *args, **kwargs):
        if 'project_id__in' in request.GET \
                and request.GET['project_id__in'] != '':
            self.queryset = self.queryset.filter(
                project_id__in=request.GET['project_id__in'].split(','))
        if 'id__in' in request.GET and request.GET['id__in'] != '':
            self.queryset = self.queryset.filter(
                id__in=request.GET['id__in'].split(','))
        return self.list(request, *args, **kwargs)

    def create(self, request, *args, **kwargs):
        request.data['owner'] = request.user.id
        return super(TestPlanViewSet, self).create(request, *args, **kwargs)

    def find_duplicate(self, serializer):
        if 'name' not in serializer.initial_data \
                or 'project' not in serializer.initial_data:
            raise ObjectDoesNotExist
        return TestPlan.objects.get(
            name=serializer.initial_data['name'],
            project=serializer.initial_data['project'])

    @detail_route(methods=['post'],
                  permission_classes=[IsAuthenticatedOrReadOnly])
    def execute(self, request, pk=None):
        workspace_path = os.path.join(
            settings.CDWS_WORKING_DIR,
            timezone.now().strftime('%Y-%m-%d-%H-%M-%f'))
        post_data = request.data
        options = request.data['options']
        json_file = None
        if 'json_file' in post_data:
            json_file = post_data['json_file']

        test_plan = TestPlan.objects.get(pk=pk)

        # launch create
        launch = Launch(test_plan=test_plan,
                        started_by=options['started_by'],
                        state=INITIALIZED)
        launch.save()

        build = Build(launch=launch,
                      version=options.get('version'),
                      branch=options.get('branch'),
                      hash=options.get('hash'))
        build.save()

        # env create
        env = {'WORKSPACE':
               os.path.join(settings.CDWS_DEPLOY_DIR, workspace_path),
               'HOME':
               os.path.join(settings.CDWS_DEPLOY_DIR, workspace_path)}
        if 'env' in post_data:
            for key, value in iter(post_data['env'].items()):
                env[key] = value
        env['REPORT_API_URL'] = 'https://{0}/{1}'.format(
            settings.CDWS_API_HOSTNAME, settings.CDWS_API_PATH)
        # environment values should be string for exec
        env['TESTPLAN_ID'] = str(test_plan.id)
        env['LAUNCH_ID'] = str(launch.id)
        env['WORKSPACE_URL'] = 'http://{}/{}/'.format(
            settings.CELERY_HOST, workspace_path)

        # queryset create
        if 'launch_items' in post_data:
            try:
                launch_items = test_plan.launchitem_set.filter(
                    id__in=post_data['launch_items']).order_by('id')
            except (KeyError, ValueError) as e:
                return Response(status=status.HTTP_400_BAD_REQUEST,
                                data={'message': '{}'.format(e)})
        else:
            launch_items = test_plan.launchitem_set.all().order_by('id')

        mapping = {}
        init_task = None
        async_tasks = []
        conclusive_tasks = []

        create_env_task = create_environment.subtask(
            [env, json_file], immutable=True, soft_time_limit=1200)

        final_task = finalize_launch.subtask(