Skip to content

Commit 4a6b3b4

Browse files
authored
Merge pull request #1 from softlayer/master
update fork
2 parents 058c5ed + 8a755be commit 4a6b3b4

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

52 files changed

+1686
-504
lines changed

CHANGELOG.md

Lines changed: 26 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,31 @@
11
# Change Log
22

3-
4-
## [5.5.1] - 2018-08-31
3+
## [5.6.0] - 2018-10-16
4+
- Changes: https://github.com/softlayer/softlayer-python/compare/v5.5.3...v5.6.0
5+
6+
+ #1026 Support for [Reserved Capacity](https://console.bluemix.net/docs/vsi/vsi_about_reserved.html#about-reserved-virtual-servers)
7+
* `slcli vs capacity create`
8+
* `slcli vs capacity create-guest`
9+
* `slcli vs capacity create-options`
10+
* `slcli vs capacity detail`
11+
* `slcli vs capacity list`
12+
+ #1050 Fix `post_uri` parameter name on docstring
13+
+ #1039 Fixed suspend cloud server order.
14+
+ #1055 Update to use click 7
15+
+ #1053 Add export/import capabilities to/from IBM Cloud Object Storage to the image manager as well as the slcli.
16+
17+
18+
## [5.5.3] - 2018-08-31
19+
- Changes: https://github.com/softlayer/softlayer-python/compare/v5.5.2...v5.5.3
20+
21+
+ Added `slcli user delete`
22+
+ #1023 Added `slcli order quote` to let users create a quote from the slcli.
23+
+ #1032 Fixed vs upgrades when using flavors.
24+
+ #1034 Added pagination to ticket list commands
25+
+ #1037 Fixed DNS manager to be more flexible and support more zone types.
26+
+ #1044 Pinned Click library version at >=5 < 7
27+
28+
## [5.5.2] - 2018-08-31
529
- Changes: https://github.com/softlayer/softlayer-python/compare/v5.5.1...v5.5.2
630

731
+ #1018 Fixed hardware credentials.

