Repository: softlayer/softlayer-python
Pull request #756 (2016-08-17 17:26:52)

Improves doc blocks for all managers with links to product information

coveralls: 
[![Coverage Status](https://coveralls.io/builds/7491646/badge)](https://coveralls.io/builds/7491646)

Coverage remained the same at 81.008% when pulling **16d1f3cd8f833ad8c962ef12c8ade35748e8d458 on sudorandom:small-doc-updates** into **6581b2c4d0e9365a3f528da0d036e20ff887cb6e on softlayer:master**.


Patch:
diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md
index 7a82eb15..460e436e 100644
--- a/CONTRIBUTING.md
+++ b/CONTRIBUTING.md
@@ -3,9 +3,9 @@ # Contributing to softlayer-python
 We are happy to accept contributions to softlayer-python.  Please follow the
 guidelines below.  
 
-* Sign our contributor agreement (CLA) You can find the [CLA here](./docs/cla-individual.md).
+* Sign our contributor agreement (CLA) You can find the [CLA here](./docs/dev/cla-individual.md).
 
-* If you're contributing on behalf of your employer we'll need a signed copy of our corporate contributor agreement (CCLA) as well.  You can find the [CCLA here](./docs/cla-corporate.md).
+* If you're contributing on behalf of your employer we'll need a signed copy of our corporate contributor agreement (CCLA) as well.  You can find the [CCLA here](./docs/dev/cla-corporate.md).
     
 * Fork the repo, make your changes, and open a pull request.
 
diff --git a/SoftLayer/CLI/block/detail.py b/SoftLayer/CLI/block/detail.py
index 37a68430..51a1fbd4 100644
--- a/SoftLayer/CLI/block/detail.py
+++ b/SoftLayer/CLI/block/detail.py
@@ -66,4 +66,37 @@ def cli(env, volume_id):
                 'Ongoing Transactions',
                 trans['transactionStatus']['friendlyName']])
 
+    table.add_row(['Replicant Count', "%u"
+                   % block_volume['replicationPartnerCount']])
+
+    if block_volume['replicationPartnerCount'] > 0:
+        # This if/else temporarily handles a bug in which the SL API
+        # returns a string or object for 'replicationStatus'; it seems that
+        # the type is string for File volumes and object for Block volumes
+        if 'message' in block_volume['replicationStatus']:
+            table.add_row(['Replication Status', "%s"
+                           % block_volume['replicationStatus']['message']])
+        else:
+            table.add_row(['Replication Status', "%s"
+                           % block_volume['replicationStatus']])
+
+        replicant_list = []
+        for replicant in block_volume['replicationPartners']:
+            replicant_table = formatting.Table(['Replicant ID',
+                                                replicant['id']])
+            replicant_table.add_row([
+                'Volume Name',
+                replicant['username']])
+            replicant_table.add_row([
+                'Target IP',
+                replicant['serviceResourceBackendIpAddress']])
+            replicant_table.add_row([
+                'Data Center',
+                replicant['serviceResource']['datacenter']['name']])
+            replicant_table.add_row([
+                'Schedule',
+                replicant['replicationSchedule']['type']['keyname']])
+            replicant_list.append(replicant_table)
+        table.add_row(['Replicant Volumes', replicant_list])
+
     env.fout(table)
diff --git a/SoftLayer/CLI/block/order.py b/SoftLayer/CLI/block/order.py
index 4393a282..22430e42 100644
--- a/SoftLayer/CLI/block/order.py
+++ b/SoftLayer/CLI/block/order.py
@@ -7,7 +7,7 @@
 from SoftLayer.CLI import exceptions
 
 
-CONTEXT_SETTINGS = dict(token_normalize_func=lambda x: x.upper())
+CONTEXT_SETTINGS = {'token_normalize_func': lambda x: x.upper()}
 
 
 @click.command(context_settings=CONTEXT_SETTINGS)
@@ -42,8 +42,14 @@
 @click.option('--location',
               help='Datacenter short name (e.g.: dal09)',
               required=True)
+@click.option('--snapshot-size',
+              type=int,
+              help='Optional parameter for ordering snapshot '
+              'space along with endurance block storage; specifies '
+              'the size (in GB) of snapshot space to order')
 @environment.pass_env
-def cli(env, storage_type, size, iops, tier, os_type, location):
+def cli(env, storage_type, size, iops, tier, os_type,
+        location, snapshot_size):
     """Order a block storage volume."""
     block_manager = SoftLayer.BlockStorageManager(env.client)
     storage_type = storage_type.lower()
@@ -62,6 +68,12 @@ def cli(env, storage_type, size, iops, tier, os_type, location):
                 'Option --iops must be a multiple of 100'
             )
 
+        if snapshot_size is not None:
+            raise exceptions.CLIAbort(
+                'Option --snapshot-size not allowed for performance volumes.'
+                ' Snapshots are only available for endurance storage.'
+            )
+
         try:
             order = block_manager.order_block_volume(
                 storage_type='performance_storage_iscsi',
@@ -84,7 +96,8 @@ def cli(env, storage_type, size, iops, tier, os_type, location):
                 location=location,
                 size=size,
                 tier_level=float(tier),
-                os_type=os_type
+                os_type=os_type,
+                snapshot_size=snapshot_size
             )
         except ValueError as ex:
             raise exceptions.ArgumentError(str(ex))
diff --git a/SoftLayer/CLI/block/replication/__init__.py b/SoftLayer/CLI/block/replication/__init__.py
new file mode 100644
index 00000000..ac0a44b7
--- /dev/null
+++ b/SoftLayer/CLI/block/replication/__init__.py
@@ -0,0 +1,1 @@
+"""Block Storage Replication Control."""
diff --git a/SoftLayer/CLI/block/replication/failback.py b/SoftLayer/CLI/block/replication/failback.py
new file mode 100644
index 00000000..3887c29c
--- /dev/null
+++ b/SoftLayer/CLI/block/replication/failback.py
@@ -0,0 +1,25 @@
+"""Failback from a replicant volume."""
+# :license: MIT, see LICENSE for more details.
+
+import click
+import SoftLayer
+from SoftLayer.CLI import environment
+
+
+@click.command()
+@click.argument('volume-id')
+@click.option('--replicant-id', help="ID of the replicant volume")
+@environment.pass_env
+def cli(env, volume_id, replicant_id):
+    """Failback a block volume from the given replicant volume."""
+    block_storage_manager = SoftLayer.BlockStorageManager(env.client)
+
+    success = block_storage_manager.failback_from_replicant(
+        volume_id,
+        replicant_id
+    )
+
+    if success:
+        click.echo("Failback from replicant is now in progress.")
+    else:
+        click.echo("Failback operation could not be initiated.")
diff --git a/SoftLayer/CLI/block/replication/failover.py b/SoftLayer/CLI/block/replication/failover.py
new file mode 100644
index 00000000..545175c4
--- /dev/null
+++ b/SoftLayer/CLI/block/replication/failover.py
@@ -0,0 +1,30 @@
+"""Failover to a replicant volume."""
+# :license: MIT, see LICENSE for more details.
+
+import click
+import SoftLayer
+from SoftLayer.CLI import environment
+
+
+@click.command()
+@click.argument('volume-id')
+@click.option('--replicant-id', help="ID of the replicant volume")
+@click.option('--immediate',
+              is_flag=True,
+              default=False,
+              help="Failover to replicant immediately.")
+@environment.pass_env
+def cli(env, volume_id, replicant_id, immediate):
+    """Failover a block volume to the given replicant volume."""
+    block_storage_manager = SoftLayer.BlockStorageManager(env.client)
+
+    success = block_storage_manager.failover_to_replicant(
+        volume_id,
+        replicant_id,
+        immediate
+    )
+
+    if success:
+        click.echo("Failover to replicant is now in progress.")
+    else:
+        click.echo("Failover operation could not be initiated.")
diff --git a/SoftLayer/CLI/block/replication/order.py b/SoftLayer/CLI/block/replication/order.py
new file mode 100644
index 00000000..c25a8beb
--- /dev/null
+++ b/SoftLayer/CLI/block/replication/order.py
@@ -0,0 +1,65 @@
+"""Order a block storage replica volume."""
+# :license: MIT, see LICENSE for more details.
+
+import click
+import SoftLayer
+from SoftLayer.CLI import environment
+from SoftLayer.CLI import exceptions
+
+
+CONTEXT_SETTINGS = {'token_normalize_func': lambda x: x.upper()}
+
+
+@click.command(context_settings=CONTEXT_SETTINGS)
+@click.argument('volume_id')
+@click.option('--snapshot-schedule', '-s',
+              help='Snapshot schedule to use for replication, '
+              '(HOURLY | DAILY | WEEKLY)',
+              required=True,
+              type=click.Choice(['HOURLY', 'DAILY', 'WEEKLY']))
+@click.option('--location', '-l',
+              help='Short name of the data center for the replicant '
+              '(e.g.: dal09)',
+              required=True)
+@click.option('--tier',
+              help='Endurance Storage Tier (IOPS per GB) of the primary'
+              ' volume for which a replicant is ordered [optional]',
+              type=click.Choice(['0.25', '2', '4']))
+@click.option('--os-type',
+              help='Operating System Type (e.g.: LINUX) of the primary'
+              ' volume for which a replica is ordered [optional]',
+              type=click.Choice([
+                  'HYPER_V',
+                  'LINUX',
+                  'VMWARE',
+                  'WINDOWS_2008',
+                  'WINDOWS_GPT',
+                  'WINDOWS',
+                  'XEN']))
+@environment.pass_env
+def cli(env, volume_id, snapshot_schedule, location, tier, os_type):
+    """Order a block storage replica volume."""
+    block_manager = SoftLayer.BlockStorageManager(env.client)
+
+    if tier is not None:
+        tier = float(tier)
+
+    try:
+        order = block_manager.order_replicant_volume(
+            volume_id,
+            snapshot_schedule=snapshot_schedule,
+            location=location,
+            tier=tier,
+            os_type=os_type,
+        )
+    except ValueError as ex:
+        raise exceptions.ArgumentError(str(ex))
+
+    if 'placedOrder' in order.keys():
+        click.echo("Order #{0} placed successfully!".format(
+            order['placedOrder']['id']))
+        for item in order['placedOrder']['items']:
+            click.echo(" > %s" % item['description'])
+    else:
+        click.echo("Order could not be placed! Please verify your options " +
+                   "and try again.")
diff --git a/SoftLayer/CLI/block/snapshot/create.py b/SoftLayer/CLI/block/snapshot/create.py
index 4e327b0d..68dc976f 100644
--- a/SoftLayer/CLI/block/snapshot/create.py
+++ b/SoftLayer/CLI/block/snapshot/create.py
@@ -16,5 +16,9 @@ def cli(env, volume_id, notes):
     block_manager = SoftLayer.BlockStorageManager(env.client)
     snapshot = block_manager.create_snapshot(volume_id, notes=notes)
 
-    if snapshot['id']:
+    if 'id' in snapshot:
         click.echo('New snapshot created with id: %s' % snapshot['id'])
+    else:
+        click.echo('Error occurred while creating snapshot.\n'
+                   'Ensure volume is not failed over or in another '
+                   'state which prevents taking snapshots.')
diff --git a/SoftLayer/CLI/file/detail.py b/SoftLayer/CLI/file/detail.py
index e8ceda92..b73ecc0d 100644
--- a/SoftLayer/CLI/file/detail.py
+++ b/SoftLayer/CLI/file/detail.py
@@ -75,4 +75,37 @@ def cli(env, volume_id):
                 'Ongoing Transactions',
                 trans['transactionStatus']['friendlyName']])
 
+    table.add_row(['Replicant Count', "%u"
+                   % file_volume['replicationPartnerCount']])
+
+    if file_volume['replicationPartnerCount'] > 0:
+        # This if/else temporarily handles a bug in which the SL API
+        # returns a string or object for 'replicationStatus'; it seems that
+        # the type is string for File volumes and object for Block volumes
+        if 'message' in file_volume['replicationStatus']:
+            table.add_row(['Replication Status', "%s"
+                           % file_volume['replicationStatus']['message']])
+        else:
+            table.add_row(['Replication Status', "%s"
+                           % file_volume['replicationStatus']])
+
+        replicant_list = []
+        for replicant in file_volume['replicationPartners']:
+            replicant_table = formatting.Table(['Replicant ID',
+                                                replicant['id']])
+            replicant_table.add_row([
+                'Volume Name',
+                replicant['username']])
+            replicant_table.add_row([
+                'Target IP',
+                replicant['serviceResourceBackendIpAddress']])
+            replicant_table.add_row([
+                'Data Center',
+                replicant['serviceResource']['datacenter']['name']])
+            replicant_table.add_row([
+                'Schedule',
+                replicant['replicationSchedule']['type']['keyname']])
+            replicant_list.append(replicant_table)
+        table.add_row(['Replicant Volumes', replicant_list])
+
     env.fout(table)
diff --git a/SoftLayer/CLI/file/order.py b/SoftLayer/CLI/file/order.py
index bb200f27..0219efdb 100644
--- a/SoftLayer/CLI/file/order.py
+++ b/SoftLayer/CLI/file/order.py
@@ -7,7 +7,7 @@
 from SoftLayer.CLI import exceptions
 
 
-CONTEXT_SETTINGS = dict(token_normalize_func=lambda x: x.upper())
+CONTEXT_SETTINGS = {'token_normalize_func': lambda x: x.upper()}
 
 
 @click.command(context_settings=CONTEXT_SETTINGS)
@@ -42,8 +42,14 @@
 @click.option('--location',
               help='Datacenter short name (e.g.: dal09)',
               required=True)
+@click.option('--snapshot-size',
+              type=int,
+              help='Optional parameter for ordering snapshot '
+              'space along with endurance file storage; specifies '
+              'the size (in GB) of snapshot space to order')
 @environment.pass_env
-def cli(env, storage_type, size, iops, tier, os_type, location):
+def cli(env, storage_type, size, iops, tier, os_type,
+        location, snapshot_size):
     """Order a file storage volume."""
     file_manager = SoftLayer.FileStorageManager(env.client)
     storage_type = storage_type.lower()
