Creating Partitions on Nodes
============================

Fuel generates Anaconda Kickstart scripts for Red Hat based systems and
preseed files for Ubuntu to partition block devices on new nodes. Most
of the work is done in the pmanager.py_ Cobbler script using the data
from the "ks_spaces" variable generated by the Nailgun VolumeManager_
class based on the volumes metadata defined in the openstack.yaml_
release fixture.

.. _pmanager.py: https://github.com/stackforge/fuel-library/blob/master/deployment/puppet/cobbler/templates/scripts/pmanager.py
.. _VolumeManager: https://github.com/stackforge/fuel-web/blob/master/nailgun/nailgun/volumes/manager.py
.. _openstack.yaml: https://github.com/stackforge/fuel-web/blob/master/nailgun/nailgun/fixtures/openstack.yaml

Volumes are created following best practices for OpenStack and other
components. Following volume types are supported:

vg
  an LVM volume group that can contain one or more volumes with type set
  to "lv"

partition
  plain non-LVM partition

raid
  a Linux software RAID-1 array of LVM volumes

Typical slave node will always have an "os" volume group and one or more
volumes of other types, depending on the roles assigned to that node and
the role-to-volumes mapping defined in the "volumes_roles_mapping"
section of openstack.yaml.

There are a few different ways to add another volume to a slave node:

#. Add a new logical volume definition to one of the existing LVM volume
   groups.
#. Create a new volume group containing your new logical volumes.
#. Create a new plain partition.

Adding an LV to an Existing Volume Group
----------------------------------------

If you need to add a new volume to an existing volume group, for example
"os", your volume definition in openstack.yaml might look like this::

  - id: "os"
    type: "vg"
    min_size: {generator: "calc_min_os_size"}
    label: "Base System"
    volumes:
      - mount: "/"
        type: "lv"
        name: "root"
        size: {generator: "calc_total_root_vg"}
        file_system: "ext4"
      - mount: "swap"
        type: "lv"
        name: "swap"
        size: {generator: "calc_swap_size"}
        file_system: "swap"
      - mount: "/mnt/some/path"
        type: "lv"
        name: "LOGICAL_VOLUME_NAME"
        size:
          generator: "calc_LOGICAL_VOLUME_size"
          generator_args: ["arg1", "arg2"]
        file_system: "ext4"

Make sure that your logical volume name ("LOGICAL_VOLUME_NAME" in the
example above) is not the same as the volume group name ("os"), and
refer to current version of openstack.yaml_ for up-to-date format.

Adding Generators to Nailgun VolumeManager
------------------------------------------

The "size" field in a volume definition can be defined either directly
as an integer number in megabytes, or indirectly via a so called
generator. Generator is a Python lambda that can be called to calculate
logical volume size dynamically. In the json example above size is
defined as a dictionary with two keys: "generator" is the name of the
generator lambda and "generator_args" is the list of arguments that will
be passed to the generator lambda.

There is the method in the VolumeManager class where generators are
defined. New volume generator 'NEW_GENERATOR_TO_CALCULATE_SIZ' needs to
be added in the generators dictionary inside this method.

.. code-block:: python

    class VolumeManager(object):
        ...
        def call_generator(self, generator, *args):
            generators = {
                ...
                'NEW_GENERATOR_TO_CALCULATE_SIZE': lambda: 1000,
                ...
            }


Creating a New Volume Group
---------------------------

Another way to add new volume to slave nodes is to create new volume
group and to define one or more logical volume inside the volume group
definition::


  - id: "NEW_VOLUME_GROUP_NAME"
    type: "vg"
    min_size: {generator: "calc_NEW_VOLUME_NAME_size"}
    label: "Label for NEW VOLUME GROUP as it will be shown on UI"
    volumes:
      - mount: "/path/to/mount/point"
        type: "lv"
        name: "LOGICAL_VOLUME_NAME"
        size:
          generator: "another_generator_to_calc_LOGICAL_VOLUME_size"
          generator_args: ["arg"]
        file_system: "xfs"

Creating a New Plain Partition
------------------------------

Some node roles may be incompatible with LVM and would require plain
partitions. If that's the case, you may have to define a standalone
volume with type "partition" instead of "vg"::

  - id: "NEW_PARTITION_NAME"
    type: "partition"
    min_size: {generator: "calc_NEW_PARTITION_NAME_size"}
    label: "Label for NEW PARTITION as it will be shown on UI"
    mount: "none"
    disk_label: "LABEL"
    file_system: "xfs"

Note how you can set mount point to "none" and define a disk label to
identify the partition instead. Its only possible to set a disk label on
a formatted portition, so you have to set "file_system" parameter to use
disk labels.

Updating the Node Role to Volumes Mapping
-----------------------------------------

Unlike a new logical volume added to a pre-existing logical volume
group, a new logical volume group or partition will not be allocated on
the node unless it is included in the role-to-volumes mapping
corresponding to one of the node's roles, like this::

    volumes_roles_mapping:
      controller:
        - {allocate_size: "min", id: "os"}
        - {allocate_size: "all", id: "image"}
      compute:
        ...

* *controller* - is a role for which partitioning information is given
* *id* - is id of volume group or plain partition
* *allocate_size* - can be "min" or "all"
  * *min* - allocate volume with minimal size
  * *all* - allocate all free space for
  volume, if several volumes have this key
  then free space will be allocated equally

Setting Volume Parameters from Nailgun Settings
-----------------------------------------------

In addition to VolumeManager generators, it is also possible to define
sizes or whatever you want in the nailgun configuration file
(/etc/nailgun/settings.yaml). All fixture files are templated using
Jinja2 templating engine just before being loaded into nailgun database.
For example, we can define mount point for a new volume as follows::

    "mount": "{{settings.NEW_LOGICAL_VOLUME_MOUNT_POINT}}"

Of course, *NEW_LOGICAL_VOLUME_MOUNT_POINT* must be defined in the
settings file.

