예제 #1
0
파일: task.py 프로젝트: eclogue/eclogue
def get_task_history():
    query = request.args or {}
    page = int(query.get('page', 1))
    size = int(query.get('pageSize', 50))
    skip = (page - 1) * size
    keyword = query.get('keyword')
    where = {}
    if keyword:
        where['name'] = keyword

    cursor = db.collection('tasks').find(where, skip=skip, limit=size)
    total = cursor.count()
    tasks = []
    job = Job()
    for task in cursor:
        job_id = task.get('job_id')
        if not job_id:
            continue

        task['job'] = job.find_by_id(job_id)
        tasks.append(task)

    return jsonify({
        'message': 'ok',
        'code': 0,
        'data': {
            'list': tasks,
            'total': total,
            'page': page,
            'pageSize': size,
        }
    })
예제 #2
0
def gitlab_job(token):
    payload = request.get_json()
    object_type = payload.get('object_type')
    tag = payload.get('tag')
    job_id = payload.get('job_id')
    project_id = payload.get('project_id')
    job_status = payload.get('job_status')
    repository = payload.get('repository')
    record = Job().collection.find_one({'token': token})
    if not record:
        return jsonify({'message': 'invalid token', 'code': 104010}), 401

    if object_type != 'job':
        return jsonify({
            'message': 'only job event allow',
            'code': 104031
        }), 403

    if not job_id or not project_id or job_status != 'success':
        return jsonify({'message': 'invalid params', 'code': 104000}), 400

    if not tag:
        return jsonify({'message': 'only tag allow', 'code': 104032}), 403

    app_info = db.collection('apps').find_one(
        {'_id': ObjectId(record.get('app_id'))})
    if not app_info:
        return jsonify({
            'message': 'job must be bind with gitlab app',
            'code': 104002
        }), 400
    params = app_info.get('params') or {}
    if params.get('project_id') != project_id:
        return jsonify({'message': 'illegal project', 'code': 104003}), 400

    # if params.get('extract') == 'artifacts':
    #     wk = Workspace()
    #     filename = wk.get_gitlab_artifacts_file(record.get('name'), project_id, job_id)
    #     gitlab = GitlabApi().dowload_artifact(project_id, job_id, filename)
    #
    # if params.get('extract') == 'docker':
    #     pass
    integration = Integration(app_info.get('type'), params)
    integration.install()

    ansible_params = load_ansible_playbook(record)
    if ansible_params.get('message') is not 'ok':
        return jsonify(payload), 400

    data = ansible_params.get('data')
    wk = Workspace()
    res = wk.load_book_from_db(name=data.get('book_name'),
                               roles=data.get('roles'))
    if not res:
        return jsonify({
            'message': 'load book failed',
            'code': 104000,
        }), 400

    return jsonify({'message': 'ok', 'code': 0, 'data': 1})
예제 #3
0
def get_jobs():
    username = login_user.get('username')
    query = request.args
    page = int(query.get('page', 1))
    size = int(query.get('pageSize', 50))
    offset = (page - 1) * size
    is_admin = login_user.get('is_admin')
    keyword = query.get('keyword')
    status = query.get('status')
    job_type = query.get('type')
    start = query.get('start')
    end = query.get('end')
    where = {'status': {'$gt': -1}}

    if not is_admin:
        where['maintainer'] = {'$in': [username]}

    if keyword:
        where['name'] = {'$regex': keyword}

    if status is not None:
        where['status'] = status

    if job_type:
        where['type'] = job_type

    date = []
    if start:
        date.append({
            'created_at': {
                '$gte': int(time.mktime(time.strptime(start, '%Y-%m-%d')))
            }
        })

    if end:
        date.append({
            'created_at': {
                '$lte': int(time.mktime(time.strptime(end, '%Y-%m-%d')))
            }
        })

    if date:
        where['$and'] = date

    jobs = Job().collection.find(where, skip=offset, limit=size)
    total = jobs.count()
    jobs = list(jobs)

    return jsonify({
        'message': 'ok',
        'code': 0,
        'data': {
            'list': jobs,
            'total': total,
            'page': page,
            'pageSize': size,
        },
    })
