diff --git a/SoftLayer/CLI/virt/create.py b/SoftLayer/CLI/virt/create.py index a0997704e..ee904ba6a 100644 --- a/SoftLayer/CLI/virt/create.py +++ b/SoftLayer/CLI/virt/create.py @@ -270,33 +270,19 @@ def cli(env, **args): output = [] if args.get('test'): result = vsi.verify_create_instance(**data) - total_monthly = 0.0 - total_hourly = 0.0 - - table = formatting.Table(['Item', 'cost']) - table.align['Item'] = 'r' - table.align['cost'] = 'r' - - for price in result['prices']: - total_monthly += float(price.get('recurringFee', 0.0)) - total_hourly += float(price.get('hourlyRecurringFee', 0.0)) - if args.get('billing') == 'hourly': - rate = "%.2f" % float(price['hourlyRecurringFee']) - elif args.get('billing') == 'monthly': - rate = "%.2f" % float(price['recurringFee']) - - table.add_row([price['item']['description'], rate]) - - total = 0 - if args.get('billing') == 'hourly': - total = total_hourly - elif args.get('billing') == 'monthly': - total = total_monthly - - billing_rate = 'monthly' - if args.get('billing') == 'hourly': - billing_rate = 'hourly' - table.add_row(['Total %s cost' % billing_rate, "%.2f" % total]) + + if result['presetId']: + ordering_mgr = SoftLayer.OrderingManager(env.client) + item_prices = ordering_mgr.get_item_prices(result['packageId']) + preset_prices = ordering_mgr.get_preset_prices(result['presetId']) + search_keys = ["guest_core", "ram"] + for price in preset_prices['prices']: + if price['item']['itemCategory']['categoryCode'] in search_keys: + item_key_name = price['item']['keyName'] + _add_item_prices(item_key_name, item_prices, result) + + table = _build_receipt_table(result['prices'], args.get('billing')) + output.append(table) output.append(formatting.FormattedItem( None, @@ -334,6 +320,35 @@ def cli(env, **args): env.fout(output) +def _add_item_prices(item_key_name, item_prices, result): + """Add the flavor item prices to the rest o the items prices""" + for item in item_prices: + if item_key_name == item['item']['keyName']: + if 'pricingLocationGroup' in item: + for location in item['pricingLocationGroup']['locations']: + if result['location'] == str(location['id']): + result['prices'].append(item) + + +def _build_receipt_table(prices, billing="hourly"): + """Retrieve the total recurring fee of the items prices""" + total = 0.000 + table = formatting.Table(['Cost', 'Item']) + table.align['Cost'] = 'r' + table.align['Item'] = 'l' + for price in prices: + rate = 0.000 + if billing == "hourly": + rate += float(price.get('hourlyRecurringFee', 0.000)) + else: + rate += float(price.get('recurringFee', 0.000)) + total += rate + + table.add_row(["%.3f" % rate, price['item']['description']]) + table.add_row(["%.3f" % total, "Total %s cost" % billing]) + return table + + def _validate_args(env, args): """Raises an ArgumentError if the given arguments are not valid.""" diff --git a/SoftLayer/fixtures/SoftLayer_Product_Package.py b/SoftLayer/fixtures/SoftLayer_Product_Package.py index 2642b77e8..b7b008788 100644 --- a/SoftLayer/fixtures/SoftLayer_Product_Package.py +++ b/SoftLayer/fixtures/SoftLayer_Product_Package.py @@ -916,7 +916,7 @@ 'prices': [{'id': 611}], }] -getItemPrices = [ +getItemPricesISCSI = [ { 'currentPriceFlag': '', 'id': 2152, @@ -1341,6 +1341,179 @@ }] }] +getItemPrices = [ + { + "hourlyRecurringFee": ".093", + "id": 204015, + "recurringFee": "62", + "item": { + "description": "4 x 2.0 GHz or higher Cores", + "id": 859, + "keyName": "GUEST_CORES_4", + }, + "pricingLocationGroup": { + "id": 503, + "locations": [ + { + "id": 449610, + "longName": "Montreal 1", + "name": "mon01", + "statusId": 2 + }, + { + "id": 449618, + "longName": "Montreal 2", + "name": "mon02", + "statusId": 2 + }, + { + "id": 448994, + "longName": "Toronto 1", + "name": "tor01", + "statusId": 2 + }, + { + "id": 350993, + "longName": "Toronto 2", + "name": "tor02", + "statusId": 2 + }, + { + "id": 221894, + "longName": "Amsterdam 2", + "name": "ams02", + "statusId": 2 + }, + { + "id": 265592, + "longName": "Amsterdam 1", + "name": "ams01", + "statusId": 2 + }, + { + "id": 814994, + "longName": "Amsterdam 3", + "name": "ams03", + "statusId": 2 + } + ] + } + }, + { + "hourlyRecurringFee": ".006", + "id": 204663, + "recurringFee": "4.1", + "item": { + "description": "100 GB (LOCAL)", + "id": 3899, + "keyName": "GUEST_DISK_100_GB_LOCAL_3", + }, + "pricingLocationGroup": { + "id": 503, + "locations": [ + { + "id": 449610, + "longName": "Montreal 1", + "name": "mon01", + "statusId": 2 + }, + { + "id": 449618, + "longName": "Montreal 2", + "name": "mon02", + "statusId": 2 + }, + { + "id": 448994, + "longName": "Toronto 1", + "name": "tor01", + "statusId": 2 + }, + { + "id": 350993, + "longName": "Toronto 2", + "name": "tor02", + "statusId": 2 + }, + { + "id": 221894, + "longName": "Amsterdam 2", + "name": "ams02", + "statusId": 2 + }, + { + "id": 265592, + "longName": "Amsterdam 1", + "name": "ams01", + "statusId": 2 + }, + { + "id": 814994, + "longName": "Amsterdam 3", + "name": "ams03", + "statusId": 2 + } + ] + } + }, + { + "hourlyRecurringFee": ".217", + "id": 204255, + "recurringFee": "144", + "item": { + "description": "16 GB ", + "id": 1017, + "keyName": "RAM_16_GB", + }, + "pricingLocationGroup": { + "id": 503, + "locations": [ + { + "id": 449610, + "longName": "Montreal 1", + "name": "mon01", + "statusId": 2 + }, + { + "id": 449618, + "longName": "Montreal 2", + "name": "mon02", + "statusId": 2 + }, + { + "id": 448994, + "longName": "Toronto 1", + "name": "tor01", + "statusId": 2 + }, + { + "id": 350993, + "longName": "Toronto 2", + "name": "tor02", + "statusId": 2 + }, + { + "id": 221894, + "longName": "Amsterdam 2", + "name": "ams02", + "statusId": 2 + }, + { + "id": 265592, + "longName": "Amsterdam 1", + "name": "ams01", + "statusId": 2 + }, + { + "id": 814994, + "longName": "Amsterdam 3", + "name": "ams03", + "statusId": 2 + } + ] + } + } +] getActivePresets = [ { "description": "M1.64x512x25", diff --git a/SoftLayer/fixtures/SoftLayer_Product_Package_Preset.py b/SoftLayer/fixtures/SoftLayer_Product_Package_Preset.py new file mode 100644 index 000000000..d111b9595 --- /dev/null +++ b/SoftLayer/fixtures/SoftLayer_Product_Package_Preset.py @@ -0,0 +1,62 @@ +getObject = { + "id": 405, + "keyName": "AC1_8X60X25", + "prices": [ + { + "hourlyRecurringFee": "1.425", + "id": 207345, + "recurringFee": "936.23", + "item": { + "description": "1 x P100 GPU", + "id": 10933, + "keyName": "1_X_P100_GPU", + "itemCategory": { + "categoryCode": "guest_pcie_device0", + "id": 1259 + } + } + }, + { + "hourlyRecurringFee": "0", + "id": 2202, + "recurringFee": "0", + "item": { + "description": "25 GB (SAN)", + "id": 1178, + "keyName": "GUEST_DISK_25_GB_SAN", + "itemCategory": { + "categoryCode": "guest_disk0", + "id": 81 + } + } + }, + { + "hourlyRecurringFee": ".342", + "id": 207361, + "recurringFee": "224.69", + "item": { + "description": "60 GB", + "id": 10939, + "keyName": "RAM_0_UNIT_PLACEHOLDER_10", + "itemCategory": { + "categoryCode": "ram", + "id": 3 + } + } + }, + { + "hourlyRecurringFee": ".181", + "id": 209595, + "recurringFee": "118.26", + "item": { + "description": "8 x 2.0 GHz or higher Cores", + "id": 11307, + "keyName": "GUEST_CORE_8", + "itemCategory": { + "categoryCode": "guest_core", + "id": 80 + } + } + } + ] +} diff --git a/SoftLayer/fixtures/SoftLayer_Virtual_Guest.py b/SoftLayer/fixtures/SoftLayer_Virtual_Guest.py index ac20acb90..69a1b95e6 100644 --- a/SoftLayer/fixtures/SoftLayer_Virtual_Guest.py +++ b/SoftLayer/fixtures/SoftLayer_Virtual_Guest.py @@ -423,7 +423,113 @@ setPublicNetworkInterfaceSpeed = True createObject = getObject createObjects = [getObject] -generateOrderTemplate = {} +generateOrderTemplate = { + "imageTemplateId": None, + "location": "1854895", + "packageId": 835, + "presetId": 405, + "prices": [ + { + "hourlyRecurringFee": "0", + "id": 45466, + "recurringFee": "0", + "item": { + "description": "CentOS 7.x - Minimal Install (64 bit)" + } + }, + { + "hourlyRecurringFee": "0", + "id": 2202, + "recurringFee": "0", + "item": { + "description": "25 GB (SAN)" + } + }, + { + "hourlyRecurringFee": "0", + "id": 905, + "recurringFee": "0", + "item": { + "description": "Reboot / Remote Console" + } + }, + { + "hourlyRecurringFee": ".02", + "id": 899, + "recurringFee": "10", + "item": { + "description": "1 Gbps Private Network Uplink" + } + }, + { + "hourlyRecurringFee": "0", + "id": 1800, + "item": { + "description": "0 GB Bandwidth Allotment" + } + }, + { + "hourlyRecurringFee": "0", + "id": 21, + "recurringFee": "0", + "item": { + "description": "1 IP Address" + } + }, + { + "hourlyRecurringFee": "0", + "id": 55, + "recurringFee": "0", + "item": { + "description": "Host Ping" + } + }, + { + "hourlyRecurringFee": "0", + "id": 57, + "recurringFee": "0", + "item": { + "description": "Email and Ticket" + } + }, + { + "hourlyRecurringFee": "0", + "id": 58, + "recurringFee": "0", + "item": { + "description": "Automated Notification" + } + }, + { + "hourlyRecurringFee": "0", + "id": 420, + "recurringFee": "0", + "item": { + "description": "Unlimited SSL VPN Users & 1 PPTP VPN User per account" + } + }, + { + "hourlyRecurringFee": "0", + "id": 418, + "recurringFee": "0", + "item": { + "description": "Nessus Vulnerability Assessment & Reporting" + } + } + ], + "quantity": 1, + "sourceVirtualGuestId": None, + "sshKeys": [], + "useHourlyPricing": True, + "virtualGuests": [ + { + "domain": "test.local", + "hostname": "test" + } + ], + "complexType": "SoftLayer_Container_Product_Order_Virtual_Guest" +} + setUserMetadata = ['meta'] reloadOperatingSystem = 'OK' setTags = True diff --git a/SoftLayer/managers/ordering.py b/SoftLayer/managers/ordering.py index 3677ecedd..01a182ae1 100644 --- a/SoftLayer/managers/ordering.py +++ b/SoftLayer/managers/ordering.py @@ -34,6 +34,7 @@ def __init__(self, client): self.package_svc = client['Product_Package'] self.order_svc = client['Product_Order'] self.billing_svc = client['Billing_Order'] + self.package_preset = client['Product_Package_Preset'] def get_packages_of_type(self, package_types, mask=None): """Get packages that match a certain type. @@ -369,6 +370,34 @@ def get_price_id_list(self, package_keyname, item_keynames): return prices + def get_preset_prices(self, preset): + """Get preset item prices. + + Retrieve a SoftLayer_Product_Package_Preset record. + + :param int preset: preset identifier. + :returns: A list of price IDs associated with the given preset_id. + + """ + mask = 'mask[prices[item]]' + + prices = self.package_preset.getObject(id=preset, mask=mask) + return prices + + def get_item_prices(self, package_id): + """Get item prices. + + Retrieve a SoftLayer_Product_Package item prices record. + + :param int package_id: package identifier. + :returns: A list of price IDs associated with the given package. + + """ + mask = 'mask[pricingLocationGroup[locations]]' + + prices = self.package_svc.getItemPrices(id=package_id, mask=mask) + return prices + def verify_order(self, package_keyname, location, item_keynames, complex_type=None, hourly=True, preset_keyname=None, extras=None, quantity=1): """Verifies an order with the given package and prices. diff --git a/tests/CLI/modules/vs_tests.py b/tests/CLI/modules/vs_tests.py index 284963444..d22fcddc1 100644 --- a/tests/CLI/modules/vs_tests.py +++ b/tests/CLI/modules/vs_tests.py @@ -672,7 +672,18 @@ def test_create_vs_test(self, confirm_mock): '--memory', '2048MB', '--datacenter', 'TEST00', '--os', 'UBUNTU_LATEST']) - self.assertEqual(result.exit_code, -1) + self.assertEqual(result.exit_code, 0) + + @mock.patch('SoftLayer.CLI.formatting.confirm') + def test_create_vs_flavor_test(self, confirm_mock): + confirm_mock.return_value = True + + result = self.run_command(['vs', 'create', '--test', '--hostname', 'TEST', + '--domain', 'TESTING', '--flavor', 'B1_2X8X25', + '--datacenter', 'TEST00', '--os', 'UBUNTU_LATEST']) + + self.assert_no_fail(result) + self.assertEqual(result.exit_code, 0) def test_create_vs_bad_memory(self): result = self.run_command(['vs', 'create', '--hostname', 'TEST', diff --git a/tests/managers/ordering_tests.py b/tests/managers/ordering_tests.py index 5149f6ed8..0ea7c7546 100644 --- a/tests/managers/ordering_tests.py +++ b/tests/managers/ordering_tests.py @@ -68,6 +68,18 @@ def test_get_package_id_by_type_returns_valid_id(self): self.assertEqual(46, package_id) + def test_get_preset_prices(self): + result = self.ordering.get_preset_prices(405) + + self.assertEqual(result, fixtures.SoftLayer_Product_Package_Preset.getObject) + self.assert_called_with('SoftLayer_Product_Package_Preset', 'getObject') + + def test_get_item_prices(self): + result = self.ordering.get_item_prices(835) + + self.assertEqual(result, fixtures.SoftLayer_Product_Package.getItemPrices) + self.assert_called_with('SoftLayer_Product_Package', 'getItemPrices') + def test_get_package_id_by_type_fails_for_nonexistent_package_type(self): p_mock = self.set_mock('SoftLayer_Product_Package', 'getAllObjects') p_mock.return_value = []