cisco_node_utils 0.9.0

Sign up to get free protection for your applications and to get access to all the features.
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