Skip to content

Commit 0f48d23

Browse files
RajarajanRajarajan
Rajarajan
authored and
Rajarajan
committed
Adding disaster recovery failover api
1 parent ff74ffc commit 0f48d23

File tree

11 files changed

+188
-5
lines changed

11 files changed

+188
-5
lines changed
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
"""Failover an inaccessible file volume to its available replicant 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 formatting
8+
from SoftLayer.CLI import exceptions
9+
10+
11+
@click.command()
12+
@click.argument('volume-id')
13+
@click.option('--replicant-id', help="ID of the replicant volume")
14+
@environment.pass_env
15+
def cli(env, volume_id, replicant_id):
16+
"""Failover an inaccessible file volume to its available replicant volume."""
17+
block_storage_manager = SoftLayer.BlockStorageManager(env.client)
18+
19+
click.secho("""WARNING:Disaster Recovery Failover a block volume to the given replicant volume.\n"""
20+
"""* This action cannot be undone\n"""
21+
"""* You will not be able to perform failback to the original\n"""
22+
"""* You cannot failover without replica""",fg = 'red' )
23+
24+
if not (formatting.confirm('Are you sure you want to continue?')):
25+
raise exceptions.CLIAbort('Aborted.')
26+
27+
success = block_storage_manager.disaster_recovery_failover_to_replicant(
28+
volume_id,
29+
replicant_id
30+
)
31+
if success:
32+
click.echo("Disaster Recovery Failover to replicant is now in progress.")
33+
else:
34+
click.echo("Disaster Recovery Failover operation could not be initiated.")
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
"""Failover an inaccessible file volume to its available replicant 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 formatting
8+
from SoftLayer.CLI import exceptions
9+
10+
11+
@click.command()
12+
@click.argument('volume-id')
13+
@click.option('--replicant-id', help="ID of the replicant volume")
14+
@environment.pass_env
15+
def cli(env, volume_id, replicant_id):
16+
"""Failover an inaccessible file volume to its available replicant volume."""
17+
file_storage_manager = SoftLayer.FileStorageManager(env.client)
18+
19+
click.secho("""WARNING : Disaster Recovery Failover should not be performed unless data center for the primary volume is unreachable.\n"""
20+
"""* This action cannot be undone\n"""
21+
"""* You will not be able to perform failback to the original without support intervention\n"""
22+
"""* You cannot failover without replica""",fg = 'red' )
23+
24+
if not (formatting.confirm('Are you sure you want to continue?')):
25+
raise exceptions.CLIAbort('Aborted.')
26+
27+
success = file_storage_manager.disaster_recovery_failover_to_replicant(
28+
volume_id,
29+
replicant_id
30+
)
31+
if success:
32+
click.echo("Disaster Recovery Failover to replicant is now in progress.")
33+
else:
34+
click.echo("Disaster Recovery Failover operation could not be initiated.")

SoftLayer/CLI/routes.py

+2
Original file line numberDiff line numberDiff line change
@@ -92,6 +92,7 @@
9292
('block:subnets-remove', 'SoftLayer.CLI.block.subnets.remove:cli'),
9393
('block:replica-failback', 'SoftLayer.CLI.block.replication.failback:cli'),
9494
('block:replica-failover', 'SoftLayer.CLI.block.replication.failover:cli'),
95+
('block:disaster-recovery-failover', 'SoftLayer.CLI.block.replication.disaster_recovery_failover:cli'),
9596
('block:replica-order', 'SoftLayer.CLI.block.replication.order:cli'),
9697
('block:replica-partners', 'SoftLayer.CLI.block.replication.partners:cli'),
9798
('block:replica-locations', 'SoftLayer.CLI.block.replication.locations:cli'),
@@ -127,6 +128,7 @@
127128
('file:access-revoke', 'SoftLayer.CLI.file.access.revoke:cli'),
128129
('file:replica-failback', 'SoftLayer.CLI.file.replication.failback:cli'),
129130
('file:replica-failover', 'SoftLayer.CLI.file.replication.failover:cli'),
131+
('file:disaster-recovery-failover', 'SoftLayer.CLI.file.replication.disaster_recovery_failover:cli'),
130132
('file:replica-order', 'SoftLayer.CLI.file.replication.order:cli'),
131133
('file:replica-partners', 'SoftLayer.CLI.file.replication.partners:cli'),
132134
('file:replica-locations', 'SoftLayer.CLI.file.replication.locations:cli'),

SoftLayer/fixtures/SoftLayer_Network_Storage.py

+1
Original file line numberDiff line numberDiff line change
@@ -221,6 +221,7 @@
221221
failoverToReplicant = True
222222
failbackFromReplicant = True
223223
restoreFromSnapshot = True
224+
disasterRecoveryFailoverToReplicant = True
224225

