diff --git a/meson.build b/meson.build index 16eae66..345de5f 100644 --- a/meson.build +++ b/meson.build @@ -72,6 +72,7 @@ pycsh_sources = [ 'src/wrapper/spaceboot_py.c', 'src/wrapper/param_list_py.c', 'src/wrapper/vmem_client_py.c', + 'src/wrapper/victoria_metrics_py.c', # Utilities 'src/utils.c', diff --git a/pycsh.pyi b/pycsh.pyi index ae70f1e..ee868fc 100644 --- a/pycsh.pyi +++ b/pycsh.pyi @@ -690,6 +690,19 @@ def sps(from: int, to: int, filename: str, node: int = None, window: int = None, :raises ProgramDiffError: See class docstring. """ +def vm_start(logfile: int = 0, username: str = None, password: str = None, pyargs: dict = None): + """Starts victoria metrics thread + + Args: + logfile (int, optional): _description_. Defaults to 0. + username (str, optional): Username for vm. Defaults to None. + password (str, optional): Password for vm. Defaults to None. + pyargs (dict, optional): _description_. Defaults to None. + """ + +def vm_stop(): + """Stops victoria metrics thread""" + def csp_init(host: str = None, model: str = None, revision: str = None, version: int = 2, dedup: int = 3) -> None: """ Initialize CSP diff --git a/src/csh/victoria_metrics.c b/src/csh/victoria_metrics.c index a72c7a1..87c1964 100644 --- a/src/csh/victoria_metrics.c +++ b/src/csh/victoria_metrics.c @@ -20,28 +20,15 @@ #include #include #include "param_sniffer.h" +#include "victoria_metrics.h" static pthread_t vm_push_thread; int vm_running = 0; -#define SERVER_PORT 8428 -#define SERVER_PORT_AUTH 8427 -#define BUFFER_SIZE 10 * 1024 * 1024 - static char buffer[BUFFER_SIZE]; static size_t buffer_size = 0; static pthread_mutex_t buffer_mutex = PTHREAD_MUTEX_INITIALIZER; -typedef struct { - int use_ssl; - int port; - int skip_verify; - int verbose; - char * username; - char * password; - char * server_ip; -} vm_args; - static size_t write_callback(char *ptr, size_t size, size_t nmemb, void *userdata) { return size * nmemb; } diff --git a/src/csh/victoria_metrics.h b/src/csh/victoria_metrics.h index 9a33098..b8f0630 100644 --- a/src/csh/victoria_metrics.h +++ b/src/csh/victoria_metrics.h @@ -10,3 +10,19 @@ void vm_add(char * metric_line); void vm_add_param(param_t * param); + +#define SERVER_PORT 8428 +#define SERVER_PORT_AUTH 8427 +#define BUFFER_SIZE 10 * 1024 * 1024 + +typedef struct { + int use_ssl; + int port; + int skip_verify; + int verbose; + char * username; + char * password; + char * server_ip; +} vm_args; + +void * vm_push(void * arg); \ No newline at end of file diff --git a/src/pycsh.c b/src/pycsh.c index 055f1e8..5b00a66 100644 --- a/src/pycsh.c +++ b/src/pycsh.c @@ -68,6 +68,7 @@ #include "wrapper/csp_init_py.h" #include "wrapper/param_list_py.h" #include "wrapper/vmem_client_py.h" +#include "wrapper/victoria_metrics_py.h" extern const char *version_string; @@ -249,6 +250,10 @@ static PyMethodDef methods[] = { {"node", pycsh_slash_node, METH_VARARGS, "Used to get or change the default node."}, {"timeout", pycsh_slash_timeout, METH_VARARGS, "Used to get or change the default timeout."}, {"queue", pycsh_param_cmd, METH_NOARGS, "Print the current command."}, + + /* Victoria Metrics */ + {"vm_start", (PyCFunction)pycsh_vm_start, METH_VARARGS | METH_KEYWORDS, "Starts the victoria metrics thread."}, + {"vm_stop", (PyCFunction)pycsh_vm_stop, METH_VARARGS | METH_KEYWORDS, "Stops the victoria metrics thread."}, /* Converted CSH commands from libparam/src/param/list/param_list_slash.c */ {"list", (PyCFunction)pycsh_param_list, METH_VARARGS | METH_KEYWORDS, "List all known parameters."}, diff --git a/src/utils.c b/src/utils.c index c0cca76..19120b7 100644 --- a/src/utils.c +++ b/src/utils.c @@ -18,6 +18,7 @@ #include "parameter/parameterarray.h" #include "parameter/pythonparameter.h" #include "parameter/parameterlist.h" +#include "../src/csh/victoria_metrics.h" /* __attribute__(()) doesn't like to treat char** and void** interchangeably. */ diff --git a/src/wrapper/victoria_metrics_py.c b/src/wrapper/victoria_metrics_py.c new file mode 100644 index 0000000..0f1283c --- /dev/null +++ b/src/wrapper/victoria_metrics_py.c @@ -0,0 +1,166 @@ +/* + * param_list_py.c + * + * Wrappers for lib/param/src/param/list/param_list_slash.c + * + */ + +#define PY_SSIZE_T_CLEAN +#include + +#include +#include +#include +#include "../pycsh.h" +#include "../utils.h" +#include "../parameter/parameter.h" +#include "../parameter/pythonparameter.h" +#include "../src/csh/victoria_metrics.h" + +#include "victoria_metrics_py.h" + +static pthread_t vm_push_thread; +extern int vm_running; + +static int pycsh_parse_vm_args(PyObject * args_in, vm_args * args_out) { + assert(args_in != NULL); + + // Get all the values from the python dict. Everything is a string when it comes from python. + PyObject * py_use_ssl = PyDict_GetItemString(args_in, "use_ssl"); + PyObject * py_port = PyDict_GetItemString(args_in, "port"); + PyObject * py_skip_verify = PyDict_GetItemString(args_in, "skip_verify"); + PyObject * py_verbose = PyDict_GetItemString(args_in, "verbose"); + PyObject * py_server_ip = PyDict_GetItemString(args_in, "server_ip"); + + // Check if long characters are larger than INT_MAX since we actually want them to be ints and not longs. + // These values are not required, so if the input is NULL we initialize them to 0. + long use_ssl; + if(py_use_ssl == NULL){ + use_ssl = 0; + } else if (PyLong_Check(py_use_ssl)){ + use_ssl = PyLong_AsLong(py_use_ssl); + if(use_ssl > INT_MAX){ + PyErr_SetString(PyExc_TypeError, "use_ssl value greater than INT_MAX"); + return -1; + } + } else { + PyErr_SetString(PyExc_TypeError, "TypeError."); + return -1; + } + + long port; + if(py_port == NULL){ + port = 0; + } else if (PyLong_Check(py_port)){ + port = PyLong_AsLong(py_port); + if(port > INT_MAX){ + PyErr_SetString(PyExc_TypeError, "port value greater than INT_MAX"); + return -1; + } + } else { + PyErr_SetString(PyExc_TypeError, "TypeError."); + return -1; + } + + long skip_verify; + if(py_skip_verify == NULL){ + skip_verify = 0; + } else if (PyLong_Check(py_skip_verify)){ + skip_verify = PyLong_AsLong(py_skip_verify); + if(skip_verify > INT_MAX){ + PyErr_SetString(PyExc_TypeError, "skip_verify value greater than INT_MAX"); + return -1; + } + } else { + PyErr_SetString(PyExc_TypeError, "TypeError."); + return -1; + } + + long verbose; + if(py_verbose == NULL){ + verbose = 0; + } else if (PyLong_Check(py_verbose)){ + verbose = PyLong_AsLong(py_verbose); + if(verbose > INT_MAX){ + PyErr_SetString(PyExc_TypeError, "verbose value greater than INT_MAX"); + return -1; + } + } else { + PyErr_SetString(PyExc_TypeError, "TypeError."); + return -1; + } + + // Server_ip is required, so we make sure it exists. + if(!py_server_ip){ + PyErr_SetString(PyExc_KeyError, "server_ip field missing."); + return -1; + } + if(!PyUnicode_Check(py_server_ip)){ + PyErr_SetString(PyExc_TypeError, "TypeError in field server_ip."); + return -1; + } + char * server_ip = strdup(PyUnicode_AsUTF8(py_server_ip)); + + // Set values in the vm_args struct. + args_out->use_ssl = (int)use_ssl; + args_out->port = (int)port; + args_out->skip_verify = (int)skip_verify; + args_out->verbose = (int)verbose; + + args_out->server_ip = server_ip; + + return 0; + +} + +PyObject * pycsh_vm_start(PyObject * self, PyObject * args, PyObject * kwds) { + if(vm_running){ + printf("Thread already running.\n"); + return Py_None; + } + + int logfile = 0; + char * tmp_username = NULL; + char * tmp_password = NULL; + PyObject * pyargs = NULL; + + static char *kwlist[] = {"logfile", "username", "password", "args", NULL}; + + if (!PyArg_ParseTupleAndKeywords(args, kwds, "|issO", kwlist, &logfile, &tmp_username, &tmp_password, &pyargs)) { + return NULL; + } + + vm_args * vmargs = calloc(1, sizeof(vm_args)); + + if(pycsh_parse_vm_args(pyargs, vmargs) < 0){ + PyErr_SetString(PyExc_ConnectionError, "Invalid arguments"); + return NULL; + } + + if (tmp_username) { + if (!tmp_password) { + PyErr_SetString(PyExc_ConnectionError, "Provide password"); + return NULL; + } + vmargs->username = strdup(tmp_username); + vmargs->password = strdup(tmp_password); + if (!vmargs->port) { + vmargs->port = SERVER_PORT_AUTH; + } + } else if (!vmargs->port) { + vmargs->port = SERVER_PORT; + } + + printf("\n%d\n", vmargs->port); + pthread_create(&vm_push_thread, NULL, &vm_push, vmargs); + vm_running = 1; + + return Py_None; + +} + +PyObject * pycsh_vm_stop(PyObject * self, PyObject * args, PyObject * kwds) { + if(!vm_running) return Py_None; + vm_running = 0; + return Py_None; +} diff --git a/src/wrapper/victoria_metrics_py.h b/src/wrapper/victoria_metrics_py.h new file mode 100644 index 0000000..369b129 --- /dev/null +++ b/src/wrapper/victoria_metrics_py.h @@ -0,0 +1,15 @@ +/* + * vm_start_py.c + * + * Wrappers for lib/param/src/param/list/param_list_slash.c + * + */ + +#pragma once + +#define PY_SSIZE_T_CLEAN +#include + +PyObject * pycsh_vm_start(PyObject * self, PyObject * args, PyObject * kwds); + +PyObject * pycsh_vm_stop(PyObject * self, PyObject * args, PyObject * kwds); \ No newline at end of file