add kiro skills
This commit is contained in:
parent
e5fde7b55f
commit
86fe35e671
53 changed files with 8406 additions and 166 deletions
|
|
@ -404,38 +404,17 @@ static int apiserver_worker_change(int type, int situation, void* data,
|
|||
static int apiserver_worker_join(int type, int situation, void* data,
|
||||
va_list ap)
|
||||
{
|
||||
/* Unified JOIN signature: (cluster_t*, char* name, worker_t*)
|
||||
* w is NULL for BEFORE_EVENT (worker not yet created). */
|
||||
cluster_t* c = va_arg(ap, cluster_t*);
|
||||
char* name = va_arg(ap, char*);
|
||||
worker_t* w = NULL;
|
||||
int rc;
|
||||
worker_t* w = va_arg(ap, worker_t*);
|
||||
(void)name;
|
||||
|
||||
if (situation == HOOK_BEFORE_EVENT) {
|
||||
/* nothing to do */
|
||||
if (w == NULL)
|
||||
return 0;
|
||||
}
|
||||
|
||||
lock(c->workermgr, RTTI_LOCK_WRITE);
|
||||
rc = key_find(c->workermgr, (void*)name, (void**)&w);
|
||||
if (w != NULL) {
|
||||
reference(w);
|
||||
}
|
||||
unlock(c->workermgr);
|
||||
|
||||
if (rc < 0) {
|
||||
return rc;
|
||||
}
|
||||
|
||||
if (rc == 0) {
|
||||
return -ENOENT;
|
||||
}
|
||||
|
||||
lock(w, RTTI_LOCK_READ);
|
||||
rc = apiserver_worker_change(type, situation, data, c, w);
|
||||
unlock(w);
|
||||
|
||||
dereference(w);
|
||||
|
||||
return rc;
|
||||
return apiserver_worker_change(type, situation, data, c, w);
|
||||
}
|
||||
|
||||
static int apiserver_worker_dead(int type, int situation, void* data,
|
||||
|
|
|
|||
|
|
@ -25,6 +25,7 @@
|
|||
#include "worker.h"
|
||||
#include "xce/errnox.h"
|
||||
#include "xstring.h"
|
||||
#include "helper.h"
|
||||
|
||||
static xce_peerinfo_t from_controller
|
||||
= { .name = "controller", .key = "keepSecret" };
|
||||
|
|
@ -159,7 +160,8 @@ static int myreq_handler(vtask_t* task)
|
|||
|
||||
rc = xce_decode(data->u.iodata.pkt, &request);
|
||||
|
||||
TRACE("%s cpu#%d opcode %d", __func__, task->vcpu, request.opcode);
|
||||
TRACE("%s cpu#%d opcode %s", __func__, task->vcpu,
|
||||
xce_request_op_name(&request));
|
||||
|
||||
init_reply_by_req(&reply, &request);
|
||||
|
||||
|
|
@ -899,6 +901,10 @@ static int check_workers_timeout(void* args)
|
|||
break;
|
||||
}
|
||||
|
||||
if (w->ep == NULL) {
|
||||
continue;
|
||||
}
|
||||
|
||||
/* check_worker_timeout_(c, w); */
|
||||
if (w != NULL) {
|
||||
reference(w);
|
||||
|
|
|
|||
|
|
@ -7,7 +7,11 @@
|
|||
#include "xce/types.h"
|
||||
#include "zarray.h"
|
||||
|
||||
extern int add_worker(void*, endpoint_t*, const char*, hostattrs_t*);
|
||||
extern int restore_worker(void*, xce_worker_t);
|
||||
extern int worker_from_capnp(cluster_t*, const void*, size_t);
|
||||
extern xce_hostconfig_t* find_matched_hostconfig(cluster_t*, const char*);
|
||||
extern int init_worker(cluster_t*, endpoint_t*, const char*,
|
||||
hostattrs_t*, xce_hostconfig_t*);
|
||||
extern int rejoin_worker(cluster_t*, worker_t*, endpoint_t*,
|
||||
hostattrs_t*, xce_hostconfig_t*);
|
||||
extern int restore_worker(void*, xce_worker_t);
|
||||
extern int worker_from_capnp(cluster_t*, const void*, size_t);
|
||||
#endif
|
||||
|
|
|
|||
|
|
@ -32,12 +32,14 @@ static int handle_client_join(cluster_t* c, endpoint_t* ep,
|
|||
static int handle_worker_join(cluster_t* c, endpoint_t* ep,
|
||||
xce_request_t* request)
|
||||
{
|
||||
int idx;
|
||||
ep_private_t* priv = (ep_private_t*)ep->data;
|
||||
xce_reply_t reply;
|
||||
xce_peerinfo_t* from = request->from;
|
||||
int rc = 0;
|
||||
xce_joindata_t* joinmsg = NULL;
|
||||
int idx = 0;
|
||||
ep_private_t* priv = (ep_private_t*)ep->data;
|
||||
xce_reply_t reply;
|
||||
xce_peerinfo_t* from = request->from;
|
||||
int rc = 0;
|
||||
xce_joindata_t* joinmsg = NULL;
|
||||
worker_t* w = NULL;
|
||||
xce_hostconfig_t* hconfig = NULL;
|
||||
|
||||
TRACE("%s worker %s joining...", __func__, from->name);
|
||||
|
||||
|
|
@ -45,54 +47,70 @@ static int handle_worker_join(cluster_t* c, endpoint_t* ep,
|
|||
|
||||
if (!validate_host(c, from->name, from->key)) {
|
||||
ERROR("%s reject join of host %s", __func__, from->name);
|
||||
rc = -EPERM;
|
||||
|
||||
rc = -EPERM;
|
||||
reply.status = rc;
|
||||
reply.data_which = RequestReply_data_nodata;
|
||||
|
||||
xce_reply(ep, c->myhost, &reply);
|
||||
return rc;
|
||||
}
|
||||
|
||||
joinmsg = request->body.joinmsg;
|
||||
|
||||
lock(c->workermgr, RTTI_LOCK_WRITE);
|
||||
xce_run_hooks(&(c->hooksys), XCE_CONTROLLER_HOOK_WORKER, HOOK_BEFORE_EVENT,
|
||||
HOOK_DETAIL_WORKER_JOIN, c, from->name, 0);
|
||||
idx = add_worker(c, ep, from->name, joinmsg->data.attrs);
|
||||
|
||||
/* A worker recovered from WAL already occupies the slot (status=UNAVAIL).
|
||||
* Reconnecting after a controller restart will fail add_worker with -EEXIST.
|
||||
* Locate the existing slot, update its endpoint, and clear UNAVAIL. */
|
||||
if (idx == -EEXIST) {
|
||||
worker_t* existing = NULL;
|
||||
if (key_find(c->workermgr, (void*)from->name, (void**)&existing) > 0 &&
|
||||
existing != NULL) {
|
||||
lock(existing, RTTI_LOCK_WRITE);
|
||||
existing->ep = ep;
|
||||
existing->host.status &= ~XCE_WORKER_UNAVAIL;
|
||||
idx = existing->idx;
|
||||
unlock(existing);
|
||||
}
|
||||
/* Fail fast before any lock: reject if no hostconfig matches (req 4.4) */
|
||||
hconfig = find_matched_hostconfig(c, from->name);
|
||||
if (hconfig == NULL) {
|
||||
rc = (errno != 0) ? -errno : -EINVAL;
|
||||
reply.status = rc;
|
||||
reply.data_which = RequestReply_data_nodata;
|
||||
xce_reply(ep, c->myhost, &reply);
|
||||
return rc;
|
||||
}
|
||||
|
||||
/* BEFORE_EVENT fires before the existence check (req 5.1) */
|
||||
xce_run_hooks(&(c->hooksys), XCE_CONTROLLER_HOOK_WORKER, HOOK_BEFORE_EVENT,
|
||||
HOOK_DETAIL_WORKER_JOIN, c, from->name, (worker_t*)NULL);
|
||||
|
||||
/* Explicit existence check under brief READ lock (req 1.1) */
|
||||
lock(c->workermgr, RTTI_LOCK_READ);
|
||||
rc = key_find(c->workermgr, (void*)from->name, (void**)&w);
|
||||
if (w != NULL)
|
||||
reference(w);
|
||||
unlock(c->workermgr);
|
||||
|
||||
if (rc < 0 && rc != -ENOENT) {
|
||||
/* Non-ENOENT error: reject join (req 1.3) */
|
||||
ERROR("%s workermgr lookup error for %s, rc %d", __func__, from->name, rc);
|
||||
idx = rc;
|
||||
} else if (w == NULL) {
|
||||
/* Host not yet registered: first-time join (req 1.2, req 2) */
|
||||
idx = init_worker(c, ep, from->name, joinmsg->data.attrs, hconfig);
|
||||
if (idx < 0)
|
||||
ERROR("%s host %s first join failed, rc %d", __func__, from->name, -idx);
|
||||
} else {
|
||||
/* Host already registered: rejoin (req 1.2, req 3) */
|
||||
idx = rejoin_worker(c, w, ep, joinmsg->data.attrs, hconfig);
|
||||
dereference(w);
|
||||
w = NULL;
|
||||
if (idx < 0)
|
||||
ERROR("%s host %s rejoin failed, rc %d", __func__, from->name, -idx);
|
||||
}
|
||||
|
||||
/* Look up worker for AFTER_EVENT persist hook (w is NULL on error) */
|
||||
if (idx >= 0)
|
||||
vget(c->workermgr, idx, (void**)&w);
|
||||
|
||||
/* AFTER_EVENT fires after create/update, outside any lock (req 5.1) */
|
||||
xce_run_hooks(&(c->hooksys), XCE_CONTROLLER_HOOK_WORKER, HOOK_AFTER_EVENT,
|
||||
HOOK_DETAIL_WORKER_JOIN, c, from->name, w);
|
||||
|
||||
reply.status = idx;
|
||||
reply.data_which = RequestReply_data_nodata;
|
||||
|
||||
/* Look up the worker so the persist hook can serialize it */
|
||||
worker_t* w = NULL;
|
||||
if (idx >= 0)
|
||||
vget(c->workermgr, idx, (void**)&w);
|
||||
xce_run_hooks(&(c->hooksys), XCE_CONTROLLER_HOOK_WORKER, HOOK_AFTER_EVENT,
|
||||
HOOK_DETAIL_WORKER_JOIN, c, w);
|
||||
xce_reply(ep, c->myhost, &reply);
|
||||
unlock(c->workermgr);
|
||||
if (idx < 0) {
|
||||
ERROR("worker %s join failed, rc %d", from->name, idx);
|
||||
|
||||
if (idx < 0)
|
||||
return idx;
|
||||
}
|
||||
|
||||
/* Set priv->u.idx before on_worker_join (req 2.3, req 5.2) */
|
||||
priv->u.idx = idx;
|
||||
on_worker_join(c, ep);
|
||||
|
||||
|
|
|
|||
|
|
@ -99,6 +99,8 @@ controller_unit_test_src = [
|
|||
'testsuite/test_p3_manual_failover.c',
|
||||
'testsuite/test_ctrl_metrics.c',
|
||||
'testsuite/test_config_repl.c',
|
||||
'testsuite/test_worker_join.c',
|
||||
'worker.c',
|
||||
'persist.c',
|
||||
'ha.c',
|
||||
'repl.c',
|
||||
|
|
@ -110,6 +112,7 @@ ssl_dep = dependency('openssl')
|
|||
|
||||
controller_unit_test_deps = [
|
||||
libxce_dep,
|
||||
librtti_dep,
|
||||
unity_dep,
|
||||
tinylog_dep,
|
||||
thread_dep,
|
||||
|
|
|
|||
|
|
@ -1193,10 +1193,12 @@ int persist_worker_hook_fun(int type, int situation, int detail,
|
|||
size_t capnp_sz = 0;
|
||||
int rc = 0;
|
||||
|
||||
/* All worker detail types — args: cluster_t*, worker_t*.
|
||||
* Caller holds the appropriate lock (workermgr write lock for JOIN,
|
||||
* worker write lock for DEAD/UPDATE), so no extra lock needed here. */
|
||||
/* All worker detail types: args are cluster_t* then worker_t*.
|
||||
* JOIN uses a unified signature with an extra char* name between them;
|
||||
* DEAD and UPDATE use (cluster_t*, worker_t*) directly. */
|
||||
(void)va_arg(ap, cluster_t*);
|
||||
if (detail == HOOK_DETAIL_WORKER_JOIN)
|
||||
(void)va_arg(ap, char*); /* skip name — present only for JOIN */
|
||||
worker_t* w = va_arg(ap, worker_t*);
|
||||
if (!w) return 0; /* guard: failed join produced no worker slot */
|
||||
rc = capnp_encode_worker(&w->host, &buf, &capnp_sz);
|
||||
|
|
|
|||
|
|
@ -470,35 +470,17 @@ static int sched_worker_change(int type, int situation, void* data,
|
|||
|
||||
static int sched_worker_join(int type, int situation, void* data, va_list ap)
|
||||
{
|
||||
/* Unified JOIN signature: (cluster_t*, char* name, worker_t*)
|
||||
* w is NULL for BEFORE_EVENT (worker not yet created). */
|
||||
cluster_t* c = va_arg(ap, cluster_t*);
|
||||
char* name = va_arg(ap, char*);
|
||||
worker_t* w = NULL;
|
||||
int rc;
|
||||
worker_t* w = va_arg(ap, worker_t*);
|
||||
(void)name;
|
||||
|
||||
if (situation == HOOK_BEFORE_EVENT) {
|
||||
/* nothing to do */
|
||||
if (w == NULL)
|
||||
return 0;
|
||||
}
|
||||
|
||||
lock(c->workermgr, RTTI_LOCK_READ);
|
||||
rc = key_find(c->workermgr, (void*)name, (void**)&w);
|
||||
if (w != NULL) {
|
||||
reference(w);
|
||||
}
|
||||
unlock(c->workermgr);
|
||||
if (rc < 0) {
|
||||
return rc;
|
||||
}
|
||||
|
||||
if (rc == 0) {
|
||||
return -ENOENT;
|
||||
}
|
||||
|
||||
rc = sched_worker_change(type, situation, data, c, w);
|
||||
|
||||
dereference(w);
|
||||
|
||||
return rc;
|
||||
return sched_worker_change(type, situation, data, c, w);
|
||||
}
|
||||
|
||||
static int sched_worker_dead(int type, int situation, void* data, va_list ap)
|
||||
|
|
|
|||
|
|
@ -33,6 +33,7 @@ extern void test_p3_hardening(void);
|
|||
extern void test_p3_manual_failover(void);
|
||||
extern void test_ctrl_metrics(void);
|
||||
extern void test_config_repl(void);
|
||||
extern void test_worker_join(void);
|
||||
|
||||
void setUp(void) { return; }
|
||||
|
||||
|
|
@ -135,6 +136,7 @@ int main(int argc, char* argv[])
|
|||
TEST(test_p3_manual_failover);
|
||||
TEST(test_ctrl_metrics);
|
||||
TEST(test_config_repl);
|
||||
TEST(test_worker_join);
|
||||
|
||||
return UNITY_END();
|
||||
}
|
||||
|
|
|
|||
|
|
@ -21,6 +21,7 @@
|
|||
#include "config.h"
|
||||
#include "persist.h"
|
||||
#include "xce/cluster_def.h"
|
||||
#include "xlist.h"
|
||||
|
||||
/* ------------------------------------------------------------------ */
|
||||
/* Helpers */
|
||||
|
|
@ -53,6 +54,34 @@ static void teardown_e2e_hook(persist_t* p)
|
|||
free(p);
|
||||
}
|
||||
|
||||
/* Zero-initialized stubs — capnp encoders handle all-NULL fields safely */
|
||||
static worker_t g_stub_worker;
|
||||
static job_t g_stub_job;
|
||||
|
||||
/* Variadic wrappers so tests can pass typed args through a real va_list */
|
||||
static int call_worker_hook(int type, int sit, int detail, persist_t* p, ...)
|
||||
{
|
||||
va_list ap;
|
||||
va_start(ap, p);
|
||||
int rc = persist_worker_hook_fun(type, sit, detail, p, ap);
|
||||
va_end(ap);
|
||||
return rc;
|
||||
}
|
||||
|
||||
static int call_job_hook(int type, int sit, int detail, persist_t* p, ...)
|
||||
{
|
||||
va_list ap;
|
||||
va_start(ap, p);
|
||||
int rc = persist_job_hook_fun(type, sit, detail, p, ap);
|
||||
va_end(ap);
|
||||
return rc;
|
||||
}
|
||||
|
||||
/* One-element xlist backed by a stack pointer array */
|
||||
#define STUB_JOB_XLIST(name) \
|
||||
void* name##_ptr_ = &g_stub_job; \
|
||||
xlist_t name = { .count = 1, .caps = 1, .delta = 1, .data = &name##_ptr_ }
|
||||
|
||||
/* Find the first .wal segment in a WAL directory. */
|
||||
static int find_first_seg(const char* wal_dir, char* out, size_t outsz)
|
||||
{
|
||||
|
|
@ -101,8 +130,11 @@ static void test_job_hook_writes_wal_before_lsn_advances(void)
|
|||
|
||||
uint64_t lsn_before = p->current_lsn;
|
||||
|
||||
int rc = persist_job_hook_fun(HOOK_AFTER_EVENT, HOOK_AFTER_EVENT,
|
||||
HOOK_DETAIL_JOB_NEW, p, NULL);
|
||||
/* JOB_NEW args: (cluster_t*, xlist_t* jlist) — list must have ≥1 job */
|
||||
STUB_JOB_XLIST(jlist);
|
||||
int rc = call_job_hook(HOOK_AFTER_EVENT, HOOK_AFTER_EVENT,
|
||||
HOOK_DETAIL_JOB_NEW, p,
|
||||
(cluster_t*)NULL, &jlist);
|
||||
TEST_ASSERT_EQUAL_INT(0, rc);
|
||||
|
||||
/* LSN must have advanced by exactly 1 */
|
||||
|
|
@ -133,8 +165,10 @@ static void test_worker_hook_writes_wal_async(void)
|
|||
|
||||
uint64_t lsn_before = p->current_lsn;
|
||||
|
||||
int rc = persist_worker_hook_fun(HOOK_AFTER_EVENT, HOOK_AFTER_EVENT,
|
||||
HOOK_DETAIL_WORKER_JOIN, p, NULL);
|
||||
/* Unified JOIN signature: (cluster_t*, char* name, worker_t*) */
|
||||
int rc = call_worker_hook(HOOK_AFTER_EVENT, HOOK_AFTER_EVENT,
|
||||
HOOK_DETAIL_WORKER_JOIN, p,
|
||||
(cluster_t*)NULL, "stub-host", &g_stub_worker);
|
||||
TEST_ASSERT_EQUAL_INT(0, rc);
|
||||
TEST_ASSERT_EQUAL_UINT64(lsn_before + 1, p->current_lsn);
|
||||
|
||||
|
|
@ -162,16 +196,17 @@ static void test_hook_sequence_preserves_lsn_order(void)
|
|||
|
||||
uint64_t lsn0 = p->current_lsn;
|
||||
|
||||
persist_job_hook_fun(HOOK_AFTER_EVENT, HOOK_AFTER_EVENT,
|
||||
HOOK_DETAIL_JOB_NEW, p, NULL);
|
||||
STUB_JOB_XLIST(jlist);
|
||||
call_job_hook(HOOK_AFTER_EVENT, HOOK_AFTER_EVENT, HOOK_DETAIL_JOB_NEW, p,
|
||||
(cluster_t*)NULL, &jlist);
|
||||
uint64_t lsn1 = p->current_lsn;
|
||||
|
||||
persist_job_hook_fun(HOOK_AFTER_EVENT, HOOK_AFTER_EVENT,
|
||||
HOOK_DETAIL_JOB_START, p, NULL);
|
||||
call_job_hook(HOOK_AFTER_EVENT, HOOK_AFTER_EVENT, HOOK_DETAIL_JOB_START, p,
|
||||
(cluster_t*)NULL, (void*)NULL, &g_stub_job);
|
||||
uint64_t lsn2 = p->current_lsn;
|
||||
|
||||
persist_job_hook_fun(HOOK_AFTER_EVENT, HOOK_AFTER_EVENT,
|
||||
HOOK_DETAIL_JOB_EXIT, p, NULL);
|
||||
call_job_hook(HOOK_AFTER_EVENT, HOOK_AFTER_EVENT, HOOK_DETAIL_JOB_EXIT, p,
|
||||
(cluster_t*)NULL, (void*)NULL, &g_stub_job);
|
||||
uint64_t lsn3 = p->current_lsn;
|
||||
|
||||
/* LSNs must be strictly increasing */
|
||||
|
|
|
|||
|
|
@ -13,6 +13,7 @@
|
|||
#include "config.h"
|
||||
#include "persist.h"
|
||||
#include "xce/cluster_def.h"
|
||||
#include "xlist.h"
|
||||
|
||||
/* ------------------------------------------------------------------ */
|
||||
/* Test helpers */
|
||||
|
|
@ -56,6 +57,44 @@ static void cleanup_hook_test_persist(persist_t* p)
|
|||
free(p);
|
||||
}
|
||||
|
||||
/* Zero-initialized stubs — capnp encoders handle all-NULL fields safely */
|
||||
static worker_t g_stub_worker;
|
||||
static job_t g_stub_job;
|
||||
static queue_t g_stub_queue;
|
||||
|
||||
/* Variadic wrappers so tests can pass typed args through a real va_list */
|
||||
static int call_worker_hook(int type, int sit, int detail, persist_t* p, ...)
|
||||
{
|
||||
va_list ap;
|
||||
va_start(ap, p);
|
||||
int rc = persist_worker_hook_fun(type, sit, detail, p, ap);
|
||||
va_end(ap);
|
||||
return rc;
|
||||
}
|
||||
|
||||
static int call_job_hook(int type, int sit, int detail, persist_t* p, ...)
|
||||
{
|
||||
va_list ap;
|
||||
va_start(ap, p);
|
||||
int rc = persist_job_hook_fun(type, sit, detail, p, ap);
|
||||
va_end(ap);
|
||||
return rc;
|
||||
}
|
||||
|
||||
static int call_queue_hook(int type, int sit, int detail, persist_t* p, ...)
|
||||
{
|
||||
va_list ap;
|
||||
va_start(ap, p);
|
||||
int rc = persist_queue_hook_fun(type, sit, detail, p, ap);
|
||||
va_end(ap);
|
||||
return rc;
|
||||
}
|
||||
|
||||
/* One-element xlist backed by a stack pointer array — no heap allocation needed */
|
||||
#define STUB_JOB_XLIST(name) \
|
||||
void* name##_ptr_ = &g_stub_job; \
|
||||
xlist_t name = { .count = 1, .caps = 1, .delta = 1, .data = &name##_ptr_ }
|
||||
|
||||
/* ------------------------------------------------------------------ */
|
||||
/* Tests — RED phase: functions not yet implemented, must fail to link */
|
||||
/* ------------------------------------------------------------------ */
|
||||
|
|
@ -67,15 +106,12 @@ static void test_job_hook_new_writes_wal_entry(void)
|
|||
|
||||
uint64_t lsn_before = p->current_lsn;
|
||||
|
||||
/* Call hook directly — bypasses hook registration system for unit test.
|
||||
* Signature: int hook_fn(int type, int situation, int detail,
|
||||
* void* data, va_list ap)
|
||||
* We pass NULL for ap (no variadic args used in P0 implementation). */
|
||||
int rc = persist_job_hook_fun(HOOK_AFTER_EVENT, HOOK_AFTER_EVENT,
|
||||
HOOK_DETAIL_JOB_NEW, p, NULL);
|
||||
/* JOB_NEW args: (cluster_t*, xlist_t* jlist) — list must have ≥1 job */
|
||||
STUB_JOB_XLIST(jlist);
|
||||
int rc = call_job_hook(HOOK_AFTER_EVENT, HOOK_AFTER_EVENT,
|
||||
HOOK_DETAIL_JOB_NEW, p,
|
||||
(cluster_t*)NULL, &jlist);
|
||||
TEST_ASSERT_EQUAL_INT(0, rc);
|
||||
|
||||
/* LSN must have advanced — WAL entry was written */
|
||||
TEST_ASSERT_GREATER_THAN(lsn_before, p->current_lsn);
|
||||
|
||||
cleanup_hook_test_persist(p);
|
||||
|
|
@ -89,8 +125,10 @@ static void test_job_hook_start_writes_wal_entry(void)
|
|||
|
||||
uint64_t lsn_before = p->current_lsn;
|
||||
|
||||
int rc = persist_job_hook_fun(HOOK_AFTER_EVENT, HOOK_AFTER_EVENT,
|
||||
HOOK_DETAIL_JOB_START, p, NULL);
|
||||
/* JOB_START args: (cluster_t*, job_pidinfo_t*, job_t*) */
|
||||
int rc = call_job_hook(HOOK_AFTER_EVENT, HOOK_AFTER_EVENT,
|
||||
HOOK_DETAIL_JOB_START, p,
|
||||
(cluster_t*)NULL, (void*)NULL, &g_stub_job);
|
||||
TEST_ASSERT_EQUAL_INT(0, rc);
|
||||
TEST_ASSERT_GREATER_THAN(lsn_before, p->current_lsn);
|
||||
|
||||
|
|
@ -105,8 +143,10 @@ static void test_job_hook_exit_writes_wal_entry(void)
|
|||
|
||||
uint64_t lsn_before = p->current_lsn;
|
||||
|
||||
int rc = persist_job_hook_fun(HOOK_AFTER_EVENT, HOOK_AFTER_EVENT,
|
||||
HOOK_DETAIL_JOB_EXIT, p, NULL);
|
||||
/* JOB_EXIT args: (cluster_t*, xce_jobfinish_t*, job_t*) */
|
||||
int rc = call_job_hook(HOOK_AFTER_EVENT, HOOK_AFTER_EVENT,
|
||||
HOOK_DETAIL_JOB_EXIT, p,
|
||||
(cluster_t*)NULL, (void*)NULL, &g_stub_job);
|
||||
TEST_ASSERT_EQUAL_INT(0, rc);
|
||||
TEST_ASSERT_GREATER_THAN(lsn_before, p->current_lsn);
|
||||
|
||||
|
|
@ -121,8 +161,10 @@ static void test_job_hook_fail_writes_wal_entry(void)
|
|||
|
||||
uint64_t lsn_before = p->current_lsn;
|
||||
|
||||
int rc = persist_job_hook_fun(HOOK_AFTER_EVENT, HOOK_AFTER_EVENT,
|
||||
HOOK_DETAIL_JOB_FAIL, p, NULL);
|
||||
/* JOB_FAIL args: (cluster_t*, job_failinfo_t*, job_t*) */
|
||||
int rc = call_job_hook(HOOK_AFTER_EVENT, HOOK_AFTER_EVENT,
|
||||
HOOK_DETAIL_JOB_FAIL, p,
|
||||
(cluster_t*)NULL, (void*)NULL, &g_stub_job);
|
||||
TEST_ASSERT_EQUAL_INT(0, rc);
|
||||
TEST_ASSERT_GREATER_THAN(lsn_before, p->current_lsn);
|
||||
|
||||
|
|
@ -137,8 +179,10 @@ static void test_job_hook_stop_writes_wal_entry(void)
|
|||
|
||||
uint64_t lsn_before = p->current_lsn;
|
||||
|
||||
int rc = persist_job_hook_fun(HOOK_AFTER_EVENT, HOOK_AFTER_EVENT,
|
||||
HOOK_DETAIL_JOB_STOP, p, NULL);
|
||||
/* STOP/RESUME/UPDATE/CKPT/PREASON/RUSAGE default args: (cluster_t*, job_t*) */
|
||||
int rc = call_job_hook(HOOK_AFTER_EVENT, HOOK_AFTER_EVENT,
|
||||
HOOK_DETAIL_JOB_STOP, p,
|
||||
(cluster_t*)NULL, &g_stub_job);
|
||||
TEST_ASSERT_EQUAL_INT(0, rc);
|
||||
TEST_ASSERT_GREATER_THAN(lsn_before, p->current_lsn);
|
||||
|
||||
|
|
@ -153,8 +197,9 @@ static void test_job_hook_resume_writes_wal_entry(void)
|
|||
|
||||
uint64_t lsn_before = p->current_lsn;
|
||||
|
||||
int rc = persist_job_hook_fun(HOOK_AFTER_EVENT, HOOK_AFTER_EVENT,
|
||||
HOOK_DETAIL_JOB_RESUME, p, NULL);
|
||||
int rc = call_job_hook(HOOK_AFTER_EVENT, HOOK_AFTER_EVENT,
|
||||
HOOK_DETAIL_JOB_RESUME, p,
|
||||
(cluster_t*)NULL, &g_stub_job);
|
||||
TEST_ASSERT_EQUAL_INT(0, rc);
|
||||
TEST_ASSERT_GREATER_THAN(lsn_before, p->current_lsn);
|
||||
|
||||
|
|
@ -169,8 +214,9 @@ static void test_job_hook_update_writes_wal_entry(void)
|
|||
|
||||
uint64_t lsn_before = p->current_lsn;
|
||||
|
||||
int rc = persist_job_hook_fun(HOOK_AFTER_EVENT, HOOK_AFTER_EVENT,
|
||||
HOOK_DETAIL_JOB_UPDATE, p, NULL);
|
||||
int rc = call_job_hook(HOOK_AFTER_EVENT, HOOK_AFTER_EVENT,
|
||||
HOOK_DETAIL_JOB_UPDATE, p,
|
||||
(cluster_t*)NULL, &g_stub_job);
|
||||
TEST_ASSERT_EQUAL_INT(0, rc);
|
||||
TEST_ASSERT_GREATER_THAN(lsn_before, p->current_lsn);
|
||||
|
||||
|
|
@ -185,8 +231,9 @@ static void test_job_hook_ckpt_writes_wal_entry(void)
|
|||
|
||||
uint64_t lsn_before = p->current_lsn;
|
||||
|
||||
int rc = persist_job_hook_fun(HOOK_AFTER_EVENT, HOOK_AFTER_EVENT,
|
||||
HOOK_DETAIL_JOB_CKPT, p, NULL);
|
||||
int rc = call_job_hook(HOOK_AFTER_EVENT, HOOK_AFTER_EVENT,
|
||||
HOOK_DETAIL_JOB_CKPT, p,
|
||||
(cluster_t*)NULL, &g_stub_job);
|
||||
TEST_ASSERT_EQUAL_INT(0, rc);
|
||||
TEST_ASSERT_GREATER_THAN(lsn_before, p->current_lsn);
|
||||
|
||||
|
|
@ -201,8 +248,9 @@ static void test_job_hook_rusage_writes_wal_entry(void)
|
|||
|
||||
uint64_t lsn_before = p->current_lsn;
|
||||
|
||||
int rc = persist_job_hook_fun(HOOK_AFTER_EVENT, HOOK_AFTER_EVENT,
|
||||
HOOK_DETAIL_JOB_RUSAGE_UPDATE, p, NULL);
|
||||
int rc = call_job_hook(HOOK_AFTER_EVENT, HOOK_AFTER_EVENT,
|
||||
HOOK_DETAIL_JOB_RUSAGE_UPDATE, p,
|
||||
(cluster_t*)NULL, &g_stub_job);
|
||||
TEST_ASSERT_EQUAL_INT(0, rc);
|
||||
TEST_ASSERT_GREATER_THAN(lsn_before, p->current_lsn);
|
||||
|
||||
|
|
@ -217,8 +265,9 @@ static void test_job_hook_setpreason_writes_wal_entry(void)
|
|||
|
||||
uint64_t lsn_before = p->current_lsn;
|
||||
|
||||
int rc = persist_job_hook_fun(HOOK_AFTER_EVENT, HOOK_AFTER_EVENT,
|
||||
HOOK_DETAIL_SETPREASON, p, NULL);
|
||||
int rc = call_job_hook(HOOK_AFTER_EVENT, HOOK_AFTER_EVENT,
|
||||
HOOK_DETAIL_SETPREASON, p,
|
||||
(cluster_t*)NULL, &g_stub_job);
|
||||
TEST_ASSERT_EQUAL_INT(0, rc);
|
||||
TEST_ASSERT_GREATER_THAN(lsn_before, p->current_lsn);
|
||||
|
||||
|
|
@ -233,8 +282,9 @@ static void test_job_hook_clrpreason_writes_wal_entry(void)
|
|||
|
||||
uint64_t lsn_before = p->current_lsn;
|
||||
|
||||
int rc = persist_job_hook_fun(HOOK_AFTER_EVENT, HOOK_AFTER_EVENT,
|
||||
HOOK_DETAIL_CLRPREASON, p, NULL);
|
||||
int rc = call_job_hook(HOOK_AFTER_EVENT, HOOK_AFTER_EVENT,
|
||||
HOOK_DETAIL_CLRPREASON, p,
|
||||
(cluster_t*)NULL, &g_stub_job);
|
||||
TEST_ASSERT_EQUAL_INT(0, rc);
|
||||
TEST_ASSERT_GREATER_THAN(lsn_before, p->current_lsn);
|
||||
|
||||
|
|
@ -249,8 +299,10 @@ static void test_queue_hook_update_writes_wal_entry(void)
|
|||
|
||||
uint64_t lsn_before = p->current_lsn;
|
||||
|
||||
int rc = persist_queue_hook_fun(HOOK_AFTER_EVENT, HOOK_AFTER_EVENT,
|
||||
HOOK_DETAIL_QUEUE_UPDATE, p, NULL);
|
||||
/* QUEUE_UPDATE args: (cluster_t*, queue_t*) */
|
||||
int rc = call_queue_hook(HOOK_AFTER_EVENT, HOOK_AFTER_EVENT,
|
||||
HOOK_DETAIL_QUEUE_UPDATE, p,
|
||||
(cluster_t*)NULL, &g_stub_queue);
|
||||
TEST_ASSERT_EQUAL_INT(0, rc);
|
||||
TEST_ASSERT_GREATER_THAN(lsn_before, p->current_lsn);
|
||||
|
||||
|
|
@ -265,8 +317,10 @@ static void test_worker_hook_join_writes_wal_entry(void)
|
|||
|
||||
uint64_t lsn_before = p->current_lsn;
|
||||
|
||||
int rc = persist_worker_hook_fun(HOOK_AFTER_EVENT, HOOK_AFTER_EVENT,
|
||||
HOOK_DETAIL_WORKER_JOIN, p, NULL);
|
||||
/* Unified JOIN signature: (cluster_t*, char* name, worker_t*) */
|
||||
int rc = call_worker_hook(HOOK_AFTER_EVENT, HOOK_AFTER_EVENT,
|
||||
HOOK_DETAIL_WORKER_JOIN, p,
|
||||
(cluster_t*)NULL, "stub-host", &g_stub_worker);
|
||||
TEST_ASSERT_EQUAL_INT(0, rc);
|
||||
TEST_ASSERT_GREATER_THAN(lsn_before, p->current_lsn);
|
||||
|
||||
|
|
@ -281,8 +335,10 @@ static void test_worker_hook_update_writes_wal_entry(void)
|
|||
|
||||
uint64_t lsn_before = p->current_lsn;
|
||||
|
||||
int rc = persist_worker_hook_fun(HOOK_AFTER_EVENT, HOOK_AFTER_EVENT,
|
||||
HOOK_DETAIL_WORKER_UPDATE, p, NULL);
|
||||
/* UPDATE signature: (cluster_t*, worker_t*) */
|
||||
int rc = call_worker_hook(HOOK_AFTER_EVENT, HOOK_AFTER_EVENT,
|
||||
HOOK_DETAIL_WORKER_UPDATE, p,
|
||||
(cluster_t*)NULL, &g_stub_worker);
|
||||
TEST_ASSERT_EQUAL_INT(0, rc);
|
||||
TEST_ASSERT_GREATER_THAN(lsn_before, p->current_lsn);
|
||||
|
||||
|
|
@ -297,8 +353,10 @@ static void test_worker_hook_dead_writes_wal_entry(void)
|
|||
|
||||
uint64_t lsn_before = p->current_lsn;
|
||||
|
||||
int rc = persist_worker_hook_fun(HOOK_AFTER_EVENT, HOOK_AFTER_EVENT,
|
||||
HOOK_DETAIL_WORKER_DEAD, p, NULL);
|
||||
/* DEAD signature: (cluster_t*, worker_t*) */
|
||||
int rc = call_worker_hook(HOOK_AFTER_EVENT, HOOK_AFTER_EVENT,
|
||||
HOOK_DETAIL_WORKER_DEAD, p,
|
||||
(cluster_t*)NULL, &g_stub_worker);
|
||||
TEST_ASSERT_EQUAL_INT(0, rc);
|
||||
TEST_ASSERT_GREATER_THAN(lsn_before, p->current_lsn);
|
||||
|
||||
|
|
@ -349,12 +407,13 @@ static void test_job_hook_new_writes_multiple_entries_sequentially(void)
|
|||
uint64_t lsn_before = p->current_lsn;
|
||||
|
||||
/* Fire three hook calls — each must advance LSN */
|
||||
persist_job_hook_fun(HOOK_AFTER_EVENT, HOOK_AFTER_EVENT,
|
||||
HOOK_DETAIL_JOB_NEW, p, NULL);
|
||||
persist_job_hook_fun(HOOK_AFTER_EVENT, HOOK_AFTER_EVENT,
|
||||
HOOK_DETAIL_JOB_START, p, NULL);
|
||||
persist_job_hook_fun(HOOK_AFTER_EVENT, HOOK_AFTER_EVENT,
|
||||
HOOK_DETAIL_JOB_EXIT, p, NULL);
|
||||
STUB_JOB_XLIST(jlist);
|
||||
call_job_hook(HOOK_AFTER_EVENT, HOOK_AFTER_EVENT, HOOK_DETAIL_JOB_NEW, p,
|
||||
(cluster_t*)NULL, &jlist);
|
||||
call_job_hook(HOOK_AFTER_EVENT, HOOK_AFTER_EVENT, HOOK_DETAIL_JOB_START, p,
|
||||
(cluster_t*)NULL, (void*)NULL, &g_stub_job);
|
||||
call_job_hook(HOOK_AFTER_EVENT, HOOK_AFTER_EVENT, HOOK_DETAIL_JOB_EXIT, p,
|
||||
(cluster_t*)NULL, (void*)NULL, &g_stub_job);
|
||||
|
||||
TEST_ASSERT_EQUAL_UINT64(lsn_before + 3, p->current_lsn);
|
||||
|
||||
|
|
|
|||
534
src/controller/testsuite/test_worker_join.c
Normal file
534
src/controller/testsuite/test_worker_join.c
Normal file
|
|
@ -0,0 +1,534 @@
|
|||
/*
|
||||
* test_worker_join.c — TDD tests for worker join refactor
|
||||
*
|
||||
* Task 1.1: Failing tests for apply_join_state (shared status/resource update)
|
||||
* Task 1.2: Failing tests for init_worker (first-time join path)
|
||||
* Task 1.3: Failing tests for rejoin_worker (reconnect update with admin-bit preservation)
|
||||
*
|
||||
* RED phase: apply_join_state / init_worker / rejoin_worker are stubs that
|
||||
* return 0 without modifying state, so all assertions below fail until
|
||||
* Tasks 2–3 provide real implementations.
|
||||
*/
|
||||
|
||||
/* rtti.h must come first — it defines 'var' and 'typeinfo_t' used by kvmgr.h */
|
||||
#include <rtti.h>
|
||||
|
||||
#include <errno.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#include <unity/unity.h>
|
||||
|
||||
#include "cluster.h"
|
||||
#include "kvmgr.h"
|
||||
#include "resobj.h"
|
||||
#include "wobj.h"
|
||||
#include "worker.h"
|
||||
#include "xce/cluster_def.h"
|
||||
#include "xce/types.h"
|
||||
#include "xstring.h"
|
||||
#include "zarray.h"
|
||||
|
||||
/* ── Stubs for controller functions not under test ───────────────────── */
|
||||
|
||||
/*
|
||||
* collect_confvars is called by on_worker_join → worker_push_config.
|
||||
* Stubbed here so worker.c links without pulling in the full dataex.c.
|
||||
*/
|
||||
void collect_confvars(cluster_t* c, collect_check_fn_t fn, xlist_t* l)
|
||||
{
|
||||
(void)c;
|
||||
(void)fn;
|
||||
(void)l;
|
||||
}
|
||||
|
||||
/* ── Forward declarations for functions under test ──────────────────── */
|
||||
|
||||
/*
|
||||
* apply_join_state is non-static in worker.c when XCE_TESTING is defined.
|
||||
*
|
||||
* RED: the current stub body does nothing and returns 0, so assertions
|
||||
* that check the modified state will fail.
|
||||
*/
|
||||
int apply_join_state(cluster_t* c, worker_t* w, endpoint_t* ep,
|
||||
xce_hostconfig_t* hconfig);
|
||||
|
||||
/*
|
||||
* init_worker and rejoin_worker are exported from worker.c.
|
||||
*
|
||||
* RED: stubs return 0 without registering or modifying any entry.
|
||||
*/
|
||||
int init_worker(cluster_t* c, endpoint_t* ep, const char* host,
|
||||
hostattrs_t* attrs, xce_hostconfig_t* hconfig);
|
||||
int rejoin_worker(cluster_t* c, worker_t* w, endpoint_t* ep,
|
||||
hostattrs_t* attrs, xce_hostconfig_t* hconfig);
|
||||
|
||||
/* ── Test fixture ────────────────────────────────────────────────────── */
|
||||
|
||||
/* Resource names and value types matching add_hostattrs_resvals in worker.c */
|
||||
#define N_RES 9
|
||||
static const char* g_res_names[N_RES] = {
|
||||
"type", "model", "cpuf", "maxcpus", "maxmem",
|
||||
"maxswap", "maxtmp", "ndisks", "slots"
|
||||
};
|
||||
static const int g_res_valtypes[N_RES] = {
|
||||
XCE_VAL_STRING, XCE_VAL_STRING,
|
||||
XCE_VAL_NUMERIC, XCE_VAL_NUMERIC, XCE_VAL_NUMERIC,
|
||||
XCE_VAL_NUMERIC, XCE_VAL_NUMERIC, XCE_VAL_NUMERIC,
|
||||
XCE_VAL_NUMERIC
|
||||
};
|
||||
|
||||
typedef struct {
|
||||
cluster_t c;
|
||||
var workermgr;
|
||||
var resmgr;
|
||||
} test_cluster_t;
|
||||
|
||||
static test_cluster_t g_tc;
|
||||
|
||||
static void setup_test_cluster(void)
|
||||
{
|
||||
kvmgr_params_t str_params = {
|
||||
.key_type = KVMGR_KEY_STRING,
|
||||
.nslots = 64,
|
||||
.delta = 16,
|
||||
.datakiller = NULL,
|
||||
};
|
||||
|
||||
memset(&g_tc, 0, sizeof(g_tc));
|
||||
|
||||
g_tc.workermgr = makeInstance(&xkvmgr_type, &str_params);
|
||||
TEST_ASSERT_NOT_NULL(g_tc.workermgr);
|
||||
|
||||
g_tc.resmgr = makeInstance(&xkvmgr_type, &str_params);
|
||||
TEST_ASSERT_NOT_NULL(g_tc.resmgr);
|
||||
|
||||
for (int i = 0; i < N_RES; i++) {
|
||||
xce_resource_t rdef;
|
||||
memset(&rdef, 0, sizeof(rdef));
|
||||
rdef.name = (char*)g_res_names[i];
|
||||
rdef.valtype = g_res_valtypes[i];
|
||||
rdef.flags = 0;
|
||||
rdef.idx = -1;
|
||||
|
||||
xce_resource_t* r = makeResource(g_tc.resmgr, &rdef);
|
||||
TEST_ASSERT_NOT_NULL_MESSAGE(r, g_res_names[i]);
|
||||
}
|
||||
|
||||
g_tc.c.workermgr = g_tc.workermgr;
|
||||
g_tc.c.resmgr = g_tc.resmgr;
|
||||
zarray_init(&g_tc.c.sharedinst, 64, NULL);
|
||||
}
|
||||
|
||||
static void teardown_test_cluster(void)
|
||||
{
|
||||
zarray_fini(&g_tc.c.sharedinst);
|
||||
if (g_tc.resmgr) dereference(g_tc.resmgr);
|
||||
if (g_tc.workermgr) dereference(g_tc.workermgr);
|
||||
memset(&g_tc, 0, sizeof(g_tc));
|
||||
}
|
||||
|
||||
/* Create a worker in the test workermgr; attrs strings must be makeString'd. */
|
||||
static worker_t* make_test_worker(const char* name, hostattrs_t* attrs)
|
||||
{
|
||||
xce_worker_t host;
|
||||
memset(&host, 0, sizeof(host));
|
||||
host.name = (char*)name;
|
||||
host.attrs = attrs;
|
||||
return makeWorker(g_tc.workermgr, &host);
|
||||
}
|
||||
|
||||
/* Retrieve a numeric resource value from a worker's resvals by resource name */
|
||||
static float get_worker_res_fval(worker_t* w, const char* name)
|
||||
{
|
||||
xce_resource_t* r = NULL;
|
||||
lock(g_tc.c.resmgr, RTTI_LOCK_READ);
|
||||
key_find(g_tc.c.resmgr, (void*)name, (void**)&r);
|
||||
unlock(g_tc.c.resmgr);
|
||||
if (r == NULL || r->idx < 0 || r->idx >= w->host.resvals.caps)
|
||||
return -1.0f;
|
||||
xce_val_t* v = (xce_val_t*)w->host.resvals.data[r->idx];
|
||||
return (v != NULL) ? v->u.fvalue : -1.0f;
|
||||
}
|
||||
|
||||
/* ── Task 1.1: Tests for apply_join_state ──────────────────────────── */
|
||||
|
||||
/*
|
||||
* After apply_join_state, XCE_WORKER_UNAVAIL must be cleared. Req 4.1
|
||||
*/
|
||||
static void test_apply_join_state_clears_unavail(void)
|
||||
{
|
||||
setup_test_cluster();
|
||||
|
||||
hostattrs_t attrs;
|
||||
memset(&attrs, 0, sizeof(attrs));
|
||||
attrs.hosttype = makeString("LINUX");
|
||||
attrs.hostmodel = makeString("x86");
|
||||
attrs.cpufactor = 1.0f;
|
||||
attrs.maxcpus = 4;
|
||||
attrs.maxmem = 16;
|
||||
attrs.maxswap = 32;
|
||||
attrs.maxtmp = 64;
|
||||
attrs.ndisks = 1;
|
||||
|
||||
worker_t* w = make_test_worker("host-ajs-1", &attrs);
|
||||
TEST_ASSERT_NOT_NULL(w);
|
||||
|
||||
lock(w, RTTI_LOCK_WRITE);
|
||||
w->host.status |= XCE_WORKER_UNAVAIL;
|
||||
unlock(w);
|
||||
|
||||
xce_hostconfig_t hconfig;
|
||||
memset(&hconfig, 0, sizeof(hconfig));
|
||||
hconfig.hosttype = (char*)"!"; /* no type override */
|
||||
hconfig.hostmodel = (char*)"!"; /* no model override */
|
||||
hconfig.mxj = 2;
|
||||
|
||||
endpoint_t ep;
|
||||
memset(&ep, 0, sizeof(ep));
|
||||
|
||||
lock(w, RTTI_LOCK_WRITE);
|
||||
int rc = apply_join_state(&g_tc.c, w, &ep, &hconfig);
|
||||
unlock(w);
|
||||
|
||||
TEST_ASSERT_EQUAL_INT(0, rc);
|
||||
TEST_ASSERT_FALSE_MESSAGE(w->host.status & XCE_WORKER_UNAVAIL,
|
||||
"UNAVAIL must be cleared after apply_join_state");
|
||||
|
||||
teardown_test_cluster();
|
||||
printf("test_apply_join_state_clears_unavail: PASS\n");
|
||||
}
|
||||
|
||||
/*
|
||||
* When hconfig provides non-"!" overrides, hosttype and hostmodel must
|
||||
* match those values after apply_join_state. Req 4.2
|
||||
*/
|
||||
static void test_apply_join_state_applies_hosttype_override(void)
|
||||
{
|
||||
setup_test_cluster();
|
||||
|
||||
hostattrs_t attrs;
|
||||
memset(&attrs, 0, sizeof(attrs));
|
||||
attrs.hosttype = makeString("OLD_TYPE");
|
||||
attrs.hostmodel = makeString("OLD_MODEL");
|
||||
attrs.cpufactor = 1.0f;
|
||||
attrs.maxcpus = 2;
|
||||
attrs.maxmem = 8;
|
||||
attrs.maxswap = 16;
|
||||
attrs.maxtmp = 32;
|
||||
attrs.ndisks = 1;
|
||||
|
||||
worker_t* w = make_test_worker("host-ajs-2", &attrs);
|
||||
TEST_ASSERT_NOT_NULL(w);
|
||||
|
||||
xce_hostconfig_t hconfig;
|
||||
memset(&hconfig, 0, sizeof(hconfig));
|
||||
hconfig.hosttype = (char*)"LINUX64";
|
||||
hconfig.hostmodel = (char*)"x86_64";
|
||||
hconfig.mxj = 4;
|
||||
|
||||
endpoint_t ep;
|
||||
memset(&ep, 0, sizeof(ep));
|
||||
|
||||
lock(w, RTTI_LOCK_WRITE);
|
||||
int rc = apply_join_state(&g_tc.c, w, &ep, &hconfig);
|
||||
unlock(w);
|
||||
|
||||
TEST_ASSERT_EQUAL_INT(0, rc);
|
||||
TEST_ASSERT_EQUAL_STRING_MESSAGE("LINUX64", w->host.attrs->hosttype,
|
||||
"hosttype must be overridden from hconfig");
|
||||
TEST_ASSERT_EQUAL_STRING_MESSAGE("x86_64", w->host.attrs->hostmodel,
|
||||
"hostmodel must be overridden from hconfig");
|
||||
|
||||
teardown_test_cluster();
|
||||
printf("test_apply_join_state_applies_hosttype_override: PASS\n");
|
||||
}
|
||||
|
||||
/*
|
||||
* After apply_join_state, 'slots' equals hconfig.mxj and numeric host-attr
|
||||
* resources match the worker's attrs. Req 4.2, 4.3
|
||||
*/
|
||||
static void test_apply_join_state_updates_resource_values(void)
|
||||
{
|
||||
setup_test_cluster();
|
||||
|
||||
hostattrs_t attrs;
|
||||
memset(&attrs, 0, sizeof(attrs));
|
||||
attrs.hosttype = makeString("LINUX");
|
||||
attrs.hostmodel = makeString("x86");
|
||||
attrs.cpufactor = 3.0f;
|
||||
attrs.maxcpus = 8;
|
||||
attrs.maxmem = 64;
|
||||
attrs.maxswap = 128;
|
||||
attrs.maxtmp = 256;
|
||||
attrs.ndisks = 4;
|
||||
|
||||
worker_t* w = make_test_worker("host-ajs-3", &attrs);
|
||||
TEST_ASSERT_NOT_NULL(w);
|
||||
|
||||
xce_hostconfig_t hconfig;
|
||||
memset(&hconfig, 0, sizeof(hconfig));
|
||||
hconfig.hosttype = (char*)"!";
|
||||
hconfig.hostmodel = (char*)"!";
|
||||
hconfig.mxj = 6;
|
||||
|
||||
endpoint_t ep;
|
||||
memset(&ep, 0, sizeof(ep));
|
||||
|
||||
lock(w, RTTI_LOCK_WRITE);
|
||||
int rc = apply_join_state(&g_tc.c, w, &ep, &hconfig);
|
||||
unlock(w);
|
||||
|
||||
TEST_ASSERT_EQUAL_INT(0, rc);
|
||||
|
||||
TEST_ASSERT_EQUAL_FLOAT_MESSAGE(6.0f, get_worker_res_fval(w, "slots"),
|
||||
"slots must equal hconfig.mxj");
|
||||
TEST_ASSERT_EQUAL_FLOAT(3.0f, get_worker_res_fval(w, "cpuf"));
|
||||
TEST_ASSERT_EQUAL_FLOAT(8.0f, get_worker_res_fval(w, "maxcpus"));
|
||||
TEST_ASSERT_EQUAL_FLOAT(64.0f, get_worker_res_fval(w, "maxmem"));
|
||||
TEST_ASSERT_EQUAL_FLOAT(128.0f, get_worker_res_fval(w, "maxswap"));
|
||||
TEST_ASSERT_EQUAL_FLOAT(256.0f, get_worker_res_fval(w, "maxtmp"));
|
||||
TEST_ASSERT_EQUAL_FLOAT(4.0f, get_worker_res_fval(w, "ndisks"));
|
||||
|
||||
teardown_test_cluster();
|
||||
printf("test_apply_join_state_updates_resource_values: PASS\n");
|
||||
}
|
||||
|
||||
/* ── Task 1.2: Tests for init_worker ────────────────────────────────── */
|
||||
|
||||
/*
|
||||
* After init_worker for a new hostname, key_find must find the entry and
|
||||
* return a non-negative index. Req 2.1, 2.3
|
||||
*/
|
||||
static void test_init_worker_creates_new_entry(void)
|
||||
{
|
||||
setup_test_cluster();
|
||||
|
||||
hostattrs_t attrs;
|
||||
memset(&attrs, 0, sizeof(attrs));
|
||||
attrs.hosttype = makeString("LINUX");
|
||||
attrs.hostmodel = makeString("x86");
|
||||
attrs.cpufactor = 1.0f;
|
||||
attrs.maxcpus = 4;
|
||||
attrs.maxmem = 16;
|
||||
attrs.maxswap = 32;
|
||||
attrs.maxtmp = 64;
|
||||
attrs.ndisks = 1;
|
||||
|
||||
xce_hostconfig_t hconfig;
|
||||
memset(&hconfig, 0, sizeof(hconfig));
|
||||
hconfig.hosttype = (char*)"!";
|
||||
hconfig.hostmodel = (char*)"!";
|
||||
hconfig.mxj = 4;
|
||||
|
||||
endpoint_t ep;
|
||||
memset(&ep, 0, sizeof(ep));
|
||||
|
||||
int idx = init_worker(&g_tc.c, &ep, "new-host-1", &attrs, &hconfig);
|
||||
TEST_ASSERT_GREATER_OR_EQUAL_MESSAGE(0, idx,
|
||||
"init_worker must return non-negative index");
|
||||
|
||||
worker_t* w = NULL;
|
||||
lock(g_tc.c.workermgr, RTTI_LOCK_READ);
|
||||
int rc = key_find(g_tc.c.workermgr, (void*)"new-host-1", (void**)&w);
|
||||
unlock(g_tc.c.workermgr);
|
||||
TEST_ASSERT_GREATER_THAN_MESSAGE(-1, rc,
|
||||
"worker must be registered in workermgr after init");
|
||||
TEST_ASSERT_NOT_NULL_MESSAGE(w, "key_find must return a valid worker pointer");
|
||||
|
||||
teardown_test_cluster();
|
||||
printf("test_init_worker_creates_new_entry: PASS\n");
|
||||
}
|
||||
|
||||
/*
|
||||
* The index returned by init_worker must match w->idx. Req 2.3
|
||||
*/
|
||||
static void test_init_worker_returns_correct_idx(void)
|
||||
{
|
||||
setup_test_cluster();
|
||||
|
||||
hostattrs_t attrs;
|
||||
memset(&attrs, 0, sizeof(attrs));
|
||||
attrs.hosttype = makeString("LINUX");
|
||||
attrs.hostmodel = makeString("x86");
|
||||
attrs.cpufactor = 1.0f;
|
||||
attrs.maxcpus = 2;
|
||||
attrs.maxmem = 8;
|
||||
attrs.maxswap = 16;
|
||||
attrs.maxtmp = 32;
|
||||
attrs.ndisks = 1;
|
||||
|
||||
xce_hostconfig_t hconfig;
|
||||
memset(&hconfig, 0, sizeof(hconfig));
|
||||
hconfig.hosttype = (char*)"!";
|
||||
hconfig.hostmodel = (char*)"!";
|
||||
hconfig.mxj = 2;
|
||||
|
||||
endpoint_t ep;
|
||||
memset(&ep, 0, sizeof(ep));
|
||||
|
||||
int idx = init_worker(&g_tc.c, &ep, "new-host-2", &attrs, &hconfig);
|
||||
TEST_ASSERT_GREATER_OR_EQUAL(0, idx);
|
||||
|
||||
worker_t* w = NULL;
|
||||
lock(g_tc.c.workermgr, RTTI_LOCK_READ);
|
||||
key_find(g_tc.c.workermgr, (void*)"new-host-2", (void**)&w);
|
||||
unlock(g_tc.c.workermgr);
|
||||
TEST_ASSERT_NOT_NULL(w);
|
||||
TEST_ASSERT_EQUAL_INT_MESSAGE(idx, w->idx,
|
||||
"returned idx must match the worker's idx field");
|
||||
|
||||
teardown_test_cluster();
|
||||
printf("test_init_worker_returns_correct_idx: PASS\n");
|
||||
}
|
||||
|
||||
/* ── Task 1.3: Tests for rejoin_worker ──────────────────────────────── */
|
||||
|
||||
/*
|
||||
* rejoin_worker must update hostattrs and ep on the existing entry. Req 3.1, 3.2
|
||||
*/
|
||||
static void test_rejoin_worker_updates_attrs_and_ep(void)
|
||||
{
|
||||
setup_test_cluster();
|
||||
|
||||
hostattrs_t old_attrs;
|
||||
memset(&old_attrs, 0, sizeof(old_attrs));
|
||||
old_attrs.hosttype = makeString("OLD_TYPE");
|
||||
old_attrs.hostmodel = makeString("OLD_MODEL");
|
||||
old_attrs.cpufactor = 1.0f;
|
||||
old_attrs.maxcpus = 2;
|
||||
old_attrs.maxmem = 8;
|
||||
old_attrs.maxswap = 16;
|
||||
old_attrs.maxtmp = 32;
|
||||
old_attrs.ndisks = 1;
|
||||
|
||||
worker_t* w = make_test_worker("rejoin-host-1", &old_attrs);
|
||||
TEST_ASSERT_NOT_NULL(w);
|
||||
|
||||
hostattrs_t new_attrs;
|
||||
memset(&new_attrs, 0, sizeof(new_attrs));
|
||||
new_attrs.hosttype = makeString("NEW_TYPE");
|
||||
new_attrs.hostmodel = makeString("NEW_MODEL");
|
||||
new_attrs.cpufactor = 2.0f;
|
||||
new_attrs.maxcpus = 8;
|
||||
new_attrs.maxmem = 64;
|
||||
new_attrs.maxswap = 128;
|
||||
new_attrs.maxtmp = 256;
|
||||
new_attrs.ndisks = 2;
|
||||
|
||||
xce_hostconfig_t hconfig;
|
||||
memset(&hconfig, 0, sizeof(hconfig));
|
||||
hconfig.hosttype = (char*)"!";
|
||||
hconfig.hostmodel = (char*)"!";
|
||||
hconfig.mxj = 4;
|
||||
|
||||
endpoint_t ep;
|
||||
memset(&ep, 0, sizeof(ep));
|
||||
|
||||
int idx = rejoin_worker(&g_tc.c, w, &ep, &new_attrs, &hconfig);
|
||||
TEST_ASSERT_GREATER_OR_EQUAL_MESSAGE(0, idx,
|
||||
"rejoin_worker must return non-negative index");
|
||||
|
||||
TEST_ASSERT_EQUAL_STRING_MESSAGE("NEW_TYPE", w->host.attrs->hosttype,
|
||||
"hosttype must be updated to new attrs value");
|
||||
TEST_ASSERT_EQUAL_STRING_MESSAGE("NEW_MODEL", w->host.attrs->hostmodel,
|
||||
"hostmodel must be updated to new attrs value");
|
||||
TEST_ASSERT_EQUAL_FLOAT_MESSAGE(2.0f, w->host.attrs->cpufactor,
|
||||
"cpufactor must be updated to new value");
|
||||
TEST_ASSERT_EQUAL_PTR_MESSAGE(&ep, w->ep,
|
||||
"ep must be updated to the new endpoint");
|
||||
|
||||
/* Release the test's own references to the new strings */
|
||||
dereference(new_attrs.hosttype);
|
||||
dereference(new_attrs.hostmodel);
|
||||
|
||||
teardown_test_cluster();
|
||||
printf("test_rejoin_worker_updates_attrs_and_ep: PASS\n");
|
||||
}
|
||||
|
||||
/*
|
||||
* Admin-set status bits must survive a rejoin_worker call. Req 3.3
|
||||
*/
|
||||
static void test_rejoin_worker_preserves_admin_bits(void)
|
||||
{
|
||||
setup_test_cluster();
|
||||
|
||||
hostattrs_t attrs;
|
||||
memset(&attrs, 0, sizeof(attrs));
|
||||
attrs.hosttype = makeString("LINUX");
|
||||
attrs.hostmodel = makeString("x86");
|
||||
attrs.cpufactor = 1.0f;
|
||||
attrs.maxcpus = 4;
|
||||
attrs.maxmem = 16;
|
||||
attrs.maxswap = 32;
|
||||
attrs.maxtmp = 64;
|
||||
attrs.ndisks = 1;
|
||||
|
||||
worker_t* w = make_test_worker("rejoin-host-2", &attrs);
|
||||
TEST_ASSERT_NOT_NULL(w);
|
||||
|
||||
/* Set all admin bits before the rejoin */
|
||||
lock(w, RTTI_LOCK_WRITE);
|
||||
w->host.status |= (XCE_WORKER_LOCKEDU | XCE_WORKER_CLOSED
|
||||
| XCE_WORKER_CLOSED_BY_ADMIN);
|
||||
unlock(w);
|
||||
|
||||
hostattrs_t new_attrs;
|
||||
memset(&new_attrs, 0, sizeof(new_attrs));
|
||||
new_attrs.hosttype = makeString("LINUX");
|
||||
new_attrs.hostmodel = makeString("x86");
|
||||
new_attrs.cpufactor = 2.0f;
|
||||
new_attrs.maxcpus = 8;
|
||||
new_attrs.maxmem = 32;
|
||||
new_attrs.maxswap = 64;
|
||||
new_attrs.maxtmp = 128;
|
||||
new_attrs.ndisks = 2;
|
||||
|
||||
xce_hostconfig_t hconfig;
|
||||
memset(&hconfig, 0, sizeof(hconfig));
|
||||
hconfig.hosttype = (char*)"!";
|
||||
hconfig.hostmodel = (char*)"!";
|
||||
hconfig.mxj = 4;
|
||||
|
||||
endpoint_t ep;
|
||||
memset(&ep, 0, sizeof(ep));
|
||||
|
||||
rejoin_worker(&g_tc.c, w, &ep, &new_attrs, &hconfig);
|
||||
|
||||
TEST_ASSERT_TRUE_MESSAGE(w->host.status & XCE_WORKER_LOCKEDU,
|
||||
"LOCKEDU must be preserved after rejoin");
|
||||
TEST_ASSERT_TRUE_MESSAGE(w->host.status & XCE_WORKER_CLOSED,
|
||||
"CLOSED must be preserved after rejoin");
|
||||
TEST_ASSERT_TRUE_MESSAGE(w->host.status & XCE_WORKER_CLOSED_BY_ADMIN,
|
||||
"CLOSED_BY_ADMIN must be preserved after rejoin");
|
||||
|
||||
dereference(new_attrs.hosttype);
|
||||
dereference(new_attrs.hostmodel);
|
||||
|
||||
teardown_test_cluster();
|
||||
printf("test_rejoin_worker_preserves_admin_bits: PASS\n");
|
||||
}
|
||||
|
||||
/* ── Dispatch ───────────────────────────────────────────────────────── */
|
||||
|
||||
void test_worker_join(void)
|
||||
{
|
||||
printf("\n>> worker join refactor tests\n\n");
|
||||
|
||||
/* xstring_table_init must be called once before any makeString usage */
|
||||
xstring_table_init(139);
|
||||
|
||||
/* Task 1.1: apply_join_state (shared status/resource update) */
|
||||
RUN_TEST(test_apply_join_state_clears_unavail);
|
||||
RUN_TEST(test_apply_join_state_applies_hosttype_override);
|
||||
RUN_TEST(test_apply_join_state_updates_resource_values);
|
||||
|
||||
/* Task 1.2: init_worker (first-time join) */
|
||||
RUN_TEST(test_init_worker_creates_new_entry);
|
||||
RUN_TEST(test_init_worker_returns_correct_idx);
|
||||
|
||||
/* Task 1.3: rejoin_worker (reconnect with admin-bit preservation) */
|
||||
RUN_TEST(test_rejoin_worker_updates_attrs_and_ep);
|
||||
RUN_TEST(test_rejoin_worker_preserves_admin_bits);
|
||||
}
|
||||
|
|
@ -146,6 +146,7 @@ func (p *HostsPagePrinter) formatRows(hosts *models.HostsResponse, truncatFlag b
|
|||
truncatFlag = false
|
||||
}
|
||||
for _, host := range hosts.Data.Rows {
|
||||
isUnavail := host.HostStatus == "UNAVAIL"
|
||||
row := make([]string, 0, len(p.fieldFormats))
|
||||
for _, format := range p.fieldFormats {
|
||||
width := p.columnWidths[format.name]
|
||||
|
|
@ -156,17 +157,41 @@ func (p *HostsPagePrinter) formatRows(hosts *models.HostsResponse, truncatFlag b
|
|||
case models.HEADER_HOST_STATUS:
|
||||
row = appendOrDefault(row, FormatStringField(host.HostStatus, width, truncatFlag, true), "-")
|
||||
case models.HEADER_HOST_HOST_TYPE:
|
||||
row = appendOrDefault(row, FormatStringField(host.HostType, width, truncatFlag, true), "-")
|
||||
if isUnavail {
|
||||
row = append(row, FormatStringField("UNKNOWN", width, truncatFlag, true))
|
||||
} else {
|
||||
row = appendOrDefault(row, FormatStringField(host.HostType, width, truncatFlag, true), "-")
|
||||
}
|
||||
case models.HEADER_HOST_NCPUS:
|
||||
row = appendOrDefault(row, fmt.Sprintf("%"+align+"*d", width, host.NumCPUs), "0")
|
||||
if isUnavail {
|
||||
row = append(row, FormatStringField("-", width, truncatFlag, true))
|
||||
} else {
|
||||
row = appendOrDefault(row, fmt.Sprintf("%"+align+"*d", width, host.NumCPUs), "0")
|
||||
}
|
||||
case models.HEADER_HOST_MAX_SLOT:
|
||||
row = appendOrDefault(row, fmt.Sprintf("%"+align+"*d", width, host.MaxSlot), "0")
|
||||
if isUnavail {
|
||||
row = append(row, FormatStringField("-", width, truncatFlag, true))
|
||||
} else {
|
||||
row = appendOrDefault(row, fmt.Sprintf("%"+align+"*d", width, host.MaxSlot), "0")
|
||||
}
|
||||
case models.HEADER_HOST_NUM_RUN:
|
||||
row = appendOrDefault(row, fmt.Sprintf("%"+align+"*d", width, host.NumRun), "0")
|
||||
if isUnavail {
|
||||
row = append(row, FormatStringField("-", width, truncatFlag, true))
|
||||
} else {
|
||||
row = appendOrDefault(row, fmt.Sprintf("%"+align+"*d", width, host.NumRun), "0")
|
||||
}
|
||||
case models.HEADER_HOST_NUM_SSUSP:
|
||||
row = appendOrDefault(row, fmt.Sprintf("%"+align+"*d", width, host.NumSSusp), "0")
|
||||
if isUnavail {
|
||||
row = append(row, FormatStringField("-", width, truncatFlag, true))
|
||||
} else {
|
||||
row = appendOrDefault(row, fmt.Sprintf("%"+align+"*d", width, host.NumSSusp), "0")
|
||||
}
|
||||
case models.HEADER_HOST_NUM_USUSP:
|
||||
row = appendOrDefault(row, fmt.Sprintf("%"+align+"*d", width, host.NumUSusp), "0")
|
||||
if isUnavail {
|
||||
row = append(row, FormatStringField("-", width, truncatFlag, true))
|
||||
} else {
|
||||
row = appendOrDefault(row, fmt.Sprintf("%"+align+"*d", width, host.NumUSusp), "0")
|
||||
}
|
||||
}
|
||||
}
|
||||
rows = append(rows, row)
|
||||
|
|
@ -188,6 +213,7 @@ func (p *HostsPagePrinter) printJsonPage(hosts *models.HostsResponse) {
|
|||
|
||||
func printHostsVerboseOutput(hosts *models.HostsResponse) {
|
||||
for _, host := range hosts.Data.Rows {
|
||||
isUnavail := host.HostStatus == "UNAVAIL"
|
||||
fmt.Printf("HOST %s\n", host.HostName)
|
||||
// table 1
|
||||
headers := models.HostDetailHeaders
|
||||
|
|
@ -199,19 +225,43 @@ func printHostsVerboseOutput(hosts *models.HostsResponse) {
|
|||
case models.HEADER_HOST_STATUS:
|
||||
val = host.HostStatus
|
||||
case models.HEADER_HOST_CPUF:
|
||||
val = fmt.Sprintf("%.2f", host.CPUFactor)
|
||||
if isUnavail {
|
||||
val = "-"
|
||||
} else {
|
||||
val = fmt.Sprintf("%.2f", host.CPUFactor)
|
||||
}
|
||||
case models.HEADER_HOST_JLU:
|
||||
val = "-"
|
||||
case models.HEADER_HOST_MAX:
|
||||
val = fmt.Sprintf("%d", host.MaxSlot)
|
||||
if isUnavail {
|
||||
val = "-"
|
||||
} else {
|
||||
val = fmt.Sprintf("%d", host.MaxSlot)
|
||||
}
|
||||
case models.HEADER_HOST_NJOBS:
|
||||
val = fmt.Sprintf("%d", host.NumRun)
|
||||
if isUnavail {
|
||||
val = "-"
|
||||
} else {
|
||||
val = fmt.Sprintf("%d", host.NumRun)
|
||||
}
|
||||
case models.HEADER_HOST_RUN:
|
||||
val = fmt.Sprintf("%d", host.NumRun)
|
||||
if isUnavail {
|
||||
val = "-"
|
||||
} else {
|
||||
val = fmt.Sprintf("%d", host.NumRun)
|
||||
}
|
||||
case models.HEADER_HOST_SSUSP:
|
||||
val = fmt.Sprintf("%d", host.NumSSusp)
|
||||
if isUnavail {
|
||||
val = "-"
|
||||
} else {
|
||||
val = fmt.Sprintf("%d", host.NumSSusp)
|
||||
}
|
||||
case models.HEADER_HOST_USUSP:
|
||||
val = fmt.Sprintf("%d", host.NumUSusp)
|
||||
if isUnavail {
|
||||
val = "-"
|
||||
} else {
|
||||
val = fmt.Sprintf("%d", host.NumUSusp)
|
||||
}
|
||||
case models.HEADER_HOST_RSV:
|
||||
val = "-"
|
||||
case models.HEADER_HOST_DISPT_WINDOW:
|
||||
|
|
@ -234,8 +284,14 @@ func printHostsVerboseOutput(hosts *models.HostsResponse) {
|
|||
|
||||
// table2
|
||||
headers2 := append([]string{""}, models.HostLoadSchedulingHeaders...)
|
||||
totalVals := []string{"Total", "0.0", "0.0", "0.0", "1%", "0.0", "33", "1", "1137", "51G", "3.9G", "6.8G", "8", "0.0"}
|
||||
reserveVals := []string{"Reserved", "0.0", "0.0", "0.0", "0%", "0.0", "0", "0", "0", "0M", "0M", "0M", "-", "-"}
|
||||
var totalVals, reserveVals []string
|
||||
if isUnavail {
|
||||
totalVals = []string{"Total", "-", "-", "-", "-", "-", "-", "-", "-", "-", "-", "-", "-", "-"}
|
||||
reserveVals = []string{"Reserved", "-", "-", "-", "-", "-", "-", "-", "-", "-", "-", "-", "-", "-"}
|
||||
} else {
|
||||
totalVals = []string{"Total", "0.0", "0.0", "0.0", "1%", "0.0", "33", "1", "1137", "51G", "3.9G", "6.8G", "8", "0.0"}
|
||||
reserveVals = []string{"Reserved", "0.0", "0.0", "0.0", "0%", "0.0", "0", "0", "0", "0M", "0M", "0M", "-", "-"}
|
||||
}
|
||||
|
||||
widths2 := make([]int, len(headers2))
|
||||
for i := range headers2 {
|
||||
|
|
|
|||
103
src/frontend/xcecli/common/output/page_print_hosts_test.go
Normal file
103
src/frontend/xcecli/common/output/page_print_hosts_test.go
Normal file
|
|
@ -0,0 +1,103 @@
|
|||
package output
|
||||
|
||||
import (
|
||||
"strings"
|
||||
"testing"
|
||||
"xcecli/models"
|
||||
)
|
||||
|
||||
func makeHostsResponse(hosts ...models.XCEHost) *models.HostsResponse {
|
||||
r := &models.HostsResponse{}
|
||||
r.Data.Rows = hosts
|
||||
r.Data.Count = len(hosts)
|
||||
return r
|
||||
}
|
||||
|
||||
func TestFormatRows_UnavailShowsDashes(t *testing.T) {
|
||||
host := models.XCEHost{
|
||||
HostName: "pc13v",
|
||||
HostStatus: "UNAVAIL",
|
||||
NumCPUs: 16,
|
||||
MaxSlot: 32,
|
||||
NumRun: 5,
|
||||
NumSSusp: 2,
|
||||
NumUSusp: 1,
|
||||
HostType: "*6_64-linux-gnu",
|
||||
}
|
||||
resp := makeHostsResponse(host)
|
||||
|
||||
p := NewHostsPagePrinter()
|
||||
p.PrepareForPrinting(resp)
|
||||
rows := p.formatRows(resp, false)
|
||||
|
||||
if len(rows) != 1 {
|
||||
t.Fatalf("expected 1 row, got %d", len(rows))
|
||||
}
|
||||
row := rows[0]
|
||||
|
||||
// Build a map from trimmed value by column header index.
|
||||
headers := p.fieldFormats
|
||||
vals := make(map[string]string, len(headers))
|
||||
for i, f := range headers {
|
||||
vals[f.name] = strings.TrimSpace(row[i])
|
||||
}
|
||||
|
||||
for _, col := range []string{
|
||||
models.HEADER_HOST_NCPUS,
|
||||
models.HEADER_HOST_MAX_SLOT,
|
||||
models.HEADER_HOST_NUM_RUN,
|
||||
models.HEADER_HOST_NUM_SSUSP,
|
||||
models.HEADER_HOST_NUM_USUSP,
|
||||
} {
|
||||
if vals[col] != "-" {
|
||||
t.Errorf("UNAVAIL host: %s = %q, want \"-\"", col, vals[col])
|
||||
}
|
||||
}
|
||||
|
||||
if vals[models.HEADER_HOST_HOST_TYPE] != "UNKNOWN" {
|
||||
t.Errorf("UNAVAIL host: HOST_TYPE = %q, want \"UNKNOWN\"", vals[models.HEADER_HOST_HOST_TYPE])
|
||||
}
|
||||
}
|
||||
|
||||
func TestFormatRows_AvailableShowsValues(t *testing.T) {
|
||||
host := models.XCEHost{
|
||||
HostName: "pc14v",
|
||||
HostStatus: "OK",
|
||||
NumCPUs: 16,
|
||||
MaxSlot: 32,
|
||||
NumRun: 5,
|
||||
NumSSusp: 2,
|
||||
NumUSusp: 1,
|
||||
HostType: "linux",
|
||||
}
|
||||
resp := makeHostsResponse(host)
|
||||
|
||||
p := NewHostsPagePrinter()
|
||||
p.PrepareForPrinting(resp)
|
||||
rows := p.formatRows(resp, false)
|
||||
|
||||
if len(rows) != 1 {
|
||||
t.Fatalf("expected 1 row, got %d", len(rows))
|
||||
}
|
||||
row := rows[0]
|
||||
|
||||
headers := p.fieldFormats
|
||||
vals := make(map[string]string, len(headers))
|
||||
for i, f := range headers {
|
||||
vals[f.name] = strings.TrimSpace(row[i])
|
||||
}
|
||||
|
||||
checks := map[string]string{
|
||||
models.HEADER_HOST_NCPUS: "16",
|
||||
models.HEADER_HOST_MAX_SLOT: "32",
|
||||
models.HEADER_HOST_NUM_RUN: "5",
|
||||
models.HEADER_HOST_NUM_SSUSP: "2",
|
||||
models.HEADER_HOST_NUM_USUSP: "1",
|
||||
models.HEADER_HOST_HOST_TYPE: "linux",
|
||||
}
|
||||
for col, want := range checks {
|
||||
if vals[col] != want {
|
||||
t.Errorf("OK host: %s = %q, want %q", col, vals[col], want)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -5,6 +5,19 @@ import (
|
|||
"time"
|
||||
)
|
||||
|
||||
// parseJobTime parses a time string in RFC3339 or "2006-01-02 15:04:05" format.
|
||||
func parseJobTime(s string) (time.Time, error) {
|
||||
if s == "" {
|
||||
return time.Time{}, fmt.Errorf("empty time string")
|
||||
}
|
||||
for _, layout := range []string{time.RFC3339Nano, time.RFC3339, "2006-01-02 15:04:05"} {
|
||||
if t, err := time.Parse(layout, s); err == nil {
|
||||
return t, nil
|
||||
}
|
||||
}
|
||||
return time.Time{}, fmt.Errorf("cannot parse time %q", s)
|
||||
}
|
||||
|
||||
// unixToTime converts a Unix-second timestamp to time.Time.
|
||||
// Returns (time, true) for ts > 0; (zero, false) for unset/zero values.
|
||||
func unixToTime(ts int64) (time.Time, bool) {
|
||||
|
|
|
|||
|
|
@ -82,19 +82,23 @@ static int copy_worker(worker_t* dst, xce_worker_t* src)
|
|||
|
||||
static int override_worker(worker_t* dst, xce_worker_t* w)
|
||||
{
|
||||
#define XCE_WORKER_ADMIN_MASK \
|
||||
(XCE_WORKER_LOCKEDU | XCE_WORKER_CLOSED | XCE_WORKER_HIDDEN | XCE_WORKER_CLOSED_BY_ADMIN)
|
||||
#define XCE_WORKER_ADMIN_MASK \
|
||||
(XCE_WORKER_LOCKEDU | XCE_WORKER_CLOSED | XCE_WORKER_HIDDEN \
|
||||
| XCE_WORKER_CLOSED_BY_ADMIN)
|
||||
uint32_t admin_status = 0;
|
||||
int rc = 0;
|
||||
int idx;
|
||||
|
||||
admin_status = dst->host.status & XCE_WORKER_ADMIN_MASK;
|
||||
idx = dst->idx;
|
||||
|
||||
kill_worker(dst);
|
||||
|
||||
rc = copy_worker(dst, w);
|
||||
if (rc >= 0) {
|
||||
dst->host.status |= admin_status;
|
||||
dst->ep = NULL;
|
||||
dst->ep = NULL;
|
||||
dst->idx = idx;
|
||||
}
|
||||
|
||||
return rc;
|
||||
|
|
@ -231,6 +235,8 @@ static worker_t* makeWorker_(var workermgr, xce_worker_t* w)
|
|||
errno = -rc;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
worker->idx = rc;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue