From 54775a739b686852fece684e461f70d1f2d19fa5 Mon Sep 17 00:00:00 2001 From: Christopher Gallo Date: Wed, 11 Jun 2025 16:28:38 -0500 Subject: [PATCH] Support for NVMe Dual Raid card ordering. Added KeyName and Category code to prices for easier troublehsooting. Fixed #2225 --- .secrets.baseline | 4 +-- SoftLayer/managers/ordering.py | 41 ++++++++++++++++++++------ tests/managers/ordering_tests.py | 18 +++++------ tests/managers/vs/vs_capacity_tests.py | 14 ++++++--- 4 files changed, 53 insertions(+), 24 deletions(-) diff --git a/.secrets.baseline b/.secrets.baseline index 394061815..f0aee0650 100644 --- a/.secrets.baseline +++ b/.secrets.baseline @@ -3,7 +3,7 @@ "files": "^.secrets.baseline$", "lines": null }, - "generated_at": "2025-02-14T20:05:29Z", + "generated_at": "2025-06-11T21:28:32Z", "plugins_used": [ { "name": "AWSKeyDetector" @@ -700,7 +700,7 @@ "hashed_secret": "8af1f8146d96a3cd862281442d0d6c5cb6f8f9e5", "is_secret": false, "is_verified": false, - "line_number": 181, + "line_number": 187, "type": "Hex High Entropy String", "verified_result": null } diff --git a/SoftLayer/managers/ordering.py b/SoftLayer/managers/ordering.py index 8a17455bd..3a56d69b5 100644 --- a/SoftLayer/managers/ordering.py +++ b/SoftLayer/managers/ordering.py @@ -357,10 +357,14 @@ def get_preset_by_key(self, package_keyname, preset_keyname, mask=None): if len(presets) == 0: raise exceptions.SoftLayerError( f"Preset {preset_keyname} does not exist in package {package_keyname}") - return presets[0] def get_price_id_list(self, package_keyname, item_keynames, core=None): + """Returns just a list of price IDs for backwards compatability""" + prices = self.get_ordering_prices(package_keyname, item_keynames, core) + return [price.get('id') for price in prices] + + def get_ordering_prices(self, package_keyname: str, item_keynames: list, core=None) -> list: """Converts a list of item keynames to a list of price IDs. This function is used to convert a list of item keynames into @@ -370,8 +374,7 @@ def get_price_id_list(self, package_keyname, item_keynames, core=None): :param str package_keyname: The package associated with the prices :param list item_keynames: A list of item keyname strings :param str core: preset guest core capacity. - :returns: A list of price IDs associated with the given item - keynames in the given package + :returns: A list of price IDs associated with the given item keynames in the given package """ mask = 'id, description, capacity, itemCategory, keyName, prices[categories], ' \ @@ -380,7 +383,8 @@ def get_price_id_list(self, package_keyname, item_keynames, core=None): item_capacity = self.get_item_capacity(items, item_keynames) prices = [] - category_dict = {"gpu0": -1, "pcie_slot0": -1} + # start at -1 so we can increment before we use it. 0 is a valid value here + category_dict = {"gpu0": -1, "pcie_slot0": -1, "disk_controller": -1} for item_keyname in item_keynames: matching_item = [] @@ -410,15 +414,33 @@ def get_price_id_list(self, package_keyname, item_keynames, core=None): # GPU and PCIe items has two generic prices and they are added to the list # according to the number of items in the order. category_dict[item_category] += 1 - category_code = item_category[:-1] + str(category_dict[item_category]) + item_category = self.get_special_category(category_dict[item_category], item_category) + price_id = [p['id'] for p in matching_item['prices'] if not p['locationGroupId'] - and p['categories'][0]['categoryCode'] == category_code][0] + and p['categories'][0]['categoryCode'] == item_category][0] - prices.append(price_id) + prices.append({ + "id": price_id, + "categories": [{"categoryCode": item_category}], + "item": {"keyName": item_keyname} + }) return prices + @staticmethod + def get_special_category(index: int, base: str) -> str: + """Handles cases where we need to find price on a special category price id""" + # disk_controller and disk_controller1 + if base == "disk_controller": + if index == 0: + return base + else: + return f"{base}1" + + # gpu0 and gpu1, pcie_slot0 and pcie_slot1 + return base[:-1] + str(index) + @staticmethod def get_item_price_id(core, prices, term=0): """get item price id @@ -644,8 +666,9 @@ def generate_order(self, package_keyname, location, item_keynames, complex_type= raise exceptions.SoftLayerError("A complex type must be specified with the order") order['complexType'] = complex_type - price_ids = self.get_price_id_list(package_keyname, item_keynames, preset_core) - order['prices'] = [{'id': price_id} for price_id in price_ids] + order['prices'] = self.get_ordering_prices(package_keyname, item_keynames, preset_core) + # price_ids = self.get_price_id_list(package_keyname, item_keynames, preset_core) + # order['prices'] = [{'id': price_id} for price_id in price_ids] container['orderContainers'] = [order] diff --git a/tests/managers/ordering_tests.py b/tests/managers/ordering_tests.py index 7d86c0051..db4f85988 100644 --- a/tests/managers/ordering_tests.py +++ b/tests/managers/ordering_tests.py @@ -435,13 +435,13 @@ def test_generate_order(self): pkg = 'PACKAGE_KEYNAME' items = ['ITEM1', 'ITEM2'] complex_type = 'My_Type' - expected_order = {'orderContainers': [ - {'complexType': 'My_Type', - 'location': 1854895, - 'packageId': 1234, - 'prices': [{'id': 1111}, {'id': 2222}], - 'quantity': 1, - 'useHourlyPricing': True} + expected_order = {'orderContainers': [{ + 'complexType': 'My_Type', + 'location': 1854895, + 'packageId': 1234, + 'prices': [{'id': 1111}, {'id': 2222}], + 'quantity': 1, + 'useHourlyPricing': True} ]} mock_pkg, mock_preset, mock_get_ids = self._patch_for_generate() @@ -568,7 +568,7 @@ def _patch_for_generate(self): # with patchers mock_pkg = mock.patch.object(self.ordering, 'get_package_by_key') mock_preset = mock.patch.object(self.ordering, 'get_preset_by_key') - mock_get_ids = mock.patch.object(self.ordering, 'get_price_id_list') + mock_get_ids = mock.patch.object(self.ordering, 'get_ordering_prices') # start each patcher, and set a cleanup to stop each patcher as well to_return = [] @@ -579,7 +579,7 @@ def _patch_for_generate(self): # set the return values on each of the mocks to_return[0].return_value = {'id': 1234} to_return[1].return_value = {'id': 5678} - to_return[2].return_value = [1111, 2222] + to_return[2].return_value = [{'id': 1111}, {'id': 2222}] return to_return def test_get_location_id_short(self): diff --git a/tests/managers/vs/vs_capacity_tests.py b/tests/managers/vs/vs_capacity_tests.py index fbebc55a5..6e4dd01a8 100644 --- a/tests/managers/vs/vs_capacity_tests.py +++ b/tests/managers/vs/vs_capacity_tests.py @@ -77,8 +77,11 @@ def test_create(self): 'quantity': 5, 'useHourlyPricing': True, 'complexType': 'SoftLayer_Container_Product_Order_Virtual_ReservedCapacity', - 'prices': [{'id': 217561} - ] + 'prices': [{ + 'id': 217561, + 'categories': [{'categoryCode': 'reserved_capacity'}], + 'item': {'keyName': 'B1_1X2_1_YEAR_TERM'} + }] } ] } @@ -103,8 +106,11 @@ def test_create_test(self): 'quantity': 5, 'useHourlyPricing': True, 'complexType': 'SoftLayer_Container_Product_Order_Virtual_ReservedCapacity', - 'prices': [{'id': 217561}], - + 'prices': [{ + 'id': 217561, + 'categories': [{'categoryCode': 'reserved_capacity'}], + 'item': {'keyName': 'B1_1X2_1_YEAR_TERM'} + }] } ] }