Skip to content

Commit 0671651

Browse files
Merge pull request #1782 from allmightyspiff/issues1778
Fixing preset-list pricing table
2 parents 743af77 + e87db34 commit 0671651

File tree

5 files changed

+75
-42
lines changed

5 files changed

+75
-42
lines changed

CONTRIBUTING.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,7 @@ Manager methods should have a decent docblock describing any parameters and what
4040

4141
Docs are generated with [Sphinx](https://docs.readthedocs.io/en/latest/intro/getting-started-with-sphinx.html) and once Sphinx is setup, you can simply do
4242

43-
`make html` in the softlayer-python/docs directory, which should generate the HTML in softlayer-python/docs/_build/html for testing.
43+
`make html` in the softlayer-python/docs directory, which should generate the HTML in `softlayer-python/docs/_build/html` for testing.
4444

4545

4646
## Unit Tests

SoftLayer/CLI/command.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -186,7 +186,7 @@ def format_help_text(self, ctx: click.Context, formatter: click.formatting.HelpF
186186
if text:
187187
text = inspect.cleandoc(text)
188188

189-
self.console.print(f"\n\t{text}\n")
189+
self.console.print(f"\n\t{text}\n", highlight=False)
190190

191191
def format_epilog(self, ctx: click.Context, formatter: click.formatting.HelpFormatter) -> None:
192192
"""Writes the epilog if it exists, then prints out any sub-commands if they exist."""

SoftLayer/CLI/order/preset_list.py

Lines changed: 54 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -8,23 +8,21 @@
88
from SoftLayer.CLI import formatting
99
from SoftLayer.managers import ordering
1010

11-
COLUMNS = ['name',
12-
'keyName',
13-
'description', ]
11+
COLUMNS = ['Name', 'Key Name', 'Description']
1412

1513

1614
@click.command(cls=SLCommand)
1715
@click.argument('package_keyname')
18-
@click.option('--keyword',
19-
help="A word (or string) used to filter preset names.")
20-
@click.option('--prices', '-p', is_flag=True, help='Use --prices to list the server item prices, e.g. --prices')
16+
@click.option('--keyword', help="A word (or string) used to filter preset names.")
17+
@click.option('--prices', '-p', is_flag=True, help='Use --prices to list the server item prices.')
2118
@environment.pass_env
2219
def cli(env, package_keyname, keyword, prices):
2320
"""List package presets.
2421
2522
.. Note::
2623
Presets are set CPU / RAM / Disk allotments. You still need to specify required items.
2724
Some packages do not have presets.
25+
Cost includes all items in a preset, and may include optional items.
2826
2927
::
3028
@@ -34,7 +32,37 @@ def cli(env, package_keyname, keyword, prices):
3432
# List the Bare Metal server presets that include a GPU
3533
slcli order preset-list BARE_METAL_SERVER --keyword gpu
3634
35+
# Get a specific flavor for Virtual Server
36+
slcli order preset-list PUBLIC_CLOUD_SERVER --prices --keyword BL2.56x242x
37+
38+
# All packages with active presets
39+
slcli call-api SoftLayer_Product_Package getAllObjects --mask="mask[id,keyName,activePresetCount]" \
40+
--json-filter='{"activePresets":{"operation":"not null"}}'
41+
┌───────────────────┬──────┬──────────────────────────────────────────────────────────────────┐
42+
│ activePresetCount │ id │ keyName │
43+
├───────────────────┼──────┼──────────────────────────────────────────────────────────────────┤
44+
│ 1 │ 144 │ 3U_GPU │
45+
│ 6 │ 200 │ BARE_METAL_SERVER │
46+
│ 1 │ 571 │ NETWORK_VLAN │
47+
│ 100 │ 835 │ PUBLIC_CLOUD_SERVER │
48+
│ 6 │ 865 │ NETWORK_VLAN_FOR_SERVICE │
49+
│ 5 │ 885 │ 8U_BI_S2_H4 │
50+
│ 56 │ 991 │ TRANSIENT_CLOUD_SERVER │
51+
│ 56 │ 1035 │ SUSPEND_CLOUD_SERVER │
52+
│ 9 │ 1045 │ 2U_BI_S3_H2000 │
53+
│ 7 │ 1075 │ 2U_IC4V_FIXED_CONFIGURATIONS │
54+
│ 32 │ 1109 │ BI_S4_H2000 │
55+
│ 8 │ 1117 │ BI_S4_H4000 │
56+
│ 7 │ 1119 │ BI_S4_H8000 │
57+
│ 5 │ 2636 │ 2U_BI_S4_H2000_AEP_ENABLED │
58+
│ 5 │ 2676 │ 4U_BI_S4_H4000_AEP_ENABLED │
59+
│ 5 │ 2700 │ 4U_BI_S4_H8000_AEP_ENABLED │
60+
│ 6 │ 2866 │ ORACLE_APPLICATION_CLUSTER_CASCADE_LAKE_SCALABLE_FAMILY_4_DRIVES │
61+
│ 1 │ 2874 │ ORACLE_APPLICATION_CLUSTER_COFFEE_LAKE_E2174G │
62+
└───────────────────┴──────┴──────────────────────────────────────────────────────────────────┘
63+
3764
"""
65+
click.secho("*NOTICE*: Cost includes all items in a preset, and may include optional items.", fg="yellow")
3866
manager = ordering.OrderingManager(env.client)
3967

4068
_filter = {}
@@ -43,19 +71,23 @@ def cli(env, package_keyname, keyword, prices):
4371
presets = manager.list_presets(package_keyname, filter=_filter)
4472

4573
if prices:
46-
table = formatting.Table(['keyName', 'priceId', 'Hourly', 'Monthly', 'Restriction', 'Location'])
74+
table = formatting.Table(['Id', 'Key Name', 'Hourly', 'Monthly', 'Location'])
75+
table.align = {"Id": "center", "Key Name": "left", "Hourly": "right", "Monthly": "right", "Location": "center"}
4776
for price in presets:
48-
locations = []
77+
locations_list = []
4978
if price['locations'] != []:
5079
for location in price['locations']:
51-
locations.append(location['name'])
52-
cr_max = get_item_price_data(price['prices'][0], 'capacityRestrictionMaximum')
53-
cr_min = get_item_price_data(price['prices'][0], 'capacityRestrictionMinimum')
54-
cr_type = get_item_price_data(price['prices'][0], 'capacityRestrictionType')
55-
table.add_row([price['keyName'], price['id'],
56-
get_item_price_data(price['prices'][0], 'hourlyRecurringFee'),
57-
get_item_price_data(price['prices'][0], 'recurringFee'),
58-
"%s - %s %s" % (cr_min, cr_max, cr_type), str(locations)])
80+
locations_list.append(location['name'])
81+
locations = ', '.join(locations_list)
82+
else:
83+
locations = "All"
84+
table.add_row([
85+
price['id'],
86+
price['keyName'],
87+
get_item_price_data(price['prices'], 'hourlyRecurringFee'),
88+
get_item_price_data(price['prices'], 'recurringFee'),
89+
locations
90+
])
5991

6092
else:
6193
table = formatting.Table(COLUMNS)
@@ -69,9 +101,10 @@ def cli(env, package_keyname, keyword, prices):
69101
env.fout(table)
70102

71103

72-
def get_item_price_data(price, item_attribute):
104+
def get_item_price_data(prices, item_attribute):
73105
"""Given an SoftLayer_Product_Item_Price, returns its default price data"""
74-
result = '-'
75-
if item_attribute in price:
76-
result = price[item_attribute]
77-
return result
106+
result = 0.0
107+
for this_price in prices:
108+
if item_attribute in this_price:
109+
result = result + float(this_price[item_attribute])
110+
return round(result, 3)

SoftLayer/managers/ordering.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@
1616

1717
PACKAGE_MASK = '''id, name, keyName, isActive, type'''
1818

19-
PRESET_MASK = '''id, name, keyName, description, categories, prices, locations'''
19+
PRESET_MASK = '''id, name, keyName, description, prices[id, hourlyRecurringFee, recurringFee], locations'''
2020

2121

2222
class OrderingManager(object):

tests/CLI/modules/order_tests.py

Lines changed: 18 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -265,15 +265,11 @@ def test_verify_monthly(self):
265265
json.loads(result.output))
266266

267267
def test_preset_list(self):
268-
active_preset1 = {'name': 'active1', 'keyName': 'PRESET1',
269-
'description': 'description1'}
270-
active_preset2 = {'name': 'active2', 'keyName': 'PRESET2',
271-
'description': 'description2'}
272-
acc_preset = {'name': 'account1', 'keyName': 'PRESET3',
273-
'description': 'description3'}
268+
active_preset1 = {'name': 'active1', 'keyName': 'PRESET1', 'description': 'description1'}
269+
active_preset2 = {'name': 'active2', 'keyName': 'PRESET2', 'description': 'description2'}
270+
acc_preset = {'name': 'account1', 'keyName': 'PRESET3', 'description': 'description3'}
274271
active_mock = self.set_mock('SoftLayer_Product_Package', 'getActivePresets')
275-
account_mock = self.set_mock('SoftLayer_Product_Package',
276-
'getAccountRestrictedActivePresets')
272+
account_mock = self.set_mock('SoftLayer_Product_Package', 'getAccountRestrictedActivePresets')
277273
active_mock.return_value = [active_preset1, active_preset2]
278274
account_mock.return_value = [acc_preset]
279275

@@ -282,16 +278,9 @@ def test_preset_list(self):
282278
self.assert_no_fail(result)
283279
self.assert_called_with('SoftLayer_Product_Package', 'getActivePresets')
284280
self.assert_called_with('SoftLayer_Product_Package', 'getAccountRestrictedActivePresets')
285-
self.assertEqual([{'name': 'active1',
286-
'keyName': 'PRESET1',
287-
'description': 'description1'},
288-
{'name': 'active2',
289-
'keyName': 'PRESET2',
290-
'description': 'description2'},
291-
{'name': 'account1',
292-
'keyName': 'PRESET3',
293-
'description': 'description3'}],
294-
json.loads(result.output))
281+
self.assertIn('"Key Name": "PRESET1",', result.output)
282+
self.assertIn('"Description": "description2"', result.output)
283+
self.assertIn('"Name": "account1",', result.output)
295284

296285
def test_preset_list_keywork(self):
297286
result = self.run_command(['order', 'preset-list', 'package', '--keyword', 'testKeyWord'])
@@ -300,9 +289,20 @@ def test_preset_list_keywork(self):
300289
self.assert_called_with('SoftLayer_Product_Package', 'getActivePresets', filter=_filter)
301290

302291
def test_preset_list_prices(self):
292+
active_preset1 = {
293+
'name': 'active1', 'keyName': 'PRESET1', 'description': 'description1', 'locations': [], 'id': 118822,
294+
'prices': [
295+
{'hourlyRecurringFee': 100, 'recurringFee': 55},
296+
{'hourlyRecurringFee': 110, 'recurringFee': 44},
297+
]
298+
}
299+
active_mock = self.set_mock('SoftLayer_Product_Package', 'getActivePresets')
300+
active_mock.return_value = [active_preset1]
303301
result = self.run_command(['order', 'preset-list', 'package', '--prices'])
304302
self.assert_no_fail(result)
305303
self.assert_called_with('SoftLayer_Product_Package', 'getActivePresets')
304+
self.assertIn("210", result.output)
305+
self.assertIn("99", result.output)
306306

307307
def test_location_list(self):
308308
result = self.run_command(['order', 'package-locations', 'package'])

0 commit comments

Comments
 (0)