support limit

This commit is contained in:
Rongsong Shen 2026-01-12 15:20:51 +08:00
parent bef32ace38
commit ecf1916d78
33 changed files with 3036 additions and 243 deletions

379
src/lib/group.c Normal file
View file

@ -0,0 +1,379 @@
#include <errno.h>
#include <stdio.h>
#include <stdlib.h>
#include "group.h"
#include "kvmgr.h"
#include "objmgr.h"
#include "utils.h"
#include "xce/types.h"
static void group_killer(xce_group_t* g)
{
int i;
if (g == NULL) {
return;
}
for (i = 0; i < g->members.count; i++) {
xce_g_member_t* m = (xce_g_member_t*)xlist_get(&(g->members), i);
if (!m->is_group) {
FREEUP(m->u.name);
}
FREEUP(m);
}
for (i = 0; i < g->ex_members.count; i++) {
xce_g_member_t* m = (xce_g_member_t*)xlist_get(&(g->ex_members), i);
if (!m->is_group) {
FREEUP(m->u.name);
}
FREEUP(m);
}
for (i = 0; i < g->affecteds.count; i++) {
xce_group_t* g1 = (xce_group_t*)xlist_get(&(g->affecteds), i);
xlist_del(&(g1->members), g);
xlist_del(&(g1->ex_members), g);
}
xlist_fini(&(g->members));
xlist_fini(&(g->ex_members));
xlist_fini(&(g->affecteds));
FREEUP(g->name);
FREEUP(g);
}
int groupmgr_init(groupmgr_t* mgr, int nslots, int delta)
{
int rc = 0;
xce_group_config_t all = { .name = "all" };
xce_group_config_t empty = { .name = "empty" };
xce_group_t* g = NULL;
memset(mgr, 0x0, sizeof(*mgr));
rc = kvmgr_init(&(mgr->groups), string_hash_key, nslots, delta,
(zarray_item_killer_t)group_killer);
if (rc < 0) {
return rc;
}
/* create builtin all group */
g = build_group(mgr, &all, NULL);
if (g == NULL) {
return -ENOMEM;
}
/* create builtin empty group */
g = build_group(mgr, &empty, NULL);
if (g == NULL) {
return -ENOMEM;
}
return 1;
}
void groupmgr_fini(groupmgr_t* mgr) { kvmgr_fini(&(mgr->groups)); }
int groupmgr_add(groupmgr_t* mgr, xce_group_t* g)
{
return kvmgr_add(&(mgr->groups), (void*)g->name, g);
}
xce_group_t* groupmgr_lock(groupmgr_t* mgr, char* name, int op)
{
return (xce_group_t*)kvmgr_lock(&(mgr->groups), (void*)name, op);
}
void groupmgr_del(groupmgr_t* mgr, xce_group_t* g)
{
kvmgr_del(&(mgr->groups), (void*)g->name);
}
int groupmgr_iter(groupmgr_t* mgr, xlist_t* l, void* arg)
{
return kvmgr_iter(&(mgr->groups), NULL, l, arg);
}
xce_group_t* get_group(groupmgr_t* mgr, char* name)
{
xce_group_t* g = NULL;
if ((mgr == NULL) || (name == NULL)) {
return NULL;
}
g = groupmgr_lock(mgr, name, OBJREF_NOP);
return g;
}
xce_g_member_t* make_g_member(groupmgr_t* mgr, char* name)
{
xce_g_member_t* m = NULL;
xce_group_t* g = NULL;
if ((mgr == NULL) || (name == NULL)) {
errno = EINVAL;
return NULL;
}
m = (xce_g_member_t*)calloc(1, sizeof(*m));
if (m == NULL) {
return NULL;
}
g = get_group(mgr, name);
if (g != NULL) {
m->is_group = 1;
m->u.g = g;
} else {
m->is_group = 0;
m->u.name = strdup(name);
}
return m;
}
static int update_group_dirty(xce_group_t* g)
{
int i;
int count = 0;
for (i = 0; i < g->affecteds.count; i++) {
xce_group_t* g1 = (xce_group_t*)xlist_get(&(g->affecteds), i);
if (g1 == NULL) {
continue;
}
g1->dirty = 1;
count++;
count += update_group_dirty(g1);
}
return count;
}
int fmt_group(char* buf, int len, xce_group_t* g)
{
int i, pos;
if (strcmp(g->name, "all") == 0) {
return snprintf(buf, len, "all");
}
if (strcmp(g->name, "empty") == 0) {
return snprintf(buf, len, "[]");
}
snprintf(buf, len, "[");
pos = strlen(buf);
for (i = 0; i < g->members.count; i++) {
xce_g_member_t* m = (xce_g_member_t*)xlist_get(&(g->members), i);
if (m == NULL) {
continue;
}
snprintf(buf + pos, len - pos, "%s ",
(m->is_group) ? m->u.g->name : m->u.name);
pos = strlen(buf);
}
for (i = 0; i < g->ex_members.count; i++) {
xce_g_member_t* m = (xce_g_member_t*)xlist_get(&(g->ex_members), i);
if (m == NULL) {
continue;
}
snprintf(buf + pos, len - pos, "~%s ",
(m->is_group) ? m->u.g->name : m->u.name);
pos = strlen(buf);
}
while ((pos > 0) && (buf[pos - 1] == ' ')) {
pos--;
}
snprintf(buf + pos, len - pos, "]");
return strlen(buf);
}
xce_group_t* build_group(groupmgr_t* mgr, xce_group_config_t* g_config,
char* default_group_name)
{
xce_group_t* g = NULL;
char* g_name;
int i;
if ((mgr == NULL) || (g_config == NULL)) {
errno = EINVAL;
return NULL;
}
g_name = g_config->name;
if ((g_name == NULL) || (strlen(g_name) == 0)) {
g_name = default_group_name;
}
if (g_name == NULL) {
return NULL;
}
g = get_group(mgr, g_name);
if (g != NULL) {
for (i = 0; i < g->members.count; i++) {
xce_g_member_t* m = (xce_g_member_t*)xlist_get(&(g->members), i);
if (m == NULL) {
continue;
}
if (m->is_group == 0) {
FREEUP(m->u.name);
}
FREEUP(m);
}
g->members.count = 0;
for (i = 0; i < g->ex_members.count; i++) {
xce_g_member_t* m = (xce_g_member_t*)xlist_get(&(g->ex_members), i);
if (m == NULL) {
continue;
}
if (m->is_group == 0) {
FREEUP(m->u.name);
}
FREEUP(m);
}
g->ex_members.count = 0;
g->dirty = 1;
} else {
g = (xce_group_t*)calloc(1, sizeof(*g));
if (g == NULL) {
return NULL;
}
xlist_init(&(g->affecteds), 4);
xlist_init(&(g->members), 4);
xlist_init(&(g->ex_members), 4);
g->name = strdup(g_name);
g->dirty = 0;
}
for (i = 0; i < g_config->members.count; i++) {
char* g_name = g_config->members.data[i];
xce_g_member_t* m = make_g_member(mgr, g_name);
if (m == NULL) {
continue;
}
xlist_store(&(g->members), m);
if (m->is_group) {
xlist_store(&(m->u.g->affecteds), g);
}
}
for (i = 0; i < g_config->ex_members.count; i++) {
char* g_name = g_config->ex_members.data[i];
xce_g_member_t* m = make_g_member(mgr, g_name);
if (m == NULL) {
continue;
}
xlist_store(&(g->ex_members), m);
if (m->is_group) {
xlist_store(&(m->u.g->affecteds), g);
}
}
groupmgr_add(mgr, g);
if (g->dirty) {
update_group_dirty(g);
}
return g;
}
static int check_in_member(xce_g_member_t* m, va_list ap)
{
char* name = va_arg(ap, char*);
if ((m == NULL) || (name == NULL)) {
return 0;
}
if (m->is_group) {
if (is_in_group(m->u.g, name)) {
return 1;
}
return 0;
}
if (strcmp(m->u.name, name) == 0) {
return 1;
}
return 0;
}
int is_in_group(xce_group_t* g, char* name)
{
if ((g == NULL) || (name == NULL)) {
return -EINVAL;
}
/* everything should be in all group */
if (strcmp(g->name, "all") == 0) {
return 1;
}
/* nothing is in empty group */
if (strcmp(g->name, "empty") == 0) {
return 0;
}
if (strcmp(g->name, name) == 0) {
return 1;
}
if (xlist_find(&(g->ex_members), (xlist_find_fn_t)check_in_member, name)
!= NULL) {
return 0;
}
if (xlist_find(&(g->members), (xlist_find_fn_t)check_in_member, name)
!= NULL) {
return 1;
}
return 0;
}
int is_group_all(xce_group_t* g)
{
if (g == NULL) {
return 0;
}
if (g->name == NULL) {
return 0;
}
return (strcmp(g->name, "all") == 0);
}