@@ -62,6 +68,12 @@ def cli(env, storage_type, size, iops, tier, os_type, location):
                 'Option --iops must be a multiple of 100'
             )
 
+        if snapshot_size is not None:
+            raise exceptions.CLIAbort(
+                'Option --snapshot-size not allowed for performance volumes.'
+                ' Snapshots are only available for endurance storage.'
+            )
+
         try:
             order = file_manager.order_file_volume(
                 storage_type='performance_storage_nfs',
@@ -84,7 +96,8 @@ def cli(env, storage_type, size, iops, tier, os_type, location):
                 location=location,
                 size=size,
                 tier_level=float(tier),
-                os_type=os_type
+                os_type=os_type,
+                snapshot_size=snapshot_size
             )
         except ValueError as ex:
             raise exceptions.ArgumentError(str(ex))
diff --git a/SoftLayer/CLI/file/replication/__init__.py b/SoftLayer/CLI/file/replication/__init__.py
new file mode 100644
index 00000000..11030ec3
--- /dev/null
+++ b/SoftLayer/CLI/file/replication/__init__.py
@@ -0,0 +1,1 @@
+"""File Storage Replication Control."""
diff --git a/SoftLayer/CLI/file/replication/failback.py b/SoftLayer/CLI/file/replication/failback.py
new file mode 100644
index 00000000..6fc2e9b7
--- /dev/null
+++ b/SoftLayer/CLI/file/replication/failback.py
@@ -0,0 +1,26 @@
+"""Failback from a replicant volume."""
+# :license: MIT, see LICENSE for more details.
+
+import click
+
+import SoftLayer
+from SoftLayer.CLI import environment
+
+
+@click.command()
+@click.argument('volume-id')
+@click.option('--replicant-id', help="ID of the replicant volume")
+@environment.pass_env
+def cli(env, volume_id, replicant_id):
+    """Failback a file volume from the given replicant volume."""
+    file_storage_manager = SoftLayer.FileStorageManager(env.client)
+
+    success = file_storage_manager.failback_from_replicant(
+        volume_id,
+        replicant_id
+    )
+
+    if success:
+        click.echo("Failback from replicant is now in progress.")
+    else:
+        click.echo("Failback operation could not be initiated.")
diff --git a/SoftLayer/CLI/file/replication/failover.py b/SoftLayer/CLI/file/replication/failover.py
new file mode 100644
index 00000000..d5695fb5
--- /dev/null
+++ b/SoftLayer/CLI/file/replication/failover.py
@@ -0,0 +1,30 @@
+"""Failover to a replicant volume."""
+# :license: MIT, see LICENSE for more details.
+
+import click
+import SoftLayer
+from SoftLayer.CLI import environment
+
+
+@click.command()
+@click.argument('volume-id')
+@click.option('--replicant-id', help="ID of the replicant volume")
+@click.option('--immediate',
+              is_flag=True,
+              default=False,
+              help="Failover to replicant immediately.")
+@environment.pass_env
+def cli(env, volume_id, replicant_id, immediate):
+    """Failover a file volume to the given replicant volume."""
+    file_storage_manager = SoftLayer.FileStorageManager(env.client)
+
+    success = file_storage_manager.failover_to_replicant(
+        volume_id,
+        replicant_id,
+        immediate
+    )
+
+    if success:
+        click.echo("Failover to replicant is now in progress.")
+    else:
+        click.echo("Failover operation could not be initiated.")
diff --git a/SoftLayer/CLI/file/replication/order.py b/SoftLayer/CLI/file/replication/order.py
new file mode 100644
index 00000000..7c4617fa
--- /dev/null
+++ b/SoftLayer/CLI/file/replication/order.py
@@ -0,0 +1,53 @@
+"""Order a file storage replica volume."""
+# :license: MIT, see LICENSE for more details.
+
+import click
+import SoftLayer
+from SoftLayer.CLI import environment
+from SoftLayer.CLI import exceptions
+
+
+CONTEXT_SETTINGS = {'token_normalize_func': lambda x: x.upper()}
+
+
+@click.command(context_settings=CONTEXT_SETTINGS)
+@click.argument('volume_id')
+@click.option('--snapshot-schedule', '-s',
+              help='Snapshot schedule to use for replication, '
+              '(HOURLY | DAILY | WEEKLY)',
+              required=True,
+              type=click.Choice(['HOURLY', 'DAILY', 'WEEKLY']))
+@click.option('--location', '-l',
+              help='Short name of the data center for the replicant '
+              '(e.g.: dal09)',
+              required=True)
+@click.option('--tier',
+              help='Endurance Storage Tier (IOPS per GB) of the primary'
+              ' volume for which a replicant is ordered [optional]',
+              type=click.Choice(['0.25', '2', '4']))
+@environment.pass_env
+def cli(env, volume_id, snapshot_schedule, location, tier):
+    """Order a file storage replica volume."""
+    file_manager = SoftLayer.FileStorageManager(env.client)
+
+    if tier is not None:
+        tier = float(tier)
+
+    try:
+        order = file_manager.order_replicant_volume(
+            volume_id,
+            snapshot_schedule=snapshot_schedule,
+            location=location,
+            tier=tier,
+        )
+    except ValueError as ex:
+        raise exceptions.ArgumentError(str(ex))
+
+    if 'placedOrder' in order.keys():
+        click.echo("Order #{0} placed successfully!".format(
+            order['placedOrder']['id']))
+        for item in order['placedOrder']['items']:
+            click.echo(" > %s" % item['description'])
+    else:
+        click.echo("Order could not be placed! Please verify your options " +
+                   "and try again.")
diff --git a/SoftLayer/CLI/file/snapshot/create.py b/SoftLayer/CLI/file/snapshot/create.py
index 9c33d026..ec7ff1ff 100644
--- a/SoftLayer/CLI/file/snapshot/create.py
+++ b/SoftLayer/CLI/file/snapshot/create.py
@@ -16,5 +16,9 @@ def cli(env, volume_id, notes):
     file_storage_manager = SoftLayer.FileStorageManager(env.client)
     snapshot = file_storage_manager.create_snapshot(volume_id, notes=notes)
 
-    if snapshot['id']:
+    if 'id' in snapshot:
         click.echo('New snapshot created with id: %s' % snapshot['id'])
+    else:
+        click.echo('Error occurred while creating snapshot.\n'
+                   'Ensure volume is not failed over or in another '
+                   'state which prevents taking snapshots.')
diff --git a/SoftLayer/CLI/hardware/detail.py b/SoftLayer/CLI/hardware/detail.py
index 012c5df5..bba31b2f 100644
--- a/SoftLayer/CLI/hardware/detail.py
+++ b/SoftLayer/CLI/hardware/detail.py
@@ -50,14 +50,13 @@ def cli(env, identifier, passwords, price):
                    result['primaryBackendIpAddress'] or formatting.blank()])
     table.add_row(['ipmi_ip',
                    result['networkManagementIpAddress'] or formatting.blank()])
-    table.add_row([
-        'os',
-        formatting.FormattedItem(
-            result['operatingSystem']['softwareLicense']
-            ['softwareDescription']['referenceCode'] or formatting.blank(),
-            result['operatingSystem']['softwareLicense']
-            ['softwareDescription']['name'] or formatting.blank()
-        )])
+    operating_system = utils.lookup(result,
+                                    'operatingSystem',
+                                    'softwareLicense',
+                                    'softwareDescription') or {}
+    table.add_row(['os', operating_system.get('name') or formatting.blank()])
+    table.add_row(['os_version',
+                   operating_system.get('version') or formatting.blank()])
 
     table.add_row(
         ['created', result['provisionDate'] or formatting.blank()])
diff --git a/SoftLayer/CLI/routes.py b/SoftLayer/CLI/routes.py
index 4e5f0ecf..03175c77 100644
--- a/SoftLayer/CLI/routes.py
+++ b/SoftLayer/CLI/routes.py
@@ -60,6 +60,9 @@
     ('block:access-authorize', 'SoftLayer.CLI.block.access.authorize:cli'),
     ('block:access-list', 'SoftLayer.CLI.block.access.list:cli'),
     ('block:access-revoke', 'SoftLayer.CLI.block.access.revoke:cli'),
+    ('block:replica-failback', 'SoftLayer.CLI.block.replication.failback:cli'),
+    ('block:replica-failover', 'SoftLayer.CLI.block.replication.failover:cli'),
+    ('block:replica-order', 'SoftLayer.CLI.block.replication.order:cli'),
     ('block:snapshot-cancel', 'SoftLayer.CLI.block.snapshot.cancel:cli'),
     ('block:snapshot-create', 'SoftLayer.CLI.block.snapshot.create:cli'),
     ('block:snapshot-delete', 'SoftLayer.CLI.block.snapshot.delete:cli'),
@@ -77,6 +80,9 @@
     ('file:access-authorize', 'SoftLayer.CLI.file.access.authorize:cli'),
     ('file:access-list', 'SoftLayer.CLI.file.access.list:cli'),
     ('file:access-revoke', 'SoftLayer.CLI.file.access.revoke:cli'),
+    ('file:replica-failback', 'SoftLayer.CLI.file.replication.failback:cli'),
+    ('file:replica-failover', 'SoftLayer.CLI.file.replication.failover:cli'),
+    ('file:replica-order', 'SoftLayer.CLI.file.replication.order:cli'),
     ('file:snapshot-cancel', 'SoftLayer.CLI.file.snapshot.cancel:cli'),
     ('file:snapshot-create', 'SoftLayer.CLI.file.snapshot.create:cli'),
     ('file:snapshot-delete', 'SoftLayer.CLI.file.snapshot.delete:cli'),
diff --git a/SoftLayer/CLI/virt/detail.py b/SoftLayer/CLI/virt/detail.py
index a4464e61..00921436 100644
--- a/SoftLayer/CLI/virt/detail.py
+++ b/SoftLayer/CLI/virt/detail.py
@@ -49,12 +49,7 @@ def cli(env, identifier, passwords=False, price=False):
                                     'operatingSystem',
                                     'softwareLicense',
                                     'softwareDescription') or {}
-    table.add_row([
-        'os',
-        formatting.FormattedItem(
-            operating_system.get('version') or formatting.blank(),
-            operating_system.get('name') or formatting.blank()
-        )])
+    table.add_row(['os', operating_system.get('name') or formatting.blank()])
     table.add_row(['os_version',
                    operating_system.get('version') or formatting.blank()])
     table.add_row(['cores', result['maxCpu']])
diff --git a/SoftLayer/fixtures/SoftLayer_Hardware_Server.py b/SoftLayer/fixtures/SoftLayer_Hardware_Server.py
index 60f0b154..6919dfef 100644
--- a/SoftLayer/fixtures/SoftLayer_Hardware_Server.py
+++ b/SoftLayer/fixtures/SoftLayer_Hardware_Server.py
@@ -31,8 +31,9 @@
     'operatingSystem': {
         'softwareLicense': {
             'softwareDescription': {
-                'referenceCode': 'Ubuntu',
-                'name': 'Ubuntu 12.04 LTS',
+                'referenceCode': 'UBUNTU_12_64',
+                'name': 'Ubuntu',
+                'version': 'Ubuntu 12.04 LTS',
             }
         },
         'passwords': [
diff --git a/SoftLayer/fixtures/SoftLayer_Network_Storage.py b/SoftLayer/fixtures/SoftLayer_Network_Storage.py
index b87b85fa..52a178e3 100644
--- a/SoftLayer/fixtures/SoftLayer_Network_Storage.py
+++ b/SoftLayer/fixtures/SoftLayer_Network_Storage.py
@@ -2,10 +2,12 @@
     'accountId': 1234,
     'billingItem': {
         'id': 449,
+        'cancellationDate': '',
         'categoryCode': 'storage_service_enterprise',
         'activeChildren': [{
             'categoryCode': 'storage_snapshot_space',
-            'id': 123
+            'id': 123,
+            'cancellationDate': '',
         }]
     },
     'capacityGb': 20,
@@ -20,8 +22,13 @@
     'serviceProviderId': 1,
     'iops': 1000,
     'storageTierLevel': {'description': '2 IOPS per GB'},
-    'snapshotCapacityGb': 10,
+    'snapshotCapacityGb': '10',
     'parentVolume': {'snapshotSizeBytes': 1024},
+    'osType': {'keyName': 'LINUX'},
+    'schedules': [{
+        'id': 978,
+        'type': {'keyname': 'SNAPSHOT_WEEKLY'},
+    }],
     'serviceResource': {'datacenter': {'id': 449500, 'name': 'dal05'}},
     'serviceResourceBackendIpAddress': '10.1.2.3',
     'serviceResourceName': 'Storage Type 01 Aggregate staaspar0101_pc01',
@@ -70,6 +77,21 @@
             'credential': {'username': 'joe', 'password': '12345'},
         },
     }],
