mirror of
				https://github.com/ClusterCockpit/cc-metric-collector.git
				synced 2025-10-31 17:05:07 +01:00 
			
		
		
		
	Merge develop branch into main (#96)
				
					
				
			* InfiniBandCollector: Scale raw readings from octets to bytes * Fix clock frequency coming from LikwidCollector and update docs * Build DEB package for Ubuntu 20.04 for releases * Fix memstat collector with numa_stats option * Remove useless prints from MemstatCollector * Replace ioutils with os and io (#87) * Use lower case for error strings in RocmSmiCollector * move maybe-usable-by-other-cc-components to pkg. Fix all files to use the new paths (#88) * Add collector for monitoring the execution of cc-metric-collector itself (#81) * Add collector to monitor execution of cc-metric-collector itself * Register SelfCollector * Fix import paths for moved packages * Check if at least one CPU with frequency information was detected * Correct type: /proc/stats -> /proc/stat * Update README.md * Run ipmitool asynchron. Improved error handling. * Corrected some typos * Add running average power limit (RAPL) metric collector * Add running average power limit (RAPL) metric collector * Do not mess up with the orignal configuration * * Corrected json config in numastatsMetric.md * Added some debug output to numastatsMetric.go * Fixed computing number of physical packages for non continous physical package IDs (e.g. on Ampere Altra Q80-30) * Fix kernel panic for receiver config with missing receiver type * Add receiver to gather remote IPMI sensor metrics * Added config option to add ipmi-sensors command line options * Add documentaion for IPMI receiver * Update to latest version of included go modules * Add go.mod to App dependency * Try to use common metric tags across hardware vendors * Add IPMI metric: current * remove prefix enumeration like 01-... * Add IPMI receiver example configuration to receivers.json * Minimal formating changes * Add hostlist package * Added tests for hostlist Expand() * Use package hostlist to expand a host list * Use package hostlist to expand a host list * Some servers return "ConsumedPowerWatt":65535 instead of "ConsumedPowerWatt":null * Updated to latest package versions * Do not allow unknown fields in JSON configuration file * Add workflow to customize packages to docs * NFS I/O Stats Collector (#91) * Initial version * Delete values for vanished mount points and comments * Fix for Likwid collector (#95) * Run LIKWID in separate thread and check metric type * Change LIKWID collector documentation to use 'type' instead of 'scope' * Re-initialize LIKWID after one read is missing due to lock toggle * Register cc-metric-collector at Zenodo (#93) * Add initial version of Zenodo project file * Orcid ID added * Update .zenodo.json Co-authored-by: Holger Obermaier <holger.obermaier@kit.edu> * Update ipmiMetric.go Co-authored-by: Holger Obermaier <40787752+ho-ob@users.noreply.github.com> Co-authored-by: Holger Obermaier <Holger.Obermaier@kit.edu>
This commit is contained in:
		
							
								
								
									
										125
									
								
								pkg/hostlist/hostlist.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										125
									
								
								pkg/hostlist/hostlist.go
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,125 @@ | ||||
| package hostlist | ||||
|  | ||||
| import ( | ||||
| 	"fmt" | ||||
| 	"regexp" | ||||
| 	"sort" | ||||
| 	"strconv" | ||||
| 	"strings" | ||||
| ) | ||||
|  | ||||
| func Expand(in string) (result []string, err error) { | ||||
|  | ||||
| 	// Create ranges regular expression | ||||
| 	reStNumber := "[[:digit:]]+" | ||||
| 	reStRange := reStNumber + "-" + reStNumber | ||||
| 	reStOptionalNumberOrRange := "(" + reStNumber + ",|" + reStRange + ",)*" | ||||
| 	reStNumberOrRange := "(" + reStNumber + "|" + reStRange + ")" | ||||
| 	reStBraceLeft := "[[]" | ||||
| 	reStBraceRight := "[]]" | ||||
| 	reStRanges := reStBraceLeft + | ||||
| 		reStOptionalNumberOrRange + | ||||
| 		reStNumberOrRange + | ||||
| 		reStBraceRight | ||||
| 	reRanges := regexp.MustCompile(reStRanges) | ||||
|  | ||||
| 	// Create host list regular expression | ||||
| 	reStDNSChars := "[a-zA-Z0-9-]+" | ||||
| 	reStPrefix := "^(" + reStDNSChars + ")" | ||||
| 	reStOptionalSuffix := "(" + reStDNSChars + ")?" | ||||
| 	re := regexp.MustCompile(reStPrefix + "([[][0-9,-]+[]])?" + reStOptionalSuffix) | ||||
|  | ||||
| 	// Remove all delimiters from the input | ||||
| 	in = strings.TrimLeft(in, ", ") | ||||
|  | ||||
| 	for len(in) > 0 { | ||||
| 		if v := re.FindStringSubmatch(in); v != nil { | ||||
|  | ||||
| 			// Remove matched part from the input | ||||
| 			lenPrefix := len(v[0]) | ||||
| 			in = in[lenPrefix:] | ||||
|  | ||||
| 			// Remove all delimiters from the input | ||||
| 			in = strings.TrimLeft(in, ", ") | ||||
|  | ||||
| 			// matched prefix, range and suffix | ||||
| 			hlPrefix := v[1] | ||||
| 			hlRanges := v[2] | ||||
| 			hlSuffix := v[3] | ||||
|  | ||||
| 			// Single node without ranges | ||||
| 			if hlRanges == "" { | ||||
| 				result = append(result, hlPrefix) | ||||
| 				continue | ||||
| 			} | ||||
|  | ||||
| 			// Node with ranges | ||||
| 			if v := reRanges.FindStringSubmatch(hlRanges); v != nil { | ||||
|  | ||||
| 				// Remove braces | ||||
| 				hlRanges = hlRanges[1 : len(hlRanges)-1] | ||||
|  | ||||
| 				// Split host ranges at , | ||||
| 				for _, hlRange := range strings.Split(hlRanges, ",") { | ||||
|  | ||||
| 					// Split host range at - | ||||
| 					RangeStartEnd := strings.Split(hlRange, "-") | ||||
|  | ||||
| 					// Range is only a single number | ||||
| 					if len(RangeStartEnd) == 1 { | ||||
| 						result = append(result, hlPrefix+RangeStartEnd[0]+hlSuffix) | ||||
| 						continue | ||||
| 					} | ||||
|  | ||||
| 					// Range has a start and an end | ||||
| 					widthRangeStart := len(RangeStartEnd[0]) | ||||
| 					widthRangeEnd := len(RangeStartEnd[1]) | ||||
| 					iStart, _ := strconv.ParseUint(RangeStartEnd[0], 10, 64) | ||||
| 					iEnd, _ := strconv.ParseUint(RangeStartEnd[1], 10, 64) | ||||
| 					if iStart > iEnd { | ||||
| 						return nil, fmt.Errorf("single range start is greater than end: %s", hlRange) | ||||
| 					} | ||||
|  | ||||
| 					// Create print format string for range numbers | ||||
| 					doPadding := widthRangeStart == widthRangeEnd | ||||
| 					widthPadding := widthRangeStart | ||||
| 					var formatString string | ||||
| 					if doPadding { | ||||
| 						formatString = "%0" + fmt.Sprint(widthPadding) + "d" | ||||
| 					} else { | ||||
| 						formatString = "%d" | ||||
| 					} | ||||
| 					formatString = hlPrefix + formatString + hlSuffix | ||||
|  | ||||
| 					// Add nodes from this range | ||||
| 					for i := iStart; i <= iEnd; i++ { | ||||
| 						result = append(result, fmt.Sprintf(formatString, i)) | ||||
| 					} | ||||
| 				} | ||||
| 			} else { | ||||
| 				return nil, fmt.Errorf("not at hostlist range: %s", hlRanges) | ||||
| 			} | ||||
| 		} else { | ||||
| 			return nil, fmt.Errorf("not a hostlist: %s", in) | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| 	if result != nil { | ||||
| 		// sort | ||||
| 		sort.Strings(result) | ||||
|  | ||||
| 		// uniq | ||||
| 		previous := 1 | ||||
| 		for current := 1; current < len(result); current++ { | ||||
| 			if result[current-1] != result[current] { | ||||
| 				if previous != current { | ||||
| 					result[previous] = result[current] | ||||
| 				} | ||||
| 				previous++ | ||||
| 			} | ||||
| 		} | ||||
| 		result = result[:previous] | ||||
| 	} | ||||
|  | ||||
| 	return | ||||
| } | ||||
							
								
								
									
										126
									
								
								pkg/hostlist/hostlist_test.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										126
									
								
								pkg/hostlist/hostlist_test.go
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,126 @@ | ||||
| package hostlist | ||||
|  | ||||
| import ( | ||||
| 	"testing" | ||||
| ) | ||||
|  | ||||
| func TestExpand(t *testing.T) { | ||||
|  | ||||
| 	// Compare two slices of strings | ||||
| 	equal := func(a, b []string) bool { | ||||
| 		if len(a) != len(b) { | ||||
| 			return false | ||||
| 		} | ||||
| 		for i, v := range a { | ||||
| 			if v != b[i] { | ||||
| 				return false | ||||
| 			} | ||||
| 		} | ||||
| 		return true | ||||
| 	} | ||||
|  | ||||
| 	type testDefinition struct { | ||||
| 		input          string | ||||
| 		resultExpected []string | ||||
| 		errorExpected  bool | ||||
| 	} | ||||
|  | ||||
| 	expandTests := []testDefinition{ | ||||
| 		{ | ||||
| 			// Single node | ||||
| 			input:          "n1", | ||||
| 			resultExpected: []string{"n1"}, | ||||
| 			errorExpected:  false, | ||||
| 		}, | ||||
| 		{ | ||||
| 			// Single node, duplicated | ||||
| 			input:          "n1,n1", | ||||
| 			resultExpected: []string{"n1"}, | ||||
| 			errorExpected:  false, | ||||
| 		}, | ||||
| 		{ | ||||
| 			// Single node with padding | ||||
| 			input:          "n[01]", | ||||
| 			resultExpected: []string{"n01"}, | ||||
| 			errorExpected:  false, | ||||
| 		}, | ||||
| 		{ | ||||
| 			// Single node with suffix | ||||
| 			input:          "n[01]-p", | ||||
| 			resultExpected: []string{"n01-p"}, | ||||
| 			errorExpected:  false, | ||||
| 		}, | ||||
| 		{ | ||||
| 			// Multiple nodes with a single range | ||||
| 			input:          "n[1-2]", | ||||
| 			resultExpected: []string{"n1", "n2"}, | ||||
| 			errorExpected:  false, | ||||
| 		}, | ||||
| 		{ | ||||
| 			// Multiple nodes with a single range and a single index | ||||
| 			input:          "n[1-2,3]", | ||||
| 			resultExpected: []string{"n1", "n2", "n3"}, | ||||
| 			errorExpected:  false, | ||||
| 		}, | ||||
| 		{ | ||||
| 			// Multiple nodes with different prefixes | ||||
| 			input:          "n[1-2],m[1,2]", | ||||
| 			resultExpected: []string{"m1", "m2", "n1", "n2"}, | ||||
| 			errorExpected:  false, | ||||
| 		}, | ||||
| 		{ | ||||
| 			// Multiple nodes with different suffixes | ||||
| 			input:          "n[1-2]-p,n[1,2]-q", | ||||
| 			resultExpected: []string{"n1-p", "n1-q", "n2-p", "n2-q"}, | ||||
| 			errorExpected:  false, | ||||
| 		}, | ||||
| 		{ | ||||
| 			// Multiple nodes with and without node ranges | ||||
| 			input:          " n09, n[01-04,06-07,09] , , n10,n04", | ||||
| 			resultExpected: []string{"n01", "n02", "n03", "n04", "n06", "n07", "n09", "n10"}, | ||||
| 			errorExpected:  false, | ||||
| 		}, | ||||
| 		{ | ||||
| 			// Forbidden DNS character | ||||
| 			input:          "n@", | ||||
| 			resultExpected: []string{}, | ||||
| 			errorExpected:  true, | ||||
| 		}, | ||||
| 		{ | ||||
| 			// Forbidden range | ||||
| 			input:          "n[1-2-2,3]", | ||||
| 			resultExpected: []string{}, | ||||
| 			errorExpected:  true, | ||||
| 		}, | ||||
| 		{ | ||||
| 			// Forbidden range limits | ||||
| 			input:          "n[2-1]", | ||||
| 			resultExpected: []string{}, | ||||
| 			errorExpected:  true, | ||||
| 		}, | ||||
| 	} | ||||
|  | ||||
| 	for _, expandTest := range expandTests { | ||||
| 		result, err := Expand(expandTest.input) | ||||
|  | ||||
| 		hasError := err != nil | ||||
| 		if hasError != expandTest.errorExpected && hasError { | ||||
| 			t.Errorf("Expand('%s') failed: unexpected error '%v'", | ||||
| 				expandTest.input, err) | ||||
| 			continue | ||||
| 		} | ||||
| 		if hasError != expandTest.errorExpected && !hasError { | ||||
| 			t.Errorf("Expand('%s') did not fail as expected: got result '%+v'", | ||||
| 				expandTest.input, result) | ||||
| 			continue | ||||
| 		} | ||||
| 		if !hasError && !equal(result, expandTest.resultExpected) { | ||||
| 			t.Errorf("Expand('%s') failed: got result '%+v', expected result '%v'", | ||||
| 				expandTest.input, result, expandTest.resultExpected) | ||||
| 			continue | ||||
| 		} | ||||
|  | ||||
| 		t.Logf("Checked hostlist.Expand('%s'): result = '%+v', err = '%v'", | ||||
| 			expandTest.input, result, err) | ||||
| 	} | ||||
| } | ||||
		Reference in New Issue
	
	Block a user