服务
CORE 使用服务的概念来指定节点启动时运行哪些进程或脚本。路由器和PC等第三层节点是由他们所运行的服务来定义的。
可以为每个节点定制服务,也可以创造新的自定义服务。可以创建具有不同名称、图标和默认服务集的新节点类型。 每个服务定义每个节点的路径、配置文件、启动索引、启动命令、验证命令、关闭命令和与节点关联的元数据。
注意: 使用 init, upstart, 或 systemd 框架时,网络名称节点空间不会经历正常的Linux引导过程 ,这些轻量节点使用已经配置好的CORE服务。
提供的服务
服务组 | 服务 | 总结 |
---|---|---|
BIRD | BGP, OSPF, RADV, RIP, Static | |
EMANE | Transport Service | |
FRR | BABEL, BGP, OSPFv2, OSPFv3, PIMD, RIP, RIPNG, Zebra | |
NRL | arouted, MGEN Sink, MGEN Actor, NHDP, OLSR, OLSRORG, OLSRv2, SMF | |
Quagga | BABEL, BGP, OSPFv2, OSPFv3, OSPFv3 MDR, RIP, RIPNG, XPIMD, Zebra | |
SDN | OVS, RYU | |
Security | Firewall, IPsec, NAT, VPN Client, VPN Server | |
Utility | ATD, Routing Utils, DHCP, FTP, IP Forward, PCAP, RADVD, SSF, UCARP | |
XORP | BGP, OLSR, OSPFv2, OSPFv3, PIMSM4, PIMSM6, RIP, RIPNG, Router Manager |
节点类型和默认服务
以下是默认节点类型和他们的服务:
节点类型 | 服务 |
---|---|
router | 针对IGP链路状态路由的 zebra, OSFPv2, OSPFv3, and IPForward 服务。 |
host | 默认路由和SSH服务, 其表示直接连接到路由器时,SSH具有默认路由。 |
PC | 为拥有默认路由且直接连接到路由器的节点提供默认路由服务. |
mdr | 针对无线优化的 MANET 指定路由的 zebra、 OSPFv3MDR 和 IPForward 服务。 |
prouter | 和 路由器 节点类型具有相同默认服务的物理路由器; 用于将Linux测试平台设备合并到仿真中。 |
配置文件可以由每个服务自动生成。例如,CORE 会自动为路由器节点生成路由协议配置,来简化虚拟网络的创建。
更改与节点相关联的服务,可以双击节点来调用配置对话框, 然后单击 服务… 按钮,或者右键单击某个节点,从右键菜单中选择 服务… 选项。通过单击服务的名称可以启用或禁止该服务。每个服务名称旁边的按钮允许您为该节点自定义此服务的所有方面。例如,可以将特殊的路由重分发命令插入到与 zebra 服务关联的 Quagga 路由配置中。
若要更改与节点类型相关的默认服务, 请使用第三层节点工具栏末端的 编辑 按钮中的 节点类型… 对话框,或是从 会话 菜单中选择 节点类型…。 注意,如果已经定制了节点,那么所选择的任何新服务都不会应用于现有节点。
节点类型被保存在 ~/.core/nodes.conf 文件中,而不是 .imn 文件。在更改现有节点的默认服务时请记住这一点; 最好只创建一个新的节点类型。并且建议不要更改默认的内置节点类型。可以在 CORE 设备之间复制nodes.conf 文件来保存你的自定义类型。
定制服务
可以为特定节点完全定制服务。 从节点的配置对话框中,单击服务名称旁边的按钮,来调用该服务的服务定制对话框。该对话框有三个选项卡用于配置服务的不同方面:Files, Directories和tartup/shutdown。
服务旁边的 黄色 自定义图标表示服务需要自定义(例如 Firewall 服务)。绿色 的自定义图标表示存在自定义配置。在自定义服务时单击 default 按钮会移除所有自定义选项。
Files选项卡用于显示或编辑用于该服务的配置文件或脚本。文件可以从下拉列表中选择,它们的内容将显示在下面的文本框中。文件的内容由 CORE daemon根据自定义对话框调用时存在的网络拓扑进程生成。
Directories选项卡显示该服务的每个节点的路径。对于默认类型,CORE节点共享相同的文件系统树,但被服务定义的每个节点除外。例如,对于每个运行 Zebra 服务的节点,其 /var/run/quagga 路径必须是唯一的,因为在每个节点运行的 Quagga 需要向该路径写入单独的 PID 文件。
默认情况下, /var/log 和 /var/run 路径按照每个节点唯一挂载。每个节点的挂载目标可以在 /tmp/pycore.nnnnn/nN.conf/ (其中 nnnnn 是会话编号,N是节点编号)中找到。
Startup/shutdown 选项卡列出用于启动和停止该服务的命令。 startup index允许在该服务启动时对为该节点启用的其他相关服务进行配置;Startup较低的服务先于Startup较高的服务. 由于Files选项卡生成的shell脚本没有执行权限设置,因此启动命令应包含shell名称,类似于 sh script.sh
。
注意! 其中的 start time 及index 选项我简单浏览了 core/daemon/core/services/coreservices.py 的 CoreService类,似乎作者移除了这两项的实现。在后续的 ServiceShim 的 tovaluelist 方法中直接填入写死的index = 0以及time = 0。可能是原有的bug导致作者放弃了实现这个功能。
Shutdown 命令可选择终止与此服务关联的进程。通常,它们使用 kill 或 killall 命令向正在运行的进程发送kill 信号。如果服务没有使用 Shutdown 命令终止正在运行的进程,那么当终止 vnoded 守护进程(使用 kill -9命令)并摧毁命名空间时,进程将被杀死。指定 shutdown 命令是一个很好的实践,这将允许适当的进程终止,以及停止和重启服务的运行控制。
Validate 命令按照启动命令执行。 Validate 命令可以执行应返回0值的流程或者脚本,对于启动出现问题的服务返回非0值。例如,pidof 命令将检查某个进程是否正在运行,是则返回0值。当 Validate 命令生成了一个非零返回值,这将产生一个异常而导致在 Check Emulation Light 中显示一个错误。
在运行时启动、停止和重启服务,需要右键单击节点并使用 服务… 菜单。
新的服务
服务可以节省配置节点所需的时间,特别是当大量节点都需要类似的配置程序时。此时可以引入新的服务来使任务自动化。
如果使用tlv等界面api配置如下所示:
其中File name和startup为必选项,其作用为节点容器初始化后复制一份脚本名为File name的脚本至于temp中的节点文件夹,startup等命令除了初始化仿真会调用也可通过界面选项来使用,具体参考其他server选项做法。
利用用户自定义
将新流程的配置捕获到服务中的最简单方法是使用 UserDefined 服务。这是一个空白服务,可以自定义其中的任何方面。 UserDefined 服务便于在添加新的服务类型之前测试服务。
创建新的服务
修改如下所示的实例服务,以便做您想做的事情。它可以生成配置/脚本文件、每个节点的挂载路径、启动进程/脚本等。sample.py 是一个 Python 文件,它定义了一个或更多需要导入的类。您可以创建多个将被导入的Python文件。将任何新文件名添加到 init.py 文件中。
把这些文件放在诸如 /home/username/.core/myservices 这样的路径中。但应注意路径最后的名称 myservices 不应该命名为类似于与现有的Python名称冲突的 服务(使用的语法是 from myservices import *’)
添加命令 custom_services_dir = /home/username/.core/myservices 到 /etc/core/core.conf 文件中。
注意: 在 custom_services_dir 使用的路径名应该是唯一的,并且不应该对应于任何现有的Python模块名。例如,不要使用 subprocess 或是 services 名称。
重启 CORE 守护进程 (core-daemon). 任何导入错误 (Python 语法)都应显示在 /var/log/core-daemon.log 日志文件上(或显示在屏幕上)。
开始在节点上使用自定义服务吧。您可以创建使用您的服务的新节点类型,或者更改现有节点的默认服务,又或者更 改单个节点。
如果您已经创建了一个可能对他人有用的新服务类型,请考虑将其贡献给 CORE 项目。
自定义服务示例
下面是带有一些说明文档的自定义服务框架。大多数人可能只会设置所需的类变量 (name/group)。然后定义 configs (他们想要生成的文件),并实现 generate_config 函数来动态创建所需的文件。最后,被提供的 startup 命令通常倾向于运行生成的shell文件。
"""
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