+    'replicationStatus': 'Replicant Volume Provisioning has completed.',
+    'replicationPartnerCount': 1,
+    'replicationPartners': [{
+        'id': 1784,
+        'username': 'TEST_REP_1',
+        'serviceResourceBackendIpAddress': '10.3.174.79',
+        'serviceResource': {'datacenter': {'name': 'wdc01'}},
+        'replicationSchedule': {'type': {'keyname': 'REPLICATION_HOURLY'}},
+    }, {
+        'id': 1785,
+        'username': 'TEST_REP_2',
+        'serviceResourceBackendIpAddress': '10.3.177.84',
+        'serviceResource': {'datacenter': {'name': 'dal01'}},
+        'replicationSchedule': {'type': {'keyname': 'REPLICATION_DAILY'}},
+    }],
 }
 
 getSnapshots = [{
@@ -82,7 +104,8 @@
 deleteObject = True
 allowAccessFromHostList = True
 removeAccessFromHostList = True
-
+failoverToReplicant = True
+failbackFromReplicant = True
 restoreFromSnapshot = True
 
 createSnapshot = {
diff --git a/SoftLayer/managers/block.py b/SoftLayer/managers/block.py
index 10f170f3..90f94cb4 100644
--- a/SoftLayer/managers/block.py
+++ b/SoftLayer/managers/block.py
@@ -11,7 +11,12 @@
 
 
 class BlockStorageManager(utils.IdentifierMixin, object):
-    """Manages Block Storage volumes."""
+    """Manages SoftLayer Block Storage volumes.
+
+    See product information here: http://www.softlayer.com/block-storage
+
+    :param SoftLayer.API.BaseClient client: the client instance
+    """
 
     def __init__(self, client):
         self.configuration = {}
@@ -46,7 +51,7 @@ def list_block_volumes(self, datacenter=None, username=None,
             (utils.query_filter('!~ ISCSI'))
 
         _filter['iscsiNetworkStorage']['storageType']['keyName'] = (
-            utils.query_filter('*BLOCK_STORAGE'))
+            utils.query_filter('*BLOCK_STORAGE*'))
         if storage_type:
             _filter['iscsiNetworkStorage']['storageType']['keyName'] = (
                 utils.query_filter('%s_BLOCK_STORAGE' % storage_type.upper()))
@@ -86,6 +91,12 @@ def get_block_volume_details(self, volume_id, **kwargs):
                 'lunId',
                 'activeTransactionCount',
                 'activeTransactions.transactionStatus[friendlyName]',
+                'replicationPartnerCount',
+                'replicationStatus',
+                'replicationPartners[id,username,'
+                'serviceResourceBackendIpAddress,'
+                'serviceResource[datacenter[name]],'
+                'replicationSchedule[type[keyname]]]',
             ]
             kwargs['mask'] = ','.join(items)
         return self.client.call('Network_Storage', 'getObject',
@@ -184,6 +195,43 @@ def deauthorize_host_to_volume(self, volume_id,
         return self.client.call('Network_Storage', 'removeAccessFromHostList',
                                 host_templates, id=volume_id, **kwargs)
 
+    def order_replicant_volume(self, volume_id, snapshot_schedule,
+                               location, tier=None, os_type=None):
+        """Places an order for a replicant block volume.
+
+        :param volume_id: The ID of the primary volume to be replicated
+        :param snapshot_schedule: The primary volume's snapshot
+                                  schedule to use for replication
+        :param location: The location for the ordered replicant volume
+        :param tier: The tier (IOPS per GB) of the primary volume
+        :param os_type: The OS type of the primary volume
+        :return: Returns a SoftLayer_Container_Product_Order_Receipt
+        """
+
+        block_mask = 'billingItem[activeChildren],storageTierLevel,'\
+                     'osType,snapshotCapacityGb,schedules,'\
+                     'hourlySchedule,dailySchedule,weeklySchedule'
+        block_volume = self.get_block_volume_details(volume_id,
+                                                     mask=block_mask)
+
+        if os_type is None:
+            if isinstance(utils.lookup(block_volume, 'osType', 'keyName'),
+                          str):
+                os_type = block_volume['osType']['keyName']
+            else:
+                raise exceptions.SoftLayerError(
+                    "Cannot find primary volume's os-type "
+                    "automatically; must specify manually")
+
+        order = storage_utils.prepare_replicant_order_object(
+            self, volume_id, snapshot_schedule, location, tier,
+            block_volume, 'block'
+        )
+
+        order['osFormatType'] = {'keyName': os_type}
+
+        return self.client.call('Product_Order', 'placeOrder', order)
+
     def delete_snapshot(self, snapshot_id):
         """Deletes the specified snapshot object.
 
@@ -193,7 +241,7 @@ def delete_snapshot(self, snapshot_id):
                                 id=snapshot_id)
 
     def order_block_volume(self, storage_type, location, size, os_type,
-                           iops=None, tier_level=None):
+                           iops=None, tier_level=None, snapshot_size=None):
         """Places an order for a block volume.
 
         :param storage_type: "performance_storage_iscsi" (performance)
@@ -203,6 +251,8 @@ def order_block_volume(self, storage_type, location, size, os_type,
         :param os_type: OS Type to use for volume alignment, see help for list
         :param iops: Number of IOPs for a "Performance" order
         :param tier_level: Tier level to use for an "Endurance" order
+        :param snapshot_size: The size of optional snapshot space,
+        if snapshot space should also be ordered (None if not ordered)
         """
 
         try:
@@ -239,6 +289,9 @@ def order_block_volume(self, storage_type, location, size, os_type,
                     ),
                 storage_utils.find_endurance_tier_price(package, tier_level),
             ]
+            if snapshot_size is not None:
+                prices.append(storage_utils.find_snapshot_space_price(
+                    package, snapshot_size, tier_level))
         else:
             raise exceptions.SoftLayerError(
                 "Block volume storage_type must be either "
@@ -413,3 +466,26 @@ def cancel_block_volume(self, volume_id,
             True,
             reason,
             id=billing_item_id)
+
+    def failover_to_replicant(self, volume_id, replicant_id, immediate=False):
+        """Failover to a volume replicant.
+
+        :param integer volume_id: The id of the volume
+        :param integer replicant_id: ID of replicant to failover to
+        :param boolean immediate: Flag indicating if failover is immediate
+        :return: Returns whether failover was successful or not
+        """
+
+        return self.client.call('Network_Storage', 'failoverToReplicant',
+                                replicant_id, immediate, id=volume_id)
+
+    def failback_from_replicant(self, volume_id, replicant_id):
+        """Failback from a volume replicant.
+
+        :param integer volume_id: The id of the volume
+        :param integer: ID of replicant to failback from
+        :return: Returns whether failback was successful or not
+        """
+
+        return self.client.call('Network_Storage', 'failbackFromReplicant',
+                                replicant_id, id=volume_id)
diff --git a/SoftLayer/managers/cdn.py b/SoftLayer/managers/cdn.py
index d93f148a..994ab8fa 100644
--- a/SoftLayer/managers/cdn.py
+++ b/SoftLayer/managers/cdn.py
@@ -15,7 +15,13 @@
 
 
 class CDNManager(utils.IdentifierMixin, object):
-    """Manage CDN accounts."""
+    """Manage CDN accounts and content.
+
+    See product information here:
+    http://www.softlayer.com/content-delivery-network
+
+    :param SoftLayer.API.BaseClient client: the client instance
+    """
 
     def __init__(self, client):
         self.client = client
diff --git a/SoftLayer/managers/dns.py b/SoftLayer/managers/dns.py
index c2524800..c1b7b3b6 100644
--- a/SoftLayer/managers/dns.py
+++ b/SoftLayer/managers/dns.py
@@ -11,9 +11,11 @@
 
 
 class DNSManager(utils.IdentifierMixin, object):
-    """Domain Name System manager.
+    """Manage SoftLayer DNS.
 
-    :param SoftLayer.API.Client client: the client instance
+    See product information here: http://www.softlayer.com/DOMAIN-SERVICES
+
+    :param SoftLayer.API.BaseClient client: the client instance
 
     """
 
diff --git a/SoftLayer/managers/file.py b/SoftLayer/managers/file.py
index 9759b1c5..ae179206 100644
--- a/SoftLayer/managers/file.py
+++ b/SoftLayer/managers/file.py
@@ -45,7 +45,7 @@ def list_file_volumes(self, datacenter=None, username=None,
             (utils.query_filter('!~ NAS'))
 
         _filter['nasNetworkStorage']['storageType']['keyName'] = (
-            utils.query_filter('*FILE_STORAGE'))
+            utils.query_filter('*FILE_STORAGE*'))
         if storage_type:
             _filter['nasNetworkStorage']['storageType']['keyName'] = (
                 utils.query_filter('%s_FILE_STORAGE' % storage_type.upper()))
@@ -86,6 +86,12 @@ def get_file_volume_details(self, volume_id, **kwargs):
                 'lunId',
                 'activeTransactionCount',
                 'activeTransactions.transactionStatus[friendlyName]',
+                'replicationPartnerCount',
+                'replicationStatus',
+                'replicationPartners[id,username,'
+                'serviceResourceBackendIpAddress,'
+                'serviceResource[datacenter[name]],'
+                'replicationSchedule[type[keyname]]]',
             ]
             kwargs['mask'] = ','.join(items)
         return self.client.call('Network_Storage', 'getObject',
@@ -187,6 +193,31 @@ def deauthorize_host_to_volume(self, volume_id,
         return self.client.call('Network_Storage', 'removeAccessFromHostList',
                                 host_templates, id=volume_id, **kwargs)
 
+    def order_replicant_volume(self, volume_id, snapshot_schedule,
+                               location, tier=None):
+        """Places an order for a replicant file volume.
+
+        :param volume_id: The ID of the primary volume to be replicated
+        :param snapshot_schedule: The primary volume's snapshot
+                                  schedule to use for replication
+        :param location: The location for the ordered replicant volume
+        :param tier: The tier (IOPS per GB) of the primary volume
+        :return: Returns a SoftLayer_Container_Product_Order_Receipt
+        """
+
+        file_mask = 'billingItem[activeChildren],storageTierLevel,'\
+                    'osType,snapshotCapacityGb,schedules,'\
+                    'hourlySchedule,dailySchedule,weeklySchedule'
+        file_volume = self.get_file_volume_details(volume_id,
+                                                   mask=file_mask)
+
+        order = storage_utils.prepare_replicant_order_object(
+            self, volume_id, snapshot_schedule, location, tier,
+            file_volume, 'file'
+        )
+
+        return self.client.call('Product_Order', 'placeOrder', order)
+
     def delete_snapshot(self, snapshot_id):
         """Deletes the specified snapshot object.
 
@@ -196,7 +227,7 @@ def delete_snapshot(self, snapshot_id):
                                 id=snapshot_id)
 
     def order_file_volume(self, storage_type, location, size, os_type,
-                          iops=None, tier_level=None):
+                          iops=None, tier_level=None, snapshot_size=None):
         """Places an order for a file volume.
 
         :param storage_type: "performance_storage_iscsi" (performance)
@@ -206,6 +237,8 @@ def order_file_volume(self, storage_type, location, size, os_type,
         :param os_type: OS Type to use for volume alignment, see help for list
         :param iops: Number of IOPs for a "Performance" order
         :param tier_level: Tier level to use for an "Endurance" order
+        :param snapshot_size: The size of optional snapshot space,
+        if snapshot space should also be ordered (None if not ordered)
         """
 
         try:
@@ -242,6 +275,9 @@ def order_file_volume(self, storage_type, location, size, os_type,
                     ),
                 storage_utils.find_endurance_tier_price(package, tier_level),
             ]
+            if snapshot_size is not None:
+                prices.append(storage_utils.find_snapshot_space_price(
+                    package, snapshot_size, tier_level))
         else:
             raise exceptions.SoftLayerError(
                 "File volume storage_type must be either "
@@ -308,6 +344,7 @@ def order_snapshot_space(self, volume_id, capacity, tier,
         :param boolean upgrade: Flag to indicate if this order is an upgrade
         :return: Returns a SoftLayer_Container_Product_Order_Receipt
         """
+
         package = storage_utils.get_package(self, 'storage_service_enterprise')
         file_mask = 'serviceResource.datacenter[id],'\
             'storageTierLevel,billingItem'
@@ -411,3 +448,26 @@ def cancel_file_volume(self, volume_id,
             True,
             reason,
             id=billing_item_id)
+
+    def failover_to_replicant(self, volume_id, replicant_id, immediate=False):
+        """Failover to a volume replicant.
+
+        :param integer volume_id: The id of the volume
+        :param integer replicant_id: ID of replicant to failover to
+        :param boolean immediate: Flag indicating if failover is immediate
+        :return: Returns whether failover was successful or not
+        """
+
+        return self.client.call('Network_Storage', 'failoverToReplicant',
+                                replicant_id, immediate, id=volume_id)
+
+    def failback_from_replicant(self, volume_id, replicant_id):
+        """Failback from a volume replicant.
+
+        :param integer volume_id: The id of the volume
+        :param integer: ID of replicant to failback from
+        :return: Returns whether failback was successful or not
+        """
+
+        return self.client.call('Network_Storage', 'failbackFromReplicant',
+                                replicant_id, id=volume_id)
diff --git a/SoftLayer/managers/firewall.py b/SoftLayer/managers/firewall.py
index 43b12412..210d481c 100644
--- a/SoftLayer/managers/firewall.py
+++ b/SoftLayer/managers/firewall.py
@@ -29,9 +29,11 @@ def has_firewall(vlan):
 
 
 class FirewallManager(utils.IdentifierMixin, object):
-    """Manages firewalls.
+    """Manages SoftLayer firewalls
 
-    :param SoftLayer.API.Client client: the API client instance
+    See product information here: http://www.softlayer.com/firewalls
+
+    :param SoftLayer.API.BaseClient client: the client instance
 
     """
 
diff --git a/SoftLayer/managers/hardware.py b/SoftLayer/managers/hardware.py
index 3a77d057..abfc3a63 100644
--- a/SoftLayer/managers/hardware.py
+++ b/SoftLayer/managers/hardware.py
@@ -19,7 +19,7 @@
 
 
 class HardwareManager(utils.IdentifierMixin, object):
-    """Manage hardware devices.
+    """Manage SoftLayer hardware servers.
 
     Example::
 
@@ -32,7 +32,9 @@ class HardwareManager(utils.IdentifierMixin, object):
        client = SoftLayer.Client()
        mgr = SoftLayer.HardwareManager(client)
 
-    :param SoftLayer.API.Client client: an API client instance
+    See product information here: http://www.softlayer.com/bare-metal-servers
+
+    :param SoftLayer.API.BaseClient client: the client instance
     :param SoftLayer.managers.OrderingManager ordering_manager: an optional
                                               manager to handle ordering.
                                               If none is provided, one will be
@@ -182,43 +184,42 @@ def get_hardware(self, hardware_id, **kwargs):
         """
 
         if 'mask' not in kwargs:
-            items = [
-                'id',
-                'globalIdentifier',
-                'fullyQualifiedDomainName',
-                'hostname',
-                'domain',
-                'provisionDate',
-                'hardwareStatus',
-                'processorPhysicalCoreAmount',
-                'memoryCapacity',
-                'notes',
-                'privateNetworkOnlyFlag',
-                'primaryBackendIpAddress',
-                'primaryIpAddress',
-                'networkManagementIpAddress',
-                'userData',
-                'datacenter',
+            kwargs['mask'] = (
+                'id,'
+                'globalIdentifier,'
+                'fullyQualifiedDomainName,'
+                'hostname,'
+                'domain,'
+                'provisionDate,'
+                'hardwareStatus,'
+                'processorPhysicalCoreAmount,'
+                'memoryCapacity,'
+                'notes,'
+                'privateNetworkOnlyFlag,'
+                'primaryBackendIpAddress,'
+                'primaryIpAddress,'
+                'networkManagementIpAddress,'
+                'userData,'
+                'datacenter,'
                 '''networkComponents[id, status, speed, maxSpeed, name,
                    ipmiMacAddress, ipmiIpAddress, macAddress, primaryIpAddress,
                    port, primarySubnet[id, netmask, broadcastAddress,
-                                       networkIdentifier, gateway]]''',
-                'hardwareChassis[id,name]',
-                'activeTransaction[id, transactionStatus[friendlyName,name]]',
+                                       networkIdentifier, gateway]],'''
+                'hardwareChassis[id,name],'
+                'activeTransaction[id, transactionStatus[friendlyName,name]],'
                 '''operatingSystem[
                     softwareLicense[softwareDescription[manufacturer,
                                                         name,
                                                         version,
                                                         referenceCode]],
-                    passwords[username,password]]''',
-                'billingItem.nextInvoiceTotalRecurringAmount',
-                'hourlyBillingFlag',
-                'tagReferences[id,tag[name,id]]',
-                'networkVlans[id,vlanNumber,networkSpace]',
-                'billingItem.orderItem.order.userRecord[username]',
-                'remoteManagementAccounts[username,password]',
-            ]
-            kwargs['mask'] = "mask[%s]" % ','.join(items)
+                    passwords[username,password]],'''
+                'billingItem.nextInvoiceTotalRecurringAmount,'
+                'hourlyBillingFlag,'
+                'tagReferences[id,tag[name,id]],'
+                'networkVlans[id,vlanNumber,networkSpace],'
+                'billingItem.orderItem.order.userRecord[username],'
+                'remoteManagementAccounts[username,password]'
+            )
 
         return self.hardware.getObject(id=hardware_id, **kwargs)
 
diff --git a/SoftLayer/managers/image.py b/SoftLayer/managers/image.py
index 57389bd8..81eee928 100644
--- a/SoftLayer/managers/image.py
+++ b/SoftLayer/managers/image.py
@@ -13,9 +13,12 @@
 
 
 class ImageManager(utils.IdentifierMixin, object):
-    """Manages server images.
+    """Manages SoftLayer server images.
 
-    :param SoftLayer.API.Client client: an API client instance
+    See product information here:
+    https://knowledgelayer.softlayer.com/topic/image-templates
+
+    :param SoftLayer.API.BaseClient client: the client instance
     """
 
     def __init__(self, client):
diff --git a/SoftLayer/managers/iscsi.py b/SoftLayer/managers/iscsi.py
index 9c07a15d..85df7d40 100644
--- a/SoftLayer/managers/iscsi.py
+++ b/SoftLayer/managers/iscsi.py
@@ -10,7 +10,14 @@
 
 
 class ISCSIManager(utils.IdentifierMixin, object):
-    """Manages iSCSI storages."""
+    """Manages SoftLayer iSCSI storage accounts.
+
+    See product information here:
+    https://knowledgelayer.softlayer.com/topic/iscsi
+
+    :param SoftLayer.API.BaseClient client: the client instance
+
+    """
 
     def __init__(self, client):
         self.configuration = {}
diff --git a/SoftLayer/managers/load_balancer.py b/SoftLayer/managers/load_balancer.py
index 47fa362d..854c8c62 100644
--- a/SoftLayer/managers/load_balancer.py
+++ b/SoftLayer/managers/load_balancer.py
@@ -9,9 +9,12 @@
 
 
 class LoadBalancerManager(utils.IdentifierMixin, object):
-    """Manages load balancers.
+    """Manages SoftLayer load balancers.
+
+    See product information here: http://www.softlayer.com/load-balancing
+
+    :param SoftLayer.API.BaseClient client: the client instance
 
-    :param SoftLayer.API.Client client: the API client instance
     """
 
     def __init__(self, client):
diff --git a/SoftLayer/managers/messaging.py b/SoftLayer/managers/messaging.py
index 8c273b6b..8c0a0519 100644
--- a/SoftLayer/managers/messaging.py
+++ b/SoftLayer/managers/messaging.py
@@ -23,7 +23,7 @@
 
 
 class QueueAuth(requests.auth.AuthBase):
-    """Message Queue authentication for requests.
+    """SoftLayer Message Queue authentication for requests.
 
     :param endpoint: endpoint URL
     :param username: SoftLayer username
@@ -72,7 +72,13 @@ def __call__(self, resp):
 
 
 class MessagingManager(object):
-    """Manage SoftLayer Message Queue."""
+    """Manage SoftLayer Message Queue accounts.
+
+    See product information here: http://www.softlayer.com/message-queue
+
+    :param SoftLayer.API.BaseClient client: the client instance
+
+    """
     def __init__(self, client):
         self.client = client
 
diff --git a/SoftLayer/managers/metadata.py b/SoftLayer/managers/metadata.py
index 38aa4db5..0246d7f7 100644
--- a/SoftLayer/managers/metadata.py
+++ b/SoftLayer/managers/metadata.py
@@ -35,7 +35,10 @@
 
 
 class MetadataManager(object):
-    """Provides an interface for the metadata service.
+    """Provides an interface for the SoftLayer metadata service.
+
+    See product information here:
+    http://sldn.softlayer.com/reference/services/SoftLayer_Resource_Metadata
 
     This provides metadata about the resourse it is called from.
     See `METADATA_ATTRIBUTES` for full list of attributes.
@@ -51,6 +54,8 @@ class MetadataManager(object):
             >>> meta.get('fqdn')
             'test.example.com'
 
+    :param SoftLayer.API.BaseClient client: the client instance
+
     """
 
     attribs = METADATA_MAPPING
diff --git a/SoftLayer/managers/network.py b/SoftLayer/managers/network.py
index 904b91f9..690da99b 100644
--- a/SoftLayer/managers/network.py
+++ b/SoftLayer/managers/network.py
@@ -36,7 +36,13 @@
 
 
 class NetworkManager(object):
-    """Manage Networks."""
+    """Manage SoftLayer network objects: VLANs, subnets, IPs and rwhois
+
+    See product information here: http://www.softlayer.com/networking
+
+    :param SoftLayer.API.BaseClient client: the client instance
+
+    """
     def __init__(self, client):
         self.client = client
         self.account = client['Account']
diff --git a/SoftLayer/managers/object_storage.py b/SoftLayer/managers/object_storage.py
index 71da8d22..395c6e2f 100644
--- a/SoftLayer/managers/object_storage.py
+++ b/SoftLayer/managers/object_storage.py
@@ -16,7 +16,13 @@
 
 
 class ObjectStorageManager(object):
-    """Manager for Object Storage."""
+    """Manager for SoftLayer Object Storage accounts.
+
+    See product information here: http://www.softlayer.com/object-storage
+
+    :param SoftLayer.API.BaseClient client: the client instance
+
+    """
 
     def __init__(self, client):
         self.client = client
diff --git a/SoftLayer/managers/ordering.py b/SoftLayer/managers/ordering.py
index f7026b9e..798a8464 100644
--- a/SoftLayer/managers/ordering.py
+++ b/SoftLayer/managers/ordering.py
@@ -8,9 +8,9 @@
 
 
 class OrderingManager(object):
-    """Manages hardware devices.
+    """Manager to help ordering via the SoftLayer API.
 
-    :param SoftLayer.API.Client client: an API client instance
+    :param SoftLayer.API.BaseClient client: the client instance
     """
 
     def __init__(self, client):
diff --git a/SoftLayer/managers/sshkey.py b/SoftLayer/managers/sshkey.py
index e7f424f3..a294d525 100644
--- a/SoftLayer/managers/sshkey.py
+++ b/SoftLayer/managers/sshkey.py
@@ -10,9 +10,12 @@
 
 
 class SshKeyManager(utils.IdentifierMixin, object):
-    """Manages account SSH keys.
+    """Manages account SSH keys in SoftLayer.
 
-    :param SoftLayer.API.Client client: an API client instance
+    See product information here:
+    https://knowledgelayer.softlayer.com/procedure/ssh-keys
+
+    :param SoftLayer.API.BaseClient client: the client instance
     """
 
     def __init__(self, client):
diff --git a/SoftLayer/managers/ssl.py b/SoftLayer/managers/ssl.py
index ef407def..f0d75dc3 100644
--- a/SoftLayer/managers/ssl.py
+++ b/SoftLayer/managers/ssl.py
@@ -8,9 +8,9 @@
 
 
 class SSLManager(object):
-    """Manages SSL certificates.
+    """Manages SSL certificates in SoftLayer.
 
-    :param SoftLayer.API.Client client: an API client instance
+    See product information here: http://www.softlayer.com/ssl-certificates
 
     Example::
 
@@ -23,6 +23,8 @@ class SSLManager(object):
        client = SoftLayer.Client()
        mgr = SoftLayer.SSLManager(client)
 
+    :param SoftLayer.API.BaseClient client: the client instance
+
     """
 
     def __init__(self, client):
diff --git a/SoftLayer/managers/storage_utils.py b/SoftLayer/managers/storage_utils.py
index bb23703b..0ee01ef9 100644
--- a/SoftLayer/managers/storage_utils.py
+++ b/SoftLayer/managers/storage_utils.py
@@ -5,6 +5,7 @@
 
     :license: MIT, see LICENSE for more details.
 """
+from SoftLayer import exceptions
 from SoftLayer import utils
 
 
@@ -282,6 +283,39 @@ def find_performance_iops_price(package, size, iops):
     raise ValueError("Could not find price for iops for the given volume")
 
 
+def find_replication_price(package, capacity, tier_level):
+    """Find the price in the given package for the desired replicant volume
+
+    :param package: The product package of the endurance storage type
+    :param capacity: The capacity of the primary storage volume
+    :param tier_level: The tier of the primary storage volume
+    :return: Returns the price for the given size, or an error if not found
+    """
+    for item in package['items']:
+        if int(item['capacity']) != capacity:
+            continue
+
+        for price in item['prices']:
+            # Only collect prices from valid location groups.
+            if price['locationGroupId'] != '':
+                continue
+
+            if not _has_category(price['categories'],
+                                 'performance_storage_replication'):
+                continue
+
+            level = ENDURANCE_TIERS.get(tier_level)
+            if level < int(price['capacityRestrictionMinimum']):
+                continue
+
+            if level > int(price['capacityRestrictionMaximum']):
+                continue
+
+            return price
+
+    raise ValueError("Could not find price for replicant volume")
+
+
 def find_snapshot_space_price(package, size, tier_level):
     """Find the price in the given package for the desired snapshot space size
 
@@ -315,6 +349,102 @@ def find_snapshot_space_price(package, size, tier_level):
     raise ValueError("Could not find price for snapshot space")
 
 
+def find_snapshot_schedule_id(volume, snapshot_schedule_keyname):
+    """Find the snapshot schedule ID for the given volume and keyname
+
+    :param volume: The volume for which the snapshot ID is desired
+    :param snapshot_schedule_keyname: The keyname of the snapshot schedule
+    :return: Returns an int value indicating the volume's snapshot schedule ID
+    """
+    for schedule in volume['schedules']:
+        if 'type' in schedule and 'keyname' in schedule['type']:
+            if schedule['type']['keyname'] == snapshot_schedule_keyname:
+                return schedule['id']
+
+    raise ValueError("The given snapshot schedule ID was not found for "
+                     "the given storage volume")
+
+
+def prepare_replicant_order_object(manager, volume_id, snapshot_schedule,
+                                   location, tier, volume, volume_type):
+    """Prepare the order object which is submitted to the placeOrder() method
+
+    :param manager: The File or Block manager calling this function
+    :param volume_id: The ID of the primary volume to be replicated
+    :param snapshot_schedule: The primary volume's snapshot
+                              schedule to use for replication
+    :param location: The location for the ordered replicant volume
+    :param tier: The tier (IOPS per GB) of the primary volume
+    :param volume: The primary volume as a SoftLayer_Network_Storage object
+    :param volume_type: The type of the primary volume ('file' or 'block')
+    :return: Returns the order object for the
+             Product_Order service's placeOrder() method
+    """
+
+    try:
+        location_id = get_location_id(manager, location)
+    except ValueError:
+        raise exceptions.SoftLayerError(
+            "Invalid data center name specified. "
+            "Please provide the lower case short name (e.g.: dal09)")
+
+    volume_capacity = int(volume['capacityGb'])
+    storage_type = volume['billingItem']['categoryCode']
+
+    if storage_type != 'storage_service_enterprise':
+        raise exceptions.SoftLayerError(
+            "Primary volume storage_type must be Endurance")
+
+    if 'snapshotCapacityGb' in volume:
+        volume_snapshot_capacity = int(volume['snapshotCapacityGb'])
+    else:
+        raise exceptions.SoftLayerError(
+            "Snapshot capacity not found for the given primary volume")
+
+    snapshot_schedule_id = find_snapshot_schedule_id(
+        volume,
+        'SNAPSHOT_' + snapshot_schedule
+    )
+
+    if volume['billingItem']['cancellationDate'] != '':
+        raise exceptions.SoftLayerError(
+            'This volume is set for cancellation; '
+            'unable to order replicant volume')
+
+    for child in volume['billingItem']['activeChildren']:
+        if child['categoryCode'] == 'storage_snapshot_space'\
+                and child['cancellationDate'] != '':
+            raise exceptions.SoftLayerError(
+                'The snapshot space for this volume is set for '
+                'cancellation; unable to order replicant volume')
+
+    if tier is None:
+        tier = find_endurance_tier_iops_per_gb(volume)
+
+    package = get_package(manager, storage_type)
+    prices = [
+        find_endurance_price(package, 'storage_service_enterprise'),
+        find_endurance_price(package, 'storage_' + volume_type),
+        find_endurance_tier_price(package, tier),
+        find_endurance_space_price(package, volume_capacity, tier),
+        find_snapshot_space_price(package, volume_snapshot_capacity, tier),
+        find_replication_price(package, volume_capacity, tier),
+    ]
+
+    replicant_order = {
+        'complexType': 'SoftLayer_Container_Product_Order_'
+                       'Network_Storage_Enterprise',
+        'packageId': package['id'],
+        'prices': prices,
+        'quantity': 1,
+        'location': location_id,
+        'originVolumeId': int(volume_id),
+        'originVolumeScheduleId': snapshot_schedule_id,
+    }
+
+    return replicant_order
+
+
 def _has_category(categories, category_code):
     return any(
         True
diff --git a/SoftLayer/managers/ticket.py b/SoftLayer/managers/ticket.py
index d95f5c83..553f3260 100644
--- a/SoftLayer/managers/ticket.py
+++ b/SoftLayer/managers/ticket.py
@@ -10,9 +10,12 @@
 
 
 class TicketManager(utils.IdentifierMixin, object):
-    """Manages support Tickets.
+    """Manages SoftLayer support tickets.
+
+    See product information here: http://www.softlayer.com/support
+
+    :param SoftLayer.API.BaseClient client: the client instance
 
-    :param SoftLayer.API.Client client: an API client instance
     """
 
     def __init__(self, client):
@@ -46,8 +49,7 @@ def get_ticket(self, ticket_id):
         """Get details about a ticket.
 
         :param integer ticket_id: the ticket ID
-        :returns: A dictionary containing a large amount of information about
-                  the specified ticket.
+        :returns: dict -- information about the specified ticket
 
         """
         mask = ('id, title, assignedUser[firstName, lastName],status,'
@@ -91,7 +93,8 @@ def upload_attachment(self, ticket_id=None, file_path=None,
         :param string file_name:
                                   The name of the attachment shown
                                   in the ticket
-        :returns The uploaded attachment
+
+        :returns: dict -- The uploaded attachment
         """
         file_content = None
         with open(file_path, 'rb') as attached_file:
@@ -109,7 +112,8 @@ def attach_hardware(self, ticket_id=None, hardware_id=None):
 
         :param integer ticket_id: the id of the ticket to attach to
         :param integer hardware_id: the id of the hardware to attach
-        :returns The new ticket attachment
+
+        :returns: dict -- The new ticket attachment
         """
         return self.ticket.addAttachedHardware(hardware_id, id=ticket_id)
 
@@ -118,7 +122,8 @@ def attach_virtual_server(self, ticket_id=None, virtual_id=None):
 
         :param integer ticket_id: the id of the ticket to attach to
         :param integer virtual_id: the id of the virtual server to attach
-        :returns The new ticket attachment
+
+        :returns: dict -- The new ticket attachment
         """
         return self.ticket.addAttachedVirtualGuest(virtual_id, id=ticket_id)
 
@@ -127,7 +132,8 @@ def detach_hardware(self, ticket_id=None, hardware_id=None):
 
         :param ticket_id: the id of the ticket to detach from
         :param hardware_id: the id of the hardware to detach
-        :return: Whether the detachment was successful
+
+        :returns: bool -- Whether the detachment was successful
         """
         return self.ticket.removeAttachedHardware(hardware_id, id=ticket_id)
 
@@ -136,6 +142,7 @@ def detach_virtual_server(self, ticket_id=None, virtual_id=None):
 
         :param ticket_id: the id of the ticket to detach from
         :param virtual_id: the id of the virtual server to detach
-        :return: Whether the detachment was successful
+
+        :returns: bool -- Whether the detachment was successful
         """
         return self.ticket.removeAttachedVirtualGuest(virtual_id, id=ticket_id)
diff --git a/SoftLayer/managers/vs.py b/SoftLayer/managers/vs.py
index 25090e07..a724b709 100644
--- a/SoftLayer/managers/vs.py
+++ b/SoftLayer/managers/vs.py
@@ -17,13 +17,9 @@
 
 
 class VSManager(utils.IdentifierMixin, object):
-    """Manages Virtual Servers.
+    """Manages SoftLayer Virtual Servers.
 
-    :param SoftLayer.API.Client client: an API client instance
-    :param SoftLayer.managers.OrderingManager ordering_manager: an optional
-                                              manager to handle ordering.
-                                              If none is provided, one will be
-                                              auto initialized.
+    See product information here: http://www.softlayer.com/virtual-servers
 
     Example::
 
@@ -36,6 +32,13 @@ class VSManager(utils.IdentifierMixin, object):
            client = SoftLayer.Client()
            mgr = SoftLayer.VSManager(client)
 
+
+    :param SoftLayer.API.BaseClient client: the client instance
+    :param SoftLayer.managers.OrderingManager ordering_manager: an optional
+                                              manager to handle ordering.
+                                              If none is provided, one will be
+                                              auto initialized.
+
     """
 
     def __init__(self, client, ordering_manager=None):
@@ -172,49 +175,50 @@ def get_instance(self, instance_id, **kwargs):
         """
 
         if 'mask' not in kwargs:
-            items = [
-                'id',
-                'globalIdentifier',
-                'fullyQualifiedDomainName',
-                'hostname',
-                'domain',
-                'createDate',
-                'modifyDate',
-                'provisionDate',
-                'notes',
-                'dedicatedAccountHostOnlyFlag',
-                'privateNetworkOnlyFlag',
-                'primaryBackendIpAddress',
-                'primaryIpAddress',
+            kwargs['mask'] = (
+                'id,'
+                'globalIdentifier,'
+                'fullyQualifiedDomainName,'
+                'hostname,'
+                'domain,'
+                'createDate,'
+                'modifyDate,'
+                'provisionDate,'
+                'notes,'
+                'dedicatedAccountHostOnlyFlag,'
+                'privateNetworkOnlyFlag,'
+                'primaryBackendIpAddress,'
+                'primaryIpAddress,'
                 '''networkComponents[id, status, speed, maxSpeed, name,
                                      macAddress, primaryIpAddress, port,
-                                     primarySubnet]''',
-                'lastKnownPowerState.name',
-                'powerState',
-                'status',
-                'maxCpu',
-                'maxMemory',
-                'datacenter',
-                'activeTransaction[id, transactionStatus[friendlyName,name]]',
-                'lastOperatingSystemReload.id',
-                'blockDevices',
-                'blockDeviceTemplateGroup[id, name, globalIdentifier]',
-                'postInstallScriptUri',
-                '''softwareComponents[
-                    passwords[username,password,notes],
-                    softwareLicense[softwareDescription[name]]]''',
+                                     primarySubnet],'''
+                'lastKnownPowerState.name,'
+                'powerState,'
+                'status,'
+                'maxCpu,'
+                'maxMemory,'
+                'datacenter,'
+                'activeTransaction[id, transactionStatus[friendlyName,name]],'
+                'lastOperatingSystemReload.id,'
+                'blockDevices,'
+                'blockDeviceTemplateGroup[id, name, globalIdentifier],'
+                'postInstallScriptUri,'
                 '''operatingSystem[passwords[username,password],
                                    softwareLicense.softwareDescription[
                                        manufacturer,name,version,
-                                       referenceCode]]''',
-                'hourlyBillingFlag',
-                'userData',
-                'billingItem.recurringFee',
-                'tagReferences[id,tag[name,id]]',
-                'networkVlans[id,vlanNumber,networkSpace]',
+                                       referenceCode]],'''
+                '''softwareComponents[
+                    passwords[username,password,notes],
+                    softwareLicense[softwareDescription[
+                                        manufacturer,name,version,
+                                        referenceCode]]],'''
+                'hourlyBillingFlag,'
+                'userData,'
+                'billingItem.recurringFee,'
+                'tagReferences[id,tag[name,id]],'
+                'networkVlans[id,vlanNumber,networkSpace],'
                 'billingItem.orderItem.order.userRecord[username]'
-            ]
-            kwargs['mask'] = "mask[%s]" % ','.join(items)
+            )
 
         return self.guest.getObject(id=instance_id, **kwargs)
 
diff --git a/docs/cla-corporate.md b/docs/dev/cla-corporate.md
similarity index 100%
rename from docs/cla-corporate.md
rename to docs/dev/cla-corporate.md
diff --git a/docs/cla-individual.md b/docs/dev/cla-individual.md
similarity index 100%
rename from docs/cla-individual.md
rename to docs/dev/cla-individual.md
diff --git a/docs/dev/index.rst b/docs/dev/index.rst
index 4814920b..21bb0d40 100644
--- a/docs/dev/index.rst
+++ b/docs/dev/index.rst
@@ -20,7 +20,7 @@ Code Organization
 Setting Up A Dev Environment
 ----------------------------
 Before working with the SoftLayer Python API client source, we strongly
-recommend that you know how to use Python's virtualization environment,
+recommend that you know how to use Python's virtual environment,
 `virtualenv <https://pypi.python.org/pypi/virtualenv>`_. Virtualenv allows you
 to create isolated Python environments that are individually tailored to
 particular development projects. Each environment can have its own set of
@@ -62,9 +62,13 @@ the root of the source tree:
   tox
 
 In addition to testing different versions of Python, tox checks for common
-mistakes in the code using `Flake8 <https://pypi.python.org/pypi/flake8/2.0>`_.
-You should eliminate the simple errors reported by Flake8 before submitting
-your code.
+mistakes in the code using `Flake8 <https://pypi.python.org/pypi/flake8/2.0>`_ and `pylint <https://www.pylint.org/>`_.
+You should eliminate the linting errors that are reported before submitting
+your code. You can run only the linting checks by using this command:
+
+::
+
+  tox -eanalysis
 
 The project's configuration instructs tox to test against many different
 versions of Python. A tox test will use as many of those as it can find on your
@@ -73,7 +77,7 @@ you point the `Travis <https://travis-ci.org>`_ continuous integration tool at
 your GitHub fork. Travis will run the test against the full suite of Python
 versions every time you push new code.
 
-Using tox to running the tests in multiple environments can be very time
+Using tox to run tests in multiple environments can be very time
 consuming. If you wish to quickly run the tests in your own environment, you
 may do so using `py.test <http://pytest.org/>`_.  The command to do that
 is:
@@ -109,7 +113,7 @@ The primary docs are built at
 
 Style
 -----
-This project follows :pep:`8` and most of the style suggestions that pyflakes
+This project tries to follow :pep:`8` and most of the style suggestions that pyflakes
 recommends. Run `Flake8 <https://pypi.python.org/pypi/flake8/2.0>`_ regularly.
 Flake8, with project-specific exceptions, can be run by using tox:
 
@@ -120,12 +124,15 @@ Flake8, with project-specific exceptions, can be run by using tox:
 
 Contributing
 ------------
-Contributing to the Python API bindings follows the fork-pull-request model on
+Contributing to the Python API bindings follows the `fork-pull-request model <https://guides.github.com/introduction/flow/>`_ on
 `GitHub <http://github.com>`_. The project uses GitHub's
 `issue tracker <https://github.com/softlayer/softlayer-python/issues>`_ and
 `pull requests <https://github.com/softlayer/softlayer-python/pulls>`_ to
 manage source control, bug fixes and new feature development regarding the API
-bindings and the CLI.
+bindings and the CLI. In order to contribute, we require that you sign a contributer agreemenet:
+
+* Sign our contributor agreement (CLA) You can find the :download:`CLA here <cla-individual.md>`.
+* If you're contributing on behalf of your employer we'll need a signed copy of our corporate contributor agreement (CCLA) as well.  You can find the :download:`CCLA here <cla-corporate.md>`.
 
 
 Developer Resources


Test patch:
diff --git a/tests/CLI/modules/block_tests.py b/tests/CLI/modules/block_tests.py
index b413a695..fbb225a0 100644
--- a/tests/CLI/modules/block_tests.py
+++ b/tests/CLI/modules/block_tests.py
@@ -73,14 +73,28 @@ def test_volume_detail(self):
             'LUN Id': '2',
             'Endurance Tier': '2 IOPS per GB',
             'IOPs': 1000,
-            'Snapshot Capacity (GB)': 10,
+            'Snapshot Capacity (GB)': '10',
             'Snapshot Used (Bytes)': 1024,
             'Capacity (GB)': '20GB',
             'Target IP': '10.1.2.3',
             'Data Center': 'dal05',
             'Type': 'ENDURANCE',
             'ID': 100,
-            '# of Active Transactions': '0'
+            '# of Active Transactions': '0',
+            'Replicant Count': '1',
+            'Replication Status': 'Replicant Volume Provisioning '
+                                  'has completed.',
+            'Replicant Volumes': [[
+                {'Replicant ID': 'Volume Name', '1784': 'TEST_REP_1'},
+                {'Replicant ID': 'Target IP', '1784': '10.3.174.79'},
+                {'Replicant ID': 'Data Center', '1784': 'wdc01'},
+                {'Replicant ID': 'Schedule', '1784': 'REPLICATION_HOURLY'},
+            ], [
+                {'Replicant ID': 'Volume Name', '1785': 'TEST_REP_2'},
+                {'Replicant ID': 'Target IP', '1785': '10.3.177.84'},
+                {'Replicant ID': 'Data Center', '1785': 'dal01'},
+                {'Replicant ID': 'Schedule', '1785': 'REPLICATION_DAILY'},
+            ]]
         }, json.loads(result.output))
 
     def test_volume_list(self):
@@ -101,29 +115,107 @@ def test_volume_list(self):
             }],
             json.loads(result.output))
 
+    def test_volume_order_performance_iops_not_given(self):
+        result = self.run_command(['block', 'volume-order',
+                                   '--storage-type=performance', '--size=20',
+                                   '--os-type=linux', '--location=dal05'])
+
+        self.assertEqual(2, result.exit_code)
+
+    def test_volume_order_performance_iops_out_of_range(self):
+        result = self.run_command(['block', 'volume-order',
+                                   '--storage-type=performance', '--size=20',
+                                   '--iops=80000', '--os-type=linux',
+                                   '--location=dal05'])
+
+        self.assertEqual(2, result.exit_code)
+
+    def test_volume_order_performance_iops_not_multiple_of_100(self):
+        result = self.run_command(['block', 'volume-order',
+                                   '--storage-type=performance', '--size=20',
+                                   '--iops=122', '--os-type=linux',
+                                   '--location=dal05'])
+
+        self.assertEqual(2, result.exit_code)
+
+    def test_volume_order_performance_snapshot_error(self):
+        result = self.run_command(['block', 'volume-order',
+                                   '--storage-type=performance', '--size=20',
+                                   '--iops=100', '--os-type=linux',
+                                   '--location=dal05', '--snapshot-size=10'])
+
+        self.assertEqual(2, result.exit_code)
+
     @mock.patch('SoftLayer.BlockStorageManager.order_block_volume')
-    def test_volume_order(self, order_mock):
+    def test_volume_order_performance(self, order_mock):
         order_mock.return_value = {
             'placedOrder': {
                 'id': 478,
-                'items': [{'description': 'Endurance Storage'},
-                          {'description': 'Block Storage'},
-                          {'description': '0.25 IOPS per GB'},
-                          {'description': '20 GB Storage Space'},
-                          ]
+                'items': [
+                    {'description': 'Performance Storage'},
+                    {'description': 'Block Storage'},
+                    {'description': '0.25 IOPS per GB'},
+                    {'description': '20 GB Storage Space'}]
+            }
+        }
+
+        result = self.run_command(['block', 'volume-order',
+                                   '--storage-type=performance', '--size=20',
+                                   '--iops=100', '--os-type=linux',
+                                   '--location=dal05'])
+
+        self.assert_no_fail(result)
+        self.assertEqual(result.output,
+                         'Order #478 placed successfully!\n'
+                         ' > Performance Storage\n > Block Storage\n'
+                         ' > 0.25 IOPS per GB\n > 20 GB Storage Space\n')
+
+    def test_volume_order_endurance_tier_not_given(self):
+        result = self.run_command(['block', 'volume-order',
+                                   '--storage-type=endurance', '--size=20',
+                                   '--os-type=linux', '--location=dal05'])
+
+        self.assertEqual(2, result.exit_code)
+
+    @mock.patch('SoftLayer.BlockStorageManager.order_block_volume')
+    def test_volume_order_endurance(self, order_mock):
+        order_mock.return_value = {
+            'placedOrder': {
+                'id': 478,
+                'items': [
+                    {'description': 'Endurance Storage'},
+                    {'description': 'Block Storage'},
+                    {'description': '0.25 IOPS per GB'},
+                    {'description': '20 GB Storage Space'},
+                    {'description': '10 GB Storage Space (Snapshot Space)'}]
                 }
         }
 
         result = self.run_command(['block', 'volume-order',
                                    '--storage-type=endurance', '--size=20',
                                    '--tier=0.25', '--os-type=linux',
-                                   '--location=dal05'])
+                                   '--location=dal05', '--snapshot-size=10'])
 
         self.assert_no_fail(result)
         self.assertEqual(result.output,
                          'Order #478 placed successfully!\n'
                          ' > Endurance Storage\n > Block Storage\n'
-                         ' > 0.25 IOPS per GB\n > 20 GB Storage Space\n')
+                         ' > 0.25 IOPS per GB\n > 20 GB Storage Space\n'
+                         ' > 10 GB Storage Space (Snapshot Space)\n')
+
+    @mock.patch('SoftLayer.BlockStorageManager.order_block_volume')
+    def test_volume_order_order_not_placed(self, order_mock):
+        order_mock.return_value = {}
+
+        result = self.run_command(['block', 'volume-order',
+                                   '--storage-type=endurance', '--size=20',
+                                   '--tier=0.25', '--os-type=linux',
+                                   '--location=dal05'])
+
+        self.assert_no_fail(result)
+        self.assertEqual(result.output,
+                         'Order could not be placed! Please verify '
+                         'your options and try again.\n')
 
     def test_enable_snapshots(self):
         result = self.run_command(['block', 'snapshot-enable', '12345678',
@@ -142,6 +234,17 @@ def test_create_snapshot(self):
 
         self.assert_no_fail(result)
 
+    @mock.patch('SoftLayer.BlockStorageManager.create_snapshot')
+    def test_create_snapshot_unsuccessful(self, snapshot_mock):
+        snapshot_mock.return_value = []
+
+        result = self.run_command(['block', 'snapshot-create', '8', '-n=note'])
+
+        self.assertEqual('Error occurred while creating snapshot.\n'
+                         'Ensure volume is not failed over or in another '
+                         'state which prevents taking snapshots.\n',
+                         result.output)
+
     def test_snapshot_list(self):
         result = self.run_command(['block', 'snapshot-list', '12345678'])
 
@@ -208,3 +311,82 @@ def test_deauthorize_host_to_volume(self):
                                    '--ip-address=192.3.2.1'])
 
         self.assert_no_fail(result)
