Inventory & Data
================

.. admonition:: What is inventory?
    :class: tip

    A pyinfra inventory contains hosts, groups and data. Hosts are where pyinfra will make changes (a server via SSH, a Docker container or the local machine). Groups are collections of one or more host. Data can be assigned to both groups and hosts.

Inventory Files
---------------

Inventory files contain groups of hosts. Groups are defined as a ``list`` or ``tuple``. For example, this inventory creates two groups, ``app_servers`` and ``db_servers``. Note that group names cannot start ``_``:

.. code:: python

    app_servers = [
        "app-1.net",
        "app-2.net"
    ]

    db_servers = (["db-1.net", "db-2.net", "db-3.net",], {})

If you save this file as ``inventory.py``, you can then use it when executing pyinfra:

.. code:: shell

    pyinfra inventory.py OPERATIONS...

.. admonition:: Autogenerated Groups
    :class: note

    A special group, ``all``, will contain every host from the inventory file (whatever the file is called).

    All the hosts are added to a group with the name of the inventory file (eg any hosts defined in ``inventories/production.py`` belong to group ``production``).

.. admonition:: inventories/
    :class: note

    Files in the ``inventories/`` directory are not automatically joined during inventory processing, rather this directory is a convention when wanting
    to store multiple related inventories. Code can be imported from files under ``inventories/`` in to the main ``inventory.py`` if desired.


Host Data
~~~~~~~~~

Data can be assigned to individual hosts in the inventory by using a tuple ``(hostname, data_dict)``:

.. code:: python

    app_servers = [
        ("app-1.net", {"install_postgres": False}),
        ("db-1.net", {"install_postgres": True}),
    ]

This data can then be used in operations:

.. code:: python

    from pyinfra import host

    if host.data.get("install_postgres"):
        apt.packages(
            packages=["postgresql-server"],
        )

Host Name
~~~~~~~~~

The name of the host can be accessed via the ``pyinfra.host.name`` attribute:

.. code:: python

    from pyinfra import host

    print(host.name)  # prints "app-1.net" for example

Groups
------

It is often useful to access the list of groups a host belongs to in operation code, this can be done via the ``pyinfra.host.groups`` attribute:

.. code:: python

    from pyinfra import host

    if "app_servers" in host.groups:
        server.shell(...)

Group Data Files
~~~~~~~~~~~~~~~~

Group data can be stored in separate files under the ``group_data`` directory (there's also a ``--group-data $DIR`` flag). Files will be loaded that match ``group_data/<group_name>.py``, and all hosts in any matching group will receive variables defined in the file as data:

.. code:: python

    app_user = "myuser"
    app_dir = "/opt/pyinfra"

These variables can then be used in operations:

.. code:: python

    from pyinfra import host

    git.repo(
        src="git@github.com:Fizzadar/pyinfra.git",
        dest=host.data.app_dir,
        user=host.data.app_user,
    )

.. Note::
    The ``group_data`` directory is relative to the current working directory. This can be changed at runtime via the ``--chdir`` flag.

Data Hierarchy
--------------

The same keys can be defined for host and group data - this means we can set a default in ``all.py`` and override it on a group or host basis. When accessing data, the first match in the following is returned:

+ "Override" data passed in via CLI args
+ Host data as defined in the inventory file
+ Normal group data
+ ``all`` group data

.. Note::
    pyinfra contains a ``debug-inventory`` command which can be used to explore the data output per-host for a given inventory/deploy, ie ``pyinfra inventory.py debug-inventory``.

Connecting with Data
--------------------

Data can be used to configure connectors. For example, setting SSH connection details can be done like so:

.. code:: python

    ssh_user = "ubuntu"
    ssh_key = "~/.ssh/some_key"
    ssh_key_password = "password for key"

The :doc:`connectors` contains full details of which data keys are available in each connector.

Global Arguments with Data
--------------------------

Data can also provide default values for :doc:`arguments`, for example:

.. code:: python

    _sudo = True
    _sudo_user = "pyinfra"

This can be set during a deploy by updating `host.data`:

.. code:: python

  host.data._sudo_user = 'apache'


External Sources for Data
-------------------------

Because pyinfra is configured in Python, you can pull in data from pretty much anywhere just using other Python packages.
