Class: Cisco::Utils

Inherits:
Object
  • Object
show all
Defined in:
lib/cisco_node_utils/cisco_cmn_utils.rb

Overview

General utility class

Class Method Summary collapse

Class Method Details

.add_quotes(value) ⇒ Object

merge_range



372
373
374
375
376
377
# File 'lib/cisco_node_utils/cisco_cmn_utils.rb', line 372

def self.add_quotes(value)
  return value if image_version?(/7.3.[0-1]/) || value.nil?
  value = "'#{value}'" unless
    value.start_with?('"', "'") && value.end_with?('"', "'")
  value
end

.array_to_str(array, sort = true) ⇒ Object

This method converts an array to string form for ex: if the array has 1, 2 to 10, 83 to 2014, 3022 and the string will be “1,2-10,83-2014,3022”



220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
# File 'lib/cisco_node_utils/cisco_cmn_utils.rb', line 220

def self.array_to_str(array, sort=true)
  farray = sort ? array.compact.uniq.sort : array.compact
  lranges = []
  unless farray.empty?
    l = array.first
    r = nil
    farray.each do |aelem|
      if r && aelem != r.succ
        if l == r
          lranges << l
        else
          lranges << Range.new(l, r)
        end
        l = aelem
      end
      r = aelem
    end
    if l == r
      lranges << l
    else
      lranges << Range.new(l, r)
    end
  end
  lranges.to_s.gsub('..', '-').delete('[').delete(']').delete(' ')
end

.attach_prefix(val, prop, prefix = nil) ⇒ Object

This method is used in config_set for CLIs which can set multiple properties using the same CLI. This method attaches prefix to the given property when the prefix is different from the property name else it attaches property name itself. It also appends the value to be set. For ex. if the set_value is <precedence> <time_range> the prop for precedence could be ‘precedence’, and for time_range, the prop and prefix could be ‘time_range’ and ‘time-range’



414
415
416
417
# File 'lib/cisco_node_utils/cisco_cmn_utils.rb', line 414

def self.attach_prefix(val, prop, prefix=nil)
  prefix = prop.to_s if prefix.nil?
  val.to_s.empty? ? val : "#{prefix} #{val}"
end

.bitmask_to_length(bitmask) ⇒ Object



172
173
174
175
176
177
178
# File 'lib/cisco_node_utils/cisco_cmn_utils.rb', line 172

def self.bitmask_to_length(bitmask)
  # Convert bitmask to a 32-bit integer,
  # convert that to binary, and count the 1s
  IPAddr.new(bitmask).to_i.to_s(2).count('1')
rescue IPAddr::InvalidAddressError => e
  raise ArgumentError, "bitmask '#{bitmask}' is not valid: #{e}"
end

.chassis_pid?(ver_regexp) ⇒ Boolean

Returns:

  • (Boolean)


111
112
113
114
# File 'lib/cisco_node_utils/cisco_cmn_utils.rb', line 111

def self.chassis_pid?(ver_regexp)
  require_relative 'platform'
  return true if Platform.chassis['pid'][ver_regexp]
end

.dash_range_to_elements(range) ⇒ Object

Convert a dash-range set into individual elements. This is useful for preparing inputs to delta_add_remove().

Inputs an array or string of dash-ranges:

["2-5", "9", "4-6"] or '2-5, 9, 4-6' or ['2-5, 9, 4-6']

Returns an array of range elements:

["2", "3", "4", "5", "6", "9"]


336
337
338
339
340
341
342
343
344
345
346
347
348
# File 'lib/cisco_node_utils/cisco_cmn_utils.rb', line 336

def self.dash_range_to_elements(range)
  return [] if range.nil?
  range = range.shift if range.is_a?(Array) && range.length == 1
  range = range.split(',') if range.is_a?(String)

  final = []
  range = dash_range_to_ruby_range(range)
  range.each do |rng|
    # 2..5 maps to ["2", "3", "4", "5"]
    final << rng.map(&:to_s)
  end
  final.flatten.uniq.sort
end

.dash_range_to_ruby_range(range) ⇒ Object

Convert a cli-dash-syntax range to ruby-range. This is useful for preparing inputs to merge_range().

Inputs an array or string of dash-syntax ranges -> returns an array of ruby ranges.

Accepts an array or string: [“2-5”, “9”, “4-6”] or ‘2-5, 9, 4-6’ Returns an array of ranges: [2..5, 9..9, 4..6]



290
291
292
293
294
295
296
297
298
299
300
301
302
# File 'lib/cisco_node_utils/cisco_cmn_utils.rb', line 290

def self.dash_range_to_ruby_range(range)
  range = range.split(',') if range.is_a?(String)
  range.map! do |rng|
    if rng[/-/]
      # '2-5' -> 2..5
      rng.split('-').inject { |a, e| a.to_i..e.to_i }
    else
      # '9' -> 9..9
      rng.to_i..rng.to_i
    end
  end
  range
end

