Repository: softlayer/softlayer-python
Pull request #982 (2018-06-19 17:37:36)

Fixed cancel block storage issue
Cancel block storage issue https://github.com/softlayer/softlayer-python/issues/880


Patch:
diff --git a/SoftLayer/managers/block.py b/SoftLayer/managers/block.py
index 74b5fb42..b1efd033 100644
--- a/SoftLayer/managers/block.py
+++ b/SoftLayer/managers/block.py
@@ -510,6 +510,10 @@ def cancel_block_volume(self, volume_id,
         block_volume = self.get_block_volume_details(
             volume_id,
             mask='mask[id,billingItem[id,hourlyFlag]]')
+
+        if 'billingItem' not in block_volume:
+            raise exceptions.SoftLayerError("Block Storage was already cancelled")
+
         billing_item_id = block_volume['billingItem']['id']
 
         if utils.lookup(block_volume, 'billingItem', 'hourlyFlag'):


Test patch:
diff --git a/tests/managers/block_tests.py b/tests/managers/block_tests.py
index 6f2e5eb6..fa5dacb2 100644
--- a/tests/managers/block_tests.py
+++ b/tests/managers/block_tests.py
@@ -41,33 +41,72 @@ def test_cancel_block_volume_immediately_hourly_billing(self):
             identifier=449,
         )
 
+    def test_cancel_block_volume_exception_billing_item_not_found(self):
+        mock = self.set_mock('SoftLayer_Network_Storage', 'getObject')
+        mock.return_value = {
+            'capacityGb': 20,
+            'createDate': '2017-06-20T14:24:21-06:00',
+            'nasType': 'ISCSI',
+            'storageTypeId': '7',
+            'serviceResourceName': 'PerfStor Aggr aggr_staasdal0601_pc01'
+        }
+        exception = self.assertRaises(
+            exceptions.SoftLayerError,
+            self.block.cancel_block_volume,
+            12345,
+            immediate=True
+        )
+        self.assertEqual(
+            'Block Storage was already cancelled',
+            str(exception)
+        )
+
+    def test_cancel_block_volume_billing_item_found(self):
+        mock = self.set_mock('SoftLayer_Network_Storage', 'getObject')
+        mock.return_value = {
+            'capacityGb': 20,
+            'createDate': '2017-06-20T14:24:21-06:00',
+            'nasType': 'ISCSI',
+            'storageTypeId': '7',
+            'serviceResourceName': 'PerfStor Aggr aggr_staasdal0601_pc01',
+            'billingItem': {'hourlyFlag': True, 'id': 449},
+        }
+        self.block.cancel_block_volume(123, immediate=False)
+
+        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)
 
-        expected_mask = 'id,'\
-                        'username,'\
-                        'password,'\
-                        'capacityGb,'\
-                        'snapshotCapacityGb,'\
-                        'parentVolume.snapshotSizeBytes,'\
-                        'storageType.keyName,'\
-                        'serviceResource.datacenter[name],'\
-                        'serviceResourceBackendIpAddress,'\
-                        'storageTierLevel,'\
-                        'provisionedIops,'\
-                        'lunId,'\
-                        'originalVolumeName,'\
-                        'originalSnapshotName,'\
-                        'originalVolumeSize,'\
-                        'activeTransactionCount,'\
-                        'activeTransactions.transactionStatus[friendlyName],'\
-                        'replicationPartnerCount,'\
-                        'replicationStatus,'\
-                        'replicationPartners[id,username,'\
-                        'serviceResourceBackendIpAddress,'\
-                        'serviceResource[datacenter[name]],'\
+        expected_mask = 'id,' \
+                        'username,' \
+                        'password,' \
+                        'capacityGb,' \
+                        'snapshotCapacityGb,' \
+                        'parentVolume.snapshotSizeBytes,' \
+                        'storageType.keyName,' \
+                        'serviceResource.datacenter[name],' \
+                        'serviceResourceBackendIpAddress,' \
+                        'storageTierLevel,' \
+                        'provisionedIops,' \
+                        'lunId,' \
+                        'originalVolumeName,' \
+                        'originalSnapshotName,' \
+                        'originalVolumeSize,' \
+                        'activeTransactionCount,' \
+                        'activeTransactions.transactionStatus[friendlyName],' \
+                        'replicationPartnerCount,' \
+                        'replicationStatus,' \
+                        'replicationPartners[id,username,' \
+                        'serviceResourceBackendIpAddress,' \
+                        'serviceResource[datacenter[name]],' \
                         'replicationSchedule[type[keyname]]]'
 
         self.assert_called_with(
@@ -75,7 +114,7 @@ def test_get_block_volume_details(self):
             'getObject',
             identifier=100,
             mask='mask[%s]' % expected_mask
-            )
+        )
 
     def test_list_block_volumes(self):
         result = self.block.list_block_volumes()
@@ -96,14 +135,14 @@ def test_list_block_volumes(self):
             }
         }
 
-        expected_mask = 'id,'\
-                        'username,'\
-                        'lunId,'\
-                        'capacityGb,'\
-                        'bytesUsed,'\
-                        'serviceResource.datacenter[name],'\
-                        'serviceResourceBackendIpAddress,'\
-                        'activeTransactionCount,'\
+        expected_mask = 'id,' \
+                        'username,' \
+                        'lunId,' \
+                        'capacityGb,' \
+                        'bytesUsed,' \
+                        'serviceResource.datacenter[name],' \
+                        'serviceResourceBackendIpAddress,' \
+                        'activeTransactionCount,' \
                         'replicationPartnerCount'
 
         self.assert_called_with(
@@ -138,14 +177,14 @@ def test_list_block_volumes_with_additional_filters(self):
             }
         }
 
-        expected_mask = 'id,'\
-                        'username,'\
-                        'lunId,'\
-                        'capacityGb,'\
-                        'bytesUsed,'\
-                        'serviceResource.datacenter[name],'\
-                        'serviceResourceBackendIpAddress,'\
-                        'activeTransactionCount,'\
+        expected_mask = 'id,' \
+                        'username,' \
+                        'lunId,' \
+                        'capacityGb,' \
+                        'bytesUsed,' \
+                        'serviceResource.datacenter[name],' \
+                        'serviceResourceBackendIpAddress,' \
+                        'activeTransactionCount,' \
                         'replicationPartnerCount'
 
         self.assert_called_with(
@@ -357,22 +396,22 @@ def test_order_block_volume_performance(self):
             'SoftLayer_Product_Order',
             'placeOrder',
             args=({
-                'complexType': 'SoftLayer_Container_Product_Order_'
-                               'Network_Storage_AsAService',
-                'packageId': 759,
-                'prices': [
-                    {'id': 189433},
-                    {'id': 189443},
-                    {'id': 190113},
-                    {'id': 190173}
-                ],
-                'volumeSize': 1000,
-                'quantity': 1,
-                'location': 449494,
-                'iops': 2000,
-                'useHourlyPricing': False,
-                'osFormatType': {'keyName': 'LINUX'}
-            },)
+                      'complexType': 'SoftLayer_Container_Product_Order_'
+                                     'Network_Storage_AsAService',
+                      'packageId': 759,
+                      'prices': [
+                          {'id': 189433},
+                          {'id': 189443},
+                          {'id': 190113},
+                          {'id': 190173}
+                      ],
+                      'volumeSize': 1000,
+                      'quantity': 1,
+                      'location': 449494,
+                      'iops': 2000,
+                      'useHourlyPricing': False,
+                      'osFormatType': {'keyName': 'LINUX'}
+                  },)
         )
 
     def test_order_block_volume_endurance(self):
@@ -401,21 +440,21 @@ def test_order_block_volume_endurance(self):
             'SoftLayer_Product_Order',
             'placeOrder',
             args=({
-                'complexType': 'SoftLayer_Container_Product_Order_'
-                               'Network_Storage_AsAService',
-                'packageId': 759,
-                'prices': [
-                    {'id': 189433},
-                    {'id': 189443},
-                    {'id': 194763},
-                    {'id': 194703}
-                ],
-                'volumeSize': 1000,
-                'quantity': 1,
-                'location': 449494,
-                'useHourlyPricing': False,
-                'osFormatType': {'keyName': 'LINUX'}
-            },)
+                      'complexType': 'SoftLayer_Container_Product_Order_'
+                                     'Network_Storage_AsAService',
+                      'packageId': 759,
+                      'prices': [
+                          {'id': 189433},
+                          {'id': 189443},
+                          {'id': 194763},
+                          {'id': 194703}
+                      ],
+                      'volumeSize': 1000,
+                      'quantity': 1,
+                      'location': 449494,
+                      'useHourlyPricing': False,
+                      'osFormatType': {'keyName': 'LINUX'}
+                  },)
         )
 
     def test_authorize_host_to_volume(self):
@@ -525,17 +564,17 @@ def test_order_block_snapshot_space_upgrade(self):
             'SoftLayer_Product_Order',
             'placeOrder',
             args=({
-                'complexType': 'SoftLayer_Container_Product_Order_Network_'
-                               'Storage_Enterprise_SnapshotSpace_Upgrade',
-                'packageId': 759,
-                'prices': [
-                    {'id': 193853}
-                ],
-                'quantity': 1,
-                'location': 449500,
-                'volumeId': 102,
-                'useHourlyPricing': False
-            },)
+                      'complexType': 'SoftLayer_Container_Product_Order_Network_'
+                                     'Storage_Enterprise_SnapshotSpace_Upgrade',
+                      'packageId': 759,
+                      'prices': [
+                          {'id': 193853}
+                      ],
+                      'quantity': 1,
+                      'location': 449500,
+                      'volumeId': 102,
+                      'useHourlyPricing': False
+                  },)
         )
 
     def test_order_block_snapshot_space(self):
@@ -554,17 +593,17 @@ def test_order_block_snapshot_space(self):
             'SoftLayer_Product_Order',
             'placeOrder',
             args=({
-                'complexType': 'SoftLayer_Container_Product_Order_Network_'
-                               'Storage_Enterprise_SnapshotSpace',
-                'packageId': 759,
-                'prices': [
-                    {'id': 193613}
-                ],
-                'quantity': 1,
-                'location': 449500,
-                'volumeId': 102,
-                'useHourlyPricing': False
-            },)
+                      'complexType': 'SoftLayer_Container_Product_Order_Network_'
+                                     'Storage_Enterprise_SnapshotSpace',
+                      'packageId': 759,
+                      'prices': [
+                          {'id': 193613}
+                      ],
+                      'quantity': 1,
+                      'location': 449500,
+                      'volumeId': 102,
+                      'useHourlyPricing': False
+                  },)
         )
 
     def test_order_block_replicant_os_type_not_found(self):
@@ -610,26 +649,26 @@ def test_order_block_replicant_performance_os_type_given(self):
             'SoftLayer_Product_Order',
             'placeOrder',
             args=({
-                'complexType': 'SoftLayer_Container_Product_Order_'
-                               'Network_Storage_AsAService',
-                'packageId': 759,
-                'prices': [
-                    {'id': 189433},
-                    {'id': 189443},
-                    {'id': 189993},
-                    {'id': 190053},
-                    {'id': 191193},
-                    {'id': 192033}
-                ],
-                'volumeSize': 500,
-                'quantity': 1,
-                'location': 449494,
-                'iops': 1000,
-                'originVolumeId': 102,
-                'originVolumeScheduleId': 978,
-                'useHourlyPricing': False,
-                'osFormatType': {'keyName': 'XEN'}
-            },)
+                      'complexType': 'SoftLayer_Container_Product_Order_'
+                                     'Network_Storage_AsAService',
+                      'packageId': 759,
+                      'prices': [
+                          {'id': 189433},
+                          {'id': 189443},
+                          {'id': 189993},
+                          {'id': 190053},
+                          {'id': 191193},
+                          {'id': 192033}
+                      ],
+                      'volumeSize': 500,
+                      'quantity': 1,
+                      'location': 449494,
+                      'iops': 1000,
+                      'originVolumeId': 102,
+                      'originVolumeScheduleId': 978,
+                      'useHourlyPricing': False,
+                      'osFormatType': {'keyName': 'XEN'}
+                  },)
         )
 
     def test_order_block_replicant_endurance(self):
@@ -651,25 +690,25 @@ def test_order_block_replicant_endurance(self):
             'SoftLayer_Product_Order',
             'placeOrder',
             args=({
-                'complexType': 'SoftLayer_Container_Product_Order_'
-                               'Network_Storage_AsAService',
-                'packageId': 759,
-                'prices': [
-                    {'id': 189433},
-                    {'id': 189443},
-                    {'id': 193433},
-                    {'id': 193373},
-                    {'id': 193613},
-                    {'id': 194693}
-                ],
-                'volumeSize': 500,
-                'quantity': 1,
-                'location': 449494,
-                'originVolumeId': 102,
-                'originVolumeScheduleId': 978,
-                'useHourlyPricing': False,
-                'osFormatType': {'keyName': 'LINUX'}
-            },)
+                      'complexType': 'SoftLayer_Container_Product_Order_'
+                                     'Network_Storage_AsAService',
+                      'packageId': 759,
+                      'prices': [
+                          {'id': 189433},
+                          {'id': 189443},
+                          {'id': 193433},
+                          {'id': 193373},
+                          {'id': 193613},
+                          {'id': 194693}
+                      ],
+                      'volumeSize': 500,
+                      'quantity': 1,
+                      'location': 449494,
+                      'originVolumeId': 102,
+                      'originVolumeScheduleId': 978,
+                      'useHourlyPricing': False,
+                      'osFormatType': {'keyName': 'LINUX'}
+                  },)
         )
 
     def test_order_block_duplicate_origin_os_type_not_found(self):
@@ -709,23 +748,23 @@ def test_order_block_duplicate_performance_no_duplicate_snapshot(self):
             'SoftLayer_Product_Order',
             'placeOrder',
             args=({
-                'complexType': 'SoftLayer_Container_Product_Order_'
-                               'Network_Storage_AsAService',
-                'packageId': 759,
-                'prices': [
-                    {'id': 189433},
-                    {'id': 189443},
-                    {'id': 189993},
-                    {'id': 190053}
-                ],
-                'volumeSize': 500,
-                'quantity': 1,
-                'location': 449500,
-                'duplicateOriginVolumeId': 102,
-                'osFormatType': {'keyName': 'LINUX'},
-                'iops': 1000,
-                'useHourlyPricing': False
-            },))
+                      'complexType': 'SoftLayer_Container_Product_Order_'
+                                     'Network_Storage_AsAService',
+                      'packageId': 759,
+                      'prices': [
+                          {'id': 189433},
+                          {'id': 189443},
+                          {'id': 189993},
+                          {'id': 190053}
+                      ],
+                      'volumeSize': 500,
+                      'quantity': 1,
+                      'location': 449500,
+                      'duplicateOriginVolumeId': 102,
+                      'osFormatType': {'keyName': 'LINUX'},
+                      'iops': 1000,
+                      'useHourlyPricing': False
+                  },))
 
     def test_order_block_duplicate_performance(self):
         mock = self.set_mock('SoftLayer_Product_Package', 'getAllObjects')
@@ -743,7 +782,7 @@ def test_order_block_duplicate_performance(self):
             duplicate_iops=2000,
             duplicate_tier_level=None,
             duplicate_snapshot_size=10
-            )
+        )
 
         self.assertEqual(fixtures.SoftLayer_Product_Order.placeOrder, result)
 
@@ -751,25 +790,25 @@ def test_order_block_duplicate_performance(self):
             'SoftLayer_Product_Order',
             'placeOrder',
             args=({
-                'complexType': 'SoftLayer_Container_Product_Order_'
-                               'Network_Storage_AsAService',
-                'packageId': 759,
-                'prices': [
-                    {'id': 189433},
-                    {'id': 189443},
-                    {'id': 190113},
-                    {'id': 190173},
-                    {'id': 191193}
-                ],
-                'volumeSize': 1000,
-                'quantity': 1,
-                'location': 449500,
-                'duplicateOriginVolumeId': 102,
-                'osFormatType': {'keyName': 'LINUX'},
-                'duplicateOriginSnapshotId': 470,
-                'iops': 2000,
-                'useHourlyPricing': False
-            },))
+                      'complexType': 'SoftLayer_Container_Product_Order_'
+                                     'Network_Storage_AsAService',
+                      'packageId': 759,
+                      'prices': [
+                          {'id': 189433},
+                          {'id': 189443},
+                          {'id': 190113},
+                          {'id': 190173},
+                          {'id': 191193}
+                      ],
+                      'volumeSize': 1000,
+                      'quantity': 1,
+                      'location': 449500,
+                      'duplicateOriginVolumeId': 102,
+                      'osFormatType': {'keyName': 'LINUX'},
+                      'duplicateOriginSnapshotId': 470,
+                      'iops': 2000,
+                      'useHourlyPricing': False
+                  },))
 
     def test_order_block_duplicate_endurance_no_duplicate_snapshot(self):
         mock = self.set_mock('SoftLayer_Product_Package', 'getAllObjects')
@@ -789,22 +828,22 @@ def test_order_block_duplicate_endurance_no_duplicate_snapshot(self):
             'SoftLayer_Product_Order',
             'placeOrder',
             args=({
-                'complexType': 'SoftLayer_Container_Product_Order_'
-                               'Network_Storage_AsAService',
-                'packageId': 759,
-                'prices': [
-                    {'id': 189433},
-                    {'id': 189443},
-                    {'id': 193433},
-                    {'id': 193373}
-                ],
-                'volumeSize': 500,
-                'quantity': 1,
-                'location': 449500,
-                'duplicateOriginVolumeId': 102,
-                'osFormatType': {'keyName': 'LINUX'},
-                'useHourlyPricing': False
-            },))
+                      'complexType': 'SoftLayer_Container_Product_Order_'
+                                     'Network_Storage_AsAService',
+                      'packageId': 759,
+                      'prices': [
+                          {'id': 189433},
+                          {'id': 189443},
+                          {'id': 193433},
+                          {'id': 193373}
+                      ],
+                      'volumeSize': 500,
+                      'quantity': 1,
+                      'location': 449500,
+                      'duplicateOriginVolumeId': 102,
+                      'osFormatType': {'keyName': 'LINUX'},
+                      'useHourlyPricing': False
+                  },))
 
     def test_order_block_duplicate_endurance(self):
         mock = self.set_mock('SoftLayer_Product_Package', 'getAllObjects')
@@ -821,7 +860,7 @@ def test_order_block_duplicate_endurance(self):
             duplicate_iops=None,
             duplicate_tier_level=4,
             duplicate_snapshot_size=10
-            )
+        )
 
         self.assertEqual(fixtures.SoftLayer_Product_Order.placeOrder, result)
 
@@ -829,24 +868,24 @@ def test_order_block_duplicate_endurance(self):
             'SoftLayer_Product_Order',
             'placeOrder',
             args=({
-                'complexType': 'SoftLayer_Container_Product_Order_'
-                               'Network_Storage_AsAService',
-                'packageId': 759,
-                'prices': [
-                    {'id': 189433},
-                    {'id': 189443},
-                    {'id': 194763},
-                    {'id': 194703},
-                    {'id': 194943}
-                ],
-                'volumeSize': 1000,
-                'quantity': 1,
-                'location': 449500,
-                'duplicateOriginVolumeId': 102,
-                'osFormatType': {'keyName': 'LINUX'},
-                'duplicateOriginSnapshotId': 470,
-                'useHourlyPricing': False
-            },))
+                      'complexType': 'SoftLayer_Container_Product_Order_'
+                                     'Network_Storage_AsAService',
+                      'packageId': 759,
+                      'prices': [
+                          {'id': 189433},
+                          {'id': 189443},
+                          {'id': 194763},
+                          {'id': 194703},
+                          {'id': 194943}
+                      ],
+                      'volumeSize': 1000,
+                      'quantity': 1,
+                      'location': 449500,
+                      'duplicateOriginVolumeId': 102,
+                      'osFormatType': {'keyName': 'LINUX'},
+                      'duplicateOriginSnapshotId': 470,
+                      'useHourlyPricing': False
+                  },))
 
     def test_order_block_modified_performance(self):
         mock = self.set_mock('SoftLayer_Product_Package', 'getAllObjects')