# CORE Config Services

* Table of Contents
{:toc}

## Overview

Config services are a newer version of services for CORE, that leverage a
templating engine, for more robust service file creation. They also
have the power of configuration key/value pairs that values that can be
defined and displayed within the GUI, to help further tweak a service,
as needed.

CORE services are a convenience for creating reusable dynamic scripts
to run on nodes, for carrying out specific task(s).

This boilds down to the following functions:
* generating files the service will use, either directly for commands or for configuration
* command(s) for starting a service
* command(s) for validating a service
* command(s) for stopping a service

Most CORE nodes will have a default set of services to run, associated with
them. You can however customize the set of services a node will use. Or even
further define a new node type within the GUI, with a set of services, that
will allow quickly dragging and dropping that node type during creation.

## Available Services

| Service Group                    | Services                                                              |
|----------------------------------|-----------------------------------------------------------------------|
| [BIRD](services/bird.md)         | BGP, OSPF, RADV, RIP, Static                                          |
| [EMANE](services/emane.md)       | Transport Service                                                     |
| [FRR](services/frr.md)           | BABEL, BGP, OSPFv2, OSPFv3, PIMD, RIP, RIPNG, Zebra                   |
| [NRL](services/nrl.md)           | arouted, MGEN Sink, MGEN Actor, NHDP, OLSR, OLSRORG, OLSRv2, SMF      |
| [Quagga](services/quagga.md)     | BABEL, BGP, OSPFv2, OSPFv3, OSPFv3 MDR, RIP, RIPNG, XPIMD, Zebra      |
| [SDN](services/sdn.md)           | OVS, RYU                                                              |
| [Security](services/security.md) | Firewall, IPsec, NAT, VPN Client, VPN Server                          |
| [Utility](services/utility.md)   | ATD, Routing Utils, DHCP, FTP, IP Forward, PCAP, RADVD, SSF, UCARP    |
| [XORP](services/xorp.md)         | BGP, OLSR, OSPFv2, OSPFv3, PIMSM4, PIMSM6, RIP, RIPNG, Router Manager |

## Node Types and Default Services

Here are the default node types and their services:

| Node Type | Services                                                                                                                                   |
|-----------|--------------------------------------------------------------------------------------------------------------------------------------------|
| *router*  | zebra, OSFPv2, OSPFv3, and IPForward services for IGP link-state routing.                                                                  |
| *PC*      | DefaultRoute service for having a default route when connected directly to a router.                                                       |
| *mdr*     | zebra, OSPFv3MDR, and IPForward services for wireless-optimized MANET Designated Router routing.                                           |
| *prouter* | a physical router, having the same default services as the *router* node type; for incorporating Linux testbed machines into an emulation. |

Configuration files can be automatically generated by each service. For
example, CORE automatically generates routing protocol configuration for the
router nodes in order to simplify the creation of virtual networks.

To change the services associated with a node, double-click on the node to
invoke its configuration dialog and click on the *Services...* button,
or right-click a node a choose *Services...* from the menu.
Services are enabled or disabled by clicking on their names. The button next to
each service name allows you to customize all aspects of this service for this
node. For example, special route redistribution commands could be inserted in
to the Quagga routing configuration associated with the zebra service.

To change the default services associated with a node type, use the Node Types
dialog available from the *Edit* button at the end of the Layer-3 nodes
toolbar, or choose *Node types...* from the  *Session* menu. Note that
any new services selected are not applied to existing nodes if the nodes have
been customized.

The node types are saved in the GUI config file **~/.coregui/config.yaml**.
Keep this in mind when changing the default services for
existing node types; it may be better to simply create a new node type. It is
recommended that you do not change the default built-in node types.

## New Services

Services can save time required to configure nodes, especially if a number
of nodes require similar configuration procedures. New services can be
introduced to automate tasks.

### Creating New Services

1. Modify the example service shown below
   to do what you want. It could generate config/script files, mount per-node
   directories, start processes/scripts, etc. Your file can define one or more
   classes to be imported. You can create multiple Python files that will be imported.