예제 #4
0
def delete_job(_id):
    is_admin = login_user.get('is_admin')
    record = Job.find_by_id(ObjectId(_id))
    if not record:
        return jsonify({'message': 'record not found', 'code': 104040}), 404

    if not is_admin or login_user.get('username') not in record['maintainer']:
        return jsonify({'message': 'permission deny', 'code': 104031}), 403

    Job.delete_one({'_id': record['_id']})

    return jsonify({
        'message': 'ok',
        'code': 0,
    })
예제 #5
0
def delete_job(_id):
    record = Job.find_by_id(ObjectId(_id))
    if not record:
        return jsonify({'message': 'record not found', 'code': 104040}), 404

    update = {'$set': {'status': -1}}

    Job.update_one({'_id': record['_id']}, update=update)
    extra = {'record': record}
    logger.info('delete job', extra=extra)

    return jsonify({
        'message': 'ok',
        'code': 0,
    })
예제 #6
0
파일: dispatch.py 프로젝트: fakegit/eclogue
def run_job(_id, history_id=None, **kwargs):
    db = Mongo()
    record = Job.find_by_id(_id)
    if not record:
        return False

    request_id = str(current_request_id())
    username = None if not login_user else login_user.get('username')
    params = (_id, request_id, username, history_id)
    queue_name = get_queue_by_job(_id)
    extra = record.get('extra')
    template = record.get('template')
    schedule = extra.get('schedule')
    ansible_type = record.get('type')
    if template.get('run_type') == 'schedule':
        existed = db.collection('scheduler_jobs').find_one(
            {'_id': record['_id']})
        if existed:
            return False

        scheduler.add_job(func=run_schedule_task,
                          trigger='cron',
                          args=params,
                          coalesce=True,
                          kwargs=kwargs,
                          id=str(record.get('_id')),
                          max_instances=1,
                          name=record.get('name'),
                          **schedule)
        return True
    else:
        func = run_playbook_task if ansible_type != 'adhoc' else run_adhoc_task
        task = Task(tiger,
                    func=func,
                    args=params,
                    kwargs=kwargs,
                    queue=queue_name,
                    unique=True,
                    lock=True,
                    lock_key=_id)

        task_record = {
            'job_id': _id,
            'type': 'trigger',
            'ansible': ansible_type,
            'state': QUEUED,
            'queue': queue_name,
            'result': '',
            'request_id': request_id,
            't_id': task.id,
            'created_at': time(),
            'kwargs': kwargs,
        }

        result = db.collection('tasks').insert_one(task_record)
        task.delay()

        return result.inserted_id
예제 #7
0
파일: job.py 프로젝트: eclogue/eclogue
 def test_get_job(self, check_book, parse_file_inventory,
                  parse_cmdb_inventory):
     data = self.get_data('playbook_job')
     data['name'] = str(uuid.uuid4())
     data['status'] = 1
     url = self.get_api_path('/jobs/%s' % str(ObjectId()))
     response = self.client.get(url, headers=self.jwt_headers)
     self.assert400(response)
     self.assertResponseCode(response, 154001)
     result = self.add_test_data(Job, data)
     url = self.get_api_path('/jobs/%s' % str(result.inserted_id))
     parse_cmdb_inventory.return_value = ''
     parse_file_inventory.return_value = ''
     response = self.client.get(url, headers=self.jwt_headers)
     self.assert200(response)
     Job.update_one({'_id': result.inserted_id}, {
         '$set': {
             'maintainer': [self.user.get('username')],
             'book_id': str(ObjectId())
         }
     })
     response = self.client.get(url, headers=self.jwt_headers)
     parse_file_inventory.assert_called()
     check_book.assert_called()
     self.assert200(response)
     self.assertResponseDataHasKey(response, 'logs')
     self.assertResponseDataHasKey(response, 'previewContent')
     self.assertResponseDataHasKey(response, 'record')
     self.assertResponseDataHasKey(response, 'roles')
     data = self.get_data('adhoc_job')
     data['maintainer'] = [self.user.get('username')]
     data['template'] = {
         'inventory_type': 'adhoc',
         'inventory': 'localhost'
     }
     result = self.add_test_data(Job, data)
     url = self.get_api_path('/jobs/%s' % str(result.inserted_id))
     response = self.client.get(url, headers=self.jwt_headers)
     parse_cmdb_inventory.assert_called()
     self.assertResponseDataHasKey(response, 'logs')
     self.assertResponseDataHasKey(response, 'previewContent')
     self.assertResponseDataHasKey(response, 'record')