+
+    def test_replicant_failover(self):
+        result = self.run_command(['block', 'replica-failover', '12345678',
+                                   '--replicant-id=5678', '--immediate'])
+
+        self.assert_no_fail(result)
+        self.assertEqual('Failover to replicant is now in progress.\n',
+                         result.output)
+
+    @mock.patch('SoftLayer.BlockStorageManager.failover_to_replicant')
+    def test_replicant_failover_unsuccessful(self, failover_mock):
+        failover_mock.return_value = False
+
+        result = self.run_command(['block', 'replica-failover', '12345678',
+                                   '--replicant-id=5678'])
+
+        self.assertEqual('Failover operation could not be initiated.\n',
+                         result.output)
+
+    def test_replicant_failback(self):
+        result = self.run_command(['block', 'replica-failback', '12345678',
+                                   '--replicant-id=5678'])
+
+        self.assert_no_fail(result)
+        self.assertEqual('Failback from replicant is now in progress.\n',
+                         result.output)
+
+    @mock.patch('SoftLayer.BlockStorageManager.failback_from_replicant')
+    def test_replicant_failback_unsuccessful(self, failback_mock):
+        failback_mock.return_value = False
+
+        result = self.run_command(['block', 'replica-failback', '12345678',
+                                   '--replicant-id=5678'])
+
+        self.assertEqual('Failback operation could not be initiated.\n',
+                         result.output)
+
+    @mock.patch('SoftLayer.BlockStorageManager.order_replicant_volume')
+    def test_replicant_order_order_not_placed(self, order_mock):
+        order_mock.return_value = {}
+
+        result = self.run_command(['block', 'replica-order', '100',
+                                   '--snapshot-schedule=DAILY',
+                                   '--location=dal05'])
+
+        self.assert_no_fail(result)
+        self.assertEqual(result.output,
+                         'Order could not be placed! Please verify '
+                         'your options and try again.\n')
+
+    @mock.patch('SoftLayer.BlockStorageManager.order_replicant_volume')
+    def test_replicant_order(self, order_mock):
+        order_mock.return_value = {
+            'placedOrder': {
+                'id': 91604,
+                'items': [
+                    {'description': 'Endurance Storage'},
+                    {'description': '2 IOPS per GB'},
+                    {'description': 'Block Storage'},
+                    {'description': '20 GB Storage Space'},
+                    {'description': '10 GB Storage Space (Snapshot Space)'},
+                    {'description': '20 GB Storage Space Replicant of: TEST'},
+                ],
+            }
+        }
+
+        result = self.run_command(['block', 'replica-order', '100',
+                                   '--snapshot-schedule=DAILY',
+                                   '--location=dal05', '--tier=2'])
+
+        self.assert_no_fail(result)
+        self.assertEqual(result.output,
+                         'Order #91604 placed successfully!\n'
+                         ' > Endurance Storage\n'
+                         ' > 2 IOPS per GB\n'
+                         ' > Block Storage\n'
+                         ' > 20 GB Storage Space\n'
+                         ' > 10 GB Storage Space (Snapshot Space)\n'
+                         ' > 20 GB Storage Space Replicant of: TEST\n')
diff --git a/tests/CLI/modules/file_tests.py b/tests/CLI/modules/file_tests.py
index 6f509d57..3cbce526 100644
--- a/tests/CLI/modules/file_tests.py
+++ b/tests/CLI/modules/file_tests.py
@@ -117,43 +117,136 @@ def test_volume_detail(self):
         result = self.run_command(['file', 'volume-detail', '1234'])
 
         self.assert_no_fail(result)
