cisco_node_utils 1.0.1 → 1.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (114) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +4 -0
  3. data/.rubocop.yml +81 -1
  4. data/.travis.yml +9 -0
  5. data/CHANGELOG.md +72 -6
  6. data/CONTRIBUTING.md +32 -7
  7. data/README.md +70 -7
  8. data/Rakefile +17 -0
  9. data/bin/check_metric_limits.rb +109 -0
  10. data/bin/git/hooks/commit-msg/enforce_style +81 -0
  11. data/bin/git/hooks/hook_lib +108 -0
  12. data/bin/git/hooks/hooks-wrapper +38 -0
  13. data/bin/git/hooks/post-flow-hotfix-start/update-version +24 -0
  14. data/bin/git/hooks/post-flow-release-finish/update-version +29 -0
  15. data/bin/git/hooks/post-flow-release-start/update-version +19 -0
  16. data/bin/git/hooks/post-merge/update-hooks +6 -0
  17. data/bin/git/hooks/post-rewrite/update-hooks +6 -0
  18. data/bin/git/hooks/pre-commit/rubocop +20 -0
  19. data/bin/git/hooks/pre-commit/validate-diffs +31 -0
  20. data/bin/git/hooks/pre-push/check-changelog +24 -0
  21. data/bin/git/hooks/pre-push/rubocop +7 -0
  22. data/bin/git/update-hooks +65 -0
  23. data/cisco_node_utils.gemspec +9 -3
  24. data/docs/README-develop-best-practices.md +404 -0
  25. data/docs/README-develop-node-utils-APIs.md +215 -365
  26. data/docs/README-maintainers.md +33 -3
  27. data/docs/template-router.rb +89 -91
  28. data/docs/template-test_router.rb +52 -55
  29. data/lib/.rubocop.yml +18 -0
  30. data/lib/cisco_node_utils.rb +2 -19
  31. data/lib/cisco_node_utils/README_YAML.md +1 -9
  32. data/lib/cisco_node_utils/bgp.rb +664 -0
  33. data/lib/cisco_node_utils/bgp_af.rb +530 -0
  34. data/lib/cisco_node_utils/bgp_neighbor.rb +425 -0
  35. data/lib/cisco_node_utils/bgp_neighbor_af.rb +709 -0
  36. data/lib/cisco_node_utils/cisco_cmn_utils.rb +59 -25
  37. data/lib/cisco_node_utils/command_reference.rb +72 -74
  38. data/lib/cisco_node_utils/command_reference_common.yaml +174 -9
  39. data/lib/cisco_node_utils/command_reference_common_bgp.yaml +535 -0
  40. data/lib/cisco_node_utils/command_reference_n7k.yaml +4 -0
  41. data/lib/cisco_node_utils/command_reference_n9k.yaml +0 -9
  42. data/lib/cisco_node_utils/configparser_lib.rb +152 -147
  43. data/lib/cisco_node_utils/dns_domain.rb +79 -0
  44. data/lib/cisco_node_utils/domain_name.rb +71 -0
  45. data/lib/cisco_node_utils/interface.rb +167 -161
  46. data/lib/cisco_node_utils/interface_ospf.rb +78 -81
  47. data/lib/cisco_node_utils/name_server.rb +64 -0
  48. data/lib/cisco_node_utils/node.rb +154 -198
  49. data/lib/cisco_node_utils/node_util.rb +61 -0
  50. data/lib/cisco_node_utils/ntp_config.rb +65 -0
  51. data/lib/cisco_node_utils/ntp_server.rb +76 -0
  52. data/lib/cisco_node_utils/platform.rb +174 -165
  53. data/lib/cisco_node_utils/radius_global.rb +146 -0
  54. data/lib/cisco_node_utils/radius_server.rb +295 -0
  55. data/lib/cisco_node_utils/router_ospf.rb +59 -63
  56. data/lib/cisco_node_utils/router_ospf_vrf.rb +226 -210
  57. data/lib/cisco_node_utils/snmpcommunity.rb +52 -58
  58. data/lib/cisco_node_utils/snmpgroup.rb +22 -23
  59. data/lib/cisco_node_utils/snmpserver.rb +99 -103
  60. data/lib/cisco_node_utils/snmpuser.rb +294 -274
  61. data/lib/cisco_node_utils/syslog_server.rb +92 -0
  62. data/lib/cisco_node_utils/syslog_settings.rb +69 -0
  63. data/lib/cisco_node_utils/tacacs_server.rb +137 -133
  64. data/lib/cisco_node_utils/tacacs_server_host.rb +84 -87
  65. data/lib/cisco_node_utils/version.rb +2 -1
  66. data/lib/cisco_node_utils/vlan.rb +28 -31
  67. data/lib/cisco_node_utils/vrf.rb +80 -0
  68. data/lib/cisco_node_utils/vtp.rb +100 -97
  69. data/lib/cisco_node_utils/yum.rb +15 -17
  70. data/tests/.rubocop.yml +15 -0
  71. data/tests/basetest.rb +81 -36
  72. data/tests/ciscotest.rb +38 -78
  73. data/{lib/cisco_node_utils → tests}/platform_info.rb +12 -8
  74. data/{lib/cisco_node_utils → tests}/platform_info.yaml +1 -1
  75. data/tests/test_bgp_af.rb +920 -0
  76. data/tests/test_bgp_neighbor.rb +403 -0
  77. data/tests/test_bgp_neighbor_af.rb +589 -0
  78. data/tests/test_command_config.rb +65 -62
  79. data/tests/test_command_reference.rb +31 -45
  80. data/tests/test_dns_domain.rb +113 -0
  81. data/tests/test_domain_name.rb +86 -0
  82. data/tests/test_interface.rb +424 -548
  83. data/tests/test_interface_ospf.rb +248 -432
  84. data/tests/test_interface_svi.rb +56 -79
  85. data/tests/test_interface_switchport.rb +196 -272
  86. data/tests/test_name_server.rb +85 -0
  87. data/tests/test_node.rb +7 -6
  88. data/tests/test_node_ext.rb +133 -186
  89. data/tests/test_ntp_config.rb +49 -0
  90. data/tests/test_ntp_server.rb +74 -0
  91. data/tests/test_platform.rb +58 -37
  92. data/tests/test_radius_global.rb +78 -0
  93. data/tests/test_radius_server.rb +185 -0
  94. data/tests/test_router_bgp.rb +838 -0
  95. data/tests/test_router_ospf.rb +49 -80
  96. data/tests/test_router_ospf_vrf.rb +274 -392
  97. data/tests/test_snmpcommunity.rb +128 -172
  98. data/tests/test_snmpgroup.rb +12 -14
  99. data/tests/test_snmpserver.rb +160 -189
  100. data/tests/test_snmpuser.rb +568 -717
  101. data/tests/test_syslog_server.rb +88 -0
  102. data/tests/test_syslog_settings.rb +54 -0
  103. data/tests/test_tacacs_server.rb +113 -148
  104. data/tests/test_tacacs_server_host.rb +108 -161
  105. data/tests/test_vlan.rb +63 -79
  106. data/tests/test_vrf.rb +92 -0
  107. data/tests/test_vtp.rb +108 -126
  108. data/tests/test_yum.rb +47 -41
  109. metadata +92 -56
  110. data/.rubocop_todo.yml +0 -293
  111. data/docs/.rubocop.yml +0 -13
  112. data/docs/template-feature.rb +0 -45
  113. data/docs/template-test_feature.rb +0 -51
  114. data/tests/test_all_cisco.rb +0 -46
