Repository: softlayer/softlayer-python
Pull request #1251 (2020-04-01 00:10:43)

implement the new feature hw billing and vs billing
https://github.com/softlayer/softlayer-python/issues/1242
- hw billing
- vs billing


Patch:
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 965e9967..444c459c 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,21 +1,7 @@
 # Change Log
 
-## [5.8.7] - 2020-03-26
-https://github.com/softlayer/softlayer-python/compare/v5.8.5...v5.8.7
-
-- #1222 Get load balancer (LBaaS) by name
-- #1221 Added version checker
-- #1227 Updated unit test suite for TravisCI to run properly
-- #1225 Add note about using multiple colon symbols not working when setting tags.
-- #1228 Support ordering [Dependent Duplicate Volumes](https://cloud.ibm.com/docs/BlockStorage?topic=BlockStorage-dependentduplicate)
-- #1233 Refactored File/Block managers to reduce duplicated code.
-- #1231 Added Refresh functions for Dependent Duplicate Volumes
-- #801 Added support for JSON styled parameters and object filters
-- #1234 Added ability to change which datacenters an image template was stored in
-
-## [5.8.6] - Skipped
-
-## [5.8.5] - 2020-01-29
+
+## [5.8.5] - 2012-01-29
 https://github.com/softlayer/softlayer-python/compare/v5.8.4...v5.8.5
 
 -  #1195 Fixed an issue with `slcli vs dns-sync --ptr`. Added `slcli hw dns-sync`
diff --git a/SoftLayer/CLI/hardware/billing.py b/SoftLayer/CLI/hardware/billing.py
new file mode 100644
index 00000000..55c68c48
--- /dev/null
+++ b/SoftLayer/CLI/hardware/billing.py
@@ -0,0 +1,38 @@
+"""Get billing for a hardware device."""
+# :license: MIT, see LICENSE for more details.
+
+import click
+
+import SoftLayer
+from SoftLayer.CLI import environment
+from SoftLayer.CLI import formatting
+from SoftLayer.CLI import helpers
+from SoftLayer import utils
+
+
+@click.command()
+@click.argument('identifier')
+@environment.pass_env
+def cli(env, identifier):
+    """Get billing for a hardware device."""
+    hardware = SoftLayer.HardwareManager(env.client)
+
+    hardware_id = helpers.resolve_id(hardware.resolve_ids, identifier, 'hardware')
+    result = hardware.get_hardware(hardware_id)
+    table = formatting.KeyValueTable(['name', 'value'])
+    table.align['name'] = 'r'
+    table.align['value'] = 'l'
+
+    table.add_row(['Id', identifier])
+
+    table.add_row(['Billing Item Id', utils.lookup(result, 'billingItem', 'id')])
+    table.add_row(['Recurring Fee', utils.lookup(result, 'billingItem', 'recurringFee')])
+    table.add_row(['Total', utils.lookup(result, 'billingItem', 'nextInvoiceTotalRecurringAmount')])
+    table.add_row(['Provision Date', utils.lookup(result, 'billingItem', 'provisionDate')])
+
+    price_table = formatting.Table(['Item', 'Recurring Price'])
+    for item in utils.lookup(result, 'billingItem', 'children') or []:
+        price_table.add_row([item['description'], item['nextInvoiceTotalRecurringAmount']])
+
+    table.add_row(['prices', price_table])
+    env.fout(table)
diff --git a/SoftLayer/CLI/routes.py b/SoftLayer/CLI/routes.py
index f6edee47..d7e6334c 100644
--- a/SoftLayer/CLI/routes.py
+++ b/SoftLayer/CLI/routes.py
@@ -20,6 +20,7 @@
 
     ('virtual', 'SoftLayer.CLI.virt'),
     ('virtual:bandwidth', 'SoftLayer.CLI.virt.bandwidth:cli'),
+    ('virtual:billing', 'SoftLayer.CLI.virt.billing:cli'),
     ('virtual:cancel', 'SoftLayer.CLI.virt.cancel:cli'),
     ('virtual:capture', 'SoftLayer.CLI.virt.capture:cli'),
     ('virtual:create', 'SoftLayer.CLI.virt.create:cli'),
@@ -231,6 +232,7 @@
     ('hardware:create', 'SoftLayer.CLI.hardware.create:cli'),
     ('hardware:create-options', 'SoftLayer.CLI.hardware.create_options:cli'),
     ('hardware:detail', 'SoftLayer.CLI.hardware.detail:cli'),
+    ('hardware:billing', 'SoftLayer.CLI.hardware.billing:cli'),
     ('hardware:edit', 'SoftLayer.CLI.hardware.edit:cli'),
     ('hardware:list', 'SoftLayer.CLI.hardware.list:cli'),
     ('hardware:power-cycle', 'SoftLayer.CLI.hardware.power:power_cycle'),
diff --git a/SoftLayer/CLI/virt/billing.py b/SoftLayer/CLI/virt/billing.py
new file mode 100644
index 00000000..7312de8d
--- /dev/null
+++ b/SoftLayer/CLI/virt/billing.py
@@ -0,0 +1,38 @@
+"""Get billing for a virtual device."""
+# :license: MIT, see LICENSE for more details.
+
+import click
+
+import SoftLayer
+from SoftLayer.CLI import environment
+from SoftLayer.CLI import formatting
+from SoftLayer.CLI import helpers
+from SoftLayer import utils
+
+
+@click.command()
+@click.argument('identifier')
+@environment.pass_env
+def cli(env, identifier):
+    """Get billing for a virtual device."""
+    virtual = SoftLayer.VSManager(env.client)
+
+    virtual_id = helpers.resolve_id(virtual.resolve_ids, identifier, 'virtual')
+    result = virtual.get_instance(virtual_id)
+    table = formatting.KeyValueTable(['name', 'value'])
+    table.align['name'] = 'r'
+    table.align['value'] = 'l'
+
+    table.add_row(['Id', identifier])
+
+    table.add_row(['Billing Item Id', utils.lookup(result, 'billingItem', 'id')])
+    table.add_row(['Recurring Fee', utils.lookup(result, 'billingItem', 'recurringFee')])
+    table.add_row(['Total', utils.lookup(result, 'billingItem', 'nextInvoiceTotalRecurringAmount')])
+    table.add_row(['Provision Date', utils.lookup(result, 'billingItem', 'provisionDate')])
+
+    price_table = formatting.Table(['Recurring Price'])
+    for item in utils.lookup(result, 'billingItem', 'children') or []:
+        price_table.add_row([item['nextInvoiceTotalRecurringAmount']])
+
+    table.add_row(['prices', price_table])
+    env.fout(table)
diff --git a/SoftLayer/consts.py b/SoftLayer/consts.py
index e651d91c..8bb1585f 100644
--- a/SoftLayer/consts.py
+++ b/SoftLayer/consts.py
@@ -5,7 +5,7 @@
 
     :license: MIT, see LICENSE for more details.
 """
-VERSION = 'v5.8.7'
+VERSION = 'v5.8.5'
 API_PUBLIC_ENDPOINT = 'https://api.softlayer.com/xmlrpc/v3.1/'
 API_PRIVATE_ENDPOINT = 'https://api.service.softlayer.com/xmlrpc/v3.1/'
 API_PUBLIC_ENDPOINT_REST = 'https://api.softlayer.com/rest/v3.1/'
diff --git a/docs/cli/hardware.rst b/docs/cli/hardware.rst
index 3cd899d4..4fd245e2 100644
--- a/docs/cli/hardware.rst
+++ b/docs/cli/hardware.rst
@@ -36,6 +36,10 @@ Provides some basic functionality to order a server. `slcli order` has a more fu
    :prog: hw detail
    :show-nested:
 
+.. click:: SoftLayer.CLI.hardware.billing:cli
+   :prog: hw billing
+   :show-nested:
+
 
 .. click:: SoftLayer.CLI.hardware.edit:cli
    :prog: hw edit
diff --git a/docs/cli/vs.rst b/docs/cli/vs.rst
index afa0f8b2..75db769a 100644
--- a/docs/cli/vs.rst
+++ b/docs/cli/vs.rst
@@ -245,6 +245,10 @@ If no timezone is specified, IMS local time (CST) will be assumed, which might n
    :prog: vs usage
    :show-nested:
 
+.. click:: SoftLayer.CLI.virt.billing:cli
+   :prog: vs billing
+   :show-nested:
+
 
 
 
diff --git a/setup.py b/setup.py
index d8e9f566..b88b324c 100644
--- a/setup.py
+++ b/setup.py
@@ -14,7 +14,7 @@
 
 setup(
     name='SoftLayer',
-    version='5.8.7',
+    version='5.8.5',
     description=DESCRIPTION,
     long_description=LONG_DESCRIPTION,
     author='SoftLayer Technologies, Inc.',


Test patch:
diff --git a/tests/CLI/modules/server_tests.py b/tests/CLI/modules/server_tests.py
index 14f8e920..588ed4d1 100644
--- a/tests/CLI/modules/server_tests.py
+++ b/tests/CLI/modules/server_tests.py
@@ -659,19 +659,19 @@ def test_dns_sync_both(self, confirm_mock):
                                            'getResourceRecords')
         getResourceRecords.return_value = []
         createAargs = ({
-            'type': 'a',
-            'host': 'hardware-test1',
-            'domainId': 12345,  # from SoftLayer_Account::getDomains
-            'data': '172.16.1.100',
-            'ttl': 7200
-        },)
+                           'type': 'a',
+                           'host': 'hardware-test1',
+                           'domainId': 12345,  # from SoftLayer_Account::getDomains
+                           'data': '172.16.1.100',
+                           'ttl': 7200
+                       },)
         createPTRargs = ({
-            'type': 'ptr',
-            'host': '100',
-            'domainId': 123456,
-            'data': 'hardware-test1.test.sftlyr.ws',
-            'ttl': 7200
-        },)
+                             'type': 'ptr',
+                             'host': '100',
+                             'domainId': 123456,
+                             'data': 'hardware-test1.test.sftlyr.ws',
+                             'ttl': 7200
+                         },)
 
         result = self.run_command(['hw', 'dns-sync', '1000'])
 
@@ -714,12 +714,12 @@ def test_dns_sync_v6(self, confirm_mock):
             }
         }
         createV6args = ({
-            'type': 'aaaa',
-            'host': 'hardware-test1',
-            'domainId': 12345,  # from SoftLayer_Account::getDomains
-            'data': '2607:f0d0:1b01:0023:0000:0000:0000:0004',
-            'ttl': 7200
-        },)
+                            'type': 'aaaa',
+                            'host': 'hardware-test1',
+                            'domainId': 12345,  # from SoftLayer_Account::getDomains
+                            'data': '2607:f0d0:1b01:0023:0000:0000:0000:0004',
+                            'ttl': 7200
+                        },)
         server.return_value = test_server
         result = self.run_command(['hw', 'dns-sync', '--aaaa-record', '1000'])
         self.assert_no_fail(result)
@@ -826,3 +826,19 @@ def test_dns_sync_misc_exception(self, confirm_mock):
         result = self.run_command(['hw', 'dns-sync', '-a', '1000'])
         self.assertEqual(result.exit_code, 2)
         self.assertIsInstance(result.exception, exceptions.CLIAbort)
+
+    def test_billing(self):
+        result = self.run_command(['hw', 'billing', '123456'])
+        billing_json = {
+            'Billing Item Id': 6327,
+            'Id': '123456',
+            'Provision Date': None,
+            'Recurring Fee': 1.54,
+            'Total': 16.08,
+            'prices': [{
+                'Item': 'test',
+                'Recurring Price': 1
+            }]
+        }
+        self.assert_no_fail(result)
+        self.assertEqual(json.loads(result.output), billing_json)
diff --git a/tests/CLI/modules/vs/vs_tests.py b/tests/CLI/modules/vs/vs_tests.py
index 2c79f42b..9fbe699c 100644
--- a/tests/CLI/modules/vs/vs_tests.py
+++ b/tests/CLI/modules/vs/vs_tests.py
@@ -320,19 +320,19 @@ def test_dns_sync_both(self, confirm_mock):
                                            'getResourceRecords')
         getResourceRecords.return_value = []
         createAargs = ({
-            'type': 'a',
-            'host': 'vs-test1',
-            'domainId': 12345,  # from SoftLayer_Account::getDomains
-            'data': '172.16.240.2',
-            'ttl': 7200
-        },)
+                           'type': 'a',
+                           'host': 'vs-test1',
+                           'domainId': 12345,  # from SoftLayer_Account::getDomains
+                           'data': '172.16.240.2',
+                           'ttl': 7200
+                       },)
         createPTRargs = ({
-            'type': 'ptr',
-            'host': '2',
-            'domainId': 123456,
-            'data': 'vs-test1.test.sftlyr.ws',
-            'ttl': 7200
-        },)
+                             'type': 'ptr',
+                             'host': '2',
+                             'domainId': 123456,
+                             'data': 'vs-test1.test.sftlyr.ws',
+                             'ttl': 7200
+                         },)
 
         result = self.run_command(['vs', 'dns-sync', '100'])
 
@@ -375,12 +375,12 @@ def test_dns_sync_v6(self, confirm_mock):
             }
         }
         createV6args = ({
-            'type': 'aaaa',
-            'host': 'vs-test1',
-            'domainId': 12345,
-            'data': '2607:f0d0:1b01:0023:0000:0000:0000:0004',
-            'ttl': 7200
-        },)
+                            'type': 'aaaa',
+                            'host': 'vs-test1',
+                            'domainId': 12345,
+                            'data': '2607:f0d0:1b01:0023:0000:0000:0000:0004',
+                            'ttl': 7200
+                        },)
         guest.return_value = test_guest
         result = self.run_command(['vs', 'dns-sync', '--aaaa-record', '100'])
         self.assert_no_fail(result)
@@ -740,3 +740,22 @@ def test_bandwidth_vs_quite(self):
         self.assertEqual(output_summary[1]['Max Date'], date)
         self.assertEqual(output_summary[2]['Max GB'], 0.1172)
         self.assertEqual(output_summary[3]['Sum GB'], 0.0009)
+
+    def test_billing(self):
+        result = self.run_command(['vs', 'billing', '123456'])
+        vir_billing = {
+            'Billing Item Id': 6327,
+            'Id': '123456',
+            'Provision Date': None,
+            'Recurring Fee': None,
+            'Total': 1.54,
+            'prices': [
+                {'Recurring Price': 1},
+                {'Recurring Price': 1},
+                {'Recurring Price': 1},
+                {'Recurring Price': 1},
+                {'Recurring Price': 1}
+            ]
+        }
+        self.assert_no_fail(result)
+        self.assertEqual(json.loads(result.output), vir_billing)