tiebreaker 0.0.2
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.
- 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
|
+
|