Repository: softlayer/softlayer-python
Pull request #748 (2016-08-04 20:30:25)

Add File commands and new Block commands
Cherrypick manual Block snapshot creation from iodine53's branch
Additional code developed collaboratively by:
    ccorales95  Cristina Corales <ccorales95@gmail.com>
    kyubifire   Nilo Lisboa <nilo_bl@hotmail.com>
    VuKevin     Kevin Vu <VuKevin@icloud.com>
    dpickle2    David Pickle <dpickle29@gmail.com>

* Add support for manual snapshots [Block & File]
  (cherrypick from iodine53's branch for Block, then
   based similar function for File on this code)

* Add support for deleting snapshots [File]

* Add support for listing snapshots [File]

* Add snapshot schedule enable/disable [Block & File]

* Add snapshot space ordering [Block & File]

* Add restoring a volume to snapshots [Block & File]

* Add cancelling snapshot space [Block & File]

* Add storage host authorization [File]

* Add listing hosts authorized to storage volumes [File]

* Add ordering File volumes [File]

* Add cancelling File volumes [File]

* Add displaying File volume details and volume lists [File]

* Add additional output for commands:
      Add used space (bytes) to detail view  [File]
      Add LUN ID column for block volume-list [Block]
      Add "success output" for several commands [Block & File]
      Add transaction info to detail/list views [Block & File]

* Fix block snapshot-list command error
  (The "snapshot-list" block command produced an error when the
  list of snapshots for a volume was not empty. Updating the object
  mask which is sent to the SLAPI fixed the problem.)

* Create "storage_utils.py" files for duplicate code
  (To prevent duplicate code between File and Block commands,
  code was refactored into SoftLayer/managers/storage_utils.py
  and SoftLayer/CLI/storage_utils.py.)


Patch:
diff --git a/SoftLayer/CLI/block/access/authorize.py b/SoftLayer/CLI/block/access/authorize.py
index 4e9dc20f..df76b60e 100644
--- a/SoftLayer/CLI/block/access/authorize.py
+++ b/SoftLayer/CLI/block/access/authorize.py
@@ -1,4 +1,4 @@
-"""Create a block storage snapshot."""
+"""Authorizes hosts on a specific block volume."""
 # :license: MIT, see LICENSE for more details.
 
 import click
@@ -40,3 +40,6 @@ def cli(env, volume_id, hardware_id, virtual_id, ip_address_id, ip_address):
                                            hardware_id,
                                            virtual_id,
                                            ip_address_id_list)
+
+    # If no exception was raised, the command succeeded
+    click.echo('The specified hosts were authorized to access %s' % volume_id)
diff --git a/SoftLayer/CLI/block/access/list.py b/SoftLayer/CLI/block/access/list.py
new file mode 100644
index 00000000..b011e263
--- /dev/null
+++ b/SoftLayer/CLI/block/access/list.py
@@ -0,0 +1,38 @@
+"""List hosts with access to block volume."""
+# :license: MIT, see LICENSE for more details.
+
+import click
+import SoftLayer
+from SoftLayer.CLI import columns as column_helper
+from SoftLayer.CLI import environment
+from SoftLayer.CLI import formatting
+from SoftLayer.CLI import storage_utils
+
+
+@click.command()
+@click.argument('volume_id')
+@click.option('--sortby', help='Column to sort by', default='name')
+@click.option('--columns',
+              callback=column_helper.get_formatter(storage_utils.COLUMNS),
+              help='Columns to display. Options: {0}'.format(
+                  ', '.join(column.name for column in storage_utils.COLUMNS)),
+              default=','.join(storage_utils.DEFAULT_COLUMNS))
+@environment.pass_env
+def cli(env, columns, sortby, volume_id):
+    """List ACLs."""
+    block_manager = SoftLayer.BlockStorageManager(env.client)
+    access_list = block_manager.get_block_volume_access_list(
+        volume_id=volume_id)
+    table = formatting.Table(columns.columns)
+    table.sortby = sortby
+
+    for key, type_name in [('allowedVirtualGuests', 'VIRTUAL'),
+                           ('allowedHardware', 'HARDWARE'),
+                           ('allowedSubnets', 'SUBNET'),
+                           ('allowedIpAddresses', 'IP')]:
+        for obj in access_list.get(key, []):
+            obj['type'] = type_name
+            table.add_row([value or formatting.blank()
+                           for value in columns.row(obj)])
+
+    env.fout(table)
diff --git a/SoftLayer/CLI/block/access/revoke.py b/SoftLayer/CLI/block/access/revoke.py
index 1b0c93a4..c2284bef 100644
--- a/SoftLayer/CLI/block/access/revoke.py
+++ b/SoftLayer/CLI/block/access/revoke.py
@@ -1,4 +1,4 @@
-"""Create a block storage snapshot."""
+"""Revokes hosts' access on a specific block volume."""
 # :license: MIT, see LICENSE for more details.
 
 import click
@@ -14,7 +14,7 @@
 @click.option('--virtual-id', '-v', multiple=True,
               help='The id of one SoftLayer_Virtual_Guest'
               ' to revoke authorization')
-@click.option('--ip_address-id', '-i', multiple=True,
+@click.option('--ip-address-id', '-i', multiple=True,
               help='The id of one SoftLayer_Network_Subnet_IpAddress'
               ' to revoke authorization')
 @click.option('--ip-address', multiple=True,
@@ -36,3 +36,6 @@ def cli(env, volume_id, hardware_id, virtual_id, ip_address_id, ip_address):
                                              hardware_id,
                                              virtual_id,
                                              ip_address_id_list)
+
+    # If no exception was raised, the command succeeded
+    click.echo('Access to %s was revoked for the specified hosts' % volume_id)
diff --git a/SoftLayer/CLI/block/cancel.py b/SoftLayer/CLI/block/cancel.py
index a23cee00..03118451 100644
--- a/SoftLayer/CLI/block/cancel.py
+++ b/SoftLayer/CLI/block/cancel.py
@@ -25,4 +25,15 @@ def cli(env, volume_id, reason, immediate):
     if not (env.skip_confirmations or formatting.no_going_back(volume_id)):
         raise exceptions.CLIAbort('Aborted')
 
-    block_storage_manager.cancel_block_volume(volume_id, reason, immediate)
+    cancelled = block_storage_manager.cancel_block_volume(volume_id,
+                                                          reason, immediate)
+
+    if cancelled:
+        if immediate:
+            click.echo('Block volume with id %s has been marked'
+                       ' for immediate cancellation' % volume_id)
+        else:
+            click.echo('Block volume with id %s has been marked'
+                       ' for cancellation' % volume_id)
+    else:
+        click.echo('Unable to cancel block volume %s' % volume_id)
diff --git a/SoftLayer/CLI/block/detail.py b/SoftLayer/CLI/block/detail.py
index c98736ed..37a68430 100644
--- a/SoftLayer/CLI/block/detail.py
+++ b/SoftLayer/CLI/block/detail.py
@@ -51,9 +51,19 @@ def cli(env, volume_id):
             'Snapshot Capacity (GB)',
             block_volume['snapshotCapacityGb'],
         ])
-        table.add_row([
-            'Snapshot Used (Bytes)',
-            block_volume['parentVolume']['snapshotSizeBytes'],
-        ])
+        if 'snapshotSizeBytes' in block_volume['parentVolume']:
+            table.add_row([
+                'Snapshot Used (Bytes)',
+                block_volume['parentVolume']['snapshotSizeBytes'],
+            ])
+
+    table.add_row(['# of Active Transactions', "%i"
+                   % block_volume['activeTransactionCount']])
+
+    if block_volume['activeTransactions']:
+        for trans in block_volume['activeTransactions']:
+            table.add_row([
+                'Ongoing Transactions',
+                trans['transactionStatus']['friendlyName']])
 
     env.fout(table)
diff --git a/SoftLayer/CLI/block/list.py b/SoftLayer/CLI/block/list.py
index 40edcb7c..afb008a9 100644
--- a/SoftLayer/CLI/block/list.py
+++ b/SoftLayer/CLI/block/list.py
@@ -16,12 +16,18 @@
                          mask="serviceResource.datacenter.name"),
     column_helper.Column(
         'storage_type',
-        lambda b: b['storageType']['keyName'].split('_').pop(0),
+        lambda b: b['storageType']['keyName'].split('_').pop(0)
+        if 'storageType' in b and 'keyName' in b['storageType']
+        and isinstance(b['storageType']['keyName'], str)
+        else '-',
         mask="storageType.keyName"),
     column_helper.Column('capacity_gb', ('capacityGb',), mask="capacityGb"),
     column_helper.Column('bytes_used', ('bytesUsed',), mask="bytesUsed"),
     column_helper.Column('ip_addr', ('serviceResourceBackendIpAddress',),
                          mask="serviceResourceBackendIpAddress"),
+    column_helper.Column('lunId', ('lunId',), mask="lunId"),
+    column_helper.Column('active_transactions', ('activeTransactionCount',),
+                         mask="activeTransactionCount"),
 ]
 
 DEFAULT_COLUMNS = [
@@ -31,7 +37,9 @@
     'storage_type',
     'capacity_gb',
     'bytes_used',
-    'ip_addr'
+    'ip_addr',
+    'lunId',
+    'active_transactions'
 ]
 
 
diff --git a/SoftLayer/CLI/block/order.py b/SoftLayer/CLI/block/order.py
index f1840078..4393a282 100644
--- a/SoftLayer/CLI/block/order.py
+++ b/SoftLayer/CLI/block/order.py
@@ -12,12 +12,12 @@
 
 @click.command(context_settings=CONTEXT_SETTINGS)
 @click.option('--storage-type',
-              help='Type of storage volume',
+              help='Type of block storage volume',
               type=click.Choice(['performance', 'endurance']),
               required=True)
 @click.option('--size',
               type=int,
-              help='Size of storage volume in GB',
+              help='Size of block storage volume in GB',
               required=True)
 @click.option('--iops',
               type=int,
diff --git a/SoftLayer/CLI/block/snapshot/__init__.py b/SoftLayer/CLI/block/snapshot/__init__.py
new file mode 100644
index 00000000..eb4d41b0
--- /dev/null
+++ b/SoftLayer/CLI/block/snapshot/__init__.py
@@ -0,0 +1,1 @@
+"""Block Storage Snapshot Control."""
diff --git a/SoftLayer/CLI/block/snapshot/cancel.py b/SoftLayer/CLI/block/snapshot/cancel.py
new file mode 100644
index 00000000..7a6150bf
--- /dev/null
+++ b/SoftLayer/CLI/block/snapshot/cancel.py
@@ -0,0 +1,40 @@
+"""Cancel a snapshot space subscription."""
+# :license: MIT, see LICENSE for more details.
+
+import click
+
+import SoftLayer
+from SoftLayer.CLI import environment
+from SoftLayer.CLI import exceptions
+from SoftLayer.CLI import formatting
+
+
+@click.command()
+@click.argument('volume-id')
+@click.option('--reason', help="An optional reason for cancellation")
+@click.option('--immediate',
+              is_flag=True,
+              help="Cancels the snapshot space immediately instead "
+                   "of on the billing anniversary")
+@environment.pass_env
+def cli(env, volume_id, reason, immediate):
+    """Cancel existing snapshot space for a given volume."""
+
+    block_storage_manager = SoftLayer.BlockStorageManager(env.client)
+
+    if not (env.skip_confirmations or formatting.no_going_back(volume_id)):
+        raise exceptions.CLIAbort('Aborted')
+
+    cancelled = block_storage_manager.cancel_snapshot_space(
+        volume_id, reason, immediate)
+
+    if cancelled:
+        if immediate:
+            click.echo('Block volume with id %s has been marked'
+                       ' for immediate snapshot cancellation' % volume_id)
+        else:
+            click.echo('Block volume with id %s has been marked'
+                       ' for snapshot cancellation' % volume_id)
+    else:
+        click.echo('Unable to cancel snapshot space for block volume %s'
+                   % volume_id)
diff --git a/SoftLayer/CLI/block/snapshot/create.py b/SoftLayer/CLI/block/snapshot/create.py
new file mode 100644
index 00000000..4e327b0d
--- /dev/null
+++ b/SoftLayer/CLI/block/snapshot/create.py
@@ -0,0 +1,20 @@
+"""Create a block storage snapshot."""
+# :license: MIT, see LICENSE for more details.
+
+import click
+import SoftLayer
+from SoftLayer.CLI import environment
+
+
+@click.command()
+@click.argument('volume_id')
+@click.option('--notes', '-n',
+              help='Notes to set on the new snapshot')
+@environment.pass_env
+def cli(env, volume_id, notes):
+    """Creates a snapshot on a given volume"""
+    block_manager = SoftLayer.BlockStorageManager(env.client)
+    snapshot = block_manager.create_snapshot(volume_id, notes=notes)
+
+    if snapshot['id']:
+        click.echo('New snapshot created with id: %s' % snapshot['id'])
diff --git a/SoftLayer/CLI/block/snapshot_delete.py b/SoftLayer/CLI/block/snapshot/delete.py
similarity index 65%
rename from SoftLayer/CLI/block/snapshot_delete.py
rename to SoftLayer/CLI/block/snapshot/delete.py
index 6daa3f4d..229a5d7e 100644
--- a/SoftLayer/CLI/block/snapshot_delete.py
+++ b/SoftLayer/CLI/block/snapshot/delete.py
@@ -1,4 +1,4 @@
-"""Create a block storage snapshot."""
+"""Delete a block storage snapshot."""
 # :license: MIT, see LICENSE for more details.
 
 import click
@@ -12,4 +12,7 @@
 def cli(env, snapshot_id):
     """Deletes a snapshot on a given volume"""
     block_manager = SoftLayer.BlockStorageManager(env.client)
-    block_manager.delete_snapshot(snapshot_id)
+    deleted = block_manager.delete_snapshot(snapshot_id)
+
+    if deleted:
+        click.echo('Snapshot %s deleted' % snapshot_id)
diff --git a/SoftLayer/CLI/block/snapshot/disable.py b/SoftLayer/CLI/block/snapshot/disable.py
new file mode 100644
index 00000000..f34d3483
--- /dev/null
+++ b/SoftLayer/CLI/block/snapshot/disable.py
@@ -0,0 +1,29 @@
+"""Disable scheduled snapshots of a specific volume"""
+# :license: MIT, see LICENSE for more details.
+
+import click
+import SoftLayer
+from SoftLayer.CLI import environment
+from SoftLayer.CLI import exceptions
+
+
+@click.command()
+@click.argument('volume_id')
+@click.option('--schedule-type',
+              help='Snapshot schedule [HOURLY|DAILY|WEEKLY]',
+              required=True)
+@environment.pass_env
+def cli(env, volume_id, schedule_type):
+    """Disables snapshots on the specified schedule for a given volume"""
+
+    if (schedule_type != 'HOURLY' and schedule_type != 'DAILY'
+            and schedule_type != 'WEEKLY'):
+        raise exceptions.CLIAbort(
+            '--schedule-type must be HOURLY, DAILY, or WEEKLY')
+
+    block_manager = SoftLayer.BlockStorageManager(env.client)
+    disabled = block_manager.disable_snapshots(volume_id, schedule_type)
+
+    if disabled:
+        click.echo('%s snapshots have been disabled for volume %s'
+                   % (schedule_type, volume_id))
diff --git a/SoftLayer/CLI/block/snapshot/enable.py b/SoftLayer/CLI/block/snapshot/enable.py
new file mode 100644
index 00000000..6ade9464
--- /dev/null
+++ b/SoftLayer/CLI/block/snapshot/enable.py
@@ -0,0 +1,58 @@
+# snapshot_enable.py
+"""Create a block storage snapshot [ENABLE]."""
+
+import click
+import SoftLayer
+from SoftLayer.CLI import environment
+from SoftLayer.CLI import exceptions
+
+
+@click.command()
+@click.argument('volume_id')
+@click.option('--schedule-type',
+              help='Snapshot schedule [HOURLY|DAILY|WEEKLY]',
+              required=True)
+@click.option('--retention-count',
+              help='Number of snapshots to retain',
+              required=True)
+@click.option('--minute',
+              help='Minute of the day when snapshots should be taken',
+              default=0)
+@click.option('--hour',
+              help='Hour of the day when snapshots should be taken',
+              default=0)
+@click.option('--day-of-week',
+              help='Day of the week when snapshots should be taken',
+              default='SUNDAY')
+@environment.pass_env
+def cli(env, volume_id, schedule_type, retention_count,
+        minute, hour, day_of_week):
+    """Enables snapshots for a given volume on the specified schedule"""
+    block_manager = SoftLayer.BlockStorageManager(env.client)
+
+    valid_schedule_types = {'HOURLY', 'DAILY', 'WEEKLY'}
+    valid_days = {'SUNDAY', 'MONDAY', 'TUESDAY', 'WEDNESDAY', 'THURSDAY',
+                  'FRIDAY', 'SATURDAY'}
+
+    if schedule_type not in valid_schedule_types:
+        raise exceptions.CLIAbort(
+            '--schedule-type must be HOURLY, DAILY, or WEEKLY, not '
+            + schedule_type)
+
+    if minute < 0 or minute > 59:
+        raise exceptions.CLIAbort(
+            '--minute value must be between 0 and 59')
+    if hour < 0 or hour > 23:
+        raise exceptions.CLIAbort(
+            '--hour value must be between 0 and 23')
+    if day_of_week not in valid_days:
+        raise exceptions.CLIAbort(
+            '--day_of_week value must be a valid day (ex: SUNDAY)')
+
+    enabled = block_manager.enable_snapshots(volume_id, schedule_type,
+                                             retention_count, minute,
+                                             hour, day_of_week)
+
+    if enabled:
+        click.echo('%s snapshots have been enabled for volume %s'
+                   % (schedule_type, volume_id))
diff --git a/SoftLayer/CLI/block/snapshot_list.py b/SoftLayer/CLI/block/snapshot/list.py
similarity index 65%
rename from SoftLayer/CLI/block/snapshot_list.py
rename to SoftLayer/CLI/block/snapshot/list.py
index 7ffea3f5..b47f5949 100644
--- a/SoftLayer/CLI/block/snapshot_list.py
+++ b/SoftLayer/CLI/block/snapshot/list.py
@@ -9,17 +9,12 @@
 
 
 COLUMNS = [
-    column_helper.Column(
-        'id',
-        ('snapshots', 'id',),
-        mask='snapshots.id'),
-    column_helper.Column('name', ('snapshots', 'notes',),
-                         mask='snapshots.notes'),
-    column_helper.Column('created',
-                         ('snapshots', 'snapshotCreationTimestamp',),
-                         mask='snapshots.snapshotCreationTimestamp'),
-    column_helper.Column('size_bytes', ('snapshots', 'snapshotSizeBytes',),
-                         mask='snapshots.snapshotSizeBytes'),
+    column_helper.Column('id', ('id',), mask='id'),
+    column_helper.Column('name', ('notes',), mask='notes'),
+    column_helper.Column('created', ('snapshotCreationTimestamp',),
+                         mask='snapshotCreationTimestamp'),
+    column_helper.Column('size_bytes', ('snapshotSizeBytes',),
+                         mask='snapshotSizeBytes'),
 ]
 
 DEFAULT_COLUMNS = [
@@ -40,12 +35,12 @@
                   ', '.join(column.name for column in COLUMNS)),
               default=','.join(DEFAULT_COLUMNS))
 @environment.pass_env
