Skip to content

Commit 5420784

Browse files
david picklesudorandom
authored andcommitted
Add File & Block replication commands and output (softlayer#752)
* Add File & Block replication commands and output Code developed collaboratively by: ccorales95 Cristina Corales <[email protected]> kyubifire Nilo Lisboa <[email protected]> VuKevin Kevin Vu <[email protected]> dpickle2 David Pickle <[email protected]> * Add support for ordering replicant volumes [Block & File] * Add support for replicant failover & failback [Block & File] * Add additional output for commands: Add replicant info to volume-detail output [Block & File] Show replicant volumes in volume-list output [Block & File] * Address review concerns; fix snapshot-create output * Add checks for failure in failover and failback commands; updated unit tests accordingly * Update of CONTEXT_SETTINGS assignments to use dict syntax * Utilize utils.lookup() function for os_type lookup; updated use of optional arguments for order_replicant_volume() functions and adjusted unit tests accordingly * snapshot-create crashed when a volume was failed over to its replicant (API returned an empty array); updated logic for command line output to prevent crashing and adjusted unit tests accordingly
1 parent f151ed8 commit 5420784

21 files changed

Lines changed: 1323 additions & 22 deletions

File tree

SoftLayer/CLI/block/detail.py

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -66,4 +66,37 @@ def cli(env, volume_id):
6666
'Ongoing Transactions',
6767
trans['transactionStatus']['friendlyName']])
6868