예제 #8
0
파일: task.py 프로젝트: eclogue/eclogue
def rollback(_id):
    record = db.collection('tasks').find_one({'_id': ObjectId(_id)})
    if not record:
        return jsonify({
            'message': 'task not found',
            'code': 194041
        }), 404

    job_id = record.get('job_id')
    if not job_id:
        return jsonify({
            'message': 'invalid job',
            'code': 1040011
        }), 400

    where = {
        '_id': ObjectId(job_id),
        'status': 1,
    }
    job_record = Job().collection.find_one(where)
    if not job_record:
        return jsonify({
            'message': 'invalid job',
            'code': 1040010
        }), 400

    history = db.collection('build_history').find_one({'task_id': _id})
    if not history:
        return jsonify({
            'message': 'failed load playbook from history',
            'code': 104041
        }), 404

    build_id = str(history.get('_id'))
    run_job(job_id, build_id)

    return jsonify({
        'message': 'ok',
        'code': 0,
    })
예제 #9
0
def check_job(_id):
    record = Job.find_one({'_id': ObjectId(_id)})
    if not record:
        return jsonify({'message': 'job not found', 'code': 104001}), 400

    body = {'template': record.get('template'), 'extra': record.get('extra')}

    payload = load_ansible_playbook(body)
    if payload.get('message') is not 'ok':
        return jsonify(payload), 400

    data = payload.get('data')
    options = data.get('options')
    private_key = data.get('private_key')
    wk = Workspace()
    res = wk.load_book_from_db(name=data.get('book_name'),
                               roles=data.get('roles'))
    if not res:
        return jsonify({
            'message': 'book not found',
            'code': 104000,
        }), 400

    entry = wk.get_book_entry(data.get('book_name'), data.get('entry'))
    with NamedTemporaryFile('w+t', delete=True) as fd:
        key_text = get_credential_content_by_id(private_key, 'private_key')
        if not key_text:
            return jsonify({
                'message': 'invalid private_key',
                'code': 104033,
            }), 401
        fd.write(key_text)
        fd.seek(0)
        options['private_key'] = fd.name
        play = PlayBookRunner(data['inventory'], options)
        play.run(entry)
        result = play.get_result()

        return jsonify({'message': 'ok', 'code': 0, 'data': result})
예제 #10
0
def job_webhook():
    query = request.args
    token = query.get('token')
    payload = request.get_json()
    if not payload or not token:
        return jsonify({'message': 'invalid params', 'code': 104000}), 400

    record = Job().collection.find_one({'token': token})
    if not record:
        return jsonify({'message': 'illegal token', 'code': 104010}), 401

    if record.get('type') == 'adhoc':
        task_id = run_job(str(record.get('_id')))
        if not task_id:
            return jsonify({
                'message': 'try to queue task faield',
                'code': 104008
            }), 400

        return jsonify({'message': 'ok', 'code': 0, 'data': task_id})

    template = record.get('template')
    app_id = template.get('app')
    income_params = {'cache': True}
    if app_id:
        app_info = Application.find_by_id(app_id)
        if not app_info:
            return jsonify({
                'message': 'app not found, please check your app',
                'code': 104001
            }), 400

        app_params = app_info.get('params')
        income = app_params.get('income')
        if income and payload.get('income'):
            income = Template(income)
            tpl = income.render(**payload.get('income'))
            tpl = yaml.safe_load(tpl)
            if tpl:
                income_params.update(tpl)

    task_id = run_job(str(record.get('_id')), **income_params)
    if not task_id:
        return jsonify({
            'message': 'put job enqueue failed',
            'code': 104002
        }), 400

    # if app_type == 'jenkins':
    #     build_id = '19'
    #     job_name = 'upward'
    #     run_job(_id, job_name, build_id)
    # elif app_type == 'gitlab':
    #     project_id = '13539397'
    #     job_id = '261939258'
    #     run_job(_id, project_id, job_id)
    # else:
    #     run_job(_id)

    # logger.error('test', extra={'a': {'b': 1}})

    return jsonify({'message': 'ok', 'code': 0, 'data': task_id})
