commit
56e98f412c
11 changed files with 239 additions and 151 deletions
10
CHANGELOG.md
10
CHANGELOG.md
|
@ -1,3 +1,13 @@
|
||||||
|
## 2020-09-29 CORE 7.2.1
|
||||||
|
|
||||||
|
* core-daemon
|
||||||
|
* fixed issue where shutting down sessions may not have removed session directories
|
||||||
|
* fixed issue with multiple emane interfaces on the same node not getting the right configuration
|
||||||
|
* Installation
|
||||||
|
* updated automated install to be a bit more robust for alternative distros
|
||||||
|
* added force install type to try and leverage a redhat/debian like install
|
||||||
|
* locked ospf mdr version installed to older commit to avoid issues with multiple interfaces on same node
|
||||||
|
|
||||||
## 2020-09-15 CORE 7.2.0
|
## 2020-09-15 CORE 7.2.0
|
||||||
|
|
||||||
* Installation
|
* Installation
|
||||||
|
|
|
@ -2,7 +2,7 @@
|
||||||
# Process this file with autoconf to produce a configure script.
|
# Process this file with autoconf to produce a configure script.
|
||||||
|
|
||||||
# this defines the CORE version number, must be static for AC_INIT
|
# this defines the CORE version number, must be static for AC_INIT
|
||||||
AC_INIT(core, 7.2.0)
|
AC_INIT(core, 7.2.1)
|
||||||
|
|
||||||
# autoconf and automake initialization
|
# autoconf and automake initialization
|
||||||
AC_CONFIG_SRCDIR([netns/version.h.in])
|
AC_CONFIG_SRCDIR([netns/version.h.in])
|
||||||
|
|
|
@ -315,10 +315,7 @@ class CoreGrpcServer(core_pb2_grpc.CoreApiServicer):
|
||||||
"""
|
"""
|
||||||
logging.debug("stop session: %s", request)
|
logging.debug("stop session: %s", request)
|
||||||
session = self.get_session(request.session_id, context)
|
session = self.get_session(request.session_id, context)
|
||||||
session.data_collect()
|
session.shutdown()
|
||||||
session.set_state(EventTypes.DATACOLLECT_STATE, send_event=True)
|
|
||||||
session.clear()
|
|
||||||
session.set_state(EventTypes.SHUTDOWN_STATE, send_event=True)
|
|
||||||
return core_pb2.StopSessionResponse(result=True)
|
return core_pb2.StopSessionResponse(result=True)
|
||||||
|
|
||||||
def CreateSession(
|
def CreateSession(
|
||||||
|
|
|
@ -735,6 +735,11 @@ class CoreHandler(socketserver.BaseRequestHandler):
|
||||||
elif message.flags & MessageFlags.DELETE.value:
|
elif message.flags & MessageFlags.DELETE.value:
|
||||||
with self._shutdown_lock:
|
with self._shutdown_lock:
|
||||||
result = self.session.delete_node(node_id)
|
result = self.session.delete_node(node_id)
|
||||||
|
if result and self.session.get_node_count() == 0:
|
||||||
|
self.session.set_state(EventTypes.SHUTDOWN_STATE)
|
||||||
|
self.session.delete_nodes()
|
||||||
|
self.session.distributed.shutdown()
|
||||||
|
self.session.sdt.shutdown()
|
||||||
|
|
||||||
# if we deleted a node broadcast out its removal
|
# if we deleted a node broadcast out its removal
|
||||||
if result and message.flags & MessageFlags.STRING.value:
|
if result and message.flags & MessageFlags.STRING.value:
|
||||||
|
|
|
@ -71,7 +71,6 @@ class EmaneState(Enum):
|
||||||
|
|
||||||
@dataclass
|
@dataclass
|
||||||
class StartData:
|
class StartData:
|
||||||
emane_net: EmaneNet
|
|
||||||
node: CoreNodeBase
|
node: CoreNodeBase
|
||||||
ifaces: List[CoreInterface] = field(default_factory=list)
|
ifaces: List[CoreInterface] = field(default_factory=list)
|
||||||
|
|
||||||
|
@ -370,9 +369,7 @@ class EmaneManager(ModelManager):
|
||||||
iface.name,
|
iface.name,
|
||||||
)
|
)
|
||||||
continue
|
continue
|
||||||
start_node = node_map.setdefault(
|
start_node = node_map.setdefault(iface.node, StartData(iface.node))
|
||||||
iface.node, StartData(emane_net, iface.node)
|
|
||||||
)
|
|
||||||
start_node.ifaces.append(iface)
|
start_node.ifaces.append(iface)
|
||||||
start_nodes = sorted(node_map.values(), key=lambda x: x.node.id)
|
start_nodes = sorted(node_map.values(), key=lambda x: x.node.id)
|
||||||
for start_node in start_nodes:
|
for start_node in start_nodes:
|
||||||
|
@ -386,7 +383,7 @@ class EmaneManager(ModelManager):
|
||||||
emanexml.build_platform_xml(self, control_net, data)
|
emanexml.build_platform_xml(self, control_net, data)
|
||||||
self.start_daemon(data.node)
|
self.start_daemon(data.node)
|
||||||
for iface in data.ifaces:
|
for iface in data.ifaces:
|
||||||
self.install_iface(data.emane_net, iface)
|
self.install_iface(iface)
|
||||||
|
|
||||||
def set_nem(self, nem_id: int, iface: CoreInterface) -> None:
|
def set_nem(self, nem_id: int, iface: CoreInterface) -> None:
|
||||||
if nem_id in self.nems_to_ifaces:
|
if nem_id in self.nems_to_ifaces:
|
||||||
|
@ -606,7 +603,12 @@ class EmaneManager(ModelManager):
|
||||||
node.host_cmd(emanecmd, cwd=path)
|
node.host_cmd(emanecmd, cwd=path)
|
||||||
logging.info("node(%s) host emane daemon running: %s", node.name, emanecmd)
|
logging.info("node(%s) host emane daemon running: %s", node.name, emanecmd)
|
||||||
|
|
||||||
def install_iface(self, emane_net: EmaneNet, iface: CoreInterface) -> None:
|
def install_iface(self, iface: CoreInterface) -> None:
|
||||||
|
emane_net = iface.net
|
||||||
|
if not isinstance(emane_net, EmaneNet):
|
||||||
|
raise CoreError(
|
||||||
|
f"emane interface not connected to emane net: {emane_net.name}"
|
||||||
|
)
|
||||||
config = self.get_iface_config(emane_net, iface)
|
config = self.get_iface_config(emane_net, iface)
|
||||||
external = config.get("external", "0")
|
external = config.get("external", "0")
|
||||||
if isinstance(iface, TunTap) and external == "0":
|
if isinstance(iface, TunTap) and external == "0":
|
||||||
|
|
|
@ -756,23 +756,18 @@ class Session:
|
||||||
"""
|
"""
|
||||||
Shutdown all session nodes and remove the session directory.
|
Shutdown all session nodes and remove the session directory.
|
||||||
"""
|
"""
|
||||||
if self.state == EventTypes.SHUTDOWN_STATE:
|
|
||||||
return
|
|
||||||
logging.info("session(%s) state(%s) shutting down", self.id, self.state)
|
logging.info("session(%s) state(%s) shutting down", self.id, self.state)
|
||||||
|
if self.state != EventTypes.SHUTDOWN_STATE:
|
||||||
self.set_state(EventTypes.DATACOLLECT_STATE, send_event=True)
|
self.set_state(EventTypes.DATACOLLECT_STATE, send_event=True)
|
||||||
self.set_state(EventTypes.SHUTDOWN_STATE, send_event=True)
|
self.set_state(EventTypes.SHUTDOWN_STATE, send_event=True)
|
||||||
|
|
||||||
# clear out current core session
|
# clear out current core session
|
||||||
self.clear()
|
self.clear()
|
||||||
|
|
||||||
# shutdown sdt
|
# shutdown sdt
|
||||||
self.sdt.shutdown()
|
self.sdt.shutdown()
|
||||||
|
|
||||||
# remove this sessions working directory
|
# remove this sessions working directory
|
||||||
preserve = self.options.get_config("preservedir") == "1"
|
preserve = self.options.get_config("preservedir") == "1"
|
||||||
if not preserve:
|
if not preserve:
|
||||||
shutil.rmtree(self.session_dir, ignore_errors=True)
|
shutil.rmtree(self.session_dir, ignore_errors=True)
|
||||||
|
|
||||||
# call session shutdown handlers
|
# call session shutdown handlers
|
||||||
for handler in self.shutdown_handlers:
|
for handler in self.shutdown_handlers:
|
||||||
handler(self)
|
handler(self)
|
||||||
|
@ -1116,7 +1111,6 @@ class Session:
|
||||||
if node:
|
if node:
|
||||||
node.shutdown()
|
node.shutdown()
|
||||||
self.sdt.delete_node(_id)
|
self.sdt.delete_node(_id)
|
||||||
self.check_shutdown()
|
|
||||||
return node is not None
|
return node is not None
|
||||||
|
|
||||||
def delete_nodes(self) -> None:
|
def delete_nodes(self) -> None:
|
||||||
|
@ -1279,25 +1273,6 @@ class Session:
|
||||||
self.add_remove_control_net(2, remove=True)
|
self.add_remove_control_net(2, remove=True)
|
||||||
self.add_remove_control_net(3, remove=True)
|
self.add_remove_control_net(3, remove=True)
|
||||||
|
|
||||||
def check_shutdown(self) -> bool:
|
|
||||||
"""
|
|
||||||
Check if we have entered the shutdown state, when no running nodes
|
|
||||||
and links remain.
|
|
||||||
|
|
||||||
:return: True if should shutdown, False otherwise
|
|
||||||
"""
|
|
||||||
node_count = self.get_node_count()
|
|
||||||
logging.debug(
|
|
||||||
"session(%s) checking shutdown: %s nodes remaining", self.id, node_count
|
|
||||||
)
|
|
||||||
shutdown = False
|
|
||||||
if node_count == 0:
|
|
||||||
shutdown = True
|
|
||||||
self.set_state(EventTypes.SHUTDOWN_STATE)
|
|
||||||
# clearing sdt saved data here for legacy gui
|
|
||||||
self.sdt.shutdown()
|
|
||||||
return shutdown
|
|
||||||
|
|
||||||
def short_session_id(self) -> str:
|
def short_session_id(self) -> str:
|
||||||
"""
|
"""
|
||||||
Return a shorter version of the session ID, appropriate for
|
Return a shorter version of the session ID, appropriate for
|
||||||
|
|
|
@ -7,6 +7,7 @@ from lxml import etree
|
||||||
|
|
||||||
from core import utils
|
from core import utils
|
||||||
from core.config import Configuration
|
from core.config import Configuration
|
||||||
|
from core.emane.nodes import EmaneNet
|
||||||
from core.emulator.distributed import DistributedServer
|
from core.emulator.distributed import DistributedServer
|
||||||
from core.errors import CoreError
|
from core.errors import CoreError
|
||||||
from core.nodes.base import CoreNode, CoreNodeBase
|
from core.nodes.base import CoreNode, CoreNodeBase
|
||||||
|
@ -166,8 +167,12 @@ def build_platform_xml(
|
||||||
add_param(platform_element, name, value)
|
add_param(platform_element, name, value)
|
||||||
|
|
||||||
# create nem xml entries for all interfaces
|
# create nem xml entries for all interfaces
|
||||||
emane_net = data.emane_net
|
|
||||||
for iface in data.ifaces:
|
for iface in data.ifaces:
|
||||||
|
emane_net = iface.net
|
||||||
|
if not isinstance(emane_net, EmaneNet):
|
||||||
|
raise CoreError(
|
||||||
|
f"emane interface not connected to emane net: {emane_net.name}"
|
||||||
|
)
|
||||||
nem_id = emane_manager.next_nem_id()
|
nem_id = emane_manager.next_nem_id()
|
||||||
emane_manager.set_nem(nem_id, iface)
|
emane_manager.set_nem(nem_id, iface)
|
||||||
emane_manager.write_nem(iface, nem_id)
|
emane_manager.write_nem(iface, nem_id)
|
||||||
|
@ -180,9 +185,7 @@ def build_platform_xml(
|
||||||
"nem", id=str(nem_id), name=iface.localname, definition=nem_definition
|
"nem", id=str(nem_id), name=iface.localname, definition=nem_definition
|
||||||
)
|
)
|
||||||
|
|
||||||
# check if this is an external transport, get default config if an interface
|
# check if this is an external transport
|
||||||
# specific one does not exist
|
|
||||||
config = emane_manager.get_iface_config(emane_net, iface)
|
|
||||||
if is_external(config):
|
if is_external(config):
|
||||||
nem_element.set("transport", "external")
|
nem_element.set("transport", "external")
|
||||||
platform_endpoint = "platformendpoint"
|
platform_endpoint = "platformendpoint"
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
[tool.poetry]
|
[tool.poetry]
|
||||||
name = "core"
|
name = "core"
|
||||||
version = "7.2.0"
|
version = "7.2.1"
|
||||||
description = "CORE Common Open Research Emulator"
|
description = "CORE Common Open Research Emulator"
|
||||||
authors = ["Boeing Research and Technology"]
|
authors = ["Boeing Research and Technology"]
|
||||||
license = "BSD-2-Clause"
|
license = "BSD-2-Clause"
|
||||||
|
|
159
docs/install.md
159
docs/install.md
|
@ -1,29 +1,72 @@
|
||||||
# Installation
|
# Installation
|
||||||
|
|
||||||
* Table of Contents
|
* Table of Contents
|
||||||
{:toc}
|
{:toc}
|
||||||
|
|
||||||
## Overview
|
## Overview
|
||||||
|
CORE provides a script to help automate the installation of dependencies,
|
||||||
|
build and install, and either generate a CORE specific python virtual environment
|
||||||
|
or build and install a python wheel.
|
||||||
|
|
||||||
CORE provides a script to help automate installing all required software
|
> **WARNING:** if Docker is installed, the default iptable rules will block CORE traffic
|
||||||
to build and run, including a python virtual environment to run it all in.
|
|
||||||
|
|
||||||
|
### Tools Used
|
||||||
The following tools will be leveraged during installation:
|
The following tools will be leveraged during installation:
|
||||||
|
|
||||||
|Tool|Description|
|
|Tool|Description|
|
||||||
|---|---|
|
|---|---|
|
||||||
|[pip](https://pip.pypa.io/en/stable/)|used to install pipx|
|
|[pip](https://pip.pypa.io/en/stable/)|used to install pipx|
|
||||||
|[pipx](https://pipxproject.github.io/pipx/)|used to install standalone python tools (invoke, poetry)|
|
|[pipx](https://pipxproject.github.io/pipx/)|used to install standalone python tools (invoke, poetry)|
|
||||||
|[invoke](http://www.pyinvoke.org/)|used to run provided tasks (install, daemon, gui, tests, etc)|
|
|[invoke](http://www.pyinvoke.org/)|used to run provided tasks (install, uninstall, reinstall, etc)|
|
||||||
|[poetry](https://python-poetry.org/)|used to install the managed python virtual environment for running CORE|
|
|[poetry](https://python-poetry.org/)|used to install python virtual environment or building a python wheel|
|
||||||
|
|
||||||
## Required Hardware
|
### Files
|
||||||
|
The following is a list of files that would be installed after running the automated installation.
|
||||||
|
|
||||||
|
> **NOTE:** the default install prefix is /usr/local, but can be changed as noted below
|
||||||
|
|
||||||
|
* executable files
|
||||||
|
* <prefix>/bin/{core-daemon, core-gui, vcmd, vnoded, etc}
|
||||||
|
* tcl/tk gui files
|
||||||
|
* <prefix>/lib/core
|
||||||
|
* <prefix>/share/core/icons
|
||||||
|
* example imn files
|
||||||
|
* <prefix>/share/core/examples
|
||||||
|
* python files
|
||||||
|
* poetry virtual env
|
||||||
|
* `cd <repo>/daemon && poetry env info`
|
||||||
|
* ~/.cache/pypoetry/virtualenvs/
|
||||||
|
* local python install
|
||||||
|
* default install path for python3 installation of a wheel
|
||||||
|
* `python3 -c "import core; print(core.__file__)"`
|
||||||
|
* configuration files
|
||||||
|
* /etc/core/{core.conf, logging.conf}
|
||||||
|
* ospf mdr repository files
|
||||||
|
* <repo>/../ospf-mdr
|
||||||
|
* emane repository files
|
||||||
|
* <repo>/../emane
|
||||||
|
|
||||||
|
### Installed Executables
|
||||||
|
After the installation complete it will have installed the following scripts.
|
||||||
|
|
||||||
|
| Name | Description |
|
||||||
|
|---|---|
|
||||||
|
| core-cleanup | tool to help removed lingering core created containers, bridges, directories |
|
||||||
|
| core-cli | tool to query, open xml files, and send commands using gRPC |
|
||||||
|
| core-daemon | runs the backed core server providing TLV and gRPC APIs |
|
||||||
|
| core-gui | runs the legacy tcl/tk based GUI |
|
||||||
|
| core-imn-to-xml | tool to help automate converting a .imn file to .xml format |
|
||||||
|
| core-manage | tool to add, remove, or check for services, models, and node types |
|
||||||
|
| core-pygui | runs the new python/tk based GUI |
|
||||||
|
| core-python | provides a convenience for running the core python virtual environment |
|
||||||
|
| core-route-monitor | tool to help monitor traffic across nodes and feed that to SDT |
|
||||||
|
| core-service-update | tool to update automate modifying a legacy service to match current naming |
|
||||||
|
| coresendmsg | tool to send TLV API commands from command line |
|
||||||
|
|
||||||
|
### Required Hardware
|
||||||
Any computer capable of running Linux should be able to run CORE. Since the physical machine will be hosting numerous
|
Any computer capable of running Linux should be able to run CORE. Since the physical machine will be hosting numerous
|
||||||
containers, as a general rule you should select a machine having as much RAM and CPU resources as possible.
|
containers, as a general rule you should select a machine having as much RAM and CPU resources as possible.
|
||||||
|
|
||||||
## Supported Linux Distributions
|
### Supported Linux Distributions
|
||||||
|
|
||||||
Plan is to support recent Ubuntu and CentOS LTS releases.
|
Plan is to support recent Ubuntu and CentOS LTS releases.
|
||||||
|
|
||||||
Verified:
|
Verified:
|
||||||
|
@ -46,14 +89,14 @@ sudo yum install -y kernel-modules-extra
|
||||||
sudo modprobe sch_netem
|
sudo modprobe sch_netem
|
||||||
```
|
```
|
||||||
|
|
||||||
## Utility Requirements
|
### Utility Requirements
|
||||||
|
The following are known dependencies that will result in errors when not met.
|
||||||
|
|
||||||
* iproute2 4.5+ is a requirement for bridge related commands
|
* iproute2 4.5+ is a requirement for bridge related commands
|
||||||
* ebtables not backed by nftables
|
* ebtables not backed by nftables
|
||||||
|
|
||||||
## Upgrading
|
## Upgrading from Older Release
|
||||||
|
Please make sure to uninstall any previous installations of CORE cleanly
|
||||||
Please make sure to uninstall the previous installation of CORE cleanly
|
|
||||||
before proceeding to install.
|
before proceeding to install.
|
||||||
|
|
||||||
Previous install was built from source:
|
Previous install was built from source:
|
||||||
|
@ -72,92 +115,97 @@ sudo yum remove core
|
||||||
sudo apt remove core
|
sudo apt remove core
|
||||||
```
|
```
|
||||||
|
|
||||||
## Automated Installation
|
## Automated Install
|
||||||
|
The automated install will do the following:
|
||||||
> **NOTE:** installing globally can have issues with dependency conflicts etc
|
|
||||||
|
|
||||||
The automated install will install do the following:
|
|
||||||
* install base tools needed for installation
|
* install base tools needed for installation
|
||||||
* python3, pip, pipx, invoke, poetry
|
* python3, pip, pipx, invoke, poetry
|
||||||
* installs system dependencies for building core
|
* installs system dependencies for building core
|
||||||
* installs latest version of [OPSF MDR](https://github.com/USNavalResearchLaboratory/ospf-mdr)
|
* clone/build/install working version of [OPSF MDR](https://github.com/USNavalResearchLaboratory/ospf-mdr)
|
||||||
* installs core into poetry managed virtual environment or locally, if flag is passed
|
* installs core into poetry managed virtual environment or locally, if flag is passed
|
||||||
* installs scripts pointing to python interpreter being used
|
* installs scripts pointing pointing to appropriate python location based on install type
|
||||||
* installs systemd service, disabled by default
|
* installs systemd service pointing to appropriate python location based on install type
|
||||||
|
|
||||||
After installation has completed you should be able to run the various
|
After installation has completed you should be able to run `core-daemon` and `core-gui`.
|
||||||
CORE scripts for running core.
|
|
||||||
|
|
||||||
> **NOTE:** provide a prefix that will be found on path when running as sudo
|
> **NOTE:** installing locally comes with its own risks, it can result it potential
|
||||||
> if the default prefix is not valid
|
> dependency conflicts with system package manager installed python dependencies
|
||||||
|
|
||||||
|
> **NOTE:** provide a prefix that will be found on path when running as sudo,
|
||||||
|
> if the default prefix /usr/local will not be valid
|
||||||
|
|
||||||
|
`install.sh` will attempt to determine your OS by way of `/etc/os-release`, currently it supports
|
||||||
|
attempts to install OSs that are debian/redhat like (yum/apt).
|
||||||
```shell
|
```shell
|
||||||
# clone CORE repo
|
# clone CORE repo
|
||||||
git clone https://github.com/coreemu/core.git
|
git clone https://github.com/coreemu/core.git
|
||||||
cd core
|
cd core
|
||||||
|
|
||||||
# run install script
|
|
||||||
# script usage: install.sh [-v] [-d] [-l] [-p <prefix>]
|
# script usage: install.sh [-v] [-d] [-l] [-p <prefix>]
|
||||||
#
|
#
|
||||||
# -v enable verbose install
|
# -v enable verbose install
|
||||||
# -d enable developer install
|
# -d enable developer install
|
||||||
# -l enable local install, not compatible with developer install
|
# -l enable local install, not compatible with developer install
|
||||||
# -p install prefix, defaults to /usr/local
|
# -p install prefix, defaults to /usr/local
|
||||||
./install.sh
|
|
||||||
|
# install core to virtual environment
|
||||||
|
./install.sh -p <prefix>
|
||||||
|
|
||||||
|
# install core locally
|
||||||
|
./install.sh -p <prefix> -l
|
||||||
```
|
```
|
||||||
|
|
||||||
### Unsupported Linux Distribution
|
### Unsupported Linux Distribution
|
||||||
|
For unsupported OSs you could attempt to do the following to translate
|
||||||
|
an installation to your use case.
|
||||||
|
|
||||||
If you are on an unsupported distribution, you can look into the
|
* make sure you have python3.6+ with venv support
|
||||||
[install.sh](https://github.com/coreemu/core/blob/master/install.sh)
|
* make sure you have python3 invoke available to leverage `<repo>/tasks.py`
|
||||||
and
|
|
||||||
[tasks.py](https://github.com/coreemu/core/blob/master/tasks.py)
|
|
||||||
files to see the various commands ran to install CORE and translate them to
|
|
||||||
your use case, assuming it is possible.
|
|
||||||
|
|
||||||
If you get install down entirely, feel free to contribute and help others.
|
```shell
|
||||||
|
cd <repo>
|
||||||
|
|
||||||
## Installed Scripts
|
# Usage: inv[oke] [--core-opts] install [--options] [other tasks here ...]
|
||||||
|
#
|
||||||
|
# Docstring:
|
||||||
|
# install core, poetry, scripts, service, and ospf mdr
|
||||||
|
#
|
||||||
|
# Options:
|
||||||
|
# -d, --dev install development mode
|
||||||
|
# -i STRING, --install-type=STRING
|
||||||
|
# -l, --local determines if core will install to local system, default is False
|
||||||
|
# -p STRING, --prefix=STRING prefix where scripts are installed, default is /usr/local
|
||||||
|
# -v, --verbose enable verbose
|
||||||
|
|
||||||
After the installation complete it will have installed the following scripts.
|
# install virtual environment
|
||||||
|
inv install -p <prefix>
|
||||||
|
|
||||||
| Name | Description |
|
# indstall locally
|
||||||
|---|---|
|
inv install -p <prefix> -l
|
||||||
| core-cleanup | tool to help removed lingering core created containers, bridges, directories |
|
|
||||||
| core-cli | tool to query, open xml files, and send commands using gRPC |
|
# this will print the commands that would be ran for a given installation
|
||||||
| core-daemon | runs the backed core server providing TLV and gRPC APIs |
|
# type without actually running them, they may help in being used as
|
||||||
| core-gui | runs the legacy tcl/tk based GUI |
|
# the basis for translating to your OS
|
||||||
| core-imn-to-xml | tool to help automate converting a .imn file to .xml format |
|
inv install --dry -v -p <prefix> -i <install type>
|
||||||
| core-manage | tool to add, remove, or check for services, models, and node types |
|
```
|
||||||
| core-pygui | runs the new python/tk based GUI |
|
|
||||||
| core-python | provides a convenience for running the core python virtual environment |
|
|
||||||
| core-route-monitor | tool to help monitor traffic across nodes and feed that to SDT |
|
|
||||||
| core-service-update | tool to update automate modifying a legacy service to match current naming |
|
|
||||||
| coresendmsg | tool to send TLV API commands from command line |
|
|
||||||
|
|
||||||
## Running User Scripts
|
## Running User Scripts
|
||||||
|
|
||||||
If you create your own python scripts to run CORE directly or using the gRPC/TLV
|
If you create your own python scripts to run CORE directly or using the gRPC/TLV
|
||||||
APIs you will need to make sure you are running them within context of the
|
APIs you will need to make sure you are running them within context of the
|
||||||
installed virtual environment. To help support this CORE provides the `core-python`
|
installed virtual environment. To help support this CORE provides the `core-python`
|
||||||
executable. This executable will allow you to enter CORE's python virtual
|
executable. This executable will allow you to enter CORE's python virtual
|
||||||
environment interpreter or to run a script within it.
|
environment interpreter or to run a script within it.
|
||||||
|
|
||||||
> **NOTE:** the following assumes CORE has been installed successfully
|
For installations installed to a virtual environment:
|
||||||
|
|
||||||
```shell
|
```shell
|
||||||
core-python <script>
|
core-python <script>
|
||||||
```
|
```
|
||||||
|
|
||||||
If CORE was installed locally, then you can run scripts using the default python3
|
For local installations:
|
||||||
interpreter.
|
|
||||||
|
|
||||||
```shell
|
```shell
|
||||||
python3 <script>
|
python3 <script>
|
||||||
```
|
```
|
||||||
|
|
||||||
## Installing EMANE
|
## Installing EMANE
|
||||||
|
|
||||||
> **NOTE:** installng emane for the virtual environment is known to work for 1.21+
|
> **NOTE:** installng emane for the virtual environment is known to work for 1.21+
|
||||||
> **NOTE:** automated install currently targets 1.25
|
> **NOTE:** automated install currently targets 1.25
|
||||||
|
|
||||||
|
@ -205,7 +253,6 @@ poetry run pip install <EMANE_REPO>/src/python
|
||||||
```
|
```
|
||||||
|
|
||||||
## Using Invoke Tasks
|
## Using Invoke Tasks
|
||||||
|
|
||||||
The invoke tool installed by way of pipx provides conveniences for running
|
The invoke tool installed by way of pipx provides conveniences for running
|
||||||
CORE tasks to help ensure usage of the create python virtual environment.
|
CORE tasks to help ensure usage of the create python virtual environment.
|
||||||
|
|
||||||
|
|
31
install.sh
31
install.sh
|
@ -3,13 +3,6 @@
|
||||||
# exit on error
|
# exit on error
|
||||||
set -e
|
set -e
|
||||||
|
|
||||||
# detect os/ver for install type
|
|
||||||
os=""
|
|
||||||
if [[ -f /etc/os-release ]]; then
|
|
||||||
. /etc/os-release
|
|
||||||
os=${ID}
|
|
||||||
fi
|
|
||||||
|
|
||||||
# parse arguments
|
# parse arguments
|
||||||
dev=""
|
dev=""
|
||||||
verbose=""
|
verbose=""
|
||||||
|
@ -42,19 +35,23 @@ while getopts "dvlp:" opt; do
|
||||||
done
|
done
|
||||||
shift $((OPTIND - 1))
|
shift $((OPTIND - 1))
|
||||||
|
|
||||||
echo "installing CORE for ${os}"
|
# install pre-reqs using yum/apt
|
||||||
case ${os} in
|
if command -v apt &> /dev/null
|
||||||
"ubuntu")
|
then
|
||||||
|
echo "setup to install CORE using apt"
|
||||||
sudo apt install -y python3-pip python3-venv
|
sudo apt install -y python3-pip python3-venv
|
||||||
;;
|
elif command -v yum &> /dev/null
|
||||||
"centos")
|
then
|
||||||
|
echo "setup to install CORE using yum"
|
||||||
sudo yum install -y python3-pip
|
sudo yum install -y python3-pip
|
||||||
;;
|
else
|
||||||
*)
|
echo "apt/yum was not found"
|
||||||
echo "unknown OS ID ${os} cannot install"
|
echo "install python3 and invoke to run the automated install"
|
||||||
;;
|
echo "inv -h install"
|
||||||
esac
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
# install pip/invoke to run install with provided options
|
||||||
python3 -m pip install --user pipx
|
python3 -m pip install --user pipx
|
||||||
python3 -m pipx ensurepath
|
python3 -m pipx ensurepath
|
||||||
export PATH=$PATH:~/.local/bin
|
export PATH=$PATH:~/.local/bin
|
||||||
|
|
94
tasks.py
94
tasks.py
|
@ -8,14 +8,22 @@ from contextlib import contextmanager
|
||||||
from enum import Enum
|
from enum import Enum
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
from tempfile import NamedTemporaryFile
|
from tempfile import NamedTemporaryFile
|
||||||
from typing import Optional
|
from typing import Optional, List
|
||||||
|
|
||||||
from invoke import task, Context
|
from invoke import task, Context
|
||||||
|
|
||||||
DAEMON_DIR: str = "daemon"
|
DAEMON_DIR: str = "daemon"
|
||||||
DEFAULT_PREFIX: str = "/usr/local"
|
DEFAULT_PREFIX: str = "/usr/local"
|
||||||
EMANE_CHECKOUT: str = "v1.2.5"
|
EMANE_CHECKOUT: str = "v1.2.5"
|
||||||
OSPFMDR_CHECKOUT: str = "26fe5a4401a26760c553fcadfde5311199e89450"
|
OSPFMDR_CHECKOUT: str = "e2b4e416b7001c5dca0224fe728249c9688e4c7a"
|
||||||
|
REDHAT_LIKE = {
|
||||||
|
"redhat",
|
||||||
|
"fedora",
|
||||||
|
}
|
||||||
|
DEBIAN_LIKE = {
|
||||||
|
"ubuntu",
|
||||||
|
"debian",
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
class Progress:
|
class Progress:
|
||||||
|
@ -55,11 +63,28 @@ class Progress:
|
||||||
class OsName(Enum):
|
class OsName(Enum):
|
||||||
UBUNTU = "ubuntu"
|
UBUNTU = "ubuntu"
|
||||||
CENTOS = "centos"
|
CENTOS = "centos"
|
||||||
|
UNKNOWN = "unknown"
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def get(cls, name: str) -> "OsName":
|
||||||
|
try:
|
||||||
|
return OsName(name)
|
||||||
|
except ValueError:
|
||||||
|
return OsName.UNKNOWN
|
||||||
|
|
||||||
|
|
||||||
class OsLike(Enum):
|
class OsLike(Enum):
|
||||||
DEBIAN = "debian"
|
DEBIAN = "debian"
|
||||||
REDHAT = "rhel fedora"
|
REDHAT = "rhel"
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def get(cls, values: List[str]) -> Optional["OsLike"]:
|
||||||
|
for value in values:
|
||||||
|
if value in DEBIAN_LIKE:
|
||||||
|
return OsLike.DEBIAN
|
||||||
|
elif value in REDHAT_LIKE:
|
||||||
|
return OsLike.REDHAT
|
||||||
|
return None
|
||||||
|
|
||||||
|
|
||||||
class OsInfo:
|
class OsInfo:
|
||||||
|
@ -68,6 +93,22 @@ class OsInfo:
|
||||||
self.like: OsLike = like
|
self.like: OsLike = like
|
||||||
self.version: float = version
|
self.version: float = version
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def get(cls, name: str, like: List[str], version: Optional[str]) -> "OsInfo":
|
||||||
|
os_name = OsName.get(name)
|
||||||
|
os_like = OsLike.get(like)
|
||||||
|
if not os_like:
|
||||||
|
like = " ".join(like)
|
||||||
|
print(f"unsupported os install type({like})")
|
||||||
|
sys.exit(1)
|
||||||
|
if version:
|
||||||
|
try:
|
||||||
|
version = float(version)
|
||||||
|
except ValueError:
|
||||||
|
print(f"os version is not a float: {version}")
|
||||||
|
sys.exit(1)
|
||||||
|
return OsInfo(os_name, os_like, version)
|
||||||
|
|
||||||
|
|
||||||
def get_python(c: Context, warn: bool = False) -> str:
|
def get_python(c: Context, warn: bool = False) -> str:
|
||||||
with c.cd(DAEMON_DIR):
|
with c.cd(DAEMON_DIR):
|
||||||
|
@ -85,7 +126,12 @@ def get_pytest(c: Context) -> str:
|
||||||
return os.path.join(venv, "bin", "pytest")
|
return os.path.join(venv, "bin", "pytest")
|
||||||
|
|
||||||
|
|
||||||
def get_os() -> OsInfo:
|
def get_os(install_type: Optional[str]) -> OsInfo:
|
||||||
|
if install_type:
|
||||||
|
name_value = OsName.UNKNOWN.value
|
||||||
|
like_value = install_type
|
||||||
|
version_value = None
|
||||||
|
else:
|
||||||
d = {}
|
d = {}
|
||||||
with open("/etc/os-release", "r") as f:
|
with open("/etc/os-release", "r") as f:
|
||||||
for line in f.readlines():
|
for line in f.readlines():
|
||||||
|
@ -97,16 +143,7 @@ def get_os() -> OsInfo:
|
||||||
name_value = d["ID"]
|
name_value = d["ID"]
|
||||||
like_value = d["ID_LIKE"]
|
like_value = d["ID_LIKE"]
|
||||||
version_value = d["VERSION_ID"]
|
version_value = d["VERSION_ID"]
|
||||||
try:
|
return OsInfo.get(name_value, like_value.split(), version_value)
|
||||||
name = OsName(name_value)
|
|
||||||
like = OsLike(like_value)
|
|
||||||
version = float(version_value)
|
|
||||||
except ValueError:
|
|
||||||
print(
|
|
||||||
f"unsupported os({name_value}) like({like_value}) version({version_value}"
|
|
||||||
)
|
|
||||||
sys.exit(1)
|
|
||||||
return OsInfo(name, like, version)
|
|
||||||
|
|
||||||
|
|
||||||
def check_existing_core(c: Context, hide: bool) -> None:
|
def check_existing_core(c: Context, hide: bool) -> None:
|
||||||
|
@ -304,9 +341,13 @@ def install_scripts(c, local=False, verbose=False, prefix=DEFAULT_PREFIX):
|
||||||
"verbose": "enable verbose",
|
"verbose": "enable verbose",
|
||||||
"local": "determines if core will install to local system, default is False",
|
"local": "determines if core will install to local system, default is False",
|
||||||
"prefix": f"prefix where scripts are installed, default is {DEFAULT_PREFIX}",
|
"prefix": f"prefix where scripts are installed, default is {DEFAULT_PREFIX}",
|
||||||
|
"install-type": "used to force an install type, "
|
||||||
|
"can be one of the following (redhat, debian)",
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
def install(c, dev=False, verbose=False, local=False, prefix=DEFAULT_PREFIX):
|
def install(
|
||||||
|
c, dev=False, verbose=False, local=False, prefix=DEFAULT_PREFIX, install_type=None
|
||||||
|
):
|
||||||
"""
|
"""
|
||||||
install core, poetry, scripts, service, and ospf mdr
|
install core, poetry, scripts, service, and ospf mdr
|
||||||
"""
|
"""
|
||||||
|
@ -315,7 +356,8 @@ def install(c, dev=False, verbose=False, local=False, prefix=DEFAULT_PREFIX):
|
||||||
c.run("sudo -v", hide=True)
|
c.run("sudo -v", hide=True)
|
||||||
p = Progress(verbose)
|
p = Progress(verbose)
|
||||||
hide = not verbose
|
hide = not verbose
|
||||||
os_info = get_os()
|
os_info = get_os(install_type)
|
||||||
|
if not c["run"]["dry"]:
|
||||||
with p.start("checking for old installations"):
|
with p.start("checking for old installations"):
|
||||||
check_existing_core(c, hide)
|
check_existing_core(c, hide)
|
||||||
with p.start("installing system dependencies"):
|
with p.start("installing system dependencies"):
|
||||||
|
@ -342,16 +384,18 @@ def install(c, dev=False, verbose=False, local=False, prefix=DEFAULT_PREFIX):
|
||||||
help={
|
help={
|
||||||
"verbose": "enable verbose",
|
"verbose": "enable verbose",
|
||||||
"local": "used determine if core is installed locally, default is False",
|
"local": "used determine if core is installed locally, default is False",
|
||||||
|
"install-type": "used to force an install type, "
|
||||||
|
"can be one of the following (redhat, debian)",
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
def install_emane(c, verbose=False, local=False):
|
def install_emane(c, verbose=False, local=False, install_type=None):
|
||||||
"""
|
"""
|
||||||
install emane and the python bindings
|
install emane and the python bindings
|
||||||
"""
|
"""
|
||||||
c.run("sudo -v", hide=True)
|
c.run("sudo -v", hide=True)
|
||||||
p = Progress(verbose)
|
p = Progress(verbose)
|
||||||
hide = not verbose
|
hide = not verbose
|
||||||
os_info = get_os()
|
os_info = get_os(install_type)
|
||||||
with p.start("installing system dependencies"):
|
with p.start("installing system dependencies"):
|
||||||
if os_info.like == OsLike.DEBIAN:
|
if os_info.like == OsLike.DEBIAN:
|
||||||
c.run(
|
c.run(
|
||||||
|
@ -458,11 +502,19 @@ def uninstall(c, dev=False, verbose=False, local=False, prefix=DEFAULT_PREFIX):
|
||||||
"verbose": "enable verbose",
|
"verbose": "enable verbose",
|
||||||
"local": "determines if core will install to local system, default is False",
|
"local": "determines if core will install to local system, default is False",
|
||||||
"prefix": f"prefix where scripts are installed, default is {DEFAULT_PREFIX}",
|
"prefix": f"prefix where scripts are installed, default is {DEFAULT_PREFIX}",
|
||||||
"branch": "branch to install latest code from, default is current branch"
|
"branch": "branch to install latest code from, default is current branch",
|
||||||
|
"install-type": "used to force an install type, "
|
||||||
|
"can be one of the following (redhat, debian)",
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
def reinstall(
|
def reinstall(
|
||||||
c, dev=False, verbose=False, local=False, prefix=DEFAULT_PREFIX, branch=None
|
c,
|
||||||
|
dev=False,
|
||||||
|
verbose=False,
|
||||||
|
local=False,
|
||||||
|
prefix=DEFAULT_PREFIX,
|
||||||
|
branch=None,
|
||||||
|
install_type=None
|
||||||
):
|
):
|
||||||
"""
|
"""
|
||||||
run the uninstall task, get latest from specified branch, and run install task
|
run the uninstall task, get latest from specified branch, and run install task
|
||||||
|
@ -479,7 +531,7 @@ def reinstall(
|
||||||
c.run("git pull", hide=hide)
|
c.run("git pull", hide=hide)
|
||||||
if not Path("tasks.py").exists():
|
if not Path("tasks.py").exists():
|
||||||
raise FileNotFoundError(f"missing tasks.py on branch: {branch}")
|
raise FileNotFoundError(f"missing tasks.py on branch: {branch}")
|
||||||
install(c, dev, verbose, local, prefix)
|
install(c, dev, verbose, local, prefix, install_type)
|
||||||
|
|
||||||
|
|
||||||
@task
|
@task
|
||||||
|
|
Loading…
Reference in a new issue