cisco_node_utils 0.9.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 (64) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +2 -0
  3. data/.rubocop.yml +3 -0
  4. data/.rubocop_todo.yml +293 -0
  5. data/CHANGELOG.md +5 -0
  6. data/CONTRIBUTING.md +31 -0
  7. data/Gemfile +4 -0
  8. data/LICENSE +201 -0
  9. data/README.md +113 -0
  10. data/Rakefile +4 -0
  11. data/cisco_node_utils.gemspec +30 -0
  12. data/lib/cisco_node_utils.rb +33 -0
  13. data/lib/cisco_node_utils/README_YAML.md +333 -0
  14. data/lib/cisco_node_utils/cisco_cmn_utils.rb +92 -0
  15. data/lib/cisco_node_utils/command_reference.rb +415 -0
  16. data/lib/cisco_node_utils/command_reference_common.yaml +845 -0
  17. data/lib/cisco_node_utils/command_reference_n3064.yaml +13 -0
  18. data/lib/cisco_node_utils/command_reference_n7k.yaml +48 -0
  19. data/lib/cisco_node_utils/command_reference_n9k.yaml +35 -0
  20. data/lib/cisco_node_utils/configparser_lib.rb +196 -0
  21. data/lib/cisco_node_utils/interface.rb +501 -0
  22. data/lib/cisco_node_utils/interface_ospf.rb +241 -0
  23. data/lib/cisco_node_utils/node.rb +673 -0
  24. data/lib/cisco_node_utils/platform.rb +184 -0
  25. data/lib/cisco_node_utils/platform_info.rb +58 -0
  26. data/lib/cisco_node_utils/platform_info.yaml +10 -0
  27. data/lib/cisco_node_utils/router_ospf.rb +96 -0
  28. data/lib/cisco_node_utils/router_ospf_vrf.rb +258 -0
  29. data/lib/cisco_node_utils/snmpcommunity.rb +91 -0
  30. data/lib/cisco_node_utils/snmpgroup.rb +55 -0
  31. data/lib/cisco_node_utils/snmpserver.rb +150 -0
  32. data/lib/cisco_node_utils/snmpuser.rb +342 -0
  33. data/lib/cisco_node_utils/tacacs_server.rb +175 -0
  34. data/lib/cisco_node_utils/tacacs_server_host.rb +128 -0
  35. data/lib/cisco_node_utils/version.rb +17 -0
  36. data/lib/cisco_node_utils/vlan.rb +153 -0
  37. data/lib/cisco_node_utils/vtp.rb +127 -0
  38. data/lib/cisco_node_utils/yum.rb +84 -0
  39. data/tests/basetest.rb +93 -0
  40. data/tests/ciscotest.rb +136 -0
  41. data/tests/cmd_config.yaml +51 -0
  42. data/tests/cmd_config_invalid.yaml +16 -0
  43. data/tests/test_all_cisco.rb +46 -0
  44. data/tests/test_command_config.rb +192 -0
  45. data/tests/test_command_reference.rb +222 -0
  46. data/tests/test_interface.rb +1017 -0
  47. data/tests/test_interface_ospf.rb +763 -0
  48. data/tests/test_interface_svi.rb +267 -0
  49. data/tests/test_interface_switchport.rb +722 -0
  50. data/tests/test_node.rb +108 -0
  51. data/tests/test_node_ext.rb +450 -0
  52. data/tests/test_platform.rb +188 -0
  53. data/tests/test_router_ospf.rb +164 -0
  54. data/tests/test_router_ospf_vrf.rb +753 -0
  55. data/tests/test_snmpcommunity.rb +344 -0
  56. data/tests/test_snmpgroup.rb +71 -0
  57. data/tests/test_snmpserver.rb +443 -0
  58. data/tests/test_snmpuser.rb +803 -0
  59. data/tests/test_tacacs_server.rb +388 -0
  60. data/tests/test_tacacs_server_host.rb +391 -0
  61. data/tests/test_vlan.rb +264 -0
  62. data/tests/test_vtp.rb +319 -0
  63. data/tests/test_yum.rb +106 -0
  64. metadata +188 -0
