parent
7a62b154fc
commit
01cca785f4
@ -0,0 +1,86 @@
|
||||
#pragma once
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "admissions_info.h"
|
||||
#include "http.h"
|
||||
#include "route_config.h"
|
||||
#include "module_database.h"
|
||||
#include "tcp_server.h"
|
||||
|
||||
#define HTTP_ROUTER_CAPACITY 32
|
||||
|
||||
/* Assumption: entrypoint is always _start. This should be enhanced later */
|
||||
struct route {
|
||||
char *route;
|
||||
struct module *module;
|
||||
/* HTTP State */
|
||||
uint32_t relative_deadline_us;
|
||||
uint64_t relative_deadline; /* cycles */
|
||||
size_t max_request_size;
|
||||
size_t max_response_size;
|
||||
char *response_content_type;
|
||||
struct admissions_info admissions_info;
|
||||
};
|
||||
|
||||
|
||||
struct http_router {
|
||||
struct route routes[HTTP_ROUTER_CAPACITY];
|
||||
size_t route_length;
|
||||
};
|
||||
|
||||
static inline void
|
||||
http_router_init(struct http_router *router)
|
||||
{
|
||||
router->route_length = 0;
|
||||
}
|
||||
|
||||
static inline int
|
||||
http_router_add_route(struct http_router *router, struct route_config *config, struct module *module)
|
||||
{
|
||||
assert(router != NULL);
|
||||
assert(config != NULL);
|
||||
assert(module != NULL);
|
||||
assert(config->route != NULL);
|
||||
assert(config->http_resp_content_type != NULL);
|
||||
|
||||
if (router->route_length < HTTP_ROUTER_CAPACITY) {
|
||||
router->routes[router->route_length] = (struct route){
|
||||
.route = config->route,
|
||||
.module = module,
|
||||
.relative_deadline_us = config->relative_deadline_us,
|
||||
.relative_deadline = (uint64_t)config->relative_deadline_us * runtime_processor_speed_MHz,
|
||||
.max_request_size = config->http_req_size,
|
||||
.max_response_size = config->http_resp_size,
|
||||
.response_content_type = config->http_resp_content_type
|
||||
};
|
||||
|
||||
/* Move strings from config */
|
||||
config->route = NULL;
|
||||
config->http_resp_content_type = NULL;
|
||||
|
||||
/* Admissions Control */
|
||||
uint64_t expected_execution = (uint64_t)config->expected_execution_us * runtime_processor_speed_MHz;
|
||||
admissions_info_initialize(&router->routes[router->route_length].admissions_info,
|
||||
config->admissions_percentile, expected_execution,
|
||||
router->routes[router->route_length].relative_deadline);
|
||||
|
||||
router->route_length++;
|
||||
return 0;
|
||||
}
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
static inline struct route *
|
||||
http_router_match_route(struct http_router *router, char *route)
|
||||
{
|
||||
for (int i = 0; i < router->route_length; i++) {
|
||||
if (strncmp(route, router->routes[i].route, strlen(router->routes[i].route)) == 0) {
|
||||
return &router->routes[i];
|
||||
}
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
@ -0,0 +1,62 @@
|
||||
#pragma once
|
||||
|
||||
#include <assert.h>
|
||||
#include <jsmn.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
#include "tenant_config_parse.h"
|
||||
|
||||
#define JSON_TOKENS_CAPACITY 16384
|
||||
|
||||
/**
|
||||
* Parses a JSON file into an array of tenant configs
|
||||
* @param file_name The path of the JSON file
|
||||
* @return tenant_config_vec_len on success. -1 on Error
|
||||
*/
|
||||
static inline int
|
||||
parse_json(const char *json_buf, ssize_t json_buf_size, struct tenant_config **tenant_config_vec)
|
||||
{
|
||||
assert(json_buf != NULL);
|
||||
assert(json_buf_size > 0);
|
||||
assert(tenant_config_vec != NULL);
|
||||
|
||||
jsmntok_t tokens[JSON_TOKENS_CAPACITY];
|
||||
int tenant_config_vec_len = 0;
|
||||
int i = 0;
|
||||
|
||||
/* Initialize the Jasmine Parser and an array to hold the tokens */
|
||||
jsmn_parser module_parser;
|
||||
jsmn_init(&module_parser);
|
||||
|
||||
/* Use Jasmine to parse the JSON */
|
||||
int total_tokens = jsmn_parse(&module_parser, json_buf, json_buf_size, tokens, JSON_TOKENS_CAPACITY);
|
||||
if (total_tokens < 0) {
|
||||
if (total_tokens == JSMN_ERROR_INVAL) {
|
||||
fprintf(stderr, "Error parsing %s: bad token, JSON string is corrupted\n", json_buf);
|
||||
} else if (total_tokens == JSMN_ERROR_PART) {
|
||||
fprintf(stderr, "Error parsing %s: JSON string is too short, expecting more JSON data\n",
|
||||
json_buf);
|
||||
} else if (total_tokens == JSMN_ERROR_NOMEM) {
|
||||
/*
|
||||
* According to the README at https://github.com/zserge/jsmn, this is a potentially recoverable
|
||||
* error. More tokens can be allocated and jsmn_parse can be re-invoked.
|
||||
*/
|
||||
fprintf(stderr, "Error parsing %s: Not enough tokens, JSON string is too large\n", json_buf);
|
||||
}
|
||||
goto err;
|
||||
}
|
||||
|
||||
i = tenant_config_vec_parse(tenant_config_vec, &tenant_config_vec_len, json_buf, tokens, i, total_tokens);
|
||||
|
||||
assert(i == total_tokens - 1);
|
||||
|
||||
done:
|
||||
return tenant_config_vec_len;
|
||||
json_parse_err:
|
||||
free(*tenant_config_vec);
|
||||
err:
|
||||
fprintf(stderr, "JSON:\n%s\n", json_buf);
|
||||
tenant_config_vec_len = -1;
|
||||
goto done;
|
||||
}
|
@ -1,40 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
struct module_config {
|
||||
char *name;
|
||||
char *path;
|
||||
char *route;
|
||||
uint16_t port;
|
||||
uint8_t admissions_percentile;
|
||||
uint32_t expected_execution_us;
|
||||
uint32_t relative_deadline_us;
|
||||
uint32_t http_req_size;
|
||||
uint32_t http_resp_size;
|
||||
char *http_resp_content_type;
|
||||
};
|
||||
|
||||
static inline void
|
||||
module_config_deinit(struct module_config *config)
|
||||
{
|
||||
free(config->name);
|
||||
free(config->path);
|
||||
free(config->http_resp_content_type);
|
||||
}
|
||||
|
||||
static inline void
|
||||
print_module_config(struct module_config *config)
|
||||
{
|
||||
printf("Name: %s\n", config->name);
|
||||
printf("Path: %s\n", config->path);
|
||||
printf("Port: %u\n", config->port);
|
||||
printf("admissions_percentile: %u\n", config->admissions_percentile);
|
||||
printf("expected_execution_us: %u\n", config->expected_execution_us);
|
||||
printf("relative_deadline_us: %u\n", config->relative_deadline_us);
|
||||
printf("http_req_size: %u\n", config->http_req_size);
|
||||
printf("http_resp_size: %u\n", config->http_resp_size);
|
||||
printf("http_resp_content_type: %s\n", config->http_resp_content_type);
|
||||
}
|
@ -0,0 +1,37 @@
|
||||
#pragma once
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
struct route_config {
|
||||
char *path;
|
||||
char *route;
|
||||
uint8_t admissions_percentile;
|
||||
uint32_t expected_execution_us;
|
||||
uint32_t relative_deadline_us;
|
||||
uint32_t http_req_size;
|
||||
uint32_t http_resp_size;
|
||||
char *http_resp_content_type;
|
||||
};
|
||||
|
||||
static inline void
|
||||
route_config_deinit(struct route_config *config)
|
||||
{
|
||||
free(config->path);
|
||||
free(config->route);
|
||||
free(config->http_resp_content_type);
|
||||
}
|
||||
|
||||
static inline void
|
||||
route_config_print(struct route_config *config)
|
||||
{
|
||||
printf("[Route] Route: %s\n", config->route);
|
||||
printf("[Route] Path: %s\n", config->path);
|
||||
printf("[Route] Admissions Percentile: %hhu\n", config->admissions_percentile);
|
||||
printf("[Route] Expected Execution (us): %u\n", config->expected_execution_us);
|
||||
printf("[Route] Relative Deadline (us): %u\n", config->relative_deadline_us);
|
||||
printf("[Route] HTTP Request Size: %u\n", config->http_req_size);
|
||||
printf("[Route] HTTP Response Size: %u\n", config->http_resp_size);
|
||||
printf("[Route] HTTP Response Content Type: %s\n", config->http_resp_content_type);
|
||||
}
|
@ -0,0 +1,106 @@
|
||||
#pragma once
|
||||
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "json.h"
|
||||
#include "route_config.h"
|
||||
|
||||
enum
|
||||
{
|
||||
route_config_json_key_http_path,
|
||||
route_config_json_key_module_path,
|
||||
route_config_json_key_admissions_percentile,
|
||||
route_config_json_key_expected_execution_us,
|
||||
route_config_json_key_relative_deadline_us,
|
||||
route_config_json_key_http_req_size,
|
||||
route_config_json_key_http_resp_size,
|
||||
route_config_json_key_http_resp_content_type,
|
||||
route_config_json_key_len
|
||||
};
|
||||
|
||||
static const char *route_config_json_keys[route_config_json_key_len] = {
|
||||
"route", "path", "admissions-percentile", "expected-execution-us", "relative-deadline-us",
|
||||
"http-req-size", "http-resp-size", "http-resp-content-type"
|
||||
};
|
||||
|
||||
static inline int
|
||||
route_config_parse(struct route_config *config, const char *json_buf, jsmntok_t *tokens, size_t tokens_base,
|
||||
int tokens_size)
|
||||
{
|
||||
int i = tokens_base;
|
||||
char key[32] = { 0 };
|
||||
|
||||
if (!has_valid_type(tokens[i], "anonymous object in array", JSMN_OBJECT, json_buf)) return -1;
|
||||
|
||||
int route_keys_len = tokens[i].size;
|
||||
|
||||
if (tokens[i].size == 0) {
|
||||
fprintf(stderr, "empty route object\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
for (int route_key_idx = 0; route_key_idx < route_keys_len; route_key_idx++) {
|
||||
i++;
|
||||
if (!is_valid_key(tokens[i])) return -1;
|
||||
sprintf(key, "%.*s", tokens[i].end - tokens[i].start, json_buf + tokens[i].start);
|
||||
|
||||
/* Advance to Value */
|
||||
i++;
|
||||
|
||||
if (strcmp(key, route_config_json_keys[route_config_json_key_http_path]) == 0) {
|
||||
if (!is_nonempty_string(tokens[i], key)) return -1;
|
||||
|
||||
config->route = strndup(json_buf + tokens[i].start, tokens[i].end - tokens[i].start);
|
||||
} else if (strcmp(key, route_config_json_keys[route_config_json_key_module_path]) == 0) {
|
||||
if (!is_nonempty_string(tokens[i], key)) return -1;
|
||||
|
||||
config->path = strndup(json_buf + tokens[i].start, tokens[i].end - tokens[i].start);
|
||||
} else if (strcmp(key, route_config_json_keys[route_config_json_key_admissions_percentile]) == 0) {
|
||||
if (!has_valid_type(tokens[i], key, JSMN_PRIMITIVE, json_buf)) return -1;
|
||||
|
||||
int rc = parse_uint8_t(tokens[i], json_buf,
|
||||
route_config_json_keys[route_config_json_key_admissions_percentile],
|
||||
&config->admissions_percentile);
|
||||
if (rc < 0) return -1;
|
||||
} else if (strcmp(key, route_config_json_keys[route_config_json_key_expected_execution_us]) == 0) {
|
||||
if (!has_valid_type(tokens[i], key, JSMN_PRIMITIVE, json_buf)) return -1;
|
||||
|
||||
int rc = parse_uint32_t(tokens[i], json_buf,
|
||||
route_config_json_keys[route_config_json_key_expected_execution_us],
|
||||
&config->expected_execution_us);
|
||||
if (rc < 0) return -1;
|
||||
} else if (strcmp(key, route_config_json_keys[route_config_json_key_relative_deadline_us]) == 0) {
|
||||
if (!has_valid_type(tokens[i], key, JSMN_PRIMITIVE, json_buf)) return -1;
|
||||
|
||||
int rc = parse_uint32_t(tokens[i], json_buf,
|
||||
route_config_json_keys[route_config_json_key_relative_deadline_us],
|
||||
&config->relative_deadline_us);
|
||||
if (rc < 0) return -1;
|
||||
} else if (strcmp(key, route_config_json_keys[route_config_json_key_http_req_size]) == 0) {
|
||||
if (!has_valid_type(tokens[i], key, JSMN_PRIMITIVE, json_buf)) return -1;
|
||||
|
||||
int rc = parse_uint32_t(tokens[i], json_buf,
|
||||
route_config_json_keys[route_config_json_key_http_req_size],
|
||||
&config->http_req_size);
|
||||
if (rc < 0) return -1;
|
||||
} else if (strcmp(key, route_config_json_keys[route_config_json_key_http_resp_size]) == 0) {
|
||||
if (!has_valid_type(tokens[i], key, JSMN_PRIMITIVE, json_buf)) return -1;
|
||||
|
||||
int rc = parse_uint32_t(tokens[i], json_buf,
|
||||
route_config_json_keys[route_config_json_key_http_resp_size],
|
||||
&config->http_resp_size);
|
||||
if (rc < 0) return -1;
|
||||
} else if (strcmp(key, route_config_json_keys[route_config_json_key_http_resp_content_type]) == 0) {
|
||||
if (!is_nonempty_string(tokens[i], key)) return -1;
|
||||
|
||||
config->http_resp_content_type = strndup(json_buf + tokens[i].start,
|
||||
tokens[i].end - tokens[i].start);
|
||||
} else {
|
||||
fprintf(stderr, "%s is not a valid key\n", key);
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
return i;
|
||||
}
|
@ -0,0 +1,9 @@
|
||||
#pragma once
|
||||
|
||||
enum SCHEDULER
|
||||
{
|
||||
SCHEDULER_FIFO = 0,
|
||||
SCHEDULER_EDF = 1
|
||||
};
|
||||
|
||||
extern enum SCHEDULER scheduler;
|
@ -0,0 +1,14 @@
|
||||
#pragma once
|
||||
|
||||
#include "http_router.h"
|
||||
#include "module.h"
|
||||
#include "tcp_server.h"
|
||||
|
||||
#define TENANT_DATABASE_CAPACITY 128
|
||||
|
||||
struct tenant {
|
||||
char *name;
|
||||
struct tcp_server tcp_server;
|
||||
struct http_router router;
|
||||
struct module_database module_db;
|
||||
};
|
@ -0,0 +1,34 @@
|
||||
#pragma once
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
#include "route_config.h"
|
||||
|
||||
struct tenant_config {
|
||||
char *name;
|
||||
uint16_t port;
|
||||
struct route_config *routes;
|
||||
size_t routes_len;
|
||||
};
|
||||
|
||||
static inline void
|
||||
tenant_config_deinit(struct tenant_config *config)
|
||||
{
|
||||
if (config->name != NULL) free(config->name);
|
||||
config->name = NULL;
|
||||
for (int i = 0; i < config->routes_len; i++) { route_config_deinit(&config->routes[i]); }
|
||||
free(config->routes);
|
||||
config->routes = NULL;
|
||||
config->routes_len = 0;
|
||||
}
|
||||
|
||||
static inline void
|
||||
tenant_config_print(struct tenant_config *config)
|
||||
{
|
||||
printf("[Tenant] Name: %s\n", config->name);
|
||||
printf("[Tenant] Path: %d\n", config->port);
|
||||
printf("[Tenant] Routes Size: %zu\n", config->routes_len);
|
||||
for (int i = 0; i < config->routes_len; i++) { route_config_print(&config->routes[i]); }
|
||||
}
|
@ -0,0 +1,117 @@
|
||||
#pragma once
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "json.h"
|
||||
#include "route_config_parse.h"
|
||||
#include "tenant_config.h"
|
||||
|
||||
enum
|
||||
{
|
||||
tenant_config_json_key_name,
|
||||
tenant_config_json_key_port,
|
||||
tenant_config_json_key_routes,
|
||||
tenant_config_json_key_len
|
||||
};
|
||||
|
||||
static const char *tenant_config_json_keys[tenant_config_json_key_len] = { "name", "port", "routes" };
|
||||
|
||||
|
||||
/* Tenant Config */
|
||||
|
||||
static inline int
|
||||
tenant_config_parse(struct tenant_config *config, const char *json_buf, jsmntok_t *tokens, size_t tokens_base,
|
||||
int tokens_size)
|
||||
{
|
||||
int i = tokens_base;
|
||||
char key[32] = { 0 };
|
||||
|
||||
if (!has_valid_type(tokens[i], "Anonymous Tenant Config Object", JSMN_OBJECT, json_buf)) return -1;
|
||||
if (!is_nonempty_object(tokens[i], "Anonymous Tenant Config Object")) return -1;
|
||||
|
||||
int tenant_key_count = tokens[i].size;
|
||||
|
||||
|
||||
for (int tenant_key_idx = 0; tenant_key_idx < tenant_key_count; tenant_key_idx++) {
|
||||
/* Advance to key */
|
||||
i++;
|
||||
|
||||
if (!is_valid_key(tokens[i])) return -1;
|
||||
if (!has_valid_size(tokens[i], key, 1)) return -1;
|
||||
|
||||
/* Copy Key */
|
||||
sprintf(key, "%.*s", tokens[i].end - tokens[i].start, json_buf + tokens[i].start);
|
||||
|
||||
/* Advance to Value */
|
||||
i++;
|
||||
|
||||
if (strcmp(key, tenant_config_json_keys[tenant_config_json_key_name]) == 0) {
|
||||
if (!is_nonempty_string(tokens[i], key)) return -1;
|
||||
|
||||
config->name = strndup(json_buf + tokens[i].start, tokens[i].end - tokens[i].start);
|
||||
} else if (strcmp(key, tenant_config_json_keys[tenant_config_json_key_port]) == 0) {
|
||||
if (!has_valid_type(tokens[i], key, JSMN_PRIMITIVE, json_buf)) return -1;
|
||||
|
||||
int rc = parse_uint16_t(tokens[i], json_buf,
|
||||
tenant_config_json_keys[tenant_config_json_key_port], &config->port);
|
||||
if (rc < 0) return -1;
|
||||
} else if (strcmp(key, tenant_config_json_keys[tenant_config_json_key_routes]) == 0) {
|
||||
if (!has_valid_type(tokens[i], key, JSMN_ARRAY, json_buf)) return -1;
|
||||
|
||||
int routes_len = tokens[i].size;
|
||||
config->routes_len = routes_len;
|
||||
config->routes = (struct route_config *)calloc(routes_len, sizeof(struct route_config));
|
||||
|
||||
for (int route_idx = 0; route_idx < routes_len; route_idx++) {
|
||||
/* Advance to object */
|
||||
i++;
|
||||
i = route_config_parse(&(config->routes)[route_idx], json_buf, tokens, i, tokens_size);
|
||||
if (i == -1) return -1;
|
||||
}
|
||||
|
||||
} else {
|
||||
fprintf(stderr, "%s is not a valid key\n", key);
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
return i;
|
||||
}
|
||||
|
||||
/* Tenant Config Vec */
|
||||
|
||||
static inline int
|
||||
tenant_config_vec_parse(struct tenant_config **tenant_config_vec, int *tenant_config_vec_len, const char *json_buf,
|
||||
jsmntok_t *tokens, size_t tokens_base, int tokens_size)
|
||||
{
|
||||
int i = tokens_base;
|
||||
|
||||
if (tokens[i].type != JSMN_ARRAY) {
|
||||
fprintf(stderr, "Outermost Config should be a JSON array, was a JSON %s\n", jsmn_type(tokens[0].type));
|
||||
return -1;
|
||||
}
|
||||
|
||||
*tenant_config_vec_len = tokens[i].size;
|
||||
if (tenant_config_vec_len == 0) {
|
||||
fprintf(stderr, "Config is an empty JSON array\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
*tenant_config_vec = (struct tenant_config *)calloc((size_t)(*tenant_config_vec_len),
|
||||
sizeof(struct tenant_config));
|
||||
if (*tenant_config_vec == NULL) {
|
||||
perror("Failed to allocate vec");
|
||||
return -1;
|
||||
}
|
||||
|
||||
|
||||
for (int tenant_idx = 0; tenant_idx < *tenant_config_vec_len; tenant_idx++) {
|
||||
i++;
|
||||
i = tenant_config_parse(&((*tenant_config_vec)[tenant_idx]), json_buf, tokens, i, tokens_size);
|
||||
if (i == -1) return -1;
|
||||
}
|
||||
|
||||
return i;
|
||||
}
|
@ -0,0 +1,102 @@
|
||||
#pragma once
|
||||
|
||||
#include <stdint.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "admissions_info.h"
|
||||
#include "http.h"
|
||||
#include "listener_thread.h"
|
||||
#include "panic.h"
|
||||
#include "scheduler_options.h"
|
||||
#include "tenant.h"
|
||||
#include "tenant_config.h"
|
||||
|
||||
int tenant_database_add(struct tenant *tenant);
|
||||
struct tenant *tenant_database_find_by_name(char *name);
|
||||
struct tenant *tenant_database_find_by_socket_descriptor(int socket_descriptor);
|
||||
struct tenant *tenant_database_find_by_port(uint16_t port);
|
||||
|
||||
static inline struct tenant *
|
||||
tenant_alloc(struct tenant_config *config)
|
||||
{
|
||||
/* Validate config */
|
||||
if (strlen(config->name) == 0) panic("name field is required\n");
|
||||
if (config->port == 0) panic("port field is required\n");
|
||||
if (config->routes_len == 0) panic("one or more routesa are required\n");
|
||||
|
||||
struct tenant *existing_tenant = tenant_database_find_by_name(config->name);
|
||||
if (existing_tenant != NULL) panic("Tenant %s is already initialized\n", existing_tenant->name);
|
||||
|
||||
existing_tenant = tenant_database_find_by_port(config->port);
|
||||
if (existing_tenant != NULL)
|
||||
panic("Tenant %s is already configured with port %u\n", existing_tenant->name, config->port);
|
||||
|
||||
for (int i = 0; i < config->routes_len; i++) {
|
||||
struct route_config *route_config = &config->routes[i];
|
||||
if (route_config->path == 0) panic("path field is required\n");
|
||||
if (route_config->route == 0) panic("route field is required\n");
|
||||
|
||||
|
||||
if (route_config->http_req_size > RUNTIME_HTTP_REQUEST_SIZE_MAX)
|
||||
panic("request_size must be between 0 and %u, was %u\n",
|
||||
(uint32_t)RUNTIME_HTTP_REQUEST_SIZE_MAX, route_config->http_req_size);
|
||||
|
||||
if (route_config->http_resp_size > RUNTIME_HTTP_RESPONSE_SIZE_MAX)
|
||||
panic("response-size must be between 0 and %u, was %u\n",
|
||||
(uint32_t)RUNTIME_HTTP_RESPONSE_SIZE_MAX, route_config->http_resp_size);
|
||||
|
||||
if (route_config->relative_deadline_us > (uint32_t)RUNTIME_RELATIVE_DEADLINE_US_MAX)
|
||||
panic("Relative-deadline-us must be between 0 and %u, was %u\n",
|
||||
(uint32_t)RUNTIME_RELATIVE_DEADLINE_US_MAX, route_config->relative_deadline_us);
|
||||
|
||||
#ifdef ADMISSIONS_CONTROL
|
||||
/* expected-execution-us and relative-deadline-us are required in case of admissions control */
|
||||
if (route_config->expected_execution_us == 0) panic("expected-execution-us is required\n");
|
||||
if (route_config->relative_deadline_us == 0) panic("relative_deadline_us is required\n");
|
||||
|
||||
if (route_config->admissions_percentile > 99 || route_config->admissions_percentile < 50)
|
||||
panic("admissions-percentile must be > 50 and <= 99 but was %u\n",
|
||||
route_config->admissions_percentile);
|
||||
|
||||
/* If the ratio is too big, admissions control is too coarse */
|
||||
uint32_t ratio = route_config->relative_deadline_us / route_config->expected_execution_us;
|
||||
if (ratio > ADMISSIONS_CONTROL_GRANULARITY)
|
||||
panic("Ratio of Deadline to Execution time cannot exceed admissions control "
|
||||
"granularity of "
|
||||
"%d\n",
|
||||
ADMISSIONS_CONTROL_GRANULARITY);
|
||||
#else
|
||||
/* relative-deadline-us is required if scheduler is EDF */
|
||||
if (scheduler == SCHEDULER_EDF && route_config->relative_deadline_us == 0)
|
||||
panic("relative_deadline_us is required\n");
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
struct tenant *tenant = (struct tenant *)calloc(1, sizeof(struct tenant));
|
||||
|
||||
/* Move name */
|
||||
tenant->name = config->name;
|
||||
config->name = NULL;
|
||||
|
||||
tcp_server_init(&tenant->tcp_server, config->port);
|
||||
http_router_init(&tenant->router);
|
||||
module_database_init(&tenant->module_db);
|
||||
|
||||
for (int i = 0; i < config->routes_len; i++) {
|
||||
/* Resolve module */
|
||||
struct module *module = module_database_find_by_path(&tenant->module_db, config->routes[i].path);
|
||||
assert(module != NULL);
|
||||
http_router_add_route(&tenant->router, &config->routes[i], module);
|
||||
}
|
||||
|
||||
return tenant;
|
||||
}
|
||||
|
||||
/**
|
||||
* Start the tenant as a server listening at tenant->port
|
||||
* @param tenant
|
||||
* @returns 0 on success, -1 on error
|
||||
*/
|
||||
int tenant_listen(struct tenant *tenant);
|
||||
int listener_thread_register_tenant(struct tenant *tenant);
|
@ -0,0 +1,28 @@
|
||||
#include "tenant.h"
|
||||
#include "tenant_functions.h"
|
||||
|
||||
/**
|
||||
* Start the tenant as a server listening at tenant->port
|
||||
* @param tenant
|
||||
* @returns 0 on success, -1 on error
|
||||
*/
|
||||
int
|
||||
tenant_listen(struct tenant *tenant)
|
||||
{
|
||||
int rc = tcp_server_listen(&tenant->tcp_server);
|
||||
if (rc < 0) goto err;
|
||||
|
||||
/* Set the socket descriptor and register with our global epoll instance to monitor for incoming HTTP requests
|
||||
*/
|
||||
|
||||
rc = listener_thread_register_tenant(tenant);
|
||||
if (unlikely(rc < 0)) goto err_add_to_epoll;
|
||||
|
||||
done:
|
||||
return rc;
|
||||
err_add_to_epoll:
|
||||
tcp_server_close(&tenant->tcp_server);
|
||||
err:
|
||||
rc = -1;
|
||||
goto done;
|
||||
}
|
@ -0,0 +1,81 @@
|
||||
#include <errno.h>
|
||||
|
||||
#include "tenant.h"
|
||||
#include "panic.h"
|
||||
|
||||
/*******************
|
||||
* Tenant Database *
|
||||
******************/
|
||||
|
||||
struct tenant *tenant_database[TENANT_DATABASE_CAPACITY] = { NULL };
|
||||
size_t tenant_database_count = 0;
|
||||
|
||||
/**
|
||||
* Adds a tenant to the in-memory tenant DB
|
||||
* @param tenant tenant to add
|
||||
* @return 0 on success. -ENOSPC when full
|
||||
*/
|
||||
int
|
||||
tenant_database_add(struct tenant *tenant)
|
||||
{
|
||||
assert(tenant_database_count <= TENANT_DATABASE_CAPACITY);
|
||||
|
||||
int rc;
|
||||
|
||||
if (tenant_database_count == TENANT_DATABASE_CAPACITY) goto err_no_space;
|
||||
tenant_database[tenant_database_count++] = tenant;
|
||||
|
||||
rc = 0;
|
||||
done:
|
||||
return rc;
|
||||
err_no_space:
|
||||
panic("Cannot add tenant. Database is full.\n");
|
||||
rc = -ENOSPC;
|
||||
goto done;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Given a name, find the associated tenant
|
||||
* @param name
|
||||
* @return tenant or NULL if no match found
|
||||
*/
|
||||
struct tenant *
|
||||
tenant_database_find_by_name(char *name)
|
||||
{
|
||||
for (size_t i = 0; i < tenant_database_count; i++) {
|
||||
assert(tenant_database[i]);
|
||||
if (strcmp(tenant_database[i]->name, name) == 0) return tenant_database[i];
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/**
|
||||
* Given a socket_descriptor, find the associated tenant
|
||||
* @param socket_descriptor
|
||||
* @return tenant or NULL if no match found
|
||||
*/
|
||||
struct tenant *
|
||||
tenant_database_find_by_socket_descriptor(int socket_descriptor)
|
||||
{
|
||||
for (size_t i = 0; i < tenant_database_count; i++) {
|
||||
assert(tenant_database[i]);
|
||||
if (tenant_database[i]->tcp_server.socket_descriptor == socket_descriptor) return tenant_database[i];
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/**
|
||||
* Given a port, find the associated tenant
|
||||
* @param port
|
||||
* @return tenant or NULL if no match found
|
||||
*/
|
||||
struct tenant *
|
||||
tenant_database_find_by_port(uint16_t port)
|
||||
{
|
||||
for (size_t i = 0; i < tenant_database_count; i++) {
|
||||
assert(tenant_database[i]);
|
||||
if (tenant_database[i]->tcp_server.port == port) return tenant_database[i];
|
||||
}
|
||||
return NULL;
|
||||
}
|
@ -1,26 +1,44 @@
|
||||
[
|
||||
{
|
||||
"name": "fibonacci",
|
||||
"path": "fibonacci.wasm.so",
|
||||
"name": "gwu",
|
||||
"port": 10010,
|
||||
"route": "/fib",
|
||||
"expected-execution-us": 6000,
|
||||
"admissions-percentile": 70,
|
||||
"relative-deadline-us": 20000,
|
||||
"http-req-size": 1024,
|
||||
"http-resp-size": 1024,
|
||||
"http-resp-content-type": "text/plain"
|
||||
"routes": [
|
||||
{
|
||||
"route": "/fib",
|
||||
"path": "fibonacci.wasm.so",
|
||||
"admissions-percentile": 70,
|
||||
"expected-execution-us": 6000,
|
||||
"relative-deadline-us": 20000,
|
||||
"http-req-size": 1024,
|
||||
"http-resp-size": 1024,
|
||||
"http-resp-content-type": "text/plain"
|
||||
},
|
||||
{
|
||||
"route": "/fib2",
|
||||
"path": "fibonacci.wasm.so",
|
||||
"expected-execution-us": 10000000,
|
||||
"admissions-percentile": 70,
|
||||
"relative-deadline-us": 20000000,
|
||||
"http-req-size": 1024,
|
||||
"http-resp-size": 1024,
|
||||
"http-resp-content-type": "text/plain"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "fibonacci_40",
|
||||
"path": "fibonacci.wasm.so",
|
||||
"port": 10040,
|
||||
"route": "/fib",
|
||||
"expected-execution-us": 10000000,
|
||||
"admissions-percentile": 70,
|
||||
"relative-deadline-us": 20000000,
|
||||
"http-req-size": 1024,
|
||||
"http-resp-size": 1024,
|
||||
"http-resp-content-type": "text/plain"
|
||||
"name": "conix",
|
||||
"port": 10020,
|
||||
"routes": [
|
||||
{
|
||||
"route": "/fib",
|
||||
"path": "fibonacci.wasm.so",
|
||||
"admissions-percentile": 70,
|
||||
"expected-execution-us": 6000,
|
||||
"relative-deadline-us": 20000,
|
||||
"http-req-size": 1024,
|
||||
"http-resp-size": 1024,
|
||||
"http-resp-content-type": "text/plain"
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
|
Loading…
Reference in new issue