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.



237
238
239
240
241
242
243
244
# File 'lib/cisco_node_utils/node.rb', line 237

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.



231
232
233
# File 'lib/cisco_node_utils/node.rb', line 231

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.



231
232
233
# File 'lib/cisco_node_utils/node.rb', line 231

def cmd_ref
  @cmd_ref
end

Class Method Details

.instanceObject



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

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”



438
439
440
# File 'lib/cisco_node_utils/node.rb', line 438

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

#cache_auto=(enable) ⇒ Object



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

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

#cache_auto?Boolean

Returns:

  • (Boolean)


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

def cache_auto?
  @client.cache_auto?
end

#cache_enable=(enable) ⇒ Object



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

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

#cache_enable?Boolean

Returns:

  • (Boolean)


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

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.



224
225
226
# File 'lib/cisco_node_utils/node.rb', line 224

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.



193
194
195
196
# File 'lib/cisco_node_utils/node.rb', line 193

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.



213
214
215
216
217
# File 'lib/cisco_node_utils/node.rb', line 213

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.



302
303
304
# File 'lib/cisco_node_utils/node.rb', line 302

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”



404
405
406
# File 'lib/cisco_node_utils/node.rb', line 404

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
# 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|
    data = row[data_key] if row[row_key].to_s[/#{row_index}/]
  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:



284
285
286
# File 'lib/cisco_node_utils/node.rb', line 284

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

#get_yang(yang_path) ⇒ Object

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



307
308
309
# File 'lib/cisco_node_utils/node.rb', line 307

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.



312
313
314
# File 'lib/cisco_node_utils/node.rb', line 312

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”



399
400
401
# File 'lib/cisco_node_utils/node.rb', line 399

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

#inspectObject



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

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”



425
426
427
# File 'lib/cisco_node_utils/node.rb', line 425

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



420
421
422
# File 'lib/cisco_node_utils/node.rb', line 420

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.



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

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



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

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.



290
291
292
# File 'lib/cisco_node_utils/node.rb', line 290

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”



317
318
319
320
321
# File 'lib/cisco_node_utils/node.rb', line 317

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)]”



324
325
326
# File 'lib/cisco_node_utils/node.rb', line 324

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

#os_version_get(feature, property) ⇒ Object



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

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

#prod_qualifier(prod, inventory) ⇒ Object



355
356
357
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
# File 'lib/cisco_node_utils/node.rb', line 355

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”



329
330
331
# File 'lib/cisco_node_utils/node.rb', line 329

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

#product_idString

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

Returns:

  • (String)

    such as “N3K-C3048TP-1GE”



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

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”



394
395
396
# File 'lib/cisco_node_utils/node.rb', line 394

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

#product_version_idString

Returns such as “V01”.

Returns:

  • (String)

    such as “V01”



389
390
391
# File 'lib/cisco_node_utils/node.rb', line 389

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.



296
297
298
299
# File 'lib/cisco_node_utils/node.rb', line 296

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:



275
276
277
# File 'lib/cisco_node_utils/node.rb', line 275

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”



444
445
446
# File 'lib/cisco_node_utils/node.rb', line 444

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



430
431
432
433
434
# File 'lib/cisco_node_utils/node.rb', line 430

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



409
410
411
412
413
414
415
416
417
# File 'lib/cisco_node_utils/node.rb', line 409

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



246
247
248
# File 'lib/cisco_node_utils/node.rb', line 246

def to_s
  client.to_s
end