Skip to content

Commit 47a55ef

Browse files
author
Kevin McDonald
committed
Adds block storage ordering tests
1 parent 653b0da commit 47a55ef

4 files changed

Lines changed: 203 additions & 48 deletions

File tree

SoftLayer/CLI/block/order.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -83,7 +83,7 @@ def cli(env, storage_type, size, iops, tier, os_type, location):
8383
storage_type='storage_service_enterprise',
8484
location=location,
8585
size=size,
86-
tier_level=tier,
86+
tier_level=float(tier),
8787
os_type=os_type
8888
)
8989
except ValueError as ex:

SoftLayer/managers/block.py

Lines changed: 48 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -10,9 +10,9 @@
1010

1111

1212
ENDURANCE_TIERS = {
13-
'0.25': '100',
14-
'2': '200',
15-
'4': '300'
13+
0.25: 100,
14+
2: 200,
15+
4: 300,
1616
}
1717

1818

@@ -22,11 +22,6 @@ class BlockStorageManager(utils.IdentifierMixin, object):
2222
def __init__(self, client):
2323
self.configuration = {}
2424
self.client = client
25-
self.account = client['Account']
26-
self.product_package = self.client['Product_Package']
27-
self.block_svc = self.client['Network_Storage']
28-
self.block_os_types = self.client['Network_Storage_Iscsi_OS_Type']
29-
self.product_order = self.client['Product_Order']
3025

3126
def list_block_volumes(self, datacenter=None, username=None,
3227
storage_type=None, **kwargs):
@@ -95,7 +90,8 @@ def get_block_volume_details(self, volume_id, **kwargs):
9590
'lunId',
9691
]
9792
kwargs['mask'] = "mask[%s]" % ','.join(items)
98-
return self.block_svc.getObject(id=volume_id, **kwargs)
93+
return self.client.call('Network_Storage', 'getObject',
94+
id=volume_id, **kwargs)
9995

