Skip to content

Commit f151ed8

Browse files
david picklesudorandom
authored andcommitted
Add snapshot space option in volume-order commands (softlayer#758)
Add the --snapshot-size option for File and Block 'volume-order' commands. If specified, this option attempts to include snapshot space of the given size as part of the order for a new storage volume. Add additional CLI module tests for File and Block to improve upon previous unit testing coverage.
1 parent 6581b2c commit f151ed8

8 files changed

Lines changed: 396 additions & 28 deletions

File tree

SoftLayer/CLI/block/order.py

Lines changed: 16 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@
77
from SoftLayer.CLI import exceptions
88

99

10-
CONTEXT_SETTINGS = dict(token_normalize_func=lambda x: x.upper())
10+
CONTEXT_SETTINGS = {'token_normalize_func': lambda x: x.upper()}
1111

1212

1313
@click.command(context_settings=CONTEXT_SETTINGS)
@@ -42,8 +42,14 @@
4242
@click.option('--location',
4343
help='Datacenter short name (e.g.: dal09)',
4444
required=True)
45+
@click.option('--snapshot-size',
46+
type=int,
47+
help='Optional parameter for ordering snapshot '
48+
'space along with endurance block storage; specifies '
49+
'the size (in GB) of snapshot space to order')
4550
@environment.pass_env
46-
def cli(env, storage_type, size, iops, tier, os_type, location):
51+
def cli(env, storage_type, size, iops, tier, os_type,
52+
location, snapshot_size):
4753
"""Order a block storage volume."""
4854
block_manager = SoftLayer.BlockStorageManager(env.client)
4955
storage_type = storage_type.lower()
@@ -62,6 +68,12 @@ def cli(env, storage_type, size, iops, tier, os_type, location):
6268
'Option --iops must be a multiple of 100'
6369
)
6470

71+
if snapshot_size is not None:
72+
raise exceptions.CLIAbort(
73+
'Option --snapshot-size not allowed for performance volumes.'
74+
' Snapshots are only available for endurance storage.'
75+
)
76+
6577
try:
6678
order = block_manager.order_block_volume(
6779
storage_type='performance_storage_iscsi',
@@ -84,7 +96,8 @@ def cli(env, storage_type, size, iops, tier, os_type, location):
8496
location=location,
8597
size=size,
8698
tier_level=float(tier),
87-
os_type=os_type
99+
os_type=os_type,
100+
snapshot_size=snapshot_size
88101
)
89102
except ValueError as ex:
90103
raise exceptions.ArgumentError(str(ex))

SoftLayer/CLI/file/order.py

Lines changed: 16 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@
77
from SoftLayer.CLI import exceptions
88

99

10-
CONTEXT_SETTINGS = dict(token_normalize_func=lambda x: x.upper())
10+
CONTEXT_SETTINGS = {'token_normalize_func': lambda x: x.upper()}
1111

1212

1313
@click.command(context_settings=CONTEXT_SETTINGS)
@@ -42,8 +42,14 @@
4242
@click.option('--location',
4343
help='Datacenter short name (e.g.: dal09)',
4444
required=True)
45+
@click.option('--snapshot-size',
46+
type=int,
47+
help='Optional parameter for ordering snapshot '
48+
'space along with endurance file storage; specifies '
49+
'the size (in GB) of snapshot space to order')
4550
@environment.pass_env
46-
def cli(env, storage_type, size, iops, tier, os_type, location):
51+
def cli(env, storage_type, size, iops, tier, os_type,
52+
location, snapshot_size):
4753
"""Order a file storage volume."""
4854
file_manager = SoftLayer.FileStorageManager(env.client)
4955
storage_type = storage_type.lower()
@@ -62,6 +68,12 @@ def cli(env, storage_type, size, iops, tier, os_type, location):
6268
'Option --iops must be a multiple of 100'
6369
)
6470

71+
if snapshot_size is not None:
72+
raise exceptions.CLIAbort(
73+
'Option --snapshot-size not allowed for performance volumes.'
74+
' Snapshots are only available for endurance storage.'
75+
)
76+
6577
try:
6678
order = file_manager.order_file_volume(
6779
storage_type='performance_storage_nfs',
@@ -84,7 +96,8 @@ def cli(env, storage_type, size, iops, tier, os_type, location):
8496
location=location,
8597
size=size,
8698
tier_level=float(tier),
87-
os_type=os_type
99+
os_type=os_type,
100+
snapshot_size=snapshot_size
88101
)
89102
except ValueError as ex:
90103
raise exceptions.ArgumentError(str(ex))

