Skip to content

Commit 7f4774c

Browse files
Merge pull request #830 from dpickle2/feature/volume-duplicate
Add volume-duplicate command for file & block storage
2 parents 44efd83 + 9342eed commit 7f4774c

File tree

15 files changed

+3060
-6
lines changed

15 files changed

+3060
-6
lines changed

SoftLayer/CLI/block/detail.py

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -99,4 +99,13 @@ def cli(env, volume_id):
9999
replicant_list.append(replicant_table)
100100
table.add_row(['Replicant Volumes', replicant_list])
101101

102+
if block_volume.get('originalVolumeSize'):
103+
duplicate_info = formatting.Table(['Original Volume Name',
104+
block_volume['originalVolumeName']])
105+
duplicate_info.add_row(['Original Volume Size',
106+
block_volume['originalVolumeSize']])
107+
duplicate_info.add_row(['Original Snapshot Name',
108+
block_volume['originalSnapshotName']])
109+
table.add_row(['Duplicate Volume Properties', duplicate_info])
110+
102111
env.fout(table)

SoftLayer/CLI/block/duplicate.py

Lines changed: 83 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,83 @@
1+
"""Order a duplicate 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('origin-volume-id')
15+
@click.option('--origin-snapshot-id', '-o',
16+
type=int,
17+
help="ID of an origin volume snapshot to use for duplcation.")
18+
@click.option('--duplicate-size', '-c',
19+
type=int,
20+
help='Size of duplicate block volume in GB. '
21+
'***If no size is specified, the size of '
22+
'the origin volume will be used.***\n'
23+
'Potential Sizes: [20, 40, 80, 100, 250, '
24+
'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)]')
28+
@click.option('--duplicate-iops', '-i',
29+
type=int,
30+
help='Performance Storage IOPS, between 100 and 6000 in '
31+
'multiples of 100 [only used for performance volumes] '
32+
'***If no IOPS value is specified, the IOPS value of the '
33+
'origin volume will be used.***\n'
34+
'Requirements: [If IOPS/GB for the origin volume is less '
35+
'than 0.3, IOPS/GB for the duplicate must also be less '
36+
'than 0.3. If IOPS/GB for the origin volume is greater '
37+
'than or equal to 0.3, IOPS/GB for the duplicate must '
38+
'also be greater than or equal to 0.3.]')
39+
@click.option('--duplicate-tier', '-t',
40+
help='Endurance Storage Tier (IOPS per GB) [only used for '
41+
'endurance volumes] ***If no tier is specified, the tier '
42+
'of the origin volume will be used.***\n'
43+
'Requirements: [If IOPS/GB for the origin volume is 0.25, '
44+
'IOPS/GB for the duplicate must also be 0.25. If IOPS/GB '
45+
'for the origin volume is greater than 0.25, IOPS/GB '
46+
'for the duplicate must also be greater than 0.25.]',
47+
type=click.Choice(['0.25', '2', '4', '10']))
48+
@click.option('--duplicate-snapshot-size', '-s',
49+
type=int,
50+
help='The size of snapshot space to order for the duplicate. '
51+
'***If no snapshot space size is specified, the snapshot '
52+
'space size of the origin volume will be used.***\n'
53+
'Input "0" for this parameter to order a duplicate volume '
54+
'with no snapshot space.')
55+
@environment.pass_env
56+
def cli(env, origin_volume_id, origin_snapshot_id, duplicate_size,
57+
duplicate_iops, duplicate_tier, duplicate_snapshot_size):
58+
"""Order a duplicate block storage volume."""
59+
block_manager = SoftLayer.BlockStorageManager(env.client)
60+
61+
if duplicate_tier is not None:
62+
duplicate_tier = float(duplicate_tier)
63+
64+
try:
65+
order = block_manager.order_duplicate_volume(
66+
origin_volume_id,
67+
origin_snapshot_id=origin_snapshot_id,
68+
duplicate_size=duplicate_size,
69+
duplicate_iops=duplicate_iops,
70+
duplicate_tier_level=duplicate_tier,
71+
duplicate_snapshot_size=duplicate_snapshot_size
72+
)
73+
except ValueError as ex:
74+
raise exceptions.ArgumentError(str(ex))
75+
76+
if 'placedOrder' in order.keys():
77+
click.echo("Order #{0} placed successfully!".format(
78+
order['placedOrder']['id']))
79+
for item in order['placedOrder']['items']:
80+
click.echo(" > %s" % item['description'])
81+
else:
82+
click.echo("Order could not be placed! Please verify your options " +
83+
"and try again.")

SoftLayer/CLI/file/detail.py

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,8 @@ def cli(env, volume_id):
1616
file_manager = SoftLayer.FileStorageManager(env.client)
1717
file_volume = file_manager.get_file_volume_details(volume_id)
1818
file_volume = utils.NestedDict(file_volume)
19-
used_space = int(file_volume['bytesUsed'])
19+
used_space = int(file_volume['bytesUsed'])\
20+
if file_volume['bytesUsed'] else 0
2021

2122
table = formatting.KeyValueTable(['Name', 'Value'])
2223
table.align['Name'] = 'r'
@@ -114,4 +115,13 @@ def cli(env, volume_id):
114115
replicant_list.append(replicant_table)
115116
table.add_row(['Replicant Volumes', replicant_list])
116117

118+
if file_volume.get('originalVolumeSize'):
119+
duplicate_info = formatting.Table(['Original Volume Name',
120+
file_volume['originalVolumeName']])
121+
duplicate_info.add_row(['Original Volume Size',
122+
file_volume['originalVolumeSize']])
123+
duplicate_info.add_row(['Original Snapshot Name',
124+
file_volume['originalSnapshotName']])
125+
table.add_row(['Duplicate Volume Properties', duplicate_info])
126+
117127
env.fout(table)

SoftLayer/CLI/file/duplicate.py

Lines changed: 79 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,79 @@
1+
"""Order a duplicate 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('origin-volume-id')
15+
@click.option('--origin-snapshot-id', '-o',
16+
type=int,
17+
help="ID of an origin volume snapshot to use for duplcation.")
18+
@click.option('--duplicate-size', '-c',
19+
type=int,
20+
help='Size of duplicate file volume in GB. '
21+
'***If no size is specified, the size of '
22+
'the origin volume will be used.***\n'
23+
'Minimum: [the size of the origin volume]')
24+
@click.option('--duplicate-iops', '-i',
25+
type=int,
26+
help='Performance Storage IOPS, between 100 and 6000 in '
27+
'multiples of 100 [only used for performance volumes] '
28+
'***If no IOPS value is specified, the IOPS value of the '
29+
'origin volume will be used.***\n'
30+
'Requirements: [If IOPS/GB for the origin volume is less '
31+
'than 0.3, IOPS/GB for the duplicate must also be less '
32+
'than 0.3. If IOPS/GB for the origin volume is greater '
33+
'than or equal to 0.3, IOPS/GB for the duplicate must '
34+
'also be greater than or equal to 0.3.]')
35+
@click.option('--duplicate-tier', '-t',
36+
help='Endurance Storage Tier (IOPS per GB) [only used for '
37+
'endurance volumes] ***If no tier is specified, the tier '
38+
'of the origin volume will be used.***\n'
39+
'Requirements: [If IOPS/GB for the origin volume is 0.25, '
40+
'IOPS/GB for the duplicate must also be 0.25. If IOPS/GB '
41+
'for the origin volume is greater than 0.25, IOPS/GB '
42+
'for the duplicate must also be greater than 0.25.]',
43+
type=click.Choice(['0.25', '2', '4', '10']))
44+
@click.option('--duplicate-snapshot-size', '-s',
45+
type=int,
46+
help='The size of snapshot space to order for the duplicate. '
47+
'***If no snapshot space size is specified, the snapshot '
48+
'space size of the origin volume will be used.***\n'
49+
'Input "0" for this parameter to order a duplicate volume '
50+
'with no snapshot space.')
51+
@environment.pass_env
52+
def cli(env, origin_volume_id, origin_snapshot_id, duplicate_size,
53+
duplicate_iops, duplicate_tier, duplicate_snapshot_size):
54+
"""Order a duplicate file storage volume."""
55+
file_manager = SoftLayer.FileStorageManager(env.client)
56+
57+
if duplicate_tier is not None:
58+
duplicate_tier = float(duplicate_tier)
59+
60+
try:
61+
order = file_manager.order_duplicate_volume(
62+
origin_volume_id,
63+
origin_snapshot_id=origin_snapshot_id,
64+
duplicate_size=duplicate_size,
65+
duplicate_iops=duplicate_iops,
66+
duplicate_tier_level=duplicate_tier,
67+
duplicate_snapshot_size=duplicate_snapshot_size
68+
)
69+
except ValueError as ex:
70+
raise exceptions.ArgumentError(str(ex))
71+
72+
if 'placedOrder' in order.keys():
73+
click.echo("Order #{0} placed successfully!".format(
74+
order['placedOrder']['id']))
75+
for item in order['placedOrder']['items']:
76+
click.echo(" > %s" % item['description'])
77+
else:
78+
click.echo("Order could not be placed! Please verify your options " +
79+
"and try again.")