@@ -14,52 +14,55 @@
14
14
  # See the License for the specific language governing permissions and
15
15
  # limitations under the License.
16
16
 
17
+ # Add some general-purpose constants and APIs to the Cisco namespace
17
18
  module Cisco
18
19
  # global constants
19
20
  DEFAULT_INSTANCE_NAME = 'default'
20
21
 
22
+ # Encryption - helper class for translating encryption type CLI
21
23
  class Encryption
22
24
  # password encryption types
23
- def Encryption.cli_to_symbol(cli)
25
+ def self.cli_to_symbol(cli)
24
26
  case cli
25
- when "0", 0
27
+ when '0', 0
26
28
  :cleartext
27
- when "3", 3
28
- :"3des" # yuck :-(
29
- when "5", 5
29
+ when '3', 3
30
+ :"3des" # yuck :-(
31
+ when '5', 5
30
32
  :md5
31
- when "6", 6
33
+ when '6', 6
32
34
  :aes
33
- when "7", 7
35
+ when '7', 7
34
36
  :cisco_type_7
35
37
  else
36
- raise KeyError
38
+ fail KeyError
37
39
  end
38
40
  end
39
41
 
40
- def Encryption.symbol_to_cli(symbol)
42
+ def self.symbol_to_cli(symbol)
41
43
  symbol = symbol.downcase if symbol.is_a? String
