class Object

Constants

MAX_SIZE

Public Instance Methods

collect_hostlist(hosts, silently_discard_bad = false) click to toggle source

Collect a hostlist string from a list of hosts

# File lib/hostlist_expand_collect.rb, line 122
def collect_hostlist(hosts, silently_discard_bad = false)
  left_right = []

  hosts.each do |host|
    # Remove leading and trailing whitespace first, and skip empty lines
    host = host.strip
    next if host.empty?

    # We cannot accept a host containing any of the three special
    # characters in the hostlist syntax (comma and flat brackets)

    unless host.scan(/[\[\]\,]/).empty?
      next if silently_discard_bad

      raise 'Error! Forbidden character!'
    end

    left_right.push [host, ""]
  end

  # Call the iterative function until it syas it's done
  looping = true

  while looping
    left_right, looping = collect_hostlist_1 left_right
  end

  return (left_right.collect { |left, right| left + right }).join(",")
end
collect_hostlist_1(left_right, silently_discard_bad = false) click to toggle source

Collect a hostlist string from a list of hosts (left + right)

# File lib/hostlist_expand_collect.rb, line 162
def collect_hostlist_1(left_right, silently_discard_bad = false)
  # Scan a list of hosts (left+right) and build two things:
  # 1) a set of all hosts seen (used later)
  # 2) a list where each host entry is preprocessed for correct sorting

  sortlist = []
  remeaning = Set.new

  left_right.each do |left, right|
    host = left + right
    remeaning.add host

    m = /^(.*?)([0-9]+)?([^0-9]*)$/.match left

    prefix, num_str, suffix = m[1], m[2], m[3]

    suffix = suffix + right

    unless num_str
      throw 'Error! Prefix is not empty' unless prefix.empty?
      sortlist.push [[host, nil], nil, nil, host]
    else
      num_int = num_str.to_i
      sortlist.push [[prefix, suffix], num_int, num_str.length, host]
    end
  end

  sortlist.sort!

  results = []
  need_another_loop = false

  sortlist.group_by { |x| x[0] }.each do |prefix_suffix, group|
    prefix, suffix = prefix_suffix

    unless suffix
      # Special case: a host with no numeric part
      results.push ["", prefix]
      remeaning.delete prefix
    else
      # General case
      range_list = []

      group.each do |prefix_suffix_2, num_int, num_width, host|
        prefix_2, suffix_2 = prefix_suffix_2

        next unless remeaning.include? host
        raise 'Error! num_int is nil' unless num_int

        # Scan for a range starting at the current host
        low = num_int

        while true
          host = "#{prefix}%0#{num_width}d#{suffix}" % num_int

          if remeaning.include? host
            remeaning.delete host
            num_int += 1
          else
            break
          end
        end

        high = num_int - 1

        raise 'Error! High < Low' if high < low

        range_list.push [low, high, num_width]
      end

      need_another_loop = true

      if range_list.length == 1 and range_list[0][0] == range_list[0][1]
        results.push [prefix, "%0#{range_list[0][2]}d#{suffix}" % range_list[0][0]]
      else
        results.push [prefix, "[" + range_list.map { |l, h, w| format_range(l, h, w)}.join(',') + "]" + suffix]
      end
    end
  end

  raise 'Error! Remeaning is not empty' unless remeaning.empty?

  return results, need_another_loop
end
expand_hostlist(hostlist, allow_duplicates=false) click to toggle source

Expand a hostlist expression string to list

Example: expand_hostlist(“n,d”) ==>

['n9', 'n10', 'n11', 'd01', 'd02']
# File lib/hostlist_expand_collect.rb, line 84
def expand_hostlist(hostlist, allow_duplicates=false)
  results = []
  bracket_level = 0
  part = ''

  (hostlist + ',').each_char do |c|
    if c == ',' and bracket_level == 0
      if part
        results.concat(expand_part part)
      end

      part = ''
      bad_part = false
    else
      part += c
    end

    bracket_level += 1 if c == '['
    bracket_level -= 1 if c == ']'

    if bracket_level > 1
      raise 'Error! Bad hostlist (nested brackets)'
    elsif bracket_level < 0
      raise 'Error! Bad hostlist (unbalanced brackets)'
    end
  end

  if bracket_level > 0
    raise 'Error! Bad hostlist (unbalanced brackets)'
  end

  return remove_duplicates(results) unless allow_duplicates

  results
end
expand_part(s) click to toggle source

Expand a part (e.g. “x[1-2]y[1-3] (no outer level commas)”)

# File lib/hostlist_expand_collect.rb, line 42
def expand_part(s)
  # Базовый случай - пустая часть развернётся в список с ""
  return [""] if s == ''

  # Разбить на:
  # 1) Строку префикс (может быть пустой)
  # 2) Rangelist в скобках (может быть пропущен)
  # 3) Остальное

  m = /([^,\[]*)(\[[^\]]*\])?(.*)/.match s

  prefix, rangelist, rest = m[1], m[2], m[3]

  # Развернём остаток
  rest_expanded = expand_part rest

  # Развернём собственную часть
  unless rangelist
    us_expanded = [prefix]
  else
    us_expanded = expand_rangelist(prefix, rangelist[1..-2])
  end

  # Комбинируем список со списком
  if us_expanded.length * rest_expanded.length > MAX_SIZE
    raise 'Error! Bad hostlist (results too large)'
  end

  us_expanded.product(rest_expanded).collect { |x, y| x + y }
end
expand_range(prefix, r) click to toggle source

Expand a range (e.g/ 1-10 or 14), putting a prefix before

# File lib/hostlist_expand_collect.rb, line 7
def expand_range(prefix, r)
  return ["#{prefix}#{r}"] if /^[0-9]+$/.match r

  m = /^([0-9]+)-([0-9]+)$/.match r
  raise 'Error! Bad hostlist (bad range)' unless m

  s_low, s_high = m[1], m[2]

  low = s_low.to_i
  high = s_high.to_i

  raise 'Error! Start > Stop' if high < low
  raise 'Error! Range too large' if high - low > MAX_SIZE

  result = []

  (low..high).each { |v| result.push "#{prefix}%0#{s_low.length}d" % v }

  result
end
expand_rangelist(prefix, rangelist) click to toggle source

Expand a rangelist (e.g “1-10,14”), putting a prefix before.

# File lib/hostlist_expand_collect.rb, line 30
def expand_rangelist(prefix, rangelist)
  result = []

  rangelist.split(",").each do |r|
    result.concat(expand_range(prefix, r))
  end

  result
end
format_range(low, high, width) click to toggle source

Format a range from low to high inclusively, with a certain width

# File lib/hostlist_expand_collect.rb, line 154
def format_range(low, high, width)
  return "%0#{width}d" % low if low == high

  "%0#{width}d-" % low + "%0#{width}d" % high
end
remove_duplicates(l) click to toggle source

Удалить дубликаты из списка

# File lib/hostlist_expand_collect.rb, line 75
def remove_duplicates(l)
  l.to_set.to_a
end