The CFEngine 3 inventory modules are pieces of CFEngine policy that are loaded
and used by `promises.cf` in order to *inventory* the system.

CFEngine Enterprise has specific functionality to show and use inventory data,
but users of the Community Version can use them as well locally on each host.

## How It Works

The inventory modules are called in `promises.cf`:

```
body common control
{
      bundlesequence => {
                        # Common bundle first (Best Practice)
                          inventory_control,
                          @(inventory.bundles),
                          ...
```

As you see, this calls the `inventory_control` bundle, and then each
bundle in the list `inventory.bundles`. That list is built in the
top-level common `inventory` bundle, which will load the right things
for some common cases. The `any.cf` inventory module is always loaded;
the rest are loaded if they are appropriate for the platform. For
instance, Debian systems will load `debian.cf` and `linux.cf` and
`lsb.cf` but may load others as needed.

The effect for users is that the right inventory modules will be
loaded and evaluated.

The `inventory_control` bundle lives in `def.cf` and defines what
inventory modules should be disabled. You can simply set
`disable_inventory` to avoid the whole system, or you can look for the
`disable_inventory_xyz` class to disable module `xyz`.

Any inventory module works the same way, by doing some discovery work
and then tagging its classes and variables with the `report` or
`inventory` tags.  For example:

```
vars:
    "ports" slist => { @(mon.listening_ports) },
    meta => { "inventory", "attribute_name=Ports listening" };
```

This defines a reported attribute "Ports listening" which contains a
list of strings representing the listening ports. More on this in a
second.

## Your Very Own Inventory Module

The good news is, writing an inventory module is incredibly easy.

They are just CFEngine bundles. You can see a simple one that collects
the listening ports in `any.cf`:

```cf3
bundle agent cfe_autorun_inventory_listening_ports
# @brief Inventory the listening ports
#
# This bundle uses `mon.listening_ports` and is always enabled by
# default, as it runs instantly and has no side effects.
{
  vars:
      "ports" slist => { @(mon.listening_ports) },
      meta => { "inventory", "attribute_name=Ports listening" };
}
```

