August 10, 2021


The SoftLayer Python Library

Covers using the softlayer-python project to interact with the SoftLayer API, along with how to use the `slcli`, a command line tool, which is bundled with the softlayer-python library

The Basics

  • softlayer-python project homepage. For source code and submitting issues.
  • Documentation Covers both the SLCLI and softlayer-python library.
  • PyPi project For latest information about the softlayer-python package, which is kept updated on PyPi.

Python 2.7 Support

As of version 5.8.0 SoftLayer-Python will no longer support python2.7, which is End Of Life as of 2020 . If you cannot install python 3.6+ for some reason, you will need to use a version of softlayer-python <= 5.7.2


Authentication

When making API calls in python, you will first create an instance of the SoftLayer Client class. This client object is responsible for all the communication with the SoftLayer API server.

The first step for setting up a client is getting authentication information, and there are several ways to do this.

To get started, we will assume you are setting up your code something like this:

import SoftLayer
# This is nice for printing out results from the API as raw JSON
from pprint import pprint as pp 

client = SoftLayer.create_client_from_env()

By default, SoftLayer.create_client_from_env() will read in the configuration from a file in your home directory called ~/.softlayer ( and ~/AppData/Roaming/softlayer on Windows)

This file can be setup manually with the slcli config setup command, or if you already know what the config should be, this file can be set up by copying over a known good configuration.

Listed below is an example config file, with comments and optional settings.

$ cat ~/.softlayer
[softlayer]
username = SLUserName
api_key = 64CharacterLongApiKeyGoesHere

# For IBMCloud ApiKeys
# https://sldn.softlayer.com/article/authenticating-softlayer-api/#cloud-api
# username = apikey
# api_key = 32CharacterLongKey

# Both rest and xmlrpc will work with this client
# however the format of the request, and output of any logging/debugging 
# information will change based on this.
endpoint_url = https://api.softlayer.com/rest/v3.1
#endpoint_url = https://api.softlayer.com/xmlrpc/v3.1

# Set to a number to stop waiting after that many seconds
timeout = 0

# If you need to proxy requests through something
# proxy = 'http://somehost'

You can also use the following environment variables to control authentication.

  • https_proxy: For anyone needing to proxy requests through a middle server
  • SL_USERNAME: username in the config
  • SL_API_KEY: api_key in the config

Setting during client creation

You can of course set these variables when you are creating the client itself as well.

client = SoftLayer.create_client_from_env(
    username='SL_USERNAME', api_key="64CharacterLongAPiKey"
)

See SoftLayer.create_client_from_env() for a full list of parameters.

The only ones that are not checked for in the config are

  • config_file: which will just tell the client to read from a non-default location
  • user_agent: Make API calls with a non-standard user_agent. Can be somewhat useful if you want your API calls to have some style in our API logs.
  • transport : Useful if you want to use a debugging transport, or maybe some special modified transport.
  • verify: which will prevent SSL verification checks. Don’t turn this off unless you mean to.

Now that the client is setup, we are able to make API calls and get data back.

client.call() is generally what you should use to interact with the API. The client.call() documentation has a full listing of all the supported parameters, but the basic syntax is as follows:

result = client.call('SoftLayer_Account', 'getObject')
pp(result)

Making API Calls has some more examples of how to use this method if needed.

client.iter_call() is useful if you want to iterate over a large return set from the API. It takes the same arguments as client.call(), however this method will return a Python Generator and only make API calls to get more data when you read the end of the current data set.

For example, the following code will first get all virtual guests on the account, THEN lookup information on each of them in turn.


virtualGuests = client.call('SoftLayer_Account', 'getVirtualGuests', mask="mask[id]", iter=True)
for guest in virtualGuest:
    guestInfo = client.call('SoftLayer_Virtual_Guest', 'getObject', id=guest.get('id'))
    pp(guestInfo)

However the following code will get virtual guests in batches of 10, get the info on each, then get the next batch of 10. This will continue until the client gets less than 10 results back

virtualGuests = client.iter_call('SoftLayer_Account', 'getVirtualGuests', mask="mask[id]", limit=10)
for guest in virtualGuest:
    guestInfo = client.call('SoftLayer_Virtual_Guest', 'getObject', id=guest.get('id'))
    pp(guestInfo)

This is mostly useful when getting all the results back from the API might take a very long time, and you want to do something in between API calls.

Service.call()

This is an older form of making API calls and you may see it in some examples occasionally. It is supported, but if you are writing new code you should use client.call()

The basic format for this type is like this:

client['SoftLayer_Account'].getObject()

The parameters you pass into that method are the same as client.call(), it is basically just formatted different.

