summaryrefslogtreecommitdiff
path: root/doc/source/admin/drivers/ansible.rst
blob: 20fb5905aa316750d3edba72533362bf586fe656 (plain)
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
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
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
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
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
419
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
========================
Ansible deploy interface
========================

`Ansible`_ is a mature and popular automation tool, written in Python
and requiring no agents running on the node being configured.
All communications with the node are by default performed over secure SSH
transport.

The ``ansible`` deploy interface uses Ansible playbooks to define the
deployment logic. It is not based on `Ironic Python Agent`_ (IPA)
and does not generally need IPA to be running in the deploy ramdisk.

Overview
========

The main advantage of this deploy interface is extended flexibility in
regards to changing and adapting node deployment logic for specific
use cases, via Ansible tooling that is already familiar to operators.

It can be used to shorten the usual feature development cycle of

* implementing logic in ironic,
* implementing logic in IPA,
* rebuilding deploy ramdisk,
* uploading deploy ramdisk to Glance/HTTP storage,
* reassigning deploy ramdisk to nodes,
* restarting ironic-conductor service(s) and
* running a test deployment

by using a "stable" deploy ramdisk and not requiring
ironic-conductor restarts (see `Extending playbooks`_).

The main disadvantage of this deploy interface is the synchronous manner
of performing deployment/cleaning tasks.
A separate ``ansible-playbook`` process is spawned for each node being
provisioned or cleaned, which consumes one thread from the thread pool
available to the ``ironic-conductor`` process and blocks this thread until
the node provisioning or cleaning step is finished or fails.
This has to be taken into account when planning an ironic deployment
that enables this deploy interface.

Each action (deploy, clean) is described by a single playbook with roles,
which is run whole during deployment, or tag-wise during cleaning.
Control of cleaning steps is through tags and auxiliary clean steps file.
The playbooks for actions can be set per-node, as can the clean steps
file.

Features
--------

Similar to deploy interfaces relying on `Ironic Python Agent`_, this deploy
interface also depends on the deploy ramdisk calling back to ironic API's
``heartbeat`` endpoint.

However, the driver is currently synchronous, so only the first heartbeat is
processed and is used as a signal to start ``ansible-playbook`` process.

User images
~~~~~~~~~~~

Supports whole-disk images and partition images:

- compressed images are downloaded to RAM and converted to disk device;
- raw images are streamed to disk directly.

For partition images the driver will create root partition, and,
if requested, ephemeral and swap partitions as set in node's
``instance_info`` by the Compute service or operator.
The create partition table will be of ``msdos`` type by default,
the node's ``disk_label`` capability is honored if set in node's
``instance_info`` (see also :ref:`choosing_the_disk_label`).

Configdrive partition
~~~~~~~~~~~~~~~~~~~~~

Creating a configdrive partition is supported for both whole disk
and partition images, on both ``msdos`` and ``GPT`` labeled disks.

Root device hints
~~~~~~~~~~~~~~~~~

Root device hints are currently supported in their basic form only,
with exact matches (see :ref:`root-device-hints` for more details).
If no root device hint is provided for the node, the first device returned as
part of ``ansible_devices`` fact is used as root device to create partitions
on or write the whole disk image to.

Node cleaning
~~~~~~~~~~~~~

Cleaning is supported, both automated and manual.
The driver has two default clean steps:

- wiping device metadata
- disk shredding

Their priority can be overridden via
``[deploy]\erase_devices_metadata_priority`` and
``[deploy]\erase_devices_priority`` options, respectively, in the ironic
configuration file.

As in the case of this driver all cleaning steps are known to the
ironic-conductor service, booting the deploy ramdisk is completely skipped
when there are no cleaning steps to perform.

.. note::

   Aborting cleaning steps is not supported.

Logging
~~~~~~~

Logging is implemented as custom Ansible callback module,
that makes use of ``oslo.log`` and ``oslo.config`` libraries
and can re-use logging configuration defined in the main ironic configuration
file to set logging for Ansible events, or use a separate file for this purpose.

It works best when ``journald`` support for logging is enabled.


Requirements
============