42
44
  case symbol
43
- when :cleartext, :none, "cleartext", "none", "0", 0
44
- "0"
45
- when :"3des", "3des", "3", 3
46
- "3"
47
- when :md5, "md5", "5", 5
48
- "5"
49
- when :aes, "aes", "6", 6
50
- "6"
51
- when :cisco_type_7, :type_7, "cisco_type_7", "type_7", "7", 7
52
- "7"
45
+ when :cleartext, :none, 'cleartext', 'none', '0', 0
46
+ '0'
47
+ when :"3des", '3des', '3', 3
48
+ '3'
49
+ when :md5, 'md5', '5', 5
50
+ '5'
51
+ when :aes, 'aes', '6', 6
52
+ '6'
53
+ when :cisco_type_7, :type_7, 'cisco_type_7', 'type_7', '7', 7
54
+ '7'
53
55
  else
54
- raise KeyError
56
+ fail KeyError
55
57
  end
56
58
  end
57
59
  end
58
60
 
61
+ # ChefUtils - helper class for Chef code generation
59
62
  class ChefUtils
60
- def ChefUtils.generic_prop_set(klass, rlbname, props)
63
+ def self.generic_prop_set(klass, rlbname, props)
61
64
  props.each do |prop|
62
- klass.instance_eval {
65
+ klass.instance_eval do
63
66
  # Helper Chef setter method, e.g.:
64
67
  # if @new_resource.foo.nil?
65
68
  # def_prop = @rlb.default_foo
@@ -79,14 +82,45 @@ module Cisco
79
82
  end
80
83
  current = instance_variable_get(rlbname).send(prop)
81
84
  if current != @new_resource.send(prop)
82
- converge_by("update #{prop} '#{current}' => " +
85
+ converge_by("update #{prop} '#{current}' => " \
83
86
  "'#{@new_resource.send(prop)}'") do
84
87
  instance_variable_get(rlbname).send("#{prop}=",
85
- @new_resource.send(prop))
88
+ @new_resource.send(prop))
86
89
  end
87
90
  end
88
- }
91
+ end
89
92
  end
90
93
  end
91
94
  end # class ChefUtils
95
+
96
+ # General utility class
97
+ class Utils
98
+ require 'ipaddr'
99
+ # Helper utility method for ip/prefix format networks.
100
+ # For ip/prefix format '1.1.1.1/24' or '2000:123:38::34/64',
101
+ # we need to mask the address using the prefix length so that they
102
+ # are converted to '1.1.1.0/24' or '2000:123:38::/64'
103
+ def self.process_network_mask(network)
104
+ mask = network.split('/')[1]
105
+ address = IPAddr.new(network).to_s
106
+ network = address + '/' + mask unless mask.nil?
107
+ network
108
+ end
109
+
110
+ # Helper to build a hash of add/remove commands for a nested array.
111
+ # Useful for network, redistribute, etc.
112
+ # should: an array of expected cmds (manifest/recipe)
113
+ # current: an array of existing cmds on the device
114
+ def self.delta_add_remove(should, current=[])
115
+ # Remove nil entries from array
116
+ should.each(&:compact!) unless should.empty?
117
+ delta = { add: should - current, remove: current - should }
118
+
119
+ # Delete entries from :remove if f1 is an update to an existing command
120
+ delta[:add].each do |id, _|
121
+ delta[:remove].delete_if { |f1, f2| [f1, f2] if f1.to_s == id.to_s }
122
+ end
123
+ delta
124
+ end # delta_add_remove
125
+ end # class Utils
92
126
  end # module Cisco
@@ -30,7 +30,7 @@ module CommandReference
30
30
  if expression.is_a? Regexp
31
31
  @regex = expression
32
32
  else
33
- raise ArgumentError
33
+ fail ArgumentError
34
34
  end
35
35
  end
36
36
 
@@ -38,7 +38,7 @@ module CommandReference
38
38
  if file.is_a? String
39
39
  @file = file
40
40
  else
41
- raise ArgumentError
41
+ fail ArgumentError
42
42
  end
43
43
  end
44
44
 
@@ -54,13 +54,15 @@ module CommandReference
54
54
  attr_reader :sources
55
55
  attr_reader :hash
56
56
 