If a specific method takes input parameters, for example SoftLayer_Dns_Domain::createARecord() takes 3 parameters (host, data, ttl). You specify these parameters as function parameters to client.call().

NOTE: Parameters are specified in the order they appear in the documentation. They are NOT specified by name.

domain_id = 123456
try:
    new_record = client.call('SoftLayer_Dns_Domain', 'createARecord', 'newHost', '1.2.3.4', '6000', id=domain_id)
    pp(new_record)
except SoftLayer.SoftLayerAPIError as e:
    print("Unable to create domain\nError: {}".format(e))

Object Masks are a complex concept of the API, but setting them is straightforward.

The basics is that it should be a string, starting with mask[ and ending with ], and between should be the list of properties you want to get back from the API. If you want to select propertyX of a property1, propertyX needs to be enclosed in [] and the end of property1, like this:

mask = "mask[id, hostname, backendRouters[hostname]]"
guests = client.call('SoftLayer_Account', 'getVirtualGuests', mask=mask)
for guest in guests:
    pp(guest)

Object Filters are another critical API concept. In this case you would specify them as a Python nested dictionary.

_filter = {"virtualGuests": {"hostname": {"operation": "testHost"}}}
guests = client.call('SoftLayer_Account', 'getVirtualGuests', filter=_filter)
for guest in guests:
    pp(guest)

You can automatically paginate API requests by using the iter=True parameter of client.call(), or by using client.iter_call() directly. If you want to control pagination yourself you can use the offset and limit paramters.

API results are not guaranteed to be in order from one API call to another, so if you do pagination, make sure to use an OrderBy Object Filter on some unique property

from SoftLayer import utils

_filter = {"virtualGuests": {"id": utils.query_filter_orderby()}
guests = client.call('SoftLayer_Account', 'getVirtualGuests', filter=_filter, limit=10, offset=0)
for guest in guests:
    pp(guest)

SoftLayer.Exceptions has a few defined Exceptions for handling API related errors.

try:
    account = client.call('SoftLayer_Account', 'getObject')
except SoftLayer.SoftLayerAPIError as e:
    print("Unable to retrieve account information faultCode={}, faultString={}".format(e.faultCode, e.faultString)


Retry Decorator

The Retry Decorator is a Python Decorator that will automatically retry an API call based on a few types of API issues.

The virtual guest manager has a few good examples of this in practice

import logging
from SoftLayer.decoration import retry

LOGGER = logging.getLogger(__name__)


@retry(logger=LOGGER)
def listGuests():
    return client.call('Account', 'getVirtualGuests')

If during this API call we get one of the following exceptions, the code will automatically retry after waiting a little bit.

    exceptions.ServerError,
    exceptions.ApplicationError,
    exceptions.RemoteSystemError,

Debugging

The Client object keeps track of all API calls made, and has a handy feature to be able to print them out in a raw format that doesn’t rely on the SoftLayer library.

If you are using the XMLRPC endpoint, this will result it a bit of python code that will directly use the requests library to send in the API call and show the payload it is sending.

The REST endpoint though will print out a nice CURL command that is nice and copy-pasteable.

Debugging Documentation has the steps needed to set up the debugger and print out the API calls.


The Managers are a collection of helper classes to make dealing with some of the more complex portions of the API a lot easier.

While it is not required to use them, they can serve as a good example to build off from. Most of the managers were designed for use with the CLI, but of note is the Ordering Manager which can help take a lot of the complexity out of creating orders.

from SoftLayer.managers import ordering

manager = ordering.OrderingManager(client)
# This just excludes all non-classic infrastructure packages
_filter = {'type': {'keyName': {'operation': '!= BLUEMIX_SERVICE'}}}
packages = manager.list_packages(filter = _filter)
print("Id, KeyName")
for package in packages:
    print("{}, {}".format(package.get('id'),package.get('keyName'))

The SoftLayer.utils section has a variety of functions that can make your life easier as well. Noteably the query_filter type functions can help format Object Filters properly and take a lot of the guess working out of the more complex filters.

from SoftLayer import utils

_filter = {"virtualGuests": {"hostname": utils.query_filter_orderby()}
guestsByHostname = client.call('SoftLayer_Account', 'getVirtualGuests', filter=_filter)
for guest in guestsByHostname:
    pp(guest)

The slcli command is included as part of the softlayer-python package. It contains a lot of prebuilt functionality that you can also find in the web portal, although some formats and outputs will be different of course.

It is a sister program of the ibmcloud sl command, but a different code base. The two strive to have the same functionality, but some differences do exist, so be aware of which one you are using.


Further Reading


Feedback?

If this article contains any error, or leaves any of your questions unanswered, please help us out by opening up a github issue.
Open an issue