From f7a8b8d97ada382ae4109dd5b6869a6b06ba502e Mon Sep 17 00:00:00 2001
From: Morris Jette <jette@schedmd.com>
Date: Thu, 7 Jan 2016 14:29:23 -0800
Subject: [PATCH] Node ActiveFeature work

sinfo to group by like ActiveFeatures
sinfo long format option for ActiveFeatures
slurm.conf parse to accept node "Features" as well as current "Feature"
scontrol to report AcitveFeatures and AvailableFeatures on separate lines
If a user sets node available features and the specification includes "knl"
  then expand that to include all possible NUMA and MCDRAM modes, the same
  as if "knl" is specified in slurm.conf
Confirm that when AcitveFeatures is set, it is a subset of the
  AvailableFeatures
---
 doc/man/man1/sinfo.1     |  7 ++++--
 src/api/node_info.c      | 12 ++++++++--
 src/common/read_config.c | 28 ++++++++++++----------
 src/common/read_config.h |  4 ++++
 src/sinfo/opts.c         | 11 ++++++++-
 src/sinfo/sinfo.c        |  7 +++++-
 src/sinfo/sinfo.h        |  1 +
 src/slurmctld/node_mgr.c | 52 +++++++++++++++++++++++++++++++++++++---
 8 files changed, 101 insertions(+), 21 deletions(-)

diff --git a/doc/man/man1/sinfo.1 b/doc/man/man1/sinfo.1
index 68ab8240413..ec7affb8bd5 100644
--- a/doc/man/man1/sinfo.1
+++ b/doc/man/man1/sinfo.1
@@ -1,4 +1,4 @@
-.TH sinfo "1" "Slurm Commands" "Janury 2016" "Slurm Commands"
+.TH sinfo "1" "Slurm Commands" "January 2016" "Slurm Commands"
 
 .SH "NAME"
 sinfo \- view information about Slurm nodes and partitions.
@@ -325,7 +325,10 @@ Default time for any job in the format "days\-hours:minutes:seconds".
 Size of temporary disk space per node in megabytes.
 .TP
 \fBfeatures\fR
-Features associated with the nodes.
+Features available on the nodes. Also see \fBfeatures_act\fR.
+.TP
+\fBfeatures_act\fR
+Features currently active on the nodes. Also see \fBfeatures\fR.
 .TP
 \fBgroups\fR
 Groups which may use the nodes.
diff --git a/src/api/node_info.c b/src/api/node_info.c
index 8a8ff14a8ba..92ca2da352c 100644
--- a/src/api/node_info.c
+++ b/src/api/node_info.c
@@ -231,8 +231,16 @@ slurm_sprint_node_table (node_info_t * node_ptr,
 
 	/****** Line ******/
 	snprintf(tmp_line, sizeof(tmp_line),
-		 "AvailableFeatures=%s ActiveFeatures=%s",
-		 node_ptr->features, node_ptr->features_act);
+		 "AvailableFeatures=%s", node_ptr->features);
+	xstrcat(out, tmp_line);
+	if (one_liner)
+		xstrcat(out, " ");
+	else
+		xstrcat(out, "\n   ");
+
+	/****** Line ******/
+	snprintf(tmp_line, sizeof(tmp_line),
+		 "ActiveFeatures=%s", node_ptr->features_act);
 	xstrcat(out, tmp_line);
 	if (one_liner)
 		xstrcat(out, " ");
diff --git a/src/common/read_config.c b/src/common/read_config.c
index 231e717ba69..35d1b651be7 100644
--- a/src/common/read_config.c
+++ b/src/common/read_config.c
@@ -571,7 +571,8 @@ static int _parse_frontend(void **dest, slurm_parser_enum_t type,
 	/* should not get here */
 }
 
