guide_rax.rst 19.8 KB
Newer Older
Matt Martz committed
1
Rackspace Cloud Guide
2
=====================
Matt Martz committed
3 4 5 6 7 8

.. _introduction:

Introduction
````````````

9
.. note:: This section of the documentation is under construction. We are in the process of adding more examples about the Rackspace modules and how they work together.  Once complete, there will also be examples for Rackspace Cloud in `ansible-examples <http://github.com/ansible/ansible-examples/>`_.
Matt Martz committed
10

11
Ansible contains a number of core modules for interacting with Rackspace Cloud.  
12

13 14
The purpose of this section is to explain how to put Ansible modules together 
(and use inventory scripts) to use Ansible in Rackspace Cloud context.
Matt Martz committed
15

16 17 18 19 20
Prerequisites for using the rax modules are minimal.  In addition to ansible itself, 
all of the modules require and are tested against pyrax 1.5 or higher. 
You'll need this Python module installed on the execution host.  

pyrax is not currently available in many operating system 
21
package repositories, so you will likely need to install it via pip:
Matt Martz committed
22 23 24 25 26

.. code-block:: bash

    $ pip install pyrax

27 28
The following steps will often execute from the control machine against the Rackspace Cloud API, so it makes sense 
to add localhost to the inventory file.  (Ansible may not require this manual step in the future):
Matt Martz committed
29 30 31 32 33 34

.. code-block:: ini

    [localhost]
    localhost ansible_connection=local

35
In playbook steps we'll typically be using the following pattern:
Matt Martz committed
36 37 38 39 40 41

.. code-block:: yaml

    - hosts: localhost
      connection: local
      gather_facts: False
42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62
      tasks:

.. _credentials_file:

Credentials File
````````````````

The `rax.py` inventory script and all `rax` modules support a standard `pyrax` credentials file that looks like:

.. code-block:: ini

    [rackspace_cloud]
    username = myraxusername
    api_key = d41d8cd98f00b204e9800998ecf8427e

Setting the environment parameter RAX_CREDS_FILE to the path of this file will help Ansible find how to load
this information.

More information about this credentials file can be found at 
https://github.com/rackspace/pyrax/blob/master/docs/getting_started.md#authenticating

Matt Martz committed
63 64 65

.. _virtual_environment:

66 67 68 69 70
Running from a Python Virtual Environment (Optional)
++++++++++++++++++++++++++++++++++++++++++++++++++++

