299 lines
14 KiB
Markdown
299 lines
14 KiB
Markdown
# Services (Deprecated)
|
|
|
|
## Overview
|
|
|
|
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.
|
|
|
|
!!! 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.
|
|
|
|
!!! 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.
|
|
|
|
!!! 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.<session id>/<node name>.conf/**
|
|
|
|
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.
|
|
|
|
!!! 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
|
|
|
|
!!! note
|
|
|
|
The directory name used in **custom_services_dir** below should be unique and
|
|
should not correspond to any existing Python module name. For example, don't
|
|
use the name **subprocess** or **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.
|
|
|
|
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
|
|
**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
|
|
```
|