-def cli(env, sortby, columns, volume_id):
+def cli(env, volume_id, sortby, columns):
     """List block storage snapshots."""
     block_manager = SoftLayer.BlockStorageManager(env.client)
     snapshots = block_manager.get_block_volume_snapshot_list(
-        volume_id=volume_id,
-        mask=columns.mask(),
+        volume_id,
+        mask=columns.mask()
     )
 
     table = formatting.Table(columns.columns)
diff --git a/SoftLayer/CLI/block/snapshot/order.py b/SoftLayer/CLI/block/snapshot/order.py
new file mode 100644
index 00000000..119c4d86
--- /dev/null
+++ b/SoftLayer/CLI/block/snapshot/order.py
@@ -0,0 +1,52 @@
+"""Order snapshot space for a block storage volume."""
+# :license: MIT, see LICENSE for more details.
+
+import click
+import SoftLayer
+from SoftLayer.CLI import environment
+from SoftLayer.CLI import exceptions
+
+
+@click.command()
+@click.argument('volume_id')
+@click.option('--capacity',
+              type=int,
+              help='Size of snapshot space to create in GB',
+              required=True)
+@click.option('--tier',
+              help='Endurance Storage Tier (IOPS per GB) of the block'
+              ' volume for which space is ordered [optional]',
+              type=click.Choice(['0.25', '2', '4']))
+@click.option('--upgrade',
+              type=bool,
+              help='Flag to indicate that the order is an upgrade',
+              default=False,
+              is_flag=True)
+@environment.pass_env
+def cli(env, volume_id, capacity, tier, upgrade):
+    """Order snapshot space for a block storage volume."""
+    block_manager = SoftLayer.BlockStorageManager(env.client)
+
+    if tier is not None:
+        tier = float(tier)
+
+    try:
+        order = block_manager.order_snapshot_space(
+            volume_id,
+            capacity=capacity,
+            tier=tier,
+            upgrade=upgrade
+        )
+    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'])
+        if 'status' in order['placedOrder'].keys():
+            click.echo(" > Order status: %s" % order['placedOrder']['status'])
+    else:
+        click.echo("Order could not be placed! Please verify your options " +
+                   "and try again.")
diff --git a/SoftLayer/CLI/block/snapshot/restore.py b/SoftLayer/CLI/block/snapshot/restore.py
new file mode 100644
index 00000000..8cdab7b0
--- /dev/null
+++ b/SoftLayer/CLI/block/snapshot/restore.py
@@ -0,0 +1,22 @@
+"""Restore a block volume from a snapshot."""
+# :license: MIT, see LICENSE for more details.
+
+import click
+import SoftLayer
+from SoftLayer.CLI import environment
+
+
+@click.command()
+@click.argument('volume_id')
+@click.option('--snapshot-id', '-s',
+              help='The id of the snapshot which will be used'
+              ' to restore the block volume')
+@environment.pass_env
+def cli(env, volume_id, snapshot_id):
+    """Restore block volume using a given snapshot"""
+    block_manager = SoftLayer.BlockStorageManager(env.client)
+    success = block_manager.restore_from_snapshot(volume_id, snapshot_id)
+
+    if success:
+        click.echo('Block volume %s is being restored using snapshot %s'
+                   % (volume_id, snapshot_id))
diff --git a/SoftLayer/CLI/file/__init__.py b/SoftLayer/CLI/file/__init__.py
new file mode 100644
index 00000000..2b9101d2
--- /dev/null
+++ b/SoftLayer/CLI/file/__init__.py
@@ -0,0 +1,1 @@
+"""File Storage."""
diff --git a/SoftLayer/CLI/file/access/__init__.py b/SoftLayer/CLI/file/access/__init__.py
new file mode 100644
index 00000000..22c4cd66
--- /dev/null
+++ b/SoftLayer/CLI/file/access/__init__.py
@@ -0,0 +1,1 @@
+"""File Storage Access Control."""
diff --git a/SoftLayer/CLI/file/access/authorize.py b/SoftLayer/CLI/file/access/authorize.py
new file mode 100644
index 00000000..92fe0365
--- /dev/null
+++ b/SoftLayer/CLI/file/access/authorize.py
@@ -0,0 +1,49 @@
+"""Authorizes hosts on a specific file volume."""
+# :license: MIT, see LICENSE for more details.
+
+import click
+import SoftLayer
+from SoftLayer.CLI import environment
+from SoftLayer.CLI import exceptions
+
+
+@click.command()
+@click.argument('volume_id')
+@click.option('--hardware-id', '-h', multiple=True,
+              help='The id of one SoftLayer_Hardware to authorize')
+@click.option('--virtual-id', '-v', multiple=True,
+              help='The id of one SoftLayer_Virtual_Guest to authorize')
+@click.option('--ip-address-id', '-i', multiple=True,
+              help='The id of one SoftLayer_Network_Subnet_IpAddress'
+              ' to authorize')
+@click.option('--ip-address', multiple=True,
+              help='An IP address to authorize')
+@click.option('--subnet-id', '-s', multiple=True,
+              help='The id of one SoftLayer_Network_Subnet to authorize')
+@environment.pass_env
+def cli(env, volume_id, hardware_id, virtual_id, ip_address_id,
+        ip_address, subnet_id):
+    """Authorizes hosts to access a given volume"""
+    file_manager = SoftLayer.FileStorageManager(env.client)
+    ip_address_id_list = list(ip_address_id)
+
+    # Convert actual IP Addresses to their SoftLayer ids
+    if ip_address is not None:
+        network_manager = SoftLayer.NetworkManager(env.client)
+        for ip_address_value in ip_address:
+            ip_address_object = network_manager.ip_lookup(ip_address_value)
+            if ip_address_object == "":
+                click.echo("IP Address not found on your account.  " +
+                           "Please confirm IP and try again.")
+                raise exceptions.ArgumentError('Incorrect IP Address')
+            else:
+                ip_address_id_list.append(ip_address_object['id'])
+
+    file_manager.authorize_host_to_volume(volume_id,
+                                          hardware_id,
+                                          virtual_id,
+                                          ip_address_id_list,
+                                          subnet_id)
+
+    # If no exception was raised, the command succeeded
+    click.echo('The specified hosts were authorized to access %s' % volume_id)
diff --git a/SoftLayer/CLI/file/access/list.py b/SoftLayer/CLI/file/access/list.py
new file mode 100644
index 00000000..cc9997a0
--- /dev/null
+++ b/SoftLayer/CLI/file/access/list.py
@@ -0,0 +1,38 @@
+"""List hosts with access to file volume."""
+# :license: MIT, see LICENSE for more details.
+
+import click
+import SoftLayer
+from SoftLayer.CLI import columns as column_helper
+from SoftLayer.CLI import environment
+from SoftLayer.CLI import formatting
+from SoftLayer.CLI import storage_utils
+
+
+@click.command()
+@click.argument('volume_id')
+@click.option('--sortby', help='Column to sort by', default='name')
+@click.option('--columns',
+              callback=column_helper.get_formatter(storage_utils.COLUMNS),
+              help='Columns to display. Options: {0}'.format(
+                  ', '.join(column.name for column in storage_utils.COLUMNS)),
+              default=','.join(storage_utils.DEFAULT_COLUMNS))
+@environment.pass_env
+def cli(env, columns, sortby, volume_id):
+    """List ACLs."""
+    file_manager = SoftLayer.FileStorageManager(env.client)
+    access_list = file_manager.get_file_volume_access_list(
+        volume_id=volume_id)
+    table = formatting.Table(columns.columns)
+    table.sortby = sortby
+
+    for key, type_name in [('allowedVirtualGuests', 'VIRTUAL'),
+                           ('allowedHardware', 'HARDWARE'),
+                           ('allowedSubnets', 'SUBNET'),
+                           ('allowedIpAddresses', 'IP')]:
+        for obj in access_list.get(key, []):
+            obj['type'] = type_name
+            table.add_row([value or formatting.blank()
+                           for value in columns.row(obj)])
+
+    env.fout(table)
diff --git a/SoftLayer/CLI/file/access/revoke.py b/SoftLayer/CLI/file/access/revoke.py
new file mode 100644
index 00000000..e9119235
--- /dev/null
+++ b/SoftLayer/CLI/file/access/revoke.py
@@ -0,0 +1,46 @@
+"""Revokes hosts' access on a specific file 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('--hardware-id', '-h', multiple=True,
+              help='The id of one SoftLayer_Hardware'
+              ' to revoke authorization')
+@click.option('--virtual-id', '-v', multiple=True,
+              help='The id of one SoftLayer_Virtual_Guest'
+              ' to revoke authorization')
+@click.option('--ip-address-id', '-i', multiple=True,
+              help='The id of one SoftLayer_Network_Subnet_IpAddress'
+              ' to revoke authorization')
+@click.option('--ip-address', multiple=True,
+              help='An IP address to revoke authorization')
+@click.option('--subnet-id', '-s', multiple=True,
+              help='The id of one SoftLayer_Network_Subnet'
+              ' to revoke authorization')
+@environment.pass_env
+def cli(env, volume_id, hardware_id, virtual_id, ip_address_id,
+        ip_address, subnet_id):
+    """Revokes authorization for hosts accessing a given volume"""
+    file_manager = SoftLayer.FileStorageManager(env.client)
+    ip_address_id_list = list(ip_address_id)
+
+    # Convert actual IP Addresses to their SoftLayer ids
+    if ip_address is not None:
+        network_manager = SoftLayer.NetworkManager(env.client)
+        for ip_address_value in ip_address:
+            ip_address_object = network_manager.ip_lookup(ip_address_value)
+            ip_address_id_list.append(ip_address_object['id'])
+
+    file_manager.deauthorize_host_to_volume(volume_id,
+                                            hardware_id,
+                                            virtual_id,
+                                            ip_address_id_list,
+                                            subnet_id)
+
+    # If no exception was raised, the command succeeded
+    click.echo('Access to %s was revoked for the specified hosts' % volume_id)
diff --git a/SoftLayer/CLI/file/cancel.py b/SoftLayer/CLI/file/cancel.py
new file mode 100644
index 00000000..05229f3b
--- /dev/null
+++ b/SoftLayer/CLI/file/cancel.py
@@ -0,0 +1,39 @@
+"""Cancel an existing file storage volume."""
+# :license: MIT, see LICENSE for more details.
+
+import click
+
+import SoftLayer
+from SoftLayer.CLI import environment
+from SoftLayer.CLI import exceptions
+from SoftLayer.CLI import formatting
+
+
+@click.command()
+@click.argument('volume-id')
+@click.option('--reason', help="An optional reason for cancellation")
+@click.option('--immediate',
+              is_flag=True,
+              help="Cancels the file storage volume immediately instead "
+                   "of on the billing anniversary")
+@environment.pass_env
+def cli(env, volume_id, reason, immediate):
+    """Cancel an existing file storage volume."""
+
+    file_storage_manager = SoftLayer.FileStorageManager(env.client)
+
+    if not (env.skip_confirmations or formatting.no_going_back(volume_id)):
+        raise exceptions.CLIAbort('Aborted')
+
+    cancelled = file_storage_manager.cancel_file_volume(volume_id,
+                                                        reason, immediate)
+
+    if cancelled:
+        if immediate:
+            click.echo('File volume with id %s has been marked'
+                       ' for immediate cancellation' % volume_id)
+        else:
+            click.echo('File volume with id %s has been marked'
+                       ' for cancellation' % volume_id)
+    else:
+        click.echo('Unable to cancle file volume %s' % volume_id)
diff --git a/SoftLayer/CLI/file/detail.py b/SoftLayer/CLI/file/detail.py
new file mode 100644
index 00000000..e8ceda92
--- /dev/null
+++ b/SoftLayer/CLI/file/detail.py
@@ -0,0 +1,78 @@
+"""Display details for a specified volume."""
+# :license: MIT, see LICENSE for more details.
+
+import click
+import SoftLayer
+from SoftLayer.CLI import environment
+from SoftLayer.CLI import formatting
+from SoftLayer import utils
+
+
+@click.command()
+@click.argument('volume_id')
+@environment.pass_env
+def cli(env, volume_id):
+    """Display details for a specified volume."""
+    file_manager = SoftLayer.FileStorageManager(env.client)
+    file_volume = file_manager.get_file_volume_details(volume_id)
+    file_volume = utils.NestedDict(file_volume)
+    used_space = int(file_volume['bytesUsed'])
+
+    table = formatting.KeyValueTable(['Name', 'Value'])
+    table.align['Name'] = 'r'
+    table.align['Value'] = 'l'
+
+    storage_type = file_volume['storageType']['keyName'].split('_').pop(0)
+    table.add_row(['ID', file_volume['id']])
+    table.add_row(['Username', file_volume['username']])
+    table.add_row(['Type', storage_type])
+    table.add_row(['Capacity (GB)', "%iGB" % file_volume['capacityGb']])
+
+    if used_space < (1 << 10):
+        table.add_row(['Used Space', "%dB" % used_space])
+    elif used_space < (1 << 20):
+        table.add_row(['Used Space', "%dKB" % (used_space / (1 << 10))])
+    elif used_space < (1 << 30):
+        table.add_row(['Used Space', "%dMB" % (used_space / (1 << 20))])
+    else:
+        table.add_row(['Used Space', "%dGB" % (used_space / (1 << 30))])
+
+    if file_volume.get('iops'):
+        table.add_row(['IOPs', file_volume['iops']])
+
+    if file_volume.get('storageTierLevel'):
+        table.add_row([
+            'Endurance Tier',
+            file_volume['storageTierLevel']['description'],
+        ])
+
+    table.add_row([
+        'Data Center',
+        file_volume['serviceResource']['datacenter']['name'],
+    ])
+    table.add_row([
+        'Target IP',
+        file_volume['serviceResourceBackendIpAddress'],
+    ])
+
+    if file_volume['snapshotCapacityGb']:
+        table.add_row([
+            'Snapshot Capacity (GB)',
+            file_volume['snapshotCapacityGb'],
+        ])
+        if 'snapshotSizeBytes' in file_volume['parentVolume']:
+            table.add_row([
+                'Snapshot Used (Bytes)',
+                file_volume['parentVolume']['snapshotSizeBytes'],
+            ])
+
+    table.add_row(['# of Active Transactions', "%i"
+                   % file_volume['activeTransactionCount']])
+
+    if file_volume['activeTransactions']:
+        for trans in file_volume['activeTransactions']:
+            table.add_row([
+                'Ongoing Transactions',
+                trans['transactionStatus']['friendlyName']])
+
+    env.fout(table)
diff --git a/SoftLayer/CLI/file/list.py b/SoftLayer/CLI/file/list.py
new file mode 100644
index 00000000..4b12de18
--- /dev/null
+++ b/SoftLayer/CLI/file/list.py
@@ -0,0 +1,72 @@
+"""List file storage volumes."""
+# :license: MIT, see LICENSE for more details.
+
+import click
+import SoftLayer
+from SoftLayer.CLI import columns as column_helper
+from SoftLayer.CLI import environment
+from SoftLayer.CLI import formatting
+
+
+COLUMNS = [
+    column_helper.Column('id', ('id',), mask="id"),
+    column_helper.Column('username', ('username',), mask="username"),
+    column_helper.Column('datacenter',
+                         ('serviceResource', 'datacenter', 'name'),
+                         mask="serviceResource.datacenter.name"),
+    column_helper.Column(
+        'storage_type',
+        lambda b: b['storageType']['keyName'].split('_').pop(0)
+        if 'storageType' in b and 'keyName' in b['storageType']
+        and isinstance(b['storageType']['keyName'], str)
+        else '-',
+        mask="storageType.keyName"),
+    column_helper.Column('capacity_gb', ('capacityGb',), mask="capacityGb"),
+    column_helper.Column('bytes_used', ('bytesUsed',), mask="bytesUsed"),
+    column_helper.Column('ip_addr', ('serviceResourceBackendIpAddress',),
+                         mask="serviceResourceBackendIpAddress"),
+    column_helper.Column('active_transactions', ('activeTransactionCount',),
+                         mask="activeTransactionCount"),
+]
+
+DEFAULT_COLUMNS = [
+    'id',
+    'username',
+    'datacenter',
+    'storage_type',
+    'capacity_gb',
+    'bytes_used',
+    'ip_addr',
+    'active_transactions'
+]
+
+
+@click.command()
+@click.option('--username', '-u', help='Volume username')
+@click.option('--datacenter', '-d', help='Datacenter shortname')
+@click.option('--storage-type',
+              help='Type of storage volume',
+              type=click.Choice(['performance', 'endurance']))
+@click.option('--sortby', help='Column to sort by', default='username')
+@click.option('--columns',
+              callback=column_helper.get_formatter(COLUMNS),
+              help='Columns to display. Options: {0}'.format(
+                  ', '.join(column.name for column in COLUMNS)),
+              default=','.join(DEFAULT_COLUMNS))
+@environment.pass_env
+def cli(env, sortby, columns, datacenter, username, storage_type):
+    """List file storage."""
+    file_manager = SoftLayer.FileStorageManager(env.client)
+    file_volumes = file_manager.list_file_volumes(datacenter=datacenter,
+                                                  username=username,
+                                                  storage_type=storage_type,
+                                                  mask=columns.mask())
+
+    table = formatting.Table(columns.columns)
+    table.sortby = sortby
+
+    for file_volume in file_volumes:
+        table.add_row([value or formatting.blank()
+                       for value in columns.row(file_volume)])
+
+    env.fout(table)
diff --git a/SoftLayer/CLI/file/order.py b/SoftLayer/CLI/file/order.py
new file mode 100644
index 00000000..bb200f27
--- /dev/null
+++ b/SoftLayer/CLI/file/order.py
@@ -0,0 +1,99 @@
+"""Order a file storage volume."""
+# :license: MIT, see LICENSE for more details.
+
+import click
+import SoftLayer
+from SoftLayer.CLI import environment
+from SoftLayer.CLI import exceptions
+
+
+CONTEXT_SETTINGS = dict(token_normalize_func=lambda x: x.upper())
+
+
+@click.command(context_settings=CONTEXT_SETTINGS)
+@click.option('--storage-type',
+              help='Type of file storage volume',
+              type=click.Choice(['performance', 'endurance']),
+              required=True)
+@click.option('--size',
+              type=int,
+              help='Size of file storage volume in GB',
+              required=True)
+@click.option('--iops',
+              type=int,
+              help='Performance Storage IOPs,'
+              ' between 100 and 6000 in multiples of 100'
+              '  [required for storage-type performance]')
+@click.option('--tier',
+              help='Endurance Storage Tier (IOP per GB)'
+              '  [required for storage-type endurance]',
+              type=click.Choice(['0.25', '2', '4']))
+@click.option('--os-type',
+              help='Operating System',
+              type=click.Choice([
+                  'HYPER_V',
+                  'LINUX',
+                  'VMWARE',
+                  'WINDOWS_2008',
+                  'WINDOWS_GPT',
+                  'WINDOWS',
+                  'XEN']),
+              required=True)
+@click.option('--location',
+              help='Datacenter short name (e.g.: dal09)',
+              required=True)
+@environment.pass_env
+def cli(env, storage_type, size, iops, tier, os_type, location):
+    """Order a file storage volume."""
+    file_manager = SoftLayer.FileStorageManager(env.client)
+    storage_type = storage_type.lower()
+
+    if storage_type == 'performance':
+        if iops is None:
+            raise exceptions.CLIAbort(
+                'Option --iops required with Performance')
+
+        if iops < 100 or iops > 6000:
+            raise exceptions.CLIAbort(
+                'Option --iops must be between 100 and 6000, inclusive')
+
+        if iops % 100 != 0:
+            raise exceptions.CLIAbort(
+                'Option --iops must be a multiple of 100'
+            )
+
+        try:
+            order = file_manager.order_file_volume(
+                storage_type='performance_storage_nfs',
+                location=location,
+                size=size,
+                iops=iops,
+                os_type=os_type
+            )
+        except ValueError as ex:
+            raise exceptions.ArgumentError(str(ex))
+
+    if storage_type == 'endurance':
+        if tier is None:
+            raise exceptions.CLIAbort(
+                'Option --tier required with Endurance in IOPS/GB [0.25,2,4]')
+
+        try:
+            order = file_manager.order_file_volume(
+                storage_type='storage_service_enterprise',
+                location=location,
+                size=size,
+                tier_level=float(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/file/snapshot/__init__.py b/SoftLayer/CLI/file/snapshot/__init__.py
new file mode 100644
index 00000000..b9ec8544
--- /dev/null
+++ b/SoftLayer/CLI/file/snapshot/__init__.py
@@ -0,0 +1,1 @@
+"""File Storage Snapshot Control."""
diff --git a/SoftLayer/CLI/file/snapshot/cancel.py b/SoftLayer/CLI/file/snapshot/cancel.py
new file mode 100644
index 00000000..e0fc26f8
--- /dev/null
+++ b/SoftLayer/CLI/file/snapshot/cancel.py
@@ -0,0 +1,40 @@
+"""Cancel a snapshot space subscription."""
+# :license: MIT, see LICENSE for more details.
+
+import click
+
+import SoftLayer
+from SoftLayer.CLI import environment
+from SoftLayer.CLI import exceptions
+from SoftLayer.CLI import formatting
+
+
+@click.command()
+@click.argument('volume-id')
+@click.option('--reason', help="An optional reason for cancellation")
+@click.option('--immediate',
+              is_flag=True,
+              help="Cancels the snapshot space immediately instead "
+                   "of on the billing anniversary")
+@environment.pass_env
+def cli(env, volume_id, reason, immediate):
+    """Cancel existing snapshot space for a given volume."""
+
+    file_storage_manager = SoftLayer.FileStorageManager(env.client)
+
+    if not (env.skip_confirmations or formatting.no_going_back(volume_id)):
+        raise exceptions.CLIAbort('Aborted')
+
+    cancelled = file_storage_manager.cancel_snapshot_space(
+        volume_id, reason, immediate)
+
+    if cancelled:
+        if immediate:
+            click.echo('File volume with id %s has been marked'
+                       ' for immediate snapshot cancellation' % volume_id)
+        else:
+            click.echo('File volume with id %s has been marked'
+                       ' for snapshot cancellation' % volume_id)
+    else:
+        click.echo('Unable to cancel snapshot space for file volume %s'
+                   % volume_id)
diff --git a/SoftLayer/CLI/file/snapshot/create.py b/SoftLayer/CLI/file/snapshot/create.py
new file mode 100644
index 00000000..9c33d026
--- /dev/null
+++ b/SoftLayer/CLI/file/snapshot/create.py
@@ -0,0 +1,20 @@
+"""Create a file storage snapshot."""
+# :license: MIT, see LICENSE for more details.
+
+import click
+import SoftLayer
+from SoftLayer.CLI import environment
+
+
+@click.command()
+@click.argument('volume_id')
+@click.option('--notes', '-n',
+              help='Notes to set on the new snapshot')
+@environment.pass_env
+def cli(env, volume_id, notes):
+    """Creates a snapshot on a given volume"""
+    file_storage_manager = SoftLayer.FileStorageManager(env.client)
+    snapshot = file_storage_manager.create_snapshot(volume_id, notes=notes)
+
+    if snapshot['id']:
+        click.echo('New snapshot created with id: %s' % snapshot['id'])
diff --git a/SoftLayer/CLI/file/snapshot/delete.py b/SoftLayer/CLI/file/snapshot/delete.py
new file mode 100644
index 00000000..8ff15313
--- /dev/null
+++ b/SoftLayer/CLI/file/snapshot/delete.py
@@ -0,0 +1,18 @@
+"""Delete a file storage snapshot."""
+# :license: MIT, see LICENSE for more details.
+
+import click
+import SoftLayer
+from SoftLayer.CLI import environment
+
+
+@click.command()
+@click.argument('snapshot_id')
+@environment.pass_env
+def cli(env, snapshot_id):
+    """Deletes a snapshot on a given volume"""
+    file_storage_manager = SoftLayer.FileStorageManager(env.client)
+    deleted = file_storage_manager.delete_snapshot(snapshot_id)
+
+    if deleted:
+        click.echo('Snapshot %s deleted' % snapshot_id)
diff --git a/SoftLayer/CLI/file/snapshot/disable.py b/SoftLayer/CLI/file/snapshot/disable.py
new file mode 100644
index 00000000..9ec4fde8
--- /dev/null
+++ b/SoftLayer/CLI/file/snapshot/disable.py
@@ -0,0 +1,29 @@
+"""Disable scheduled snapshots of a specific volume"""
+# :license: MIT, see LICENSE for more details.
+
+import click
+import SoftLayer
+from SoftLayer.CLI import environment
+from SoftLayer.CLI import exceptions
+
+
+@click.command()
+@click.argument('volume_id')
+@click.option('--schedule-type',
+              help='Snapshot schedule [HOURLY|DAILY|WEEKLY]',
+              required=True)
+@environment.pass_env
+def cli(env, volume_id, schedule_type):
+    """Disables snapshots on the specified schedule for a given volume"""
+
+    if (schedule_type != 'HOURLY' and schedule_type != 'DAILY'
+            and schedule_type != 'WEEKLY'):
+        raise exceptions.CLIAbort(
+            '--schedule_type must be HOURLY, DAILY, or WEEKLY')
+
+    file_manager = SoftLayer.FileStorageManager(env.client)
+    disabled = file_manager.disable_snapshots(volume_id, schedule_type)
+
+    if disabled:
+        click.echo('%s snapshots have been disabled for volume %s'
+                   % (schedule_type, volume_id))
diff --git a/SoftLayer/CLI/file/snapshot/enable.py b/SoftLayer/CLI/file/snapshot/enable.py
new file mode 100644
index 00000000..7e73feef
--- /dev/null
+++ b/SoftLayer/CLI/file/snapshot/enable.py
@@ -0,0 +1,58 @@
+# snapshot_enable.py
+"""Create a file storage snapshot [ENABLE]."""
+
+import click
+import SoftLayer
+from SoftLayer.CLI import environment
+from SoftLayer.CLI import exceptions
+
+
+@click.command()
+@click.argument('volume_id')
+@click.option('--schedule-type',
+              help='Snapshot schedule [HOURLY|DAILY|WEEKLY]',
+              required=True)
+@click.option('--retention-count',
+              help='Number of snapshots to retain',
+              required=True)
+@click.option('--minute',
+              help='Minute of the day when snapshots should be taken',
+              default=0)
+@click.option('--hour',
+              help='Hour of the day when snapshots should be taken',
+              default=0)
+@click.option('--day-of-week',
+              help='Day of the week when snapshots should be taken',
+              default='SUNDAY')
+@environment.pass_env
+def cli(env, volume_id, schedule_type, retention_count,
+        minute, hour, day_of_week):
+    """Enables snapshots for a given volume on the specified schedule"""
+    file_manager = SoftLayer.FileStorageManager(env.client)
+
+    valid_schedule_types = {'HOURLY', 'DAILY', 'WEEKLY'}
+    valid_days = {'SUNDAY', 'MONDAY', 'TUESDAY', 'WEDNESDAY', 'THURSDAY',
+                  'FRIDAY', 'SATURDAY'}
+
+    if schedule_type not in valid_schedule_types:
+        raise exceptions.CLIAbort(
+            '--schedule-type must be HOURLY, DAILY, or WEEKLY, not '
+            + schedule_type)
+
+    if minute < 0 or minute > 59:
+        raise exceptions.CLIAbort(
+            '--minute value must be between 0 and 59')
+    if hour < 0 or hour > 23:
+        raise exceptions.CLIAbort(
+            '--hour value must be between 0 and 23')
+    if day_of_week not in valid_days:
+        raise exceptions.CLIAbort(
+            '--day_of_week value must be a valid day (ex: SUNDAY)')
+
+    enabled = file_manager.enable_snapshots(volume_id, schedule_type,
+                                            retention_count, minute,
+                                            hour, day_of_week)
+
+    if enabled:
+        click.echo('%s snapshots have been enabled for volume %s'
+                   % (schedule_type, volume_id))
diff --git a/SoftLayer/CLI/file/snapshot/list.py b/SoftLayer/CLI/file/snapshot/list.py
new file mode 100644
index 00000000..494900f0
--- /dev/null
+++ b/SoftLayer/CLI/file/snapshot/list.py
@@ -0,0 +1,53 @@
+"""List file storage snapshots."""
+# :license: MIT, see LICENSE for more details.
+
+import click
+import SoftLayer
+from SoftLayer.CLI import columns as column_helper
+from SoftLayer.CLI import environment
+from SoftLayer.CLI import formatting
+
+
+COLUMNS = [
+    column_helper.Column('id', ('id',), mask='id'),
+    column_helper.Column('name', ('notes',), mask='notes'),
+    column_helper.Column('created', ('snapshotCreationTimestamp',),
+                         mask='snapshotCreationTimestamp'),
+    column_helper.Column('size_bytes', ('snapshotSizeBytes',),
+                         mask='snapshotSizeBytes'),
+]
+
+DEFAULT_COLUMNS = [
+    'id',
+    'name',
+    'created',
+    'size_bytes'
+]
+
+
+@click.command()
+@click.argument('volume_id')
+@click.option('--sortby', help='Column to sort by',
+              default='created')
+@click.option('--columns',
+              callback=column_helper.get_formatter(COLUMNS),
+              help='Columns to display. Options: {0}'.format(
+                  ', '.join(column.name for column in COLUMNS)),
+              default=','.join(DEFAULT_COLUMNS))
+@environment.pass_env
+def cli(env, volume_id, sortby, columns):
+    """List file storage snapshots."""
+    file_manager = SoftLayer.FileStorageManager(env.client)
+    snapshots = file_manager.get_file_volume_snapshot_list(
+        volume_id,
+        mask=columns.mask()
+    )
+
+    table = formatting.Table(columns.columns)
+    table.sortby = sortby
+
+    for snapshot in snapshots:
+        table.add_row([value or formatting.blank()
+                       for value in columns.row(snapshot)])
+
+    env.fout(table)
diff --git a/SoftLayer/CLI/file/snapshot/order.py b/SoftLayer/CLI/file/snapshot/order.py
new file mode 100644
index 00000000..fbfff517
--- /dev/null
+++ b/SoftLayer/CLI/file/snapshot/order.py
@@ -0,0 +1,52 @@
+"""Order snapshot space for a file storage volume."""
+# :license: MIT, see LICENSE for more details.
+
+import click
+import SoftLayer
+from SoftLayer.CLI import environment
+from SoftLayer.CLI import exceptions
+
+
+@click.command()
+@click.argument('volume_id')
+@click.option('--capacity',
+              type=int,
+              help='Size of snapshot space to create in GB',
+              required=True)
+@click.option('--tier',
+              help='Endurance Storage Tier (IOPS per GB) of the file'
+              ' volume for which space is ordered [optional]',
+              type=click.Choice(['0.25', '2', '4']))
+@click.option('--upgrade',
+              type=bool,
+              help='Flag to indicate that the order is an upgrade',
+              default=False,
+              is_flag=True)
+@environment.pass_env
+def cli(env, volume_id, capacity, tier, upgrade):
+    """Order snapshot space for a file storage volume."""
+    file_manager = SoftLayer.FileStorageManager(env.client)
+
+    if tier is not None:
+        tier = float(tier)
+
+    try:
+        order = file_manager.order_snapshot_space(
+            volume_id,
+            capacity=capacity,
+            tier=tier,
+            upgrade=upgrade
+        )
+    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'])
+        if 'status' in order['placedOrder'].keys():
+            click.echo(" > Order status: %s" % order['placedOrder']['status'])
+    else:
+        click.echo("Order could not be placed! Please verify your options " +
+                   "and try again.")
diff --git a/SoftLayer/CLI/file/snapshot/restore.py b/SoftLayer/CLI/file/snapshot/restore.py
new file mode 100644
index 00000000..881a9338
--- /dev/null
+++ b/SoftLayer/CLI/file/snapshot/restore.py
@@ -0,0 +1,22 @@
+"""Restore a file volume from a snapshot."""
+# :license: MIT, see LICENSE for more details.
+
+import click
+import SoftLayer
+from SoftLayer.CLI import environment
+
+
+@click.command()
+@click.argument('volume_id')
+@click.option('--snapshot-id', '-s',
+              help='The id of the snapshot which will be used'
+              ' to restore the block volume')
+@environment.pass_env
+def cli(env, volume_id, snapshot_id):
+    """Restore file volume using a given snapshot"""
+    file_manager = SoftLayer.FileStorageManager(env.client)
+    success = file_manager.restore_from_snapshot(volume_id, snapshot_id)
+
+    if success:
+        click.echo('File volume %s is being restored using snapshot %s'
+                   % (volume_id, snapshot_id))
diff --git a/SoftLayer/CLI/routes.py b/SoftLayer/CLI/routes.py
index f7ff388f..4e5f0ecf 100644
--- a/SoftLayer/CLI/routes.py
+++ b/SoftLayer/CLI/routes.py
@@ -57,17 +57,38 @@
     ('dns:zone-print', 'SoftLayer.CLI.dns.zone_print:cli'),
 
     ('block', 'SoftLayer.CLI.block'),
-    ('block:volume-list', 'SoftLayer.CLI.block.list:cli'),
-    ('block:volume-detail', 'SoftLayer.CLI.block.detail:cli'),
+    ('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: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'),
+    ('block:snapshot-disable', 'SoftLayer.CLI.block.snapshot.disable:cli'),
+    ('block:snapshot-enable', 'SoftLayer.CLI.block.snapshot.enable:cli'),
+    ('block:snapshot-list', 'SoftLayer.CLI.block.snapshot.list:cli'),
+    ('block:snapshot-order', 'SoftLayer.CLI.block.snapshot.order:cli'),
+    ('block:snapshot-restore', 'SoftLayer.CLI.block.snapshot.restore:cli'),
     ('block:volume-cancel', 'SoftLayer.CLI.block.cancel:cli'),
+    ('block:volume-detail', 'SoftLayer.CLI.block.detail:cli'),
+    ('block:volume-list', 'SoftLayer.CLI.block.list:cli'),
     ('block:volume-order', 'SoftLayer.CLI.block.order:cli'),
 
-    ('block:snapshot-list', 'SoftLayer.CLI.block.snapshot_list:cli'),
-    ('block:snapshot-delete', 'SoftLayer.CLI.block.snapshot_delete:cli'),
-
-    ('block:access-list', 'SoftLayer.CLI.block.access_list:cli'),
-    ('block:access-authorize', 'SoftLayer.CLI.block.access.authorize:cli'),
-    ('block:access-revoke', 'SoftLayer.CLI.block.access.revoke:cli'),
+    ('file', 'SoftLayer.CLI.file'),
+    ('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: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'),
+    ('file:snapshot-disable', 'SoftLayer.CLI.file.snapshot.disable:cli'),
+    ('file:snapshot-enable', 'SoftLayer.CLI.file.snapshot.enable:cli'),
+    ('file:snapshot-list', 'SoftLayer.CLI.file.snapshot.list:cli'),
+    ('file:snapshot-order', 'SoftLayer.CLI.file.snapshot.order:cli'),
+    ('file:snapshot-restore', 'SoftLayer.CLI.file.snapshot.restore:cli'),
+    ('file:volume-cancel', 'SoftLayer.CLI.file.cancel:cli'),
+    ('file:volume-detail', 'SoftLayer.CLI.file.detail:cli'),
+    ('file:volume-list', 'SoftLayer.CLI.file.list:cli'),
+    ('file:volume-order', 'SoftLayer.CLI.file.order:cli'),
 
     ('firewall', 'SoftLayer.CLI.firewall'),
     ('firewall:add', 'SoftLayer.CLI.firewall.add:cli'),
diff --git a/SoftLayer/CLI/block/access_list.py b/SoftLayer/CLI/storage_utils.py
similarity index 65%
rename from SoftLayer/CLI/block/access_list.py
rename to SoftLayer/CLI/storage_utils.py
index 4b45a54b..dd00596b 100644
--- a/SoftLayer/CLI/block/access_list.py
+++ b/SoftLayer/CLI/storage_utils.py
@@ -1,11 +1,7 @@
-"""List hosts with access to volume."""
+"""Utility functions for use with File and Block commands."""
 # :license: MIT, see LICENSE for more details.
 
-import click
-import SoftLayer
 from SoftLayer.CLI import columns as column_helper
-from SoftLayer.CLI import environment
-from SoftLayer.CLI import formatting
 
 
 def _format_name(obj):
@@ -92,32 +88,3 @@ def _format_name(obj):
     'username',
     'password',
 ]
-
-
-@click.command()
-@click.argument('volume_id')
-@click.option('--sortby', help='Column to sort by', default='name')
-@click.option('--columns',
-              callback=column_helper.get_formatter(COLUMNS),
-              help='Columns to display. Options: {0}'.format(
-                  ', '.join(column.name for column in COLUMNS)),
-              default=','.join(DEFAULT_COLUMNS))
-@environment.pass_env
-def cli(env, columns, sortby, volume_id):
-    """List ACLs."""
-    block_manager = SoftLayer.BlockStorageManager(env.client)
-    access_list = block_manager.get_block_volume_access_list(
-        volume_id=volume_id)
-    table = formatting.Table(columns.columns)
-    table.sortby = sortby
-
-    for key, type_name in [('allowedVirtualGuests', 'VIRTUAL'),
-                           ('allowedHardware', 'HARDWARE'),
-                           ('allowedSubnets', 'SUBNET'),
-                           ('allowedIpAddresses', 'IP')]:
-        for obj in access_list.get(key, []):
-            obj['type'] = type_name
-            table.add_row([value or formatting.blank()
-                           for value in columns.row(obj)])
-
-    env.fout(table)
diff --git a/SoftLayer/fixtures/SoftLayer_Account.py b/SoftLayer/fixtures/SoftLayer_Account.py
index 509945b3..e3649d31 100644
--- a/SoftLayer/fixtures/SoftLayer_Account.py
+++ b/SoftLayer/fixtures/SoftLayer_Account.py
@@ -472,6 +472,7 @@
     'username': 'user',
     'password': 'pass',
     'serviceResourceBackendIpAddress': '127.0.0.1',
+    'storageType': {'keyName': 'ENDURANCE_STORAGE'},
 }]
 
 getActiveQuotes = [{
diff --git a/SoftLayer/fixtures/SoftLayer_Network_Storage.py b/SoftLayer/fixtures/SoftLayer_Network_Storage.py
index 5a45f14f..b87b85fa 100644
--- a/SoftLayer/fixtures/SoftLayer_Network_Storage.py
+++ b/SoftLayer/fixtures/SoftLayer_Network_Storage.py
@@ -1,6 +1,13 @@
 getObject = {
     'accountId': 1234,
-    'billingItem': {'id': 449},
+    'billingItem': {
+        'id': 449,
+        'categoryCode': 'storage_service_enterprise',
+        'activeChildren': [{
+            'categoryCode': 'storage_snapshot_space',
+            'id': 123
+        }]
+    },
     'capacityGb': 20,
     'createDate': '2015:50:15-04:00',
     'guestId': '',
@@ -12,7 +19,7 @@
     'password': '',
     'serviceProviderId': 1,
     'iops': 1000,
-    'storageTierLevel': {'description': 'Tier 1'},
+    'storageTierLevel': {'description': '2 IOPS per GB'},
     'snapshotCapacityGb': 10,
     'parentVolume': {'snapshotSizeBytes': 1024},
     'serviceResource': {'datacenter': {'id': 449500, 'name': 'dal05'}},
@@ -20,6 +27,9 @@
     'serviceResourceName': 'Storage Type 01 Aggregate staaspar0101_pc01',
     'username': 'username',
     'storageType': {'keyName': 'ENDURANCE_STORAGE'},
+    'bytesUsed': 0,
+    'activeTransactions': None,
+    'activeTransactionCount': 0,
     'allowedVirtualGuests': [{
         'id': 1234,
         'hostname': 'test-server',
@@ -61,7 +71,23 @@
         },
     }],
 }
-getSnapshots = []
+
+getSnapshots = [{
+    'id': 470,
+    'notes': 'unit_testing_note',
+    'snapshotCreationTimestamp': '2016-07-06T07:41:19-05:00',
+    'snapshotSizeBytes': '42',
+}]
+
 deleteObject = True
 allowAccessFromHostList = True
 removeAccessFromHostList = True
+
+restoreFromSnapshot = True
+
+createSnapshot = {
+    'id': 449
+}
+
+enableSnapshots = True
+disableSnapshots = True
diff --git a/SoftLayer/managers/__init__.py b/SoftLayer/managers/__init__.py
index cab39fbb..b9a44a0d 100644
--- a/SoftLayer/managers/__init__.py
+++ b/SoftLayer/managers/__init__.py
@@ -10,6 +10,7 @@
 from SoftLayer.managers.block import BlockStorageManager
 from SoftLayer.managers.cdn import CDNManager
 from SoftLayer.managers.dns import DNSManager
+from SoftLayer.managers.file import FileStorageManager
 from SoftLayer.managers.firewall import FirewallManager
 from SoftLayer.managers.hardware import HardwareManager
 from SoftLayer.managers.image import ImageManager
@@ -29,6 +30,7 @@
     'BlockStorageManager',
     'CDNManager',
     'DNSManager',
+    'FileStorageManager',
     'FirewallManager',
     'HardwareManager',
     'ImageManager',
diff --git a/SoftLayer/managers/block.py b/SoftLayer/managers/block.py
index 26040321..10f170f3 100644
--- a/SoftLayer/managers/block.py
+++ b/SoftLayer/managers/block.py
@@ -6,16 +6,10 @@
     :license: MIT, see LICENSE for more details.
 """
 from SoftLayer import exceptions