예제 #11
0
def get_job(_id):
    username = login_user.get('username')
    if not _id:
        return jsonify({'message': 'invalid id', 'code': 154000}), 400

    job = Job.find_one({
        '_id': ObjectId(_id),
        'maintainer': {
            '$in': [username]
        }
    })

    # @todo job status
    if not job:
        return jsonify({
            'message': 'invalid id',
            'code': 154001,
        }), 400

    template = job.get('template')
    inventory_type = template.get('inventory_type')
    inventory = template.get('inventory')
    if job.get('type') == 'adhoc':
        inventory_content = parse_cmdb_inventory(inventory)
        return jsonify({
            'message': 'ok',
            'code': 0,
            'data': {
                'record': job,
                'previewContent': inventory_content,
            },
        })

    if inventory_type == 'file':
        inventory_content = parse_file_inventory(inventory)
    else:
        inventory_content = parse_cmdb_inventory(inventory)

    check_playbook(job['book_id'])
    if inventory_type == 'file':
        book = Book.find_one({'_id': ObjectId(job['book_id'])})
        if not book:
            hosts = []
        else:
            hosts = get_inventory_by_book(book.get('_id'),
                                          book_name=book.get('name'))
    else:
        hosts = get_inventory_from_cmdb()

    roles = []
    condition = {
        'book_id': str(job['book_id']),
        'role': 'roles',
        'is_dir': True
    }
    parent = Playbook.find_one(condition)
    if parent:
        where = {
            'book_id': job['book_id'],
            'is_dir': True,
            'parent': parent.get('path')
        }
        cursor = Playbook.find(where)
        roles = list(cursor)

    logs = None
    task = Task.find_one({'job_id': _id})
    if task:
        log = db.collection('logs').find_one({'task_id': str(task['_id'])})
        if log:
            logs = log.get('message')

    return jsonify({
        'message': 'ok',
        'code': 0,
        'data': {
            'record': job,
            'previewContent': inventory_content,
            'hosts': hosts,
            'roles': roles,
            'logs': logs,
        },
    })
예제 #12
0
def add_jobs():
    """
    add job
    TODO(sang)
    :return: json
    """
    body = request.get_json()
    user = login_user
    if not body:
        return jsonify({
            'message': 'miss required params',
            'code': 104000,
        }), 400

    job_type = body.get('type')
    if job_type == 'adhoc':
        return add_adhoc()

    current_id = body.get('currentId')
    record = None
    if current_id:
        record = Job.find_by_id(current_id)
        if not record:
            return jsonify({'message': 'job not found', 'code': 104044}), 400

    payload = load_ansible_playbook(body)
    if payload.get('message') is not 'ok':
        return jsonify(payload), 400

    data = payload.get('data')
    options = data.get('options')
    is_check = body.get('check')
    private_key = data.get('private_key')
    wk = Workspace()
    res = wk.load_book_from_db(name=data.get('book_name'),
                               roles=data.get('roles'))
    if not res:
        return jsonify({
            'message': 'book not found',
            'code': 104000,
        }), 400

    entry = wk.get_book_entry(data.get('book_name'), data.get('entry'))
    dry_run = bool(is_check)
    options['check'] = dry_run
    if dry_run:
        with NamedTemporaryFile('w+t', delete=True) as fd:
            if private_key:
                key_text = get_credential_content_by_id(
                    private_key, 'private_key')
                if not key_text:
                    return jsonify({
                        'message': 'invalid private_key',
                        'code': 104033,
                    }), 401

                fd.write(key_text)
                fd.seek(0)
                options['private_key'] = fd.name

            play = PlayBookRunner(data['inventory'],
                                  options,
                                  callback=CallbackModule())
            play.run(entry)

            return jsonify({
                'message': 'ok',
                'code': 0,
                'data': {
                    'result': play.get_result(),
                    'options': options
                }
            })

    name = data.get('name')
    existed = Job.find_one({'name': name})
    if existed and not current_id:
        return jsonify({
            'message': 'name existed',
            'code': 104001,
        }), 400

    token = str(base64.b64encode(bytes(current_request_id(), 'utf8')), 'utf8')
    new_record = {
        'name': name,
        'type': 'playbook',
        'token': token,
        'description': data.get('description'),
        'book_id': data.get('book_id'),
        'template': data.get('template'),
        'extra': data.get('extra'),
        'entry': data['entry'],
        'status': data['status'],
        'maintainer': [user.get('username')],
        'created_at': int(time.time()),
        'updated_at': datetime.datetime.now().isoformat(),
    }

    if record:
        Job.update_one({'_id': record['_id']}, update={'$set': new_record})
    else:
        Job.insert_one(new_record)

    return jsonify({
        'message': 'ok',
        'code': 0,
    })
