Class: Cisco::CommandReference
- Inherits:
-
Object
- Object
- Cisco::CommandReference
- Defined in:
- lib/cisco_node_utils/command_reference.rb
Overview
Builds reference hash for the platform specified in the product id.
Constant Summary collapse
- KNOWN_PLATFORMS =
%w(C3064 C3132 C3172 N3k N5k N6k N7k N8k N9k XRv9k)
- KNOWN_FILTERS =
%w(nexus ios_xr cli nxapi_structured)
- @@debug =
rubocop:disable Style/ClassVars
false
Instance Attribute Summary collapse
-
#data_formats ⇒ Object
readonly
Returns the value of attribute data_formats.
-
#files ⇒ Object
readonly
Returns the value of attribute files.
-
#platform ⇒ Object
readonly
Returns the value of attribute platform.
-
#product_id ⇒ Object
readonly
Returns the value of attribute product_id.
Class Method Summary collapse
- .debug=(value) ⇒ Object
-
.filter_hash(hash, platform: nil, product_id: nil, data_formats: nil, allow_unknown_keys: true) ⇒ Object
Helper method Given a Hash of command reference data as read from YAML, does: - Delete any platform-specific data not applicable to this platform - Delete any product-specific data not applicable to this product_id - Delete any data-model-specific data not supported by this node Returns the filtered hash (possibly empty).
-
.hash_merge(input_hash, base_hash = nil) ⇒ Object
Helper method Given a suitably filtered Hash of command reference data, does: - Inherit data from the given base_hash (if any) and extend/override it with the given input data.
- .key_match(key, platform, product_id, data_formats) ⇒ Object
- .platform_to_filter(platform) ⇒ Object
-
.value_append(base_value, new_value) ⇒ Object
Helper method.
Instance Method Summary collapse
-
#build_cmd_ref ⇒ Object
Build complete reference hash.
-
#debug(text) ⇒ Object
Print debug statements.
- #empty? ⇒ Boolean
- #filter_hash(input_hash) ⇒ Object
-
#initialize(product: nil, platform: nil, data_formats: [], files: nil) ⇒ CommandReference
constructor
Constructor.
- #inspect ⇒ Object
-
#load_yaml(yaml_file) ⇒ Object
Read in yaml file.
-
#lookup(feature, name) ⇒ Object
Get the command reference.
- #supports?(feature, property = nil) ⇒ Boolean
- #to_s ⇒ Object
Constructor Details
#initialize(product: nil, platform: nil, data_formats: [], files: nil) ⇒ CommandReference
Constructor. Normal usage is to pass product, platform, data_formats, in which case usual YAML files will be located then the list will be filtered down to only those matching the given settings. For testing purposes (only!) you can pass an explicit list of files to load instead. This list will NOT be filtered further by product_id.
354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 |
# File 'lib/cisco_node_utils/command_reference.rb', line 354 def initialize(product: nil, platform: nil, data_formats: [], files: nil) @product_id = product @platform = platform @data_formats = data_formats @hash = {} if files @files = files else @files = Dir.glob(__dir__ + '/cmd_ref/*.yaml') end build_cmd_ref end |
Instance Attribute Details
#data_formats ⇒ Object (readonly)
Returns the value of attribute data_formats.
346 347 348 |
# File 'lib/cisco_node_utils/command_reference.rb', line 346 def data_formats @data_formats end |
#files ⇒ Object (readonly)
Returns the value of attribute files.
346 347 348 |
# File 'lib/cisco_node_utils/command_reference.rb', line 346 def files @files end |
#platform ⇒ Object (readonly)
Returns the value of attribute platform.
346 347 348 |
# File 'lib/cisco_node_utils/command_reference.rb', line 346 def platform @platform end |
#product_id ⇒ Object (readonly)
Returns the value of attribute product_id.
346 347 348 |
# File 'lib/cisco_node_utils/command_reference.rb', line 346 def product_id @product_id end |
Class Method Details
.debug=(value) ⇒ Object
340 341 342 343 344 |
# File 'lib/cisco_node_utils/command_reference.rb', line 340 def self.debug=(value) fail ArgumentError, 'Debug must be boolean' unless value == true || value == false @@debug = value # rubocop:disable Style/ClassVars end |
.filter_hash(hash, platform: nil, product_id: nil, data_formats: nil, allow_unknown_keys: true) ⇒ Object
Helper method Given a Hash of command reference data as read from YAML, does:
-
Delete any platform-specific data not applicable to this platform
-
Delete any product-specific data not applicable to this product_id
-
Delete any data-model-specific data not supported by this node
Returns the filtered hash (possibly empty)
472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 |
# File 'lib/cisco_node_utils/command_reference.rb', line 472 def self.filter_hash(hash, platform: nil, product_id: nil, data_formats: nil, allow_unknown_keys: true) result = {} exclude = hash['_exclude'] || [] exclude.each do |value| # We don't allow exclusion by data_format - just platform/product if key_match(value, platform, product_id, nil) == true debug "Exclude this product (#{product_id}, #{value})" return result end end # to_inspect: sub-keys we want to recurse into to_inspect = [] # regexp_match: did we find a product_id regexp that matches? regexp_match = false hash.each do |key, value| next if key == '_exclude' if CmdRef.keys.include?(key) result[key] = value elsif key != 'else' match = key_match(key, platform, product_id, data_formats) next if match == false if match == :unknown fail "Unrecognized key '#{key}'" unless allow_unknown_keys end regexp_match = true if match == true to_inspect << key end end # If we didn't find any platform regexp match, # and an 'else' sub-hash is provided, descend into 'else' to_inspect << 'else' if hash.key?('else') && !regexp_match # Recurse! Sub-hashes can override the base hash to_inspect.each do |key| unless hash[key].is_a?(Hash) result[key] = hash[key] next end begin result[key] = filter_hash(hash[key], platform: platform, product_id: product_id, data_formats: data_formats, allow_unknown_keys: false) rescue RuntimeError => e # Recursively wrap the error as needed to provide context raise "[#{key}]: #{e}" end end result end |
.hash_merge(input_hash, base_hash = nil) ⇒ Object
Helper method Given a suitably filtered Hash of command reference data, does:
-
Inherit data from the given base_hash (if any) and extend/override it with the given input data.
541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 |
# File 'lib/cisco_node_utils/command_reference.rb', line 541 def self.hash_merge(input_hash, base_hash=nil) return base_hash if input_hash.nil? result = base_hash result ||= {} # to_inspect: sub-hashes we want to recurse into to_inspect = [] input_hash.each do |key, value| if CmdRef.keys.include?(key) result[key] = value elsif value.is_a?(Hash) to_inspect << value elsif value.nil? next else fail "Unexpected non-hash data: #{value}" end end # Recurse! Sub-hashes can override the base hash to_inspect.each do |hash| result = hash_merge(hash, result) end result end |
.key_match(key, platform, product_id, data_formats) ⇒ Object
454 455 456 457 458 459 460 461 462 463 464 |
# File 'lib/cisco_node_utils/command_reference.rb', line 454 def self.key_match(key, platform, product_id, data_formats) if KNOWN_PLATFORMS.include?(key) return platform_to_filter(key) =~ product_id ? true : false elsif KNOWN_FILTERS.include?(key) return true if data_formats && data_formats.include?(key.to_sym) return true if key == platform.to_s return false else return :unknown end end |
.platform_to_filter(platform) ⇒ Object
439 440 441 442 443 444 445 446 447 448 449 450 |
# File 'lib/cisco_node_utils/command_reference.rb', line 439 def self.platform_to_filter(platform) if KNOWN_PLATFORMS.include?(platform) case platform when 'XRv9k' /XRV9/ else Regexp.new platform.tr('k', '') end else fail IndexError, "Unknown platform key '#{platform}'" end end |
.value_append(base_value, new_value) ⇒ Object
Helper method. Combines the two given values (either or both of which may be arrays) into a single combined array value_append(‘foo’, ‘bar’) ==> [‘foo’, ‘bar’] value_append(‘foo’, [‘bar’, ‘baz’]) ==> [‘foo’, ‘bar’, ‘baz’]
571 572 573 574 575 |
# File 'lib/cisco_node_utils/command_reference.rb', line 571 def self.value_append(base_value, new_value) base_value = [base_value] unless base_value.is_a?(Array) new_value = [new_value] unless new_value.is_a?(Array) base_value + new_value end |
Instance Method Details
#build_cmd_ref ⇒ Object
Build complete reference hash.
372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 |
# File 'lib/cisco_node_utils/command_reference.rb', line 372 def build_cmd_ref # Example id's: N3K-C3048TP-1GE, N3K-C3064PQ-10GE, N7K-C7009, N7K-C7009 debug "Product: #{@product_id}" debug "Files being used: #{@files.join(', ')}" @files.each do |file| feature = File.basename(file).split('.')[0] debug "Processing file '#{file}' as feature '#{feature}'" feature_hash = load_yaml(file) if feature_hash.empty? debug "Feature #{feature} is empty" next end begin feature_hash = filter_hash(feature_hash) rescue RuntimeError => e raise "#{file}: #{e}" end if feature_hash.empty? debug "Feature #{feature} is excluded" @hash[feature] = UnsupportedCmdRef.new(feature, nil, file) next end base_hash = {} if feature_hash.key?('_template') base_hash = CommandReference.hash_merge(feature_hash['_template']) end feature_hash.each do |name, value| fail "No entries under '#{name}' in '#{file}'" if value.nil? @hash[feature] ||= {} if value.empty? @hash[feature][name] = UnsupportedCmdRef.new(feature, name, file) else values = CommandReference.hash_merge(value, base_hash.clone) @hash[feature][name] = CmdRef.new(feature, name, values, file) end end end end |
#debug(text) ⇒ Object
Print debug statements
433 434 435 |
# File 'lib/cisco_node_utils/command_reference.rb', line 433 def debug(text) puts "DEBUG: #{text}" if @@debug end |
#empty? ⇒ Boolean
428 429 430 |
# File 'lib/cisco_node_utils/command_reference.rb', line 428 def empty? @hash.empty? end |
#filter_hash(input_hash) ⇒ Object
530 531 532 533 534 535 |
# File 'lib/cisco_node_utils/command_reference.rb', line 530 def filter_hash(input_hash) CommandReference.filter_hash(input_hash, platform: platform, product_id: product_id, data_formats: data_formats) end |
#inspect ⇒ Object
698 699 700 701 702 |
# File 'lib/cisco_node_utils/command_reference.rb', line 698 def inspect "CommandReference for '#{product_id}' " \ "(platform:'#{platform}', data formats:#{data_formats}) " \ "based on #{files.length} files" end |
#load_yaml(yaml_file) ⇒ Object
Read in yaml file. The expectation is that a file corresponds to a feature
669 670 671 672 673 674 675 676 677 678 679 680 681 682 683 684 685 686 687 |
# File 'lib/cisco_node_utils/command_reference.rb', line 669 def load_yaml(yaml_file) fail "File #{yaml_file} doesn't exist." unless File.exist?(yaml_file) # Parse YAML file into a tree of nodes # Psych::SyntaxError doesn't inherit from StandardError in some versions, # so we want to explicitly catch it if using Psych. rescue_errors = [::StandardError, ::Psych::SyntaxError] yaml_parsed = File.open(yaml_file, 'r') do |f| begin YAML.parse(f) rescue *rescue_errors => e raise "unable to parse #{yaml_file}: #{e}" end end return {} unless yaml_parsed # Validate the node tree validate_yaml(yaml_parsed, yaml_file) # If validation passed, convert the node tree to a Ruby Hash. yaml_parsed.transform end |
#lookup(feature, name) ⇒ Object
Get the command reference
421 422 423 424 425 426 |
# File 'lib/cisco_node_utils/command_reference.rb', line 421 def lookup(feature, name) value = @hash[feature] value = value[name] if value.is_a? Hash fail IndexError, "No CmdRef defined for #{feature}, #{name}" if value.nil? value end |
#supports?(feature, property = nil) ⇒ Boolean
414 415 416 417 418 |
# File 'lib/cisco_node_utils/command_reference.rb', line 414 def supports?(feature, property=nil) value = @hash[feature] value = value[property] if value.is_a?(Hash) && property !(value.is_a?(UnsupportedCmdRef) || value.nil?) end |
#to_s ⇒ Object
689 690 691 692 693 694 695 696 |
# File 'lib/cisco_node_utils/command_reference.rb', line 689 def to_s @num_features ||= @hash.values.length @num_attributes ||= @hash.values.inject(0) do |sum, n| sum + (n.is_a?(Hash) ? n.values.length : 1) end "CommandReference describing #{@num_features} features " \ "with #{@num_attributes} attributes in total" end |