Skip to content

Commit 363d6ad

Browse files
Merge pull request #893 from sghatty/feature/FBLOCK-58
Feature/fblock 58 - Added volume-modify feature for File/Block.
2 parents 7858dbd + 74830b5 commit 363d6ad

File tree

15 files changed

+634
-45
lines changed

15 files changed

+634
-45
lines changed

SoftLayer/CLI/block/detail.py

Lines changed: 10 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -62,12 +62,10 @@ def cli(env, volume_id):
6262

6363
if block_volume['activeTransactions']:
6464
for trans in block_volume['activeTransactions']:
65-
table.add_row([
66-
'Ongoing Transactions',
67-
trans['transactionStatus']['friendlyName']])
65+
if 'transactionStatus' in trans and 'friendlyName' in trans['transactionStatus']:
66+
table.add_row(['Ongoing Transaction', trans['transactionStatus']['friendlyName']])
6867

69-
table.add_row(['Replicant Count', "%u"
70-
% block_volume['replicationPartnerCount']])
68+
table.add_row(['Replicant Count', "%u" % block_volume.get('replicationPartnerCount', 0)])
7169

7270
if block_volume['replicationPartnerCount'] > 0:
7371
# This if/else temporarily handles a bug in which the SL API
@@ -102,12 +100,12 @@ def cli(env, volume_id):
102100
table.add_row(['Replicant Volumes', replicant_list])
103101

104102
if block_volume.get('originalVolumeSize'):
105-
duplicate_info = formatting.Table(['Original Volume Name',
106-
block_volume['originalVolumeName']])
107-
duplicate_info.add_row(['Original Volume Size',
108-
block_volume['originalVolumeSize']])
109-
duplicate_info.add_row(['Original Snapshot Name',
110-
block_volume['originalSnapshotName']])
111-
table.add_row(['Duplicate Volume Properties', duplicate_info])
103+
original_volume_info = formatting.Table(['Property', 'Value'])
104+
original_volume_info.add_row(['Original Volume Size', block_volume['originalVolumeSize']])
105+
if block_volume.get('originalVolumeName'):
106+
original_volume_info.add_row(['Original Volume Name', block_volume['originalVolumeName']])
107+
if block_volume.get('originalSnapshotName'):
108+
original_volume_info.add_row(['Original Snapshot Name', block_volume['originalSnapshotName']])
109+
table.add_row(['Original Volume Properties', original_volume_info])
112110

113111
env.fout(table)