예제 #13
0
파일: job.py 프로젝트: eclogue/eclogue
    def test_add_adhoc_job(self, parse_cmdb_inventory,
                           get_credential_content_by_id, adhoc_runner):
        data = self.get_data('adhoc_job')
        data['name'] = str(uuid.uuid4())
        url = self.get_api_path('/jobs')
        response = self.client.post(url, data='{}', headers=self.jwt_headers)
        self.assert400(response)
        self.assertResponseCode(response, 104000)
        params = data.copy()
        # None module params should response 400
        params['module'] = None
        params['type'] = 'adhoc'
        current_user = self.user
        params['maintainer'] = [current_user.get('username')]
        payload = self.body(params)
        response = self.client.post(url,
                                    data=payload,
                                    headers=self.jwt_headers)
        self.assert400(response)
        self.assertResponseCode(response, 104002)
        # test parse_cmdb_inventory return None
        parse_cmdb_inventory.return_value = None
        params['module'] = 'ls'
        payload = self.body(params)
        response = self.client.post(url,
                                    data=payload,
                                    headers=self.jwt_headers)
        self.assert400(response)
        self.assertResponseCode(response, 104004)

        # test with check and no private_key condition
        params['check'] = True
        params['private_key'] = None
        parse_cmdb_inventory.return_value = 'localhost'
        runner_instance = adhoc_runner.return_value
        runner_instance.get_result.return_value = data['name']
        payload = self.body(params)
        response = self.client.post(url,
                                    data=payload,
                                    headers=self.jwt_headers)
        self.assert200(response)
        runner_instance.run.assert_called()
        # test run with private_key
        params['private_key'] = True
        get_credential_content_by_id.return_value = None
        payload = self.body(params)
        response = self.client.post(url,
                                    data=payload,
                                    headers=self.jwt_headers)
        self.assert400(response)
        self.assertResponseCode(response, 104004)
        # assume get private_key return correct
        get_credential_content_by_id.return_value = 'test-private'
        response = self.client.post(url,
                                    data=payload,
                                    headers=self.jwt_headers)
        self.assert200(response)
        runner_instance.run.assert_called()
        params['check'] = False
        payload = self.body(params)
        response = self.client.post(url,
                                    data=payload,
                                    headers=self.jwt_headers)
        self.assert200(response)
        runner_instance.run.assert_called()
        response = self.client.post(url,
                                    data=payload,
                                    headers=self.jwt_headers)
        self.assert400(response)
        record = Job.find_one({'name': data['name']})
        assert record
        assert record.get('name') == data.get('name')
        params['job_id'] = str(ObjectId())
        payload = self.body(params)
        response = self.client.post(url,
                                    data=payload,
                                    headers=self.jwt_headers)
        self.assert404(response)
        self.assertResponseCode(response, 104040)
        params['job_id'] = str(record.get('_id'))
        payload = self.body(params)
        response = self.client.post(url,
                                    data=payload,
                                    headers=self.jwt_headers)
        self.assert200(response)
        Job().collection.delete_one({'name': data['name']})
