hpe3par_sdk 1.0.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.gitignore +55 -0
- data/.rspec +2 -0
- data/.travis.yml +9 -0
- data/Gemfile +15 -0
- data/LICENSE +201 -0
- data/README.md +192 -0
- data/Rakefile +103 -0
- data/bin/console +25 -0
- data/bin/setup +20 -0
- data/hpe3par_sdk.gemspec +45 -0
- data/lib/Hpe3parSdk.rb +19 -0
- data/lib/Hpe3parSdk/client.rb +2647 -0
- data/lib/Hpe3parSdk/constants.rb +421 -0
- data/lib/Hpe3parSdk/cpg_manager.rb +72 -0
- data/lib/Hpe3parSdk/exceptions.rb +503 -0
- data/lib/Hpe3parSdk/flash_cache_manager.rb +54 -0
- data/lib/Hpe3parSdk/host_manager.rb +152 -0
- data/lib/Hpe3parSdk/host_set_manager.rb +128 -0
- data/lib/Hpe3parSdk/http.rb +164 -0
- data/lib/Hpe3parSdk/models.rb +1850 -0
- data/lib/Hpe3parSdk/multi_log.rb +86 -0
- data/lib/Hpe3parSdk/port_manager.rb +62 -0
- data/lib/Hpe3parSdk/qos_manager.rb +64 -0
- data/lib/Hpe3parSdk/ssh.rb +46 -0
- data/lib/Hpe3parSdk/task_manager.rb +98 -0
- data/lib/Hpe3parSdk/tcl_parser.rb +15 -0
- data/lib/Hpe3parSdk/util.rb +30 -0
- data/lib/Hpe3parSdk/version.rb +14 -0
- data/lib/Hpe3parSdk/vlun_manager.rb +119 -0
- data/lib/Hpe3parSdk/volume_manager.rb +292 -0
- data/lib/Hpe3parSdk/volume_set_manager.rb +126 -0
- data/lib/Hpe3parSdk/wsapi_version.rb +73 -0
- metadata +194 -0
@@ -0,0 +1,292 @@
|
|
1
|
+
# (c) Copyright 2016-2017 Hewlett Packard Enterprise Development LP
|
2
|
+
#
|
3
|
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
4
|
+
# you may not use this file except in compliance with the License.
|
5
|
+
# You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
|
6
|
+
#
|
7
|
+
# Unless required by applicable law or agreed to in writing, software distributed
|
8
|
+
# under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
|
9
|
+
# CONDITIONS OF ANY KIND, either express or implied. See the License for the
|
10
|
+
# specific language governing permissions and limitations under the License.
|
11
|
+
|
12
|
+
require_relative 'util'
|
13
|
+
require_relative 'constants'
|
14
|
+
require_relative 'exceptions'
|
15
|
+
require_relative 'task_manager'
|
16
|
+
require_relative 'ssh'
|
17
|
+
require_relative 'volume_set_manager'
|
18
|
+
require_relative 'models'
|
19
|
+
|
20
|
+
module Hpe3parSdk
|
21
|
+
class VolumeManager
|
22
|
+
def initialize(http, ssh, app_type)
|
23
|
+
@http = http
|
24
|
+
@ssh = ssh
|
25
|
+
@task = TaskManager.new(http)
|
26
|
+
@volume_set = VolumeSetManager.new(http)
|
27
|
+
@app_type = app_type
|
28
|
+
end
|
29
|
+
|
30
|
+
def get_volumes(volume_type)
|
31
|
+
volumes = Array[]
|
32
|
+
response = @http.get('/volumes')
|
33
|
+
response[1]['members'].each do |member|
|
34
|
+
volumes.push(VirtualVolume.new(member)) if member['copyType'] == volume_type
|
35
|
+
end
|
36
|
+
volumes
|
37
|
+
end
|
38
|
+
|
39
|
+
def get_volume(name)
|
40
|
+
if name.nil? || name.strip.empty?
|
41
|
+
raise 'Volume name cannot be nil or empty'
|
42
|
+
else
|
43
|
+
VirtualVolume.new(@http.get("/volumes/#{name}")[1])
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
def get_volume_by_wwn(wwn)
|
48
|
+
response = @http.get("/volumes?query=\"wwn EQ #{wwn}\"")
|
49
|
+
if response[1].key?('members') && !response[1]['members'].empty?
|
50
|
+
return VirtualVolume.new(response[1]['members'][0])
|
51
|
+
else
|
52
|
+
raise HTTPNotFound.new(nil, "Volume with WWN #{wwn} does not exist", nil, 404)
|
53
|
+
end
|
54
|
+
end
|
55
|
+
|
56
|
+
def create_volume(name, cpg_name, size_mib, optional = nil)
|
57
|
+
info = { 'name' => name,
|
58
|
+
'cpg' => cpg_name,
|
59
|
+
'sizeMiB' => size_mib,
|
60
|
+
#Adding information related to telemetry
|
61
|
+
'objectKeyValues' => [
|
62
|
+
{
|
63
|
+
'key' => 'type' ,
|
64
|
+
'value' => @app_type
|
65
|
+
}
|
66
|
+
]
|
67
|
+
}
|
68
|
+
|
69
|
+
info = Util.merge_hash(info, optional) if optional
|
70
|
+
volumes_url = '/volumes'
|
71
|
+
@http.post(volumes_url, body: info)
|
72
|
+
end
|
73
|
+
|
74
|
+
def get_volume_snapshot_names(name)
|
75
|
+
snapshots = []
|
76
|
+
headers, body = @http.get("/volumes?query=\"copyOf EQ #{name}\"")
|
77
|
+
for member in body['members']
|
78
|
+
snapshots.push(member['name'])
|
79
|
+
end
|
80
|
+
snapshots
|
81
|
+
end
|
82
|
+
|
83
|
+
def get_volume_snapshots(name)
|
84
|
+
response = @http.get("/volumes?query=\"copyOf EQ #{name}\"")
|
85
|
+
volume_snapshots = []
|
86
|
+
response[1]['members'].each do |snapshot|
|
87
|
+
volume_snapshots.push(VirtualVolume.new(snapshot))
|
88
|
+
end
|
89
|
+
volume_snapshots
|
90
|
+
end
|
91
|
+
|
92
|
+
def delete_volume(name)
|
93
|
+
response = @http.delete("/volumes/#{name}")
|
94
|
+
response[1]
|
95
|
+
end
|
96
|
+
|
97
|
+
def modify_volume(name, volume_mods)
|
98
|
+
@http.put("/volumes/#{name}", body: volume_mods)
|
99
|
+
if volume_mods.key? ('newName') && !volume_mods['newName'].nil?
|
100
|
+
name = volume_mods['newName']
|
101
|
+
end
|
102
|
+
setVolumeMetaData(name, 'type', @app_type)
|
103
|
+
end
|
104
|
+
|
105
|
+
def grow_volume(name, amount)
|
106
|
+
info = { 'action' => VolumeCustomAction::GROW_VOLUME, 'sizeMiB' => amount }
|
107
|
+
response = @http.put("/volumes/#{name}", body: info)
|
108
|
+
response[1]
|
109
|
+
end
|
110
|
+
|
111
|
+
def create_physical_copy(src_name, dest_name, dest_cpg, optional = nil)
|
112
|
+
parameters = { :destVolume => dest_name, :destCPG => dest_cpg }
|
113
|
+
parameters = Util.merge_hash(parameters, optional) if optional
|
114
|
+
|
115
|
+
|
116
|
+
if !parameters.key?(:online) || !((parameters[:online]))
|
117
|
+
# 3Par won't allow destCPG to be set if it's not an online copy.
|
118
|
+
parameters.delete(:destCPG)
|
119
|
+
end
|
120
|
+
|
121
|
+
info = { :action => 'createPhysicalCopy', :parameters => parameters }
|
122
|
+
|
123
|
+
response = @http.post("/volumes/#{src_name}", body: info)
|
124
|
+
response[1]
|
125
|
+
end
|
126
|
+
|
127
|
+
def stop_offline_physical_copy(volume_name)
|
128
|
+
_sync_physical_copy(volume_name, VolumeCustomAction::STOP_PHYSICAL_COPY)
|
129
|
+
end
|
130
|
+
|
131
|
+
def is_online_physical_copy(name)
|
132
|
+
task = _find_task(name, active=true)
|
133
|
+
if task.nil?
|
134
|
+
false
|
135
|
+
else
|
136
|
+
true
|
137
|
+
end
|
138
|
+
end
|
139
|
+
|
140
|
+
def stop_online_physical_copy(name)
|
141
|
+
task = _find_task(name, active=false)
|
142
|
+
unless task.nil?
|
143
|
+
task_id = task[0].split(",")[0]
|
144
|
+
unless task_id.nil?
|
145
|
+
cmd = ['canceltask', '-f', task_id]
|
146
|
+
command = cmd.join(" ")
|
147
|
+
result = @ssh.run(command)
|
148
|
+
unless result.include? "is not active"
|
149
|
+
ready = false
|
150
|
+
while ready == false
|
151
|
+
sleep(1)
|
152
|
+
task = _find_task(name, false)
|
153
|
+
if task.nil?
|
154
|
+
ready = true
|
155
|
+
end
|
156
|
+
end
|
157
|
+
end
|
158
|
+
end
|
159
|
+
end
|
160
|
+
end
|
161
|
+
|
162
|
+
def _find_task(name, active=false)
|
163
|
+
cmd = ['showtask']
|
164
|
+
if active
|
165
|
+
cmd.push('-active')
|
166
|
+
end
|
167
|
+
cmd.push(name)
|
168
|
+
command = cmd.join(" ")
|
169
|
+
|
170
|
+
result = @ssh.run(command).split("\n")
|
171
|
+
if result[0].gsub("\n",'') =='No tasks.'
|
172
|
+
return nil
|
173
|
+
end
|
174
|
+
result
|
175
|
+
end
|
176
|
+
|
177
|
+
def resync_physical_copy(volume_name)
|
178
|
+
_sync_physical_copy(volume_name, VolumeCustomAction::RESYNC_PHYSICAL_COPY)
|
179
|
+
end
|
180
|
+
|
181
|
+
def _sync_physical_copy(volume_name, action)
|
182
|
+
info = { 'action' => action }
|
183
|
+
response = @http.put("/volumes/#{volume_name}", body: info)
|
184
|
+
response[1]
|
185
|
+
end
|
186
|
+
|
187
|
+
def get_online_physical_copy_status(name)
|
188
|
+
status = nil
|
189
|
+
tasks = @task.get_all_tasks
|
190
|
+
tasks.each do |task|
|
191
|
+
status = task['status'] if task['name'] == name &&
|
192
|
+
task['type'] == TaskType::ONLINE_COPY
|
193
|
+
end
|
194
|
+
raise HPE3PARException.new(nil, 'Volume not an online physical copy') if status.nil?
|
195
|
+
status
|
196
|
+
end
|
197
|
+
|
198
|
+
def tune_volume(name, tune_operation, optional = nil)
|
199
|
+
info = { 'action' => VolumeCustomAction::TUNE_VOLUME,
|
200
|
+
'tuneOperation' => tune_operation }
|
201
|
+
info = Util.merge_hash(info, optional) if optional
|
202
|
+
response = @http.put("/volumes/#{name}", body: info)
|
203
|
+
response[1]
|
204
|
+
end
|
205
|
+
|
206
|
+
|
207
|
+
|
208
|
+
def create_snapshot(name, copy_of_name, optional = nil)
|
209
|
+
parameters = { 'name' => name }
|
210
|
+
parameters = Util.merge_hash(parameters, optional) if optional
|
211
|
+
info = { 'action' => 'createSnapshot',
|
212
|
+
'parameters' => parameters }
|
213
|
+
|
214
|
+
response = @http.post("/volumes/#{copy_of_name}", body: info)
|
215
|
+
response[1]
|
216
|
+
end
|
217
|
+
|
218
|
+
def restore_snapshot(name, optional = nil)
|
219
|
+
info = { 'action' => VolumeCustomAction::PROMOTE_VIRTUAL_COPY }
|
220
|
+
info = Util.merge_hash(info, optional) if optional
|
221
|
+
|
222
|
+
response = @http.put("/volumes/#{name}", body: info)
|
223
|
+
response[1]
|
224
|
+
end
|
225
|
+
|
226
|
+
def setVolumeMetaData(name, key, value)
|
227
|
+
key_exists = false
|
228
|
+
info = {
|
229
|
+
'key' => key,
|
230
|
+
'value' => value
|
231
|
+
}
|
232
|
+
|
233
|
+
begin
|
234
|
+
response = @http.post("/volumes/#{name}/objectKeyValues", body: info)
|
235
|
+
rescue Hpe3parSdk::HTTPConflict => ex
|
236
|
+
key_exists = true
|
237
|
+
rescue
|
238
|
+
end
|
239
|
+
|
240
|
+
if key_exists
|
241
|
+
info = {
|
242
|
+
'value' => value
|
243
|
+
}
|
244
|
+
begin
|
245
|
+
response = @http.put("/volumes/#{name}/objectKeyValues/#{key}", body: info)
|
246
|
+
rescue; end
|
247
|
+
end
|
248
|
+
end
|
249
|
+
|
250
|
+
def volume_exists?(name)
|
251
|
+
begin
|
252
|
+
get_volume(name)
|
253
|
+
return true
|
254
|
+
rescue Hpe3parSdk::HTTPNotFound => ex
|
255
|
+
return false
|
256
|
+
end
|
257
|
+
end
|
258
|
+
|
259
|
+
def volume_set_exists?(name)
|
260
|
+
begin
|
261
|
+
get_volume_set(name)
|
262
|
+
return true
|
263
|
+
rescue Hpe3parSdk::HTTPNotFound => ex
|
264
|
+
return false
|
265
|
+
end
|
266
|
+
end
|
267
|
+
|
268
|
+
def online_physical_copy_exists?(src_name, phy_copy_name)
|
269
|
+
begin
|
270
|
+
if volume_exists?(src_name) and volume_exists?(phy_copy_name) and !_find_task(phy_copy_name,true).nil?
|
271
|
+
return true
|
272
|
+
else
|
273
|
+
return false
|
274
|
+
end
|
275
|
+
rescue Hpe3parSdk::HTTPNotFound => ex
|
276
|
+
return false
|
277
|
+
end
|
278
|
+
end
|
279
|
+
|
280
|
+
def offline_physical_copy_exists?(src_name, phy_copy_name)
|
281
|
+
begin
|
282
|
+
if volume_exists?(src_name) and volume_exists?(phy_copy_name) and !_find_task(src_name + "->" + phy_copy_name,true).nil?
|
283
|
+
return true
|
284
|
+
else
|
285
|
+
return false
|
286
|
+
end
|
287
|
+
rescue Hpe3parSdk::HTTPNotFound => ex
|
288
|
+
return false
|
289
|
+
end
|
290
|
+
end
|
291
|
+
end
|
292
|
+
end
|
@@ -0,0 +1,126 @@
|
|
1
|
+
# (c) Copyright 2016-2017 Hewlett Packard Enterprise Development LP
|
2
|
+
#
|
3
|
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
4
|
+
# you may not use this file except in compliance with the License.
|
5
|
+
# You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
|
6
|
+
#
|
7
|
+
# Unless required by applicable law or agreed to in writing, software distributed
|
8
|
+
# under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
|
9
|
+
# CONDITIONS OF ANY KIND, either express or implied. See the License for the
|
10
|
+
# specific language governing permissions and limitations under the License.
|
11
|
+
|
12
|
+
require_relative 'util'
|
13
|
+
require_relative 'constants'
|
14
|
+
require_relative 'models'
|
15
|
+
|
16
|
+
module Hpe3parSdk
|
17
|
+
class VolumeSetManager
|
18
|
+
def initialize(http, host_and_vv_set_filter_supported = false)
|
19
|
+
@http = http
|
20
|
+
@vv_set_filter_supported = host_and_vv_set_filter_supported
|
21
|
+
end
|
22
|
+
|
23
|
+
def find_all_volume_sets(name)
|
24
|
+
vv_sets = []
|
25
|
+
if @vv_set_filter_supported
|
26
|
+
query = %("setmembers EQ #{name}")
|
27
|
+
response = @http.get("/volumesets?query=#{query}")
|
28
|
+
volume_sets = response[1]['members']
|
29
|
+
volume_sets.each do |volume_set|
|
30
|
+
vv_sets.push(VolumeSet.new(volume_set))
|
31
|
+
end
|
32
|
+
else
|
33
|
+
volume_sets = get_volume_sets
|
34
|
+
volume_sets.each do |volume_set|
|
35
|
+
if !volume_set.setmembers.nil? && !volume_set.setmembers.empty? && volume_set.setmembers.include?(name)
|
36
|
+
vv_sets.push(volume_set)
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
40
|
+
vv_sets
|
41
|
+
end
|
42
|
+
|
43
|
+
def get_volume_sets
|
44
|
+
response = @http.get('/volumesets')
|
45
|
+
volume_set_members = []
|
46
|
+
response[1]['members'].each do |volume_set_member|
|
47
|
+
volume_set_members.push(VolumeSet.new(volume_set_member))
|
48
|
+
end
|
49
|
+
volume_set_members
|
50
|
+
end
|
51
|
+
|
52
|
+
def get_volume_set(name)
|
53
|
+
if name.nil? || name.strip.empty?
|
54
|
+
raise 'Volume set name cannot be nil or empty'
|
55
|
+
else
|
56
|
+
response = @http.get('/volumesets/' + name)
|
57
|
+
VolumeSet.new(response[1])
|
58
|
+
end
|
59
|
+
end
|
60
|
+
|
61
|
+
def create_volume_set(name, domain = nil, comment = nil, setmembers = nil)
|
62
|
+
info = { 'name' => name }
|
63
|
+
|
64
|
+
info['domain'] = domain if domain
|
65
|
+
|
66
|
+
info['comment'] = comment if comment
|
67
|
+
|
68
|
+
if setmembers
|
69
|
+
members = { 'setmembers' => setmembers }
|
70
|
+
info = Util.merge_hash(info, members)
|
71
|
+
end
|
72
|
+
@http.post('/volumesets', body: info)
|
73
|
+
end
|
74
|
+
|
75
|
+
def delete_volume_set(name)
|
76
|
+
@http.delete("/volumesets/#{name}")[1]
|
77
|
+
end
|
78
|
+
|
79
|
+
def modify_volume_set(name, action = nil, newName = nil, comment = nil, flash_cache_policy = nil, setmembers = nil)
|
80
|
+
info = {}
|
81
|
+
|
82
|
+
info['action'] = action if action
|
83
|
+
|
84
|
+
info['newName'] = newName if newName
|
85
|
+
|
86
|
+
info['comment'] = comment if comment
|
87
|
+
|
88
|
+
info['flashCachePolicy'] = flash_cache_policy if flash_cache_policy
|
89
|
+
|
90
|
+
if setmembers
|
91
|
+
members = { 'setmembers' => setmembers }
|
92
|
+
info = Util.merge_hash(info, members)
|
93
|
+
end
|
94
|
+
|
95
|
+
@http.put("/volumesets/#{name}", body: info)[1]
|
96
|
+
end
|
97
|
+
|
98
|
+
# QoS Priority Optimization methods
|
99
|
+
def add_volumes_to_volume_set(set_name, setmembers)
|
100
|
+
modify_volume_set(set_name, SetCustomAction::MEM_ADD, nil, nil, nil, setmembers)
|
101
|
+
end
|
102
|
+
|
103
|
+
def remove_volumes_from_volume_set(set_name, setmembers)
|
104
|
+
modify_volume_set(set_name, SetCustomAction::MEM_REMOVE, nil, nil, nil, setmembers)
|
105
|
+
end
|
106
|
+
|
107
|
+
def create_snapshot_of_volume_set(name, copy_of_name, optional = nil)
|
108
|
+
parameters = { 'name' => name }
|
109
|
+
parameters = Util.merge_hash(parameters, optional) if optional
|
110
|
+
|
111
|
+
info = { 'action' => 'createSnapshot', 'parameters' => parameters }
|
112
|
+
|
113
|
+
response = @http.post("/volumesets/#{copy_of_name}", body: info)
|
114
|
+
response[1]
|
115
|
+
end
|
116
|
+
|
117
|
+
def volume_set_exists?(name)
|
118
|
+
begin
|
119
|
+
get_volume_set(name)
|
120
|
+
return true
|
121
|
+
rescue Hpe3parSdk::HTTPNotFound => ex
|
122
|
+
return false
|
123
|
+
end
|
124
|
+
end
|
125
|
+
end
|
126
|
+
end
|
@@ -0,0 +1,73 @@
|
|
1
|
+
# (c) Copyright 2016-2017 Hewlett Packard Enterprise Development LP
|
2
|
+
#
|
3
|
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
4
|
+
# you may not use this file except in compliance with the License.
|
5
|
+
# You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
|
6
|
+
#
|
7
|
+
# Unless required by applicable law or agreed to in writing, software distributed
|
8
|
+
# under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
|
9
|
+
# CONDITIONS OF ANY KIND, either express or implied. See the License for the
|
10
|
+
# specific language governing permissions and limitations under the License.
|
11
|
+
|
12
|
+
module Hpe3parSdk
|
13
|
+
class WSAPIVersion
|
14
|
+
include Comparable
|
15
|
+
attr_accessor :major, :minor, :revision
|
16
|
+
|
17
|
+
def self.parse(version)
|
18
|
+
version_parts = version.split('.')
|
19
|
+
validate_version(version, version_parts)
|
20
|
+
|
21
|
+
@major = version_parts[0].to_i
|
22
|
+
@minor = version_parts[1].to_i
|
23
|
+
@revision = version_parts[2].to_i
|
24
|
+
obj_version = WSAPIVersion.new(@major, @minor, @revision)
|
25
|
+
return obj_version
|
26
|
+
end
|
27
|
+
|
28
|
+
def initialize(major, minor, revision)
|
29
|
+
@major = major
|
30
|
+
@minor = minor
|
31
|
+
@revision = revision
|
32
|
+
end
|
33
|
+
|
34
|
+
def <=>(other_version)
|
35
|
+
if major < other_version.major
|
36
|
+
return -1
|
37
|
+
end
|
38
|
+
|
39
|
+
if major > other_version.major
|
40
|
+
return 1
|
41
|
+
end
|
42
|
+
|
43
|
+
if minor < other_version.minor
|
44
|
+
return -1
|
45
|
+
end
|
46
|
+
|
47
|
+
if minor > other_version.minor
|
48
|
+
return 1
|
49
|
+
end
|
50
|
+
|
51
|
+
if revision < other_version.revision
|
52
|
+
return -1
|
53
|
+
end
|
54
|
+
|
55
|
+
if revision > other_version.revision
|
56
|
+
return 1
|
57
|
+
end
|
58
|
+
|
59
|
+
return 0
|
60
|
+
end
|
61
|
+
|
62
|
+
def to_s
|
63
|
+
major.to_s + '.' + minor.to_s + '.' + revision.to_s
|
64
|
+
end
|
65
|
+
|
66
|
+
private
|
67
|
+
def self.validate_version(version, version_parts)
|
68
|
+
if version_parts.length != 3
|
69
|
+
raise 'Invalid Version detected ' + version
|
70
|
+
end
|
71
|
+
end
|
72
|
+
end
|
73
|
+
end
|