From bd3e2f5d0e1b5369b1005c93136dec0b016f7156 Mon Sep 17 00:00:00 2001 From: Blake Harnden <32446120+bharnden@users.noreply.github.com> Date: Mon, 15 Nov 2021 14:33:05 -0800 Subject: [PATCH] docs: adding initial documentation to help cover using config services --- docs/configservices.md | 206 +++++++++++++++++++++++++++++++++++++++++ docs/index.md | 1 + docs/services.md | 5 +- 3 files changed, 209 insertions(+), 3 deletions(-) create mode 100644 docs/configservices.md diff --git a/docs/configservices.md b/docs/configservices.md new file mode 100644 index 00000000..93718f3f --- /dev/null +++ b/docs/configservices.md @@ -0,0 +1,206 @@ +# 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 Configuration +from core.configservice.base import ConfigService, ConfigServiceMode, ShadowDir +from core.emulator.enumerations import ConfigDataTypes + +# 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] = [ + Configuration(id="value1", type=ConfigDataTypes.STRING, label="Text"), + Configuration(id="value2", type=ConfigDataTypes.BOOL, label="Boolean"), + Configuration( + id="value3", + type=ConfigDataTypes.STRING, + 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: + if name == "example-start.sh": + 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 diff --git a/docs/index.md b/docs/index.md index 05882860..0bfe5a26 100644 --- a/docs/index.md +++ b/docs/index.md @@ -29,6 +29,7 @@ networking scenarios, security studies, and increasing the size of physical test |[gRPC API](grpc.md)|Covers how control core using gRPC| |[Distributed](distributed.md)|Details for running CORE across multiple servers| |[Control Network](ctrlnet.md)|How to use control networks to communicate with nodes from host| +|[Config Services](configservices.md)|Overview of provided config services and creating custom ones| |[Services](services.md)|Overview of provided services and creating custom ones| |[EMANE](emane.md)|Overview of EMANE integration and integrating custom EMANE models| |[Performance](performance.md)|Notes on performance when using CORE| diff --git a/docs/services.md b/docs/services.md index ccd8e9a5..477a828c 100644 --- a/docs/services.md +++ b/docs/services.md @@ -149,12 +149,11 @@ ideas for a service before adding a new service type. 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. Add any new filenames to the __init__.py file. + files that will be imported. 2. Put these files in a directory such as /home/username/.core/myservices Note that the last component of this directory name **myservices** should not - be named something like **services** which conflicts with an existing Python - name (the syntax 'from myservices import *' is used). + be named something like **services** which conflicts with an existing module. 3. Add a **custom_services_dir = /home/username/.core/myservices** entry to the /etc/core/core.conf file.