Migrating API v1 applications to v3
From SoftLayer Development Network Wiki
Contents |
API Differences
We've collected quite a bit of feedback on our original API rollout in March 2006. Your suggestions have led to a completely new API with greater emphasis on functionality and expandability. API version 3 has a number of fundamental structural changes in it. Take heed of the following before you start converting your applications:
One Endpoint Per Service
One of the biggest changes to the new API is the introduction of services. API version 3 lets you concurrently call multiple services, with each service representing a service provided by SoftLayer. Services represent everything from your account to your users, servers, tickets, hosted domains, StorageLayer accounts, and everything in between. As we roll out new products we'll likewise roll out corresponding API services for them.
There are now multiple endpoints to the SoftLayer API. Version 1 gave you two endpoints into our systems, one for the SOAP interface and one for the XML-RPC interface. Version 3 of the API has multiple endpoints, two for each service SoftLayer provides, one for SOAP and one for XML-RPC. Your new code can now maintain multiple SOAP or XML-RPC connections instead of a single connection that retrieves everything.
Verison 3 endpoints are hosted on the private network at api.service.softlayer.com in the v3 folder. Instead of directly referencing an HTML file to access the API, simply point your v3 application to the name of the service you want to access at the end of your endpoint URL.
| API v1 Endpoint | API v3 Endpoint | |
| "http://api.service.softlayer.com" followed by... | ||
| SOAP | /soap/v1/SL-Service.html | /soap/v3/SoftLayer_Account?wsdl
/soap/v3/SoftLayer_Hardware_Server?wsdl /soap/v3/SoftLayer_Network_Vlan?wsdl ... Other endpoints as defined by their service name. |
| XML-RPC | /xmlrpc/v1/SL-Service.html | /xmlrpc/v3/SoftLayer_Account
/xmlrpc/v3/SoftLayer_Hardware_Server /xmlrpc/v3/SoftLayer_Network_Vlan ... Other endpoints as defined by their service name. |
New Return Data
The second largest change in API version 3 is how your data is treated by our services' methods. API version 1 methods return values consisted of array data or lists of arrays. API version 3 treats data as objects, and returns . Version 3's service WSDL files are explicitly typed to retrieve specific data as objects. Furthermore, it is now possible to create objects based directly based from our service WSDL definitions. This is a great benefit to those who develop in modern object-oriented languages like C# and Java.
XML-RPC isn't an object-oriented transmission system, so the XML-RPC version of our API returns the same data as it's SOAP counterpart treated as array data instead of objects.
New Ways to Pass Data to the API
Along with new ways to return data to you we have new ways to send data to our services. API version 3 accepts method data via either SOAP headers or XML-RPC header structs. The previous API accepted solely method parameters and authentication headers in SOAP. Your application's initialization parameter objects create objects on our side of the API and are acted upon when you run a method call. See the code examples below to see initialization parameters in action.
User Permissions are Enforced
User permissions are strictly enforced in API v3. Our new API uses the same functionality, hardware, and master users permissions system as our customer portal. If your API user does not have permission in our portal to view or edit a certain object, then they won't be able to do it via the API. The API will either return an exception for change requests or return no data in get requests if your account doesn't have the proper permission. If you're receiving permissions errors in your API v3 application then check the permissions of the user you're executing API calls through in our customer portal.
Method Conversion
Unfortunately due to the differences between API v1 and v3's structures there is not a one-to-one correspondence between API v1 and v3 methods. The following table describes the best services and methods to call in API v3 to approximate an API v1 method call. Remember that version 3 methods return objects which contain different information than the result sets from version 1 methods.
Examples
Our API employs a wide range of functions, so our example deals with with a simple usage case: retrieving a list of servers under an account, the temperature of the first CPU for each server, and outputting a simple HTML page that lists servers by hostname, domain, IP address, and CPU temperature. Keep in mind that in programming there are usually many ways to accomplish the same task. Our solutions may not match yours, but we hope that these examples highlight the process differences in using the different API versions. A large majority of our API v1 users are using PHP for their calls, so our examples also use PHP.
Output for every example, no matter the API version or protocol used should resemble this table:
| Hostname | Domain Name | Public IP Address | CPU Temperature |
|---|---|---|---|
| myhost | example.org | x.y.z.w | x degrees C |
| anotherHost | example.org | a.b.c.d | y degrees C |
SOAP in PHP
Our version 1 script is pretty simple with two methods to call, getServerList and getIpmiSensorData. We've created a wrapper function, soapCall(), to help automate the individual API calls necessary to generate our server table.
API v1 SOAP Example
<?php ini_set("soap.wsdl_cache_enabled", "0"); // Set these to your API user and key before beginning. define('API_USER', 'set me'); define('API_KEY', 'set me'); define('API_ENDPOINT', 'http://api.service.softlayer.com/soap/v1/SL-Service.html'); /** * A simple SoftLayer API SOAP wrapper * * @param string $methodName The method to call * @param array $parameters Paramaters to pass to the called method * @return array */ function soapCall($methodName, $parameters) { global $client, $authHeader; try { $result = $client->__call($methodName, $parameters, null, $authHeader); } catch (SoapFault $e) { trigger_error($e->getMessage()); } return $result; } // Set up the authentication header. $authHeader = new SoapHeader('myUrn', 'authenticate',array(API_USER, API_KEY)); // Set up the SOAP client $client = new SoapClient(null, array('location' => API_ENDPOINT, 'uri' => API_ENDPOINT)); // Get a list of servers first. $serverList = soapCall('getServerList', array()); // Now find out server CPU temperatures. Add temps to the serverList array. foreach ($serverList as $key => $server) { $sensorData = soapCall('getIpmiSensorData', array($server['ID'])); // Different IPMI cards return different sensor data. Find out which sensor // to read. $cpuKeys = array('CPU', 'CPU Temp', 'CPU Temp 1'); foreach ($cpuKeys as $cpuKey) { if (isset($sensorData[$cpuKey])) { $tempKey = $cpuKey; } } $cpuTemp = $sensorData[$tempKey]; $serverList[$key]['CPU_TEMPERATURE'] = $cpuTemp['READING'].' '.$cpuTemp['UNITS']; } // All done! Output the result. ?> <html> <head> <title>SoftLayer API v1 Example - SOAP</title> </head> <body> <table> <tr> <th>Hostname</th> <th>Domain Name</th> <th>Public IP Address</th> <th>CPU Temperature</th> </tr> <?php foreach ($serverList as $server): ?> <tr> <td><?=$server['HOSTNAME'] ?></td> <td><?=$server['DOMAIN'] ?></td> <td><?=$server['PUBLIC_IP_ADDRESS'] ?></td> <td><?=$server['CPU_TEMPERATURE'] ?></td> </tr> <?php endforeach; ?> </table> </body> </html>
Since API version 3 is object based we'll have to modify our sample script to reference object members instead of array values. Likewise we'll need to code service handling into the soapCall() wrapper function. One we make these modifications our script looks like this:
API v3 SOAP Example
<?php ini_set("soap.wsdl_cache_enabled", "0"); // Set these to your API user and key before beginning define('API_USER', 'set me'); define('API_KEY', 'set me'); /** * A simple SOAP wrapper for the SoftLayer API. * */ class SLSoapClient extends SoapClient { /** * The SL Service to use. * * @var string */ private $serviceName; /** * SOAP authentication header * * @var SoapHeader */ private $authHeader; /** * A class to initialize the SL service object * * @var stdClass */ private $initParameters = null; /** * Connect to a SL SOAP Service * * @param string $serviceName The SL Service to connect to */ public function __construct($serviceName) { $this->serviceName = $serviceName; parent::__construct('http://api.service.softlayer.com/soap/v3/'.$serviceName.'?wsdl', array()); // Set the authentication header. $authParams = new stdClass(); $authParams->username = API_USER; $authParams->apiKey = API_KEY; $this->authHeader = new SoapHeader('slApi', 'authenticate', $authParams); } /** * Set the SL object init parameters * * @param stdClass $initParameters */ public function setInitParameters($initParameters) { $this->initParameters = $initParameters; } /** * Call a SL API method * * @param string $function_name The API method to call, specified directly in the class. * @param array $arguments Arguments to pass to the method. If using an init parameter put this as the last element in the array. * @return stdClass */ public function __call($function_name, $arguments = null) { try { // Only pass init paramaters to the SOAP call if they're are defined. if (is_null($this->initParameters)) { $result = parent::__call($function_name, $arguments, array(), array($this->authHeader)); } else { $initHeader = new SoapHeader('slApi', $this->serviceName.'InitParameters', $this->initParameters); $result = parent::__call($function_name, $arguments, array(), array($this->authHeader, $initHeader)); } } catch (SoapFault $e) { trigger_error($e->getMessage()); } return $result; } } // Set up an empty object for the init parameters we're using later. $initParameters = new stdClass(); // Set up our two SOAP clients. We're accessing two services, so we'll have two // SOAP clients. $accountClient = new SLSoapClient('SoftLayer_Account'); $serverClient = new SLSoapClient('SoftLayer_Hardware_Server'); // Get a list of servers first. $serverList = $accountClient->getHardware(); // Now find out server CPU temperatures. Add sensor data to the server objects. foreach ($serverList as $key => $server) { $initParameters->id = $server->id; $serverClient->setInitParameters($initParameters); $serverList[$key]->sensorData = $serverClient->getSensorData(); // Different IPMI cards return different sensor data. Find out which sensor // to read. $cpuKeys = array('CPU', 'CPU Temperature', 'CPU Temperature 1'); foreach ($serverList[$key]->sensorData as $sensor) { if (in_array($sensor->sensorId, $cpuKeys)) { $serverList[$key]->cpuTemperature = $sensor->sensorReading.' '.$sensor->sensorUnits; } } } // All done! Output the result. ?> <html> <head> <title>SoftLayer API v3 Example - SOAP</title> </head> <body> <table> <tr> <th>Hostname</th> <th>Domain Name</th> <th>Public IP Address</th> <th>CPU Temperature</th> </tr> <?php foreach ($serverList as $server): ?> <tr> <td><?=$server->hostname ?></td> <td><?=$server->domain ?></td> <td><?=$server->primaryIpAddress ?></td> <td><?=$server->cpuTemperature ?></td> </tr> <?php endforeach; ?> </table> </body> </html>
There are a number of differences between the version 1 and version 3 scripts.
- The SOAP wrapper is object based. One of SOAP's advantages as a Remote Procedure Call system is how well integrates with object-oriented languages, and our new API takes advantage of that. The SLSoapClient class exemplifies how our API uses and returns objects. Notice the getSensorData requests pass along an initialization parameter to the SoftLayer SOAP service. Initialization parameters prepare a SOAP object on our end to act upon your method request. API v3 SOAP calls require init parameters instead of method parameters. The SLSoapClient objects abstracts API calls as object methods. Our sample wrapper class does this to make it easier to call the API. This should also make porting this code to languages that are more heavily dependent on objects like C# and Java a bit easier.
- SOAP calls are made from two client instances. Our example initialized two SLSoapClient objects to talk to separate SoftLayer API services, the SoftLayer_Account and SoftLayer_Hardware_Server classes. This can greatly benefit multi-threaded languages and hopefully make it easier on the developer to keep track of which method calls are made to which service from which object.
- API endpoints are different. Our v3 sample script connects to the services in the v3 folder on the SoftLayer API server. Likewise each endpoint is defined by the name of the service we want to use.
- Method calls return stdClass objects instead of arrays. Notice how the view logic in the HTML table, and the code that assigns CPU temperatures to servers relies on object members instead of array data. The v3 SOAP service returns stdClass objects for PHP when classes aren't specifically defined.
Since the v3 SOAP WSDL files are explicitly typed you can take this example further by applying a classmap to the wrapper in conjunction with the PHP SOAP __getTypes() function. This would enable you to deal directly with SoftLayer objects as defined in the WSDL and cause the wrapper class to return SoftLayer objects instead of stdClass objects.
XML-RPC in PHP
Our version 1 XML-RPC script is nearly identical to it's SOAP counterpart. Differences lie in the XML-RPC wrapper, replacing SOAP specific functions with XML-RPC functions.
API v1 XML-RPC Example
<?php // Set these to your API user and key before beginning. define('API_USER', 'set me'); define('API_KEY', 'set me'); /** * A simple SoftLayer API XML-RPC wrapper * * @param string $methodName The method to call * @param arrary $parameters Parameters to pass to the method * @return array */ function xmlrpcCall($methodName, $parameters) { // The first parameters of all SL API XML-RPC calls must be the API user // and key. Add the API user and key to the beginning of the parameter // array. $parameters = array_merge(array(API_KEY, API_USER), $parameters); $request = xmlrpc_encode_request($methodName, $parameters); // Making the XML-RPC call interpreting the response is taken from the PHP // manual: // http://www.php.net/manual/en/function.xmlrpc-encode-request.php $context = stream_context_create(array( 'http' => array( 'method' => "POST", 'header' => "Content-Type: text/xml", 'content' => $request ))); $file = file_get_contents(API_ENDPOINT, false, $context); $response = xmlrpc_decode($file); if (xmlrpc_is_fault($response)) { trigger_error("xmlrpc: $response[faultString] ($response[faultCode])"); } else { return $response; } } // Get a list of servers first. $serverList = (xmlrpcCall('getServerList', array())); // Now find out server CPU temperatures. Add temps to the serverList array. foreach ($serverList as $key => $server) { $sensorData = xmlrpcCall('getIpmiSensorData', array($server['ID'])); // Different IPMI cards return different sensor data. Find out which sensor // to read. $cpuKeys = array('CPU', 'CPU Temp', 'CPU Temp 1'); foreach ($cpuKeys as $cpuKey) { if (isset($sensorData[$cpuKey])) { $tempKey = $cpuKey; } } $cpuTemp = $sensorData[$tempKey]; $serverList[$key]['CPU_TEMPERATURE'] = $cpuTemp['READING'].' '.$cpuTemp['UNITS']; } // All done! Output the result. ?> <html> <head> <title>SoftLayer API v1 Example - XML-RPC</title> </head> <body> <table> <tr> <th>Hostname</th> <th>Domain Name</th> <th>Public IP Address</th> <th>CPU Temperature</th> </tr> <?php foreach ($serverList as $server): ?> <tr> <td><?=$server['HOSTNAME'] ?></td> <td><?=$server['DOMAIN'] ?></td> <td><?=$server['PUBLIC_IP_ADDRESS'] ?></td> <td><?=$server['CPU_TEMPERATURE'] ?></td> </tr> <?php endforeach; ?> </table> </body> </html>
XML-RPC isn't as powerful as SOAP in an object-oriented sense, so our sample script will still use arrays to handle data, though the data in our API-returned arrays is different. Once we've taken care of the new return data and service handling in the xmlrpcCall() wrapper function our script looks like this:
API v3 XML-RPC Example
<?php // Set these to your API user and key before beginning. define('API_USER', 'set me'); define('API_KEY', 'set me'); /** * A simple SoftLayer API XML-RPC wrapper * * @param string $serviceName The SoftLayer API service to query * @param string $methodName The method to call * @param array $initParameters The SoftLayer API service's Initialization Parameter * @param array $methodParameters The parameters required for your method call * @return array */ function xmlrpcCall($serviceName, $methodName, $initParameters = array(), $methodParameters = array()) { // Build the XML-RPC request array. Everything goes into a "headers" array, // with authentication and service initialization parameters in sub-arrays. $headers = array(); $headers['authenticate'] = array(); $headers['authenticate']['username'] = API_USER; $headers['authenticate']['apiKey'] = API_KEY; $headers[$serviceName.'InitParameters'] = $initParameters; $request = array(); $request[0] = array('headers' => $headers); $request = array_merge($request, $methodParameters); $encodedRequest = xmlrpc_encode_request($methodName, $request); // Making the XML-RPC call interpreting the response is taken from the PHP // manual: // http://www.php.net/manual/en/function.xmlrpc-encode-request.php $context = stream_context_create(array( 'http' => array( 'method' => "POST", 'header' => "Content-Type: text/xml", 'content' => $encodedRequest ))); $file = file_get_contents('http://api.service.softlayer.com/xmlrpc/v3/'.$serviceName, false, $context); $response = xmlrpc_decode($file); if (xmlrpc_is_fault($response)) { trigger_error("xmlrpc: $response[faultString] ($response[faultCode])"); } else { return $response; } } // Get a list of servers first. $serverList = xmlrpcCall('SoftLayer_Account', 'getHardware', array()); // Now find out server CPU temperatures. Add temps to the serverList array. foreach ($serverList as $key => $server) { $sensorData = xmlrpcCall('SoftLayer_Hardware_Server', 'getSensorData', array('id' => $server['id'])); // Different IPMI cards return different sensor data. Find out which sensor // to read. $cpuKeys = array('CPU', 'CPU Temperature', 'CPU Temperature 1'); foreach ($sensorData as $sensor) { if (in_array($sensor['sensorId'], $cpuKeys)) { $serverList[$key]['cpuTemperature'] = $sensor['sensorReading'].' '.$sensor['sensorUnits']; } } } // All done! Output the result. ?> <html> <head> <title>SoftLayer API v3 Example - XML-RPC</title> </head> <body> <table> <tr> <th>Hostname</th> <th>Domain Name</th> <th>Public IP Address</th> <th>CPU Temperature</th> </tr> <?php foreach ($serverList as $server): ?> <tr> <td><?=$server['hostname'] ?></td> <td><?=$server['domain'] ?></td> <td><?=$server['primaryIpAddress'] ?></td> <td><?=$server['cpuTemperature'] ?></td> </tr> <?php endforeach; ?> </table> </body> </html>
The changes between XML-RPC script versions aren't as sigifigant as the SOAP example differences, but there are some notable things to look for.
- The XML-RPC wrapper takes a service name parameter. Since XML-RPC is handled via a standard HTTP transaction we don't need to create multiple API clients like we did in the API v3 SOAP example. We do, however need to pass along the service and method names to the wrapper.
- XML-RPC options are passed as header structs. In API version 1 we handled authentication by setting usernames and API keys as the first two method parameters. In version 3 we define the 'authenticate' XML-RPC struct and pass it to the API with the service's init parameters in the XML-RPC header. The XML-RPC header is a convenient way to tell our API which services and user to interact with in our backend systems. This removes the necessity for recalling parameter order, and makes it a bit easier on developers to pass data to the API.
- Method parameters are sent as initilaization parameter arrays. Along with the authentication information ethod parameters are now passed by XML-RPC struct to the API. In our example we defined an array with prarmeter names and values to send to the API service. Again, this removes the necessity for keeping parameters in order and lets the developer verify exactly what they're passing to the API.
- Return array keys are lower camel case. API v3 XML-RPC return array keys match their corresponding v3 SOAP object member names.
Frequently Asked Questions
Version 3 is an evolutionary step in the SoftLayer API. Migrating your applications will open access into more of our backend systems. We highly recommend migrating your code, as new API features will be released into version 3. Please head to the SLDN forums if you have any questions on migrating your code. Our development staff and user base will be more than happy to assist you.
You went from version 1 to version 3. What happened to version 2?
We learned quite a few things when we made API version 1. API version 2 was an internal version for use by the SoftLayer development team. There's a saying that the second system you write is your most complicated. In the end we took our feedback from version 1, our test code in version 2, and came up with what we've got now in version 3.
What will happen to version 1?
While we don't have any plans to discontinue access to API version 1, we also won't be adding any new functionality to it aside from bug fixes. Your API v1 based applications will continue to work as they have been. We also won't take down API v1's documentation PDFs and examples, but won't be updating them either.
This is a huge change. I don't even know where to begin now.
We recommend starting out by looking at the SoftLayer_Account service. The SoftLayer_Account contains a large number of relationships that relate the servers and services you purchase from softLayer with your SoftLayer customer account. You can drill down into more detail form the relational properties attached to the SoftLayer_Account service.
There's quite a bit more to this new API. How fast is this?
We've taken a very serious look at speed and efficiency when developing this API. We were so pleased with our testing that we rewrote our customer facing portal to use this API as it's backend. Since doing so system load has noticeably dropped on the web and database servers that power our customer portal.