SoftLayer/CLI/routes.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -76,6 +76,7 @@
7676
('block:snapshot-restore', 'SoftLayer.CLI.block.snapshot.restore:cli'),
7777
('block:volume-cancel', 'SoftLayer.CLI.block.cancel:cli'),
7878
('block:volume-detail', 'SoftLayer.CLI.block.detail:cli'),
79+
('block:volume-duplicate', 'SoftLayer.CLI.block.duplicate:cli'),
7980
('block:volume-list', 'SoftLayer.CLI.block.list:cli'),
8081
('block:volume-order', 'SoftLayer.CLI.block.order:cli'),
8182

@@ -98,6 +99,7 @@
9899
('file:snapshot-restore', 'SoftLayer.CLI.file.snapshot.restore:cli'),
99100
('file:volume-cancel', 'SoftLayer.CLI.file.cancel:cli'),
100101
('file:volume-detail', 'SoftLayer.CLI.file.detail:cli'),
102+
('file:volume-duplicate', 'SoftLayer.CLI.file.duplicate:cli'),
101103
('file:volume-list', 'SoftLayer.CLI.file.list:cli'),
102104
('file:volume-order', 'SoftLayer.CLI.file.order:cli'),
103105

SoftLayer/fixtures/SoftLayer_Network_Storage.py