57
- @@KEYS = %w(default_value
57
+ # rubocop:disable Style/ClassVars
58
+ @@keys = %w(default_value
58
59
  config_set config_set_append
59
60
  config_get config_get_token config_get_token_append
60
61
  test_config_get test_config_get_regex test_config_result)
62
+ # rubocop:enable Style/ClassVars
61
63
 
62
64
  def initialize(feature, name, ref, source)
63
- raise ArgumentError, "'#{ref}' is not a hash." unless ref.is_a? Hash
65
+ fail ArgumentError, "'#{ref}' is not a hash." unless ref.is_a? Hash
64
66
 
65
67
  @feature = feature
66
68
  @name = name
@@ -72,14 +74,14 @@ module CommandReference
72
74
 
73
75
  # Overwrite values from more specific references.
74
76
  def merge(values, file)
75
- values.each { |key, value|
76
- unless @@KEYS.include?(key)
77
- raise "Unrecognized key #{key} for #{feature}, #{name} in #{file}"
77
+ values.each do |key, value|
78
+ unless @@keys.include?(key)
79
+ fail "Unrecognized key #{key} for #{feature}, #{name} in #{file}"
78
80
  end
79
81
  if value.nil?
80
82
  # Some attributes can store an explicit nil.
81
83
  # Others treat this as unset (allowing a platform to override common).
82
- if key == "default_value"
84
+ if key == 'default_value'
83
85
  @hash[key] = value
84
86
  else
85
87
  @hash.delete(key)
@@ -87,7 +89,7 @@ module CommandReference
87
89
  else
88
90
  @hash[key] = value
89
91
  end
90
- }
92
+ end
91
93
  @sources << file
92
94
  end
93
95
 
@@ -98,29 +100,26 @@ module CommandReference
98
100
  # If value is a string and it is empty OR the first letter is lower case
99
101
  # then leave value untouched.
100
102
  # If value is a string and the first letter is uppercase this indicates
101
- # that it could be a constant in Ruby so attempt to convert it to a Constant.
102
- if value.is_a?(String) and not value.empty?
103
+ # that it could be a constant in Ruby, so attempt to convert it
104
+ # to a Constant.
105
+ if value.is_a?(String) && !value.empty?
103
106
  if value[0].chr == value[0].chr.upcase
104
- begin
105
- value = Object.const_get(value) if Object.const_defined?(value)
106
- rescue NameError
107
- # debug("#{value} looks like a constant but is not")
108
- end
107
+ value = Object.const_get(value) if Object.const_defined?(value)
109
108
  end
110
109
  end
111
110
  value
112
111
  end
113
112
 
114
113
  def test_config_result(value)
115
- result = @hash["test_config_result"][value]
114
+ result = @hash['test_config_result'][value]
116
115
  convert_to_constant(result)
117
116
  end
118
117
 
119
118
  def method_missing(method_name, *args, &block)
120
- super(method_name, *args, &block) unless @@KEYS.include?(method_name.to_s)
119
+ super(method_name, *args, &block) unless @@keys.include?(method_name.to_s)
121
120
  method_name = method_name.to_s
122
121
  unless @hash.include?(method_name)
123
- raise IndexError, "No #{method_name} defined for #{@feature}, #{@name}"
122
+ fail IndexError, "No #{method_name} defined for #{@feature}, #{@name}"
124
123
  end
125
124
  # puts("get #{method_name}: '#{@hash[method_name]}'")
126
125
  @hash[method_name]
@@ -128,32 +127,27 @@ module CommandReference
128
127
 
129
128
  # Print useful debugging information about the object.
130
129
  def to_s
131
- str = ""
130
+ str = ''
132
131
  str << "Command: #{@feature} #{@name}\n"
133
- @hash.each { |key, value|
134
- str << " #{key}: #{value}\n"
135
- }
132
+ @hash.each { |key, value| str << " #{key}: #{value}\n" }
136
133
  str
137
134
  end
138
135
 
139
136
  # Check that all necessary values have been populated.
140
137
  def valid?
141
- return false unless @feature and @name
138
+ return false unless @feature && @name
142
139
  true
143
140
  end
144
141
  end
145
142
 
146
143
  # Builds reference hash for the platform specified in the product id.
147
144
  class CommandReference
148
- @@debug = false
149
-
150
- def self.debug
151
- @@debug
152
- end
145
+ @@debug = false # rubocop:disable Style/ClassVars
153
146
 
154
147
  def self.debug=(value)
155
- raise ArgumentError, "Debug must be boolean" unless value == true or value == false
156
- @@debug = value
148
+ fail ArgumentError, 'Debug must be boolean' unless value == true ||
149
+ value == false
150
+ @@debug = value # rubocop:disable Style/ClassVars
157
151
  end
158
152
 
159
153
  attr_reader :files, :product_id
@@ -171,25 +165,31 @@ module CommandReference
171
165
  @files = files
172
166
  else
173
167
  @files = []
174
- # Hashes are unordered in Ruby 1.8.7. Instead, we use an array of objects.
168
+ # Hashes are unordered in Ruby 1.8.7, so instead, we use an array
169
+ # of objects.
170
+ # rubocop:disable Metrics/LineLength
175
171
  platforms = [
176
172
  CommandPlatformFile.new(//,
177
173
  File.join(File.dirname(__FILE__),
178
- "command_reference_common.yaml")),
174
+ 'command_reference_common.yaml')),
175
+ CommandPlatformFile.new(//,
176
+ File.join(File.dirname(__FILE__),
177
+ 'command_reference_common_bgp.yaml')),
179
178
  CommandPlatformFile.new(/N9K/,
180
179
  File.join(File.dirname(__FILE__),
181
- "command_reference_n9k.yaml")),
180
+ 'command_reference_n9k.yaml')),
182
181
  CommandPlatformFile.new(/N7K/,
183
182
  File.join(File.dirname(__FILE__),
184
- "command_reference_n7k.yaml")),
183
+ 'command_reference_n7k.yaml')),
185
184
  CommandPlatformFile.new(/C3064/,
186
185
  File.join(File.dirname(__FILE__),
187
- "command_reference_n3064.yaml")),
186
+ 'command_reference_n3064.yaml')),
188
187
  ]