-        self.assertEqual({'Username': 'username',
-                          'Used Space': '0B',
-                          'Endurance Tier': '2 IOPS per GB',
-                          'IOPs': 1000,
-                          'Snapshot Capacity (GB)': 10,
-                          'Snapshot Used (Bytes)': 1024,
-                          'Capacity (GB)': '20GB',
-                          'Target IP': '10.1.2.3',
-                          'Data Center': 'dal05',
-                          'Type': 'ENDURANCE',
-                          'ID': 100,
-                          '# of Active Transactions': '0'
-                          }, json.loads(result.output))
+        self.assertEqual({
+            'Username': 'username',
+            'Used Space': '0B',
+            'Endurance Tier': '2 IOPS per GB',
+            'IOPs': 1000,
+            'Snapshot Capacity (GB)': '10',
+            'Snapshot Used (Bytes)': 1024,
+            'Capacity (GB)': '20GB',
+            'Target IP': '10.1.2.3',
+            'Data Center': 'dal05',
+            'Type': 'ENDURANCE',
+            'ID': 100,
+            '# of Active Transactions': '0',
+            'Replicant Count': '1',
+            'Replication Status': 'Replicant Volume Provisioning '
+                                  'has completed.',
+            'Replicant Volumes': [[
+                {'Replicant ID': 'Volume Name', '1784': 'TEST_REP_1'},
+                {'Replicant ID': 'Target IP', '1784': '10.3.174.79'},
+                {'Replicant ID': 'Data Center', '1784': 'wdc01'},
+                {'Replicant ID': 'Schedule', '1784': 'REPLICATION_HOURLY'},
+            ], [
+                {'Replicant ID': 'Volume Name', '1785': 'TEST_REP_2'},
+                {'Replicant ID': 'Target IP', '1785': '10.3.177.84'},
+                {'Replicant ID': 'Data Center', '1785': 'dal01'},
+                {'Replicant ID': 'Schedule', '1785': 'REPLICATION_DAILY'},
+            ]]
+        }, json.loads(result.output))
+
+    def test_volume_order_performance_iops_not_given(self):
+        result = self.run_command(['file', 'volume-order',
+                                   '--storage-type=performance', '--size=20',
+                                   '--os-type=linux', '--location=dal05'])
+
+        self.assertEqual(2, result.exit_code)
+
+    def test_volume_order_performance_iops_out_of_range(self):
+        result = self.run_command(['file', 'volume-order',
+                                   '--storage-type=performance', '--size=20',
+                                   '--iops=80000', '--os-type=linux',
+                                   '--location=dal05'])
+
+        self.assertEqual(2, result.exit_code)
+
+    def test_volume_order_performance_iops_not_multiple_of_100(self):
+        result = self.run_command(['file', 'volume-order',
+                                   '--storage-type=performance', '--size=20',
+                                   '--iops=122', '--os-type=linux',
+                                   '--location=dal05'])
+
+        self.assertEqual(2, result.exit_code)
+
+    def test_volume_order_performance_snapshot_error(self):
+        result = self.run_command(['file', 'volume-order',
+                                   '--storage-type=performance', '--size=20',
+                                   '--iops=100', '--os-type=linux',
+                                   '--location=dal05', '--snapshot-size=10'])
+
+        self.assertEqual(2, result.exit_code)
 
     @mock.patch('SoftLayer.FileStorageManager.order_file_volume')