2. Put these files in a directory such as ~/.coregui/custom_services
   Note that the last component of this directory name **myservices** should not
   be named something like **services** which conflicts with an existing module.

3. Add a **custom_config_services_dir = ~/.coregui/custom_services** entry to the
   /etc/core/core.conf file.

   **NOTE:**
   The directory name used in **custom_services_dir** should be unique and
   should not correspond to
   any existing Python module name. For example, don't use the name **subprocess**
   or **services**.

4. Restart the CORE daemon (core-daemon). Any import errors (Python syntax)
   should be displayed in the terminal (or service log, like journalctl).

5. Start using your custom service on your nodes. You can create a new node
   type that uses your service, or change the default services for an existing
   node type, or change individual nodes. .

### Example Custom Service

Below is the skeleton for a custom service with some documentation. Most
people would likely only setup the required class variables **(name/group)**.
Then define the **files** to generate and implement the
**get_text_template** function to dynamically create the files wanted. Finally,
the **startup** commands would be supplied, which typically tend to be
running the shell files generated.

```python
from typing import Dict, List

from core.config import ConfigString, ConfigBool, Configuration
from core.configservice.base import ConfigService, ConfigServiceMode, ShadowDir

# class that subclasses ConfigService
class ExampleService(ConfigService):
    # unique name for your service within CORE
    name: str = "Example"
    # the group your service is associated with, used for display in GUI
    group: str = "ExampleGroup"
    # directories that the service should shadow mount, hiding the system directory
    directories: List[str] = [
       "/usr/local/core",
    ]
    # files that this service should generate, defaults to nodes home directory
    # or can provide an absolute path to a mounted directory
    files: List[str] = [
        "example-start.sh",
        "/usr/local/core/file1",
    ]
    # executables that should exist on path, that this service depends on
    executables: List[str] = []
    # other services that this service depends on, can be used to define service start order
    dependencies: List[str] = []
    # commands to run to start this service
    startup: List[str] = []
    # commands to run to validate this service
    validate: List[str] = []
    # commands to run to stop this service
    shutdown: List[str] = []
    # validation mode, blocking, non-blocking, and timer
    validation_mode: ConfigServiceMode = ConfigServiceMode.BLOCKING
    # configurable values that this service can use, for file generation
    default_configs: List[Configuration] = [
        ConfigString(id="value1", label="Text"),
        ConfigBool(id="value2", label="Boolean"),
        ConfigString(id="value3", label="Multiple Choice", options=["value1", "value2", "value3"]),
    ]
    # sets of values to set for the configuration defined above, can be used to
    # provide convenient sets of values to typically use
    modes: Dict[str, Dict[str, str]] = {
        "mode1": {"value1": "value1", "value2": "0", "value3": "value2"},
        "mode2": {"value1": "value2", "value2": "1", "value3": "value3"},
        "mode3": {"value1": "value3", "value2": "0", "value3": "value1"},
    }
    # defines directories that this service can help shadow within a node
    shadow_directories: List[ShadowDir] = [
        ShadowDir(path="/user/local/core", src="/opt/core")
    ]

    def get_text_template(self, name: str) -> str:
        return """
        # sample script 1
        # node id(${node.id}) name(${node.name})
        # config: ${config}
        echo hello
        """
```

#### Validation Mode

Validation modes are used to determine if a service has started up successfully.

* blocking - startup commands are expected to run til completion and return 0 exit code
* non-blocking - startup commands are ran, but do not wait for completion
* timer - startup commands are ran, and an arbitrary amount of time is waited to consider started

#### Shadow Directories

Shadow directories provide a convenience for copying a directory and the files within
it to a nodes home directory, to allow a unique set of per node files.

* `ShadowDir(path="/user/local/core")` - copies files at the given location into the node
* `ShadowDir(path="/user/local/core", src="/opt/core")` - copies files to the given location,
  but sourced from the provided location
* `ShadowDir(path="/user/local/core", templates=True)` - copies files and treats them as
  templates for generation
* `ShadowDir(path="/user/local/core", has_node_paths=True)` - copies files from the given
  location, and looks for unique node names directories within it, using a directory named
  default, when not preset
