@@ -51,6 +51,8 @@ typedef enum {
5151 MATCH_LESS_THAN = 4 ,
5252} match_comparison_t ;
5353
54+ #define MIN_MATCH_HOSTLIST 1024
55+
5456struct timestamp_value {
5557 double t_value ;
5658 match_timestamp_type_t t_type ;
@@ -428,6 +430,91 @@ static struct list_constraint *create_results_constraint (struct match_ctx *mctx
428430 errp );
429431}
430432
433+ static int match_hostlist (struct list_constraint * c ,
434+ const struct job * job ,
435+ unsigned int * comparisons ,
436+ flux_error_t * errp )
437+ {
438+ struct hostlist * hl = zlistx_first (c -> values );
439+ const char * host ;
440+
441+ /* nodelist may not exist if job never ran */
442+ if (!job -> nodelist )
443+ return 0 ;
444+ if (!job -> nodelist_hl ) {
445+ /* hack to remove const */
446+ struct job * jobtmp = (struct job * )job ;
447+ if (!(jobtmp -> nodelist_hl = hostlist_decode (job -> nodelist )))
448+ return 0 ;
449+ }
450+ host = hostlist_first (hl );
451+ while (host ) {
452+ if (inc_check_comparison (c -> mctx , comparisons , errp ) < 0 )
453+ return -1 ;
454+ if (hostlist_find (job -> nodelist_hl , host ) >= 0 )
455+ return 1 ;
456+ host = hostlist_next (hl );
457+ }
458+ return 0 ;
459+ }
460+
461+ /* zlistx_set_destructor */
462+ static void wrap_hostlist_destroy (void * * item )
463+ {
464+ if (item ) {
465+ struct hostlist * hl = * item ;
466+ hostlist_destroy (hl );
467+ (* item ) = NULL ;
468+ }
469+ }
470+
471+ static struct list_constraint * create_hostlist_constraint (
472+ struct match_ctx * mctx ,
473+ json_t * values ,
474+ flux_error_t * errp )
475+ {
476+ struct list_constraint * c ;
477+ struct hostlist * hl = NULL ;
478+ json_t * entry ;
479+ size_t index ;
480+
481+ if (!(c = list_constraint_new (mctx ,
482+ match_hostlist ,
483+ wrap_hostlist_destroy ,
484+ errp )))
485+ return NULL ;
486+ /* Create a single hostlist if user specifies multiple nodes or
487+ * RFC29 hostlist range */
488+ if (!(hl = hostlist_create ())) {
489+ errprintf (errp , "failed to create hostlist structure" );
490+ goto error ;
491+ }
492+ json_array_foreach (values , index , entry ) {
493+ if (!json_is_string (entry )) {
494+ errprintf (errp , "host value must be a string" );
495+ goto error ;
496+ }
497+ if (hostlist_append (hl , json_string_value (entry )) <= 0 ) {
498+ errprintf (errp , "host value not in valid Hostlist format" );
499+ goto error ;
500+ }
501+ }
502+ if (hostlist_count (hl ) > mctx -> max_hostlist ) {
503+ errprintf (errp , "too many hosts specified" );
504+ goto error ;
505+ }
506+ if (!zlistx_add_end (c -> values , hl )) {
507+ errprintf (errp , "failed to append hostlist structure" );
508+ hostlist_destroy (hl );
509+ goto error ;
510+ }
511+ return c ;
512+ error :
513+ hostlist_destroy (hl );
514+ list_constraint_destroy (c );
515+ return NULL ;
516+ }
517+
431518static int match_timestamp (struct list_constraint * c ,
432519 const struct job * job ,
433520 unsigned int * comparisons ,
@@ -665,6 +752,8 @@ struct list_constraint *list_constraint_create (struct match_ctx *mctx,
665752 return create_states_constraint (mctx , values , errp );
666753 else if (streq (op , "results" ))
667754 return create_results_constraint (mctx , values , errp );
755+ else if (streq (op , "hostlist" ))
756+ return create_hostlist_constraint (mctx , values , errp );
668757 else if (streq (op , "t_submit" )
669758 || streq (op , "t_depend" )
670759 || streq (op , "t_run" )
@@ -743,6 +832,30 @@ struct match_ctx *match_ctx_create (flux_t *h)
743832 goto error ;
744833 }
745834
835+ if (flux_get_size (mctx -> h , & mctx -> max_hostlist ) < 0 ) {
836+ flux_log_error (h , "failed to get instance size" );
837+ goto error ;
838+ }
839+
840+ /* Notes:
841+ *
842+ * We do not want a hostlist constraint match to DoS this module.
843+ * So we want to configure a "max" amount of hosts that can exist
844+ * within a hostlist constraint.
845+ *
846+ * Under normal operating conditions, the number of brokers should
847+ * represent the most likely maximum. But there are some corner
848+ * cases. For example, the instance gets reconfigured to be
849+ * smaller, which is not an uncommon thing to do towards a
850+ * cluster's end of life and hardware is beginning to die.
851+ *
852+ * So we configure the following compromise. If the number of
853+ * brokers is below our defined minimum MIN_MATCH_HOSTLIST, we'll
854+ * allow max_hostlist to be increased to this number.
855+ */
856+ if (mctx -> max_hostlist < MIN_MATCH_HOSTLIST )
857+ mctx -> max_hostlist = MIN_MATCH_HOSTLIST ;
858+
746859 return mctx ;
747860
748861error :
0 commit comments