aboutsummaryrefslogtreecommitdiffstats
path: root/api/__init__.py
blob: 3235022326e7002e3d4e320424287a79b641bcf4 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
##############################################################################
# Copyright (c) 2016 Huawei Technologies Co.,Ltd and others.
#
# All rights reserved. This program and the accompanying materials
# are made available under the terms of the Apache License, Version 2.0
# which accompanies this distribution, and is available at
# http://www.apache.org/licenses/LICENSE-2.0
##############################################################################
from __future__ import absolute_import
import logging

from flask import request
from flask_restful import Resource

from yardstick import _init_logging
from yardstick.common import constants as consts
from yardstick.common import utils as common_utils

_init_logging()
LOG = logging.getLogger(__name__)
LOG.setLevel(logging.DEBUG)


class ApiResource(Resource):

    def _post_args(self):
        data = request.json if request.json else {}
        params = common_utils.translate_to_str(data)
        action = params.get('action', request.form.get('action', ''))
        args = params.get('args', {})

        try:
            args['file'] = request.files['file']
        except KeyError:
            pass

        args.update({k: v for k, v in request.form.items()})

        return action, args

    def _get_args(self):
        .highlight .hll { background-color: #ffffcc }
.highlight .c { color: #888888 } /* Comment */
.highlight .err { color: #a61717; background-color: #e3d2d2 } /* Error */
.highlight .k { color: #008800; font-weight: bold } /* Keyword */
.highlight .ch { color: #888888 } /* Comment.Hashbang */
.highlight .cm { color: #888888 } /* Comment.Multiline */
.highlight .cp { color: #cc0000; font-weight: bold } /* Comment.Preproc */
.highlight .cpf { color: #888888 } /* Comment.PreprocFile */
.highlight .c1 { color: #888888 } /* Comment.Single */
.highlight .cs { color: #cc0000; font-weight: bold; background-color: #fff0f0 } /* Comment.Special */
.highlight .gd { color: #000000; background-color: #ffdddd } /* Generic.Deleted */
.highlight .ge { font-style: italic } /* Generic.Emph */
.highlight .gr { color: #aa0000 } /* Generic.Error */
.highlight .gh { color: #333333 } /* Generic.Heading */
.highlight .gi { color: #000000; background-color: #ddffdd } /* Generic.Inserted */
.highlight .go { color: #888888 } /* Generic.Output */
.highlight .gp { color: #555555 } /* Generic.Prompt */
.highlight .gs { font-weight: bold } /* Generic.Strong */
.highlight .gu { color: #666666 } /* Generic.Subheading */
.highlight .gt { color: #aa0000 } /* Generic.Traceback */
.highlight .kc { color: #008800; font-weight: bold } /* Keyword.Constant */
.highlight .kd { color: #008800; font-weight: bold } /* Keyword.Declaration */
.highlight .kn { color: #008800; font-weight: bold } /* Keyword.Namespace */
.highlight .kp { color: #008800 } /* Keyword.Pseudo */
.highlight .kr { color: #008800; font-weight: bold } /* Keyword.Reserved */
.highlight .kt { color: #888888; font-weight: bold } /* Keyword.Type */
.highlight .m { color: #0000DD; font-weight: bold } /* Literal.Number */
.highlight .s { color: #dd2200; background-color: #fff0f0 } /* Literal.String */
.highlight .na { color: #336699 } /* Name.Attribute */
.highlight .nb { color: #003388 } /* Name.Builtin */
.highlight .nc { color: #bb0066; font-weight: bold } /* Name.Class */
.highlight .no { color: #003366; font-weight: bold } /* Name.Constant */
.highlight .nd { color: #555555 } /* Name.Decorator */
.highlight .ne { color: #bb0066; font-weight: bold } /* Name.Exception */
.highlight .nf { color: #0066bb; font-weight: bold } /* Name.Function */
.highlight .nl { color: #336699; font-style: italic } /* Name.Label */
.highlight .nn { color: #bb0066; font-weight: bold } /* Name.Namespace */
.highlight .py { color: #336699; font-weight: bold } /* Name.Property */
.highlight .nt { color: #bb0066; font-weight: bold } /* Name.Tag */
.highlight .nv { color: #336699 } /* Name.Variable */
.highlight .ow { color: #008800 } /* Operator.Word */
.highlight .w { color: #bbbbbb } /* Text.Whitespace */
.highlight .mb { color: #0000DD; font-weight: bold } /* Literal.Number.Bin */
.highlight .mf { color: #0000DD; font-weight: bold } /* Literal.Number.Float */
.highlight .mh { color: #0000DD; font-weight: bold } /* Literal.Number.Hex */
.highlight .mi { color: #0000DD; font-weight: bold } /* Literal.Number.Integer */
.highlight .mo { color: #0000DD; font-weight: bold } /* Literal.Number.Oct */
.highlight .sa { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Affix */
.highlight .sb { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Backtick *
ghlight .vi { color: #3333bb } /* Name.Variable.Instance */ .highlight .vm { color: #336699 } /* Name.Variable.Magic */ .highlight .il { color: #0000DD; font-weight: bold } /* Literal.Number.Integer.Long */
#include "qemu/osdep.h"
#include <glib.h>
#include <windows.h>
#include <io.h>
#include "qga/guest-agent-core.h"
#include "qga/channel.h"

typedef struct GAChannelReadState {
    guint thread_id;
    uint8_t *buf;
    size_t buf_size;
    size_t cur; /* current buffer start */
    size_t pending; /* pending buffered bytes to read */
    OVERLAPPED ov;
    bool ov_pending; /* whether on async read is outstanding */
} GAChannelReadState;

struct GAChannel {
    HANDLE handle;
    GAChannelCallback cb;
    gpointer user_data;
    GAChannelReadState rstate;
    GIOCondition pending_events; /* TODO: use GAWatch.pollfd.revents */
    GSource *source;
};

typedef struct GAWatch {
    GSource source;
    GPollFD pollfd;
    GAChannel *channel;
    GIOCondition events_mask;
} GAWatch;

/*
 * Called by glib prior to polling to set up poll events if polling is needed.
 *
 */
static gboolean ga_channel_prepare(GSource *source, gint *timeout_ms)
{
    GAWatch *watch = (GAWatch *)source;
    GAChannel *c = (GAChannel *)watch->channel;
    GAChannelReadState *rs = &c->rstate;
    DWORD count_read, count_to_read = 0;
    bool success;
    GIOCondition new_events = 0;

    g_debug("prepare");
    /* go ahead and submit another read if there's room in the buffer
     * and no previous reads are outstanding
     */
    if (!rs->ov_pending) {
        if (rs->cur + rs->pending >= rs->buf_size) {
            if (rs->cur) {
                memmove(rs->buf, rs->buf + rs->cur, rs->pending);
                rs->cur = 0;
            }
        }
        count_to_read = rs->buf_size - rs->cur - rs->pending;
    }

    if (rs->ov_pending || count_to_read <= 0) {
            goto out;
    }

    /* submit the read */
    success = ReadFile(c->handle, rs->buf + rs->cur + rs->pending,
                       count_to_read, &count_read, &rs->ov);
    if (success) {
        rs->pending += count_read;
        rs->ov_pending = false;
    } else {
        if (GetLastError() == ERROR_IO_PENDING) {
            rs->ov_pending = true;
        } else {
            new_events |= G_IO_ERR;
        }
    }

out:
    /* dont block forever, iterate the main loop every once and a while */
    *timeout_ms = 500;
    /* if there's data in the read buffer, or another event is pending,
     * skip polling and issue user cb.
     */
    if (rs->pending) {
        new_events |= G_IO_IN;
    }
    c->pending_events |= new_events;
    return !!c->pending_events;
}

/*
 * Called by glib after an outstanding read request is completed.
 */
static gboolean ga_channel_check(GSource *source)
{
    GAWatch *watch = (GAWatch *)source;
    GAChannel *c = (GAChannel *)watch->channel;
    GAChannelReadState *rs = &c->rstate;
    DWORD count_read, error;
    BOOL success;

    GIOCondition new_events = 0;

    g_debug("check");

    /* failing this implies we issued a read that completed immediately,
     * yet no data was placed into the buffer (and thus we did not skip
     * polling). but since EOF is not obtainable until we retrieve an
     * overlapped result, it must be the case that there was data placed
     * into the buffer, or an error was generated by Readfile(). in either
     * case, we should've skipped the polling for this round.
     */
    g_assert(rs->ov_pending);

    success = GetOverlappedResult(c->handle, &rs->ov, &count_read, FALSE);
    if (success) {
        g_debug("thread: overlapped result, count_read: %d", (int)count_read);
        rs->pending += count_read;
        new_events |= G_IO_IN;
    } else {
        error = GetLastError();
        if (error == 0 || error == ERROR_HANDLE_EOF ||
            error == ERROR_NO_SYSTEM_RESOURCES ||
            error == ERROR_OPERATION_ABORTED) {
            /* note: On WinXP SP3 with rhel6ga virtio-win-1.1.16 vioser drivers,
             * ENSR seems to be synonymous with when we'd normally expect
             * ERROR_HANDLE_EOF. So treat it as such. Microsoft's
             * recommendation for ERROR_NO_SYSTEM_RESOURCES is to
             * retry the read, so this happens to work out anyway. On newer
             * virtio-win driver, this seems to be replaced with EOA, so
             * handle that in the same fashion.
             */
            new_events |= G_IO_HUP;
        } else if (error != ERROR_IO_INCOMPLETE) {
            g_critical("error retrieving overlapped result: %d", (int)error);
            new_events |= G_IO_ERR;
        }
    }

    if (new_events) {
        rs->ov_pending = 0;
    }
    c->pending_events |= new_events;

    return !!c->pending_events;
}

/*
 * Called by glib after either prepare or check routines signal readiness
 */
static gboolean ga_channel_dispatch(GSource *source, GSourceFunc unused,
                                    gpointer user_data)
{
    GAWatch *watch = (GAWatch *)source;
    GAChannel *c = (GAChannel *)watch->channel;
    GAChannelReadState *rs = &c->rstate;
    gboolean success;

    g_debug("dispatch");
    success = c->cb(watch->pollfd.revents, c->user_data);

    if (c->pending_events & G_IO_ERR) {
        g_critical("channel error, removing source");
        return false;
    }

    /* TODO: replace rs->pending with watch->revents */
    c->pending_events &= ~G_IO_HUP;
    if (!rs->pending) {
        c->pending_events &= ~G_IO_IN;
    } else {
        c->pending_events = 0;
    }
    return success;
}

static void ga_channel_finalize(GSource *source)
{
    g_debug("finalize");
}

GSourceFuncs ga_channel_watch_funcs = {
    ga_channel_prepare,
    ga_channel_check,
    ga_channel_dispatch,
    ga_channel_finalize
};

static GSource *ga_channel_create_watch(GAChannel *c)
{
    GSource *source = g_source_new(&ga_channel_watch_funcs, sizeof(GAWatch));
    GAWatch *watch = (GAWatch *)source;

    watch->channel = c;
    watch->pollfd.fd = (gintptr) c->rstate.ov.hEvent;
    g_source_add_poll(source, &watch->pollfd);

    return source;
}

GIOStatus ga_channel_read(GAChannel *c, char *buf, size_t size, gsize *count)
{
    GAChannelReadState *rs = &c->rstate;
    GIOStatus status;
    size_t to_read = 0;

    if (c->pending_events & G_IO_ERR) {
        return G_IO_STATUS_ERROR;
    }

    *count = to_read = MIN(size, rs->pending);
    if (to_read) {
        memcpy(buf, rs->buf + rs->cur, to_read);
        rs->cur += to_read;
        rs->pending -= to_read;
        status = G_IO_STATUS_NORMAL;
    } else {
        status = G_IO_STATUS_AGAIN;
    }

    return status;
}

static GIOStatus ga_channel_write(GAChannel *c, const char *buf, size_t size,
                                  size_t *count)
{
    GIOStatus status;
    OVERLAPPED ov = {0};
    BOOL ret;
    DWORD written;

    ov.hEvent = CreateEvent(NULL, FALSE, FALSE, NULL);
    ret = WriteFile(c->handle, buf, size, &written, &ov);
    if (!ret) {
        if (GetLastError() == ERROR_IO_PENDING) {
            /* write is pending */
            ret = GetOverlappedResult(c->handle, &ov, &written, TRUE);
            if (!ret) {
                if (!GetLastError()) {
                    status = G_IO_STATUS_AGAIN;
                } else {
                    status = G_IO_STATUS_ERROR;
                }
            } else {
                /* write is complete */
                status = G_IO_STATUS_NORMAL;
                *count = written;
            }
        } else {
            status = G_IO_STATUS_ERROR;
        }
    } else {
        /* write returned immediately */
        status = G_IO_STATUS_NORMAL;
        *count = written;
    }

    if (ov.hEvent) {
        CloseHandle(ov.hEvent);
        ov.hEvent = NULL;
    }
    return status;
}

GIOStatus ga_channel_write_all(GAChannel *c, const char *buf, size_t size)
{
    GIOStatus status = G_IO_STATUS_NORMAL;
    size_t count = 0;

    while (size) {
        status = ga_channel_write(c, buf, size, &count);
        if (status == G_IO_STATUS_NORMAL) {
            size -= count;
            buf += count;
        } else if (status != G_IO_STATUS_AGAIN) {
            break;
        }
    }

    return status;
}

static gboolean ga_channel_open(GAChannel *c, GAChannelMethod method,
                                const gchar *path)
{
    COMMTIMEOUTS comTimeOut = {0};
    gchar newpath[MAXPATHLEN] = {0};
    comTimeOut.ReadIntervalTimeout = 1;

    if (method != GA_CHANNEL_VIRTIO_SERIAL && method != GA_CHANNEL_ISA_SERIAL) {
        g_critical("unsupported communication method");
        return false;
    }

    if (method == GA_CHANNEL_ISA_SERIAL){
        snprintf(newpath, sizeof(newpath), "\\\\.\\%s", path);
    }else {
        g_strlcpy(newpath, path, sizeof(newpath));
    }

    c->handle = CreateFile(newpath, GENERIC_READ | GENERIC_WRITE, 0, NULL,
                           OPEN_EXISTING,
                           FILE_FLAG_NO_BUFFERING | FILE_FLAG_OVERLAPPED, NULL);
    if (c->handle == INVALID_HANDLE_VALUE) {
        g_critical("error opening path %s", newpath);
        return false;
    }

    if (method == GA_CHANNEL_ISA_SERIAL && !SetCommTimeouts(c->handle,&comTimeOut)) {
        g_critical("error setting timeout for com port: %lu",GetLastError());
        CloseHandle(c->handle);
        return false;
    }

    return true;
}

GAChannel *ga_channel_new(GAChannelMethod method, const gchar *path,
                          GAChannelCallback cb, gpointer opaque)
{
    GAChannel *c = g_new0(GAChannel, 1);
    SECURITY_ATTRIBUTES sec_attrs;

    if (!ga_channel_open(c, method, path)) {
        g_critical("error opening channel");
        g_free(c);
        return NULL;
    }

    c->cb = cb;
    c->user_data = opaque;

    sec_attrs.nLength = sizeof(SECURITY_ATTRIBUTES);
    sec_attrs.lpSecurityDescriptor = NULL;
    sec_attrs.bInheritHandle = false;

    c->rstate.buf_size = QGA_READ_COUNT_DEFAULT;
    c->rstate.buf = g_malloc(QGA_READ_COUNT_DEFAULT);
    c->rstate.ov.hEvent = CreateEvent(&sec_attrs, FALSE, FALSE, NULL);

    c->source = ga_channel_create_watch(c);
    g_source_attach(c->source, NULL);
    return c;
}

void ga_channel_free(GAChannel *c)
{
    if (c->source) {
        g_source_destroy(c->source);
    }
    if (c->rstate.ov.hEvent) {
        CloseHandle(c->rstate.ov.hEvent);
    }
    g_free(c->rstate.buf);
    g_free(c);
}