225226
createSnapshot = {
226227
'id': 449

SoftLayer/managers/storage.py

+10-1
Original file line numberDiff line numberDiff line change
@@ -398,13 +398,22 @@ def failover_to_replicant(self, volume_id, replicant_id):
398398
"""
399399
return self.client.call('Network_Storage', 'failoverToReplicant', replicant_id, id=volume_id)
400400

401+
def disaster_recovery_failover_to_replicant(self, volume_id, replicant_id):
402+
"""Disaster Recovery Failover to a volume replicant.
403+
404+
:param integer volume_id: The id of the volume
405+
:param integer replicant: ID of replicant to failover to
406+
:return: Returns whether failover to successful or not
407+
"""
408+
return self.client.call('Network_Storage', 'disasterRecoveryFailoverToReplicant', replicant_id, id=volume_id)
409+
401410
def failback_from_replicant(self, volume_id):
402411
"""Failback from a volume replicant.
403412
404413
:param integer volume_id: The id of the volume
405414
:return: Returns whether failback was successful or not
406415
"""
407-
416+
408417
return self.client.call('Network_Storage', 'failbackFromReplicant', id=volume_id)
409418

410419
def cancel_volume(self, volume_id, reason='No longer needed', immediate=False):

docs/cli/block.rst

+4
Original file line numberDiff line numberDiff line change
@@ -142,3 +142,7 @@ Block Commands
142142
.. click:: SoftLayer.CLI.block.set_note:cli
143143
:prog: block volume-set-note
144144
:show-nested:
145+
146+
.. click:: SoftLayer.CLI.block.replication.disaster_recovery_failover:cli
147+
:prog: block disaster-recovery-failover
148+
:show-nested:

docs/cli/file.rst

+4
Original file line numberDiff line numberDiff line change
@@ -121,4 +121,8 @@ File Commands
121121

122122
.. click:: SoftLayer.CLI.file.set_note:cli
123123
:prog: file volume-set-note
124+
:show-nested:
125+
126+
.. click:: SoftLayer.CLI.file.replication.disaster_recovery_failover:cli
127+
:prog: file disaster-recovery-failover
124128
:show-nested:

tests/CLI/modules/block_tests.py

+37-2
Original file line numberDiff line numberDiff line change
@@ -4,8 +4,9 @@
44
55
:license: MIT, see LICENSE for more details.
66
"""
7-
from SoftLayer import exceptions
7+
from SoftLayer import SoftLayerAPIError
88
from SoftLayer import testing
9+
from SoftLayer.CLI import exceptions
910

1011
import json
1112
import mock
@@ -48,7 +49,7 @@ def test_volume_set_lun_id_in_range_missing_value(self):
4849
def test_volume_set_lun_id_not_in_range(self):
4950
value = '-1'
5051
lun_mock = self.set_mock('SoftLayer_Network_Storage', 'createOrUpdateLunId')
51-
lun_mock.side_effect = exceptions.SoftLayerAPIError(
52+
lun_mock.side_effect = SoftLayerAPIError(
5253
'SoftLayer_Exception_Network_Storage_Iscsi_InvalidLunId',
5354
'The LUN ID specified is out of the valid range: %s [min: 0 max: 4095]' % (value))
5455
result = self.run_command('block volume-set-lun-id 1234 42'.split())
@@ -498,6 +499,18 @@ def test_replicant_failover(self):
498499
self.assert_no_fail(result)
499500
self.assertEqual('Failover to replicant is now in progress.\n',
500501
result.output)
502+
503+
@mock.patch('SoftLayer.CLI.formatting.confirm')
504+
@mock.patch('SoftLayer.BlockStorageManager.disaster_recovery_failover_to_replicant')
505+
def test_disaster_recovery_failover(self, disaster_recovery_failover_mock, confirm_mock):
506+
confirm_mock.return_value = True
507+
disaster_recovery_failover_mock.return_value = True
508+
result = self.run_command(['block', 'disaster-recovery-failover', '12345678',
509+
'--replicant-id=5678'])
510+
511+
self.assert_no_fail(result)
512+
self.assertIn('Disaster Recovery Failover to replicant is now in progress.\n',
513+
result.output)
501514

502515
def test_replication_locations(self):
503516
result = self.run_command(['block', 'replica-locations', '1234'])
@@ -558,6 +571,28 @@ def test_replicant_failover_unsuccessful(self, failover_mock):
558571
self.assertEqual('Failover operation could not be initiated.\n',
559572
result.output)
560573

574+
@mock.patch('SoftLayer.CLI.formatting.confirm')
575+
@mock.patch('SoftLayer.BlockStorageManager.disaster_recovery_failover_to_replicant')
576+
def test_disaster_recovery_failover_unsuccesful(self, disaster_recovery_failover_mock, confirm_mock):
577+
confirm_mock.return_value = True
578+
disaster_recovery_failover_mock.return_value = False
579+
580+
result = self.run_command(['block', 'disaster-recovery-failover', '12345678',
581+
'--replicant-id=5678'])
582+
583+
self.assertIn('Disaster Recovery Failover operation could not be initiated.\n',
584+
result.output)
585+
586+
@mock.patch('SoftLayer.CLI.formatting.confirm')
587+
def test_disaster_recovery_failover_aborted(self, confirm_mock):
588+
confirm_mock.return_value = False
589+
590+
result = self.run_command(['block', 'disaster-recovery-failover', '12345678',
591+
'--replicant-id=5678'])
592+
593+
self.assertEqual(result.exit_code, 2)
594+
self.assertIsInstance(result.exception, exceptions.CLIAbort)
595+
561596
def test_replicant_failback(self):
562597
result = self.run_command(['block', 'replica-failback', '12345678'])
563598

tests/CLI/modules/file_tests.py

+38-2
Original file line numberDiff line numberDiff line change
@@ -4,8 +4,9 @@
44
55
:license: MIT, see LICENSE for more details.
66
"""
7-
from SoftLayer import exceptions
7+
from SoftLayer import SoftLayerError
88
from SoftLayer import testing
9+
from SoftLayer.CLI import exceptions
910

1011
import json
1112
import mock
@@ -126,7 +127,7 @@ def test_volume_cancel_without_billing_item(self):
126127
result = self.run_command([
127128
'--really', 'file', 'volume-cancel', '1234'])
128129

129-
self.assertIsInstance(result.exception, exceptions.SoftLayerError)
130+
self.assertIsInstance(result.exception, SoftLayerError)
130131

131132
def test_volume_detail(self):
132133
result = self.run_command(['file', 'volume-detail', '1234'])
@@ -493,6 +494,41 @@ def test_replicant_failover_unsuccessful(self, failover_mock):
493494
self.assertEqual('Failover operation could not be initiated.\n',
494495
result.output)
495496

497+
@mock.patch('SoftLayer.CLI.formatting.confirm')
498+
@mock.patch('SoftLayer.FileStorageManager.disaster_recovery_failover_to_replicant')
499+
def test_disaster_recovery_failover(self, disaster_recovery_failover_mock, confirm_mock):
500+
confirm_mock.return_value = True
501+
disaster_recovery_failover_mock.return_value = True
502+
result = self.run_command(['file', 'disaster-recovery-failover', '12345678',
503+
'--replicant-id=5678'])
504+
505+
self.assert_no_fail(result)
506+
self.assertEqual('Disaster Recovery Failover to replicant is now in progress.\n',
507+
result.output)
508+
509+
@mock.patch('SoftLayer.CLI.formatting.confirm')
510+
@mock.patch('SoftLayer.FileStorageManager.disaster_recovery_failover_to_replicant')
511+
def test_disaster_recovery_failover_unsuccesful(self, disaster_recovery_failover_mock, confirm_mock):
512+
confirm_mock.return_value = True
513+
disaster_recovery_failover_mock.return_value = False
514+
515+
result = self.run_command(['file', 'disaster-recovery-failover', '12345678',
516+
'--replicant-id=5678'])
517+
518+
self.assertEqual('Disaster Recovery Failover operation could not be initiated.\n',
519+
result.output)
520+
521+
@mock.patch('SoftLayer.CLI.formatting.confirm')
522+
def test_disaster_recovery_failover_aborted(self, confirm_mock):
523+
confirm_mock.return_value = False
524+
525+
result = self.run_command(['file', 'disaster-recovery-failover', '12345678',
526+
'--replicant-id=5678'])
527+
528+
self.assertEqual(result.exit_code, 2)
529+
self.assertIsInstance(result.exception, exceptions.CLIAbort)
530+
531+
496532
def test_replicant_failback(self):
497533
result = self.run_command(['file', 'replica-failback', '12345678'])
498534

tests/managers/block_tests.py

+12
Original file line numberDiff line numberDiff line change
@@ -384,6 +384,18 @@ def test_replicant_failover(self):
384384
identifier=1234,
385385
)
386386

387+
def test_disaster_recovery_failover(self):
388+
result = self.block.disaster_recovery_failover_to_replicant(1234, 5678)
389+
390+
self.assertEqual(
391+
SoftLayer_Network_Storage.disasterRecoveryFailoverToReplicant, result)
392+
self.assert_called_with(
393+
'SoftLayer_Network_Storage',
394+
'disasterRecoveryFailoverToReplicant',
395+
args=(5678,),
396+
identifier=1234,
397+
)
398+
387399
def test_replicant_failback(self):
388400
result = self.block.failback_from_replicant(1234)
389401

tests/managers/file_tests.py

+12
Original file line numberDiff line numberDiff line change
@@ -289,6 +289,18 @@ def test_replicant_failover(self):
289289
identifier=1234,
290290
)
291291

292+
def test_disaster_recovery_failover(self):
293+
result = self.file.disaster_recovery_failover_to_replicant(1234, 5678)
294+
295+
self.assertEqual(
296+
SoftLayer_Network_Storage.disasterRecoveryFailoverToReplicant, result)
297+
self.assert_called_with(
298+
'SoftLayer_Network_Storage',
299+
'disasterRecoveryFailoverToReplicant',
300+
args=(5678,),
301+
identifier=1234,
302+
)
303+
292304
def test_replicant_failback(self):
293305
result = self.file.failback_from_replicant(1234)
294306

0 commit comments

Comments
 (0)