Class: Cisco::Node

Inherits:
Object
  • Object
show all
Includes:
Singleton
Defined in:
lib/cisco_node_utils/node.rb

Overview

class Cisco::Node Singleton representing the network node (switch/router) that is running this code. The singleton is lazily instantiated, meaning that it doesn’t exist until some client requests it (with Node.instance())

Constant Summary collapse

@@lazy_connect =

For unit testing - we won’t know the node connection info at load time.

false

Instance Attribute Summary collapse

Class Method Summary collapse

Instance Method Summary collapse

Constructor Details

#initializeNode



122
123
124
125
126
# File 'lib/cisco_node_utils/node.rb', line 122

def initialize
  @client = nil
  @cmd_ref = nil
  connect unless @@lazy_connect
end

Instance Attribute Details

#clientObject (readonly)

hidden as well



113
114
115
# File 'lib/cisco_node_utils/node.rb', line 113

def client
  @client
end

#cmd_refObject (readonly)

END NODE API Here and below are implementation details and private APIs that most providers shouldn’t need to know about or use.



113
114
115
# File 'lib/cisco_node_utils/node.rb', line 113

def cmd_ref
  @cmd_ref
end

Class Method Details

.lazy_connect=(val) ⇒ Object



118
119
120
# File 'lib/cisco_node_utils/node.rb', line 118

def Node.lazy_connect=(val)
  @@lazy_connect = val
end

Instance Method Details

#bootString



573
574
575
# File 'lib/cisco_node_utils/node.rb', line 573

def boot
  config_get("show_version", "boot_image")
end

#build_config_get(feature, ref, type) ⇒ String, Array

Helper method to use the feature, name config_get if present else use feature, “template” config_get



274
275
276
277
278
279
280
281
282
283
284
# File 'lib/cisco_node_utils/node.rb', line 274

def build_config_get(feature, ref, type)
  raise "lazy_connect specified but did not request connect" unless @cmd_ref
  # Use feature name config_get string if present
  # else use feature template: config_get
  if ref.hash.key?("config_get")
    return show(ref.config_get, type)
  else
    template = @cmd_ref.lookup(feature, "_template")
    return show(template.config_get, type)
  end
end

#build_config_get_token(feature, ref, args) ⇒ String, Array

Helper method to build a multi-line config_get_token if the feature, name contains a config_get_token_append entry.



237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
# File 'lib/cisco_node_utils/node.rb', line 237

def build_config_get_token(feature, ref, args)
  raise "lazy_connect specified but did not request connect" unless @cmd_ref
  # Why clone token? A bug in some ruby versions caused token to convert
  # to type Regexp unexpectedly. The clone hard copy resolved it.

  # If the options are presented as type Hash process as
  # key-value replacement pairs
  return ref.config_get_token.clone unless args[0].is_a?(Hash)
  options = args[0]
  token = []
  # Use _template yaml entry if config_get_token_append
  if ref.to_s[/config_get_token_append/]
    # Get yaml feature template:
    template = @cmd_ref.lookup(feature, "_template")
    # Process config_get_token: from template:
    token.push(replace_token_ids(template.config_get_token, options))
    # Process config_get_token_append sequence: from template:
    template.config_get_token_append.each do |line|
      token.push(replace_token_ids(line, options))
    end
    # Add feature->property config_get_token append line
    token.push(ref.config_get_token_append)
  else
    token.push(replace_token_ids(ref.config_get_token, options))
  end
  token.flatten!
  token.compact!
  token
end

#build_config_set(feature, ref, args) ⇒ String, Array

Helper method to build a multi-line config_set if the feature, name contains a config_get_set_append yaml entry.



293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
# File 'lib/cisco_node_utils/node.rb', line 293

def build_config_set(feature, ref, args)
  raise "lazy_connect specified but did not request connect" unless @cmd_ref
  # If the options are presented as type Hash process as
  # key-value replacement pairs
  return ref.config_set unless args[0].is_a?(Hash)
  options = args[0]
  config_set = []
  # Use _template yaml entry if config_set_append
  if ref.to_s[/config_set_append/]
    # Get yaml feature template:
    template = @cmd_ref.lookup(feature, "_template")
    # Process config_set: from template:
    config_set.push(replace_token_ids(template.config_set, options))
    # Process config_set_append sequence: from template:
    template.config_set_append.each do |line|
      config_set.push(replace_token_ids(line, options))
    end
    # Add feature->property config_set append line
    config_set.push(replace_token_ids(ref.config_set_append, options))
  else
    config_set.push(replace_token_ids(ref.config_set, options))
  end
  config_set.flatten!
  config_set.compact!
  config_set
end

#cache_auto=(enable) ⇒ Object



163
164
165
# File 'lib/cisco_node_utils/node.rb', line 163

def cache_auto=(enable)
  @client.cache_auto = enable
