brocadesan 0.4.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 (79) hide show
  1. data/README +113 -0
  2. data/Rakefile +46 -0
  3. data/brocadesan.gemspec +14 -0
  4. data/lib/brocadesan.rb +8 -0
  5. data/lib/brocadesan/alias.rb +64 -0
  6. data/lib/brocadesan/config/brocade/san/switch_cmd_mapping.yml +116 -0
  7. data/lib/brocadesan/config/parser_mapping.yml +21 -0
  8. data/lib/brocadesan/device.rb +296 -0
  9. data/lib/brocadesan/monkey/string.rb +11 -0
  10. data/lib/brocadesan/provisioning.rb +894 -0
  11. data/lib/brocadesan/switch.rb +882 -0
  12. data/lib/brocadesan/wwn.rb +63 -0
  13. data/lib/brocadesan/zone.rb +60 -0
  14. data/lib/brocadesan/zone_configuration.rb +38 -0
  15. data/lib/meta_methods.rb +263 -0
  16. data/test/alias_test.rb +68 -0
  17. data/test/device_test.rb +203 -0
  18. data/test/output_helpers.rb +308 -0
  19. data/test/outputs/agshow_1.txt +7 -0
  20. data/test/outputs/agshow_1.yml +31 -0
  21. data/test/outputs/agshow_2.txt +4 -0
  22. data/test/outputs/agshow_2.yml +3 -0
  23. data/test/outputs/apt_policy_1.txt +6 -0
  24. data/test/outputs/apt_policy_1.yml +3 -0
  25. data/test/outputs/cfgshow_1.txt +5 -0
  26. data/test/outputs/cfgshow_1.yml +7 -0
  27. data/test/outputs/cfgshow_2.txt +31 -0
  28. data/test/outputs/cfgshow_2.yml +32 -0
  29. data/test/outputs/cfgshow_3.txt +9 -0
  30. data/test/outputs/cfgshow_3.yml +12 -0
  31. data/test/outputs/cfgtransshow_1.txt +2 -0
  32. data/test/outputs/cfgtransshow_1.yml +5 -0
  33. data/test/outputs/cfgtransshow_2.txt +3 -0
  34. data/test/outputs/cfgtransshow_2.yml +4 -0
  35. data/test/outputs/cfgtransshow_3.txt +3 -0
  36. data/test/outputs/cfgtransshow_3.yml +4 -0
  37. data/test/outputs/chassisname_1.txt +2 -0
  38. data/test/outputs/chassisname_1.yml +3 -0
  39. data/test/outputs/dlsshow_1.txt +4 -0
  40. data/test/outputs/dlsshow_1.yml +4 -0
  41. data/test/outputs/dlsshow_2.txt +4 -0
  42. data/test/outputs/dlsshow_2.yml +4 -0
  43. data/test/outputs/fabricshow_1.txt +10 -0
  44. data/test/outputs/fabricshow_1.yml +34 -0
  45. data/test/outputs/iodshow_1.txt +4 -0
  46. data/test/outputs/iodshow_1.yml +4 -0
  47. data/test/outputs/islshow_1.txt +6 -0
  48. data/test/outputs/islshow_1.yml +62 -0
  49. data/test/outputs/islshow_2.txt +2 -0
  50. data/test/outputs/islshow_2.yml +2 -0
  51. data/test/outputs/lscfg_show_1.txt +71 -0
  52. data/test/outputs/lscfg_show_1.yml +5 -0
  53. data/test/outputs/ns_1.txt +80 -0
  54. data/test/outputs/ns_1.yml +39 -0
  55. data/test/outputs/ns_2.txt +37 -0
  56. data/test/outputs/ns_2.yml +21 -0
  57. data/test/outputs/putty.log +1867 -0
  58. data/test/outputs/switch_1.txt +25 -0
  59. data/test/outputs/switch_1.yml +73 -0
  60. data/test/outputs/switch_2.txt +18 -0
  61. data/test/outputs/switch_2.yml +42 -0
  62. data/test/outputs/switch_3.txt +14 -0
  63. data/test/outputs/switch_3.yml +46 -0
  64. data/test/outputs/switchstatusshow_1.txt +21 -0
  65. data/test/outputs/switchstatusshow_1.yml +19 -0
  66. data/test/outputs/trunkshow_1.txt +8 -0
  67. data/test/outputs/trunkshow_1.yml +44 -0
  68. data/test/outputs/trunkshow_2.txt +2 -0
  69. data/test/outputs/trunkshow_2.yml +2 -0
  70. data/test/outputs/version_1.txt +6 -0
  71. data/test/outputs/version_1.yml +8 -0
  72. data/test/outputs/vf_switch_1.txt +25 -0
  73. data/test/outputs/vf_switch_1.yml +73 -0
  74. data/test/provisioning_test.rb +1043 -0
  75. data/test/switch_test.rb +476 -0
  76. data/test/wwn_test.rb +41 -0
  77. data/test/zone_configuration_test.rb +65 -0
  78. data/test/zone_test.rb +73 -0
  79. metadata +170 -0