Special considerations need to 
be taken if pyrax is not installed globally but instead using a python virtualenv (it's fine if you install it globally).  
Matt Martz committed
71

72 73
Ansible assumes, unless otherwise instructed, that the python binary will live at 
/usr/bin/python.  This is done so via the interpret line in the modules, however 
74 75
when instructed using ansible_python_interpreter, ansible will use this specified path instead for finding 
python.
Matt Martz committed
76

77
If using virtualenv, you may wish to modify your localhost inventory definition to find this location as follows:
Matt Martz committed
78 79 80 81 82 83 84 85 86 87 88

.. code-block:: ini

    [localhost]
    localhost ansible_connection=local ansible_python_interpreter=/path/to/ansible_venv/bin/python

.. _provisioning:

Provisioning
````````````

89 90 91 92
Now for the fun parts.

The 'rax' module provides the ability to provision instances within Rackspace Cloud.  Typically the 
provisioning task will be performed from your Ansible control server against the Rackspace cloud API.
Matt Martz committed
93 94 95 96 97 98 99

.. note::

   Authentication with the Rackspace-related modules is handled by either 
   specifying your username and API key as environment variables or passing
   them as module arguments.

100
Here is a basic example of provisioning a instance in ad-hoc mode:
Matt Martz committed
101 102 103 104 105

.. code-block:: bash

    $ ansible localhost -m rax -a "name=awx flavor=4 image=ubuntu-1204-lts-precise-pangolin wait=yes" -c local

106
Here's what it would look like in a playbook, assuming the parameters were defined in variables:
Matt Martz committed
107 108 109 110 111 112 113 114 115 116 117 118 119 120 121

.. code-block:: yaml

    tasks:
      - name: Provision a set of instances
        local_action:
            module: rax
            name: "{{ rax_name }}"
            flavor: "{{ rax_flavor }}"
            image: "{{ rax_image }}"
            count: "{{ rax_count }}"
            group: "{{ group }}"
            wait: yes
        register: rax

122 123
By registering the return value of the step, it is then possible to dynamically add the resulting hosts to inventory (temporarily, in memory).
This facilitates performing configuration actions on the hosts immediately in a subsequent task::
Matt Martz committed
124

125
    - name: Add the instances we created (by public IP) to the group 'raxhosts'
Matt Martz committed
126 127 128 129 130 131 132 133 134
      local_action:
          module: add_host 
          hostname: "{{ item.name }}"
          ansible_ssh_host: "{{ item.rax_accessipv4 }}"
          ansible_ssh_pass: "{{ item.rax_adminpass }}"
          groupname: raxhosts
      with_items: rax.success
      when: rax.action == 'create'

135
With the host group now created, a second play in your provision playbook could now configure them, for example::
Matt Martz committed
136 137 138 139

    - name: Configuration play
      hosts: raxhosts
      user: root
140 141 142
      roles:
        - ntp
        - webserver
Matt Martz committed
143 144


145 146
The method above ties the configuration of a host with the provisioning step.  This isn't always what you want, and leads us 
to the next section.
Matt Martz committed
147 148 149 150 151 152

.. _host_inventory:

Host Inventory
``````````````

153
Once your nodes are spun up, you'll probably want to talk to them again.  
Matt Martz committed
154

155 156 157 158 159 160
The best way to handle his is to use the rax inventory plugin, which dynamically queries Rackspace Cloud and tells Ansible what
nodes you have to manage.

You might want to use this even if you are spinning up Ansible via other tools, including the Rackspace Cloud user interface.

The inventory plugin can be used to group resources by their meta data.  Utilizing meta data is highly 
161
recommended in rax and can provide an easy way to sort between host groups and roles.
Matt Martz committed
162

163 164 165 166 167
If you don't want to use the ``rax.py`` dynamic inventory script, you could also still choose to manually manage your INI inventroy file,
though this is less recommended.   

In Ansible it is quite possible to use multiple dynamic inventory plugins along with INI file data.  Just put them in a common
directory and be sure the scripts are chmod +x, and the INI-based ones are not.
Matt Martz committed
168 169 170 171 172 173

.. _raxpy:

rax.py
++++++

174 175 176
To use the rackspace dynamic inventory script, copy ``rax.py`` from ``plugins/inventory`` into your inventory directory.  You can specify credentials 
for ``rax.py`` utilizing the ``RAX_CREDS_FILE`` environment variable.

177
.. note:: Users of AnsibleWorks AWX will note that dynamic inventory is natively supported by AWX, and all you have to do is associate a group with your Rackspace Cloud credentials, and it will easily synchronize without going through these steps::
Matt Martz committed
178 179 180

    $ RAX_CREDS_FILE=~/.raxpub ansible all -i rax.py -m setup

181 182
``rax.py`` also accepts a ``RAX_REGION`` environment variable, which can contain an individual region, or a 
comma separated list of regions.
Matt Martz committed
183

184 185 186 187 188
When using ``rax.py``, you will not have a 'localhost' defined in the inventory.  

As mentioned previously, you will often be running most of these modules outside of the host loop, 
and will need 'localhost' defined.  The recommended way to do this, would be to create an ``inventory`` directory, 
and place both the ``rax.py`` script and a file containing ``localhost`` in it.  
Matt Martz committed
189

190
Executing ``ansible`` or ``ansible-playbook`` and specifying the ``inventory`` directory instead 
191
of an individual file, will cause ansible to evaluate each file in that directory for inventory.
Matt Martz committed
192

193 194
Let's test our inventory script to see if it can talk to Rackspace Cloud.

Matt Martz committed
195 196 197 198
.. code-block:: bash

    $ RAX_CREDS_FILE=~/.raxpub ansible all -i inventory/ -m setup

199 200
Assuming things are properly configured, the ``rax.py`` inventory script will output information similar to the 
following information, which will be utilized for inventory and variables. 
Matt Martz committed
201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298

.. code-block:: json

    {
        "ORD": [
            "test"
        ],
        "_meta": {
            "hostvars": {
                "test": {
                    "ansible_ssh_host": "1.1.1.1",
                    "rax_accessipv4": "1.1.1.1",
                    "rax_accessipv6": "2607:f0d0:1002:51::4",
                    "rax_addresses": {
                        "private": [
                            {
                                "addr": "2.2.2.2",
                                "version": 4
                            }
                        ],
                        "public": [
                            {
                                "addr": "1.1.1.1",
                                "version": 4
                            },
                            {
                                "addr": "2607:f0d0:1002:51::4",
                                "version": 6
                            }
                        ]
                    },
                    "rax_config_drive": "",
                    "rax_created": "2013-11-14T20:48:22Z",
                    "rax_flavor": {
                        "id": "performance1-1",
                        "links": [
                            {
                                "href": "https://ord.servers.api.rackspacecloud.com/111111/flavors/performance1-1",
                                "rel": "bookmark"
                            }
                        ]
                    },
                    "rax_hostid": "e7b6961a9bd943ee82b13816426f1563bfda6846aad84d52af45a4904660cde0",
                    "rax_human_id": "test",
                    "rax_id": "099a447b-a644-471f-87b9-a7f580eb0c2a",
                    "rax_image": {
                        "id": "b211c7bf-b5b4-4ede-a8de-a4368750c653",
                        "links": [
                            {
                                "href": "https://ord.servers.api.rackspacecloud.com/111111/images/b211c7bf-b5b4-4ede-a8de-a4368750c653",
                                "rel": "bookmark"
                            }
                        ]
                    },
                    "rax_key_name": null,
                    "rax_links": [
                        {
                            "href": "https://ord.servers.api.rackspacecloud.com/v2/111111/servers/099a447b-a644-471f-87b9-a7f580eb0c2a",
                            "rel": "self"
                        },
                        {
                            "href": "https://ord.servers.api.rackspacecloud.com/111111/servers/099a447b-a644-471f-87b9-a7f580eb0c2a",
                            "rel": "bookmark"
                        }
                    ],
                    "rax_metadata": {
                        "foo": "bar"
                    },
                    "rax_name": "test",
                    "rax_name_attr": "name",
                    "rax_networks": {
                        "private": [
                            "2.2.2.2"
                        ],
                        "public": [
                            "1.1.1.1",
                            "2607:f0d0:1002:51::4"
                        ]
                    },
                    "rax_os-dcf_diskconfig": "AUTO",
                    "rax_os-ext-sts_power_state": 1,
                    "rax_os-ext-sts_task_state": null,
                    "rax_os-ext-sts_vm_state": "active",
                    "rax_progress": 100,
                    "rax_status": "ACTIVE",
                    "rax_tenant_id": "111111",
                    "rax_updated": "2013-11-14T20:49:27Z",
                    "rax_user_id": "22222"
                }
            }
        }
    }

.. _standard_inventory:

Standard Inventory
++++++++++++++++++

299 300 301 302
When utilizing a standard ini formatted inventory file (as opposed to the inventory plugin), 
it may still be adventageous to retrieve discoverable hostvar information  from the Rackspace API.  

This can be achieved with the ``rax_facts`` module and an inventory file similar to the following:
Matt Martz committed
303 304 305

.. code-block:: ini

306 307 308
    [test_servers]
    hostname1 rax_region=ORD
    hostname2 rax_region=ORD
Matt Martz committed
309 310 311 312

.. code-block:: yaml

    - name: Gather info about servers
313
      hosts: test_servers
Matt Martz committed
314 315 316 317 318 319 320 321 322 323 324 325
      gather_facts: False
      tasks:
        - name: Get facts about servers
          local_action:
            module: rax_facts
            credentials: ~/.raxpub
            name: "{{ inventory_hostname }}"
            region: "{{ rax_region }}"
        - name: Map some facts
          set_fact:
            ansible_ssh_host: "{{ rax_accessipv4 }}"

326 327 328
While you don't need to know how it works, it may be interesting to know what kind of variables are returned.

The ``rax_facts`` module provides facts as followings, which match the ``rax.py`` inventory script:
Matt Martz committed
329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418

.. code-block:: json

    {
        "ansible_facts": {
            "rax_accessipv4": "1.1.1.1",
            "rax_accessipv6": "2607:f0d0:1002:51::4",
            "rax_addresses": {
                "private": [
                    {
                        "addr": "2.2.2.2",
                        "version": 4
                    }
                ],
                "public": [
                    {
                        "addr": "1.1.1.1",
                        "version": 4
                    },
                    {
                        "addr": "2607:f0d0:1002:51::4",
                        "version": 6
                    }
                ]
            },
            "rax_config_drive": "",
            "rax_created": "2013-11-14T20:48:22Z",
            "rax_flavor": {
                "id": "performance1-1",
                "links": [
                    {
                        "href": "https://ord.servers.api.rackspacecloud.com/111111/flavors/performance1-1",
                        "rel": "bookmark"
                    }
                ]
            },
            "rax_hostid": "e7b6961a9bd943ee82b13816426f1563bfda6846aad84d52af45a4904660cde0",
            "rax_human_id": "test",
            "rax_id": "099a447b-a644-471f-87b9-a7f580eb0c2a",
            "rax_image": {
                "id": "b211c7bf-b5b4-4ede-a8de-a4368750c653",
                "links": [
                    {
                        "href": "https://ord.servers.api.rackspacecloud.com/111111/images/b211c7bf-b5b4-4ede-a8de-a4368750c653",
                        "rel": "bookmark"
                    }
                ]
            },
            "rax_key_name": null,
            "rax_links": [
                {
                    "href": "https://ord.servers.api.rackspacecloud.com/v2/111111/servers/099a447b-a644-471f-87b9-a7f580eb0c2a",
                    "rel": "self"
                },
                {
                    "href": "https://ord.servers.api.rackspacecloud.com/111111/servers/099a447b-a644-471f-87b9-a7f580eb0c2a",
                    "rel": "bookmark"
                }
            ],
            "rax_metadata": {
                "foo": "bar"
            },
            "rax_name": "test",
            "rax_name_attr": "name",
            "rax_networks": {
                "private": [
                    "2.2.2.2"
                ],
                "public": [
                    "1.1.1.1",
                    "2607:f0d0:1002:51::4"
                ]
            },
            "rax_os-dcf_diskconfig": "AUTO",
            "rax_os-ext-sts_power_state": 1,
            "rax_os-ext-sts_task_state": null,
            "rax_os-ext-sts_vm_state": "active",
            "rax_progress": 100,
            "rax_status": "ACTIVE",
            "rax_tenant_id": "111111",
            "rax_updated": "2013-11-14T20:49:27Z",
            "rax_user_id": "22222"
        },
        "changed": false
    }


Use Cases
`````````

419
This section covers some additional usage examples built around a specific use case.
Matt Martz committed
420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541

.. _example_1:

Example 1
+++++++++

Create an isolated cloud network and build a server

.. code-block:: yaml
   
    - name: Build Servers on an Isolated Network
      hosts: localhost
      connection: local
      gather_facts: False
      tasks:
        - name: Network create request
          local_action:
            module: rax_network
            credentials: ~/.raxpub
            label: my-net
            cidr: 192.168.3.0/24
            region: IAD
            state: present
            
        - name: Server create request
          local_action:
            module: rax
            credentials: ~/.raxpub
            name: web%04d.example.org
            flavor: 2
            image: ubuntu-1204-lts-precise-pangolin
            disk_config: manual
            networks:
              - public
              - my-net
            region: IAD
            state: present
            count: 5
            exact_count: yes
            group: web
            wait: yes
            wait_timeout: 360
          register: rax

.. _example_2:

Example 2
+++++++++

Build a complete webserver environment with servers, custom networks and load balancers, install nginx and create a custom index.html

.. code-block:: yaml
   
    ---
    - name: Build environment
      hosts: localhost
      connection: local
      gather_facts: False
      tasks:
        - name: Load Balancer create request
          local_action:
            module: rax_clb
            credentials: ~/.raxpub
            name: my-lb
            port: 80
            protocol: HTTP
            algorithm: ROUND_ROBIN
            type: PUBLIC
            timeout: 30
            region: IAD
            wait: yes
            state: present
            meta:
              app: my-cool-app
          register: clb
    
        - name: Network create request
          local_action:
            module: rax_network
            credentials: ~/.raxpub
            label: my-net
            cidr: 192.168.3.0/24
            state: present
            region: IAD
          register: network
    
        - name: Server create request
          local_action:
            module: rax
            credentials: ~/.raxpub
            name: web%04d.example.org
            flavor: performance1-1
            image: ubuntu-1204-lts-precise-pangolin
            disk_config: manual
            networks:
              - public
              - private
              - my-net
            region: IAD
            state: present
            count: 5
            exact_count: yes
            group: web
            wait: yes
          register: rax
    
        - name: Add servers to web host group
          local_action:
            module: add_host
            hostname: "{{ item.name }}"
            ansible_ssh_host: "{{ item.rax_accessipv4 }}"
            ansible_ssh_pass: "{{ item.rax_adminpass }}"
            ansible_ssh_user: root
            groupname: web
          with_items: rax.success
          when: rax.action == 'create'
    
        - name: Add servers to Load balancer
          local_action:
            module: rax_clb_nodes
            credentials: ~/.raxpub
            load_balancer_id: "{{ clb.balancer.id }}"
542
            address: "{{ item.rax_networks.private|first }}"
Matt Martz committed
543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574
            port: 80
            condition: enabled
            type: primary
            wait: yes
            region: IAD
          with_items: rax.success
          when: rax.action == 'create'
    
    - name: Configure servers
      hosts: web
      handlers:
        - name: restart nginx
          service: name=nginx state=restarted
    
      tasks:
        - name: Install nginx
          apt: pkg=nginx state=latest update_cache=yes cache_valid_time=86400
          notify:
            - restart nginx
    
        - name: Ensure nginx starts on boot
          service: name=nginx state=started enabled=yes
    
        - name: Create custom index.html
          copy: content="{{ inventory_hostname }}" dest=/usr/share/nginx/www/index.html
                owner=root group=root mode=0644


.. _advanced_usage:

Advanced Usage
``````````````
575

Matt Martz committed
576 577 578 579 580
.. _awx_autoscale:

AWX Autoscaling
+++++++++++++++

581
AnsibleWorks's "AWX" solution also contains a very nice feature for auto-scaling use cases.  
582 583 584
In this mode, a simple curl script can call a defined URL and the server will "dial out" to the requester 
and configure an instance that is spinning up.  This can be a great way to reconfigure ephmeral nodes.  
See the AWX documentation for more details.  Click on the AWX link in the sidebar for details.
Matt Martz committed
585

586 587
A benefit of using the callback in AWX over pull mode is that job results are still centrally recorded 
and less information has to be shared with remote hosts.
Matt Martz committed
588 589 590 591 592 593

.. _pending_information:

Pending Information
```````````````````

594
More to come!
595 596