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.
- 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
|