@@ -0,0 +1,16 @@
1
+ # test command config yaml file
2
+ ---
3
+ feature-global_bad_syntax:
4
+ command: |
5
+ fature ospf
6
+
7
+
8
+ feature-nested_bad_syntax:
9
+ command: >
10
+ interface loopback0
11
+ descrion testloopback
12
+
13
+ feature-input_too_long:
14
+ command: |
15
+ router ospf bluethisoutputistolongwaytoolong
16
+
@@ -0,0 +1,46 @@
1
+ #!/usr/bin/env ruby
2
+ #
3
+ # One-stop shop for running all of our current test cases.
4
+ # December 2014, Glenn F. Matthews
5
+ #
6
+ # Copyright (c) 2014-2015 Cisco and/or its affiliates.
7
+ #
8
+ # Licensed under the Apache License, Version 2.0 (the "License");
9
+ # you may not use this file except in compliance with the License.
10
+ # You may obtain a copy of the License at
11
+ #
12
+ # http://www.apache.org/licenses/LICENSE-2.0
13
+ #
14
+ # Unless required by applicable law or agreed to in writing, software
15
+ # distributed under the License is distributed on an "AS IS" BASIS,
16
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
17
+ # See the License for the specific language governing permissions and
18
+ # limitations under the License.
19
+
20
+ require 'rubygems'
21
+ gem 'minitest', '>= 2.5.1', '< 5.0.0'
22
+ require 'minitest/autorun'
23
+
24
+ # Basic sanity
25
+ require File.expand_path("../test_command_reference", __FILE__)
26
+ require File.expand_path("../test_node", __FILE__)
27
+ require File.expand_path("../test_node_ext", __FILE__)
28
+
29
+ # Feature tests - please keep in alphabetical order
30
+ require File.expand_path("../test_command_config", __FILE__)
31
+ require File.expand_path("../test_interface", __FILE__)
32
+ require File.expand_path("../test_interface_ospf", __FILE__)
33
+ require File.expand_path("../test_interface_svi", __FILE__)
34
+ require File.expand_path("../test_interface_switchport", __FILE__)
35
+ require File.expand_path("../test_platform", __FILE__)
36
+ require File.expand_path("../test_router_ospf", __FILE__)
37
+ require File.expand_path("../test_router_ospf_vrf", __FILE__)
38
+ require File.expand_path("../test_snmpcommunity", __FILE__)
39
+ require File.expand_path("../test_snmpgroup", __FILE__)
40
+ require File.expand_path("../test_snmpserver", __FILE__)
41
+ require File.expand_path("../test_snmpuser", __FILE__)
42
+ require File.expand_path("../test_tacacs_server", __FILE__)
43
+ require File.expand_path("../test_tacacs_server_host", __FILE__)
44
+ require File.expand_path("../test_vlan", __FILE__)
45
+ require File.expand_path("../test_vtp", __FILE__)
46
+ require File.expand_path("../test_yum", __FILE__)
@@ -0,0 +1,192 @@
1
+ #!/usr/bin/env ruby
2
+ #
3
+ # Michael Wiebe, December 2014
4
+ #
5
+ # Copyright (c) 2014-2015 Cisco and/or its affiliates.
6
+ #
7
+ # Licensed under the Apache License, Version 2.0 (the "License");
8
+ # you may not use this file except in compliance with the License.
9
+ # You may obtain a copy of the License at
10
+ #
11
+ # http://www.apache.org/licenses/LICENSE-2.0
12
+ #
13
+ # Unless required by applicable law or agreed to in writing, software
14
+ # distributed under the License is distributed on an "AS IS" BASIS,
15
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16
+ # See the License for the specific language governing permissions and
17
+ # limitations under the License.
18
+
19
+ require File.expand_path("../ciscotest", __FILE__)
20
+ require File.expand_path("../../lib/cisco_node_utils/configparser_lib", __FILE__)
21
+ require 'timeout'
22
+ require 'yaml'
23
+
24
+ def load_yaml(test_type=:positive)
25
+ if test_type == :positive
26
+ path = File.expand_path("../cmd_config.yaml", __FILE__)
27
+ elsif test_type == :negative
28
+ path = File.expand_path("../cmd_config_invalid.yaml", __FILE__)
29
+ else
30
+ raise TypeError
31
+ end
32
+ config_hash = YAML.load(File.read(path))
33
+ end
34
+
35
+ class TestCommandConfig < CiscoTestCase
36
+ include ConfigParser
37
+
38
+ # ---------------------------------------------------------------------------
39
+ # Helper Methods
40
+ # ---------------------------------------------------------------------------
41
+
42
+ def remove_whitespace(commands)
43
+ commands.gsub(/^\s*$\n/, '')
44
+ end # remove_whitespace
45
+
46
+ def compare_with_results(desired_config_str, current_key)
47
+ retrieve_command = "show running all"
48
+ running_config_str = node.show(retrieve_command)
49
+
50
+ begin
51
+ should_config = ConfigParser::Configuration.new(desired_config_str)
52
+ running_config = ConfigParser::Configuration.new(running_config_str)
53
+ existing = should_config.compare_with(running_config)
54
+ rescue StopIteration => e
55
+ puts e.what
56
+ rescue ArgumentError => e
57
+ puts e.what
58
+ end
59
+ # puts "Existing command block:\n#{existing}"
60
+ assert_equal(existing.empty?, false,
61
+ "Error: Expected configuration \n'#{desired_config_str}'\n does not exist.\
62
+ \nHash Key: #{current_key}")
63
+ end
64
+
65
+ def send_device_config(config_cmd_hash)
66
+ config_cmd_hash.each do |k, v|
67
+ v.each do |k1, v1|
68
+ # Send commands
69
+ cfg_cmd_str = "configure terminal\n#{v1.gsub(/^/, " ")}\n end\n"
70
+ cfg_string = remove_whitespace(cfg_cmd_str)
71
+ # puts "cfg_string: \n||\n#{cfg_string}||\n"
72
+ begin
73
+ output = node.config(cfg_string)
74
+ # puts "output : #{output}"
75
+ # make sure config is present in success case
76
+ compare_with_results(v1, k)
77
+ rescue CliError => e
78
+ known_failure = e.message[/ERROR:.*port channel not present/]
79
+ refute(known_failure, "ERROR: port channel not present")
80
+ raise
81
+ end
82
+ end # k1, v1
83
+ end
84
+ end
85
+
86
+ def build_int_scale_config(add=true)
87
+ add ? s = "" : s = "no "
88
+ current_interface = 0
89
+ num_interfaces = 1024
90
+ command_list = ""
91
+ while current_interface < num_interfaces
92
+ command_list += "#{s}interface loopback#{current_interface}\n"
93
+ current_interface += 1
94
+ end
95
+ command_list
96
+ end
97
+
98
+ # ---------------------------------------------------------------------------
99
+ # Test Case Methods
100
+ # ---------------------------------------------------------------------------
101
+
102
+ def test_valid_config
103
+ cfg_hash = load_yaml
104
+ begin
105
+ send_device_config(cfg_hash)
106
+ end
107
+ end
108
+
109
+ def test_valid_scale
110
+ show_int_count = "show int brief | i '^\S+\s+--' | count"
111
+ pre = @device.cmd(show_int_count)[/^(\d+)$/]
112
+
113
+ # Add 1024 loopback interfaces
114
+ cfg_hash_add = Hash.new { |h, k| h[k] = Hash.new(&h.default_proc) }
115
+ cfg_hash_add ["loopback-int-add"]["command"] = "#{build_int_scale_config}"
116
+ begin
117
+ send_device_config(cfg_hash_add)
118
+ rescue Timeout::Error
119
+ puts "\n -- Long-running command, extending timeout +30 sec"
120
+ sleep 30 # long-running command
121
+ curr = @device.cmd("show int brief | count")[/^(\d+)$/]
122
+ flunk("Timeout while creating 1024 loopback interfaces" +
123
+ "(pre:#{pre} curr:#{curr}") unless (pre == curr - 1024)
124
+ end
125
+
126
+ # Remove 1024 loopback interfaces
127
+ cfg_hash_remove = Hash.new { |h, k| h[k] = Hash.new(&h.default_proc) }
128
+ cfg_hash_remove ["loopback-int-add"]["command"] = "#{build_int_scale_config(add=false)}"
129
+ begin
130
+ send_device_config(cfg_hash_remove)
131
+ rescue Timeout::Error
132
+ puts "\n -- Long-running command, extending timeout +30 sec"
133
+ sleep 30 # long-running: n95 can take 70+ sec to remove all of these
134
+ curr = @device.cmd(show_int_count)[/^(\d+)$/]
135
+ flunk("Timeout while deleting 1024 loopback interfaces " +
136
+ "(pre:#{pre} curr:#{curr}") unless (pre == curr)
137
+ end
138
+ end
139
+
140
+ def test_invalid_config
141
+ cfg_hash = load_yaml(test_type=:negative)
142
+ cfg_hash.each do |k, v|
143
+ v.each do |k1, v1|
144
+ cfg_cmd_str = "configure terminal\n#{v1.gsub(/^/, " ")}\n end\n"
145
+ cfg_string = remove_whitespace(cfg_cmd_str)
146
+ assert_raises(CliError) do
147
+ output = node.config(cfg_string)
148
+ end
149
+ end # k1, v1
150
+ end
151
+ end
152
+
153
+ def test_indent_with_tab
154
+ assert_raises(RuntimeError,
155
+ "Should have caught TAB char in indent area") do
156
+ Configuration.new(" \t interface loopback10")
157
+ end
158
+ end
159
+
160
+ def test_build_min_config_hash
161
+ # 1. Get superset of running-config and agent-config
162
+ # 2. From superset derive minimum needed for parity with running
163
+ runn_str = "
164
+ \ninterface loopback10
165
+ \n description foo
166
+ \ninterface loopback11
167
+ \ninterface loopback12
168
+ \ninterface loopback13"
169
+ runn_hash = Configuration.new(runn_str)
170
+
171
+ agent_str = "
172
+ \ninterface loopback10
173
+ \n description 10
174
+ \ninterface loopback11
175
+ \nno interface loopback12
176
+ \ninterface loopback13"
177
+ agent_hash = Configuration.new(agent_str)
178
+
179
+ min_expected = "interface loopback10\ndescription 10\nno interface loopback12"
180
+
181
+ superset_str = agent_hash.compare_with(runn_hash)
182
+ superset_hash = Configuration.new(superset_str)
183
+
184
+ min_config_hash =
185
+ Configuration.build_min_config_hash(superset_hash.configuration,
186
+ agent_hash.configuration)
187
+ min_config_str = Configuration.config_hash_to_str(min_config_hash)
188
+
189
+ assert_equal(min_config_str.include?(min_expected), true,
190
+ "Error:\nExpected:\n#{min_expected}\n\nFound:\n#{min_config_str}")
191
+ end
192
+ end
@@ -0,0 +1,222 @@
1
+ #!/usr/bin/env ruby
2
+ #
3
+ # Unit testing for CommandReference and CmdRef classes.
4
+ # November 2014, Glenn F. Matthews
5
+ #
6
+ # Copyright (c) 2014-2015 Cisco and/or its affiliates.
7
+ #
8
+ # Licensed under the Apache License, Version 2.0 (the "License");
9
+ # you may not use this file except in compliance with the License.
10
+ # You may obtain a copy of the License at
11
+ #
12
+ # http://www.apache.org/licenses/LICENSE-2.0
13
+ #
14
+ # Unless required by applicable law or agreed to in writing, software
15
+ # distributed under the License is distributed on an "AS IS" BASIS,
16
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
17
+ # See the License for the specific language governing permissions and
18
+ # limitations under the License.
19
+ #
20
+
21
+ require 'minitest/autorun'
22
+ require 'tempfile'
23
+ require File.expand_path("../../lib/cisco_node_utils/command_reference", __FILE__)
24
+
25
+ class TestCmdRef < MiniTest::Unit::TestCase
26
+ include CommandReference
27
+
28
+ def setup
29
+ @input_file = Tempfile.new('test.yaml')
30
+ end
31
+
32
+ def teardown
33
+ @input_file.close!
34
+ end
35
+
36
+ def load_file
37
+ CommandReference.new("", [@input_file.path])
38
+ end
39
+
40
+ def write(string)
41
+ @input_file.write(string + "\n")
42
+ @input_file.flush
43
+ end
44
+
45
+ def test_load_empty_file
46
+ # should load successfully but yield an empty hash
47
+ reference = load_file
48
+ assert(reference.empty?)
49
+ end
50
+
51
+ def test_load_whitespace_only
52
+ write(" ")
53
+ reference = load_file
54
+ assert(reference.empty?)
55
+ end
56
+
57
+ def test_load_not_valid_yaml
58
+ # The control characters embedded below are not permitted in YAML.
59
+ # Syck (in older Ruby versions) will incorrectly accept these
60
+ # while parsing the file, but our CmdRef constructor will eventually
61
+ # reject the data.
62
+ # Psych (in newer Ruby versions) will correctly reject
63
+ # this data at parse time.
64
+ write("
65
+ feature\a\e:
66
+ name\b\f:
67
+ default_value:\vtrue")
68
+ assert_raises(RuntimeError) do
69
+ load_file
70
+ end
71
+ end
72
+
73
+ def test_load_feature_no_name
74
+ # should error out
75
+ write("feature:")
76
+ assert_raises(RuntimeError) do
77
+ load_file
78
+ end
79
+ end
80
+
81
+ def test_load_feature_name_no_data
82
+ write("
83
+ feature:
84
+ name:")
85
+ assert_raises(RuntimeError) do
86
+ reference = load_file
87
+ end
88
+ end
89
+
90
+ def test_load_feature_name_default
91
+ write("
92
+ feature:
93
+ name:
94
+ default_value: true")
95
+ reference = load_file
96
+ assert(!reference.empty?)
97
+ ref = reference.lookup("feature", "name")
98
+ assert_equal(true, ref.default_value)
99
+ end
100
+
101
+ def test_load_duplicate_feature
102
+ write("
103
+ feature:
104
+ name:
105
+ default_value: false
106
+ feature:
107
+ name:
108
+ config_get: 'show feature'
109
+ ")
110
+ assert_raises(RuntimeError) do
111
+ reference = load_file
112
+ end
113
+ end
114
+
115
+ def test_load_duplicate_name
116
+ write("
117
+ feature:
118
+ name:
119
+ default_value: false
120
+ name:
121
+ config_get: 'show feature'")
122
+ assert_raises(RuntimeError) do
123
+ reference = load_file
124
+ end
125
+ end
126
+
127
+ def test_load_duplicate_param
128
+ write("
129
+ feature:
130
+ name:
131
+ default_value: false
132
+ default_value: true")
133
+ assert_raises(RuntimeError) do
134
+ reference = load_file
135
+ end
136
+ end
137
+
138
+ def test_load_unsupported_key
139
+ write("
140
+ feature:
141
+ name:
142
+ config_get: 'show feature'
143
+ what_is_this: \"I don't even\"")
144
+ assert_raises(RuntimeError) do
145
+ reference = load_file
146
+ end
147
+ end
148
+
149
+ =begin
150
+ # Alphabetization of features is not enforced at this time.
151
+ def test_load_features_unalphabetized
152
+ write("
153
+ zzz:
154
+ name:
155
+ default_value: true
156
+ zzy:
157
+ name:
158
+ default_value: false")
159
+ self.assert_raises(RuntimeError) do
160
+ reference = load_file
161
+ end
162
+ end
163
+ =end
164
+
165
+ def type_check(obj, cls)
166
+ assert(obj.is_a?(cls), "#{obj} should be #{cls} but is #{obj.class}")
167
+ end
168
+
169
+ def test_load_types
170
+ write("
171
+ feature:
172
+ name:
173
+ default_value: true
174
+ config_get: show hello
175
+ config_get_token: !ruby/regexp '/hello world/'
176
+ config_set: [ \"hello\", \"world\" ]
177
+ test_config_get_regex: !ruby/regexp '/hello world/'
178
+ ")
179
+ reference = load_file
180
+ ref = reference.lookup("feature", "name")
181
+ type_check(ref.default_value, TrueClass)
182
+ type_check(ref.config_get, String)
183
+ type_check(ref.config_get_token, Regexp)
184
+ type_check(ref.config_set, Array)
185
+ type_check(ref.test_config_get_regex, Regexp)
186
+ end
187
+
188
+ def test_load_common
189
+ reference = CommandReference.new("")
190
+ assert(reference.files.any? { |filename| /common.yaml/ =~ filename })
191
+ refute(reference.files.any? { |filename| /n9k.yaml/ =~ filename })
192
+ refute(reference.files.any? { |filename| /n7k.yaml/ =~ filename })
193
+ refute(reference.files.any? { |filename| /n3064.yaml/ =~ filename })
194
+ # Some spot checks
195
+ type_check(reference.lookup("vtp", "feature").config_get_token, String)
196
+ type_check(reference.lookup("vtp", "version").default_value, Integer)
197
+ end
198
+
199
+ def test_load_n9k
200
+ reference = CommandReference.new("N9K-C9396PX")
201
+ assert(reference.files.any? { |filename| /common.yaml/ =~ filename })
202
+ assert(reference.files.any? { |filename| /n9k.yaml/ =~ filename })
203
+ refute(reference.files.any? { |filename| /n7k.yaml/ =~ filename })
204
+ refute(reference.files.any? { |filename| /n3064.yaml/ =~ filename })
205
+ end
206
+
207
+ def test_load_n7k
208
+ reference = CommandReference.new("N7K-C7010")
209
+ assert(reference.files.any? { |filename| /common.yaml/ =~ filename })
210
+ refute(reference.files.any? { |filename| /n9k.yaml/ =~ filename })
211
+ assert(reference.files.any? { |filename| /n7k.yaml/ =~ filename })
212
+ refute(reference.files.any? { |filename| /n3064.yaml/ =~ filename })
213
+ end
214
+
215
+ def test_load_n3k_3064
216
+ reference = CommandReference.new("N3K-C3064PQ-10GE")
217
+ assert(reference.files.any? { |filename| /common.yaml/ =~ filename })
218
+ refute(reference.files.any? { |filename| /n9k.yaml/ =~ filename })
219
+ refute(reference.files.any? { |filename| /n7k.yaml/ =~ filename })
220
+ assert(reference.files.any? { |filename| /n3064.yaml/ =~ filename })
221
+ end
222
+ end