Skip to content

Commit 08255fe

Browse files
author
Ryan Rossiter
committed
Add CLI doc and move order generation
verifyOrder and placeOrder need to be called with the same generated order dict, and the output of verifyOrder can't be sent to placeOrder, since it strips out some extra data.
1 parent 010f6f8 commit 08255fe

File tree

8 files changed

+271
-59
lines changed

8 files changed

+271
-59
lines changed

SoftLayer/CLI/order/category_list.py

Lines changed: 18 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,24 @@
1717
help="List only the required categories for the package")
1818
@environment.pass_env
1919
def cli(env, package_keyname, required):
20-
"""List package categories."""
20+
"""List the categories of a package.
21+
22+
Package keynames can be retrieved from `slcli order package-list`
23+
24+
\b
25+
Example:
26+
# List the categories of Bare Metal servers
27+
slcli order category-list BARE_METAL_SERVER
28+
29+
When using the --required flag, it will list out only the categories
30+
that are required for ordering that package (see `slcli order item-list`)
31+
32+
\b
33+
Example:
34+
# List the required categories for Bare Metal servers
35+
slcli order category-list BARE_METAL_SERVER --required
36+
37+
"""
2138
client = env.client
2239
manager = ordering.OrderingManager(client)
2340
table = formatting.Table(COLUMNS)

SoftLayer/CLI/order/item_list.py

Lines changed: 22 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,28 @@
1919
help="Category code to filter items by")
2020
@environment.pass_env
2121
def cli(env, package_keyname, keyword, category):
22-
"""List package items."""
22+
"""List package items used for ordering.
23+
24+
The items listed can be used with `slcli order place` to specify
25+
the items that are being ordered in the package.
26+
27+
Package keynames can be retrieved using `slcli order package-list`
28+
29+
\b
30+
Example:
31+
# List all items in the VSI package
32+
slcli order item-list CLOUD_SERVER
33+
34+
The --keyword option is used to filter items by name.
35+
The --category option is used to filter items by category.
36+
Both --keyword and --category can be used together.
37+
38+
\b
39+
Example:
40+
# List Ubuntu OSes from the os category of the Bare Metal package
41+
slcli order item-list BARE_METAL_SERVER --category os --keyword ubuntu
42+
43+
"""
2344
table = formatting.Table(COLUMNS)
2445
manager = ordering.OrderingManager(env.client)
2546

SoftLayer/CLI/order/package_list.py

Lines changed: 18 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
"""List package presets."""
1+
"""List packages."""
22
# :license: MIT, see LICENSE for more details.
33

44
import click
@@ -16,7 +16,23 @@
1616
help="A word (or string) used to filter package names.")
1717
@environment.pass_env
1818
def cli(env, keyword):
19-
"""List package presets."""
19+
"""List packages that can be ordered via the placeOrder API.
20+
21+
\b
22+
Example:
23+
# List out all packages for ordering
24+
slcli order package-list
25+
26+
27+
Keywords can also be used for some simple filtering functionality
28+
to help find a package easier.
29+
30+
\b
31+
Example:
32+
# List out all packages with "server" in the name
33+
slcli order package-list --keyword server
34+
35+
"""
2036
manager = ordering.OrderingManager(env.client)
2137
table = formatting.Table(COLUMNS)
2238

SoftLayer/CLI/order/place.py