SoftLayer/CLI/block/duplicate.py

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -22,9 +22,7 @@
2222
'the origin volume will be used.***\n'
2323
'Potential Sizes: [20, 40, 80, 100, 250, '
2424
'500, 1000, 2000, 4000, 8000, 12000] '
25-
'Minimum: [the size of the origin volume] '
26-
'Maximum: [the minimum of 12000 GB or '
27-
'10*(origin volume size)]')
25+
'Minimum: [the size of the origin volume]')
2826
@click.option('--duplicate-iops', '-i',
2927
type=int,
3028
help='Performance Storage IOPS, between 100 and 6000 in '

SoftLayer/CLI/block/modify.py

Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
1+
"""Modify an existing block storage volume."""
2+
# :license: MIT, see LICENSE for more details.
3+
4+
import click
5+
import SoftLayer
6+
from SoftLayer.CLI import environment
7+
from SoftLayer.CLI import exceptions
8+
9+
10+
CONTEXT_SETTINGS = {'token_normalize_func': lambda x: x.upper()}
11+
12+
13+
@click.command(context_settings=CONTEXT_SETTINGS)
14+
@click.argument('volume-id')
15+
@click.option('--new-size', '-c',
16+
type=int,
17+
help='New Size of block volume in GB. ***If no size is given, the original size of volume is used.***\n'
18+
'Potential Sizes: [20, 40, 80, 100, 250, 500, 1000, 2000, 4000, 8000, 12000]\n'
19+
'Minimum: [the original size of the volume]')
20+
@click.option('--new-iops', '-i',
21+
type=int,
22+
help='Performance Storage IOPS, between 100 and 6000 in multiples of 100 [only for performance volumes] '
23+
'***If no IOPS value is specified, the original IOPS value of the volume will be used.***\n'
24+
'Requirements: [If original IOPS/GB for the volume is less than 0.3, new IOPS/GB must also be '
25+
'less than 0.3. If original IOPS/GB for the volume is greater than or equal to 0.3, new IOPS/GB '
26+
'for the volume must also be greater than or equal to 0.3.]')
27+
@click.option('--new-tier', '-t',
28+
help='Endurance Storage Tier (IOPS per GB) [only for endurance volumes] '
29+
'***If no tier is specified, the original tier of the volume will be used.***\n'
30+
'Requirements: [If original IOPS/GB for the volume is 0.25, new IOPS/GB for the volume must also '
31+
'be 0.25. If original IOPS/GB for the volume is greater than 0.25, new IOPS/GB for the volume '
32+
'must also be greater than 0.25.]',
33+
type=click.Choice(['0.25', '2', '4', '10']))
34+
@environment.pass_env
35+
def cli(env, volume_id, new_size, new_iops, new_tier):
36+
"""Modify an existing block storage volume."""
37+
block_manager = SoftLayer.BlockStorageManager(env.client)
38+
39+
if new_tier is not None:
40+
new_tier = float(new_tier)
41+
42+
try:
43+
order = block_manager.order_modified_volume(
44+
volume_id,
45+
new_size=new_size,
46+
new_iops=new_iops,
47+
new_tier_level=new_tier,
48+
)
49+
except ValueError as ex:
50+
raise exceptions.ArgumentError(str(ex))
51+
52+
if 'placedOrder' in order.keys():
53+
click.echo("Order #{0} placed successfully!".format(order['placedOrder']['id']))
54+
for item in order['placedOrder']['items']:
55+
click.echo(" > %s" % item['description'])
56+
else:
57+
click.echo("Order could not be placed! Please verify your options and try again.")

SoftLayer/CLI/file/detail.py

Lines changed: 10 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -78,12 +78,10 @@ def cli(env, volume_id):
7878

7979
if file_volume['activeTransactions']:
8080
for trans in file_volume['activeTransactions']:
81-
table.add_row([
82-
'Ongoing Transactions',
83-
trans['transactionStatus']['friendlyName']])
81+
if 'transactionStatus' in trans and 'friendlyName' in trans['transactionStatus']:
82+
table.add_row(['Ongoing Transaction', trans['transactionStatus']['friendlyName']])
8483

85-
table.add_row(['Replicant Count', "%u"
86-
% file_volume['replicationPartnerCount']])
84+
table.add_row(['Replicant Count', "%u" % file_volume.get('replicationPartnerCount', 0)])
8785

8886
if file_volume['replicationPartnerCount'] > 0:
8987
# This if/else temporarily handles a bug in which the SL API
@@ -118,12 +116,12 @@ def cli(env, volume_id):
118116
table.add_row(['Replicant Volumes', replicant_list])
119117

120118
if file_volume.get('originalVolumeSize'):
121-
duplicate_info = formatting.Table(['Original Volume Name',
122-
file_volume['originalVolumeName']])
123-
duplicate_info.add_row(['Original Volume Size',
124-
file_volume['originalVolumeSize']])
125-
duplicate_info.add_row(['Original Snapshot Name',
126-
file_volume['originalSnapshotName']])
127-
table.add_row(['Duplicate Volume Properties', duplicate_info])
119+
original_volume_info = formatting.Table(['Property', 'Value'])
120+
original_volume_info.add_row(['Original Volume Size', file_volume['originalVolumeSize']])
121+
if file_volume.get('originalVolumeName'):
122+
original_volume_info.add_row(['Original Volume Name', file_volume['originalVolumeName']])
123+
if file_volume.get('originalSnapshotName'):
124+
original_volume_info.add_row(['Original Snapshot Name', file_volume['originalSnapshotName']])
125+
table.add_row(['Original Volume Properties', original_volume_info])
128126

129127
env.fout(table)

SoftLayer/CLI/file/modify.py

Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
1+
"""Modify an existing file storage volume."""
2+
# :license: MIT, see LICENSE for more details.
3+
4+
import click
5+
import SoftLayer
6+
from SoftLayer.CLI import environment
7+
from SoftLayer.CLI import exceptions
8+
9+
10+
CONTEXT_SETTINGS = {'token_normalize_func': lambda x: x.upper()}
11+
12+
13+
@click.command(context_settings=CONTEXT_SETTINGS)
14+
@click.argument('volume-id')
15+
@click.option('--new-size', '-c',
16+
type=int,
17+
help='New Size of file volume in GB. ***If no size is given, the original size of volume is used.***\n'
18+
'Potential Sizes: [20, 40, 80, 100, 250, 500, 1000, 2000, 4000, 8000, 12000]\n'
19+
'Minimum: [the original size of the volume]')
20+
@click.option('--new-iops', '-i',
21+
type=int,
22+
help='Performance Storage IOPS, between 100 and 6000 in multiples of 100 [only for performance volumes] '
23+
'***If no IOPS value is specified, the original IOPS value of the volume will be used.***\n'
24+
'Requirements: [If original IOPS/GB for the volume is less than 0.3, new IOPS/GB must also be '
25+
'less than 0.3. If original IOPS/GB for the volume is greater than or equal to 0.3, new IOPS/GB '
26+
'for the volume must also be greater than or equal to 0.3.]')
27+
@click.option('--new-tier', '-t',
28+
help='Endurance Storage Tier (IOPS per GB) [only for endurance volumes] '
29+
'***If no tier is specified, the original tier of the volume will be used.***\n'
30+
'Requirements: [If original IOPS/GB for the volume is 0.25, new IOPS/GB for the volume must also '
31+
'be 0.25. If original IOPS/GB for the volume is greater than 0.25, new IOPS/GB for the volume '
32+
'must also be greater than 0.25.]',
33+
type=click.Choice(['0.25', '2', '4', '10']))
34+
@environment.pass_env
35+
def cli(env, volume_id, new_size, new_iops, new_tier):
36+
"""Modify an existing file storage volume."""
37+
file_manager = SoftLayer.FileStorageManager(env.client)
38+
39+
if new_tier is not None:
40+
new_tier = float(new_tier)
41+
42+
try:
43+
order = file_manager.order_modified_volume(
44+
volume_id,
45+
new_size=new_size,
46+
new_iops=new_iops,
47+
new_tier_level=new_tier,
48+
)
49+
except ValueError as ex:
50+
raise exceptions.ArgumentError(str(ex))
51+
52+
if 'placedOrder' in order.keys():
53+
click.echo("Order #{0} placed successfully!".format(order['placedOrder']['id']))
54+
for item in order['placedOrder']['items']:
55+
click.echo(" > %s" % item['description'])
56+
else:
57+
click.echo("Order could not be placed! Please verify your options and try again.")

SoftLayer/CLI/routes.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -80,6 +80,7 @@
8080
('block:volume-detail', 'SoftLayer.CLI.block.detail:cli'),
8181
('block:volume-duplicate', 'SoftLayer.CLI.block.duplicate:cli'),
8282
('block:volume-list', 'SoftLayer.CLI.block.list:cli'),
83+
('block:volume-modify', 'SoftLayer.CLI.block.modify:cli'),
8384
('block:volume-order', 'SoftLayer.CLI.block.order:cli'),
8485
('block:volume-set-lun-id', 'SoftLayer.CLI.block.lun:cli'),
8586

@@ -105,6 +106,7 @@
105106
('file:volume-detail', 'SoftLayer.CLI.file.detail:cli'),
106107
('file:volume-duplicate', 'SoftLayer.CLI.file.duplicate:cli'),
107108
('file:volume-list', 'SoftLayer.CLI.file.list:cli'),
109+
('file:volume-modify', 'SoftLayer.CLI.file.modify:cli'),
108110
('file:volume-order', 'SoftLayer.CLI.file.order:cli'),
109111

110112
('firewall', 'SoftLayer.CLI.firewall'),

SoftLayer/fixtures/SoftLayer_Network_Storage.py

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -39,8 +39,10 @@
3939

4040
getObject = {
4141
'accountId': 1234,
42-
'activeTransactionCount': 0,
43-
'activeTransactions': None,
42+
'activeTransactionCount': 1,
43+
'activeTransactions': [{
44+
'transactionStatus': {'friendlyName': 'This is a buffer time in which the customer may cancel the server'}
45+
}],
4446
'allowedHardware': [{
4547
'allowedHost': {
4648
'credential': {'username': 'joe', 'password': '12345'},
@@ -104,8 +106,8 @@
104106
'lunId': 2,
105107
'nasType': 'ISCSI',
106108
'notes': """{'status': 'available'}""",
107-
'originalSnapshotName': 'test-origin-snapshot-name',
108-
'originalVolumeName': 'test-origin-volume-name',
109+
'originalSnapshotName': 'test-original-snapshot-name',
110+
'originalVolumeName': 'test-original-volume-name',
109111
'originalVolumeSize': '20',
110112
'osType': {'keyName': 'LINUX'},
111113
'parentVolume': {'snapshotSizeBytes': 1024},

SoftLayer/managers/block.py

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -303,6 +303,35 @@ def order_duplicate_volume(self, origin_volume_id, origin_snapshot_id=None,
303303

304304
return self.client.call('Product_Order', 'placeOrder', order)
305305

306+
def order_modified_volume(self, volume_id, new_size=None, new_iops=None, new_tier_level=None):
307+
"""Places an order for modifying an existing block volume.
308+
309+
:param volume_id: The ID of the volume to be modified
310+
:param new_size: The new size/capacity for the volume
311+
:param new_iops: The new IOPS for the volume
312+
:param new_tier_level: The new tier level for the volume
313+
:return: Returns a SoftLayer_Container_Product_Order_Receipt
314+
"""
315+
316+
mask_items = [
317+
'id',
318+
'billingItem',
319+
'storageType[keyName]',
320+
'capacityGb',
321+
'provisionedIops',
322+
'storageTierLevel',
323+
'staasVersion',
324+
'hasEncryptionAtRest',
325+
]
326+
block_mask = ','.join(mask_items)
327+
volume = self.get_block_volume_details(volume_id, mask=block_mask)
328+
329+
order = storage_utils.prepare_modify_order_object(
330+
self, volume, new_iops, new_tier_level, new_size
331+
)
332+
333+
return self.client.call('Product_Order', 'placeOrder', order)
334+
306335
def delete_snapshot(self, snapshot_id):
307336
"""Deletes the specified snapshot object.
308337

SoftLayer/managers/file.py

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -283,6 +283,35 @@ def order_duplicate_volume(self, origin_volume_id, origin_snapshot_id=None,
283283

284284
return self.client.call('Product_Order', 'placeOrder', order)
285285

286+
def order_modified_volume(self, volume_id, new_size=None, new_iops=None, new_tier_level=None):
287+
"""Places an order for modifying an existing file volume.
288+
289+
:param volume_id: The ID of the volume to be modified
290+
:param new_size: The new size/capacity for the volume
291+
:param new_iops: The new IOPS for the volume
292+
:param new_tier_level: The new tier level for the volume
293+
:return: Returns a SoftLayer_Container_Product_Order_Receipt
294+
"""
295+
296+
mask_items = [
297+
'id',
298+
'billingItem',
299+
'storageType[keyName]',
300+
'capacityGb',
301+
'provisionedIops',
302+
'storageTierLevel',
303+
'staasVersion',
304+
'hasEncryptionAtRest',
305+
]
306+
file_mask = ','.join(mask_items)
307+
volume = self.get_file_volume_details(volume_id, mask=file_mask)
308+
309+
order = storage_utils.prepare_modify_order_object(
310+
self, volume, new_iops, new_tier_level, new_size
311+
)
312+
313+
return self.client.call('Product_Order', 'placeOrder', order)
314+
286315
def delete_snapshot(self, snapshot_id):
287316
"""Deletes the specified snapshot object.
288317

0 commit comments

Comments
 (0)