Tutorial

The WebFaction API is an XML-RPC API available at https://api.webfaction.com/. The API lets you automate many tasks that you would ordinarily do with the control panel or an SSH session, like modifying an email address, creating a file, or installing an application. You can also use the WebFaction API to create scripts that install applications, which you can share with other WebFaction users.

In this tutorial, you’ll learn how to

  • log in to the API
  • complete a few common control panel tasks with the API
  • use your new API skills to create a sharable application install script

Before you begin

In this tutorial, we’ll demonstrate some ways to use the API using the Python programming language and the Python standard library’s xmlrpclib module. You can use other languages to work with the API and to create install scripts, but to follow along with this tutorial you’ll need to use the interactive Python interpreter.

On most systems you can run python2.7 in a terminal session to start an interactive Python interpreter (including on your WebFaction server in an SSH session). You’ll see a prompt that looks something like this:

$ python2.7
Python 2.7.12 (default, Oct 11 2016, 05:24:00)
[GCC 4.2.1 Compatible Apple LLVM 8.0.0 (clang-800.0.38)] on darwin
Type "help", "copyright", "credits" or "license" for more information.
>>>

Next, create create an xmlrpclib.ServerProxy instance called server that we can use to communicate with the API throughout the rest of the tutorial:

>>> import xmlrpclib
>>> server = xmlrpclib.ServerProxy('https://api.webfaction.com/')

Logging in

Before we can make any useful requests to the API, we need to log in to the API to receive a session ID. The session ID is a secret string that you provide to all subsequent API calls, which associates each of your requests to your WebFaction account.

To log in to the API, we’ll need to call the login method with four arguments:

  • your account name (that you use to log in to the WebFaction control panel)
  • your password (that you use to log in to the WebFaction control panel)
  • your server name
  • the API version (typically the latest version, currently version 2)

Note

To preserve backwards compatibility, the WebFaction API is versioned. Which version number you pass to login will determine what arguments API methods will accept and what values they will return. For new projects or projects that don’t need to worry about backwards compatibility, choosing the latest version is recommended.

For more information, see API Versions.

Continuing from the session we started in the previous section, here’s how to log in with Python:

>>> session_id, account = server.login('test5', 'password', 'Web500', 2)
>>> session_id
'6zlwc5j1ne8kr'
>>> account
{'username': 'demo', 'web_server': 'Web500', 'version': 2, 'home': '/home',
'mail_server': 'Mailbox3', 'id': 1}

The XML-RPC API returns an array containing a string and a struct. Python represents the data as Python types. So the array becomes a Python list, the string becomes an ordinary Python string, and the struct becomes a Python dictionary. In this case, the first list element is the session ID ('6zlwc5j1ne8kr'). The second list element is a dict containing details about your account.

Now that we have the session_id, we can make some more interesting API calls.

Calling API methods

Once you have a session ID, you can call the other methods provided by the API. You can find a complete list of API methods in the API Reference.

Every API method except login requires a session ID. For example, the method list_emails can tell us what email addresses have been created for the account The method takes one argument: a session ID. Calling it looks like this:

>>> server.list_app_types(session_id)
[{'autoresponder_subject': '', 'autoresponder_message': '', 'targets': '',
'autoresponder_on': False, 'email_address': 'demo@example.com', 'id': 482804,
'autoresponder_from': ''}]

As another example, the method change_mailbox_password takes exactly three arguments:

  1. a session ID
  2. a mailbox name
  3. a strong password

Some methods can accept several arguments where some of those arguments are optional. If a method has optional arguments, then you don’t need to supply the optional arguments if those arguments appear after any arguments that you want to provide. For example, the method create_email can accept up to nine arguments, but several of those arguments are optional (for setting up autoresponders or scripts to process incoming mail). If you don’t want to use autoresponders or scripts, then only the first three arguments are required:

>>> server.create_email(session_id, 'demo@example.com', 'demo')
{'autoresponder_subject': '', 'autoresponder_message': '', 'id': 482807,
'targets': 'demo', 'autoresponder_on': 0, 'script_path': '',
'email_address': 'demo@example.com', 'script_machine': '',
'autoresponder_from': ''}

But if we wanted to create an email address such that a script will receive each incoming message but not use an autoresponder, then we must provide all of the arguments to the create_email method, since the script arguments come after the autoresponder arguments:

>>> server.create_email(session_id, 'demo2@example.com', 'demo', False,
                        '', '', '', 'Web500', '/home/demo/mymailscript.py')
{'autoresponder_subject': '', 'autoresponder_message': '', 'id': 482808,
'targets': 'demo', 'autoresponder_on': 0,
'script_path': '/home/demo/mymailscript.py',
'email_address': 'demo2@example.com', 'script_machine': 'Web500',
'autoresponder_from': ''}

Now that you know how to call API methods, you can combine several methods to complete more complicated tasks.

Creating an application with the API

Suppose we wanted to start developing a site with a database and a PHP script. One way to do this would be to log in to the control panel, create a Static/CGI/PHP application, create a MySQL database user and database, and then open an SSH session to create an index.php file. But we can also do all of that with the API. Let’s try it out.

First, let’s create the Static/CGI/PHP application with the create_app method. The method takes three required arguments:

  1. a session ID
  2. an app name
  3. an application type string (in this case, static_php70)
>>> server.create_app(session_id, 'development_app', 'static_php70')
{'name': 'development_app', 'port': 0, 'machine': 'Web500',
'autostart': False, 'open_port': False, 'type': 'static_php70',
'id': 1033807, 'extra_info': ''}

Next, let’s create a MySQL database and user with the create_db method. In addition to a session ID, it requires three arguments: a database name, a database type (in our case, mysql), and a password for the default user (automatically named after the database).

>>> server.create_db(session_id, 'development', 'mysql', 'mysecret')
{'machine': 'Web500', 'db_type': 'mysql', 'db_user': 'development',
'name': 'development'}

Next, let’s remove the index.html that’s created in each new Static/CGI/PHP app. We can do that with the system method, which requires two arguments: a session ID and a shell command.

>>> server.system(session_id, "rm /home/demo/webapps/development_app/index.html")
''

Now we’re free to start development by creating an index.php file. To do that, we can use the write_file method:

>>> server.write_file(session_id,
                      '/home/demo/webapps/development_app/index.php',
                      """<?php
    $appname = 'Development App!';
    echo "Welcome to " . $appname;
    ?>
    """)
''

Now that you’ve had an introduction to the API, you can explore the other API methods available and use the API to automate many parts of your account.

Creating an install script for the control panel

In the previous sections, we worked with the API by manually running a bunch of commands. While we could turn those commands into a script that we could run locally, a more flexible option is to create an install script for the control panel. In this section, you’ll learn how to create a Python install script for the control panel that handles creating and deleting an application and that you can share with others.

Running an install script

To run a custom install script with the control panel:

  1. Log in to the WebFaction control panel.
  2. Click Domains / websites ‣ Applications. The list of applications appears.
  3. Click the Add new application button. The Create a new application form appears.
  4. In the Name field, enter a name for the application.
  5. In the App category menu, click to select Custom.
  6. In the App type menu, click to select Custom install script.
  7. If applicable, in the Machine menu, click to select a web server.
  8. In the Script URL field, enter the URL for the install script.
  9. Click the Fetch script button.
  10. Click the Save button.

When you click the control panel’s Save button, the script is called as if you ran install_script from the command line with these arguments:

install_script create username password machine app_name autostart extra_info

where:

  • username is the control panel username
  • password is the user’s hashed password
  • machine is the machine name from the control panel Machine menu
  • app_name is the application name from the control panel’s Name field
  • autostart is a boolean value for whether the user selected the control panel’s Autostart checkbox
  • extra_info is the contents of the control panel’s Extra info field

