Skip to content
Navigation Menu
{{ message }}
forked from MoroccoAgriServices/softlayer-python
-
Notifications
You must be signed in to change notification settings - Fork 1
Expand file tree
/
Copy pathstorage_utils.py
More file actions
454 lines (346 loc) · 15.6 KB
/
Copy pathstorage_utils.py
File metadata and controls
454 lines (346 loc) · 15.6 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
"""
SoftLayer.storage_utils
~~~~~~~~~~~~~~~
Utility functions used by File and Block Storage Managers
:license: MIT, see LICENSE for more details.
"""
from SoftLayer import exceptions
from SoftLayer import utils
ENDURANCE_TIERS = {
0.25: 100,
2: 200,
4: 300,
}
def populate_host_templates(host_templates,
hardware_ids=None,
virtual_guest_ids=None,
ip_address_ids=None,
subnet_ids=None):
"""Populate the given host_templates array with the IDs provided
:param host_templates: The array to which host templates will be added
:param hardware_ids: A List of SoftLayer_Hardware ids
:param virtual_guest_ids: A List of SoftLayer_Virtual_Guest ids
:param ip_address_ids: A List of SoftLayer_Network_Subnet_IpAddress ids
:param subnet_ids: A List of SoftLayer_Network_Subnet ids
"""
if hardware_ids is not None:
for hardware_id in hardware_ids:
host_templates.append({
'objectType': 'SoftLayer_Hardware',
'id': hardware_id
})
if virtual_guest_ids is not None:
for virtual_guest_id in virtual_guest_ids:
host_templates.append({
'objectType': 'SoftLayer_Virtual_Guest',
'id': virtual_guest_id
})
if ip_address_ids is not None:
for ip_address_id in ip_address_ids:
host_templates.append({
'objectType': 'SoftLayer_Network_Subnet_IpAddress',
'id': ip_address_id
})
if subnet_ids is not None:
for subnet_id in subnet_ids:
host_templates.append({
'objectType': 'SoftLayer_Network_Subnet',
'id': subnet_id
})
def get_package(manager, category_code):
"""Returns a product packaged based on type of storage.
:param manager: The storage manager which calls this function.
:param category_code: Category code of product package.
:return: Returns a packaged based on type of storage.
"""
_filter = utils.NestedDict({})
_filter['categories']['categoryCode'] = (
utils.query_filter(category_code))
_filter['statusCode'] = (utils.query_filter('ACTIVE'))
packages = manager.client.call(
'Product_Package', 'getAllObjects',
filter=_filter.to_dict(),
mask='id,name,items[prices[categories],attributes]'
)
if len(packages) == 0:
raise ValueError('No packages were found for %s' % category_code)
if len(packages) > 1:
raise ValueError('More than one package was found for %s'
% category_code)
return packages[0]
def get_location_id(manager, location):
"""Returns location id
:param manager: The storage manager which calls this function.
:param location: Datacenter short name
:return: Returns location id
"""
loc_svc = manager.client['Location_Datacenter']
datacenters = loc_svc.getDatacenters(mask='mask[longName,id,name]')
for datacenter in datacenters:
if datacenter['name'] == location:
location = datacenter['id']
return location
raise ValueError('Invalid datacenter name specified.')
def find_endurance_price(package, price_category):
"""Find the price in the given package that has the specified category
:param package: The product package of the endurance storage type
:param price_category: The price category to search for
:return: Returns the price for the given category, or an error if not found
"""
for item in package['items']:
for price in item['prices']:
# Only collect prices from valid location groups.
if price['locationGroupId'] != '':
continue
if not _has_category(price['categories'], price_category):
continue
return price
raise ValueError("Could not find price for endurance storage")
def find_endurance_space_price(package, size, tier_level):
"""Find the price in the given package with the specified size and tier
:param package: The product package of the endurance storage type
:param size: The size for which a price is desired
:param tier_level: The endurance tier for which a price is desired
:return: Returns the price for the size and tier, or an error if not found
"""
for item in package['items']:
if int(item['capacity']) != size:
continue
for price in item['prices']:
# Only collect prices from valid location groups.
if price['locationGroupId'] != '':
continue
if not _has_category(price['categories'],
'performance_storage_space'):
continue
level = ENDURANCE_TIERS.get(tier_level)
if level < int(price['capacityRestrictionMinimum']):
continue
if level > int(price['capacityRestrictionMaximum']):
continue
return price
raise ValueError("Could not find price for disk space")
def find_endurance_tier_price(package, tier_level):
"""Find the price in the given package with the specified tier level
:param package: The product package of the endurance storage type
:param tier_level: The endurance tier for which a price is desired
:return: Returns the price for the given tier, or an error if not found
"""
for item in package['items']:
for attribute in item.get('attributes', []):
if int(attribute['value']) == ENDURANCE_TIERS.get(tier_level):
break
else:
continue
for price in item['prices']:
# Only collect prices from valid location groups.
if price['locationGroupId'] != '':
continue
if not _has_category(price['categories'], 'storage_tier_level'):
continue
return price
raise ValueError("Could not find price for tier")
def find_endurance_tier_iops_per_gb(volume):
"""Find the tier for the given endurance volume (IOPS per GB)
:param volume: The volume for which the tier level is desired
:return: Returns a float value indicating the IOPS per GB for the volume
"""
tier_description_split = volume['storageTierLevel']['description'].split()
if tier_description_split != []:
iops_per_gb = tier_description_split[0]
if iops_per_gb == '0.25':
return 0.25
if iops_per_gb == '2':
return 2.0
if iops_per_gb == '4':
return 4.0
raise ValueError("Could not find tier IOPS per GB for this volume")
def find_performance_price(package, price_category):
"""Find the price in the given package that has the specified category
:param package: The product package of the performance storage type
:param price_category: The price category to search for
:return: Returns the price for the given category, or an error if not found
"""
for item in package['items']:
for price in item['prices']:
# Only collect prices from valid location groups.
if price['locationGroupId'] != '':
continue
if not _has_category(price['categories'], price_category):
continue
return price
raise ValueError("Could not find price for performance storage")
def find_performance_space_price(package, size):
"""Find the price in the given package with the specified size
:param package: The product package of the performance storage type
:param size: The size for which a price is desired
:return: Returns the price for the given size, or an error if not found
"""
for item in package['items']:
if int(item['capacity']) != size:
continue
for price in item['prices']:
# Only collect prices from valid location groups.
if price['locationGroupId'] != '':
continue
if not _has_category(price['categories'],
'performance_storage_space'):
continue
return price
raise ValueError("Could not find disk space price for the given volume")
def find_performance_iops_price(package, size, iops):
"""Find the price in the given package with the specified size and iops
:param package: The product package of the performance storage type
:param size: The size for which a price is desired
:param iops: The number of IOPS for which a price is desired
:return: Returns the price for the size and IOPS, or an error if not found
"""
for item in package['items']:
if int(item['capacity']) != int(iops):
continue
for price in item['prices']:
# Only collect prices from valid location groups.
if price['locationGroupId'] != '':
continue
if not _has_category(price['categories'],
'performance_storage_iops'):
continue
if size < int(price['capacityRestrictionMinimum']):
continue
if size > int(price['capacityRestrictionMaximum']):
continue
return price
raise ValueError("Could not find price for iops for the given volume")
def find_replication_price(package, capacity, tier_level):
"""Find the price in the given package for the desired replicant volume
:param package: The product package of the endurance storage type
:param capacity: The capacity of the primary storage volume
:param tier_level: The tier of the primary storage volume
:return: Returns the price for the given size, or an error if not found
"""
for item in package['items']:
if int(item['capacity']) != capacity:
continue
for price in item['prices']:
# Only collect prices from valid location groups.
if price['locationGroupId'] != '':
continue
if not _has_category(price['categories'],
'performance_storage_replication'):
continue
level = ENDURANCE_TIERS.get(tier_level)
if level < int(price['capacityRestrictionMinimum']):
continue
if level > int(price['capacityRestrictionMaximum']):
continue
return price
raise ValueError("Could not find price for replicant volume")
def find_snapshot_space_price(package, size, tier_level):
"""Find the price in the given package for the desired snapshot space size
:param package: The product package of the endurance storage type
:param size: The snapshot space size for which a price is desired
:param tier_level: The tier of the volume for which space is being ordered
:return: Returns the price for the given size, or an error if not found
"""
for item in package['items']:
if int(item['capacity']) != size:
continue
for price in item['prices']:
# Only collect prices from valid location groups.
if price['locationGroupId'] != '':
continue
if not _has_category(price['categories'],
'storage_snapshot_space'):
continue
level = ENDURANCE_TIERS.get(tier_level)
if level < int(price['capacityRestrictionMinimum']):
continue
if level > int(price['capacityRestrictionMaximum']):
continue
return price
raise ValueError("Could not find price for snapshot space")
def find_snapshot_schedule_id(volume, snapshot_schedule_keyname):
"""Find the snapshot schedule ID for the given volume and keyname
:param volume: The volume for which the snapshot ID is desired
:param snapshot_schedule_keyname: The keyname of the snapshot schedule
:return: Returns an int value indicating the volume's snapshot schedule ID
"""
for schedule in volume['schedules']:
if 'type' in schedule and 'keyname' in schedule['type']:
if schedule['type']['keyname'] == snapshot_schedule_keyname:
return schedule['id']
raise ValueError("The given snapshot schedule ID was not found for "
"the given storage volume")
def prepare_replicant_order_object(manager, volume_id, snapshot_schedule,
location, tier, volume, volume_type):
"""Prepare the order object which is submitted to the placeOrder() method
:param manager: The File or Block manager calling this function
:param volume_id: The ID of the primary volume to be replicated
:param snapshot_schedule: The primary volume's snapshot
schedule to use for replication
:param location: The location for the ordered replicant volume
:param tier: The tier (IOPS per GB) of the primary volume
:param volume: The primary volume as a SoftLayer_Network_Storage object
:param volume_type: The type of the primary volume ('file' or 'block')
:return: Returns the order object for the
Product_Order service's placeOrder() method
"""
try:
location_id = get_location_id(manager, location)
except ValueError:
raise exceptions.SoftLayerError(
"Invalid data center name specified. "
"Please provide the lower case short name (e.g.: dal09)")
volume_capacity = int(volume['capacityGb'])
storage_type = volume['billingItem']['categoryCode']
if storage_type != 'storage_service_enterprise':
raise exceptions.SoftLayerError(
"Primary volume storage_type must be Endurance")
if 'snapshotCapacityGb' in volume:
volume_snapshot_capacity = int(volume['snapshotCapacityGb'])
else:
raise exceptions.SoftLayerError(
"Snapshot capacity not found for the given primary volume")
snapshot_schedule_id = find_snapshot_schedule_id(
volume,
'SNAPSHOT_' + snapshot_schedule
)
if volume['billingItem']['cancellationDate'] != '':
raise exceptions.SoftLayerError(
'This volume is set for cancellation; '
'unable to order replicant volume')
for child in volume['billingItem']['activeChildren']:
if child['categoryCode'] == 'storage_snapshot_space'\
and child['cancellationDate'] != '':
raise exceptions.SoftLayerError(
'The snapshot space for this volume is set for '
'cancellation; unable to order replicant volume')
if tier is None:
tier = find_endurance_tier_iops_per_gb(volume)
package = get_package(manager, storage_type)
prices = [
find_endurance_price(package, 'storage_service_enterprise'),
find_endurance_price(package, 'storage_' + volume_type),
find_endurance_tier_price(package, tier),
find_endurance_space_price(package, volume_capacity, tier),
find_snapshot_space_price(package, volume_snapshot_capacity, tier),
find_replication_price(package, volume_capacity, tier),
]
replicant_order = {
'complexType': 'SoftLayer_Container_Product_Order_'
'Network_Storage_Enterprise',
'packageId': package['id'],
'prices': prices,
'quantity': 1,
'location': location_id,
'originVolumeId': int(volume_id),
'originVolumeScheduleId': snapshot_schedule_id,
}
return replicant_order
def _has_category(categories, category_code):
return any(
True
for category
in categories
if category['categoryCode'] == category_code
)
You can’t perform that action at this time.