end

#cache_auto?Boolean



159
160
161
# File 'lib/cisco_node_utils/node.rb', line 159

def cache_auto?
  @client.cache_auto?
end

#cache_enable=(enable) ⇒ Object



155
156
157
# File 'lib/cisco_node_utils/node.rb', line 155

def cache_enable=(enable)
  @client.cache_enable = enable
end

#cache_enable?Boolean



151
152
153
# File 'lib/cisco_node_utils/node.rb', line 151

def cache_enable?
  @client.cache_enable?
end

#cache_flushObject

Clear the cache of CLI output results.

If cache_auto is true (default) then this will be performed automatically whenever a config_set() is called, but providers may also call this to explicitly force the cache to be cleared.



106
107
# File 'lib/cisco_node_utils/node.rb', line 106

def cache_flush
end

#config(commands) ⇒ Object

Send a config command to the device. In general, clients should use config_set() rather than calling this function directly.

Raises:



467
468
469
470
471
# File 'lib/cisco_node_utils/node.rb', line 467

def config(commands)
  @client.config(commands)
rescue CiscoNxapi::CliError => e
  raise Cisco::CliError.new(e.input, e.clierror, e.previous)
end

#config_get(feature, name, *args) ⇒ String, ...

Convenience wrapper for show(command, :structured). Uses CommandReference to look up the given show command and key of interest, executes that command, and returns the value corresponding to that key.

Examples:

config_get(“show_version”, “system_image”)


config_get(“ospf”, “router_id”,

{:name => "green", :vrf => "one"})

Raises:

  • (IndexError)

    if the given (feature, name) pair is not in the CommandReference data or if the data doesn’t have values defined for the ‘config_get’ and (optional) ‘config_get_token’ fields.

  • (Cisco::CliError)

    if the given command is rejected by the device.



71
72
# File 'lib/cisco_node_utils/node.rb', line 71

def config_get(feature, name)
end

#config_get_default(feature, name) ⇒ String

Uses CommandReference to lookup the default value for a given feature and feature property.

Examples:

config_get_default(“vtp”, “file”)


Raises:

  • (IndexError)

    if the given (feature, name) pair is not in the CommandReference data or if the data doesn’t have values defined for the ‘default_value’ field.



84
85
# File 'lib/cisco_node_utils/node.rb', line 84

def config_get_default(feature, name)
end

#config_set(feature, name, *args) ⇒ Object

Uses CommandReference to look up the given config command(s) of interest and then applies the configuration.

Examples:

config_set(“vtp”, “domain”, “example.com”)


config_set(“ospf”, “router_id”,

{:name => "green", :vrf => "one", :state => "",
 :router_id => "192.0.0.1"})

Raises:

  • (IndexError)

    if no relevant cmd_ref config_set exists

  • (ArgumentError)

    if too many or too few args are provided.

  • (Cisco::CliError)

    if any command is rejected by the device.



98
99
# File 'lib/cisco_node_utils/node.rb', line 98

def config_set(feature, name, *args)
end

#connect(*args) ⇒ Object

“hidden” API - used for UT but shouldn’t be used elsewhere



133
134
135
136
137
# File 'lib/cisco_node_utils/node.rb', line 133

def connect(*args)
  @client = CiscoNxapi::NxapiClient.new(*args)
  @cmd_ref = CommandReference::CommandReference.new(product_id)
  cache_flush
end

#domain_nameString



528
529
530
531
532
533
534
535
# File 'lib/cisco_node_utils/node.rb', line 528

def domain_name
  result = config_get("domain_name", "domain_name")
  if result.nil?
    return ""
  else
    return result[0]
  end
end

#host_nameString



523
524
525
# File 'lib/cisco_node_utils/node.rb', line 523

def host_name
  config_get("show_version", "host_name")
end

#last_reset_reasonString



560
561
562
# File 'lib/cisco_node_utils/node.rb', line 560

def last_reset_reason
  config_get("show_version", "last_reset_reason")
end

#last_reset_timeString



550
551
552
553
554
555
556
557
# File 'lib/cisco_node_utils/node.rb', line 550

def last_reset_time
  output = config_get("show_version", "last_reset_time")
  return "" if output.nil?
  # NX-OS may provide leading/trailing whitespace:
  # " Sat Oct 25 00:39:25 2014\n"
  # so be sure to strip() it down to the actual string.
  output.strip
end

#osString



485
486
487
488
489
# File 'lib/cisco_node_utils/node.rb', line 485

def os
   o = config_get("show_version", "header")
   raise "failed to retrieve operating system information" if o.nil?
   o.split("\n")[0]
end

#os_versionString



492
493
494
# File 'lib/cisco_node_utils/node.rb', line 492

def os_version
  config_get("show_version", "version")
end

#product_descriptionString



497
498
499
# File 'lib/cisco_node_utils/node.rb', line 497