-    def test_volume_order(self, order_mock):
+    def test_volume_order_performance(self, order_mock):
         order_mock.return_value = {
             'placedOrder': {
                 'id': 478,
-                'items': [{'description': 'Endurance Storage'},
-                          {'description': 'File Storage'},
-                          {'description': '0.25 IOPS per GB'},
-                          {'description': '20 GB Storage Space'},
-                          ]
+                'items': [
+                    {'description': 'Performance Storage'},
+                    {'description': 'File Storage'},
+                    {'description': '0.25 IOPS per GB'},
+                    {'description': '20 GB Storage Space'}]
+            }
+        }
+
+        result = self.run_command(['file', 'volume-order',
+                                   '--storage-type=performance', '--size=20',
+                                   '--iops=100', '--os-type=linux',
+                                   '--location=dal05'])
+
+        self.assert_no_fail(result)
+        self.assertEqual(result.output,
+                         'Order #478 placed successfully!\n'
+                         ' > Performance Storage\n > File Storage\n'
+                         ' > 0.25 IOPS per GB\n > 20 GB Storage Space\n')
+
+    def test_volume_order_endurance_tier_not_given(self):
+        result = self.run_command(['file', 'volume-order',
+                                   '--storage-type=endurance', '--size=20',
+                                   '--os-type=linux', '--location=dal05'])
+
+        self.assertEqual(2, result.exit_code)
+
+    @mock.patch('SoftLayer.FileStorageManager.order_file_volume')
+    def test_volume_order_endurance(self, order_mock):
+        order_mock.return_value = {
+            'placedOrder': {
+                'id': 478,
+                'items': [
+                    {'description': 'Endurance Storage'},
+                    {'description': 'File Storage'},
+                    {'description': '0.25 IOPS per GB'},
+                    {'description': '20 GB Storage Space'},
+                    {'description': '10 GB Storage Space (Snapshot Space)'}]
                 }
         }
 
         result = self.run_command(['file', 'volume-order',
                                    '--storage-type=endurance', '--size=20',
                                    '--tier=0.25', '--os-type=linux',
-                                   '--location=dal05'])
+                                   '--location=dal05', '--snapshot-size=10'])
 
         self.assert_no_fail(result)
         self.assertEqual(result.output,
                          'Order #478 placed successfully!\n'
                          ' > Endurance Storage\n > File Storage\n'
-                         ' > 0.25 IOPS per GB\n > 20 GB Storage Space\n')
+                         ' > 0.25 IOPS per GB\n > 20 GB Storage Space\n'
+                         ' > 10 GB Storage Space (Snapshot Space)\n')
+
+    @mock.patch('SoftLayer.FileStorageManager.order_file_volume')
+    def test_volume_order_order_not_placed(self, order_mock):
+        order_mock.return_value = {}
+
+        result = self.run_command(['file', 'volume-order',
+                                   '--storage-type=endurance', '--size=20',
+                                   '--tier=0.25', '--os-type=linux',
+                                   '--location=dal05'])
+
+        self.assert_no_fail(result)
+        self.assertEqual(result.output,
+                         'Order could not be placed! Please verify '
+                         'your options and try again.\n')
 
     def test_enable_snapshots(self):
         result = self.run_command(['file', 'snapshot-enable', '12345678',
@@ -173,6 +266,17 @@ def test_create_snapshot(self):
         self.assert_no_fail(result)
         self.assertEqual('New snapshot created with id: 449\n', result.output)
 
+    @mock.patch('SoftLayer.FileStorageManager.create_snapshot')
+    def test_create_snapshot_unsuccessful(self, snapshot_mock):
+        snapshot_mock.return_value = []
+
+        result = self.run_command(['file', 'snapshot-create', '8', '-n=note'])
+
+        self.assertEqual('Error occurred while creating snapshot.\n'
+                         'Ensure volume is not failed over or in another '
+                         'state which prevents taking snapshots.\n',
+                         result.output)
+
     def test_snapshot_restore(self):
         result = self.run_command(['file', 'snapshot-restore', '12345678',
                                    '--snapshot-id=87654321'])
@@ -215,3 +319,82 @@ def test_snapshot_cancel(self):
                          ' for snapshot cancellation\n', result.output)
         self.assert_called_with('SoftLayer_Billing_Item', 'cancelItem',
                                 args=(False, True, None))
+
+    def test_replicant_failover(self):
+        result = self.run_command(['file', 'replica-failover', '12345678',
+                                   '--replicant-id=5678', '--immediate'])
+
+        self.assert_no_fail(result)
+        self.assertEqual('Failover to replicant is now in progress.\n',
+                         result.output)
+
+    @mock.patch('SoftLayer.FileStorageManager.failover_to_replicant')
+    def test_replicant_failover_unsuccessful(self, failover_mock):
+        failover_mock.return_value = False
+
+        result = self.run_command(['file', 'replica-failover', '12345678',
+                                   '--replicant-id=5678'])
+
+        self.assertEqual('Failover operation could not be initiated.\n',
+                         result.output)
+
+    def test_replicant_failback(self):
+        result = self.run_command(['file', 'replica-failback', '12345678',
+                                   '--replicant-id=5678'])
+
+        self.assert_no_fail(result)
+        self.assertEqual('Failback from replicant is now in progress.\n',
+                         result.output)
+
+    @mock.patch('SoftLayer.FileStorageManager.failback_from_replicant')
+    def test_replicant_failback_unsuccessful(self, failback_mock):
+        failback_mock.return_value = False
+
+        result = self.run_command(['file', 'replica-failback', '12345678',
+                                   '--replicant-id=5678'])
+
+        self.assertEqual('Failback operation could not be initiated.\n',
+                         result.output)
+
+    @mock.patch('SoftLayer.FileStorageManager.order_replicant_volume')
+    def test_replicant_order_order_not_placed(self, order_mock):
+        order_mock.return_value = {}
+
+        result = self.run_command(['file', 'replica-order', '100',
+                                   '--snapshot-schedule=DAILY',
+                                   '--location=dal05'])
+
+        self.assert_no_fail(result)
+        self.assertEqual(result.output,
+                         'Order could not be placed! Please verify '
+                         'your options and try again.\n')
+
+    @mock.patch('SoftLayer.FileStorageManager.order_replicant_volume')
+    def test_replicant_order(self, order_mock):
+        order_mock.return_value = {
+            'placedOrder': {
+                'id': 77309,
+                'items': [
+                    {'description': 'Endurance Storage'},
+                    {'description': '2 IOPS per GB'},
+                    {'description': 'File Storage'},
+                    {'description': '20 GB Storage Space'},
+                    {'description': '10 GB Storage Space (Snapshot Space)'},
+                    {'description': '20 GB Storage Space Replicant of: TEST'},
+                ],
+            }
+        }
+
+        result = self.run_command(['file', 'replica-order', '100',
+                                   '--snapshot-schedule=DAILY',
+                                   '--location=dal05', '--tier=2'])
+
+        self.assert_no_fail(result)
+        self.assertEqual(result.output,
+                         'Order #77309 placed successfully!\n'
+                         ' > Endurance Storage\n'
+                         ' > 2 IOPS per GB\n'
+                         ' > File Storage\n'
+                         ' > 20 GB Storage Space\n'
+                         ' > 10 GB Storage Space (Snapshot Space)\n'
+                         ' > 20 GB Storage Space Replicant of: TEST\n')
diff --git a/tests/CLI/modules/server_tests.py b/tests/CLI/modules/server_tests.py
index 63fcab7e..851e4e0c 100644
--- a/tests/CLI/modules/server_tests.py
+++ b/tests/CLI/modules/server_tests.py
@@ -41,6 +41,7 @@ def test_server_details(self):
             'memory': 2048,
             'notes': 'These are test notes.',
             'os': 'Ubuntu',
+            'os_version': 'Ubuntu 12.04 LTS',
             'owner': 'chechu',
             'price rate': 16.08,
             'private_ip': '10.1.0.2',
@@ -55,7 +56,7 @@ def test_server_details(self):
         }
 
         self.assert_no_fail(result)