SoftLayer/managers/block.py

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -193,7 +193,7 @@ def delete_snapshot(self, snapshot_id):
193193
id=snapshot_id)
194194

195195
def order_block_volume(self, storage_type, location, size, os_type,
196-
iops=None, tier_level=None):
196+
iops=None, tier_level=None, snapshot_size=None):
197197
"""Places an order for a block volume.
198198
199199
:param storage_type: "performance_storage_iscsi" (performance)
@@ -203,6 +203,8 @@ def order_block_volume(self, storage_type, location, size, os_type,
203203
:param os_type: OS Type to use for volume alignment, see help for list
204204
:param iops: Number of IOPs for a "Performance" order
205205
:param tier_level: Tier level to use for an "Endurance" order
206+
:param snapshot_size: The size of optional snapshot space,
207+
if snapshot space should also be ordered (None if not ordered)
206208
"""
207209

208210
try:
@@ -239,6 +241,9 @@ def order_block_volume(self, storage_type, location, size, os_type,
239241
),
240242
storage_utils.find_endurance_tier_price(package, tier_level),
241243
]
244+
if snapshot_size is not None:
245+
prices.append(storage_utils.find_snapshot_space_price(
246+
package, snapshot_size, tier_level))
242247
else:
243248
raise exceptions.SoftLayerError(
244249
"Block volume storage_type must be either "

SoftLayer/managers/file.py

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -196,7 +196,7 @@ def delete_snapshot(self, snapshot_id):
196196
id=snapshot_id)
197197

198198
def order_file_volume(self, storage_type, location, size, os_type,
199-
iops=None, tier_level=None):
199+
iops=None, tier_level=None, snapshot_size=None):
200200
"""Places an order for a file volume.
201201
202202
:param storage_type: "performance_storage_iscsi" (performance)
@@ -206,6 +206,8 @@ def order_file_volume(self, storage_type, location, size, os_type,
206206
:param os_type: OS Type to use for volume alignment, see help for list
207207
:param iops: Number of IOPs for a "Performance" order
208208
:param tier_level: Tier level to use for an "Endurance" order
209+
:param snapshot_size: The size of optional snapshot space,
210+
if snapshot space should also be ordered (None if not ordered)
209211
"""
210212

211213
try:
@@ -242,6 +244,9 @@ def order_file_volume(self, storage_type, location, size, os_type,
242244
),
243245
storage_utils.find_endurance_tier_price(package, tier_level),
244246
]
247+
if snapshot_size is not None:
248+
prices.append(storage_utils.find_snapshot_space_price(
249+
package, snapshot_size, tier_level))
245250
else:
246251
raise exceptions.SoftLayerError(
247252
"File volume storage_type must be either "

tests/CLI/modules/block_tests.py

Lines changed: 86 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -101,29 +101,107 @@ def test_volume_list(self):
101101
}],
102102
json.loads(result.output))
103103