예제 #14
0
파일: job.py 프로젝트: eclogue/eclogue
    def test_add_job(self, load_ansible_playbook, workspace, key_mock, runner):
        data = self.get_data('playbook_job')
        data['name'] = str(uuid.uuid4())
        url = self.get_api_path('/jobs')
        response = self.client.post(url, data='{}', headers=self.jwt_headers)
        self.assert400(response)
        self.assertResponseCode(response, 104000)
        params = data.copy()
        params['type'] = 'playbook'
        payload = self.body(params)
        load_ansible_playbook.return_value = {'message': 'test', 'code': 123}
        response = self.client.post(url,
                                    data=payload,
                                    headers=self.jwt_headers)
        self.assert400(response)
        self.assertResponseCode(response, 123)
        options = {}
        ansible_payload = {
            'message': 'ok',
            'code': 0,
            'data': {
                'inventory': options.get('inventory'),
                'options': options,
                'name': data.get('name'),
                'entry': data['entry'],
                'book_id': data.get('book_id'),
                'book_name': 'test',
                'roles': ['test'],
                'inventory_type': 'cmdb',
                'private_key': True,
                'template': {},
                'extra': {},
                'status': 1,
            }
        }

        load_ansible_playbook.return_value = ansible_payload
        instance = workspace.return_value
        instance.load_book_from_db.return_value = False
        response = self.client.post(url,
                                    data=payload,
                                    headers=self.jwt_headers)
        self.assert400(response)
        self.assertResponseCode(response, 104000)
        instance.load_book_from_db.return_value = True
        instance.get_book_entry.return_value = '/dev/null'
        params['check'] = True
        params['private_key'] = True
        payload = self.body(params)
        key_mock.return_value = 'test_private_key'
        instance = runner.return_value
        instance.get_result.return_value = 'f**k'
        response = self.client.post(url,
                                    data=payload,
                                    headers=self.jwt_headers)
        self.assert200(response)
        params['check'] = False
        payload = self.body(params)
        instance.get_result.return_value = 'f**k'
        response = self.client.post(url,
                                    data=payload,
                                    headers=self.jwt_headers)
        self.assert200(response)
        response = self.client.post(url,
                                    data=payload,
                                    headers=self.jwt_headers)
        self.assert400(response)
        self.assertResponseCode(response, 104001)
        Job().collection.delete_one({'name': data['name']})
예제 #15
0
def job_detail(_id):
    query = request.args
    record = Job.find_by_id(_id)
    if not record:
        return jsonify({
            'message': 'record not found',
            'code': 104040,
        }), 404

    if record.get('type') == 'adhoc':
        template = record.get('template')
        inventory = template.get('inventory')
        inventory_content = parse_cmdb_inventory(inventory)
        template['inventory_content'] = inventory_content
    else:
        book = Book.find_by_id(record.get('book_id'))
        record['book_name'] = book.get('name')
        template = record.get('template')
        result = load_ansible_playbook(record)
        if result['message'] == 'ok':
            record['command'] = to_playbook_command(result)
            command = to_playbook_command(result)
        else:
            record['command'] = 'invalid ansible command~!'
        if template:
            app_id = template.get('app')
            if app_id:
                app = db.collection('apps').find_one({'_id': ObjectId(app_id)})
                if app:
                    template['app_name'] = app.get('name')
                    template['app_params'] = app.get('params')

            inventory_type = template.get('inventory_type')
            inventory = template.get('inventory')
            if inventory_type == 'file':
                inventory_content = parse_file_inventory(inventory)
            else:
                inventory_content = parse_cmdb_inventory(inventory)

            template['inventory_content'] = inventory_content
            role_ids = template.get('roles')
            if role_ids:
                roles = Playbook.find_by_ids(role_ids)
                template['roles'] = list(map(lambda i: i.get('name'), roles))
            record['template'] = template
    page = int(query.get('page', 1))
    size = int(query.get('pageSize', 20))
    offset = (page - 1) * size
    tasks = get_tasks_by_job(_id, offset=offset, limit=size)
    tasks = list(tasks)
    logs = []
    sort = [('_id', -1)]
    task = db.collection('tasks').find_one({'job_id': _id}, sort=sort)
    if task:
        records = db.collection('task_logs').find(
            {'task_id': str(task['_id'])})
        for item in records:
            logs.append({
                'content': str(item.get('content')),
                'created_at': item.get('created_at')
            })

    return jsonify({
        'message': 'ok',
        'code': 0,
        'data': {
            'job': record,
            'logs': logs,
            'tasks': tasks,
        }
    })