10096
def get_block_volume_access_list(self, volume_id, **kwargs):
10197
"""Returns a list of authorized hosts for a specified volume.
@@ -113,7 +109,8 @@ def get_block_volume_access_list(self, volume_id, **kwargs):
113109
'allowedIpAddresses[allowedHost[credential]]',
114110
]
115111
kwargs['mask'] = "mask[%s]" % ','.join(items)
116-
return self.block_svc.getObject(id=volume_id, **kwargs)
112+
return self.client.call('Network_Storage', 'getObject',
113+
id=volume_id, **kwargs)
117114

118115
def get_block_volume_snapshot_list(self, volume_id, **kwargs):
119116
"""Returns a list of snapshots for the specified volume.
@@ -123,29 +120,34 @@ def get_block_volume_snapshot_list(self, volume_id, **kwargs):
123120
:return: Returns a list of snapshots for the specified volume.
124121
"""
125122
if 'mask' not in kwargs:
126-
items = [
127-
'snapshots.id',
128-
'snapshots.notes',
129-
'snapshots.snapshotSizeBytes',
130-
'snapshots.storageType.keyName',
131-
'snapshots.snapshotCreationTimestamp',
132-
'snapshots[hourlySchedule,dailySchedule,weeklySchedule]',
133-
]
123+
items = '''snapshots[
124+
id,
125+
notes,
126+
snapshotSizeBytes,
127+
storageType[keyName],
128+
snapshotCreationTimestamp
129+
hourlySchedule,
130+
dailySchedule,
131+
weeklySchedule
132+
]'''
134133
kwargs['mask'] = "mask[%s]" % ','.join(items)
135-
return self.block_svc.getObject(id=volume_id, **kwargs)
134+
return self.client.call('Network_Storage', 'getObject',
135+
id=volume_id, **kwargs)
136136

137137
def delete_snapshot(self, snapshot_id):
138138
"""Deletes the specified snapshot object.
139139
140140
:param snapshot_id: The ID of the snapshot object to delete.
141141
"""
142-
return self.block_svc.deleteObject(id=snapshot_id)
142+
return self.client.call('Network_Storage', 'deleteObject',
143+
id=snapshot_id)
143144

144145
def order_block_volume(self, storage_type, location, size, os_type,
145146
iops=None, tier_level=None):
146147
"""Places an order for a block volume.
147148
148-
:param storage_type: "Performance" or "Endurance"
149+
:param storage_type: "performance_storage_iscsi" (performance)
150+
or "storage_service_enterprise" (endurance)
149151
:param location: Datacenter in which to order iSCSI volume
150152
:param size: Size of the desired volume, in GB
151153
:param os_type: OS Type to use for volume alignment, see help for list
@@ -162,14 +164,14 @@ def order_block_volume(self, storage_type, location, size, os_type,
162164

163165
base_type_name = 'SoftLayer_Container_Product_Order_Network_'
164166
package = self._get_package(storage_type)
165-
if package['name'] == 'Performance':
167+
if storage_type == 'performance_storage_iscsi':
166168
complex_type = base_type_name + 'PerformanceStorage_Iscsi'
167169
prices = [
168170
_find_performance_block_price(package),
169171
_find_performance_space_price(package, iops),
170172
_find_performance_iops_price(package, size, iops),
171173
]
172-
elif package['name'] == 'Endurance':
174+
elif storage_type == 'storage_service_enterprise':
173175
complex_type = base_type_name + 'Storage_Enterprise'
174176
prices = [
175177
_find_endurance_block_price(package),
@@ -190,32 +192,34 @@ def order_block_volume(self, storage_type, location, size, os_type,
190192
'location': location_id,
191193
}
192194

193-
return self.product_order.placeOrder(order)
195+
return self.client.call('Product_Order', 'placeOrder', order)
194196

195-
def _get_package(self, category_code, **kwargs):
197+
def _get_package(self, category_code):
196198
"""Returns a product packaged based on type of storage.
197199
198200
:param category_code: Category code of product package.
199-
:param kwargs:
200201
:return: Returns a packaged based on type of storage.
201202
"""
202-
if 'mask' not in kwargs:
203-
items = [
204-
'id',
205-
'name',
206-
'items',
207-
'items[prices[categories],attributes]'
208-
]
209-
kwargs['mask'] = "mask[%s]" % ','.join(items)
210203

211-
_filter = utils.NestedDict(kwargs.get('filter') or {})
204+
_filter = utils.NestedDict({})
212205
_filter['categories']['categoryCode'] = (
213206
utils.query_filter(category_code))
214207
_filter['statusCode'] = (utils.query_filter('ACTIVE'))
215-
kwargs['filter'] = _filter.to_dict()
216208

217-
func = getattr(self.product_package, 'getAllObjects')
218-
return func(**kwargs).pop()
209+
packages = self.client.call('Product_Package', 'getAllObjects',
210+
filter=_filter.to_dict(),
211+
mask="""
212+
id,
213+
name,
214+
items[prices[categories],attributes]
215+
""")
216+
if len(packages) == 0:
217+
raise ValueError('No packages were found for %s' % category_code)
218+
if len(packages) > 1:
219+
raise ValueError('More than one package was found for %s'
220+
% category_code)
221+
222+
return packages[0]
219223

220224
def _get_location_id(self, location):
221225
"""Returns location id
@@ -297,7 +301,10 @@ def _find_endurance_space_price(package, size, tier_level):
297301
continue
298302

299303
level = ENDURANCE_TIERS.get(tier_level)
300-
if price['capacityRestrictionMinimum'] != level:
304+
if level < int(price['capacityRestrictionMinimum']):
305+
continue
306+
307+
if level > int(price['capacityRestrictionMaximum']):
301308
continue
302309

303310
return price
@@ -307,8 +314,8 @@ def _find_endurance_space_price(package, size, tier_level):
307314

308315
def _find_endurance_tier_price(package, tier_level):
309316
for item in package['items']:
310-
for attribute in item['attributes']:
311-
if attribute['value'] != ENDURANCE_TIERS.get(tier_level):
317+
for attribute in item.get('attributes', []):
318+
if int(attribute['value']) == ENDURANCE_TIERS.get(tier_level):
312319
break
313320
else:
314321
continue

tests/managers/block_tests.py

Lines changed: 152 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -45,7 +45,7 @@ def test_get_block_volume_access_list(self):
4545
identifier=100)
4646

4747
def test_get_block_volume_snapshot_list(self):
48-
result = self.block.get_block_volume_access_list(100)
48+
result = self.block.get_block_volume_snapshot_list(100)
4949

5050
self.assertEqual(fixtures.SoftLayer_Network_Storage.getObject,
5151
result)
@@ -59,3 +59,154 @@ def test_delete_snapshot(self):
5959
result)
6060
self.assert_called_with('SoftLayer_Network_Storage', 'deleteObject',
6161
identifier=100)
62+
63+
def test_order_block_volume_no_package(self):
64+
mock = self.set_mock('SoftLayer_Product_Package', 'getAllObjects')
65+
mock.return_value = []
66+
67+
self.assertRaises(
68+
ValueError,
69+
self.block.order_block_volume,
70+
"performance_storage_iscsi", "dal05", 100, "LINUX", iops=100,
71+
)
72+
73+
def test_order_block_volume_too_many_packages(self):
74+
mock = self.set_mock('SoftLayer_Product_Package', 'getAllObjects')
75+
mock.return_value = [{}, {}]
76+
77+
self.assertRaises(
78+
ValueError,
79+
self.block.order_block_volume,
80+
"performance_storage_iscsi", "dal05", 100, "LINUX", iops=100,
81+
)
82+
83+
def test_order_block_volume_performance(self):
84+
mock = self.set_mock('SoftLayer_Product_Package', 'getAllObjects')
85+
mock.return_value = [{
86+
'id': 1,
87+
'name': 'Performance',
88+
'items': [{
89+
'capacity': '1',
90+
'prices': [{
91+
'id': 1,
92+
'locationGroupId': '',
93+
'categories': [{
94+
'categoryCode': 'performance_storage_iscsi',
95+
}],
96+
}],
97+
}, {
98+
'capacity': '100',
99+
'prices': [{
100+
'id': 2,
101+
'locationGroupId': '',
102+
'categories': [{
103+
'categoryCode': 'performance_storage_space',
104+
}],
105+
}],
106+
}, {
107+
'capacity': '100',
108+
'prices': [{
109+
'id': 3,
110+
'locationGroupId': '',
111+
'categories': [{
112+
'categoryCode': 'performance_storage_iops',
113+
}],
114+
'capacityRestrictionMinimum': '100',
115+
'capacityRestrictionMaximum': '100',
116+
}],
117+
}],
118+
}]
119+
120+
result = self.block.order_block_volume(
121+
"performance_storage_iscsi", "dal05", 100, "LINUX", iops=100)
122+
123+
self.assertEqual(
124+
result,
125+
{
126+
'orderDate': '2013-08-01 15:23:45',
127+
'orderId': 1234,
128+
'prices': [{
129+
'hourlyRecurringFee': '2',
130+
'id': 1,
131+
'item': {'description': 'this is a thing', 'id': 1},
132+
'laborFee': '2',
133+
'oneTimeFee': '2',
134+
'oneTimeFeeTax': '.1',
135+
'quantity': 1,
136+
'recurringFee': '2',
137+
'recurringFeeTax': '.1',
138+
'setupFee': '1'}],
139+
},
140+
)
141+
142+
def test_order_block_volume_endurance(self):
143+
mock = self.set_mock('SoftLayer_Product_Package', 'getAllObjects')
144+
mock.return_value = [{
145+
'id': 1,
146+
'name': 'Performance',
147+
'items': [{
148+
'capacity': '1',
149+
'prices': [{
150+
'id': 1,
151+
'locationGroupId': '',
152+
'categories': [{
153+
'categoryCode': 'storage_block',
154+
}],
155+
}],
156+
}, {
157+
'capacity': '1',
158+
'prices': [{
159+
'id': 2,
160+
'locationGroupId': '',
161+
'categories': [{
162+
'categoryCode': 'storage_service_enterprise',
163+
}],
164+
}],
165+
}, {
166+
'capacity': '100',
167+
'prices': [{
168+
'id': 3,
169+
'locationGroupId': '',
170+
'categories': [{
171+
'categoryCode': 'performance_storage_space',
172+
}],
173+
'capacityRestrictionMinimum': '100',
174+
'capacityRestrictionMaximum': '100',
175+
}],
176+
}, {
177+
'capacity': '100',
178+
'attributes': [{
179+
'value': '100',
180+
}],
181+
'prices': [{
182+
'id': 4,
183+
'locationGroupId': '',
184+
'categories': [{
185+
'categoryCode': 'storage_tier_level',
186+
}],
187+
}],
188+
}],
189+
}]
190+
191+
result = self.block.order_block_volume(
192+
"storage_service_enterprise", "dal05", 100, "LINUX",
193+
tier_level=0.25)
194+
195+
self.assertEqual(
196+
result,
197+
{
198+
'orderDate': '2013-08-01 15:23:45',
199+
'orderId': 1234,
200+
'prices': [{
201+
'hourlyRecurringFee': '2',
202+
'id': 1,
203+
'item': {'description': 'this is a thing', 'id': 1},
204+
'laborFee': '2',
205+
'oneTimeFee': '2',
206+
'oneTimeFeeTax': '.1',
207+
'quantity': 1,
208+
'recurringFee': '2',
209+
'recurringFeeTax': '.1',
210+
'setupFee': '1'}],
211+
},
212+
)

tox.ini

Lines changed: 2 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -2,15 +2,12 @@
22
envlist = py27,py33,py34,py35,pypy,analysis,coverage
33

44
[testenv]
5-
setenv =
6-
LANG=C.UTF-8
7-
LC_ALL=C.UTF-8
85
deps = -r{toxinidir}/tools/test-requirements.txt
9-
commands = py.test tests
6+
commands = py.test {posargs:tests}
107

118
[testenv:coverage]
129
basepython = python2.7
13-
commands = py.test tests \
10+
commands = py.test {posargs:tests} \
1411
--cov=SoftLayer \
1512
--cov-fail-under=77 \
1613
--cov-report=html \

0 commit comments

Comments
 (0)