-        self.assertEqual(json.loads(result.output), expected)
+        self.assertEqual(expected, json.loads(result.output))
 
     def test_detail_vs_empty_tag(self):
         mock = self.set_mock('SoftLayer_Hardware_Server', 'getObject')
diff --git a/tests/CLI/modules/vs_tests.py b/tests/CLI/modules/vs_tests.py
index 53bad7c3..9818d7d3 100644
--- a/tests/CLI/modules/vs_tests.py
+++ b/tests/CLI/modules/vs_tests.py
@@ -49,7 +49,7 @@ def test_detail_vs(self):
                           'guid': '1a2b3c-1701',
                           'memory': 1024,
                           'modified': {},
-                          'os': '12.04-64 Minimal for VSI',
+                          'os': 'Ubuntu',
                           'os_version': '12.04-64 Minimal for VSI',
                           'notes': 'notes',
                           'price rate': 1.54,
diff --git a/tests/managers/block_tests.py b/tests/managers/block_tests.py
index 45337478..091e6e66 100644
--- a/tests/managers/block_tests.py
+++ b/tests/managers/block_tests.py
@@ -6,6 +6,7 @@
 """
 
 import SoftLayer
+from SoftLayer import exceptions
 from SoftLayer import fixtures
 from SoftLayer import testing
 
@@ -113,6 +114,30 @@ def test_cancel_snapshot_immediately(self):
             identifier=123,
         )
 
+    def test_replicant_failover(self):
+        result = self.block.failover_to_replicant(1234, 5678, immediate=True)
+
+        self.assertEqual(
+            fixtures.SoftLayer_Network_Storage.failoverToReplicant, result)
+        self.assert_called_with(
+            'SoftLayer_Network_Storage',
+            'failoverToReplicant',
+            args=(5678, True),
+            identifier=1234,
+        )
+
+    def test_replicant_failback(self):
+        result = self.block.failback_from_replicant(1234, 5678)
+
+        self.assertEqual(
+            fixtures.SoftLayer_Network_Storage.failbackFromReplicant, result)
+        self.assert_called_with(
+            'SoftLayer_Network_Storage',
+            'failbackFromReplicant',
+            args=(5678,),
+            identifier=1234,
+        )
+
     def test_order_block_volume_performance(self):
         mock = self.set_mock('SoftLayer_Product_Package', 'getAllObjects')
         mock.return_value = [{
@@ -155,7 +180,7 @@ def test_order_block_volume_performance(self):
             "dal05",
             100,
             "LINUX",
-            iops=100
+            iops=100,
             )
 
         self.assertEqual(
@@ -231,7 +256,95 @@ def test_order_block_volume_endurance(self):
             "dal05",
             100,
             "LINUX",
-            tier_level=0.25
+            tier_level=0.25,
+            )
+
+        self.assertEqual(
+            result,
+            {
+                'orderDate': '2013-08-01 15:23:45',
+                'orderId': 1234,
+                'prices': [{
+                    'hourlyRecurringFee': '2',
+                    'id': 1,
+                    'item': {'description': 'this is a thing', 'id': 1},
+                    'laborFee': '2',
+                    'oneTimeFee': '2',
+                    'oneTimeFeeTax': '.1',
+                    'quantity': 1,
+                    'recurringFee': '2',
+                    'recurringFeeTax': '.1',
+                    'setupFee': '1'}],
+                },
+            )
+
+    def test_order_block_volume_endurance_with_snapshot(self):
+        mock = self.set_mock('SoftLayer_Product_Package', 'getAllObjects')
+        mock.return_value = [{
+            'id': 1,
+            'name': 'Endurance',
+            'items': [{
+                'capacity': '1',
+                'prices': [{
+                    'id': 1,
+                    'locationGroupId': '',
+                    'categories': [{
+                        'categoryCode': 'storage_block',
+                    }],
+                }],
+            }, {
+                'capacity': '1',
+                'prices': [{
+                    'id': 2,
+                    'locationGroupId': '',
+                    'categories': [{
+                        'categoryCode': 'storage_service_enterprise',
+                    }],
+                }],
+            }, {
+                'capacity': '100',
+                'prices': [{
+                    'id': 3,
+                    'locationGroupId': '',
+                    'categories': [{
+                        'categoryCode': 'performance_storage_space',
+                    }],
+                    'capacityRestrictionMinimum': '100',
+                    'capacityRestrictionMaximum': '100',
+                }],
+            }, {
+                'capacity': '100',
+                'attributes': [{
+                    'value': '100',
+                }],
+                'prices': [{
+                    'id': 4,
+                    'locationGroupId': '',
+                    'categories': [{
+                        'categoryCode': 'storage_tier_level',
+                    }],
+                }],
+            }, {
+                'capacity': '10',
+                'prices': [{
+                    'id': 5,
+                    'locationGroupId': '',
+                    'categories': [{
+                        'categoryCode': 'storage_snapshot_space',
+                    }],
+                    'capacityRestrictionMinimum': '100',
+                    'capacityRestrictionMaximum': '100',
+                }],
+            }],
+        }]
+
+        result = self.block.order_block_volume(
+            "storage_service_enterprise",
+            "dal05",
+            100,
+            "LINUX",
+            tier_level=0.25,
+            snapshot_size=10,
             )
 
         self.assertEqual(
@@ -431,3 +544,241 @@ def test_order_snapshot_space(self):
                     'setupFee': '1'}],
                 },
             )
+
+    def test_order_block_replicant_invalid_location(self):
+        self.assertRaises(
+            exceptions.SoftLayerError,
+            self.block.order_replicant_volume,
+            100,
+            'WEEKLY',
+            'moon_center',
+            os_type='LINUX',
+        )
+
+    def test_order_block_replicant_invalid_storage_type(self):
+        mock = self.set_mock('SoftLayer_Network_Storage', 'getObject')
+
+        mock.return_value = {
+            'capacityGb': 20,
+            'billingItem': {
+                'categoryCode': 'not_the_storage_you_are_looking_for'
+            }
+        }
+
+        self.assertRaises(
+            exceptions.SoftLayerError,
+            self.block.order_replicant_volume,
+            100,
+            'WEEKLY',
+            'dal05',
+            os_type='LINUX',
+        )
+
+    def test_order_block_replicant_no_snapshot_space(self):
+        mock = self.set_mock('SoftLayer_Network_Storage', 'getObject')
+
+        mock.return_value = {
+            'capacityGb': 20,
+            'billingItem': {
+                'categoryCode': 'storage_service_enterprise'
+            }
+        }
+
+        self.assertRaises(
+            exceptions.SoftLayerError,
+            self.block.order_replicant_volume,
+            100,
+            'WEEKLY',
+            'dal05',
+            os_type='LINUX',
+        )
+
+    def test_order_block_replicant_primary_volume_cancelled(self):
+        mock = self.set_mock('SoftLayer_Network_Storage', 'getObject')
+
+        mock.return_value = {
+            'capacityGb': 20,
+            'snapshotCapacityGb': '10',
+            'schedules': [{
+                'id': 7770,
+                'type': {'keyname': 'SNAPSHOT_WEEKLY'}
+            }],
+            'billingItem': {
+                'categoryCode': 'storage_service_enterprise',
+                'cancellationDate': '2016-09-04T22:00:00-07:00'
+            }
+        }
+
+        self.assertRaises(
+            exceptions.SoftLayerError,
+            self.block.order_replicant_volume,
+            100,
+            'WEEKLY',
+            'dal05',
+            os_type='LINUX',
+        )
+
+    def test_order_block_replicant_snapshot_space_cancelled(self):
+        mock = self.set_mock('SoftLayer_Network_Storage', 'getObject')
+
+        mock.return_value = {
+            'capacityGb': 20,
+            'snapshotCapacityGb': '10',
+            'schedules': [{
+                'id': 7770,
+                'type': {'keyname': 'SNAPSHOT_WEEKLY'}
+            }],
+            'billingItem': {
+                'categoryCode': 'storage_service_enterprise',
+                'cancellationDate': '',
+                'activeChildren': [{
+                    'categoryCode': 'storage_snapshot_space',
+                    'cancellationDate': '2016-09-04T22:00:00-07:00'
+                }]
+            }
+        }
+
+        self.assertRaises(
+            exceptions.SoftLayerError,
+            self.block.order_replicant_volume,
+            100,
+            'WEEKLY',
+            'dal05',
+            os_type='LINUX',
+        )
+
+    def test_order_block_replicant_os_type_not_found(self):
+        mock = self.set_mock('SoftLayer_Network_Storage', 'getObject')
+
+        mock.return_value = {}
+
+        self.assertRaises(
+            exceptions.SoftLayerError,
+            self.block.order_replicant_volume,
+            100,
+            'WEEKLY',
+            'dal05',
+            tier='2',
+        )
+
+    def test_order_block_replicant(self):
+        mock = self.set_mock('SoftLayer_Product_Package', 'getAllObjects')
+        mock.return_value = [{
+            'id': 240,
+            'name': 'Endurance',
+            'items': [{
+                'capacity': '0',
+                'attributes': [{
+                    'value': '42',
+                }],
+                'prices': [{
+                    'locationGroupId': '530',
+                }],
+            }, {
+                'capacity': '10',
+                'attributes': [{
+                    'value': '200',
+                }],
+                'prices': [{
+                    'locationGroupId': '530',
+                }, {
+                    'locationGroupId': '',
+                    'categories': [{
+                        'categoryCode': 'storage_service_enterprise',
+                    }],
+                }, {
+                    'locationGroupId': '',
+                    'categories': [{
+                        'categoryCode': 'storage_tier_level',
+                    }],
+                }, {
+                    'id': 46130,
+                    'locationGroupId': '',
+                    'categories': [{
+                        'categoryCode': 'storage_snapshot_space',
+                    }],
+                    'capacityRestrictionMinimum': '200',
+                    'capacityRestrictionMaximum': '200',
+                }],
+            }, {
+                'capacity': '20',
+                'prices': [{
+                    'locationGroupId': '530',
+                }, {
+                    'locationGroupId': '',
+                    'categories': [{
+                        'categoryCode': 'storage_block',
+                    }],
+                }, {
+                    'locationGroupId': '',
+                    'categories': [{
+                        'categoryCode': 'storage_service_enterprise',
+                    }],
+                }, {
+                    'locationGroupId': '',
+                    'categories': [{
+                        'categoryCode': 'performance_storage_space',
+                    }],
+                    'capacityRestrictionMinimum': '742',
+                }, {
+                    'locationGroupId': '',
+                    'categories': [{
+                        'categoryCode': 'performance_storage_space',
+                    }],
+                    'capacityRestrictionMinimum': '42',
+                    'capacityRestrictionMaximum': '42',
+                }, {
+                    'locationGroupId': '',
+                    'categories': [{
+                        'categoryCode': 'performance_storage_space',
+                    }],
+                    'capacityRestrictionMinimum': '200',
+                    'capacityRestrictionMaximum': '200',
+                }, {
+                    'locationGroupId': '',
+                    'categories': [{
+                        'categoryCode': 'performance_storage_replication',
+                    }],
+                    'capacityRestrictionMinimum': '742',
+                }, {
+                    'locationGroupId': '',
+                    'categories': [{
+                        'categoryCode': 'performance_storage_replication',
+                    }],
+                    'capacityRestrictionMinimum': '42',
+                    'capacityRestrictionMaximum': '42',
+                }, {
+                    'locationGroupId': '',
+                    'categories': [{
+                        'categoryCode': 'performance_storage_replication',
+                    }],
+                    'capacityRestrictionMinimum': '200',
+                    'capacityRestrictionMaximum': '200',
+                }],
+            }],
+        }]
+
+        result = self.block.order_replicant_volume(
+            100,
+            'WEEKLY',
+            'dal05',
+            )
+
+        self.assertEqual(
+            result,
+            {
+                'orderId': 1234,
+                'orderDate': '2013-08-01 15:23:45',
+                'prices': [{
+                    'hourlyRecurringFee': '2',
+                    'id': 1,
+                    'item': {'description': 'this is a thing', 'id': 1},
+                    'laborFee': '2',
+                    'oneTimeFee': '2',
+                    'oneTimeFeeTax': '.1',
+                    'quantity': 1,
+                    'recurringFee': '2',
+                    'recurringFeeTax': '.1',
+                    'setupFee': '1'}],
+                },
+            )
diff --git a/tests/managers/file_tests.py b/tests/managers/file_tests.py
index 7187a7d8..1f9c5617 100644
--- a/tests/managers/file_tests.py
+++ b/tests/managers/file_tests.py
@@ -6,6 +6,7 @@
 """
 
 import SoftLayer