Lines changed: 42 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -28,12 +28,51 @@
2828
default='hourly',
2929
show_default=True,
3030
help="Billing rate")
31+
@click.option('--complex-type', help=("The complex type of the order. This typically begins"
32+
" with 'SoftLayer_Container_Product_Order_'."))
3133
@click.option('--extras',
3234
help="JSON string denoting extra data that needs to be sent with the order")
3335
@click.argument('order_items', nargs=-1)
3436
@environment.pass_env
35-
def cli(env, package_keyname, location, preset, verify, billing, extras, order_items):
36-
"""Place or verify an order."""
37+
def cli(env, package_keyname, location, preset, verify, billing, complex_type,
38+
extras, order_items):
39+
"""Place or verify an order.
40+
41+
This CLI command is used for placing/verifying an order of the specified package in
42+
the given location (denoted by a datacenter's long name). Orders made via the CLI
43+
can then be converted to be made programmatically by calling
44+
SoftLayer.OrderingManager.place_order() with the same keynames.
45+
46+
Packages for ordering can be retrived from `slcli order package-list`
47+
Presets for ordering can be retrieved from `slcli order preset-list` (not all packages
48+
have presets)
49+
50+
Items can be retrieved from `slcli order item-list`. In order to find required
51+
items for the order, use `slcli order category-list`, and then provide the
52+
--category option for each category code in `slcli order item-list`.
53+
54+
\b
55+
Example:
56+
# Order an hourly VSI with 4 CPU, 16 GB RAM, 100 GB SAN disk,
57+
# Ubuntu 16.04, and 1 Gbps public & private uplink in dal13
58+
slcli order place --billing hourly CLOUD_SERVER DALLAS13 \\
59+
GUEST_CORES_4 \\
60+
RAM_16_GB \\
61+
REBOOT_REMOTE_CONSOLE \\
62+
1_GBPS_PUBLIC_PRIVATE_NETWORK_UPLINKS \\
63+
BANDWIDTH_0_GB_2 \\
64+
1_IP_ADDRESS \\
65+
GUEST_DISK_100_GB_SAN \\
66+
OS_UBUNTU_16_04_LTS_XENIAL_XERUS_MINIMAL_64_BIT_FOR_VSI \\
67+
MONITORING_HOST_PING \\
68+
NOTIFICATION_EMAIL_AND_TICKET \\
69+
AUTOMATED_NOTIFICATION \\
70+
UNLIMITED_SSL_VPN_USERS_1_PPTP_VPN_USER_PER_ACCOUNT \\
71+
NESSUS_VULNERABILITY_ASSESSMENT_REPORTING \\
72+
--extras '{"virtualGuests": [{"hostname": "test", "domain": "softlayer.com"}]}' \\
73+
--complex-type SoftLayer_Container_Product_Order_Virtual_Guest
74+
75+
"""
3776
manager = ordering.OrderingManager(env.client)
3877

3978
if extras:
@@ -43,6 +82,7 @@ def cli(env, package_keyname, location, preset, verify, billing, extras, order_i
4382
kwargs = {'preset_keyname': preset,
4483
'extras': extras,
4584
'quantity': 1,
85+
'complex_type': complex_type,
4686
'hourly': True if billing == 'hourly' else False}
4787

4888
if verify:

SoftLayer/CLI/order/preset_list.py

Lines changed: 20 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -18,13 +18,31 @@
1818
help="A word (or string) used to filter preset names.")
1919
@environment.pass_env
2020
def cli(env, package_keyname, keyword):
21-
"""List package presets."""
21+
"""List package presets.
22+
23+
Package keynames can be retrieved from `slcli order package-list`.
24+
Some packages do not have presets.
25+
26+
\b
27+
Example:
28+
# List the presets for Bare Metal servers
29+
slcli order preset-list BARE_METAL_SERVER
30+
31+
The --keyword option can also be used for additional filtering on
32+
the returned presets.
33+
34+
\b
35+
Example:
36+
# List the Bare Metal server presets that include a GPU
37+
slcli order preset-list BARE_METAL_SERVER --keyword gpu
38+
39+
"""
2240
table = formatting.Table(COLUMNS)
2341
manager = ordering.OrderingManager(env.client)
2442

2543
_filter = {}
2644
if keyword:
27-
_filter = {'presets': {'name': {'operation': '*= %s' % keyword}}}
45+
_filter = {'activePresets': {'name': {'operation': '*= %s' % keyword}}}
2846
presets = manager.list_presets(package_keyname, filter=_filter)
2947

3048
for preset in presets:

SoftLayer/managers/ordering.py