Ansible
    Tested with, and targets, Ansible 2.4.x

Bootstrap image requirements
----------------------------

- password-less sudo permissions for the user used by Ansible
- python 2.7.x
- openssh-server
- GNU coreutils
- utils-linux
- parted
- gdisk
- qemu-utils
- python-requests (for ironic callback and streaming image download)
- python-netifaces (for ironic callback)

A set of scripts to build a suitable deploy ramdisk based on TinyCore Linux
and ``tinyipa`` ramdisk, and an element for ``diskimage-builder`` can be found
in ironic-staging-drivers_ project but will be eventually migrated to the new
ironic-python-agent-builder_ project.

Setting up your environment
===========================

#. Install ironic (either as part of OpenStack or standalone)

   - If using ironic as part of OpenStack, ensure that the Image service is
     configured to use the Object Storage service as backend,
     and the Bare Metal service is configured accordingly, see
     :doc:`Configure the Image service for temporary URLs <../../install/configure-glance-swift>`.

#. Install Ansible version as specified in ``ironic/driver-requirements.txt``
   file
#. Edit ironic configuration file

   A. Add ``ansible`` to the list of deploy interfaces defined in
      ``[DEFAULT]\enabled_deploy_interfaces`` option.
   B. Ensure that a hardware type supporting ``ansible`` deploy interface
      is enabled in ``[DEFAULT]\enabled_hardware_types`` option.
   C. Modify options in the ``[ansible]`` section of ironic's configuration
      file if needed (see `Configuration file`_).

#. (Re)start ironic-conductor service
#. Build suitable deploy kernel and ramdisk images
#. Upload them to Glance or put in your HTTP storage
#. Create new or update existing nodes to use the enabled driver
   of your choice and populate `Driver properties for the Node`_ when
   different from defaults.
#. Deploy the node as usual.

Ansible-deploy options
----------------------

Configuration file
~~~~~~~~~~~~~~~~~~~

Driver options are configured in ``[ansible]`` section of ironic
configuration file, for their descriptions and default values please see
`configuration file sample  <../../configuration/config.html#ansible>`_.

Driver properties for the Node
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

Set them per-node via ``openstack baremetal node set`` command,
for example:

.. code-block:: shell

   openstack baremetal node set <node> \
       --deploy-interface ansible \
       --driver-info ansible_username=stack \
       --driver-info ansible_key_file=/etc/ironic/id_rsa


ansible_username
    User name to use for Ansible to access the node.
    Default is taken from ``[ansible]/default_username`` option of the
    ironic configuration file (defaults to ``ansible``).

ansible_key_file
    Private SSH key used to access the node.
    Default is taken from ``[ansible]/default_key_file`` option of the
    ironic configuration file.
    If neither is set, the default private SSH keys of the user running
    the ``ironic-conductor`` process will be used.

ansible_deploy_playbook
    Playbook to use when deploying this node.
    Default is taken from ``[ansible]/default_deploy_playbook`` option of the
    ironic configuration file (defaults to ``deploy.yaml``).

ansible_shutdown_playbook
    Playbook to use to gracefully shutdown the node in-band.
    Default is taken from ``[ansible]/default_shutdown_playbook`` option of the
    ironic configuration file (defaults to ``shutdown.yaml``).

ansible_clean_playbook
    Playbook to use when cleaning the node.
    Default is taken from ``[ansible]/default_clean_playbook`` option of the
    ironic configuration file (defaults to ``clean.yaml``).

ansible_clean_steps_config
    Auxiliary YAML file that holds description of cleaning steps
    used by this node, and defines playbook tags in
    ``ansible_clean_playbook`` file corresponding to each cleaning step.
    Default is taken from ``[ansible]/default_clean_steps_config`` option of the
    ironic configuration file (defaults to ``clean_steps.yaml``).


Customizing the deployment logic
================================


Expected playbooks directory layout
-----------------------------------

The ``[ansible]\playbooks_path`` option in the ironic configuration file
is expected to have a standard layout for an Ansible project with
some additions::

    <playbooks_path>
    |
    \_ inventory
    \_ add-ironic-nodes.yaml
    \_ roles
     \_ role1
     \_ role2
     \_ ...
    |
    \_callback_plugins
     \_ ...
    |
    \_ library
     \_ ...