View file

@ -392,3 +392,4 @@ int jobmgr_iter(jobmgr_t* mgr, xlist_t* l, void* arg)
{
return kvmgr_iter(&(mgr->jobs), NULL, l, arg);
}

View file

@ -33,6 +33,8 @@ libxce_src = [ gen_srcs[0],
'preason.c',
'xcealloc.c',
'misc.c',
'group.c',
'xcelimit.c',
]
incdir = include_directories('../include')
@ -71,6 +73,9 @@ libxce_test_src = ['testsuite/test.c',
'testsuite/test_ordq.c',
'testsuite/test_preason.c',
'testsuite/test_kvmgr.c',
'testsuite/test_group.c',
'testsuite/test_xlist.c',
'testsuite/test_limit.c',
]
libxce_test_deps = [libxce_deps, unity_dep, libxce_dep]

View file

@ -94,10 +94,15 @@ static int add_res_preason(xce_job_preason_t* reasons, int code, char* host,
int count;
xce_res_reason_t* p1;
if ((reasons == NULL) || (host == NULL) || (res == NULL)) {
if ((reasons == NULL) || (res == NULL)) {
return -EINVAL;
}
/* FIXME: global resource reason */
if (host == NULL) {
host = "";
}
h = strdup(host);
r = strdup(res);
p1 = (xce_res_reason_t*)calloc(1, sizeof(*p1));

View file

@ -1,3 +1,4 @@
#include <argp.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
@ -16,26 +17,94 @@ extern void test_preason(void);
extern void test_ordq(void);
extern void test_delay(void);
extern void test_kvmgr(void);
extern void test_group(void);
extern void test_xlist(void);
extern void test_limit(void);
void setUp(void) { return; }
void tearDown(void) { return; }
static char doc[] = "xcelib_test";
static char args_doc[] = "";
#define MAX_TESTS (256)
struct arguments {
int ntests;
char* tests[MAX_TESTS];
};
static struct argp_option options[] = {
{ "run", 't', "STR", 0, "run test" },
{ 0 }
};
static error_t parse_opt(int key, char* arg, struct argp_state* state)
{
struct arguments* arguments = state->input;
switch (key) {
case 't':
if (arguments->ntests >= MAX_TESTS) {
fprintf(stderr, "Too many tests\n");
exit(-1);
}
arguments->tests[arguments->ntests] = arg;
arguments->ntests++;
break;
default:
return ARGP_ERR_UNKNOWN;
}
return 0;
}
static struct argp argp = { options, parse_opt, args_doc, doc };
struct arguments arguments;
static int check_test(struct arguments* args, char* x)
{
int i;
if (args->ntests == 0) {
return 1;
}
for (i = 0; i < args->ntests; i++) {
if (strcmp(x, args->tests[i]) == 0) {
return 1;
}
}
return 0;
}
#define TEST(x) \
do { \
if (check_test(&arguments, #x)) { \
x(); \
} \
} while (0)
int main(int argc, char* argv[])
{
argp_parse(&argp, argc, argv, 0, 0, &arguments);
UNITY_BEGIN();
test_config();
test_idgen();
test_ht_string();
test_ht_int();
test_zarray();
test_xstrings();
test_error();
test_utils();
test_preason();
test_ordq();
test_delay();
test_kvmgr();
TEST(test_config);
TEST(test_idgen);
TEST(test_ht_string);
TEST(test_ht_int);
TEST(test_zarray);
TEST(test_xstrings);
TEST(test_error);
TEST(test_utils);
TEST(test_preason);
TEST(test_ordq);
TEST(test_delay);
TEST(test_kvmgr);
TEST(test_group);
TEST(test_xlist);
TEST(test_limit);
return UNITY_END();
}

View file

@ -0,0 +1,271 @@
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unity.h>
#include "group.h"
#include "utils.h"
static void test_all(void)
{
groupmgr_t mgr;
xce_group_t* all = NULL;
int rc = 0;
groupmgr_init(&mgr, 16, 16);
all = get_group(&mgr, "all");
TEST_ASSERT_TRUE(all != NULL);
rc = is_in_group(all, "rshen");
TEST_ASSERT_TRUE(rc > 0);
groupmgr_fini(&mgr);
}
static void test_empty(void)
{
groupmgr_t mgr;
xce_group_t* empty = NULL;
int rc = 0;
groupmgr_init(&mgr, 16, 16);
empty = get_group(&mgr, "empty");
TEST_ASSERT_TRUE(empty != NULL);
rc = is_in_group(empty, "rshen");
TEST_ASSERT_TRUE(rc == 0);
groupmgr_fini(&mgr);
}
static void test_all_1(void)
{
groupmgr_t mgr;
xce_group_config_t gdata = {
.name = "g1",
.members = { .count = 1, .data = (char*[]) { "all" } }
};
xce_group_t* g = NULL;
int rc = 0;
groupmgr_init(&mgr, 16, 16);
g = build_group(&mgr, &gdata, NULL);
TEST_ASSERT_TRUE(g != NULL);
rc = is_in_group(g, "rshen");
TEST_ASSERT_TRUE(rc > 0);
groupmgr_fini(&mgr);
}
static void test_all_2(void)
{
groupmgr_t mgr;
xce_group_config_t gdata = {
.name = "g1",
.members = { .count = 2, .data = (char*[]) { "g2", "all" } }
};
xce_group_t* g = NULL;
int rc = 0;
groupmgr_init(&mgr, 16, 16);
g = build_group(&mgr, &gdata, NULL);
TEST_ASSERT_TRUE(g != NULL);
rc = is_in_group(g, "rshen");
TEST_ASSERT_TRUE(rc > 0);
groupmgr_fini(&mgr);
}
static void test_empty_1(void)
{
groupmgr_t mgr;
xce_group_config_t gdata = {
.name = "g1",
.ex_members = { .count = 1, .data = (char*[]) { "all" } }
};
xce_group_t* g = NULL;
int rc = 0;
groupmgr_init(&mgr, 16, 16);
g = build_group(&mgr, &gdata, NULL);
TEST_ASSERT_TRUE(g != NULL);
rc = is_in_group(g, "rshen");
TEST_ASSERT_TRUE(rc == 0);
groupmgr_fini(&mgr);
}
static void test_empty_2(void)
{
groupmgr_t mgr;
xce_group_config_t gdata = {
.name = "g1",
.members = { .count = 1, .data = (char*[]) { "empty" } }
};
xce_group_t* g = NULL;
int rc = 0;
groupmgr_init(&mgr, 16, 16);
g = build_group(&mgr, &gdata, NULL);
TEST_ASSERT_TRUE(g != NULL);
rc = is_in_group(g, "rshen");
TEST_ASSERT_TRUE(rc == 0);
groupmgr_fini(&mgr);
}
static void test_include(void)
{
groupmgr_t mgr;
xce_group_config_t gdata = {
.name = "g1",
.members = { .count = 1, .data = (char*[]) { "rshen" } }
};
xce_group_t* g = NULL;
int rc = 0;
groupmgr_init(&mgr, 16, 16);
g = build_group(&mgr, &gdata, NULL);
TEST_ASSERT_TRUE(g != NULL);
rc = is_in_group(g, "rshen");
TEST_ASSERT_TRUE(rc > 0);
groupmgr_fini(&mgr);
}
static void test_exclude(void)
{
groupmgr_t mgr;
xce_group_config_t gdata = {
.name = "g1",
.members = { .count = 1, .data = (char*[]) { "all" } },
.ex_members = { .count = 1, .data = (char*[]) { "rshen" } }
};
xce_group_t* g = NULL;
int rc = 0;
groupmgr_init(&mgr, 16, 16);
g = build_group(&mgr, &gdata, NULL);
TEST_ASSERT_TRUE(g != NULL);
rc = is_in_group(g, "rshen");
TEST_ASSERT_TRUE(rc == 0);
groupmgr_fini(&mgr);
}
static void test_nest(void)
{
groupmgr_t mgr;
xce_group_config_t gdata1 = {
.name = "g1",
.members = { .count = 1, .data = (char*[]) { "rshen" } }
};
xce_group_config_t gdata2 = {
.name = "g2",
.members = { .count = 1, .data = (char*[]) { "g1" } }
};
xce_group_t* g = NULL;
int rc = 0;
groupmgr_init(&mgr, 16, 16);
g = build_group(&mgr, &gdata1, NULL);
TEST_ASSERT_TRUE(g != NULL);
g = build_group(&mgr, &gdata2, NULL);
TEST_ASSERT_TRUE(g != NULL);
rc = is_in_group(g, "rshen");
TEST_ASSERT_TRUE(rc != 0);
groupmgr_fini(&mgr);
}
static void test_nest2(void)
{
groupmgr_t mgr;
xce_group_config_t gdata1 = {
.name = "g1",
.members = { .count = 1, .data = (char*[]) { "rshen" } }
};
xce_group_config_t gdata2 = {
.name = "g2",
.members = { .count = 1, .data = (char*[]) { "all" } },
.ex_members = { .count = 1, .data = (char*[]) { "g1" } }
};
xce_group_t* g = NULL;
int rc = 0;
groupmgr_init(&mgr, 16, 16);
g = build_group(&mgr, &gdata1, NULL);
TEST_ASSERT_TRUE(g != NULL);
g = build_group(&mgr, &gdata2, NULL);
TEST_ASSERT_TRUE(g != NULL);
rc = is_in_group(g, "rshen");
TEST_ASSERT_TRUE(rc == 0);
groupmgr_fini(&mgr);
}
void test_group(void)
{
printf("\n>> group test\n\n");
RUN_TEST(test_all);
RUN_TEST(test_empty);
RUN_TEST(test_all_1);
RUN_TEST(test_all_2);
RUN_TEST(test_empty_1);
RUN_TEST(test_empty_2);
RUN_TEST(test_include);
RUN_TEST(test_exclude);
RUN_TEST(test_nest);
RUN_TEST(test_nest2);
}

View file

@ -0,0 +1,324 @@
#include <stdarg.h>
#include <stdio.h>
#include <stdlib.h>
#include <unity.h>
#include "group.h"
#include "xce/types.h"
#include "xcelimit.h"
static void test_build_consumer1(void)
{
xce_consumer_t* c = NULL;
groupmgr_t mgr;
xce_group_config_t gdata = {
.members = { .count = 2, .data = (char*[]) { "rshen", "jsxing" } }
};
int rc = 0;
char buf[1024];
groupmgr_init(&mgr, 4, 4);
rc = build_consumer(&c, &mgr, CONSUMER_USER, 1, &gdata, "user_group1");
TEST_ASSERT_TRUE(rc == 1);
rc = fmt_consumer(buf, sizeof(buf) - 1, c);
TEST_ASSERT_TRUE(rc > 0);
printf("\n >>> consumer: %s\n", buf);
kill_consumer(c);
groupmgr_fini(&mgr);
return;
}
static void test_build_consumer2(void)
{
xce_consumer_t* c = NULL;
groupmgr_t mgr;
xce_group_config_t gdata = {
.members = { .count = 1, .data = (char*[]) { "all" } },
.ex_members = { .count = 2, .data = (char*[]) { "rshen", "jsxing" } }
};
int rc = 0;
char buf[1024];
groupmgr_init(&mgr, 4, 4);
rc = build_consumer(&c, &mgr, CONSUMER_USER, 1, &gdata, "user_group1");
TEST_ASSERT_TRUE(rc == 1);
rc = fmt_consumer(buf, sizeof(buf) - 1, c);
TEST_ASSERT_TRUE(rc > 0);
printf("\n >>> consumer: %s\n", buf);
kill_consumer(c);
groupmgr_fini(&mgr);
return;
}
static void test_build_limit_ok1(void)
{
groupmgr_t umgr, qmgr, hmgr, pmgr;
xce_limit_t* limit;
int rc;
xce_limit_config_t limit_config = {
.name = "limit1",
.per_user = 0,
.per_queue = 0,
.per_project = 0,
.per_host = 0,
.users
= &(xce_group_config_t) { .members = { .count = 1,
.data = (char*[]) { "u1" } } },
.queues = NULL,
.hosts = NULL,
.projects = NULL,
.val
= &(xce_limit_val_t) { .resname = "mem", .percent = 1, .val = 80.0 }
};
char buf[1024];
groupmgr_init(&umgr, 4, 4);
groupmgr_init(&qmgr, 4, 4);
groupmgr_init(&hmgr, 4, 4);
groupmgr_init(&pmgr, 4, 4);
rc = build_limit(&limit, &umgr, &qmgr, &hmgr, &pmgr, &limit_config);
TEST_ASSERT_TRUE(rc == 1);
memset(buf, 0x0, sizeof(buf));
rc = fmt_limit(buf, sizeof(buf) - 1, limit);
TEST_ASSERT_TRUE(rc > 0);
printf("%s\n", buf);
kill_limit(limit);
groupmgr_fini(&pmgr);
groupmgr_fini(&hmgr);
groupmgr_fini(&qmgr);
groupmgr_fini(&umgr);
}
static void test_match_limit_ok1(void)
{
groupmgr_t umgr, qmgr, hmgr, pmgr;
xce_limit_t* limit;
int rc;
xce_limit_config_t limit_config = {
.name = "limit1",
.per_user = 0,
.per_queue = 0,
.per_project = 0,
.per_host = 0,
.users
= &(xce_group_config_t) { .members = { .count = 1,
.data = (char*[]) { "all" } } },
.queues
= &(xce_group_config_t) { .members = { .count = 1,
.data = (char*[]) { "all" } } },
.hosts = NULL,
.projects
= &(xce_group_config_t) { .members = { .count = 1,
.data = (char*[]) { "all" } } },
.val
= &(xce_limit_val_t) { .resname = "mem", .percent = 1, .val = 80.0 }
};
char buf[1024];
groupmgr_init(&umgr, 4, 4);
groupmgr_init(&qmgr, 4, 4);
groupmgr_init(&hmgr, 4, 4);
groupmgr_init(&pmgr, 4, 4);
rc = build_limit(&limit, &umgr, &qmgr, &hmgr, &pmgr, &limit_config);
TEST_ASSERT_TRUE(rc == 1);
memset(buf, 0x0, sizeof(buf));
rc = fmt_limit(buf, sizeof(buf) - 1, limit);
TEST_ASSERT_TRUE(rc > 0);
printf("%s\n", buf);
rc = match_limit(limit, 1, "rshen", "xce", "project1");
TEST_ASSERT_TRUE(rc > 0);
kill_limit(limit);
groupmgr_fini(&pmgr);
groupmgr_fini(&hmgr);
groupmgr_fini(&qmgr);
groupmgr_fini(&umgr);
}
static void test_match_limit_ok2(void)
{
groupmgr_t umgr, qmgr, hmgr, pmgr;
xce_limit_t* limit;
int rc;
xce_limit_config_t limit_config = {
.name = "limit1",
.per_user = 0,
.per_queue = 0,
.per_project = 0,
.per_host = 0,
.users = NULL,
.queues = NULL,
.hosts = NULL,
.projects = NULL,
.val
= &(xce_limit_val_t) { .resname = "mem", .percent = 1, .val = 80.0 }
};
char buf[1024];
groupmgr_init(&umgr, 4, 4);
groupmgr_init(&qmgr, 4, 4);
groupmgr_init(&hmgr, 4, 4);
groupmgr_init(&pmgr, 4, 4);
rc = build_limit(&limit, &umgr, &qmgr, &hmgr, &pmgr, &limit_config);
TEST_ASSERT_TRUE(rc == 1);
memset(buf, 0x0, sizeof(buf));
rc = fmt_limit(buf, sizeof(buf) - 1, limit);
TEST_ASSERT_TRUE(rc > 0);
printf("%s\n", buf);
rc = match_limit(limit, 1, "rshen", "xce", "project1");
TEST_ASSERT_TRUE(rc > 0);
kill_limit(limit);
groupmgr_fini(&pmgr);
groupmgr_fini(&hmgr);
groupmgr_fini(&qmgr);
groupmgr_fini(&umgr);
}
static void test_match_limit1(void)
{
groupmgr_t umgr, qmgr, hmgr, pmgr;
xce_limit_t* limit;
int rc;
xce_limit_config_t limit_config = {
.name = "limit1",
.per_user = 0,
.per_queue = 0,
.per_project = 0,
.per_host = 0,
.users
= &(xce_group_config_t) { .members
= { .count = 1, .data = (char*[]) { "all" } },
.ex_members
= { .count = 1,
.data = (char*[]) { "rshen" } } },
.queues
= &(xce_group_config_t) { .members = { .count = 1,
.data = (char*[]) { "all" } } },
.hosts = NULL,
.projects
= &(xce_group_config_t) { .members = { .count = 1,
.data = (char*[]) { "all" } } },
.val
= &(xce_limit_val_t) { .resname = "mem", .percent = 1, .val = 80.0 }
};
char buf[1024];
groupmgr_init(&umgr, 4, 4);
groupmgr_init(&qmgr, 4, 4);
groupmgr_init(&hmgr, 4, 4);
groupmgr_init(&pmgr, 4, 4);
rc = build_limit(&limit, &umgr, &qmgr, &hmgr, &pmgr, &limit_config);
TEST_ASSERT_TRUE(rc == 1);
memset(buf, 0x0, sizeof(buf));
rc = fmt_limit(buf, sizeof(buf) - 1, limit);
TEST_ASSERT_TRUE(rc > 0);
printf("%s\n", buf);
rc = match_limit(limit, 1, "rshen", "xce", "project1");
TEST_ASSERT_TRUE(rc == 0);
kill_limit(limit);
groupmgr_fini(&pmgr);
groupmgr_fini(&hmgr);
groupmgr_fini(&qmgr);
groupmgr_fini(&umgr);
}
static void test_match_limit2(void)
{
groupmgr_t umgr, qmgr, hmgr, pmgr;
xce_limit_t* limit;
int rc;
xce_limit_config_t limit_config = {
.name = "limit1",
.per_user = 0,
.per_queue = 0,
.per_project = 0,
.per_host = 0,
.users
= &(xce_group_config_t) { .members
= { .count = 1, .data = (char*[]) { "all" } },
.ex_members
= { .count = 1,
.data = (char*[]) { "rshen" } } },
.queues = NULL,
.hosts = NULL,
.projects = NULL,
.val
= &(xce_limit_val_t) { .resname = "slots", .percent = 0, .val = 3.0 }
};
char buf[1024];
groupmgr_init(&umgr, 4, 4);
groupmgr_init(&qmgr, 4, 4);
groupmgr_init(&hmgr, 4, 4);
groupmgr_init(&pmgr, 4, 4);
rc = build_limit(&limit, &umgr, &qmgr, &hmgr, &pmgr, &limit_config);
TEST_ASSERT_TRUE(rc == 1);
memset(buf, 0x0, sizeof(buf));
rc = fmt_limit(buf, sizeof(buf) - 1, limit);
TEST_ASSERT_TRUE(rc > 0);
printf("%s\n", buf);
rc = match_limit(limit, 1, "rshen", "normal", "default");
TEST_ASSERT_TRUE(rc == 0);
kill_limit(limit);
groupmgr_fini(&pmgr);
groupmgr_fini(&hmgr);
groupmgr_fini(&qmgr);
groupmgr_fini(&umgr);
}
void test_limit(void)
{
printf("\n>> limit test\n\n");
RUN_TEST(test_build_consumer1);
RUN_TEST(test_build_consumer2);
RUN_TEST(test_build_limit_ok1);
RUN_TEST(test_match_limit_ok1);
RUN_TEST(test_match_limit_ok2);
RUN_TEST(test_match_limit1);
RUN_TEST(test_match_limit2);
}

View file

@ -58,6 +58,27 @@ static void test_add_res_reason(void)
erase_job_preason(&reason);
}
static void test_add_shared_res_reason(void)
{
xce_job_preason_t reason;
memset(&reason, 0x0, sizeof(reason));
add_job_preason(&reason, 1, NULL, "res1");
TEST_ASSERT_TRUE(reason.main_reason == 0);
TEST_ASSERT_TRUE((reason.nres_reasons == 1)
&& (reason.res_based_reasons != NULL)
&& (reason.res_based_reasons[0] != NULL));
TEST_ASSERT_TRUE(
(reason.res_based_reasons[0]->reason == 1)
&& (strcmp(reason.res_based_reasons[0]->host, "") == 0)
&& (strcmp(reason.res_based_reasons[0]->resource, "res1") == 0));
TEST_ASSERT_TRUE((reason.nhost_reasons == 0)
&& (reason.host_based_reasons == NULL));
erase_job_preason(&reason);
}
static void test_add_res_host_reason(void)
{
xce_job_preason_t reason;
@ -161,6 +182,7 @@ void test_preason(void)
RUN_TEST(test_main_reason);
RUN_TEST(test_add_host_reason);
RUN_TEST(test_add_res_reason);
RUN_TEST(test_add_shared_res_reason);
RUN_TEST(test_add_res_host_reason);
RUN_TEST(test_add_main_reset_other);
RUN_TEST(test_add_more_host_reason);

View file

@ -0,0 +1,139 @@
#include <stdarg.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unity.h>
#include "utils.h"
#include "xlist.h"
static void test_xlist1(void)
{
xlist_t lst;
char* hello = "Hello";
void* p = NULL;
int idx;
xlist_init(&lst, 2);
idx = xlist_store(&lst, (void*)hello);
TEST_ASSERT_TRUE(idx >= 0);
p = xlist_get(&lst, idx);
TEST_ASSERT_TRUE(p == hello);
xlist_fini(&lst);
}
static int find_check_(void* data, va_list ap)
{
char* p = va_arg(ap, char*);
if (data == p) {
return 1;
}
if (p == NULL) {
return 0;
}
if (data == NULL) {
return 0;
}
if (strcmp((char*)data, p) == 0) {
return 1;
}
return 0;
}
static void test_xlist_find(void)
{
xlist_t lst;
char* hello = "Hello";
void* p = NULL;
int idx;
xlist_init(&lst, 2);
idx = xlist_store(&lst, (void*)hello);
TEST_ASSERT_TRUE(idx >= 0);
p = xlist_find(&lst, (xlist_find_fn_t)find_check_, "Hello");
TEST_ASSERT_TRUE(p != NULL);
TEST_ASSERT_TRUE(strcmp((char*)p, "Hello") == 0);
xlist_fini(&lst);
}
static void test_xlist_del(void)
{
xlist_t lst;
char* hello = "Hello";
void* p = NULL;
int idx;
int i;
xlist_init(&lst, 2);
for (i = 0; i < 5; i++) {
idx = xlist_store(&lst, (void*)hello);
TEST_ASSERT_TRUE(idx >= 0);
}
p = xlist_find(&lst, (xlist_find_fn_t)find_check_, "Hello");
TEST_ASSERT_TRUE(p != NULL);
TEST_ASSERT_TRUE(strcmp((char*)p, "Hello") == 0);
xlist_del(&lst, (void*)hello);
p = xlist_find(&lst, (xlist_find_fn_t)find_check_, "Hello");
TEST_ASSERT_TRUE(p == NULL);
TEST_ASSERT_TRUE(lst.count == 0);
xlist_fini(&lst);
}
static int do_iter_(void* arg1, void* arg2)
{
int* count = (int*)arg2;
int i = (int)(long)arg1;
(*count) += i;
return 0;
}
static void test_xlist_iter(void)
{
xlist_t lst;
int idx;
int i;
int count = 0;
xlist_init(&lst, 2);
for (i = 0; i < 5; i++) {
idx = xlist_store(&lst, (void*)(long)i);
TEST_ASSERT_TRUE(idx >= 0);
}
xlist_iter(&lst, (xlist_iter_fn_t)do_iter_, &count);
TEST_ASSERT_TRUE(count == 10);
xlist_fini(&lst);
}
void test_xlist(void)
{
printf("\n>> xlist test\n\n");
RUN_TEST(test_xlist1);
RUN_TEST(test_xlist_find);
RUN_TEST(test_xlist_del);
RUN_TEST(test_xlist_iter);
}

View file

@ -368,7 +368,8 @@ int xce_free_queues(int nqueues, xce_queue_t* queues)
return 0;
}
int xce_grouplist(xce_ctx_t* ctx, int group_type, xce_group_t*** groups)
int xce_group_configlist(xce_ctx_t* ctx, int group_type,
xce_group_config_t*** groups)
{
#if 0
int table;
@ -388,7 +389,7 @@ int xce_grouplist(xce_ctx_t* ctx, int group_type, xce_group_t*** groups)
#endif
}
int xce_free_groups(int ngroups, xce_group_t* groups)
int xce_free_groups(int ngroups, xce_group_config_t* groups)
{
/* TODO */
return 0;

484
src/lib/xcelimit.c Normal file
View file

@ -0,0 +1,484 @@
#include <errno.h>
#include <stdarg.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "group.h"
#include "kvmgr.h"
#include "objmgr.h"
#include "utils.h"
#include "xce/types.h"
#include "xcelimit.h"
int fmt_consumer(char* buf, int len, xce_consumer_t* c)
{
struct {
xce_consumer_type_t type;
char* name;
char* per_name;
} consumer_mapping[] = {
{ CONSUMER_USER, "USER", "PER_USER" },
{ CONSUMER_HOST, "HOST", "PER_HOST" },
{ CONSUMER_PROJECT, "PROJECT", "PER_PROJECT" },
{ CONSUMER_QUEUE, "QUEUE", "PER_QUEUE" },
{ -1, NULL, NULL }
};
int i;
if ((buf == NULL) || (c == NULL) || (len <= 0)) {
return -EINVAL;
}
for (i = 0; consumer_mapping[i].type >= 0; i++) {
if (c->ctype == consumer_mapping[i].type) {
char* t;
int l;
int rc;
if (c->per_consumer) {
t = consumer_mapping[i].per_name;
} else {
t = consumer_mapping[i].name;
}
snprintf(buf, len, "%s = ", t);
l = strlen(buf);
rc = fmt_group(buf + l, len - l, c->group);
if (rc < 0) {
return rc;
}
return strlen(buf);
}
}
return -EINVAL;
}
int build_consumer(xce_consumer_t** pconsumer, groupmgr_t* mgr,
xce_consumer_type_t type, int per_consumer,
xce_group_config_t* g_config, char* default_name_prefix)
{
xce_consumer_t* c = NULL;
xce_group_t* g = NULL;
*pconsumer = NULL;
c = (xce_consumer_t*)calloc(1, sizeof(*c));
if (c == NULL) {
return -ENOMEM;
}
*pconsumer = c;
c->ctype = type;
c->per_consumer = (per_consumer != 0) ? 1 : 0;
c->group = NULL;
if (g_config != NULL) {
char* default_g_name = NULL;
char consumer_group_name[2 * XCE_MAXNAME_LEN + 1];
if ((g_config->name == NULL) || (strlen(g_config->name) == 0)) {
char* suffix = NULL;
switch (type) {
case CONSUMER_USER:
suffix = "ug";
break;
case CONSUMER_PROJECT:
suffix = "pg";
break;
case CONSUMER_HOST:
suffix = "hg";
break;
case CONSUMER_QUEUE:
suffix = "qg";
break;
default:
suffix = "unknown";
break;
}
sprintf(consumer_group_name, "%s-%s-group", default_name_prefix,
suffix);
default_g_name = consumer_group_name;
}
else {
default_g_name = g_config->name;
}
g = build_group(mgr, g_config, default_g_name);
if (g == NULL) {
return -ENOMEM;
}
} else {
g = get_group(mgr, "all");
}
c->group = g;
return 1;
}
void kill_consumer(xce_consumer_t* c)
{
if (c == NULL) {
return;
}
/* leave c->group managed by group manager */
FREEUP(c);
}
int fmt_limit(char* buf, int len, xce_limit_t* limit)
{
int pos = 0;
int i, rc;
rc = snprintf(buf + pos, len - pos, "limit: %s\n", limit->name);
if (rc < 0) {
return rc;
}
pos += rc;
for (i = 0; i < CONSUMER_MAX; i++) {
xce_consumer_t* c = limit->consumers[i];
if (c != NULL) {
rc = fmt_consumer(buf + pos, len - pos, c);
if (rc < 0) {
return rc;
}
pos += rc;
rc = snprintf(buf + pos, len - pos, "\n");
if (rc < 0) {
return rc;
}
pos += rc;
}
}
rc = snprintf(buf + pos, len - pos, "\n%s = %f%s\n", limit->val.resname,
limit->val.val, (limit->val.percent != 0) ? "%" : "");
if (rc < 0) {
return rc;
}
return strlen(buf);
}
void kill_limit(xce_limit_t* limit)
{
int i;
if (limit == NULL) {
return;
}
FREEUP(limit->name);
for (i = 0; i < CONSUMER_MAX; i++) {
xce_consumer_t* c = limit->consumers[i];
if (c != NULL) {
kill_consumer(c);
}
}
FREEUP(limit->val.resname);
FREEUP(limit);
}
int build_limit(xce_limit_t** plimit, groupmgr_t* ugmgr, groupmgr_t* qgmgr,
groupmgr_t* hgmgr, groupmgr_t* pgmgr,
xce_limit_config_t* config)
{
xce_limit_t* limit = NULL;
int rc = 0;
*plimit = NULL;
limit = (xce_limit_t*)calloc(1, sizeof(*limit));
if (limit == NULL) {
return -ENOMEM;
}
*plimit = limit;
limit->name = strdup(config->name);
rc = build_consumer(&(limit->consumers[CONSUMER_USER]), ugmgr,
CONSUMER_USER, config->per_user, config->users,
config->name);
if (rc < 0) {
return rc;
}
rc = build_consumer(&(limit->consumers[CONSUMER_HOST]), hgmgr,
CONSUMER_HOST, config->per_host, config->hosts,
config->name);
if (rc < 0) {
return rc;
}
rc = build_consumer(&(limit->consumers[CONSUMER_QUEUE]), qgmgr,
CONSUMER_QUEUE, config->per_queue, config->queues,
config->name);
if (rc < 0) {
return rc;
}
rc = build_consumer(&(limit->consumers[CONSUMER_PROJECT]), pgmgr,
CONSUMER_PROJECT, config->per_project, config->projects,
config->name);
if (rc < 0) {
return rc;
}
limit->val.resname = strdup(config->val->resname);
limit->val.percent = config->val->percent;
limit->val.val = config->val->val;
return 1;
}
int match_limit(xce_limit_t* limit, int global, ...)
{
va_list ap;
char* user = NULL;
char* queue = NULL;
char* project = NULL;
char* host = NULL;
xce_consumer_t* c = NULL;
va_start(ap, global);
user = va_arg(ap, char*);
queue = va_arg(ap, char*);
project = va_arg(ap, char*);
if ((limit == NULL) || (user == NULL) || (queue == NULL)
|| (project == NULL)) {
return -EINVAL;
}
if (!global) {
host = va_arg(ap, char*);
if (host == NULL) {
return -EINVAL;
}
}
c = limit->consumers[CONSUMER_USER];
if (c != NULL) {
if (is_in_group(c->group, user) <= 0) {
return 0;
}
}
c = limit->consumers[CONSUMER_QUEUE];
if (c != NULL) {
if (is_in_group(c->group, queue) <= 0) {
return 0;
}
}
c = limit->consumers[CONSUMER_PROJECT];
if (c != NULL) {
if (is_in_group(c->group, project) <= 0) {
return 0;
}
}
if (!global) {
c = limit->consumers[CONSUMER_HOST];
if (c != NULL) {
if (is_in_group(c->group, host) <= 0) {
return 0;
}
}
}
return 1;
}
int build_vres_name(char** vname, xce_limit_t* limit, char* user, char* queue,
char* project)
{
char resname[2 * XCE_MAXNAME_LEN + 1];
xce_consumer_t* c = NULL;
char* p = NULL;
int rc = 0;
*vname = NULL;
if ((vname == NULL) || (limit == NULL) || (user == NULL) || (queue == NULL)
|| (project == NULL)) {
return -EINVAL;
}
memset(resname, 0x0, sizeof(resname));
p = &resname[0];
rc = sprintf(p, "limit=%s", limit->name);
if (rc < 0) {
return rc;
}
p += rc;
c = limit->consumers[CONSUMER_USER];
if ((c != NULL) && (c->per_consumer == 1)) {
rc = sprintf(p, "/u=%s", user);
if (rc < 0) {
return rc;
}
p += rc;
}
c = limit->consumers[CONSUMER_QUEUE];
if ((c != NULL) && (c->per_consumer == 1)) {
rc = sprintf(p, "/q=%s", queue);
if (rc < 0) {
return rc;
}
p += rc;
}
c = limit->consumers[CONSUMER_PROJECT];
if ((c != NULL) && (c->per_consumer == 1)) {
rc = sprintf(p, "/p=%s", project);
if (rc < 0) {
return rc;
}
p += rc;
}
*vname = strdup(resname);
if ((*vname) != NULL) {
return 1;
}
return -ENOMEM;
}
int is_host_based_limit(resmgr_t* mgr, xce_limit_t* limit)
{
xce_consumer_t* c = NULL;
xce_resource_t* r = NULL;
if (limit == NULL) {
return -EINVAL;
}
r = resmgr_lockres(mgr, limit->val.resname, OBJREF_NOP);
if ((r->flags & (XCE_RESF_GLOBAL | XCE_RESF_SHARED)) == 0) {
if (limit->val.percent != 0) {
return 1;
}
}
c = limit->consumers[CONSUMER_HOST];
if (c == NULL) {
return 0;
}
return c->per_consumer;
}
int add_vres(xce_resource_t** pvres, resmgr_t* mgr, char* name, int global)
{
xce_resource_t* vres = NULL;
int rc;
vres = (xce_resource_t*)calloc(1, sizeof(*vres));
*pvres = vres;
if (vres == NULL) {
return -ENOMEM;
}
vres->name = strdup(name);
vres->idx = -1;
vres->desc = NULL;
vres->valtype = XCE_VAL_NUMERIC;
vres->ordtype = XCE_ORD_DECR;
vres->flags = XCE_RESF_LIMIT | XCE_RESF_CONSUMABLE | XCE_RESF_RELEASE;
if (global) {
vres->flags |= (XCE_RESF_SHARED | XCE_RESF_GLOBAL);
} else {
vres->flags |= XCE_RESF_DEFAULT_HOST;
}
rc = resmgr_add_res(mgr, vres);
if (rc < 0) {
return rc;
}
vres->idx = rc;
return rc;
}
int instantiate_limit(xce_vres_t** pvres, resmgr_t* mgr, xce_limit_t* limit,
char* user, char* queue, char* project)
{
xce_vres_t* vres = NULL;
int rc = 0;
char* vresname = NULL;
int shared = 1;
if ((pvres == NULL) || (mgr == NULL) || (limit == NULL) || (user == NULL)
|| (queue == NULL) || (project == NULL)) {
return -EINVAL;
}
vres = (xce_vres_t*)calloc(1, sizeof(*vres));
*pvres = vres;
if (vres == NULL) {
return -ENOMEM;
}
vres->limit = limit;
if (is_host_based_limit(mgr, limit)) {
shared = 0;
}
rc = build_vres_name(&(vresname), limit, user, queue, project);
if (rc < 0) {
return rc;
}
vres->vres = resmgr_lockres(mgr, vresname, OBJREF_NOP);
if (vres->vres == NULL) {
rc = add_vres(&(vres->vres), mgr, vresname, shared);
if (rc < 0) {
FREEUP(vresname);
return rc;
}
}
FREEUP(vresname);
vres->res = resmgr_lockres(mgr, limit->val.resname, OBJREF_NOP);
if (vres->res == NULL) {
return -ENOENT;
}
return rc;
}
void kill_vres(xce_vres_t* vres)
{
if (vres == NULL) {
return;
}
}

View file

@ -46,6 +46,7 @@ static int xlist_ensure(xlist_t* l)
int xlist_store(xlist_t* l, void* data)
{
int rc;
int idx;
if (l == NULL) {
return -EINVAL;
@ -56,10 +57,26 @@ int xlist_store(xlist_t* l, void* data)
return rc;
}
l->data[l->count] = data;
idx = l->count;
l->data[idx] = data;
l->count++;
return 1;
return idx;
}
void* xlist_get(xlist_t* l, int idx)
{
if ((l == NULL) || (idx < 0)) {
errno = EINVAL;
return NULL;
}
if (idx >= l->caps) {
errno = 0;
return NULL;
}
return (void*)l->data[idx];
}
void xlist_clear(xlist_t* l)
@ -86,6 +103,31 @@ int xlist_iter(xlist_t* l, xlist_iter_fn_t fn, void* data)
return 0;
}
int xlist_del(xlist_t* l, void* data)
{
int i, j;
if (l == NULL) {
return -EINVAL;
}
j = -1;
i = 0;
while (i < l->count) {
if (l->data[i] == data) {
for (j = i + 1; j < l->count; j++) {
l->data[j - 1] = l->data[j];
}
l->count--;
} else {
i++;
}
}
return 1;
}
void* xlist_find(xlist_t* l, xlist_find_fn_t fn, ...)
{
int i;