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(C3048 C3064 C3132 C3172 N35 N3k N3k-F N5k N6k N7k N9k N9k-F 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.
357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 |
# File 'lib/cisco_node_utils/command_reference.rb', line 357 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.
349 350 351 |
# File 'lib/cisco_node_utils/command_reference.rb', line 349 def data_formats @data_formats end |
#files ⇒ Object (readonly)
Returns the value of attribute files.
349 350 351 |
# File 'lib/cisco_node_utils/command_reference.rb', line 349 def files @files end |
#platform ⇒ Object (readonly)
Returns the value of attribute platform.
349 350 351 |
# File 'lib/cisco_node_utils/command_reference.rb', line 349 def platform @platform end |
#product_id ⇒ Object (readonly)
Returns the value of attribute product_id.
349 350 351 |
# File 'lib/cisco_node_utils/command_reference.rb', line 349 def product_id @product_id end |
Class Method Details
.debug=(value) ⇒ Object
343 344 345 346 347 |
# File 'lib/cisco_node_utils/command_reference.rb', line 343 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)
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 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 |
# File 'lib/cisco_node_utils/command_reference.rb', line 492 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.
561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 583 584 |
# File 'lib/cisco_node_utils/command_reference.rb', line 561 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
474 475 476 477 478 479 480 481 482 483 484 |
# File 'lib/cisco_node_utils/command_reference.rb', line 474 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
443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 |
# File 'lib/cisco_node_utils/command_reference.rb', line 443 def self.platform_to_filter(platform) if KNOWN_PLATFORMS.include?(platform) case platform when 'XRv9k' /XRV9/ when 'N35' /N3K-C35/ when 'N9k' # For non-fretta n9k platforms we need to # match everything except the trailing -F /^N9...(?!.*-F)/ when 'N9k-F' # For fretta n9k we need to include the trailing -F /^N9.*-F$/ when 'N3k' # For non-fretta n3k platforms we need to # match everything except the trailing -F /^N3...(?!.*-F)/ when 'N3k-F' # For fretta n3k we need to include the trailing -F /^N3.*-F$/ 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’]
591 592 593 594 595 |
# File 'lib/cisco_node_utils/command_reference.rb', line 591 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.
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 413 414 415 |
# File 'lib/cisco_node_utils/command_reference.rb', line 375 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
436 437 438 |
# File 'lib/cisco_node_utils/command_reference.rb', line 436 def debug(text) puts "DEBUG: #{text}" if @@debug end |
#empty? ⇒ Boolean
431 432 433 |
# File 'lib/cisco_node_utils/command_reference.rb', line 431 def empty? @hash.empty? end |
#filter_hash(input_hash) ⇒ Object
550 551 552 553 554 555 |
# File 'lib/cisco_node_utils/command_reference.rb', line 550 def filter_hash(input_hash) CommandReference.filter_hash(input_hash, platform: platform, product_id: product_id, data_formats: data_formats) end |
#inspect ⇒ Object
718 719 720 721 722 |
# File 'lib/cisco_node_utils/command_reference.rb', line 718 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
689 690 691 692 693 694 695 696 697 698 699 700 701 702 703 704 705 706 707 |
# File 'lib/cisco_node_utils/command_reference.rb', line 689 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
424 425 426 427 428 429 |
# File 'lib/cisco_node_utils/command_reference.rb', line 424 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
417 418 419 420 421 |
# File 'lib/cisco_node_utils/command_reference.rb', line 417 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
709 710 711 712 713 714 715 716 |
# File 'lib/cisco_node_utils/command_reference.rb', line 709 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 |