@@ -0,0 +1,203 @@
1
+ require 'brocadesan'
2
+ require 'minitest/autorun'
3
+ require 'output_helpers'
4
+
5
+ class DeviceTest < MiniTest::Test
6
+ include SshStoryWriter
7
+ include Mock::Net::SSH
8
+ patch_revert
9
+
10
+ def setup
11
+ @device = TestDevice.new("test","test","test")
12
+ #Net::SSH::set_error ""
13
+ end
14
+
15
+ def test_device_setup
16
+ assert_instance_of TestDevice, @device
17
+ end
18
+
19
+ def test_prompt
20
+ assert_equal @device.prompt, TestDevice::DEFAULT_QUERY_PROMPT
21
+ @new_device = TestDevice.new("test","test","test",:prompt => "$ ")
22
+ assert_equal @new_device.prompt, "$ "
23
+ end
24
+
25
+ def test_get_mode
26
+ @device.instance_variable_set(:@opts,{:interactive=>true})
27
+ assert_equal "interactive", @device.get_mode
28
+
29
+ @device = TestDevice.new("test","test","test", :interactive=>true)
30
+ assert_equal "interactive", @device.get_mode
31
+
32
+ @device.set_mode("script")
33
+ assert_equal "script", @device.get_mode
34
+ end
35
+
36
+ def test_set_mode
37
+ assert_equal "interactive", @device.set_mode("interactive")
38
+
39
+ assert_equal "interactive", @device.set_mode(:interactive)
40
+
41
+ assert_equal "script", @device.set_mode("script")
42
+
43
+ assert_equal "script", @device.set_mode(:script)
44
+
45
+ assert_equal "script", @device.set_mode("blabla")
46
+ end
47
+
48
+ def test_session
49
+ #stub start with test connection coming from net/ssh/test
50
+ Net::SSH.stub :start, connection do
51
+ @device.session do
52
+ assert_instance_of Net::SSH::Connection::Session, @device.instance_variable_get(:@session)
53
+ assert_equal 1, @device.instance_variable_get(:@session_level)
54
+ @device.session do
55
+ assert_equal 2, @device.instance_variable_get(:@session_level)
56
+ end
57
+ refute @device.instance_variable_get(:@session).closed?
58
+ assert_equal 1, @device.instance_variable_get(:@session_level)
59
+ end
60
+ end
61
+ assert @device.instance_variable_get(:@session).closed?
62
+
63
+ # session can we called only with block, raises error otherwise
64
+ exp = assert_raises(TestDevice::Error) do
65
+ @device.session
66
+ end
67
+ end
68
+
69
+ def test_script_mode
70
+ #stub start with test connection coming from net/ssh/test
71
+ @device.set_mode :interactive
72
+ assert_equal "interactive", @device.get_mode
73
+ Net::SSH.stub :start, connection do
74
+ @device.script_mode do
75
+ assert_equal "script", @device.get_mode
76
+ end
77
+ end
78
+ assert_equal "interactive", @device.get_mode
79
+
80
+ # this can we called only with block, raises error otherwise
81
+ exp = assert_raises(LocalJumpError) do
82
+ @device.script_mode
83
+ end
84
+ end
85
+
86
+ def test_interactive_mode
87
+ #stub start with test connection coming from net/ssh/test
88
+ @device.set_mode :script
89
+ assert_equal "script", @device.get_mode
90
+ Net::SSH.stub :start, connection do
91
+ @device.interactive_mode do
92
+ assert_equal "interactive", @device.get_mode
93
+ end
94
+ end
95
+ assert_equal "script", @device.get_mode
96
+
97
+ # this can we called only with block, raises error otherwise
98
+ exp = assert_raises(LocalJumpError) do
99
+ @device.interactive_mode
100
+ end
101
+ end
102
+
103
+ def test_query_in_session
104
+ cmds = ["test"]
105
+ exp_response = write_non_interactive_story(cmds,["test_ok"],TestDevice::DEFAULT_QUERY_PROMPT)
106
+ Net::SSH.stub :start, connection do
107
+ @device.session do
108
+ response=@device.query(cmds[0])
109
+ assert_instance_of TestDevice::Response, response
110
+ assert_equal exp_response, response.data
111
+ end
112
+ end
113
+ end
114
+
115
+ def test_interactive_query
116
+ cmds = ["cfgsave","y"]
117
+ replies = ["confirm? [y,n]"]
118
+ exp_response = write_interactive_story(cmds,replies,TestDevice::DEFAULT_QUERY_PROMPT)
119
+
120
+ @device.set_mode("interactive")
121
+ # connection is net/ssh/test method
122
+ @device.instance_variable_set(:@session,connection)
123
+
124
+ response=nil
125
+ assert_scripted do
126
+
127
+ response=@device.query("cfgsave","y")
128
+
129
+ assert_equal exp_response, response.data
130
+ end
131
+ assert_instance_of TestDevice::Response, response
132
+
133
+ # response safety net test for infinite newline response
134
+ # create story with 150 newline commands
135
+ cmds = Array.new(150, "")
136
+ replies = Array.new(150,"confirm? [y,n]")
137
+ exp_response = write_interactive_story(cmds,replies,TestDevice::DEFAULT_QUERY_PROMPT)
138
+
139
+
140
+ # this is pure hack, this raises RuntimeError as the connection script is expecting 150 responses
141
+ # but there are only 100 if them as expected
142
+ # since the script did not match reality we had to assume this worked
143
+ # we do not get any response so we cannot check it precisly
144
+ # but we at least check the internal retries value
145
+ # not the best test but so far the best I came up with
146
+
147
+ assert_raises RuntimeError do
148
+ # story contains 150 new line responses
149
+ # this closses the channel after 100
150
+ @device.query("")
151
+ end
152
+
153
+ assert_equal 100, @device.instance_variable_get(:@retries)
154
+ end
155
+
156
+ def test_non_interactive_query
157
+ cmds = ["cfgshow","switchshow"]
158
+ replies = ["not_available","is_available"]
159
+ error = "cannot execute this"
160
+ # write non itneractive ssh story that should play out when running the query
161
+ exp_response = write_non_interactive_story(cmds,replies,TestDevice::DEFAULT_QUERY_PROMPT)
162
+
163
+ # connection is net/ssh/test method
164
+ @device.instance_variable_set(:@session,connection)
165
+ response=nil
166
+ assert_scripted do
167
+
168
+ response=@device.query(*cmds)
169
+
170
+ assert_equal exp_response, response.data
171
+ end
172
+ assert_instance_of TestDevice::Response, response
173
+
174
+ #errors
175
+ # write story endig with error
176
+ exp_response = write_failed_simple_story(cmds[0],error,TestDevice::DEFAULT_QUERY_PROMPT)
177
+ exp = assert_raises TestDevice::Error do
178
+ @device.query(cmds[0])
179
+ end
180
+ assert_equal "#{error}\n", exp.message
181
+ end
182
+ end
183
+
184
+ class ResponseTest < MiniTest::Test
185
+ def setup
186
+ @response = TestDevice::Response.new("> ")
187
+ end
188
+
189
+ def test_parse
190
+ #parse should end with end
191
+ @response.parsed="test"
192
+ @response.parse
193
+ assert_equal a={:parsing_position=>"end"}, @response.parsed
194
+ end
195
+
196
+ def test_prompt
197
+ assert_equal @response.instance_variable_get(:@prompt), "> "
198
+ end
199
+ end
200
+
201
+ class TestDevice
202
+ include SshDevice
203
+ end
@@ -0,0 +1,308 @@
1
+ require 'yaml'
2
+ require 'net/ssh/test'
3
+
4
+ module OutputReader
5
+
6
+ attr_accessor :output_dir
7
+
8
+ def read_all_starting_with(regexp,&block)
9
+ @output_dir=Dir.pwd if @output_dir.nil?
10
+ files=Dir.glob(File.join(@output_dir,"#{regexp}*.txt"))
11
+ files.each do |f|
12
+ contents = File.read(f)
13
+ yield f, contents
14
+ end
15
+ end
16
+
17
+ def read_yaml_for(file)
18
+ parts=file.split(".")
19
+ file_name=parts[0..-2].join(".")
20
+ YAML.load(File.read("#{file_name}.yml"))
21
+ end
22
+
23
+ def new_mock_response
24
+ Brocade::SAN::Switch::Response.new("> ")
25
+ end
26
+ end
27
+
28
+ # this is used to check the _exec methods in device
29
+ # for the remaining test see the below monkeypath mock
30
+ module SshStoryWriter
31
+ include Net::SSH::Test
32
+
33
+ # creates a interactive ssh story for testing and returns expeced response for that story
34
+ def write_interactive_story(cmds,partial_replies,prompt="")
35
+ cmd=cmds.shift
36
+ response = "#{prompt}"
37
+ story do |session|
38
+ channel = session.opens_channel
39
+ channel.sends_request_pty
40
+ channel.sends_exec cmd
41
+ response +="#{cmd}\n"
42
+ partial_replies.each_with_index do |pr,i|
43
+ channel.gets_data pr
44
+ channel.sends_data "#{cmds[i]}\n"
45
+
46
+ response +="#{pr}"
47
+ response +="#{cmds[i]}\n"
48
+ end
49
+ channel.gets_close
50
+ channel.sends_close
51
+ end
52
+
53
+ return response
54
+ end
55
+
56
+ # creates non interactive ssh story for testing and returns expeced response for that story
57
+ def write_non_interactive_story(cmds,replies,prompt="")
58
+ response = ""
59
+ story do |session|
60
+ replies.each_with_index do |reply,i|
61
+ channel = session.opens_channel
62
+ channel.sends_exec cmds[i]
63
+ channel.gets_data reply
64
+ channel.gets_close
65
+ channel.sends_close
66
+ response +="#{prompt}#{cmds[i]}\n"
67
+ response +="#{reply}\n"
68
+ end
69
+
70
+ end
71
+
72
+ return response
73
+ end
74
+
75
+ def write_failed_simple_story(cmd,error,prompt="")
76
+ response = ""
77
+ story do |session|
78
+ channel = session.opens_channel
79
+ channel.sends_exec cmd
80
+ channel.gets_extended_data error
81
+ #session.gets_channel_extended_data(channel, error)
82
+ channel.gets_close
83
+ channel.sends_close
84
+ response +="#{prompt}#{cmd}\n"
85
+ #response +="#{reply}\n"
86
+
87
+ end
88
+
89
+ return response
90
+ end
91
+ end
92
+
93
+ # patch for request_pry based on https://github.com/test-kitchen/test-kitchen/blob/master/spec/kitchen/ssh_spec.rb#L86-L113
94
+ module Net
95
+
96
+ module SSH
97
+
98
+ module Test
99
+
100
+ class Channel
101
+
102
+ def sends_request_pty
103
+ pty_data = ["xterm", 80, 24, 640, 480, "\0"]
104
+
105
+ script.events << Class.new(Net::SSH::Test::LocalPacket) do
106
+ def types
107
+ if @type == 98 && @data[1] == "pty-req"
108
+ @types ||= [
109
+ :long, :string, :bool, :string,
110
+ :long, :long, :long, :long, :string
111
+ ]
112
+ else
113
+ super
114
+ end
115
+ end
116
+ end.new(:channel_request, remote_id, "pty-req", false, *pty_data)
117
+ end
118
+ end
119
+ end
120
+ end
121
+ end
122
+
123
+ # simplified mock module that is used to monkey patch/stub Net::SSH.start
124
+ # the stubed method resturns mocked Session
125
+ # the session for now response to exec!, closed? and close
126
+ # which is sufficient for my testing
127
+ # Usage:
128
+ # put following into subclasees MiniTest::Test that should use this mock
129
+ # include Mock::Net::SSH
130
+ # patch_set
131
+ #
132
+ # or
133
+ #
134
+ # put following into subclasees MiniTest::Test that should not use this mock
135
+ # mention this explicitely as some other test might have set it
136
+ # include Mock::Net::SSH
137
+ # patch_revert
138
+ #
139
+ module Mock
140
+ module Net
141
+ module SSH
142
+
143
+ def self.included(base)
144
+ base.extend(ClassMethods)
145
+ end
146
+
147
+ def initialize(*args)
148
+ patch_configure
149
+ super(*args)
150
+ end
151
+
152
+ def patch_configure; end
153
+
154
+ module ClassMethods
155
+ def patch_set
156
+ alias_method :patch_configure, :monkey_patch
157
+ end
158
+
159
+ def patch_revert
160
+ alias_method :patch_configure, :monkey_patch_revert
161
+ end
162
+ end
163
+
164
+ @@data="Response"
165
+ @@error=""
166
+ @@channel="channel"
167
+
168
+ def self.get_data
169
+ @@data
170
+ end
171
+
172
+ def self.get_error
173
+ @@error
174
+ end
175
+
176
+ def self.get_channel
177
+ @@channel
178
+ end
179
+
180
+ def self.set_data(x)
181
+ @@data=x
182
+ end
183
+
184
+ def self.set_error(x)
185
+ @@error=x
186
+ end
187
+
188
+ def self.set_channel(x)
189
+ @@channel=x
190
+ end
191
+
192
+ class Session
193
+ def exec!(command, &block)
194
+ @data=Mock::Net::SSH::get_data.dup
195
+ @error=Mock::Net::SSH::get_error.dup
196
+ @ch=Mock::Net::SSH::get_channel.dup
197
+
198
+ if block
199
+ block.call(@ch, :stdout, @data)
200
+ block.call(@ch, :stderr, @error)
201
+ else
202
+ $stdout.print(data)
203
+ end
204
+ end
205
+
206
+ def close
207
+ @closed=true
208
+ end
209
+
210
+ def closed?
211
+ @closed.nil? ? false : @closed
212
+ end
213
+ end
214
+
215
+ private
216
+ def monkey_patch
217
+ ::Net::SSH.instance_eval do
218
+ singleton = self.singleton_class
219
+ if !singleton.respond_to? :old_start
220
+ singleton.send(:alias_method, :old_start, :start)
221
+
222
+ def self.start(host, user, options={}, &block)
223
+ if block
224
+ yield Mock::Net::SSH::Session.new
225
+ else
226
+ return Mock::Net::SSH::Session.new
227
+ end
228
+ end
229
+ end
230
+ end
231
+ end
232
+
233
+ def monkey_patch_revert
234
+ ::Net::SSH.instance_eval do
235
+ if self.singleton_class.respond_to? :old_start
236
+ self.singleton_class.send(:alias_method, :start, :old_start)
237
+ self.singleton_class.send(:undef_method, :old_start)
238
+ end
239
+ end
240
+ end
241
+ end
242
+ end
243
+ end
244
+
245
+ module Kernel
246
+ # stubs query method by another method that assiges the parameters to @test_string and runing original query
247
+ def query_stub(&block)
248
+ self.instance_eval do
249
+ self.singleton_class.send(:alias_method, :old_query, :query)
250
+ @query_string=""
251
+ def query(*cmds)
252
+ @query_string||=""
253
+ @query_string<<cmds.join(",")
254
+ old_query(*cmds)
255
+ end
256
+ end
257
+ yield
258
+ self.instance_eval do
259
+ self.singleton_class.send(:alias_method, :query, :old_query)
260
+ self.singleton_class.send(:undef_method, :old_query)
261
+ end
262
+ end
263
+
264
+ # stubs query method by another method that assiges the parameters to @test_string and runing original query
265
+ def abort_transaction_stub(&block)
266
+ self.instance_eval do
267
+ self.singleton_class.send(:alias_method, :old_abort_transaction, :abort_transaction)
268
+ def abort_transaction
269
+ @trans_aborted=true
270
+ old_abort_transaction
271
+ end
272
+ end
273
+ yield
274
+ self.instance_eval do
275
+ self.singleton_class.send(:alias_method, :abort_transaction, :old_abort_transaction)
276
+ self.singleton_class.send(:undef_method, :old_abort_transaction)
277
+ end
278
+ end
279
+
280
+ # stubs query method by another method that assiges the parameters to @test_string and runing original query
281
+ def raise_if_obj_do_not_exist_stub(&block)
282
+ self.instance_eval do
283
+ self.singleton_class.send(:alias_method, :old_raise_if_obj_do_not_exist, :raise_if_obj_do_not_exist)
284
+ def raise_if_obj_do_not_exist(obj)
285
+ if !@run.nil?
286
+ old_raise_if_obj_do_not_exist(obj)
287
+ end
288
+ @run=true
289
+ end
290
+ end
291
+ yield
292
+ self.instance_eval do
293
+ self.singleton_class.send(:alias_method, :raise_if_obj_do_not_exist, :old_raise_if_obj_do_not_exist)
294
+ self.singleton_class.send(:undef_method, :old_raise_if_obj_do_not_exist)
295
+ end
296
+ end
297
+
298
+ def multistub(cmds,&block)
299
+ if !cmds.empty?
300
+ cmd = cmds.shift
301
+ stub cmd[0], cmd[1] do
302
+ multistub cmds, &block
303
+ end
304
+ else
305
+ yield
306
+ end
307
+ end
308
+ end