core-extra/docs/services.md

296 lines
14 KiB
Markdown
Raw Normal View History

# CORE Services
* Table of Contents
{:toc}
## Services
CORE uses the concept of services to specify what processes or scripts run on a
node when it is started. Layer-3 nodes such as routers and PCs are defined by
the services that they run.
Services may be customized for each node, or new custom services can be
created. New node types can be created each having a different name, icon, and
set of default services. Each service defines the per-node directories,
configuration files, startup index, starting commands, validation commands,
shutdown commands, and meta-data associated with a node.
2020-04-10 05:33:20 +01:00
> **NOTE:** **Network namespace nodes do not undergo the normal Linux boot process**
using the **init**, **upstart**, or **systemd** frameworks. These
lightweight nodes use configured CORE *services*.
## 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. |
| *host* | DefaultRoute and SSH services, representing an SSH server having a default route when connected directly to a router. |
| *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.
## Customizing a Service
A service can be fully customized for a particular node. From the node's
configuration dialog, click on the button next to the service name to invoke
the service customization dialog for that service.
The dialog has three tabs for configuring the different aspects of the service:
files, directories, and startup/shutdown.
2020-04-10 05:33:20 +01:00
> **NOTE:** A **yellow** customize icon next to a service indicates that service
requires customization (e.g. the *Firewall* service).
A **green** customize icon indicates that a custom configuration exists.
Click the *Defaults* button when customizing a service to remove any
customizations.
The Files tab is used to display or edit the configuration files or scripts that
are used for this service. Files can be selected from a drop-down list, and
their contents are displayed in a text entry below. The file contents are
generated by the CORE daemon based on the network topology that exists at
the time the customization dialog is invoked.
The Directories tab shows the per-node directories for this service. For the
default types, CORE nodes share the same filesystem tree, except for these
per-node directories that are defined by the services. For example, the
**/var/run/quagga** directory needs to be unique for each node running
the Zebra service, because Quagga running on each node needs to write separate
PID files to that directory.
2020-04-10 05:33:20 +01:00
> **NOTE:** The **/var/log** and **/var/run** directories are
mounted uniquely per-node by default.
Per-node mount targets can be found in **/tmp/pycore.nnnnn/nN.conf/**
(where *nnnnn* is the session number and *N* is the node number.)
The Startup/shutdown tab lists commands that are used to start and stop this
service. The startup index allows configuring when this service starts relative
to the other services enabled for this node; a service with a lower startup
index value is started before those with higher values. Because shell scripts
generated by the Files tab will not have execute permissions set, the startup
commands should include the shell name, with
something like ```sh script.sh```.
Shutdown commands optionally terminate the process(es) associated with this
service. Generally they send a kill signal to the running process using the
*kill* or *killall* commands. If the service does not terminate
the running processes using a shutdown command, the processes will be killed
when the *vnoded* daemon is terminated (with *kill -9*) and
the namespace destroyed. It is a good practice to
specify shutdown commands, which will allow for proper process termination, and
for run-time control of stopping and restarting services.
Validate commands are executed following the startup commands. A validate
command can execute a process or script that should return zero if the service
has started successfully, and have a non-zero return value for services that
have had a problem starting. For example, the *pidof* command will check
if a process is running and return zero when found. When a validate command
produces a non-zero return value, an exception is generated, which will cause
an error to be displayed in the Check Emulation Light.
2020-04-10 05:33:20 +01:00
> **NOTE:** To start, stop, and restart services during run-time, right-click a
node and use the *Services...* menu.
## 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.
### Leveraging UserDefined
The easiest way to capture the configuration of a new process into a service
is by using the **UserDefined** service. This is a blank service where any
aspect may be customized. The UserDefined service is convenient for testing
ideas for a service before adding a new service type.
### 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. sample.py is a Python file that
defines 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 `/home/<user>/.coregui/custom_services`
Note that the last component of this directory name **custom_services** should not
be named the same as any python module, due to naming conflicts.
3. Add a **custom_services_dir = `/home/<user>/.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 daemon output.
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.
If you have created a new service type that may be useful to others, please
consider contributing it to the CORE project.
#### 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 **configs** (files they want to generate) and implement the
2020-11-13 03:36:14 +00:00
**generate_config** function to dynamically create the files wanted. Finally
the **startup** commands would be supplied, which typically tends to be
running the shell files generated.
```python
"""
Simple example custom service, used to drive shell commands on a node.
"""
from typing import Tuple
from core.nodes.base import CoreNode
from core.services.coreservices import CoreService, ServiceMode
class ExampleService(CoreService):
"""
Example Custom CORE Service
:cvar name: name used as a unique ID for this service and is required, no spaces
:cvar group: allows you to group services within the GUI under a common name
:cvar executables: executables this service depends on to function, if executable is
not on the path, service will not be loaded
:cvar dependencies: services that this service depends on for startup, tuple of
service names
:cvar dirs: directories that this service will create within a node
:cvar configs: files that this service will generate, without a full path this file
goes in the node's directory e.g. /tmp/pycore.12345/n1.conf/myfile
:cvar startup: commands used to start this service, any non-zero exit code will
cause a failure
:cvar validate: commands used to validate that a service was started, any non-zero
exit code will cause a failure
:cvar validation_mode: validation mode, used to determine startup success.
NON_BLOCKING - runs startup commands, and validates success with validation commands
BLOCKING - runs startup commands, and validates success with the startup commands themselves
TIMER - runs startup commands, and validates success by waiting for "validation_timer" alone
:cvar validation_timer: time in seconds for a service to wait for validation, before
determining success in TIMER/NON_BLOCKING modes.
:cvar validation_period: period in seconds to wait before retrying validation,
only used in NON_BLOCKING mode
:cvar shutdown: shutdown commands to stop this service
"""
name: str = "ExampleService"
group: str = "Utility"
executables: Tuple[str, ...] = ()
dependencies: Tuple[str, ...] = ()
dirs: Tuple[str, ...] = ()
configs: Tuple[str, ...] = ("myservice1.sh", "myservice2.sh")
startup: Tuple[str, ...] = tuple(f"sh {x}" for x in configs)
validate: Tuple[str, ...] = ()
validation_mode: ServiceMode = ServiceMode.NON_BLOCKING
validation_timer: int = 5
validation_period: float = 0.5
shutdown: Tuple[str, ...] = ()
@classmethod
def on_load(cls) -> None:
"""
Provides a way to run some arbitrary logic when the service is loaded, possibly
to help facilitate dynamic settings for the environment.
:return: nothing
"""
pass
@classmethod
def get_configs(cls, node: CoreNode) -> Tuple[str, ...]:
"""
Provides a way to dynamically generate the config files from the node a service
will run. Defaults to the class definition and can be left out entirely if not
needed.
:param node: core node that the service is being ran on
:return: tuple of config files to create
"""
return cls.configs
@classmethod
def generate_config(cls, node: CoreNode, filename: str) -> str:
"""
Returns a string representation for a file, given the node the service is
starting on the config filename that this information will be used for. This
must be defined, if "configs" are defined.
:param node: core node that the service is being ran on
:param filename: configuration file to generate
:return: configuration file content
"""
cfg = "#!/bin/sh\n"
if filename == cls.configs[0]:
cfg += "# auto-generated by MyService (sample.py)\n"
for iface in node.get_ifaces():
cfg += f'echo "Node {node.name} has interface {iface.name}"\n'
elif filename == cls.configs[1]:
cfg += "echo hello"
return cfg
@classmethod
def get_startup(cls, node: CoreNode) -> Tuple[str, ...]:
"""
Provides a way to dynamically generate the startup commands from the node a
service will run. Defaults to the class definition and can be left out entirely
if not needed.
:param node: core node that the service is being ran on
:return: tuple of startup commands to run
"""
return cls.startup
@classmethod
def get_validate(cls, node: CoreNode) -> Tuple[str, ...]:
"""
Provides a way to dynamically generate the validate commands from the node a
service will run. Defaults to the class definition and can be left out entirely
if not needed.
:param node: core node that the service is being ran on
:return: tuple of commands to validate service startup with
"""
return cls.validate
```