Later, if you click the control panel’s Delete button for the installed application, then the control panel will run install_script with the following arguments:

install_script delete username password app_name autostart extra_info

The control panel expects that all install scripts will be Python 2.7 scripts enclosed in these tags:

-----BEGIN WEBFACTION INSTALL SCRIPT-----
-----END WEBFACTION INSTALL SCRIPT-----

The control panel expects install scripts run with the create argument to have the following behavior:

  • The script will call create_app (for example, to create a Static/CGI/PHP or Custom app (listening on port) application)
  • If the script runs successfully, then the script prints only the application ID to standard output and prints nothing to standard error.
  • If the script fails, then the script prints error messages to standard output or standard error.

The control panel expects install scripts run with the delete argument to have the following behavior:

  • If the script runs successfully, then the script prints nothing to standard output or standard error.
  • If the script fails, then the script prints error messages to standard output or standard error.

Whenever a script fails (when run with the create or delete arguments), whatever was printed to standard output or standard error are displayed as error messages in the control panel.

Additionally, if your script is written in Python and you include a docstring (PEP 257) at the beginning of the script, then the control panel will display the docstring as help text in the control panel.

Example install script

To see how an install script is typically put together, here’s how the application we created with the API in the previous section would look as an install script:

-----BEGIN WEBFACTION INSTALL SCRIPT-----
#!/usr/bin/env python2.7

"""A sample WebFaction control panel install script."""

import random
import sys
import string
import xmlrpclib

server = xmlrpclib.ServerProxy('https://api.webfaction.com/')

def randompassword():
   """Generate a password."""
   chars = string.ascii_uppercase + string.ascii_lowercase + string.digits
   size = random.randint(16, 24)
   return ''.join(random.choice(chars) for x in xrange(size))


def create(session_id, app_name, home):
   """Create the application."""
   # prepare database details
   db_name = "{}_db".format(app_name)
   db_user = db_name
   db_password = randompassword()

   # create database
   server.create_db(session_id, db_name, 'mysql', db_password)

   # create static app
   app_info = server.create_app(session_id, app_name, 'static_php70')

   # remove existing index.html
   server.system(session_id,
                 "rm {}/webapps/{}/index.html".format(home, app_name))

   # create PHP file
   server.write_file(session_id,
                     '{}/webapps/{}/index.php'.format(home, app_name),
                     """<?php
$db_user = {db_user}
$db_pass = {db_password}
$appname = '{app_name}';

echo "Welcome to " . $appname;
?>
""".format(db_user=db_user, db_password=db_password, app_name=app_name))

   # when finished, always print app id
   print app_info['id']


def delete(session_id, app_name):
   """Delete the application and its database and database user."""
   db_name = "{}_db".format(app_name)
   db_user = db_name

   server.delete_app(session_id, app_name)
   server.delete_db(session_id, db_name, 'mysql')
   server.delete_db_user(session_id, db_user, 'mysql')


def main():
   username, password, machine, app_name, autostart, extra_info = sys.argv[2:]
   session_id, account_info = server.login(username, password, machine, 2)
   home = "{}/{}".format(account_info['home'], account_info['username'])

   if sys.argv[1] == 'create':
       create(session_id, app_name, home)
   else:
       delete(session_id, app_name)


if __name__ == '__main__':
   main()

-----END WEBFACTION INSTALL SCRIPT-----

Sharing your scripts

Once you’ve made a script, you can share it with others by making the file available at public URL. You can automatically open the installer script in the WebFaction control panel by using a special URL in the format of https://my.webfaction.com/new-application?script_url=script where script is the URL for for your installer script.

For example, you can use the example script from the previous section by opening https://my.webfaction.com/new-application?script_url=https://docs.webfaction.com/xmlrpc-api/_downloads/example.txt in a browser.

Additional resources

For more about the API, please see the API Reference. For help with the API, join us in the WebFaction Q&A community.