diff --git a/SoftLayer/CLI/hardware/billing.py b/SoftLayer/CLI/hardware/billing.py new file mode 100644 index 000000000..55c68c485 --- /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 f6edee475..d7e6334c8 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 000000000..7312de8d8 --- /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/docs/cli/hardware.rst b/docs/cli/hardware.rst index 3cd899d4a..4fd245e22 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 afa0f8b2d..75db769a8 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/tests/CLI/modules/server_tests.py b/tests/CLI/modules/server_tests.py index 14f8e9201..588ed4d1e 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 2c79f42bf..9fbe699c2 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)