Class: Cisco::Node

Inherits:
Object
  • Object
show all
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())

Instance Attribute Summary collapse

Class Method Summary collapse

Instance Method Summary collapse

Constructor Details

#initializeNode

Returns a new instance of Node.



240
241
242
243
244
245
246
247
# File 'lib/cisco_node_utils/node.rb', line 240

def initialize
  @client = Cisco::Client.create
  @cmd_ref = nil
  @cmd_ref = CommandReference.new(product:      product_id,
                                  platform:     @client.platform,
                                  data_formats: @client.data_formats)
  cache_flush
end

Instance Attribute Details

#clientObject (readonly)

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



234
235
236
# File 'lib/cisco_node_utils/node.rb', line 234

def client
  @client
end

#cmd_refObject (readonly)

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



234
235
236
# File 'lib/cisco_node_utils/node.rb', line 234

def cmd_ref
  @cmd_ref
end

Class Method Details

.instanceObject



236
237
238
# File 'lib/cisco_node_utils/node.rb', line 236

def self.instance
  @instance ||= new
end

Instance Method Details

#bootString

Returns such as “bootflash:///n3000-uk9-kickstart.6.0.2.U5.0.941.bin”.

Returns:

  • (String)

    such as “bootflash:///n3000-uk9-kickstart.6.0.2.U5.0.941.bin”



441
442
443
# File 'lib/cisco_node_utils/node.rb', line 441

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

#cache_auto=(enable) ⇒ Object



269
270
271
# File 'lib/cisco_node_utils/node.rb', line 269

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

#cache_auto?Boolean

Returns:

  • (Boolean)


265
266
267
# File 'lib/cisco_node_utils/node.rb', line 265

def cache_auto?
  @client.cache_auto?
end

#cache_enable=(enable) ⇒ Object



261
262
263
# File 'lib/cisco_node_utils/node.rb', line 261

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

#cache_enable?Boolean

Returns:

  • (Boolean)


257
258
259
# File 'lib/cisco_node_utils/node.rb', line 257

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.



227
228
229
# File 'lib/cisco_node_utils/node.rb', line 227

def cache_flush
  @client.cache_flush
end

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

Convenience wrapper for get() 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"})

Parameters:

  • feature (String)
  • name (String)

Returns:

  • (String, Hash, Array)

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 ‘get_command’ and (optional) ‘get_value’ fields.

  • (Cisco::UnsupportedError)

    if the (feature, name) pair is flagged in the YAML as unsupported on this device.

  • (Cisco::RequestFailed)

    if the command is rejected by the device.



53
54
55
56
57
58
59
60
61
62
63
64
65
# File 'lib/cisco_node_utils/node.rb', line 53

def config_get(feature, property, *args)
  ref = @cmd_ref.lookup(feature, property)

  # If we have a default value but no getter, just return the default
  return ref.default_value if ref.default_value? && !ref.getter?

  get_args, ref = massage_structured(ref.getter(*args).clone, ref)
  data = get(command:     get_args[:command],
             data_format: get_args[:data_format],
             context:     get_args[:context],
             value:       get_args[:value])
  massage(data, ref)
end

#config_get_default(feature, property) ⇒ String?

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

Examples:

config_get_default(“vtp”, “file”)


Parameters:

  • feature (String)
  • name (String)

Returns:

  • (String)
  • (nil)

    if this feature/name pair is marked as unsupported

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.



196
197
198
199
# File 'lib/cisco_node_utils/node.rb', line 196

def config_get_default(feature, property)
  ref = @cmd_ref.lookup(feature, property)
  ref.default_value
end

#config_set(feature, property, *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"})

Parameters:

  • feature (String)
  • name (String)
  • args (*String)

    zero or more args to be substituted into the cmdref.

Raises:

  • (IndexError)

    if no relevant cmd_ref config_set exists

  • (ArgumentError)

    if too many or too few args are provided.

  • (Cisco::UnsupportedError)

    if this feature/name is unsupported

  • (Cisco::RequestFailed)

    if any command is rejected by the device.



216
217
218
219
220
# File 'lib/cisco_node_utils/node.rb', line 216

