Skip to content

Commit e30f4d3

Browse files
author
David Pickle
committed
Add volume-duplicate command for file & block storage
1 parent 44efd83 commit e30f4d3

File tree

13 files changed

+2974
-1
lines changed

13 files changed

+2974
-1
lines changed

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/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: 33 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',

SoftLayer/fixtures/SoftLayer_Product_Package.py

Lines changed: 133 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -156,6 +156,139 @@
156156
'sort': 99}]}]
157157

158158

159+
SAAS_PACKAGE = {
160+
'id': 759,
161+
'name': 'Storage As A Service (StaaS)',
162+
'categories': [{'categoryCode': 'storage_as_a_service'}],
163+
'items': [
164+
{
165+
'capacity': '0',
166+
'keyName': '',
167+
'prices': [{'id': 189433,
168+
'categories': [{
169+
'categoryCode': 'storage_as_a_service'}],
170+
'locationGroupId': ''}]
171+
}, {
172+
'capacity': '0',
173+
'keyName': '',
174+
'prices': [{'categories': [{'categoryCode': 'storage_block'}],
175+
'id': 189443,
176+
'locationGroupId': ''}]
177+
}, {
178+
'capacity': '0',
179+
'keyName': '',
180+
'prices': [{'categories': [{'categoryCode': 'storage_file'}],
181+
'id': 189453,
182+
'locationGroupId': ''}]
183+
}, {
184+
'capacity': '0',
185+
'capacityMaximum': '999',
186+
'capacityMinimum': '500',
187+
'itemCategory': {'categoryCode': 'performance_storage_space'},
188+
'keyName': '500_999_GBS',
189+
'prices': [{'id': 189993,
190+
'categories': [{
191+
'categoryCode': 'performance_storage_space'}],
192+
'locationGroupId': ''}]
193+
}, {
194+
'capacity': '0',
195+
'capacityMaximum': '1999',
196+
'capacityMinimum': '1000',
197+
'itemCategory': {'categoryCode': 'performance_storage_space'},
198+
'keyName': '1000_1999_GBS',
199+
'prices': [{'id': 190113,
200+
'categories': [{
201+
'categoryCode': 'performance_storage_space'}],
202+
'locationGroupId': ''}]
203+
}, {
204+
'capacity': '0',
205+
'capacityMaximum': '12000',
206+
'capacityMinimum': '1',
207+
'keyName': 'STORAGE_SPACE_FOR_2_IOPS_PER_GB',
208+
'prices': [{'id': 193433,
209+
'categories': [{
210+
'categoryCode': 'performance_storage_space'}],
211+
'locationGroupId': ''}]
212+
}, {
213+
'capacity': '0',
214+
'capacityMaximum': '12000',
215+
'capacityMinimum': '1',
216+
'keyName': 'STORAGE_SPACE_FOR_4_IOPS_PER_GB',
217+
'prices': [{'id': 194763,
218+
'categories': [{
219+
'categoryCode': 'performance_storage_space'}],
220+
'locationGroupId': ''}]
221+
}, {
222+
'capacity': '0',
223+
'capacityMaximum': '10000',
224+
'capacityMinimum': '100',
225+
'keyName': '',
226+
'itemCategory': {'categoryCode': 'performance_storage_iops'},
227+
'prices': [{'capacityRestrictionMaximum': '999',
228+
'capacityRestrictionMinimum': '500',
229+
'capacityRestrictionType': 'STORAGE_SPACE',
230+
'categories': [{
231+
'categoryCode': 'performance_storage_iops'}],
232+
'id': 190053,
233+
'locationGroupId': ''}]
234+
}, {
235+
'capacity': '0',
236+
'capacityMaximum': '20000',
237+
'capacityMinimum': '100',
238+
'keyName': '',
239+
'itemCategory': {'categoryCode': 'performance_storage_iops'},
240+
'prices': [{'capacityRestrictionMaximum': '1999',
241+
'capacityRestrictionMinimum': '1000',
242+
'capacityRestrictionType': 'STORAGE_SPACE',
243+
'categories': [{
244+
'categoryCode': 'performance_storage_iops'}],
245+
'id': 190173,
246+
'locationGroupId': ''}]
247+
}, {
248+
'capacity': '200',
249+
'itemCategory': {'categoryCode': 'storage_tier_level'},
250+
'keyName': '',
251+
'prices': [{'id': 193373,
252+
'categories': [{
253+
'categoryCode': 'storage_tier_level'}],
254+
'locationGroupId': ''}]
255+
}, {
256+
'capacity': '300',
257+
'itemCategory': {'categoryCode': 'storage_tier_level'},
258+
'keyName': '',
259+
'prices': [{'id': 194703,
260+
'categories': [{
261+
'categoryCode': 'storage_tier_level'}],
262+
'locationGroupId': ''}]
263+
}, {
264+
'capacity': '10',
265+
'keyName': '',
266+
'prices': [{'capacityRestrictionMaximum': '48000',
267+
'capacityRestrictionMinimum': '100',
268+
'capacityRestrictionType': 'IOPS',
269+
'categories': [{
270+
'categoryCode': 'storage_snapshot_space'}],
271+
'id': 191193,
272+
'locationGroupId': ''},
273+
{'capacityRestrictionMaximum': '200',
274+
'capacityRestrictionMinimum': '200',
275+
'capacityRestrictionType': 'STORAGE_TIER_LEVEL',
276+
'categories': [{
277+
'categoryCode': 'storage_snapshot_space'}],
278+
'id': 193613,
279+
'locationGroupId': ''},
280+
{'capacityRestrictionMaximum': '300',
281+
'capacityRestrictionMinimum': '300',
282+
'capacityRestrictionType': 'STORAGE_TIER_LEVEL',
283+
'categories': [{
284+
'categoryCode': 'storage_snapshot_space'}],
285+
'id': 194943,
286+
'locationGroupId': ''}]
287+
}
288+
]
289+
}
290+
291+
159292
getAllObjects = [{
160293
'activePresets': [{
161294
'description': 'Single Xeon 1270, 8GB Ram, 2x1TB SATA disks, Non-RAID',

0 commit comments

Comments
 (0)