From 4fb6ac01403405c7ed2b6299a544362f3b467b15 Mon Sep 17 00:00:00 2001 From: Holger Obermaier <40787752+ho-ob@users.noreply.github.com> Date: Tue, 29 Nov 2022 14:04:31 +0100 Subject: [PATCH] Add hostlist package --- pkg/hostlist/hostlist.go | 125 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 125 insertions(+) create mode 100644 pkg/hostlist/hostlist.go diff --git a/pkg/hostlist/hostlist.go b/pkg/hostlist/hostlist.go new file mode 100644 index 0000000..6ed5ae0 --- /dev/null +++ b/pkg/hostlist/hostlist.go @@ -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 +}