SoftLayer/CLI/columns.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -57,7 +57,7 @@ def mask(self):
5757
def get_formatter(columns):
5858
"""This function returns a callback to use with click options.
5959
60-
The retuend function parses a comma-separated value and returns a new
60+
The returned function parses a comma-separated value and returns a new
6161
ColumnFormatter.
6262
6363
:param columns: a list of Column instances

SoftLayer/CLI/core.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -137,7 +137,7 @@ def cli(env,
137137

138138
@cli.resultcallback()
139139
@environment.pass_env
140-
def output_diagnostics(env, verbose=0, **kwargs):
140+
def output_diagnostics(env, result, verbose=0, **kwargs):
141141
"""Output diagnostic information."""
142142

143143
if verbose > 0:

SoftLayer/CLI/image/export.py

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -12,17 +12,25 @@
1212
@click.command()
1313
@click.argument('identifier')
1414
@click.argument('uri')
15+
@click.option('--ibm-api-key',
16+
default=None,
17+
help="The IBM Cloud API Key with access to IBM Cloud Object "
18+
"Storage instance. For help creating this key see "
19+
"https://console.bluemix.net/docs/services/cloud-object-"
20+
"storage/iam/users-serviceids.html#serviceidapikeys")
1521
@environment.pass_env
16-
def cli(env, identifier, uri):
22+
def cli(env, identifier, uri, ibm_api_key):
1723
"""Export an image to object storage.
1824
1925
The URI for an object storage object (.vhd/.iso file) of the format:
2026
swift://<objectStorageAccount>@<cluster>/<container>/<objectPath>
27+
or cos://<clusterName>/<bucketName>/<objectPath> if using IBM Cloud
28+
Object Storage
2129
"""
2230

2331
image_mgr = SoftLayer.ImageManager(env.client)
2432
image_id = helpers.resolve_id(image_mgr.resolve_ids, identifier, 'image')
25-
result = image_mgr.export_image_to_uri(image_id, uri)
33+
result = image_mgr.export_image_to_uri(image_id, uri, ibm_api_key)
2634

2735
if not result:
2836
raise exceptions.CLIAbort("Failed to export Image")

SoftLayer/CLI/image/import.py

Lines changed: 39 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -16,15 +16,44 @@
1616
default="",
1717
help="The note to be applied to the imported template")
1818
@click.option('--os-code',
19-
default="",
2019
help="The referenceCode of the operating system software"
21-
" description for the imported VHD")
20+
" description for the imported VHD, ISO, or RAW image")
21+
@click.option('--ibm-api-key',
22+
default=None,
23+
help="The IBM Cloud API Key with access to IBM Cloud Object "
24+
"Storage instance and IBM KeyProtect instance. For help "
25+
"creating this key see https://console.bluemix.net/docs/"
26+
"services/cloud-object-storage/iam/users-serviceids.html"
27+
"#serviceidapikeys")
28+
@click.option('--root-key-id',
29+
default=None,
30+
help="ID of the root key in Key Protect")
31+
@click.option('--wrapped-dek',
32+
default=None,
33+
help="Wrapped Data Encryption Key provided by IBM KeyProtect. "
34+
"For more info see https://console.bluemix.net/docs/"
35+
"services/key-protect/wrap-keys.html#wrap-keys")
36+
@click.option('--kp-id',
37+
default=None,
38+
help="ID of the IBM Key Protect Instance")
39+
@click.option('--cloud-init',
40+
is_flag=True,
41+
help="Specifies if image is cloud-init")
42+
@click.option('--byol',
43+
is_flag=True,
44+
help="Specifies if image is bring your own license")
45+
@click.option('--is-encrypted',
46+
is_flag=True,
47+
help="Specifies if image is encrypted")
2248
@environment.pass_env
23-
def cli(env, name, note, os_code, uri):
49+
def cli(env, name, note, os_code, uri, ibm_api_key, root_key_id, wrapped_dek,
50+
kp_id, cloud_init, byol, is_encrypted):
2451
"""Import an image.
2552
2653
The URI for an object storage object (.vhd/.iso file) of the format:
2754
swift://<objectStorageAccount>@<cluster>/<container>/<objectPath>
55+
or cos://<clusterName>/<bucketName>/<objectPath> if using IBM Cloud
56+
Object Storage
2857
"""
2958

3059
image_mgr = SoftLayer.ImageManager(env.client)
@@ -33,6 +62,13 @@ def cli(env, name, note, os_code, uri):
3362
note=note,
3463
os_code=os_code,
3564
uri=uri,
65+
ibm_api_key=ibm_api_key,
66+
root_key_id=root_key_id,
67+
wrapped_dek=wrapped_dek,
68+
kp_id=kp_id,
69+
cloud_init=cloud_init,
70+
byol=byol,
71+
is_encrypted=is_encrypted
3672
)
3773

3874
if not result:

SoftLayer/CLI/routes.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@
3030
('virtual:reload', 'SoftLayer.CLI.virt.reload:cli'),
3131
('virtual:upgrade', 'SoftLayer.CLI.virt.upgrade:cli'),
3232
('virtual:credentials', 'SoftLayer.CLI.virt.credentials:cli'),
33+
('virtual:capacity', 'SoftLayer.CLI.virt.capacity:cli'),
3334

3435
('dedicatedhost', 'SoftLayer.CLI.dedicatedhost'),
3536
('dedicatedhost:list', 'SoftLayer.CLI.dedicatedhost.list:cli'),
Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
"""Manages Reserved Capacity."""
2+
# :license: MIT, see LICENSE for more details.
3+
4+
import importlib
5+
import os
6+
7+
import click
8+
9+
CONTEXT = {'help_option_names': ['-h', '--help'],
10+
'max_content_width': 999}
11+
12+
13+
class CapacityCommands(click.MultiCommand):
14+
"""Loads module for capacity related commands.
15+
16+
Will automatically replace _ with - where appropriate.
17+
I'm not sure if this is better or worse than using a long list of manual routes, so I'm trying it here.
18+
CLI/virt/capacity/create_guest.py -> slcli vs capacity create-guest
19+
"""
20+
21+
def __init__(self, **attrs):
22+
click.MultiCommand.__init__(self, **attrs)
23+
self.path = os.path.dirname(__file__)
24+
25+
def list_commands(self, ctx):
26+
"""List all sub-commands."""
27+
commands = []
28+
for filename in os.listdir(self.path):
29+
if filename == '__init__.py':
30+
continue
31+
if filename.endswith('.py'):
32+
commands.append(filename[:-3].replace("_", "-"))
33+
commands.sort()
34+
return commands
35+
36+
def get_command(self, ctx, cmd_name):
37+
"""Get command for click."""
38+
path = "%s.%s" % (__name__, cmd_name)
39+
path = path.replace("-", "_")
40+
module = importlib.import_module(path)
41+
return getattr(module, 'cli')
42+
43+
44+
# Required to get the sub-sub-sub command to work.
45+
@click.group(cls=CapacityCommands, context_settings=CONTEXT)
46+
def cli():
47+
"""Base command for all capacity related concerns"""
48+
pass
Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
"""Create a Reserved Capacity instance."""
2+
3+
import click
4+
5+
6+
from SoftLayer.CLI import environment
7+
from SoftLayer.CLI import formatting
8+
from SoftLayer.managers.vs_capacity import CapacityManager as CapacityManager
9+
10+
11+
@click.command(epilog=click.style("""WARNING: Reserved Capacity is on a yearly contract"""
12+
""" and not cancelable until the contract is expired.""", fg='red'))
13+
@click.option('--name', '-n', required=True, prompt=True,
14+
help="Name for your new reserved capacity")
15+
@click.option('--backend_router_id', '-b', required=True, prompt=True, type=int,
16+
help="backendRouterId, create-options has a list of valid ids to use.")
17+
@click.option('--flavor', '-f', required=True, prompt=True,
18+
help="Capacity keyname (C1_2X2_1_YEAR_TERM for example).")
19+
@click.option('--instances', '-i', required=True, prompt=True, type=int,
20+
help="Number of VSI instances this capacity reservation can support.")
21+
@click.option('--test', is_flag=True,
22+
help="Do not actually create the virtual server")
23+
@environment.pass_env
24+
def cli(env, name, backend_router_id, flavor, instances, test=False):
25+
"""Create a Reserved Capacity instance.
26+
27+
*WARNING*: Reserved Capacity is on a yearly contract and not cancelable until the contract is expired.
28+
"""
29+
manager = CapacityManager(env.client)
30+
31+
result = manager.create(
32+
name=name,
33+
backend_router_id=backend_router_id,
34+
flavor=flavor,
35+
instances=instances,
36+
test=test)
37+
if test:
38+
table = formatting.Table(['Name', 'Value'], "Test Order")
39+
container = result['orderContainers'][0]
40+
table.add_row(['Name', container['name']])
41+
table.add_row(['Location', container['locationObject']['longName']])
42+
for price in container['prices']:
43+
table.add_row(['Contract', price['item']['description']])
44+
table.add_row(['Hourly Total', result['postTaxRecurring']])
45+
else:
46+
table = formatting.Table(['Name', 'Value'], "Reciept")
47+
table.add_row(['Order Date', result['orderDate']])
48+
table.add_row(['Order ID', result['orderId']])
49+
table.add_row(['status', result['placedOrder']['status']])
50+
table.add_row(['Hourly Total', result['orderDetails']['postTaxRecurring']])
51+
env.fout(table)
Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,63 @@
1+
"""List Reserved Capacity"""
2+
# :license: MIT, see LICENSE for more details.
3+
4+
import click
5+
6+
from SoftLayer.CLI import environment
7+
from SoftLayer.CLI import formatting
8+
from SoftLayer.CLI import helpers
9+
from SoftLayer.CLI.virt.create import _parse_create_args as _parse_create_args
10+
from SoftLayer.CLI.virt.create import _update_with_like_args as _update_with_like_args
11+
from SoftLayer.managers.vs_capacity import CapacityManager as CapacityManager
12+
13+
14+
@click.command()
15+
@click.option('--capacity-id', type=click.INT, help="Reserve capacity Id to provision this guest into.")
16+
@click.option('--primary-disk', type=click.Choice(['25', '100']), default='25', help="Size of the main drive.")
17+
@click.option('--hostname', '-H', required=True, prompt=True, help="Host portion of the FQDN.")
18+
@click.option('--domain', '-D', required=True, prompt=True, help="Domain portion of the FQDN.")
19+
@click.option('--os', '-o', help="OS install code. Tip: you can specify <OS>_LATEST.")
20+
@click.option('--image', help="Image ID. See: 'slcli image list' for reference.")
21+
@click.option('--boot-mode', type=click.STRING,
22+
help="Specify the mode to boot the OS in. Supported modes are HVM and PV.")
23+
@click.option('--postinstall', '-i', help="Post-install script to download.")
24+
@helpers.multi_option('--key', '-k', help="SSH keys to add to the root user.")
25+
@helpers.multi_option('--disk', help="Additional disk sizes.")
26+
@click.option('--private', is_flag=True, help="Forces the VS to only have access the private network.")
27+
@click.option('--like', is_eager=True, callback=_update_with_like_args,
28+
help="Use the configuration from an existing VS.")
29+
@click.option('--network', '-n', help="Network port speed in Mbps.")
30+
@helpers.multi_option('--tag', '-g', help="Tags to add to the instance.")
31+
@click.option('--userdata', '-u', help="User defined metadata string.")
32+
@click.option('--ipv6', is_flag=True, help="Adds an IPv6 address to this guest")
33+
@click.option('--test', is_flag=True,
34+
help="Test order, will return the order container, but not actually order a server.")
35+
@environment.pass_env
36+
def cli(env, **args):
37+
"""Allows for creating a virtual guest in a reserved capacity."""
38+
create_args = _parse_create_args(env.client, args)
39+
if args.get('ipv6'):
40+
create_args['ipv6'] = True
41+
create_args['primary_disk'] = args.get('primary_disk')
42+
manager = CapacityManager(env.client)
43+
capacity_id = args.get('capacity_id')
44+
test = args.get('test')
45+
46+
result = manager.create_guest(capacity_id, test, create_args)
47+
48+
env.fout(_build_receipt(result, test))
49+
50+
51+
def _build_receipt(result, test=False):
52+
title = "OrderId: %s" % (result.get('orderId', 'No order placed'))
53+
table = formatting.Table(['Item Id', 'Description'], title=title)
54+
table.align['Description'] = 'l'
55+
56+
if test:
57+
prices = result['prices']
58+
else:
59+
prices = result['orderDetails']['prices']
60+
61+
for item in prices:
62+
table.add_row([item['id'], item['item']['description']])
63+
return table
Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
"""List options for creating Reserved Capacity"""
2+
# :license: MIT, see LICENSE for more details.
3+
4+
import click
5+
6+
from SoftLayer.CLI import environment
7+
from SoftLayer.CLI import formatting
8+
from SoftLayer.managers.vs_capacity import CapacityManager as CapacityManager
9+
10+
11+
@click.command()
12+
@environment.pass_env
13+
def cli(env):
14+
"""List options for creating Reserved Capacity"""
15+
manager = CapacityManager(env.client)
16+
items = manager.get_create_options()
17+
18+
items.sort(key=lambda term: int(term['capacity']))
19+
table = formatting.Table(["KeyName", "Description", "Term", "Default Hourly Price Per Instance"],
20+
title="Reserved Capacity Options")
21+
table.align["Hourly Price"] = "l"
22+
table.align["Description"] = "l"
23+
table.align["KeyName"] = "l"
24+
for item in items:
25+
table.add_row([
26+
item['keyName'], item['description'], item['capacity'], get_price(item)
27+
])
28+
env.fout(table)
29+
30+
regions = manager.get_available_routers()
31+
location_table = formatting.Table(['Location', 'POD', 'BackendRouterId'], 'Orderable Locations')
32+
for region in regions:
33+
for location in region['locations']:
34+
for pod in location['location']['pods']:
35+
location_table.add_row([region['keyname'], pod['backendRouterName'], pod['backendRouterId']])
36+
env.fout(location_table)
37+
38+
39+
def get_price(item):
40+
"""Finds the price with the default locationGroupId"""
41+
the_price = "No Default Pricing"
42+
for price in item.get('prices', []):
43+
if not price.get('locationGroupId'):
44+
the_price = "%0.4f" % float(price['hourlyRecurringFee'])
45+
return the_price

0 commit comments

Comments
 (0)