Computerglitch

An ongoing adventure

Fabric Quick Start

Sometimes I need to use a one-off command for a simple task and end up wrapping the command in a for loop, calling ssh, and running the command over a few hosts. While this works great in a pinch, if I find myself using the same loop over and over I’ll create a new definition in Fabric.

If you’re unfamiliar with Fabric this post attempts to get you up to speed with the basics on how to use Fabric. First lets get Fabric installed.

1
2
3
apt-get install gcc python-setuptools python-crypto
easy_install pip
pip install fabric

Fabric uses Paramiko which is a Python interface for SSH. In the example fabfile below I’ll be logging into remote nodes over SSH so make sure you have your key-based or host-based authentication in place.

At this point we can create our first fabfile. I’m going to show you two basic but powerful features to get you started on your first fabfile. From this framework you should be able to start building a very nice fabfile to manage your nodes!

The beauty of Fabric is that it’s “just Python” so you’re free to do what you want with your fabfile.

Here is a basic fabfile with complete comments:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
#!/usr/bin/env python

#import the fabric API
from fabric.api import *

#Function to define a range of nodes with prefix and number range
#Example usage: nodes("www", "00", "20") would output an array with www-00 - www-19
def nodes(prefix, r1, r2):
    nodes = []
    for nodenumber in range (int(r1),int(r2)):
        nodes.append(prefix + '-' + str(nodenumber).zfill(2))
    return nodes

#Don't brick the program on commands that return nonzero on run commands
#More info here: http://docs.fabfile.org/en/1.4.1/usage/execution.html#failure-handling
env.warn_only=True

#Example of using Roles to define subsets of nodes
env.roledefs = {
   'web_nodes' : [ 'www-00', 'www-01', 'www-02' ],
   'dns_nodes' : [ 'dns-00', 'dns-01' ],
   'gph_nodes' : nodes('graphic', '00', '14')
}
@roles('web_nodes')
def webUptime():
    run('uptime')
@roles('dns_nodes')
def dnsDate():
    run('date')

#Example that can be called by: fab -H host1,host2,host3 nodePackageSearch:package=httpd
def nodePackageSearch(package=''):
    run('rpm -qa | grep %s' % package)

Lets break this fabfile down. I first have a function that allows us to create a range of nodes. This function will be helpful later when we are defining node roles.

1
2
3
4
5
def nodes(prefix, r1, r2):
    nodes = []
    for nodenumber in range (int(r1),int(r2)):
        nodes.append(prefix + '-' + str(nodenumber).zfill(2))
    return nodes

Next, I’m defining roles for my nodes and assigning my nodes to those roles. As you can see this is where the nodes function defined earlier will come in handy.

1
2
3
4
5
env.roledefs = {
   'web_nodes' : [ 'www-00', 'www-01', 'www-02' ],
   'dns_nodes' : [ 'dns-00', 'dns-01' ],
   'gph_nodes' : nodes('gtnode', '00', '14')
}

Now we can assign functions to our roles. In this example I’m getting the uptime on all of my web nodes, and I have another function that allows me to get the date on all of my dns servers.

1
2
3
4
5
6
@roles('web_nodes')
def webUptime():
    run('uptime')
@roles('dns_nodes')
def dnsDate():
    run('date')

Finally, I have a function that allows me to query nodes for a specific from the command line.

1
2
def nodePackageSearch(package=''):
    run('rpm -qa | grep %s' % package)

To run Fabric use the command fab in the same directory as your fabfile (or use -f and specify the path to your fabfile).

To get the list commands available from our fabfile you can use fab -l which will return:

1
2
3
4
5
6
Available commands:

    dnsDate
    nodePackageSearch
    nodes
    webUptime

So to get the uptime on all of our web nodes you can simply run fab webUptime which will return results similar to:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
[www-00] Executing task 'webUptime'
[www-00] run: uptime
[www-00] out:  13:51:13 up 1 day, 18:30,  1 user,  load average: 0.00, 0.00, 0.00
[www-00] out:

[www-01] Executing task 'webUptime'
[www-01] run: uptime
[www-01] out:  13:51:14 up 28 days, 23:08, 10 users,  load average: 0.00, 0.00, 0.00
[www-01] out:

[www-02] Executing task 'webUptime'
[www-02] run: uptime
[www-02] out:  13:51:14 up 28 days, 23:43,  1 user,  load average: 0.42, 0.56, 0.61
[www-02] out:


Done.
Disconnecting from www-00... done.
Disconnecting from www-01... done.
Disconnecting from www-02... done.

If you have a large amount of nodes you will want to use the -P switch to execute your commands in parallel, fab -P dnsDate

1
2
3
4
5
6
7
8
9
10
11
12
[dns-00] Executing task 'dnsDate'
[dns-01] Executing task 'dnsDate'
[dns-01] run: date
[dns-00] run: date
[dns-00] out: Thu May  1 13:57:46 EDT 2014
[dns-01] out: Thu May  1 13:57:46 EDT 2014
[dns-00] out:

[dns-01] out:


Done.

To use our package search function from the command line we can use fab -H node-03,node-04 nodePackageSearch:package=nc-1.84 which outputs:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
[node-03] Executing task 'nodePackageSearch'
[node-03] run: rpm -qa | grep nc-1.84
[node-03] out: nc-1.84-22.el6.x86_64
[node-03] out:

[node-04] Executing task 'nodePackageSearch'
[node-04] run: rpm -qa | grep nc-1.84
[node-04] out: nc-1.84-22.el6.x86_64
[node-04] out:


Done.
Disconnecting from node-04... done.
Disconnecting from node-03... done.

As you can see Fabric is a very powerful tool that can be modified for just about any environment.

Official Fabric Tutorial: http://docs.fabfile.org/en/latest/tutorial.html

Official Fabric Documentation: http://docs.fabfile.org

Comments