104+
def test_volume_order_performance_iops_not_given(self):
105+
result = self.run_command(['block', 'volume-order',
106+
'--storage-type=performance', '--size=20',
107+
'--os-type=linux', '--location=dal05'])
108+
109+
self.assertEqual(2, result.exit_code)
110+
111+
def test_volume_order_performance_iops_out_of_range(self):
112+
result = self.run_command(['block', 'volume-order',
113+
'--storage-type=performance', '--size=20',
114+
'--iops=80000', '--os-type=linux',
115+
'--location=dal05'])
116+
117+
self.assertEqual(2, result.exit_code)
118+
119+
def test_volume_order_performance_iops_not_multiple_of_100(self):
120+
result = self.run_command(['block', 'volume-order',
121+
'--storage-type=performance', '--size=20',
122+
'--iops=122', '--os-type=linux',
123+
'--location=dal05'])
124+
125+
self.assertEqual(2, result.exit_code)
126+
127+
def test_volume_order_performance_snapshot_error(self):
128+
result = self.run_command(['block', 'volume-order',
129+
'--storage-type=performance', '--size=20',
130+
'--iops=100', '--os-type=linux',
131+
'--location=dal05', '--snapshot-size=10'])
132+
133+
self.assertEqual(2, result.exit_code)
134+
135+
@mock.patch('SoftLayer.BlockStorageManager.order_block_volume')
136+
def test_volume_order_performance(self, order_mock):
137+
order_mock.return_value = {
138+
'placedOrder': {
139+
'id': 478,
140+
'items': [
141+
{'description': 'Performance Storage'},
142+
{'description': 'Block Storage'},
143+
{'description': '0.25 IOPS per GB'},
144+
{'description': '20 GB Storage Space'}]
145+
}
146+
}
147+
148+
result = self.run_command(['block', 'volume-order',
149+
'--storage-type=performance', '--size=20',
150+
'--iops=100', '--os-type=linux',
151+
'--location=dal05'])
152+
153+
self.assert_no_fail(result)
154+
self.assertEqual(result.output,
155+
'Order #478 placed successfully!\n'
156+
' > Performance Storage\n > Block Storage\n'
157+
' > 0.25 IOPS per GB\n > 20 GB Storage Space\n')
158+
159+
def test_volume_order_endurance_tier_not_given(self):
160+
result = self.run_command(['block', 'volume-order',
161+
'--storage-type=endurance', '--size=20',
162+
'--os-type=linux', '--location=dal05'])
163+
164+
self.assertEqual(2, result.exit_code)
165+
104166
@mock.patch('SoftLayer.BlockStorageManager.order_block_volume')
105-
def test_volume_order(self, order_mock):
167+
def test_volume_order_endurance(self, order_mock):
106168
order_mock.return_value = {
107169
'placedOrder': {
108170
'id': 478,
109-
'items': [{'description': 'Endurance Storage'},
110-
{'description': 'Block Storage'},
111-
{'description': '0.25 IOPS per GB'},
112-
{'description': '20 GB Storage Space'},
113-
]
171+
'items': [
172+
{'description': 'Endurance Storage'},
173+
{'description': 'Block Storage'},
174+
{'description': '0.25 IOPS per GB'},
175+
{'description': '20 GB Storage Space'},
176+
{'description': '10 GB Storage Space (Snapshot Space)'}]
114177
}
115178
}
116179

117180
result = self.run_command(['block', 'volume-order',
118181
'--storage-type=endurance', '--size=20',
119182
'--tier=0.25', '--os-type=linux',
120-
'--location=dal05'])
183+
'--location=dal05', '--snapshot-size=10'])
121184

122185
self.assert_no_fail(result)
123186
self.assertEqual(result.output,
124187
'Order #478 placed successfully!\n'
125188
' > Endurance Storage\n > Block Storage\n'
126-
' > 0.25 IOPS per GB\n > 20 GB Storage Space\n')
189+
' > 0.25 IOPS per GB\n > 20 GB Storage Space\n'
190+
' > 10 GB Storage Space (Snapshot Space)\n')
191+
192+
@mock.patch('SoftLayer.BlockStorageManager.order_block_volume')
193+
def test_volume_order_order_not_placed(self, order_mock):
194+
order_mock.return_value = {}
195+
196+
result = self.run_command(['block', 'volume-order',
197+
'--storage-type=endurance', '--size=20',
198+
'--tier=0.25', '--os-type=linux',
199+
'--location=dal05'])
200+
201+
self.assert_no_fail(result)
202+
self.assertEqual(result.output,
203+
'Order could not be placed! Please verify '
204+
'your options and try again.\n')
127205

128206
def test_enable_snapshots(self):
129207
result = self.run_command(['block', 'snapshot-enable', '12345678',

tests/CLI/modules/file_tests.py

Lines changed: 86 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -131,29 +131,107 @@ def test_volume_detail(self):
131131
'# of Active Transactions': '0'
132132
}, json.loads(result.output))
133133