188
+ # rubocop:enable Metrics/LineLength
189
189
  # Build array
190
- platforms.each { |reference|
190
+ platforms.each do |reference|
191
191
  @files << reference.file if reference.match(@product_id)
192
- }
192
+ end
193
193
  end
194
194
 
195
195
  build_cmd_ref
@@ -204,19 +204,19 @@ module CommandReference
204
204
 
205
205
  reference_yaml = {}
206
206
 
207
- @files.each { |file|
207
+ @files.each do |file|
208
208
  debug "Processing file '#{file}'"
209
209
  reference_yaml = load_yaml(file)
210
210
 
211
- reference_yaml.each { |feature, names|
212
- if names.nil? or names.empty?
213
- raise "No names under feature #{feature}: #{names}"
211
+ reference_yaml.each do |feature, names|
212
+ if names.nil? || names.empty?
213
+ fail "No names under feature #{feature}: #{names}"
214
214
  elsif @hash[feature].nil?
215
215
  @hash[feature] = {}
216
216
  else
217
217
  debug " Merging feature '#{feature}' retrieved from '#{file}'."
218
218
  end
219
- names.each { |name, values|
219
+ names.each do |name, values|
220
220
  debug " Processing feature '#{feature}' name '#{name}'"
221
221
  if @hash[feature][name].nil?
222
222
  begin
@@ -225,14 +225,15 @@ module CommandReference
225
225
  raise "Invalid data for '#{feature}', '#{name}': #{e}"
226
226
  end
227
227
  else
228
- debug " Merging feature '#{feature}' name '#{name}' from '#{file}'."
228
+ debug " Merging feature '#{feature}' name '#{name}' " \
229
+ "from '#{file}'."
229
230
  @hash[feature][name].merge(values, file)
230
231
  end
231
- }
232
- }
233
- }
232
+ end
233
+ end
234
+ end
234
235
 
235
- raise "Missing values in CommandReference." unless valid?
236
+ fail 'Missing values in CommandReference.' unless valid?
236
237
  end
237
238
 
238
239
  # Get the command reference
@@ -243,9 +244,7 @@ module CommandReference
243
244
  # happens if @hash[feature] doesn't exist
244
245
  value = nil
245
246
  end
246
- if value.nil?
247
- raise IndexError, "No CmdRef defined for #{feature}, #{name}"
248
- end
247
+ fail IndexError, "No CmdRef defined for #{feature}, #{name}" if value.nil?
249
248
  value
250
249
  end
251
250
 
@@ -258,18 +257,18 @@ module CommandReference
258
257
  puts "DEBUG: #{text}" if @@debug
259
258
  end
260
259
 
261
- def is_mapping?(node)
260
+ def mapping?(node)
262
261
  # Need to handle both Syck::Map and Psych::Nodes::Mapping