+from SoftLayer import exceptions
 from SoftLayer import fixtures
 from SoftLayer import testing
 
@@ -141,6 +142,30 @@ def test_cancel_snapshot_immediately(self):
             identifier=123,
         )
 
+    def test_replicant_failover(self):
+        result = self.file.failover_to_replicant(1234, 5678, immediate=True)
+
+        self.assertEqual(
+            fixtures.SoftLayer_Network_Storage.failoverToReplicant, result)
+        self.assert_called_with(
+            'SoftLayer_Network_Storage',
+            'failoverToReplicant',
+            args=(5678, True),
+            identifier=1234,
+        )
+
+    def test_replicant_failback(self):
+        result = self.file.failback_from_replicant(1234, 5678)
+
+        self.assertEqual(
+            fixtures.SoftLayer_Network_Storage.failbackFromReplicant, result)
+        self.assert_called_with(
+            'SoftLayer_Network_Storage',
+            'failbackFromReplicant',
+            args=(5678,),
+            identifier=1234,
+        )
+
     def test_delete_snapshot(self):
         result = self.file.delete_snapshot(100)
 
@@ -222,7 +247,7 @@ def test_order_file_volume_performance(self):
             "dal05",
             100,
             "LINUX",
-            iops=100
+            iops=100,
             )
 
         self.assertEqual(
@@ -306,7 +331,95 @@ def test_order_file_volume_endurance(self):
             "dal05",
             100,
             "LINUX",
-            tier_level=0.25
+            tier_level=0.25,
+            )
+
+        self.assertEqual(
+            result,
+            {
+                'orderDate': '2013-08-01 15:23:45',
+                'orderId': 1234,
+                'prices': [{
+                    'hourlyRecurringFee': '2',
+                    'id': 1,
+                    'item': {'description': 'this is a thing', 'id': 1},
+                    'laborFee': '2',
+                    'oneTimeFee': '2',
+                    'oneTimeFeeTax': '.1',
+                    'quantity': 1,
+                    'recurringFee': '2',
+                    'recurringFeeTax': '.1',
+                    'setupFee': '1'}],
+                },
+            )
+
+    def test_order_file_volume_endurance_with_snapshot(self):
+        mock = self.set_mock('SoftLayer_Product_Package', 'getAllObjects')
+        mock.return_value = [{
+            'id': 1,
+            'name': 'Endurance',
+            'items': [{
+                'capacity': '1',
+                'prices': [{
+                    'id': 1,
+                    'locationGroupId': '',
+                    'categories': [{
+                        'categoryCode': 'storage_file',
+                    }],
+                }],
+            }, {
+                'capacity': '1',
+                'prices': [{
+                    'id': 2,
+                    'locationGroupId': '',
+                    'categories': [{
+                        'categoryCode': 'storage_service_enterprise',
+                    }],
+                }],
+            }, {
+                'capacity': '100',
+                'prices': [{
+                    'id': 3,
+                    'locationGroupId': '',
+                    'categories': [{
+                        'categoryCode': 'performance_storage_space',
+                    }],
+                    'capacityRestrictionMinimum': '100',
+                    'capacityRestrictionMaximum': '100',
+                }],
+            }, {
+                'capacity': '100',
+                'attributes': [{
+                    'value': '100',
+                }],
+                'prices': [{
+                    'id': 4,
+                    'locationGroupId': '',
+                    'categories': [{
+                        'categoryCode': 'storage_tier_level',
+                    }],
+                }],
+            }, {
+                'capacity': '10',
+                'prices': [{
+                    'id': 5,
+                    'locationGroupId': '',
+                    'categories': [{
+                        'categoryCode': 'storage_snapshot_space',
+                    }],
+                    'capacityRestrictionMinimum': '100',
+                    'capacityRestrictionMaximum': '100',
+                }],
+            }],
+        }]
+
+        result = self.file.order_file_volume(
+            "storage_service_enterprise",
+            "dal05",
+            100,
+            "LINUX",
+            tier_level=0.25,
+            snapshot_size=10,
             )
 
         self.assertEqual(
@@ -432,3 +545,222 @@ def test_order_snapshot_space(self):
                     'setupFee': '1'}],
                 },
             )
+
+    def test_order_file_replicant_invalid_location(self):
+        self.assertRaises(
+            exceptions.SoftLayerError,
+            self.file.order_replicant_volume,
+            100,
+            'WEEKLY',
+            'moon_center',
+        )
+
+    def test_order_file_replicant_invalid_storage_type(self):
+        mock = self.set_mock('SoftLayer_Network_Storage', 'getObject')
+
+        mock.return_value = {
+            'capacityGb': 20,
+            'billingItem': {
+                'categoryCode': 'not_the_storage_you_are_looking_for'
+            }
+        }
+
+        self.assertRaises(
+            exceptions.SoftLayerError,
+            self.file.order_replicant_volume,
+            100,
+            'WEEKLY',
+            'dal05',
+        )
+
+    def test_order_file_replicant_no_snapshot_space(self):
+        mock = self.set_mock('SoftLayer_Network_Storage', 'getObject')
+
+        mock.return_value = {
+            'capacityGb': 20,
+            'billingItem': {
+                'categoryCode': 'storage_service_enterprise'
+            }
+        }
+
+        self.assertRaises(
+            exceptions.SoftLayerError,
+            self.file.order_replicant_volume,
+            100,
+            'WEEKLY',
+            'dal05',
+        )
+
+    def test_order_file_replicant_primary_volume_cancelled(self):
+        mock = self.set_mock('SoftLayer_Network_Storage', 'getObject')
+
+        mock.return_value = {
+            'capacityGb': 20,
+            'snapshotCapacityGb': '10',
+            'schedules': [{
+                'id': 7770,
+                'type': {'keyname': 'SNAPSHOT_WEEKLY'}
+            }],
+            'billingItem': {
+                'categoryCode': 'storage_service_enterprise',
+                'cancellationDate': '2016-09-04T22:00:00-07:00'
+            }
+        }
+
+        self.assertRaises(
+            exceptions.SoftLayerError,
+            self.file.order_replicant_volume,
+            100,
+            'WEEKLY',
+            'dal05',
+        )
+
+    def test_order_file_replicant_snapshot_space_cancelled(self):
+        mock = self.set_mock('SoftLayer_Network_Storage', 'getObject')
+
+        mock.return_value = {
+            'capacityGb': 20,
+            'snapshotCapacityGb': '10',
+            'schedules': [{
+                'id': 7770,
+                'type': {'keyname': 'SNAPSHOT_WEEKLY'}
+            }],
+            'billingItem': {
+                'categoryCode': 'storage_service_enterprise',
+                'cancellationDate': '',
+                'activeChildren': [{
+                    'categoryCode': 'storage_snapshot_space',
+                    'cancellationDate': '2016-09-04T22:00:00-07:00'
+                }]
+            }
+        }
+
+        self.assertRaises(
+            exceptions.SoftLayerError,
+            self.file.order_replicant_volume,
+            100,
+            'WEEKLY',
+            'dal05',
+        )
+
+    def test_order_file_replicant(self):
+        mock = self.set_mock('SoftLayer_Product_Package', 'getAllObjects')
+        mock.return_value = [{
+            'id': 240,
+            'name': 'Endurance',
+            'items': [{
+                'capacity': '0',
+                'attributes': [{
+                    'value': '42',
+                }],
+                'prices': [{
+                    'locationGroupId': '530',
+                }],
+            }, {
+                'capacity': '10',
+                'attributes': [{
+                    'value': '200',
+                }],
+                'prices': [{
+                    'locationGroupId': '530',
+                }, {
+                    'locationGroupId': '',
+                    'categories': [{
+                        'categoryCode': 'storage_service_enterprise',
+                    }],
+                }, {
+                    'locationGroupId': '',
+                    'categories': [{
+                        'categoryCode': 'storage_tier_level',
+                    }],
+                }, {
+                    'id': 46130,
+                    'locationGroupId': '',
+                    'categories': [{
+                        'categoryCode': 'storage_snapshot_space',
+                    }],
+                    'capacityRestrictionMinimum': '200',
+                    'capacityRestrictionMaximum': '200',
+                }],
+            }, {
+                'capacity': '20',
+                'prices': [{
+                    'locationGroupId': '530',
+                }, {
+                    'locationGroupId': '',
+                    'categories': [{
+                        'categoryCode': 'storage_file',
+                    }],
+                }, {
+                    'locationGroupId': '',
+                    'categories': [{
+                        'categoryCode': 'storage_service_enterprise',
+                    }],
+                }, {
+                    'locationGroupId': '',
+                    'categories': [{
+                        'categoryCode': 'performance_storage_space',
+                    }],
+                    'capacityRestrictionMinimum': '742',
+                }, {
+                    'locationGroupId': '',
+                    'categories': [{
+                        'categoryCode': 'performance_storage_space',
+                    }],
+                    'capacityRestrictionMinimum': '42',
+                    'capacityRestrictionMaximum': '42',
+                }, {
+                    'locationGroupId': '',
+                    'categories': [{
+                        'categoryCode': 'performance_storage_space',
+                    }],
+                    'capacityRestrictionMinimum': '200',
+                    'capacityRestrictionMaximum': '200',
+                }, {
+                    'locationGroupId': '',
+                    'categories': [{
+                        'categoryCode': 'performance_storage_replication',
+                    }],
+                    'capacityRestrictionMinimum': '742',
+                }, {
+                    'locationGroupId': '',
+                    'categories': [{
+                        'categoryCode': 'performance_storage_replication',
+                    }],
+                    'capacityRestrictionMinimum': '42',
+                    'capacityRestrictionMaximum': '42',
+                }, {
+                    'locationGroupId': '',
+                    'categories': [{
+                        'categoryCode': 'performance_storage_replication',
+                    }],
+                    'capacityRestrictionMinimum': '200',
+                    'capacityRestrictionMaximum': '200',
+                }],
+            }],
+        }]
+
+        result = self.file.order_replicant_volume(
+            100,
+            'WEEKLY',
+            'dal05',
+            )
+
+        self.assertEqual(
+            result,
+            {
+                'orderId': 1234,
+                'orderDate': '2013-08-01 15:23:45',
+                'prices': [{
+                    'hourlyRecurringFee': '2',
+                    'id': 1,
+                    'item': {'description': 'this is a thing', 'id': 1},
+                    'laborFee': '2',
+                    'oneTimeFee': '2',
+                    'oneTimeFeeTax': '.1',
+                    'quantity': 1,
+                    'recurringFee': '2',
+                    'recurringFeeTax': '.1',
+                    'setupFee': '1'}],
+                },
+            )