Repository: softlayer/softlayer-python

Issue:
Add functionality to list file/block storage snapshot schedules
This functionality needs to be added to both file and block managers; snapshot-enable and snapshot-disable exist, but nothing to show the enabled and disabled snapshot schedules for a given volume.

Perhaps `slcli block snapshot-schedule-list` ?

Discussion:

Patch:
diff --git a/SoftLayer/CLI/block/snapshot/schedule_list.py b/SoftLayer/CLI/block/snapshot/schedule_list.py
new file mode 100644
index 00000000..022427e1
--- /dev/null
+++ b/SoftLayer/CLI/block/snapshot/schedule_list.py
@@ -0,0 +1,70 @@
+"""List 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 formatting
+
+
+@click.command()
+@click.argument('volume_id')
+@environment.pass_env
+def cli(env, volume_id):
+    """Lists snapshot schedules for a given volume"""
+
+    block_manager = SoftLayer.BlockStorageManager(env.client)
+
+    snapshot_schedules = block_manager.list_volume_schedules(volume_id)
+
+    table = formatting.Table(['id',
+                              'active',
+                              'type',
+                              'replication',
+                              'date_created',
+                              'minute',
+                              'hour',
+                              'day',
+                              'week',
+                              'day_of_week',
+                              'date_of_month',
+                              'month_of_year',
+                              'maximum_snapshots'])
+
+    for schedule in snapshot_schedules:
+
+        if 'REPLICATION' in schedule['type']['keyname']:
+            replication = '*'
+        else:
+            replication = formatting.blank()
+
+        block_schedule_type = schedule['type']['keyname'].replace('REPLICATION_', '')
+        block_schedule_type = block_schedule_type.replace('SNAPSHOT_', '')
+
+        property_list = ['MINUTE', 'HOUR', 'DAY', 'WEEK',
+                         'DAY_OF_WEEK', 'DAY_OF_MONTH',
+                         'MONTH_OF_YEAR', 'SNAPSHOT_LIMIT']
+
+        schedule_properties = []
+        for prop_key in property_list:
+            item = formatting.blank()
+            for schedule_property in schedule.get('properties', []):
+                if schedule_property['type']['keyname'] == prop_key:
+                    if schedule_property['value'] == '-1':
+                        item = '*'
+                    else:
+                        item = schedule_property['value']
+                    break
+            schedule_properties.append(item)
+
+        table_row = [
+            schedule['id'],
+            '*' if schedule.get('active', '') else '',
+            block_schedule_type,
+            replication,
+            schedule.get('createDate', '')]
+        table_row.extend(schedule_properties)
+
+        table.add_row(table_row)
+
+    env.fout(table)
diff --git a/SoftLayer/CLI/file/snapshot/schedule_list.py b/SoftLayer/CLI/file/snapshot/schedule_list.py
new file mode 100644
index 00000000..c83c50da
--- /dev/null
+++ b/SoftLayer/CLI/file/snapshot/schedule_list.py
@@ -0,0 +1,70 @@
+"""List 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 formatting
+
+
+@click.command()
+@click.argument('volume_id')
+@environment.pass_env
+def cli(env, volume_id):
+    """Lists snapshot schedules for a given volume"""
+
+    file_manager = SoftLayer.FileStorageManager(env.client)
+
+    snapshot_schedules = file_manager.list_volume_schedules(volume_id)
+
+    table = formatting.Table(['id',
+                              'active',
+                              'type',
+                              'replication',
+                              'date_created',
+                              'minute',
+                              'hour',
+                              'day',
+                              'week',
+                              'day_of_week',
+                              'date_of_month',
+                              'month_of_year',
+                              'maximum_snapshots'])
+
+    for schedule in snapshot_schedules:
+
+        if 'REPLICATION' in schedule['type']['keyname']:
+            replication = '*'
+        else:
+            replication = formatting.blank()
+
+        file_schedule_type = schedule['type']['keyname'].replace('REPLICATION_', '')
+        file_schedule_type = file_schedule_type.replace('SNAPSHOT_', '')
+
+        property_list = ['MINUTE', 'HOUR', 'DAY', 'WEEK',
+                         'DAY_OF_WEEK', 'DAY_OF_MONTH',
+                         'MONTH_OF_YEAR', 'SNAPSHOT_LIMIT']
+
+        schedule_properties = []
+        for prop_key in property_list:
+            item = formatting.blank()
+            for schedule_property in schedule.get('properties', []):
+                if schedule_property['type']['keyname'] == prop_key:
+                    if schedule_property['value'] == '-1':
+                        item = '*'
+                    else:
+                        item = schedule_property['value']
+                    break
+            schedule_properties.append(item)
+
+        table_row = [
+            schedule['id'],
+            '*' if schedule.get('active', '') else '',
+            file_schedule_type,
+            replication,
+            schedule.get('createDate', '')
+            ]
+        table_row.extend(schedule_properties)
+        table.add_row(table_row)
+
+    env.fout(table)
diff --git a/SoftLayer/CLI/routes.py b/SoftLayer/CLI/routes.py
index 3b406ec2..f02795cc 100644
--- a/SoftLayer/CLI/routes.py
+++ b/SoftLayer/CLI/routes.py
@@ -72,6 +72,8 @@
     ('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-schedule-list',
+     'SoftLayer.CLI.block.snapshot.schedule_list: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'),
@@ -98,6 +100,8 @@
     ('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-schedule-list',
+     'SoftLayer.CLI.file.snapshot.schedule_list: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'),
diff --git a/SoftLayer/fixtures/SoftLayer_Network_Storage.py b/SoftLayer/fixtures/SoftLayer_Network_Storage.py
index 80996cd7..8a0c9772 100644
--- a/SoftLayer/fixtures/SoftLayer_Network_Storage.py
+++ b/SoftLayer/fixtures/SoftLayer_Network_Storage.py
@@ -132,10 +132,22 @@
         'username': 'TEST_REP_2',
     }],
     'replicationStatus': 'Replicant Volume Provisioning has completed.',
-    'schedules': [{
-        'id': 978,
-        'type': {'keyname': 'SNAPSHOT_WEEKLY'},
-    }],
+    'schedules': [
+        {
+            'id': 978,
+            'type': {'keyname': 'SNAPSHOT_WEEKLY'},
+            'properties': [
+                {'type': {'keyname': 'MINUTE'}, 'value': '30'},
+            ]
+        },
+        {
+            'id': 988,
+            'type': {'keyname': 'REPLICATION_INTERVAL'},
+            'properties': [
+                {'type': {'keyname': 'MINUTE'}, 'value': '-1'},
+            ]
+        }
+    ],
     'serviceProviderId': 1,
     'serviceResource': {'datacenter': {'id': 449500, 'name': 'dal05'}},
     'serviceResourceBackendIpAddress': '10.1.2.3',
@@ -188,6 +200,18 @@
     'name': 'dal05'
 }]
 
+listVolumeSchedules = [
+    {
+        'id': 978,
+        'type': {'keyname': 'SNAPSHOT_WEEKLY'},
+        'properties': [{'type': {'keyname': 'MINUTE'}, 'value': '30'}]
+    },
+    {
+        'id': 988,
+        'type': {'keyname': 'REPLICATION_INTERVAL'},
+        'properties': [{'type': {'keyname': 'MINUTE'}, 'value': '-1'}]
+    }
+]
 
 deleteObject = True
 allowAccessFromHostList = True
diff --git a/SoftLayer/managers/block.py b/SoftLayer/managers/block.py
index 0bd5b60f..ed0d3261 100644
--- a/SoftLayer/managers/block.py
+++ b/SoftLayer/managers/block.py
@@ -474,6 +474,20 @@ def disable_snapshots(self, volume_id, schedule_type):
         return self.client.call('Network_Storage', 'disableSnapshots',
                                 schedule_type, id=volume_id)
 
+    def list_volume_schedules(self, volume_id):
+        """Lists schedules for a given volume
+
+        :param integer volume_id: The id of the volume
+        :return: Returns list of schedules assigned to a given volume
+        """
+        volume_detail = self.client.call(
+            'Network_Storage',
+            'getObject',
+            id=volume_id,
+            mask='schedules[type,properties[type]]')
+
+        return utils.lookup(volume_detail, 'schedules')
+
     def restore_from_snapshot(self, volume_id, snapshot_id):
         """Restores a specific volume from a snapshot
 
diff --git a/SoftLayer/managers/file.py b/SoftLayer/managers/file.py
index 9eccdc8a..92c060b7 100644
--- a/SoftLayer/managers/file.py
+++ b/SoftLayer/managers/file.py
@@ -394,6 +394,20 @@ def disable_snapshots(self, volume_id, schedule_type):
         return self.client.call('Network_Storage', 'disableSnapshots',
                                 schedule_type, id=volume_id)
 
+    def list_volume_schedules(self, volume_id):
+        """Lists schedules for a given volume
+
+        :param integer volume_id: The id of the volume
+        :return: Returns list of schedules assigned to a given volume
+        """
+        volume_detail = self.client.call(
+            'Network_Storage',
+            'getObject',
+            id=volume_id,
+            mask='schedules[type,properties[type]]')
+
+        return utils.lookup(volume_detail, 'schedules')
+
     def order_snapshot_space(self, volume_id, capacity, tier,
                              upgrade, **kwargs):
         """Orders snapshot space for the given file volume.


Test patch:
diff --git a/tests/CLI/modules/block_tests.py b/tests/CLI/modules/block_tests.py
index 352871b1..08914757 100644
--- a/tests/CLI/modules/block_tests.py
+++ b/tests/CLI/modules/block_tests.py
@@ -302,6 +302,43 @@ def test_disable_snapshots(self):
                                    '--schedule-type=HOURLY'])
         self.assert_no_fail(result)
 
+    def test_list_volume_schedules(self):
+        result = self.run_command([
+            'block', 'snapshot-schedule-list', '12345678'])
+        self.assert_no_fail(result)
+        self.assertEqual([
+            {
+                "week": None,
+                "maximum_snapshots": None,
+                "hour": None,
+                "day_of_week": None,
+                "day": None,
+                "replication": None,
+                "date_of_month": None,
+                "month_of_year": None,
+                "active": "",
+                "date_created": "",
+                "type": "WEEKLY",
+                "id": 978,
+                "minute": '30'
+            },
+            {
+                "week": None,
+                "maximum_snapshots": None,
+                "hour": None,
+                "day_of_week": None,
+                "day": None,
+                "replication": '*',
+                "date_of_month": None,
+                "month_of_year": None,
+                "active": "",
+                "date_created": "",
+                "type": "INTERVAL",
+                "id": 988,
+                "minute": '*'
+            }
+        ], json.loads(result.output))
+
     def test_create_snapshot(self):
         result = self.run_command(['block', 'snapshot-create', '12345678'])
 
diff --git a/tests/CLI/modules/file_tests.py b/tests/CLI/modules/file_tests.py
index 6614e115..14c522f8 100644
--- a/tests/CLI/modules/file_tests.py
+++ b/tests/CLI/modules/file_tests.py
@@ -304,6 +304,43 @@ def test_disable_snapshots(self):
                                    '--schedule-type=HOURLY'])
         self.assert_no_fail(result)
 
+    def test_list_volume_schedules(self):
+        result = self.run_command([
+            'file', 'snapshot-schedule-list', '12345678'])
+        self.assert_no_fail(result)
+        self.assertEqual([
+            {
+                "week": None,
+                "maximum_snapshots": None,
+                "hour": None,
+                "day_of_week": None,
+                "day": None,
+                "replication": None,
+                "date_of_month": None,
+                "month_of_year": None,
+                "active": "",
+                "date_created": "",
+                "type": "WEEKLY",
+                "id": 978,
+                "minute": '30'
+            },
+            {
+                "week": None,
+                "maximum_snapshots": None,
+                "hour": None,
+                "day_of_week": None,
+                "day": None,
+                "replication": '*',
+                "date_of_month": None,
+                "month_of_year": None,
+                "active": "",
+                "date_created": "",
+                "type": "INTERVAL",
+                "id": 988,
+                "minute": '*'
+            }
+        ], json.loads(result.output))
+
     def test_create_snapshot(self):
         result = self.run_command(['file', 'snapshot-create', '12345678'])
 
diff --git a/tests/managers/block_tests.py b/tests/managers/block_tests.py
index 2f6cf2ab..203c021b 100644
--- a/tests/managers/block_tests.py
+++ b/tests/managers/block_tests.py
@@ -492,6 +492,22 @@ def test_disable_snapshots(self):
             'disableSnapshots',
             identifier=12345678)
 
+    def test_list_volume_schedules(self):
+        result = self.block.list_volume_schedules(12345678)
+
+        self.assertEqual(
+            fixtures.SoftLayer_Network_Storage.listVolumeSchedules,
+            result)
+
+        expected_mask = 'schedules[type,properties[type]]'
+
+        self.assert_called_with(
+            'SoftLayer_Network_Storage',
+            'getObject',
+            identifier=12345678,
+            mask='mask[%s]' % expected_mask
+        )
+
     def test_order_block_snapshot_space_upgrade(self):
         mock = self.set_mock('SoftLayer_Product_Package', 'getAllObjects')
         mock.return_value = [fixtures.SoftLayer_Product_Package.SAAS_PACKAGE]