+from SoftLayer.managers import storage_utils
 from SoftLayer import utils
 
 
-ENDURANCE_TIERS = {
-    0.25: 100,
-    2: 200,
-    4: 300,
-}
-
-
 class BlockStorageManager(utils.IdentifierMixin, object):
     """Manages Block Storage volumes."""
 
@@ -37,10 +31,12 @@ def list_block_volumes(self, datacenter=None, username=None,
             items = [
                 'id',
                 'username',
+                'lunId',
                 'capacityGb',
                 'bytesUsed',
                 'serviceResource.datacenter[name]',
-                'serviceResourceBackendIpAddress'
+                'serviceResourceBackendIpAddress',
+                'activeTransactionCount'
             ]
             kwargs['mask'] = ','.join(items)
 
@@ -88,6 +84,8 @@ def get_block_volume_details(self, volume_id, **kwargs):
                 'storageTierLevel',
                 'iops',
                 'lunId',
+                'activeTransactionCount',
+                'activeTransactions.transactionStatus[friendlyName]',
             ]
             kwargs['mask'] = ','.join(items)
         return self.client.call('Network_Storage', 'getObject',
@@ -120,16 +118,17 @@ def get_block_volume_snapshot_list(self, volume_id, **kwargs):
         :return: Returns a list of snapshots for the specified volume.
         """
         if 'mask' not in kwargs:
-            items = '''snapshots[
-    id,
-    notes,
-    snapshotSizeBytes,
-    storageType[keyName],
-    snapshotCreationTimestamp
-    hourlySchedule,
-    dailySchedule,
-    weeklySchedule
-]'''
+            items = [
+                'id',
+                'notes',
+                'snapshotSizeBytes',
+                'storageType[keyName]',
+                'snapshotCreationTimestamp',
+                'hourlySchedule',
+                'dailySchedule',
+                'weeklySchedule'
+                ]
+
             kwargs['mask'] = ','.join(items)
 
         return self.client.call('Network_Storage', 'getSnapshots',
@@ -138,76 +137,52 @@ def get_block_volume_snapshot_list(self, volume_id, **kwargs):
     def authorize_host_to_volume(self, volume_id,
                                  hardware_ids=None,
                                  virtual_guest_ids=None,
-                                 ip_address_ids=None):
+                                 ip_address_ids=None,
+                                 **kwargs):
         """Authorizes hosts to Block Storage Volumes
 
-        :param volume_id: The Volume to authorize hosts to
+        :param volume_id: The Block volume to authorize hosts to
         :param hardware_ids: A List of SoftLayer_Hardware ids
         :param virtual_guest_ids: A List of SoftLayer_Virtual_Guest ids
         :param ip_address_ids: A List of SoftLayer_Network_Subnet_IpAddress ids
+        :return: Returns an array of
+                SoftLayer_Network_Storage_Allowed_Host objects
+                which now have access to the given Block volume
         """
         host_templates = []
-
-        if hardware_ids is not None:
-            for hardware_id in hardware_ids:
-                host_templates.append({
-                    'objectType': 'SoftLayer_Hardware',
-                    'id': hardware_id
-                })
-
-        if virtual_guest_ids is not None:
-            for virtual_guest_id in virtual_guest_ids:
-                host_templates.append({
-                    'objectType': 'SoftLayer_Virtual_Guest',
-                    'id': virtual_guest_id
-                })
-
-        if ip_address_ids is not None:
-            for ip_address_id in ip_address_ids:
-                host_templates.append({
-                    'objectType': 'SoftLayer_Network_Subnet_IpAddress',
-                    'id': ip_address_id
-                })
+        storage_utils.populate_host_templates(host_templates,
+                                              hardware_ids,
+                                              virtual_guest_ids,
+                                              ip_address_ids,
+                                              None)
 
         return self.client.call('Network_Storage', 'allowAccessFromHostList',
-                                host_templates, id=volume_id)
+                                host_templates, id=volume_id, **kwargs)
 
     def deauthorize_host_to_volume(self, volume_id,
                                    hardware_ids=None,
                                    virtual_guest_ids=None,
-                                   ip_address_ids=None):
+                                   ip_address_ids=None,
+                                   **kwargs):
         """Revokes authorization of hosts to Block Storage Volumes
 
-        :param volume_id: The Volume to deauthorize hosts to
+        :param volume_id: The Block volume to deauthorize hosts to
         :param hardware_ids: A List of SoftLayer_Hardware ids
         :param virtual_guest_ids: A List of SoftLayer_Virtual_Guest ids
         :param ip_address_ids: A List of SoftLayer_Network_Subnet_IpAddress ids
+        :return: Returns an array of
+                SoftLayer_Network_Storage_Allowed_Host objects
+                which have access to the given Block volume
         """
         host_templates = []
-
-        if hardware_ids is not None:
-            for hardware_id in hardware_ids:
-                host_templates.append({
-                    'objectType': 'SoftLayer_Hardware',
-                    'id': hardware_id
-                })
-
-        if virtual_guest_ids is not None:
-            for virtual_guest_id in virtual_guest_ids:
-                host_templates.append({
-                    'objectType': 'SoftLayer_Virtual_Guest',
-                    'id': virtual_guest_id
-                })
-
-        if ip_address_ids is not None:
-            for ip_address_id in ip_address_ids:
-                host_templates.append({
-                    'objectType': 'SoftLayer_Network_Subnet_IpAddress',
-                    'id': ip_address_id
-                })
+        storage_utils.populate_host_templates(host_templates,
+                                              hardware_ids,
+                                              virtual_guest_ids,
+                                              ip_address_ids,
+                                              None)
 
         return self.client.call('Network_Storage', 'removeAccessFromHostList',
-                                host_templates, id=volume_id)
+                                host_templates, id=volume_id, **kwargs)
 
     def delete_snapshot(self, snapshot_id):
         """Deletes the specified snapshot object.
@@ -231,32 +206,43 @@ def order_block_volume(self, storage_type, location, size, os_type,
         """
 
         try:
-            location_id = self._get_location_id(location)
+            location_id = storage_utils.get_location_id(self, location)
         except ValueError:
             raise exceptions.SoftLayerError(
                 "Invalid datacenter name specified. "
                 "Please provide the lower case short name (e.g.: dal09)")
 
         base_type_name = 'SoftLayer_Container_Product_Order_Network_'
-        package = self._get_package(storage_type)
+        package = storage_utils.get_package(self, storage_type)
         if storage_type == 'performance_storage_iscsi':
             complex_type = base_type_name + 'PerformanceStorage_Iscsi'
             prices = [
-                _find_performance_block_price(package),
-                _find_performance_space_price(package, iops),
-                _find_performance_iops_price(package, size, iops),
+                storage_utils.find_performance_price(
+                    package,
+                    'performance_storage_iscsi'
+                    ),
+                storage_utils.find_performance_space_price(package, iops),
+                storage_utils.find_performance_iops_price(package, size, iops),
             ]
         elif storage_type == 'storage_service_enterprise':
             complex_type = base_type_name + 'Storage_Enterprise'
             prices = [
-                _find_endurance_block_price(package),
-                _find_endurance_price(package),
-                _find_endurance_space_price(package, size, tier_level),
-                _find_endurance_tier_price(package, tier_level),
+                storage_utils.find_endurance_price(package, 'storage_block'),
+                storage_utils.find_endurance_price(
+                    package,
+                    'storage_service_enterprise'
+                    ),
+                storage_utils.find_endurance_space_price(
+                    package,
+                    size,
+                    tier_level
+                    ),
+                storage_utils.find_endurance_tier_price(package, tier_level),
             ]
         else:
             raise exceptions.SoftLayerError(
-                "storage_type must be either Performance or Endurance")
+                "Block volume storage_type must be either "
+                "Performance or Endurance")
 
         order = {
             'complexType': complex_type,
@@ -269,209 +255,161 @@ def order_block_volume(self, storage_type, location, size, os_type,
 
         return self.client.call('Product_Order', 'placeOrder', order)
 
-    def _get_package(self, category_code):
-        """Returns a product packaged based on type of storage.
+    def create_snapshot(self, volume_id, notes='', **kwargs):
+        """Creates a snapshot on the given block volume.
 
-        :param category_code: Category code of product package.
-        :return: Returns a packaged based on type of storage.
+        :param integer volume_id: The id of the volume
+        :param string notes: The notes or "name" to assign the snapshot
+        :return: Returns the id of the new snapshot
         """
 
-        _filter = utils.NestedDict({})
-        _filter['categories']['categoryCode'] = (
-            utils.query_filter(category_code))
-        _filter['statusCode'] = (utils.query_filter('ACTIVE'))
-
-        packages = self.client.call('Product_Package', 'getAllObjects',
-                                    filter=_filter.to_dict(),
-                                    mask="""
-id,
-name,
-items[prices[categories],attributes]
-""")
-        if len(packages) == 0:
-            raise ValueError('No packages were found for %s' % category_code)
-        if len(packages) > 1:
-            raise ValueError('More than one package was found for %s'
-                             % category_code)
-
-        return packages[0]
-
-    def _get_location_id(self, location):
-        """Returns location id
-
-        :param location: Datacenter short name
-        :return: Returns location id
-        """
-        loc_svc = self.client['Location_Datacenter']
-        datacenters = loc_svc.getDatacenters(mask='mask[longName,id,name]')
-        for datacenter in datacenters:
-            if datacenter['name'] == location:
-                location = datacenter['id']
-                return location
-        raise ValueError('Invalid datacenter name specified.')
+        return self.client.call('Network_Storage', 'createSnapshot',
+                                notes, id=volume_id, **kwargs)
 
-    def cancel_block_volume(self, volume_id,
-                            reason='No longer needed',
-                            immediate=False):
-        """Cancels the given block storage volume.
+    def order_snapshot_space(self, volume_id, capacity, tier,
+                             upgrade, **kwargs):
+        """Orders snapshot space for the given block volume.
 
-        :param integer volume_id: the volume ID
-        :param string reason: The reason for cancellation
-        :param boolean immediate_flag: Cancel immediately or
-        on anniversary date
+        :param integer volume_id: The id of the volume
+        :param integer capacity: The capacity to order, in GB
+        :param float tier: The tier level of the block volume, in IOPS per GB
+        :param boolean upgrade: Flag to indicate if this order is an upgrade
+        :return: Returns a SoftLayer_Container_Product_Order_Receipt
         """
-        block_volume = self.get_block_volume_details(
-            volume_id,
-            mask='mask[id,billingItem[id]]')
-        billing_item_id = block_volume['billingItem']['id']
-
-        self.client['Billing_Item'].cancelItem(
-            immediate,
-            True,
-            reason,
-            id=billing_item_id)
-
-
-def _find_endurance_block_price(package):
-    for item in package['items']:
-        for price in item['prices']:
-            if price['locationGroupId'] != '':
-                continue
-
-            if not _has_category(price['categories'], 'storage_block'):
-                continue
-
-            return price
-
-    raise ValueError("Could not find price for block storage")
-
-
-def _find_endurance_price(package):
-    for item in package['items']:
-        for price in item['prices']:
-            if price['locationGroupId'] != '':
-                continue
-
-            if not _has_category(price['categories'],
-                                 'storage_service_enterprise'):
-                continue
-
-            return price
-
-    raise ValueError("Could not find price for endurance block storage")
-
-
-def _find_endurance_space_price(package, size, tier_level):
-    for item in package['items']:
-        if int(item['capacity']) != size:
-            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_space'):
-                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 disk space")
+        package = storage_utils.get_package(self, 'storage_service_enterprise')
+        block_mask = 'serviceResource.datacenter[id],'\
+            'storageTierLevel,billingItem'
+        block_volume = self.get_block_volume_details(volume_id,
+                                                     mask=block_mask,
+                                                     **kwargs)
+
+        storage_type = block_volume['billingItem']['categoryCode']
+        if storage_type != 'storage_service_enterprise':
+            raise exceptions.SoftLayerError(
+                "Block volume storage_type must be Endurance")
 
+        if tier is None:
+            tier = storage_utils.find_endurance_tier_iops_per_gb(block_volume)
+        prices = [storage_utils.find_snapshot_space_price(
+            package, capacity, tier)]
 
-def _find_endurance_tier_price(package, tier_level):
-    for item in package['items']:
-        for attribute in item.get('attributes', []):
-            if int(attribute['value']) == ENDURANCE_TIERS.get(tier_level):
-                break
+        if upgrade:
+            complex_type = 'SoftLayer_Container_Product_Order_'\
+                           'Network_Storage_Enterprise_SnapshotSpace_Upgrade'
         else:
-            continue
-
-        for price in item['prices']:
-            # Only collect prices from valid location groups.
-            if price['locationGroupId'] != '':
-                continue
-
-            if not _has_category(price['categories'], 'storage_tier_level'):
-                continue
-
-            return price
-
-    raise ValueError("Could not find price for tier")
-
+            complex_type = 'SoftLayer_Container_Product_Order_'\
+                           'Network_Storage_Enterprise_SnapshotSpace'
 
-def _find_performance_block_price(package):
-    for item in package['items']:
-        for price in item['prices']:
-            # Only collect prices from valid location groups.
-            if price['locationGroupId'] != '':
-                continue
-
-            if not _has_category(price['categories'],
-                                 'performance_storage_iscsi'):
-                continue
+        snapshot_space_order = {
+            'complexType': complex_type,
+            'packageId': package['id'],
+            'prices': prices,
+            'quantity': 1,
+            'location': block_volume['serviceResource']['datacenter']['id'],
+            'volumeId': volume_id,
+        }
 
-            return price
+        return self.client.call('Product_Order', 'placeOrder',
+                                snapshot_space_order)
 
-    raise ValueError("Could not find price for performance storage")
+    def cancel_snapshot_space(self, volume_id,
+                              reason='No longer needed',
+                              immediate=False):
+        """Cancels snapshot space for a given volume.
 
+        :param integer volume_id: The volume ID
+        :param string reason: The reason for cancellation
+        :param boolean immediate_flag: Cancel immediately or
+        on anniversary date
+        """
 
-def _find_performance_space_price(package, size):
-    for item in package['items']:
-        if int(item['capacity']) != size:
-            continue
+        block_volume = self.get_block_volume_details(
+            volume_id,
+            mask='mask[id,billingItem[activeChildren]]')
 
-        for price in item['prices']:
-            # Only collect prices from valid location groups.
-            if price['locationGroupId'] != '':
-                continue
+        if 'activeChildren' not in block_volume['billingItem']:
+            raise exceptions.SoftLayerError(
+                'No snapshot space found to cancel')
 
-            if not _has_category(price['categories'],
-                                 'performance_storage_space'):
-                continue
+        children_array = block_volume['billingItem']['activeChildren']
+        billing_item_id = None
 
-            return price
+        for child in children_array:
+            if child['categoryCode'] == 'storage_snapshot_space':
+                billing_item_id = child['id']
+                break
 
-    raise ValueError("Could not find price for disk space")
+        if not billing_item_id:
+            raise exceptions.SoftLayerError(
+                'No snapshot space found to cancel')
 
+        return self.client['Billing_Item'].cancelItem(
+            immediate,
+            True,
+            reason,
+            id=billing_item_id)
 
-def _find_performance_iops_price(package, size, iops):
-    for item in package['items']:
-        if int(item['capacity']) != int(iops):
-            continue
+    def enable_snapshots(self, volume_id, schedule_type, retention_count,
+                         minute, hour, day_of_week, **kwargs):
+        """Enables snapshots for a specific block volume at a given schedule
+
+        :param integer volume_id: The id of the volume
+        :param string schedule_type: 'HOURLY'|'DAILY'|'WEEKLY'
+        :param integer retention_count: Number of snapshots to be kept
+        :param integer minute: Minute when to take snapshot
+        :param integer hour: Hour when to take snapshot
+        :param string day_of_week: Day when to take snapshot
+        :return: Returns whether successfully scheduled or not
+        """
 
-        for price in item['prices']:
-            # Only collect prices from valid location groups.
-            if price['locationGroupId'] != '':
-                continue
+        return self.client.call('Network_Storage', 'enableSnapshots',
+                                schedule_type,
+                                retention_count,
+                                minute,
+                                hour,
+                                day_of_week,
+                                id=volume_id,
+                                **kwargs)
+
+    def disable_snapshots(self, volume_id, schedule_type):
+        """Disables snapshots for a specific block volume at a given schedule
+
+        :param integer volume_id: The id of the volume
+        :param string schedule_type: 'HOURLY'|'DAILY'|'WEEKLY'
+        :return: Returns whether successfully disabled or not
+        """
 
-            if not _has_category(price['categories'],
-                                 'performance_storage_iops'):
-                continue
+        return self.client.call('Network_Storage', 'disableSnapshots',
+                                schedule_type, id=volume_id)
 
-            if size < int(price['capacityRestrictionMinimum']):
-                continue
+    def restore_from_snapshot(self, volume_id, snapshot_id):
+        """Restores a specific volume from a snapshot
 
-            if size > int(price['capacityRestrictionMaximum']):
-                continue
+        :param integer volume_id: The id of the volume
+        :param integer snapshot_id: The id of the restore point
+        :return: Returns whether succesfully restored or not
+        """
 
-            return price
+        return self.client.call('Network_Storage', 'restoreFromSnapshot',
+                                snapshot_id, id=volume_id)
 
-    raise ValueError("Could not find price for iops")
+    def cancel_block_volume(self, volume_id,
+                            reason='No longer needed',
+                            immediate=False):
+        """Cancels the given block storage volume.
 
+        :param integer volume_id: The volume ID
+        :param string reason: The reason for cancellation
+        :param boolean immediate_flag: Cancel immediately or
+        on anniversary date
+        """
+        block_volume = self.get_block_volume_details(
+            volume_id,
+            mask='mask[id,billingItem[id]]')
+        billing_item_id = block_volume['billingItem']['id']
 
-def _has_category(categories, category_code):
-    return any(
-        True
-        for category
-        in categories
-        if category['categoryCode'] == category_code
-    )
+        return self.client['Billing_Item'].cancelItem(
+            immediate,
+            True,
+            reason,
+            id=billing_item_id)
diff --git a/SoftLayer/managers/file.py b/SoftLayer/managers/file.py
new file mode 100644
index 00000000..9759b1c5
--- /dev/null
+++ b/SoftLayer/managers/file.py
@@ -0,0 +1,413 @@
+"""
+    SoftLayer.file
+    ~~~~~~~~~~~~~~~
+    File Storage Manager
+
+    :license: MIT, see LICENSE for more details.
+"""
+from SoftLayer import exceptions
+from SoftLayer.managers import storage_utils
+from SoftLayer import utils
+
+
+class FileStorageManager(utils.IdentifierMixin, object):
+    """Manages file Storage volumes."""
+
+    def __init__(self, client):
+        self.configuration = {}
+        self.client = client
+
+    def list_file_volumes(self, datacenter=None, username=None,
+                          storage_type=None, **kwargs):
+        """Returns a list of file volumes.
+
+        :param datacenter: Datacenter short name (e.g.: dal09)
+        :param username: Name of volume.
+        :param storage_type: Type of volume: Endurance or Performance
+        :param kwargs:
+        :return: Returns a list of file volumes.
+        """
+        if 'mask' not in kwargs:
+            items = [
+                'id',
+                'username',
+                'capacityGb',
+                'bytesUsed',
+                'serviceResource.datacenter[name]',
+                'serviceResourceBackendIpAddress',
+                'activeTransactionCount'
+            ]
+            kwargs['mask'] = ','.join(items)
+
+        _filter = utils.NestedDict(kwargs.get('filter') or {})
+
+        _filter['nasNetworkStorage']['serviceResource']['type']['type'] = \
+            (utils.query_filter('!~ NAS'))
+
+        _filter['nasNetworkStorage']['storageType']['keyName'] = (
+            utils.query_filter('*FILE_STORAGE'))
+        if storage_type:
+            _filter['nasNetworkStorage']['storageType']['keyName'] = (
+                utils.query_filter('%s_FILE_STORAGE' % storage_type.upper()))
+
+        if datacenter:
+            _filter['nasNetworkStorage']['serviceResource']['datacenter'][
+                'name'] = (utils.query_filter(datacenter))
+
+        if username:
+            _filter['nasNetworkStorage']['username'] = \
+                (utils.query_filter(username))
+
+        kwargs['filter'] = _filter.to_dict()
+        return self.client.call('Account', 'getNasNetworkStorage', **kwargs)
+
+    def get_file_volume_details(self, volume_id, **kwargs):
+        """Returns details about the specified volume.
+
+        :param volume_id: ID of volume.
+        :param kwargs:
+        :return: Returns details about the specified volume.
+        """
+
+        if 'mask' not in kwargs:
+            items = [
+                'id',
+                'username',
+                'password',
+                'capacityGb',
+                'bytesUsed',
+                'snapshotCapacityGb',
+                'parentVolume.snapshotSizeBytes',
+                'storageType.keyName',
+                'serviceResource.datacenter[name]',
+                'serviceResourceBackendIpAddress',
+                'storageTierLevel',
+                'iops',
+                'lunId',
+                'activeTransactionCount',
+                'activeTransactions.transactionStatus[friendlyName]',
+            ]
+            kwargs['mask'] = ','.join(items)
+        return self.client.call('Network_Storage', 'getObject',
+                                id=volume_id, **kwargs)
+
+    def get_file_volume_access_list(self, volume_id, **kwargs):
+        """Returns a list of authorized hosts for a specified volume.
+
+        :param volume_id: ID of volume.
+        :param kwargs:
+        :return: Returns a list of authorized hosts for a specified volume.
+        """
+        if 'mask' not in kwargs:
+            items = [
+                'id',
+                'allowedVirtualGuests[allowedHost[credential]]',
+                'allowedHardware[allowedHost[credential]]',
+                'allowedSubnets[allowedHost[credential]]',
+                'allowedIpAddresses[allowedHost[credential]]',
+            ]
+            kwargs['mask'] = ','.join(items)
+        return self.client.call('Network_Storage', 'getObject',
+                                id=volume_id, **kwargs)
+
+    def get_file_volume_snapshot_list(self, volume_id, **kwargs):
+        """Returns a list of snapshots for the specified volume.
+
+        :param volume_id: ID of volume.
+        :param kwargs:
+        :return: Returns a list of snapshots for the specified volume.
+        """
+        if 'mask' not in kwargs:
+            items = [
+                'id',
+                'notes',
+                'snapshotSizeBytes',
+                'storageType[keyName]',
+                'snapshotCreationTimestamp',
+                'hourlySchedule',
+                'dailySchedule',
+                'weeklySchedule'
+                ]
+            kwargs['mask'] = ','.join(items)
+
+        return self.client.call('Network_Storage', 'getSnapshots',
+                                id=volume_id, **kwargs)
+
+    def authorize_host_to_volume(self, volume_id,
+                                 hardware_ids=None,
+                                 virtual_guest_ids=None,
+                                 ip_address_ids=None,
+                                 subnet_ids=None,
+                                 **kwargs):
+        """Authorizes hosts to File Storage Volumes
+
+        :param volume_id: The File volume to authorize hosts to
+        :param hardware_ids: A List of SoftLayer_Hardware ids
+        :param virtual_guest_ids: A List of SoftLayer_Virtual_Guest ids
+        :param ip_address_ids: A List of SoftLayer_Network_Subnet_IpAddress ids
+        :param subnet_ids: A List of SoftLayer_Network_Subnet ids
+        :return: Returns an array of
+                SoftLayer_Network_Storage_Allowed_Host objects
+                which now have access to the given File volume
+        """
+        host_templates = []
+        storage_utils.populate_host_templates(host_templates,
+                                              hardware_ids,
+                                              virtual_guest_ids,
+                                              ip_address_ids,
+                                              subnet_ids)
+
+        return self.client.call('Network_Storage', 'allowAccessFromHostList',
+                                host_templates, id=volume_id, **kwargs)
+
+    def deauthorize_host_to_volume(self, volume_id,
+                                   hardware_ids=None,
+                                   virtual_guest_ids=None,
+                                   ip_address_ids=None,
+                                   subnet_ids=None,
+                                   **kwargs):
+        """Revokes authorization of hosts to File Storage Volumes
+
+        :param volume_id: The File volume to deauthorize hosts to
+        :param hardware_ids: A List of SoftLayer_Hardware ids
+        :param virtual_guest_ids: A List of SoftLayer_Virtual_Guest ids
+        :param ip_address_ids: A List of SoftLayer_Network_Subnet_IpAddress ids
+        :param subnet_ids: A List of SoftLayer_Network_Subnet ids
+        :return: Returns an array of
+                SoftLayer_Network_Storage_Allowed_Host objects
+                which have access to the given File volume
+        """
+        host_templates = []
+        storage_utils.populate_host_templates(host_templates,
+                                              hardware_ids,
+                                              virtual_guest_ids,
+                                              ip_address_ids,
+                                              subnet_ids)
+
+        return self.client.call('Network_Storage', 'removeAccessFromHostList',
+                                host_templates, id=volume_id, **kwargs)
+
+    def delete_snapshot(self, snapshot_id):
+        """Deletes the specified snapshot object.
+
+        :param snapshot_id: The ID of the snapshot object to delete.
+        """
+        return self.client.call('Network_Storage', 'deleteObject',
+                                id=snapshot_id)
+
+    def order_file_volume(self, storage_type, location, size, os_type,
+                          iops=None, tier_level=None):
+        """Places an order for a file volume.
+
+        :param storage_type: "performance_storage_iscsi" (performance)
+                             or "storage_service_enterprise" (endurance)
+        :param location: Datacenter in which to order iSCSI volume
+        :param size: Size of the desired volume, in GB
+        :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
+        """
+
+        try:
+            location_id = storage_utils.get_location_id(self, location)
+        except ValueError:
+            raise exceptions.SoftLayerError(
+                "Invalid datacenter name specified. "
+                "Please provide the lower case short name (e.g.: dal09)")
+
+        base_type_name = 'SoftLayer_Container_Product_Order_Network_'
+        package = storage_utils.get_package(self, storage_type)
+        if storage_type == 'performance_storage_nfs':
+            complex_type = base_type_name + 'PerformanceStorage_Nfs'
+            prices = [
+                storage_utils.find_performance_price(
+                    package,
+                    'performance_storage_nfs'
+                    ),
+                storage_utils.find_performance_space_price(package, iops),
+                storage_utils.find_performance_iops_price(package, size, iops),
+            ]
+        elif storage_type == 'storage_service_enterprise':
+            complex_type = base_type_name + 'Storage_Enterprise'
+            prices = [
+                storage_utils.find_endurance_price(package, 'storage_file'),
+                storage_utils.find_endurance_price(
+                    package,
+                    'storage_service_enterprise'
+                    ),
+                storage_utils.find_endurance_space_price(
+                    package,
+                    size,
+                    tier_level
+                    ),
+                storage_utils.find_endurance_tier_price(package, tier_level),
+            ]
+        else:
+            raise exceptions.SoftLayerError(
+                "File volume storage_type must be either "
+                "Performance or Endurance")
+
+        order = {
+            'complexType': complex_type,
+            'packageId': package['id'],
+            'osFormatType': {'keyName': os_type},
+            'prices': prices,
+            'quantity': 1,
+            'location': location_id,
+        }
+
+        return self.client.call('Product_Order', 'placeOrder', order)
+
+    def create_snapshot(self, volume_id, notes='', **kwargs):
+        """Creates a snapshot on the given file volume.
+
+        :param integer volume_id: The id of the volume
+        :param string notes: The notes or "name" to assign the snapshot
+        :return: Returns the id of the new snapshot
+        """
+
+        return self.client.call('Network_Storage', 'createSnapshot',
+                                notes, id=volume_id, **kwargs)
+
+    def enable_snapshots(self, volume_id, schedule_type, retention_count,
+                         minute, hour, day_of_week, **kwargs):
+        """Enables snapshots for a specific file volume at a given schedule
+
+        :param integer volume_id: The id of the volume
+        :param string schedule_type: 'HOURLY'|'DAILY'|'WEEKLY'
+        :return: Returns whether successfully scheduled or not
+        """
+
+        return self.client.call('Network_Storage', 'enableSnapshots',
+                                schedule_type,
+                                retention_count,
+                                minute,
+                                hour,
+                                day_of_week,
+                                id=volume_id,
+                                **kwargs)
+
+    def disable_snapshots(self, volume_id, schedule_type):
+        """Disables snapshots for a specific file volume at a given schedule
+
+        :param integer volume_id: The id of the volume
+        :param string schedule_type: 'HOURLY'|'DAILY'|'WEEKLY'
+        :return: Returns whether successfully disabled or not
+        """
+
+        return self.client.call('Network_Storage', 'disableSnapshots',
+                                schedule_type, id=volume_id)
+
+    def order_snapshot_space(self, volume_id, capacity, tier,
+                             upgrade, **kwargs):
+        """Orders snapshot space for the given file volume.
+
+        :param integer volume_id: The id of the volume
+        :param integer capacity: The capacity to order, in GB
+        :param float tier: The tier level of the file volume, in IOPS per GB
+        :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'
+        file_volume = self.get_file_volume_details(volume_id,
+                                                   mask=file_mask,
+                                                   **kwargs)
+
+        storage_type = file_volume['billingItem']['categoryCode']
+        if storage_type != 'storage_service_enterprise':
+            raise exceptions.SoftLayerError(
+                "File volume storage_type must be Endurance")
+
+        if tier is None:
+            tier = storage_utils.find_endurance_tier_iops_per_gb(file_volume)
+        prices = [storage_utils.find_snapshot_space_price(
+            package, capacity, tier)]
+
+        if upgrade:
+            complex_type = 'SoftLayer_Container_Product_Order_'\
+                           'Network_Storage_Enterprise_SnapshotSpace_Upgrade'
+        else:
+            complex_type = 'SoftLayer_Container_Product_Order_'\
+                           'Network_Storage_Enterprise_SnapshotSpace'
+
+        snapshot_space_order = {
+            'complexType': complex_type,
+            'packageId': package['id'],
+            'prices': prices,
+            'quantity': 1,
+            'location': file_volume['serviceResource']['datacenter']['id'],
+            'volumeId': volume_id,
+        }
+
+        return self.client.call('Product_Order', 'placeOrder',
+                                snapshot_space_order)
+
+    def cancel_snapshot_space(self, volume_id,
+                              reason='No longer needed',
+                              immediate=False):
+        """Cancels snapshot space for a given volume.
+
+        :param integer volume_id: The volume ID
+        :param string reason: The reason for cancellation
+        :param boolean immediate_flag: Cancel immediately or
+        on anniversary date
+        """
+
+        file_volume = self.get_file_volume_details(
+            volume_id,
+            mask='mask[id,billingItem[activeChildren]]')
+        children_array = file_volume['billingItem']['activeChildren']
+        billing_item_id = None
+
+        if 'activeChildren' not in file_volume['billingItem']:
+            raise exceptions.SoftLayerError(
+                'No snapshot space found to cancel')
+
+        for child in children_array:
+            if child['categoryCode'] == 'storage_snapshot_space':
+                billing_item_id = child['id']
+                break
+
+        if not billing_item_id:
+            raise exceptions.SoftLayerError(
+                'No snapshot space found to cancel')
+
+        return self.client['Billing_Item'].cancelItem(
+            immediate,
+            True,
+            reason,
+            id=billing_item_id)
+
+    def restore_from_snapshot(self, volume_id, snapshot_id):
+        """Restores a specific volume from a snapshot
+
+        :param integer volume_id: The id of the volume
+        :param integer snapshot_id: The id of the restore point
+        :return: Returns whether succesfully restored or not
+        """
+
+        return self.client.call('Network_Storage', 'restoreFromSnapshot',
+                                snapshot_id, id=volume_id)
+
+    def cancel_file_volume(self, volume_id,
+                           reason='No longer needed',
+                           immediate=False):
+        """Cancels the given file storage volume.
+
+        :param integer volume_id: The volume ID
+        :param string reason: The reason for cancellation
+        :param boolean immediate_flag: Cancel immediately or
+        on anniversary date
+        """
+        file_volume = self.get_file_volume_details(
+            volume_id,
+            mask='mask[id,billingItem[id]]')
+        billing_item_id = file_volume['billingItem']['id']
+
+        return self.client['Billing_Item'].cancelItem(
+            immediate,
+            True,
+            reason,
+            id=billing_item_id)
diff --git a/SoftLayer/managers/storage_utils.py b/SoftLayer/managers/storage_utils.py
new file mode 100644
index 00000000..bb23703b
--- /dev/null
+++ b/SoftLayer/managers/storage_utils.py
@@ -0,0 +1,324 @@
+"""
+    SoftLayer.storage_utils
+    ~~~~~~~~~~~~~~~
+    Utility functions used by File and Block Storage Managers
+
+    :license: MIT, see LICENSE for more details.
+"""
+from SoftLayer import utils
+
+
+ENDURANCE_TIERS = {
+    0.25: 100,
+    2: 200,
+    4: 300,
+}
+
+
+def populate_host_templates(host_templates,
+                            hardware_ids=None,
+                            virtual_guest_ids=None,
+                            ip_address_ids=None,
+                            subnet_ids=None):
+    """Populate the given host_templates array with the IDs provided
+
+    :param host_templates: The array to which host templates will be added
+    :param hardware_ids: A List of SoftLayer_Hardware ids
+    :param virtual_guest_ids: A List of SoftLayer_Virtual_Guest ids
+    :param ip_address_ids: A List of SoftLayer_Network_Subnet_IpAddress ids
+    :param subnet_ids: A List of SoftLayer_Network_Subnet ids
+    """
+    if hardware_ids is not None:
+        for hardware_id in hardware_ids:
+            host_templates.append({
+                'objectType': 'SoftLayer_Hardware',
+                'id': hardware_id
+            })
+
+    if virtual_guest_ids is not None:
+        for virtual_guest_id in virtual_guest_ids:
+            host_templates.append({
+                'objectType': 'SoftLayer_Virtual_Guest',
+                'id': virtual_guest_id
+            })
+
+    if ip_address_ids is not None:
+        for ip_address_id in ip_address_ids:
+            host_templates.append({
+                'objectType': 'SoftLayer_Network_Subnet_IpAddress',
+                'id': ip_address_id
+            })
+
+    if subnet_ids is not None:
+        for subnet_id in subnet_ids:
+            host_templates.append({
+                'objectType': 'SoftLayer_Network_Subnet',
+                'id': subnet_id
+            })
+
+
+def get_package(manager, category_code):
+    """Returns a product packaged based on type of storage.
+
+    :param manager: The storage manager which calls this function.
+    :param category_code: Category code of product package.
+    :return: Returns a packaged based on type of storage.
+    """
+
+    _filter = utils.NestedDict({})
+    _filter['categories']['categoryCode'] = (
+        utils.query_filter(category_code))
+    _filter['statusCode'] = (utils.query_filter('ACTIVE'))
+
+    packages = manager.client.call(
+        'Product_Package', 'getAllObjects',
+        filter=_filter.to_dict(),
+        mask='id,name,items[prices[categories],attributes]'
+    )
+    if len(packages) == 0:
+        raise ValueError('No packages were found for %s' % category_code)
+    if len(packages) > 1:
+        raise ValueError('More than one package was found for %s'
+                         % category_code)
+
+    return packages[0]
+
+
+def get_location_id(manager, location):
+    """Returns location id
+
+    :param manager: The storage manager which calls this function.
+    :param location: Datacenter short name
+    :return: Returns location id
+    """
+    loc_svc = manager.client['Location_Datacenter']
+    datacenters = loc_svc.getDatacenters(mask='mask[longName,id,name]')
+    for datacenter in datacenters:
+        if datacenter['name'] == location:
+            location = datacenter['id']
+            return location
+    raise ValueError('Invalid datacenter name specified.')
+
+
+def find_endurance_price(package, price_category):
+    """Find the price in the given package that has the specified category
+
+    :param package: The product package of the endurance storage type
+    :param price_category: The price category to search for
+    :return: Returns the price for the given category, or an error if not found
+    """
+    for item in package['items']:
+        for price in item['prices']:
+            # Only collect prices from valid location groups.
+            if price['locationGroupId'] != '':
+                continue
+
+            if not _has_category(price['categories'], price_category):
+                continue
+
+            return price
+
+    raise ValueError("Could not find price for endurance storage")
+
+
+def find_endurance_space_price(package, size, tier_level):
+    """Find the price in the given package with the specified size and tier
+
+    :param package: The product package of the endurance storage type
+    :param size: The size for which a price is desired
+    :param tier_level: The endurance tier for which a price is desired
+    :return: Returns the price for the size and tier, or an error if not found
+    """
+    for item in package['items']:
+        if int(item['capacity']) != size:
+            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_space'):
+                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 disk space")
+
+
+def find_endurance_tier_price(package, tier_level):
+    """Find the price in the given package with the specified tier level
+
+    :param package: The product package of the endurance storage type
+    :param tier_level: The endurance tier for which a price is desired
+    :return: Returns the price for the given tier, or an error if not found
+    """
+    for item in package['items']:
+        for attribute in item.get('attributes', []):
+            if int(attribute['value']) == ENDURANCE_TIERS.get(tier_level):
+                break
+        else:
+            continue
+
+        for price in item['prices']:
+            # Only collect prices from valid location groups.
+            if price['locationGroupId'] != '':
+                continue
+
+            if not _has_category(price['categories'], 'storage_tier_level'):
+                continue
+
+            return price
+
+    raise ValueError("Could not find price for tier")
+
+
+def find_endurance_tier_iops_per_gb(volume):
+    """Find the tier for the given endurance volume (IOPS per GB)
+
+    :param volume: The volume for which the tier level is desired
+    :return: Returns a float value indicating the IOPS per GB for the volume
+    """
+    tier_description_split = volume['storageTierLevel']['description'].split()
+
+    if tier_description_split != []:
+        iops_per_gb = tier_description_split[0]
+
+        if iops_per_gb == '0.25':
+            return 0.25
+
+        if iops_per_gb == '2':
+            return 2.0
+
+        if iops_per_gb == '4':
+            return 4.0
+
+    raise ValueError("Could not find tier IOPS per GB for this volume")
+
+
+def find_performance_price(package, price_category):
+    """Find the price in the given package that has the specified category
+
+    :param package: The product package of the performance storage type
+    :param price_category: The price category to search for
+    :return: Returns the price for the given category, or an error if not found
+    """
+    for item in package['items']:
+        for price in item['prices']:
+            # Only collect prices from valid location groups.
+            if price['locationGroupId'] != '':
+                continue
+
+            if not _has_category(price['categories'], price_category):
+                continue
+
+            return price
+
+    raise ValueError("Could not find price for performance storage")
+
+
+def find_performance_space_price(package, size):
+    """Find the price in the given package with the specified size
+
+    :param package: The product package of the performance storage type
+    :param size: The size for which a price is desired
+    :return: Returns the price for the given size, or an error if not found
+    """
+    for item in package['items']:
+        if int(item['capacity']) != size:
+            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_space'):
+                continue
+
+            return price
+
+    raise ValueError("Could not find disk space price for the given volume")
+
+
+def find_performance_iops_price(package, size, iops):
+    """Find the price in the given package with the specified size and iops
+
+    :param package: The product package of the performance storage type
+    :param size: The size for which a price is desired
+    :param iops: The number of IOPS for which a price is desired
+    :return: Returns the price for the size and IOPS, or an error if not found
+    """
+    for item in package['items']:
+        if int(item['capacity']) != int(iops):
+            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_iops'):
+                continue
+
+            if size < int(price['capacityRestrictionMinimum']):
+                continue
+
+            if size > int(price['capacityRestrictionMaximum']):
+                continue
+
+            return price
+
+    raise ValueError("Could not find price for iops for the given volume")
+
+
+def find_snapshot_space_price(package, size, tier_level):
+    """Find the price in the given package for the desired snapshot space size
+
+    :param package: The product package of the endurance storage type
+    :param size: The snapshot space size for which a price is desired
+    :param tier_level: The tier of the volume for which space is being ordered
+    :return: Returns the price for the given size, or an error if not found
+    """
+    for item in package['items']:
+        if int(item['capacity']) != size:
+            continue
+
+        for price in item['prices']:
+            # Only collect prices from valid location groups.
+            if price['locationGroupId'] != '':
+                continue
+
+            if not _has_category(price['categories'],
+                                 'storage_snapshot_space'):
+                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 snapshot space")
+
+
+def _has_category(categories, category_code):
+    return any(
+        True
+        for category
+        in categories
+        if category['categoryCode'] == category_code
+    )


Test patch:
diff --git a/tests/CLI/modules/block_tests.py b/tests/CLI/modules/block_tests.py
index 98f13f44..b413a695 100644
--- a/tests/CLI/modules/block_tests.py
+++ b/tests/CLI/modules/block_tests.py
@@ -7,6 +7,7 @@
 from SoftLayer import testing
 
 import json
+import mock
 
 
 class BlockTests(testing.TestCase):
@@ -59,18 +60,18 @@ def test_volume_cancel(self):
             '--really', 'block', 'volume-cancel', '1234'])
 
         self.assert_no_fail(result)
-        self.assertEqual("", result.output)
+        self.assertEqual('Block volume with id 1234 has been marked'
+                         ' for cancellation\n', result.output)
         self.assert_called_with('SoftLayer_Billing_Item', 'cancelItem',
                                 args=(False, True, None))
 
     def test_volume_detail(self):
         result = self.run_command(['block', 'volume-detail', '1234'])
-
         self.assert_no_fail(result)
         self.assertEqual({
             'Username': 'username',
             'LUN Id': '2',
-            'Endurance Tier': 'Tier 1',
+            'Endurance Tier': '2 IOPS per GB',
             'IOPs': 1000,
             'Snapshot Capacity (GB)': 10,
             'Snapshot Used (Bytes)': 1024,
@@ -79,6 +80,7 @@ def test_volume_detail(self):
             'Data Center': 'dal05',
             'Type': 'ENDURANCE',
             'ID': 100,
+            '# of Active Transactions': '0'
         }, json.loads(result.output))
 
     def test_volume_list(self):
@@ -92,7 +94,117 @@ def test_volume_list(self):
                 'datacenter': None,
                 'id': 100,
                 'ip_addr': '10.1.2.3',
+                'lunId': None,
                 'storage_type': 'ENDURANCE',
-                'username': 'username'
+                'username': 'username',
+                'active_transactions': None
+            }],
+            json.loads(result.output))
+
+    @mock.patch('SoftLayer.BlockStorageManager.order_block_volume')
+    def test_volume_order(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'},
+                          ]
+                }
+        }
+
+        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 #478 placed successfully!\n'
+                         ' > Endurance Storage\n > Block Storage\n'
+                         ' > 0.25 IOPS per GB\n > 20 GB Storage Space\n')
+
+    def test_enable_snapshots(self):
+        result = self.run_command(['block', 'snapshot-enable', '12345678',
+                                   '--schedule-type=HOURLY', '--minute=10',
+                                   '--retention-count=5'])
+
+        self.assert_no_fail(result)
+
+    def test_disable_snapshots(self):
+        result = self.run_command(['block', 'snapshot-disable', '12345678',
+                                   '--schedule-type=HOURLY'])
+        self.assert_no_fail(result)
+
+    def test_create_snapshot(self):
+        result = self.run_command(['block', 'snapshot-create', '12345678'])
+
+        self.assert_no_fail(result)
+
+    def test_snapshot_list(self):
+        result = self.run_command(['block', 'snapshot-list', '12345678'])
+
+        self.assert_no_fail(result)
+        self.assertEqual([
+            {
+                'id': 470,
+                'name': 'unit_testing_note',
+                'created': '2016-07-06T07:41:19-05:00',
+                'size_bytes': '42',
             }],
             json.loads(result.output))
+
+    def test_snapshot_cancel(self):
+        result = self.run_command(['--really',
+                                   'block', 'snapshot-cancel', '1234'])
+
+        self.assert_no_fail(result)
+        self.assertEqual('Block volume with id 1234 has been marked'
+                         ' for snapshot cancellation\n', result.output)
+        self.assert_called_with('SoftLayer_Billing_Item', 'cancelItem',
+                                args=(False, True, None))
+
+    def test_snapshot_restore(self):
+        result = self.run_command(['block', 'snapshot-restore', '12345678',
+                                   '--snapshot-id=87654321'])
+
+        self.assert_no_fail(result)
+        self.assertEqual(result.output, 'Block volume 12345678 is being'
+                         ' restored using snapshot 87654321\n')
+
+    @mock.patch('SoftLayer.BlockStorageManager.order_snapshot_space')
+    def test_snapshot_order(self, order_mock):
+        order_mock.return_value = {
+            'placedOrder': {
+                'id': 8702,
+                'items': [{'description':
+                           '10 GB Storage Space (Snapshot Space)'}],
+                'status': 'PENDING_APPROVAL',
+                }
+        }
+
+        result = self.run_command(['block', 'snapshot-order', '1234',
+                                   '--capacity=10', '--tier=0.25'])
+
+        self.assert_no_fail(result)
+        self.assertEqual(result.output,
+                         'Order #8702 placed successfully!\n'
+                         ' > 10 GB Storage Space (Snapshot Space)\n'
+                         ' > Order status: PENDING_APPROVAL\n')
+
+    def test_authorize_host_to_volume(self):
+        result = self.run_command(['block', 'access-authorize', '12345678',
+                                   '--hardware-id=100', '--virtual-id=10',
+                                   '--ip-address-id=192',
+                                   '--ip-address=192.3.2.1'])
+
+        self.assert_no_fail(result)
+
+    def test_deauthorize_host_to_volume(self):
+        result = self.run_command(['block', 'access-revoke', '12345678',
+                                   '--hardware-id=100', '--virtual-id=10',
+                                   '--ip-address-id=192',
+                                   '--ip-address=192.3.2.1'])
+
+        self.assert_no_fail(result)
diff --git a/tests/CLI/modules/file_tests.py b/tests/CLI/modules/file_tests.py
new file mode 100644
index 00000000..6f509d57
--- /dev/null
+++ b/tests/CLI/modules/file_tests.py
@@ -0,0 +1,217 @@
+"""
+    SoftLayer.tests.CLI.modules.file_tests
+    ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+    :license: MIT, see LICENSE for more details.
+"""
+from SoftLayer import testing
+
+import json
+import mock
+
+
+class FileTests(testing.TestCase):
+
+    def test_access_list(self):
+        result = self.run_command(['file', 'access-list', '1234'])
+
+        self.assert_no_fail(result)
+        self.assertEqual([
+            {
+                'username': 'joe',
+                'name': 'test-server.example.com',
+                'type': 'VIRTUAL',
+                'host_iqn': 'test-server',
+                'password': '12345',
+                'private_ip_address': '10.0.0.1',
+                'id': 1234,
+            },
+            {
+                'username': 'joe',
+                'name': 'test-server.example.com',
+                'type': 'HARDWARE',
+                'host_iqn': 'test-server',
+                'password': '12345',
+                'private_ip_address': '10.0.0.2',
+                'id': 1234,
+            },
+            {
+                'username': 'joe',
+                'name': '10.0.0.1/24 (backend subnet)',
+                'type': 'SUBNET',
+                'host_iqn': 'test-server',
+                'password': '12345',
+                'private_ip_address': None,
+                'id': 1234,
+            },
+            {
+                'username': 'joe',
+                'name': '10.0.0.1 (backend ip)',
+                'type': 'IP',
+                'host_iqn': 'test-server',
+                'password': '12345',
+                'private_ip_address': None,
+                'id': 1234,
+            }],
+            json.loads(result.output),)
+
+    def test_authorize_host_to_volume(self):
+        result = self.run_command(['file', 'access-authorize', '12345678',
+                                   '--hardware-id=100', '--virtual-id=10',
+                                   '--ip-address-id=192',
+                                   '--ip-address=192.3.2.1',
+                                   '--subnet-id=200'])
+
+        self.assert_no_fail(result)
+
+    def test_deauthorize_host_to_volume(self):
+        result = self.run_command(['file', 'access-revoke', '12345678',
+                                   '--hardware-id=100', '--virtual-id=10',
+                                   '--ip-address-id=192',
+                                   '--ip-address=192.3.2.1',
+                                   '--subnet-id=200'])
+
+        self.assert_no_fail(result)
+
+    def test_volume_list(self):
+        result = self.run_command(['file', 'volume-list'])
+
+        self.assert_no_fail(result)
+        self.assertEqual([
+            {
+                'bytes_used': None,
+                'capacity_gb': 10,
+                'datacenter': 'Dallas',
+                'id': 1,
+                'ip_addr': '127.0.0.1',
+                'storage_type': 'ENDURANCE',
+                'username': 'user',
+                'active_transactions': None
+            }],
+            json.loads(result.output))
+
+    def test_snapshot_list(self):
+        result = self.run_command(['file', 'snapshot-list', '1234'])
+
+        self.assert_no_fail(result)
+        self.assertEqual([
+            {
+                'id': 470,
+                'name': 'unit_testing_note',
+                'created': '2016-07-06T07:41:19-05:00',
+                'size_bytes': '42',
+            }],
+            json.loads(result.output))
+
+    def test_volume_cancel(self):
+        result = self.run_command([
+            '--really', 'file', 'volume-cancel', '1234'])
+
+        self.assert_no_fail(result)
+        self.assertEqual('File volume with id 1234 has been marked'
+                         ' for cancellation\n', result.output)
+        self.assert_called_with('SoftLayer_Billing_Item', 'cancelItem',
+                                args=(False, True, None))
+
+    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))
+
+    @mock.patch('SoftLayer.FileStorageManager.order_file_volume')
+    def test_volume_order(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'},
+                          ]
+                }
+        }
+
+        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 #478 placed successfully!\n'
+                         ' > Endurance Storage\n > File Storage\n'
+                         ' > 0.25 IOPS per GB\n > 20 GB Storage Space\n')
+
+    def test_enable_snapshots(self):
+        result = self.run_command(['file', 'snapshot-enable', '12345678',
+                                   '--schedule-type=HOURLY', '--minute=10',
+                                   '--retention-count=5'])
+
+        self.assert_no_fail(result)
+
+    def test_disable_snapshots(self):
+        result = self.run_command(['file', 'snapshot-disable', '12345678',
+                                   '--schedule-type=HOURLY'])
+        self.assert_no_fail(result)
+
+    def test_create_snapshot(self):
+        result = self.run_command(['file', 'snapshot-create', '12345678'])
+
+        self.assert_no_fail(result)
+        self.assertEqual('New snapshot created with id: 449\n', result.output)
+
+    def test_snapshot_restore(self):
+        result = self.run_command(['file', 'snapshot-restore', '12345678',
+                                   '--snapshot-id=87654321'])
+
+        self.assert_no_fail(result)
+        self.assertEqual(result.output, 'File volume 12345678 is being'
+                         ' restored using snapshot 87654321\n')
+
+    def test_delete_snapshot(self):
+        result = self.run_command(['file', 'snapshot-delete', '12345678'])
+
+        self.assert_no_fail(result)
+
+    @mock.patch('SoftLayer.FileStorageManager.order_snapshot_space')
+    def test_snapshot_order(self, order_mock):
+        order_mock.return_value = {
+            'placedOrder': {
+                'id': 8702,
+                'items': [{'description':
+                           '10 GB Storage Space (Snapshot Space)'}],
+                'status': 'PENDING_APPROVAL',
+                }
+        }
+
+        result = self.run_command(['file', 'snapshot-order', '1234',
+                                   '--capacity=10', '--tier=0.25'])
+
+        self.assert_no_fail(result)
+        self.assertEqual(result.output,
+                         'Order #8702 placed successfully!\n'
+                         ' > 10 GB Storage Space (Snapshot Space)\n'
+                         ' > Order status: PENDING_APPROVAL\n')
+
+    def test_snapshot_cancel(self):
+        result = self.run_command(['--really',
+                                   'file', 'snapshot-cancel', '1234'])
+
+        self.assert_no_fail(result)
+        self.assertEqual('File volume with id 1234 has been marked'
+                         ' for snapshot cancellation\n', result.output)
+        self.assert_called_with('SoftLayer_Billing_Item', 'cancelItem',
+                                args=(False, True, None))
diff --git a/tests/managers/block_tests.py b/tests/managers/block_tests.py
index e278cf32..45337478 100644
--- a/tests/managers/block_tests.py
+++ b/tests/managers/block_tests.py
@@ -17,48 +17,63 @@ def set_up(self):
     def test_cancel_block_volume_immediately(self):
         self.block.cancel_block_volume(123, immediate=True)
 
-        self.assert_called_with('SoftLayer_Billing_Item', 'cancelItem',
-                                args=(True, True, 'No longer needed'),
-                                identifier=449)
+        self.assert_called_with(
+            'SoftLayer_Billing_Item',
+            'cancelItem',
+            args=(True, True, 'No longer needed'),
+            identifier=449,
+        )
 
     def test_get_block_volume_details(self):
         result = self.block.get_block_volume_details(100)
 
-        self.assertEqual(fixtures.SoftLayer_Network_Storage.getObject,
-                         result)
-        self.assert_called_with('SoftLayer_Network_Storage', 'getObject',
-                                identifier=100)
+        self.assertEqual(fixtures.SoftLayer_Network_Storage.getObject, result)
+
+        self.assert_called_with(
+            'SoftLayer_Network_Storage',
+            'getObject',
+            identifier=100
+            )
 
     def test_list_block_volumes(self):
         result = self.block.list_block_volumes()
 
         self.assertEqual(fixtures.SoftLayer_Account.getIscsiNetworkStorage,
                          result)
+
         self.assert_called_with('SoftLayer_Account', 'getIscsiNetworkStorage')
 
     def test_get_block_volume_access_list(self):
         result = self.block.get_block_volume_access_list(100)
 
-        self.assertEqual(fixtures.SoftLayer_Network_Storage.getObject,
-                         result)
-        self.assert_called_with('SoftLayer_Network_Storage', 'getObject',
-                                identifier=100)
+        self.assertEqual(fixtures.SoftLayer_Network_Storage.getObject, result)
+
+        self.assert_called_with(
+            'SoftLayer_Network_Storage',
+            'getObject',
+            identifier=100)
 
     def test_get_block_volume_snapshot_list(self):
         result = self.block.get_block_volume_snapshot_list(100)
 
         self.assertEqual(fixtures.SoftLayer_Network_Storage.getSnapshots,
                          result)