Well, the slist copy is a CFEngine detail (we get the listening ports from the
monitoring daemon), so just assume that the data is correct. What's important is
the second line that starts with [`meta`](https://docs.cfengine.com/docs/master/reference-promise-types.html#meta).
That defines metadata for the promise that CFEngine will use to determine that
this data is indeed inventory data and should be reported to the CFEngine
Enterprise Hub.

That's it. Really. The comments are optional but nice to have. You don't have to
put your new bundle in a file under the `inventory` directory, either. The
variables and classes can be declared anywhere as long as they have the right
tags. So you can use the `services` directory or whatever else makes sense to
you.

# CFEngine Enterprise vs. Community

In CFEngine Enterprise, the reported data is aggregated in the hub and
reported across the whole host population.

In CFEngine Community, users can use the `classesmatching()` and
`variablesmatching()` functions to collect all the inventory variables
and classes and report them in other ways.

## Implementation Best Practice for CFEngine Enterprise

It is important that inventory variables and classes are continually
defined. Only inventory variables and classes defined during the last
reported run are available for use by the inventory reporting interface.

Inventory items that change frequently can create a burden on the
Enterprise reporting infrastructure. Generally, inventory attributes
should change infrequently.

If you wish to inventory attributes that frequently change or are expensive to
discover consider implementing a sample interval and caching mechanism.

# What Modules Are Available?

As soon as you use the `promises.cf` provided in the parent directory,
quite a few inventory modules will be enabled (if appropriate for your
system). Here's the list of modules and what they provide. Note they
are all enabled by code in `def.cf` as explained above.

## Package Inventory
* lives in: `any.cf`
* applies to: All systems
* runs: package modules in order to report on packages installed and patches
  available
* disable: define the class ```disable_inventory_package_refresh```. Note this
  also disables the default package inventory used by the new packages promise
  implementation. This will cause the ```packagesmatching()``` and
  ```packageupdatesmatching()``` functions to rely on data supplied by the
  legacy package promise implementation.

## LSB

* lives in: `lsb.cf`
* applies to: LSB systems (most Linux distributions, basically)
* runs: `lsb_release -a`
* sample data:

```
Distributor ID:	Ubuntu
Description:	Ubuntu 14.04 LTS
Release:	14.04
Codename:	trusty
```

* provides:
    * classes `lsb_$(os)`, `lsb_$(os)_$(release)`, `lsb_$(os)_$(codename)`
    * variables: `inventory_lsb.os` (Distributor ID), `inventory_lsb.codename`, `inventory_lsb.release`, `inventory_lsb.flavor`, `inventory_lsb.description`

* sample output:

```
% cf-agent -KI -binventory_control,inventory_lsb

R: inventory_lsb: OS = Ubuntu, codename = trusty, release = 14.04, flavor = Ubuntu_14_04, description = Ubuntu 14.04 LTS
```

## SUSE

* lives in: `suse.cf`
* applies to: SUSE Linux
* provides classes: `suse_pure` and `suse_derived`

## Debian

* lives in: `debian.cf`
* applies to: Debian and its derivatives
* provides:
    * variables: `inventory_debian.mint_release` and `inventory_debian.mint_codename`
    * classes: `debian_pure`, `debian_derived`, `linuxmint`, `lmde`, `linuxmint_$(mint_release)`, `linuxmint_$(mint_codename)`, `$(mint_codename)`

## Red Hat

* lives in: `redhat.cf`
* applies to: Red Hat and its derivatives
* provides classes: `redhat_pure`, `redhat_derived`

## Windows

* lives in: `windows.cf`

## Mac OS X

* lives in: `macos.cf`

## Generic (unknown OS)

* lives in: `generic.cf` (see `any.cf` for generally applicable inventory modules)

## LLDP

* lives in: `any.cf`
* runs `inventory_control.lldpctl_exec` through a Perl filter
* provides variables: `cfe_autorun_inventory_LLDP.K` for each `K` returned by the LLDB executable

## mtab

* lives in: `any.cf`
* parses: `/etc/mtab`
* provides classes: `have_mount_FSTYPE` and `have_mount_FSTYPE_MOUNTPOINT`

* sample output (note this is verbose mode with `-v` because there's a lot of output):

```
% cf-agent -Kv -binventory_control,cfe_autorun_inventory_mtab|grep 'cfe_autorun_inventory_mtab: we have'

R: cfe_autorun_inventory_mtab: we have a ext4 mount under /
...
R: cfe_autorun_inventory_mtab: we have a cgroup mount under /sys/fs/cgroup/systemd
R: cfe_autorun_inventory_mtab: we have a tmpfs mount under /run/shm
```

## fstab

* lives in: `any.cf`
* parses: `sys.fstab`
* provides classes: `have_fs_FSTYPE` `have_fs_MOUNTPOINT` and `have_fs_FSTYPE_MOUNTPOINT`

* sample output (note this is verbose mode with `-v` because there's a LOT of output):

```
% cf-agent -Kv -binventory_control,cfe_autorun_inventory_fstab|grep 'cfe_autorun_inventory_fstab: we have'

R: cfe_autorun_inventory_fstab: we have a ext4 fstab entry under /
R: cfe_autorun_inventory_fstab: we have a cifs fstab entry under /backups/load
R: cfe_autorun_inventory_fstab: we have a auto fstab entry under /mnt/cdrom
```

## DMI decoding

* lives in: `any.cf`
* runs: `dmidecode`
* provides variables: `cfe_autorun_inventory_dmidecode.dmi[K]` for each key K in the `dmidecode` output

* sample output (sudo is needed to access the DMI):

```
% sudo /var/cfengine/bin/cf-agent -KI -binventory_control,cfe_autorun_inventory_dmidecode

R: cfe_autorun_inventory_dmidecode: Obtained BIOS vendor = 'Intel Corp.'
R: cfe_autorun_inventory_dmidecode: Obtained BIOS version = 'BLH6710H.86A.0146.2013.1555.1888'
R: cfe_autorun_inventory_dmidecode: Obtained System serial number = ''
R: cfe_autorun_inventory_dmidecode: Obtained System manufacturer = ''
R: cfe_autorun_inventory_dmidecode: Obtained System version = ''
R: cfe_autorun_inventory_dmidecode: Obtained CPU model = 'Intel(R) Core(TM) i7-2600 CPU @ 3.40GHz'
```

## Listening ports

* lives in: `any.cf`
* provides variables: `cfe_autorun_inventory_listening_ports.ports` as a copy of `mon.listening_ports`

## Disk space

* lives in: `any.cf`
* provides variables: `cfe_autorun_inventory_disk.free` as a copy of `mon.value_diskfree`

## Available memory

* lives in: `any.cf`
* provides variables: `cfe_autorun_inventory_memory.free` as a copy of `mon.value_mem_free` and `cfe_autorun_inventory_memory.total` as a copy of `mon.value_mem_total`

## Load average

* lives in: `any.cf`
* provides variables: `cfe_autorun_inventory_loadaverage.value` as a copy of `mon.value_loadavg`

## procfs

* lives in: `any.cf`
* parses: `consoles`, `cpuinfo`, `modules`, `partitions`, `version`
* provides variables: `cfe_autorun_inventory_proc.console_count`, `cfe_autorun_inventory_proc.cpuinfo[K]` for each CPU info key, `cfe_autorun_inventory_proc.paritions[K]` for each partition key
* provides classes: `_have_console_CONSOLENAME`, `have_module_MODULENAME`

* sample output (note this is verbose mode with `-v` because there's a LOT of output):

```
% cf-agent -Kv -binventory_control,cfe_autorun_inventory_proc|grep 'cfe_autorun_inventory_proc: we have'

R: cfe_autorun_inventory_proc: we have console tty0

R: cfe_autorun_inventory_proc: we have module snd_seq_midi
...
R: cfe_autorun_inventory_proc: we have module ghash_clmulni_intel

R: cfe_autorun_inventory_proc: we have cpuinfo flags = fpu vme de pse tsc msr pae mce cx8 apic sep mtrr pge mca cmov pat pse36 clflush dts acpi mmx fxsr sse sse2 ss ht tm pbe syscall nx rdtscp lm constant_tsc arch_perfmon pebs bts rep_good nopl xtopology nonstop_tsc aperfmperf eagerfpu pni pclmulqdq dtes64 monitor ds_cpl vmx smx est tm2 ssse3 cx16 xtpr pdcm pcid sse4_1 sse4_2 x2apic popcnt tsc_deadline_timer aes xsave avx lahf_lm ida arat epb xsaveopt pln pts dtherm tpr_shadow vnmi flexpriority ept vpid
...
R: cfe_autorun_inventory_proc: we have cpuinfo model name = Intel(R) Core(TM) i7-2600 CPU @ 3.40GHz

R: cfe_autorun_inventory_proc: we have partitions sr0 with 1048575 blocks
...
R: cfe_autorun_inventory_proc: we have partitions sda with 468851544 blocks

R: cfe_autorun_inventory_proc: we have kernel version 'Linux version 3.11.0-15-generic (buildd@roseapple) (gcc version 4.8.1 (Ubuntu/Linaro 4.8.1-10ubuntu8) ) #25-Ubuntu SMP Thu Jan 30 17:22:01 UTC 2014'
```