The extra files relied by this driver are:

inventory
    Ansible inventory file containing a single entry of
    ``conductor ansible_connection=local``.
    This basically defines an alias to ``localhost``.
    Its purpose is to make logging for tasks performed by Ansible locally and
    referencing the localhost in playbooks more intuitive.
    This also suppresses warnings produced by Ansible about ``hosts`` file
    being empty.

add-ironic-nodes.yaml
    This file contains an Ansible play that populates in-memory Ansible
    inventory with access information received from the ansible-deploy
    interface, as well as some per-node variables.
    Include it in all your custom playbooks as the first play.

The default ``deploy.yaml`` playbook is using several smaller roles that
correspond to particular stages of deployment process:

- ``discover`` - e.g. set root device and image target
- ``prepare`` - if needed, prepare system, for example create partitions
- ``deploy`` - download/convert/write user image and configdrive
- ``configure`` - post-deployment steps, e.g. installing the bootloader

Some more included roles are:

- ``shutdown`` - used to gracefully power the node off in-band
- ``clean`` - defines cleaning procedure, with each clean step defined
  as separate playbook tag.

Extending playbooks
-------------------

Most probably you'd start experimenting like this:

#. Create a copy of ``deploy.yaml`` playbook *in the same folder*,
   name it distinctively.
#. Create Ansible roles with your customized logic in ``roles`` folder.

   A. In your custom deploy playbook, replace the ``prepare`` role
      with your own one that defines steps to be run
      *before* image download/writing.
      This is a good place to set facts overriding those provided/omitted
      by the driver, like ``ironic_partitions`` or ``ironic_root_device``,
      and create custom partitions or (software) RAIDs.
   B. In your custom deploy playbook, replace the ``configure`` role
      with your own one that defines steps to be run
      *after* image is written to disk.
      This is a good place for example to configure the bootloader and
      add kernel options to avoid additional reboots.
   C. Use those new roles in your new playbook.

#. Assign the custom deploy playbook you've created to the node's
   ``driver_info/ansible_deploy_playbook`` field.
#. Run deployment.

   A. No ironic-conductor restart is necessary.
   B. A new deploy ramdisk must be built and assigned to nodes only when
      you want to use a command/script/package not present in the current
      deploy ramdisk and you can not or do not want to install those
      at runtime.

Variables you have access to
----------------------------

This driver will pass the single JSON-ified extra var argument to
Ansible (as in ``ansible-playbook -e ..``).
Those values are then accessible in your plays as well
(some of them are optional and might not be defined):

.. code-block:: yaml


   ironic:
     nodes:
     - ip: "<IPADDRESS>"
       name: "<NODE_UUID>"
       user: "<USER ANSIBLE WILL USE>"
       extra: "<COPY OF NODE's EXTRA FIELD>"
     image:
       url: "<URL TO FETCH THE USER IMAGE FROM>"
       disk_format: "<qcow2|raw|...>"
       container_format: "<bare|...>"
       checksum: "<hash-algo:hashstring>"
       mem_req: "<REQUIRED FREE MEMORY TO DOWNLOAD IMAGE TO RAM>"
       tags: "<LIST OF IMAGE TAGS AS DEFINED IN GLANCE>"
       properties: "<DICT OF IMAGE PROPERTIES AS DEFINED IN GLANCE>"
     configdrive:
       type: "<url|file>"
       location: "<URL OR PATH ON CONDUCTOR>"
     partition_info:
       label: "<msdos|gpt>"
       preserve_ephemeral: "<bool>"
       ephemeral_format: "<FILESYSTEM TO CREATE ON EPHEMERAL PARTITION>"
       partitions: "<LIST OF PARTITIONS IN FORMAT EXPECTED BY PARTED MODULE>"


``ironic.nodes``
    List of dictionaries (currently of only one element) that will be used by
    ``add-ironic-nodes.yaml`` play to populate in-memory inventory.
    It also contains a copy of node's ``extra`` field so you can access it in
    the playbooks. The Ansible's host is set to node's UUID.