134+
def test_volume_order_performance_iops_not_given(self):
135+
result = self.run_command(['file', 'volume-order',
136+
'--storage-type=performance', '--size=20',
137+
'--os-type=linux', '--location=dal05'])
138+
139+
self.assertEqual(2, result.exit_code)
140+
141+
def test_volume_order_performance_iops_out_of_range(self):
142+
result = self.run_command(['file', 'volume-order',
143+
'--storage-type=performance', '--size=20',
144+
'--iops=80000', '--os-type=linux',
145+
'--location=dal05'])
146+
147+
self.assertEqual(2, result.exit_code)
148+
149+
def test_volume_order_performance_iops_not_multiple_of_100(self):
150+
result = self.run_command(['file', 'volume-order',
151+
'--storage-type=performance', '--size=20',
152+
'--iops=122', '--os-type=linux',
153+
'--location=dal05'])
154+
155+
self.assertEqual(2, result.exit_code)
156+
157+
def test_volume_order_performance_snapshot_error(self):
158+
result = self.run_command(['file', 'volume-order',
159+
'--storage-type=performance', '--size=20',
160+
'--iops=100', '--os-type=linux',
161+
'--location=dal05', '--snapshot-size=10'])
162+
163+
self.assertEqual(2, result.exit_code)
164+
165+
@mock.patch('SoftLayer.FileStorageManager.order_file_volume')
166+
def test_volume_order_performance(self, order_mock):
167+
order_mock.return_value = {
168+
'placedOrder': {
169+
'id': 478,
170+
'items': [
171+
{'description': 'Performance Storage'},
172+
{'description': 'File Storage'},
173+
{'description': '0.25 IOPS per GB'},
174+
{'description': '20 GB Storage Space'}]
175+
}
176+
}
177+
178+
result = self.run_command(['file', 'volume-order',
179+
'--storage-type=performance', '--size=20',
180+
'--iops=100', '--os-type=linux',
181+
'--location=dal05'])
182+
183+
self.assert_no_fail(result)
184+
self.assertEqual(result.output,
185+
'Order #478 placed successfully!\n'
186+
' > Performance Storage\n > File Storage\n'
187+
' > 0.25 IOPS per GB\n > 20 GB Storage Space\n')
188+
189+
def test_volume_order_endurance_tier_not_given(self):
190+
result = self.run_command(['file', 'volume-order',
191+
'--storage-type=endurance', '--size=20',
192+
'--os-type=linux', '--location=dal05'])
193+
194+
self.assertEqual(2, result.exit_code)
195+
134196
@mock.patch('SoftLayer.FileStorageManager.order_file_volume')
135-
def test_volume_order(self, order_mock):
197+
def test_volume_order_endurance(self, order_mock):
136198
order_mock.return_value = {
137199
'placedOrder': {
138200
'id': 478,
139-
'items': [{'description': 'Endurance Storage'},
140-
{'description': 'File Storage'},
141-
{'description': '0.25 IOPS per GB'},
142-
{'description': '20 GB Storage Space'},
143-
]
201+
'items': [
202+
{'description': 'Endurance Storage'},
203+
{'description': 'File Storage'},
204+
{'description': '0.25 IOPS per GB'},
205+
{'description': '20 GB Storage Space'},
206+
{'description': '10 GB Storage Space (Snapshot Space)'}]
144207
}
145208
}
146209

147210
result = self.run_command(['file', 'volume-order',
148211
'--storage-type=endurance', '--size=20',
149212
'--tier=0.25', '--os-type=linux',
150-
'--location=dal05'])
213+
'--location=dal05', '--snapshot-size=10'])
151214

152215
self.assert_no_fail(result)
153216
self.assertEqual(result.output,
154217
'Order #478 placed successfully!\n'
155218
' > Endurance Storage\n > File Storage\n'
156-
' > 0.25 IOPS per GB\n > 20 GB Storage Space\n')
219+
' > 0.25 IOPS per GB\n > 20 GB Storage Space\n'
220+
' > 10 GB Storage Space (Snapshot Space)\n')
221+
222+
@mock.patch('SoftLayer.FileStorageManager.order_file_volume')
223+
def test_volume_order_order_not_placed(self, order_mock):
224+
order_mock.return_value = {}
225+
226+
result = self.run_command(['file', 'volume-order',
227+
'--storage-type=endurance', '--size=20',
228+
'--tier=0.25', '--os-type=linux',
229+
'--location=dal05'])
230+
231+
self.assert_no_fail(result)
232+
self.assertEqual(result.output,
233+
'Order could not be placed! Please verify '
234+
'your options and try again.\n')
157235

158236
def test_enable_snapshots(self):
159237
result = self.run_command(['file', 'snapshot-enable', '12345678',

0 commit comments

Comments
 (0)