예제 #16
0
파일: dispatch.py 프로젝트: fakegit/eclogue
def run_playbook_task(_id, request_id, username, history_id, **kwargs):
    db = Mongo()
    record = Job.find_by_id(ObjectId(_id))
    task_record = TaskModel.find_one({'request_id': request_id})
    if not task_record:
        return False

    start_at = time()
    state = 'progressing'
    result = ''
    task_id = task_record.get('_id')
    job_id = task_record.get('job_id')
    old_stdout = sys.stdout
    old_stderr = sys.stderr
    sys.stderr = sys.stdout = temp_stdout = Reporter(str(task_id))
    try:
        if history_id:
            history = db.collection('build_history').find_one(
                {'_id': ObjectId(history_id)})
            record = history['job_info']
            kwargs = task_record.get('kwargs')

        template = record.get('template')
        body = {
            'template': record.get('template'),
            'extra': record.get('extra')
        }

        payload = load_ansible_playbook(body)
        if payload.get('message') is not 'ok':
            raise Exception('load ansible options error: ' +
                            payload.get('message'))

        app_id = template.get('app')
        if app_id:
            app_info = Application.find_by_id(ObjectId(app_id))
            if not app_info:
                raise Exception('app not found: {}'.format(app_id))

            app_type = app_info.get('type')
            app_params = app_info.get('params')
            if kwargs:
                app_params.update(kwargs)

            integration = Integration(app_type, app_params)
            integration.install()

        data = payload.get('data')
        options = data.get('options')
        private_key = data.get('private_key')
        wk = Workspace()
        roles = data.get('roles')
        if history_id:
            bookspace = wk.build_book(history_id)
        else:
            bookname = data.get('book_name')
            bookspace = wk.load_book_from_db(name=bookname,
                                             roles=roles,
                                             build_id=task_id)

        if not bookspace or not os.path.isdir(bookspace):
            raise Exception('install playbook failed, book name: {}'.format(
                data.get('book_name')))

        entry = os.path.join(bookspace, data.get('entry'))

        with NamedTemporaryFile('w+t', delete=False) as fd:
            if private_key:
                key_text = get_credential_content_by_id(
                    private_key, 'private_key')
                if not key_text:
                    raise Exception('invalid private_key')

                fd.write(key_text)
                fd.seek(0)
                options['private-key'] = fd.name

            options['tags'] = ['uptime']
            options['verbosity'] = 2
            inventory = data.get('inventory')
            logger.info('ansible-playbook run load inventory: \n{}'.format(
                yaml.safe_dump(inventory)))
            play = PlayBookRunner(data.get('inventory'),
                                  options,
                                  job_id=job_id)
            play.run(entry)
            result = play.get_result()
            builds = db.collection('build_history').count({'job_id': _id})
            state = 'finish'
            # @todo
            if builds > cache_result_numer:
                last_one = db.collection('build_history').find_one(
                    {'job_id': _id}, sort=[('_id', 1)])
                if last_one:
                    db.fs().delete(last_one.get('file_id'))
                    db.collection('build_history').delete_one(
                        {'_id': last_one['_id']})

            with TemporaryDirectory() as dir_name:
                bookname = data.get('book_name')
                zip_file = os.path.join(dir_name, bookname)
                zip_file = make_zip(bookspace, zip_file)
                with open(zip_file, mode='rb') as stream:
                    filename = bookname + '.zip'
                    file_id = db.save_file(filename=filename, fileobj=stream)
                    store_info = {
                        'task_id': str(task_id),
                        'file_id': str(file_id),
                        'job_id': str(_id),
                        'job_info': record,
                        'filename': filename,
                        'created_at': time(),
                        'kwargs': kwargs,
                    }
                    db.collection('build_history').insert_one(store_info)
                    shutil.rmtree(bookspace)

    except Exception as e:
        result = str(e)
        extra = {'task_id': task_id}
        logger.error('run task with exception: {}'.format(str(e)), extra=extra)
        state = 'error'
        extra_options = record.get('extra')
        user = User.find_one({'username': username})
        if user:
            user_id = str(user['_id'])
            notification = extra_options.get('notification')
            message = '[error]run job: {}, message: {}'.format(
                record.get('name'), str(e))
            sys.stdout.write(message)
            if notification and type(notification) == list:
                Notify().dispatch(user_id=user_id,
                                  msg_type='task',
                                  content=message,
                                  channel=notification)

    finally:
        content = temp_stdout.getvalue()
        temp_stdout.close(True)
        sys.stdout = old_stdout
        sys.stderr = old_stderr
        finish_at = time()
        update = {
            '$set': {
                'start_at': start_at,
                'finish_at': finish_at,
                'state': state,
                'duration': finish_at - start_at,
                'result': result,
            }
        }
        TaskModel.update_one({'_id': task_id}, update=update)
        trace = {
            'task_id': str(task_id),
            'request_id': request_id,
            'username': username,
            'content': str(content),
            'created_at': time(),
        }
        db.collection('task_logs').insert_one(trace)
예제 #17
0
def add_adhoc():
    payload = request.get_json()
    if not payload:
        return jsonify({
            'message': 'invalid params',
            'code': 104001,
        }), 400

    module = payload.get('module')
    args = payload.get('args')
    inventory = payload.get('inventory')
    private_key = payload.get('private_key')
    verbosity = payload.get('verbosity', 0)
    name = payload.get('name')
    schedule = payload.get('schedule')
    check = payload.get('check')
    job_id = payload.get('job_id')
    extra_options = payload.get('extraOptions')
    status = int(payload.get('status', 0))
    notification = payload.get('notification')
    maintainer = payload.get('maintainer') or []
    if maintainer and isinstance(maintainer, list):
        users = User.find({'username': {'$in': maintainer}})
        names = list(map(lambda i: i['username'], users))
        maintainer.extend(names)

    if login_user.get('username'):
        maintainer.append(login_user.get('username'))

    if not job_id:
        existed = Job.find_one({'name': name})
        if existed and existed.get('status') != -1:
            return jsonify({'message': 'name exist', 'code': 104007}), 400

    if not module or not inventory or not name:
        return jsonify({
            'message': 'miss required params',
            'code': 104002,
        }), 400

    # check_module = db.collection('ansible_modules').find_one({
    #     'name': module
    # })
    #
    # if not check_module:
    #     return jsonify({
    #         'message': 'invalid module',
    #         'code': 104003,
    #     }), 400

    inventory_content = parse_cmdb_inventory(inventory)
    if not inventory_content:
        return jsonify({
            'message': 'invalid inventory',
            'code': 104004,
        }), 400

    options = {}
    if extra_options:
        options.update(extra_options)

    if verbosity:
        options['verbosity'] = verbosity

    if check:
        with NamedTemporaryFile('w+t', delete=True) as fd:
            if private_key:
                key_text = get_credential_content_by_id(
                    private_key, 'private_key')
                if not key_text:
                    return jsonify({
                        'message': 'invalid private key',
                        'code': 104004,
                    }), 400

                fd.write(key_text)
                fd.seek(0)
                options['private_key'] = fd.name

            tasks = [{'action': {'module': module, 'args': args}}]
            hosts = inventory_content
            runner = AdHocRunner(hosts, options=options)
            runner.run('all', tasks)
            result = runner.get_result()

            return jsonify({'message': 'ok', 'code': 0, 'data': result})

    else:
        token = str(base64.b64encode(bytes(current_request_id(), 'utf8')),
                    'utf8')
        data = {
            'name': name,
            'template': {
                'inventory': inventory,
                'private_key': private_key,
                'verbosity': verbosity,
                'module': module,
                'args': args,
                'extraOptions': extra_options,
            },
            'extra': {
                'schedule': schedule,
                'notification': notification,
            },
            'token': token,
            'maintainer': maintainer,
            'type': 'adhoc',
            'created_at': time.time(),
            'status': status,
            'add_by': login_user.get('username')
        }

        if job_id:
            record = Job.find_one({'_id': ObjectId(job_id)})
            if not record:
                return jsonify({
                    'message': 'record not found',
                    'code': 104040
                }), 404

            update = {
                '$set': data,
            }
            Job.update_one({'_id': ObjectId(job_id)}, update=update)
        else:
            Job.insert_one(data)

    return jsonify({
        'message': 'ok',
        'code': 0,
    })