69+
table.add_row(['Replicant Count', "%u"
70+
% block_volume['replicationPartnerCount']])
71+
72+
if block_volume['replicationPartnerCount'] > 0:
73+
# This if/else temporarily handles a bug in which the SL API
74+
# returns a string or object for 'replicationStatus'; it seems that
75+
# the type is string for File volumes and object for Block volumes
76+
if 'message' in block_volume['replicationStatus']:
77+
table.add_row(['Replication Status', "%s"
78+
% block_volume['replicationStatus']['message']])
79+
else:
80+
table.add_row(['Replication Status', "%s"
81+
% block_volume['replicationStatus']])
82+
83+
replicant_list = []
84+
for replicant in block_volume['replicationPartners']:
85+
replicant_table = formatting.Table(['Replicant ID',
86+
replicant['id']])
87+
replicant_table.add_row([
88+
'Volume Name',
89+
replicant['username']])
90+
replicant_table.add_row([
91+
'Target IP',
92+
replicant['serviceResourceBackendIpAddress']])
93+
replicant_table.add_row([
94+
'Data Center',
95+
replicant['serviceResource']['datacenter']['name']])
96+
replicant_table.add_row([
97+
'Schedule',
98+
replicant['replicationSchedule']['type']['keyname']])
99+
replicant_list.append(replicant_table)
100+
table.add_row(['Replicant Volumes', replicant_list])
101+
69102
env.fout(table)
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
"""Block Storage Replication Control."""
Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
"""Failback from a replicant volume."""
2+
# :license: MIT, see LICENSE for more details.
3+
4+
import click
5+
import SoftLayer
6+
from SoftLayer.CLI import environment
7+
8+
9+
@click.command()
10+
@click.argument('volume-id')
11+
@click.option('--replicant-id', help="ID of the replicant volume")
12+
@environment.pass_env
13+
def cli(env, volume_id, replicant_id):
14+
"""Failback a block volume from the given replicant volume."""
15+
block_storage_manager = SoftLayer.BlockStorageManager(env.client)
16+
17+
success = block_storage_manager.failback_from_replicant(
18+
volume_id,
19+
replicant_id
20+
)
21+
22+
if success:
23+
click.echo("Failback from replicant is now in progress.")
24+
else:
25+
click.echo("Failback operation could not be initiated.")
Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
"""Failover to a replicant volume."""
2+
# :license: MIT, see LICENSE for more details.
3+
4+
import click
5+
import SoftLayer
6+
from SoftLayer.CLI import environment
7+
8+
9+
@click.command()
10+
@click.argument('volume-id')
11+
@click.option('--replicant-id', help="ID of the replicant volume")
12+
@click.option('--immediate',
13+
is_flag=True,
14+
default=False,
15+
help="Failover to replicant immediately.")
16+
@environment.pass_env
17+
def cli(env, volume_id, replicant_id, immediate):
18+
"""Failover a block volume to the given replicant volume."""
19+
block_storage_manager = SoftLayer.BlockStorageManager(env.client)
20+
21+
success = block_storage_manager.failover_to_replicant(
22+
volume_id,
23+
replicant_id,
24+
immediate
25+
)
26+
27+
if success:
28+
click.echo("Failover to replicant is now in progress.")
29+
else:
30+
click.echo("Failover operation could not be initiated.")
Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,65 @@
1+
"""Order a block storage replica 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('--snapshot-schedule', '-s',
16+
help='Snapshot schedule to use for replication, '
17+
'(HOURLY | DAILY | WEEKLY)',
18+
required=True,
19+
type=click.Choice(['HOURLY', 'DAILY', 'WEEKLY']))
20+
@click.option('--location', '-l',
21+
help='Short name of the data center for the replicant '
22+
'(e.g.: dal09)',
23+
required=True)
24+
@click.option('--tier',
25+
help='Endurance Storage Tier (IOPS per GB) of the primary'
26+
' volume for which a replicant is ordered [optional]',
27+
type=click.Choice(['0.25', '2', '4']))
28+
@click.option('--os-type',
29+
help='Operating System Type (e.g.: LINUX) of the primary'
30+
' volume for which a replica is ordered [optional]',
31+
type=click.Choice([
32+
'HYPER_V',
33+
'LINUX',
34+
'VMWARE',
35+
'WINDOWS_2008',
36+
'WINDOWS_GPT',
37+
'WINDOWS',
38+
'XEN']))
39+
@environment.pass_env
40+
def cli(env, volume_id, snapshot_schedule, location, tier, os_type):
41+
"""Order a block storage replica volume."""
42+
block_manager = SoftLayer.BlockStorageManager(env.client)
43+
44+
if tier is not None:
45+
tier = float(tier)
46+
47+
try:
48+
order = block_manager.order_replicant_volume(
49+
volume_id,
50+
snapshot_schedule=snapshot_schedule,
51+
location=location,
52+
tier=tier,
53+
os_type=os_type,
54+
)
55+
except ValueError as ex:
56+
raise exceptions.ArgumentError(str(ex))
57+
58+
if 'placedOrder' in order.keys():
59+
click.echo("Order #{0} placed successfully!".format(
60+
order['placedOrder']['id']))
61+
for item in order['placedOrder']['items']:
62+
click.echo(" > %s" % item['description'])
63+
else:
64+
click.echo("Order could not be placed! Please verify your options " +
65+
"and try again.")

SoftLayer/CLI/block/snapshot/create.py

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,5 +16,9 @@ def cli(env, volume_id, notes):
1616
block_manager = SoftLayer.BlockStorageManager(env.client)
1717
snapshot = block_manager.create_snapshot(volume_id, notes=notes)
1818

19-
if snapshot['id']:
19+
if 'id' in snapshot:
2020
click.echo('New snapshot created with id: %s' % snapshot['id'])
21+
else:
22+
click.echo('Error occurred while creating snapshot.\n'
23+
'Ensure volume is not failed over or in another '
24+
'state which prevents taking snapshots.')

SoftLayer/CLI/file/detail.py

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -75,4 +75,37 @@ def cli(env, volume_id):
7575
'Ongoing Transactions',
7676
trans['transactionStatus']['friendlyName']])
7777

78+
table.add_row(['Replicant Count', "%u"
79+
% file_volume['replicationPartnerCount']])
80+
81+
if file_volume['replicationPartnerCount'] > 0:
82+
# This if/else temporarily handles a bug in which the SL API
83+
# returns a string or object for 'replicationStatus'; it seems that
84+
# the type is string for File volumes and object for Block volumes
85+
if 'message' in file_volume['replicationStatus']:
86+
table.add_row(['Replication Status', "%s"
87+
% file_volume['replicationStatus']['message']])
88+
else:
89+
table.add_row(['Replication Status', "%s"
90+
% file_volume['replicationStatus']])
91+
92+
replicant_list = []
93+
for replicant in file_volume['replicationPartners']:
94+
replicant_table = formatting.Table(['Replicant ID',
95+
replicant['id']])
96+
replicant_table.add_row([
97+
'Volume Name',
98+
replicant['username']])
99+
replicant_table.add_row([
100+
'Target IP',
101+
replicant['serviceResourceBackendIpAddress']])
102+
replicant_table.add_row([
103+
'Data Center',
104+
replicant['serviceResource']['datacenter']['name']])
105+
replicant_table.add_row([
106+
'Schedule',
107+
replicant['replicationSchedule']['type']['keyname']])
108+
replicant_list.append(replicant_table)
109+
table.add_row(['Replicant Volumes', replicant_list])
110+
78111
env.fout(table)
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
"""File Storage Replication Control."""
Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
"""Failback from a replicant volume."""
2+
# :license: MIT, see LICENSE for more details.
3+
4+
import click
5+
6+
import SoftLayer
7+
from SoftLayer.CLI import environment
8+
9+
10+
@click.command()
11+
@click.argument('volume-id')
12+
@click.option('--replicant-id', help="ID of the replicant volume")
13+
@environment.pass_env
14+
def cli(env, volume_id, replicant_id):
15+
"""Failback a file volume from the given replicant volume."""
16+
file_storage_manager = SoftLayer.FileStorageManager(env.client)
17+
18+
success = file_storage_manager.failback_from_replicant(
19+
volume_id,
20+
replicant_id
21+
)
22+
23+
if success:
24+
click.echo("Failback from replicant is now in progress.")
25+
else:
26+
click.echo("Failback operation could not be initiated.")
Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
"""Failover to a replicant volume."""
2+
# :license: MIT, see LICENSE for more details.
3+
4+
import click
5+
import SoftLayer
6+
from SoftLayer.CLI import environment
7+
8+
9+
@click.command()
10+
@click.argument('volume-id')
11+
@click.option('--replicant-id', help="ID of the replicant volume")
12+
@click.option('--immediate',
13+
is_flag=True,
14+
default=False,
15+
help="Failover to replicant immediately.")
16+
@environment.pass_env
17+
def cli(env, volume_id, replicant_id, immediate):
18+
"""Failover a file volume to the given replicant volume."""
19+
file_storage_manager = SoftLayer.FileStorageManager(env.client)
20+
21+
success = file_storage_manager.failover_to_replicant(
22+
volume_id,
23+
replicant_id,
24+
immediate
25+
)
26+
27+
if success:
28+
click.echo("Failover to replicant is now in progress.")
29+
else:
30+
click.echo("Failover operation could not be initiated.")

0 commit comments

Comments
 (0)