263
262
  node.class.ancestors.any? { |name| /Map/ =~ name.to_s }
264
263
  end
265
- private :is_mapping?
264
+ private :mapping?
266
265
 
267
266
  def get_keys_values_from_map(node)
268
267
  if node.class.ancestors.any? { |name| /Psych/ =~ name.to_s }
269
268
  # A Psych::Node::Mapping instance has an Array of children in
270
269
  # the format [key1, val1, key2, val2]
271
- key_children = node.children.select.each_with_index { |n, i| i.even? }
272
- val_children = node.children.select.each_with_index { |n, i| i.odd? }
270
+ key_children = node.children.select.each_with_index { |_, i| i.even? }
271
+ val_children = node.children.select.each_with_index { |_, i| i.odd? }
273
272
  else
274
273
  # Syck::Map nodes have a .children method but it doesn't work :-(
275
274
  # Instead we access the node.value which is a hash.
@@ -291,18 +290,20 @@ module CommandReference
291
290
  # @param depth Depth into the node tree
292
291
  # @param parents String describing parents of this node, for messages
293
292
  def validate_yaml(node, filename, depth=0, parents=nil)
294
- return unless node and (is_mapping?(node) or node.children)
293
+ return unless node && (mapping?(node) || node.children)
295
294
  # Psych wraps everything in a Document instance, while
296
295
  # Syck does not. To keep the "depth" counting consistent,
297
296
  # we need to ignore Documents.
298
- depth += 1 unless node.class.ancestors.any? { |name| /Document/ =~ name.to_s }
297
+ unless node.class.ancestors.any? { |name| /Document/ =~ name.to_s }
298
+ depth += 1
299
+ end
299
300
  debug "Validating #{node.class} at depth #{depth}"
300
301
 
301
302
  # No special validation for non-mapping nodes - just recurse
302
- unless is_mapping?(node)
303
- node.children.each { |child|
303
+ unless mapping?(node)
304
+ node.children.each do |child|
304
305
  validate_yaml(child, filename, depth, parents)
305
- }
306
+ end
306
307
  return
307
308
  end
308
309
 
@@ -325,7 +326,7 @@ module CommandReference
325
326
  dup = key_arr.detect { |e| key_arr.index(e) != key_arr.rindex(e) }
326
327
  if dup
327
328
  msg = "Duplicate #{label} '#{dup}'#{parents} in #{filename}!"
328
- raise msg
329
+ fail msg
329
330
  end
330
331
 
331
332
  =begin
@@ -338,7 +339,7 @@ module CommandReference
338
339
  if depth == 1
339
340
  last_key = nil
340
341
  key_arr.each do |key|
341
- if last_key and key < last_key
342
+ if last_key && key < last_key
342
343
  raise RuntimeError, "features out of order (#{last_key} > #{key})"
343
344
  end
344
345
  last_key = key
@@ -363,7 +364,7 @@ module CommandReference
363
364
 
364
365
  # Read in yaml file.
365
366
  def load_yaml(yaml_file)
366
- raise "File #{yaml_file} doesn't exist." unless File.exist?(yaml_file)
367
+ fail "File #{yaml_file} doesn't exist." unless File.exist?(yaml_file)
367
368
  # Parse YAML file into a tree of nodes
368
369
  # Psych::SyntaxError doesn't inherit from StandardError in some versions,
369
370
  # so we want to explicitly catch it if using Psych.
@@ -394,22 +395,19 @@ module CommandReference
394
395
  # Check that all resources were pulled in correctly.
395
396
  def valid?
396
397
  complete_status = true
397
- @hash.each { |feature, names|
398
- names.each { |name, ref|
398
+ @hash.each_value do |names|
399
+ names.each_value do |ref|
399
400
  status = ref.valid?
400
- debug "Reference does not contain all supported values:\n#{ref}" unless status
401
- complete_status = (status and complete_status)
402
- }
403
- }
401
+ debug('Reference does not contain all supported values:' \
402
+ "\n#{ref}") unless status
403
+ complete_status = (status && complete_status)
404
+ end
405
+ end
404
406
  complete_status
405
407
  end
406
408
 
407
409
  def to_s
408
- @hash.each { |feature, names|
409
- names.each { |name, ref|
410
- ref.to_s
411
- }
412
- }
410
+ @hash.each_value { |names| names.each_value(&:to_s) }
413
411
  end
414
412
  end
415
413
  end