.delta_add_remove(should, current = [], opt = nil) ⇒ Object



144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
# File 'lib/cisco_node_utils/cisco_cmn_utils.rb', line 144

def self.delta_add_remove(should, current=[], opt=nil)
  current = [] if current.nil?
  should = [] if should.nil?

  # Remove nil entries from array
  should.each(&:compact!) if depth(should) > 1
  delta = { add: should - current, remove: current - should }

  # Some cli properties cannot be updated, thus must be removed first
  return delta if opt == :updates_not_allowed

  # Delete entries from :remove if f1 is an update to an existing command
  delta[:add].each do |id, _|
    # Differentiate between comparing nested and unnested arrays by
    # checking the depth of the array.
    if depth(should) == 1
      delta[:remove].delete_if { |f1| [f1] if f1.to_s == id.to_s }
    else
      delta[:remove].delete_if { |f1, f2| [f1, f2] if f1.to_s == id.to_s }
    end
  end
  delta
end

.depth(a) ⇒ Object

Helper to build a hash of add/remove commands for a nested array. Useful for network, redistribute, etc.

 should: an array of expected cmds (manifest/recipe)
current: an array of existing cmds on the device


139
140
141
142
# File 'lib/cisco_node_utils/cisco_cmn_utils.rb', line 139

def self.depth(a)
  return 0 unless a.is_a?(Array)
  1 + depth(a[0])
end

.extract_value(match_method, prop, prefix = nil) ⇒ Object

This method is used in config_get for CLIs which have multiple properties in the same output Given a match_method which defines regex pattern and the match criteria, this method, extracts the value of a property with or without a prefix. For ex. if the regex pattern is something like: (?<action>S+)? the property to extract is action and prefix is nil for (?<src_port>range S+) the property to extract is src_port and prefix is range



389
390
391
392
393
394
395
396
397
398
399
400
401
402
# File 'lib/cisco_node_utils/cisco_cmn_utils.rb', line 389

def self.extract_value(match_method, prop, prefix=nil)
  prefix = prop if prefix.nil?
  mm = match_method

  return nil if mm.nil?

  return nil unless mm.names.include?(prop)

  # extract and return value that follows prefix + <space>
  regexp = Regexp.new("#{Regexp.escape(prefix)} (?<extracted>.*)")
  value_match = regexp.match(mm[prop])
  return nil if value_match.nil?
  value_match[:extracted]
end

.fretta?Boolean

Returns:

  • (Boolean)


116
117
118
119
120
121
122
# File 'lib/cisco_node_utils/cisco_cmn_utils.rb', line 116

def self.fretta?
  require_relative 'platform'
  Platform.slots.each do |_x, row|
    return true if row['pid'][/-R/]
  end
  false
end

.get_reset_range(total_range, remove_ranges) ⇒ Object

For spanning tree range based parameters, the range is very dynamic and so before the parameters are set, the rest of the range needs to be reset For ex: if the ranges 2-42 and 83-200 are getting set, and the total range of the given parameter is 1-4000 then 1,43-82,201-4000 needs to be reset. This method takes the set ranges and gives back the range to be reset



194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
# File 'lib/cisco_node_utils/cisco_cmn_utils.rb', line 194

def self.get_reset_range(total_range, remove_ranges)
  fail 'invalid range' unless total_range.include?('-')
  return total_range if remove_ranges.empty?

  trs = total_range.gsub('-', '..')
  tra = trs.split('..').map { |d| Integer(d) }
  tr = tra[0]..tra[1]
  tarray = tr.to_a
  remove_ranges.each do |rr, _val|
    rarray = rr.gsub('-', '..').split(',')
    rarray.each do |elem|
      if elem.include?('..')
        elema = elem.split('..').map { |d| Integer(d) }
        ele = elema[0]..elema[1]
        tarray -= ele.to_a
      else
        tarray.delete(elem.to_i)
      end
    end
  end
  Utils.array_to_str(tarray)
end

.image_version?(ver_regexp) ⇒ Boolean

Returns:

  • (Boolean)


106
107
108
109
# File 'lib/cisco_node_utils/cisco_cmn_utils.rb', line 106

def self.image_version?(ver_regexp)
  require_relative 'platform'
  return true if Platform.image_version[ver_regexp]
end

.length_to_bitmask(length) ⇒ Object

delta_add_remove



168
169
170
# File 'lib/cisco_node_utils/cisco_cmn_utils.rb', line 168

def self.length_to_bitmask(length)
  IPAddr.new('255.255.255.255').mask(length).to_s
end

.merge_range(range) ⇒ Object

Merge overlapping ranges.

Inputs an array of ruby ranges: [2..5, 9..9, 4..6] Returns an array of merged ruby ranges: [2..6, 9..9]



355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
# File 'lib/cisco_node_utils/cisco_cmn_utils.rb', line 355

