tiebreaker 0.0.2
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/classes/NaElement.rb +396 -0
- data/classes/NaErrno.rb +1170 -0
- data/classes/NaServer.rb +789 -0
- data/classes/common.rb +37 -0
- data/classes/confreader.rb +16 -0
- data/classes/main.rb +79 -0
- data/classes/ntap.rb +610 -0
- data/classes/worker.rb +265 -0
- data/config.json +90 -0
- data/logs/config0.log +0 -0
- data/pids/pid +0 -0
- data/tiebreaker.rb +12 -0
- metadata +136 -0
data/classes/NaServer.rb
ADDED
@@ -0,0 +1,789 @@
|
|
1
|
+
#============================================================#
|
2
|
+
# #
|
3
|
+
# $ID:$ #
|
4
|
+
# #
|
5
|
+
# NaServer.rb #
|
6
|
+
# #
|
7
|
+
# Client-side interface to ONTAP and DataFabric Manager APIs.#
|
8
|
+
# #
|
9
|
+
# Copyright (c) 2011 NetApp, Inc. All rights reserved. #
|
10
|
+
# Specifications subject to change without notice. #
|
11
|
+
# #
|
12
|
+
#============================================================#
|
13
|
+
|
14
|
+
require 'net/http'
|
15
|
+
require 'net/https'
|
16
|
+
require 'rexml/document'
|
17
|
+
require 'rexml/streamlistener'
|
18
|
+
include REXML
|
19
|
+
require 'stringio'
|
20
|
+
include StreamListener
|
21
|
+
require 'NaElement'
|
22
|
+
require 'pathname'
|
23
|
+
|
24
|
+
# Official NMSDK release version
|
25
|
+
$NMSDK_VERSION = '5.4'
|
26
|
+
|
27
|
+
# This method return the platform information of unix systems.
|
28
|
+
# This method is used internally for NMSDK/API Usage Tracking.
|
29
|
+
# NOTE: DO NOT REMOVE/MODIFY THIS METHOD.
|
30
|
+
# NOTE: DO NOT USE THIS METHOD EXTERNALLY.
|
31
|
+
def get_unix_info()
|
32
|
+
v = $VERBOSE
|
33
|
+
$VERBOSE = nil
|
34
|
+
sysname = `uname -s`
|
35
|
+
$VERBOSE = v
|
36
|
+
sysname = sysname.chomp
|
37
|
+
|
38
|
+
if(sysname.include?("Linux")) # for linux (rhel, suse, oel)
|
39
|
+
# check if it is SUSE
|
40
|
+
filepath = Pathname.new("/etc/SuSE-release")
|
41
|
+
if(filepath.exist?())
|
42
|
+
release_file = '/etc/SuSE-release'
|
43
|
+
else # for RHEL, OEL, etc.
|
44
|
+
release_file = '/etc/issue'
|
45
|
+
end
|
46
|
+
|
47
|
+
$VERBOSE = nil
|
48
|
+
flavor = `head -n 1 #{release_file}`
|
49
|
+
$VERBOSE = v
|
50
|
+
# Remove the string within parentheses
|
51
|
+
sysname = flavor.sub(/\(\w+\)/, '').chomp
|
52
|
+
sysname = sysname.sub(/\s+\Z/, "")
|
53
|
+
|
54
|
+
else # for other unix platforms (solaris, aix, hpux)
|
55
|
+
$VERBOSE = nil
|
56
|
+
if(sysname.eql?("AIX"))
|
57
|
+
version = `oslevel`
|
58
|
+
else
|
59
|
+
version = `uname -r`
|
60
|
+
end
|
61
|
+
$VERBOSE = v
|
62
|
+
sysname = sysname + " " + version.chomp
|
63
|
+
end
|
64
|
+
|
65
|
+
$VERBOSE = nil
|
66
|
+
if(sysname.eql?("HP-UX"))
|
67
|
+
processor = `uname -m`
|
68
|
+
else
|
69
|
+
processor = `uname -p`
|
70
|
+
end
|
71
|
+
|
72
|
+
if(sysname.include?("SunOS"))
|
73
|
+
isainfo = `isainfo -b`
|
74
|
+
isainfo = isainfo.chomp
|
75
|
+
bitinfo = " " + isainfo + "-bit"
|
76
|
+
else
|
77
|
+
bitinfo = ""
|
78
|
+
end
|
79
|
+
$VERBOSE = v
|
80
|
+
|
81
|
+
os_info = sysname + " " + processor.chomp + bitinfo
|
82
|
+
return os_info
|
83
|
+
end
|
84
|
+
|
85
|
+
|
86
|
+
# This method return the platform information of windows systems.
|
87
|
+
# This method is used internally for NMSDK/API Usage Tracking.
|
88
|
+
# NOTE: DO NOT REMOVE/MODIFY THIS METHOD.
|
89
|
+
# NOTE: DO NOT USE THIS METHOD EXTERNALLY.
|
90
|
+
def get_windows_info()
|
91
|
+
sysname = ""
|
92
|
+
processor = ""
|
93
|
+
os_info = ""
|
94
|
+
|
95
|
+
require 'win32/registry'
|
96
|
+
|
97
|
+
Win32::Registry::HKEY_LOCAL_MACHINE.open('SOFTWARE\Microsoft\Windows NT\CurrentVersion') do |reg|
|
98
|
+
type, sysname = reg.read('ProductName')
|
99
|
+
end
|
100
|
+
|
101
|
+
Win32::Registry::HKEY_LOCAL_MACHINE.open('SYSTEM\ControlSet001\Control\Session Manager\Environment') do |reg|
|
102
|
+
type, processor = reg.read('PROCESSOR_ARCHITECTURE')
|
103
|
+
end
|
104
|
+
|
105
|
+
os_info = sysname + " " + processor
|
106
|
+
return os_info
|
107
|
+
end
|
108
|
+
|
109
|
+
# The client platform information string.
|
110
|
+
# NOTE: DO NOT REMOVE/MODIFY THIS VARIABLE.
|
111
|
+
$NMSDK_PLATFORM = ""
|
112
|
+
platform = RUBY_PLATFORM
|
113
|
+
if (platform.include?("mingw") )
|
114
|
+
$NMSDK_PLATFORM = get_windows_info()
|
115
|
+
else
|
116
|
+
$NMSDK_PLATFORM = get_unix_info()
|
117
|
+
end
|
118
|
+
|
119
|
+
|
120
|
+
# Class for managing Network Appliance(r) Storage System
|
121
|
+
# using ONTAPI(tm) and DataFabric Manager API(tm).
|
122
|
+
#
|
123
|
+
# An NaServer encapsulates an administrative connection to
|
124
|
+
# a NetApp Storage Systems running Data ONTAP 6.4 or later.
|
125
|
+
# NaServer can also be used to establish connection with
|
126
|
+
# OnCommand Unified Manager (OCUM). You construct NaElement objects
|
127
|
+
# that represent queries or commands, and use invoke_elem()
|
128
|
+
# to send them to the storage systems or OCUM server. Also,
|
129
|
+
# a convenience routine called invoke() can be used to bypass
|
130
|
+
# the element construction step. The return from the call is
|
131
|
+
# another NaElement which either has children containing the
|
132
|
+
# command results, or an error indication.
|
133
|
+
#
|
134
|
+
# The following routines are available for setting up
|
135
|
+
# administrative connections to a storage system or OCUM server.
|
136
|
+
#
|
137
|
+
|
138
|
+
$ZAPI_stack = []
|
139
|
+
$ZAPI_atts = {}
|
140
|
+
$tag_element_stack = []
|
141
|
+
|
142
|
+
class NaServer
|
143
|
+
|
144
|
+
#dtd files
|
145
|
+
FILER_dtd = 'file:/etc/netapp_filer.dtd'
|
146
|
+
DFM_dtd = 'file:/etc/netapp_dfm.dtd'
|
147
|
+
AGENT_dtd = 'file:/etc/netapp_agent.dtd'
|
148
|
+
|
149
|
+
#URLs
|
150
|
+
AGENT_URL = '/apis/XMLrequest'
|
151
|
+
FILER_URL = '/servlets/netapp.servlets.admin.XMLrequest_filer'
|
152
|
+
NETCACHE_URL = '/servlets/netapp.servlets.admin.XMLrequest'
|
153
|
+
DFM_URL = '/apis/XMLrequest'
|
154
|
+
|
155
|
+
ZAPI_xmlns = 'http://www.netapp.com/filer/admin'
|
156
|
+
|
157
|
+
# Create a new connection to server 'server'. Before use,
|
158
|
+
# you either need to set the style to "hosts.equiv" or set
|
159
|
+
# the username (always "root" at present) and password with
|
160
|
+
# set_admin_user().
|
161
|
+
|
162
|
+
def initialize(server, major_version, minor_version)
|
163
|
+
@server = server
|
164
|
+
@major_version = major_version
|
165
|
+
@minor_version = minor_version
|
166
|
+
@transport_type = "HTTP"
|
167
|
+
@port = 80
|
168
|
+
@user = "root"
|
169
|
+
@password = ""
|
170
|
+
@style = "LOGIN"
|
171
|
+
@timeout = 0
|
172
|
+
@vfiler = ""
|
173
|
+
@server_type = "FILER"
|
174
|
+
@debug_style = ""
|
175
|
+
@xml = ""
|
176
|
+
@originator_id = ""
|
177
|
+
@enable_server_cert_verification = false
|
178
|
+
@enable_hostname_verification = false
|
179
|
+
@cert_file = nil
|
180
|
+
@key_file = nil
|
181
|
+
@key_passwd = nil
|
182
|
+
@ca_file = nil
|
183
|
+
@url = FILER_URL
|
184
|
+
@dtd = FILER_dtd
|
185
|
+
|
186
|
+
# Following parameters are used for NMSDK/API Usage Tracking.
|
187
|
+
# NOTE: DO NOT REMOVE/MODIFY THESE VARIABLES.
|
188
|
+
@nmsdk_version = $NMSDK_VERSION
|
189
|
+
@nmsdk_platform = $NMSDK_PLATFORM
|
190
|
+
@nmsdk_language = "Ruby"
|
191
|
+
@nmsdk_app = ""
|
192
|
+
end
|
193
|
+
|
194
|
+
|
195
|
+
# Set the client application name.
|
196
|
+
def set_application_name(app_name)
|
197
|
+
@nmsdk_app = app_name
|
198
|
+
end
|
199
|
+
|
200
|
+
|
201
|
+
# Get the client application name.
|
202
|
+
def get_application_name()
|
203
|
+
return @nmsdk_app
|
204
|
+
end
|
205
|
+
|
206
|
+
|
207
|
+
# Pass in 'LOGIN' to cause the server to use HTTP simple
|
208
|
+
# authentication with a username and password. Pass in 'HOSTS'
|
209
|
+
# to use the hosts.equiv file on the filer to determine access
|
210
|
+
# rights (the username must be root in that case). Pass in
|
211
|
+
# 'CERTIFICATE' to use certificate based authentication with the
|
212
|
+
# DataFabric Manager server.
|
213
|
+
#
|
214
|
+
# If $style = CERTIFICATE, you can use certificates to authenticate
|
215
|
+
# clients who attempt to connect to a server without the need of
|
216
|
+
# username and password. This style will internally set the transport
|
217
|
+
# type to HTTPS. Verification of the server's certificate is required
|
218
|
+
# in order to properly authenticate the identity of the server.
|
219
|
+
# Server certificate verification will be enabled by default using this
|
220
|
+
# style and Server certificate verification will always enable hostname
|
221
|
+
# verification. You can disable server certificate (with hostname)
|
222
|
+
# verification using set_server_cert_verification().
|
223
|
+
|
224
|
+
def set_style(style)
|
225
|
+
if(!style.eql?("HOSTS") and !style.eql?("LOGIN") and !style.eql?("CERTIFICATE"))
|
226
|
+
return fail_response(13001, "NaServer::set_style: bad style \"" + style + "\"")
|
227
|
+
end
|
228
|
+
if (style.eql?("CERTIFICATE"))
|
229
|
+
ret = set_transport_type("HTTPS")
|
230
|
+
if (ret)
|
231
|
+
return ret
|
232
|
+
end
|
233
|
+
@enable_server_cert_verification = true
|
234
|
+
@enable_hostname_verification = true
|
235
|
+
else
|
236
|
+
@enable_server_cert_verification = false
|
237
|
+
@enable_hostname_verification = false
|
238
|
+
end
|
239
|
+
@style = style
|
240
|
+
return nil
|
241
|
+
end
|
242
|
+
|
243
|
+
|
244
|
+
# Get the authentication style
|
245
|
+
|
246
|
+
def get_style()
|
247
|
+
return @style
|
248
|
+
end
|
249
|
+
|
250
|
+
|
251
|
+
# Set the admin username and password. At present 'user' must always be 'root'.
|
252
|
+
|
253
|
+
def set_admin_user(user, password)
|
254
|
+
@user = user
|
255
|
+
@password = password
|
256
|
+
end
|
257
|
+
|
258
|
+
|
259
|
+
|
260
|
+
# Pass in one of these keywords: 'FILER' or 'DFM' or 'OCUM' to indicate
|
261
|
+
# whether the server is a storage system (filer) or a OCUM server.
|
262
|
+
#
|
263
|
+
# If you also use set_port(), call set_port() AFTER calling this routine.
|
264
|
+
#
|
265
|
+
# The default is 'FILER'.
|
266
|
+
|
267
|
+
def set_server_type(server_type)
|
268
|
+
if (server_type.casecmp('filer') == 0)
|
269
|
+
@url = FILER_URL
|
270
|
+
@dtd = FILER_dtd
|
271
|
+
elsif (server_type.casecmp('netcache') == 0)
|
272
|
+
@url = NETCACHE_URL
|
273
|
+
@port = 80
|
274
|
+
elsif (server_type.casecmp('agent') == 0)
|
275
|
+
@url = AGENT_URL
|
276
|
+
@port = 4092
|
277
|
+
@dtd = AGENT_dtd
|
278
|
+
elsif (server_type.casecmp('dfm') == 0)
|
279
|
+
@url = DFM_URL
|
280
|
+
@port = 8088
|
281
|
+
@dtd = DFM_dtd
|
282
|
+
if(@transport_type == "HTTPS")
|
283
|
+
@port = 8488
|
284
|
+
end
|
285
|
+
elsif (server_type.casecmp('ocum') == 0)
|
286
|
+
@url = DFM_URL
|
287
|
+
@port = 443
|
288
|
+
@transport_type = "HTTPS"
|
289
|
+
@dtd = DFM_dtd
|
290
|
+
else
|
291
|
+
return fail_response(13001, "NaServer::set_server_type: bad type \"" + server_type + "\"")
|
292
|
+
end
|
293
|
+
@server_type = server_type
|
294
|
+
return nil
|
295
|
+
end
|
296
|
+
|
297
|
+
|
298
|
+
# Get the type of server this server connection applies to.
|
299
|
+
|
300
|
+
def get_server_type()
|
301
|
+
return @server_type
|
302
|
+
end
|
303
|
+
|
304
|
+
|
305
|
+
# Override the default transport type. The valid transport
|
306
|
+
# type are currently 'HTTP' and 'HTTPS'.
|
307
|
+
|
308
|
+
def set_transport_type(scheme)
|
309
|
+
if(!scheme.eql?("HTTP") and !scheme.eql?("HTTPS"))
|
310
|
+
return fail_response(13001, "NaServer::set_transport_type: bad type \" " + scheme + "\"")
|
311
|
+
end
|
312
|
+
if(scheme.eql?("HTTP"))
|
313
|
+
if(@server_type.eql?("OCUM"))
|
314
|
+
return fail_response(13001, "Server type '" + @server_type + "' does not support '" + scheme + "' transport type")
|
315
|
+
end
|
316
|
+
|
317
|
+
@transport_type = "HTTP"
|
318
|
+
if(@server_type.eql?("DFM"))
|
319
|
+
@port = 8088
|
320
|
+
else
|
321
|
+
@port = 80
|
322
|
+
end
|
323
|
+
elsif(scheme.eql?("HTTPS"))
|
324
|
+
@transport_type = "HTTPS"
|
325
|
+
if(@server_type.eql?("DFM"))
|
326
|
+
@port = 8488
|
327
|
+
else
|
328
|
+
@port = 443
|
329
|
+
end
|
330
|
+
end
|
331
|
+
return nil
|
332
|
+
end
|
333
|
+
|
334
|
+
|
335
|
+
# Retrieve the transport used for this connection.
|
336
|
+
|
337
|
+
def get_transport_type()
|
338
|
+
return @transport_type
|
339
|
+
end
|
340
|
+
|
341
|
+
|
342
|
+
# Set the style of debug.
|
343
|
+
|
344
|
+
def set_debug_style(debug_style)
|
345
|
+
if(!debug_style.eql?("NA_PRINT_DONT_PARSE"))
|
346
|
+
return fail_response(13001, "NaServer::set_debug_style: bad style \"" + debug_style + "\"")
|
347
|
+
else
|
348
|
+
@debug_style = debug_style
|
349
|
+
end
|
350
|
+
end
|
351
|
+
|
352
|
+
|
353
|
+
# Override the default port for this server. If you
|
354
|
+
# also call set_server_type(), you must call it before
|
355
|
+
# calling set_port().
|
356
|
+
|
357
|
+
def set_port(port)
|
358
|
+
@port = port
|
359
|
+
end
|
360
|
+
|
361
|
+
|
362
|
+
# Retrieve the port used for the remote server.
|
363
|
+
|
364
|
+
def get_port()
|
365
|
+
return @port
|
366
|
+
end
|
367
|
+
|
368
|
+
|
369
|
+
# Check the type of debug style and return the
|
370
|
+
# value for different needs. Return true if debug style
|
371
|
+
# is NA_PRINT_DONT_PARSE, else return false.
|
372
|
+
|
373
|
+
def is_debugging()
|
374
|
+
if(@debug_style.eql?("NA_PRINT_DONT_PARSE"))
|
375
|
+
return true
|
376
|
+
else
|
377
|
+
return false
|
378
|
+
end
|
379
|
+
end
|
380
|
+
|
381
|
+
|
382
|
+
# Return the raw XML output.
|
383
|
+
|
384
|
+
def get_raw_xml_output()
|
385
|
+
return @xml
|
386
|
+
end
|
387
|
+
|
388
|
+
|
389
|
+
# Save the raw XML output.
|
390
|
+
|
391
|
+
def set_raw_xml_output(xml)
|
392
|
+
@xml = xml
|
393
|
+
end
|
394
|
+
|
395
|
+
|
396
|
+
# Determines whether https is enabled.
|
397
|
+
|
398
|
+
def use_https()
|
399
|
+
if(@transport_type.eql?("HTTPS"))
|
400
|
+
return true
|
401
|
+
else
|
402
|
+
return false
|
403
|
+
end
|
404
|
+
end
|
405
|
+
|
406
|
+
|
407
|
+
def parse_raw_xml(xmlresponse)
|
408
|
+
xml_response = StringIO.new(xmlresponse)
|
409
|
+
Document.parse_stream(xml_response, MyListener.new)
|
410
|
+
if($tag_element_stack.length > 0)
|
411
|
+
print("\nError : No corresponding end tag for the element \"" + $tag_element_stack.pop() + "\"\n")
|
412
|
+
exit
|
413
|
+
end
|
414
|
+
stack_len = $ZAPI_stack.length
|
415
|
+
if(stack_len <= 0)
|
416
|
+
return fail_response(13001, "Zapi::parse_xml-no elements on stack")
|
417
|
+
end
|
418
|
+
r = $ZAPI_stack.pop()
|
419
|
+
return r
|
420
|
+
end
|
421
|
+
|
422
|
+
|
423
|
+
|
424
|
+
def parse_xml(xmlresponse)
|
425
|
+
xml_response = StringIO.new(xmlresponse)
|
426
|
+
Document.parse_stream(xml_response, MyListener.new)
|
427
|
+
if($tag_element_stack.length > 0)
|
428
|
+
print("\nError : No corresponding end tag for the element \"" + $tag_element_stack.pop() + "\"\n")
|
429
|
+
exit
|
430
|
+
end
|
431
|
+
stack_len = $ZAPI_stack.length
|
432
|
+
if(stack_len <= 0)
|
433
|
+
return fail_response(13001, "Zapi::parse_xml-no elements on stack")
|
434
|
+
end
|
435
|
+
r = $ZAPI_stack.pop()
|
436
|
+
if (r.name != "netapp")
|
437
|
+
return fail_response(13001, "Zapi::parse_xml - Expected <netapp> element but got" + r.name)
|
438
|
+
end
|
439
|
+
results = r.child_get("results")
|
440
|
+
unless(results)
|
441
|
+
return fail_response(13001, "Zapi::parse_xml - No results element in output!")
|
442
|
+
end
|
443
|
+
return results
|
444
|
+
end
|
445
|
+
|
446
|
+
|
447
|
+
# Submit an XML request already encapsulated as
|
448
|
+
# an NaElement and return the result in another
|
449
|
+
# NaElement.
|
450
|
+
|
451
|
+
def invoke_elem(req)
|
452
|
+
xmlrequest = req.toEncodedString()
|
453
|
+
vfiler_req = ""
|
454
|
+
originator_id_req = ""
|
455
|
+
if(!@vfiler.eql?(""))
|
456
|
+
vfiler_req = " vfiler=\"" + @vfiler + "\""
|
457
|
+
end
|
458
|
+
if(!@originator_id.eql?(""))
|
459
|
+
originator_id_req = " originator_id=\"" + @originator_id + "\""
|
460
|
+
end
|
461
|
+
|
462
|
+
app_name_req = ""
|
463
|
+
if(!@nmsdk_app.eql?(""))
|
464
|
+
app_name_req = " nmsdk_app='" + @nmsdk_app + "'"
|
465
|
+
end
|
466
|
+
|
467
|
+
content = "<?xml version=\'1.0\' encoding=\'utf-8\'?>" +
|
468
|
+
"\n" +
|
469
|
+
"<!DOCTYPE netapp SYSTEM \'" + @dtd + "\'>" +
|
470
|
+
"\n" +
|
471
|
+
"<netapp" +
|
472
|
+
vfiler_req +
|
473
|
+
originator_id_req +
|
474
|
+
" version='" + @major_version.to_s() + "." + @minor_version.to_s() + "' xmlns='" + ZAPI_xmlns + "'" +
|
475
|
+
" nmsdk_version='" + @nmsdk_version + "'" +
|
476
|
+
" nmsdk_platform='" + @nmsdk_platform + "'" +
|
477
|
+
" nmsdk_language='" + @nmsdk_language + "'" +
|
478
|
+
app_name_req +
|
479
|
+
">" +
|
480
|
+
xmlrequest +
|
481
|
+
"</netapp>"
|
482
|
+
|
483
|
+
if(@debug_style.eql?("NA_PRINT_DONT_PARSE"))
|
484
|
+
print("INPUT \n " + content)
|
485
|
+
end
|
486
|
+
|
487
|
+
begin
|
488
|
+
http = Net::HTTP.new(@server, @port)
|
489
|
+
if(@transport_type.eql?("HTTPS"))
|
490
|
+
http.use_ssl = true
|
491
|
+
http.verify_mode = OpenSSL::SSL::VERIFY_NONE
|
492
|
+
# Server Certificate Verification
|
493
|
+
if(@enable_server_cert_verification.eql?(true))
|
494
|
+
http.ca_file = @ca_file
|
495
|
+
http.verify_mode = OpenSSL::SSL::VERIFY_PEER | OpenSSL::SSL::VERIFY_FAIL_IF_NO_PEER_CERT
|
496
|
+
unless(@enable_hostname_verification)
|
497
|
+
OpenSSL::SSL.module_eval do
|
498
|
+
verify_method = method(:verify_certificate_identity)
|
499
|
+
metaclass = class << OpenSSL::SSL; self; end
|
500
|
+
metaclass.send :define_method, :verify_certificate_identity do |cert, hostname|
|
501
|
+
true
|
502
|
+
end
|
503
|
+
end
|
504
|
+
end
|
505
|
+
end
|
506
|
+
# Client Certificate Verification
|
507
|
+
if(@cert_file != nil)
|
508
|
+
pem = File.read(@cert_file)
|
509
|
+
http.cert = OpenSSL::X509::Certificate.new(pem)
|
510
|
+
# @key_file is nil when the certificate and key are in the same file (@cert_file)
|
511
|
+
if(@key_file == nil)
|
512
|
+
http.key = OpenSSL::PKey::RSA.new(pem)
|
513
|
+
else
|
514
|
+
key = File.read(@key_file)
|
515
|
+
http.key = OpenSSL::PKey::RSA.new(key, @key_passwd)
|
516
|
+
end
|
517
|
+
end
|
518
|
+
end
|
519
|
+
if(@timeout > 0)
|
520
|
+
http.open_timeout = @timeout
|
521
|
+
http.read_timeout = @timeout
|
522
|
+
end
|
523
|
+
request = Net::HTTP::Post.new(@url)
|
524
|
+
if(!@style.eql?("HOSTS"))
|
525
|
+
request.basic_auth @user, @password
|
526
|
+
end
|
527
|
+
request.content_type = "text/xml; charset=\"UTF-8\""
|
528
|
+
request.body = content
|
529
|
+
response = http.start {|http| http.request(request)}
|
530
|
+
rescue Timeout::Error => msg
|
531
|
+
print("\nError : ")
|
532
|
+
return fail_response(13001, msg)
|
533
|
+
rescue Errno::ECONNREFUSED => msg
|
534
|
+
print("\nError : ")
|
535
|
+
return fail_response(111, msg)
|
536
|
+
rescue OpenSSL::SSL::SSLError => msg
|
537
|
+
return fail_response(13001, msg)
|
538
|
+
rescue => msg
|
539
|
+
print("\nError : ")
|
540
|
+
return fail_response(13001, msg)
|
541
|
+
end
|
542
|
+
|
543
|
+
if(!response)
|
544
|
+
return fail_response(13001,"No response received")
|
545
|
+
end
|
546
|
+
if(response.code.eql?("401"))
|
547
|
+
return fail_response(13002,"Authorization failed")
|
548
|
+
end
|
549
|
+
return parse_xml(response.body)
|
550
|
+
end
|
551
|
+
|
552
|
+
|
553
|
+
#A convenience routine which wraps invoke_elem().
|
554
|
+
#It constructs an NaElement with name $api, and for
|
555
|
+
#each argument name/value pair, adds a child element
|
556
|
+
#to it. It's an error to have an even number of
|
557
|
+
#arguments to this function.
|
558
|
+
#Example: myserver->invoke('snapshot-create',
|
559
|
+
# 'snapshot', 'mysnapshot',
|
560
|
+
# 'volume', 'vol0');
|
561
|
+
#
|
562
|
+
|
563
|
+
def invoke(api, *args)
|
564
|
+
num_parms = args.length
|
565
|
+
if ((num_parms & 1) != 0)
|
566
|
+
return self.fail_response(13001, "in Zapi::invoke, invalid number of parameters")
|
567
|
+
end
|
568
|
+
xi = NaElement.new(api)
|
569
|
+
i = 0
|
570
|
+
while(i < num_parms)
|
571
|
+
key = args[i]
|
572
|
+
i = i + 1
|
573
|
+
value = args[i]
|
574
|
+
i = i + 1
|
575
|
+
xi.child_add(NaElement.new(key, value))
|
576
|
+
end
|
577
|
+
return invoke_elem(xi)
|
578
|
+
end
|
579
|
+
|
580
|
+
|
581
|
+
#Sets the vfiler name. This function is used for vfiler-tunneling.
|
582
|
+
|
583
|
+
def set_vfiler(vfiler_name)
|
584
|
+
if(@major_version >= 1 and @minor_version >= 7)
|
585
|
+
@vfiler = vfiler_name
|
586
|
+
return 1
|
587
|
+
end
|
588
|
+
return 0
|
589
|
+
end
|
590
|
+
|
591
|
+
|
592
|
+
# Sets the vserver name. This function is used for vfiler-tunneling.
|
593
|
+
# However, vserver tunneling actually uses vfiler-tunneling.
|
594
|
+
# Hence this function internally sets the vfiler name.
|
595
|
+
|
596
|
+
def set_vserver(vserver_name)
|
597
|
+
if(@major_version >= 1 and @minor_version >= 15)
|
598
|
+
@vfiler = vserver_name
|
599
|
+
return 1
|
600
|
+
end
|
601
|
+
print("\nONTAPI version must be at least 1.15 to send API to a vserver\n")
|
602
|
+
return 0
|
603
|
+
end
|
604
|
+
|
605
|
+
|
606
|
+
# Gets the vserver name. This function is added for vserver-tunneling.
|
607
|
+
# However, vserver tunneling actually uses vfiler-tunneling. Hence this
|
608
|
+
# function actually returns the vfiler name.
|
609
|
+
|
610
|
+
def get_vserver()
|
611
|
+
return @vfiler
|
612
|
+
end
|
613
|
+
|
614
|
+
|
615
|
+
# Function to set the originator_id before executing any ONTAP API.
|
616
|
+
|
617
|
+
def set_originator_id(originator_id)
|
618
|
+
@originator_id = originator_id
|
619
|
+
return 1
|
620
|
+
end
|
621
|
+
|
622
|
+
|
623
|
+
# Gets the originator_id for the given server context on which the
|
624
|
+
# ONTAP API commands get invoked.
|
625
|
+
|
626
|
+
def get_originator_id()
|
627
|
+
return @originator_id
|
628
|
+
end
|
629
|
+
|
630
|
+
#Sets the connection timeout value, in seconds,for the given server context.
|
631
|
+
|
632
|
+
def set_timeout(timeout)
|
633
|
+
@timeout = timeout
|
634
|
+
end
|
635
|
+
|
636
|
+
|
637
|
+
#Retrieves the connection timeout value (in seconds) for the given server context.
|
638
|
+
|
639
|
+
def get_timeout()
|
640
|
+
return @timeout
|
641
|
+
end
|
642
|
+
|
643
|
+
|
644
|
+
# Enables or disables server certificate verification by the client.
|
645
|
+
# Server certificate verification is enabled by default when style
|
646
|
+
# is set to CERTIFICATE. Hostname (CN) verification is always enabled
|
647
|
+
# during server certificate verification.
|
648
|
+
|
649
|
+
def set_server_cert_verification(enable)
|
650
|
+
unless(enable.eql?(true) or enable.eql?(false))
|
651
|
+
return fail_response(13001, "NaServer::set_server_cert_verification: invalid argument " + enable + "specified");
|
652
|
+
end
|
653
|
+
unless (use_https())
|
654
|
+
return fail_response(13001, "NaServer::set_server_cert_verification: server certificate verification can only be enabled or disabled for HTTPS transport")
|
655
|
+
end
|
656
|
+
@enable_server_cert_verification = enable
|
657
|
+
@enable_hostname_verification = enable
|
658
|
+
return nil
|
659
|
+
end
|
660
|
+
|
661
|
+
|
662
|
+
# Determines whether server certificate verification is enabled or not.
|
663
|
+
# Returns true if it is enabled, else returns false.
|
664
|
+
|
665
|
+
|
666
|
+
def is_server_cert_verification_enabled()
|
667
|
+
return @enable_server_cert_verification
|
668
|
+
end
|
669
|
+
|
670
|
+
|
671
|
+
# Sets the client certificate and key files that are required for client authentication
|
672
|
+
# by the server using certificates. If key file is not defined, then the certificate file
|
673
|
+
# will be used as the key file.
|
674
|
+
|
675
|
+
|
676
|
+
def set_client_cert_and_key (cert_file, key_file = nil, key_passwd = nil)
|
677
|
+
unless(cert_file)
|
678
|
+
return fail_response(13001, "NaServer::set_client_cert_and_key: certificate file not specified")
|
679
|
+
end
|
680
|
+
@cert_file = cert_file
|
681
|
+
@key_file = key_file
|
682
|
+
if(key_passwd == nil)
|
683
|
+
@key_passwd = ""
|
684
|
+
else
|
685
|
+
@key_passwd = key_passwd
|
686
|
+
end
|
687
|
+
|
688
|
+
return nil
|
689
|
+
end
|
690
|
+
|
691
|
+
|
692
|
+
# Specifies the certificates of the Certificate Authorities (CAs) that are
|
693
|
+
# trusted by this application and that will be used to verify the server certificate.
|
694
|
+
|
695
|
+
|
696
|
+
def set_ca_certs (ca_file)
|
697
|
+
if(ca_file == nil)
|
698
|
+
return fail_response(13001, "NaServer::set_ca_certs: missing CA certificate file")
|
699
|
+
end
|
700
|
+
@ca_file = ca_file
|
701
|
+
|
702
|
+
return nil
|
703
|
+
end
|
704
|
+
|
705
|
+
|
706
|
+
# Enables or disables hostname verification by the client during server certificate the
|
707
|
+
# server certificate.
|
708
|
+
|
709
|
+
|
710
|
+
def set_hostname_verification (enable)
|
711
|
+
unless(enable.eql?(true) or enable.eql?(false))
|
712
|
+
return fail_response(13001, "NaServer::set_hostname_verification: invalid argument " + enable + "specified");
|
713
|
+
end
|
714
|
+
unless (@enable_server_cert_verification)
|
715
|
+
return fail_response(13001, "NaServer::set_hostname_verification: server certificate verification is not enabled")
|
716
|
+
end
|
717
|
+
@enable_hostname_verification = enable
|
718
|
+
return nil
|
719
|
+
end
|
720
|
+
|
721
|
+
|
722
|
+
# Determines whether hostname verification is enabled or not.
|
723
|
+
# Returns true if it is enabled, else returns false
|
724
|
+
|
725
|
+
|
726
|
+
def is_hostname_verification_enabled ()
|
727
|
+
return @enable_hostname_verification
|
728
|
+
end
|
729
|
+
|
730
|
+
|
731
|
+
|
732
|
+
|
733
|
+
|
734
|
+
|
735
|
+
# "private" subroutines for use by the public routines
|
736
|
+
# This is a private function, not to be called from outside NaServer
|
737
|
+
# This is used when the transmission path fails, and we don't actually
|
738
|
+
# get back any XML from the server.
|
739
|
+
|
740
|
+
def fail_response(errno, reason)
|
741
|
+
n = NaElement.new("results")
|
742
|
+
n.attr_set("status", "failed")
|
743
|
+
n.attr_set("reason", reason)
|
744
|
+
n.attr_set("errno", errno)
|
745
|
+
return n
|
746
|
+
end
|
747
|
+
end
|
748
|
+
|
749
|
+
|
750
|
+
class MyListener
|
751
|
+
|
752
|
+
def tag_start(element, attributes)
|
753
|
+
n = NaElement.new(element)
|
754
|
+
$tag_element_stack.push(element)
|
755
|
+
$ZAPI_stack.push(n)
|
756
|
+
attributes.each { |key, value| $ZAPI_atts[key] = value ; n.attr_set(key, value) }
|
757
|
+
end
|
758
|
+
|
759
|
+
def tag_end(element)
|
760
|
+
stack_len = $ZAPI_stack.length
|
761
|
+
if($tag_element_stack.length <= 0)
|
762
|
+
print("\nError : Missing start tag for " + element + "\n")
|
763
|
+
exit
|
764
|
+
end
|
765
|
+
tag_element = $tag_element_stack.pop()
|
766
|
+
if(not tag_element.eql?(element))
|
767
|
+
print("\nError : Missing start tag for " + element + "\n")
|
768
|
+
exit
|
769
|
+
end
|
770
|
+
if(stack_len > 1)
|
771
|
+
n = $ZAPI_stack.pop()
|
772
|
+
i = $ZAPI_stack.length
|
773
|
+
if(i != stack_len - 1)
|
774
|
+
print("pop did not work!!!!\n")
|
775
|
+
end
|
776
|
+
$ZAPI_stack[i-1].child_add(n)
|
777
|
+
end
|
778
|
+
end
|
779
|
+
|
780
|
+
def text(text)
|
781
|
+
text = text.chomp
|
782
|
+
i = $ZAPI_stack.length
|
783
|
+
if(text.length > 0 and i > 0)
|
784
|
+
text = NaElement.escapeHTML(text)
|
785
|
+
$ZAPI_stack[i-1].add_content(text)
|
786
|
+
end
|
787
|
+
end
|
788
|
+
end
|
789
|
+
|