Lines changed: 71 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
66
:license: MIT, see LICENSE for more details.
77
"""
8+
# pylint: disable=no-self-use
89

910
from SoftLayer import exceptions
1011

@@ -380,7 +381,7 @@ def get_price_id_list(self, package_keyname, item_keynames):
380381

381382
return prices
382383

383-
def verify_order(self, package_keyname, location, item_keynames,
384+
def verify_order(self, package_keyname, location, item_keynames, complex_type=None,
384385
hourly=True, preset_keyname=None, extras=None, quantity=1):
385386
"""Verifies an order with the given package and prices.
386387
@@ -392,6 +393,8 @@ def verify_order(self, package_keyname, location, item_keynames,
392393
:param list item_keynames: The list of item keyname strings to order. To see list of
393394
possible keynames for a package, use list_items()
394395
(or `slcli order item-list`)
396+
:param str complex_type: The complex type to send with the order. Typically begins
397+
with 'SoftLayer_Container_Product_Order_'.
395398
:param bool hourly: If true, uses hourly billing, otherwise uses monthly billing
396399
:param string preset_keyname: If needed, specifies a preset to use for that package.
397400
To see a list of possible keynames for a package, use
@@ -404,33 +407,13 @@ def verify_order(self, package_keyname, location, item_keynames,
404407
:param int quantity: The number of resources to order
405408
406409
"""
407-
order = {}
408-
extras = extras or {}
409-
410-
package = self.get_package_by_key(package_keyname, mask='id')
411-
if not package:
412-
raise exceptions.SoftLayerError("Package {} does not exist".format(package_keyname))
413-
414-
# if there was extra data given for the order, add it to the order
415-
# example: VSIs require hostname and domain set on the order, so
416-
# extras will be {'virtualGuests': [{'hostname': 'test',
417-
# 'domain': 'softlayer.com'}]}
418-
order.update(extras)
419-
order['packageId'] = package['id']
420-
order['location'] = location
421-
order['quantity'] = quantity
422-
order['useHourlyPricing'] = hourly
423-
424-
if preset_keyname:
425-
preset_id = self.get_preset_by_key(package_keyname, preset_keyname)['id']
426-
order['presetId'] = preset_id
427-
428-
price_ids = self.get_price_id_list(package_keyname, item_keynames)
429-
order['prices'] = [{'id': price_id} for price_id in price_ids]
430-
410+
order = self.generate_order(package_keyname, location, item_keynames,
411+
complex_type=complex_type, hourly=hourly,
412+
preset_keyname=preset_keyname,
413+
extras=extras, quantity=quantity)
431414
return self.order_svc.verifyOrder(order)
432415

433-
def place_order(self, package_keyname, location, item_keynames,
416+
def place_order(self, package_keyname, location, item_keynames, complex_type=None,
434417
hourly=True, preset_keyname=None, extras=None, quantity=1):
435418
"""Places an order with the given package and prices.
436419
@@ -441,6 +424,40 @@ def place_order(self, package_keyname, location, item_keynames,
441424
:param list item_keynames: The list of item keyname strings to order. To see list of
442425
possible keynames for a package, use list_items()
443426
(or `slcli order item-list`)
427+
:param str complex_type: The complex type to send with the order. Typically begins
428+
with 'SoftLayer_Container_Product_Order_'.
429+
:param bool hourly: If true, uses hourly billing, otherwise uses monthly billing
430+
:param string preset_keyname: If needed, specifies a preset to use for that package.
431+
To see a list of possible keynames for a package, use
432+
list_preset() (or `slcli order preset-list`)
433+
:param dict extras: The extra data for the order in dictionary format.
434+
Example: A VSI order requires hostname and domain to be set, so
435+
extras will look like the following:
436+
{'virtualGuests': [{'hostname': 'test',
437+
'domain': 'softlayer.com'}]}
438+
:param int quantity: The number of resources to order
439+
440+
"""
441+
order = self.generate_order(package_keyname, location, item_keynames,
442+
complex_type=complex_type, hourly=hourly,
443+
preset_keyname=preset_keyname,
444+
extras=extras, quantity=quantity)
445+
return self.order_svc.placeOrder(order)
446+
447+
def generate_order(self, package_keyname, location, item_keynames, complex_type=None,
448+
hourly=True, preset_keyname=None, extras=None, quantity=1):
449+
"""Generates an order with the given package and prices.
450+
451+
This function takes in parameters needed for an order and generates an order
452+
dictionary. This dictionary can then be used in either verify or placeOrder().
453+
454+
:param str package_keyname: The keyname for the package being ordered
455+
:param str location: The datacenter location string for ordering (Ex: DALLAS13)
456+
:param list item_keynames: The list of item keyname strings to order. To see list of
457+
possible keynames for a package, use list_items()
458+
(or `slcli order item-list`)
459+
:param str complex_type: The complex type to send with the order. Typically begins
460+
with 'SoftLayer_Container_Product_Order_'.
444461
:param bool hourly: If true, uses hourly billing, otherwise uses monthly billing
445462
:param string preset_keyname: If needed, specifies a preset to use for that package.
446463
To see a list of possible keynames for a package, use
@@ -453,10 +470,31 @@ def place_order(self, package_keyname, location, item_keynames,
453470
:param int quantity: The number of resources to order
454471
455472
"""
456-
# verify the order, and if the order is valid, the proper prices will be filled
457-
# into the order template, so we can just send that to placeOrder to order it
458-
verified_order = self.verify_order(package_keyname, location, item_keynames,
459-
hourly=hourly,
460-
preset_keyname=preset_keyname,
461-
extras=extras, quantity=quantity)
462-
return self.order_svc.placeOrder(verified_order)
473+
order = {}
474+
extras = extras or {}
475+
476+
package = self.get_package_by_key(package_keyname, mask='id')
477+
if not package:
478+
raise exceptions.SoftLayerError("Package {} does not exist".format(package_keyname))
479+
480+
# if there was extra data given for the order, add it to the order
481+
# example: VSIs require hostname and domain set on the order, so
482+
# extras will be {'virtualGuests': [{'hostname': 'test',
483+
# 'domain': 'softlayer.com'}]}
484+
order.update(extras)
485+
order['packageId'] = package['id']
486+
order['location'] = location
487+
order['quantity'] = quantity
488+
order['useHourlyPricing'] = hourly
489+
490+
if preset_keyname:
491+
preset_id = self.get_preset_by_key(package_keyname, preset_keyname)['id']
492+
order['presetId'] = preset_id
493+
494+
if not complex_type:
495+
raise exceptions.SoftLayerError("A complex type must be specified with the order")
496+
order['complexType'] = complex_type
497+
498+
price_ids = self.get_price_id_list(package_keyname, item_keynames)
499+
order['prices'] = [{'id': price_id} for price_id in price_ids]
500+
return order

tests/CLI/modules/order_tests.py

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -75,10 +75,10 @@ def test_place(self):
7575
place_mock.return_value = order
7676
items_mock.return_value = self._get_order_items()
7777

78-
result = self.run_command(['-y', 'order', 'place', 'package', 'DALLAS13', 'ITEM1'])
78+
result = self.run_command(['-y', 'order', 'place', 'package', 'DALLAS13', 'ITEM1',
79+
'--complex-type', 'SoftLayer_Container_Product_Order_Thing'])
7980

8081
self.assert_no_fail(result)
81-
self.assert_called_with('SoftLayer_Product_Order', 'verifyOrder')
8282
self.assert_called_with('SoftLayer_Product_Order', 'placeOrder')
8383
self.assertEqual({'id': 1234,
8484
'created': order_date,
@@ -97,6 +97,7 @@ def test_verify_hourly(self):
9797
items_mock.return_value = self._get_order_items()
9898

9999
result = self.run_command(['order', 'place', '--billing', 'hourly', '--verify',
100+
'--complex-type', 'SoftLayer_Container_Product_Order_Thing',
100101
'package', 'DALLAS13', 'ITEM1', 'ITEM2'])
101102

102103
self.assert_no_fail(result)
@@ -123,6 +124,7 @@ def test_verify_monthly(self):
123124
items_mock.return_value = self._get_order_items()
124125

125126
result = self.run_command(['order', 'place', '--billing', 'monthly', '--verify',
127+
'--complex-type', 'SoftLayer_Container_Product_Order_Thing',
126128
'package', 'DALLAS13', 'ITEM1', 'ITEM2'])
127129

128130
self.assert_no_fail(result)

0 commit comments

Comments
 (0)