# Policy Manager

## Quick Start

To start the policy manager simply run:

```
./neatpmd
```

in the `neat/policy` directory. See `./neatpmd -h` for more options.

## NEAT properties

NEAT properties are the basic building block of the NEAT Policy Manager. Properties are essentially `key|value` pairs used to label or describe any entity referenced by the components of the PM. These include user requirements passed through the NEAT API, features enforced by policies as well as information about supported protocols and network characteristics maintained in the CIB. 

Properties also contain meta attributes such as a `score` denoting the importance or weight of the property and a `precedence`, indicating whether a property is mandatory or optional. The syntax and behaviour of NEAT properties is documented [**here**](doc/NEAT_properties.md).


## NEAT Policies

Policies are based around NEAT properties. In addition to some metadata, each policy contains the following attributes:

+ `match`: object containing a set of properties which trigger the policy. A policy is triggered (or "matches") only if *all* of these properties are contained within the input candidate. An empty or missing match object will match *any* candidate.
+ `properties`: object containing a set of properties which should be applied to the connection candidate (if feasible given the property precedences).

NEAT profiles are functionally identical to NEAT policies but are applied earlier in the Policy Manager workflow.

For more details refer to [**NEAT policies**](doc/NEAT_policies.md).

## Policy Manager Workflow

The Policy Manager listens for *requests* generated by the NEAT logic and returns a list of *candidates* ranked according to the associated property scores. To achieve this the PM queries the Policy Information Base (PIB) and Characteristics Information Base (CIB) repositories. NEAT requests are specified using JSON. In the most basic version a request is comprised of a JSON object containing an arbitrary number of NEAT properties. As an input the PM expects a JSON array containing at least one request:

```
[{"remote_ip": {"value": "10.54.1.23"}, "low_latency": {"value": true}}]
```

Each NEAT request is processed in three steps:

1. **Profile Lookup**: the purpose of this step is to expand high-level properties (e.g., `low_lateny`) to concrete, system-specific properties. This is accomplished using so-called *profiles* which use the same format as policies. The PM performs a lookup in which each request is iteratively compared against all profile entries installed in the PIB. 

   Whenever any subset of the request properties triggers a profile (*match*), the corresponding profile properties are appended to the request (*update*). The updated request becomes a candidate for subsequent lookup steps. Note, that each profile match may yield multiple candidates as an output. Similarly, candidates may be eliminated if any of the profile properties contradict properties in the input.
 
2. **CIB Lookup**: this lookup acts as a filter on the candidate list, either eliminating candidates or updating their properties, using information collected about the system, its connected networks and supported features. Each candidate from the profiles lookup is compared against all *rows* of the CIB table. The lookup returns *N* candidates with the largest aggregate score.

3. **Policy Lookup**: For each resulting  candidate the PM iterates through all policies in the PIB and compares the match properties with the candidate properties. The policy lookup mechanism is identical to the profile lookup. 

After the lookups the PM ranks the resulting candidates by the sum of the scores of their properties, and returns them resulting JSON array to the NEAT logic.


## CIB format

[See CIB documentation](doc/CIB_format.md)

## Policy Manager Interface (PI)

[See PI documentation](doc/pm_interface.md)

# Example

## Running the PM


To start the policy manager run:

```
$ python3.5 ./neatpmd --cib ./examples/cib/ --pib ./examples/pib
```

in the `neat/policy` directory. The `--cib` and `--pib` options specify the respective locations of the CIB and the PIB. By default the PM will create a Unix domain socket located at `~/.neat/neat_pm_socket`, where it will listen for JSON strings containing application requests, and it will output the list of generated candidates. The directory for the domain socket may be overridden using the `--sock` option.

We can test `neatpmd` using the `socat` utility:

```
$ NEAT_PM_SOCKET=~/.neat/neat_pm_socket
$ JSON='[{"transport": {"value": "TCP"}, "MTU": {"value": [1500, Infinity]}, "low_latency": {"precedence": 2, "value": true}, "remote_ip": {"precedence": 2, "value": "10.54.1.23"}}]'
$ echo $JSON | socat -d -d STDIO UNIX-CONNECT:$NEAT_PM_SOCKET
``` 

or to read from a file:

``` 
$ NEAT_PM_SOCKET=~/.neat/neat_pm_socket
$ cat request.json | socat -d -d STDIO UNIX-CONNECT:$NEAT_PM_SOCKET
``` 


The PM will return a JSON string containing the list of connection candidates (two of them for the given example) into the Unix socket. 

```
[{"MTU": {"value": {"end": 9000.0, "start": 1500.0}}, "low_latency": {"precedence": 2, "value": true}, "remote_ip": {"precedence": 2, "value": "10.54.1.23"}, "transport": {"value": "TCP"}}, {"MTU": {"value": {"end": 1500.0, "start": 300.0}}, "low_latency": {"precedence": 2, "value": true}, "remote_ip": {"precedence": 2, "value": "10:54:2.2"}, "transport": {"value": "UDP"}}]
```

## Requirements

The Policy Manager requires Python version 3.5 or higher. The following Python external modules are used if available: `netifaces` (to autogenerate CIB entries for local interfaces), `aiohttp` (for REST API).

On Debian  like systems these modules can be installed using:

```
$ apt install python3-pip
$ pip3 install netifaces
$ pip3 install aiohttp
```

