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,882 @@
|
|
1
|
+
require 'net/ssh'
|
2
|
+
require 'yaml'
|
3
|
+
|
4
|
+
# Brocade namespace
|
5
|
+
module Brocade
|
6
|
+
# SAN namespace
|
7
|
+
module SAN
|
8
|
+
# TODO: zoneshow, cfgshow and alishow tests
|
9
|
+
# configuration class
|
10
|
+
class Configuration #:nodoc:
|
11
|
+
def self.cmd_mapping_path(_class)
|
12
|
+
File.realpath("../config/#{_class.name.underscore}_cmd_mapping.yml",__FILE__)
|
13
|
+
end
|
14
|
+
|
15
|
+
def self.parser_path
|
16
|
+
File.realpath("../config/parser_mapping.yml",__FILE__)
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
# Class to model SAN switch from Brocade
|
21
|
+
class Switch
|
22
|
+
|
23
|
+
include SshDevice
|
24
|
+
|
25
|
+
# Maps each method name to command to be run to obtain it and to hash key where it ill be stored.
|
26
|
+
# As well sets the format of the returned value. This is used only for documentation purposes.
|
27
|
+
#
|
28
|
+
# See lib/config/brocade/san/switch_cmd_mapping.yml for details
|
29
|
+
#
|
30
|
+
# Example:
|
31
|
+
# :name:
|
32
|
+
# :cmd: switchshow
|
33
|
+
# :attr: switch_name
|
34
|
+
# :format: string
|
35
|
+
#
|
36
|
+
# This will cause that class will have instance method called name(forced=true).
|
37
|
+
# When the method is called first time or +forced+ is true the +switchshow+ command
|
38
|
+
# will be queried on the switch.
|
39
|
+
# The query response will be then parsed and stored in Response +parsed+ hash under +:switch_name+ key.
|
40
|
+
# The +parsed+ hash is then merged into switch +configuration+ hash.
|
41
|
+
# At the end the +:switch_name+ key is returned from +configuration+ hash.
|
42
|
+
#
|
43
|
+
# The parser needs to ensure that it parses the +cmd+ output and stores the proper value into Response +parsed+ under +:attr+ key.
|
44
|
+
|
45
|
+
CMD_MAPPING=YAML.load(File.read(Configuration::cmd_mapping_path(self)))
|
46
|
+
|
47
|
+
# Maps each command to the parser method to use
|
48
|
+
#
|
49
|
+
# See lib/config/parser_mapping.yml for details
|
50
|
+
PARSER_MAPPING=YAML.load(File.read(Configuration::parser_path))
|
51
|
+
|
52
|
+
# zone configuration, zone and zone aliases naming rule
|
53
|
+
# must start with an alphabetic character and may contain
|
54
|
+
# alphanumeric characters and the underscore ( _ ) character.
|
55
|
+
NAME_RULE='^[a-z][a-z_\d]*$'
|
56
|
+
|
57
|
+
# Used to dynamically create named methods based on CMD_MAPPING
|
58
|
+
def self.attributes(args)
|
59
|
+
args.each do |arg|
|
60
|
+
define_method arg do |forced=false|
|
61
|
+
self.get(arg,forced)
|
62
|
+
end
|
63
|
+
end
|
64
|
+
end
|
65
|
+
|
66
|
+
# verifies if name matches convetion defined in NAME_RULE
|
67
|
+
# raises Switch::Error: Incorrect name format if not
|
68
|
+
# this method is used internally mostly
|
69
|
+
|
70
|
+
def self.verify_name(name)
|
71
|
+
raise Switch::Error.incorrect(name) if !name.match(/#{NAME_RULE}/i)
|
72
|
+
end
|
73
|
+
|
74
|
+
# Creates a Switch instance and tests a connection.
|
75
|
+
#
|
76
|
+
# Checks as well if the switch is virtual fabric enabled since that defines the way it will be queried further.
|
77
|
+
def initialize(*params)
|
78
|
+
super(*params)
|
79
|
+
@configuration={}
|
80
|
+
vf
|
81
|
+
end
|
82
|
+
|
83
|
+
# Hash containing parsed attributes
|
84
|
+
#
|
85
|
+
# Can be used to obtain parsed attributes for which there is no named method.
|
86
|
+
# These attributes however has to be obtained as colateral of running another public method.
|
87
|
+
#
|
88
|
+
# Generally this attribute should not be accessed directly but named method for the given attribute should be created.
|
89
|
+
#
|
90
|
+
# Example:
|
91
|
+
#
|
92
|
+
# # this will call switchshow (see CMD_MAPPING)
|
93
|
+
# # and load :several switchow values into configuration as well
|
94
|
+
# # the command however returns only whether the ls_attributtes
|
95
|
+
#
|
96
|
+
# switch.configuration
|
97
|
+
# => nil
|
98
|
+
# switch.ls_attributes
|
99
|
+
# => {:fid=>"128", :base_switch=>"no", :default_switch=>"yes", :address_mode=>"0"}
|
100
|
+
# switch.configuration
|
101
|
+
# => {:vf=>"enabled", :parsing_position=>"end", :switch_name=>"H2C04R065-U03-A01", :switch_type=>"62.3", :switch_state=>"Online", ...
|
102
|
+
# switch.configuration[:switch_name]
|
103
|
+
# => "H2C04R065-U03-A01"
|
104
|
+
#
|
105
|
+
# # this swichname will be taken from cache as the switchshow was started as part of ls_attributes
|
106
|
+
# switch.name
|
107
|
+
# => "H2C04R065-U03-A01"
|
108
|
+
|
109
|
+
|
110
|
+
attr_reader :configuration
|
111
|
+
|
112
|
+
# Fabric id of the switch to be queried. Can be set using +set_context+.
|
113
|
+
#
|
114
|
+
# If the given +fid+ does not exist default switch for the provided
|
115
|
+
# account will be queried.
|
116
|
+
|
117
|
+
attr_reader :fid
|
118
|
+
|
119
|
+
attributes CMD_MAPPING.keys
|
120
|
+
|
121
|
+
# Sets the FID of the switch to be queried and clears cache.
|
122
|
+
# Next queries will be done directly on switch.
|
123
|
+
#
|
124
|
+
# If not +fid+ is given it will set it to default 128.
|
125
|
+
# If the switch does not support virtual fabrics this will be ignored.
|
126
|
+
#
|
127
|
+
# Returns the fid that was set
|
128
|
+
def set_context(fid=128)
|
129
|
+
@loaded={}
|
130
|
+
@fid = fid.to_i==0 ? 128 : fid.to_i
|
131
|
+
end
|
132
|
+
|
133
|
+
# gets the +attr+
|
134
|
+
#
|
135
|
+
# +attr+ has to be speficied in the CMD_MAPPING
|
136
|
+
#
|
137
|
+
# named methods are wrappers around this method so you should not use this directly
|
138
|
+
def get(attr,forced=false)
|
139
|
+
raise Switch::Error.unknown if CMD_MAPPING[attr.to_sym].nil?
|
140
|
+
|
141
|
+
cmd=CMD_MAPPING[attr.to_sym][:cmd]
|
142
|
+
|
143
|
+
refresh(cmd,"",forced)
|
144
|
+
|
145
|
+
@configuration[CMD_MAPPING[attr.to_sym][:attr].to_sym]
|
146
|
+
end
|
147
|
+
|
148
|
+
# returns switches in the fabric in hash form
|
149
|
+
|
150
|
+
def fabric(forced=false)
|
151
|
+
cmd="fabricshow"
|
152
|
+
refresh(cmd,"",forced)
|
153
|
+
@configuration[:fabric]
|
154
|
+
end
|
155
|
+
|
156
|
+
# If called with +true+ argument it will get the virtual_fabric from the switch instead of cache
|
157
|
+
#
|
158
|
+
# Returns value in (string) format
|
159
|
+
|
160
|
+
def vf(forced=false)
|
161
|
+
if !@configuration[:vf] || forced
|
162
|
+
# using this instead of fosconfig --show as that command does not work everywhere
|
163
|
+
# we could user #ls_attributes method but that loaded whole switchshow os this will be a bit faster, especially with big switches
|
164
|
+
# it needs to be faster as vf is called during initialization
|
165
|
+
# if the switch is vf there will be LS Attributes line, otherwise it will be empty
|
166
|
+
response=query("switchshow|grep \"^LS Attributes\"")
|
167
|
+
@configuration[:vf] = response.data.split("\n").size == 2 ? "enabled" : "disabled"
|
168
|
+
end
|
169
|
+
@configuration[:vf]
|
170
|
+
end
|
171
|
+
|
172
|
+
# Returns ZoneConfigurations array
|
173
|
+
#
|
174
|
+
# If +full+ is set to true it loads full configurations with zones and aliases, otherwise it gets just the names.
|
175
|
+
#
|
176
|
+
# It laods even zone configurations that were create as part of ongoing transaction.
|
177
|
+
def zone_configurations(full=false,forced=false)
|
178
|
+
get_configshow(full,forced)[:zone_configurations]
|
179
|
+
end
|
180
|
+
|
181
|
+
# Returns effective ZoneConfiguration
|
182
|
+
#
|
183
|
+
# If +full+ is set to true it loads full effective configuration with zones and aliases, otherwise it gets just the name
|
184
|
+
def effective_configuration(full=false,forced=false)
|
185
|
+
zone_configurations(full,forced).select {|z| z.effective == true}.first
|
186
|
+
end
|
187
|
+
|
188
|
+
# returns all zones defined on the switch including zones created in ongoing transaction as array of Zone
|
189
|
+
|
190
|
+
def zones(forced=false)
|
191
|
+
get_configshow(true,forced)[:zones]
|
192
|
+
end
|
193
|
+
|
194
|
+
# returns all aliases defined on the switch including aliases created in ongoing transaction as array of Alias
|
195
|
+
|
196
|
+
def aliases(forced=false)
|
197
|
+
get_configshow(true,forced)[:aliases]
|
198
|
+
end
|
199
|
+
|
200
|
+
# returns Zone with name of +str+ if exists, +nil+ otherwise
|
201
|
+
def find_zone(str)
|
202
|
+
find(str,:object=>:zone)
|
203
|
+
end
|
204
|
+
|
205
|
+
# returns Zone array of Zones with name matching +regexp+ if exists, [] otherwise
|
206
|
+
#
|
207
|
+
# find is case insesitive
|
208
|
+
def find_zones(regexp)
|
209
|
+
zones = find(regexp,:object=>:zone,:find_mode=>:partial)
|
210
|
+
return [] if zones==[nil]
|
211
|
+
zones
|
212
|
+
end
|
213
|
+
|
214
|
+
# returns Alias with name of +str+ if exists, +nil+ otherwise
|
215
|
+
def find_alias(str)
|
216
|
+
al = find(str,:object=>:alias)
|
217
|
+
end
|
218
|
+
|
219
|
+
# returns Alias array of Aliases with name matching +regexp+ if exists, [] otherwise
|
220
|
+
#
|
221
|
+
# find is case insesitive
|
222
|
+
def find_aliases(regexp)
|
223
|
+
aliases = find(regexp,:object=>:alias,:find_mode=>:partial)
|
224
|
+
return [] if aliases==[nil]
|
225
|
+
aliases
|
226
|
+
end
|
227
|
+
|
228
|
+
# returns all WWNs
|
229
|
+
#
|
230
|
+
# +mode+
|
231
|
+
#
|
232
|
+
# [:local] returns all local WWNs
|
233
|
+
# [:cached] returns wwns cached from remote switches
|
234
|
+
# [:all] returns all local and remote wwns
|
235
|
+
|
236
|
+
def wwns(forced=false,mode=:local)
|
237
|
+
if mode==:local
|
238
|
+
get_ns(true,forced,:local=>true)
|
239
|
+
elsif mode==:cached
|
240
|
+
get_ns(true,forced,:remote=>true)
|
241
|
+
else
|
242
|
+
get_ns(true,forced,:local=>true).concat get_ns(true,forced,:remote=>true)
|
243
|
+
end
|
244
|
+
end
|
245
|
+
|
246
|
+
# returns WWN of given +value+ if exists, +nil+ if not
|
247
|
+
#
|
248
|
+
# if +forced+ is true it will load the data from switch instead of the cache
|
249
|
+
#
|
250
|
+
# +opts+
|
251
|
+
#
|
252
|
+
# [:fabric_wide] searches whole fabric
|
253
|
+
def find_wwn(value,forced=true,opts={:fabric_wide=>false})
|
254
|
+
objs = opts[:fabric_wide]==true ? get_ns(true,forced,:local=>true).concat(get_ns(true,forced,:remote=>true)) : get_ns(true,forced,:local=>true)
|
255
|
+
objs.find {|k| value.downcase == k.value.downcase}
|
256
|
+
end
|
257
|
+
|
258
|
+
# finds configuration object by +str+. Case insensitive.
|
259
|
+
# If the +object+ option is not specified it searches :zone objects. It finds only saved objects. If the object was created but the transaction is not confirmed it will not find it.
|
260
|
+
#
|
261
|
+
# +opts+
|
262
|
+
#
|
263
|
+
# [:object] :zones (default) - finds zones
|
264
|
+
#
|
265
|
+
# :aliases - finds aliases
|
266
|
+
# [:find_mode] :partial - find partial matches
|
267
|
+
#
|
268
|
+
# :exact(default) - finds exact matches
|
269
|
+
#
|
270
|
+
# Example:
|
271
|
+
#
|
272
|
+
# switch.find("zone1",:object=>:zone)
|
273
|
+
def find(str,opts={})
|
274
|
+
# do not change the following 3 lines without writing test for it
|
275
|
+
obj = !opts[:object].nil? && [:zone,:alias].include?(opts[:object]) ? opts[:object] : :zone
|
276
|
+
mode = !opts[:find_mode].nil? && [:partial,:exact].include?(opts[:find_mode]) ? opts[:find_mode] : :exact
|
277
|
+
grep_exp = mode==:exact ? " | grep -i -E ^#{obj}\.#{str}:" : " | grep -i -E ^#{obj}\..*#{str}.*:"
|
278
|
+
|
279
|
+
response = script_mode do
|
280
|
+
query("configshow"+grep_exp)
|
281
|
+
end
|
282
|
+
response.parse
|
283
|
+
|
284
|
+
#output of configshow is stored to find_results
|
285
|
+
|
286
|
+
objs=response.parsed[:find_results]
|
287
|
+
objs||=[]
|
288
|
+
|
289
|
+
result=[]
|
290
|
+
|
291
|
+
objs.each do |item|
|
292
|
+
i = obj==:zone ? Zone.new(item[:obj]) : Alias.new(item[:obj])
|
293
|
+
item[:members].split(";").each do |member|
|
294
|
+
i.add_member(member)
|
295
|
+
end
|
296
|
+
result<<i
|
297
|
+
end
|
298
|
+
# result is array of Zone or Alias instances
|
299
|
+
result
|
300
|
+
end
|
301
|
+
|
302
|
+
# finds configuration objects that have member specified by +str+. Case insensitive.
|
303
|
+
# If the +object+ option is not specified it searches :zone objects.
|
304
|
+
# See find_by_member_from_cfgshow.
|
305
|
+
#
|
306
|
+
# +opts+
|
307
|
+
#
|
308
|
+
# [:object] :all (default) - finds all objects (zones, configs, aliases)
|
309
|
+
#
|
310
|
+
# :aliases - finds aliases
|
311
|
+
#
|
312
|
+
# :zones - finds zones
|
313
|
+
#
|
314
|
+
# [:find_mode] :partial - find partial matches
|
315
|
+
#
|
316
|
+
# :exact(default) - finds exact matches
|
317
|
+
#
|
318
|
+
# [:transaction] false (defualt) - ignores object in ongoing transaction
|
319
|
+
#
|
320
|
+
# true - includes objects in ongoing transaction
|
321
|
+
#
|
322
|
+
# Example:
|
323
|
+
#
|
324
|
+
# switch.find_by_member("zone1",:object=>:cfg)
|
325
|
+
#
|
326
|
+
# Note:
|
327
|
+
#
|
328
|
+
# Zone can be only members of zone configuration and alias can be only members of zones so finding object that have them as members works with default :all :object.
|
329
|
+
# However WWNs can be part both of zones and aliases so that is why there is option to speficy the object
|
330
|
+
|
331
|
+
def find_by_member(str,opts={})
|
332
|
+
obj = !opts[:object].nil? && [:zone,:alias,:all].include?(opts[:object]) ? opts[:object] : :all
|
333
|
+
obj = "zone|alias|cfg" if obj==:all
|
334
|
+
mode = !opts[:find_mode].nil? && [:partial,:exact].include?(opts[:find_mode]) ? opts[:find_mode] : :exact
|
335
|
+
|
336
|
+
trans_inc = !opts[:transaction].nil? && [true,false].include?(opts[:transaction]) ? opts[:transaction] : false
|
337
|
+
|
338
|
+
base_grep1 = "^#{obj}\."
|
339
|
+
base_grep2 = mode==:exact ? "(:|;)#{str}(;|$)" : ":.*#{str}"
|
340
|
+
|
341
|
+
if trans_inc
|
342
|
+
grep_exp1 = /#{base_grep1}/i
|
343
|
+
grep_exp2 = /#{base_grep2}/i
|
344
|
+
response = script_mode do
|
345
|
+
query("cfgshow")
|
346
|
+
end
|
347
|
+
response.send :cfgshow_to_configshow, grep_exp1, grep_exp2
|
348
|
+
else
|
349
|
+
grep_exp = " | grep -i -E \"#{base_grep1}\""
|
350
|
+
grep_exp += " | grep -i -E \"#{base_grep2}\""
|
351
|
+
response = script_mode do
|
352
|
+
query("configshow"+grep_exp)
|
353
|
+
end
|
354
|
+
end
|
355
|
+
|
356
|
+
response.parse
|
357
|
+
#output of configshow is stored to find_results
|
358
|
+
|
359
|
+
objs=response.parsed[:find_results]
|
360
|
+
objs||=[]
|
361
|
+
|
362
|
+
result=[]
|
363
|
+
|
364
|
+
objs.each do |item|
|
365
|
+
i = case item[:type]
|
366
|
+
when :zone
|
367
|
+
Zone.new(item[:obj])
|
368
|
+
when :alias
|
369
|
+
Alias.new(item[:obj])
|
370
|
+
when :cfg
|
371
|
+
ZoneConfiguration.new(item[:obj])
|
372
|
+
end
|
373
|
+
item[:members].split(";").each do |member|
|
374
|
+
i.add_member(member)
|
375
|
+
end
|
376
|
+
result<<i
|
377
|
+
end
|
378
|
+
# result is array of Zone, Alias, and ZoneConfiguration instances
|
379
|
+
result
|
380
|
+
end
|
381
|
+
|
382
|
+
private
|
383
|
+
|
384
|
+
def should_refresh?(cmd, forced)
|
385
|
+
!@loaded || !@loaded[key(cmd)] || forced
|
386
|
+
end
|
387
|
+
|
388
|
+
def get_configshow(full=false,forced=false)
|
389
|
+
cmd="cfgshow"
|
390
|
+
filter = full==false ? "-e cfg: -e configuration:" : ""
|
391
|
+
|
392
|
+
refresh(cmd,filter,forced)
|
393
|
+
|
394
|
+
#storing configs
|
395
|
+
tmp_cfg={}
|
396
|
+
tmp_cfg[:zone_configurations]=[]
|
397
|
+
|
398
|
+
|
399
|
+
# storing defined
|
400
|
+
@configuration[:defined_configuration][:cfg].each do |config,members|
|
401
|
+
|
402
|
+
effective = @configuration[:effective_configuration][:cfg].keys[0]==config ? true : false
|
403
|
+
|
404
|
+
cfg=ZoneConfiguration.new(config,:effective=>effective)
|
405
|
+
members.each do |member|
|
406
|
+
cfg.add_member member
|
407
|
+
end
|
408
|
+
|
409
|
+
tmp_cfg[:zone_configurations]<<cfg
|
410
|
+
end
|
411
|
+
|
412
|
+
if full
|
413
|
+
# storing zones
|
414
|
+
tmp_cfg[:zones]=[]
|
415
|
+
|
416
|
+
if @configuration[:defined_configuration][:zone]
|
417
|
+
# storing defined
|
418
|
+
active_zones = tmp_cfg[:zone_configurations].select {|z| z.effective == true}.first.members
|
419
|
+
@configuration[:defined_configuration][:zone].each do |zone,members|
|
420
|
+
active = active_zones.include?(zone) ? true : false
|
421
|
+
z=Zone.new(zone,:active=>active)
|
422
|
+
members.each do |member|
|
423
|
+
z.add_member member
|
424
|
+
end
|
425
|
+
tmp_cfg[:zones]<<z
|
426
|
+
end
|
427
|
+
end
|
428
|
+
|
429
|
+
|
430
|
+
# storing aliases
|
431
|
+
tmp_cfg[:aliases]=[]
|
432
|
+
|
433
|
+
if @configuration[:defined_configuration][:alias]
|
434
|
+
# storing defined
|
435
|
+
@configuration[:defined_configuration][:alias].each do |al_name,members|
|
436
|
+
al=Alias.new(al_name)
|
437
|
+
members.each do |member|
|
438
|
+
al.add_member member
|
439
|
+
end
|
440
|
+
tmp_cfg[:aliases]<<al
|
441
|
+
end
|
442
|
+
end
|
443
|
+
end
|
444
|
+
|
445
|
+
tmp_cfg
|
446
|
+
end
|
447
|
+
|
448
|
+
def get_ns(full=false,forced=false,opts={:local=>true})
|
449
|
+
cmd = opts[:local] ? "nsshow -t" : "nscamshow -t"
|
450
|
+
key = opts[:local] ? :wwn_local : :wwn_remote
|
451
|
+
filter = "" #full==false ? "-e cfg: -e configuration:" : ""
|
452
|
+
|
453
|
+
refresh(cmd,filter,forced)
|
454
|
+
|
455
|
+
#storing wwns
|
456
|
+
tmp_wwns=[]
|
457
|
+
|
458
|
+
# storing defined
|
459
|
+
@configuration[key].each do |wwn|
|
460
|
+
domain_id=wwn[:domain_id]==0 ? self.domain.to_i : wwn[:domain_id]
|
461
|
+
w=Wwn.new(wwn[:value],wwn[:dev_type],domain_id,wwn[:port_index],:symbol=>wwn[:symbol])
|
462
|
+
tmp_wwns<<w
|
463
|
+
end
|
464
|
+
|
465
|
+
tmp_wwns
|
466
|
+
end
|
467
|
+
|
468
|
+
def refresh(cmd,filter="",forced=true)
|
469
|
+
return @loaded[key(cmd)] if !should_refresh?(cmd+filter,forced)
|
470
|
+
grep_exp=filter.empty? ? "" : " | grep #{filter}"
|
471
|
+
response=script_mode do
|
472
|
+
query(cmd+grep_exp)
|
473
|
+
end
|
474
|
+
response.parse
|
475
|
+
|
476
|
+
#puts response.data
|
477
|
+
|
478
|
+
@configuration||={}
|
479
|
+
@configuration.merge!(response.parsed)
|
480
|
+
|
481
|
+
@loaded||={}
|
482
|
+
# when we use filter we need to mark the cmd as false
|
483
|
+
@loaded[key(cmd)]=false
|
484
|
+
@loaded[key(cmd+filter.to_s)]=true
|
485
|
+
|
486
|
+
return @loaded[key(cmd)]
|
487
|
+
end
|
488
|
+
|
489
|
+
def key(cmd)
|
490
|
+
cmd.gsub(/\s+/,'_').to_sym
|
491
|
+
end
|
492
|
+
|
493
|
+
def fullcmd(cmd)
|
494
|
+
if @configuration[:vf]=="enabled" && @fid
|
495
|
+
cmds = cmd.split("|")
|
496
|
+
if cmds.size>1
|
497
|
+
"fosexec --fid #{@fid} \'#{cmds.shift}\' |#{cmds.join("|")}"
|
498
|
+
else
|
499
|
+
"fosexec --fid #{@fid} \'#{cmds.shift}\'"
|
500
|
+
end
|
501
|
+
else
|
502
|
+
cmd
|
503
|
+
end
|
504
|
+
end
|
505
|
+
end
|
506
|
+
|
507
|
+
class Switch
|
508
|
+
# class extending SshDevice::Response
|
509
|
+
class Response < self::Response
|
510
|
+
|
511
|
+
# Wrapper around SshDevice::Response +parse+ that
|
512
|
+
# includes before and after hooks
|
513
|
+
|
514
|
+
def parse # :nodoc:
|
515
|
+
before_parse
|
516
|
+
super
|
517
|
+
after_parse
|
518
|
+
end
|
519
|
+
|
520
|
+
private
|
521
|
+
|
522
|
+
# transfers cfgshow to configshow format
|
523
|
+
def cfgshow_to_configshow(*greps)
|
524
|
+
# check if we have configshow output
|
525
|
+
return false if !@data.match(/> cfgshow/m)
|
526
|
+
# replace command
|
527
|
+
@data.gsub!("cfgshow","configshow")
|
528
|
+
# remove all lines below Effective configuration: included and Defined configuration line
|
529
|
+
@data.gsub!(/(Defined configuration:\s*|Effective configuration:.*)/im,"")
|
530
|
+
# remove spaces followig ; (includes newlines)
|
531
|
+
@data.gsub!(/;\s+/m,";")
|
532
|
+
# remove newlines before first member
|
533
|
+
@data.gsub!(/\t\n\t+/m,":")
|
534
|
+
# removes tab before first member
|
535
|
+
@data.gsub!(/([a-z_0-9])\t+([a-z_0-9])/im,'\1:\2')
|
536
|
+
# substitute alias,zone and cfg name
|
537
|
+
@data.gsub!(/^\s*(zone|alias|cfg):\s+/,'\1.')
|
538
|
+
#removes any empty character except newline
|
539
|
+
@data.gsub!(/[ \t]+\n/,"\n")
|
540
|
+
greps.each do |grep_t|
|
541
|
+
@data=@data.split("\n").grep(grep_t).join("\n")
|
542
|
+
end
|
543
|
+
@data="> configshow\n#{@data}"
|
544
|
+
true
|
545
|
+
end
|
546
|
+
|
547
|
+
def before_parse
|
548
|
+
reset
|
549
|
+
@parsed[:find_results]=[]
|
550
|
+
@parsed[:base]={}
|
551
|
+
end
|
552
|
+
|
553
|
+
def after_parse
|
554
|
+
@parsed[:ports].uniq! if @parsed[:ports]
|
555
|
+
to_purge = [:pointer,:last_key,:key,:domain,:was_popped]
|
556
|
+
to_purge<<:find_results if @parsed[:find_results].empty?
|
557
|
+
to_purge<<:base if @parsed[:base].empty?
|
558
|
+
to_purge.each {|k| @parsed.delete(k)}
|
559
|
+
end
|
560
|
+
|
561
|
+
def parse_line(line)
|
562
|
+
return if line.empty?
|
563
|
+
# we detect which command output we parse - commands start with defined prompt on the XML line
|
564
|
+
@parsed[:parsing_position] = case
|
565
|
+
# stripping fosexec, all pipes and ' to get pure command
|
566
|
+
when line.match(/^#{@prompt}/) then line.gsub(/(fosexec --fid \d+ \')|\'$|\' \|.*$/,"").split(" ")[1]
|
567
|
+
else @parsed[:parsing_position]
|
568
|
+
end
|
569
|
+
#some default processing
|
570
|
+
if line.match(/^#{@prompt}/)
|
571
|
+
case @parsed[:parsing_position]
|
572
|
+
when "islshow"
|
573
|
+
@parsed[:isl_links]||=[]
|
574
|
+
when "trunkshow"
|
575
|
+
@parsed[:trunk_links]||=[]
|
576
|
+
when "agshow"
|
577
|
+
@parsed[:ag]||=[]
|
578
|
+
when "cfgtransshow"
|
579
|
+
# if empty hash is returned the response was unexpected
|
580
|
+
@parsed[:cfg_transaction]={}
|
581
|
+
end
|
582
|
+
end
|
583
|
+
#do not process if we are on command line
|
584
|
+
return if line.match(/^#{@prompt}/)
|
585
|
+
|
586
|
+
# we parse only certain commands definned in PARSER_MAPPING
|
587
|
+
# all other commands are ignored by parser
|
588
|
+
# you can call them usign query and parse by yourself if you need some other
|
589
|
+
# or define parser mapping, command mapping and update given parser
|
590
|
+
case
|
591
|
+
when @parsed[:parsing_position].match(/#{PARSER_MAPPING.map{ |k,v| v=='simple' ? k : nil }.compact.join("|")}/i)
|
592
|
+
parse_simple(line)
|
593
|
+
when @parsed[:parsing_position].match(/#{PARSER_MAPPING.map{ |k,v| v=='oneline' ? k : nil }.compact.join("|")}/i)
|
594
|
+
parse_oneline(line)
|
595
|
+
when @parsed[:parsing_position].match(/#{PARSER_MAPPING.map{ |k,v| v=='multiline' ? k : nil }.compact.join("|")}/i)
|
596
|
+
parse_multiline(line)
|
597
|
+
when @parsed[:parsing_position].match(/#{PARSER_MAPPING.map{ |k,v| v=='cfgshow' ? k : nil }.compact.join("|")}/i)
|
598
|
+
parse_cfgshow(line)
|
599
|
+
when @parsed[:parsing_position].match(/#{PARSER_MAPPING.map{ |k,v| v=='ns' ? k : nil }.compact.join("|")}/i)
|
600
|
+
parse_ns(line)
|
601
|
+
when @parsed[:parsing_position].match(/#{PARSER_MAPPING.map{ |k,v| v=='trunk' ? k : nil }.compact.join("|")}/i)
|
602
|
+
parse_trunk(line)
|
603
|
+
end
|
604
|
+
end
|
605
|
+
|
606
|
+
# parser used to parse commands with 1 line output
|
607
|
+
def parse_oneline(line)
|
608
|
+
@parsed[@parsed[:parsing_position].to_sym]=line
|
609
|
+
end
|
610
|
+
|
611
|
+
# parser used to parse commands with multi lines output
|
612
|
+
def parse_multiline(line)
|
613
|
+
# switchstatusshow
|
614
|
+
case
|
615
|
+
when line.match(/^\s*[a-z]+.*:/i)
|
616
|
+
arr = line.split(":")
|
617
|
+
@parsed[arr[0].strip.gsub(/\s+/,"_").gsub(/([a-z])([A-Z])/,'\1_\2').downcase.to_sym]=arr[1..-1].join(":").strip
|
618
|
+
when line.match(/^There is no outstanding/) && @parsed[:parsing_position]=="cfgtransshow"
|
619
|
+
@parsed[:cfg_transaction] = {:id=>-1, :abortable=>nil, :msg => "no transaction"}
|
620
|
+
when line.match(/^Current transaction token is (.+)$/) && @parsed[:parsing_position]=="cfgtransshow"
|
621
|
+
@parsed[:cfg_transaction][:id] = $1
|
622
|
+
when line.match(/It is(.+)abortable$/) && @parsed[:parsing_position]=="cfgtransshow"
|
623
|
+
@parsed[:cfg_transaction][:abortable] = $1.match(/not/) ? false : true
|
624
|
+
else
|
625
|
+
#supportshow
|
626
|
+
@parsed[@parsed[:parsing_position].to_sym]||=""
|
627
|
+
@parsed[@parsed[:parsing_position].to_sym]+=line+"\n"
|
628
|
+
end
|
629
|
+
end
|
630
|
+
|
631
|
+
# parser for multiline output where each line is independent
|
632
|
+
def parse_simple(line)
|
633
|
+
case
|
634
|
+
#extra handling
|
635
|
+
when line.match(/^zoning/)
|
636
|
+
@parsed[:zoning_enabled]=line.match(/:\s+ON/) ? true : false
|
637
|
+
@parsed[:active_config]=@parsed[:zoning_enabled] ? line.gsub(/(.*\()|(\).*)/,'') : nil
|
638
|
+
|
639
|
+
when line.match(/^LS Attributes/)
|
640
|
+
@parsed[:ls_attributes]||={}
|
641
|
+
sub_ls_attrs=line.gsub(/(.*\[)|(\].*)/,'').split(",")
|
642
|
+
sub_ls_attrs.each do |attr|
|
643
|
+
if attr.match(/Address Mode/)
|
644
|
+
@parsed[:ls_attributes][:address_mode]=attr.gsub(/Address Mode/,'').strip
|
645
|
+
else
|
646
|
+
key , value = attr.split(":").map {|a| a.strip.downcase}
|
647
|
+
@parsed[:ls_attributes][key.gsub(/\s+/,"_").to_sym]=value
|
648
|
+
end
|
649
|
+
end
|
650
|
+
# port line in director
|
651
|
+
when line.match(/\s?\d{1,3}\s+\d{1,2}\s+\d{1,2}\s+[\dabcdef-]{6}/i)
|
652
|
+
l=line.split
|
653
|
+
@parsed[:ports]||=[]
|
654
|
+
@parsed[:ports]<<{:index => l[0].to_i,:slot=>l[1].to_i, :port=>l[2].to_i, :address=>l[3].strip, :media => l[4].strip,
|
655
|
+
:speed=>l[5].strip, :state=>l[6].strip, :proto=>l[7].strip, :comment=>l[8..-1].join(" ")}
|
656
|
+
# port line in san blade
|
657
|
+
when line.match(/\s?\d{1,3}\s+\d{1,2}\s+[\dabcdef]{6}/i)
|
658
|
+
l=line.split
|
659
|
+
@parsed[:ports]||=[]
|
660
|
+
@parsed[:ports]<<{:index => l[0].to_i, :port=>l[1].to_i, :address=>l[2].strip, :media => l[3].strip,
|
661
|
+
:speed=>l[4].strip, :state=>l[5].strip, :proto=>l[6].strip, :comment=>l[7..-1].join(" ")}
|
662
|
+
when line.match(/^Index|^=/)
|
663
|
+
""
|
664
|
+
when line.match(/Created switches/)
|
665
|
+
@parsed[:created_switches]=line.split(":")[1].strip.split(" ").map {|l1| l1.to_i}
|
666
|
+
#fabrics
|
667
|
+
when line.match(/^\s*\d+:\s[a-f0-9]{6}/i)
|
668
|
+
l=line.split(" ")
|
669
|
+
@parsed[:fabric]||=[]
|
670
|
+
@parsed[:fabric] << {:domain_id => l[0].gsub(/:/,"").strip, :sid => l[1].strip, :wwn=>l[2].strip, :eth_ip=>l[3].strip, :fc_ip=>l[4].strip, :name=>l[5].strip.gsub(/\"|>/,""), :local => l[5].strip.match(/^>/) ? true : false }
|
671
|
+
when line.match(/^(zone|alias|cfg)\./)
|
672
|
+
l=line.gsub!(/^(zone|alias|cfg)\./,"").split(":")
|
673
|
+
@parsed[:find_results] << {:obj=>l.shift,:members=>l.join(":"), :type => $1.to_sym}
|
674
|
+
#agshow
|
675
|
+
when line.match(/^([a-f0-9]{2}:){7}[a-f0-9]{2}\s+\d+/i) && @parsed[:parsing_position]=="agshow"
|
676
|
+
l=line.split(" ")
|
677
|
+
@parsed[:ag]||=[]
|
678
|
+
@parsed[:ag] << {:wwn => l[0].strip, :ports => l[1].to_i, :eth_ip=>l[2].strip, :version=>l[3].strip, :local => l[4].strip.match(/^local/) ? true : false, :name => l[5].strip }
|
679
|
+
#islshow
|
680
|
+
# 1: 0-> 0 10:00:00:05:33:23:86:00 1 H2C04R065-U03-A sp: 8.000G bw: 64.000G TRUNK QOS
|
681
|
+
when line.match(/\d+:\s*\d+->.+sp:/)
|
682
|
+
l_tricky, l_simple = line.split("->")
|
683
|
+
l_t = l_tricky.split(":")
|
684
|
+
|
685
|
+
l = l_simple.split(" ")
|
686
|
+
@parsed[:isl_links]||=[]
|
687
|
+
@parsed[:isl_links] << {:id => l_t[0].to_i,
|
688
|
+
:source_port_index => l_t[1].to_i,
|
689
|
+
:destination_port_index => l[0].to_i,
|
690
|
+
:destination_switch_wwn => l[1].strip,
|
691
|
+
:destination_switch_domain => l[2].to_i,
|
692
|
+
:destination_switch_name => l[3].strip,
|
693
|
+
:speed => l[5].to_i,
|
694
|
+
:bandwidth => l[7].to_i,
|
695
|
+
:trunk => l[8..-1].include?("TRUNK"),
|
696
|
+
:qos => l[8..-1].include?("QOS"),
|
697
|
+
:cr_recov => l[8..-1].include?("CR_RECOV")
|
698
|
+
}
|
699
|
+
#default handling if it doesno match specialized match
|
700
|
+
# parse lines formated like
|
701
|
+
# param: value
|
702
|
+
else
|
703
|
+
if line.match(/^\s*[a-z]+.*:/i)
|
704
|
+
arr = line.split(":")
|
705
|
+
@parsed[str_to_key(arr[0])]=arr[1..-1].join(":").strip
|
706
|
+
end
|
707
|
+
end
|
708
|
+
end
|
709
|
+
|
710
|
+
# parser dedicated to cfgshow format
|
711
|
+
def parse_cfgshow(line)
|
712
|
+
# once effective_configuration is loaded we ignore rest
|
713
|
+
return if @parsed[:effective_configuration] and !@parsed[:effective_configuration][:cfg].nil?
|
714
|
+
|
715
|
+
# we use array stack to point to the object being parsed
|
716
|
+
@parsed[:pointer]||=[]
|
717
|
+
@parsed[:pointer].push @parsed[:base] if @parsed[:pointer].empty?
|
718
|
+
@parsed[:key]||=[]
|
719
|
+
@parsed[:last_key]||=nil
|
720
|
+
@parsed[:was_popped]||=false
|
721
|
+
|
722
|
+
if (matches=line.match(/^\s*([a-z]+\s*[a-z]+):(.*)/i))
|
723
|
+
key=str_to_key(matches[1])
|
724
|
+
after_colon=matches[2].strip
|
725
|
+
|
726
|
+
# superkey -> defined_configuration, efective_configuration, cfg, zone, alias
|
727
|
+
if after_colon.empty?
|
728
|
+
@parsed[key]||={}
|
729
|
+
# we have new superkey so we pop the old from pointer stack
|
730
|
+
# and we push the new in
|
731
|
+
@parsed[:pointer].pop if !@parsed[:pointer].empty?
|
732
|
+
@parsed[:pointer].push @parsed[key]
|
733
|
+
# subkey
|
734
|
+
else
|
735
|
+
# sometimes the previous key does not have any members (or they are filtered out)
|
736
|
+
# in that case the key and pointer were not poped below so we pop them now
|
737
|
+
if @parsed[:last_key]==key && !@parsed[:was_popped]
|
738
|
+
@parsed[:pointer].pop
|
739
|
+
@parsed[:key].pop
|
740
|
+
end
|
741
|
+
# we define the last subkey as hash
|
742
|
+
# and push the array to pointer stack
|
743
|
+
@parsed[:pointer].last[key]||={}
|
744
|
+
@parsed[:pointer].push @parsed[:pointer].last[key]
|
745
|
+
|
746
|
+
# first value is name of the key
|
747
|
+
# 2nd is list of key members
|
748
|
+
value=after_colon.split(" ")[0].strip
|
749
|
+
members=after_colon.split(" ").length>1 ? after_colon.split(" ")[1..-1].join(" ") : ""
|
750
|
+
|
751
|
+
@parsed[:key].push value
|
752
|
+
|
753
|
+
# adding value into the current pointer
|
754
|
+
@parsed[:pointer].last[value]||=[]
|
755
|
+
|
756
|
+
# assign members into the last pointer array item
|
757
|
+
if !members.empty?
|
758
|
+
@parsed[:pointer].last[value]||=[]
|
759
|
+
members.split(";").each do |member|
|
760
|
+
@parsed[:pointer].last[value]<<member.strip if !member.match(/^\s*$/)
|
761
|
+
end
|
762
|
+
# if the line does not end with ; next line starts with new key or super key
|
763
|
+
# hence we remove last pointer from stack as we are done with it
|
764
|
+
if !line.strip.match(/;$/)
|
765
|
+
@parsed[:pointer].pop
|
766
|
+
@parsed[:key].pop
|
767
|
+
@parsed[:was_popped]=true
|
768
|
+
else
|
769
|
+
@parsed[:was_popped]=false
|
770
|
+
end
|
771
|
+
else
|
772
|
+
@parsed[:was_popped]=false
|
773
|
+
end
|
774
|
+
end
|
775
|
+
@parsed[:last_key]=key
|
776
|
+
# this line defines another members of last pointer key array item
|
777
|
+
elsif line.match(/^\t/)
|
778
|
+
@parsed[:last_key]=nil
|
779
|
+
# sometimes it is not defined yet
|
780
|
+
# we push the members in
|
781
|
+
@parsed[:pointer].last[@parsed[:key].last]||=[]
|
782
|
+
line.split(";").each do |member|
|
783
|
+
@parsed[:pointer].last[@parsed[:key].last]<<member.strip if !member.match(/^\s*$/)
|
784
|
+
end
|
785
|
+
# if the line does not end with ; next line starts with new key or super key
|
786
|
+
# hence we remove last pointer from stack as we are done with it
|
787
|
+
if !line.strip.match(/;$/)
|
788
|
+
@parsed[:pointer].pop
|
789
|
+
@parsed[:key].pop
|
790
|
+
@parsed[:was_popped]=true
|
791
|
+
else
|
792
|
+
@parsed[:was_popped]=false
|
793
|
+
end
|
794
|
+
end
|
795
|
+
true
|
796
|
+
end
|
797
|
+
|
798
|
+
# name server parser
|
799
|
+
def parse_ns(line)
|
800
|
+
@parsed[:domain]||=0
|
801
|
+
@parsed[:key] = @parsed[:parsing_position]=="nsshow" ? :wwn_local : :wwn_remote
|
802
|
+
@parsed[@parsed[:key]]||=[]
|
803
|
+
|
804
|
+
case
|
805
|
+
|
806
|
+
# changing domain id
|
807
|
+
when line.match(/Switch entry for/)
|
808
|
+
@parsed[:domain]=line.split(" ").last.to_i
|
809
|
+
|
810
|
+
# new WWN
|
811
|
+
when line.match(/(^\s+N |^\s+U )/)
|
812
|
+
@parsed[@parsed[:key]].push Hash.new
|
813
|
+
@parsed[@parsed[:key]].last[:value]=line.split(";")[2]
|
814
|
+
@parsed[@parsed[:key]].last[:domain_id]=@parsed[:domain]
|
815
|
+
@parsed[@parsed[:key]].last[:symbol]||=""
|
816
|
+
|
817
|
+
# symbol
|
818
|
+
when line.match(/(PortSymb|NodeSymb)/)
|
819
|
+
@parsed[@parsed[:key]].last[:symbol]=line.split(":")[1..-1].join(":").strip
|
820
|
+
|
821
|
+
# detype
|
822
|
+
when line.match(/(Device type)/)
|
823
|
+
@parsed[@parsed[:key]].last[:dev_type]=line.split(":")[1].strip
|
824
|
+
when line.match(/(Port Index)/)
|
825
|
+
@parsed[@parsed[:key]].last[:port_index]=line.split(":")[1].strip.to_i
|
826
|
+
end
|
827
|
+
|
828
|
+
end
|
829
|
+
|
830
|
+
# trunk parser
|
831
|
+
def parse_trunk(line)
|
832
|
+
return if line.match(/No trunking/i)
|
833
|
+
@parsed[:trunk_links]||=[]
|
834
|
+
l_tricky, l_simple = line.split("->")
|
835
|
+
l_t = l_tricky.split(":")
|
836
|
+
|
837
|
+
l = l_simple.split(" ")
|
838
|
+
case
|
839
|
+
when line.match(/\d+:\s*\d+->/)
|
840
|
+
@parsed[:trunk_links]<<{:id => l_t[0].to_i, :members => [
|
841
|
+
{
|
842
|
+
:source_port_index => l_t[1].to_i,
|
843
|
+
:destination_port_index => l[0].to_i,
|
844
|
+
:destination_switch_wwn => l[1].strip,
|
845
|
+
:destination_switch_domain => l[2].to_i,
|
846
|
+
:deskew => l[4].to_i,
|
847
|
+
:master => l[5]=="MASTER"
|
848
|
+
}]
|
849
|
+
}
|
850
|
+
else
|
851
|
+
@parsed[:trunk_links].last[:members]<<{
|
852
|
+
:source_port_index => l_t[0].to_i,
|
853
|
+
:destination_port_index => l[0].to_i,
|
854
|
+
:destination_switch_wwn => l[1].strip,
|
855
|
+
:destination_switch_domain => l[2].to_i,
|
856
|
+
:deskew => l[4].to_i,
|
857
|
+
:master => l[5]=="MASTER"
|
858
|
+
}
|
859
|
+
end
|
860
|
+
end
|
861
|
+
|
862
|
+
# transforamtino method to define parsed key based on the string
|
863
|
+
def str_to_key(str)
|
864
|
+
str.strip.gsub(/\s+/,"_").gsub(/([a-z])([A-Z])/,'\1_\2').downcase.to_sym
|
865
|
+
end
|
866
|
+
end
|
867
|
+
end
|
868
|
+
|
869
|
+
class Switch
|
870
|
+
# class extending SshDevice::Error
|
871
|
+
class Error < self::Error
|
872
|
+
WRONG_FORMAT="Error: Incorrect format"
|
873
|
+
def self.incorrect(str) #:nodoc:
|
874
|
+
self.new("#{WRONG_FORMAT} - #{str}")
|
875
|
+
end
|
876
|
+
|
877
|
+
def self.unknown
|
878
|
+
self.new("Unknown attribute")
|
879
|
+
end
|
880
|
+
end
|
881
|
+
end
|
882
|
+
end; end
|