-        self.assert_called_with('SoftLayer_Network_Storage', 'getSnapshots',
-                                identifier=100)
+
+        self.assert_called_with(
+            'SoftLayer_Network_Storage',
+            'getSnapshots',
+            identifier=100)
 
     def test_delete_snapshot(self):
         result = self.block.delete_snapshot(100)
 
         self.assertEqual(fixtures.SoftLayer_Network_Storage.deleteObject,
                          result)
-        self.assert_called_with('SoftLayer_Network_Storage', 'deleteObject',
-                                identifier=100)
+
+        self.assert_called_with(
+            'SoftLayer_Network_Storage',
+            'deleteObject',
+            identifier=100)
 
     def test_order_block_volume_no_package(self):
         mock = self.set_mock('SoftLayer_Product_Package', 'getAllObjects')
@@ -67,7 +82,11 @@ def test_order_block_volume_no_package(self):
         self.assertRaises(
             ValueError,
             self.block.order_block_volume,
-            "performance_storage_iscsi", "dal05", 100, "LINUX", iops=100,
+            "performance_storage_iscsi",
+            "dal05",
+            100,
+            "LINUX",
+            iops=100,
         )
 
     def test_order_block_volume_too_many_packages(self):
@@ -77,7 +96,21 @@ def test_order_block_volume_too_many_packages(self):
         self.assertRaises(
             ValueError,
             self.block.order_block_volume,
-            "performance_storage_iscsi", "dal05", 100, "LINUX", iops=100,
+            "performance_storage_iscsi",
+            "dal05",
+            100,
+            "LINUX",
+            iops=100,
+        )
+
+    def test_cancel_snapshot_immediately(self):
+        self.block.cancel_snapshot_space(1234, immediate=True)
+
+        self.assert_called_with(
+            'SoftLayer_Billing_Item',
+            'cancelItem',
+            args=(True, True, 'No longer needed'),
+            identifier=123,
         )
 
     def test_order_block_volume_performance(self):