Lines changed: 36 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,34 @@
1+
DUPLICATABLE_VOLUME = {
2+
'accountId': 1234,
3+
'activeTransactions': None,
4+
'activeTransactionCount': 0,
5+
'billingItem': {
6+
'activeChildren': [{
7+
'categoryCode': 'storage_snapshot_space',
8+
'id': 125,
9+
'cancellationDate': '',
10+
}],
11+
'cancellationDate': '',
12+
'id': 454,
13+
'location': {'id': 449500}
14+
},
15+
'capacityGb': 500,
16+
'id': 102,
17+
'iops': 1000,
18+
'lunId': 2,
19+
'osType': {'keyName': 'LINUX'},
20+
'originalVolumeSize': '500',
21+
'parentVolume': {'snapshotSizeBytes': 1024},
22+
'provisionedIops': '1000',
23+
'replicationPartnerCount': 0,
24+
'serviceResource': {'datacenter': {'id': 449500, 'name': 'dal05'}},
25+
'serviceResourceBackendIpAddress': '10.1.2.3',
26+
'snapshotCapacityGb': '10',
27+
'storageTierLevel': 'READHEAVY_TIER',
28+
'storageType': {'keyName': 'ENDURANCE_BLOCK_STORAGE'},
29+
'username': 'duplicatable_volume_username'
30+
}
31+
132
getObject = {
233
'accountId': 1234,
334
'billingItem': {
@@ -8,7 +39,8 @@
839
'categoryCode': 'storage_snapshot_space',
940
'id': 123,
1041
'cancellationDate': '',
11-
}]
42+
}],
43+
'location': {'id': 449500}
1244
},
1345
'capacityGb': 20,
1446
'createDate': '2015:50:15-04:00',
@@ -25,6 +57,9 @@
2557
'snapshotCapacityGb': '10',
2658
'parentVolume': {'snapshotSizeBytes': 1024},
2759
'osType': {'keyName': 'LINUX'},
60+
'originalSnapshotName': 'test-origin-snapshot-name',
61+
'originalVolumeName': 'test-origin-volume-name',
62+
'originalVolumeSize': '20',
2863
'schedules': [{
2964
'id': 978,
3065
'type': {'keyname': 'SNAPSHOT_WEEKLY'},

0 commit comments

Comments
 (0)