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.



244
245
246
247
248
249
250
251
# File 'lib/cisco_node_utils/node.rb', line 244

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.



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

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.



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

def cmd_ref
  @cmd_ref
end

Class Method Details

.instanceObject



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

def self.instance
  @instance ||= new
end

.reset_instanceObject

Allow instance cache to be reset



240
241
242
# File 'lib/cisco_node_utils/node.rb', line 240

def self.reset_instance
  @instance = nil
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”



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

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

#cache_auto=(enable) ⇒ Object



273
274
275
# File 'lib/cisco_node_utils/node.rb', line 273

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

#cache_auto?Boolean

Returns:

  • (Boolean)


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

def cache_auto?
  @client.cache_auto?
end

#cache_enable=(enable) ⇒ Object



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

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

#cache_enable?Boolean

Returns:

  • (Boolean)


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

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.



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

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.



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

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.



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

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.



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

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”



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

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$/']


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

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:



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

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

#get_yang(yang_path) ⇒ Object

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



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

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.



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

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”



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

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

#inspectObject



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

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”



416
417
418
# File 'lib/cisco_node_utils/node.rb', line 416

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



411
412
413
# File 'lib/cisco_node_utils/node.rb', line 411

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.



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

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



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

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
# 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
  end
  [get_args, ref]
end

#merge_yang(yang) ⇒ Object

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



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

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”



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

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



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

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

#os_version_get(feature, property) ⇒ Object



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

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

#prod_qualifier(prod, inventory) ⇒ Object



362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
# File 'lib/cisco_node_utils/node.rb', line 362

def prod_qualifier(prod, inventory)
  case prod
  when /N(3|9)K/
    # one datapoint is used to determine if the current n9k/n3k
    # platform is a fretta based or non-fretta.
    #
    # Module == *-R
    inventory.each do |row|
      if row['productid'][/-R/]
        # Append -F for fretta platform.
        return prod.concat('-F') unless prod[/-F/]
      end
    end
  end
  prod
end

#product_descriptionString

Returns such as “Nexus 3048 Chassis”.

Returns:

  • (String)

    such as “Nexus 3048 Chassis”



336
337
338
# File 'lib/cisco_node_utils/node.rb', line 336

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

#product_idString

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

Returns:

  • (String)

    such as “N3K-C3048TP-1GE”



341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
# File 'lib/cisco_node_utils/node.rb', line 341

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”



385
386
387
# File 'lib/cisco_node_utils/node.rb', line 385

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

#product_version_idString

Returns such as “V01”.

Returns:

  • (String)

    such as “V01”



380
381
382
# File 'lib/cisco_node_utils/node.rb', line 380

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.



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

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:



282
283
284
# File 'lib/cisco_node_utils/node.rb', line 282

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”



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

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



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

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



400
401
402
403
404
405
406
407
408
# File 'lib/cisco_node_utils/node.rb', line 400

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



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

def to_s
  client.to_s
end