@@ -118,7 +151,12 @@ def test_order_block_volume_performance(self):
         }]
 
         result = self.block.order_block_volume(
-            "performance_storage_iscsi", "dal05", 100, "LINUX", iops=100)
+            "performance_storage_iscsi",
+            "dal05",
+            100,
+            "LINUX",
+            iops=100
+            )
 
         self.assertEqual(
             result,
@@ -189,8 +227,12 @@ def test_order_block_volume_endurance(self):
         }]
 
         result = self.block.order_block_volume(
-            "storage_service_enterprise", "dal05", 100, "LINUX",
-            tier_level=0.25)
+            "storage_service_enterprise",
+            "dal05",
+            100,
+            "LINUX",
+            tier_level=0.25
+            )
 
         self.assertEqual(
             result,
@@ -212,17 +254,180 @@ def test_order_block_volume_endurance(self):
             )
 
     def test_authorize_host_to_volume(self):
-        result = self.block.authorize_host_to_volume(50,
-                                                     hardware_ids=[100],
-                                                     virtual_guest_ids=[200],
-                                                     ip_address_ids=[300])
+        result = self.block.authorize_host_to_volume(
+            50,
+            hardware_ids=[100],
+            virtual_guest_ids=[200],
+            ip_address_ids=[300])
+
         self.assertEqual(fixtures.SoftLayer_Network_Storage.
                          allowAccessFromHostList, result)
 
+        self.assert_called_with(
+            'SoftLayer_Network_Storage',
+            'allowAccessFromHostList',
+            identifier=50)
+
     def test_deauthorize_host_to_volume(self):
-        result = self.block.authorize_host_to_volume(50,
-                                                     hardware_ids=[100],
-                                                     virtual_guest_ids=[200],
-                                                     ip_address_ids=[300])
+        result = self.block.deauthorize_host_to_volume(
+            50,
+            hardware_ids=[100],
+            virtual_guest_ids=[200],
+            ip_address_ids=[300])
+
         self.assertEqual(fixtures.SoftLayer_Network_Storage.
                          removeAccessFromHostList, result)
+
+        self.assert_called_with(
+            'SoftLayer_Network_Storage',
+            'removeAccessFromHostList',
+            identifier=50)
+
+    def test_create_snapshot(self):
+        result = self.block.create_snapshot(123, 'hello world')
+
+        self.assertEqual(fixtures.SoftLayer_Network_Storage.createSnapshot,
+                         result)
+
+        self.assert_called_with(
+            'SoftLayer_Network_Storage',
+            'createSnapshot',
+            identifier=123)
+
+    def test_snapshot_restore(self):
+        result = self.block.restore_from_snapshot(12345678, 87654321)
+
+        self.assertEqual(
+            fixtures.SoftLayer_Network_Storage.restoreFromSnapshot,
+            result)
+        self.assert_called_with(
+            'SoftLayer_Network_Storage',
+            'restoreFromSnapshot',
+            identifier=12345678)
+
+    def test_enable_snapshots(self):
+        result = self.block.enable_snapshots(12345678, 'WEEKLY', 10,
+                                             47, 16, 'FRIDAY')
+
+        self.assertEqual(fixtures.SoftLayer_Network_Storage.enableSnapshots,
+                         result)
+
+        self.assert_called_with(
+            'SoftLayer_Network_Storage',
+            'enableSnapshots',
+            identifier=12345678)
+
+    def test_disable_snapshots(self):
+        result = self.block.disable_snapshots(12345678, 'HOURLY')
+
+        self.assertEqual(fixtures.SoftLayer_Network_Storage.disableSnapshots,
+                         result)
+        self.assert_called_with(
+            'SoftLayer_Network_Storage',
+            'disableSnapshots',
+            identifier=12345678)
+
+    def test_order_snapshot_space_no_package(self):
+        mock = self.set_mock('SoftLayer_Product_Package', 'getAllObjects')
+        mock.return_value = []
+
+        self.assertRaises(
+            ValueError,
+            self.block.order_snapshot_space,
+            100,
+            5,
+            None,
+            False,
+        )
+
+    def test_order_snapshot_space_too_many_packages(self):
+        mock = self.set_mock('SoftLayer_Product_Package', 'getAllObjects')
+        mock.return_value = [{}, {}]
+
+        self.assertRaises(
+            ValueError,
+            self.block.order_snapshot_space,
+            100,
+            5,
+            None,
+            False,
+        )
+
+    def test_order_snapshot_space(self):
+        mock = self.set_mock('SoftLayer_Product_Package', 'getAllObjects')
+        mock.return_value = [{
+            'id': 240,
+            'name': 'Endurance',
+            'items': [{
+                'capacity': '0',
+            }, {
+                'capacity': '5',
+                'prices': [{
+                    'locationGroupId': '530',
+                    }],
+            }, {
+                'capacity': '5',
+                'prices': [{
+                    'locationGroupId': '',
+                    'categories': [{
+                        'categoryCode': 'storage_block',
+                    }],
+                }],
+            }, {
+                'capacity': '5',
+                'prices': [{
+                    'locationGroupId': '',
+                    'categories': [{
+                        'categoryCode': 'storage_snapshot_space',
+                    }],
+                    'capacityRestrictionMinimum': '300',
+                }],
+            }, {
+                'capacity': '5',
+                'prices': [{
+                    'locationGroupId': '',
+                    'categories': [{
+                        'categoryCode': 'storage_snapshot_space',
+                    }],
+                    'capacityRestrictionMinimum': '100',
+                    'capacityRestrictionMaximum': '100',
+                }],
+            }, {
+                'capacity': '5',
+                'prices': [{
+                    'id': 46130,
+                    'locationGroupId': '',
+                    'categories': [{
+                        'categoryCode': 'storage_snapshot_space',
+                    }],
+                    'capacityRestrictionMinimum': '200',
+                    'capacityRestrictionMaximum': '200',
+                }],
+            }],
+        }]
+
+        result = self.block.order_snapshot_space(
+            100,
+            5,
+            None,
+            False,
+            )
+
+        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
new file mode 100644
index 00000000..7187a7d8
--- /dev/null
+++ b/tests/managers/file_tests.py
@@ -0,0 +1,434 @@
+"""
+    SoftLayer.tests.managers.file_tests
+    ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+    :license: MIT, see LICENSE for more details.
+"""
+
+import SoftLayer
+from SoftLayer import fixtures
+from SoftLayer import testing
+
+
+class FileTests(testing.TestCase):
+    def set_up(self):
+        self.file = SoftLayer.FileStorageManager(self.client)
+
+    def test_cancel_file_volume_immediately(self):
+        self.file.cancel_file_volume(123, immediate=True)
+
+        self.assert_called_with(
+            'SoftLayer_Billing_Item',
+            'cancelItem',
+            args=(True, True, 'No longer needed'),
+            identifier=449,
+        )
+
+    def test_authorize_host_to_volume(self):
+        result = self.file.authorize_host_to_volume(
+            50,
+            hardware_ids=[100],
+            virtual_guest_ids=[200],
+            ip_address_ids=[300],
+            subnet_ids=[400])
+
+        self.assertEqual(fixtures.SoftLayer_Network_Storage.
+                         allowAccessFromHostList, result)
+
+        self.assert_called_with(
+            'SoftLayer_Network_Storage',
+            'allowAccessFromHostList',
+            identifier=50)
+
+    def test_deauthorize_host_to_volume(self):
+        result = self.file.deauthorize_host_to_volume(
+            50,
+            hardware_ids=[100],
+            virtual_guest_ids=[200],
+            ip_address_ids=[300],
+            subnet_ids=[400])
+
+        self.assertEqual(fixtures.SoftLayer_Network_Storage.
+                         removeAccessFromHostList, result)
+
+        self.assert_called_with(
+            'SoftLayer_Network_Storage',
+            'removeAccessFromHostList',
+            identifier=50)
+
+    def test_get_file_volume_access_list(self):
+        result = self.file.get_file_volume_access_list(100)
+
+        self.assertEqual(fixtures.SoftLayer_Network_Storage.getObject, result)
+
+        self.assert_called_with(
+            'SoftLayer_Network_Storage',
+            'getObject',
+            identifier=100)
+
+    def test_enable_snapshots(self):
+        result = self.file.enable_snapshots(12345678, 'WEEKLY', 10,
+                                            47, 16, 'FRIDAY')
+
+        self.assertEqual(fixtures.SoftLayer_Network_Storage.enableSnapshots,
+                         result)
+
+        self.assert_called_with(
+            'SoftLayer_Network_Storage',
+            'enableSnapshots',
+            identifier=12345678)
+
+    def test_disable_snapshots(self):
+        result = self.file.disable_snapshots(12345678, 'HOURLY')
+
+        self.assertEqual(fixtures.SoftLayer_Network_Storage.disableSnapshots,
+                         result)
+        self.assert_called_with(
+            'SoftLayer_Network_Storage',
+            'disableSnapshots',
+            identifier=12345678)
+
+    def test_snapshot_restore(self):
+        result = self.file.restore_from_snapshot(12345678, 87654321)
+
+        self.assertEqual(
+            fixtures.SoftLayer_Network_Storage.restoreFromSnapshot,
+            result)
+        self.assert_called_with(
+            'SoftLayer_Network_Storage',
+            'restoreFromSnapshot',
+            identifier=12345678)
+
+    def test_get_file_volume_details(self):
+        result = self.file.get_file_volume_details(100)
+
+        self.assertEqual(fixtures.SoftLayer_Network_Storage.getObject, result)
+
+        self.assert_called_with(
+            'SoftLayer_Network_Storage',
+            'getObject',
+            identifier=100)
+
+    def test_get_file_volume_snapshot_list(self):
+        result = self.file.get_file_volume_snapshot_list(100)
+
+        self.assertEqual(fixtures.SoftLayer_Network_Storage.getSnapshots,
+                         result)
+
+        self.assert_called_with(
+            'SoftLayer_Network_Storage',
+            'getSnapshots',
+            identifier=100)
+
+    def test_create_snapshot(self):
+        result = self.file.create_snapshot(123, 'hello world')
+
+        self.assertEqual(fixtures.SoftLayer_Network_Storage.createSnapshot,
+                         result)
+
+        self.assert_called_with(
+            'SoftLayer_Network_Storage',
+            'createSnapshot',
+            identifier=123)
+
+    def test_cancel_snapshot_immediately(self):
+        self.file.cancel_snapshot_space(1234, immediate=True)
+
+        self.assert_called_with(
+            'SoftLayer_Billing_Item',
+            'cancelItem',
+            args=(True, True, 'No longer needed'),
+            identifier=123,
+        )
+
+    def test_delete_snapshot(self):
+        result = self.file.delete_snapshot(100)
+
+        self.assertEqual(fixtures.SoftLayer_Network_Storage.deleteObject,
+                         result)
+
+        self.assert_called_with(
+            'SoftLayer_Network_Storage',
+            'deleteObject',
+            identifier=100)
+
+    def test_order_file_volume_no_package(self):
+        mock = self.set_mock('SoftLayer_Product_Package', 'getAllObjects')
+        mock.return_value = []
+
+        self.assertRaises(
+            ValueError,
+            self.file.order_file_volume,
+            "performance_storage_nfs",
+            "dal05",
+            40,
+            "LINUX",
+            iops=100,
+        )
+
+    def test_order_file_volume_too_many_packages(self):
+        mock = self.set_mock('SoftLayer_Product_Package', 'getAllObjects')
+        mock.return_value = [{}, {}]
+
+        self.assertRaises(
+            ValueError,
+            self.file.order_file_volume,
+            "performance_storage_nfs",
+            "dal05",
+            40,
+            "LINUX",
+            iops=100,
+        )
+
+    def test_order_file_volume_performance(self):
+        mock = self.set_mock('SoftLayer_Product_Package', 'getAllObjects')
+        mock.return_value = [{
+            'id': 1,
+            'name': 'Performance',
+            'items': [{
+                'capacity': '1',
+                'prices': [{
+                    'id': 1,
+                    'locationGroupId': '',
+                    'categories': [{
+                        'categoryCode': 'performance_storage_nfs',
+                    }],
+                }],
+            }, {
+                'capacity': '100',
+                'prices': [{
+                    'id': 2,
+                    'locationGroupId': '',
+                    'categories': [{
+                        'categoryCode': 'performance_storage_space',
+                    }],
+                }],
+            }, {
+                'capacity': '100',
+                'prices': [{
+                    'id': 3,
+                    'locationGroupId': '',
+                    'categories': [{
+                        'categoryCode': 'performance_storage_iops',
+                    }],
+                    'capacityRestrictionMinimum': '100',
+                    'capacityRestrictionMaximum': '100',
+                }],
+            }],
+        }]
+
+        result = self.file.order_file_volume(
+            "performance_storage_nfs",
+            "dal05",
+            100,
+            "LINUX",
+            iops=100
+            )
+
+        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_list_file_volumes(self):
+        result = self.file.list_file_volumes()
+
+        self.assertEqual(fixtures.SoftLayer_Account.getNasNetworkStorage,
+                         result)
+
+        self.assert_called_with('SoftLayer_Account', 'getNasNetworkStorage')
+
+    def test_order_file_volume_endurance(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',
+                    }],
+                }],
+            }],
+        }]
+
+        result = self.file.order_file_volume(
+            "storage_service_enterprise",
+            "dal05",
+            100,
+            "LINUX",
+            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_snapshot_space_no_package(self):
+        mock = self.set_mock('SoftLayer_Product_Package', 'getAllObjects')
+        mock.return_value = []
+
+        self.assertRaises(
+            ValueError,
+            self.file.order_snapshot_space,
+            100,
+            5,
+            None,
+            False,
+        )
+
+    def test_order_snapshot_space_too_many_packages(self):
+        mock = self.set_mock('SoftLayer_Product_Package', 'getAllObjects')
+        mock.return_value = [{}, {}]
+
+        self.assertRaises(
+            ValueError,
+            self.file.order_snapshot_space,
+            100,
+            5,
+            None,
+            False,
+        )
+
+    def test_order_snapshot_space(self):
+        mock = self.set_mock('SoftLayer_Product_Package', 'getAllObjects')
+        mock.return_value = [{
+            'id': 240,
+            'name': 'Endurance',
+            'items': [{
+                'capacity': '0',
+            }, {
+                'capacity': '5',
+                'prices': [{
+                    'locationGroupId': '530',
+                    }],
+            }, {
+                'capacity': '5',
+                'prices': [{
+                    'locationGroupId': '',
+                    'categories': [{
+                        'categoryCode': 'storage_file',
+                    }],
+                }],
+            }, {
+                'capacity': '5',
+                'prices': [{
+                    'locationGroupId': '',
+                    'categories': [{
+                        'categoryCode': 'storage_snapshot_space',
+                    }],
+                    'capacityRestrictionMinimum': '300',
+                }],
+            }, {
+                'capacity': '5',
+                'prices': [{
+                    'locationGroupId': '',
+                    'categories': [{
+                        'categoryCode': 'storage_snapshot_space',
+                    }],
+                    'capacityRestrictionMinimum': '100',
+                    'capacityRestrictionMaximum': '100',
+                }],
+            }, {
+                'capacity': '5',
+                'prices': [{
+                    'id': 46130,
+                    'locationGroupId': '',
+                    'categories': [{
+                        'categoryCode': 'storage_snapshot_space',
+                    }],
+                    'capacityRestrictionMinimum': '200',
+                    'capacityRestrictionMaximum': '200',
+                }],
+            }],
+        }]
+
+        result = self.file.order_snapshot_space(
+            100,
+            5,
+            None,
+            False,
+            )
+
+        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'}],
+                },
+            )