brocadesan 0.4.0
Sign up to get free protection for your applications and to get access to all the features.
- data/README +113 -0
- data/Rakefile +46 -0
- data/brocadesan.gemspec +14 -0
- data/lib/brocadesan.rb +8 -0
- data/lib/brocadesan/alias.rb +64 -0
- data/lib/brocadesan/config/brocade/san/switch_cmd_mapping.yml +116 -0
- data/lib/brocadesan/config/parser_mapping.yml +21 -0
- data/lib/brocadesan/device.rb +296 -0
- data/lib/brocadesan/monkey/string.rb +11 -0
- data/lib/brocadesan/provisioning.rb +894 -0
- data/lib/brocadesan/switch.rb +882 -0
- data/lib/brocadesan/wwn.rb +63 -0
- data/lib/brocadesan/zone.rb +60 -0
- data/lib/brocadesan/zone_configuration.rb +38 -0
- data/lib/meta_methods.rb +263 -0
- data/test/alias_test.rb +68 -0
- data/test/device_test.rb +203 -0
- data/test/output_helpers.rb +308 -0
- data/test/outputs/agshow_1.txt +7 -0
- data/test/outputs/agshow_1.yml +31 -0
- data/test/outputs/agshow_2.txt +4 -0
- data/test/outputs/agshow_2.yml +3 -0
- data/test/outputs/apt_policy_1.txt +6 -0
- data/test/outputs/apt_policy_1.yml +3 -0
- data/test/outputs/cfgshow_1.txt +5 -0
- data/test/outputs/cfgshow_1.yml +7 -0
- data/test/outputs/cfgshow_2.txt +31 -0
- data/test/outputs/cfgshow_2.yml +32 -0
- data/test/outputs/cfgshow_3.txt +9 -0
- data/test/outputs/cfgshow_3.yml +12 -0
- data/test/outputs/cfgtransshow_1.txt +2 -0
- data/test/outputs/cfgtransshow_1.yml +5 -0
- data/test/outputs/cfgtransshow_2.txt +3 -0
- data/test/outputs/cfgtransshow_2.yml +4 -0
- data/test/outputs/cfgtransshow_3.txt +3 -0
- data/test/outputs/cfgtransshow_3.yml +4 -0
- data/test/outputs/chassisname_1.txt +2 -0
- data/test/outputs/chassisname_1.yml +3 -0
- data/test/outputs/dlsshow_1.txt +4 -0
- data/test/outputs/dlsshow_1.yml +4 -0
- data/test/outputs/dlsshow_2.txt +4 -0
- data/test/outputs/dlsshow_2.yml +4 -0
- data/test/outputs/fabricshow_1.txt +10 -0
- data/test/outputs/fabricshow_1.yml +34 -0
- data/test/outputs/iodshow_1.txt +4 -0
- data/test/outputs/iodshow_1.yml +4 -0
- data/test/outputs/islshow_1.txt +6 -0
- data/test/outputs/islshow_1.yml +62 -0
- data/test/outputs/islshow_2.txt +2 -0
- data/test/outputs/islshow_2.yml +2 -0
- data/test/outputs/lscfg_show_1.txt +71 -0
- data/test/outputs/lscfg_show_1.yml +5 -0
- data/test/outputs/ns_1.txt +80 -0
- data/test/outputs/ns_1.yml +39 -0
- data/test/outputs/ns_2.txt +37 -0
- data/test/outputs/ns_2.yml +21 -0
- data/test/outputs/putty.log +1867 -0
- data/test/outputs/switch_1.txt +25 -0
- data/test/outputs/switch_1.yml +73 -0
- data/test/outputs/switch_2.txt +18 -0
- data/test/outputs/switch_2.yml +42 -0
- data/test/outputs/switch_3.txt +14 -0
- data/test/outputs/switch_3.yml +46 -0
- data/test/outputs/switchstatusshow_1.txt +21 -0
- data/test/outputs/switchstatusshow_1.yml +19 -0
- data/test/outputs/trunkshow_1.txt +8 -0
- data/test/outputs/trunkshow_1.yml +44 -0
- data/test/outputs/trunkshow_2.txt +2 -0
- data/test/outputs/trunkshow_2.yml +2 -0
- data/test/outputs/version_1.txt +6 -0
- data/test/outputs/version_1.yml +8 -0
- data/test/outputs/vf_switch_1.txt +25 -0
- data/test/outputs/vf_switch_1.yml +73 -0
- data/test/provisioning_test.rb +1043 -0
- data/test/switch_test.rb +476 -0
- data/test/wwn_test.rb +41 -0
- data/test/zone_configuration_test.rb +65 -0
- data/test/zone_test.rb +73 -0
- metadata +170 -0
@@ -0,0 +1,894 @@
|
|
1
|
+
# Brocade namespace
|
2
|
+
module Brocade
|
3
|
+
# SAN namespace
|
4
|
+
module SAN
|
5
|
+
|
6
|
+
# Provisioning namespace
|
7
|
+
module Provisioning
|
8
|
+
# holds replies strings as constants
|
9
|
+
module Replies
|
10
|
+
# from cfgtransshow
|
11
|
+
NO_ZONING_TRANSACTION = "There is no outstanding zoning transaction"
|
12
|
+
# from cfgtransabort
|
13
|
+
NO_TRANSACTION = "There is no outstanding transaction"
|
14
|
+
OUTSTANDING_TRANSACTION = "trans_abort: there is an outstanding transaction"
|
15
|
+
OPERATION_CANCELLED = "Operation cancelled"
|
16
|
+
NO_CHANGE="Nothing changed: nothing to save, returning"
|
17
|
+
FLASH_UPDT="Updating flash"
|
18
|
+
DOES_NOT_EXIST="does not exist"
|
19
|
+
RBAC_DENIED="RBAC permission denied"
|
20
|
+
CURRENT_TRANSACTION = "Current transaction token is"
|
21
|
+
|
22
|
+
end
|
23
|
+
# Agent class, used for provisioning tasks
|
24
|
+
#
|
25
|
+
# Under development - do not use
|
26
|
+
#
|
27
|
+
# TODO: need to properly test it live (partialy done)
|
28
|
+
|
29
|
+
class Agent < Brocade::SAN::Switch
|
30
|
+
|
31
|
+
# Creates a Brocade::SAN::Provisioning::Agent instance, tests a connection and test if user has enough rights for provisioning.
|
32
|
+
# Raises Error otherwise
|
33
|
+
#
|
34
|
+
# Checks as well if the switch is virtual fabric enabled since that defines the way it will be queried further.
|
35
|
+
def self.create(*params)
|
36
|
+
agent=new(*params)
|
37
|
+
end
|
38
|
+
|
39
|
+
# Queries the agent for ongoing transaction.
|
40
|
+
#
|
41
|
+
# Retruns Transaction instance or false if there is no transaction.
|
42
|
+
#
|
43
|
+
# Raises Error when transaction details could not be obtained
|
44
|
+
def get_transaction
|
45
|
+
trans=Transaction.new(cfg_transaction(true))
|
46
|
+
return false if trans.id==-1
|
47
|
+
raise Error.new(Error::TRANS_UNEXPECTED) if trans.id.nil?
|
48
|
+
trans
|
49
|
+
end
|
50
|
+
|
51
|
+
# Opens a provisioning transaction.
|
52
|
+
#
|
53
|
+
# Transaction runs in 1 session.
|
54
|
+
#
|
55
|
+
# Command allows transaction within transaction.
|
56
|
+
#
|
57
|
+
# cfg_save will be run at the end of the transaction is there was no error raised and this is the top-most transaction block.
|
58
|
+
#
|
59
|
+
# cfg_save wil not run as result of the transaction, you can however run it as last command in transaction
|
60
|
+
|
61
|
+
def transaction(opts={:auto_enable => false})
|
62
|
+
@transaction_level||=0
|
63
|
+
@transaction_level+=1
|
64
|
+
session do
|
65
|
+
raise_if_transaction_running
|
66
|
+
@transaction ||= true
|
67
|
+
raise Error.cannot_obtain_transaction_lock if not lock_transaction
|
68
|
+
begin
|
69
|
+
yield
|
70
|
+
# get_transaction in case cfgsave or cfgenable was run in transaction block
|
71
|
+
# if there is no transaction we do not need to run it
|
72
|
+
# if there is transaction but opend by someone else then t
|
73
|
+
cfg_save if @transaction_level==1 && get_transaction
|
74
|
+
rescue => e
|
75
|
+
abort_transaction
|
76
|
+
raise e
|
77
|
+
end
|
78
|
+
end
|
79
|
+
ensure
|
80
|
+
@transaction_level-=1
|
81
|
+
@transaction = nil if @transaction_level==0
|
82
|
+
end
|
83
|
+
|
84
|
+
# Creates alias and saves config
|
85
|
+
#
|
86
|
+
# If started outside transaction block it runs as single command transaction, otherwise it is not commited when the command returns
|
87
|
+
#
|
88
|
+
# +al+ must be of class Alias. It will be created as it is with all the members of +al+ instance
|
89
|
+
#
|
90
|
+
# raises error if alias exists or different transaction is running or response is unexpected
|
91
|
+
#
|
92
|
+
# Returns Alias
|
93
|
+
|
94
|
+
def alias_create(al)
|
95
|
+
obj_create al, Alias
|
96
|
+
end
|
97
|
+
|
98
|
+
# Creates +zone+ and saves config.
|
99
|
+
#
|
100
|
+
# If started outside transaction block it runs as single command transaction, otherwise it is not commited when the command returns.
|
101
|
+
#
|
102
|
+
# +zone+ must be of class Zone. It will be created as it is with all the +zone+ members.
|
103
|
+
#
|
104
|
+
# +members+ must exist. Exaception is members matching WWN and D,P notation.
|
105
|
+
#
|
106
|
+
# Raises error if +zone+ exists already or different transaction is running or response is unexpected.
|
107
|
+
#
|
108
|
+
# Returns Zone
|
109
|
+
|
110
|
+
def zone_create(zone)
|
111
|
+
obj_create zone, Zone
|
112
|
+
end
|
113
|
+
|
114
|
+
# Creates zone configuration and saves fabric configuration.
|
115
|
+
#
|
116
|
+
# If started outside transaction block it runs as single command transaction, otherwise it is not commited when the command returns.
|
117
|
+
#
|
118
|
+
# +cfg+ must be of class ZoneConfiguration. It will be created as it is with all the members of +cfg+.
|
119
|
+
#
|
120
|
+
# Members must exist on the switch as zones.
|
121
|
+
#
|
122
|
+
# Raises error if +cfg already exists or some of the members do not exist or different transaction is running or response is unexpected.
|
123
|
+
#
|
124
|
+
# Returns ZoneConfiguration
|
125
|
+
|
126
|
+
def cfg_create(cfg)
|
127
|
+
obj_create cfg, ZoneConfiguration
|
128
|
+
end
|
129
|
+
|
130
|
+
# Deletes alias and saves config
|
131
|
+
#
|
132
|
+
# NOTE: This command checks if every member exists before creating the cfg, if the cfg has many members it will take lot of time, however cfg creation is on daily task.
|
133
|
+
#
|
134
|
+
# If started outside transaction block it runs as single command transaction, otherwise it is not commited when the command returns
|
135
|
+
#
|
136
|
+
# +al+ must be of class Alias.
|
137
|
+
#
|
138
|
+
# raises error if alias does not exist or transaction is running or response is unexpected
|
139
|
+
#
|
140
|
+
# this is low level command and it only removes the alias
|
141
|
+
#
|
142
|
+
# it will not remove alias reference from zones, see Agent::#alias_purge that removes all
|
143
|
+
#
|
144
|
+
# Returns nil if deletion is successful
|
145
|
+
|
146
|
+
def alias_delete(al)
|
147
|
+
obj_delete al, Alias
|
148
|
+
end
|
149
|
+
|
150
|
+
|
151
|
+
# Removes +zone+ and saves config.
|
152
|
+
#
|
153
|
+
# If started outside transaction block it runs as single command transaction, otherwise it is not commited when the command returns.
|
154
|
+
#
|
155
|
+
# +zone+ must be of class Zone.
|
156
|
+
#
|
157
|
+
# Raises error if +zone+ does not exist or different transaction is running or response is unexpected.
|
158
|
+
#
|
159
|
+
# This is low level command, it will remove the +zone+ record but not zone references from zone configurations, use #zone_purge for this purpose.
|
160
|
+
#
|
161
|
+
# Returns nil if deletion is successful
|
162
|
+
|
163
|
+
def zone_delete(zone)
|
164
|
+
obj_delete zone, Zone
|
165
|
+
end
|
166
|
+
|
167
|
+
# Removes zone configuration and saves fabric configuration.
|
168
|
+
#
|
169
|
+
# If started outside transaction block it runs as single command transaction, otherwise it is not commited when the command returns.
|
170
|
+
#
|
171
|
+
# +cfg+ must be of class ZoneConfiguration. It will delete only zone config, keep the members intact.
|
172
|
+
#
|
173
|
+
# Raises error if zone configuration does not exist or different transaction is running or response is unexpected.
|
174
|
+
#
|
175
|
+
# Returns nil if deletion is successful
|
176
|
+
|
177
|
+
def cfg_delete(cfg)
|
178
|
+
obj_delete cfg, ZoneConfiguration
|
179
|
+
end
|
180
|
+
|
181
|
+
# Changes alias and saves config
|
182
|
+
#
|
183
|
+
# If started outside transaction block it runs as single command transaction, otherwise it is not commited when the command returns
|
184
|
+
#
|
185
|
+
# +al+ must be of class Alias. alias with the Alias#name will be removed and it will be created anew as it is with all the members defined in +al+ instance
|
186
|
+
#
|
187
|
+
# raises error if alias does not exist or different transaction is running or response is unexpected
|
188
|
+
#
|
189
|
+
# this is shorthand method that instead of modifiyng the alias removes the alias and recreates it
|
190
|
+
#
|
191
|
+
# use alias_remove and alias_add if the above is not an option
|
192
|
+
#
|
193
|
+
# Returns Alias
|
194
|
+
|
195
|
+
def alias_change(al)
|
196
|
+
obj_change al, Alias
|
197
|
+
end
|
198
|
+
|
199
|
+
|
200
|
+
# Changes +zone+ and saves config.
|
201
|
+
#
|
202
|
+
# If started outside transaction block it runs as single command transaction, otherwise it is not commited when the command returns.
|
203
|
+
#
|
204
|
+
# +zone+ must be of class Zone. +zone+ with the name will be removed and it will be created anew as it is with all the +zone+ members.
|
205
|
+
#
|
206
|
+
# Raises error if +member+ does not exist (WWN and D,P members are exception) or different transaction is running or response is unexpected.
|
207
|
+
#
|
208
|
+
# This is shorthand method that instead of modifiyng the zone removes the zone and then recreates it.
|
209
|
+
#
|
210
|
+
# Returns Zone
|
211
|
+
|
212
|
+
def zone_change(zone)
|
213
|
+
obj_change zone, Zone
|
214
|
+
end
|
215
|
+
|
216
|
+
# Removes member from alias +al+
|
217
|
+
#
|
218
|
+
# If started outside transaction block it runs as single command transaction, otherwise it is not commited when the command returns
|
219
|
+
#
|
220
|
+
# +al+ must be instance of Alias and +member+ must minstance of Wwn or String matching Alias::MEMBER_RULE
|
221
|
+
#
|
222
|
+
# raises error if alias does not exist, +member+ does not match the rule or different transaction is running or response is unexpected
|
223
|
+
#
|
224
|
+
# Returns Alias or nil if the removed member was last one (it removes the Alias as well)
|
225
|
+
|
226
|
+
def alias_remove(al,member)
|
227
|
+
obj_remove(al,Alias,member)
|
228
|
+
end
|
229
|
+
|
230
|
+
# Remove +member+ from +zone+.
|
231
|
+
#
|
232
|
+
# If started outside transaction block it runs as single command transaction, otherwise it is not commited when the command returns.
|
233
|
+
#
|
234
|
+
# +zone+ must be of class Zone and +member+ of class Alias or Wwn or String matching Alias::MEMBER_RULE
|
235
|
+
#
|
236
|
+
# Raises error if zone does not exist or different transaction is running or response is unexpected.
|
237
|
+
#
|
238
|
+
# Returns Zone or nil if the removed member was last one (it removes the Zone as well)
|
239
|
+
|
240
|
+
def zone_remove(zone,member)
|
241
|
+
obj_remove(zone,Zone,member)
|
242
|
+
end
|
243
|
+
|
244
|
+
# Remove zone +member+ from configuration +cfg+.
|
245
|
+
#
|
246
|
+
# If started outside transaction block it runs as single command transaction, otherwise it is not commited when the command returns.
|
247
|
+
#
|
248
|
+
# +member+ must be of class Zone and +cfg+ of class ZoneConfiguration.
|
249
|
+
#
|
250
|
+
# Raises error if +cfg+ does not exist or different transaction is running or response is unexpected.
|
251
|
+
#
|
252
|
+
# Returns ZoneConfiguration or nil if the removed member was last one (it removes the ZoneConfiguration as well)
|
253
|
+
|
254
|
+
def cfg_remove(cfg,member)
|
255
|
+
obj_remove(cfg,ZoneConfiguration,member)
|
256
|
+
end
|
257
|
+
|
258
|
+
# Purges the Zone +zone+ completely, along with all references.
|
259
|
+
#
|
260
|
+
# +zone+ must me instance of Zone.
|
261
|
+
# The method first removes the zone from all zone configurations it is member of.
|
262
|
+
# Then deletes the zone and saves configuration.
|
263
|
+
#
|
264
|
+
# Should return nil if the +zone+ was purged.
|
265
|
+
|
266
|
+
def zone_purge(zone)
|
267
|
+
obj_purge(zone,Zone)
|
268
|
+
end
|
269
|
+
|
270
|
+
# Purges the Alias +al+ completely, along with all references.
|
271
|
+
#
|
272
|
+
# +al+ must be instance of Alias.
|
273
|
+
#
|
274
|
+
# The method first removes the alias from all zones it is member of.
|
275
|
+
# Then deletes the alias and saves configuration.
|
276
|
+
#
|
277
|
+
# Should return nil if the +al+ was purged.
|
278
|
+
def alias_purge(al)
|
279
|
+
obj_purge(al,Alias)
|
280
|
+
end
|
281
|
+
|
282
|
+
# Adds zone cofiguration member to zone configuration.
|
283
|
+
#
|
284
|
+
# If started outside transaction block it runs as single command transaction, otherwise it is not commited when the command returns.
|
285
|
+
#
|
286
|
+
# +member+ must be instance of Zone and +cfg+ instance of class ZoneConfiguration.
|
287
|
+
#
|
288
|
+
# Zone and zone configuration must both exist.
|
289
|
+
#
|
290
|
+
# Raises error if +cfg+ or +member+ do not exist or different transaction is running or response is unexpected.
|
291
|
+
#
|
292
|
+
# Returns ZoneConfiguration
|
293
|
+
|
294
|
+
def cfg_add(cfg,member)
|
295
|
+
obj_add(cfg,ZoneConfiguration,member)
|
296
|
+
end
|
297
|
+
|
298
|
+
# Adds zone +member+ to +zone+.
|
299
|
+
#
|
300
|
+
# If started outside transaction block it runs as single command transaction, otherwise it is not commited when the command returns
|
301
|
+
#
|
302
|
+
# +member+ must be of class Alias or Wwn or Strign matching Alias::MEMBER_RULE and +zone+ of class Zone.
|
303
|
+
#
|
304
|
+
# Zone and member must exist. Exception is if +member+ is Wwn.
|
305
|
+
#
|
306
|
+
# Raises error if zone or member does not exist or transaction is running or response is unexpected.
|
307
|
+
#
|
308
|
+
# Returns Zone
|
309
|
+
|
310
|
+
def zone_add(zone,member)
|
311
|
+
obj_add(zone,Zone,member)
|
312
|
+
end
|
313
|
+
|
314
|
+
# Adds alias member to alias
|
315
|
+
#
|
316
|
+
# If started outside transaction block it runs as single command transaction, otherwise it is not commited when the command returns
|
317
|
+
#
|
318
|
+
# +member+ must be instance of Wwn or String matching Alias::MEMBER_RULE (WWN or D,P)
|
319
|
+
#
|
320
|
+
# +al+ must be Alias and must exist in switch configuration, the +member+ does not need to, it has only to match the rule
|
321
|
+
#
|
322
|
+
# raises error if +al+ does not exist, member does not match the rule, dirrefent transaction is running or response is unexpected
|
323
|
+
#
|
324
|
+
# Returns Alias
|
325
|
+
|
326
|
+
def alias_add(al,member)
|
327
|
+
obj_add(al,Alias,member)
|
328
|
+
end
|
329
|
+
|
330
|
+
# Renames the +object+ to +newname+
|
331
|
+
#
|
332
|
+
# +object+ and +newname can be instance of Alias, Zone, ZoneConfiguration or String
|
333
|
+
#
|
334
|
+
# Returns renamed object or raises error
|
335
|
+
def rename_object(object,newname)
|
336
|
+
session do
|
337
|
+
response = script_mode do
|
338
|
+
query("zoneobjectrename \"#{object}\", \"#{newname}\"")
|
339
|
+
end
|
340
|
+
|
341
|
+
validate_and_save(response)
|
342
|
+
pull newname.to_s, :object => get_obj_type(object)
|
343
|
+
end
|
344
|
+
end
|
345
|
+
|
346
|
+
# Enables zoning configuration +cfg+.
|
347
|
+
#
|
348
|
+
# +cfg+ must be ZoneConfiguration instance, will only use it's name and ignore its members.
|
349
|
+
#
|
350
|
+
# Raises erros if cancelled, nothing saved or unexpected result, otherwise returns true.
|
351
|
+
def cfg_enable(cfg)
|
352
|
+
raise Agent::Error.new(Agent::Error::CFG_BAD) if !cfg.kind_of? ZoneConfiguration
|
353
|
+
|
354
|
+
response = interactive_mode do
|
355
|
+
query("cfgenable \"#{cfg}\"","y")
|
356
|
+
end
|
357
|
+
|
358
|
+
case
|
359
|
+
when response.data.match(/#{Replies::OPERATION_CANCELLED}/)
|
360
|
+
raise Agent::Error.new(Agent::Error::CFGSAVE_CANC)
|
361
|
+
when response.data.match(/#{Replies::FLASH_UPDT}/)
|
362
|
+
# if any transaction was in progres it is closed now
|
363
|
+
@transaction=nil
|
364
|
+
return true
|
365
|
+
else
|
366
|
+
raise Agent::Error.new(response.data)
|
367
|
+
end
|
368
|
+
end
|
369
|
+
|
370
|
+
# Check if there is different zoning transaction in progress.
|
371
|
+
#
|
372
|
+
# Returns +true+ or +false+.
|
373
|
+
#
|
374
|
+
# if started in within transactino block it will simply return false.
|
375
|
+
def check_for_running_transaction
|
376
|
+
# ignore this command when within transaction block
|
377
|
+
# as this command is started at the begining of transaction
|
378
|
+
return false if @transaction
|
379
|
+
response = script_mode do
|
380
|
+
query("cfgtransshow")
|
381
|
+
end
|
382
|
+
response.data.match(/#{Replies::NO_ZONING_TRANSACTION}/) ? false : true
|
383
|
+
end
|
384
|
+
|
385
|
+
# Aborts transaction
|
386
|
+
|
387
|
+
# returns
|
388
|
+
# +true+ if transaction was aborted and
|
389
|
+
# +false+ if there was no transaction to abort and
|
390
|
+
# raises error if it is not owner of the transaction
|
391
|
+
def abort_transaction
|
392
|
+
response = script_mode do
|
393
|
+
query("cfgtransabort")
|
394
|
+
end
|
395
|
+
#empty response is ok
|
396
|
+
case
|
397
|
+
when response.data.split("\n").size==1
|
398
|
+
return true
|
399
|
+
when response.data.match(/#{Replies::NO_TRANSACTION}/)
|
400
|
+
return false
|
401
|
+
when response.data.match(/#{Replies::OUTSTANDING_TRANSACTION}/)
|
402
|
+
raise Agent::Error.new(Agent::Error::TRANS_NOTOWNER)
|
403
|
+
else
|
404
|
+
error = response.data.split("\n").delete_if {|item| item.match(/^#{@prompt}/)}.join("\n")
|
405
|
+
raise Agent::Error.new(error)
|
406
|
+
end
|
407
|
+
end
|
408
|
+
|
409
|
+
# Checks if object exists in fabric. Finds even objects not stored in memory but as well object created in open transaction.
|
410
|
+
#
|
411
|
+
# +str+ is name of the object
|
412
|
+
#
|
413
|
+
# +opts+
|
414
|
+
# :object => :zone, :alias, :cfg
|
415
|
+
#
|
416
|
+
# if no :object is specified :zone is used by default
|
417
|
+
def exist?(str,opts={})
|
418
|
+
obj = !opts[:object].nil? && [:zone,:alias,:cfg].include?(opts[:object]) ? opts[:object] : :zone
|
419
|
+
|
420
|
+
response = script_mode do
|
421
|
+
query("#{show_cmd(obj)} \"#{str}\"")
|
422
|
+
end
|
423
|
+
return response.data.match(/#{Replies::DOES_NOT_EXIST}/) ? false : true
|
424
|
+
end
|
425
|
+
|
426
|
+
# Pulls object matching +str+ from switch configuration, even objects not yet saved
|
427
|
+
#
|
428
|
+
# Returns the object or nil
|
429
|
+
#
|
430
|
+
# options
|
431
|
+
# [:object] :zone (default)
|
432
|
+
# :alias
|
433
|
+
# :cfg
|
434
|
+
# :all
|
435
|
+
def pull(str,opts={})
|
436
|
+
obj = !opts[:object].nil? && [:zone,:alias,:cfg, :all].include?(opts[:object]) ? opts[:object] : :zone
|
437
|
+
|
438
|
+
response=nil
|
439
|
+
|
440
|
+
session do
|
441
|
+
if obj == :all
|
442
|
+
[:cfg, :zone, :alias].each do |type|
|
443
|
+
obj = type
|
444
|
+
response = script_mode do
|
445
|
+
query("#{show_cmd(type)} \"#{str}\"")
|
446
|
+
end
|
447
|
+
response.parse
|
448
|
+
break if !response.parsed[:base].nil?
|
449
|
+
end
|
450
|
+
else
|
451
|
+
response = script_mode do
|
452
|
+
query("#{show_cmd(obj)} \"#{str}\"")
|
453
|
+
end
|
454
|
+
response.parse
|
455
|
+
end
|
456
|
+
end
|
457
|
+
return nil if response.parsed[:base].nil?
|
458
|
+
|
459
|
+
object = response.parsed[:base][obj] #hash with single key
|
460
|
+
name = object.keys[0]
|
461
|
+
case obj
|
462
|
+
when :zone
|
463
|
+
item = Zone.new(name)
|
464
|
+
when :alias
|
465
|
+
item = Alias.new(name)
|
466
|
+
when :cfg
|
467
|
+
item = ZoneConfiguration.new(name)
|
468
|
+
end
|
469
|
+
object[name].each do |member|
|
470
|
+
item.add_member(member)
|
471
|
+
end
|
472
|
+
|
473
|
+
return response.data.match(/#{Replies::DOES_NOT_EXIST}/) ? nil : item
|
474
|
+
end
|
475
|
+
|
476
|
+
private_class_method :new
|
477
|
+
|
478
|
+
private
|
479
|
+
|
480
|
+
def initialize(*params) #:nodoc:
|
481
|
+
super(*params)
|
482
|
+
raise Error.new(Error::BAD_USER) if not verify
|
483
|
+
end
|
484
|
+
|
485
|
+
def show_cmd(obj_sym)
|
486
|
+
cmd = case obj_sym
|
487
|
+
when :zone
|
488
|
+
"zoneshow"
|
489
|
+
when :alias
|
490
|
+
"alishow"
|
491
|
+
when :cfg
|
492
|
+
"cfgshow"
|
493
|
+
end
|
494
|
+
end
|
495
|
+
# verifies if the specified user can change zoning configuration
|
496
|
+
#
|
497
|
+
# returns true if yes, false if no
|
498
|
+
def verify
|
499
|
+
response = script_mode do
|
500
|
+
query("configshow | grep RBAC")
|
501
|
+
end
|
502
|
+
response.data.match(/#{Replies::RBAC_DENIED}/) ? false : true
|
503
|
+
end
|
504
|
+
|
505
|
+
# saves if the response if empty and if so it save config, otherwise raises error
|
506
|
+
def validate_and_save(response)
|
507
|
+
#true response is ok
|
508
|
+
if response.data.split("\n").size==1
|
509
|
+
# will not save if part of transaction
|
510
|
+
cfg_save if @transaction.nil?
|
511
|
+
else
|
512
|
+
error = response.data.split("\n").delete_if {|item| item.match(/^#{@prompt}/)}.join("\n")
|
513
|
+
raise Agent::Error.new(error)
|
514
|
+
end
|
515
|
+
true
|
516
|
+
end
|
517
|
+
|
518
|
+
# Obtains transaction lock and stores that transaction in @transaction
|
519
|
+
#
|
520
|
+
# Returns +false+ otherwise
|
521
|
+
def lock_transaction
|
522
|
+
begin
|
523
|
+
return true if @transaction.kind_of? Transaction
|
524
|
+
if get_transaction==false
|
525
|
+
lock_alias=Alias.new("brocade_san_lock")
|
526
|
+
lock_alias.add_member("50:00:00:00:00:00:00:00")
|
527
|
+
alias_create lock_alias
|
528
|
+
alias_delete lock_alias
|
529
|
+
@transaction = get_transaction
|
530
|
+
true
|
531
|
+
else
|
532
|
+
false
|
533
|
+
end
|
534
|
+
rescue
|
535
|
+
return false
|
536
|
+
end
|
537
|
+
end
|
538
|
+
|
539
|
+
# Saves config
|
540
|
+
# Raises erros if cancelled, nothing saved or unexpected result, otherwise returns true
|
541
|
+
# this method will be called by adhoc methods or by finishing transaction, should not be called directly
|
542
|
+
# because it does not check for running transaction, it expect the caller to take care of that
|
543
|
+
def cfg_save
|
544
|
+
response = interactive_mode do
|
545
|
+
query("cfgsave","y")
|
546
|
+
end
|
547
|
+
|
548
|
+
case
|
549
|
+
when response.data.match(/#{Replies::OPERATION_CANCELLED}/)
|
550
|
+
raise Agent::Error.new(Agent::Error::CFGSAVE_CANC)
|
551
|
+
when response.data.match(/#{Replies::NO_CHANGE}/)
|
552
|
+
raise Agent::Error.new(Agent::Error::CFGSAVE_NOCHANGE)
|
553
|
+
when response.data.match(/#{Replies::FLASH_UPDT}/)
|
554
|
+
@transaction=nil
|
555
|
+
return true
|
556
|
+
else
|
557
|
+
raise Agent::Error.new(response.data)
|
558
|
+
end
|
559
|
+
end
|
560
|
+
|
561
|
+
# pulls given object from switch
|
562
|
+
def obj_pull(obj,klass)
|
563
|
+
pull obj.to_s, :object=> get_obj_type(obj)
|
564
|
+
end
|
565
|
+
|
566
|
+
# creates the appropriate object
|
567
|
+
def obj_create(obj,klass)
|
568
|
+
obj_manipulate obj,klass do |man|
|
569
|
+
man.in_session do
|
570
|
+
raise_if_members_do_not_exist obj
|
571
|
+
get_response_and_validate_for(obj,klass,"create",nil)
|
572
|
+
end
|
573
|
+
end
|
574
|
+
end
|
575
|
+
|
576
|
+
|
577
|
+
# deletes the appropriate object
|
578
|
+
def obj_delete(obj,klass)
|
579
|
+
obj_manipulate obj,klass do |man|
|
580
|
+
man.in_session do
|
581
|
+
get_response_and_validate_for(obj,klass,"delete",nil)
|
582
|
+
end
|
583
|
+
end
|
584
|
+
end
|
585
|
+
|
586
|
+
# changes the appropriate object
|
587
|
+
def obj_change(obj,klass)
|
588
|
+
#raise_if_obj_is_not obj , klass
|
589
|
+
session do
|
590
|
+
# raise_if_transaction_running
|
591
|
+
cmd = klass.to_s.split("::").last.downcase
|
592
|
+
transaction do
|
593
|
+
self.send("#{cmd}_delete", obj)
|
594
|
+
self.send("#{cmd}_create", obj)
|
595
|
+
end
|
596
|
+
obj_pull obj, klass
|
597
|
+
end
|
598
|
+
end
|
599
|
+
|
600
|
+
# removes member from object
|
601
|
+
def obj_remove(obj,klass,member)
|
602
|
+
obj_manipulate obj,klass do |man|
|
603
|
+
man.checks do
|
604
|
+
raise_if_member_is_not_valid_for klass, member
|
605
|
+
end
|
606
|
+
man.in_session do
|
607
|
+
raise_if_obj_do_not_exist obj
|
608
|
+
get_response_and_validate_for(obj,klass,"remove",member)
|
609
|
+
end
|
610
|
+
end
|
611
|
+
end
|
612
|
+
|
613
|
+
# adds member to object
|
614
|
+
def obj_add(obj,klass,member)
|
615
|
+
obj_manipulate obj,klass do |man|
|
616
|
+
man.checks do
|
617
|
+
raise_if_member_is_not_valid_for klass, member
|
618
|
+
end
|
619
|
+
man.in_session do
|
620
|
+
raise_if_obj_do_not_exist obj
|
621
|
+
raise_if_obj_do_not_exist member
|
622
|
+
get_response_and_validate_for(obj,klass,"add",member)
|
623
|
+
end
|
624
|
+
end
|
625
|
+
end
|
626
|
+
|
627
|
+
|
628
|
+
# purges given object compltely
|
629
|
+
|
630
|
+
def obj_purge(obj,klass)
|
631
|
+
obj_manipulate obj,klass do |man|
|
632
|
+
man.in_session do
|
633
|
+
parents = find_by_member(obj.name, :find_mode => :exact, :transaction => true)
|
634
|
+
transaction do
|
635
|
+
parents.each do |par|
|
636
|
+
obj_remove par, par.class, obj
|
637
|
+
end
|
638
|
+
obj_delete obj, obj.class
|
639
|
+
end
|
640
|
+
end
|
641
|
+
end
|
642
|
+
end
|
643
|
+
|
644
|
+
# raises error if member is not valid for given class
|
645
|
+
|
646
|
+
def raise_if_member_is_not_valid_for(klass,member)
|
647
|
+
case klass.to_s
|
648
|
+
when "Brocade::SAN::ZoneConfiguration"
|
649
|
+
raise Agent::Error.new(Agent::Error::ZONE_BAD) if !member.kind_of? Zone
|
650
|
+
when "Brocade::SAN::Zone"
|
651
|
+
raise Agent::Error.new(Agent::Error::MEMBER_BAD) if !member.kind_of?(Alias) && !member.kind_of?(Wwn) && !member.match(/^#{Alias::MEMBER_RULE}$/)
|
652
|
+
when "Brocade::SAN::Alias"
|
653
|
+
case member.class.to_s
|
654
|
+
when "Brocade::SAN::Wwn"
|
655
|
+
when "String"
|
656
|
+
raise Agent::Error.new(Agent::Error::ALIAS_MEMBER_BAD) if !member.match(/^#{Alias::MEMBER_RULE}$/)
|
657
|
+
else
|
658
|
+
raise Agent::Error.new(Agent::Error::ALIAS_MEMBER_BAD)
|
659
|
+
end
|
660
|
+
end
|
661
|
+
end
|
662
|
+
|
663
|
+
# raises proper error if obj is not of class klass
|
664
|
+
def raise_if_obj_is_not(obj,klass)
|
665
|
+
case klass.to_s
|
666
|
+
when "Brocade::SAN::ZoneConfiguration"
|
667
|
+
raise Agent::Error.new(Agent::Error::CFG_BAD) if !obj.kind_of? klass
|
668
|
+
when "Brocade::SAN::Zone"
|
669
|
+
raise Agent::Error.new(Agent::Error::ZONE_BAD) if !obj.kind_of? klass
|
670
|
+
when "Brocade::SAN::Alias"
|
671
|
+
raise Agent::Error.new(Agent::Error::ALIAS_BAD) if !obj.kind_of? klass
|
672
|
+
end
|
673
|
+
end
|
674
|
+
|
675
|
+
# raises error if another transcation is already running
|
676
|
+
def raise_if_transaction_running
|
677
|
+
raise Agent::Error.new(Agent::Error::TRNS_IPRG) if check_for_running_transaction
|
678
|
+
end
|
679
|
+
|
680
|
+
# raise error if object does not actually exist in configuration
|
681
|
+
def raise_if_obj_do_not_exist(obj)
|
682
|
+
klass = obj.class.to_s
|
683
|
+
case klass
|
684
|
+
when "Brocade::SAN::ZoneConfiguration"
|
685
|
+
raise Agent::Error.does_not_exist("Config #{obj.name}") if !exist?(obj.name,:object => :cfg)
|
686
|
+
when "Brocade::SAN::Zone"
|
687
|
+
raise Agent::Error.does_not_exist("Zone #{obj.name}") if !exist?(obj.name,:object => :zone)
|
688
|
+
when "Brocade::SAN::Alias"
|
689
|
+
raise Agent::Error.does_not_exist("Alias #{obj.name}") if !exist?(obj.name,:object => :alias)
|
690
|
+
when "String" # Wwn and D,P notation for zone and alias members
|
691
|
+
raise Agent::Error.new(Agent::Error::MEMBER_BAD_2) if !obj.match(/^#{Alias::MEMBER_RULE}$/)
|
692
|
+
end
|
693
|
+
end
|
694
|
+
|
695
|
+
# raise error if members of obj do not actually exists in configuration
|
696
|
+
def raise_if_members_do_not_exist(obj)
|
697
|
+
klass = obj.class.to_s
|
698
|
+
raise Agent::Error.members_empty if obj.members.empty?
|
699
|
+
# pull all zones
|
700
|
+
if klass == "Brocade::SAN::ZoneConfiguration"
|
701
|
+
zones(true)
|
702
|
+
end
|
703
|
+
obj.members.each do |member|
|
704
|
+
case klass
|
705
|
+
when "Brocade::SAN::ZoneConfiguration"
|
706
|
+
# exist? is slow for for big configurations
|
707
|
+
#raise Agent::Error.does_not_exist("Zone #{member}") if !exist?(member,:object => :zone)
|
708
|
+
raise Agent::Error.does_not_exist("Zone #{member}") if zones.select {|z|z.name==member}.empty?
|
709
|
+
when "Brocade::SAN::Zone"
|
710
|
+
# no need to check if WWNs exist
|
711
|
+
next if member.match(/#{Alias::MEMBER_RULE}/i)
|
712
|
+
# check if other members => only aliases exist
|
713
|
+
raise Agent::Error.does_not_exist("Alias #{member}") if !exist?(member,:object => :alias)
|
714
|
+
# no checks for Alias required - the members do not need to exists for Alias
|
715
|
+
end
|
716
|
+
end
|
717
|
+
end
|
718
|
+
|
719
|
+
#return proper cmd to use
|
720
|
+
def get_cmd(klass,task)
|
721
|
+
cmd = case klass.to_s
|
722
|
+
when "Brocade::SAN::ZoneConfiguration"
|
723
|
+
"cfg#{task}"
|
724
|
+
when "Brocade::SAN::Zone"
|
725
|
+
"zone#{task}"
|
726
|
+
when "Brocade::SAN::Alias"
|
727
|
+
"ali#{task}"
|
728
|
+
end
|
729
|
+
end
|
730
|
+
|
731
|
+
#return proper cmd to use
|
732
|
+
def get_obj_type(obj)
|
733
|
+
type = case obj.class.to_s
|
734
|
+
when "Brocade::SAN::ZoneConfiguration"
|
735
|
+
:cfg
|
736
|
+
when "Brocade::SAN::Zone"
|
737
|
+
:zone
|
738
|
+
when "Brocade::SAN::Alias"
|
739
|
+
:alias
|
740
|
+
when "String"
|
741
|
+
:all
|
742
|
+
end
|
743
|
+
end
|
744
|
+
|
745
|
+
#return proper member part of provisioning query command for given word, obj and member
|
746
|
+
def get_member_part(obj, word, member=nil)
|
747
|
+
case word
|
748
|
+
when "delete"
|
749
|
+
""
|
750
|
+
when "create"
|
751
|
+
", \"#{obj.members.join(";")}\""
|
752
|
+
when "remove","add"
|
753
|
+
", \"#{member}\""
|
754
|
+
else
|
755
|
+
""
|
756
|
+
end
|
757
|
+
end
|
758
|
+
|
759
|
+
# manipulates object
|
760
|
+
# this is private method that is for use only in another private methods
|
761
|
+
# follows this template for given obj,klass and member arguments
|
762
|
+
# raise_if_obj_is_not obj , klass
|
763
|
+
# checks
|
764
|
+
# session do
|
765
|
+
# raise_if_transaction_running
|
766
|
+
# in_sesssion
|
767
|
+
# end
|
768
|
+
# the checks and in_session blocks can be defined on the manipulator object yielded to the block
|
769
|
+
# Example:
|
770
|
+
# obj_manipulate obj,klass,member do |man|
|
771
|
+
# man.checks do
|
772
|
+
# #puts checks here
|
773
|
+
# end
|
774
|
+
# man.in_session do
|
775
|
+
# #puts session block here
|
776
|
+
# end
|
777
|
+
# end
|
778
|
+
|
779
|
+
def obj_manipulate(*args,&block)
|
780
|
+
obj,klass,member = args
|
781
|
+
manipulator = Brocade::SAN::Provisioning::ObjectManipulator.new
|
782
|
+
# block can define checks and in_session handlers
|
783
|
+
yield manipulator
|
784
|
+
# standard template follows
|
785
|
+
# check if class is proper
|
786
|
+
raise_if_obj_is_not obj , klass
|
787
|
+
# run run check block - should contain checks not requiring session -> switch access
|
788
|
+
manipulator.run_checks
|
789
|
+
# starting session
|
790
|
+
session do
|
791
|
+
# standard transaction check
|
792
|
+
raise_if_transaction_running
|
793
|
+
# run block defined in in_session block
|
794
|
+
manipulator.run_session
|
795
|
+
# returns the fresh object
|
796
|
+
obj_pull(obj,klass)
|
797
|
+
end
|
798
|
+
end
|
799
|
+
|
800
|
+
# gets response and validates
|
801
|
+
def get_response_and_validate_for(obj,klass,word,member=nil)
|
802
|
+
# gets proper command for given word and klass
|
803
|
+
cmd_part = get_cmd klass, word
|
804
|
+
object_member_part = get_member_part obj, word, member
|
805
|
+
response = script_mode do
|
806
|
+
query("#{cmd_part} \"#{obj}\"#{object_member_part}")
|
807
|
+
end
|
808
|
+
validate_and_save(response)
|
809
|
+
end
|
810
|
+
|
811
|
+
end
|
812
|
+
|
813
|
+
class Agent
|
814
|
+
# Class that holds Agent::Error messages
|
815
|
+
class Error < self::Error
|
816
|
+
BAD_USER = "User has insufficient rights to do provisioning"
|
817
|
+
TRNS_IPRG = "Another zoning transaction is already in progress"
|
818
|
+
ALIAS_BAD = "Parameter should be of Alias class"
|
819
|
+
MEMBER_BAD = "Parameter should be of Alias or Wwn class"
|
820
|
+
MEMBER_BAD_2 = "Parameter should be of Wwn or D,P notation"
|
821
|
+
ZONE_BAD = "Parameter should be of Zone class"
|
822
|
+
CFG_BAD = "Parameter should be of ZoneConfiguration class"
|
823
|
+
CFGSAVE_CANC = "cfgsave was cancelled"
|
824
|
+
CFGSAVE_NOCHANGE = "cfgsave: nothing changed, nothing to save"
|
825
|
+
TRANS_NOTOWNER = "Cannot abort transaction you are not owner of"
|
826
|
+
OBJ_NOTEXIST = "does not exist"
|
827
|
+
MEMBERS_EMPTY = "Cannot create. Object has no members"
|
828
|
+
ALIAS_MEMBER_BAD = "Not correct alias member"
|
829
|
+
TRANS_UNEXPECTED="Cannot get transaction details"
|
830
|
+
TRANS_UNLOCKABLE="Cannot get transaction lock"
|
831
|
+
|
832
|
+
# used to raise modifyable "does not exist message"
|
833
|
+
def self.does_not_exist(str) #:nodoc:
|
834
|
+
new("#{str} #{OBJ_NOTEXIST}")
|
835
|
+
end
|
836
|
+
|
837
|
+
def self.members_empty #:nodoc:
|
838
|
+
new(MEMBERS_EMPTY)
|
839
|
+
end
|
840
|
+
|
841
|
+
def self.cannot_obtain_transaction_lock
|
842
|
+
new(TRANS_UNLOCKABLE)
|
843
|
+
end
|
844
|
+
end
|
845
|
+
|
846
|
+
# simple Transaction class that processes Switch#cfg_transaction output
|
847
|
+
#
|
848
|
+
# Value of +id+ means:
|
849
|
+
# [:id] -1 - no transaction exist
|
850
|
+
#
|
851
|
+
# nil - unexpected result
|
852
|
+
#
|
853
|
+
# string - string representation of current transaction id
|
854
|
+
|
855
|
+
class Transaction
|
856
|
+
attr_writer :abortable
|
857
|
+
# Specifies the id of Transaction
|
858
|
+
attr_accessor :id
|
859
|
+
|
860
|
+
# takes hash returned by cfg_transaction command
|
861
|
+
def initialize(opts={}) #:nodoc:
|
862
|
+
# empty hash means unexpected result of cfg_transaction
|
863
|
+
if [:id, :abortable].all? {|k| opts.key? k}
|
864
|
+
@id = opts[:id]
|
865
|
+
@abortable = opts[:abortable]
|
866
|
+
end
|
867
|
+
end
|
868
|
+
|
869
|
+
# Returns abortable flag
|
870
|
+
def abortable?
|
871
|
+
@abortable
|
872
|
+
end
|
873
|
+
end
|
874
|
+
end
|
875
|
+
|
876
|
+
# manipulator object used on #obj_manipulate to insert block of commands into standard obj manipulation template, see the #obj_manipulate
|
877
|
+
class ObjectManipulator #:nodoc:
|
878
|
+
def checks(&block)
|
879
|
+
@checks = block
|
880
|
+
end
|
881
|
+
|
882
|
+
def run_checks
|
883
|
+
@checks.call if @checks
|
884
|
+
end
|
885
|
+
|
886
|
+
def in_session(&block)
|
887
|
+
@in_session = block
|
888
|
+
end
|
889
|
+
|
890
|
+
def run_session
|
891
|
+
@in_session.call if @in_session
|
892
|
+
end
|
893
|
+
end
|
894
|
+
end; end; end
|