def self.merge_range(range)
  # sort to lowest range 'first' values:
  #    [2..5, 9..9, 4..6]  ->  [2..5, 4..6, 9..9]
  range = range.sort_by(&:first)

  *merged = range.shift
  range.each do |r|
    lastr = merged[-1]
    if lastr.last >= r.first - 1
      merged[-1] = lastr.first..[r.last, lastr.last].max
    else
      merged.push(r)
    end
  end
  merged
end

.nexus_i2_imageObject

Helper utility to check for older Nexus I2 images



101
102
103
104
# File 'lib/cisco_node_utils/cisco_cmn_utils.rb', line 101

def self.nexus_i2_image
  require_relative 'platform'
  true if Platform.image_version[/7.0.3.I2/]
end

.normalize_intf_pattern(show_name) ⇒ Object

Helper utility to normalize the interface name pattern to be filtered. This is only a problem on N7k which has to use a section filter due to a show run int bug (no ‘all’ keyword for some interfaces).



422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
# File 'lib/cisco_node_utils/cisco_cmn_utils.rb', line 422

def self.normalize_intf_pattern(show_name)
  return '' if show_name.nil? || show_name.empty?
  require_relative 'platform'
  return show_name unless Platform.hardware_type[/Nexus7/]
  return show_name if show_name[-1] == '$' #  already normalized
  pat = show_name.downcase + '$'
  case pat
  when /ethernet/
    pat.sub!(/ethernet/, 'Ethernet')
  when /loopback/
    pat.sub!(/loopback/, 'loopback')
  when /port-channel/
    pat.sub!(/port-channel/, 'port-channel')
  when /vlan/
    pat.sub!(/vlan/, 'Vlan')
  else
    # wildcard the first char of the name
    pat.sub!(/./, '.')
  end
  pat
end

.normalize_range_array(range, fmt = :array) ⇒ Object

normalize_range_array

Given a list of ranges, merge any overlapping ranges and sort them.

Note: The ranges are converted to ruby ranges for easy merging, then converted back to a cli-syntax range.

Accepts an array or string:

["2-5", "9", "4-6"]  -or-  '2-5, 9, 4-6'  -or-  ["2-5, 9, 4-6"]

Returns a merged and ordered range as an array or string:

["2-6", "9"] -or- '2-6,9'


258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
# File 'lib/cisco_node_utils/cisco_cmn_utils.rb', line 258

def self.normalize_range_array(range, fmt=:array)
  return range if range.nil? || range.empty?
  range = range.clone

  # Handle string within an array: ["2-5, 9, 4-6"] to '2-5, 9, 4-6'
  range = range.shift if range.is_a?(Array) && range.length == 1

  # Handle string only: '2-5, 9, 4-6' to ["2-5", "9", "4-6"]
  range = range.split(',') if range.is_a?(String)

  # Convert to ruby-syntax ranges
  range = dash_range_to_ruby_range(range)

  # Sort & Merge
  merged = merge_range(range)

  # Convert back to cli dash-syntax
  merged_array = ruby_range_to_dash_range(merged)

  return merged_array.join(',') if fmt == :string
  merged_array
end

.process_network_mask(network) ⇒ Object

Helper utility method for ip/prefix format networks. For ip/prefix format ‘1.1.1.1/24’ or ‘2000:123:38::34/64’, we need to mask the address using the prefix length so that they are converted to ‘1.1.1.0/24’ or ‘2000:123:38::/64’



128
129
130
131
132
133
# File 'lib/cisco_node_utils/cisco_cmn_utils.rb', line 128

def self.process_network_mask(network)
  mask = network.split('/')[1]
  address = IPAddr.new(network).to_s
  network = address + '/' + mask unless mask.nil?
  network
end

.ruby_range_to_dash_range(range, type = :array) ⇒ Object

Convert a ruby-range to cli-dash-syntax.

Inputs an array of ruby ranges -> returns an array or string of dash-syntax ranges.

when (:array) [2..6, 9..9] -> [‘2-6’, ‘9’]

when (:string) [2..6, 9..9] -> ‘2-6, 9’



313
314
315
316
317
318
319
320
321
322
323
324
325
326
# File 'lib/cisco_node_utils/cisco_cmn_utils.rb', line 313

def self.ruby_range_to_dash_range(range, type=:array)
  fail unless range.is_a?(Array)
  range.map! do |r|
    if r.first == r.last
      # 9..9 -> '9'
      r.first.to_s
    else
      # 2..6 -> '2-6'
      r.first.to_s + '-' + r.last.to_s
    end
  end
  return range.join(', ') if type == :string
  range
end

.zero_pad_macaddr(mac) ⇒ Object

Helper to 0-pad a mac address.



181
182
183
184
185
# File 'lib/cisco_node_utils/cisco_cmn_utils.rb', line 181

def self.zero_pad_macaddr(mac)
  return nil if mac.nil? || mac.empty?
  o1, o2, o3 = mac.split('.').map { |o| o.to_i(16).to_s(10) }
  sprintf('%04x.%04x.%04x', o1, o2, o3)
end