def config_set(feature, property, *args)
  ref = @cmd_ref.lookup(feature, property)
  set_args = ref.setter(*args)
  set(**set_args)
end

#delete_yang(yang) ⇒ Object

Delete the specified JSON YANG config from the device.



305
306
307
# File 'lib/cisco_node_utils/node.rb', line 305

def delete_yang(yang)
  @client.set(data_format: :yang_json, values: [yang], mode: :delete_config)
end

#domain_nameString

Returns such as “example.com”.

Returns:

  • (String)

    such as “example.com”



407
408
409
# File 'lib/cisco_node_utils/node.rb', line 407

def domain_name
  config_get('dnsclient', 'domain_name')
end

#drill_down_structured(value, ref) ⇒ Object

Drill down into structured nxapi table data and return value from the row specified by a two part key.

Example: Get vlanshowbr-vlanname in the row that contains vlan id 1000 “get_value”=>[“vlanshowbr-vlanid-utf 1000”, “vlanshowbr-vlanname”] Example with optional regexp match “get_value”=>[“vlanshowbr-vlanid-utf 1000”, “vlanshowbr-vlanname”,

'/^shutdown$/']


105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
# File 'lib/cisco_node_utils/node.rb', line 105

def drill_down_structured(value, ref)
  # Nothing to do unless nxapi_structured
  return value unless ref.hash['drill_down']

  row_key = ref.hash['get_value'][0][/^\S+/]

  # Escape special characters if any in row_index and add
  # anchors for exact match.
  row_index = Regexp.escape(ref.hash['get_value'][0][/\S+$/])
  row_index = "^#{row_index}$"

  data_key = ref.hash['get_value'][1]
  regexp_filter = nil
  if ref.hash['get_value'][2]
    regexp_filter = Regexp.new ref.hash['get_value'][2][1..-2]
  end
  # Get the value using the row_key, row_index and data_key
  value = value.is_a?(Hash) ? [value] : value
  data = nil
  value.each do |row|
    if row[row_key].to_s[/#{row_index}/]
      data = row[data_key]
      data = data.nil? ? '' : data
    end
  end
  return value if data.nil?
  if regexp_filter
    filtered = regexp_filter.match(data)
    return filtered.nil? ? filtered : filtered[filtered.size - 1]
  end
  data
end

#get(**kwargs) ⇒ Object

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

Raises:



287
288
289
# File 'lib/cisco_node_utils/node.rb', line 287

def get(**kwargs)
  @client.get(**kwargs)
end

#get_yang(yang_path) ⇒ Object

Retrieve JSON YANG config from the device for the specified path.



310
311
312
# File 'lib/cisco_node_utils/node.rb', line 310

def get_yang(yang_path)
  @client.get(data_format: :yang_json, command: yang_path)
end

#get_yang_oper(yang_path) ⇒ Object

Retrieve JSON YANG operational data for the specified path.



315
316
317
# File 'lib/cisco_node_utils/node.rb', line 315

def get_yang_oper(yang_path)
  @client.get(data_format: :yang_json, command: yang_path, mode: :get_oper)
end

#host_nameString

Returns such as “bxb-oa-n3k-7”.

Returns:

  • (String)

    such as “bxb-oa-n3k-7”



402
403
404
# File 'lib/cisco_node_utils/node.rb', line 402

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

#inspectObject



253
254
255
# File 'lib/cisco_node_utils/node.rb', line 253

def inspect
  "Node: client:'#{client.inspect}' cmd_ref:'#{cmd_ref.inspect}'"
end

#last_reset_reasonString

Returns such as “Reset Requested by CLI command reload”.

Returns:

  • (String)

    such as “Reset Requested by CLI command reload”



428
429
430
# File 'lib/cisco_node_utils/node.rb', line 428

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

#last_reset_timeString

Returns timestamp of last reset time.

Returns:

  • (String)

    timestamp of last reset time



423
424
425
# File 'lib/cisco_node_utils/node.rb', line 423

def last_reset_time
  config_get('show_version', 'last_reset_time')
end

#massage(value, ref) ⇒ Object

Attempt to massage the given value into the format specified by the given CmdRef object.



140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
# File 'lib/cisco_node_utils/node.rb', line 140

def massage(value, ref)
  Cisco::Logger.debug "Massaging '#{value}' (#{value.inspect})"
  value = drill_down_structured(value, ref)
  if value.is_a?(Array) && !ref.multiple
    fail "Expected zero/one value but got '#{value}'" if value.length > 1
    value = value[0]
  end
  if (value.nil? || value.to_s.empty?) &&
     ref.default_value? && ref.auto_default
    Cisco::Logger.debug "Default: #{ref.default_value}"
    return ref.default_value
  end
  if ref.multiple && ref.hash['get_data_format'] == :nxapi_structured
    return value if value.nil?
    value = [value.to_s] if value.is_a?(String) || value.is_a?(Fixnum)
  end
  return value unless ref.kind
  value = massage_kind(value, ref)
  Cisco::Logger.debug "Massaged to '#{value}'"
  value
end

#massage_kind(value, ref) ⇒ Object



162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
# File 'lib/cisco_node_utils/node.rb', line 162

def massage_kind(value, ref)
  case ref.kind
  when :boolean
    if value.nil? || value.empty?
      value = false
    elsif /^no / =~ value
      value = false
    elsif /disable$/ =~ value
      value = false
    else
      value = true
    end
  when :int
    value = value.to_i unless value.nil?
  when :string
    value = '' if value.nil?
    value = value.to_s.strip
  when :symbol
    value = value.to_sym unless value.nil?
  end
  value
end

#massage_structured(get_args, ref) ⇒ Object

The yaml file may specifiy an Array as the get_value to drill down into nxapi_structured table output. The table may contain multiple rows but only one of the rows has the interesting data.



70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
# File 'lib/cisco_node_utils/node.rb', line 70

def massage_structured(get_args, ref)
  # Nothing to do unless nxapi_structured.
  return [get_args, ref] unless
    ref.hash['get_data_format'] == :nxapi_structured

  # The CmdRef object will contain a get_value Array with 2 values.
  # The first value is the key to identify the correct row in the table
  # of structured output and the second is the key to identify the data
  # to retrieve.
  #
  # Example: Get vlanshowbr-vlanname in the row that contains a specific
  #  vlan_id.
  # "get_value"=>["vlanshowbr-vlanid-utf <vlan_id>", "vlanshowbr-vlanname"]
  #
  # TBD: Why do we need to check is_a?(Array) here?
  if ref.hash['get_value'].is_a?(Array) && ref.hash['get_value'].size >= 2
    # Replace the get_value hash entry with the value after any tokens
    # specified in the yaml file have been replaced and set get_args[:value]
    # to nil so that the structured table data can be retrieved properly.
    ref.hash['get_value'] = get_args[:value]
    ref.hash['drill_down'] = true
    get_args[:value] = nil
    cache_flush
  end
  [get_args, ref]
end

#merge_yang(yang) ⇒ Object

Merge the specified JSON YANG config with the running config on the device.



293
294
295
# File 'lib/cisco_node_utils/node.rb', line 293

def merge_yang(yang)
  @client.set(data_format: :yang_json, values: [yang], mode: :merge_config)
end

#osString

Returns such as “Cisco Nexus Operating System (NX-OS) Software”.

Returns:

  • (String)

    such as “Cisco Nexus Operating System (NX-OS) Software”



320
321
322
323
324
# File 'lib/cisco_node_utils/node.rb', line 320

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

#os_versionString

Returns such as “6.0(2)U5(1) [build 6.0(2)U5(0.941)]”.

Returns:

  • (String)

    such as “6.0(2)U5(1) [build 6.0(2)U5(0.941)]”



327
328
329
# File 'lib/cisco_node_utils/node.rb', line 327

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

#os_version_get(feature, property) ⇒ Object



451
452
453
# File 'lib/cisco_node_utils/node.rb', line 451

def os_version_get(feature, property)
  @cmd_ref.lookup(feature, property).os_version
end

#prod_qualifier(prod, inventory) ⇒ Object



358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
# File 'lib/cisco_node_utils/node.rb', line 358

def prod_qualifier(prod, inventory)
  case prod
  when /N9K/
    # Two datapoints are used to determine if the current n9k
    # platform is a fretta based n9k or non-fretta.
    #
    # 1) Image Version == 7.0(3)F*
    # 2) Fabric Module == N9K-C9*-FM-R
    if @cmd_ref
      ver = os_version
    else
      ver = get(command:     'show version',
                data_format: :nxapi_structured)['kickstart_ver_str']
    end
    # Append -F for fretta platform.
    inventory.each do |row|
      if row['productid'][/N9K-C9...-FM-R/] && ver[/7.0\(3\)F/]
        return prod.concat('-F') unless prod[/-F/]
      end
    end
  when /N3K/
    if @cmd_ref
      ver = os_version
    else
      ver = get(command:     'show version',
                data_format: :nxapi_structured)['kickstart_ver_str']
    end
    # Append -F for fretta platform.
    return prod.concat('-F') if ver[/7.0\(3\)F/] && !prod[/-F/]
  end
  prod
end

#product_descriptionString

Returns such as “Nexus 3048 Chassis”.

Returns:

  • (String)

    such as “Nexus 3048 Chassis”



332
333
334
# File 'lib/cisco_node_utils/node.rb', line 332

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

#product_idString

Returns such as “N3K-C3048TP-1GE”.

Returns:

  • (String)

    such as “N3K-C3048TP-1GE”



337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
# File 'lib/cisco_node_utils/node.rb', line 337

def product_id
  if @cmd_ref
    prod = config_get('inventory', 'productid')
    all  = config_get('inventory', 'all')
    prod_qualifier(prod, all)
  else
    # We use this function to *find* the appropriate CommandReference
    if @client.platform == :nexus
      entries = get(command:     'show inventory',
                    data_format: :nxapi_structured)
      prod = entries['TABLE_inv']['ROW_inv'][0]['productid']
      prod_qualifier(prod, entries['TABLE_inv']['ROW_inv'])
    elsif @client.platform == :ios_xr
      # No support for structured output for this command yet
      output = get(command:     'show inventory',
                   data_format: :cli)
      return /NAME: .*\nPID: (\S+)/.match(output)[1]
    end
  end
end

#product_serial_numberString

Returns such as “FOC1722R0ET”.

Returns:

  • (String)

    such as “FOC1722R0ET”



397
398
399
# File 'lib/cisco_node_utils/node.rb', line 397

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

#product_version_idString

Returns such as “V01”.

Returns:

  • (String)

    such as “V01”



392
393
394
# File 'lib/cisco_node_utils/node.rb', line 392

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

#replace_yang(yang) ⇒ Object

Replace the running config on the device with the specified JSON YANG config.



299
300
301
302
# File 'lib/cisco_node_utils/node.rb', line 299

def replace_yang(yang)
  @client.set(data_format: :yang_json, values: [yang],
              mode: :replace_config)
end

#set(**kwargs) ⇒ Object

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

Raises:



278
279
280
# File 'lib/cisco_node_utils/node.rb', line 278

def set(**kwargs)
  @client.set(**kwargs)
end

#systemString

Returns such as “bootflash:///n3000-uk9.6.0.2.U5.0.941.bin”.

Returns:

  • (String)

    such as “bootflash:///n3000-uk9.6.0.2.U5.0.941.bin”



447
448
449
# File 'lib/cisco_node_utils/node.rb', line 447

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

#system_cpu_utilizationFloat

Returns combined user/kernel CPU utilization.

Returns:

  • (Float)

    combined user/kernel CPU utilization



433
434
435
436
437
# File 'lib/cisco_node_utils/node.rb', line 433

def system_cpu_utilization
  output = config_get('system', 'resources')
  return output if output.nil?
  output['cpu_state_user'].to_f + output['cpu_state_kernel'].to_f
end

#system_uptimeInteger

Returns System uptime, in seconds.

Returns:

  • (Integer)

    System uptime, in seconds



412
413
414
415
416
417
418
419
420
# File 'lib/cisco_node_utils/node.rb', line 412

def system_uptime
  cache_flush
  t = config_get('show_system', 'uptime')
  fail 'failed to retrieve system uptime' if t.nil?
  # 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



249
250
251
# File 'lib/cisco_node_utils/node.rb', line 249

def to_s
  client.to_s
end