def product_description
  config_get("show_version", "description")
end

#product_idString



502
503
504
505
506
507
508
509
510
# File 'lib/cisco_node_utils/node.rb', line 502

def product_id
  if @cmd_ref
    return config_get("inventory", "productid")
  else
    # We use this function to *find* the appropriate CommandReference
    entries = show("show inventory", :structured)
    return entries["TABLE_inv"]["ROW_inv"][0]["productid"]
  end
end

#product_serial_numberString



518
519
520
# File 'lib/cisco_node_utils/node.rb', line 518

def product_serial_number
  config_get("inventory", "serialnum")
end

#product_version_idString



513
514
515
# File 'lib/cisco_node_utils/node.rb', line 513

def product_version_id
  config_get("inventory", "versionid")
end

#reloadObject

TODO: remove me



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

def reload
  @client.reload
end

#replace_token_ids(regexp, values) ⇒ String

Helper method to replace <> place holders in the config_get_token and config_get_token_append yaml entries.



200
201
202
203
204
# File 'lib/cisco_node_utils/node.rb', line 200

def replace_token_ids(regexp, values)
  final = replace_token_ids_string(regexp, values) if regexp.is_a?(String)
  final = replace_token_ids_array(regexp, values) if regexp.is_a?(Array)
  final
end

#replace_token_ids_array(regexp, values) ⇒ String



223
224
225
226
227
228
229
# File 'lib/cisco_node_utils/node.rb', line 223

def replace_token_ids_array(regexp, values)
  final_regexp = []
  regexp.each do |line|
    final_regexp.push(replace_token_ids_string(line, values))
  end
  final_regexp
end

#replace_token_ids_string(regexp, values) ⇒ String



209
210
211
212
213
214
215
216
217
218
# File 'lib/cisco_node_utils/node.rb', line 209

def replace_token_ids_string(regexp, values)
  replace = regexp.scan(/<(\S+)>/).flatten.map(&:to_sym)
  replace.each do |item|
    regexp = regexp.sub "<#{item}>",
      values[item].to_s if values.key?(item)
  end
  # Only return lines that actually replaced ids or did not have any
  # ids to replace. Implicit nil returned if not.
  return regexp if /<\S+>/.match(regexp).nil?
end

#show(command, type = :ascii) ⇒ Object

Send a show command to the device. In general, clients should use config_get() rather than calling this function directly.

Raises:



478
479
480
481
482
# File 'lib/cisco_node_utils/node.rb', line 478

def show(command, type=:ascii)
  @client.show(command, type)
rescue CiscoNxapi::CliError => e
  raise Cisco::CliError.new(e.input, e.clierror, e.previous)
end

#systemString



579
580
581
# File 'lib/cisco_node_utils/node.rb', line 579

def system
  config_get("show_version", "system_image")
end

#system_cpu_utilizationFloat



565
566
567
568
569
# File 'lib/cisco_node_utils/node.rb', line 565

def system_cpu_utilization
  output = config_get("system", "resources")
  raise "failed to retrieve cpu utilization" if output.nil?
  output["cpu_state_user"].to_f + output["cpu_state_kernel"].to_f
end

#system_uptimeInteger



538
539
540
541
542
543
544
545
546
547
# File 'lib/cisco_node_utils/node.rb', line 538

def system_uptime
  cache_flush
  t = config_get("show_system", "uptime")
  raise "failed to retrieve system uptime" if t.nil?
  t = t.shift
  # time units: t = ["0", "23", "15", "49"]
  t.map!(&:to_i)
  d, h, m, s = t
  (s + 60 * (m + 60 * (h + 24 * (d))))
end

#to_sObject



128
129
130
# File 'lib/cisco_node_utils/node.rb', line 128

def to_s
  @client.to_s
end

#token_str_to_regexp(token, args) ⇒ Object

Helper method for converting token strings to regexps. This helper facilitates non-standard regexp options like ignore-case. Example inputs:

token = ["/%s/i", "/%s foo %s/", "/zzz/i"]
args = ["LoopBack2", "no", "bar"]

Expected outputs:

[/LoopBack2/i, /no foo bar/, /zzz/i]


175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
# File 'lib/cisco_node_utils/node.rb', line 175

def token_str_to_regexp(token, args)
  unless args[0].is_a? Hash
    expected_args = token.join.scan(/%/).length
    raise "Given #{args.length} args, but token #{token} requires " +
      "#{expected_args}" unless args.length == expected_args
  end
  # replace all %s with *args
  token.map! { |str| sprintf(str, *args.shift(str.scan(/%/).length)) }
  # convert all to Regexp objects
  token.map! { |str|
    if str[-2..-1] == '/i'
      Regexp.new(str[1..-3], Regexp::IGNORECASE)
    else
      Regexp.new(str[1..-2])
    end
  }
  token
end