-static void _add_knl_features(slurm_conf_node_t *n)
+/* Expand a feature of "knl" to all appropriate KNL options */
+extern void add_knl_features(char **features)
 {
 	static uint16_t avail_mcdram = 0, avail_numa = 0;
 	static uint16_t default_mcdram = 0, default_numa = 0;
@@ -580,17 +581,17 @@ static void _add_knl_features(slurm_conf_node_t *n)
 	char *tmp_str, *token, *last = NULL;
 	int i, j;
 
-	if (!n->feature)
+	if ((features == NULL) || (features[0] ==  NULL))
 		return;
 
-	i = strlen(n->feature) + 1;	/* oversized */
+	i = strlen(features[0]) + 1;	/* oversized */
 	tmp_str = xmalloc(i);
 	/* Remove white space from feature specification */
-	for (i = 0, j = 0; n->feature[i]; i++) {
-		if (!isspace(n->feature[i]))
-			tmp_str[j++] = n->feature[i];
+	for (i = 0, j = 0; features[0][i]; i++) {
+		if (!isspace(features[0][i]))
+			tmp_str[j++] = features[0][i];
 	}
-	strcpy(n->feature, tmp_str);
+	strcpy(*features, tmp_str);
 
 	token = strtok_r(tmp_str, ",", &last);
 	while (token) {
@@ -614,13 +615,13 @@ static void _add_knl_features(slurm_conf_node_t *n)
 
 	if (!has_mcdram) {
 		tmp_str = knl_mcdram_str(avail_mcdram);
-		xstrfmtcat(n->feature, ",%s", tmp_str);
+		xstrfmtcat(*features, ",%s", tmp_str);
 		xfree(tmp_str);
 	}
 
 	if (!has_numa) {
 		tmp_str = knl_numa_str(avail_numa);
-		xstrfmtcat(n->feature, ",%s", tmp_str);
+		xstrfmtcat(*features, ",%s", tmp_str);
 		xfree(tmp_str);
 	}
 }
@@ -639,6 +640,7 @@ static int _parse_nodename(void **dest, slurm_parser_enum_t type,
 		{"CPUs", S_P_UINT16},
 		{"CPUSpecList", S_P_STRING},
 		{"Feature", S_P_STRING},
+		{"Features", S_P_STRING},
 		{"Gres", S_P_STRING},
 		{"MemSpecLimit", S_P_UINT32},
 		{"NodeAddr", S_P_STRING},
@@ -726,9 +728,11 @@ static int _parse_nodename(void **dest, slurm_parser_enum_t type,
 		if (!s_p_get_string(&n->cpu_spec_list, "CPUSpecList", tbl))
 			s_p_get_string(&n->cpu_spec_list, "CPUSpecList", dflt);
 
-		if (!s_p_get_string(&n->feature, "Feature", tbl))
-			s_p_get_string(&n->feature, "Feature", dflt);
-		_add_knl_features(n);
+		if (!s_p_get_string(&n->feature, "Feature",  tbl) &&
+		    !s_p_get_string(&n->feature, "Features", tbl) &&
+		    !s_p_get_string(&n->feature, "Feature",  dflt))
+			s_p_get_string(&n->feature, "Features", dflt);
+		add_knl_features(&n->feature);
 
 		if (!s_p_get_string(&n->gres, "Gres", tbl))
 			s_p_get_string(&n->gres, "Gres", dflt);
diff --git a/src/common/read_config.h b/src/common/read_config.h
index 96f0ea20a83..7a322fed4a8 100644
--- a/src/common/read_config.h
+++ b/src/common/read_config.h
@@ -578,4 +578,8 @@ extern char *get_extra_conf_path(char *conf_name);
  */
 extern bool run_in_daemon(char *daemons);
 
+/* Expand a feature of "knl" to all appropriate KNL options
+ * features IN/OUT - Expand string as appropriate, must be xmalloc'ed */
+extern void add_knl_features(char **features);
+
 #endif /* !_READ_CONFIG_H */
diff --git a/src/sinfo/opts.c b/src/sinfo/opts.c
index c901b9f472c..0184cf3c3fb 100644
--- a/src/sinfo/opts.c
+++ b/src/sinfo/opts.c
@@ -3,7 +3,7 @@
  *****************************************************************************
  *  Copyright (C) 2002-2007 The Regents of the University of California.
  *  Copyright (C) 2008-2010 Lawrence Livermore National Security.
- *  Portions Copyright (C) 2010 SchedMD <http://www.schedmd.com>.
+ *  Portions Copyright (C) 2010-2016 SchedMD <http://www.schedmd.com>.
  *  Produced at Lawrence Livermore National Laboratory (cf, DISCLAIMER).
  *  Written by Joey Ekstrom <ekstrom1@llnl.gov>, Morris Jette <jette1@llnl.gov>
  *  CODE-OCEC-09-009. All rights reserved.
@@ -606,6 +606,7 @@ _parse_format( char* format )
 					right_justify,
 					suffix );
 		} else if (field[0] == 'b') {
+			params.match_flags.features_act_flag = true;
 			format_add_features_act( params.format_list,
 					field_size,
 					right_justify,
@@ -937,6 +938,12 @@ static int _parse_long_format (char* format_long)
 					     field_size,
 					     right_justify,
 					     suffix );
+		} else if (!strcasecmp(token, "features_act")) {
+			params.match_flags.features_act_flag = true;
+			format_add_features_act( params.format_list,
+					field_size,
+					right_justify,
+					suffix );
 		} else if (!strcasecmp(token, "groups")) {
 			params.match_flags.groups_flag = true;
 			format_add_groups( params.format_list,
@@ -1241,6 +1248,8 @@ void _print_options( void )
 			"true" : "false");
 	printf("features_flag   = %s\n", params.match_flags.features_flag ?
 			"true" : "false");
+	printf("features_flag_act = %s\n", params.match_flags.features_act_flag?
+			"true" : "false");
 	printf("groups_flag     = %s\n", params.match_flags.groups_flag ?
 					"true" : "false");
 	printf("gres_flag       = %s\n", params.match_flags.gres_flag ?
diff --git a/src/sinfo/sinfo.c b/src/sinfo/sinfo.c
index 47ea1597b68..477357806fc 100644
--- a/src/sinfo/sinfo.c
+++ b/src/sinfo/sinfo.c
@@ -3,7 +3,7 @@
  *****************************************************************************
  *  Copyright (C) 2002-2007 The Regents of the University of California.
  *  Copyright (C) 2008-2010 Lawrence Livermore National Security.
- *  Portions Copyright (C) 2010-2011 SchedMD <http://www.schedmd.com>.
+ *  Portions Copyright (C) 2010-2016 SchedMD <http://www.schedmd.com>.
  *  Produced at Lawrence Livermore National Laboratory (cf, DISCLAIMER).
  *  Written by Joey Ekstrom <ekstrom1@llnl.gov>, Morris Jette <jette1@llnl.gov>
  *  CODE-OCEC-09-009. All rights reserved.
@@ -753,6 +753,11 @@ static bool _match_node_data(sinfo_data_t *sinfo_ptr, node_info_t *node_ptr)
 	    (xstrcmp(node_ptr->features, sinfo_ptr->features)))
 		return false;
 
+	if (sinfo_ptr->nodes &&
+	    params.match_flags.features_act_flag &&
+	    (xstrcmp(node_ptr->features_act, sinfo_ptr->features_act)))
+		return false;
+
 	if (sinfo_ptr->nodes &&
 	    params.match_flags.gres_flag &&
 	    (xstrcmp(node_ptr->gres, sinfo_ptr->gres)))
diff --git a/src/sinfo/sinfo.h b/src/sinfo/sinfo.h
index e7e9eeede34..290fb85d2f0 100644
--- a/src/sinfo/sinfo.h
+++ b/src/sinfo/sinfo.h
@@ -137,6 +137,7 @@ struct sinfo_match_flags {
 	bool sct_flag;
 	bool disk_flag;
 	bool features_flag;
+	bool features_act_flag;
 	bool groups_flag;
 	bool gres_flag;
 	bool hostnames_flag;
diff --git a/src/slurmctld/node_mgr.c b/src/slurmctld/node_mgr.c
index 0f5d10afed6..154e9541fec 100644
--- a/src/slurmctld/node_mgr.c
+++ b/src/slurmctld/node_mgr.c
@@ -1200,6 +1200,41 @@ _equivalent_node_state(struct node_record *node_ptr, uint32_t new_state)
 	return false;
 }
 
+/* Confirm that the selected ActiveFeatures are a subset of AvailableFeatures */
+static bool _valid_features_act(char *features_act, char *features)
+{
+	bool valid_subset = true;
+	char *tmp_act, *last_act = NULL, *tok_act;
+	char *tmp_avail, *last_avail = NULL, *tok_avail;
+
+	if (!features_act || (features_act[0] == '\0'))
+		return true;
+	if (!features || (features[0] == '\0'))
+		return false;
+
+	tmp_act = xstrdup(features_act);
+        tok_act = strtok_r(tmp_act, ",", &last_act);
+        while (tok_act) {
+		last_avail = NULL;
+		tmp_avail = xstrdup(features);
+		tok_avail = strtok_r(tmp_avail, ",", &last_avail);
+		while (tok_avail) {
+			if (!xstrcmp(tok_act, tok_avail))
+				break;
+		        tok_avail = strtok_r(NULL, ",", &last_avail);
+		}
+		xfree(tmp_avail);
+		if (!tok_avail) {	/* No match found */
+			valid_subset = false;
+			break;
+		}
+                tok_act = strtok_r(NULL, ",", &last_act);
+	}
+	xfree(tmp_act);
+
+	return valid_subset;
+}
+
 /*
  * update_node - update the configuration data for one or more nodes
  * IN update_node_msg - update node request
@@ -1263,6 +1298,8 @@ int update_node ( update_node_msg_t * update_node_msg )
 		}
 	}
 
+	add_knl_features(&update_node_msg->features);
+
 	while ( (this_node_name = hostlist_shift (host_list)) ) {
 		int err_code = 0;
 
@@ -1296,17 +1333,26 @@ int update_node ( update_node_msg_t * update_node_msg )
 
 		if (update_node_msg->features) {
 			xfree(node_ptr->features);
-			if (update_node_msg->features[0])
+			if (update_node_msg->features[0]) {
 				node_ptr->features =
 					xstrdup(update_node_msg->features);
+			}
 			/* _update_node_features() logs and updates config */
 		}
 
-		if (update_node_msg->features_act) {
+		if (update_node_msg->features_act &&
+		    !_valid_features_act(update_node_msg->features_act,
+					 node_ptr->features)) {
+			info("Invalid node ActiveFeatures (%s not subset of %s)",
+			     update_node_msg->features_act,
+			     node_ptr->features);
+			error_code = ESLURM_INVALID_FEATURE;
+		} else if (update_node_msg->features_act) {
 			xfree(node_ptr->features_act);
-			if (update_node_msg->features_act[0])
+			if (update_node_msg->features_act[0]) {
 				node_ptr->features_act =
 					xstrdup(update_node_msg->features_act);
+			}
 			/* _update_node_features() logs and updates config */
 		}
 
-- 
GitLab