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