feat(controller-status): ctrlstatus client command (task 4.1)
This commit is contained in:
parent
03813bf560
commit
9e87d2c92e
2 changed files with 123 additions and 1 deletions
|
|
@ -43,7 +43,7 @@
|
||||||
- _Depends: 3.1_
|
- _Depends: 3.1_
|
||||||
|
|
||||||
- [ ] 4. Test client command + wait-primary integration
|
- [ ] 4. Test client command + wait-primary integration
|
||||||
- [ ] 4.1 Add the `ctrlstatus` client command
|
- [x] 4.1 Add the `ctrlstatus` client command
|
||||||
- Add a client command that issues the any-node status call and prints the fields in human-readable form: the current primary, the answering node framed as "answered by <node> (<role>)" distinct from the primary, the configured controllers and count, the loaded config version and committed config version, the content checksum as hex, term, commit/apply positions, fenced state, and cluster name; render an empty primary as "unknown". The command exit status reflects success or failure of the query.
|
- Add a client command that issues the any-node status call and prints the fields in human-readable form: the current primary, the answering node framed as "answered by <node> (<role>)" distinct from the primary, the configured controllers and count, the loaded config version and committed config version, the content checksum as hex, term, commit/apply positions, fenced state, and cluster name; render an empty primary as "unknown". The command exit status reflects success or failure of the query.
|
||||||
- Observable: piping `ctrlstatus` through the client prints the status fields (with "answered by" and "unknown" rendering) and exits zero on success / non-zero with the error printed on failure.
|
- Observable: piping `ctrlstatus` through the client prints the status fields (with "answered by" and "unknown" rendering) and exits zero on success / non-zero with the error printed on failure.
|
||||||
- _Requirements: 1.3, 3.3, 5.1, 5.2, 8.1, 8.2, 8.3_
|
- _Requirements: 1.3, 3.3, 5.1, 5.2, 8.1, 8.2, 8.3_
|
||||||
|
|
|
||||||
|
|
@ -120,6 +120,7 @@ extern int cmd_echo(int, char*[]);
|
||||||
extern int cmd_expect_jobs(int, char*[]);
|
extern int cmd_expect_jobs(int, char*[]);
|
||||||
extern int cmd_expect_job(int, char*[]);
|
extern int cmd_expect_job(int, char*[]);
|
||||||
extern int cmd_wait_primary(int, char*[]);
|
extern int cmd_wait_primary(int, char*[]);
|
||||||
|
extern int cmd_ctrlstatus(int, char*[]);
|
||||||
static int worker_house_keeping(void*);
|
static int worker_house_keeping(void*);
|
||||||
|
|
||||||
/* Session verdict / loop state (xce-test-e2e-primitives task 1.1).
|
/* Session verdict / loop state (xce-test-e2e-primitives task 1.1).
|
||||||
|
|
@ -208,6 +209,7 @@ static command_t client_commands[] = {
|
||||||
{ "expect-job", cmd_expect_job, "assert a JobId is present" },
|
{ "expect-job", cmd_expect_job, "assert a JobId is present" },
|
||||||
{ "wait-primary", cmd_wait_primary,
|
{ "wait-primary", cmd_wait_primary,
|
||||||
"wait until a primary serves [timeout_ms]" },
|
"wait until a primary serves [timeout_ms]" },
|
||||||
|
{ "ctrlstatus", cmd_ctrlstatus, "show controller/cluster status" },
|
||||||
{ "shutdown", cmd_shutdown, "shutdown xce controller" },
|
{ "shutdown", cmd_shutdown, "shutdown xce controller" },
|
||||||
{ "jobrun", cmd_jobrun, "run a job" },
|
{ "jobrun", cmd_jobrun, "run a job" },
|
||||||
{ "jobsub", cmd_jobsub, "submit a job" },
|
{ "jobsub", cmd_jobsub, "submit a job" },
|
||||||
|
|
@ -1191,6 +1193,126 @@ int cmd_wait_primary(int argc, char* argv[])
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Map a ctrl_role_t raw value (CtrlStatus.responderRole, see ha.h:60-63 and
|
||||||
|
* design.md "Schema — reply payload") to a display string. PRIMARY==0,
|
||||||
|
* STANDBY==1, INIT==2, ELECTING==3; anything else is reported verbatim so an
|
||||||
|
* unexpected role is never silently rendered as a known one. */
|
||||||
|
static const char* ctrlstatus_role_str(uint8_t role)
|
||||||
|
{
|
||||||
|
switch (role) {
|
||||||
|
case 0:
|
||||||
|
return "primary";
|
||||||
|
case 1:
|
||||||
|
return "standby";
|
||||||
|
case 2:
|
||||||
|
return "initializing";
|
||||||
|
case 3:
|
||||||
|
return "electing";
|
||||||
|
default:
|
||||||
|
return "unknown-role";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* `ctrlstatus` (controller-status task 4.1, Req 1.3/3.3/5.1/5.2/8.1/8.2/8.3,
|
||||||
|
* design.md "cmd_ctrlstatus (test client) — State contract").
|
||||||
|
*
|
||||||
|
* Issues the any-node status read (xcecli_ctrlstatus, is_write=false: whatever
|
||||||
|
* controller answers — primary or standby hint view — rides the configured
|
||||||
|
* failover/rotation) and prints the identity/health fields in human-readable
|
||||||
|
* form.
|
||||||
|
*
|
||||||
|
* On failure (no reachable controller etc.) it prints a typed error and returns
|
||||||
|
* the negative rc so the session records the command as failed and exits
|
||||||
|
* non-zero (Req 8.3). On success it prints the fields and returns 0 (Req 8.2). */
|
||||||
|
int cmd_ctrlstatus(int argc, char* argv[])
|
||||||
|
{
|
||||||
|
xce_ctrlstatus_t* status = NULL;
|
||||||
|
int rc;
|
||||||
|
int i;
|
||||||
|
|
||||||
|
(void)argv;
|
||||||
|
|
||||||
|
if (argc != 1) {
|
||||||
|
printf("ctrlstatus\n");
|
||||||
|
return -EINVAL;
|
||||||
|
}
|
||||||
|
|
||||||
|
rc = xcecli_ctrlstatus(active_ctx->ctx, &status);
|
||||||
|
|
||||||
|
if (rc < 0) {
|
||||||
|
char ebuf[128];
|
||||||
|
|
||||||
|
ebuf[0] = '\0';
|
||||||
|
xce_strerror_r(-rc, ebuf, sizeof(ebuf));
|
||||||
|
SET_ERROR(-rc);
|
||||||
|
|
||||||
|
switch (-rc) {
|
||||||
|
case E_XCE_NOMASTER:
|
||||||
|
fprintf(stderr, "ctrlstatus failed: no-master (%s)\n", ebuf);
|
||||||
|
break;
|
||||||
|
case E_XCE_NOT_READY:
|
||||||
|
fprintf(stderr, "ctrlstatus failed: not-ready (%s)\n", ebuf);
|
||||||
|
break;
|
||||||
|
case ETIMEDOUT:
|
||||||
|
fprintf(stderr, "ctrlstatus failed: timeout (%s)\n", ebuf);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
fprintf(stderr, "ctrlstatus failed: %s\n", ebuf);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
return rc;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Current primary (Req 2.x): an empty primaryName means no elected/known
|
||||||
|
* primary — render the well-defined "unknown" (Req 1.3/2.2/2.3) rather than
|
||||||
|
* an empty or stale value. */
|
||||||
|
printf("Primary: %s\n",
|
||||||
|
NULLSTR(status->primaryName) ? "unknown" : status->primaryName);
|
||||||
|
|
||||||
|
/* Answering node + role, framed as a property of the responder and kept
|
||||||
|
* DISTINCT from the authoritative Primary line above (Req 3.1/3.2/3.3): a
|
||||||
|
* transient responder role (electing/initializing) must not be misread as
|
||||||
|
* the cluster's primary state. */
|
||||||
|
printf("answered by %s (%s)\n",
|
||||||
|
NULLSTR(status->responderName) ? "unknown" : status->responderName,
|
||||||
|
ctrlstatus_role_str(status->responderRole));
|
||||||
|
|
||||||
|
printf("Cluster: %s\n",
|
||||||
|
NULLSTR(status->clusterName) ? "unknown" : status->clusterName);
|
||||||
|
|
||||||
|
/* Configured controller topology + count (Req 4.1/4.2). */
|
||||||
|
printf("Controllers (%u):\n", status->peerCount);
|
||||||
|
for (i = 0; i < status->peers.count; i++) {
|
||||||
|
xce_ctrlpeer_t* peer = status->peers.list[i];
|
||||||
|
|
||||||
|
if (peer == NULL) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
printf("\t%s\t%s:%u\n", NULLSTR(peer->name) ? "?" : peer->name,
|
||||||
|
NULLSTR(peer->host) ? "?" : peer->host, peer->port);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Config version + committed config version (Req 5.1) and the content
|
||||||
|
* checksum as the hex string the controller already exposes (Req 5.2). */
|
||||||
|
printf("configVersion: %llu\n", (unsigned long long)status->configVersion);
|
||||||
|
printf("committedConfigVersion: %llu\n",
|
||||||
|
(unsigned long long)status->committedConfigVersion);
|
||||||
|
printf("configChecksum: %s\n",
|
||||||
|
NULLSTR(status->configChecksum) ? "unknown" : status->configChecksum);
|
||||||
|
|
||||||
|
/* HA health / progress (Req 6.x). */
|
||||||
|
printf("term: %llu\n", (unsigned long long)status->term);
|
||||||
|
printf("committedLsn: %llu\n", (unsigned long long)status->committedLsn);
|
||||||
|
printf("appliedLsn: %llu\n", (unsigned long long)status->appliedLsn);
|
||||||
|
printf("fenced: %s\n", status->fenced ? "true" : "false");
|
||||||
|
|
||||||
|
xcecli_free_ctrlstatus(status);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
static void print_jobinfo(xce_job_t* job)
|
static void print_jobinfo(xce_job_t* job)
|
||||||
{
|
{
|
||||||
char idstring[128];
|
char idstring[128];
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue