Example #1
0
from apscheduler.schedulers.background import BackgroundScheduler
from flask import Flask
from config import Config
from flask_sqlalchemy import SQLAlchemy
from flask_migrate import Migrate
from flask_basicauth import BasicAuth

app = Flask(__name__)
basic_auth = BasicAuth(app)
app.config.from_object(Config)
db = SQLAlchemy(app)
migrate = Migrate(app, db)
sche = BackgroundScheduler()
sche.start()
from app import routes
import time
import sys
import os
from flask import Blueprint, request, session, url_for
from flask import render_template, redirect, jsonify
from flask_basicauth import BasicAuth
from authlib.integrations.flask_oauth2 import current_token
from authlib.oauth2 import OAuth2Error
from .models import db, User, OAuth2Client
from .oauth2 import authorization, require_oauth
from .intents import api_delegate
from .admin import admin_render

basic_auth = BasicAuth()
bp = Blueprint('routes', 'home')


@bp.route('/oauth/authorize', methods=['GET', 'POST'])
def authorize():
    user = User.query.filter_by(
        username=os.environ["GOOGLE_DEFAULT_USERNAME"]).first()
    resp = authorization.create_authorization_response(grant_user=user)
    return resp


@bp.route('/oauth/token', methods=['POST'])
def issue_token():
    return authorization.create_token_response()


@bp.route('/oauth/revoke', methods=['POST'])
Example #3
0
    return app_obj


def add_bp(app_obj):
    modules_define = [
        test.app, record.app, promise.app, motion.app, archive.app
    ]

    for bp_app in modules_define:
        app_obj.register_blueprint(bp_app)


app = init_app()
# client = FlaskAPNS()
# client.init_app(app)
admin_basic_auth = BasicAuth(app)
migrate = Migrate(app, db)


@app.route('/')
@app.route('/index')
def index():
    return 'This is index page'


@app.teardown_appcontext
def session_clear(exception):
    if exception and session.is_active:
        session.rollback()
    else:
        session.commit()
Example #4
0
def make_app(test_mode_internal=False):
    if test_mode_internal:
        app, db = appmaker.make_test_app()
    else:  # pragma: no cover
        app, db = appmaker.make_app()

    app.config['TEMPLATES_AUTO_RELOAD'] = True

    if 0:
        app.config['BASIC_AUTH_USERNAME'] = '******'
        app.config['BASIC_AUTH_PASSWORD'] = '******'
        app.config['BASIC_AUTH_FORCE'] = True
        basic_auth = BasicAuth(app)

    urlvalidate.register(app)

    app.jinja_env.globals["web_root"] = config.web_root
    app.jinja_env.globals["action_prefix"] = config.action_prefix
    app.jinja_env.globals["test_mode"] = config.test_mode

    def format_datetime(tstamp):
        """ Format float date into UTC timestamp. """
        return time.strftime("%c", time.gmtime(tstamp))

    app.jinja_env.filters["datetime"] = format_datetime

    app.jinja_env.trim_blocks = True
    app.jinja_env.lstrip_blocks = True

    @app.route('/api1/js/<path:path>')
    def js_static(path):
        return send_from_directory('js', path)

    @app.route("/api1/")
    def _entry():
        cml = Global.current_member_list()

        published = ProposalMetadata.all_public()
        member_applications = cml.applications()
        return render_template("index.html",
                               proposals=published,
                               member_applications=member_applications,
                               cml=cml)

    @app.route("/api1/unpublished-proposals")
    def _unpublished():
        unpublished_proposals = (
            Global.current_member_list().proposals().filter(
                ProposalMetadata.file_public == False))
        return render_template("unpublished_proposals.html",
                               proposals=unpublished_proposals)

    @app.route("/api1/debug")
    def get_debug_overview():
        if config.test_mode:
            return render_template("debug_overview.html")
        else:
            abort(404)

    @app.route("/api1/debug/objects")
    def _get_debug_objects():
        if config.test_mode:
            objmap = get_all_objects()
            return render_template("debug_objects.html", objmap=objmap)
        else:
            abort(404)

    @app.route("/api1/debug/hashrefs-by-type/<objtype:name>")
    def _get_debug_hashrefs_by_type(name):
        if config.test_mode:
            Cls = name2type[name]
            res = [obj.hashref() for obj in Cls.query]

            return jsonify(res)
        else:
            abort(404)

    @app.route("/api1/debug/current-member-list-hash")
    def _get_debug_current_member_list_hash():
        if not config.test_mode:
            abort(404)
        return Global.current_member_list().hashref()

    @app.route("/api1/debug/meta-for-raw-file/<shex:hashval>")
    def _get_debug_meta_for_raw(hashval):
        if not config.test_mode:
            abort(404)

        obj = RawFile.by_hash(hashval)
        if obj is None:
            abort(404)
        else:
            return obj.proposal_metadata.hashref()

    @app.route("/api1/debug/vote-for-raw-file/<shex:hashval>")
    def _get_debug_vote_for_raw_file(hashval):
        if not config.test_mode:
            abort(404)

        v = ProposalVote.by_raw_file_hash(hashval)
        if v is None:
            abort(404)
        else:
            return v.hashref()

    @app.route("/api1/debug/result-for-vote/<shex:hashval>")
    def _get_debug_result_for_vote(hashval):
        if not config.test_mode:
            abort(404)

        v = ProposalVote.by_hash(hashval)
        if v is None:
            abort(404)
        else:
            return v.result.hashref()

    @app.route("/api1/debug/summary-of-proposal-vote-result/<shex:hashval>")
    def _get_debug_summary_of_proposal_vote_result(hashval):
        if not config.test_mode:
            abort(404)

        v = ProposalVoteResult.by_hash(hashval)
        if v is None:
            abort(404)
        else:
            return jsonify(v.summarize())

    @app.route("/api1/debug/summary-of-member-election-result/<shex:hashval>")
    def _get_debug_summary_of_member_election_result(hashval):
        if not config.test_mode:
            abort(404)

        v = MemberElectionResult.by_hash(hashval)
        if v is None:
            abort(404)
        else:
            return jsonify(v.summarize())

    @app.route("/api1/debug/testkeys")
    def get_testkeys():
        if config.test_mode:
            from test_tmemberlist import makeTestMemberKeys

            testmembers = makeTestMemberKeys()

            return render_template("debug_testkeys.html",
                                   testmembers=zip(*testmembers))
        else:
            abort(404)

    @app.route("/api1/debug/shutdown")
    def debug_shutdown():
        """ Shutdown the web server. """
        if config.test_mode:
            os.kill(os.getppid(), signal.SIGTERM)
        else:
            abort(404)

    @app.route("/api1/render/<objtype:name>/<shex:hashval>")
    def _render(name, hashval):
        Cls = name2type[name]
        obj = Cls.by_hash(hashval)

        if obj is None:
            abort(404)

        if not obj.public():
            abort(401)  # FIXME: or simply 404?

        try:
            j = obj.toJ()
        except RuntimeError:
            abort(404)  # no JSON representation possible -> can't be rendered

        v = obj.extraRender()
        v.update({name: obj})

        return render_template("render_%s.html" % obj.__tablename__,
                               render_object_type=name,
                               render_object_id=hashval,
                               **v)

    @app.route("/api1/raw/<objtype:name>/<shex:hashval>")
    def _get_raw(name, hashval):
        Cls = name2type[name]
        obj = Cls.by_hash(hashval)

        if obj is None:
            abort(404)
        if not obj.public():
            abort(401)

        if isinstance(obj, butypes.RawFile):
            return Response(response=obj.data,
                            mimetype=obj.proposal_metadata.mime_type,
                            headers={
                                "Content-Disposition":
                                "attachment;filename=%s" %
                                obj.proposal_metadata.filename
                            })
        else:
            return Response(response=obj.x_json, mimetype="application/json")

    @app.route("/api1/form/<name>")
    @app.route("/api1/form/<name>/<shex:hashval>")
    def _form(name, hashval=None):
        if name not in [
                "generic",
                "proposal-upload",
                "proposal-publish",
                "open-proposal-vote",
                "close-proposal-vote",
                "cast-proposal-ballot",
                "cast-proposal-ballot-multiple",
                "cast-member-ballot-multiple",
                "propose-member",
        ]:
            abort(404)

        formopts = {}

        if name == "cast-proposal-ballot":
            pv = ProposalVote.by_hash(hashval)

            if pv is None:
                formopts = {"proposal": None}
            else:
                formopts = {"proposal": pv.proposal_metadata}
        elif name == "cast-member-ballot-multiple":
            formopts = {
                "member_applications":
                Global.current_member_list().applications()
            }

        return render_template("form_" + name + ".html",
                               cml=Global.current_member_list(),
                               hashval=hashval,
                               **formopts)

    @app.route("/api1/form/cast-member-ballot/<name>/<address>")
    def _form_cast_member_ballot(name, address):
        return render_template("form_cast-member-ballot.html",
                               cml=Global.current_member_list(),
                               member_name=name,
                               member_address=address)

    @app.route("/api1/form/close-member-elections")
    def _form_close_member_elections():
        cml = Global.current_member_list()
        ma_names = " ".join(ma.new_member.name for ma in cml.applications())
        return render_template("form_close-member-elections.html",
                               cml=cml,
                               ma_names=ma_names)

    @app.route("/api1/actions-by-member", methods=["GET"])
    def _actions_by_member_name():
        name = request.args.get('name')

        if name is None:
            abort(403, "Member name is missing.")

        if Member.by_name(name) is None:
            abort(404, "Member '%s' not found." % name)

        actions = queries.ActionByMemberNameAndType(name)
        return render_template("actions-by-member.html",
                               actions=actions,
                               membername=name)

    @app.route("/api1/proposal-ballots-by-member", methods=["GET"])
    def _proposal_ballots_by_member_name():
        name = request.args.get('name')

        if name is None:
            abort(403, "Member name is missing.")

        if Member.by_name(name) is None:
            abort(404, "Member '%s' not found." % name)

        ballots = queries.ActionByMemberNameAndType(name,
                                                    "cast-proposal-ballot")

        published = list(ProposalMetadata.all_public())

        # calculate set of published proposals that have and have not
        # yet been voted on by the selected member.
        pms_voted_on_by_member = [(ProposalVote.by_hash(
            ballot.parser.actvars["vote_hash"]).proposal_metadata)
                                  for ballot in ballots]

        pms_not_voted_on_by_member = [
            pm for pm in published if pm not in pms_voted_on_by_member
        ]

        return render_template(
            "proposal-ballots-by-member.html",
            ballots=ballots,
            membername=name,
            published=published,
            pms_voted_on_by_member=pms_voted_on_by_member,
            pms_not_voted_on_by_member=pms_not_voted_on_by_member,
            ProposalVote=ProposalVote)

    @app.route("/api1/action", methods=["POST"])
    def _action():
        try:
            with write_lock:
                author_name = request.form[
                    "author_name"] if "author_name" in request.form else None
                action_string = request.form[
                    "action_string"] if "action_string" in request.form else None
                signature = request.form[
                    "signature"] if "signature" in request.form else None

                upload = request.files[
                    "upload"] if "upload" in request.files else None

                log.debug("action: Extracted fields")
                log.debug("action: string: %s", action_string)
                if author_name is None:
                    abort(403, "Author name field is missing.")
                if action_string is None:
                    abort(403, "Action string is missing.")
                if signature is None:
                    abort(403, "Signature is missing.")

                author = Member.by_name(author_name)

                if author is None:
                    abort(403, "Author '%s' does not exist." % author_name)

                if upload is not None:
                    data = upload.read(config.max_upload + 1)
                    if len(data) > config.max_upload:
                        abort(413, "Upload too large.")

                    hash_ref = butype.sha256(data)
                    log.debug("Upload data hash: %s, len: %d", hash_ref,
                              len(data))

                    if RawFile.by_hash(hash_ref) is not None:
                        abort(409, "File exists.")

                else:
                    hash_ref = None
                    data = None
                log.debug("action: Handled upload")

                try:
                    action = butypes.Action(  #timestamp=None,
                        author=author,
                        action_string=action_string,
                        signature=signature)
                    returnval = action.apply(upload, data)
                    db.session.commit()
                except jvalidate.ValidationError as ve:
                    log.warn("Action: Problem: %d, %s", ve.status, str(ve))
                    if ve.error_page is not None:
                        return render_template(ve.error_page["template"],
                                               **ve.error_page), ve.status
                    else:
                        abort(ve.status, str(ve))

                except sqlalchemy.exc.IntegrityError as ie:  # pragma: no cover
                    # should not happen, but better be prepared
                    log.warn("SQLalchemy integrity error: %s", ie)
                    abort(403, "Database integrity error.")

                log.debug("action: Successful execution")
                return render_template("on_action_%s.html" % action.parser.act,
                                       action=action,
                                       returnval=returnval), 201
        finally:
            try:
                db.session.close()
                db.session.expire_all()
            except Exception as e:
                log.warn("Error (%s) while invalidating session after action.",
                         str(e))
                abort(500, "Problem closing DB session.")

    @app.route("/api1/multi-action", methods=["POST"])
    def _multi_action():
        try:
            with write_lock:
                author_name = request.form[
                    "author_name"] if "author_name" in request.form else None
                multi_action_string = request.form[
                    "action_string"] if "action_string" in request.form else None
                multi_action_string = multi_action_string.replace("\r", "")
                multi_signature = request.form[
                    "signature"] if "signature" in request.form else None

                log.debug("multi-action: Extracted fields")

                if author_name is None:
                    abort(403, "Author name field is missing.")
                if multi_action_string is None:
                    abort(403, "Multi-action string is missing.")
                if multi_signature is None:
                    abort(403, "Multi-signature is missing.")

                author = Member.by_name(author_name)

                if author is None:
                    abort(403, "Author '%s' does not exist." % author_name)

                try:
                    multi_action = butypes.MultiAction(
                        author=author,
                        multi_action_string=multi_action_string,
                        multi_signature=multi_signature)
                    returnvals = multi_action.apply()
                    db.session.commit()
                except jvalidate.ValidationError as ve:
                    log.warn("MultiAction: Problem: %d, %s", ve.status,
                             str(ve))
                    if ve.error_page is not None:
                        return render_template(ve.error_page["template"],
                                               **ve.error_page), ve.status
                    else:
                        abort(ve.status, str(ve))
                except sqlalchemy.exc.IntegrityError as ie:  # pragma: no cover
                    # should not happen, but better be prepared
                    log.warn("SQLalchemy integrity error: %s", ie)
                    abort(403, "Database integrity error.")

                log.debug("multi-action: Successful execution")
                return render_template("on_multi_action.html",
                                       multi_action=multi_action,
                                       returnvals=returnvals), 201
        finally:
            try:
                db.session.close()
                db.session.expire_all()
            except Exception as e:
                log.warn(
                    "Error (%s) while invalidating session after multi-action.",
                    str(e))
                abort(500, "Problem closing DB session.")

    @app.route("/api1/zip/<objtype:name>/<shex:hashval>")
    def _get_zip(name, hashval):
        import io
        import zipfile

        Cls = name2type[name]
        obj = Cls.by_hash(hashval)

        if obj is None:
            abort(404)
        if not obj.public():
            abort(401)

        output = io.BytesIO()

        with zipfile.ZipFile(file=output,
                             mode="w",
                             compression=zipfile.ZIP_DEFLATED) as zip:

            already = set()

            def writeObjAndDeps(obj, level):
                log.debug("Writing object: %d %s", level, obj)
                if obj.public() and obj not in already:
                    if obj.__tablename__ != RawFile.__tablename__:
                        zip.writestr(
                            os.path.join("buv-export", obj.__tablename__,
                                         obj.hashref() + ".json"),
                            obj.serialize())
                    else:
                        zip.writestr(
                            os.path.join("buv-export", obj.__tablename__,
                                         obj.hashref() + ".file"),
                            obj.serialize())

                    already.add(obj)
                    for dep in obj.dependencies():
                        writeObjAndDeps(dep, level + 1)

            writeObjAndDeps(obj, 0)

        mime_type = "application/zip"
        return Response(response=bytes(output.getbuffer()),
                        mimetype=mime_type,
                        headers={
                            "Content-Disposition":
                            "attachment;filename=%s" % obj.__tablename__ +
                            "-" + obj.hashref() + ".zip"
                        })

    # SQLAlchemy sessions are currently long lived - they persist for
    # the lifetime of the worker.  To make sure that all operations
    # happen on the current data set, force expiry of all session's
    # objects here.
    # TODO: Figure out how to properly configure per-request sessions.
    @app.before_request
    def expire_session():
        log.info("expiring session")
        #try:
        #    db.session.commit()
        #except sqlalchemy.exc.InvalidRequestError:
        #    # commit will fail if request was aborted - just ignore then
        #    log.warn("encountered invalid request while expiring session")
        #    pass

        db.session.expire_all()
        #db.session.close()

    return app, db
Example #5
0
    def __init__(
        self,
        environment,
        host,
        port,
        auth_credentials=None,
        tls_cert=None,
        tls_key=None,
        stats_csv_writer=None,
        delayed_start=False,
    ):
        """
        Create WebUI instance and start running the web server in a separate greenlet (self.greenlet)

        Arguments:
        environment: Reference to the current Locust Environment
        host: Host/interface that the web server should accept connections to
        port: Port that the web server should listen to
        auth_credentials:  If provided, it will enable basic auth with all the routes protected by default.
                           Should be supplied in the format: "user:pass".
        tls_cert: A path to a TLS certificate
        tls_key: A path to a TLS private key
        delayed_start: Whether or not to delay starting web UI until `start()` is called. Delaying web UI start
                       allows for adding Flask routes or Blueprints before accepting requests, avoiding errors.
        """
        environment.web_ui = self
        self.stats_csv_writer = stats_csv_writer or StatsCSV(
            environment, stats_module.PERCENTILES_TO_REPORT)
        self.environment = environment
        self.host = host
        self.port = port
        self.tls_cert = tls_cert
        self.tls_key = tls_key
        app = Flask(__name__)
        CORS(app)
        self.app = app
        app.jinja_env.add_extension("jinja2.ext.do")
        app.debug = True
        app.root_path = os.path.dirname(os.path.abspath(__file__))
        self.app.config["BASIC_AUTH_ENABLED"] = False
        self.auth = None
        self.greenlet = None
        self._swarm_greenlet = None

        if auth_credentials is not None:
            credentials = auth_credentials.split(":")
            if len(credentials) == 2:
                self.app.config["BASIC_AUTH_USERNAME"] = credentials[0]
                self.app.config["BASIC_AUTH_PASSWORD"] = credentials[1]
                self.app.config["BASIC_AUTH_ENABLED"] = True
                self.auth = BasicAuth()
                self.auth.init_app(self.app)
            else:
                raise AuthCredentialsError(
                    "Invalid auth_credentials. It should be a string in the following format: 'user.pass'"
                )
        if environment.runner:
            self.update_template_args()
        if not delayed_start:
            self.start()

        @app.route("/")
        @self.auth_required_if_enabled
        def index():
            if not environment.runner:
                return make_response(
                    "Error: Locust Environment does not have any runner", 500)
            self.update_template_args()
            return render_template("index.html", **self.template_args)

        @app.route("/swarm", methods=["POST"])
        @self.auth_required_if_enabled
        def swarm():
            assert request.method == "POST"

            parsed_options_dict = vars(environment.parsed_options
                                       ) if environment.parsed_options else {}
            for key, value in request.form.items():
                if key == "user_count":  # if we just renamed this field to "users" we wouldnt need this
                    user_count = int(value)
                elif key == "spawn_rate":
                    spawn_rate = float(value)
                elif key == "host":
                    # Replace < > to guard against XSS
                    environment.host = str(request.form["host"]).replace(
                        "<", "").replace(">", "")
                elif key in parsed_options_dict:
                    # cast to use same type that it already has in options. Wont work for None defaults
                    parsed_options_dict[key] = type(
                        parsed_options_dict[key])(value)

            if environment.shape_class:
                environment.runner.start_shape()
                return jsonify({
                    "success": True,
                    "message": "Swarming started using shape class",
                    "host": environment.host
                })

            if self._swarm_greenlet is not None:
                self._swarm_greenlet.kill(block=True)
                self._swarm_greenlet = None
            self._swarm_greenlet = gevent.spawn(environment.runner.start,
                                                user_count, spawn_rate)
            self._swarm_greenlet.link_exception(greenlet_exception_handler)
            return jsonify({
                "success": True,
                "message": "Swarming started",
                "host": environment.host
            })

        @app.route("/stop")
        @self.auth_required_if_enabled
        def stop():
            if self._swarm_greenlet is not None:
                self._swarm_greenlet.kill(block=True)
                self._swarm_greenlet = None
            environment.runner.stop()
            return jsonify({"success": True, "message": "Test stopped"})

        @app.route("/stats/reset")
        @self.auth_required_if_enabled
        def reset_stats():
            environment.events.reset_stats.fire()
            environment.runner.stats.reset_all()
            environment.runner.exceptions = {}
            return "ok"

        @app.route("/stats/report")
        @self.auth_required_if_enabled
        def stats_report():
            res = get_html_report(
                self.environment,
                show_download_link=not request.args.get("download"))
            if request.args.get("download"):
                res = app.make_response(res)
                res.headers[
                    "Content-Disposition"] = "attachment;filename=report_%s.html" % time(
                    )
            return res

        def _download_csv_suggest_file_name(suggest_filename_prefix):
            """Generate csv file download attachment filename suggestion.

            Arguments:
            suggest_filename_prefix: Prefix of the filename to suggest for saving the download. Will be appended with timestamp.
            """

            return f"{suggest_filename_prefix}_{time()}.csv"

        def _download_csv_response(csv_data, filename_prefix):
            """Generate csv file download response with 'csv_data'.

            Arguments:
            csv_data: CSV header and data rows.
            filename_prefix: Prefix of the filename to suggest for saving the download. Will be appended with timestamp.
            """

            response = make_response(csv_data)
            response.headers["Content-type"] = "text/csv"
            response.headers[
                "Content-disposition"] = f"attachment;filename={_download_csv_suggest_file_name(filename_prefix)}"
            return response

        @app.route("/stats/requests/csv")
        @self.auth_required_if_enabled
        def request_stats_csv():
            data = StringIO()
            writer = csv.writer(data)
            self.stats_csv_writer.requests_csv(writer)
            return _download_csv_response(data.getvalue(), "requests")

        @app.route("/stats/requests_full_history/csv")
        @self.auth_required_if_enabled
        def request_stats_full_history_csv():
            options = self.environment.parsed_options
            if options and options.stats_history_enabled:
                return send_file(
                    os.path.abspath(
                        self.stats_csv_writer.stats_history_file_name()),
                    mimetype="text/csv",
                    as_attachment=True,
                    download_name=_download_csv_suggest_file_name(
                        "requests_full_history"),
                    etag=True,
                    cache_timeout=None,
                    conditional=True,
                    last_modified=None,
                )

            return make_response(
                "Error: Server was not started with option to generate full history.",
                404)

        @app.route("/stats/failures/csv")
        @self.auth_required_if_enabled
        def failures_stats_csv():
            data = StringIO()
            writer = csv.writer(data)
            self.stats_csv_writer.failures_csv(writer)
            return _download_csv_response(data.getvalue(), "failures")

        @app.route("/stats/requests")
        @self.auth_required_if_enabled
        @memoize(timeout=DEFAULT_CACHE_TIME, dynamic_timeout=True)
        def request_stats():
            stats = []

            for s in chain(sort_stats(self.environment.runner.stats.entries),
                           [environment.runner.stats.total]):
                stats.append({
                    "method":
                    s.method,
                    "name":
                    s.name,
                    "safe_name":
                    escape(s.name, quote=False),
                    "num_requests":
                    s.num_requests,
                    "num_failures":
                    s.num_failures,
                    "avg_response_time":
                    s.avg_response_time,
                    "min_response_time":
                    0 if s.min_response_time is None else proper_round(
                        s.min_response_time),
                    "max_response_time":
                    proper_round(s.max_response_time),
                    "current_rps":
                    s.current_rps,
                    "current_fail_per_sec":
                    s.current_fail_per_sec,
                    "median_response_time":
                    s.median_response_time,
                    "ninetieth_response_time":
                    s.get_response_time_percentile(0.9),
                    "avg_content_length":
                    s.avg_content_length,
                })

            errors = []
            for e in environment.runner.errors.values():
                err_dict = e.to_dict()
                err_dict["name"] = escape(err_dict["name"])
                err_dict["error"] = escape(err_dict["error"])
                errors.append(err_dict)

            # Truncate the total number of stats and errors displayed since a large number of rows will cause the app
            # to render extremely slowly. Aggregate stats should be preserved.
            report = {"stats": stats[:500], "errors": errors[:500]}
            if len(stats) > 500:
                report["stats"] += [stats[-1]]

            if stats:
                report["total_rps"] = stats[len(stats) - 1]["current_rps"]
                report[
                    "fail_ratio"] = environment.runner.stats.total.fail_ratio
                report[
                    "current_response_time_percentile_95"] = environment.runner.stats.total.get_current_response_time_percentile(
                        0.95)
                report[
                    "current_response_time_percentile_50"] = environment.runner.stats.total.get_current_response_time_percentile(
                        0.5)

            is_distributed = isinstance(environment.runner, MasterRunner)
            if is_distributed:
                workers = []
                for worker in environment.runner.clients.values():
                    workers.append({
                        "id": worker.id,
                        "state": worker.state,
                        "user_count": worker.user_count,
                        "cpu_usage": worker.cpu_usage,
                        "memory_usage": worker.memory_usage,
                    })

                report["workers"] = workers

            report["state"] = environment.runner.state
            report["user_count"] = environment.runner.user_count

            return jsonify(report)

        @app.route("/exceptions")
        @self.auth_required_if_enabled
        def exceptions():
            return jsonify({
                "exceptions": [{
                    "count": row["count"],
                    "msg": row["msg"],
                    "traceback": row["traceback"],
                    "nodes": ", ".join(row["nodes"]),
                } for row in environment.runner.exceptions.values()]
            })

        @app.route("/exceptions/csv")
        @self.auth_required_if_enabled
        def exceptions_csv():
            data = StringIO()
            writer = csv.writer(data)
            self.stats_csv_writer.exceptions_csv(writer)
            return _download_csv_response(data.getvalue(), "exceptions")
Example #6
0
from flask import Flask, request, jsonify
from flask_restx import Resource, Api
from flask_basicauth import BasicAuth

app = Flask(__name__)
# กำหนด username กับ password
app.config['BASIC_AUTH_USERNAME'] = '******'
app.config['BASIC_AUTH_PASSWORD'] = '******'
api = Api(app)
basic_auth = BasicAuth(app)  # มาจาก class BasicAuth

tasks = [{'id': 1, 'name': 'Do homework'}]


# การเข้าถึง resource
class TodoList(Resource):
    @basic_auth.required  # decorator ถ้าจะใช้ function get() จะต้องผ่าน basic_auth ก่อน (ต้อง login ก่อน)
    def get(self):
        return jsonify({'tasks': tasks})

    @basic_auth.required  # decorator ถ้าจะใช้ function post() จะต้องผ่าน basic_auth ก่อน (ต้อง login ก่อน)
    def post(self):
        task = {
            'id': len(tasks) + 1,
            'name': api.payload['name'],
        }
        tasks.append(task)
        return api.payload, 201


api.add_resource(