``ironic.image``
    All fields of node's ``instance_info`` that start with ``image_`` are
    passed inside this variable. Some extra notes and fields:

    - ``mem_req`` is calculated from image size (if available) and config
      option ``[ansible]extra_memory``.
    - if ``checksum`` is not in the form ``<hash-algo>:<hash-sum>``, hashing
      algorithm is assumed to be ``md5`` (default in Glance).
    - ``validate_certs`` - boolean (``yes/no``) flag that turns validating
      image store SSL certificate on or off (default is 'yes').
      Governed by ``[ansible]image_store_insecure`` option
      in ironic configuration file.
    - ``cafile`` - custom CA bundle to use for validating image store
      SSL certificate.
      Takes value of ``[ansible]image_store_cafile`` if that is defined.
      Currently is not used by default playbooks, as Ansible has no way to
      specify the custom CA bundle to use for single HTTPS actions,
      however you can use this value in your custom playbooks to for example
      upload and register this CA in the ramdisk at deploy time.
    - ``client_cert`` - cert file for client-side SSL authentication.
      Takes value of ``[ansible]image_store_certfile`` option if defined.
      Currently is not used by default playbooks,
      however you can use this value in your custom playbooks.
    - ``client_key`` - private key file for client-side SSL authentication.
      Takes value of ``[ansible]image_store_keyfile`` option if defined.
      Currently is not used by default playbooks,
      however you can use this value in your custom playbooks.

``ironic.partition_info.partitions``
    Optional. List of dictionaries defining partitions to create on the node
    in the form:

    .. code-block:: yaml

       partitions:
       - name: "<NAME OF PARTITION>"
         unit: "<UNITS FOR SIZE>"
         size: "<SIZE OF THE PARTITION>"
         type: "<primary|extended|logical>"
         align: "<ONE OF PARTED_SUPPORTED OPTIONS>"
         format: "<PARTITION TYPE TO SET>"
         flags:
           flag_name: "<bool>"

    The driver will populate this list from ``root_gb``, ``swap_mb`` and
    ``ephemeral_gb`` fields of ``instance_info``.
    The driver will also prepend the ``bios_grub``-labeled partition
    when deploying on GPT-labeled disk,
    and pre-create a 64 MiB partition for configdrive if it is set in
    ``instance_info``.

    Please read the documentation included in the ``ironic_parted`` module's
    source for more info on the module and its arguments.

``ironic.partition_info.ephemeral_format``
    Optional. Taken from ``instance_info``, it defines file system to be
    created on the ephemeral partition.
    Defaults to the value of ``[pxe]\default_ephemeral_format`` option
    in ironic configuration file.

``ironic.partition_info.preserve_ephemeral``
    Optional. Taken from the ``instance_info``, it specifies if the ephemeral
    partition must be preserved or rebuilt. Defaults to ``no``.

As usual for Ansible playbooks, you also have access to standard
Ansible facts discovered by ``setup`` module.

Included custom Ansible modules
-------------------------------

The provided ``playbooks_path/library`` folder includes several custom
Ansible modules used by default implementation of ``deploy`` and
``prepare`` roles.
You can use these modules in your playbooks as well.

``stream_url``
    Streaming download from HTTP(S) source to the disk device directly,
    tries to be compatible with Ansible's ``get_url`` module in terms of
    module arguments.
    Due to the low level of such operation it is not idempotent.

``ironic_parted``
    creates partition tables and partitions with ``parted`` utility.
    Due to the low level of such operation it is not idempotent.
    Please read the documentation included in the module's source
    for more information about this module and its arguments.
    The name is chosen so that the ``parted`` module included in Ansible
    is not shadowed.

.. _Ansible: http://docs.ansible.com/ansible/latest/index.html
.. _Ironic Python Agent: http://docs.openstack.org/developer/ironic-python-agent
.. _ironic-staging-drivers: http://git.openstack.org/cgit/openstack/ironic-staging-drivers/tree/imagebuild?h=stable/pike
.. _ironic-python-agent-builder: http://git.openstack.org/cgit/openstack/ironic-python-agent-builder