diff --git a/src/controller/worker.c b/src/controller/worker.c index 431fd8e..0c25f88 100644 --- a/src/controller/worker.c +++ b/src/controller/worker.c @@ -616,13 +616,13 @@ int worker_ctrl(cluster_t* c, xce_hostctrl_t* ctrl) if (!(w->host.status & XCE_WORKER_CLOSED)) { goto clean; } - w->host.status &= ~XCE_WORKER_CLOSED; + w->host.status &= ~(XCE_WORKER_CLOSED | XCE_WORKER_CLOSED_BY_ADMIN); break; case HOST_CLOSE: if (w->host.status & XCE_WORKER_CLOSED) { goto clean; } - w->host.status |= XCE_WORKER_CLOSED; + w->host.status |= (XCE_WORKER_CLOSED | XCE_WORKER_CLOSED_BY_ADMIN); break; default: rc = -1; diff --git a/src/frontend/apiserver/controllers/hosts.go b/src/frontend/apiserver/controllers/hosts.go index 8172959..92c4e9d 100644 --- a/src/frontend/apiserver/controllers/hosts.go +++ b/src/frontend/apiserver/controllers/hosts.go @@ -536,21 +536,16 @@ func validateHost(ctx *gin.Context, control int, hostnames []string) (successHos } func isHostControlActionAllowed(actionType int, status int) bool { + isClosed := (status&models.HOSTSTAT["CLOSED"]) != 0 || + (status&models.HOSTSTAT["CLOSED_BY_ADMIN"]) != 0 switch actionType { - case 1: // open strictly - if status&models.HOSTSTAT["CLOSED"] != 0 { - return true - } + case 1: // open + return isClosed case 2: // close - if (status & models.HOSTSTAT["CLOSED"]) != 0 { - return false - } else { - return true - } + return !isClosed default: return false } - return false } func preCheckHostControlRequest(req *models.HostControlRequest) (bool, error) { logger.Debug("Enter preCheckHostControlRequest") diff --git a/src/frontend/apiserver/models/hosts.go b/src/frontend/apiserver/models/hosts.go index 512a790..0b47273 100644 --- a/src/frontend/apiserver/models/hosts.go +++ b/src/frontend/apiserver/models/hosts.go @@ -10,16 +10,19 @@ const ( var SupportedHostActions = []string{"open", "close"} var HOSTSTAT = map[string]int{ - "UNAVAIL": 0x00010000, - "LOCKEDU": 0x00020000, - "LOCKEDW": 0x00040000, - "BUSY": 0x00080000, - "CLOSED": 0x00100000, + "UNAVAIL": 0x00010000, + "LOCKEDU": 0x00020000, + "LOCKEDW": 0x00040000, + "BUSY": 0x00080000, + "CLOSED": 0x00100000, + "CLOSED_BY_ADMIN": 0x00400000, } func HostStateStr(state int) string { if (state & HOSTSTAT["UNAVAIL"]) != 0 { return "UNAVAIL" + } else if (state & HOSTSTAT["CLOSED_BY_ADMIN"]) != 0 { + return "CLOSED_BY_ADMIN" } else if (state & HOSTSTAT["CLOSED"]) != 0 { return "CLOSED" } else if (state & HOSTSTAT["BUSY"]) != 0 { diff --git a/src/frontend/apiserver/models/hosts_test.go b/src/frontend/apiserver/models/hosts_test.go index 99ce5da..f9d3629 100644 --- a/src/frontend/apiserver/models/hosts_test.go +++ b/src/frontend/apiserver/models/hosts_test.go @@ -23,6 +23,11 @@ func TestHostStateStr(t *testing.T) { state: HOSTSTAT["CLOSED"], want: "CLOSED", }, + { + name: "CLOSED_BY_ADMIN flag", + state: HOSTSTAT["CLOSED_BY_ADMIN"], + want: "CLOSED_BY_ADMIN", + }, { name: "BUSY flag", state: HOSTSTAT["BUSY"], @@ -48,6 +53,11 @@ func TestHostStateStr(t *testing.T) { state: HOSTSTAT["UNAVAIL"] | HOSTSTAT["CLOSED"], want: "UNAVAIL", // UNAVAIL takes priority }, + { + name: "combined CLOSED+CLOSED_BY_ADMIN flags", + state: HOSTSTAT["CLOSED"] | HOSTSTAT["CLOSED_BY_ADMIN"], + want: "CLOSED_BY_ADMIN", // CLOSED_BY_ADMIN overrides generic CLOSED + }, } for _, tt := range tests { diff --git a/src/include/xce/types.h b/src/include/xce/types.h index d951cf1..70aecf8 100644 --- a/src/include/xce/types.h +++ b/src/include/xce/types.h @@ -172,11 +172,13 @@ typedef struct { #define XCE_WORKER_LOCKEDW (0x00040000) #define XCE_WORKER_BUSY (0x00080000) #define XCE_WORKER_CLOSED (0x00100000) -#define XCE_WORKER_HIDDEN (0x00200000) /* Admin explicitly removed — invisible but not yet cleaned up */ +#define XCE_WORKER_HIDDEN (0x00200000) /* Admin explicitly removed — invisible but not yet cleaned up */ +#define XCE_WORKER_CLOSED_BY_ADMIN (0x00400000) /* Admin closed — host closed explicitly by admin */ -#define IS_WORKER_UNAVAIL(status) (((status) & XCE_WORKER_UNAVAIL) != 0) -#define IS_WORKER_LOCKEDU(status) (((status) & XCE_WORKER_LOCKEDU) != 0) -#define IS_WORKER_LOCKEDW(status) (((status) & XCE_WORKER_LOCKEDW) != 0) +#define IS_WORKER_UNAVAIL(status) (((status) & XCE_WORKER_UNAVAIL) != 0) +#define IS_WORKER_LOCKEDU(status) (((status) & XCE_WORKER_LOCKEDU) != 0) +#define IS_WORKER_LOCKEDW(status) (((status) & XCE_WORKER_LOCKEDW) != 0) +#define IS_WORKER_CLOSED_BY_ADMIN(status) (((status) & XCE_WORKER_CLOSED_BY_ADMIN) != 0) #define IS_WORKER_LOCKED(status) \ (IS_WORKER_LOCKEDU(status) || IS_WORKER_LOCKEDW(status)) #define IS_WORKER_BUSY(status) (((status) & XCE_WORKER_BUSY) != 0) diff --git a/src/lib/wobj.c b/src/lib/wobj.c index accee30..ab148a2 100644 --- a/src/lib/wobj.c +++ b/src/lib/wobj.c @@ -22,12 +22,34 @@ static size_t worker_datasize(va_list ap) return sz; } -static int worker_init(meta_obj_t* obj, va_list ap) +static void kill_worker(worker_t* w) { - worker_t* dst = RTTI_OBJ_TO_DATA(worker_t, obj); - worker_private_t* priv = (worker_private_t*)RTTI_OBJ_TO_PRIV(obj); - xce_worker_t* src = va_arg(ap, xce_worker_t*); + dereference(w->host.name); + if (w->host.attrs != NULL) { + dereference(w->host.attrs->hosttype); + dereference(w->host.attrs->hostmodel); + FREEUP(w->host.attrs); + } + + if (w->host.dispatch_window_config != NULL) { + free_timewindow_config(w->host.dispatch_window_config); + w->host.dispatch_window_config = NULL; + } + + if (w->host.thresholds != NULL) { + free_resource_thresholds(w->host.thresholds, w->host.nthresholds); + w->host.thresholds = NULL; + w->host.nthresholds = 0; + } + + zarray_fini(&(w->host.resvals)); + + return; +} + +static int copy_worker(worker_t* dst, xce_worker_t* src) +{ if ((src == NULL) || (src->attrs == NULL)) { return -EINVAL; } @@ -55,6 +77,42 @@ static int worker_init(meta_obj_t* obj, va_list ap) dst->host.nresvals_to_sched = 0; dst->host.resvals_to_sched = NULL; + return 1; +} + +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) + uint32_t admin_status = 0; + int rc = 0; + + admin_status = dst->host.status & XCE_WORKER_ADMIN_MASK; + + kill_worker(dst); + + rc = copy_worker(dst, w); + if (rc >= 0) { + dst->host.status |= admin_status; + dst->ep = NULL; + } + + return rc; +#undef XCE_WORKER_ADMIN_MASK +} + +static int worker_init(meta_obj_t* obj, va_list ap) +{ + worker_t* dst = RTTI_OBJ_TO_DATA(worker_t, obj); + worker_private_t* priv = (worker_private_t*)RTTI_OBJ_TO_PRIV(obj); + xce_worker_t* src = va_arg(ap, xce_worker_t*); + int rc = 0; + + rc = copy_worker(dst, src); + if (rc < 0) { + return rc; + } + my_rwlock_init(&(priv->lock)); priv->refcount = 1; @@ -66,26 +124,7 @@ static void worker_destroy(meta_obj_t* obj) worker_t* w = RTTI_OBJ_TO_DATA(worker_t, obj); worker_private_t* priv = (worker_private_t*)RTTI_OBJ_TO_PRIV(obj); - dereference(w->host.name); - - if (w->host.attrs != NULL) { - dereference(w->host.attrs->hosttype); - dereference(w->host.attrs->hostmodel); - FREEUP(w->host.attrs); - } - - if (w->host.dispatch_window_config != NULL) { - free_timewindow_config(w->host.dispatch_window_config); - w->host.dispatch_window_config = NULL; - } - - if (w->host.thresholds != NULL) { - free_resource_thresholds(w->host.thresholds, w->host.nthresholds); - w->host.thresholds = NULL; - w->host.nthresholds = 0; - } - - zarray_fini(&(w->host.resvals)); + kill_worker(w); my_rwlock_fini(&(priv->lock)); memset(priv, 0x0, sizeof(*priv)); @@ -163,9 +202,10 @@ static typeinfo_t worker_type = { static worker_t* makeWorker_(var workermgr, xce_worker_t* w) { worker_t* worker = NULL; + worker_t* w1 = NULL; int rc = 0; - rc = key_find(workermgr, (void*)w->name, (void**)&worker); + rc = key_find(workermgr, (void*)w->name, (void**)&w1); if ((rc < 0) && (rc != -ENOENT)) { errno = -rc; @@ -174,20 +214,24 @@ static worker_t* makeWorker_(var workermgr, xce_worker_t* w) } if (rc > 0) { - errno = EEXIST; - return NULL; - } - - worker = makeInstance(&worker_type, w); - if (worker != NULL) { - rc = key_add(workermgr, (void*)worker->host.name, (void*)worker); - + rc = override_worker(w1, w); if (rc < 0) { - destroy(worker); - - errno = -rc; return NULL; } + + worker = w1; + } else { + worker = makeInstance(&worker_type, w); + if (worker != NULL) { + rc = key_add(workermgr, (void*)worker->host.name, (void*)worker); + + if (rc < 0) { + destroy(worker); + + errno = -rc; + return NULL; + } + } } return worker;