commit
87816748b3
151 changed files with 7535 additions and 5086 deletions
32
.github/workflows/daemon-checks.yml
vendored
Normal file
32
.github/workflows/daemon-checks.yml
vendored
Normal file
|
@ -0,0 +1,32 @@
|
||||||
|
name: Daemon Checks
|
||||||
|
|
||||||
|
on: [push]
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
build:
|
||||||
|
runs-on: ubuntu-18.04
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v1
|
||||||
|
- name: Set up Python 3.7
|
||||||
|
uses: actions/setup-python@v1
|
||||||
|
with:
|
||||||
|
python-version: 3.7
|
||||||
|
- name: Install pipenv
|
||||||
|
run: |
|
||||||
|
python -m pip install --upgrade pip
|
||||||
|
pip install pipenv
|
||||||
|
cd daemon
|
||||||
|
cp setup.py.in setup.py
|
||||||
|
pipenv install --dev
|
||||||
|
- name: isort
|
||||||
|
run: |
|
||||||
|
cd daemon
|
||||||
|
pipenv run isort -c
|
||||||
|
- name: black
|
||||||
|
run: |
|
||||||
|
cd daemon
|
||||||
|
pipenv run black --check --exclude ".+_pb2.*.py|doc|build|utm\.py|setup\.py" .
|
||||||
|
- name: flake8
|
||||||
|
run: |
|
||||||
|
cd daemon
|
||||||
|
pipenv run flake8
|
|
@ -46,8 +46,8 @@ MAINTAINERCLEANFILES = .version \
|
||||||
|
|
||||||
|
|
||||||
if PYTHON3
|
if PYTHON3
|
||||||
PYTHON_DEB_DEP = python3 >= 3.0
|
PYTHON_DEB_DEP = python3 >= 3.6
|
||||||
PYTHON_RPM_DEP = python3 >= 3.0
|
PYTHON_RPM_DEP = python3 >= 3.6
|
||||||
else
|
else
|
||||||
PYTHON_DEB_DEP = python (>= 2.7), python (<< 3.0)
|
PYTHON_DEB_DEP = python (>= 2.7), python (<< 3.0)
|
||||||
PYTHON_RPM_DEP = python >= 2.7, python < 3.0
|
PYTHON_RPM_DEP = python >= 2.7, python < 3.0
|
||||||
|
|
|
@ -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, 5.3.1)
|
AC_INIT(core, 5.4.0)
|
||||||
|
|
||||||
# autoconf and automake initialization
|
# autoconf and automake initialization
|
||||||
AC_CONFIG_SRCDIR([netns/version.h.in])
|
AC_CONFIG_SRCDIR([netns/version.h.in])
|
||||||
|
@ -236,7 +236,6 @@ AC_CONFIG_FILES([Makefile
|
||||||
gui/Makefile
|
gui/Makefile
|
||||||
gui/icons/Makefile
|
gui/icons/Makefile
|
||||||
scripts/Makefile
|
scripts/Makefile
|
||||||
scripts/perf/Makefile
|
|
||||||
man/Makefile
|
man/Makefile
|
||||||
docs/Makefile
|
docs/Makefile
|
||||||
daemon/Makefile
|
daemon/Makefile
|
||||||
|
|
|
@ -13,7 +13,7 @@
|
||||||
<maven.compiler.source>1.8</maven.compiler.source>
|
<maven.compiler.source>1.8</maven.compiler.source>
|
||||||
<maven.compiler.target>1.8</maven.compiler.target>
|
<maven.compiler.target>1.8</maven.compiler.target>
|
||||||
<jung.version>2.1.1</jung.version>
|
<jung.version>2.1.1</jung.version>
|
||||||
<jackson.version>2.9.9</jackson.version>
|
<jackson.version>2.10.0.pr2</jackson.version>
|
||||||
<grpc.version>1.20.0</grpc.version>
|
<grpc.version>1.20.0</grpc.version>
|
||||||
<log4j.version>2.9.0</log4j.version>
|
<log4j.version>2.9.0</log4j.version>
|
||||||
</properties>
|
</properties>
|
||||||
|
|
|
@ -70,6 +70,7 @@ public class Controller implements Initializable {
|
||||||
private GraphToolbar graphToolbar = new GraphToolbar(this);
|
private GraphToolbar graphToolbar = new GraphToolbar(this);
|
||||||
|
|
||||||
// dialogs
|
// dialogs
|
||||||
|
private Rj45Dialog rj45Dialog = new Rj45Dialog(this);
|
||||||
private SessionsDialog sessionsDialog = new SessionsDialog(this);
|
private SessionsDialog sessionsDialog = new SessionsDialog(this);
|
||||||
private ServiceDialog serviceDialog = new ServiceDialog(this);
|
private ServiceDialog serviceDialog = new ServiceDialog(this);
|
||||||
private NodeServicesDialog nodeServicesDialog = new NodeServicesDialog(this);
|
private NodeServicesDialog nodeServicesDialog = new NodeServicesDialog(this);
|
||||||
|
@ -140,36 +141,15 @@ public class Controller implements Initializable {
|
||||||
Platform.runLater(() -> borderPane.setRight(null));
|
Platform.runLater(() -> borderPane.setRight(null));
|
||||||
|
|
||||||
// get session to join
|
// get session to join
|
||||||
Session session = coreClient.getSession(sessionId);
|
Session session = coreClient.joinSession(sessionId);
|
||||||
SessionState sessionState = SessionState.get(session.getState());
|
|
||||||
|
|
||||||
// update client to use this session
|
|
||||||
coreClient.updateSession(sessionId);
|
|
||||||
coreClient.updateState(sessionState);
|
|
||||||
|
|
||||||
// setup event handlers
|
|
||||||
coreClient.setupEventHandlers(this);
|
|
||||||
|
|
||||||
// display all nodes
|
// display all nodes
|
||||||
logger.info("joining core session({}) state({}): {}", sessionId, sessionState, session);
|
|
||||||
for (CoreNode node : session.getNodes()) {
|
for (CoreNode node : session.getNodes()) {
|
||||||
NodeType nodeType = NodeType.find(node.getType(), node.getModel());
|
|
||||||
if (nodeType == null) {
|
|
||||||
logger.info(String.format("failed to find node type(%s) model(%s): %s",
|
|
||||||
node.getType(), node.getModel(), node.getName()));
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
node.setNodeType(nodeType);
|
|
||||||
networkGraph.addNode(node);
|
networkGraph.addNode(node);
|
||||||
}
|
}
|
||||||
|
|
||||||
// display all links
|
// display all links
|
||||||
for (CoreLink link : session.getLinks()) {
|
for (CoreLink link : session.getLinks()) {
|
||||||
if (link.getInterfaceOne() != null || link.getInterfaceTwo() != null) {
|
|
||||||
link.setType(LinkTypes.WIRED.getValue());
|
|
||||||
}
|
|
||||||
|
|
||||||
networkGraph.addLink(link);
|
networkGraph.addLink(link);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -191,7 +171,7 @@ public class Controller implements Initializable {
|
||||||
Platform.runLater(() -> decorator.setTitle(String.format("CORE (Session %s)", sessionId)));
|
Platform.runLater(() -> decorator.setTitle(String.format("CORE (Session %s)", sessionId)));
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean startSession() throws IOException {
|
public boolean startSession() {
|
||||||
// force nodes to get latest positions
|
// force nodes to get latest positions
|
||||||
networkGraph.updatePositions();
|
networkGraph.updatePositions();
|
||||||
|
|
||||||
|
@ -201,12 +181,18 @@ public class Controller implements Initializable {
|
||||||
List<Hook> hooks = hooksDialog.getHooks();
|
List<Hook> hooks = hooksDialog.getHooks();
|
||||||
|
|
||||||
// start/create session
|
// start/create session
|
||||||
|
boolean result = false;
|
||||||
progressBar.setVisible(true);
|
progressBar.setVisible(true);
|
||||||
boolean result = coreClient.start(nodes, links, hooks);
|
try {
|
||||||
progressBar.setVisible(false);
|
result = coreClient.start(nodes, links, hooks);
|
||||||
if (result) {
|
if (result) {
|
||||||
showMobilityScriptDialogs();
|
showMobilityScriptDialogs();
|
||||||
saveXmlMenuItem.setDisable(false);
|
saveXmlMenuItem.setDisable(false);
|
||||||
|
}
|
||||||
|
} catch (IOException ex) {
|
||||||
|
Toast.error("Failure Starting Session", ex);
|
||||||
|
} finally {
|
||||||
|
progressBar.setVisible(false);
|
||||||
}
|
}
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
@ -292,6 +278,7 @@ public class Controller implements Initializable {
|
||||||
connectDialog.setOwner(window);
|
connectDialog.setOwner(window);
|
||||||
guiPreferencesDialog.setOwner(window);
|
guiPreferencesDialog.setOwner(window);
|
||||||
nodeTypeCreateDialog.setOwner(window);
|
nodeTypeCreateDialog.setOwner(window);
|
||||||
|
rj45Dialog.setOwner(window);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void showMobilityScriptDialogs() {
|
private void showMobilityScriptDialogs() {
|
||||||
|
@ -448,6 +435,7 @@ public class Controller implements Initializable {
|
||||||
connectToCore(address, port);
|
connectToCore(address, port);
|
||||||
|
|
||||||
logger.info("controller initialize");
|
logger.info("controller initialize");
|
||||||
|
coreClient.initialize(this);
|
||||||
swingNode.setContent(networkGraph.getGraphViewer());
|
swingNode.setContent(networkGraph.getGraphViewer());
|
||||||
|
|
||||||
// update graph preferences
|
// update graph preferences
|
||||||
|
|
|
@ -1,8 +1,6 @@
|
||||||
package com.core.client;
|
package com.core.client;
|
||||||
|
|
||||||
import com.core.Controller;
|
import com.core.Controller;
|
||||||
import com.core.data.ServiceFile;
|
|
||||||
import com.core.data.WlanConfig;
|
|
||||||
import com.core.data.*;
|
import com.core.data.*;
|
||||||
|
|
||||||
import java.io.File;
|
import java.io.File;
|
||||||
|
@ -23,16 +21,14 @@ public interface ICoreClient {
|
||||||
|
|
||||||
boolean stopThroughput() throws IOException;
|
boolean stopThroughput() throws IOException;
|
||||||
|
|
||||||
void updateSession(Integer sessionId);
|
|
||||||
|
|
||||||
void updateState(SessionState state);
|
|
||||||
|
|
||||||
SessionOverview createSession() throws IOException;
|
SessionOverview createSession() throws IOException;
|
||||||
|
|
||||||
boolean deleteSession(Integer sessionId) throws IOException;
|
boolean deleteSession(Integer sessionId) throws IOException;
|
||||||
|
|
||||||
List<SessionOverview> getSessions() throws IOException;
|
List<SessionOverview> getSessions() throws IOException;
|
||||||
|
|
||||||
|
Session joinSession(Integer sessionId) throws IOException;
|
||||||
|
|
||||||
Session getSession(Integer sessionId) throws IOException;
|
Session getSession(Integer sessionId) throws IOException;
|
||||||
|
|
||||||
boolean start(Collection<CoreNode> nodes, Collection<CoreLink> links, List<Hook> hooks) throws IOException;
|
boolean start(Collection<CoreNode> nodes, Collection<CoreLink> links, List<Hook> hooks) throws IOException;
|
||||||
|
@ -117,5 +113,7 @@ public interface ICoreClient {
|
||||||
|
|
||||||
boolean setLocationConfig(LocationConfig config) throws IOException;
|
boolean setLocationConfig(LocationConfig config) throws IOException;
|
||||||
|
|
||||||
void setupEventHandlers(Controller controller) throws IOException;
|
void initialize(Controller controller);
|
||||||
|
|
||||||
|
List<String> getInterfaces() throws IOException;
|
||||||
}
|
}
|
||||||
|
|
|
@ -33,6 +33,7 @@ public class CoreGrpcClient implements ICoreClient {
|
||||||
private final ExecutorService executorService = Executors.newFixedThreadPool(6);
|
private final ExecutorService executorService = Executors.newFixedThreadPool(6);
|
||||||
private boolean handlingEvents = false;
|
private boolean handlingEvents = false;
|
||||||
private boolean handlingThroughputs = false;
|
private boolean handlingThroughputs = false;
|
||||||
|
private Controller controller;
|
||||||
|
|
||||||
private CoreProto.Node nodeToProto(CoreNode node) {
|
private CoreProto.Node nodeToProto(CoreNode node) {
|
||||||
CoreProto.Position position = CoreProto.Position.newBuilder()
|
CoreProto.Position position = CoreProto.Position.newBuilder()
|
||||||
|
@ -58,6 +59,9 @@ public class CoreGrpcClient implements ICoreClient {
|
||||||
if (node.getIcon() != null) {
|
if (node.getIcon() != null) {
|
||||||
builder.setIcon(node.getIcon());
|
builder.setIcon(node.getIcon());
|
||||||
}
|
}
|
||||||
|
if (node.getImage() != null) {
|
||||||
|
builder.setImage(node.getImage());
|
||||||
|
}
|
||||||
|
|
||||||
return builder.build();
|
return builder.build();
|
||||||
}
|
}
|
||||||
|
@ -83,33 +87,32 @@ public class CoreGrpcClient implements ICoreClient {
|
||||||
|
|
||||||
private CoreProto.LinkOptions linkOptionsToProto(CoreLinkOptions options) {
|
private CoreProto.LinkOptions linkOptionsToProto(CoreLinkOptions options) {
|
||||||
CoreProto.LinkOptions.Builder builder = CoreProto.LinkOptions.newBuilder();
|
CoreProto.LinkOptions.Builder builder = CoreProto.LinkOptions.newBuilder();
|
||||||
boolean unidirectional = false;
|
if (options.getUnidirectional() != null) {
|
||||||
if (options.getUnidirectional() != null && options.getUnidirectional() == 1) {
|
builder.setUnidirectional(options.getUnidirectional());
|
||||||
unidirectional = true;
|
|
||||||
}
|
}
|
||||||
if (options.getBandwidth() != null) {
|
if (options.getBandwidth() != null) {
|
||||||
builder.setBandwidth(options.getBandwidth().intValue());
|
builder.setBandwidth(options.getBandwidth());
|
||||||
}
|
}
|
||||||
if (options.getBurst() != null) {
|
if (options.getBurst() != null) {
|
||||||
builder.setBurst(options.getBurst().intValue());
|
builder.setBurst(options.getBurst());
|
||||||
}
|
}
|
||||||
if (options.getDelay() != null) {
|
if (options.getDelay() != null) {
|
||||||
builder.setDelay(options.getDelay().intValue());
|
builder.setDelay(options.getDelay());
|
||||||
}
|
}
|
||||||
if (options.getDup() != null) {
|
if (options.getDup() != null) {
|
||||||
builder.setDup(options.getDup().intValue());
|
builder.setDup(options.getDup());
|
||||||
}
|
}
|
||||||
if (options.getJitter() != null) {
|
if (options.getJitter() != null) {
|
||||||
builder.setJitter(options.getJitter().intValue());
|
builder.setJitter(options.getJitter());
|
||||||
}
|
}
|
||||||
if (options.getMburst() != null) {
|
if (options.getMburst() != null) {
|
||||||
builder.setMburst(options.getMburst().intValue());
|
builder.setMburst(options.getMburst());
|
||||||
}
|
}
|
||||||
if (options.getMer() != null) {
|
if (options.getMer() != null) {
|
||||||
builder.setMer(options.getMer().intValue());
|
builder.setMer(options.getMer());
|
||||||
}
|
}
|
||||||
if (options.getPer() != null) {
|
if (options.getPer() != null) {
|
||||||
builder.setPer(options.getPer().intValue());
|
builder.setPer(options.getPer().floatValue());
|
||||||
}
|
}
|
||||||
if (options.getKey() != null) {
|
if (options.getKey() != null) {
|
||||||
builder.setKey(options.getKey());
|
builder.setKey(options.getKey());
|
||||||
|
@ -117,7 +120,6 @@ public class CoreGrpcClient implements ICoreClient {
|
||||||
if (options.getOpaque() != null) {
|
if (options.getOpaque() != null) {
|
||||||
builder.setOpaque(options.getOpaque());
|
builder.setOpaque(options.getOpaque());
|
||||||
}
|
}
|
||||||
builder.setUnidirectional(unidirectional);
|
|
||||||
return builder.build();
|
return builder.build();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -157,14 +159,26 @@ public class CoreGrpcClient implements ICoreClient {
|
||||||
|
|
||||||
private CoreNode protoToNode(CoreProto.Node protoNode) {
|
private CoreNode protoToNode(CoreProto.Node protoNode) {
|
||||||
CoreNode node = new CoreNode(protoNode.getId());
|
CoreNode node = new CoreNode(protoNode.getId());
|
||||||
|
node.setType(protoNode.getTypeValue());
|
||||||
node.setName(protoNode.getName());
|
node.setName(protoNode.getName());
|
||||||
node.setEmane(protoNode.getEmane());
|
|
||||||
node.setIcon(protoNode.getIcon());
|
node.setIcon(protoNode.getIcon());
|
||||||
node.setModel(protoNode.getModel());
|
node.setModel(protoNode.getModel());
|
||||||
|
if (!protoNode.getEmane().isEmpty()) {
|
||||||
|
node.setEmane(protoNode.getEmane());
|
||||||
|
}
|
||||||
|
if (!protoNode.getImage().isEmpty()) {
|
||||||
|
node.setImage(protoNode.getImage());
|
||||||
|
}
|
||||||
node.setServices(new HashSet<>(protoNode.getServicesList()));
|
node.setServices(new HashSet<>(protoNode.getServicesList()));
|
||||||
node.getPosition().setX((double) protoNode.getPosition().getX());
|
node.getPosition().setX((double) protoNode.getPosition().getX());
|
||||||
node.getPosition().setY((double) protoNode.getPosition().getY());
|
node.getPosition().setY((double) protoNode.getPosition().getY());
|
||||||
node.setType(protoNode.getTypeValue());
|
NodeType nodeType = NodeType.find(node.getType(), node.getModel());
|
||||||
|
if (nodeType == null) {
|
||||||
|
logger.error("failed to find node type({}) model({}): {}",
|
||||||
|
node.getType(), node.getModel(), node.getName());
|
||||||
|
}
|
||||||
|
node.setNodeType(nodeType);
|
||||||
|
node.setLoaded(true);
|
||||||
return node;
|
return node;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -172,7 +186,9 @@ public class CoreGrpcClient implements ICoreClient {
|
||||||
CoreInterface coreInterface = new CoreInterface();
|
CoreInterface coreInterface = new CoreInterface();
|
||||||
coreInterface.setId(protoInterface.getId());
|
coreInterface.setId(protoInterface.getId());
|
||||||
coreInterface.setName(protoInterface.getName());
|
coreInterface.setName(protoInterface.getName());
|
||||||
coreInterface.setMac(protoInterface.getMac());
|
if (!protoInterface.getMac().isEmpty()) {
|
||||||
|
coreInterface.setMac(protoInterface.getMac());
|
||||||
|
}
|
||||||
String ip4String = String.format("%s/%s", protoInterface.getIp4(), protoInterface.getIp4Mask());
|
String ip4String = String.format("%s/%s", protoInterface.getIp4(), protoInterface.getIp4Mask());
|
||||||
IPAddress ip4 = new IPAddressString(ip4String).getAddress();
|
IPAddress ip4 = new IPAddressString(ip4String).getAddress();
|
||||||
coreInterface.setIp4(ip4);
|
coreInterface.setIp4(ip4);
|
||||||
|
@ -184,6 +200,7 @@ public class CoreGrpcClient implements ICoreClient {
|
||||||
|
|
||||||
private CoreLink protoToLink(CoreProto.Link linkProto) {
|
private CoreLink protoToLink(CoreProto.Link linkProto) {
|
||||||
CoreLink link = new CoreLink();
|
CoreLink link = new CoreLink();
|
||||||
|
link.setType(linkProto.getTypeValue());
|
||||||
link.setNodeOne(linkProto.getNodeOneId());
|
link.setNodeOne(linkProto.getNodeOneId());
|
||||||
link.setNodeTwo(linkProto.getNodeTwoId());
|
link.setNodeTwo(linkProto.getNodeTwoId());
|
||||||
CoreInterface interfaceOne = protoToInterface(linkProto.getInterfaceOne());
|
CoreInterface interfaceOne = protoToInterface(linkProto.getInterfaceOne());
|
||||||
|
@ -193,24 +210,29 @@ public class CoreGrpcClient implements ICoreClient {
|
||||||
|
|
||||||
CoreLinkOptions options = new CoreLinkOptions();
|
CoreLinkOptions options = new CoreLinkOptions();
|
||||||
CoreProto.LinkOptions protoOptions = linkProto.getOptions();
|
CoreProto.LinkOptions protoOptions = linkProto.getOptions();
|
||||||
options.setBandwidth((double) protoOptions.getBandwidth());
|
options.setBandwidth((int) protoOptions.getBandwidth());
|
||||||
options.setDelay((double) protoOptions.getDelay());
|
options.setDelay((int) protoOptions.getDelay());
|
||||||
options.setDup((double) protoOptions.getDup());
|
options.setDup((int) protoOptions.getDup());
|
||||||
options.setJitter((double) protoOptions.getJitter());
|
options.setJitter((int) protoOptions.getJitter());
|
||||||
options.setPer((double) protoOptions.getPer());
|
options.setPer((double) protoOptions.getPer());
|
||||||
options.setBurst((double) protoOptions.getBurst());
|
options.setBurst((int) protoOptions.getBurst());
|
||||||
if (protoOptions.hasField(CoreProto.LinkOptions.getDescriptor().findFieldByName("key"))) {
|
if (protoOptions.hasField(CoreProto.LinkOptions.getDescriptor().findFieldByName("key"))) {
|
||||||
options.setKey(protoOptions.getKey());
|
options.setKey(protoOptions.getKey());
|
||||||
}
|
}
|
||||||
options.setMburst((double) protoOptions.getMburst());
|
options.setMburst((int) protoOptions.getMburst());
|
||||||
options.setMer((double) protoOptions.getMer());
|
options.setMer((int) protoOptions.getMer());
|
||||||
options.setOpaque(protoOptions.getOpaque());
|
options.setOpaque(protoOptions.getOpaque());
|
||||||
options.setUnidirectional(protoOptions.getUnidirectional() ? 1 : 0);
|
options.setUnidirectional(protoOptions.getUnidirectional());
|
||||||
link.setOptions(options);
|
link.setOptions(options);
|
||||||
|
|
||||||
return link;
|
return link;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void initialize(Controller controller) {
|
||||||
|
this.controller = controller;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void setConnection(String address, int port) {
|
public void setConnection(String address, int port) {
|
||||||
this.address = address;
|
this.address = address;
|
||||||
|
@ -283,16 +305,6 @@ public class CoreGrpcClient implements ICoreClient {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public void updateSession(Integer sessionId) {
|
|
||||||
this.sessionId = sessionId;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void updateState(SessionState state) {
|
|
||||||
sessionState = state;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public SessionOverview createSession() throws IOException {
|
public SessionOverview createSession() throws IOException {
|
||||||
CoreProto.CreateSessionRequest request = CoreProto.CreateSessionRequest.newBuilder().build();
|
CoreProto.CreateSessionRequest request = CoreProto.CreateSessionRequest.newBuilder().build();
|
||||||
|
@ -345,6 +357,7 @@ public class CoreGrpcClient implements ICoreClient {
|
||||||
try {
|
try {
|
||||||
CoreProto.GetSessionResponse response = blockingStub.getSession(request);
|
CoreProto.GetSessionResponse response = blockingStub.getSession(request);
|
||||||
Session session = new Session();
|
Session session = new Session();
|
||||||
|
session.setId(sessionId);
|
||||||
for (CoreProto.Node protoNode : response.getSession().getNodesList()) {
|
for (CoreProto.Node protoNode : response.getSession().getNodesList()) {
|
||||||
if (CoreProto.NodeType.Enum.PEER_TO_PEER == protoNode.getType()) {
|
if (CoreProto.NodeType.Enum.PEER_TO_PEER == protoNode.getType()) {
|
||||||
continue;
|
continue;
|
||||||
|
@ -355,19 +368,43 @@ public class CoreGrpcClient implements ICoreClient {
|
||||||
session.getNodes().add(node);
|
session.getNodes().add(node);
|
||||||
}
|
}
|
||||||
for (CoreProto.Link linkProto : response.getSession().getLinksList()) {
|
for (CoreProto.Link linkProto : response.getSession().getLinksList()) {
|
||||||
logger.info("adding link: {} - {}", linkProto.getNodeOneId(), linkProto.getNodeTwoId());
|
logger.info("adding link: {}", linkProto);
|
||||||
CoreLink link = protoToLink(linkProto);
|
CoreLink link = protoToLink(linkProto);
|
||||||
session.getLinks().add(link);
|
session.getLinks().add(link);
|
||||||
}
|
}
|
||||||
session.setState(response.getSession().getStateValue());
|
SessionState state = SessionState.get(response.getSession().getStateValue());
|
||||||
|
session.setState(state);
|
||||||
return session;
|
return session;
|
||||||
} catch (StatusRuntimeException ex) {
|
} catch (StatusRuntimeException ex) {
|
||||||
throw new IOException(ex);
|
throw new IOException(ex);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Session joinSession(Integer sessionId) throws IOException {
|
||||||
|
// stop handling previous session events if currently running
|
||||||
|
if (isRunning()) {
|
||||||
|
handlingEvents = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// join desired session
|
||||||
|
Session session = getSession(sessionId);
|
||||||
|
this.sessionId = session.getId();
|
||||||
|
sessionState = session.getState();
|
||||||
|
logger.info("joining session({}) state({})", this.sessionId, sessionState);
|
||||||
|
|
||||||
|
// setup event handlers if joined session is running
|
||||||
|
if (isRunning()) {
|
||||||
|
setupEventHandlers();
|
||||||
|
}
|
||||||
|
|
||||||
|
return session;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean start(Collection<CoreNode> nodes, Collection<CoreLink> links, List<Hook> hooks) throws IOException {
|
public boolean start(Collection<CoreNode> nodes, Collection<CoreLink> links, List<Hook> hooks) throws IOException {
|
||||||
|
setupEventHandlers();
|
||||||
|
|
||||||
boolean result = setState(SessionState.DEFINITION);
|
boolean result = setState(SessionState.DEFINITION);
|
||||||
if (!result) {
|
if (!result) {
|
||||||
return false;
|
return false;
|
||||||
|
@ -806,6 +843,8 @@ public class CoreGrpcClient implements ICoreClient {
|
||||||
@Override
|
@Override
|
||||||
public boolean deleteNode(CoreNode node) throws IOException {
|
public boolean deleteNode(CoreNode node) throws IOException {
|
||||||
CoreProto.DeleteNodeRequest request = CoreProto.DeleteNodeRequest.newBuilder()
|
CoreProto.DeleteNodeRequest request = CoreProto.DeleteNodeRequest.newBuilder()
|
||||||
|
.setSessionId(sessionId)
|
||||||
|
.setNodeId(node.getId())
|
||||||
.build();
|
.build();
|
||||||
try {
|
try {
|
||||||
CoreProto.DeleteNodeResponse response = blockingStub.deleteNode(request);
|
CoreProto.DeleteNodeResponse response = blockingStub.deleteNode(request);
|
||||||
|
@ -1132,8 +1171,7 @@ public class CoreGrpcClient implements ICoreClient {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
private void setupEventHandlers() throws IOException {
|
||||||
public void setupEventHandlers(Controller controller) throws IOException {
|
|
||||||
logger.info("setting up event handlers");
|
logger.info("setting up event handlers");
|
||||||
handlingEvents = true;
|
handlingEvents = true;
|
||||||
try {
|
try {
|
||||||
|
@ -1151,22 +1189,22 @@ public class CoreGrpcClient implements ICoreClient {
|
||||||
logger.info("handling event: {}", event);
|
logger.info("handling event: {}", event);
|
||||||
switch (event.getEventTypeCase()) {
|
switch (event.getEventTypeCase()) {
|
||||||
case SESSION_EVENT:
|
case SESSION_EVENT:
|
||||||
handleSessionEvents(controller, event.getSessionEvent());
|
handleSessionEvents(event.getSessionEvent());
|
||||||
break;
|
break;
|
||||||
case NODE_EVENT:
|
case NODE_EVENT:
|
||||||
handleNodeEvents(controller, event.getNodeEvent());
|
handleNodeEvents(event.getNodeEvent());
|
||||||
break;
|
break;
|
||||||
case LINK_EVENT:
|
case LINK_EVENT:
|
||||||
handleLinkEvents(controller, event.getLinkEvent());
|
handleLinkEvents(event.getLinkEvent());
|
||||||
break;
|
break;
|
||||||
case CONFIG_EVENT:
|
case CONFIG_EVENT:
|
||||||
handleConfigEvents(controller, event.getConfigEvent());
|
handleConfigEvents(event.getConfigEvent());
|
||||||
break;
|
break;
|
||||||
case EXCEPTION_EVENT:
|
case EXCEPTION_EVENT:
|
||||||
handleExceptionEvents(controller, event.getExceptionEvent());
|
handleExceptionEvents(event.getExceptionEvent());
|
||||||
break;
|
break;
|
||||||
case FILE_EVENT:
|
case FILE_EVENT:
|
||||||
handleFileEvents(controller, event.getFileEvent());
|
handleFileEvents(event.getFileEvent());
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
logger.error("unknown event type: {}", event.getEventTypeCase());
|
logger.error("unknown event type: {}", event.getEventTypeCase());
|
||||||
|
@ -1185,7 +1223,7 @@ public class CoreGrpcClient implements ICoreClient {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void handleSessionEvents(Controller controller, CoreProto.SessionEvent event) {
|
private void handleSessionEvents(CoreProto.SessionEvent event) {
|
||||||
logger.info("session event: {}", event);
|
logger.info("session event: {}", event);
|
||||||
SessionState state = SessionState.get(event.getEvent());
|
SessionState state = SessionState.get(event.getEvent());
|
||||||
if (state == null) {
|
if (state == null) {
|
||||||
|
@ -1196,7 +1234,7 @@ public class CoreGrpcClient implements ICoreClient {
|
||||||
// session state event
|
// session state event
|
||||||
if (state.getValue() <= 6) {
|
if (state.getValue() <= 6) {
|
||||||
logger.info("event updating session state: {}", state);
|
logger.info("event updating session state: {}", state);
|
||||||
updateState(state);
|
sessionState = state;
|
||||||
// mobility script event
|
// mobility script event
|
||||||
} else if (state.getValue() <= 9) {
|
} else if (state.getValue() <= 9) {
|
||||||
Integer nodeId = event.getNodeId();
|
Integer nodeId = event.getNodeId();
|
||||||
|
@ -1211,21 +1249,21 @@ public class CoreGrpcClient implements ICoreClient {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void handleNodeEvents(Controller controller, CoreProto.NodeEvent event) {
|
private void handleNodeEvents(CoreProto.NodeEvent event) {
|
||||||
logger.info("node event: {}", event);
|
logger.info("node event: {}", event);
|
||||||
CoreNode node = protoToNode(event.getNode());
|
CoreNode node = protoToNode(event.getNode());
|
||||||
controller.getNetworkGraph().setNodeLocation(node);
|
controller.getNetworkGraph().setNodeLocation(node);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void handleExceptionEvents(Controller controller, CoreProto.ExceptionEvent event) {
|
private void handleExceptionEvents(CoreProto.ExceptionEvent event) {
|
||||||
logger.info("exception event: {}", event);
|
logger.info("exception event: {}", event);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void handleConfigEvents(Controller controller, CoreProto.ConfigEvent event) {
|
private void handleConfigEvents(CoreProto.ConfigEvent event) {
|
||||||
logger.info("config event: {}", event);
|
logger.info("config event: {}", event);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void handleLinkEvents(Controller controller, CoreProto.LinkEvent event) {
|
private void handleLinkEvents(CoreProto.LinkEvent event) {
|
||||||
logger.info("link event: {}", event);
|
logger.info("link event: {}", event);
|
||||||
CoreLink link = protoToLink(event.getLink());
|
CoreLink link = protoToLink(event.getLink());
|
||||||
MessageFlags flag = MessageFlags.get(event.getMessageTypeValue());
|
MessageFlags flag = MessageFlags.get(event.getMessageTypeValue());
|
||||||
|
@ -1239,7 +1277,18 @@ public class CoreGrpcClient implements ICoreClient {
|
||||||
controller.getNetworkGraph().getGraphViewer().repaint();
|
controller.getNetworkGraph().getGraphViewer().repaint();
|
||||||
}
|
}
|
||||||
|
|
||||||
private void handleFileEvents(Controller controller, CoreProto.FileEvent event) {
|
private void handleFileEvents(CoreProto.FileEvent event) {
|
||||||
logger.info("file event: {}", event);
|
logger.info("file event: {}", event);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public List<String> getInterfaces() throws IOException {
|
||||||
|
CoreProto.GetInterfacesRequest request = CoreProto.GetInterfacesRequest.newBuilder().build();
|
||||||
|
try {
|
||||||
|
CoreProto.GetInterfacesResponse response = blockingStub.getInterfaces(request);
|
||||||
|
return response.getInterfacesList();
|
||||||
|
} catch (StatusRuntimeException ex) {
|
||||||
|
throw new IOException(ex);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -15,7 +15,7 @@ public class CoreLink {
|
||||||
private double throughput;
|
private double throughput;
|
||||||
private boolean visible = true;
|
private boolean visible = true;
|
||||||
private Integer messageType;
|
private Integer messageType;
|
||||||
private Integer type = 1;
|
private Integer type = LinkTypes.WIRED.getValue();
|
||||||
private Integer nodeOne;
|
private Integer nodeOne;
|
||||||
private Integer nodeTwo;
|
private Integer nodeTwo;
|
||||||
private CoreInterface interfaceOne;
|
private CoreInterface interfaceOne;
|
||||||
|
|
|
@ -8,14 +8,14 @@ import lombok.NoArgsConstructor;
|
||||||
public class CoreLinkOptions {
|
public class CoreLinkOptions {
|
||||||
private String opaque;
|
private String opaque;
|
||||||
private Integer session;
|
private Integer session;
|
||||||
private Double jitter;
|
private Integer jitter;
|
||||||
private Integer key;
|
private Integer key;
|
||||||
private Double mburst;
|
private Integer mburst;
|
||||||
private Double mer;
|
private Integer mer;
|
||||||
private Double per;
|
private Double per;
|
||||||
private Double bandwidth;
|
private Integer bandwidth;
|
||||||
private Double burst;
|
private Integer burst;
|
||||||
private Double delay;
|
private Integer delay;
|
||||||
private Double dup;
|
private Integer dup;
|
||||||
private Integer unidirectional;
|
private Boolean unidirectional;
|
||||||
}
|
}
|
||||||
|
|
|
@ -28,6 +28,7 @@ public class CoreNode {
|
||||||
private String url;
|
private String url;
|
||||||
private NodeType nodeType;
|
private NodeType nodeType;
|
||||||
private String icon;
|
private String icon;
|
||||||
|
private String image;
|
||||||
private boolean loaded = true;
|
private boolean loaded = true;
|
||||||
private LayeredIcon graphIcon;
|
private LayeredIcon graphIcon;
|
||||||
private RadioIcon radioIcon = new RadioIcon();
|
private RadioIcon radioIcon = new RadioIcon();
|
||||||
|
|
|
@ -1,5 +1,7 @@
|
||||||
package com.core.data;
|
package com.core.data;
|
||||||
|
|
||||||
|
import javafx.scene.control.Label;
|
||||||
|
import javafx.scene.image.ImageView;
|
||||||
import lombok.Data;
|
import lombok.Data;
|
||||||
import lombok.EqualsAndHashCode;
|
import lombok.EqualsAndHashCode;
|
||||||
import org.apache.logging.log4j.LogManager;
|
import org.apache.logging.log4j.LogManager;
|
||||||
|
@ -18,7 +20,10 @@ public class NodeType {
|
||||||
public static final int SWITCH = 4;
|
public static final int SWITCH = 4;
|
||||||
public static final int HUB = 5;
|
public static final int HUB = 5;
|
||||||
public static final int WLAN = 6;
|
public static final int WLAN = 6;
|
||||||
|
public static final int RJ45 = 7;
|
||||||
public static final int EMANE = 10;
|
public static final int EMANE = 10;
|
||||||
|
public static final int DOCKER = 15;
|
||||||
|
public static final int LXC = 16;
|
||||||
@EqualsAndHashCode.Include
|
@EqualsAndHashCode.Include
|
||||||
private final int id;
|
private final int id;
|
||||||
private final int value;
|
private final int value;
|
||||||
|
@ -27,21 +32,14 @@ public class NodeType {
|
||||||
private String model;
|
private String model;
|
||||||
private String icon;
|
private String icon;
|
||||||
|
|
||||||
// PHYSICAL = 1
|
|
||||||
// RJ45 = 7
|
|
||||||
// TUNNEL = 8
|
|
||||||
// KTUNNEL = 9
|
|
||||||
// EMANE = 10
|
|
||||||
// TAP_BRIDGE = 11
|
|
||||||
// PEER_TO_PEER = 12
|
|
||||||
// CONTROL_NET = 13
|
|
||||||
// EMANE_NET = 14;
|
|
||||||
|
|
||||||
static {
|
static {
|
||||||
add(new NodeType(SWITCH, "lanswitch", "Switch", "/icons/switch-100.png"));
|
add(new NodeType(SWITCH, "lanswitch", "Switch", "/icons/switch-100.png"));
|
||||||
add(new NodeType(HUB, "hub", "Hub", "/icons/hub-100.png"));
|
add(new NodeType(HUB, "hub", "Hub", "/icons/hub-100.png"));
|
||||||
add(new NodeType(WLAN, "wlan", "WLAN", "/icons/wlan-100.png"));
|
add(new NodeType(WLAN, "wlan", "WLAN", "/icons/wlan-100.png"));
|
||||||
|
add(new NodeType(RJ45, "rj45", "RJ45", "/icons/rj45-80.png"));
|
||||||
add(new NodeType(EMANE, "wlan", "EMANE", "/icons/emane-100.png"));
|
add(new NodeType(EMANE, "wlan", "EMANE", "/icons/emane-100.png"));
|
||||||
|
add(new NodeType(DOCKER, null, "DockerNode", "/icons/dockernode-100.png"));
|
||||||
|
add(new NodeType(LXC, null, "LxcNode", "/icons/lxcnode-100.png"));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -53,6 +51,19 @@ public class NodeType {
|
||||||
this.icon = icon;
|
this.icon = icon;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public Label createLabel(int size) {
|
||||||
|
ImageView labelIcon = new ImageView(icon);
|
||||||
|
labelIcon.setFitWidth(size);
|
||||||
|
labelIcon.setFitHeight(size);
|
||||||
|
Label label = new Label(display, labelIcon);
|
||||||
|
label.setUserData(id);
|
||||||
|
return label;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static boolean isDefault(NodeType nodeType) {
|
||||||
|
return nodeType.value == DEFAULT || nodeType.value == DOCKER || nodeType.value == LXC;
|
||||||
|
}
|
||||||
|
|
||||||
public static void add(NodeType nodeType) {
|
public static void add(NodeType nodeType) {
|
||||||
ID_LOOKUP.put(nodeType.getId(), nodeType);
|
ID_LOOKUP.put(nodeType.getId(), nodeType);
|
||||||
}
|
}
|
||||||
|
@ -74,7 +85,7 @@ public class NodeType {
|
||||||
.filter(nodeType -> {
|
.filter(nodeType -> {
|
||||||
boolean sameType = nodeType.getValue() == type;
|
boolean sameType = nodeType.getValue() == type;
|
||||||
boolean sameModel = true;
|
boolean sameModel = true;
|
||||||
if (!model.isEmpty()) {
|
if (model != null && !model.isEmpty()) {
|
||||||
sameModel = model.equals(nodeType.getModel());
|
sameModel = model.equals(nodeType.getModel());
|
||||||
}
|
}
|
||||||
return sameType && sameModel;
|
return sameType && sameModel;
|
||||||
|
|
|
@ -9,7 +9,8 @@ import java.util.List;
|
||||||
@Data
|
@Data
|
||||||
@NoArgsConstructor
|
@NoArgsConstructor
|
||||||
public class Session {
|
public class Session {
|
||||||
private Integer state;
|
private Integer id;
|
||||||
|
private SessionState state;
|
||||||
private List<CoreNode> nodes = new ArrayList<>();
|
private List<CoreNode> nodes = new ArrayList<>();
|
||||||
private List<CoreLink> links = new ArrayList<>();
|
private List<CoreLink> links = new ArrayList<>();
|
||||||
}
|
}
|
||||||
|
|
|
@ -6,18 +6,16 @@ import inet.ipaddr.IPAddressString;
|
||||||
import org.apache.logging.log4j.LogManager;
|
import org.apache.logging.log4j.LogManager;
|
||||||
import org.apache.logging.log4j.Logger;
|
import org.apache.logging.log4j.Logger;
|
||||||
|
|
||||||
import java.beans.IndexedPropertyDescriptor;
|
|
||||||
import java.util.Comparator;
|
import java.util.Comparator;
|
||||||
import java.util.HashSet;
|
import java.util.HashSet;
|
||||||
import java.util.Queue;
|
import java.util.Queue;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
import java.util.concurrent.ArrayBlockingQueue;
|
|
||||||
import java.util.concurrent.LinkedBlockingQueue;
|
import java.util.concurrent.LinkedBlockingQueue;
|
||||||
import java.util.concurrent.atomic.AtomicBoolean;
|
|
||||||
|
|
||||||
public class CoreAddresses {
|
public class CoreAddresses {
|
||||||
|
private static final String ADDRESS = "10.0.0.0/24";
|
||||||
private static final Logger logger = LogManager.getLogger();
|
private static final Logger logger = LogManager.getLogger();
|
||||||
private IPAddress currentSubnet = new IPAddressString("10.0.0.0/24").getAddress().toPrefixBlock();
|
private IPAddress currentSubnet = new IPAddressString(ADDRESS).getAddress().toPrefixBlock();
|
||||||
private Queue<IPAddress> deleted = new LinkedBlockingQueue<>();
|
private Queue<IPAddress> deleted = new LinkedBlockingQueue<>();
|
||||||
private Set<IPAddress> usedSubnets = new HashSet<>();
|
private Set<IPAddress> usedSubnets = new HashSet<>();
|
||||||
|
|
||||||
|
@ -69,11 +67,6 @@ public class CoreAddresses {
|
||||||
public void reset() {
|
public void reset() {
|
||||||
deleted.clear();
|
deleted.clear();
|
||||||
usedSubnets.clear();
|
usedSubnets.clear();
|
||||||
currentSubnet = new IPAddressString("10.0.0.0/24").getAddress().toPrefixBlock();
|
currentSubnet = new IPAddressString(ADDRESS).getAddress().toPrefixBlock();
|
||||||
}
|
|
||||||
|
|
||||||
public static void main(String... args) {
|
|
||||||
IPAddress addresses = new IPAddressString("10.0.0.0/16").getAddress();
|
|
||||||
System.out.println(String.format("address: %s", addresses.increment(257)));
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -60,6 +60,8 @@ public class CorePopupGraphMousePlugin<V, E> extends EditingPopupGraphMousePlugi
|
||||||
ContextMenu contextMenu = new ContextMenu();
|
ContextMenu contextMenu = new ContextMenu();
|
||||||
switch (node.getType()) {
|
switch (node.getType()) {
|
||||||
case NodeType.DEFAULT:
|
case NodeType.DEFAULT:
|
||||||
|
case NodeType.DOCKER:
|
||||||
|
case NodeType.LXC:
|
||||||
contextMenu = new NodeContextMenu(controller, node);
|
contextMenu = new NodeContextMenu(controller, node);
|
||||||
break;
|
break;
|
||||||
case NodeType.WLAN:
|
case NodeType.WLAN:
|
||||||
|
@ -68,6 +70,9 @@ public class CorePopupGraphMousePlugin<V, E> extends EditingPopupGraphMousePlugi
|
||||||
case NodeType.EMANE:
|
case NodeType.EMANE:
|
||||||
contextMenu = new EmaneContextMenu(controller, node);
|
contextMenu = new EmaneContextMenu(controller, node);
|
||||||
break;
|
break;
|
||||||
|
case NodeType.RJ45:
|
||||||
|
contextMenu = new Rj45ContextMenu(controller, node);
|
||||||
|
break;
|
||||||
default:
|
default:
|
||||||
logger.warn("no context menu for node: {}", node.getType());
|
logger.warn("no context menu for node: {}", node.getType());
|
||||||
break;
|
break;
|
||||||
|
|
|
@ -298,8 +298,8 @@ public class NetworkGraph {
|
||||||
Pair<CoreNode> endpoints = graph.getEndpoints(link);
|
Pair<CoreNode> endpoints = graph.getEndpoints(link);
|
||||||
CoreNode nodeOne = endpoints.getFirst();
|
CoreNode nodeOne = endpoints.getFirst();
|
||||||
CoreNode nodeTwo = endpoints.getSecond();
|
CoreNode nodeTwo = endpoints.getSecond();
|
||||||
boolean nodeOneIsDefault = isNode(nodeOne);
|
boolean nodeOneIsDefault = NodeType.isDefault(nodeOne.getNodeType());
|
||||||
boolean nodeTwoIsDefault = isNode(nodeTwo);
|
boolean nodeTwoIsDefault = NodeType.isDefault(nodeTwo.getNodeType());
|
||||||
|
|
||||||
// check what we are linking together
|
// check what we are linking together
|
||||||
IPAddress subnet = null;
|
IPAddress subnet = null;
|
||||||
|
@ -360,7 +360,7 @@ public class NetworkGraph {
|
||||||
currentInterface = link.getInterfaceTwo();
|
currentInterface = link.getInterfaceTwo();
|
||||||
}
|
}
|
||||||
|
|
||||||
if (isNode(currentNode)) {
|
if (NodeType.isDefault(currentNode.getNodeType())) {
|
||||||
interfaces.add(currentInterface);
|
interfaces.add(currentInterface);
|
||||||
} else {
|
} else {
|
||||||
Set<CoreInterface> nextInterfaces = getNetworkInterfaces(currentNode, visited);
|
Set<CoreInterface> nextInterfaces = getNetworkInterfaces(currentNode, visited);
|
||||||
|
@ -407,10 +407,6 @@ public class NetworkGraph {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private boolean isNode(CoreNode node) {
|
|
||||||
return node.getType() == NodeType.DEFAULT;
|
|
||||||
}
|
|
||||||
|
|
||||||
private CoreInterface createInterface(CoreNode node, int interfaceId, IPAddress subnet) {
|
private CoreInterface createInterface(CoreNode node, int interfaceId, IPAddress subnet) {
|
||||||
CoreInterface coreInterface = new CoreInterface();
|
CoreInterface coreInterface = new CoreInterface();
|
||||||
coreInterface.setId(interfaceId);
|
coreInterface.setId(interfaceId);
|
||||||
|
@ -429,8 +425,8 @@ public class NetworkGraph {
|
||||||
CoreInterface interfaceOne = link.getInterfaceOne();
|
CoreInterface interfaceOne = link.getInterfaceOne();
|
||||||
CoreNode nodeTwo = getVertex(link.getNodeTwo());
|
CoreNode nodeTwo = getVertex(link.getNodeTwo());
|
||||||
CoreInterface interfaceTwo = link.getInterfaceTwo();
|
CoreInterface interfaceTwo = link.getInterfaceTwo();
|
||||||
boolean nodeOneIsDefault = isNode(nodeOne);
|
boolean nodeOneIsDefault = NodeType.isDefault(nodeOne.getNodeType());
|
||||||
boolean nodeTwoIsDefault = isNode(nodeTwo);
|
boolean nodeTwoIsDefault = NodeType.isDefault(nodeTwo.getNodeType());
|
||||||
|
|
||||||
// check what we are unlinking
|
// check what we are unlinking
|
||||||
Set<CoreInterface> interfaces;
|
Set<CoreInterface> interfaces;
|
||||||
|
@ -445,7 +441,7 @@ public class NetworkGraph {
|
||||||
logger.info("unlinking node one from network reuse subnet: {}", subnet);
|
logger.info("unlinking node one from network reuse subnet: {}", subnet);
|
||||||
}
|
}
|
||||||
} else if (nodeTwoIsDefault) {
|
} else if (nodeTwoIsDefault) {
|
||||||
interfaces = getNetworkInterfaces(nodeOne, new HashSet<>());
|
interfaces = getNetworkInterfaces(nodeOne, new HashSet<>());
|
||||||
if (interfaces.isEmpty()) {
|
if (interfaces.isEmpty()) {
|
||||||
subnet = interfaceTwo.getIp4().toPrefixBlock();
|
subnet = interfaceTwo.getIp4().toPrefixBlock();
|
||||||
logger.info("unlinking node two from network reuse subnet: {}", subnet);
|
logger.info("unlinking node two from network reuse subnet: {}", subnet);
|
||||||
|
@ -461,16 +457,22 @@ public class NetworkGraph {
|
||||||
|
|
||||||
private void handleVertexAdded(GraphEvent.Vertex<CoreNode, CoreLink> vertexEvent) {
|
private void handleVertexAdded(GraphEvent.Vertex<CoreNode, CoreLink> vertexEvent) {
|
||||||
CoreNode node = vertexEvent.getVertex();
|
CoreNode node = vertexEvent.getVertex();
|
||||||
if (!node.isLoaded()) {
|
if (node.isLoaded()) {
|
||||||
node.setNodeType(nodeType);
|
return;
|
||||||
if (node.getType() == NodeType.EMANE) {
|
|
||||||
String emaneModel = controller.getNodeEmaneDialog().getModels().get(0);
|
|
||||||
node.setEmane(emaneModel);
|
|
||||||
}
|
|
||||||
|
|
||||||
logger.info("adding user created node: {}", node);
|
|
||||||
nodeMap.put(node.getId(), node);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
node.setNodeType(nodeType);
|
||||||
|
if (node.getType() == NodeType.EMANE) {
|
||||||
|
String emaneModel = controller.getNodeEmaneDialog().getModels().get(0);
|
||||||
|
node.setEmane(emaneModel);
|
||||||
|
} else if (node.getType() == NodeType.DOCKER || node.getType() == NodeType.LXC) {
|
||||||
|
node.setImage("ubuntu");
|
||||||
|
} else if (node.getType() == NodeType.RJ45) {
|
||||||
|
Platform.runLater(() -> controller.getRj45Dialog().showDialog(node));
|
||||||
|
}
|
||||||
|
|
||||||
|
logger.info("adding user created node: {}", node);
|
||||||
|
nodeMap.put(node.getId(), node);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void handleVertexRemoved(GraphEvent.Vertex<CoreNode, CoreLink> vertexEvent) {
|
private void handleVertexRemoved(GraphEvent.Vertex<CoreNode, CoreLink> vertexEvent) {
|
||||||
|
@ -506,7 +508,7 @@ public class NetworkGraph {
|
||||||
try {
|
try {
|
||||||
controller.getCoreClient().deleteNode(node);
|
controller.getCoreClient().deleteNode(node);
|
||||||
} catch (IOException ex) {
|
} catch (IOException ex) {
|
||||||
logger.error("error deleting node");
|
logger.error("error deleting node", ex);
|
||||||
Toast.error(String.format("Error deleting node: %s", node.getName()));
|
Toast.error(String.format("Error deleting node: %s", node.getName()));
|
||||||
}
|
}
|
||||||
graphViewer.getPickedVertexState().pick(node, false);
|
graphViewer.getPickedVertexState().pick(node, false);
|
||||||
|
|
21
corefx/src/main/java/com/core/graph/Rj45ContextMenu.java
Normal file
21
corefx/src/main/java/com/core/graph/Rj45ContextMenu.java
Normal file
|
@ -0,0 +1,21 @@
|
||||||
|
package com.core.graph;
|
||||||
|
|
||||||
|
import com.core.Controller;
|
||||||
|
import com.core.data.CoreNode;
|
||||||
|
import org.apache.logging.log4j.LogManager;
|
||||||
|
import org.apache.logging.log4j.Logger;
|
||||||
|
|
||||||
|
class Rj45ContextMenu extends AbstractNodeContextMenu {
|
||||||
|
private static final Logger logger = LogManager.getLogger();
|
||||||
|
|
||||||
|
Rj45ContextMenu(Controller controller, CoreNode coreNode) {
|
||||||
|
super(controller, coreNode);
|
||||||
|
setup();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void setup() {
|
||||||
|
if (!controller.getCoreClient().isRunning()) {
|
||||||
|
addMenuItem("Delete Node", event -> controller.deleteNode(coreNode));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
171
corefx/src/main/java/com/core/ui/DetailsPanel.java
Normal file
171
corefx/src/main/java/com/core/ui/DetailsPanel.java
Normal file
|
@ -0,0 +1,171 @@
|
||||||
|
package com.core.ui;
|
||||||
|
|
||||||
|
import com.core.Controller;
|
||||||
|
import com.core.data.CoreInterface;
|
||||||
|
import com.core.data.CoreNode;
|
||||||
|
import com.core.ui.textfields.DoubleFilter;
|
||||||
|
import com.core.utils.FxmlUtils;
|
||||||
|
import com.jfoenix.controls.JFXButton;
|
||||||
|
import com.jfoenix.controls.JFXTextField;
|
||||||
|
import inet.ipaddr.IPAddress;
|
||||||
|
import javafx.event.ActionEvent;
|
||||||
|
import javafx.event.EventHandler;
|
||||||
|
import javafx.fxml.FXML;
|
||||||
|
import javafx.geometry.Insets;
|
||||||
|
import javafx.geometry.Orientation;
|
||||||
|
import javafx.scene.control.Label;
|
||||||
|
import javafx.scene.control.ScrollPane;
|
||||||
|
import javafx.scene.control.Separator;
|
||||||
|
import javafx.scene.control.TextFormatter;
|
||||||
|
import javafx.scene.layout.GridPane;
|
||||||
|
import javafx.util.converter.DoubleStringConverter;
|
||||||
|
import javafx.util.converter.IntegerStringConverter;
|
||||||
|
import org.apache.logging.log4j.LogManager;
|
||||||
|
import org.apache.logging.log4j.Logger;
|
||||||
|
|
||||||
|
import java.util.function.UnaryOperator;
|
||||||
|
|
||||||
|
abstract class DetailsPanel extends ScrollPane {
|
||||||
|
private static final Logger logger = LogManager.getLogger();
|
||||||
|
private static final int START_INDEX = 1;
|
||||||
|
final Controller controller;
|
||||||
|
@FXML Label title;
|
||||||
|
@FXML ScrollPane scrollPane;
|
||||||
|
@FXML GridPane gridPane;
|
||||||
|
int index = START_INDEX;
|
||||||
|
|
||||||
|
DetailsPanel(Controller controller) {
|
||||||
|
this.controller = controller;
|
||||||
|
FxmlUtils.loadRootController(this, "/fxml/details_panel.fxml");
|
||||||
|
setPrefWidth(400);
|
||||||
|
}
|
||||||
|
|
||||||
|
void setTitle(String text) {
|
||||||
|
title.setText(text);
|
||||||
|
}
|
||||||
|
|
||||||
|
void addButton(String text, EventHandler<ActionEvent> handler) {
|
||||||
|
JFXButton emaneButton = new JFXButton(text);
|
||||||
|
emaneButton.getStyleClass().add("core-button");
|
||||||
|
emaneButton.setMaxWidth(Double.MAX_VALUE);
|
||||||
|
emaneButton.setOnAction(handler);
|
||||||
|
gridPane.add(emaneButton, 0, index++, 2, 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
void addLabel(String text) {
|
||||||
|
Label label = new Label(text);
|
||||||
|
label.getStyleClass().add("details-label");
|
||||||
|
gridPane.add(label, 0, index++, 2, 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
void addSeparator() {
|
||||||
|
Separator separator = new Separator(Orientation.HORIZONTAL);
|
||||||
|
gridPane.add(separator, 0, index++, 2, 1);
|
||||||
|
GridPane.setMargin(separator, new Insets(10, 0, 0, 0));
|
||||||
|
}
|
||||||
|
|
||||||
|
void addInterface(CoreInterface coreInterface, CoreNode linkedNode) {
|
||||||
|
if (linkedNode != null) {
|
||||||
|
addRow("Linked To", linkedNode.getName(), true);
|
||||||
|
}
|
||||||
|
addRow("Interface", coreInterface.getName(), true);
|
||||||
|
if (coreInterface.getMac() != null) {
|
||||||
|
addRow("MAC", coreInterface.getMac(), true);
|
||||||
|
}
|
||||||
|
addAddress("IP4", coreInterface.getIp4());
|
||||||
|
addAddress("IP6", coreInterface.getIp6());
|
||||||
|
}
|
||||||
|
|
||||||
|
void addInterface(CoreInterface coreInterface) {
|
||||||
|
addInterface(coreInterface, null);
|
||||||
|
}
|
||||||
|
|
||||||
|
JFXTextField addRow(String labelText, String value, boolean disabled) {
|
||||||
|
Label label = new Label(labelText);
|
||||||
|
JFXTextField textField = new JFXTextField(value);
|
||||||
|
textField.setDisable(disabled);
|
||||||
|
gridPane.addRow(index++, label, textField);
|
||||||
|
return textField;
|
||||||
|
}
|
||||||
|
|
||||||
|
JFXTextField addDoubleRow(String labelText, Double value) {
|
||||||
|
Label label = new Label(labelText);
|
||||||
|
String valueString = null;
|
||||||
|
if (value != null) {
|
||||||
|
valueString = value.toString();
|
||||||
|
}
|
||||||
|
JFXTextField textField = new JFXTextField();
|
||||||
|
TextFormatter<Double> formatter = new TextFormatter<>(
|
||||||
|
new DoubleStringConverter(), null, new DoubleFilter());
|
||||||
|
textField.setTextFormatter(formatter);
|
||||||
|
textField.setText(valueString);
|
||||||
|
gridPane.addRow(index++, label, textField);
|
||||||
|
return textField;
|
||||||
|
}
|
||||||
|
|
||||||
|
Double getDouble(JFXTextField textField) {
|
||||||
|
if (textField.getText() == null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
Double value = null;
|
||||||
|
try {
|
||||||
|
logger.info("double field text: {}", textField.getText());
|
||||||
|
value = Double.parseDouble(textField.getText());
|
||||||
|
} catch (NumberFormatException ex) {
|
||||||
|
logger.error("error getting double value", ex);
|
||||||
|
}
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
|
||||||
|
JFXTextField addIntegerRow(String labelText, Integer value) {
|
||||||
|
Label label = new Label(labelText);
|
||||||
|
String valueString = null;
|
||||||
|
if (value != null) {
|
||||||
|
valueString = value.toString();
|
||||||
|
}
|
||||||
|
JFXTextField textField = new JFXTextField();
|
||||||
|
UnaryOperator<TextFormatter.Change> filter = change -> {
|
||||||
|
String text = change.getText();
|
||||||
|
if (text.matches("[0-9]*")) {
|
||||||
|
return change;
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
};
|
||||||
|
TextFormatter<Integer> formatter = new TextFormatter<>(
|
||||||
|
new IntegerStringConverter(), null, filter);
|
||||||
|
textField.setTextFormatter(formatter);
|
||||||
|
textField.setText(valueString);
|
||||||
|
gridPane.addRow(index++, label, textField);
|
||||||
|
return textField;
|
||||||
|
}
|
||||||
|
|
||||||
|
Integer getInteger(JFXTextField textField) {
|
||||||
|
if (textField.getText() == null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
Integer value = null;
|
||||||
|
try {
|
||||||
|
logger.info("integer field text: {}", textField.getText());
|
||||||
|
value = Integer.parseInt(textField.getText());
|
||||||
|
} catch (NumberFormatException ex) {
|
||||||
|
logger.error("error getting integer value", ex);
|
||||||
|
}
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void addAddress(String label, IPAddress ip) {
|
||||||
|
if (ip == null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
addRow(label, ip.toString(), true);
|
||||||
|
}
|
||||||
|
|
||||||
|
void clear() {
|
||||||
|
if (gridPane.getChildren().size() > START_INDEX) {
|
||||||
|
gridPane.getChildren().remove(START_INDEX, gridPane.getChildren().size());
|
||||||
|
}
|
||||||
|
index = START_INDEX;
|
||||||
|
}
|
||||||
|
}
|
|
@ -40,6 +40,7 @@ public class GraphToolbar extends VBox {
|
||||||
private SVGGlyph stopIcon;
|
private SVGGlyph stopIcon;
|
||||||
private JFXListView<Label> nodesList = new JFXListView<>();
|
private JFXListView<Label> nodesList = new JFXListView<>();
|
||||||
private JFXListView<Label> devicesList = new JFXListView<>();
|
private JFXListView<Label> devicesList = new JFXListView<>();
|
||||||
|
private JFXListView<Label> containersList = new JFXListView<>();
|
||||||
private JFXButton selectedEditButton;
|
private JFXButton selectedEditButton;
|
||||||
private NodeType selectedNodeType;
|
private NodeType selectedNodeType;
|
||||||
private boolean isEditing = false;
|
private boolean isEditing = false;
|
||||||
|
@ -49,6 +50,7 @@ public class GraphToolbar extends VBox {
|
||||||
@FXML private ComboBox<String> graphModeCombo;
|
@FXML private ComboBox<String> graphModeCombo;
|
||||||
@FXML private JFXButton nodesButton;
|
@FXML private JFXButton nodesButton;
|
||||||
@FXML private JFXButton devicesButton;
|
@FXML private JFXButton devicesButton;
|
||||||
|
@FXML private JFXButton containersButton;
|
||||||
|
|
||||||
public GraphToolbar(Controller controller) {
|
public GraphToolbar(Controller controller) {
|
||||||
this.controller = controller;
|
this.controller = controller;
|
||||||
|
@ -63,6 +65,7 @@ public class GraphToolbar extends VBox {
|
||||||
setupDrawingButton();
|
setupDrawingButton();
|
||||||
setupNodesButton();
|
setupNodesButton();
|
||||||
setupDevicesButton();
|
setupDevicesButton();
|
||||||
|
setupContainersButton();
|
||||||
|
|
||||||
// initial state
|
// initial state
|
||||||
setSelected(true, pickingButton);
|
setSelected(true, pickingButton);
|
||||||
|
@ -113,28 +116,20 @@ public class GraphToolbar extends VBox {
|
||||||
|
|
||||||
public void setupNodeTypes() {
|
public void setupNodeTypes() {
|
||||||
// clear existing configuration
|
// clear existing configuration
|
||||||
labelMap.clear();
|
for (Label label : nodesList.getItems()) {
|
||||||
|
int id = (int) label.getUserData();
|
||||||
|
labelMap.remove(id);
|
||||||
|
}
|
||||||
nodesList.getItems().clear();
|
nodesList.getItems().clear();
|
||||||
devicesList.getItems().clear();
|
|
||||||
|
|
||||||
|
// add current default nodes
|
||||||
for (NodeType nodeType : NodeType.getAll()) {
|
for (NodeType nodeType : NodeType.getAll()) {
|
||||||
ImageView icon = new ImageView(nodeType.getIcon());
|
if (NodeType.DEFAULT == nodeType.getValue()) {
|
||||||
icon.setFitWidth(NODES_ICON_SIZE);
|
addNodeType(nodeType.getValue(), nodeType.getModel(), nodesList);
|
||||||
icon.setFitHeight(NODES_ICON_SIZE);
|
|
||||||
Label label = new Label(nodeType.getDisplay(), icon);
|
|
||||||
label.setUserData(nodeType.getId());
|
|
||||||
labelMap.put(nodeType.getId(), label);
|
|
||||||
|
|
||||||
if (nodeType.getValue() == NodeType.DEFAULT) {
|
|
||||||
nodesList.getItems().add(label);
|
|
||||||
} else {
|
|
||||||
devicesList.getItems().add(label);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Comparator<Label> comparator = Comparator.comparing(Label::getText);
|
Comparator<Label> comparator = Comparator.comparing(Label::getText);
|
||||||
nodesList.getItems().sort(comparator);
|
nodesList.getItems().sort(comparator);
|
||||||
devicesList.getItems().sort(comparator);
|
|
||||||
|
|
||||||
// initial node
|
// initial node
|
||||||
nodesList.getSelectionModel().selectFirst();
|
nodesList.getSelectionModel().selectFirst();
|
||||||
|
@ -143,9 +138,6 @@ public class GraphToolbar extends VBox {
|
||||||
selectedEditButton = nodesButton;
|
selectedEditButton = nodesButton;
|
||||||
controller.getNetworkGraph().setNodeType(selectedNodeType);
|
controller.getNetworkGraph().setNodeType(selectedNodeType);
|
||||||
updateButtonValues(nodesButton, selectedNodeLabel);
|
updateButtonValues(nodesButton, selectedNodeLabel);
|
||||||
|
|
||||||
// initial device
|
|
||||||
updateButtonValues(devicesButton, devicesList.getItems().get(0));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void updateButtonValues(JFXButton button, Label label) {
|
private void updateButtonValues(JFXButton button, Label label) {
|
||||||
|
@ -167,49 +159,54 @@ public class GraphToolbar extends VBox {
|
||||||
}
|
}
|
||||||
|
|
||||||
private void setupNodesButton() {
|
private void setupNodesButton() {
|
||||||
nodesButton.setTooltip(new Tooltip("Network Nodes (host, pc, etc)"));
|
setupButtonSelection("CORE Nodes", nodesButton, nodesList);
|
||||||
nodesList.setOnMouseClicked(event -> {
|
}
|
||||||
Label selectedLabel = nodesList.getSelectionModel().getSelectedItem();
|
|
||||||
|
private void setupDevicesButton() {
|
||||||
|
addNodeType(NodeType.HUB, "hub", devicesList);
|
||||||
|
addNodeType(NodeType.SWITCH, "lanswitch", devicesList);
|
||||||
|
addNodeType(NodeType.WLAN, "wlan", devicesList);
|
||||||
|
addNodeType(NodeType.EMANE, "wlan", devicesList);
|
||||||
|
addNodeType(NodeType.RJ45, "rj45", devicesList);
|
||||||
|
devicesList.getSelectionModel().selectFirst();
|
||||||
|
updateButtonValues(devicesButton, devicesList.getSelectionModel().getSelectedItem());
|
||||||
|
setupButtonSelection("Network Nodes", devicesButton, devicesList);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void setupContainersButton() {
|
||||||
|
addNodeType(NodeType.DOCKER, null, containersList);
|
||||||
|
addNodeType(NodeType.LXC, null, containersList);
|
||||||
|
containersList.getSelectionModel().selectFirst();
|
||||||
|
updateButtonValues(containersButton, containersList.getSelectionModel().getSelectedItem());
|
||||||
|
setupButtonSelection("Container Nodes", containersButton, containersList);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void addNodeType(int type, String model, JFXListView<Label> list) {
|
||||||
|
NodeType nodeType = NodeType.find(type, model);
|
||||||
|
Label label = nodeType.createLabel(NODES_ICON_SIZE);
|
||||||
|
labelMap.put(nodeType.getId(), label);
|
||||||
|
list.getItems().add(label);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void setupButtonSelection(String tooltipText, JFXButton button, JFXListView<Label> list) {
|
||||||
|
button.setTooltip(new Tooltip(tooltipText));
|
||||||
|
list.setOnMouseClicked(event -> {
|
||||||
|
Label selectedLabel = list.getSelectionModel().getSelectedItem();
|
||||||
if (selectedLabel == null) {
|
if (selectedLabel == null) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
updateButtonValues(nodesButton, selectedLabel);
|
updateButtonValues(button, selectedLabel);
|
||||||
selectedNodeType = NodeType.get((int) selectedLabel.getUserData());
|
selectedNodeType = NodeType.get((int) selectedLabel.getUserData());
|
||||||
logger.info("selected node type: {}", selectedNodeType);
|
|
||||||
setSelectedEditButton(nodesButton);
|
|
||||||
devicesList.getSelectionModel().clearSelection();
|
|
||||||
controller.getNetworkGraph().setNodeType(selectedNodeType);
|
controller.getNetworkGraph().setNodeType(selectedNodeType);
|
||||||
|
setSelectedEditButton(button);
|
||||||
logger.info("node selected: {} - type: {}", selectedLabel, selectedNodeType);
|
logger.info("node selected: {} - type: {}", selectedLabel, selectedNodeType);
|
||||||
setEditMode();
|
setEditMode();
|
||||||
});
|
});
|
||||||
|
|
||||||
JFXPopup popup = new JFXPopup(nodesList);
|
JFXPopup popup = new JFXPopup(list);
|
||||||
nodesButton.setOnAction(event -> popup.show(nodesButton, JFXPopup.PopupVPosition.TOP,
|
button.setOnAction(event -> popup.show(button, JFXPopup.PopupVPosition.TOP,
|
||||||
JFXPopup.PopupHPosition.LEFT, nodesButton.getWidth(), 0));
|
JFXPopup.PopupHPosition.LEFT, button.getWidth(), 0));
|
||||||
}
|
|
||||||
|
|
||||||
private void setupDevicesButton() {
|
|
||||||
devicesButton.setTooltip(new Tooltip("Device Nodes (WLAN, EMANE, Switch, etc)"));
|
|
||||||
devicesList.setOnMouseClicked(event -> {
|
|
||||||
Label selectedLabel = devicesList.getSelectionModel().getSelectedItem();
|
|
||||||
if (selectedLabel == null) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
updateButtonValues(devicesButton, selectedLabel);
|
|
||||||
selectedNodeType = NodeType.get((int) selectedLabel.getUserData());
|
|
||||||
logger.info("selected node type: {}", selectedNodeType);
|
|
||||||
controller.getNetworkGraph().setNodeType(selectedNodeType);
|
|
||||||
setSelectedEditButton(devicesButton);
|
|
||||||
nodesList.getSelectionModel().clearSelection();
|
|
||||||
logger.info("device selected: {} - type: {}", selectedLabel, selectedNodeType);
|
|
||||||
setEditMode();
|
|
||||||
});
|
|
||||||
|
|
||||||
JFXPopup popup = new JFXPopup(devicesList);
|
|
||||||
devicesButton.setOnAction(event -> popup.show(devicesButton, JFXPopup.PopupVPosition.TOP,
|
|
||||||
JFXPopup.PopupHPosition.LEFT, devicesButton.getWidth(), 0));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@FXML
|
@FXML
|
||||||
|
@ -241,15 +238,11 @@ public class GraphToolbar extends VBox {
|
||||||
private void startSession() {
|
private void startSession() {
|
||||||
runButton.setDisable(true);
|
runButton.setDisable(true);
|
||||||
new Thread(() -> {
|
new Thread(() -> {
|
||||||
try {
|
boolean result = controller.startSession();
|
||||||
boolean result = controller.startSession();
|
if (result) {
|
||||||
if (result) {
|
Toast.success("Session Started");
|
||||||
Toast.success("Session Started");
|
|
||||||
setRunButton(true);
|
|
||||||
}
|
|
||||||
} catch (IOException ex) {
|
|
||||||
Toast.error("Failure Starting Session", ex);
|
|
||||||
}
|
}
|
||||||
|
setRunButton(result);
|
||||||
}).start();
|
}).start();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -274,6 +267,7 @@ public class GraphToolbar extends VBox {
|
||||||
pickingButton.fire();
|
pickingButton.fire();
|
||||||
devicesButton.setDisable(true);
|
devicesButton.setDisable(true);
|
||||||
nodesButton.setDisable(true);
|
nodesButton.setDisable(true);
|
||||||
|
containersButton.setDisable(true);
|
||||||
runButton.pseudoClassStateChanged(START_CLASS, false);
|
runButton.pseudoClassStateChanged(START_CLASS, false);
|
||||||
runButton.pseudoClassStateChanged(STOP_CLASS, true);
|
runButton.pseudoClassStateChanged(STOP_CLASS, true);
|
||||||
if (runButton.getGraphic() != stopIcon) {
|
if (runButton.getGraphic() != stopIcon) {
|
||||||
|
@ -285,6 +279,7 @@ public class GraphToolbar extends VBox {
|
||||||
Platform.runLater(() -> {
|
Platform.runLater(() -> {
|
||||||
devicesButton.setDisable(false);
|
devicesButton.setDisable(false);
|
||||||
nodesButton.setDisable(false);
|
nodesButton.setDisable(false);
|
||||||
|
containersButton.setDisable(false);
|
||||||
runButton.pseudoClassStateChanged(START_CLASS, true);
|
runButton.pseudoClassStateChanged(START_CLASS, true);
|
||||||
runButton.pseudoClassStateChanged(STOP_CLASS, false);
|
runButton.pseudoClassStateChanged(STOP_CLASS, false);
|
||||||
if (runButton.getGraphic() != startIcon) {
|
if (runButton.getGraphic() != startIcon) {
|
||||||
|
|
|
@ -7,47 +7,27 @@ import com.core.data.CoreLink;
|
||||||
import com.core.data.CoreLinkOptions;
|
import com.core.data.CoreLinkOptions;
|
||||||
import com.core.data.CoreNode;
|
import com.core.data.CoreNode;
|
||||||
import com.core.graph.NetworkGraph;
|
import com.core.graph.NetworkGraph;
|
||||||
import com.core.ui.textfields.DoubleFilter;
|
|
||||||
import com.core.utils.FxmlUtils;
|
|
||||||
import com.jfoenix.controls.JFXButton;
|
|
||||||
import com.jfoenix.controls.JFXTextField;
|
import com.jfoenix.controls.JFXTextField;
|
||||||
import inet.ipaddr.IPAddress;
|
|
||||||
import javafx.event.ActionEvent;
|
|
||||||
import javafx.event.EventHandler;
|
|
||||||
import javafx.fxml.FXML;
|
|
||||||
import javafx.geometry.Insets;
|
|
||||||
import javafx.geometry.Orientation;
|
|
||||||
import javafx.scene.control.Label;
|
|
||||||
import javafx.scene.control.ScrollPane;
|
|
||||||
import javafx.scene.control.Separator;
|
|
||||||
import javafx.scene.control.TextFormatter;
|
|
||||||
import javafx.scene.layout.GridPane;
|
|
||||||
import javafx.util.converter.DoubleStringConverter;
|
|
||||||
import org.apache.logging.log4j.LogManager;
|
import org.apache.logging.log4j.LogManager;
|
||||||
import org.apache.logging.log4j.Logger;
|
import org.apache.logging.log4j.Logger;
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
|
|
||||||
public class LinkDetails extends ScrollPane {
|
public class LinkDetails extends DetailsPanel {
|
||||||
private static final Logger logger = LogManager.getLogger();
|
private static final Logger logger = LogManager.getLogger();
|
||||||
private static final int START_INDEX = 1;
|
|
||||||
private final Controller controller;
|
|
||||||
private int index = START_INDEX;
|
|
||||||
@FXML private GridPane gridPane;
|
|
||||||
|
|
||||||
public LinkDetails(Controller controller) {
|
public LinkDetails(Controller controller) {
|
||||||
this.controller = controller;
|
super(controller);
|
||||||
FxmlUtils.loadRootController(this, "/fxml/link_details.fxml");
|
|
||||||
setPrefWidth(400);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setLink(CoreLink link) {
|
public void setLink(CoreLink link) {
|
||||||
NetworkGraph graph = controller.getNetworkGraph();
|
NetworkGraph graph = controller.getNetworkGraph();
|
||||||
ICoreClient coreClient = controller.getCoreClient();
|
ICoreClient coreClient = controller.getCoreClient();
|
||||||
|
|
||||||
clear();
|
clear();
|
||||||
|
|
||||||
|
setTitle("Link Details");
|
||||||
addSeparator();
|
addSeparator();
|
||||||
|
|
||||||
CoreNode nodeOne = graph.getVertex(link.getNodeOne());
|
CoreNode nodeOne = graph.getVertex(link.getNodeOne());
|
||||||
CoreInterface interfaceOne = link.getInterfaceOne();
|
CoreInterface interfaceOne = link.getInterfaceOne();
|
||||||
addLabel(nodeOne.getName());
|
addLabel(nodeOne.getName());
|
||||||
|
@ -55,7 +35,6 @@ public class LinkDetails extends ScrollPane {
|
||||||
addInterface(interfaceOne);
|
addInterface(interfaceOne);
|
||||||
}
|
}
|
||||||
|
|
||||||
addSeparator();
|
|
||||||
CoreNode nodeTwo = graph.getVertex(link.getNodeTwo());
|
CoreNode nodeTwo = graph.getVertex(link.getNodeTwo());
|
||||||
CoreInterface interfaceTwo = link.getInterfaceTwo();
|
CoreInterface interfaceTwo = link.getInterfaceTwo();
|
||||||
addLabel(nodeTwo.getName());
|
addLabel(nodeTwo.getName());
|
||||||
|
@ -63,20 +42,19 @@ public class LinkDetails extends ScrollPane {
|
||||||
addInterface(interfaceTwo);
|
addInterface(interfaceTwo);
|
||||||
}
|
}
|
||||||
|
|
||||||
addSeparator();
|
|
||||||
addLabel("Properties");
|
addLabel("Properties");
|
||||||
JFXTextField bandwidthField = addRow("Bandwidth (bps)", link.getOptions().getBandwidth());
|
JFXTextField bandwidthField = addIntegerRow("Bandwidth (bps)", link.getOptions().getBandwidth());
|
||||||
JFXTextField delayField = addRow("Delay (us)", link.getOptions().getDelay());
|
JFXTextField delayField = addIntegerRow("Delay (us)", link.getOptions().getDelay());
|
||||||
JFXTextField jitterField = addRow("Jitter (us)", link.getOptions().getJitter());
|
JFXTextField jitterField = addIntegerRow("Jitter (us)", link.getOptions().getJitter());
|
||||||
JFXTextField lossField = addRow("Loss (%)", link.getOptions().getPer());
|
JFXTextField lossField = addDoubleRow("Loss (%)", link.getOptions().getPer());
|
||||||
JFXTextField dupsField = addRow("Duplicate (%)", link.getOptions().getDup());
|
JFXTextField dupsField = addIntegerRow("Duplicate (%)", link.getOptions().getDup());
|
||||||
addButton("Update", event -> {
|
addButton("Update", event -> {
|
||||||
CoreLinkOptions options = link.getOptions();
|
CoreLinkOptions options = link.getOptions();
|
||||||
options.setBandwidth(getDouble(bandwidthField));
|
options.setBandwidth(getInteger(bandwidthField));
|
||||||
options.setDelay(getDouble(delayField));
|
options.setDelay(getInteger(delayField));
|
||||||
options.setJitter(getDouble(jitterField));
|
options.setJitter(getInteger(jitterField));
|
||||||
options.setPer(getDouble(lossField));
|
options.setPer(getDouble(lossField));
|
||||||
options.setDup(getDouble(dupsField));
|
options.setDup(getInteger(dupsField));
|
||||||
|
|
||||||
if (coreClient.isRunning()) {
|
if (coreClient.isRunning()) {
|
||||||
try {
|
try {
|
||||||
|
@ -88,92 +66,4 @@ public class LinkDetails extends ScrollPane {
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
private Double getDouble(JFXTextField textField) {
|
|
||||||
if (textField.getText() == null) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
Double value = null;
|
|
||||||
try {
|
|
||||||
logger.info("double field text: {}", textField.getText());
|
|
||||||
value = Double.parseDouble(textField.getText());
|
|
||||||
} catch (NumberFormatException ex) {
|
|
||||||
logger.error("error getting double value", ex);
|
|
||||||
}
|
|
||||||
return value;
|
|
||||||
}
|
|
||||||
|
|
||||||
private void addButton(String text, EventHandler<ActionEvent> handler) {
|
|
||||||
JFXButton button = new JFXButton(text);
|
|
||||||
button.getStyleClass().add("core-button");
|
|
||||||
button.setMaxWidth(Double.MAX_VALUE);
|
|
||||||
button.setOnAction(handler);
|
|
||||||
gridPane.add(button, 0, index++, 2, 1);
|
|
||||||
GridPane.setMargin(button, new Insets(10, 0, 0, 0));
|
|
||||||
}
|
|
||||||
|
|
||||||
private void addLabel(String text) {
|
|
||||||
Label label = new Label(text);
|
|
||||||
label.getStyleClass().add("details-label");
|
|
||||||
gridPane.add(label, 0, index++, 2, 1);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void addSeparator() {
|
|
||||||
Separator separator = new Separator(Orientation.HORIZONTAL);
|
|
||||||
gridPane.add(separator, 0, index++, 2, 1);
|
|
||||||
GridPane.setMargin(separator, new Insets(10, 0, 0, 0));
|
|
||||||
}
|
|
||||||
|
|
||||||
private void addInterface(CoreInterface coreInterface) {
|
|
||||||
addRow("Interface", coreInterface.getName(), true);
|
|
||||||
if (coreInterface.getMac() != null) {
|
|
||||||
addRow("MAC", coreInterface.getMac(), true);
|
|
||||||
}
|
|
||||||
addIp4Address(coreInterface.getIp4());
|
|
||||||
addIp6Address(coreInterface.getIp6());
|
|
||||||
}
|
|
||||||
|
|
||||||
private void addRow(String labelText, String value, boolean disabled) {
|
|
||||||
Label label = new Label(labelText);
|
|
||||||
JFXTextField textField = new JFXTextField(value);
|
|
||||||
textField.setDisable(disabled);
|
|
||||||
gridPane.addRow(index++, label, textField);
|
|
||||||
}
|
|
||||||
|
|
||||||
private JFXTextField addRow(String labelText, Double value) {
|
|
||||||
Label label = new Label(labelText);
|
|
||||||
String doubleString = null;
|
|
||||||
if (value != null) {
|
|
||||||
doubleString = value.toString();
|
|
||||||
}
|
|
||||||
JFXTextField textField = new JFXTextField();
|
|
||||||
TextFormatter<Double> formatter = new TextFormatter<>(
|
|
||||||
new DoubleStringConverter(), null, new DoubleFilter());
|
|
||||||
textField.setTextFormatter(formatter);
|
|
||||||
textField.setText(doubleString);
|
|
||||||
gridPane.addRow(index++, label, textField);
|
|
||||||
return textField;
|
|
||||||
}
|
|
||||||
|
|
||||||
private void addIp4Address(IPAddress ip) {
|
|
||||||
if (ip == null) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
addRow("IP4", ip.toString(), true);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void addIp6Address(IPAddress ip) {
|
|
||||||
if (ip == null) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
addRow("IP6", ip.toString(), true);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void clear() {
|
|
||||||
if (gridPane.getChildren().size() > START_INDEX) {
|
|
||||||
gridPane.getChildren().remove(START_INDEX, gridPane.getChildren().size());
|
|
||||||
}
|
|
||||||
index = START_INDEX;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -5,70 +5,56 @@ import com.core.data.CoreInterface;
|
||||||
import com.core.data.CoreLink;
|
import com.core.data.CoreLink;
|
||||||
import com.core.data.CoreNode;
|
import com.core.data.CoreNode;
|
||||||
import com.core.data.NodeType;
|
import com.core.data.NodeType;
|
||||||
import com.core.utils.FxmlUtils;
|
|
||||||
import com.jfoenix.controls.JFXButton;
|
|
||||||
import com.jfoenix.controls.JFXListView;
|
import com.jfoenix.controls.JFXListView;
|
||||||
import com.jfoenix.controls.JFXScrollPane;
|
import com.jfoenix.controls.JFXScrollPane;
|
||||||
import com.jfoenix.controls.JFXTextField;
|
import com.jfoenix.controls.JFXTextField;
|
||||||
import inet.ipaddr.IPAddress;
|
|
||||||
import javafx.event.ActionEvent;
|
|
||||||
import javafx.event.EventHandler;
|
|
||||||
import javafx.fxml.FXML;
|
|
||||||
import javafx.geometry.Insets;
|
|
||||||
import javafx.geometry.Orientation;
|
|
||||||
import javafx.scene.control.Label;
|
|
||||||
import javafx.scene.control.ScrollPane;
|
|
||||||
import javafx.scene.control.Separator;
|
|
||||||
import javafx.scene.layout.GridPane;
|
|
||||||
import org.apache.logging.log4j.LogManager;
|
import org.apache.logging.log4j.LogManager;
|
||||||
import org.apache.logging.log4j.Logger;
|
import org.apache.logging.log4j.Logger;
|
||||||
|
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
|
|
||||||
public class NodeDetails extends ScrollPane {
|
public class NodeDetails extends DetailsPanel {
|
||||||
private static final Logger logger = LogManager.getLogger();
|
private static final Logger logger = LogManager.getLogger();
|
||||||
private static final int START_INDEX = 1;
|
|
||||||
private final Controller controller;
|
|
||||||
@FXML private Label title;
|
|
||||||
@FXML private ScrollPane scrollPane;
|
|
||||||
@FXML private GridPane gridPane;
|
|
||||||
private int index = START_INDEX;
|
|
||||||
|
|
||||||
public NodeDetails(Controller controller) {
|
public NodeDetails(Controller controller) {
|
||||||
this.controller = controller;
|
super(controller);
|
||||||
FxmlUtils.loadRootController(this, "/fxml/node_details.fxml");
|
|
||||||
setPrefWidth(400);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setNode(CoreNode node) {
|
public void setNode(CoreNode node) {
|
||||||
clear();
|
clear();
|
||||||
title.setText(node.getName());
|
boolean sessionRunning = controller.getCoreClient().isRunning();
|
||||||
|
|
||||||
|
setTitle(node.getName());
|
||||||
addSeparator();
|
addSeparator();
|
||||||
|
|
||||||
addLabel("Properties");
|
addLabel("Properties");
|
||||||
|
addRow("ID", node.getId().toString(), true);
|
||||||
if (node.getType() == NodeType.DEFAULT) {
|
if (node.getType() == NodeType.DEFAULT) {
|
||||||
addRow("Model", node.getModel(), true);
|
addRow("Model", node.getModel(), true);
|
||||||
} else {
|
} else {
|
||||||
addRow("Type", node.getNodeType().getDisplay(), true);
|
addRow("Type", node.getNodeType().getDisplay(), true);
|
||||||
}
|
}
|
||||||
|
addRow("X", node.getPosition().getX().toString(), true);
|
||||||
|
addRow("Y", node.getPosition().getY().toString(), true);
|
||||||
|
|
||||||
if (node.getEmane() != null) {
|
if (node.getType() == NodeType.EMANE) {
|
||||||
addRow("EMANE", node.getEmane(), true);
|
addRow("EMANE", node.getEmane(), true);
|
||||||
}
|
}
|
||||||
|
|
||||||
addSeparator();
|
if (node.getType() == NodeType.DOCKER || node.getType() == NodeType.LXC) {
|
||||||
addLabel("Position");
|
setContainerDetails(node, sessionRunning);
|
||||||
if (node.getPosition().getX() != null) {
|
|
||||||
addRow("X", node.getPosition().getX().toString(), true);
|
|
||||||
}
|
|
||||||
if (node.getPosition().getY() != null) {
|
|
||||||
addRow("Y", node.getPosition().getY().toString(), true);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
addSeparator();
|
boolean firstInterface = true;
|
||||||
addLabel("Interfaces");
|
|
||||||
for (CoreLink link : controller.getNetworkGraph().getGraph().getIncidentEdges(node)) {
|
for (CoreLink link : controller.getNetworkGraph().getGraph().getIncidentEdges(node)) {
|
||||||
|
if (firstInterface) {
|
||||||
|
firstInterface = false;
|
||||||
|
addLabel("Interfaces");
|
||||||
|
} else {
|
||||||
|
addSeparator();
|
||||||
|
}
|
||||||
|
|
||||||
CoreNode linkedNode;
|
CoreNode linkedNode;
|
||||||
CoreInterface coreInterface;
|
CoreInterface coreInterface;
|
||||||
if (node.getId().equals(link.getNodeOne())) {
|
if (node.getId().equals(link.getNodeOne())) {
|
||||||
|
@ -83,7 +69,6 @@ public class NodeDetails extends ScrollPane {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
addSeparator();
|
|
||||||
if (linkedNode.getType() == NodeType.EMANE) {
|
if (linkedNode.getType() == NodeType.EMANE) {
|
||||||
String emaneModel = linkedNode.getEmane();
|
String emaneModel = linkedNode.getEmane();
|
||||||
String linkedLabel = String.format("%s - %s", linkedNode.getName(), emaneModel);
|
String linkedLabel = String.format("%s - %s", linkedNode.getName(), emaneModel);
|
||||||
|
@ -109,8 +94,8 @@ public class NodeDetails extends ScrollPane {
|
||||||
if (services.isEmpty()) {
|
if (services.isEmpty()) {
|
||||||
services = controller.getDefaultServices().getOrDefault(node.getModel(), Collections.emptySet());
|
services = controller.getDefaultServices().getOrDefault(node.getModel(), Collections.emptySet());
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!services.isEmpty()) {
|
if (!services.isEmpty()) {
|
||||||
addSeparator();
|
|
||||||
addLabel("Services");
|
addLabel("Services");
|
||||||
JFXListView<String> listView = new JFXListView<>();
|
JFXListView<String> listView = new JFXListView<>();
|
||||||
listView.setMouseTransparent(true);
|
listView.setMouseTransparent(true);
|
||||||
|
@ -122,61 +107,14 @@ public class NodeDetails extends ScrollPane {
|
||||||
JFXScrollPane.smoothScrolling(scrollPane);
|
JFXScrollPane.smoothScrolling(scrollPane);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void addButton(String text, EventHandler<ActionEvent> handler) {
|
private void setContainerDetails(CoreNode node, boolean sessionRunning) {
|
||||||
JFXButton emaneButton = new JFXButton(text);
|
JFXTextField imageField = addRow("Image", node.getImage(), sessionRunning);
|
||||||
emaneButton.getStyleClass().add("core-button");
|
if (!sessionRunning) {
|
||||||
emaneButton.setMaxWidth(Double.MAX_VALUE);
|
addButton("Update", event -> {
|
||||||
emaneButton.setOnAction(handler);
|
if (node.getType() == NodeType.DOCKER || node.getType() == NodeType.LXC) {
|
||||||
gridPane.add(emaneButton, 0, index++, 2, 1);
|
node.setImage(imageField.getText());
|
||||||
}
|
}
|
||||||
|
});
|
||||||
private void addLabel(String text) {
|
|
||||||
Label label = new Label(text);
|
|
||||||
label.getStyleClass().add("details-label");
|
|
||||||
gridPane.add(label, 0, index++, 2, 1);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void addSeparator() {
|
|
||||||
Separator separator = new Separator(Orientation.HORIZONTAL);
|
|
||||||
gridPane.add(separator, 0, index++, 2, 1);
|
|
||||||
GridPane.setMargin(separator, new Insets(10, 0, 0, 0));
|
|
||||||
}
|
|
||||||
|
|
||||||
private void addInterface(CoreInterface coreInterface, CoreNode linkedNode) {
|
|
||||||
addRow("Linked To", linkedNode.getName(), true);
|
|
||||||
addRow("Interface", coreInterface.getName(), true);
|
|
||||||
if (coreInterface.getMac() != null) {
|
|
||||||
addRow("MAC", coreInterface.getMac(), true);
|
|
||||||
}
|
}
|
||||||
addIp4Address(coreInterface.getIp4());
|
|
||||||
addIp6Address(coreInterface.getIp6());
|
|
||||||
}
|
|
||||||
|
|
||||||
private void addRow(String labelText, String value, boolean disabled) {
|
|
||||||
Label label = new Label(labelText);
|
|
||||||
JFXTextField textField = new JFXTextField(value);
|
|
||||||
textField.setDisable(disabled);
|
|
||||||
gridPane.addRow(index++, label, textField);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void addIp4Address(IPAddress ip) {
|
|
||||||
if (ip == null) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
addRow("IP4", ip.toString(), true);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void addIp6Address(IPAddress ip) {
|
|
||||||
if (ip == null) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
addRow("IP6", ip.toString(), true);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void clear() {
|
|
||||||
if (gridPane.getChildren().size() > START_INDEX) {
|
|
||||||
gridPane.getChildren().remove(START_INDEX, gridPane.getChildren().size());
|
|
||||||
}
|
|
||||||
index = START_INDEX;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -129,7 +129,7 @@ public class NodeServicesDialog extends StageDialog {
|
||||||
|
|
||||||
Set<String> nodeServices = node.getServices();
|
Set<String> nodeServices = node.getServices();
|
||||||
if (nodeServices.isEmpty()) {
|
if (nodeServices.isEmpty()) {
|
||||||
nodeServices = getController().getDefaultServices().get(node.getModel());
|
nodeServices = getController().getDefaultServices().getOrDefault(node.getModel(), new HashSet<>());
|
||||||
}
|
}
|
||||||
|
|
||||||
for (List<ServiceItem> items : serviceItemGroups.values()) {
|
for (List<ServiceItem> items : serviceItemGroups.values()) {
|
||||||
|
|
45
corefx/src/main/java/com/core/ui/dialogs/Rj45Dialog.java
Normal file
45
corefx/src/main/java/com/core/ui/dialogs/Rj45Dialog.java
Normal file
|
@ -0,0 +1,45 @@
|
||||||
|
package com.core.ui.dialogs;
|
||||||
|
|
||||||
|
import com.core.Controller;
|
||||||
|
import com.core.data.CoreNode;
|
||||||
|
import com.core.ui.Toast;
|
||||||
|
import com.jfoenix.controls.JFXButton;
|
||||||
|
import com.jfoenix.controls.JFXComboBox;
|
||||||
|
import javafx.fxml.FXML;
|
||||||
|
import javafx.stage.Modality;
|
||||||
|
import org.apache.logging.log4j.LogManager;
|
||||||
|
import org.apache.logging.log4j.Logger;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
public class Rj45Dialog extends StageDialog {
|
||||||
|
private static final Logger logger = LogManager.getLogger();
|
||||||
|
@FXML private JFXComboBox<String> interfacesComboBox;
|
||||||
|
private JFXButton saveButton;
|
||||||
|
|
||||||
|
public Rj45Dialog(Controller controller) {
|
||||||
|
super(controller, "/fxml/rj45_dialog.fxml", 600, 150);
|
||||||
|
setTitle("Select RJ45 Interface");
|
||||||
|
saveButton = createButton("Save");
|
||||||
|
addCancelButton();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void showDialog(CoreNode node) {
|
||||||
|
try {
|
||||||
|
List<String> interfaces = getCoreClient().getInterfaces();
|
||||||
|
logger.info("local interfaces: {}", interfaces);
|
||||||
|
interfacesComboBox.getItems().setAll(interfaces);
|
||||||
|
interfacesComboBox.getSelectionModel().selectFirst();
|
||||||
|
saveButton.setOnAction(event -> {
|
||||||
|
String name = interfacesComboBox.getSelectionModel().getSelectedItem();
|
||||||
|
node.setName(name);
|
||||||
|
getController().getNetworkGraph().getGraphViewer().repaint();
|
||||||
|
close();
|
||||||
|
});
|
||||||
|
show();
|
||||||
|
} catch (IOException ex) {
|
||||||
|
Toast.error("Failed to get RJ45 interfaces", ex);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -28,6 +28,8 @@ import java.io.IOException;
|
||||||
@Data
|
@Data
|
||||||
public class StageDialog {
|
public class StageDialog {
|
||||||
private static final Logger logger = LogManager.getLogger();
|
private static final Logger logger = LogManager.getLogger();
|
||||||
|
private static final int STAGE_WIDTH = 800;
|
||||||
|
private static final int STAGE_HEIGHT = 600;
|
||||||
private final Controller controller;
|
private final Controller controller;
|
||||||
private final Stage stage = new Stage(StageStyle.DECORATED);
|
private final Stage stage = new Stage(StageStyle.DECORATED);
|
||||||
private final Scene scene;
|
private final Scene scene;
|
||||||
|
@ -35,10 +37,18 @@ public class StageDialog {
|
||||||
private final HBox buttonBar = new HBox();
|
private final HBox buttonBar = new HBox();
|
||||||
|
|
||||||
public StageDialog(Controller controller, String fxmlPath) {
|
public StageDialog(Controller controller, String fxmlPath) {
|
||||||
this(controller, fxmlPath, Modality.APPLICATION_MODAL);
|
this(controller, fxmlPath, Modality.APPLICATION_MODAL, STAGE_WIDTH, STAGE_HEIGHT);
|
||||||
|
}
|
||||||
|
|
||||||
|
public StageDialog(Controller controller, String fxmlPath, int width, int height) {
|
||||||
|
this(controller, fxmlPath, Modality.APPLICATION_MODAL, width, height);
|
||||||
}
|
}
|
||||||
|
|
||||||
public StageDialog(Controller controller, String fxmlPath, Modality modality) {
|
public StageDialog(Controller controller, String fxmlPath, Modality modality) {
|
||||||
|
this(controller, fxmlPath, modality, STAGE_WIDTH, STAGE_HEIGHT);
|
||||||
|
}
|
||||||
|
|
||||||
|
public StageDialog(Controller controller, String fxmlPath, Modality modality, int width, int height) {
|
||||||
this.controller = controller;
|
this.controller = controller;
|
||||||
|
|
||||||
JFXDecorator decorator = new JFXDecorator(stage, gridPane);
|
JFXDecorator decorator = new JFXDecorator(stage, gridPane);
|
||||||
|
@ -49,8 +59,8 @@ public class StageDialog {
|
||||||
scene = new Scene(decorator);
|
scene = new Scene(decorator);
|
||||||
stage.setScene(scene);
|
stage.setScene(scene);
|
||||||
|
|
||||||
stage.setWidth(800);
|
stage.setWidth(width);
|
||||||
stage.setHeight(600);
|
stage.setHeight(height);
|
||||||
scene.setOnKeyPressed(event -> {
|
scene.setOnKeyPressed(event -> {
|
||||||
if (KeyCode.ESCAPE == event.getCode()) {
|
if (KeyCode.ESCAPE == event.getCode()) {
|
||||||
stage.close();
|
stage.close();
|
||||||
|
|
|
@ -19,13 +19,16 @@
|
||||||
<RowConstraints minHeight="10.0" prefHeight="30.0" vgrow="SOMETIMES" />
|
<RowConstraints minHeight="10.0" prefHeight="30.0" vgrow="SOMETIMES" />
|
||||||
</rowConstraints>
|
</rowConstraints>
|
||||||
<padding>
|
<padding>
|
||||||
<Insets bottom="10.0" left="10.0" right="10.0" top="10.0" />
|
<Insets bottom="5.0" left="10.0" right="10.0" top="5.0" />
|
||||||
</padding>
|
</padding>
|
||||||
<children>
|
<children>
|
||||||
<Label fx:id="title" styleClass="details-title" text="Node Details" GridPane.columnSpan="2147483647">
|
<Label fx:id="title" styleClass="details-title" text="Node Details" GridPane.columnSpan="2147483647">
|
||||||
<font>
|
<font>
|
||||||
<Font name="System Bold" size="17.0" />
|
<Font name="System Bold" size="17.0" />
|
||||||
</font>
|
</font>
|
||||||
|
<GridPane.margin>
|
||||||
|
<Insets />
|
||||||
|
</GridPane.margin>
|
||||||
</Label>
|
</Label>
|
||||||
</children>
|
</children>
|
||||||
</GridPane>
|
</GridPane>
|
|
@ -10,6 +10,7 @@
|
||||||
<JFXButton fx:id="pickingButton" styleClass="toolbar-button" />
|
<JFXButton fx:id="pickingButton" styleClass="toolbar-button" />
|
||||||
<JFXButton fx:id="nodesButton" styleClass="toolbar-button" text="Nodes" />
|
<JFXButton fx:id="nodesButton" styleClass="toolbar-button" text="Nodes" />
|
||||||
<JFXButton fx:id="devicesButton" styleClass="toolbar-button" text="Devices" />
|
<JFXButton fx:id="devicesButton" styleClass="toolbar-button" text="Devices" />
|
||||||
|
<JFXButton fx:id="containersButton" styleClass="toolbar-button" text="Containers" />
|
||||||
<JFXButton fx:id="drawingButton" styleClass="toolbar-button" />
|
<JFXButton fx:id="drawingButton" styleClass="toolbar-button" />
|
||||||
</children>
|
</children>
|
||||||
<padding>
|
<padding>
|
||||||
|
|
|
@ -1,34 +0,0 @@
|
||||||
<?xml version="1.0" encoding="UTF-8"?>
|
|
||||||
|
|
||||||
<?import javafx.geometry.Insets?>
|
|
||||||
<?import javafx.scene.control.Label?>
|
|
||||||
<?import javafx.scene.control.ScrollPane?>
|
|
||||||
<?import javafx.scene.layout.AnchorPane?>
|
|
||||||
<?import javafx.scene.layout.ColumnConstraints?>
|
|
||||||
<?import javafx.scene.layout.GridPane?>
|
|
||||||
<?import javafx.scene.layout.RowConstraints?>
|
|
||||||
<?import javafx.scene.text.Font?>
|
|
||||||
|
|
||||||
<fx:root fitToWidth="true" prefHeight="418.0" prefWidth="300.0" type="ScrollPane" xmlns="http://javafx.com/javafx/8.0.171" xmlns:fx="http://javafx.com/fxml/1">
|
|
||||||
<content>
|
|
||||||
<GridPane fx:id="gridPane" hgap="5.0" prefWidth="300.0" vgap="5.0" AnchorPane.bottomAnchor="0.0" AnchorPane.leftAnchor="0.0" AnchorPane.rightAnchor="0.0" AnchorPane.topAnchor="0.0">
|
|
||||||
<columnConstraints>
|
|
||||||
<ColumnConstraints hgrow="SOMETIMES" minWidth="10.0" prefWidth="100.0" />
|
|
||||||
<ColumnConstraints hgrow="SOMETIMES" minWidth="10.0" prefWidth="100.0" />
|
|
||||||
</columnConstraints>
|
|
||||||
<rowConstraints>
|
|
||||||
<RowConstraints minHeight="10.0" prefHeight="30.0" vgrow="SOMETIMES" />
|
|
||||||
</rowConstraints>
|
|
||||||
<padding>
|
|
||||||
<Insets bottom="10.0" left="10.0" right="10.0" top="10.0" />
|
|
||||||
</padding>
|
|
||||||
<children>
|
|
||||||
<Label styleClass="details-title" text="Link Details" GridPane.columnSpan="2147483647">
|
|
||||||
<font>
|
|
||||||
<Font name="System Bold" size="17.0" />
|
|
||||||
</font>
|
|
||||||
</Label>
|
|
||||||
</children>
|
|
||||||
</GridPane>
|
|
||||||
</content>
|
|
||||||
</fx:root>
|
|
30
corefx/src/main/resources/fxml/rj45_dialog.fxml
Normal file
30
corefx/src/main/resources/fxml/rj45_dialog.fxml
Normal file
|
@ -0,0 +1,30 @@
|
||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
|
||||||
|
<?import com.jfoenix.controls.JFXComboBox?>
|
||||||
|
<?import javafx.geometry.Insets?>
|
||||||
|
<?import javafx.scene.control.Label?>
|
||||||
|
<?import javafx.scene.layout.ColumnConstraints?>
|
||||||
|
<?import javafx.scene.layout.GridPane?>
|
||||||
|
<?import javafx.scene.layout.RowConstraints?>
|
||||||
|
<?import javafx.scene.layout.VBox?>
|
||||||
|
|
||||||
|
<VBox maxHeight="1.7976931348623157E308" maxWidth="1.7976931348623157E308" xmlns="http://javafx.com/javafx/8.0.171" xmlns:fx="http://javafx.com/fxml/1">
|
||||||
|
<children>
|
||||||
|
<GridPane hgap="10.0" VBox.vgrow="ALWAYS">
|
||||||
|
<columnConstraints>
|
||||||
|
<ColumnConstraints hgrow="SOMETIMES" minWidth="10.0" percentWidth="30.0" prefWidth="100.0" />
|
||||||
|
<ColumnConstraints hgrow="SOMETIMES" minWidth="10.0" percentWidth="70.0" prefWidth="100.0" />
|
||||||
|
</columnConstraints>
|
||||||
|
<rowConstraints>
|
||||||
|
<RowConstraints minHeight="10.0" prefHeight="30.0" vgrow="SOMETIMES" />
|
||||||
|
</rowConstraints>
|
||||||
|
<children>
|
||||||
|
<Label text="Interface" />
|
||||||
|
<JFXComboBox fx:id="interfacesComboBox" maxWidth="1.7976931348623157E308" GridPane.columnIndex="1" />
|
||||||
|
</children>
|
||||||
|
<VBox.margin>
|
||||||
|
<Insets bottom="5.0" left="5.0" right="5.0" top="5.0" />
|
||||||
|
</VBox.margin>
|
||||||
|
</GridPane>
|
||||||
|
</children>
|
||||||
|
</VBox>
|
BIN
corefx/src/main/resources/icons/dockernode-100.png
Normal file
BIN
corefx/src/main/resources/icons/dockernode-100.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 362 B |
BIN
corefx/src/main/resources/icons/lxcnode-100.png
Normal file
BIN
corefx/src/main/resources/icons/lxcnode-100.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 394 B |
BIN
corefx/src/main/resources/icons/rj45-80.png
Normal file
BIN
corefx/src/main/resources/icons/rj45-80.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 162 B |
23
daemon/.pre-commit-config.yaml
Normal file
23
daemon/.pre-commit-config.yaml
Normal file
|
@ -0,0 +1,23 @@
|
||||||
|
repos:
|
||||||
|
- repo: local
|
||||||
|
hooks:
|
||||||
|
- id: isort
|
||||||
|
name: isort
|
||||||
|
stages: [commit]
|
||||||
|
language: system
|
||||||
|
entry: bash -c 'cd daemon && pipenv run isort --atomic -y'
|
||||||
|
types: [python]
|
||||||
|
|
||||||
|
- id: black
|
||||||
|
name: black
|
||||||
|
stages: [commit]
|
||||||
|
language: system
|
||||||
|
entry: bash -c 'cd daemon && pipenv run black --exclude ".+_pb2.*.py|doc|build|utm\.py" .'
|
||||||
|
types: [python]
|
||||||
|
|
||||||
|
- id: flake8
|
||||||
|
name: flake8
|
||||||
|
stages: [commit]
|
||||||
|
language: system
|
||||||
|
entry: bash -c 'cd daemon && pipenv run flake8'
|
||||||
|
types: [python]
|
20
daemon/Pipfile
Normal file
20
daemon/Pipfile
Normal file
|
@ -0,0 +1,20 @@
|
||||||
|
[[source]]
|
||||||
|
name = "pypi"
|
||||||
|
url = "https://pypi.org/simple"
|
||||||
|
verify_ssl = true
|
||||||
|
|
||||||
|
[scripts]
|
||||||
|
coredev = "python scripts/core-daemon -f data/core.conf -l data/logging.conf"
|
||||||
|
coretest = "python -m pytest -v tests"
|
||||||
|
|
||||||
|
[dev-packages]
|
||||||
|
grpcio-tools = "*"
|
||||||
|
isort = "*"
|
||||||
|
pre-commit = "*"
|
||||||
|
flake8 = "*"
|
||||||
|
black = "==19.3b0"
|
||||||
|
pytest = "*"
|
||||||
|
mock = "*"
|
||||||
|
|
||||||
|
[packages]
|
||||||
|
core = {editable = true,path = "."}
|
448
daemon/Pipfile.lock
generated
Normal file
448
daemon/Pipfile.lock
generated
Normal file
|
@ -0,0 +1,448 @@
|
||||||
|
{
|
||||||
|
"_meta": {
|
||||||
|
"hash": {
|
||||||
|
"sha256": "d702e6eed5a1362bf261543572bbffd2e8a87140b8d8cb07b99fb0d25220a2b5"
|
||||||
|
},
|
||||||
|
"pipfile-spec": 6,
|
||||||
|
"requires": {},
|
||||||
|
"sources": [
|
||||||
|
{
|
||||||
|
"name": "pypi",
|
||||||
|
"url": "https://pypi.org/simple",
|
||||||
|
"verify_ssl": true
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"default": {
|
||||||
|
"configparser": {
|
||||||
|
"hashes": [
|
||||||
|
"sha256:254c1d9c79f60c45dfde850850883d5aaa7f19a23f13561243a050d5a7c3fe4c",
|
||||||
|
"sha256:c7d282687a5308319bf3d2e7706e575c635b0a470342641c93bea0ea3b5331df"
|
||||||
|
],
|
||||||
|
"version": "==4.0.2"
|
||||||
|
},
|
||||||
|
"core": {
|
||||||
|
"editable": true,
|
||||||
|
"path": "."
|
||||||
|
},
|
||||||
|
"enum34": {
|
||||||
|
"hashes": [
|
||||||
|
"sha256:2d81cbbe0e73112bdfe6ef8576f2238f2ba27dd0d55752a776c41d38b7da2850",
|
||||||
|
"sha256:644837f692e5f550741432dd3f223bbb9852018674981b1664e5dc339387588a",
|
||||||
|
"sha256:6bd0f6ad48ec2aa117d3d141940d484deccda84d4fcd884f5c3d93c23ecd8c79",
|
||||||
|
"sha256:8ad8c4783bf61ded74527bffb48ed9b54166685e4230386a9ed9b1279e2df5b1"
|
||||||
|
],
|
||||||
|
"version": "==1.1.6"
|
||||||
|
},
|
||||||
|
"future": {
|
||||||
|
"hashes": [
|
||||||
|
"sha256:67045236dcfd6816dc439556d009594abf643e5eb48992e36beac09c2ca659b8"
|
||||||
|
],
|
||||||
|
"version": "==0.17.1"
|
||||||
|
},
|
||||||
|
"grpcio": {
|
||||||
|
"hashes": [
|
||||||
|
"sha256:1303578092f1f6e4bfbc354c04ac422856c393723d3ffa032fff0f7cb5cfd693",
|
||||||
|
"sha256:229c6b313cd82bec8f979b059d87f03cc1a48939b543fe170b5a9c5cf6a6bc69",
|
||||||
|
"sha256:3cd3d99a8b5568d0d186f9520c16121a0f2a4bcad8e2b9884b76fb88a85a7774",
|
||||||
|
"sha256:41cfb222db358227521f9638a6fbc397f310042a4db5539a19dea01547c621cd",
|
||||||
|
"sha256:43330501660f636fd6547d1e196e395cd1e2c2ae57d62219d6184a668ffebda0",
|
||||||
|
"sha256:45d7a2bd8b4f25a013296683f4140d636cdbb507d94a382ea5029a21e76b1648",
|
||||||
|
"sha256:47dc935658a13b25108823dabd010194ddea9610357c5c1ef1ad7b3f5157ebee",
|
||||||
|
"sha256:480aa7e2b56238badce0b9413a96d5b4c90c3bfbd79eba5a0501e92328d9669e",
|
||||||
|
"sha256:4a0934c8b0f97e1d8c18e76c45afc0d02d33ab03125258179f2ac6c7a13f3626",
|
||||||
|
"sha256:5624dab19e950f99e560400c59d87b685809e4cfcb2c724103f1ab14c06071f7",
|
||||||
|
"sha256:60515b1405bb3dadc55e6ca99429072dad3e736afcf5048db5452df5572231ff",
|
||||||
|
"sha256:610f97ebae742a57d336a69b09a9c7d7de1f62aa54aaa8adc635b38f55ba4382",
|
||||||
|
"sha256:64ea189b2b0859d1f7b411a09185028744d494ef09029630200cc892e366f169",
|
||||||
|
"sha256:686090c6c1e09e4f49585b8508d0a31d58bc3895e4049ea55b197d1381e9f70f",
|
||||||
|
"sha256:7745c365195bb0605e3d47b480a2a4d1baa8a41a5fd0a20de5fa48900e2c886a",
|
||||||
|
"sha256:79491e0d2b77a1c438116bf9e5f9e2e04e78b78524615e2ce453eff62db59a09",
|
||||||
|
"sha256:825177dd4c601c487836b7d6b4ba268db59787157911c623ba59a7c03c8d3adc",
|
||||||
|
"sha256:8a060e1f72fb94eee8a035ed29f1201ce903ad14cbe27bda56b4a22a8abda045",
|
||||||
|
"sha256:90168cc6353e2766e47b650c963f21cfff294654b10b3a14c67e26a4e3683634",
|
||||||
|
"sha256:94b7742734bceeff6d8db5edb31ac844cb68fc7f13617eca859ff1b78bb20ba1",
|
||||||
|
"sha256:962aebf2dd01bbb2cdb64580e61760f1afc470781f9ecd5fe8f3d8dcd8cf4556",
|
||||||
|
"sha256:9c8d9eacdce840b72eee7924c752c31b675f8aec74790e08cff184a4ea8aa9c1",
|
||||||
|
"sha256:af5b929debc336f6bab9b0da6915f9ee5e41444012aed6a79a3c7e80d7662fdf",
|
||||||
|
"sha256:b9cdb87fc77e9a3eabdc42a512368538d648fa0760ad30cf97788076985c790a",
|
||||||
|
"sha256:c5e6380b90b389454669dc67d0a39fb4dc166416e01308fcddd694236b8329ef",
|
||||||
|
"sha256:d60c90fe2bfbee735397bf75a2f2c4e70c5deab51cd40c6e4fa98fae018c8db6",
|
||||||
|
"sha256:d8582c8b1b1063249da1588854251d8a91df1e210a328aeb0ece39da2b2b763b",
|
||||||
|
"sha256:ddbf86ba3aa0ad8fed2867910d2913ee237d55920b55f1d619049b3399f04efc",
|
||||||
|
"sha256:e46bc0664c5c8a0545857aa7a096289f8db148e7f9cca2d0b760113e8994bddc",
|
||||||
|
"sha256:f6437f70ec7fed0ca3a0eef1146591bb754b418bb6c6b21db74f0333d624e135",
|
||||||
|
"sha256:f71693c3396530c6b00773b029ea85e59272557e9bd6077195a6593e4229892a",
|
||||||
|
"sha256:f79f7455f8fbd43e8e9d61914ecf7f48ba1c8e271801996fef8d6a8f3cc9f39f"
|
||||||
|
],
|
||||||
|
"version": "==1.23.0"
|
||||||
|
},
|
||||||
|
"lxml": {
|
||||||
|
"hashes": [
|
||||||
|
"sha256:02ca7bf899da57084041bb0f6095333e4d239948ad3169443f454add9f4e9cb4",
|
||||||
|
"sha256:096b82c5e0ea27ce9138bcbb205313343ee66a6e132f25c5ed67e2c8d960a1bc",
|
||||||
|
"sha256:0a920ff98cf1aac310470c644bc23b326402d3ef667ddafecb024e1713d485f1",
|
||||||
|
"sha256:17cae1730a782858a6e2758fd20dd0ef7567916c47757b694a06ffafdec20046",
|
||||||
|
"sha256:17e3950add54c882e032527795c625929613adbd2ce5162b94667334458b5a36",
|
||||||
|
"sha256:1f4f214337f6ee5825bf90a65d04d70aab05526c08191ab888cb5149501923c5",
|
||||||
|
"sha256:2e8f77db25b0a96af679e64ff9bf9dddb27d379c9900c3272f3041c4d1327c9d",
|
||||||
|
"sha256:4dffd405390a45ecb95ab5ab1c1b847553c18b0ef8ed01e10c1c8b1a76452916",
|
||||||
|
"sha256:6b899931a5648862c7b88c795eddff7588fb585e81cecce20f8d9da16eff96e0",
|
||||||
|
"sha256:726c17f3e0d7a7200718c9a890ccfeab391c9133e363a577a44717c85c71db27",
|
||||||
|
"sha256:760c12276fee05c36f95f8040180abc7fbebb9e5011447a97cdc289b5d6ab6fc",
|
||||||
|
"sha256:796685d3969815a633827c818863ee199440696b0961e200b011d79b9394bbe7",
|
||||||
|
"sha256:891fe897b49abb7db470c55664b198b1095e4943b9f82b7dcab317a19116cd38",
|
||||||
|
"sha256:a471628e20f03dcdfde00770eeaf9c77811f0c331c8805219ca7b87ac17576c5",
|
||||||
|
"sha256:a63b4fd3e2cabdcc9d918ed280bdde3e8e9641e04f3c59a2a3109644a07b9832",
|
||||||
|
"sha256:b0b84408d4eabc6de9dd1e1e0bc63e7731e890c0b378a62443e5741cfd0ae90a",
|
||||||
|
"sha256:be78485e5d5f3684e875dab60f40cddace2f5b2a8f7fede412358ab3214c3a6f",
|
||||||
|
"sha256:c27eaed872185f047bb7f7da2d21a7d8913457678c9a100a50db6da890bc28b9",
|
||||||
|
"sha256:c81cb40bff373ab7a7446d6bbca0190bccc5be3448b47b51d729e37799bb5692",
|
||||||
|
"sha256:d11874b3c33ee441059464711cd365b89fa1a9cf19ae75b0c189b01fbf735b84",
|
||||||
|
"sha256:e9c028b5897901361d81a4718d1db217b716424a0283afe9d6735fe0caf70f79",
|
||||||
|
"sha256:fe489d486cd00b739be826e8c1be188ddb74c7a1ca784d93d06fda882a6a1681"
|
||||||
|
],
|
||||||
|
"version": "==4.4.1"
|
||||||
|
},
|
||||||
|
"six": {
|
||||||
|
"hashes": [
|
||||||
|
"sha256:3350809f0555b11f552448330d0b52d5f24c91a322ea4a15ef22629740f3761c",
|
||||||
|
"sha256:d16a0141ec1a18405cd4ce8b4613101da75da0e9a7aec5bdd4fa804d0e0eba73"
|
||||||
|
],
|
||||||
|
"version": "==1.12.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"develop": {
|
||||||
|
"appdirs": {
|
||||||
|
"hashes": [
|
||||||
|
"sha256:9e5896d1372858f8dd3344faf4e5014d21849c756c8d5701f78f8a103b372d92",
|
||||||
|
"sha256:d8b24664561d0d34ddfaec54636d502d7cea6e29c3eaf68f3df6180863e2166e"
|
||||||
|
],
|
||||||
|
"version": "==1.4.3"
|
||||||
|
},
|
||||||
|
"aspy.yaml": {
|
||||||
|
"hashes": [
|
||||||
|
"sha256:463372c043f70160a9ec950c3f1e4c3a82db5fca01d334b6bc89c7164d744bdc",
|
||||||
|
"sha256:e7c742382eff2caed61f87a39d13f99109088e5e93f04d76eb8d4b28aa143f45"
|
||||||
|
],
|
||||||
|
"version": "==1.3.0"
|
||||||
|
},
|
||||||
|
"atomicwrites": {
|
||||||
|
"hashes": [
|
||||||
|
"sha256:03472c30eb2c5d1ba9227e4c2ca66ab8287fbfbbda3888aa93dc2e28fc6811b4",
|
||||||
|
"sha256:75a9445bac02d8d058d5e1fe689654ba5a6556a1dfd8ce6ec55a0ed79866cfa6"
|
||||||
|
],
|
||||||
|
"version": "==1.3.0"
|
||||||
|
},
|
||||||
|
"attrs": {
|
||||||
|
"hashes": [
|
||||||
|
"sha256:69c0dbf2ed392de1cb5ec704444b08a5ef81680a61cb899dc08127123af36a79",
|
||||||
|
"sha256:f0b870f674851ecbfbbbd364d6b5cbdff9dcedbc7f3f5e18a6891057f21fe399"
|
||||||
|
],
|
||||||
|
"version": "==19.1.0"
|
||||||
|
},
|
||||||
|
"black": {
|
||||||
|
"hashes": [
|
||||||
|
"sha256:09a9dcb7c46ed496a9850b76e4e825d6049ecd38b611f1224857a79bd985a8cf",
|
||||||
|
"sha256:68950ffd4d9169716bcb8719a56c07a2f4485354fec061cdd5910aa07369731c"
|
||||||
|
],
|
||||||
|
"index": "pypi",
|
||||||
|
"version": "==19.3b0"
|
||||||
|
},
|
||||||
|
"cfgv": {
|
||||||
|
"hashes": [
|
||||||
|
"sha256:edb387943b665bf9c434f717bf630fa78aecd53d5900d2e05da6ad6048553144",
|
||||||
|
"sha256:fbd93c9ab0a523bf7daec408f3be2ed99a980e20b2d19b50fc184ca6b820d289"
|
||||||
|
],
|
||||||
|
"version": "==2.0.1"
|
||||||
|
},
|
||||||
|
"click": {
|
||||||
|
"hashes": [
|
||||||
|
"sha256:2335065e6395b9e67ca716de5f7526736bfa6ceead690adf616d925bdc622b13",
|
||||||
|
"sha256:5b94b49521f6456670fdb30cd82a4eca9412788a93fa6dd6df72c94d5a8ff2d7"
|
||||||
|
],
|
||||||
|
"version": "==7.0"
|
||||||
|
},
|
||||||
|
"entrypoints": {
|
||||||
|
"hashes": [
|
||||||
|
"sha256:589f874b313739ad35be6e0cd7efde2a4e9b6fea91edcc34e58ecbb8dbe56d19",
|
||||||
|
"sha256:c70dd71abe5a8c85e55e12c19bd91ccfeec11a6e99044204511f9ed547d48451"
|
||||||
|
],
|
||||||
|
"version": "==0.3"
|
||||||
|
},
|
||||||
|
"flake8": {
|
||||||
|
"hashes": [
|
||||||
|
"sha256:19241c1cbc971b9962473e4438a2ca19749a7dd002dd1a946eaba171b4114548",
|
||||||
|
"sha256:8e9dfa3cecb2400b3738a42c54c3043e821682b9c840b0448c0503f781130696"
|
||||||
|
],
|
||||||
|
"index": "pypi",
|
||||||
|
"version": "==3.7.8"
|
||||||
|
},
|
||||||
|
"grpcio": {
|
||||||
|
"hashes": [
|
||||||
|
"sha256:1303578092f1f6e4bfbc354c04ac422856c393723d3ffa032fff0f7cb5cfd693",
|
||||||
|
"sha256:229c6b313cd82bec8f979b059d87f03cc1a48939b543fe170b5a9c5cf6a6bc69",
|
||||||
|
"sha256:3cd3d99a8b5568d0d186f9520c16121a0f2a4bcad8e2b9884b76fb88a85a7774",
|
||||||
|
"sha256:41cfb222db358227521f9638a6fbc397f310042a4db5539a19dea01547c621cd",
|
||||||
|
"sha256:43330501660f636fd6547d1e196e395cd1e2c2ae57d62219d6184a668ffebda0",
|
||||||
|
"sha256:45d7a2bd8b4f25a013296683f4140d636cdbb507d94a382ea5029a21e76b1648",
|
||||||
|
"sha256:47dc935658a13b25108823dabd010194ddea9610357c5c1ef1ad7b3f5157ebee",
|
||||||
|
"sha256:480aa7e2b56238badce0b9413a96d5b4c90c3bfbd79eba5a0501e92328d9669e",
|
||||||
|
"sha256:4a0934c8b0f97e1d8c18e76c45afc0d02d33ab03125258179f2ac6c7a13f3626",
|
||||||
|
"sha256:5624dab19e950f99e560400c59d87b685809e4cfcb2c724103f1ab14c06071f7",
|
||||||
|
"sha256:60515b1405bb3dadc55e6ca99429072dad3e736afcf5048db5452df5572231ff",
|
||||||
|
"sha256:610f97ebae742a57d336a69b09a9c7d7de1f62aa54aaa8adc635b38f55ba4382",
|
||||||
|
"sha256:64ea189b2b0859d1f7b411a09185028744d494ef09029630200cc892e366f169",
|
||||||
|
"sha256:686090c6c1e09e4f49585b8508d0a31d58bc3895e4049ea55b197d1381e9f70f",
|
||||||
|
"sha256:7745c365195bb0605e3d47b480a2a4d1baa8a41a5fd0a20de5fa48900e2c886a",
|
||||||
|
"sha256:79491e0d2b77a1c438116bf9e5f9e2e04e78b78524615e2ce453eff62db59a09",
|
||||||
|
"sha256:825177dd4c601c487836b7d6b4ba268db59787157911c623ba59a7c03c8d3adc",
|
||||||
|
"sha256:8a060e1f72fb94eee8a035ed29f1201ce903ad14cbe27bda56b4a22a8abda045",
|
||||||
|
"sha256:90168cc6353e2766e47b650c963f21cfff294654b10b3a14c67e26a4e3683634",
|
||||||
|
"sha256:94b7742734bceeff6d8db5edb31ac844cb68fc7f13617eca859ff1b78bb20ba1",
|
||||||
|
"sha256:962aebf2dd01bbb2cdb64580e61760f1afc470781f9ecd5fe8f3d8dcd8cf4556",
|
||||||
|
"sha256:9c8d9eacdce840b72eee7924c752c31b675f8aec74790e08cff184a4ea8aa9c1",
|
||||||
|
"sha256:af5b929debc336f6bab9b0da6915f9ee5e41444012aed6a79a3c7e80d7662fdf",
|
||||||
|
"sha256:b9cdb87fc77e9a3eabdc42a512368538d648fa0760ad30cf97788076985c790a",
|
||||||
|
"sha256:c5e6380b90b389454669dc67d0a39fb4dc166416e01308fcddd694236b8329ef",
|
||||||
|
"sha256:d60c90fe2bfbee735397bf75a2f2c4e70c5deab51cd40c6e4fa98fae018c8db6",
|
||||||
|
"sha256:d8582c8b1b1063249da1588854251d8a91df1e210a328aeb0ece39da2b2b763b",
|
||||||
|
"sha256:ddbf86ba3aa0ad8fed2867910d2913ee237d55920b55f1d619049b3399f04efc",
|
||||||
|
"sha256:e46bc0664c5c8a0545857aa7a096289f8db148e7f9cca2d0b760113e8994bddc",
|
||||||
|
"sha256:f6437f70ec7fed0ca3a0eef1146591bb754b418bb6c6b21db74f0333d624e135",
|
||||||
|
"sha256:f71693c3396530c6b00773b029ea85e59272557e9bd6077195a6593e4229892a",
|
||||||
|
"sha256:f79f7455f8fbd43e8e9d61914ecf7f48ba1c8e271801996fef8d6a8f3cc9f39f"
|
||||||
|
],
|
||||||
|
"version": "==1.23.0"
|
||||||
|
},
|
||||||
|
"grpcio-tools": {
|
||||||
|
"hashes": [
|
||||||
|
"sha256:056f2a274edda4315e825ac2e3a9536f5415b43aa51669196860c8de6e76d847",
|
||||||
|
"sha256:0c953251585fdcd422072e4b7f4243fce215f22e21db94ec83c5970e41db6e18",
|
||||||
|
"sha256:142a73f5769f37bf2e4a8e4a77ef60f7af5f55635f60428322b49c87bd8f9cc0",
|
||||||
|
"sha256:1b333e2a068d8ef89a01eb23a098d2a789659c3178de79da9bd3d0ffb944cc6d",
|
||||||
|
"sha256:2124f19cc51d63405a0204ae38ef355732ab0a235975ab41ff6f6f9701905432",
|
||||||
|
"sha256:24c3a04adfb6c6f1bc4a2f8498d7661ca296ae352b498e538832c22ddde7bf81",
|
||||||
|
"sha256:3a2054e9640cbdd0ce8a345afb86be52875c5a8f9f5973a5c64791a8002da2dd",
|
||||||
|
"sha256:3fd15a09eecef83440ac849dcda2ff522f8ee1603ebfcdbb0e9b320ef2012e41",
|
||||||
|
"sha256:457e7a7dfa0b6bb608a766edba6f20c9d626a790df802016b930ad242fec4470",
|
||||||
|
"sha256:49ad5661d54ff0d164e4b441ee5e05191187d497380afa16d36d72eb8ef048de",
|
||||||
|
"sha256:561078e425d21a6720c3c3828385d949e24c0765e2852a46ecc3ad3fca2706e5",
|
||||||
|
"sha256:5a4f65ab06b32dc34112ed114dee3b698c8463670474334ece5b0b531073804c",
|
||||||
|
"sha256:8883e0e34676356d219a4cd37d239c3ead655cc550836236b52068e091416fff",
|
||||||
|
"sha256:8d2b45b1faf81098780e07d6a1c207b328b07e913160b8baa7e2e8d89723e06c",
|
||||||
|
"sha256:b0ebddb6ecc4c161369f93bb3a74c6120a498d3ddc759b64679709a885dd6d4f",
|
||||||
|
"sha256:b786ba4842c50de865dd3885b5570690a743e84a327b7213dd440eb0e6b996f8",
|
||||||
|
"sha256:be8efa010f5a80f1862ead80c3b19b5eb97dc954a0f59a1e2487078576105e03",
|
||||||
|
"sha256:c29106eaff0e2e708a9a89628dc0134ef145d0d3631f0ef421c09f380c30e354",
|
||||||
|
"sha256:c3c71236a056ec961b2b8b3b7c0b3b5a826283bc69c4a1c6415d23b70fea8243",
|
||||||
|
"sha256:cbc35031ec2b29af36947d085a7fbbcd8b79b84d563adf6156103d82565f78db",
|
||||||
|
"sha256:d47307c22744918e803c1eec7263a14f36aaf34fe496bff9ccbcae67c02b40ae",
|
||||||
|
"sha256:db088c98e563c1bb070da5984c8df08b45b61e4d9c6d2a8a1ffeed2af89fd1f3",
|
||||||
|
"sha256:df4dd1cb670062abdacc1fbce41cae4e08a4a212d28dd94fdbbf90615d027f73",
|
||||||
|
"sha256:e3adcf1499ca08d1e036ff44aedf55ed78653d946f4c4426b6e72ab757cc4dec",
|
||||||
|
"sha256:e3b3e32e0cda4dc382ec5bed8599dab644e4b3fc66a9ab54eb58248e207880b9",
|
||||||
|
"sha256:ed524195b35304c670755efa1eca579e5c290a66981f97004a5b2c0d12d6897d",
|
||||||
|
"sha256:edb42432790b1f8ec9f08faf9326d7e5dfe6e1d8c8fe4db39abc0a49c1c76537",
|
||||||
|
"sha256:eff1f995e5aa4cc941b6bbc45b5b57842f8f62bbe1a99427281c2c70cf42312c",
|
||||||
|
"sha256:f2fcdc2669662d77b400f80e20315a3661466e3cb3df1730f8083f9e49465cbc",
|
||||||
|
"sha256:f52ec9926daf48f41389d39d01570967b99c7dbc12bffc134cc3a3c5b5540ba2",
|
||||||
|
"sha256:fd007d67fdfbd2a13bf8a8c8ced8353b42a92ca72dbee54e951d8ddbc6ca12bc",
|
||||||
|
"sha256:ff9045e928dbb7943ea8559bfabebee95a43a830e00bf52c16202d2d805780fb"
|
||||||
|
],
|
||||||
|
"index": "pypi",
|
||||||
|
"version": "==1.23.0"
|
||||||
|
},
|
||||||
|
"identify": {
|
||||||
|
"hashes": [
|
||||||
|
"sha256:4f1fe9a59df4e80fcb0213086fcf502bc1765a01ea4fe8be48da3b65afd2a017",
|
||||||
|
"sha256:d8919589bd2a5f99c66302fec0ef9027b12ae150b0b0213999ad3f695fc7296e"
|
||||||
|
],
|
||||||
|
"version": "==1.4.7"
|
||||||
|
},
|
||||||
|
"importlib-metadata": {
|
||||||
|
"hashes": [
|
||||||
|
"sha256:652234b6ab8f2506ae58e528b6fbcc668831d3cc758e1bc01ef438d328b68cdb",
|
||||||
|
"sha256:6f264986fb88042bc1f0535fa9a557e6a376cfe5679dc77caac7fe8b5d43d05f"
|
||||||
|
],
|
||||||
|
"markers": "python_version < '3.8'",
|
||||||
|
"version": "==0.22"
|
||||||
|
},
|
||||||
|
"importlib-resources": {
|
||||||
|
"hashes": [
|
||||||
|
"sha256:6e2783b2538bd5a14678284a3962b0660c715e5a0f10243fd5e00a4b5974f50b",
|
||||||
|
"sha256:d3279fd0f6f847cced9f7acc19bd3e5df54d34f93a2e7bb5f238f81545787078"
|
||||||
|
],
|
||||||
|
"markers": "python_version < '3.7'",
|
||||||
|
"version": "==1.0.2"
|
||||||
|
},
|
||||||
|
"isort": {
|
||||||
|
"hashes": [
|
||||||
|
"sha256:54da7e92468955c4fceacd0c86bd0ec997b0e1ee80d97f67c35a78b719dccab1",
|
||||||
|
"sha256:6e811fcb295968434526407adb8796944f1988c5b65e8139058f2014cbe100fd"
|
||||||
|
],
|
||||||
|
"index": "pypi",
|
||||||
|
"version": "==4.3.21"
|
||||||
|
},
|
||||||
|
"mccabe": {
|
||||||
|
"hashes": [
|
||||||
|
"sha256:ab8a6258860da4b6677da4bd2fe5dc2c659cff31b3ee4f7f5d64e79735b80d42",
|
||||||
|
"sha256:dd8d182285a0fe56bace7f45b5e7d1a6ebcbf524e8f3bd87eb0f125271b8831f"
|
||||||
|
],
|
||||||
|
"version": "==0.6.1"
|
||||||
|
},
|
||||||
|
"mock": {
|
||||||
|
"hashes": [
|
||||||
|
"sha256:83657d894c90d5681d62155c82bda9c1187827525880eda8ff5df4ec813437c3",
|
||||||
|
"sha256:d157e52d4e5b938c550f39eb2fd15610db062441a9c2747d3dbfa9298211d0f8"
|
||||||
|
],
|
||||||
|
"index": "pypi",
|
||||||
|
"version": "==3.0.5"
|
||||||
|
},
|
||||||
|
"more-itertools": {
|
||||||
|
"hashes": [
|
||||||
|
"sha256:409cd48d4db7052af495b09dec721011634af3753ae1ef92d2b32f73a745f832",
|
||||||
|
"sha256:92b8c4b06dac4f0611c0729b2f2ede52b2e1bac1ab48f089c7ddc12e26bb60c4"
|
||||||
|
],
|
||||||
|
"version": "==7.2.0"
|
||||||
|
},
|
||||||
|
"nodeenv": {
|
||||||
|
"hashes": [
|
||||||
|
"sha256:ad8259494cf1c9034539f6cced78a1da4840a4b157e23640bc4a0c0546b0cb7a"
|
||||||
|
],
|
||||||
|
"version": "==1.3.3"
|
||||||
|
},
|
||||||
|
"packaging": {
|
||||||
|
"hashes": [
|
||||||
|
"sha256:a7ac867b97fdc07ee80a8058fe4435ccd274ecc3b0ed61d852d7d53055528cf9",
|
||||||
|
"sha256:c491ca87294da7cc01902edbe30a5bc6c4c28172b5138ab4e4aa1b9d7bfaeafe"
|
||||||
|
],
|
||||||
|
"version": "==19.1"
|
||||||
|
},
|
||||||
|
"pluggy": {
|
||||||
|
"hashes": [
|
||||||
|
"sha256:0db4b7601aae1d35b4a033282da476845aa19185c1e6964b25cf324b5e4ec3e6",
|
||||||
|
"sha256:fa5fa1622fa6dd5c030e9cad086fa19ef6a0cf6d7a2d12318e10cb49d6d68f34"
|
||||||
|
],
|
||||||
|
"version": "==0.13.0"
|
||||||
|
},
|
||||||
|
"pre-commit": {
|
||||||
|
"hashes": [
|
||||||
|
"sha256:1d3c0587bda7c4e537a46c27f2c84aa006acc18facf9970bf947df596ce91f3f",
|
||||||
|
"sha256:fa78ff96e8e9ac94c748388597693f18b041a181c94a4f039ad20f45287ba44a"
|
||||||
|
],
|
||||||
|
"index": "pypi",
|
||||||
|
"version": "==1.18.3"
|
||||||
|
},
|
||||||
|
"protobuf": {
|
||||||
|
"hashes": [
|
||||||
|
"sha256:00a1b0b352dc7c809749526d1688a64b62ea400c5b05416f93cfb1b11a036295",
|
||||||
|
"sha256:01acbca2d2c8c3f7f235f1842440adbe01bbc379fa1cbdd80753801432b3fae9",
|
||||||
|
"sha256:0a795bca65987b62d6b8a2d934aa317fd1a4d06a6dd4df36312f5b0ade44a8d9",
|
||||||
|
"sha256:0ec035114213b6d6e7713987a759d762dd94e9f82284515b3b7331f34bfaec7f",
|
||||||
|
"sha256:31b18e1434b4907cb0113e7a372cd4d92c047ce7ba0fa7ea66a404d6388ed2c1",
|
||||||
|
"sha256:32a3abf79b0bef073c70656e86d5bd68a28a1fbb138429912c4fc07b9d426b07",
|
||||||
|
"sha256:55f85b7808766e5e3f526818f5e2aeb5ba2edcc45bcccede46a3ccc19b569cb0",
|
||||||
|
"sha256:64ab9bc971989cbdd648c102a96253fdf0202b0c38f15bd34759a8707bdd5f64",
|
||||||
|
"sha256:64cf847e843a465b6c1ba90fb6c7f7844d54dbe9eb731e86a60981d03f5b2e6e",
|
||||||
|
"sha256:917c8662b585470e8fd42f052661fc66d59fccaae450a60044307dcbf82a3335",
|
||||||
|
"sha256:afed9003d7f2be2c3df20f64220c30faec441073731511728a2cb4cab4cd46a6",
|
||||||
|
"sha256:bf8e05d638b585d1752c5a84247134a0350d3a8b73d3632489a014a9f6f1e758",
|
||||||
|
"sha256:d831b047bd69becaf64019a47179eb22118a50dd008340655266a906c69c6417",
|
||||||
|
"sha256:de2760583ed28749ff885789c1cbc6c9c06d6de92fc825740ab99deb2f25ea4d",
|
||||||
|
"sha256:eabc4cf1bc19689af8022ba52fd668564a8d96e0d08f3b4732d26a64255216a4",
|
||||||
|
"sha256:fcff6086c86fb1628d94ea455c7b9de898afc50378042927a59df8065a79a549"
|
||||||
|
],
|
||||||
|
"version": "==3.9.1"
|
||||||
|
},
|
||||||
|
"py": {
|
||||||
|
"hashes": [
|
||||||
|
"sha256:64f65755aee5b381cea27766a3a147c3f15b9b6b9ac88676de66ba2ae36793fa",
|
||||||
|
"sha256:dc639b046a6e2cff5bbe40194ad65936d6ba360b52b3c3fe1d08a82dd50b5e53"
|
||||||
|
],
|
||||||
|
"version": "==1.8.0"
|
||||||
|
},
|
||||||
|
"pycodestyle": {
|
||||||
|
"hashes": [
|
||||||
|
"sha256:95a2219d12372f05704562a14ec30bc76b05a5b297b21a5dfe3f6fac3491ae56",
|
||||||
|
"sha256:e40a936c9a450ad81df37f549d676d127b1b66000a6c500caa2b085bc0ca976c"
|
||||||
|
],
|
||||||
|
"version": "==2.5.0"
|
||||||
|
},
|
||||||
|
"pyflakes": {
|
||||||
|
"hashes": [
|
||||||
|
"sha256:17dbeb2e3f4d772725c777fabc446d5634d1038f234e77343108ce445ea69ce0",
|
||||||
|
"sha256:d976835886f8c5b31d47970ed689944a0262b5f3afa00a5a7b4dc81e5449f8a2"
|
||||||
|
],
|
||||||
|
"version": "==2.1.1"
|
||||||
|
},
|
||||||
|
"pyparsing": {
|
||||||
|
"hashes": [
|
||||||
|
"sha256:6f98a7b9397e206d78cc01df10131398f1c8b8510a2f4d97d9abd82e1aacdd80",
|
||||||
|
"sha256:d9338df12903bbf5d65a0e4e87c2161968b10d2e489652bb47001d82a9b028b4"
|
||||||
|
],
|
||||||
|
"version": "==2.4.2"
|
||||||
|
},
|
||||||
|
"pytest": {
|
||||||
|
"hashes": [
|
||||||
|
"sha256:95d13143cc14174ca1a01ec68e84d76ba5d9d493ac02716fd9706c949a505210",
|
||||||
|
"sha256:b78fe2881323bd44fd9bd76e5317173d4316577e7b1cddebae9136a4495ec865"
|
||||||
|
],
|
||||||
|
"index": "pypi",
|
||||||
|
"version": "==5.1.2"
|
||||||
|
},
|
||||||
|
"pyyaml": {
|
||||||
|
"hashes": [
|
||||||
|
"sha256:0113bc0ec2ad727182326b61326afa3d1d8280ae1122493553fd6f4397f33df9",
|
||||||
|
"sha256:01adf0b6c6f61bd11af6e10ca52b7d4057dd0be0343eb9283c878cf3af56aee4",
|
||||||
|
"sha256:5124373960b0b3f4aa7df1707e63e9f109b5263eca5976c66e08b1c552d4eaf8",
|
||||||
|
"sha256:5ca4f10adbddae56d824b2c09668e91219bb178a1eee1faa56af6f99f11bf696",
|
||||||
|
"sha256:7907be34ffa3c5a32b60b95f4d95ea25361c951383a894fec31be7252b2b6f34",
|
||||||
|
"sha256:7ec9b2a4ed5cad025c2278a1e6a19c011c80a3caaac804fd2d329e9cc2c287c9",
|
||||||
|
"sha256:87ae4c829bb25b9fe99cf71fbb2140c448f534e24c998cc60f39ae4f94396a73",
|
||||||
|
"sha256:9de9919becc9cc2ff03637872a440195ac4241c80536632fffeb6a1e25a74299",
|
||||||
|
"sha256:a5a85b10e450c66b49f98846937e8cfca1db3127a9d5d1e31ca45c3d0bef4c5b",
|
||||||
|
"sha256:b0997827b4f6a7c286c01c5f60384d218dca4ed7d9efa945c3e1aa623d5709ae",
|
||||||
|
"sha256:b631ef96d3222e62861443cc89d6563ba3eeb816eeb96b2629345ab795e53681",
|
||||||
|
"sha256:bf47c0607522fdbca6c9e817a6e81b08491de50f3766a7a0e6a5be7905961b41",
|
||||||
|
"sha256:f81025eddd0327c7d4cfe9b62cf33190e1e736cc6e97502b3ec425f574b3e7a8"
|
||||||
|
],
|
||||||
|
"version": "==5.1.2"
|
||||||
|
},
|
||||||
|
"six": {
|
||||||
|
"hashes": [
|
||||||
|
"sha256:3350809f0555b11f552448330d0b52d5f24c91a322ea4a15ef22629740f3761c",
|
||||||
|
"sha256:d16a0141ec1a18405cd4ce8b4613101da75da0e9a7aec5bdd4fa804d0e0eba73"
|
||||||
|
],
|
||||||
|
"version": "==1.12.0"
|
||||||
|
},
|
||||||
|
"toml": {
|
||||||
|
"hashes": [
|
||||||
|
"sha256:229f81c57791a41d65e399fc06bf0848bab550a9dfd5ed66df18ce5f05e73d5c",
|
||||||
|
"sha256:235682dd292d5899d361a811df37e04a8828a5b1da3115886b73cf81ebc9100e"
|
||||||
|
],
|
||||||
|
"version": "==0.10.0"
|
||||||
|
},
|
||||||
|
"virtualenv": {
|
||||||
|
"hashes": [
|
||||||
|
"sha256:680af46846662bb38c5504b78bad9ed9e4f3ba2d54f54ba42494fdf94337fe30",
|
||||||
|
"sha256:f78d81b62d3147396ac33fc9d77579ddc42cc2a98dd9ea38886f616b33bc7fb2"
|
||||||
|
],
|
||||||
|
"version": "==16.7.5"
|
||||||
|
},
|
||||||
|
"wcwidth": {
|
||||||
|
"hashes": [
|
||||||
|
"sha256:3df37372226d6e63e1b1e1eda15c594bca98a22d33a23832a90998faa96bc65e",
|
||||||
|
"sha256:f4ebe71925af7b40a864553f761ed559b43544f8f71746c2d756c7fe788ade7c"
|
||||||
|
],
|
||||||
|
"version": "==0.1.7"
|
||||||
|
},
|
||||||
|
"zipp": {
|
||||||
|
"hashes": [
|
||||||
|
"sha256:3718b1cbcd963c7d4c5511a8240812904164b7f381b647143a89d3b98f9bcd8e",
|
||||||
|
"sha256:f06903e9f1f43b12d371004b4ac7b06ab39a44adc747266928ae6debfa7b3335"
|
||||||
|
],
|
||||||
|
"version": "==0.6.0"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -9,14 +9,17 @@ from core import constants
|
||||||
logging.getLogger(__name__).addHandler(logging.NullHandler())
|
logging.getLogger(__name__).addHandler(logging.NullHandler())
|
||||||
|
|
||||||
|
|
||||||
def load_logging_config():
|
def load_logging_config(config_path=None):
|
||||||
"""
|
"""
|
||||||
Load CORE logging configuration file.
|
Load CORE logging configuration file.
|
||||||
|
|
||||||
|
:param str config_path: path to logging config file,
|
||||||
|
when None defaults to /etc/core/logging.conf
|
||||||
:return: nothing
|
:return: nothing
|
||||||
"""
|
"""
|
||||||
log_config_path = os.path.join(constants.CORE_CONF_DIR, "logging.conf")
|
if not config_path:
|
||||||
with open(log_config_path, "r") as log_config_file:
|
config_path = os.path.join(constants.CORE_CONF_DIR, "logging.conf")
|
||||||
|
with open(config_path, "r") as log_config_file:
|
||||||
log_config = json.load(log_config_file)
|
log_config = json.load(log_config_file)
|
||||||
logging.config.dictConfig(log_config)
|
logging.config.dictConfig(log_config)
|
||||||
|
|
||||||
|
@ -28,3 +31,11 @@ class CoreCommandError(subprocess.CalledProcessError):
|
||||||
|
|
||||||
def __str__(self):
|
def __str__(self):
|
||||||
return "Command(%s), Status(%s):\n%s" % (self.cmd, self.returncode, self.output)
|
return "Command(%s), Status(%s):\n%s" % (self.cmd, self.returncode, self.output)
|
||||||
|
|
||||||
|
|
||||||
|
class CoreError(Exception):
|
||||||
|
"""
|
||||||
|
Used for errors when dealing with CoreEmu and Sessions.
|
||||||
|
"""
|
||||||
|
|
||||||
|
pass
|
||||||
|
|
|
@ -10,8 +10,7 @@ from contextlib import contextmanager
|
||||||
|
|
||||||
import grpc
|
import grpc
|
||||||
|
|
||||||
from core.api.grpc import core_pb2
|
from core.api.grpc import core_pb2, core_pb2_grpc
|
||||||
from core.api.grpc import core_pb2_grpc
|
|
||||||
from core.nodes.ipaddress import Ipv4Prefix, Ipv6Prefix, MacAddress
|
from core.nodes.ipaddress import Ipv4Prefix, Ipv6Prefix, MacAddress
|
||||||
|
|
||||||
|
|
||||||
|
@ -99,7 +98,7 @@ class InterfaceHelper(object):
|
||||||
ip4mask=ip4_mask,
|
ip4mask=ip4_mask,
|
||||||
ip6=ip6,
|
ip6=ip6,
|
||||||
ip6mask=ip6_mask,
|
ip6mask=ip6_mask,
|
||||||
mac=str(mac)
|
mac=str(mac),
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
@ -215,7 +214,9 @@ class CoreGrpcClient(object):
|
||||||
:rtype: core_pb2.SetSessionOptionsResponse
|
:rtype: core_pb2.SetSessionOptionsResponse
|
||||||
:raises grpc.RpcError: when session doesn't exist
|
:raises grpc.RpcError: when session doesn't exist
|
||||||
"""
|
"""
|
||||||
request = core_pb2.SetSessionOptionsRequest(session_id=session_id, config=config)
|
request = core_pb2.SetSessionOptionsRequest(
|
||||||
|
session_id=session_id, config=config
|
||||||
|
)
|
||||||
return self.stub.SetSessionOptions(request)
|
return self.stub.SetSessionOptions(request)
|
||||||
|
|
||||||
def get_session_location(self, session_id):
|
def get_session_location(self, session_id):
|
||||||
|
@ -230,7 +231,17 @@ class CoreGrpcClient(object):
|
||||||
request = core_pb2.GetSessionLocationRequest(session_id=session_id)
|
request = core_pb2.GetSessionLocationRequest(session_id=session_id)
|
||||||
return self.stub.GetSessionLocation(request)
|
return self.stub.GetSessionLocation(request)
|
||||||
|
|
||||||
def set_session_location(self, session_id, x=None, y=None, z=None, lat=None, lon=None, alt=None, scale=None):
|
def set_session_location(
|
||||||
|
self,
|
||||||
|
session_id,
|
||||||
|
x=None,
|
||||||
|
y=None,
|
||||||
|
z=None,
|
||||||
|
lat=None,
|
||||||
|
lon=None,
|
||||||
|
alt=None,
|
||||||
|
scale=None,
|
||||||
|
):
|
||||||
"""
|
"""
|
||||||
Set session location.
|
Set session location.
|
||||||
|
|
||||||
|
@ -247,7 +258,9 @@ class CoreGrpcClient(object):
|
||||||
:raises grpc.RpcError: when session doesn't exist
|
:raises grpc.RpcError: when session doesn't exist
|
||||||
"""
|
"""
|
||||||
position = core_pb2.SessionPosition(x=x, y=y, z=z, lat=lat, lon=lon, alt=alt)
|
position = core_pb2.SessionPosition(x=x, y=y, z=z, lat=lat, lon=lon, alt=alt)
|
||||||
request = core_pb2.SetSessionLocationRequest(session_id=session_id, position=position, scale=scale)
|
request = core_pb2.SetSessionLocationRequest(
|
||||||
|
session_id=session_id, position=position, scale=scale
|
||||||
|
)
|
||||||
return self.stub.SetSessionLocation(request)
|
return self.stub.SetSessionLocation(request)
|
||||||
|
|
||||||
def set_session_state(self, session_id, state):
|
def set_session_state(self, session_id, state):
|
||||||
|
@ -324,7 +337,9 @@ class CoreGrpcClient(object):
|
||||||
:rtype: core_pb2.EditNodeResponse
|
:rtype: core_pb2.EditNodeResponse
|
||||||
:raises grpc.RpcError: when session or node doesn't exist
|
:raises grpc.RpcError: when session or node doesn't exist
|
||||||
"""
|
"""
|
||||||
request = core_pb2.EditNodeRequest(session_id=session_id, node_id=node_id, position=position)
|
request = core_pb2.EditNodeRequest(
|
||||||
|
session_id=session_id, node_id=node_id, position=position
|
||||||
|
)
|
||||||
return self.stub.EditNode(request)
|
return self.stub.EditNode(request)
|
||||||
|
|
||||||
def delete_node(self, session_id, node_id):
|
def delete_node(self, session_id, node_id):
|
||||||
|
@ -350,7 +365,9 @@ class CoreGrpcClient(object):
|
||||||
:rtype: core_pb2.NodeCommandResponse
|
:rtype: core_pb2.NodeCommandResponse
|
||||||
:raises grpc.RpcError: when session or node doesn't exist
|
:raises grpc.RpcError: when session or node doesn't exist
|
||||||
"""
|
"""
|
||||||
request = core_pb2.NodeCommandRequest(session_id=session_id, node_id=node_id, command=command)
|
request = core_pb2.NodeCommandRequest(
|
||||||
|
session_id=session_id, node_id=node_id, command=command
|
||||||
|
)
|
||||||
return self.stub.NodeCommand(request)
|
return self.stub.NodeCommand(request)
|
||||||
|
|
||||||
def get_node_terminal(self, session_id, node_id):
|
def get_node_terminal(self, session_id, node_id):
|
||||||
|
@ -363,7 +380,9 @@ class CoreGrpcClient(object):
|
||||||
:rtype: core_pb2.GetNodeTerminalResponse
|
:rtype: core_pb2.GetNodeTerminalResponse
|
||||||
:raises grpc.RpcError: when session or node doesn't exist
|
:raises grpc.RpcError: when session or node doesn't exist
|
||||||
"""
|
"""
|
||||||
request = core_pb2.GetNodeTerminalRequest(session_id=session_id, node_id=node_id)
|
request = core_pb2.GetNodeTerminalRequest(
|
||||||
|
session_id=session_id, node_id=node_id
|
||||||
|
)
|
||||||
return self.stub.GetNodeTerminal(request)
|
return self.stub.GetNodeTerminal(request)
|
||||||
|
|
||||||
def get_node_links(self, session_id, node_id):
|
def get_node_links(self, session_id, node_id):
|
||||||
|
@ -379,7 +398,15 @@ class CoreGrpcClient(object):
|
||||||
request = core_pb2.GetNodeLinksRequest(session_id=session_id, node_id=node_id)
|
request = core_pb2.GetNodeLinksRequest(session_id=session_id, node_id=node_id)
|
||||||
return self.stub.GetNodeLinks(request)
|
return self.stub.GetNodeLinks(request)
|
||||||
|
|
||||||
def add_link(self, session_id, node_one_id, node_two_id, interface_one=None, interface_two=None, options=None):
|
def add_link(
|
||||||
|
self,
|
||||||
|
session_id,
|
||||||
|
node_one_id,
|
||||||
|
node_two_id,
|
||||||
|
interface_one=None,
|
||||||
|
interface_two=None,
|
||||||
|
options=None,
|
||||||
|
):
|
||||||
"""
|
"""
|
||||||
Add a link between nodes.
|
Add a link between nodes.
|
||||||
|
|
||||||
|
@ -394,12 +421,25 @@ class CoreGrpcClient(object):
|
||||||
:raises grpc.RpcError: when session or one of the nodes don't exist
|
:raises grpc.RpcError: when session or one of the nodes don't exist
|
||||||
"""
|
"""
|
||||||
link = core_pb2.Link(
|
link = core_pb2.Link(
|
||||||
node_one_id=node_one_id, node_two_id=node_two_id, type=core_pb2.LinkType.WIRED,
|
node_one_id=node_one_id,
|
||||||
interface_one=interface_one, interface_two=interface_two, options=options)
|
node_two_id=node_two_id,
|
||||||
|
type=core_pb2.LinkType.WIRED,
|
||||||
|
interface_one=interface_one,
|
||||||
|
interface_two=interface_two,
|
||||||
|
options=options,
|
||||||
|
)
|
||||||
request = core_pb2.AddLinkRequest(session_id=session_id, link=link)
|
request = core_pb2.AddLinkRequest(session_id=session_id, link=link)
|
||||||
return self.stub.AddLink(request)
|
return self.stub.AddLink(request)
|
||||||
|
|
||||||
def edit_link(self, session_id, node_one_id, node_two_id, options, interface_one_id=None, interface_two_id=None):
|
def edit_link(
|
||||||
|
self,
|
||||||
|
session_id,
|
||||||
|
node_one_id,
|
||||||
|
node_two_id,
|
||||||
|
options,
|
||||||
|
interface_one_id=None,
|
||||||
|
interface_two_id=None,
|
||||||
|
):
|
||||||
"""
|
"""
|
||||||
Edit a link between nodes.
|
Edit a link between nodes.
|
||||||
|
|
||||||
|
@ -414,11 +454,23 @@ class CoreGrpcClient(object):
|
||||||
:raises grpc.RpcError: when session or one of the nodes don't exist
|
:raises grpc.RpcError: when session or one of the nodes don't exist
|
||||||
"""
|
"""
|
||||||
request = core_pb2.EditLinkRequest(
|
request = core_pb2.EditLinkRequest(
|
||||||
session_id=session_id, node_one_id=node_one_id, node_two_id=node_two_id, options=options,
|
session_id=session_id,
|
||||||
interface_one_id=interface_one_id, interface_two_id=interface_two_id)
|
node_one_id=node_one_id,
|
||||||
|
node_two_id=node_two_id,
|
||||||
|
options=options,
|
||||||
|
interface_one_id=interface_one_id,
|
||||||
|
interface_two_id=interface_two_id,
|
||||||
|
)
|
||||||
return self.stub.EditLink(request)
|
return self.stub.EditLink(request)
|
||||||
|
|
||||||
def delete_link(self, session_id, node_one_id, node_two_id, interface_one_id=None, interface_two_id=None):
|
def delete_link(
|
||||||
|
self,
|
||||||
|
session_id,
|
||||||
|
node_one_id,
|
||||||
|
node_two_id,
|
||||||
|
interface_one_id=None,
|
||||||
|
interface_two_id=None,
|
||||||
|
):
|
||||||
"""
|
"""
|
||||||
Delete a link between nodes.
|
Delete a link between nodes.
|
||||||
|
|
||||||
|
@ -432,8 +484,12 @@ class CoreGrpcClient(object):
|
||||||
:raises grpc.RpcError: when session doesn't exist
|
:raises grpc.RpcError: when session doesn't exist
|
||||||
"""
|
"""
|
||||||
request = core_pb2.DeleteLinkRequest(
|
request = core_pb2.DeleteLinkRequest(
|
||||||
session_id=session_id, node_one_id=node_one_id, node_two_id=node_two_id,
|
session_id=session_id,
|
||||||
interface_one_id=interface_one_id, interface_two_id=interface_two_id)
|
node_one_id=node_one_id,
|
||||||
|
node_two_id=node_two_id,
|
||||||
|
interface_one_id=interface_one_id,
|
||||||
|
interface_two_id=interface_two_id,
|
||||||
|
)
|
||||||
return self.stub.DeleteLink(request)
|
return self.stub.DeleteLink(request)
|
||||||
|
|
||||||
def get_hooks(self, session_id):
|
def get_hooks(self, session_id):
|
||||||
|
@ -486,7 +542,9 @@ class CoreGrpcClient(object):
|
||||||
:rtype: core_pb2.GetMobilityConfigResponse
|
:rtype: core_pb2.GetMobilityConfigResponse
|
||||||
:raises grpc.RpcError: when session or node doesn't exist
|
:raises grpc.RpcError: when session or node doesn't exist
|
||||||
"""
|
"""
|
||||||
request = core_pb2.GetMobilityConfigRequest(session_id=session_id, node_id=node_id)
|
request = core_pb2.GetMobilityConfigRequest(
|
||||||
|
session_id=session_id, node_id=node_id
|
||||||
|
)
|
||||||
return self.stub.GetMobilityConfig(request)
|
return self.stub.GetMobilityConfig(request)
|
||||||
|
|
||||||
def set_mobility_config(self, session_id, node_id, config):
|
def set_mobility_config(self, session_id, node_id, config):
|
||||||
|
@ -500,7 +558,9 @@ class CoreGrpcClient(object):
|
||||||
:rtype: core_pb2.SetMobilityConfigResponse
|
:rtype: core_pb2.SetMobilityConfigResponse
|
||||||
:raises grpc.RpcError: when session or node doesn't exist
|
:raises grpc.RpcError: when session or node doesn't exist
|
||||||
"""
|
"""
|
||||||
request = core_pb2.SetMobilityConfigRequest(session_id=session_id, node_id=node_id, config=config)
|
request = core_pb2.SetMobilityConfigRequest(
|
||||||
|
session_id=session_id, node_id=node_id, config=config
|
||||||
|
)
|
||||||
return self.stub.SetMobilityConfig(request)
|
return self.stub.SetMobilityConfig(request)
|
||||||
|
|
||||||
def mobility_action(self, session_id, node_id, action):
|
def mobility_action(self, session_id, node_id, action):
|
||||||
|
@ -514,7 +574,9 @@ class CoreGrpcClient(object):
|
||||||
:rtype: core_pb2.MobilityActionResponse
|
:rtype: core_pb2.MobilityActionResponse
|
||||||
:raises grpc.RpcError: when session or node doesn't exist
|
:raises grpc.RpcError: when session or node doesn't exist
|
||||||
"""
|
"""
|
||||||
request = core_pb2.MobilityActionRequest(session_id=session_id, node_id=node_id, action=action)
|
request = core_pb2.MobilityActionRequest(
|
||||||
|
session_id=session_id, node_id=node_id, action=action
|
||||||
|
)
|
||||||
return self.stub.MobilityAction(request)
|
return self.stub.MobilityAction(request)
|
||||||
|
|
||||||
def get_services(self):
|
def get_services(self):
|
||||||
|
@ -554,7 +616,9 @@ class CoreGrpcClient(object):
|
||||||
services = service_defaults[node_type]
|
services = service_defaults[node_type]
|
||||||
default = core_pb2.ServiceDefaults(node_type=node_type, services=services)
|
default = core_pb2.ServiceDefaults(node_type=node_type, services=services)
|
||||||
defaults.append(default)
|
defaults.append(default)
|
||||||
request = core_pb2.SetServiceDefaultsRequest(session_id=session_id, defaults=defaults)
|
request = core_pb2.SetServiceDefaultsRequest(
|
||||||
|
session_id=session_id, defaults=defaults
|
||||||
|
)
|
||||||
return self.stub.SetServiceDefaults(request)
|
return self.stub.SetServiceDefaults(request)
|
||||||
|
|
||||||
def get_node_service(self, session_id, node_id, service):
|
def get_node_service(self, session_id, node_id, service):
|
||||||
|
@ -568,7 +632,9 @@ class CoreGrpcClient(object):
|
||||||
:rtype: core_pb2.GetNodeServiceResponse
|
:rtype: core_pb2.GetNodeServiceResponse
|
||||||
:raises grpc.RpcError: when session or node doesn't exist
|
:raises grpc.RpcError: when session or node doesn't exist
|
||||||
"""
|
"""
|
||||||
request = core_pb2.GetNodeServiceRequest(session_id=session_id, node_id=node_id, service=service)
|
request = core_pb2.GetNodeServiceRequest(
|
||||||
|
session_id=session_id, node_id=node_id, service=service
|
||||||
|
)
|
||||||
return self.stub.GetNodeService(request)
|
return self.stub.GetNodeService(request)
|
||||||
|
|
||||||
def get_node_service_file(self, session_id, node_id, service, file_name):
|
def get_node_service_file(self, session_id, node_id, service, file_name):
|
||||||
|
@ -584,10 +650,13 @@ class CoreGrpcClient(object):
|
||||||
:raises grpc.RpcError: when session or node doesn't exist
|
:raises grpc.RpcError: when session or node doesn't exist
|
||||||
"""
|
"""
|
||||||
request = core_pb2.GetNodeServiceFileRequest(
|
request = core_pb2.GetNodeServiceFileRequest(
|
||||||
session_id=session_id, node_id=node_id, service=service, file=file_name)
|
session_id=session_id, node_id=node_id, service=service, file=file_name
|
||||||
|
)
|
||||||
return self.stub.GetNodeServiceFile(request)
|
return self.stub.GetNodeServiceFile(request)
|
||||||
|
|
||||||
def set_node_service(self, session_id, node_id, service, startup, validate, shutdown):
|
def set_node_service(
|
||||||
|
self, session_id, node_id, service, startup, validate, shutdown
|
||||||
|
):
|
||||||
"""
|
"""
|
||||||
Set service data for a node.
|
Set service data for a node.
|
||||||
|
|
||||||
|
@ -602,8 +671,13 @@ class CoreGrpcClient(object):
|
||||||
:raises grpc.RpcError: when session or node doesn't exist
|
:raises grpc.RpcError: when session or node doesn't exist
|
||||||
"""
|
"""
|
||||||
request = core_pb2.SetNodeServiceRequest(
|
request = core_pb2.SetNodeServiceRequest(
|
||||||
session_id=session_id, node_id=node_id, service=service, startup=startup, validate=validate,
|
session_id=session_id,
|
||||||
shutdown=shutdown)
|
node_id=node_id,
|
||||||
|
service=service,
|
||||||
|
startup=startup,
|
||||||
|
validate=validate,
|
||||||
|
shutdown=shutdown,
|
||||||
|
)
|
||||||
return self.stub.SetNodeService(request)
|
return self.stub.SetNodeService(request)
|
||||||
|
|
||||||
def set_node_service_file(self, session_id, node_id, service, file_name, data):
|
def set_node_service_file(self, session_id, node_id, service, file_name, data):
|
||||||
|
@ -620,7 +694,12 @@ class CoreGrpcClient(object):
|
||||||
:raises grpc.RpcError: when session or node doesn't exist
|
:raises grpc.RpcError: when session or node doesn't exist
|
||||||
"""
|
"""
|
||||||
request = core_pb2.SetNodeServiceFileRequest(
|
request = core_pb2.SetNodeServiceFileRequest(
|
||||||
session_id=session_id, node_id=node_id, service=service, file=file_name, data=data)
|
session_id=session_id,
|
||||||
|
node_id=node_id,
|
||||||
|
service=service,
|
||||||
|
file=file_name,
|
||||||
|
data=data,
|
||||||
|
)
|
||||||
return self.stub.SetNodeServiceFile(request)
|
return self.stub.SetNodeServiceFile(request)
|
||||||
|
|
||||||
def service_action(self, session_id, node_id, service, action):
|
def service_action(self, session_id, node_id, service, action):
|
||||||
|
@ -635,7 +714,9 @@ class CoreGrpcClient(object):
|
||||||
:rtype: core_pb2.ServiceActionResponse
|
:rtype: core_pb2.ServiceActionResponse
|
||||||
:raises grpc.RpcError: when session or node doesn't exist
|
:raises grpc.RpcError: when session or node doesn't exist
|
||||||
"""
|
"""
|
||||||
request = core_pb2.ServiceActionRequest(session_id=session_id, node_id=node_id, service=service, action=action)
|
request = core_pb2.ServiceActionRequest(
|
||||||
|
session_id=session_id, node_id=node_id, service=service, action=action
|
||||||
|
)
|
||||||
return self.stub.ServiceAction(request)
|
return self.stub.ServiceAction(request)
|
||||||
|
|
||||||
def get_wlan_config(self, session_id, node_id):
|
def get_wlan_config(self, session_id, node_id):
|
||||||
|
@ -662,7 +743,9 @@ class CoreGrpcClient(object):
|
||||||
:rtype: core_pb2.SetWlanConfigResponse
|
:rtype: core_pb2.SetWlanConfigResponse
|
||||||
:raises grpc.RpcError: when session doesn't exist
|
:raises grpc.RpcError: when session doesn't exist
|
||||||
"""
|
"""
|
||||||
request = core_pb2.SetWlanConfigRequest(session_id=session_id, node_id=node_id, config=config)
|
request = core_pb2.SetWlanConfigRequest(
|
||||||
|
session_id=session_id, node_id=node_id, config=config
|
||||||
|
)
|
||||||
return self.stub.SetWlanConfig(request)
|
return self.stub.SetWlanConfig(request)
|
||||||
|
|
||||||
def get_emane_config(self, session_id):
|
def get_emane_config(self, session_id):
|
||||||
|
@ -715,10 +798,13 @@ class CoreGrpcClient(object):
|
||||||
:raises grpc.RpcError: when session doesn't exist
|
:raises grpc.RpcError: when session doesn't exist
|
||||||
"""
|
"""
|
||||||
request = core_pb2.GetEmaneModelConfigRequest(
|
request = core_pb2.GetEmaneModelConfigRequest(
|
||||||
session_id=session_id, node_id=node_id, model=model, interface=interface_id)
|
session_id=session_id, node_id=node_id, model=model, interface=interface_id
|
||||||
|
)
|
||||||
return self.stub.GetEmaneModelConfig(request)
|
return self.stub.GetEmaneModelConfig(request)
|
||||||
|
|
||||||
def set_emane_model_config(self, session_id, node_id, model, config, interface_id=-1):
|
def set_emane_model_config(
|
||||||
|
self, session_id, node_id, model, config, interface_id=-1
|
||||||
|
):
|
||||||
"""
|
"""
|
||||||
Set emane model configuration for a node or a node's interface.
|
Set emane model configuration for a node or a node's interface.
|
||||||
|
|
||||||
|
@ -732,7 +818,12 @@ class CoreGrpcClient(object):
|
||||||
:raises grpc.RpcError: when session doesn't exist
|
:raises grpc.RpcError: when session doesn't exist
|
||||||
"""
|
"""
|
||||||
request = core_pb2.SetEmaneModelConfigRequest(
|
request = core_pb2.SetEmaneModelConfigRequest(
|
||||||
session_id=session_id, node_id=node_id, model=model, config=config, interface_id=interface_id)
|
session_id=session_id,
|
||||||
|
node_id=node_id,
|
||||||
|
model=model,
|
||||||
|
config=config,
|
||||||
|
interface_id=interface_id,
|
||||||
|
)
|
||||||
return self.stub.SetEmaneModelConfig(request)
|
return self.stub.SetEmaneModelConfig(request)
|
||||||
|
|
||||||
def get_emane_model_configs(self, session_id):
|
def get_emane_model_configs(self, session_id):
|
||||||
|
|
File diff suppressed because it is too large
Load diff
|
@ -12,23 +12,25 @@ import threading
|
||||||
|
|
||||||
from core import utils
|
from core import utils
|
||||||
from core.api.tlv import coreapi
|
from core.api.tlv import coreapi
|
||||||
from core.nodes.base import CoreNodeBase, CoreNetworkBase
|
from core.emulator.enumerations import (
|
||||||
from core.emulator.enumerations import ConfigDataTypes
|
ConfigDataTypes,
|
||||||
from core.emulator.enumerations import ConfigFlags
|
ConfigFlags,
|
||||||
from core.emulator.enumerations import ConfigTlvs
|
ConfigTlvs,
|
||||||
from core.emulator.enumerations import EventTlvs
|
EventTlvs,
|
||||||
from core.emulator.enumerations import EventTypes
|
EventTypes,
|
||||||
from core.emulator.enumerations import ExecuteTlvs
|
ExecuteTlvs,
|
||||||
from core.emulator.enumerations import FileTlvs
|
FileTlvs,
|
||||||
from core.emulator.enumerations import LinkTlvs
|
LinkTlvs,
|
||||||
from core.emulator.enumerations import MessageFlags
|
MessageFlags,
|
||||||
from core.emulator.enumerations import MessageTypes
|
MessageTypes,
|
||||||
from core.emulator.enumerations import NodeTlvs
|
NodeTlvs,
|
||||||
from core.emulator.enumerations import NodeTypes
|
NodeTypes,
|
||||||
from core.emulator.enumerations import RegisterTlvs
|
RegisterTlvs,
|
||||||
|
)
|
||||||
from core.nodes import nodeutils
|
from core.nodes import nodeutils
|
||||||
from core.nodes.ipaddress import IpAddress
|
from core.nodes.base import CoreNetworkBase, CoreNodeBase
|
||||||
from core.nodes.interface import GreTap
|
from core.nodes.interface import GreTap
|
||||||
|
from core.nodes.ipaddress import IpAddress
|
||||||
from core.nodes.network import GreTapBridge
|
from core.nodes.network import GreTapBridge
|
||||||
from core.nodes.physical import PhysicalNode
|
from core.nodes.physical import PhysicalNode
|
||||||
|
|
||||||
|
@ -147,7 +149,12 @@ class CoreBroker(object):
|
||||||
while len(self.servers) > 0:
|
while len(self.servers) > 0:
|
||||||
name, server = self.servers.popitem()
|
name, server = self.servers.popitem()
|
||||||
if server.sock is not None:
|
if server.sock is not None:
|
||||||
logging.info("closing connection with %s: %s:%s", name, server.host, server.port)
|
logging.info(
|
||||||
|
"closing connection with %s: %s:%s",
|
||||||
|
name,
|
||||||
|
server.host,
|
||||||
|
server.port,
|
||||||
|
)
|
||||||
server.close()
|
server.close()
|
||||||
self.dorecvloop = False
|
self.dorecvloop = False
|
||||||
if self.recvthread is not None:
|
if self.recvthread is not None:
|
||||||
|
@ -157,7 +164,7 @@ class CoreBroker(object):
|
||||||
"""
|
"""
|
||||||
Reset to initial state.
|
Reset to initial state.
|
||||||
"""
|
"""
|
||||||
logging.info("clearing state")
|
logging.debug("broker reset")
|
||||||
self.nodemap_lock.acquire()
|
self.nodemap_lock.acquire()
|
||||||
self.nodemap.clear()
|
self.nodemap.clear()
|
||||||
for server in self.nodecounts:
|
for server in self.nodecounts:
|
||||||
|
@ -208,14 +215,22 @@ class CoreBroker(object):
|
||||||
r, _w, _x = select.select(rlist, [], [], 1.0)
|
r, _w, _x = select.select(rlist, [], [], 1.0)
|
||||||
for sock in r:
|
for sock in r:
|
||||||
server = self.getserverbysock(sock)
|
server = self.getserverbysock(sock)
|
||||||
logging.info("attempting to receive from server: peer:%s remote:%s",
|
logging.info(
|
||||||
server.sock.getpeername(), server.sock.getsockname())
|
"attempting to receive from server: peer:%s remote:%s",
|
||||||
|
server.sock.getpeername(),
|
||||||
|
server.sock.getsockname(),
|
||||||
|
)
|
||||||
if server is None:
|
if server is None:
|
||||||
# servers may have changed; loop again
|
# servers may have changed; loop again
|
||||||
continue
|
continue
|
||||||
rcvlen = self.recv(server)
|
rcvlen = self.recv(server)
|
||||||
if rcvlen == 0:
|
if rcvlen == 0:
|
||||||
logging.info("connection with server(%s) closed: %s:%s", server.name, server.host, server.port)
|
logging.info(
|
||||||
|
"connection with server(%s) closed: %s:%s",
|
||||||
|
server.name,
|
||||||
|
server.host,
|
||||||
|
server.port,
|
||||||
|
)
|
||||||
|
|
||||||
def recv(self, server):
|
def recv(self, server):
|
||||||
"""
|
"""
|
||||||
|
@ -236,7 +251,9 @@ class CoreBroker(object):
|
||||||
return 0
|
return 0
|
||||||
|
|
||||||
if len(msghdr) != coreapi.CoreMessage.header_len:
|
if len(msghdr) != coreapi.CoreMessage.header_len:
|
||||||
logging.warning("warning: broker received not enough data len=%s", len(msghdr))
|
logging.warning(
|
||||||
|
"warning: broker received not enough data len=%s", len(msghdr)
|
||||||
|
)
|
||||||
return len(msghdr)
|
return len(msghdr)
|
||||||
|
|
||||||
msgtype, msgflags, msglen = coreapi.CoreMessage.unpack_header(msghdr)
|
msgtype, msgflags, msglen = coreapi.CoreMessage.unpack_header(msghdr)
|
||||||
|
@ -293,11 +310,17 @@ class CoreBroker(object):
|
||||||
with self.servers_lock:
|
with self.servers_lock:
|
||||||
server = self.servers.get(name)
|
server = self.servers.get(name)
|
||||||
if server is not None:
|
if server is not None:
|
||||||
if host == server.host and port == server.port and server.sock is not None:
|
if (
|
||||||
|
host == server.host
|
||||||
|
and port == server.port
|
||||||
|
and server.sock is not None
|
||||||
|
):
|
||||||
# leave this socket connected
|
# leave this socket connected
|
||||||
return
|
return
|
||||||
|
|
||||||
logging.info("closing connection with %s @ %s:%s", name, server.host, server.port)
|
logging.info(
|
||||||
|
"closing connection with %s @ %s:%s", name, server.host, server.port
|
||||||
|
)
|
||||||
server.close()
|
server.close()
|
||||||
del self.servers[name]
|
del self.servers[name]
|
||||||
|
|
||||||
|
@ -307,7 +330,9 @@ class CoreBroker(object):
|
||||||
try:
|
try:
|
||||||
server.connect()
|
server.connect()
|
||||||
except IOError:
|
except IOError:
|
||||||
logging.exception("error connecting to server(%s): %s:%s", name, host, port)
|
logging.exception(
|
||||||
|
"error connecting to server(%s): %s:%s", name, host, port
|
||||||
|
)
|
||||||
if server.sock is not None:
|
if server.sock is not None:
|
||||||
self.startrecvloop()
|
self.startrecvloop()
|
||||||
self.servers[name] = server
|
self.servers[name] = server
|
||||||
|
@ -328,7 +353,12 @@ class CoreBroker(object):
|
||||||
logging.exception("error deleting server")
|
logging.exception("error deleting server")
|
||||||
|
|
||||||
if server.sock is not None:
|
if server.sock is not None:
|
||||||
logging.info("closing connection with %s @ %s:%s", server.name, server.host, server.port)
|
logging.info(
|
||||||
|
"closing connection with %s @ %s:%s",
|
||||||
|
server.name,
|
||||||
|
server.host,
|
||||||
|
server.port,
|
||||||
|
)
|
||||||
server.close()
|
server.close()
|
||||||
|
|
||||||
def getserverbyname(self, name):
|
def getserverbyname(self, name):
|
||||||
|
@ -414,16 +444,31 @@ class CoreBroker(object):
|
||||||
remotenum = n2num
|
remotenum = n2num
|
||||||
|
|
||||||
if key in self.tunnels.keys():
|
if key in self.tunnels.keys():
|
||||||
logging.warning("tunnel with key %s (%s-%s) already exists!", key, n1num, n2num)
|
logging.warning(
|
||||||
|
"tunnel with key %s (%s-%s) already exists!", key, n1num, n2num
|
||||||
|
)
|
||||||
else:
|
else:
|
||||||
_id = key & ((1 << 16) - 1)
|
_id = key & ((1 << 16) - 1)
|
||||||
logging.info("adding tunnel for %s-%s to %s with key %s", n1num, n2num, remoteip, key)
|
logging.info(
|
||||||
|
"adding tunnel for %s-%s to %s with key %s", n1num, n2num, remoteip, key
|
||||||
|
)
|
||||||
if localnum in self.physical_nodes:
|
if localnum in self.physical_nodes:
|
||||||
# no bridge is needed on physical nodes; use the GreTap directly
|
# no bridge is needed on physical nodes; use the GreTap directly
|
||||||
gt = GreTap(node=None, name=None, session=self.session,
|
gt = GreTap(
|
||||||
remoteip=remoteip, key=key)
|
node=None,
|
||||||
|
name=None,
|
||||||
|
session=self.session,
|
||||||
|
remoteip=remoteip,
|
||||||
|
key=key,
|
||||||
|
)
|
||||||
else:
|
else:
|
||||||
gt = self.session.create_node(cls=GreTapBridge, _id=_id, policy="ACCEPT", remoteip=remoteip, key=key)
|
gt = self.session.create_node(
|
||||||
|
cls=GreTapBridge,
|
||||||
|
_id=_id,
|
||||||
|
policy="ACCEPT",
|
||||||
|
remoteip=remoteip,
|
||||||
|
key=key,
|
||||||
|
)
|
||||||
gt.localnum = localnum
|
gt.localnum = localnum
|
||||||
gt.remotenum = remotenum
|
gt.remotenum = remotenum
|
||||||
self.tunnels[key] = gt
|
self.tunnels[key] = gt
|
||||||
|
@ -444,12 +489,10 @@ class CoreBroker(object):
|
||||||
:param int node_id: node id of network to add tunnel to
|
:param int node_id: node id of network to add tunnel to
|
||||||
:return: list of gre taps
|
:return: list of gre taps
|
||||||
:rtype: list
|
:rtype: list
|
||||||
|
:raises core.CoreError: when node to add net tunnel to does not exist
|
||||||
"""
|
"""
|
||||||
try:
|
net = self.session.get_node(node_id)
|
||||||
net = self.session.get_node(node_id)
|
logging.info("adding net tunnel for: id(%s) %s", node_id, net)
|
||||||
logging.info("adding net tunnel for: id(%s) %s", node_id, net)
|
|
||||||
except KeyError:
|
|
||||||
raise KeyError("network node %s not found" % node_id)
|
|
||||||
|
|
||||||
# add other nets here that do not require tunnels
|
# add other nets here that do not require tunnels
|
||||||
if nodeutils.is_node(net, NodeTypes.EMANE_NET):
|
if nodeutils.is_node(net, NodeTypes.EMANE_NET):
|
||||||
|
@ -457,8 +500,13 @@ class CoreBroker(object):
|
||||||
return None
|
return None
|
||||||
|
|
||||||
server_interface = getattr(net, "serverintf", None)
|
server_interface = getattr(net, "serverintf", None)
|
||||||
if nodeutils.is_node(net, NodeTypes.CONTROL_NET) and server_interface is not None:
|
if (
|
||||||
logging.warning("control networks with server interfaces do not need a tunnel")
|
nodeutils.is_node(net, NodeTypes.CONTROL_NET)
|
||||||
|
and server_interface is not None
|
||||||
|
):
|
||||||
|
logging.warning(
|
||||||
|
"control networks with server interfaces do not need a tunnel"
|
||||||
|
)
|
||||||
return None
|
return None
|
||||||
|
|
||||||
servers = self.getserversbynode(node_id)
|
servers = self.getserversbynode(node_id)
|
||||||
|
@ -491,12 +539,18 @@ class CoreBroker(object):
|
||||||
myip = host
|
myip = host
|
||||||
key = self.tunnelkey(node_id, IpAddress.to_int(myip))
|
key = self.tunnelkey(node_id, IpAddress.to_int(myip))
|
||||||
if key in self.tunnels.keys():
|
if key in self.tunnels.keys():
|
||||||
logging.info("tunnel already exists, returning existing tunnel: %s", key)
|
logging.info(
|
||||||
|
"tunnel already exists, returning existing tunnel: %s", key
|
||||||
|
)
|
||||||
gt = self.tunnels[key]
|
gt = self.tunnels[key]
|
||||||
r.append(gt)
|
r.append(gt)
|
||||||
continue
|
continue
|
||||||
logging.info("adding tunnel for net %s to %s with key %s", node_id, host, key)
|
logging.info(
|
||||||
gt = GreTap(node=None, name=None, session=self.session, remoteip=host, key=key)
|
"adding tunnel for net %s to %s with key %s", node_id, host, key
|
||||||
|
)
|
||||||
|
gt = GreTap(
|
||||||
|
node=None, name=None, session=self.session, remoteip=host, key=key
|
||||||
|
)
|
||||||
self.tunnels[key] = gt
|
self.tunnels[key] = gt
|
||||||
r.append(gt)
|
r.append(gt)
|
||||||
# attaching to net will later allow gt to be destroyed
|
# attaching to net will later allow gt to be destroyed
|
||||||
|
@ -515,7 +569,9 @@ class CoreBroker(object):
|
||||||
"""
|
"""
|
||||||
key = self.tunnelkey(n1num, n2num)
|
key = self.tunnelkey(n1num, n2num)
|
||||||
try:
|
try:
|
||||||
logging.info("deleting tunnel between %s - %s with key: %s", n1num, n2num, key)
|
logging.info(
|
||||||
|
"deleting tunnel between %s - %s with key: %s", n1num, n2num, key
|
||||||
|
)
|
||||||
gt = self.tunnels.pop(key)
|
gt = self.tunnels.pop(key)
|
||||||
except KeyError:
|
except KeyError:
|
||||||
gt = None
|
gt = None
|
||||||
|
@ -643,12 +699,19 @@ class CoreBroker(object):
|
||||||
elif message.message_type == MessageTypes.CONFIG.value:
|
elif message.message_type == MessageTypes.CONFIG.value:
|
||||||
# broadcast location and services configuration everywhere
|
# broadcast location and services configuration everywhere
|
||||||
confobj = message.get_tlv(ConfigTlvs.OBJECT.value)
|
confobj = message.get_tlv(ConfigTlvs.OBJECT.value)
|
||||||
if confobj == "location" or confobj == "services" or confobj == "session" or confobj == "all":
|
if (
|
||||||
|
confobj == "location"
|
||||||
|
or confobj == "services"
|
||||||
|
or confobj == "session"
|
||||||
|
or confobj == "all"
|
||||||
|
):
|
||||||
servers = self.getservers()
|
servers = self.getservers()
|
||||||
elif message.message_type == MessageTypes.FILE.value:
|
elif message.message_type == MessageTypes.FILE.value:
|
||||||
# broadcast hook scripts and custom service files everywhere
|
# broadcast hook scripts and custom service files everywhere
|
||||||
filetype = message.get_tlv(FileTlvs.TYPE.value)
|
filetype = message.get_tlv(FileTlvs.TYPE.value)
|
||||||
if filetype is not None and (filetype[:5] == "hook:" or filetype[:8] == "service:"):
|
if filetype is not None and (
|
||||||
|
filetype[:5] == "hook:" or filetype[:8] == "service:"
|
||||||
|
):
|
||||||
servers = self.getservers()
|
servers = self.getservers()
|
||||||
if message.message_type == MessageTypes.LINK.value:
|
if message.message_type == MessageTypes.LINK.value:
|
||||||
# prepare a server list from two node numbers in link message
|
# prepare a server list from two node numbers in link message
|
||||||
|
@ -695,11 +758,19 @@ class CoreBroker(object):
|
||||||
# server of its local name
|
# server of its local name
|
||||||
tlvdata = b""
|
tlvdata = b""
|
||||||
tlvdata += coreapi.CoreConfigTlv.pack(ConfigTlvs.OBJECT.value, "broker")
|
tlvdata += coreapi.CoreConfigTlv.pack(ConfigTlvs.OBJECT.value, "broker")
|
||||||
tlvdata += coreapi.CoreConfigTlv.pack(ConfigTlvs.TYPE.value, ConfigFlags.UPDATE.value)
|
tlvdata += coreapi.CoreConfigTlv.pack(
|
||||||
tlvdata += coreapi.CoreConfigTlv.pack(ConfigTlvs.DATA_TYPES.value, (ConfigDataTypes.STRING.value,))
|
ConfigTlvs.TYPE.value, ConfigFlags.UPDATE.value
|
||||||
tlvdata += coreapi.CoreConfigTlv.pack(ConfigTlvs.VALUES.value,
|
)
|
||||||
"%s:%s:%s" % (server.name, server.host, server.port))
|
tlvdata += coreapi.CoreConfigTlv.pack(
|
||||||
tlvdata += coreapi.CoreConfigTlv.pack(ConfigTlvs.SESSION.value, "%s" % self.session.id)
|
ConfigTlvs.DATA_TYPES.value, (ConfigDataTypes.STRING.value,)
|
||||||
|
)
|
||||||
|
tlvdata += coreapi.CoreConfigTlv.pack(
|
||||||
|
ConfigTlvs.VALUES.value,
|
||||||
|
"%s:%s:%s" % (server.name, server.host, server.port),
|
||||||
|
)
|
||||||
|
tlvdata += coreapi.CoreConfigTlv.pack(
|
||||||
|
ConfigTlvs.SESSION.value, "%s" % self.session.id
|
||||||
|
)
|
||||||
msg = coreapi.CoreConfMessage.pack(0, tlvdata)
|
msg = coreapi.CoreConfMessage.pack(0, tlvdata)
|
||||||
server.sock.send(msg)
|
server.sock.send(msg)
|
||||||
|
|
||||||
|
@ -760,7 +831,10 @@ class CoreBroker(object):
|
||||||
if nodecls is None:
|
if nodecls is None:
|
||||||
logging.warning("broker unimplemented node type %s", nodetype)
|
logging.warning("broker unimplemented node type %s", nodetype)
|
||||||
return handle_locally, servers
|
return handle_locally, servers
|
||||||
if issubclass(nodecls, CoreNetworkBase) and nodetype != NodeTypes.WIRELESS_LAN.value:
|
if (
|
||||||
|
issubclass(nodecls, CoreNetworkBase)
|
||||||
|
and nodetype != NodeTypes.WIRELESS_LAN.value
|
||||||
|
):
|
||||||
# network node replicated on all servers; could be optimized
|
# network node replicated on all servers; could be optimized
|
||||||
# don"t replicate WLANs, because ebtables rules won"t work
|
# don"t replicate WLANs, because ebtables rules won"t work
|
||||||
servers = self.getservers()
|
servers = self.getservers()
|
||||||
|
@ -810,7 +884,9 @@ class CoreBroker(object):
|
||||||
|
|
||||||
# determine link message destination using non-network nodes
|
# determine link message destination using non-network nodes
|
||||||
nn = message.node_numbers()
|
nn = message.node_numbers()
|
||||||
logging.debug("checking link nodes (%s) with network nodes (%s)", nn, self.network_nodes)
|
logging.debug(
|
||||||
|
"checking link nodes (%s) with network nodes (%s)", nn, self.network_nodes
|
||||||
|
)
|
||||||
if nn[0] in self.network_nodes:
|
if nn[0] in self.network_nodes:
|
||||||
if nn[1] in self.network_nodes:
|
if nn[1] in self.network_nodes:
|
||||||
# two network nodes linked together - prevent loops caused by
|
# two network nodes linked together - prevent loops caused by
|
||||||
|
@ -854,7 +930,9 @@ class CoreBroker(object):
|
||||||
if host is None:
|
if host is None:
|
||||||
host = self.getlinkendpoint(message, localn == nn[0])
|
host = self.getlinkendpoint(message, localn == nn[0])
|
||||||
|
|
||||||
logging.debug("handle locally(%s) and local node(%s)", handle_locally, localn)
|
logging.debug(
|
||||||
|
"handle locally(%s) and local node(%s)", handle_locally, localn
|
||||||
|
)
|
||||||
if localn is None:
|
if localn is None:
|
||||||
message = self.addlinkendpoints(message, servers1, servers2)
|
message = self.addlinkendpoints(message, servers1, servers2)
|
||||||
elif message.flags & MessageFlags.ADD.value:
|
elif message.flags & MessageFlags.ADD.value:
|
||||||
|
@ -889,10 +967,10 @@ class CoreBroker(object):
|
||||||
if server.host is not None:
|
if server.host is not None:
|
||||||
ip2 = server.host
|
ip2 = server.host
|
||||||
break
|
break
|
||||||
tlvdata = message.raw_message[coreapi.CoreMessage.header_len:]
|
tlvdata = message.raw_message[coreapi.CoreMessage.header_len :]
|
||||||
tlvdata += coreapi.CoreLinkTlv.pack(LinkTlvs.OPAQUE.value, "%s:%s" % (ip1, ip2))
|
tlvdata += coreapi.CoreLinkTlv.pack(LinkTlvs.OPAQUE.value, "%s:%s" % (ip1, ip2))
|
||||||
newraw = coreapi.CoreLinkMessage.pack(message.flags, tlvdata)
|
newraw = coreapi.CoreLinkMessage.pack(message.flags, tlvdata)
|
||||||
msghdr = newraw[:coreapi.CoreMessage.header_len]
|
msghdr = newraw[: coreapi.CoreMessage.header_len]
|
||||||
return coreapi.CoreLinkMessage(message.flags, msghdr, tlvdata)
|
return coreapi.CoreLinkMessage(message.flags, msghdr, tlvdata)
|
||||||
|
|
||||||
def getlinkendpoint(self, msg, first_is_local):
|
def getlinkendpoint(self, msg, first_is_local):
|
||||||
|
@ -934,10 +1012,12 @@ class CoreBroker(object):
|
||||||
:return: should handle locally or not
|
:return: should handle locally or not
|
||||||
:rtype: bool
|
:rtype: bool
|
||||||
"""
|
"""
|
||||||
hdr = msg[:coreapi.CoreMessage.header_len]
|
hdr = msg[: coreapi.CoreMessage.header_len]
|
||||||
msgtype, flags, _msglen = coreapi.CoreMessage.unpack_header(hdr)
|
msgtype, flags, _msglen = coreapi.CoreMessage.unpack_header(hdr)
|
||||||
msgcls = coreapi.CLASS_MAP[msgtype]
|
msgcls = coreapi.CLASS_MAP[msgtype]
|
||||||
return self.handle_message(msgcls(flags, hdr, msg[coreapi.CoreMessage.header_len:]))
|
return self.handle_message(
|
||||||
|
msgcls(flags, hdr, msg[coreapi.CoreMessage.header_len :])
|
||||||
|
)
|
||||||
|
|
||||||
def forwardmsg(self, message, servers):
|
def forwardmsg(self, message, servers):
|
||||||
"""
|
"""
|
||||||
|
@ -957,9 +1037,19 @@ class CoreBroker(object):
|
||||||
# local emulation server, handle this locally
|
# local emulation server, handle this locally
|
||||||
handle_locally = True
|
handle_locally = True
|
||||||
elif server.sock is None:
|
elif server.sock is None:
|
||||||
logging.info("server %s @ %s:%s is disconnected", server.name, server.host, server.port)
|
logging.info(
|
||||||
|
"server %s @ %s:%s is disconnected",
|
||||||
|
server.name,
|
||||||
|
server.host,
|
||||||
|
server.port,
|
||||||
|
)
|
||||||
else:
|
else:
|
||||||
logging.info("forwarding message to server(%s): %s:%s", server.name, server.host, server.port)
|
logging.info(
|
||||||
|
"forwarding message to server(%s): %s:%s",
|
||||||
|
server.name,
|
||||||
|
server.host,
|
||||||
|
server.port,
|
||||||
|
)
|
||||||
logging.debug("message being forwarded:\n%s", message)
|
logging.debug("message being forwarded:\n%s", message)
|
||||||
server.sock.send(message.raw_message)
|
server.sock.send(message.raw_message)
|
||||||
return handle_locally
|
return handle_locally
|
||||||
|
@ -986,7 +1076,10 @@ class CoreBroker(object):
|
||||||
lhost, lport = None, None
|
lhost, lport = None, None
|
||||||
if server.sock:
|
if server.sock:
|
||||||
lhost, lport = server.sock.getsockname()
|
lhost, lport = server.sock.getsockname()
|
||||||
f.write("%s %s %s %s %s\n" % (server.name, server.host, server.port, lhost, lport))
|
f.write(
|
||||||
|
"%s %s %s %s %s\n"
|
||||||
|
% (server.name, server.host, server.port, lhost, lport)
|
||||||
|
)
|
||||||
except IOError:
|
except IOError:
|
||||||
logging.exception("error writing server list to the file: %s", filename)
|
logging.exception("error writing server list to the file: %s", filename)
|
||||||
|
|
||||||
|
@ -1015,7 +1108,9 @@ class CoreBroker(object):
|
||||||
with open(filename, "w") as f:
|
with open(filename, "w") as f:
|
||||||
f.write("%s\n%s\n" % (serverstr, nodestr))
|
f.write("%s\n%s\n" % (serverstr, nodestr))
|
||||||
except IOError:
|
except IOError:
|
||||||
logging.exception("error writing server file %s for node %s", filename, name)
|
logging.exception(
|
||||||
|
"error writing server file %s for node %s", filename, name
|
||||||
|
)
|
||||||
|
|
||||||
def local_instantiation_complete(self):
|
def local_instantiation_complete(self):
|
||||||
"""
|
"""
|
||||||
|
@ -1031,7 +1126,9 @@ class CoreBroker(object):
|
||||||
|
|
||||||
# broadcast out instantiate complete
|
# broadcast out instantiate complete
|
||||||
tlvdata = b""
|
tlvdata = b""
|
||||||
tlvdata += coreapi.CoreEventTlv.pack(EventTlvs.TYPE.value, EventTypes.INSTANTIATION_COMPLETE.value)
|
tlvdata += coreapi.CoreEventTlv.pack(
|
||||||
|
EventTlvs.TYPE.value, EventTypes.INSTANTIATION_COMPLETE.value
|
||||||
|
)
|
||||||
message = coreapi.CoreEventMessage.pack(0, tlvdata)
|
message = coreapi.CoreEventMessage.pack(0, tlvdata)
|
||||||
for session_client in self.session_clients:
|
for session_client in self.session_clients:
|
||||||
session_client.sendall(message)
|
session_client.sendall(message)
|
||||||
|
|
|
@ -7,26 +7,27 @@ CORE API messaging is leveraged for communication with the GUI.
|
||||||
|
|
||||||
import socket
|
import socket
|
||||||
import struct
|
import struct
|
||||||
from past.builtins import basestring
|
|
||||||
|
|
||||||
from enum import Enum
|
from enum import Enum
|
||||||
|
|
||||||
|
from past.builtins import basestring
|
||||||
|
|
||||||
from core.api.tlv import structutils
|
from core.api.tlv import structutils
|
||||||
from core.emulator.enumerations import ConfigTlvs
|
from core.emulator.enumerations import (
|
||||||
from core.emulator.enumerations import EventTlvs
|
ConfigTlvs,
|
||||||
from core.emulator.enumerations import EventTypes
|
EventTlvs,
|
||||||
from core.emulator.enumerations import ExceptionTlvs
|
EventTypes,
|
||||||
from core.emulator.enumerations import ExecuteTlvs
|
ExceptionTlvs,
|
||||||
from core.emulator.enumerations import FileTlvs
|
ExecuteTlvs,
|
||||||
from core.emulator.enumerations import InterfaceTlvs
|
FileTlvs,
|
||||||
from core.emulator.enumerations import LinkTlvs
|
InterfaceTlvs,
|
||||||
from core.emulator.enumerations import MessageFlags
|
LinkTlvs,
|
||||||
from core.emulator.enumerations import MessageTypes
|
MessageFlags,
|
||||||
from core.emulator.enumerations import NodeTlvs
|
MessageTypes,
|
||||||
from core.emulator.enumerations import RegisterTlvs
|
NodeTlvs,
|
||||||
from core.emulator.enumerations import SessionTlvs
|
RegisterTlvs,
|
||||||
from core.nodes.ipaddress import IpAddress
|
SessionTlvs,
|
||||||
from core.nodes.ipaddress import MacAddress
|
)
|
||||||
|
from core.nodes.ipaddress import IpAddress, MacAddress
|
||||||
|
|
||||||
|
|
||||||
class CoreTlvData(object):
|
class CoreTlvData(object):
|
||||||
|
@ -139,6 +140,7 @@ class CoreTlvDataUint16(CoreTlvData):
|
||||||
"""
|
"""
|
||||||
Helper class for packing uint16 data.
|
Helper class for packing uint16 data.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
data_format = "!H"
|
data_format = "!H"
|
||||||
data_type = int
|
data_type = int
|
||||||
pad_len = 0
|
pad_len = 0
|
||||||
|
@ -148,6 +150,7 @@ class CoreTlvDataUint32(CoreTlvData):
|
||||||
"""
|
"""
|
||||||
Helper class for packing uint32 data.
|
Helper class for packing uint32 data.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
data_format = "!2xI"
|
data_format = "!2xI"
|
||||||
data_type = int
|
data_type = int
|
||||||
pad_len = 2
|
pad_len = 2
|
||||||
|
@ -157,6 +160,7 @@ class CoreTlvDataUint64(CoreTlvData):
|
||||||
"""
|
"""
|
||||||
Helper class for packing uint64 data.
|
Helper class for packing uint64 data.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
data_format = "!2xQ"
|
data_format = "!2xQ"
|
||||||
data_type = int
|
data_type = int
|
||||||
pad_len = 2
|
pad_len = 2
|
||||||
|
@ -166,6 +170,7 @@ class CoreTlvDataString(CoreTlvData):
|
||||||
"""
|
"""
|
||||||
Helper class for packing string data.
|
Helper class for packing string data.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
data_type = str
|
data_type = str
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
|
@ -204,6 +209,7 @@ class CoreTlvDataUint16List(CoreTlvData):
|
||||||
"""
|
"""
|
||||||
List of unsigned 16-bit values.
|
List of unsigned 16-bit values.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
data_type = tuple
|
data_type = tuple
|
||||||
data_format = "!H"
|
data_format = "!H"
|
||||||
|
|
||||||
|
@ -253,6 +259,7 @@ class CoreTlvDataIpv4Addr(CoreTlvDataObj):
|
||||||
"""
|
"""
|
||||||
Utility class for packing/unpacking Ipv4 addresses.
|
Utility class for packing/unpacking Ipv4 addresses.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
data_type = IpAddress.from_string
|
data_type = IpAddress.from_string
|
||||||
data_format = "!2x4s"
|
data_format = "!2x4s"
|
||||||
pad_len = 2
|
pad_len = 2
|
||||||
|
@ -283,6 +290,7 @@ class CoreTlvDataIPv6Addr(CoreTlvDataObj):
|
||||||
"""
|
"""
|
||||||
Utility class for packing/unpacking Ipv6 addresses.
|
Utility class for packing/unpacking Ipv6 addresses.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
data_format = "!16s2x"
|
data_format = "!16s2x"
|
||||||
data_type = IpAddress.from_string
|
data_type = IpAddress.from_string
|
||||||
pad_len = 2
|
pad_len = 2
|
||||||
|
@ -313,6 +321,7 @@ class CoreTlvDataMacAddr(CoreTlvDataObj):
|
||||||
"""
|
"""
|
||||||
Utility class for packing/unpacking mac addresses.
|
Utility class for packing/unpacking mac addresses.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
data_format = "!2x8s"
|
data_format = "!2x8s"
|
||||||
data_type = MacAddress.from_string
|
data_type = MacAddress.from_string
|
||||||
pad_len = 2
|
pad_len = 2
|
||||||
|
@ -345,6 +354,7 @@ class CoreTlv(object):
|
||||||
"""
|
"""
|
||||||
Base class for representing CORE TLVs.
|
Base class for representing CORE TLVs.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
header_format = "!BB"
|
header_format = "!BB"
|
||||||
header_len = struct.calcsize(header_format)
|
header_len = struct.calcsize(header_format)
|
||||||
|
|
||||||
|
@ -379,10 +389,12 @@ class CoreTlv(object):
|
||||||
:param data: data to unpack
|
:param data: data to unpack
|
||||||
:return: unpacked data class
|
:return: unpacked data class
|
||||||
"""
|
"""
|
||||||
tlv_type, tlv_len = struct.unpack(cls.header_format, data[:cls.header_len])
|
tlv_type, tlv_len = struct.unpack(cls.header_format, data[: cls.header_len])
|
||||||
header_len = cls.header_len
|
header_len = cls.header_len
|
||||||
if tlv_len == 0:
|
if tlv_len == 0:
|
||||||
tlv_type, _zero, tlv_len = struct.unpack(cls.long_header_format, data[:cls.long_header_len])
|
tlv_type, _zero, tlv_len = struct.unpack(
|
||||||
|
cls.long_header_format, data[: cls.long_header_len]
|
||||||
|
)
|
||||||
header_len = cls.long_header_len
|
header_len = cls.long_header_len
|
||||||
tlv_size = header_len + tlv_len
|
tlv_size = header_len + tlv_len
|
||||||
# for 32-bit alignment
|
# for 32-bit alignment
|
||||||
|
@ -435,7 +447,11 @@ class CoreTlv(object):
|
||||||
:return: string representation
|
:return: string representation
|
||||||
:rtype: str
|
:rtype: str
|
||||||
"""
|
"""
|
||||||
return "%s <tlvtype = %s, value = %s>" % (self.__class__.__name__, self.type_str(), self.value)
|
return "%s <tlvtype = %s, value = %s>" % (
|
||||||
|
self.__class__.__name__,
|
||||||
|
self.type_str(),
|
||||||
|
self.value,
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
class CoreNodeTlv(CoreTlv):
|
class CoreNodeTlv(CoreTlv):
|
||||||
|
@ -686,14 +702,16 @@ class CoreMessage(object):
|
||||||
:return: unpacked tuple
|
:return: unpacked tuple
|
||||||
:rtype: tuple
|
:rtype: tuple
|
||||||
"""
|
"""
|
||||||
message_type, message_flags, message_len = struct.unpack(cls.header_format, data[:cls.header_len])
|
message_type, message_flags, message_len = struct.unpack(
|
||||||
|
cls.header_format, data[: cls.header_len]
|
||||||
|
)
|
||||||
return message_type, message_flags, message_len
|
return message_type, message_flags, message_len
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def create(cls, flags, values):
|
def create(cls, flags, values):
|
||||||
tlv_data = structutils.pack_values(cls.tlv_class, values)
|
tlv_data = structutils.pack_values(cls.tlv_class, values)
|
||||||
packed = cls.pack(flags, tlv_data)
|
packed = cls.pack(flags, tlv_data)
|
||||||
header_data = packed[:cls.header_len]
|
header_data = packed[: cls.header_len]
|
||||||
return cls(flags, header_data, tlv_data)
|
return cls(flags, header_data, tlv_data)
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
|
@ -705,7 +723,9 @@ class CoreMessage(object):
|
||||||
:param tlv_data: data to get length from for packing
|
:param tlv_data: data to get length from for packing
|
||||||
:return: combined header and tlv data
|
:return: combined header and tlv data
|
||||||
"""
|
"""
|
||||||
header = struct.pack(cls.header_format, cls.message_type, message_flags, len(tlv_data))
|
header = struct.pack(
|
||||||
|
cls.header_format, cls.message_type, message_flags, len(tlv_data)
|
||||||
|
)
|
||||||
return header + tlv_data
|
return header + tlv_data
|
||||||
|
|
||||||
def add_tlv_data(self, key, value):
|
def add_tlv_data(self, key, value):
|
||||||
|
@ -807,7 +827,11 @@ class CoreMessage(object):
|
||||||
:return: string representation
|
:return: string representation
|
||||||
:rtype: str
|
:rtype: str
|
||||||
"""
|
"""
|
||||||
result = "%s <msgtype = %s, flags = %s>" % (self.__class__.__name__, self.type_str(), self.flag_str())
|
result = "%s <msgtype = %s, flags = %s>" % (
|
||||||
|
self.__class__.__name__,
|
||||||
|
self.type_str(),
|
||||||
|
self.flag_str(),
|
||||||
|
)
|
||||||
|
|
||||||
for key in self.tlv_data:
|
for key in self.tlv_data:
|
||||||
value = self.tlv_data[key]
|
value = self.tlv_data[key]
|
||||||
|
@ -879,6 +903,7 @@ class CoreNodeMessage(CoreMessage):
|
||||||
"""
|
"""
|
||||||
CORE node message class.
|
CORE node message class.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
message_type = MessageTypes.NODE.value
|
message_type = MessageTypes.NODE.value
|
||||||
tlv_class = CoreNodeTlv
|
tlv_class = CoreNodeTlv
|
||||||
|
|
||||||
|
@ -887,6 +912,7 @@ class CoreLinkMessage(CoreMessage):
|
||||||
"""
|
"""
|
||||||
CORE link message class.
|
CORE link message class.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
message_type = MessageTypes.LINK.value
|
message_type = MessageTypes.LINK.value
|
||||||
tlv_class = CoreLinkTlv
|
tlv_class = CoreLinkTlv
|
||||||
|
|
||||||
|
@ -895,6 +921,7 @@ class CoreExecMessage(CoreMessage):
|
||||||
"""
|
"""
|
||||||
CORE execute message class.
|
CORE execute message class.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
message_type = MessageTypes.EXECUTE.value
|
message_type = MessageTypes.EXECUTE.value
|
||||||
tlv_class = CoreExecuteTlv
|
tlv_class = CoreExecuteTlv
|
||||||
|
|
||||||
|
@ -903,6 +930,7 @@ class CoreRegMessage(CoreMessage):
|
||||||
"""
|
"""
|
||||||
CORE register message class.
|
CORE register message class.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
message_type = MessageTypes.REGISTER.value
|
message_type = MessageTypes.REGISTER.value
|
||||||
tlv_class = CoreRegisterTlv
|
tlv_class = CoreRegisterTlv
|
||||||
|
|
||||||
|
@ -911,6 +939,7 @@ class CoreConfMessage(CoreMessage):
|
||||||
"""
|
"""
|
||||||
CORE configuration message class.
|
CORE configuration message class.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
message_type = MessageTypes.CONFIG.value
|
message_type = MessageTypes.CONFIG.value
|
||||||
tlv_class = CoreConfigTlv
|
tlv_class = CoreConfigTlv
|
||||||
|
|
||||||
|
@ -919,6 +948,7 @@ class CoreFileMessage(CoreMessage):
|
||||||
"""
|
"""
|
||||||
CORE file message class.
|
CORE file message class.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
message_type = MessageTypes.FILE.value
|
message_type = MessageTypes.FILE.value
|
||||||
tlv_class = CoreFileTlv
|
tlv_class = CoreFileTlv
|
||||||
|
|
||||||
|
@ -927,6 +957,7 @@ class CoreIfaceMessage(CoreMessage):
|
||||||
"""
|
"""
|
||||||
CORE interface message class.
|
CORE interface message class.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
message_type = MessageTypes.INTERFACE.value
|
message_type = MessageTypes.INTERFACE.value
|
||||||
tlv_class = CoreInterfaceTlv
|
tlv_class = CoreInterfaceTlv
|
||||||
|
|
||||||
|
@ -935,6 +966,7 @@ class CoreEventMessage(CoreMessage):
|
||||||
"""
|
"""
|
||||||
CORE event message class.
|
CORE event message class.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
message_type = MessageTypes.EVENT.value
|
message_type = MessageTypes.EVENT.value
|
||||||
tlv_class = CoreEventTlv
|
tlv_class = CoreEventTlv
|
||||||
|
|
||||||
|
@ -943,6 +975,7 @@ class CoreSessionMessage(CoreMessage):
|
||||||
"""
|
"""
|
||||||
CORE session message class.
|
CORE session message class.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
message_type = MessageTypes.SESSION.value
|
message_type = MessageTypes.SESSION.value
|
||||||
tlv_class = CoreSessionTlv
|
tlv_class = CoreSessionTlv
|
||||||
|
|
||||||
|
@ -951,6 +984,7 @@ class CoreExceptionMessage(CoreMessage):
|
||||||
"""
|
"""
|
||||||
CORE exception message class.
|
CORE exception message class.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
message_type = MessageTypes.EXCEPTION.value
|
message_type = MessageTypes.EXCEPTION.value
|
||||||
tlv_class = CoreExceptionTlv
|
tlv_class = CoreExceptionTlv
|
||||||
|
|
||||||
|
|
File diff suppressed because it is too large
Load diff
|
@ -12,6 +12,7 @@ class CoreServer(socketserver.ThreadingMixIn, socketserver.TCPServer):
|
||||||
TCP server class, manages sessions and spawns request handlers for
|
TCP server class, manages sessions and spawns request handlers for
|
||||||
incoming connections.
|
incoming connections.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
daemon_threads = True
|
daemon_threads = True
|
||||||
allow_reuse_address = True
|
allow_reuse_address = True
|
||||||
|
|
||||||
|
@ -34,6 +35,7 @@ class CoreUdpServer(socketserver.ThreadingMixIn, socketserver.UDPServer):
|
||||||
UDP server class, manages sessions and spawns request handlers for
|
UDP server class, manages sessions and spawns request handlers for
|
||||||
incoming connections.
|
incoming connections.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
daemon_threads = True
|
daemon_threads = True
|
||||||
allow_reuse_address = True
|
allow_reuse_address = True
|
||||||
|
|
||||||
|
|
|
@ -3,8 +3,7 @@ Converts CORE data objects into legacy API messages.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
from core.api.tlv import coreapi, structutils
|
from core.api.tlv import coreapi, structutils
|
||||||
from core.emulator.enumerations import ConfigTlvs
|
from core.emulator.enumerations import ConfigTlvs, NodeTlvs
|
||||||
from core.emulator.enumerations import NodeTlvs
|
|
||||||
|
|
||||||
|
|
||||||
def convert_node(node_data):
|
def convert_node(node_data):
|
||||||
|
@ -14,28 +13,31 @@ def convert_node(node_data):
|
||||||
:param core.emulator.data.NodeData node_data: node data to convert
|
:param core.emulator.data.NodeData node_data: node data to convert
|
||||||
:return: packed node message
|
:return: packed node message
|
||||||
"""
|
"""
|
||||||
tlv_data = structutils.pack_values(coreapi.CoreNodeTlv, [
|
tlv_data = structutils.pack_values(
|
||||||
(NodeTlvs.NUMBER, node_data.id),
|
coreapi.CoreNodeTlv,
|
||||||
(NodeTlvs.TYPE, node_data.node_type),
|
[
|
||||||
(NodeTlvs.NAME, node_data.name),
|
(NodeTlvs.NUMBER, node_data.id),
|
||||||
(NodeTlvs.IP_ADDRESS, node_data.ip_address),
|
(NodeTlvs.TYPE, node_data.node_type),
|
||||||
(NodeTlvs.MAC_ADDRESS, node_data.mac_address),
|
(NodeTlvs.NAME, node_data.name),
|
||||||
(NodeTlvs.IP6_ADDRESS, node_data.ip6_address),
|
(NodeTlvs.IP_ADDRESS, node_data.ip_address),
|
||||||
(NodeTlvs.MODEL, node_data.model),
|
(NodeTlvs.MAC_ADDRESS, node_data.mac_address),
|
||||||
(NodeTlvs.EMULATION_ID, node_data.emulation_id),
|
(NodeTlvs.IP6_ADDRESS, node_data.ip6_address),
|
||||||
(NodeTlvs.EMULATION_SERVER, node_data.emulation_server),
|
(NodeTlvs.MODEL, node_data.model),
|
||||||
(NodeTlvs.SESSION, node_data.session),
|
(NodeTlvs.EMULATION_ID, node_data.emulation_id),
|
||||||
(NodeTlvs.X_POSITION, node_data.x_position),
|
(NodeTlvs.EMULATION_SERVER, node_data.emulation_server),
|
||||||
(NodeTlvs.Y_POSITION, node_data.y_position),
|
(NodeTlvs.SESSION, node_data.session),
|
||||||
(NodeTlvs.CANVAS, node_data.canvas),
|
(NodeTlvs.X_POSITION, node_data.x_position),
|
||||||
(NodeTlvs.NETWORK_ID, node_data.network_id),
|
(NodeTlvs.Y_POSITION, node_data.y_position),
|
||||||
(NodeTlvs.SERVICES, node_data.services),
|
(NodeTlvs.CANVAS, node_data.canvas),
|
||||||
(NodeTlvs.LATITUDE, node_data.latitude),
|
(NodeTlvs.NETWORK_ID, node_data.network_id),
|
||||||
(NodeTlvs.LONGITUDE, node_data.longitude),
|
(NodeTlvs.SERVICES, node_data.services),
|
||||||
(NodeTlvs.ALTITUDE, node_data.altitude),
|
(NodeTlvs.LATITUDE, node_data.latitude),
|
||||||
(NodeTlvs.ICON, node_data.icon),
|
(NodeTlvs.LONGITUDE, node_data.longitude),
|
||||||
(NodeTlvs.OPAQUE, node_data.opaque)
|
(NodeTlvs.ALTITUDE, node_data.altitude),
|
||||||
])
|
(NodeTlvs.ICON, node_data.icon),
|
||||||
|
(NodeTlvs.OPAQUE, node_data.opaque),
|
||||||
|
],
|
||||||
|
)
|
||||||
return coreapi.CoreNodeMessage.pack(node_data.message_type, tlv_data)
|
return coreapi.CoreNodeMessage.pack(node_data.message_type, tlv_data)
|
||||||
|
|
||||||
|
|
||||||
|
@ -46,19 +48,22 @@ def convert_config(config_data):
|
||||||
:param core.emulator.data.ConfigData config_data: config data to convert
|
:param core.emulator.data.ConfigData config_data: config data to convert
|
||||||
:return: packed message
|
:return: packed message
|
||||||
"""
|
"""
|
||||||
tlv_data = structutils.pack_values(coreapi.CoreConfigTlv, [
|
tlv_data = structutils.pack_values(
|
||||||
(ConfigTlvs.NODE, config_data.node),
|
coreapi.CoreConfigTlv,
|
||||||
(ConfigTlvs.OBJECT, config_data.object),
|
[
|
||||||
(ConfigTlvs.TYPE, config_data.type),
|
(ConfigTlvs.NODE, config_data.node),
|
||||||
(ConfigTlvs.DATA_TYPES, config_data.data_types),
|
(ConfigTlvs.OBJECT, config_data.object),
|
||||||
(ConfigTlvs.VALUES, config_data.data_values),
|
(ConfigTlvs.TYPE, config_data.type),
|
||||||
(ConfigTlvs.CAPTIONS, config_data.captions),
|
(ConfigTlvs.DATA_TYPES, config_data.data_types),
|
||||||
(ConfigTlvs.BITMAP, config_data.bitmap),
|
(ConfigTlvs.VALUES, config_data.data_values),
|
||||||
(ConfigTlvs.POSSIBLE_VALUES, config_data.possible_values),
|
(ConfigTlvs.CAPTIONS, config_data.captions),
|
||||||
(ConfigTlvs.GROUPS, config_data.groups),
|
(ConfigTlvs.BITMAP, config_data.bitmap),
|
||||||
(ConfigTlvs.SESSION, config_data.session),
|
(ConfigTlvs.POSSIBLE_VALUES, config_data.possible_values),
|
||||||
(ConfigTlvs.INTERFACE_NUMBER, config_data.interface_number),
|
(ConfigTlvs.GROUPS, config_data.groups),
|
||||||
(ConfigTlvs.NETWORK_ID, config_data.network_id),
|
(ConfigTlvs.SESSION, config_data.session),
|
||||||
(ConfigTlvs.OPAQUE, config_data.opaque),
|
(ConfigTlvs.INTERFACE_NUMBER, config_data.interface_number),
|
||||||
])
|
(ConfigTlvs.NETWORK_ID, config_data.network_id),
|
||||||
|
(ConfigTlvs.OPAQUE, config_data.opaque),
|
||||||
|
],
|
||||||
|
)
|
||||||
return coreapi.CoreConfMessage.pack(config_data.message_type, tlv_data)
|
return coreapi.CoreConfMessage.pack(config_data.message_type, tlv_data)
|
||||||
|
|
|
@ -3,6 +3,7 @@ Utilities for working with python struct data.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
import logging
|
import logging
|
||||||
|
|
||||||
from past.builtins import basestring
|
from past.builtins import basestring
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -40,7 +40,11 @@ class ConfigShim(object):
|
||||||
"""
|
"""
|
||||||
group_strings = []
|
group_strings = []
|
||||||
for config_group in config_groups:
|
for config_group in config_groups:
|
||||||
group_string = "%s:%s-%s" % (config_group.name, config_group.start, config_group.stop)
|
group_string = "%s:%s-%s" % (
|
||||||
|
config_group.name,
|
||||||
|
config_group.start,
|
||||||
|
config_group.stop,
|
||||||
|
)
|
||||||
group_strings.append(group_string)
|
group_strings.append(group_string)
|
||||||
return "|".join(group_strings)
|
return "|".join(group_strings)
|
||||||
|
|
||||||
|
@ -96,7 +100,7 @@ class ConfigShim(object):
|
||||||
captions=captions,
|
captions=captions,
|
||||||
possible_values="|".join(possible_values),
|
possible_values="|".join(possible_values),
|
||||||
bitmap=configurable_options.bitmap,
|
bitmap=configurable_options.bitmap,
|
||||||
groups=groups_str
|
groups=groups_str,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
@ -127,13 +131,19 @@ class Configuration(object):
|
||||||
|
|
||||||
def __str__(self):
|
def __str__(self):
|
||||||
return "%s(id=%s, type=%s, default=%s, options=%s)" % (
|
return "%s(id=%s, type=%s, default=%s, options=%s)" % (
|
||||||
self.__class__.__name__, self.id, self.type, self.default, self.options)
|
self.__class__.__name__,
|
||||||
|
self.id,
|
||||||
|
self.type,
|
||||||
|
self.default,
|
||||||
|
self.options,
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
class ConfigurableManager(object):
|
class ConfigurableManager(object):
|
||||||
"""
|
"""
|
||||||
Provides convenience methods for storing and retrieving configuration options for nodes.
|
Provides convenience methods for storing and retrieving configuration options for nodes.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
_default_node = -1
|
_default_node = -1
|
||||||
_default_type = _default_node
|
_default_type = _default_node
|
||||||
|
|
||||||
|
@ -187,11 +197,15 @@ class ConfigurableManager(object):
|
||||||
:param str config_type: configuration type to store configuration for
|
:param str config_type: configuration type to store configuration for
|
||||||
:return: nothing
|
:return: nothing
|
||||||
"""
|
"""
|
||||||
logging.debug("setting config for node(%s) type(%s): %s", node_id, config_type, config)
|
logging.debug(
|
||||||
|
"setting config for node(%s) type(%s): %s", node_id, config_type, config
|
||||||
|
)
|
||||||
node_configs = self.node_configurations.setdefault(node_id, OrderedDict())
|
node_configs = self.node_configurations.setdefault(node_id, OrderedDict())
|
||||||
node_configs[config_type] = config
|
node_configs[config_type] = config
|
||||||
|
|
||||||
def get_config(self, _id, node_id=_default_node, config_type=_default_type, default=None):
|
def get_config(
|
||||||
|
self, _id, node_id=_default_node, config_type=_default_type, default=None
|
||||||
|
):
|
||||||
"""
|
"""
|
||||||
Retrieves a specific configuration for a node and configuration type.
|
Retrieves a specific configuration for a node and configuration type.
|
||||||
|
|
||||||
|
@ -256,6 +270,7 @@ class ConfigurableOptions(object):
|
||||||
"""
|
"""
|
||||||
Provides a base for defining configuration options within CORE.
|
Provides a base for defining configuration options within CORE.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
name = None
|
name = None
|
||||||
bitmap = None
|
bitmap = None
|
||||||
options = []
|
options = []
|
||||||
|
@ -278,9 +293,7 @@ class ConfigurableOptions(object):
|
||||||
:return: configuration group definition
|
:return: configuration group definition
|
||||||
:rtype: list[ConfigGroup]
|
:rtype: list[ConfigGroup]
|
||||||
"""
|
"""
|
||||||
return [
|
return [ConfigGroup("Options", 1, len(cls.configurations()))]
|
||||||
ConfigGroup("Options", 1, len(cls.configurations()))
|
|
||||||
]
|
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def default_values(cls):
|
def default_values(cls):
|
||||||
|
@ -290,7 +303,9 @@ class ConfigurableOptions(object):
|
||||||
:return: ordered configuration mapping default values
|
:return: ordered configuration mapping default values
|
||||||
:rtype: OrderedDict
|
:rtype: OrderedDict
|
||||||
"""
|
"""
|
||||||
return OrderedDict([(config.id, config.default) for config in cls.configurations()])
|
return OrderedDict(
|
||||||
|
[(config.id, config.default) for config in cls.configurations()]
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
class ModelManager(ConfigurableManager):
|
class ModelManager(ConfigurableManager):
|
||||||
|
@ -336,7 +351,7 @@ class ModelManager(ConfigurableManager):
|
||||||
|
|
||||||
def get_model_config(self, node_id, model_name):
|
def get_model_config(self, node_id, model_name):
|
||||||
"""
|
"""
|
||||||
Set configuration data for a model.
|
Retrieve configuration data for a model.
|
||||||
|
|
||||||
:param int node_id: node id to set model configuration for
|
:param int node_id: node id to set model configuration for
|
||||||
:param str model_name: model to set configuration for
|
:param str model_name: model to set configuration for
|
||||||
|
@ -365,7 +380,9 @@ class ModelManager(ConfigurableManager):
|
||||||
:param dict config: model configuration, None for default configuration
|
:param dict config: model configuration, None for default configuration
|
||||||
:return: nothing
|
:return: nothing
|
||||||
"""
|
"""
|
||||||
logging.info("setting mobility model(%s) for node(%s): %s", model_class.name, node.id, config)
|
logging.debug(
|
||||||
|
"setting model(%s) for node(%s): %s", model_class.name, node.id, config
|
||||||
|
)
|
||||||
self.set_model_config(node.id, model_class.name, config)
|
self.set_model_config(node.id, model_class.name, config)
|
||||||
config = self.get_model_config(node.id, model_class.name)
|
config = self.get_model_config(node.id, model_class.name)
|
||||||
node.setmodel(model_class, config)
|
node.setmodel(model_class, config)
|
||||||
|
|
|
@ -1,8 +1,7 @@
|
||||||
"""
|
"""
|
||||||
EMANE Bypass model for CORE
|
EMANE Bypass model for CORE
|
||||||
"""
|
"""
|
||||||
from core.config import ConfigGroup
|
from core.config import ConfigGroup, Configuration
|
||||||
from core.config import Configuration
|
|
||||||
from core.emane import emanemodel
|
from core.emane import emanemodel
|
||||||
from core.emulator.enumerations import ConfigDataTypes
|
from core.emulator.enumerations import ConfigDataTypes
|
||||||
|
|
||||||
|
@ -21,7 +20,7 @@ class EmaneBypassModel(emanemodel.EmaneModel):
|
||||||
_type=ConfigDataTypes.BOOL,
|
_type=ConfigDataTypes.BOOL,
|
||||||
default="0",
|
default="0",
|
||||||
options=["True", "False"],
|
options=["True", "False"],
|
||||||
label="There are no parameters for the bypass model."
|
label="There are no parameters for the bypass model.",
|
||||||
)
|
)
|
||||||
]
|
]
|
||||||
|
|
||||||
|
@ -37,6 +36,4 @@ class EmaneBypassModel(emanemodel.EmaneModel):
|
||||||
# override config groups
|
# override config groups
|
||||||
@classmethod
|
@classmethod
|
||||||
def config_groups(cls):
|
def config_groups(cls):
|
||||||
return [
|
return [ConfigGroup("Bypass Parameters", 1, 1)]
|
||||||
ConfigGroup("Bypass Parameters", 1, 1),
|
|
||||||
]
|
|
||||||
|
|
|
@ -4,12 +4,13 @@ commeffect.py: EMANE CommEffect model for CORE
|
||||||
|
|
||||||
import logging
|
import logging
|
||||||
import os
|
import os
|
||||||
|
from builtins import int
|
||||||
|
|
||||||
from lxml import etree
|
from lxml import etree
|
||||||
from past.builtins import basestring
|
from past.builtins import basestring
|
||||||
|
|
||||||
from core.config import ConfigGroup
|
from core.config import ConfigGroup
|
||||||
from core.emane import emanemanifest
|
from core.emane import emanemanifest, emanemodel
|
||||||
from core.emane import emanemodel
|
|
||||||
from core.xml import emanexml
|
from core.xml import emanexml
|
||||||
|
|
||||||
try:
|
try:
|
||||||
|
@ -56,9 +57,7 @@ class EmaneCommEffectModel(emanemodel.EmaneModel):
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def config_groups(cls):
|
def config_groups(cls):
|
||||||
return [
|
return [ConfigGroup("CommEffect SHIM Parameters", 1, len(cls.configurations()))]
|
||||||
ConfigGroup("CommEffect SHIM Parameters", 1, len(cls.configurations()))
|
|
||||||
]
|
|
||||||
|
|
||||||
def build_xml_files(self, config, interface=None):
|
def build_xml_files(self, config, interface=None):
|
||||||
"""
|
"""
|
||||||
|
@ -76,7 +75,9 @@ class EmaneCommEffectModel(emanemodel.EmaneModel):
|
||||||
shim_name = emanexml.shim_file_name(self, interface)
|
shim_name = emanexml.shim_file_name(self, interface)
|
||||||
|
|
||||||
# create and write nem document
|
# create and write nem document
|
||||||
nem_element = etree.Element("nem", name="%s NEM" % self.name, type="unstructured")
|
nem_element = etree.Element(
|
||||||
|
"nem", name="%s NEM" % self.name, type="unstructured"
|
||||||
|
)
|
||||||
transport_type = "virtual"
|
transport_type = "virtual"
|
||||||
if interface and interface.transport_type == "raw":
|
if interface and interface.transport_type == "raw":
|
||||||
transport_type = "raw"
|
transport_type = "raw"
|
||||||
|
@ -90,7 +91,9 @@ class EmaneCommEffectModel(emanemodel.EmaneModel):
|
||||||
emanexml.create_file(nem_element, "nem", nem_file)
|
emanexml.create_file(nem_element, "nem", nem_file)
|
||||||
|
|
||||||
# create and write shim document
|
# create and write shim document
|
||||||
shim_element = etree.Element("shim", name="%s SHIM" % self.name, library=self.shim_library)
|
shim_element = etree.Element(
|
||||||
|
"shim", name="%s SHIM" % self.name, library=self.shim_library
|
||||||
|
)
|
||||||
|
|
||||||
# append all shim options (except filterfile) to shimdoc
|
# append all shim options (except filterfile) to shimdoc
|
||||||
for configuration in self.config_shim:
|
for configuration in self.config_shim:
|
||||||
|
@ -108,7 +111,16 @@ class EmaneCommEffectModel(emanemodel.EmaneModel):
|
||||||
shim_file = os.path.join(self.session.session_dir, shim_name)
|
shim_file = os.path.join(self.session.session_dir, shim_name)
|
||||||
emanexml.create_file(shim_element, "shim", shim_file)
|
emanexml.create_file(shim_element, "shim", shim_file)
|
||||||
|
|
||||||
def linkconfig(self, netif, bw=None, delay=None, loss=None, duplicate=None, jitter=None, netif2=None):
|
def linkconfig(
|
||||||
|
self,
|
||||||
|
netif,
|
||||||
|
bw=None,
|
||||||
|
delay=None,
|
||||||
|
loss=None,
|
||||||
|
duplicate=None,
|
||||||
|
jitter=None,
|
||||||
|
netif2=None,
|
||||||
|
):
|
||||||
"""
|
"""
|
||||||
Generate CommEffect events when a Link Message is received having
|
Generate CommEffect events when a Link Message is received having
|
||||||
link parameters.
|
link parameters.
|
||||||
|
@ -136,7 +148,7 @@ class EmaneCommEffectModel(emanemodel.EmaneModel):
|
||||||
jitter=convert_none(jitter),
|
jitter=convert_none(jitter),
|
||||||
loss=convert_none(loss),
|
loss=convert_none(loss),
|
||||||
duplicate=convert_none(duplicate),
|
duplicate=convert_none(duplicate),
|
||||||
unicast=long(convert_none(bw)),
|
unicast=int(convert_none(bw)),
|
||||||
broadcast=long(convert_none(mbw))
|
broadcast=int(convert_none(mbw)),
|
||||||
)
|
)
|
||||||
service.publish(nemid2, event)
|
service.publish(nemid2, event)
|
||||||
|
|
|
@ -7,13 +7,9 @@ import logging
|
||||||
import os
|
import os
|
||||||
import threading
|
import threading
|
||||||
|
|
||||||
from core import CoreCommandError, utils
|
from core import CoreCommandError, CoreError, constants, utils
|
||||||
from core import constants
|
|
||||||
from core.api.tlv import coreapi, dataconversion
|
from core.api.tlv import coreapi, dataconversion
|
||||||
from core.config import ConfigGroup
|
from core.config import ConfigGroup, ConfigShim, Configuration, ModelManager
|
||||||
from core.config import ConfigShim
|
|
||||||
from core.config import Configuration
|
|
||||||
from core.config import ModelManager
|
|
||||||
from core.emane import emanemanifest
|
from core.emane import emanemanifest
|
||||||
from core.emane.bypass import EmaneBypassModel
|
from core.emane.bypass import EmaneBypassModel
|
||||||
from core.emane.commeffect import EmaneCommEffectModel
|
from core.emane.commeffect import EmaneCommEffectModel
|
||||||
|
@ -21,13 +17,15 @@ from core.emane.emanemodel import EmaneModel
|
||||||
from core.emane.ieee80211abg import EmaneIeee80211abgModel
|
from core.emane.ieee80211abg import EmaneIeee80211abgModel
|
||||||
from core.emane.rfpipe import EmaneRfPipeModel
|
from core.emane.rfpipe import EmaneRfPipeModel
|
||||||
from core.emane.tdma import EmaneTdmaModel
|
from core.emane.tdma import EmaneTdmaModel
|
||||||
from core.emulator.enumerations import ConfigDataTypes
|
from core.emulator.enumerations import (
|
||||||
from core.emulator.enumerations import ConfigFlags
|
ConfigDataTypes,
|
||||||
from core.emulator.enumerations import ConfigTlvs
|
ConfigFlags,
|
||||||
from core.emulator.enumerations import MessageFlags
|
ConfigTlvs,
|
||||||
from core.emulator.enumerations import MessageTypes
|
MessageFlags,
|
||||||
from core.emulator.enumerations import NodeTypes
|
MessageTypes,
|
||||||
from core.emulator.enumerations import RegisterTlvs
|
NodeTypes,
|
||||||
|
RegisterTlvs,
|
||||||
|
)
|
||||||
from core.nodes import nodeutils
|
from core.nodes import nodeutils
|
||||||
from core.xml import emanexml
|
from core.xml import emanexml
|
||||||
|
|
||||||
|
@ -48,7 +46,7 @@ EMANE_MODELS = [
|
||||||
EmaneIeee80211abgModel,
|
EmaneIeee80211abgModel,
|
||||||
EmaneCommEffectModel,
|
EmaneCommEffectModel,
|
||||||
EmaneBypassModel,
|
EmaneBypassModel,
|
||||||
EmaneTdmaModel
|
EmaneTdmaModel,
|
||||||
]
|
]
|
||||||
DEFAULT_EMANE_PREFIX = "/usr"
|
DEFAULT_EMANE_PREFIX = "/usr"
|
||||||
|
|
||||||
|
@ -59,6 +57,7 @@ class EmaneManager(ModelManager):
|
||||||
building EMANE config files from all of the EmaneNode objects in this
|
building EMANE config files from all of the EmaneNode objects in this
|
||||||
emulation, and for controlling the EMANE daemons.
|
emulation, and for controlling the EMANE daemons.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
name = "emane"
|
name = "emane"
|
||||||
config_type = RegisterTlvs.EMULATION_SERVER.value
|
config_type = RegisterTlvs.EMULATION_SERVER.value
|
||||||
SUCCESS, NOT_NEEDED, NOT_READY = (0, 1, 2)
|
SUCCESS, NOT_NEEDED, NOT_READY = (0, 1, 2)
|
||||||
|
@ -79,8 +78,12 @@ class EmaneManager(ModelManager):
|
||||||
self._ifccounts = {}
|
self._ifccounts = {}
|
||||||
self._ifccountslock = threading.Lock()
|
self._ifccountslock = threading.Lock()
|
||||||
# port numbers are allocated from these counters
|
# port numbers are allocated from these counters
|
||||||
self.platformport = self.session.options.get_config_int("emane_platform_port", 8100)
|
self.platformport = self.session.options.get_config_int(
|
||||||
self.transformport = self.session.options.get_config_int("emane_transform_port", 8200)
|
"emane_platform_port", 8100
|
||||||
|
)
|
||||||
|
self.transformport = self.session.options.get_config_int(
|
||||||
|
"emane_transform_port", 8200
|
||||||
|
)
|
||||||
self.doeventloop = False
|
self.doeventloop = False
|
||||||
self.eventmonthread = None
|
self.eventmonthread = None
|
||||||
|
|
||||||
|
@ -124,7 +127,9 @@ class EmaneManager(ModelManager):
|
||||||
|
|
||||||
# otherwise retrieve the interfaces node configuration, avoid using defaults
|
# otherwise retrieve the interfaces node configuration, avoid using defaults
|
||||||
if not config:
|
if not config:
|
||||||
config = self.get_configs(node_id=interface.node.id, config_type=model_name)
|
config = self.get_configs(
|
||||||
|
node_id=interface.node.id, config_type=model_name
|
||||||
|
)
|
||||||
|
|
||||||
# get non interface config, when none found
|
# get non interface config, when none found
|
||||||
if not config:
|
if not config:
|
||||||
|
@ -186,11 +191,15 @@ class EmaneManager(ModelManager):
|
||||||
self.event_device = self.get_config("eventservicedevice")
|
self.event_device = self.get_config("eventservicedevice")
|
||||||
eventnetidx = self.session.get_control_net_index(self.event_device)
|
eventnetidx = self.session.get_control_net_index(self.event_device)
|
||||||
if eventnetidx < 0:
|
if eventnetidx < 0:
|
||||||
logging.error("invalid emane event service device provided: %s", self.event_device)
|
logging.error(
|
||||||
|
"invalid emane event service device provided: %s", self.event_device
|
||||||
|
)
|
||||||
return False
|
return False
|
||||||
|
|
||||||
# make sure the event control network is in place
|
# make sure the event control network is in place
|
||||||
eventnet = self.session.add_remove_control_net(net_index=eventnetidx, remove=False, conf_required=False)
|
eventnet = self.session.add_remove_control_net(
|
||||||
|
net_index=eventnetidx, remove=False, conf_required=False
|
||||||
|
)
|
||||||
if eventnet is not None:
|
if eventnet is not None:
|
||||||
# direct EMANE events towards control net bridge
|
# direct EMANE events towards control net bridge
|
||||||
self.event_device = eventnet.brname
|
self.event_device = eventnet.brname
|
||||||
|
@ -211,8 +220,10 @@ class EmaneManager(ModelManager):
|
||||||
Load EMANE models and make them available.
|
Load EMANE models and make them available.
|
||||||
"""
|
"""
|
||||||
for emane_model in emane_models:
|
for emane_model in emane_models:
|
||||||
logging.info("loading emane model: %s", emane_model.__name__)
|
logging.debug("loading emane model: %s", emane_model.__name__)
|
||||||
emane_prefix = self.session.options.get_config("emane_prefix", default=DEFAULT_EMANE_PREFIX)
|
emane_prefix = self.session.options.get_config(
|
||||||
|
"emane_prefix", default=DEFAULT_EMANE_PREFIX
|
||||||
|
)
|
||||||
emane_model.load(emane_prefix)
|
emane_model.load(emane_prefix)
|
||||||
self.models[emane_model.name] = emane_model
|
self.models[emane_model.name] = emane_model
|
||||||
|
|
||||||
|
@ -225,7 +236,9 @@ class EmaneManager(ModelManager):
|
||||||
"""
|
"""
|
||||||
with self._emane_node_lock:
|
with self._emane_node_lock:
|
||||||
if emane_node.id in self._emane_nodes:
|
if emane_node.id in self._emane_nodes:
|
||||||
raise KeyError("non-unique EMANE object id %s for %s" % (emane_node.id, emane_node))
|
raise KeyError(
|
||||||
|
"non-unique EMANE object id %s for %s" % (emane_node.id, emane_node)
|
||||||
|
)
|
||||||
self._emane_nodes[emane_node.id] = emane_node
|
self._emane_nodes[emane_node.id] = emane_node
|
||||||
|
|
||||||
def getnodes(self):
|
def getnodes(self):
|
||||||
|
@ -254,7 +267,9 @@ class EmaneManager(ModelManager):
|
||||||
for node_id in self.session.nodes:
|
for node_id in self.session.nodes:
|
||||||
node = self.session.nodes[node_id]
|
node = self.session.nodes[node_id]
|
||||||
if nodeutils.is_node(node, NodeTypes.EMANE):
|
if nodeutils.is_node(node, NodeTypes.EMANE):
|
||||||
logging.debug("adding emane node: id(%s) name(%s)", node.id, node.name)
|
logging.debug(
|
||||||
|
"adding emane node: id(%s) name(%s)", node.id, node.name
|
||||||
|
)
|
||||||
self.add_node(node)
|
self.add_node(node)
|
||||||
|
|
||||||
if not self._emane_nodes:
|
if not self._emane_nodes:
|
||||||
|
@ -267,12 +282,19 @@ class EmaneManager(ModelManager):
|
||||||
if self.session.master:
|
if self.session.master:
|
||||||
otadev = self.get_config("otamanagerdevice")
|
otadev = self.get_config("otamanagerdevice")
|
||||||
netidx = self.session.get_control_net_index(otadev)
|
netidx = self.session.get_control_net_index(otadev)
|
||||||
logging.debug("emane ota manager device: index(%s) otadev(%s)", netidx, otadev)
|
logging.debug(
|
||||||
|
"emane ota manager device: index(%s) otadev(%s)", netidx, otadev
|
||||||
|
)
|
||||||
if netidx < 0:
|
if netidx < 0:
|
||||||
logging.error("EMANE cannot start, check core config. invalid OTA device provided: %s", otadev)
|
logging.error(
|
||||||
|
"EMANE cannot start, check core config. invalid OTA device provided: %s",
|
||||||
|
otadev,
|
||||||
|
)
|
||||||
return EmaneManager.NOT_READY
|
return EmaneManager.NOT_READY
|
||||||
|
|
||||||
ctrlnet = self.session.add_remove_control_net(net_index=netidx, remove=False, conf_required=False)
|
ctrlnet = self.session.add_remove_control_net(
|
||||||
|
net_index=netidx, remove=False, conf_required=False
|
||||||
|
)
|
||||||
self.distributedctrlnet(ctrlnet)
|
self.distributedctrlnet(ctrlnet)
|
||||||
eventdev = self.get_config("eventservicedevice")
|
eventdev = self.get_config("eventservicedevice")
|
||||||
logging.debug("emane event service device: eventdev(%s)", eventdev)
|
logging.debug("emane event service device: eventdev(%s)", eventdev)
|
||||||
|
@ -280,10 +302,15 @@ class EmaneManager(ModelManager):
|
||||||
netidx = self.session.get_control_net_index(eventdev)
|
netidx = self.session.get_control_net_index(eventdev)
|
||||||
logging.debug("emane event service device index: %s", netidx)
|
logging.debug("emane event service device index: %s", netidx)
|
||||||
if netidx < 0:
|
if netidx < 0:
|
||||||
logging.error("EMANE cannot start, check core config. invalid event service device: %s", eventdev)
|
logging.error(
|
||||||
|
"EMANE cannot start, check core config. invalid event service device: %s",
|
||||||
|
eventdev,
|
||||||
|
)
|
||||||
return EmaneManager.NOT_READY
|
return EmaneManager.NOT_READY
|
||||||
|
|
||||||
ctrlnet = self.session.add_remove_control_net(net_index=netidx, remove=False, conf_required=False)
|
ctrlnet = self.session.add_remove_control_net(
|
||||||
|
net_index=netidx, remove=False, conf_required=False
|
||||||
|
)
|
||||||
self.distributedctrlnet(ctrlnet)
|
self.distributedctrlnet(ctrlnet)
|
||||||
|
|
||||||
if self.checkdistributed():
|
if self.checkdistributed():
|
||||||
|
@ -323,7 +350,9 @@ class EmaneManager(ModelManager):
|
||||||
for node_id in self._emane_nodes:
|
for node_id in self._emane_nodes:
|
||||||
emane_node = self._emane_nodes[node_id]
|
emane_node = self._emane_nodes[node_id]
|
||||||
for netif in emane_node.netifs():
|
for netif in emane_node.netifs():
|
||||||
nems.append((netif.node.name, netif.name, emane_node.getnemid(netif)))
|
nems.append(
|
||||||
|
(netif.node.name, netif.name, emane_node.getnemid(netif))
|
||||||
|
)
|
||||||
|
|
||||||
if nems:
|
if nems:
|
||||||
emane_nems_filename = os.path.join(self.session.session_dir, "emane_nems")
|
emane_nems_filename = os.path.join(self.session.session_dir, "emane_nems")
|
||||||
|
@ -346,7 +375,11 @@ class EmaneManager(ModelManager):
|
||||||
with self._emane_node_lock:
|
with self._emane_node_lock:
|
||||||
for key in sorted(self._emane_nodes.keys()):
|
for key in sorted(self._emane_nodes.keys()):
|
||||||
emane_node = self._emane_nodes[key]
|
emane_node = self._emane_nodes[key]
|
||||||
logging.debug("post startup for emane node: %s - %s", emane_node.id, emane_node.name)
|
logging.debug(
|
||||||
|
"post startup for emane node: %s - %s",
|
||||||
|
emane_node.id,
|
||||||
|
emane_node.name,
|
||||||
|
)
|
||||||
emane_node.model.post_startup()
|
emane_node.model.post_startup()
|
||||||
for netif in emane_node.netifs():
|
for netif in emane_node.netifs():
|
||||||
x, y, z = netif.node.position.get()
|
x, y, z = netif.node.position.get()
|
||||||
|
@ -361,8 +394,12 @@ class EmaneManager(ModelManager):
|
||||||
self._emane_nodes.clear()
|
self._emane_nodes.clear()
|
||||||
|
|
||||||
# don't clear self._ifccounts here; NEM counts are needed for buildxml
|
# don't clear self._ifccounts here; NEM counts are needed for buildxml
|
||||||
self.platformport = self.session.options.get_config_int("emane_platform_port", 8100)
|
self.platformport = self.session.options.get_config_int(
|
||||||
self.transformport = self.session.options.get_config_int("emane_transform_port", 8200)
|
"emane_platform_port", 8100
|
||||||
|
)
|
||||||
|
self.transformport = self.session.options.get_config_int(
|
||||||
|
"emane_transform_port", 8200
|
||||||
|
)
|
||||||
|
|
||||||
def shutdown(self):
|
def shutdown(self):
|
||||||
"""
|
"""
|
||||||
|
@ -385,7 +422,10 @@ class EmaneManager(ModelManager):
|
||||||
received. This is used to snoop the Link add messages to get NEM
|
received. This is used to snoop the Link add messages to get NEM
|
||||||
counts of NEMs that exist on other servers.
|
counts of NEMs that exist on other servers.
|
||||||
"""
|
"""
|
||||||
if message.message_type == MessageTypes.LINK.value and message.flags & MessageFlags.ADD.value:
|
if (
|
||||||
|
message.message_type == MessageTypes.LINK.value
|
||||||
|
and message.flags & MessageFlags.ADD.value
|
||||||
|
):
|
||||||
nn = message.node_numbers()
|
nn = message.node_numbers()
|
||||||
# first node is always link layer node in Link add message
|
# first node is always link layer node in Link add message
|
||||||
if nn[0] in self.session.broker.network_nodes:
|
if nn[0] in self.session.broker.network_nodes:
|
||||||
|
@ -450,7 +490,9 @@ class EmaneManager(ModelManager):
|
||||||
config = copy.deepcopy(self.get_configs())
|
config = copy.deepcopy(self.get_configs())
|
||||||
config["platform_id_start"] = str(platformid)
|
config["platform_id_start"] = str(platformid)
|
||||||
config["nem_id_start"] = str(nemid)
|
config["nem_id_start"] = str(nemid)
|
||||||
config_data = ConfigShim.config_data(0, None, typeflags, self.emane_config, config)
|
config_data = ConfigShim.config_data(
|
||||||
|
0, None, typeflags, self.emane_config, config
|
||||||
|
)
|
||||||
message = dataconversion.convert_config(config_data)
|
message = dataconversion.convert_config(config_data)
|
||||||
server.sock.send(message)
|
server.sock.send(message)
|
||||||
# increment nemid for next server by number of interfaces
|
# increment nemid for next server by number of interfaces
|
||||||
|
@ -469,7 +511,9 @@ class EmaneManager(ModelManager):
|
||||||
# assume self._objslock is already held here
|
# assume self._objslock is already held here
|
||||||
logging.info("emane building xml...")
|
logging.info("emane building xml...")
|
||||||
# on master, control network bridge added earlier in startup()
|
# on master, control network bridge added earlier in startup()
|
||||||
ctrlnet = self.session.add_remove_control_net(net_index=0, remove=False, conf_required=False)
|
ctrlnet = self.session.add_remove_control_net(
|
||||||
|
net_index=0, remove=False, conf_required=False
|
||||||
|
)
|
||||||
self.buildplatformxml(ctrlnet)
|
self.buildplatformxml(ctrlnet)
|
||||||
self.buildnemxml()
|
self.buildnemxml()
|
||||||
self.buildeventservicexml()
|
self.buildeventservicexml()
|
||||||
|
@ -495,7 +539,10 @@ class EmaneManager(ModelManager):
|
||||||
prefix = session.options.get_config("controlnet", default="")
|
prefix = session.options.get_config("controlnet", default="")
|
||||||
prefixes = prefix.split()
|
prefixes = prefix.split()
|
||||||
if len(prefixes) < len(servers):
|
if len(prefixes) < len(servers):
|
||||||
logging.info("setting up default controlnet prefixes for distributed (%d configured)", len(prefixes))
|
logging.info(
|
||||||
|
"setting up default controlnet prefixes for distributed (%d configured)",
|
||||||
|
len(prefixes),
|
||||||
|
)
|
||||||
prefix = ctrlnet.DEFAULT_PREFIX_LIST[0]
|
prefix = ctrlnet.DEFAULT_PREFIX_LIST[0]
|
||||||
prefixes = prefix.split()
|
prefixes = prefix.split()
|
||||||
servers.remove("localhost")
|
servers.remove("localhost")
|
||||||
|
@ -510,8 +557,10 @@ class EmaneManager(ModelManager):
|
||||||
tlvdata += coreapi.CoreConfigTlv.pack(ConfigTlvs.TYPE.value, 0)
|
tlvdata += coreapi.CoreConfigTlv.pack(ConfigTlvs.TYPE.value, 0)
|
||||||
tlvdata += coreapi.CoreConfigTlv.pack(ConfigTlvs.VALUES.value, vals)
|
tlvdata += coreapi.CoreConfigTlv.pack(ConfigTlvs.VALUES.value, vals)
|
||||||
rawmsg = coreapi.CoreConfMessage.pack(0, tlvdata)
|
rawmsg = coreapi.CoreConfMessage.pack(0, tlvdata)
|
||||||
msghdr = rawmsg[:coreapi.CoreMessage.header_len]
|
msghdr = rawmsg[: coreapi.CoreMessage.header_len]
|
||||||
msg = coreapi.CoreConfMessage(flags=0, hdr=msghdr, data=rawmsg[coreapi.CoreMessage.header_len:])
|
msg = coreapi.CoreConfMessage(
|
||||||
|
flags=0, hdr=msghdr, data=rawmsg[coreapi.CoreMessage.header_len :]
|
||||||
|
)
|
||||||
logging.debug("sending controlnet message:\n%s", msg)
|
logging.debug("sending controlnet message:\n%s", msg)
|
||||||
self.session.broker.handle_message(msg)
|
self.session.broker.handle_message(msg)
|
||||||
|
|
||||||
|
@ -526,7 +575,11 @@ class EmaneManager(ModelManager):
|
||||||
|
|
||||||
# skip nodes that already have a model set
|
# skip nodes that already have a model set
|
||||||
if emane_node.model:
|
if emane_node.model:
|
||||||
logging.debug("node(%s) already has model(%s)", emane_node.id, emane_node.model.name)
|
logging.debug(
|
||||||
|
"node(%s) already has model(%s)",
|
||||||
|
emane_node.id,
|
||||||
|
emane_node.model.name,
|
||||||
|
)
|
||||||
continue
|
continue
|
||||||
|
|
||||||
# set model configured for node, due to legacy messaging configuration before nodes exist
|
# set model configured for node, due to legacy messaging configuration before nodes exist
|
||||||
|
@ -578,7 +631,9 @@ class EmaneManager(ModelManager):
|
||||||
# assume self._objslock is already held here
|
# assume self._objslock is already held here
|
||||||
for key in sorted(self._emane_nodes.keys()):
|
for key in sorted(self._emane_nodes.keys()):
|
||||||
emane_node = self._emane_nodes[key]
|
emane_node = self._emane_nodes[key]
|
||||||
nemid = emanexml.build_node_platform_xml(self, ctrlnet, emane_node, nemid, platform_xmls)
|
nemid = emanexml.build_node_platform_xml(
|
||||||
|
self, ctrlnet, emane_node, nemid, platform_xmls
|
||||||
|
)
|
||||||
|
|
||||||
def buildnemxml(self):
|
def buildnemxml(self):
|
||||||
"""
|
"""
|
||||||
|
@ -593,7 +648,9 @@ class EmaneManager(ModelManager):
|
||||||
"""
|
"""
|
||||||
Calls emanegentransportxml using a platform.xml file to build the transportdaemon*.xml.
|
Calls emanegentransportxml using a platform.xml file to build the transportdaemon*.xml.
|
||||||
"""
|
"""
|
||||||
utils.check_cmd(["emanegentransportxml", "platform.xml"], cwd=self.session.session_dir)
|
utils.check_cmd(
|
||||||
|
["emanegentransportxml", "platform.xml"], cwd=self.session.session_dir
|
||||||
|
)
|
||||||
|
|
||||||
def buildeventservicexml(self):
|
def buildeventservicexml(self):
|
||||||
"""
|
"""
|
||||||
|
@ -638,7 +695,7 @@ class EmaneManager(ModelManager):
|
||||||
|
|
||||||
emanecmd = ["emane", "-d", "-l", loglevel]
|
emanecmd = ["emane", "-d", "-l", loglevel]
|
||||||
if realtime:
|
if realtime:
|
||||||
emanecmd += "-r",
|
emanecmd += ("-r",)
|
||||||
|
|
||||||
otagroup, _otaport = self.get_config("otamanagergroup").split(":")
|
otagroup, _otaport = self.get_config("otamanagergroup").split(":")
|
||||||
otadev = self.get_config("otamanagerdevice")
|
otadev = self.get_config("otamanagerdevice")
|
||||||
|
@ -657,27 +714,37 @@ class EmaneManager(ModelManager):
|
||||||
n = node.id
|
n = node.id
|
||||||
|
|
||||||
# control network not yet started here
|
# control network not yet started here
|
||||||
self.session.add_remove_control_interface(node, 0, remove=False, conf_required=False)
|
self.session.add_remove_control_interface(
|
||||||
|
node, 0, remove=False, conf_required=False
|
||||||
|
)
|
||||||
|
|
||||||
if otanetidx > 0:
|
if otanetidx > 0:
|
||||||
logging.info("adding ota device ctrl%d", otanetidx)
|
logging.info("adding ota device ctrl%d", otanetidx)
|
||||||
self.session.add_remove_control_interface(node, otanetidx, remove=False, conf_required=False)
|
self.session.add_remove_control_interface(
|
||||||
|
node, otanetidx, remove=False, conf_required=False
|
||||||
|
)
|
||||||
|
|
||||||
if eventservicenetidx >= 0:
|
if eventservicenetidx >= 0:
|
||||||
logging.info("adding event service device ctrl%d", eventservicenetidx)
|
logging.info("adding event service device ctrl%d", eventservicenetidx)
|
||||||
self.session.add_remove_control_interface(node, eventservicenetidx, remove=False, conf_required=False)
|
self.session.add_remove_control_interface(
|
||||||
|
node, eventservicenetidx, remove=False, conf_required=False
|
||||||
|
)
|
||||||
|
|
||||||
# multicast route is needed for OTA data
|
# multicast route is needed for OTA data
|
||||||
args = [constants.IP_BIN, "route", "add", otagroup, "dev", otadev]
|
args = [constants.IP_BIN, "route", "add", otagroup, "dev", otadev]
|
||||||
node.check_cmd(args)
|
node.network_cmd(args)
|
||||||
|
|
||||||
# multicast route is also needed for event data if on control network
|
# multicast route is also needed for event data if on control network
|
||||||
if eventservicenetidx >= 0 and eventgroup != otagroup:
|
if eventservicenetidx >= 0 and eventgroup != otagroup:
|
||||||
args = [constants.IP_BIN, "route", "add", eventgroup, "dev", eventdev]
|
args = [constants.IP_BIN, "route", "add", eventgroup, "dev", eventdev]
|
||||||
node.check_cmd(args)
|
node.network_cmd(args)
|
||||||
|
|
||||||
# start emane
|
# start emane
|
||||||
args = emanecmd + ["-f", os.path.join(path, "emane%d.log" % n), os.path.join(path, "platform%d.xml" % n)]
|
args = emanecmd + [
|
||||||
|
"-f",
|
||||||
|
os.path.join(path, "emane%d.log" % n),
|
||||||
|
os.path.join(path, "platform%d.xml" % n),
|
||||||
|
]
|
||||||
output = node.check_cmd(args)
|
output = node.check_cmd(args)
|
||||||
logging.info("node(%s) emane daemon running: %s", node.name, args)
|
logging.info("node(%s) emane daemon running: %s", node.name, args)
|
||||||
logging.info("node(%s) emane daemon output: %s", node.name, output)
|
logging.info("node(%s) emane daemon output: %s", node.name, output)
|
||||||
|
@ -760,10 +827,12 @@ class EmaneManager(ModelManager):
|
||||||
return
|
return
|
||||||
|
|
||||||
if self.service is None:
|
if self.service is None:
|
||||||
logging.error("Warning: EMANE events will not be generated "
|
logging.error(
|
||||||
"because the emaneeventservice\n binding was "
|
"Warning: EMANE events will not be generated "
|
||||||
"unable to load "
|
"because the emaneeventservice\n binding was "
|
||||||
"(install the python-emaneeventservice bindings)")
|
"unable to load "
|
||||||
|
"(install the python-emaneeventservice bindings)"
|
||||||
|
)
|
||||||
return
|
return
|
||||||
self.doeventloop = True
|
self.doeventloop = True
|
||||||
self.eventmonthread = threading.Thread(target=self.eventmonitorloop)
|
self.eventmonthread = threading.Thread(target=self.eventmonitorloop)
|
||||||
|
@ -792,7 +861,10 @@ class EmaneManager(ModelManager):
|
||||||
"""
|
"""
|
||||||
if self.service is None:
|
if self.service is None:
|
||||||
return
|
return
|
||||||
logging.info("subscribing to EMANE location events. (%s)", threading.currentThread().getName())
|
logging.info(
|
||||||
|
"subscribing to EMANE location events. (%s)",
|
||||||
|
threading.currentThread().getName(),
|
||||||
|
)
|
||||||
while self.doeventloop is True:
|
while self.doeventloop is True:
|
||||||
_uuid, _seq, events = self.service.nextEvent()
|
_uuid, _seq, events = self.service.nextEvent()
|
||||||
|
|
||||||
|
@ -805,7 +877,10 @@ class EmaneManager(ModelManager):
|
||||||
if eid == LocationEvent.IDENTIFIER:
|
if eid == LocationEvent.IDENTIFIER:
|
||||||
self.handlelocationevent(nem, eid, data)
|
self.handlelocationevent(nem, eid, data)
|
||||||
|
|
||||||
logging.info("unsubscribing from EMANE location events. (%s)", threading.currentThread().getName())
|
logging.info(
|
||||||
|
"unsubscribing from EMANE location events. (%s)",
|
||||||
|
threading.currentThread().getName(),
|
||||||
|
)
|
||||||
|
|
||||||
def handlelocationevent(self, rxnemid, eid, data):
|
def handlelocationevent(self, rxnemid, eid, data):
|
||||||
"""
|
"""
|
||||||
|
@ -815,7 +890,11 @@ class EmaneManager(ModelManager):
|
||||||
events.restore(data)
|
events.restore(data)
|
||||||
for event in events:
|
for event in events:
|
||||||
txnemid, attrs = event
|
txnemid, attrs = event
|
||||||
if "latitude" not in attrs or "longitude" not in attrs or "altitude" not in attrs:
|
if (
|
||||||
|
"latitude" not in attrs
|
||||||
|
or "longitude" not in attrs
|
||||||
|
or "altitude" not in attrs
|
||||||
|
):
|
||||||
logging.warning("dropped invalid location event")
|
logging.warning("dropped invalid location event")
|
||||||
continue
|
continue
|
||||||
|
|
||||||
|
@ -844,20 +923,37 @@ class EmaneManager(ModelManager):
|
||||||
x = int(x)
|
x = int(x)
|
||||||
y = int(y)
|
y = int(y)
|
||||||
z = int(z)
|
z = int(z)
|
||||||
logging.info("location event NEM %s (%s, %s, %s) -> (%s, %s, %s)", nemid, lat, lon, alt, x, y, z)
|
logging.info(
|
||||||
|
"location event NEM %s (%s, %s, %s) -> (%s, %s, %s)",
|
||||||
|
nemid,
|
||||||
|
lat,
|
||||||
|
lon,
|
||||||
|
alt,
|
||||||
|
x,
|
||||||
|
y,
|
||||||
|
z,
|
||||||
|
)
|
||||||
xbit_check = x.bit_length() > 16 or x < 0
|
xbit_check = x.bit_length() > 16 or x < 0
|
||||||
ybit_check = y.bit_length() > 16 or y < 0
|
ybit_check = y.bit_length() > 16 or y < 0
|
||||||
zbit_check = z.bit_length() > 16 or z < 0
|
zbit_check = z.bit_length() > 16 or z < 0
|
||||||
if any([xbit_check, ybit_check, zbit_check]):
|
if any([xbit_check, ybit_check, zbit_check]):
|
||||||
logging.error("Unable to build node location message, received lat/long/alt exceeds coordinate "
|
logging.error(
|
||||||
"space: NEM %s (%d, %d, %d)", nemid, x, y, z)
|
"Unable to build node location message, received lat/long/alt exceeds coordinate "
|
||||||
|
"space: NEM %s (%d, %d, %d)",
|
||||||
|
nemid,
|
||||||
|
x,
|
||||||
|
y,
|
||||||
|
z,
|
||||||
|
)
|
||||||
return False
|
return False
|
||||||
|
|
||||||
# generate a node message for this location update
|
# generate a node message for this location update
|
||||||
try:
|
try:
|
||||||
node = self.session.get_node(n)
|
node = self.session.get_node(n)
|
||||||
except KeyError:
|
except CoreError:
|
||||||
logging.exception("location event NEM %s has no corresponding node %s" % (nemid, n))
|
logging.exception(
|
||||||
|
"location event NEM %s has no corresponding node %s" % (nemid, n)
|
||||||
|
)
|
||||||
return False
|
return False
|
||||||
|
|
||||||
# don"t use node.setposition(x,y,z) which generates an event
|
# don"t use node.setposition(x,y,z) which generates an event
|
||||||
|
@ -889,18 +985,26 @@ class EmaneGlobalModel(EmaneModel):
|
||||||
"eventservicedevice": _DEFAULT_DEV,
|
"eventservicedevice": _DEFAULT_DEV,
|
||||||
"eventservicegroup": "224.1.2.8:45703",
|
"eventservicegroup": "224.1.2.8:45703",
|
||||||
"otamanagerdevice": _DEFAULT_DEV,
|
"otamanagerdevice": _DEFAULT_DEV,
|
||||||
"otamanagergroup": "224.1.2.8:45702"
|
"otamanagergroup": "224.1.2.8:45702",
|
||||||
}
|
}
|
||||||
emulator_config = emanemanifest.parse(emulator_xml, emulator_defaults)
|
emulator_config = emanemanifest.parse(emulator_xml, emulator_defaults)
|
||||||
emulator_config.insert(
|
emulator_config.insert(
|
||||||
0,
|
0,
|
||||||
Configuration(_id="platform_id_start", _type=ConfigDataTypes.INT32, default="1",
|
Configuration(
|
||||||
label="Starting Platform ID (core)")
|
_id="platform_id_start",
|
||||||
|
_type=ConfigDataTypes.INT32,
|
||||||
|
default="1",
|
||||||
|
label="Starting Platform ID (core)",
|
||||||
|
),
|
||||||
)
|
)
|
||||||
|
|
||||||
nem_config = [
|
nem_config = [
|
||||||
Configuration(_id="nem_id_start", _type=ConfigDataTypes.INT32, default="1",
|
Configuration(
|
||||||
label="Starting NEM ID (core)")
|
_id="nem_id_start",
|
||||||
|
_type=ConfigDataTypes.INT32,
|
||||||
|
default="1",
|
||||||
|
label="Starting NEM ID (core)",
|
||||||
|
)
|
||||||
]
|
]
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
|
@ -913,7 +1017,7 @@ class EmaneGlobalModel(EmaneModel):
|
||||||
config_len = len(cls.configurations())
|
config_len = len(cls.configurations())
|
||||||
return [
|
return [
|
||||||
ConfigGroup("Platform Attributes", 1, emulator_len),
|
ConfigGroup("Platform Attributes", 1, emulator_len),
|
||||||
ConfigGroup("NEM Parameters", emulator_len + 1, config_len)
|
ConfigGroup("NEM Parameters", emulator_len + 1, config_len),
|
||||||
]
|
]
|
||||||
|
|
||||||
def __init__(self, session, _id=None):
|
def __init__(self, session, _id=None):
|
||||||
|
|
|
@ -122,7 +122,7 @@ def parse(manifest_path, defaults):
|
||||||
_type=config_type_value,
|
_type=config_type_value,
|
||||||
default=config_default,
|
default=config_default,
|
||||||
options=possible,
|
options=possible,
|
||||||
label=config_descriptions
|
label=config_descriptions,
|
||||||
)
|
)
|
||||||
configurations.append(configuration)
|
configurations.append(configuration)
|
||||||
|
|
||||||
|
|
|
@ -4,8 +4,8 @@ Defines Emane Models used within CORE.
|
||||||
import logging
|
import logging
|
||||||
import os
|
import os
|
||||||
|
|
||||||
from core.config import ConfigGroup
|
from core import CoreError
|
||||||
from core.config import Configuration
|
from core.config import ConfigGroup, Configuration
|
||||||
from core.emane import emanemanifest
|
from core.emane import emanemanifest
|
||||||
from core.emulator.enumerations import ConfigDataTypes
|
from core.emulator.enumerations import ConfigDataTypes
|
||||||
from core.location.mobility import WirelessModel
|
from core.location.mobility import WirelessModel
|
||||||
|
@ -18,6 +18,7 @@ class EmaneModel(WirelessModel):
|
||||||
handling configuration messages based on the list of
|
handling configuration messages based on the list of
|
||||||
configurable parameters. Helper functions also live here.
|
configurable parameters. Helper functions also live here.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
# default mac configuration settings
|
# default mac configuration settings
|
||||||
mac_library = None
|
mac_library = None
|
||||||
mac_xml = None
|
mac_xml = None
|
||||||
|
@ -27,18 +28,18 @@ class EmaneModel(WirelessModel):
|
||||||
# default phy configuration settings, using the universal model
|
# default phy configuration settings, using the universal model
|
||||||
phy_library = None
|
phy_library = None
|
||||||
phy_xml = "emanephy.xml"
|
phy_xml = "emanephy.xml"
|
||||||
phy_defaults = {
|
phy_defaults = {"subid": "1", "propagationmodel": "2ray", "noisemode": "none"}
|
||||||
"subid": "1",
|
|
||||||
"propagationmodel": "2ray",
|
|
||||||
"noisemode": "none"
|
|
||||||
}
|
|
||||||
phy_config = []
|
phy_config = []
|
||||||
|
|
||||||
# support for external configurations
|
# support for external configurations
|
||||||
external_config = [
|
external_config = [
|
||||||
Configuration("external", ConfigDataTypes.BOOL, default="0"),
|
Configuration("external", ConfigDataTypes.BOOL, default="0"),
|
||||||
Configuration("platformendpoint", ConfigDataTypes.STRING, default="127.0.0.1:40001"),
|
Configuration(
|
||||||
Configuration("transportendpoint", ConfigDataTypes.STRING, default="127.0.0.1:50002")
|
"platformendpoint", ConfigDataTypes.STRING, default="127.0.0.1:40001"
|
||||||
|
),
|
||||||
|
Configuration(
|
||||||
|
"transportendpoint", ConfigDataTypes.STRING, default="127.0.0.1:50002"
|
||||||
|
),
|
||||||
]
|
]
|
||||||
|
|
||||||
config_ignore = set()
|
config_ignore = set()
|
||||||
|
@ -85,7 +86,7 @@ class EmaneModel(WirelessModel):
|
||||||
return [
|
return [
|
||||||
ConfigGroup("MAC Parameters", 1, mac_len),
|
ConfigGroup("MAC Parameters", 1, mac_len),
|
||||||
ConfigGroup("PHY Parameters", mac_len + 1, phy_len),
|
ConfigGroup("PHY Parameters", mac_len + 1, phy_len),
|
||||||
ConfigGroup("External Parameters", phy_len + 1, config_len)
|
ConfigGroup("External Parameters", phy_len + 1, config_len),
|
||||||
]
|
]
|
||||||
|
|
||||||
def build_xml_files(self, config, interface=None):
|
def build_xml_files(self, config, interface=None):
|
||||||
|
@ -109,7 +110,9 @@ class EmaneModel(WirelessModel):
|
||||||
|
|
||||||
# create nem xml file
|
# create nem xml file
|
||||||
nem_file = os.path.join(self.session.session_dir, nem_name)
|
nem_file = os.path.join(self.session.session_dir, nem_name)
|
||||||
emanexml.create_nem_xml(self, config, nem_file, transport_name, mac_name, phy_name)
|
emanexml.create_nem_xml(
|
||||||
|
self, config, nem_file, transport_name, mac_name, phy_name
|
||||||
|
)
|
||||||
|
|
||||||
# create mac xml file
|
# create mac xml file
|
||||||
mac_file = os.path.join(self.session.session_dir, mac_name)
|
mac_file = os.path.join(self.session.session_dir, mac_name)
|
||||||
|
@ -125,7 +128,7 @@ class EmaneModel(WirelessModel):
|
||||||
|
|
||||||
:return: nothing
|
:return: nothing
|
||||||
"""
|
"""
|
||||||
logging.info("emane model(%s) has no post setup tasks", self.name)
|
logging.debug("emane model(%s) has no post setup tasks", self.name)
|
||||||
|
|
||||||
def update(self, moved, moved_netifs):
|
def update(self, moved, moved_netifs):
|
||||||
"""
|
"""
|
||||||
|
@ -140,10 +143,19 @@ class EmaneModel(WirelessModel):
|
||||||
try:
|
try:
|
||||||
wlan = self.session.get_node(self.id)
|
wlan = self.session.get_node(self.id)
|
||||||
wlan.setnempositions(moved_netifs)
|
wlan.setnempositions(moved_netifs)
|
||||||
except KeyError:
|
except CoreError:
|
||||||
logging.exception("error during update")
|
logging.exception("error during update")
|
||||||
|
|
||||||
def linkconfig(self, netif, bw=None, delay=None, loss=None, duplicate=None, jitter=None, netif2=None):
|
def linkconfig(
|
||||||
|
self,
|
||||||
|
netif,
|
||||||
|
bw=None,
|
||||||
|
delay=None,
|
||||||
|
loss=None,
|
||||||
|
duplicate=None,
|
||||||
|
jitter=None,
|
||||||
|
netif2=None,
|
||||||
|
):
|
||||||
"""
|
"""
|
||||||
Invoked when a Link Message is received. Default is unimplemented.
|
Invoked when a Link Message is received. Default is unimplemented.
|
||||||
|
|
||||||
|
@ -156,4 +168,6 @@ class EmaneModel(WirelessModel):
|
||||||
:param core.netns.vif.Veth netif2: interface two
|
:param core.netns.vif.Veth netif2: interface two
|
||||||
:return: nothing
|
:return: nothing
|
||||||
"""
|
"""
|
||||||
logging.warning("emane model(%s) does not support link configuration", self.name)
|
logging.warning(
|
||||||
|
"emane model(%s) does not support link configuration", self.name
|
||||||
|
)
|
||||||
|
|
|
@ -17,7 +17,6 @@ class EmaneIeee80211abgModel(emanemodel.EmaneModel):
|
||||||
@classmethod
|
@classmethod
|
||||||
def load(cls, emane_prefix):
|
def load(cls, emane_prefix):
|
||||||
cls.mac_defaults["pcrcurveuri"] = os.path.join(
|
cls.mac_defaults["pcrcurveuri"] = os.path.join(
|
||||||
emane_prefix,
|
emane_prefix, "share/emane/xml/models/mac/ieee80211abg/ieee80211pcr.xml"
|
||||||
"share/emane/xml/models/mac/ieee80211abg/ieee80211pcr.xml"
|
|
||||||
)
|
)
|
||||||
super(EmaneIeee80211abgModel, cls).load(emane_prefix)
|
super(EmaneIeee80211abgModel, cls).load(emane_prefix)
|
||||||
|
|
|
@ -6,10 +6,8 @@ share the same MAC+PHY model.
|
||||||
|
|
||||||
import logging
|
import logging
|
||||||
|
|
||||||
|
from core.emulator.enumerations import LinkTypes, NodeTypes, RegisterTlvs
|
||||||
from core.nodes.base import CoreNetworkBase
|
from core.nodes.base import CoreNetworkBase
|
||||||
from core.emulator.enumerations import LinkTypes
|
|
||||||
from core.emulator.enumerations import NodeTypes
|
|
||||||
from core.emulator.enumerations import RegisterTlvs
|
|
||||||
|
|
||||||
try:
|
try:
|
||||||
from emane.events import LocationEvent
|
from emane.events import LocationEvent
|
||||||
|
@ -24,6 +22,7 @@ class EmaneNet(CoreNetworkBase):
|
||||||
"""
|
"""
|
||||||
EMANE network base class.
|
EMANE network base class.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
apitype = NodeTypes.EMANE.value
|
apitype = NodeTypes.EMANE.value
|
||||||
linktype = LinkTypes.WIRELESS.value
|
linktype = LinkTypes.WIRELESS.value
|
||||||
# icon used
|
# icon used
|
||||||
|
@ -45,14 +44,30 @@ class EmaneNode(EmaneNet):
|
||||||
self.model = None
|
self.model = None
|
||||||
self.mobility = None
|
self.mobility = None
|
||||||
|
|
||||||
def linkconfig(self, netif, bw=None, delay=None, loss=None, duplicate=None, jitter=None, netif2=None):
|
def linkconfig(
|
||||||
|
self,
|
||||||
|
netif,
|
||||||
|
bw=None,
|
||||||
|
delay=None,
|
||||||
|
loss=None,
|
||||||
|
duplicate=None,
|
||||||
|
jitter=None,
|
||||||
|
netif2=None,
|
||||||
|
):
|
||||||
"""
|
"""
|
||||||
The CommEffect model supports link configuration.
|
The CommEffect model supports link configuration.
|
||||||
"""
|
"""
|
||||||
if not self.model:
|
if not self.model:
|
||||||
return
|
return
|
||||||
return self.model.linkconfig(netif=netif, bw=bw, delay=delay, loss=loss,
|
return self.model.linkconfig(
|
||||||
duplicate=duplicate, jitter=jitter, netif2=netif2)
|
netif=netif,
|
||||||
|
bw=bw,
|
||||||
|
delay=delay,
|
||||||
|
loss=loss,
|
||||||
|
duplicate=duplicate,
|
||||||
|
jitter=jitter,
|
||||||
|
netif2=netif2,
|
||||||
|
)
|
||||||
|
|
||||||
def config(self, conf):
|
def config(self, conf):
|
||||||
self.conf = conf
|
self.conf = conf
|
||||||
|
@ -69,7 +84,9 @@ class EmaneNode(EmaneNet):
|
||||||
def updatemodel(self, config):
|
def updatemodel(self, config):
|
||||||
if not self.model:
|
if not self.model:
|
||||||
raise ValueError("no model set to update for node(%s)", self.id)
|
raise ValueError("no model set to update for node(%s)", self.id)
|
||||||
logging.info("node(%s) updating model(%s): %s", self.id, self.model.name, config)
|
logging.info(
|
||||||
|
"node(%s) updating model(%s): %s", self.id, self.model.name, config
|
||||||
|
)
|
||||||
self.model.set_configs(config, node_id=self.id)
|
self.model.set_configs(config, node_id=self.id)
|
||||||
|
|
||||||
def setmodel(self, model, config):
|
def setmodel(self, model, config):
|
||||||
|
@ -124,13 +141,18 @@ class EmaneNode(EmaneNet):
|
||||||
EMANE daemons have been started, because that is their only chance
|
EMANE daemons have been started, because that is their only chance
|
||||||
to bind to the TAPs.
|
to bind to the TAPs.
|
||||||
"""
|
"""
|
||||||
if self.session.emane.genlocationevents() and self.session.emane.service is None:
|
if (
|
||||||
|
self.session.emane.genlocationevents()
|
||||||
|
and self.session.emane.service is None
|
||||||
|
):
|
||||||
warntxt = "unable to publish EMANE events because the eventservice "
|
warntxt = "unable to publish EMANE events because the eventservice "
|
||||||
warntxt += "Python bindings failed to load"
|
warntxt += "Python bindings failed to load"
|
||||||
logging.error(warntxt)
|
logging.error(warntxt)
|
||||||
|
|
||||||
for netif in self.netifs():
|
for netif in self.netifs():
|
||||||
external = self.session.emane.get_config("external", self.id, self.model.name)
|
external = self.session.emane.get_config(
|
||||||
|
"external", self.id, self.model.name
|
||||||
|
)
|
||||||
if external == "0":
|
if external == "0":
|
||||||
netif.setaddrs()
|
netif.setaddrs()
|
||||||
|
|
||||||
|
@ -168,7 +190,6 @@ class EmaneNode(EmaneNet):
|
||||||
logging.info("nemid for %s is unknown", ifname)
|
logging.info("nemid for %s is unknown", ifname)
|
||||||
return
|
return
|
||||||
lat, lon, alt = self.session.location.getgeo(x, y, z)
|
lat, lon, alt = self.session.location.getgeo(x, y, z)
|
||||||
logging.info("setnemposition %s (%s) x,y,z=(%d,%d,%s)(%.6f,%.6f,%.6f)", ifname, nemid, x, y, z, lat, lon, alt)
|
|
||||||
event = LocationEvent()
|
event = LocationEvent()
|
||||||
|
|
||||||
# altitude must be an integer or warning is printed
|
# altitude must be an integer or warning is printed
|
||||||
|
@ -200,8 +221,6 @@ class EmaneNode(EmaneNet):
|
||||||
continue
|
continue
|
||||||
x, y, z = netif.node.getposition()
|
x, y, z = netif.node.getposition()
|
||||||
lat, lon, alt = self.session.location.getgeo(x, y, z)
|
lat, lon, alt = self.session.location.getgeo(x, y, z)
|
||||||
logging.info("setnempositions %d %s (%s) x,y,z=(%d,%d,%s)(%.6f,%.6f,%.6f)",
|
|
||||||
i, ifname, nemid, x, y, z, lat, lon, alt)
|
|
||||||
# altitude must be an integer or warning is printed
|
# altitude must be an integer or warning is printed
|
||||||
alt = int(round(alt))
|
alt = int(round(alt))
|
||||||
event.append(nemid, latitude=lat, longitude=lon, altitude=alt)
|
event.append(nemid, latitude=lat, longitude=lon, altitude=alt)
|
||||||
|
|
|
@ -17,7 +17,6 @@ class EmaneRfPipeModel(emanemodel.EmaneModel):
|
||||||
@classmethod
|
@classmethod
|
||||||
def load(cls, emane_prefix):
|
def load(cls, emane_prefix):
|
||||||
cls.mac_defaults["pcrcurveuri"] = os.path.join(
|
cls.mac_defaults["pcrcurveuri"] = os.path.join(
|
||||||
emane_prefix,
|
emane_prefix, "share/emane/xml/models/mac/rfpipe/rfpipepcr.xml"
|
||||||
"share/emane/xml/models/mac/rfpipe/rfpipepcr.xml"
|
|
||||||
)
|
)
|
||||||
super(EmaneRfPipeModel, cls).load(emane_prefix)
|
super(EmaneRfPipeModel, cls).load(emane_prefix)
|
||||||
|
|
|
@ -21,14 +21,16 @@ class EmaneTdmaModel(emanemodel.EmaneModel):
|
||||||
|
|
||||||
# add custom schedule options and ignore it when writing emane xml
|
# add custom schedule options and ignore it when writing emane xml
|
||||||
schedule_name = "schedule"
|
schedule_name = "schedule"
|
||||||
default_schedule = os.path.join(constants.CORE_DATA_DIR, "examples", "tdma", "schedule.xml")
|
default_schedule = os.path.join(
|
||||||
|
constants.CORE_DATA_DIR, "examples", "tdma", "schedule.xml"
|
||||||
|
)
|
||||||
config_ignore = {schedule_name}
|
config_ignore = {schedule_name}
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def load(cls, emane_prefix):
|
def load(cls, emane_prefix):
|
||||||
cls.mac_defaults["pcrcurveuri"] = os.path.join(
|
cls.mac_defaults["pcrcurveuri"] = os.path.join(
|
||||||
emane_prefix,
|
emane_prefix,
|
||||||
"share/emane/xml/models/mac/tdmaeventscheduler/tdmabasemodelpcr.xml"
|
"share/emane/xml/models/mac/tdmaeventscheduler/tdmabasemodelpcr.xml",
|
||||||
)
|
)
|
||||||
super(EmaneTdmaModel, cls).load(emane_prefix)
|
super(EmaneTdmaModel, cls).load(emane_prefix)
|
||||||
cls.mac_config.insert(
|
cls.mac_config.insert(
|
||||||
|
@ -37,8 +39,8 @@ class EmaneTdmaModel(emanemodel.EmaneModel):
|
||||||
_id=cls.schedule_name,
|
_id=cls.schedule_name,
|
||||||
_type=ConfigDataTypes.STRING,
|
_type=ConfigDataTypes.STRING,
|
||||||
default=cls.default_schedule,
|
default=cls.default_schedule,
|
||||||
label="TDMA schedule file (core)"
|
label="TDMA schedule file (core)",
|
||||||
)
|
),
|
||||||
)
|
)
|
||||||
|
|
||||||
def post_startup(self):
|
def post_startup(self):
|
||||||
|
@ -57,5 +59,7 @@ class EmaneTdmaModel(emanemodel.EmaneModel):
|
||||||
event_device = self.session.emane.event_device
|
event_device = self.session.emane.event_device
|
||||||
|
|
||||||
# initiate tdma schedule
|
# initiate tdma schedule
|
||||||
logging.info("setting up tdma schedule: schedule(%s) device(%s)", schedule, event_device)
|
logging.info(
|
||||||
|
"setting up tdma schedule: schedule(%s) device(%s)", schedule, event_device
|
||||||
|
)
|
||||||
utils.check_cmd(["emaneevent-tdmaschedule", "-i", event_device, schedule])
|
utils.check_cmd(["emaneevent-tdmaschedule", "-i", event_device, schedule])
|
||||||
|
|
|
@ -7,8 +7,7 @@ import sys
|
||||||
import core.services
|
import core.services
|
||||||
from core.emulator.emudata import IdGen
|
from core.emulator.emudata import IdGen
|
||||||
from core.emulator.session import Session
|
from core.emulator.session import Session
|
||||||
from core.nodes import nodemaps
|
from core.nodes import nodemaps, nodeutils
|
||||||
from core.nodes import nodeutils
|
|
||||||
from core.services.coreservices import ServiceManager
|
from core.services.coreservices import ServiceManager
|
||||||
|
|
||||||
|
|
||||||
|
@ -73,7 +72,7 @@ class CoreEmu(object):
|
||||||
service_paths = self.config.get("custom_services_dir")
|
service_paths = self.config.get("custom_services_dir")
|
||||||
logging.debug("custom service paths: %s", service_paths)
|
logging.debug("custom service paths: %s", service_paths)
|
||||||
if service_paths:
|
if service_paths:
|
||||||
for service_path in service_paths.split(','):
|
for service_path in service_paths.split(","):
|
||||||
service_path = service_path.strip()
|
service_path = service_path.strip()
|
||||||
custom_service_errors = ServiceManager.add_services(service_path)
|
custom_service_errors = ServiceManager.add_services(service_path)
|
||||||
self.service_errors.extend(custom_service_errors)
|
self.service_errors.extend(custom_service_errors)
|
||||||
|
|
|
@ -4,117 +4,118 @@ CORE data objects.
|
||||||
|
|
||||||
import collections
|
import collections
|
||||||
|
|
||||||
ConfigData = collections.namedtuple("ConfigData", [
|
ConfigData = collections.namedtuple(
|
||||||
"message_type",
|
"ConfigData",
|
||||||
"node",
|
[
|
||||||
"object",
|
"message_type",
|
||||||
"type",
|
"node",
|
||||||
"data_types",
|
"object",
|
||||||
"data_values",
|
"type",
|
||||||
"captions",
|
"data_types",
|
||||||
"bitmap",
|
"data_values",
|
||||||
"possible_values",
|
"captions",
|
||||||
"groups",
|
"bitmap",
|
||||||
"session",
|
"possible_values",
|
||||||
"interface_number",
|
"groups",
|
||||||
"network_id",
|
"session",
|
||||||
"opaque"
|
"interface_number",
|
||||||
])
|
"network_id",
|
||||||
|
"opaque",
|
||||||
|
],
|
||||||
|
)
|
||||||
ConfigData.__new__.__defaults__ = (None,) * len(ConfigData._fields)
|
ConfigData.__new__.__defaults__ = (None,) * len(ConfigData._fields)
|
||||||
|
|
||||||
EventData = collections.namedtuple("EventData", [
|
EventData = collections.namedtuple(
|
||||||
"node",
|
"EventData", ["node", "event_type", "name", "data", "time", "session"]
|
||||||
"event_type",
|
)
|
||||||
"name",
|
|
||||||
"data",
|
|
||||||
"time",
|
|
||||||
"session"
|
|
||||||
])
|
|
||||||
EventData.__new__.__defaults__ = (None,) * len(EventData._fields)
|
EventData.__new__.__defaults__ = (None,) * len(EventData._fields)
|
||||||
|
|
||||||
ExceptionData = collections.namedtuple("ExceptionData", [
|
ExceptionData = collections.namedtuple(
|
||||||
"node",
|
"ExceptionData", ["node", "session", "level", "source", "date", "text", "opaque"]
|
||||||
"session",
|
)
|
||||||
"level",
|
|
||||||
"source",
|
|
||||||
"date",
|
|
||||||
"text",
|
|
||||||
"opaque"
|
|
||||||
])
|
|
||||||
ExceptionData.__new__.__defaults__ = (None,) * len(ExceptionData._fields)
|
ExceptionData.__new__.__defaults__ = (None,) * len(ExceptionData._fields)
|
||||||
|
|
||||||
FileData = collections.namedtuple("FileData", [
|
FileData = collections.namedtuple(
|
||||||
"message_type",
|
"FileData",
|
||||||
"node",
|
[
|
||||||
"name",
|
"message_type",
|
||||||
"mode",
|
"node",
|
||||||
"number",
|
"name",
|
||||||
"type",
|
"mode",
|
||||||
"source",
|
"number",
|
||||||
"session",
|
"type",
|
||||||
"data",
|
"source",
|
||||||
"compressed_data"
|
"session",
|
||||||
])
|
"data",
|
||||||
|
"compressed_data",
|
||||||
|
],
|
||||||
|
)
|
||||||
FileData.__new__.__defaults__ = (None,) * len(FileData._fields)
|
FileData.__new__.__defaults__ = (None,) * len(FileData._fields)
|
||||||
|
|
||||||
NodeData = collections.namedtuple("NodeData", [
|
NodeData = collections.namedtuple(
|
||||||
"message_type",
|
"NodeData",
|
||||||
"id",
|
[
|
||||||
"node_type",
|
"message_type",
|
||||||
"name",
|
"id",
|
||||||
"ip_address",
|
"node_type",
|
||||||
"mac_address",
|
"name",
|
||||||
"ip6_address",
|
"ip_address",
|
||||||
"model",
|
"mac_address",
|
||||||
"emulation_id",
|
"ip6_address",
|
||||||
"emulation_server",
|
"model",
|
||||||
"session",
|
"emulation_id",
|
||||||
"x_position",
|
"emulation_server",
|
||||||
"y_position",
|
"session",
|
||||||
"canvas",
|
"x_position",
|
||||||
"network_id",
|
"y_position",
|
||||||
"services",
|
"canvas",
|
||||||
"latitude",
|
"network_id",
|
||||||
"longitude",
|
"services",
|
||||||
"altitude",
|
"latitude",
|
||||||
"icon",
|
"longitude",
|
||||||
"opaque"
|
"altitude",
|
||||||
])
|
"icon",
|
||||||
|
"opaque",
|
||||||
|
],
|
||||||
|
)
|
||||||
NodeData.__new__.__defaults__ = (None,) * len(NodeData._fields)
|
NodeData.__new__.__defaults__ = (None,) * len(NodeData._fields)
|
||||||
|
|
||||||
LinkData = collections.namedtuple("LinkData", [
|
LinkData = collections.namedtuple(
|
||||||
"message_type",
|
"LinkData",
|
||||||
"node1_id",
|
[
|
||||||
"node2_id",
|
"message_type",
|
||||||
"delay",
|
"node1_id",
|
||||||
"bandwidth",
|
"node2_id",
|
||||||
"per",
|
"delay",
|
||||||
"dup",
|
"bandwidth",
|
||||||
"jitter",
|
"per",
|
||||||
"mer",
|
"dup",
|
||||||
"burst",
|
"jitter",
|
||||||
"session",
|
"mer",
|
||||||
"mburst",
|
"burst",
|
||||||
"link_type",
|
"session",
|
||||||
"gui_attributes",
|
"mburst",
|
||||||
"unidirectional",
|
"link_type",
|
||||||
"emulation_id",
|
"gui_attributes",
|
||||||
"network_id",
|
"unidirectional",
|
||||||
"key",
|
"emulation_id",
|
||||||
"interface1_id",
|
"network_id",
|
||||||
"interface1_name",
|
"key",
|
||||||
"interface1_ip4",
|
"interface1_id",
|
||||||
"interface1_ip4_mask",
|
"interface1_name",
|
||||||
"interface1_mac",
|
"interface1_ip4",
|
||||||
"interface1_ip6",
|
"interface1_ip4_mask",
|
||||||
"interface1_ip6_mask",
|
"interface1_mac",
|
||||||
"interface2_id",
|
"interface1_ip6",
|
||||||
"interface2_name",
|
"interface1_ip6_mask",
|
||||||
"interface2_ip4",
|
"interface2_id",
|
||||||
"interface2_ip4_mask",
|
"interface2_name",
|
||||||
"interface2_mac",
|
"interface2_ip4",
|
||||||
"interface2_ip6",
|
"interface2_ip4_mask",
|
||||||
"interface2_ip6_mask",
|
"interface2_mac",
|
||||||
"opaque"
|
"interface2_ip6",
|
||||||
])
|
"interface2_ip6_mask",
|
||||||
|
"opaque",
|
||||||
|
],
|
||||||
|
)
|
||||||
LinkData.__new__.__defaults__ = (None,) * len(LinkData._fields)
|
LinkData.__new__.__defaults__ = (None,) * len(LinkData._fields)
|
||||||
|
|
|
@ -1,10 +1,7 @@
|
||||||
from core.emulator.enumerations import LinkTypes
|
from core.emulator.enumerations import LinkTypes, NodeTypes
|
||||||
from core.emulator.enumerations import NodeTypes
|
|
||||||
from core.nodes import nodeutils
|
from core.nodes import nodeutils
|
||||||
from core.nodes.base import CoreNetworkBase
|
from core.nodes.base import CoreNetworkBase
|
||||||
from core.nodes.ipaddress import Ipv4Prefix
|
from core.nodes.ipaddress import Ipv4Prefix, Ipv6Prefix, MacAddress
|
||||||
from core.nodes.ipaddress import Ipv6Prefix
|
|
||||||
from core.nodes.ipaddress import MacAddress
|
|
||||||
|
|
||||||
|
|
||||||
class IdGen(object):
|
class IdGen(object):
|
||||||
|
@ -41,7 +38,7 @@ def create_interface(node, network, interface_data):
|
||||||
addrlist=interface_data.get_addresses(),
|
addrlist=interface_data.get_addresses(),
|
||||||
hwaddr=interface_data.mac,
|
hwaddr=interface_data.mac,
|
||||||
ifindex=interface_data.id,
|
ifindex=interface_data.id,
|
||||||
ifname=interface_data.name
|
ifname=interface_data.name,
|
||||||
)
|
)
|
||||||
return node.netif(interface_data.id, network)
|
return node.netif(interface_data.id, network)
|
||||||
|
|
||||||
|
@ -64,7 +61,7 @@ def link_config(network, interface, link_options, devname=None, interface_two=No
|
||||||
"loss": link_options.per,
|
"loss": link_options.per,
|
||||||
"duplicate": link_options.dup,
|
"duplicate": link_options.dup,
|
||||||
"jitter": link_options.jitter,
|
"jitter": link_options.jitter,
|
||||||
"netif2": interface_two
|
"netif2": interface_two,
|
||||||
}
|
}
|
||||||
|
|
||||||
# hacky check here, because physical and emane nodes do not conform to the same linkconfig interface
|
# hacky check here, because physical and emane nodes do not conform to the same linkconfig interface
|
||||||
|
@ -79,12 +76,13 @@ class NodeOptions(object):
|
||||||
Options for creating and updating nodes within core.
|
Options for creating and updating nodes within core.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
def __init__(self, name=None, model="PC"):
|
def __init__(self, name=None, model="PC", image=None):
|
||||||
"""
|
"""
|
||||||
Create a NodeOptions object.
|
Create a NodeOptions object.
|
||||||
|
|
||||||
:param str name: name of node, defaults to node class name postfix with its id
|
:param str name: name of node, defaults to node class name postfix with its id
|
||||||
:param str model: defines services for default and physical nodes, defaults to "router"
|
:param str model: defines services for default and physical nodes, defaults to "router"
|
||||||
|
:param str image: image to use for docker nodes
|
||||||
"""
|
"""
|
||||||
self.name = name
|
self.name = name
|
||||||
self.model = model
|
self.model = model
|
||||||
|
@ -99,6 +97,7 @@ class NodeOptions(object):
|
||||||
self.alt = None
|
self.alt = None
|
||||||
self.emulation_id = None
|
self.emulation_id = None
|
||||||
self.emulation_server = None
|
self.emulation_server = None
|
||||||
|
self.image = image
|
||||||
|
|
||||||
def set_position(self, x, y):
|
def set_position(self, x, y):
|
||||||
"""
|
"""
|
||||||
|
@ -240,7 +239,7 @@ class IpPrefixes(object):
|
||||||
ip4_mask=ip4_mask,
|
ip4_mask=ip4_mask,
|
||||||
ip6=ip6,
|
ip6=ip6,
|
||||||
ip6_mask=ip6_mask,
|
ip6_mask=ip6_mask,
|
||||||
mac=mac
|
mac=mac,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -12,6 +12,7 @@ class MessageTypes(Enum):
|
||||||
"""
|
"""
|
||||||
CORE message types.
|
CORE message types.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
NODE = 0x01
|
NODE = 0x01
|
||||||
LINK = 0x02
|
LINK = 0x02
|
||||||
EXECUTE = 0x03
|
EXECUTE = 0x03
|
||||||
|
@ -28,6 +29,7 @@ class MessageFlags(Enum):
|
||||||
"""
|
"""
|
||||||
CORE message flags.
|
CORE message flags.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
ADD = 0x01
|
ADD = 0x01
|
||||||
DELETE = 0x02
|
DELETE = 0x02
|
||||||
CRI = 0x04
|
CRI = 0x04
|
||||||
|
@ -41,6 +43,7 @@ class NodeTlvs(Enum):
|
||||||
"""
|
"""
|
||||||
Node type, length, value enumerations.
|
Node type, length, value enumerations.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
NUMBER = 0x01
|
NUMBER = 0x01
|
||||||
TYPE = 0x02
|
TYPE = 0x02
|
||||||
NAME = 0x03
|
NAME = 0x03
|
||||||
|
@ -67,6 +70,7 @@ class NodeTypes(Enum):
|
||||||
"""
|
"""
|
||||||
Node types.
|
Node types.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
DEFAULT = 0
|
DEFAULT = 0
|
||||||
PHYSICAL = 1
|
PHYSICAL = 1
|
||||||
TBD = 3
|
TBD = 3
|
||||||
|
@ -81,12 +85,15 @@ class NodeTypes(Enum):
|
||||||
PEER_TO_PEER = 12
|
PEER_TO_PEER = 12
|
||||||
CONTROL_NET = 13
|
CONTROL_NET = 13
|
||||||
EMANE_NET = 14
|
EMANE_NET = 14
|
||||||
|
DOCKER = 15
|
||||||
|
LXC = 16
|
||||||
|
|
||||||
|
|
||||||
class Rj45Models(Enum):
|
class Rj45Models(Enum):
|
||||||
"""
|
"""
|
||||||
RJ45 model types.
|
RJ45 model types.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
LINKED = 0
|
LINKED = 0
|
||||||
WIRELESS = 1
|
WIRELESS = 1
|
||||||
INSTALLED = 2
|
INSTALLED = 2
|
||||||
|
@ -97,6 +104,7 @@ class LinkTlvs(Enum):
|
||||||
"""
|
"""
|
||||||
Link type, length, value enumerations.
|
Link type, length, value enumerations.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
N1_NUMBER = 0x01
|
N1_NUMBER = 0x01
|
||||||
N2_NUMBER = 0x02
|
N2_NUMBER = 0x02
|
||||||
DELAY = 0x03
|
DELAY = 0x03
|
||||||
|
@ -135,6 +143,7 @@ class LinkTypes(Enum):
|
||||||
"""
|
"""
|
||||||
Link types.
|
Link types.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
WIRELESS = 0
|
WIRELESS = 0
|
||||||
WIRED = 1
|
WIRED = 1
|
||||||
|
|
||||||
|
@ -143,6 +152,7 @@ class ExecuteTlvs(Enum):
|
||||||
"""
|
"""
|
||||||
Execute type, length, value enumerations.
|
Execute type, length, value enumerations.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
NODE = 0x01
|
NODE = 0x01
|
||||||
NUMBER = 0x02
|
NUMBER = 0x02
|
||||||
TIME = 0x03
|
TIME = 0x03
|
||||||
|
@ -156,6 +166,7 @@ class RegisterTlvs(Enum):
|
||||||
"""
|
"""
|
||||||
Register type, length, value enumerations.
|
Register type, length, value enumerations.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
WIRELESS = 0x01
|
WIRELESS = 0x01
|
||||||
MOBILITY = 0x02
|
MOBILITY = 0x02
|
||||||
UTILITY = 0x03
|
UTILITY = 0x03
|
||||||
|
@ -169,6 +180,7 @@ class ConfigTlvs(Enum):
|
||||||
"""
|
"""
|
||||||
Configuration type, length, value enumerations.
|
Configuration type, length, value enumerations.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
NODE = 0x01
|
NODE = 0x01
|
||||||
OBJECT = 0x02
|
OBJECT = 0x02
|
||||||
TYPE = 0x03
|
TYPE = 0x03
|
||||||
|
@ -188,6 +200,7 @@ class ConfigFlags(Enum):
|
||||||
"""
|
"""
|
||||||
Configuration flags.
|
Configuration flags.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
NONE = 0x00
|
NONE = 0x00
|
||||||
REQUEST = 0x01
|
REQUEST = 0x01
|
||||||
UPDATE = 0x02
|
UPDATE = 0x02
|
||||||
|
@ -198,6 +211,7 @@ class ConfigDataTypes(Enum):
|
||||||
"""
|
"""
|
||||||
Configuration data types.
|
Configuration data types.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
UINT8 = 0x01
|
UINT8 = 0x01
|
||||||
UINT16 = 0x02
|
UINT16 = 0x02
|
||||||
UINT32 = 0x03
|
UINT32 = 0x03
|
||||||
|
@ -215,6 +229,7 @@ class FileTlvs(Enum):
|
||||||
"""
|
"""
|
||||||
File type, length, value enumerations.
|
File type, length, value enumerations.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
NODE = 0x01
|
NODE = 0x01
|
||||||
NAME = 0x02
|
NAME = 0x02
|
||||||
MODE = 0x03
|
MODE = 0x03
|
||||||
|
@ -230,6 +245,7 @@ class InterfaceTlvs(Enum):
|
||||||
"""
|
"""
|
||||||
Interface type, length, value enumerations.
|
Interface type, length, value enumerations.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
NODE = 0x01
|
NODE = 0x01
|
||||||
NUMBER = 0x02
|
NUMBER = 0x02
|
||||||
NAME = 0x03
|
NAME = 0x03
|
||||||
|
@ -249,6 +265,7 @@ class EventTlvs(Enum):
|
||||||
"""
|
"""
|
||||||
Event type, length, value enumerations.
|
Event type, length, value enumerations.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
NODE = 0x01
|
NODE = 0x01
|
||||||
TYPE = 0x02
|
TYPE = 0x02
|
||||||
NAME = 0x03
|
NAME = 0x03
|
||||||
|
@ -261,6 +278,7 @@ class EventTypes(Enum):
|
||||||
"""
|
"""
|
||||||
Event types.
|
Event types.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
NONE = 0
|
NONE = 0
|
||||||
DEFINITION_STATE = 1
|
DEFINITION_STATE = 1
|
||||||
CONFIGURATION_STATE = 2
|
CONFIGURATION_STATE = 2
|
||||||
|
@ -283,6 +301,7 @@ class SessionTlvs(Enum):
|
||||||
"""
|
"""
|
||||||
Session type, length, value enumerations.
|
Session type, length, value enumerations.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
NUMBER = 0x01
|
NUMBER = 0x01
|
||||||
NAME = 0x02
|
NAME = 0x02
|
||||||
FILE = 0x03
|
FILE = 0x03
|
||||||
|
@ -297,6 +316,7 @@ class ExceptionTlvs(Enum):
|
||||||
"""
|
"""
|
||||||
Exception type, length, value enumerations.
|
Exception type, length, value enumerations.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
NODE = 0x01
|
NODE = 0x01
|
||||||
SESSION = 0x02
|
SESSION = 0x02
|
||||||
LEVEL = 0x03
|
LEVEL = 0x03
|
||||||
|
@ -310,6 +330,7 @@ class ExceptionLevels(Enum):
|
||||||
"""
|
"""
|
||||||
Exception levels.
|
Exception levels.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
NONE = 0
|
NONE = 0
|
||||||
FATAL = 1
|
FATAL = 1
|
||||||
ERROR = 2
|
ERROR = 2
|
||||||
|
|
|
@ -15,23 +15,21 @@ import time
|
||||||
from multiprocessing.pool import ThreadPool
|
from multiprocessing.pool import ThreadPool
|
||||||
|
|
||||||
import core.nodes.base
|
import core.nodes.base
|
||||||
from core import constants
|
from core import CoreError, constants, utils
|
||||||
from core import utils
|
|
||||||
from core.api.tlv import coreapi
|
from core.api.tlv import coreapi
|
||||||
from core.api.tlv.broker import CoreBroker
|
from core.api.tlv.broker import CoreBroker
|
||||||
from core.emane.emanemanager import EmaneManager
|
from core.emane.emanemanager import EmaneManager
|
||||||
from core.emulator.data import EventData, NodeData
|
from core.emulator.data import EventData, ExceptionData, NodeData
|
||||||
from core.emulator.data import ExceptionData
|
from core.emulator.emudata import (
|
||||||
from core.emulator.emudata import IdGen
|
IdGen,
|
||||||
from core.emulator.emudata import LinkOptions, NodeOptions
|
LinkOptions,
|
||||||
from core.emulator.emudata import create_interface
|
NodeOptions,
|
||||||
from core.emulator.emudata import is_net_node
|
create_interface,
|
||||||
from core.emulator.emudata import link_config
|
is_net_node,
|
||||||
from core.emulator.enumerations import EventTypes, LinkTypes
|
link_config,
|
||||||
from core.emulator.enumerations import ExceptionLevels
|
)
|
||||||
from core.emulator.enumerations import NodeTypes
|
from core.emulator.enumerations import EventTypes, ExceptionLevels, LinkTypes, NodeTypes
|
||||||
from core.emulator.sessionconfig import SessionConfig
|
from core.emulator.sessionconfig import SessionConfig, SessionMetaData
|
||||||
from core.emulator.sessionconfig import SessionMetaData
|
|
||||||
from core.location.corelocation import CoreLocation
|
from core.location.corelocation import CoreLocation
|
||||||
from core.location.event import EventLoop
|
from core.location.event import EventLoop
|
||||||
from core.location.mobility import MobilityManager
|
from core.location.mobility import MobilityManager
|
||||||
|
@ -40,8 +38,7 @@ from core.nodes.base import CoreNodeBase
|
||||||
from core.nodes.ipaddress import MacAddress
|
from core.nodes.ipaddress import MacAddress
|
||||||
from core.plugins.sdt import Sdt
|
from core.plugins.sdt import Sdt
|
||||||
from core.services.coreservices import CoreServices
|
from core.services.coreservices import CoreServices
|
||||||
from core.xml import corexml
|
from core.xml import corexml, corexmldeployment
|
||||||
from core.xml import corexmldeployment
|
|
||||||
from core.xml.corexml import CoreXmlReader, CoreXmlWriter
|
from core.xml.corexml import CoreXmlReader, CoreXmlWriter
|
||||||
|
|
||||||
|
|
||||||
|
@ -85,7 +82,9 @@ class Session(object):
|
||||||
# hooks handlers
|
# hooks handlers
|
||||||
self._hooks = {}
|
self._hooks = {}
|
||||||
self._state_hooks = {}
|
self._state_hooks = {}
|
||||||
self.add_state_hook(state=EventTypes.RUNTIME_STATE.value, hook=self.runtime_state_hook)
|
self.add_state_hook(
|
||||||
|
state=EventTypes.RUNTIME_STATE.value, hook=self.runtime_state_hook
|
||||||
|
)
|
||||||
|
|
||||||
# handlers for broadcasting information
|
# handlers for broadcasting information
|
||||||
self.event_handlers = []
|
self.event_handlers = []
|
||||||
|
@ -131,7 +130,9 @@ class Session(object):
|
||||||
:return: nodes, network nodes if present, and tunnel if present
|
:return: nodes, network nodes if present, and tunnel if present
|
||||||
:rtype: tuple
|
:rtype: tuple
|
||||||
"""
|
"""
|
||||||
logging.debug("link message between node1(%s) and node2(%s)", node_one_id, node_two_id)
|
logging.debug(
|
||||||
|
"link message between node1(%s) and node2(%s)", node_one_id, node_two_id
|
||||||
|
)
|
||||||
|
|
||||||
# values to fill
|
# values to fill
|
||||||
net_one = None
|
net_one = None
|
||||||
|
@ -171,8 +172,14 @@ class Session(object):
|
||||||
net_two = node_two
|
net_two = node_two
|
||||||
node_two = None
|
node_two = None
|
||||||
|
|
||||||
logging.debug("link node types n1(%s) n2(%s) net1(%s) net2(%s) tunnel(%s)",
|
logging.debug(
|
||||||
node_one, node_two, net_one, net_two, tunnel)
|
"link node types n1(%s) n2(%s) net1(%s) net2(%s) tunnel(%s)",
|
||||||
|
node_one,
|
||||||
|
node_two,
|
||||||
|
net_one,
|
||||||
|
net_two,
|
||||||
|
tunnel,
|
||||||
|
)
|
||||||
return node_one, node_two, net_one, net_two, tunnel
|
return node_one, node_two, net_one, net_two, tunnel
|
||||||
|
|
||||||
# TODO: this doesn't appear to ever be used, EMANE or basic wireless range
|
# TODO: this doesn't appear to ever be used, EMANE or basic wireless range
|
||||||
|
@ -183,27 +190,47 @@ class Session(object):
|
||||||
:param list objects: possible objects to deal with
|
:param list objects: possible objects to deal with
|
||||||
:param bool connect: link interfaces if True, unlink otherwise
|
:param bool connect: link interfaces if True, unlink otherwise
|
||||||
:return: nothing
|
:return: nothing
|
||||||
|
:raises core.CoreError: when objects to link is less than 2, or no common networks are found
|
||||||
"""
|
"""
|
||||||
objects = [x for x in objects if x]
|
objects = [x for x in objects if x]
|
||||||
if len(objects) < 2:
|
if len(objects) < 2:
|
||||||
raise ValueError("wireless link failure: %s", objects)
|
raise CoreError("wireless link failure: %s" % objects)
|
||||||
logging.debug("handling wireless linking objects(%s) connect(%s)", objects, connect)
|
logging.debug(
|
||||||
|
"handling wireless linking objects(%s) connect(%s)", objects, connect
|
||||||
|
)
|
||||||
common_networks = objects[0].commonnets(objects[1])
|
common_networks = objects[0].commonnets(objects[1])
|
||||||
if not common_networks:
|
if not common_networks:
|
||||||
raise ValueError("no common network found for wireless link/unlink")
|
raise CoreError("no common network found for wireless link/unlink")
|
||||||
|
|
||||||
for common_network, interface_one, interface_two in common_networks:
|
for common_network, interface_one, interface_two in common_networks:
|
||||||
if not nodeutils.is_node(common_network, [NodeTypes.WIRELESS_LAN, NodeTypes.EMANE]):
|
if not nodeutils.is_node(
|
||||||
logging.info("skipping common network that is not wireless/emane: %s", common_network)
|
common_network, [NodeTypes.WIRELESS_LAN, NodeTypes.EMANE]
|
||||||
|
):
|
||||||
|
logging.info(
|
||||||
|
"skipping common network that is not wireless/emane: %s",
|
||||||
|
common_network,
|
||||||
|
)
|
||||||
continue
|
continue
|
||||||
|
|
||||||
logging.info("wireless linking connect(%s): %s - %s", connect, interface_one, interface_two)
|
logging.info(
|
||||||
|
"wireless linking connect(%s): %s - %s",
|
||||||
|
connect,
|
||||||
|
interface_one,
|
||||||
|
interface_two,
|
||||||
|
)
|
||||||
if connect:
|
if connect:
|
||||||
common_network.link(interface_one, interface_two)
|
common_network.link(interface_one, interface_two)
|
||||||
else:
|
else:
|
||||||
common_network.unlink(interface_one, interface_two)
|
common_network.unlink(interface_one, interface_two)
|
||||||
|
|
||||||
def add_link(self, node_one_id, node_two_id, interface_one=None, interface_two=None, link_options=None):
|
def add_link(
|
||||||
|
self,
|
||||||
|
node_one_id,
|
||||||
|
node_two_id,
|
||||||
|
interface_one=None,
|
||||||
|
interface_two=None,
|
||||||
|
link_options=None,
|
||||||
|
):
|
||||||
"""
|
"""
|
||||||
Add a link between nodes.
|
Add a link between nodes.
|
||||||
|
|
||||||
|
@ -212,13 +239,15 @@ class Session(object):
|
||||||
:param core.emulator.emudata.InterfaceData interface_one: node one interface data, defaults to none
|
:param core.emulator.emudata.InterfaceData interface_one: node one interface data, defaults to none
|
||||||
:param core.emulator.emudata.InterfaceData interface_two: node two interface data, defaults to none
|
:param core.emulator.emudata.InterfaceData interface_two: node two interface data, defaults to none
|
||||||
:param core.emulator.emudata.LinkOptions link_options: data for creating link, defaults to no options
|
:param core.emulator.emudata.LinkOptions link_options: data for creating link, defaults to no options
|
||||||
:return:
|
:return: nothing
|
||||||
"""
|
"""
|
||||||
if not link_options:
|
if not link_options:
|
||||||
link_options = LinkOptions()
|
link_options = LinkOptions()
|
||||||
|
|
||||||
# get node objects identified by link data
|
# get node objects identified by link data
|
||||||
node_one, node_two, net_one, net_two, tunnel = self._link_nodes(node_one_id, node_two_id)
|
node_one, node_two, net_one, net_two, tunnel = self._link_nodes(
|
||||||
|
node_one_id, node_two_id
|
||||||
|
)
|
||||||
|
|
||||||
if node_one:
|
if node_one:
|
||||||
node_one.lock.acquire()
|
node_one.lock.acquire()
|
||||||
|
@ -234,27 +263,43 @@ class Session(object):
|
||||||
else:
|
else:
|
||||||
# 2 nodes being linked, ptp network
|
# 2 nodes being linked, ptp network
|
||||||
if all([node_one, node_two]) and not net_one:
|
if all([node_one, node_two]) and not net_one:
|
||||||
logging.info("adding link for peer to peer nodes: %s - %s", node_one.name, node_two.name)
|
logging.info(
|
||||||
|
"adding link for peer to peer nodes: %s - %s",
|
||||||
|
node_one.name,
|
||||||
|
node_two.name,
|
||||||
|
)
|
||||||
ptp_class = nodeutils.get_node_class(NodeTypes.PEER_TO_PEER)
|
ptp_class = nodeutils.get_node_class(NodeTypes.PEER_TO_PEER)
|
||||||
start = self.state > EventTypes.DEFINITION_STATE.value
|
start = self.state > EventTypes.DEFINITION_STATE.value
|
||||||
net_one = self.create_node(cls=ptp_class, start=start)
|
net_one = self.create_node(cls=ptp_class, start=start)
|
||||||
|
|
||||||
# node to network
|
# node to network
|
||||||
if node_one and net_one:
|
if node_one and net_one:
|
||||||
logging.info("adding link from node to network: %s - %s", node_one.name, net_one.name)
|
logging.info(
|
||||||
|
"adding link from node to network: %s - %s",
|
||||||
|
node_one.name,
|
||||||
|
net_one.name,
|
||||||
|
)
|
||||||
interface = create_interface(node_one, net_one, interface_one)
|
interface = create_interface(node_one, net_one, interface_one)
|
||||||
link_config(net_one, interface, link_options)
|
link_config(net_one, interface, link_options)
|
||||||
|
|
||||||
# network to node
|
# network to node
|
||||||
if node_two and net_one:
|
if node_two and net_one:
|
||||||
logging.info("adding link from network to node: %s - %s", node_two.name, net_one.name)
|
logging.info(
|
||||||
|
"adding link from network to node: %s - %s",
|
||||||
|
node_two.name,
|
||||||
|
net_one.name,
|
||||||
|
)
|
||||||
interface = create_interface(node_two, net_one, interface_two)
|
interface = create_interface(node_two, net_one, interface_two)
|
||||||
if not link_options.unidirectional:
|
if not link_options.unidirectional:
|
||||||
link_config(net_one, interface, link_options)
|
link_config(net_one, interface, link_options)
|
||||||
|
|
||||||
# network to network
|
# network to network
|
||||||
if net_one and net_two:
|
if net_one and net_two:
|
||||||
logging.info("adding link from network to network: %s - %s", net_one.name, net_two.name)
|
logging.info(
|
||||||
|
"adding link from network to network: %s - %s",
|
||||||
|
net_one.name,
|
||||||
|
net_two.name,
|
||||||
|
)
|
||||||
if nodeutils.is_node(net_two, NodeTypes.RJ45):
|
if nodeutils.is_node(net_two, NodeTypes.RJ45):
|
||||||
interface = net_two.linknet(net_one)
|
interface = net_two.linknet(net_one)
|
||||||
else:
|
else:
|
||||||
|
@ -264,7 +309,9 @@ class Session(object):
|
||||||
|
|
||||||
if not link_options.unidirectional:
|
if not link_options.unidirectional:
|
||||||
interface.swapparams("_params_up")
|
interface.swapparams("_params_up")
|
||||||
link_config(net_two, interface, link_options, devname=interface.name)
|
link_config(
|
||||||
|
net_two, interface, link_options, devname=interface.name
|
||||||
|
)
|
||||||
interface.swapparams("_params_up")
|
interface.swapparams("_params_up")
|
||||||
|
|
||||||
# a tunnel node was found for the nodes
|
# a tunnel node was found for the nodes
|
||||||
|
@ -293,12 +340,16 @@ class Session(object):
|
||||||
if node_one and nodeutils.is_node(node_one, NodeTypes.PHYSICAL):
|
if node_one and nodeutils.is_node(node_one, NodeTypes.PHYSICAL):
|
||||||
logging.info("adding link for physical node: %s", node_one.name)
|
logging.info("adding link for physical node: %s", node_one.name)
|
||||||
addresses = interface_one.get_addresses()
|
addresses = interface_one.get_addresses()
|
||||||
node_one.adoptnetif(tunnel, interface_one.id, interface_one.mac, addresses)
|
node_one.adoptnetif(
|
||||||
|
tunnel, interface_one.id, interface_one.mac, addresses
|
||||||
|
)
|
||||||
link_config(node_one, tunnel, link_options)
|
link_config(node_one, tunnel, link_options)
|
||||||
elif node_two and nodeutils.is_node(node_two, NodeTypes.PHYSICAL):
|
elif node_two and nodeutils.is_node(node_two, NodeTypes.PHYSICAL):
|
||||||
logging.info("adding link for physical node: %s", node_two.name)
|
logging.info("adding link for physical node: %s", node_two.name)
|
||||||
addresses = interface_two.get_addresses()
|
addresses = interface_two.get_addresses()
|
||||||
node_two.adoptnetif(tunnel, interface_two.id, interface_two.mac, addresses)
|
node_two.adoptnetif(
|
||||||
|
tunnel, interface_two.id, interface_two.mac, addresses
|
||||||
|
)
|
||||||
link_config(node_two, tunnel, link_options)
|
link_config(node_two, tunnel, link_options)
|
||||||
finally:
|
finally:
|
||||||
if node_one:
|
if node_one:
|
||||||
|
@ -306,7 +357,14 @@ class Session(object):
|
||||||
if node_two:
|
if node_two:
|
||||||
node_two.lock.release()
|
node_two.lock.release()
|
||||||
|
|
||||||
def delete_link(self, node_one_id, node_two_id, interface_one_id, interface_two_id, link_type=LinkTypes.WIRED):
|
def delete_link(
|
||||||
|
self,
|
||||||
|
node_one_id,
|
||||||
|
node_two_id,
|
||||||
|
interface_one_id,
|
||||||
|
interface_two_id,
|
||||||
|
link_type=LinkTypes.WIRED,
|
||||||
|
):
|
||||||
"""
|
"""
|
||||||
Delete a link between nodes.
|
Delete a link between nodes.
|
||||||
|
|
||||||
|
@ -316,9 +374,12 @@ class Session(object):
|
||||||
:param int interface_two_id: interface id for node two
|
:param int interface_two_id: interface id for node two
|
||||||
:param core.emulator.enumerations.LinkTypes link_type: link type to delete
|
:param core.emulator.enumerations.LinkTypes link_type: link type to delete
|
||||||
:return: nothing
|
:return: nothing
|
||||||
|
:raises core.CoreError: when no common network is found for link being deleted
|
||||||
"""
|
"""
|
||||||
# get node objects identified by link data
|
# get node objects identified by link data
|
||||||
node_one, node_two, net_one, net_two, _tunnel = self._link_nodes(node_one_id, node_two_id)
|
node_one, node_two, net_one, net_two, _tunnel = self._link_nodes(
|
||||||
|
node_one_id, node_two_id
|
||||||
|
)
|
||||||
|
|
||||||
if node_one:
|
if node_one:
|
||||||
node_one.lock.acquire()
|
node_one.lock.acquire()
|
||||||
|
@ -342,18 +403,31 @@ class Session(object):
|
||||||
# otherwise get interfaces between a node and network
|
# otherwise get interfaces between a node and network
|
||||||
if not interface_one and not interface_two:
|
if not interface_one and not interface_two:
|
||||||
common_networks = node_one.commonnets(node_two)
|
common_networks = node_one.commonnets(node_two)
|
||||||
for network, common_interface_one, common_interface_two in common_networks:
|
for (
|
||||||
|
network,
|
||||||
|
common_interface_one,
|
||||||
|
common_interface_two,
|
||||||
|
) in common_networks:
|
||||||
if (net_one and network == net_one) or not net_one:
|
if (net_one and network == net_one) or not net_one:
|
||||||
interface_one = common_interface_one
|
interface_one = common_interface_one
|
||||||
interface_two = common_interface_two
|
interface_two = common_interface_two
|
||||||
break
|
break
|
||||||
|
|
||||||
if all([interface_one, interface_two]) and any([interface_one.net, interface_two.net]):
|
if all([interface_one, interface_two]) and any(
|
||||||
if interface_one.net != interface_two.net and all([interface_one.up, interface_two.up]):
|
[interface_one.net, interface_two.net]
|
||||||
raise ValueError("no common network found")
|
):
|
||||||
|
if interface_one.net != interface_two.net and all(
|
||||||
|
[interface_one.up, interface_two.up]
|
||||||
|
):
|
||||||
|
raise CoreError("no common network found")
|
||||||
|
|
||||||
logging.info("deleting link node(%s):interface(%s) node(%s):interface(%s)",
|
logging.info(
|
||||||
node_one.name, interface_one.name, node_two.name, interface_two.name)
|
"deleting link node(%s):interface(%s) node(%s):interface(%s)",
|
||||||
|
node_one.name,
|
||||||
|
interface_one.name,
|
||||||
|
node_two.name,
|
||||||
|
interface_two.name,
|
||||||
|
)
|
||||||
net_one = interface_one.net
|
net_one = interface_one.net
|
||||||
interface_one.detachnet()
|
interface_one.detachnet()
|
||||||
interface_two.detachnet()
|
interface_two.detachnet()
|
||||||
|
@ -363,23 +437,40 @@ class Session(object):
|
||||||
node_two.delnetif(interface_two.netindex)
|
node_two.delnetif(interface_two.netindex)
|
||||||
elif node_one and net_one:
|
elif node_one and net_one:
|
||||||
interface = node_one.netif(interface_one_id)
|
interface = node_one.netif(interface_one_id)
|
||||||
logging.info("deleting link node(%s):interface(%s) node(%s)",
|
if interface:
|
||||||
node_one.name, interface.name, net_one.name)
|
logging.info(
|
||||||
interface.detachnet()
|
"deleting link node(%s):interface(%s) node(%s)",
|
||||||
node_one.delnetif(interface.netindex)
|
node_one.name,
|
||||||
|
interface.name,
|
||||||
|
net_one.name,
|
||||||
|
)
|
||||||
|
interface.detachnet()
|
||||||
|
node_one.delnetif(interface.netindex)
|
||||||
elif node_two and net_one:
|
elif node_two and net_one:
|
||||||
interface = node_two.netif(interface_two_id)
|
interface = node_two.netif(interface_two_id)
|
||||||
logging.info("deleting link node(%s):interface(%s) node(%s)",
|
if interface:
|
||||||
node_two.name, interface.name, net_one.name)
|
logging.info(
|
||||||
interface.detachnet()
|
"deleting link node(%s):interface(%s) node(%s)",
|
||||||
node_two.delnetif(interface.netindex)
|
node_two.name,
|
||||||
|
interface.name,
|
||||||
|
net_one.name,
|
||||||
|
)
|
||||||
|
interface.detachnet()
|
||||||
|
node_two.delnetif(interface.netindex)
|
||||||
finally:
|
finally:
|
||||||
if node_one:
|
if node_one:
|
||||||
node_one.lock.release()
|
node_one.lock.release()
|
||||||
if node_two:
|
if node_two:
|
||||||
node_two.lock.release()
|
node_two.lock.release()
|
||||||
|
|
||||||
def update_link(self, node_one_id, node_two_id, interface_one_id=None, interface_two_id=None, link_options=None):
|
def update_link(
|
||||||
|
self,
|
||||||
|
node_one_id,
|
||||||
|
node_two_id,
|
||||||
|
interface_one_id=None,
|
||||||
|
interface_two_id=None,
|
||||||
|
link_options=None,
|
||||||
|
):
|
||||||
"""
|
"""
|
||||||
Update link information between nodes.
|
Update link information between nodes.
|
||||||
|
|
||||||
|
@ -389,12 +480,16 @@ class Session(object):
|
||||||
:param int interface_two_id: interface id for node two
|
:param int interface_two_id: interface id for node two
|
||||||
:param core.emulator.emudata.LinkOptions link_options: data to update link with
|
:param core.emulator.emudata.LinkOptions link_options: data to update link with
|
||||||
:return: nothing
|
:return: nothing
|
||||||
|
:raises core.CoreError: when updating a wireless type link, when there is a unknown
|
||||||
|
link between networks
|
||||||
"""
|
"""
|
||||||
if not link_options:
|
if not link_options:
|
||||||
link_options = LinkOptions()
|
link_options = LinkOptions()
|
||||||
|
|
||||||
# get node objects identified by link data
|
# get node objects identified by link data
|
||||||
node_one, node_two, net_one, net_two, _tunnel = self._link_nodes(node_one_id, node_two_id)
|
node_one, node_two, net_one, net_two, _tunnel = self._link_nodes(
|
||||||
|
node_one_id, node_two_id
|
||||||
|
)
|
||||||
|
|
||||||
if node_one:
|
if node_one:
|
||||||
node_one.lock.acquire()
|
node_one.lock.acquire()
|
||||||
|
@ -404,7 +499,7 @@ class Session(object):
|
||||||
try:
|
try:
|
||||||
# wireless link
|
# wireless link
|
||||||
if link_options.type == LinkTypes.WIRELESS.value:
|
if link_options.type == LinkTypes.WIRELESS.value:
|
||||||
raise ValueError("cannot update wireless link")
|
raise CoreError("cannot update wireless link")
|
||||||
else:
|
else:
|
||||||
if not node_one and not node_two:
|
if not node_one and not node_two:
|
||||||
if net_one and net_two:
|
if net_one and net_two:
|
||||||
|
@ -417,11 +512,13 @@ class Session(object):
|
||||||
interface = net_two.getlinknetif(net_one)
|
interface = net_two.getlinknetif(net_one)
|
||||||
|
|
||||||
if not interface:
|
if not interface:
|
||||||
raise ValueError("modify unknown link between nets")
|
raise CoreError("modify unknown link between nets")
|
||||||
|
|
||||||
if upstream:
|
if upstream:
|
||||||
interface.swapparams("_params_up")
|
interface.swapparams("_params_up")
|
||||||
link_config(net_one, interface, link_options, devname=interface.name)
|
link_config(
|
||||||
|
net_one, interface, link_options, devname=interface.name
|
||||||
|
)
|
||||||
interface.swapparams("_params_up")
|
interface.swapparams("_params_up")
|
||||||
else:
|
else:
|
||||||
link_config(net_one, interface, link_options)
|
link_config(net_one, interface, link_options)
|
||||||
|
@ -431,10 +528,15 @@ class Session(object):
|
||||||
link_config(net_two, interface, link_options)
|
link_config(net_two, interface, link_options)
|
||||||
else:
|
else:
|
||||||
interface.swapparams("_params_up")
|
interface.swapparams("_params_up")
|
||||||
link_config(net_two, interface, link_options, devname=interface.name)
|
link_config(
|
||||||
|
net_two,
|
||||||
|
interface,
|
||||||
|
link_options,
|
||||||
|
devname=interface.name,
|
||||||
|
)
|
||||||
interface.swapparams("_params_up")
|
interface.swapparams("_params_up")
|
||||||
else:
|
else:
|
||||||
raise ValueError("modify link for unknown nodes")
|
raise CoreError("modify link for unknown nodes")
|
||||||
elif not node_one:
|
elif not node_one:
|
||||||
# node1 = layer 2node, node2 = layer3 node
|
# node1 = layer 2node, node2 = layer3 node
|
||||||
interface = node_two.netif(interface_two_id, net_one)
|
interface = node_two.netif(interface_two_id, net_one)
|
||||||
|
@ -446,15 +548,28 @@ class Session(object):
|
||||||
else:
|
else:
|
||||||
common_networks = node_one.commonnets(node_two)
|
common_networks = node_one.commonnets(node_two)
|
||||||
if not common_networks:
|
if not common_networks:
|
||||||
raise ValueError("no common network found")
|
raise CoreError("no common network found")
|
||||||
|
|
||||||
for net_one, interface_one, interface_two in common_networks:
|
for net_one, interface_one, interface_two in common_networks:
|
||||||
if interface_one_id is not None and interface_one_id != node_one.getifindex(interface_one):
|
if (
|
||||||
|
interface_one_id is not None
|
||||||
|
and interface_one_id != node_one.getifindex(interface_one)
|
||||||
|
):
|
||||||
continue
|
continue
|
||||||
|
|
||||||
link_config(net_one, interface_one, link_options, interface_two=interface_two)
|
link_config(
|
||||||
|
net_one,
|
||||||
|
interface_one,
|
||||||
|
link_options,
|
||||||
|
interface_two=interface_two,
|
||||||
|
)
|
||||||
if not link_options.unidirectional:
|
if not link_options.unidirectional:
|
||||||
link_config(net_one, interface_two, link_options, interface_two=interface_one)
|
link_config(
|
||||||
|
net_one,
|
||||||
|
interface_two,
|
||||||
|
link_options,
|
||||||
|
interface_two=interface_one,
|
||||||
|
)
|
||||||
finally:
|
finally:
|
||||||
if node_one:
|
if node_one:
|
||||||
node_one.lock.release()
|
node_one.lock.release()
|
||||||
|
@ -499,8 +614,23 @@ class Session(object):
|
||||||
name = "%s%s" % (node_class.__name__, _id)
|
name = "%s%s" % (node_class.__name__, _id)
|
||||||
|
|
||||||
# create node
|
# create node
|
||||||
logging.info("creating node(%s) id(%s) name(%s) start(%s)", node_class.__name__, _id, name, start)
|
logging.info(
|
||||||
node = self.create_node(cls=node_class, _id=_id, name=name, start=start)
|
"creating node(%s) id(%s) name(%s) start(%s)",
|
||||||
|
node_class.__name__,
|
||||||
|
_id,
|
||||||
|
name,
|
||||||
|
start,
|
||||||
|
)
|
||||||
|
if _type in [NodeTypes.DOCKER, NodeTypes.LXC]:
|
||||||
|
node = self.create_node(
|
||||||
|
cls=node_class,
|
||||||
|
_id=_id,
|
||||||
|
name=name,
|
||||||
|
start=start,
|
||||||
|
image=node_options.image,
|
||||||
|
)
|
||||||
|
else:
|
||||||
|
node = self.create_node(cls=node_class, _id=_id, name=name, start=start)
|
||||||
|
|
||||||
# set node attributes
|
# set node attributes
|
||||||
node.icon = node_options.icon
|
node.icon = node_options.icon
|
||||||
|
@ -511,13 +641,20 @@ class Session(object):
|
||||||
self.set_node_position(node, node_options)
|
self.set_node_position(node, node_options)
|
||||||
|
|
||||||
# add services to default and physical nodes only
|
# add services to default and physical nodes only
|
||||||
if _type in [NodeTypes.DEFAULT, NodeTypes.PHYSICAL]:
|
if _type in [
|
||||||
|
NodeTypes.DEFAULT,
|
||||||
|
NodeTypes.PHYSICAL,
|
||||||
|
NodeTypes.DOCKER,
|
||||||
|
NodeTypes.LXC,
|
||||||
|
]:
|
||||||
node.type = node_options.model
|
node.type = node_options.model
|
||||||
logging.debug("set node type: %s", node.type)
|
logging.debug("set node type: %s", node.type)
|
||||||
self.services.add_services(node, node.type, node_options.services)
|
self.services.add_services(node, node.type, node_options.services)
|
||||||
|
|
||||||
# boot nodes if created after runtime, LcxNodes, Physical, and RJ45 are all PyCoreNodes
|
# boot nodes if created after runtime, LcxNodes, Physical, and RJ45 are all PyCoreNodes
|
||||||
is_boot_node = isinstance(node, CoreNodeBase) and not nodeutils.is_node(node, NodeTypes.RJ45)
|
is_boot_node = isinstance(node, CoreNodeBase) and not nodeutils.is_node(
|
||||||
|
node, NodeTypes.RJ45
|
||||||
|
)
|
||||||
if self.state == EventTypes.RUNTIME_STATE.value and is_boot_node:
|
if self.state == EventTypes.RUNTIME_STATE.value and is_boot_node:
|
||||||
self.write_nodes()
|
self.write_nodes()
|
||||||
self.add_remove_control_interface(node=node, remove=False)
|
self.add_remove_control_interface(node=node, remove=False)
|
||||||
|
@ -533,25 +670,17 @@ class Session(object):
|
||||||
:param core.emulator.emudata.NodeOptions node_options: data to update node with
|
:param core.emulator.emudata.NodeOptions node_options: data to update node with
|
||||||
:return: True if node updated, False otherwise
|
:return: True if node updated, False otherwise
|
||||||
:rtype: bool
|
:rtype: bool
|
||||||
|
:raises core.CoreError: when node to update does not exist
|
||||||
"""
|
"""
|
||||||
result = False
|
# get node to update
|
||||||
try:
|
node = self.get_node(node_id)
|
||||||
# get node to update
|
|
||||||
node = self.get_node(node_id)
|
|
||||||
|
|
||||||
# set node position and broadcast it
|
# set node position and broadcast it
|
||||||
self.set_node_position(node, node_options)
|
self.set_node_position(node, node_options)
|
||||||
|
|
||||||
# update attributes
|
# update attributes
|
||||||
node.canvas = node_options.canvas
|
node.canvas = node_options.canvas
|
||||||
node.icon = node_options.icon
|
node.icon = node_options.icon
|
||||||
|
|
||||||
# set node as updated successfully
|
|
||||||
result = True
|
|
||||||
except KeyError:
|
|
||||||
logging.error("failure to update node that does not exist: %s", node_id)
|
|
||||||
|
|
||||||
return result
|
|
||||||
|
|
||||||
def set_node_position(self, node, node_options):
|
def set_node_position(self, node, node_options):
|
||||||
"""
|
"""
|
||||||
|
@ -594,7 +723,7 @@ class Session(object):
|
||||||
message_type=0,
|
message_type=0,
|
||||||
id=node.id,
|
id=node.id,
|
||||||
x_position=node.position.x,
|
x_position=node.position.x,
|
||||||
y_position=node.position.y
|
y_position=node.position.y,
|
||||||
)
|
)
|
||||||
self.broadcast_node(node_data)
|
self.broadcast_node(node_data)
|
||||||
|
|
||||||
|
@ -613,7 +742,10 @@ class Session(object):
|
||||||
|
|
||||||
:return: True if active, False otherwise
|
:return: True if active, False otherwise
|
||||||
"""
|
"""
|
||||||
result = self.state in {EventTypes.RUNTIME_STATE.value, EventTypes.DATACOLLECT_STATE.value}
|
result = self.state in {
|
||||||
|
EventTypes.RUNTIME_STATE.value,
|
||||||
|
EventTypes.DATACOLLECT_STATE.value,
|
||||||
|
}
|
||||||
logging.info("session(%s) checking if active: %s", self.id, result)
|
logging.info("session(%s) checking if active: %s", self.id, result)
|
||||||
return result
|
return result
|
||||||
|
|
||||||
|
@ -721,9 +853,18 @@ class Session(object):
|
||||||
if not node_options:
|
if not node_options:
|
||||||
node_options = NodeOptions()
|
node_options = NodeOptions()
|
||||||
node_options.model = "mdr"
|
node_options.model = "mdr"
|
||||||
return self.add_node(_type=NodeTypes.DEFAULT, _id=_id, node_options=node_options)
|
return self.add_node(
|
||||||
|
_type=NodeTypes.DEFAULT, _id=_id, node_options=node_options
|
||||||
|
)
|
||||||
|
|
||||||
def create_emane_network(self, model, geo_reference, geo_scale=None, node_options=NodeOptions(), config=None):
|
def create_emane_network(
|
||||||
|
self,
|
||||||
|
model,
|
||||||
|
geo_reference,
|
||||||
|
geo_scale=None,
|
||||||
|
node_options=NodeOptions(),
|
||||||
|
config=None,
|
||||||
|
):
|
||||||
"""
|
"""
|
||||||
Convenience method for creating an emane network.
|
Convenience method for creating an emane network.
|
||||||
|
|
||||||
|
@ -847,7 +988,11 @@ class Session(object):
|
||||||
state_name = state.name
|
state_name = state.name
|
||||||
|
|
||||||
if self.state == state_value:
|
if self.state == state_value:
|
||||||
logging.info("session(%s) is already in state: %s, skipping change", self.id, state_name)
|
logging.info(
|
||||||
|
"session(%s) is already in state: %s, skipping change",
|
||||||
|
self.id,
|
||||||
|
state_name,
|
||||||
|
)
|
||||||
return
|
return
|
||||||
|
|
||||||
self.state = state_value
|
self.state = state_value
|
||||||
|
@ -908,9 +1053,11 @@ class Session(object):
|
||||||
:param str data: hook data
|
:param str data: hook data
|
||||||
:return: nothing
|
:return: nothing
|
||||||
"""
|
"""
|
||||||
logging.info("setting state hook: %s - %s from %s", hook_type, file_name, source_name)
|
logging.info(
|
||||||
|
"setting state hook: %s - %s from %s", hook_type, file_name, source_name
|
||||||
|
)
|
||||||
|
|
||||||
_hook_id, state = hook_type.split(':')[:2]
|
_hook_id, state = hook_type.split(":")[:2]
|
||||||
if not state.isdigit():
|
if not state.isdigit():
|
||||||
logging.error("error setting hook having state '%s'", state)
|
logging.error("error setting hook having state '%s'", state)
|
||||||
return
|
return
|
||||||
|
@ -964,8 +1111,14 @@ class Session(object):
|
||||||
# execute hook file
|
# execute hook file
|
||||||
try:
|
try:
|
||||||
args = ["/bin/sh", file_name]
|
args = ["/bin/sh", file_name]
|
||||||
subprocess.check_call(args, stdout=stdout, stderr=stderr,
|
subprocess.check_call(
|
||||||
close_fds=True, cwd=self.session_dir, env=self.get_environment())
|
args,
|
||||||
|
stdout=stdout,
|
||||||
|
stderr=stderr,
|
||||||
|
close_fds=True,
|
||||||
|
cwd=self.session_dir,
|
||||||
|
env=self.get_environment(),
|
||||||
|
)
|
||||||
except (OSError, subprocess.CalledProcessError):
|
except (OSError, subprocess.CalledProcessError):
|
||||||
logging.exception("error running hook: %s", file_name)
|
logging.exception("error running hook: %s", file_name)
|
||||||
|
|
||||||
|
@ -979,10 +1132,15 @@ class Session(object):
|
||||||
for hook in self._state_hooks.get(state, []):
|
for hook in self._state_hooks.get(state, []):
|
||||||
try:
|
try:
|
||||||
hook(state)
|
hook(state)
|
||||||
except:
|
except Exception:
|
||||||
message = "exception occured when running %s state hook: %s" % (coreapi.state_name(state), hook)
|
message = "exception occured when running %s state hook: %s" % (
|
||||||
|
coreapi.state_name(state),
|
||||||
|
hook,
|
||||||
|
)
|
||||||
logging.exception(message)
|
logging.exception(message)
|
||||||
self.exception(ExceptionLevels.ERROR, "Session.run_state_hooks", None, message)
|
self.exception(
|
||||||
|
ExceptionLevels.ERROR, "Session.run_state_hooks", None, message
|
||||||
|
)
|
||||||
|
|
||||||
def add_state_hook(self, state, hook):
|
def add_state_hook(self, state, hook):
|
||||||
"""
|
"""
|
||||||
|
@ -994,7 +1152,7 @@ class Session(object):
|
||||||
"""
|
"""
|
||||||
hooks = self._state_hooks.setdefault(state, [])
|
hooks = self._state_hooks.setdefault(state, [])
|
||||||
if hook in hooks:
|
if hook in hooks:
|
||||||
raise ValueError("attempting to add duplicate state hook")
|
raise CoreError("attempting to add duplicate state hook")
|
||||||
hooks.append(hook)
|
hooks.append(hook)
|
||||||
|
|
||||||
if self.state == state:
|
if self.state == state:
|
||||||
|
@ -1055,15 +1213,23 @@ class Session(object):
|
||||||
if os.path.isfile(environment_config_file):
|
if os.path.isfile(environment_config_file):
|
||||||
utils.load_config(environment_config_file, env)
|
utils.load_config(environment_config_file, env)
|
||||||
except IOError:
|
except IOError:
|
||||||
logging.warning("environment configuration file does not exist: %s", environment_config_file)
|
logging.warning(
|
||||||
|
"environment configuration file does not exist: %s",
|
||||||
|
environment_config_file,
|
||||||
|
)
|
||||||
|
|
||||||
# attempt to read and add user environment file
|
# attempt to read and add user environment file
|
||||||
if self.user:
|
if self.user:
|
||||||
environment_user_file = os.path.join("/home", self.user, ".core", "environment")
|
environment_user_file = os.path.join(
|
||||||
|
"/home", self.user, ".core", "environment"
|
||||||
|
)
|
||||||
try:
|
try:
|
||||||
utils.load_config(environment_user_file, env)
|
utils.load_config(environment_user_file, env)
|
||||||
except IOError:
|
except IOError:
|
||||||
logging.debug("user core environment settings file not present: %s", environment_user_file)
|
logging.debug(
|
||||||
|
"user core environment settings file not present: %s",
|
||||||
|
environment_user_file,
|
||||||
|
)
|
||||||
|
|
||||||
return env
|
return env
|
||||||
|
|
||||||
|
@ -1113,21 +1279,22 @@ class Session(object):
|
||||||
|
|
||||||
return node_id
|
return node_id
|
||||||
|
|
||||||
def create_node(self, cls, *clsargs, **clskwds):
|
def create_node(self, cls, *args, **kwargs):
|
||||||
"""
|
"""
|
||||||
Create an emulation node.
|
Create an emulation node.
|
||||||
|
|
||||||
:param class cls: node class to create
|
:param class cls: node class to create
|
||||||
:param list clsargs: list of arguments for the class to create
|
:param list args: list of arguments for the class to create
|
||||||
:param dict clskwds: dictionary of arguments for the class to create
|
:param dict kwargs: dictionary of arguments for the class to create
|
||||||
:return: the created node instance
|
:return: the created node instance
|
||||||
|
:raises core.CoreError: when id of the node to create already exists
|
||||||
"""
|
"""
|
||||||
node = cls(self, *clsargs, **clskwds)
|
node = cls(self, *args, **kwargs)
|
||||||
|
|
||||||
with self._nodes_lock:
|
with self._nodes_lock:
|
||||||
if node.id in self.nodes:
|
if node.id in self.nodes:
|
||||||
node.shutdown()
|
node.shutdown()
|
||||||
raise KeyError("duplicate node id %s for %s" % (node.id, node.name))
|
raise CoreError("duplicate node id %s for %s" % (node.id, node.name))
|
||||||
self.nodes[node.id] = node
|
self.nodes[node.id] = node
|
||||||
|
|
||||||
return node
|
return node
|
||||||
|
@ -1139,9 +1306,10 @@ class Session(object):
|
||||||
:param int _id: node id to retrieve
|
:param int _id: node id to retrieve
|
||||||
:return: node for the given id
|
:return: node for the given id
|
||||||
:rtype: core.nodes.base.CoreNode
|
:rtype: core.nodes.base.CoreNode
|
||||||
|
:raises core.CoreError: when node does not exist
|
||||||
"""
|
"""
|
||||||
if _id not in self.nodes:
|
if _id not in self.nodes:
|
||||||
raise KeyError("unknown node id %s" % _id)
|
raise CoreError("unknown node id %s" % _id)
|
||||||
return self.nodes[_id]
|
return self.nodes[_id]
|
||||||
|
|
||||||
def delete_node(self, _id):
|
def delete_node(self, _id):
|
||||||
|
@ -1153,6 +1321,7 @@ class Session(object):
|
||||||
:rtype: bool
|
:rtype: bool
|
||||||
"""
|
"""
|
||||||
# delete node and check for session shutdown if a node was removed
|
# delete node and check for session shutdown if a node was removed
|
||||||
|
logging.info("deleting node(%s)", _id)
|
||||||
result = False
|
result = False
|
||||||
with self._nodes_lock:
|
with self._nodes_lock:
|
||||||
if _id in self.nodes:
|
if _id in self.nodes:
|
||||||
|
@ -1185,7 +1354,9 @@ class Session(object):
|
||||||
with open(file_path, "w") as f:
|
with open(file_path, "w") as f:
|
||||||
for _id in self.nodes.keys():
|
for _id in self.nodes.keys():
|
||||||
node = self.nodes[_id]
|
node = self.nodes[_id]
|
||||||
f.write("%s %s %s %s\n" % (_id, node.name, node.apitype, type(node)))
|
f.write(
|
||||||
|
"%s %s %s %s\n" % (_id, node.name, node.apitype, type(node))
|
||||||
|
)
|
||||||
except IOError:
|
except IOError:
|
||||||
logging.exception("error writing nodes file")
|
logging.exception("error writing nodes file")
|
||||||
|
|
||||||
|
@ -1194,8 +1365,13 @@ class Session(object):
|
||||||
Log information about the session in its current state.
|
Log information about the session in its current state.
|
||||||
"""
|
"""
|
||||||
logging.info("session id=%s name=%s state=%s", self.id, self.name, self.state)
|
logging.info("session id=%s name=%s state=%s", self.id, self.name, self.state)
|
||||||
logging.info("file=%s thumbnail=%s node_count=%s/%s",
|
logging.info(
|
||||||
self.file_name, self.thumbnail, self.get_node_count(), len(self.nodes))
|
"file=%s thumbnail=%s node_count=%s/%s",
|
||||||
|
self.file_name,
|
||||||
|
self.thumbnail,
|
||||||
|
self.get_node_count(),
|
||||||
|
len(self.nodes),
|
||||||
|
)
|
||||||
|
|
||||||
def exception(self, level, source, node_id, text):
|
def exception(self, level, source, node_id, text):
|
||||||
"""
|
"""
|
||||||
|
@ -1214,7 +1390,7 @@ class Session(object):
|
||||||
level=level,
|
level=level,
|
||||||
source=source,
|
source=source,
|
||||||
date=time.ctime(),
|
date=time.ctime(),
|
||||||
text=text
|
text=text,
|
||||||
)
|
)
|
||||||
|
|
||||||
self.broadcast_exception(exception_data)
|
self.broadcast_exception(exception_data)
|
||||||
|
@ -1265,8 +1441,12 @@ class Session(object):
|
||||||
count = 0
|
count = 0
|
||||||
for node_id in self.nodes:
|
for node_id in self.nodes:
|
||||||
node = self.nodes[node_id]
|
node = self.nodes[node_id]
|
||||||
is_p2p_ctrlnet = nodeutils.is_node(node, (NodeTypes.PEER_TO_PEER, NodeTypes.CONTROL_NET))
|
is_p2p_ctrlnet = nodeutils.is_node(
|
||||||
is_tap = nodeutils.is_node(node, NodeTypes.TAP_BRIDGE) and not nodeutils.is_node(node, NodeTypes.TUNNEL)
|
node, (NodeTypes.PEER_TO_PEER, NodeTypes.CONTROL_NET)
|
||||||
|
)
|
||||||
|
is_tap = nodeutils.is_node(
|
||||||
|
node, NodeTypes.TAP_BRIDGE
|
||||||
|
) and not nodeutils.is_node(node, NodeTypes.TUNNEL)
|
||||||
if is_p2p_ctrlnet or is_tap:
|
if is_p2p_ctrlnet or is_tap:
|
||||||
continue
|
continue
|
||||||
|
|
||||||
|
@ -1283,8 +1463,11 @@ class Session(object):
|
||||||
# this is called from instantiate() after receiving an event message
|
# this is called from instantiate() after receiving an event message
|
||||||
# for the instantiation state, and from the broker when distributed
|
# for the instantiation state, and from the broker when distributed
|
||||||
# nodes have been started
|
# nodes have been started
|
||||||
logging.info("session(%s) checking if not in runtime state, current state: %s", self.id,
|
logging.debug(
|
||||||
coreapi.state_name(self.state))
|
"session(%s) checking if not in runtime state, current state: %s",
|
||||||
|
self.id,
|
||||||
|
coreapi.state_name(self.state),
|
||||||
|
)
|
||||||
if self.state == EventTypes.RUNTIME_STATE.value:
|
if self.state == EventTypes.RUNTIME_STATE.value:
|
||||||
logging.info("valid runtime state found, returning")
|
logging.info("valid runtime state found, returning")
|
||||||
return
|
return
|
||||||
|
@ -1331,7 +1514,9 @@ class Session(object):
|
||||||
and links remain.
|
and links remain.
|
||||||
"""
|
"""
|
||||||
node_count = self.get_node_count()
|
node_count = self.get_node_count()
|
||||||
logging.info("session(%s) checking shutdown: %s nodes remaining", self.id, node_count)
|
logging.debug(
|
||||||
|
"session(%s) checking shutdown: %s nodes remaining", self.id, node_count
|
||||||
|
)
|
||||||
|
|
||||||
shutdown = False
|
shutdown = False
|
||||||
if node_count == 0:
|
if node_count == 0:
|
||||||
|
@ -1362,9 +1547,15 @@ class Session(object):
|
||||||
for _id in self.nodes:
|
for _id in self.nodes:
|
||||||
node = self.nodes[_id]
|
node = self.nodes[_id]
|
||||||
# TODO: PyCoreNode is not the type to check
|
# TODO: PyCoreNode is not the type to check
|
||||||
if isinstance(node, CoreNodeBase) and not nodeutils.is_node(node, NodeTypes.RJ45):
|
if isinstance(node, CoreNodeBase) and not nodeutils.is_node(
|
||||||
|
node, NodeTypes.RJ45
|
||||||
|
):
|
||||||
# add a control interface if configured
|
# add a control interface if configured
|
||||||
logging.info("booting node: %s", node.name)
|
logging.info(
|
||||||
|
"booting node(%s): %s",
|
||||||
|
node.name,
|
||||||
|
[x.name for x in node.services],
|
||||||
|
)
|
||||||
self.add_remove_control_interface(node=node, remove=False)
|
self.add_remove_control_interface(node=node, remove=False)
|
||||||
result = pool.apply_async(self.services.boot_services, (node,))
|
result = pool.apply_async(self.services.boot_services, (node,))
|
||||||
results.append(result)
|
results.append(result)
|
||||||
|
@ -1444,7 +1635,12 @@ class Session(object):
|
||||||
:return: control net node
|
:return: control net node
|
||||||
:rtype: core.nodes.network.CtrlNet
|
:rtype: core.nodes.network.CtrlNet
|
||||||
"""
|
"""
|
||||||
logging.debug("add/remove control net: index(%s) remove(%s) conf_required(%s)", net_index, remove, conf_required)
|
logging.debug(
|
||||||
|
"add/remove control net: index(%s) remove(%s) conf_required(%s)",
|
||||||
|
net_index,
|
||||||
|
remove,
|
||||||
|
conf_required,
|
||||||
|
)
|
||||||
prefix_spec_list = self.get_control_net_prefixes()
|
prefix_spec_list = self.get_control_net_prefixes()
|
||||||
prefix_spec = prefix_spec_list[net_index]
|
prefix_spec = prefix_spec_list[net_index]
|
||||||
if not prefix_spec:
|
if not prefix_spec:
|
||||||
|
@ -1467,7 +1663,7 @@ class Session(object):
|
||||||
return None
|
return None
|
||||||
|
|
||||||
return control_net
|
return control_net
|
||||||
except KeyError:
|
except CoreError:
|
||||||
if remove:
|
if remove:
|
||||||
return None
|
return None
|
||||||
|
|
||||||
|
@ -1513,10 +1709,12 @@ class Session(object):
|
||||||
break
|
break
|
||||||
|
|
||||||
if not prefix:
|
if not prefix:
|
||||||
logging.error("control network prefix not found for server: %s", servers[0])
|
logging.error(
|
||||||
|
"control network prefix not found for server: %s", servers[0]
|
||||||
|
)
|
||||||
assign_address = False
|
assign_address = False
|
||||||
try:
|
try:
|
||||||
prefix = prefixes[0].split(':', 1)[1]
|
prefix = prefixes[0].split(":", 1)[1]
|
||||||
except IndexError:
|
except IndexError:
|
||||||
prefix = prefixes[0]
|
prefix = prefixes[0]
|
||||||
# len(prefixes) == 1
|
# len(prefixes) == 1
|
||||||
|
@ -1528,9 +1726,14 @@ class Session(object):
|
||||||
|
|
||||||
logging.info("controlnet prefix: %s - %s", type(prefix), prefix)
|
logging.info("controlnet prefix: %s - %s", type(prefix), prefix)
|
||||||
control_net_class = nodeutils.get_node_class(NodeTypes.CONTROL_NET)
|
control_net_class = nodeutils.get_node_class(NodeTypes.CONTROL_NET)
|
||||||
control_net = self.create_node(cls=control_net_class, _id=_id, prefix=prefix,
|
control_net = self.create_node(
|
||||||
assign_address=assign_address,
|
cls=control_net_class,
|
||||||
updown_script=updown_script, serverintf=server_interface)
|
_id=_id,
|
||||||
|
prefix=prefix,
|
||||||
|
assign_address=assign_address,
|
||||||
|
updown_script=updown_script,
|
||||||
|
serverintf=server_interface,
|
||||||
|
)
|
||||||
|
|
||||||
# tunnels between controlnets will be built with Broker.addnettunnels()
|
# tunnels between controlnets will be built with Broker.addnettunnels()
|
||||||
# TODO: potentially remove documentation saying node ids are ints
|
# TODO: potentially remove documentation saying node ids are ints
|
||||||
|
@ -1541,7 +1744,9 @@ class Session(object):
|
||||||
|
|
||||||
return control_net
|
return control_net
|
||||||
|
|
||||||
def add_remove_control_interface(self, node, net_index=0, remove=False, conf_required=True):
|
def add_remove_control_interface(
|
||||||
|
self, node, net_index=0, remove=False, conf_required=True
|
||||||
|
):
|
||||||
"""
|
"""
|
||||||
Add a control interface to a node when a 'controlnet' prefix is
|
Add a control interface to a node when a 'controlnet' prefix is
|
||||||
listed in the config file or session options. Uses
|
listed in the config file or session options. Uses
|
||||||
|
@ -1569,7 +1774,10 @@ class Session(object):
|
||||||
control_ip = node.id
|
control_ip = node.id
|
||||||
|
|
||||||
try:
|
try:
|
||||||
addrlist = ["%s/%s" % (control_net.prefix.addr(control_ip), control_net.prefix.prefixlen)]
|
addrlist = [
|
||||||
|
"%s/%s"
|
||||||
|
% (control_net.prefix.addr(control_ip), control_net.prefix.prefixlen)
|
||||||
|
]
|
||||||
except ValueError:
|
except ValueError:
|
||||||
msg = "Control interface not added to node %s. " % node.id
|
msg = "Control interface not added to node %s. " % node.id
|
||||||
msg += "Invalid control network prefix (%s). " % control_net.prefix
|
msg += "Invalid control network prefix (%s). " % control_net.prefix
|
||||||
|
@ -1577,10 +1785,13 @@ class Session(object):
|
||||||
logging.exception(msg)
|
logging.exception(msg)
|
||||||
return
|
return
|
||||||
|
|
||||||
interface1 = node.newnetif(net=control_net,
|
interface1 = node.newnetif(
|
||||||
ifindex=control_net.CTRLIF_IDX_BASE + net_index,
|
net=control_net,
|
||||||
ifname="ctrl%d" % net_index, hwaddr=MacAddress.random(),
|
ifindex=control_net.CTRLIF_IDX_BASE + net_index,
|
||||||
addrlist=addrlist)
|
ifname="ctrl%d" % net_index,
|
||||||
|
hwaddr=MacAddress.random(),
|
||||||
|
addrlist=addrlist,
|
||||||
|
)
|
||||||
node.netif(interface1).control = True
|
node.netif(interface1).control = True
|
||||||
|
|
||||||
def update_control_interface_hosts(self, net_index=0, remove=False):
|
def update_control_interface_hosts(self, net_index=0, remove=False):
|
||||||
|
@ -1596,7 +1807,7 @@ class Session(object):
|
||||||
|
|
||||||
try:
|
try:
|
||||||
control_net = self.get_control_net(net_index)
|
control_net = self.get_control_net(net_index)
|
||||||
except KeyError:
|
except CoreError:
|
||||||
logging.exception("error retrieving control net node")
|
logging.exception("error retrieving control net node")
|
||||||
return
|
return
|
||||||
|
|
||||||
|
@ -1642,15 +1853,26 @@ class Session(object):
|
||||||
|
|
||||||
if current_time > 0:
|
if current_time > 0:
|
||||||
if event_time <= current_time:
|
if event_time <= current_time:
|
||||||
logging.warning("could not schedule past event for time %s (run time is now %s)", event_time, current_time)
|
logging.warning(
|
||||||
|
"could not schedule past event for time %s (run time is now %s)",
|
||||||
|
event_time,
|
||||||
|
current_time,
|
||||||
|
)
|
||||||
return
|
return
|
||||||
event_time = event_time - current_time
|
event_time = event_time - current_time
|
||||||
|
|
||||||
self.event_loop.add_event(event_time, self.run_event, node=node, name=name, data=data)
|
self.event_loop.add_event(
|
||||||
|
event_time, self.run_event, node=node, name=name, data=data
|
||||||
|
)
|
||||||
|
|
||||||
if not name:
|
if not name:
|
||||||
name = ""
|
name = ""
|
||||||
logging.info("scheduled event %s at time %s data=%s", name, event_time + current_time, data)
|
logging.info(
|
||||||
|
"scheduled event %s at time %s data=%s",
|
||||||
|
name,
|
||||||
|
event_time + current_time,
|
||||||
|
data,
|
||||||
|
)
|
||||||
|
|
||||||
# TODO: if data is None, this blows up, but this ties into how event functions are ran, need to clean that up
|
# TODO: if data is None, this blows up, but this ties into how event functions are ran, need to clean that up
|
||||||
def run_event(self, node_id=None, name=None, data=None):
|
def run_event(self, node_id=None, name=None, data=None):
|
||||||
|
|
|
@ -1,8 +1,5 @@
|
||||||
from core.config import ConfigurableManager
|
from core.config import ConfigurableManager, ConfigurableOptions, Configuration
|
||||||
from core.config import ConfigurableOptions
|
from core.emulator.enumerations import ConfigDataTypes, RegisterTlvs
|
||||||
from core.config import Configuration
|
|
||||||
from core.emulator.enumerations import ConfigDataTypes
|
|
||||||
from core.emulator.enumerations import RegisterTlvs
|
|
||||||
from core.plugins.sdt import Sdt
|
from core.plugins.sdt import Sdt
|
||||||
|
|
||||||
|
|
||||||
|
@ -10,21 +7,56 @@ class SessionConfig(ConfigurableManager, ConfigurableOptions):
|
||||||
"""
|
"""
|
||||||
Provides session configuration.
|
Provides session configuration.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
name = "session"
|
name = "session"
|
||||||
options = [
|
options = [
|
||||||
Configuration(_id="controlnet", _type=ConfigDataTypes.STRING, label="Control Network"),
|
Configuration(
|
||||||
Configuration(_id="controlnet0", _type=ConfigDataTypes.STRING, label="Control Network 0"),
|
_id="controlnet", _type=ConfigDataTypes.STRING, label="Control Network"
|
||||||
Configuration(_id="controlnet1", _type=ConfigDataTypes.STRING, label="Control Network 1"),
|
),
|
||||||
Configuration(_id="controlnet2", _type=ConfigDataTypes.STRING, label="Control Network 2"),
|
Configuration(
|
||||||
Configuration(_id="controlnet3", _type=ConfigDataTypes.STRING, label="Control Network 3"),
|
_id="controlnet0", _type=ConfigDataTypes.STRING, label="Control Network 0"
|
||||||
Configuration(_id="controlnet_updown_script", _type=ConfigDataTypes.STRING, label="Control Network Script"),
|
),
|
||||||
Configuration(_id="enablerj45", _type=ConfigDataTypes.BOOL, default="1", options=["On", "Off"],
|
Configuration(
|
||||||
label="Enable RJ45s"),
|
_id="controlnet1", _type=ConfigDataTypes.STRING, label="Control Network 1"
|
||||||
Configuration(_id="preservedir", _type=ConfigDataTypes.BOOL, default="0", options=["On", "Off"],
|
),
|
||||||
label="Preserve session dir"),
|
Configuration(
|
||||||
Configuration(_id="enablesdt", _type=ConfigDataTypes.BOOL, default="0", options=["On", "Off"],
|
_id="controlnet2", _type=ConfigDataTypes.STRING, label="Control Network 2"
|
||||||
label="Enable SDT3D output"),
|
),
|
||||||
Configuration(_id="sdturl", _type=ConfigDataTypes.STRING, default=Sdt.DEFAULT_SDT_URL, label="SDT3D URL")
|
Configuration(
|
||||||
|
_id="controlnet3", _type=ConfigDataTypes.STRING, label="Control Network 3"
|
||||||
|
),
|
||||||
|
Configuration(
|
||||||
|
_id="controlnet_updown_script",
|
||||||
|
_type=ConfigDataTypes.STRING,
|
||||||
|
label="Control Network Script",
|
||||||
|
),
|
||||||
|
Configuration(
|
||||||
|
_id="enablerj45",
|
||||||
|
_type=ConfigDataTypes.BOOL,
|
||||||
|
default="1",
|
||||||
|
options=["On", "Off"],
|
||||||
|
label="Enable RJ45s",
|
||||||
|
),
|
||||||
|
Configuration(
|
||||||
|
_id="preservedir",
|
||||||
|
_type=ConfigDataTypes.BOOL,
|
||||||
|
default="0",
|
||||||
|
options=["On", "Off"],
|
||||||
|
label="Preserve session dir",
|
||||||
|
),
|
||||||
|
Configuration(
|
||||||
|
_id="enablesdt",
|
||||||
|
_type=ConfigDataTypes.BOOL,
|
||||||
|
default="0",
|
||||||
|
options=["On", "Off"],
|
||||||
|
label="Enable SDT3D output",
|
||||||
|
),
|
||||||
|
Configuration(
|
||||||
|
_id="sdturl",
|
||||||
|
_type=ConfigDataTypes.STRING,
|
||||||
|
default=Sdt.DEFAULT_SDT_URL,
|
||||||
|
label="SDT3D URL",
|
||||||
|
),
|
||||||
]
|
]
|
||||||
config_type = RegisterTlvs.UTILITY.value
|
config_type = RegisterTlvs.UTILITY.value
|
||||||
|
|
||||||
|
@ -32,9 +64,16 @@ class SessionConfig(ConfigurableManager, ConfigurableOptions):
|
||||||
super(SessionConfig, self).__init__()
|
super(SessionConfig, self).__init__()
|
||||||
self.set_configs(self.default_values())
|
self.set_configs(self.default_values())
|
||||||
|
|
||||||
def get_config(self, _id, node_id=ConfigurableManager._default_node,
|
def get_config(
|
||||||
config_type=ConfigurableManager._default_type, default=None):
|
self,
|
||||||
value = super(SessionConfig, self).get_config(_id, node_id, config_type, default)
|
_id,
|
||||||
|
node_id=ConfigurableManager._default_node,
|
||||||
|
config_type=ConfigurableManager._default_type,
|
||||||
|
default=None,
|
||||||
|
):
|
||||||
|
value = super(SessionConfig, self).get_config(
|
||||||
|
_id, node_id, config_type, default
|
||||||
|
)
|
||||||
if value == "":
|
if value == "":
|
||||||
value = default
|
value = default
|
||||||
return value
|
return value
|
||||||
|
@ -58,5 +97,6 @@ class SessionMetaData(ConfigurableManager):
|
||||||
passed in from configure messages destined to the "metadata" object.
|
passed in from configure messages destined to the "metadata" object.
|
||||||
The data is not otherwise interpreted or processed.
|
The data is not otherwise interpreted or processed.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
name = "metadata"
|
name = "metadata"
|
||||||
config_type = RegisterTlvs.UTILITY.value
|
config_type = RegisterTlvs.UTILITY.value
|
||||||
|
|
|
@ -17,6 +17,7 @@ class CoreLocation(object):
|
||||||
track of a latitude/longitude/altitude reference point and scale in
|
track of a latitude/longitude/altitude reference point and scale in
|
||||||
order to convert between X,Y and geo coordinates.
|
order to convert between X,Y and geo coordinates.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
name = "location"
|
name = "location"
|
||||||
config_type = RegisterTlvs.UTILITY.value
|
config_type = RegisterTlvs.UTILITY.value
|
||||||
|
|
||||||
|
@ -118,7 +119,14 @@ class CoreLocation(object):
|
||||||
try:
|
try:
|
||||||
lat, lon = utm.to_latlon(e, n, zone[0], zone[1])
|
lat, lon = utm.to_latlon(e, n, zone[0], zone[1])
|
||||||
except utm.OutOfRangeError:
|
except utm.OutOfRangeError:
|
||||||
logging.exception("UTM out of range error for n=%s zone=%s xyz=(%s,%s,%s)", n, zone, x, y, z)
|
logging.exception(
|
||||||
|
"UTM out of range error for n=%s zone=%s xyz=(%s,%s,%s)",
|
||||||
|
n,
|
||||||
|
zone,
|
||||||
|
x,
|
||||||
|
y,
|
||||||
|
z,
|
||||||
|
)
|
||||||
lat, lon = self.refgeo[:2]
|
lat, lon = self.refgeo[:2]
|
||||||
# self.info("getgeo(%s,%s,%s) e=%s n=%s zone=%s lat,lon,alt=" \
|
# self.info("getgeo(%s,%s,%s) e=%s n=%s zone=%s lat,lon,alt=" \
|
||||||
# "%.3f,%.3f,%.3f" % (x, y, z, e, n, zone, lat, lon, alt))
|
# "%.3f,%.3f,%.3f" % (x, y, z, e, n, zone, lat, lon, alt))
|
||||||
|
@ -265,9 +273,9 @@ class CoreLocation(object):
|
||||||
if n < 0:
|
if n < 0:
|
||||||
# refpt in northern hemisphere and we crossed south of equator
|
# refpt in northern hemisphere and we crossed south of equator
|
||||||
n += 10000000
|
n += 10000000
|
||||||
zone = (zone[0], 'M')
|
zone = (zone[0], "M")
|
||||||
elif n > 10000000:
|
elif n > 10000000:
|
||||||
# refpt in southern hemisphere and we crossed north of equator
|
# refpt in southern hemisphere and we crossed north of equator
|
||||||
n -= 10000000
|
n -= 10000000
|
||||||
zone = (zone[0], 'N')
|
zone = (zone[0], "N")
|
||||||
return e, n, zone
|
return e, n, zone
|
||||||
|
|
|
@ -5,6 +5,7 @@ event.py: event loop implementation using a heap queue and threads.
|
||||||
import heapq
|
import heapq
|
||||||
import threading
|
import threading
|
||||||
import time
|
import time
|
||||||
|
|
||||||
from past.builtins import cmp
|
from past.builtins import cmp
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -11,20 +11,18 @@ import time
|
||||||
from builtins import int
|
from builtins import int
|
||||||
from functools import total_ordering
|
from functools import total_ordering
|
||||||
|
|
||||||
from core import utils
|
from core import CoreError, utils
|
||||||
from core.config import ConfigGroup
|
from core.config import ConfigGroup, ConfigurableOptions, Configuration, ModelManager
|
||||||
from core.config import ConfigurableOptions
|
from core.emulator.data import EventData, LinkData
|
||||||
from core.config import Configuration
|
from core.emulator.enumerations import (
|
||||||
from core.config import ModelManager
|
ConfigDataTypes,
|
||||||
from core.emulator.data import EventData
|
EventTypes,
|
||||||
from core.emulator.data import LinkData
|
LinkTypes,
|
||||||
from core.emulator.enumerations import ConfigDataTypes
|
MessageFlags,
|
||||||
from core.emulator.enumerations import EventTypes
|
MessageTypes,
|
||||||
from core.emulator.enumerations import LinkTypes
|
NodeTlvs,
|
||||||
from core.emulator.enumerations import MessageFlags
|
RegisterTlvs,
|
||||||
from core.emulator.enumerations import MessageTypes
|
)
|
||||||
from core.emulator.enumerations import NodeTlvs
|
|
||||||
from core.emulator.enumerations import RegisterTlvs
|
|
||||||
from core.nodes.base import CoreNodeBase
|
from core.nodes.base import CoreNodeBase
|
||||||
from core.nodes.ipaddress import IpAddress
|
from core.nodes.ipaddress import IpAddress
|
||||||
|
|
||||||
|
@ -34,6 +32,7 @@ class MobilityManager(ModelManager):
|
||||||
Member of session class for handling configuration data for mobility and
|
Member of session class for handling configuration data for mobility and
|
||||||
range models.
|
range models.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
name = "MobilityManager"
|
name = "MobilityManager"
|
||||||
config_type = RegisterTlvs.WIRELESS.value
|
config_type = RegisterTlvs.WIRELESS.value
|
||||||
|
|
||||||
|
@ -73,13 +72,17 @@ class MobilityManager(ModelManager):
|
||||||
node_ids = self.nodes()
|
node_ids = self.nodes()
|
||||||
|
|
||||||
for node_id in node_ids:
|
for node_id in node_ids:
|
||||||
logging.info("checking mobility startup for node: %s", node_id)
|
logging.debug("checking mobility startup for node: %s", node_id)
|
||||||
logging.info("node mobility configurations: %s", self.get_all_configs(node_id))
|
logging.debug(
|
||||||
|
"node mobility configurations: %s", self.get_all_configs(node_id)
|
||||||
|
)
|
||||||
|
|
||||||
try:
|
try:
|
||||||
node = self.session.get_node(node_id)
|
node = self.session.get_node(node_id)
|
||||||
except KeyError:
|
except CoreError:
|
||||||
logging.warning("skipping mobility configuration for unknown node: %s", node_id)
|
logging.warning(
|
||||||
|
"skipping mobility configuration for unknown node: %s", node_id
|
||||||
|
)
|
||||||
continue
|
continue
|
||||||
|
|
||||||
for model_name in self.models:
|
for model_name in self.models:
|
||||||
|
@ -109,12 +112,14 @@ class MobilityManager(ModelManager):
|
||||||
|
|
||||||
try:
|
try:
|
||||||
node = self.session.get_node(node_id)
|
node = self.session.get_node(node_id)
|
||||||
except KeyError:
|
except CoreError:
|
||||||
logging.exception("Ignoring event for model '%s', unknown node '%s'", name, node_id)
|
logging.exception(
|
||||||
|
"Ignoring event for model '%s', unknown node '%s'", name, node_id
|
||||||
|
)
|
||||||
return
|
return
|
||||||
|
|
||||||
# name is e.g. "mobility:ns2script"
|
# name is e.g. "mobility:ns2script"
|
||||||
models = name[9:].split(',')
|
models = name[9:].split(",")
|
||||||
for model in models:
|
for model in models:
|
||||||
try:
|
try:
|
||||||
cls = self.models[model]
|
cls = self.models[model]
|
||||||
|
@ -122,7 +127,10 @@ class MobilityManager(ModelManager):
|
||||||
logging.warning("Ignoring event for unknown model '%s'", model)
|
logging.warning("Ignoring event for unknown model '%s'", model)
|
||||||
continue
|
continue
|
||||||
|
|
||||||
if cls.config_type in [RegisterTlvs.WIRELESS.value, RegisterTlvs.MOBILITY.value]:
|
if cls.config_type in [
|
||||||
|
RegisterTlvs.WIRELESS.value,
|
||||||
|
RegisterTlvs.MOBILITY.value,
|
||||||
|
]:
|
||||||
model = node.mobility
|
model = node.mobility
|
||||||
else:
|
else:
|
||||||
continue
|
continue
|
||||||
|
@ -132,12 +140,23 @@ class MobilityManager(ModelManager):
|
||||||
continue
|
continue
|
||||||
|
|
||||||
if cls.name != model.name:
|
if cls.name != model.name:
|
||||||
logging.warning("Ignoring event for %s wrong model %s,%s", node.name, cls.name, model.name)
|
logging.warning(
|
||||||
|
"Ignoring event for %s wrong model %s,%s",
|
||||||
|
node.name,
|
||||||
|
cls.name,
|
||||||
|
model.name,
|
||||||
|
)
|
||||||
continue
|
continue
|
||||||
|
|
||||||
if event_type == EventTypes.STOP.value or event_type == EventTypes.RESTART.value:
|
if (
|
||||||
|
event_type == EventTypes.STOP.value
|
||||||
|
or event_type == EventTypes.RESTART.value
|
||||||
|
):
|
||||||
model.stop(move_initial=True)
|
model.stop(move_initial=True)
|
||||||
if event_type == EventTypes.START.value or event_type == EventTypes.RESTART.value:
|
if (
|
||||||
|
event_type == EventTypes.START.value
|
||||||
|
or event_type == EventTypes.RESTART.value
|
||||||
|
):
|
||||||
model.start()
|
model.start()
|
||||||
if event_type == EventTypes.PAUSE.value:
|
if event_type == EventTypes.PAUSE.value:
|
||||||
model.pause()
|
model.pause()
|
||||||
|
@ -166,7 +185,7 @@ class MobilityManager(ModelManager):
|
||||||
event_type=event_type,
|
event_type=event_type,
|
||||||
name="mobility:%s" % model.name,
|
name="mobility:%s" % model.name,
|
||||||
data=data,
|
data=data,
|
||||||
time="%s" % time.time()
|
time="%s" % time.time(),
|
||||||
)
|
)
|
||||||
|
|
||||||
self.session.broadcast_event(event_data)
|
self.session.broadcast_event(event_data)
|
||||||
|
@ -184,7 +203,7 @@ class MobilityManager(ModelManager):
|
||||||
for node_id in self.nodes():
|
for node_id in self.nodes():
|
||||||
try:
|
try:
|
||||||
node = self.session.get_node(node_id)
|
node = self.session.get_node(node_id)
|
||||||
except KeyError:
|
except CoreError:
|
||||||
continue
|
continue
|
||||||
if node.model:
|
if node.model:
|
||||||
node.model.update(moved, moved_netifs)
|
node.model.update(moved, moved_netifs)
|
||||||
|
@ -200,7 +219,7 @@ class MobilityManager(ModelManager):
|
||||||
node_id = node.id
|
node_id = node.id
|
||||||
self.phys[node_id] = node
|
self.phys[node_id] = node
|
||||||
if netnum not in self.physnets:
|
if netnum not in self.physnets:
|
||||||
self.physnets[netnum] = [node_id, ]
|
self.physnets[netnum] = [node_id]
|
||||||
else:
|
else:
|
||||||
self.physnets[netnum].append(node_id)
|
self.physnets[netnum].append(node_id)
|
||||||
|
|
||||||
|
@ -216,14 +235,19 @@ class MobilityManager(ModelManager):
|
||||||
:param message: link message to handle
|
:param message: link message to handle
|
||||||
:return: nothing
|
:return: nothing
|
||||||
"""
|
"""
|
||||||
if message.message_type == MessageTypes.LINK.value and message.flags & MessageFlags.ADD.value:
|
if (
|
||||||
|
message.message_type == MessageTypes.LINK.value
|
||||||
|
and message.flags & MessageFlags.ADD.value
|
||||||
|
):
|
||||||
nn = message.node_numbers()
|
nn = message.node_numbers()
|
||||||
# first node is always link layer node in Link add message
|
# first node is always link layer node in Link add message
|
||||||
if nn[0] not in self.session.broker.network_nodes:
|
if nn[0] not in self.session.broker.network_nodes:
|
||||||
return
|
return
|
||||||
if nn[1] in self.session.broker.physical_nodes:
|
if nn[1] in self.session.broker.physical_nodes:
|
||||||
# record the fact that this PhysicalNode is linked to a net
|
# record the fact that this PhysicalNode is linked to a net
|
||||||
dummy = CoreNodeBase(session=self.session, _id=nn[1], name="n%d" % nn[1], start=False)
|
dummy = CoreNodeBase(
|
||||||
|
session=self.session, _id=nn[1], name="n%d" % nn[1], start=False
|
||||||
|
)
|
||||||
self.addphys(nn[0], dummy)
|
self.addphys(nn[0], dummy)
|
||||||
|
|
||||||
# TODO: remove need to handling old style messages
|
# TODO: remove need to handling old style messages
|
||||||
|
@ -271,6 +295,7 @@ class WirelessModel(ConfigurableOptions):
|
||||||
Base class used by EMANE models and the basic range model.
|
Base class used by EMANE models and the basic range model.
|
||||||
Used for managing arbitrary configuration parameters.
|
Used for managing arbitrary configuration parameters.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
config_type = RegisterTlvs.WIRELESS.value
|
config_type = RegisterTlvs.WIRELESS.value
|
||||||
bitmap = None
|
bitmap = None
|
||||||
position_callback = None
|
position_callback = None
|
||||||
|
@ -323,21 +348,44 @@ class BasicRangeModel(WirelessModel):
|
||||||
and unlinks nodes based on this distance. This was formerly done from
|
and unlinks nodes based on this distance. This was formerly done from
|
||||||
the GUI.
|
the GUI.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
name = "basic_range"
|
name = "basic_range"
|
||||||
options = [
|
options = [
|
||||||
Configuration(_id="range", _type=ConfigDataTypes.UINT32, default="275", label="wireless range (pixels)"),
|
Configuration(
|
||||||
Configuration(_id="bandwidth", _type=ConfigDataTypes.UINT64, default="54000000", label="bandwidth (bps)"),
|
_id="range",
|
||||||
Configuration(_id="jitter", _type=ConfigDataTypes.UINT64, default="0", label="transmission jitter (usec)"),
|
_type=ConfigDataTypes.UINT32,
|
||||||
Configuration(_id="delay", _type=ConfigDataTypes.UINT64, default="5000",
|
default="275",
|
||||||
label="transmission delay (usec)"),
|
label="wireless range (pixels)",
|
||||||
Configuration(_id="error", _type=ConfigDataTypes.STRING, default="0", label="error rate (%)")
|
),
|
||||||
|
Configuration(
|
||||||
|
_id="bandwidth",
|
||||||
|
_type=ConfigDataTypes.UINT64,
|
||||||
|
default="54000000",
|
||||||
|
label="bandwidth (bps)",
|
||||||
|
),
|
||||||
|
Configuration(
|
||||||
|
_id="jitter",
|
||||||
|
_type=ConfigDataTypes.UINT64,
|
||||||
|
default="0",
|
||||||
|
label="transmission jitter (usec)",
|
||||||
|
),
|
||||||
|
Configuration(
|
||||||
|
_id="delay",
|
||||||
|
_type=ConfigDataTypes.UINT64,
|
||||||
|
default="5000",
|
||||||
|
label="transmission delay (usec)",
|
||||||
|
),
|
||||||
|
Configuration(
|
||||||
|
_id="error",
|
||||||
|
_type=ConfigDataTypes.STRING,
|
||||||
|
default="0",
|
||||||
|
label="error rate (%)",
|
||||||
|
),
|
||||||
]
|
]
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def config_groups(cls):
|
def config_groups(cls):
|
||||||
return [
|
return [ConfigGroup("Basic Range Parameters", 1, len(cls.configurations()))]
|
||||||
ConfigGroup("Basic Range Parameters", 1, len(cls.configurations()))
|
|
||||||
]
|
|
||||||
|
|
||||||
def __init__(self, session, _id):
|
def __init__(self, session, _id):
|
||||||
"""
|
"""
|
||||||
|
@ -345,7 +393,6 @@ class BasicRangeModel(WirelessModel):
|
||||||
|
|
||||||
:param core.session.Session session: related core session
|
:param core.session.Session session: related core session
|
||||||
:param int _id: object id
|
:param int _id: object id
|
||||||
:param dict config: values
|
|
||||||
"""
|
"""
|
||||||
super(BasicRangeModel, self).__init__(session=session, _id=_id)
|
super(BasicRangeModel, self).__init__(session=session, _id=_id)
|
||||||
self.session = session
|
self.session = session
|
||||||
|
@ -353,7 +400,7 @@ class BasicRangeModel(WirelessModel):
|
||||||
self._netifs = {}
|
self._netifs = {}
|
||||||
self._netifslock = threading.Lock()
|
self._netifslock = threading.Lock()
|
||||||
|
|
||||||
self.range = None
|
self.range = 0
|
||||||
self.bw = None
|
self.bw = None
|
||||||
self.delay = None
|
self.delay = None
|
||||||
self.loss = None
|
self.loss = None
|
||||||
|
@ -367,7 +414,11 @@ class BasicRangeModel(WirelessModel):
|
||||||
:return: nothing
|
:return: nothing
|
||||||
"""
|
"""
|
||||||
self.range = int(float(config["range"]))
|
self.range = int(float(config["range"]))
|
||||||
logging.info("basic range model configured for WLAN %d using range %d", self.wlan.id, self.range)
|
logging.debug(
|
||||||
|
"basic range model configured for WLAN %d using range %d",
|
||||||
|
self.wlan.id,
|
||||||
|
self.range,
|
||||||
|
)
|
||||||
self.bw = int(config["bandwidth"])
|
self.bw = int(config["bandwidth"])
|
||||||
if self.bw == 0:
|
if self.bw == 0:
|
||||||
self.bw = None
|
self.bw = None
|
||||||
|
@ -388,8 +439,14 @@ class BasicRangeModel(WirelessModel):
|
||||||
"""
|
"""
|
||||||
with self._netifslock:
|
with self._netifslock:
|
||||||
for netif in self._netifs:
|
for netif in self._netifs:
|
||||||
self.wlan.linkconfig(netif, bw=self.bw, delay=self.delay, loss=self.loss, duplicate=None,
|
self.wlan.linkconfig(
|
||||||
jitter=self.jitter)
|
netif,
|
||||||
|
bw=self.bw,
|
||||||
|
delay=self.delay,
|
||||||
|
loss=self.loss,
|
||||||
|
duplicate=None,
|
||||||
|
jitter=self.jitter,
|
||||||
|
)
|
||||||
|
|
||||||
def get_position(self, netif):
|
def get_position(self, netif):
|
||||||
"""
|
"""
|
||||||
|
@ -532,7 +589,7 @@ class BasicRangeModel(WirelessModel):
|
||||||
node1_id=interface1.node.id,
|
node1_id=interface1.node.id,
|
||||||
node2_id=interface2.node.id,
|
node2_id=interface2.node.id,
|
||||||
network_id=self.wlan.id,
|
network_id=self.wlan.id,
|
||||||
link_type=LinkTypes.WIRELESS.value
|
link_type=LinkTypes.WIRELESS.value,
|
||||||
)
|
)
|
||||||
|
|
||||||
def sendlinkmsg(self, netif, netif2, unlink=False):
|
def sendlinkmsg(self, netif, netif2, unlink=False):
|
||||||
|
@ -606,6 +663,7 @@ class WayPointMobility(WirelessModel):
|
||||||
"""
|
"""
|
||||||
Abstract class for mobility models that set node waypoints.
|
Abstract class for mobility models that set node waypoints.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
name = "waypoint"
|
name = "waypoint"
|
||||||
config_type = RegisterTlvs.MOBILITY.value
|
config_type = RegisterTlvs.MOBILITY.value
|
||||||
|
|
||||||
|
@ -666,7 +724,9 @@ class WayPointMobility(WirelessModel):
|
||||||
# no more waypoints or queued items, loop?
|
# no more waypoints or queued items, loop?
|
||||||
if not self.empty_queue_stop:
|
if not self.empty_queue_stop:
|
||||||
# keep running every refresh_ms, even with empty queue
|
# keep running every refresh_ms, even with empty queue
|
||||||
self.session.event_loop.add_event(0.001 * self.refresh_ms, self.runround)
|
self.session.event_loop.add_event(
|
||||||
|
0.001 * self.refresh_ms, self.runround
|
||||||
|
)
|
||||||
return
|
return
|
||||||
if not self.loopwaypoints():
|
if not self.loopwaypoints():
|
||||||
return self.stop(move_initial=False)
|
return self.stop(move_initial=False)
|
||||||
|
@ -918,16 +978,50 @@ class Ns2ScriptedMobility(WayPointMobility):
|
||||||
Handles the ns-2 script format, generated by scengen/setdest or
|
Handles the ns-2 script format, generated by scengen/setdest or
|
||||||
BonnMotion.
|
BonnMotion.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
name = "ns2script"
|
name = "ns2script"
|
||||||
options = [
|
options = [
|
||||||
Configuration(_id="file", _type=ConfigDataTypes.STRING, label="mobility script file"),
|
Configuration(
|
||||||
Configuration(_id="refresh_ms", _type=ConfigDataTypes.UINT32, default="50", label="refresh time (ms)"),
|
_id="file", _type=ConfigDataTypes.STRING, label="mobility script file"
|
||||||
Configuration(_id="loop", _type=ConfigDataTypes.BOOL, default="1", options=["On", "Off"], label="loop"),
|
),
|
||||||
Configuration(_id="autostart", _type=ConfigDataTypes.STRING, label="auto-start seconds (0.0 for runtime)"),
|
Configuration(
|
||||||
Configuration(_id="map", _type=ConfigDataTypes.STRING, label="node mapping (optional, e.g. 0:1,1:2,2:3)"),
|
_id="refresh_ms",
|
||||||
Configuration(_id="script_start", _type=ConfigDataTypes.STRING, label="script file to run upon start"),
|
_type=ConfigDataTypes.UINT32,
|
||||||
Configuration(_id="script_pause", _type=ConfigDataTypes.STRING, label="script file to run upon pause"),
|
default="50",
|
||||||
Configuration(_id="script_stop", _type=ConfigDataTypes.STRING, label="script file to run upon stop")
|
label="refresh time (ms)",
|
||||||
|
),
|
||||||
|
Configuration(
|
||||||
|
_id="loop",
|
||||||
|
_type=ConfigDataTypes.BOOL,
|
||||||
|
default="1",
|
||||||
|
options=["On", "Off"],
|
||||||
|
label="loop",
|
||||||
|
),
|
||||||
|
Configuration(
|
||||||
|
_id="autostart",
|
||||||
|
_type=ConfigDataTypes.STRING,
|
||||||
|
label="auto-start seconds (0.0 for runtime)",
|
||||||
|
),
|
||||||
|
Configuration(
|
||||||
|
_id="map",
|
||||||
|
_type=ConfigDataTypes.STRING,
|
||||||
|
label="node mapping (optional, e.g. 0:1,1:2,2:3)",
|
||||||
|
),
|
||||||
|
Configuration(
|
||||||
|
_id="script_start",
|
||||||
|
_type=ConfigDataTypes.STRING,
|
||||||
|
label="script file to run upon start",
|
||||||
|
),
|
||||||
|
Configuration(
|
||||||
|
_id="script_pause",
|
||||||
|
_type=ConfigDataTypes.STRING,
|
||||||
|
label="script file to run upon pause",
|
||||||
|
),
|
||||||
|
Configuration(
|
||||||
|
_id="script_stop",
|
||||||
|
_type=ConfigDataTypes.STRING,
|
||||||
|
label="script file to run upon stop",
|
||||||
|
),
|
||||||
]
|
]
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
|
@ -958,7 +1052,11 @@ class Ns2ScriptedMobility(WayPointMobility):
|
||||||
|
|
||||||
def update_config(self, config):
|
def update_config(self, config):
|
||||||
self.file = config["file"]
|
self.file = config["file"]
|
||||||
logging.info("ns-2 scripted mobility configured for WLAN %d using file: %s", self.id, self.file)
|
logging.info(
|
||||||
|
"ns-2 scripted mobility configured for WLAN %d using file: %s",
|
||||||
|
self.id,
|
||||||
|
self.file,
|
||||||
|
)
|
||||||
self.refresh_ms = int(config["refresh_ms"])
|
self.refresh_ms = int(config["refresh_ms"])
|
||||||
self.loop = config["loop"].lower() == "on"
|
self.loop = config["loop"].lower() == "on"
|
||||||
self.autostart = config["autostart"]
|
self.autostart = config["autostart"]
|
||||||
|
@ -982,7 +1080,9 @@ class Ns2ScriptedMobility(WayPointMobility):
|
||||||
try:
|
try:
|
||||||
f = open(filename, "r")
|
f = open(filename, "r")
|
||||||
except IOError:
|
except IOError:
|
||||||
logging.exception("ns-2 scripted mobility failed to load file: %s", self.file)
|
logging.exception(
|
||||||
|
"ns-2 scripted mobility failed to load file: %s", self.file
|
||||||
|
)
|
||||||
return
|
return
|
||||||
logging.info("reading ns-2 script file: %s" % filename)
|
logging.info("reading ns-2 script file: %s" % filename)
|
||||||
ln = 0
|
ln = 0
|
||||||
|
@ -990,7 +1090,7 @@ class Ns2ScriptedMobility(WayPointMobility):
|
||||||
inodenum = None
|
inodenum = None
|
||||||
for line in f:
|
for line in f:
|
||||||
ln += 1
|
ln += 1
|
||||||
if line[:2] != '$n':
|
if line[:2] != "$n":
|
||||||
continue
|
continue
|
||||||
try:
|
try:
|
||||||
if line[:8] == "$ns_ at ":
|
if line[:8] == "$ns_ at ":
|
||||||
|
@ -1001,7 +1101,7 @@ class Ns2ScriptedMobility(WayPointMobility):
|
||||||
# $ns_ at 1.00 "$node_(6) setdest 500.0 178.0 25.0"
|
# $ns_ at 1.00 "$node_(6) setdest 500.0 178.0 25.0"
|
||||||
parts = line.split()
|
parts = line.split()
|
||||||
time = float(parts[2])
|
time = float(parts[2])
|
||||||
nodenum = parts[3][1 + parts[3].index('('):parts[3].index(')')]
|
nodenum = parts[3][1 + parts[3].index("(") : parts[3].index(")")]
|
||||||
x = float(parts[5])
|
x = float(parts[5])
|
||||||
y = float(parts[6])
|
y = float(parts[6])
|
||||||
z = None
|
z = None
|
||||||
|
@ -1012,15 +1112,15 @@ class Ns2ScriptedMobility(WayPointMobility):
|
||||||
# $node_(6) set X_ 780.0
|
# $node_(6) set X_ 780.0
|
||||||
parts = line.split()
|
parts = line.split()
|
||||||
time = 0.0
|
time = 0.0
|
||||||
nodenum = parts[0][1 + parts[0].index('('):parts[0].index(')')]
|
nodenum = parts[0][1 + parts[0].index("(") : parts[0].index(")")]
|
||||||
if parts[2] == 'X_':
|
if parts[2] == "X_":
|
||||||
if ix is not None and iy is not None:
|
if ix is not None and iy is not None:
|
||||||
self.addinitial(self.map(inodenum), ix, iy, iz)
|
self.addinitial(self.map(inodenum), ix, iy, iz)
|
||||||
ix = iy = iz = None
|
ix = iy = iz = None
|
||||||
ix = float(parts[3])
|
ix = float(parts[3])
|
||||||
elif parts[2] == 'Y_':
|
elif parts[2] == "Y_":
|
||||||
iy = float(parts[3])
|
iy = float(parts[3])
|
||||||
elif parts[2] == 'Z_':
|
elif parts[2] == "Z_":
|
||||||
iz = float(parts[3])
|
iz = float(parts[3])
|
||||||
self.addinitial(self.map(nodenum), ix, iy, iz)
|
self.addinitial(self.map(nodenum), ix, iy, iz)
|
||||||
ix = iy = iz = None
|
ix = iy = iz = None
|
||||||
|
@ -1028,7 +1128,9 @@ class Ns2ScriptedMobility(WayPointMobility):
|
||||||
else:
|
else:
|
||||||
raise ValueError
|
raise ValueError
|
||||||
except ValueError:
|
except ValueError:
|
||||||
logging.exception("skipping line %d of file %s '%s'", ln, self.file, line)
|
logging.exception(
|
||||||
|
"skipping line %d of file %s '%s'", ln, self.file, line
|
||||||
|
)
|
||||||
continue
|
continue
|
||||||
if ix is not None and iy is not None:
|
if ix is not None and iy is not None:
|
||||||
self.addinitial(self.map(inodenum), ix, iy, iz)
|
self.addinitial(self.map(inodenum), ix, iy, iz)
|
||||||
|
@ -1054,7 +1156,9 @@ class Ns2ScriptedMobility(WayPointMobility):
|
||||||
return sessfn
|
return sessfn
|
||||||
|
|
||||||
if self.session.user is not None:
|
if self.session.user is not None:
|
||||||
userfn = os.path.join('/home', self.session.user, '.core', 'configs', file_name)
|
userfn = os.path.join(
|
||||||
|
"/home", self.session.user, ".core", "configs", file_name
|
||||||
|
)
|
||||||
if os.path.exists(userfn):
|
if os.path.exists(userfn):
|
||||||
return userfn
|
return userfn
|
||||||
|
|
||||||
|
@ -1100,16 +1204,22 @@ class Ns2ScriptedMobility(WayPointMobility):
|
||||||
|
|
||||||
:return: nothing
|
:return: nothing
|
||||||
"""
|
"""
|
||||||
if self.autostart == '':
|
if self.autostart == "":
|
||||||
logging.info("not auto-starting ns-2 script for %s" % self.wlan.name)
|
logging.info("not auto-starting ns-2 script for %s" % self.wlan.name)
|
||||||
return
|
return
|
||||||
try:
|
try:
|
||||||
t = float(self.autostart)
|
t = float(self.autostart)
|
||||||
except ValueError:
|
except ValueError:
|
||||||
logging.exception("Invalid auto-start seconds specified '%s' for %s", self.autostart, self.wlan.name)
|
logging.exception(
|
||||||
|
"Invalid auto-start seconds specified '%s' for %s",
|
||||||
|
self.autostart,
|
||||||
|
self.wlan.name,
|
||||||
|
)
|
||||||
return
|
return
|
||||||
self.movenodesinitial()
|
self.movenodesinitial()
|
||||||
logging.info("scheduling ns-2 script for %s autostart at %s" % (self.wlan.name, t))
|
logging.info(
|
||||||
|
"scheduling ns-2 script for %s autostart at %s" % (self.wlan.name, t)
|
||||||
|
)
|
||||||
self.state = self.STATE_RUNNING
|
self.state = self.STATE_RUNNING
|
||||||
self.session.event_loop.add_event(t, self.run)
|
self.session.event_loop.add_event(t, self.run)
|
||||||
|
|
||||||
|
@ -1167,8 +1277,10 @@ class Ns2ScriptedMobility(WayPointMobility):
|
||||||
filename = self.script_pause
|
filename = self.script_pause
|
||||||
elif typestr == "stop":
|
elif typestr == "stop":
|
||||||
filename = self.script_stop
|
filename = self.script_stop
|
||||||
if filename is None or filename == '':
|
if filename is None or filename == "":
|
||||||
return
|
return
|
||||||
filename = self.findfile(filename)
|
filename = self.findfile(filename)
|
||||||
args = ["/bin/sh", filename, typestr]
|
args = ["/bin/sh", filename, typestr]
|
||||||
utils.check_cmd(args, cwd=self.session.session_dir, env=self.session.get_environment())
|
utils.check_cmd(
|
||||||
|
args, cwd=self.session.session_dir, env=self.session.get_environment()
|
||||||
|
)
|
||||||
|
|
|
@ -11,17 +11,14 @@ import signal
|
||||||
import socket
|
import socket
|
||||||
import string
|
import string
|
||||||
import threading
|
import threading
|
||||||
|
from builtins import range
|
||||||
from socket import AF_INET, AF_INET6
|
from socket import AF_INET, AF_INET6
|
||||||
|
|
||||||
from builtins import range
|
from core import CoreCommandError, constants, utils
|
||||||
|
from core.emulator.data import LinkData, NodeData
|
||||||
from core import CoreCommandError, utils
|
from core.emulator.enumerations import LinkTypes, NodeTypes
|
||||||
from core import constants
|
from core.nodes import client, ipaddress, nodeutils
|
||||||
from core.emulator.data import NodeData, LinkData
|
from core.nodes.interface import CoreInterface, TunTap, Veth
|
||||||
from core.emulator.enumerations import NodeTypes, LinkTypes
|
|
||||||
from core.nodes import client, nodeutils, ipaddress
|
|
||||||
from core.nodes.interface import TunTap, CoreInterface
|
|
||||||
from core.nodes.interface import Veth
|
|
||||||
|
|
||||||
_DEFAULT_MTU = 1500
|
_DEFAULT_MTU = 1500
|
||||||
|
|
||||||
|
@ -32,6 +29,7 @@ class NodeBase(object):
|
||||||
"""
|
"""
|
||||||
Base class for CORE nodes (nodes and networks)
|
Base class for CORE nodes (nodes and networks)
|
||||||
"""
|
"""
|
||||||
|
|
||||||
apitype = None
|
apitype = None
|
||||||
|
|
||||||
# TODO: appears start has no usage, verify and remove
|
# TODO: appears start has no usage, verify and remove
|
||||||
|
@ -117,7 +115,7 @@ class NodeBase(object):
|
||||||
|
|
||||||
:param bool sort: boolean used to determine if interfaces should be sorted
|
:param bool sort: boolean used to determine if interfaces should be sorted
|
||||||
:return: network interfaces
|
:return: network interfaces
|
||||||
:rtype: list
|
:rtype: list[core.nodes.interfaces.CoreInterface]
|
||||||
"""
|
"""
|
||||||
if sort:
|
if sort:
|
||||||
return [self._netif[x] for x in sorted(self._netif)]
|
return [self._netif[x] for x in sorted(self._netif)]
|
||||||
|
@ -170,7 +168,7 @@ class NodeBase(object):
|
||||||
:param str lon: longitude
|
:param str lon: longitude
|
||||||
:param str alt: altitude
|
:param str alt: altitude
|
||||||
:return: node data object
|
:return: node data object
|
||||||
:rtype: core.data.NodeData
|
:rtype: core.emulator.data.NodeData
|
||||||
"""
|
"""
|
||||||
if self.apitype is None:
|
if self.apitype is None:
|
||||||
return None
|
return None
|
||||||
|
@ -199,7 +197,7 @@ class NodeBase(object):
|
||||||
altitude=alt,
|
altitude=alt,
|
||||||
model=model,
|
model=model,
|
||||||
emulation_server=emulation_server,
|
emulation_server=emulation_server,
|
||||||
services=services
|
services=services,
|
||||||
)
|
)
|
||||||
|
|
||||||
return node_data
|
return node_data
|
||||||
|
@ -212,7 +210,7 @@ class NodeBase(object):
|
||||||
|
|
||||||
:param flags: message flags
|
:param flags: message flags
|
||||||
:return: list of link data
|
:return: list of link data
|
||||||
:rtype: core.data.LinkData
|
:rtype: list[core.data.LinkData]
|
||||||
"""
|
"""
|
||||||
return []
|
return []
|
||||||
|
|
||||||
|
@ -236,16 +234,6 @@ class CoreNodeBase(NodeBase):
|
||||||
self.nodedir = None
|
self.nodedir = None
|
||||||
self.tmpnodedir = False
|
self.tmpnodedir = False
|
||||||
|
|
||||||
def addservice(self, service):
|
|
||||||
"""
|
|
||||||
Add a services to the service list.
|
|
||||||
|
|
||||||
:param core.services.coreservices.CoreService service: service to add
|
|
||||||
:return: nothing
|
|
||||||
"""
|
|
||||||
if service is not None:
|
|
||||||
self.services.append(service)
|
|
||||||
|
|
||||||
def makenodedir(self):
|
def makenodedir(self):
|
||||||
"""
|
"""
|
||||||
Create the node directory.
|
Create the node directory.
|
||||||
|
@ -320,7 +308,7 @@ class CoreNodeBase(NodeBase):
|
||||||
|
|
||||||
:param int ifindex: interface of index to attach
|
:param int ifindex: interface of index to attach
|
||||||
:param core.nodes.interface.CoreInterface net: network to attach
|
:param core.nodes.interface.CoreInterface net: network to attach
|
||||||
:return:
|
:return: nothing
|
||||||
"""
|
"""
|
||||||
if ifindex not in self._netif:
|
if ifindex not in self._netif:
|
||||||
raise ValueError("ifindex %s does not exist" % ifindex)
|
raise ValueError("ifindex %s does not exist" % ifindex)
|
||||||
|
@ -418,10 +406,13 @@ class CoreNode(CoreNodeBase):
|
||||||
"""
|
"""
|
||||||
Provides standard core node logic.
|
Provides standard core node logic.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
apitype = NodeTypes.DEFAULT.value
|
apitype = NodeTypes.DEFAULT.value
|
||||||
valid_address_types = {"inet", "inet6", "inet6link"}
|
valid_address_types = {"inet", "inet6", "inet6link"}
|
||||||
|
|
||||||
def __init__(self, session, _id=None, name=None, nodedir=None, bootsh="boot.sh", start=True):
|
def __init__(
|
||||||
|
self, session, _id=None, name=None, nodedir=None, bootsh="boot.sh", start=True
|
||||||
|
):
|
||||||
"""
|
"""
|
||||||
Create a CoreNode instance.
|
Create a CoreNode instance.
|
||||||
|
|
||||||
|
@ -434,7 +425,9 @@ class CoreNode(CoreNodeBase):
|
||||||
"""
|
"""
|
||||||
super(CoreNode, self).__init__(session, _id, name, start)
|
super(CoreNode, self).__init__(session, _id, name, start)
|
||||||
self.nodedir = nodedir
|
self.nodedir = nodedir
|
||||||
self.ctrlchnlname = os.path.abspath(os.path.join(self.session.session_dir, self.name))
|
self.ctrlchnlname = os.path.abspath(
|
||||||
|
os.path.join(self.session.session_dir, self.name)
|
||||||
|
)
|
||||||
self.client = None
|
self.client = None
|
||||||
self.pid = None
|
self.pid = None
|
||||||
self.up = False
|
self.up = False
|
||||||
|
@ -475,9 +468,12 @@ class CoreNode(CoreNodeBase):
|
||||||
vnoded = [
|
vnoded = [
|
||||||
constants.VNODED_BIN,
|
constants.VNODED_BIN,
|
||||||
"-v",
|
"-v",
|
||||||
"-c", self.ctrlchnlname,
|
"-c",
|
||||||
"-l", self.ctrlchnlname + ".log",
|
self.ctrlchnlname,
|
||||||
"-p", self.ctrlchnlname + ".pid"
|
"-l",
|
||||||
|
self.ctrlchnlname + ".log",
|
||||||
|
"-p",
|
||||||
|
self.ctrlchnlname + ".pid",
|
||||||
]
|
]
|
||||||
if self.nodedir:
|
if self.nodedir:
|
||||||
vnoded += ["-C", self.nodedir]
|
vnoded += ["-C", self.nodedir]
|
||||||
|
@ -493,11 +489,11 @@ class CoreNode(CoreNodeBase):
|
||||||
|
|
||||||
# bring up the loopback interface
|
# bring up the loopback interface
|
||||||
logging.debug("bringing up loopback interface")
|
logging.debug("bringing up loopback interface")
|
||||||
self.check_cmd([constants.IP_BIN, "link", "set", "lo", "up"])
|
self.network_cmd([constants.IP_BIN, "link", "set", "lo", "up"])
|
||||||
|
|
||||||
# set hostname for node
|
# set hostname for node
|
||||||
logging.debug("setting hostname: %s", self.name)
|
logging.debug("setting hostname: %s", self.name)
|
||||||
self.check_cmd(["hostname", self.name])
|
self.network_cmd(["hostname", self.name])
|
||||||
|
|
||||||
# mark node as up
|
# mark node as up
|
||||||
self.up = True
|
self.up = True
|
||||||
|
@ -572,6 +568,17 @@ class CoreNode(CoreNodeBase):
|
||||||
"""
|
"""
|
||||||
return self.client.cmd_output(args)
|
return self.client.cmd_output(args)
|
||||||
|
|
||||||
|
def network_cmd(self, args):
|
||||||
|
"""
|
||||||
|
Runs a command for a node that is used to configure and setup network interfaces.
|
||||||
|
|
||||||
|
:param list[str]|str args: command to run
|
||||||
|
:return: combined stdout and stderr
|
||||||
|
:rtype: str
|
||||||
|
:raises CoreCommandError: when a non-zero exit status occurs
|
||||||
|
"""
|
||||||
|
return self.check_cmd(args)
|
||||||
|
|
||||||
def check_cmd(self, args):
|
def check_cmd(self, args):
|
||||||
"""
|
"""
|
||||||
Runs shell command on node.
|
Runs shell command on node.
|
||||||
|
@ -601,7 +608,9 @@ class CoreNode(CoreNodeBase):
|
||||||
"""
|
"""
|
||||||
if path[0] != "/":
|
if path[0] != "/":
|
||||||
raise ValueError("path not fully qualified: %s" % path)
|
raise ValueError("path not fully qualified: %s" % path)
|
||||||
hostpath = os.path.join(self.nodedir, os.path.normpath(path).strip("/").replace("/", "."))
|
hostpath = os.path.join(
|
||||||
|
self.nodedir, os.path.normpath(path).strip("/").replace("/", ".")
|
||||||
|
)
|
||||||
os.mkdir(hostpath)
|
os.mkdir(hostpath)
|
||||||
self.mount(hostpath, path)
|
self.mount(hostpath, path)
|
||||||
|
|
||||||
|
@ -615,8 +624,13 @@ class CoreNode(CoreNodeBase):
|
||||||
:raises CoreCommandError: when a non-zero exit status occurs
|
:raises CoreCommandError: when a non-zero exit status occurs
|
||||||
"""
|
"""
|
||||||
source = os.path.abspath(source)
|
source = os.path.abspath(source)
|
||||||
logging.info("node(%s) mounting: %s at %s", self.name, source, target)
|
logging.debug("node(%s) mounting: %s at %s", self.name, source, target)
|
||||||
cmd = 'mkdir -p "%s" && %s -n --bind "%s" "%s"' % (target, constants.MOUNT_BIN, source, target)
|
cmd = 'mkdir -p "%s" && %s -n --bind "%s" "%s"' % (
|
||||||
|
target,
|
||||||
|
constants.MOUNT_BIN,
|
||||||
|
source,
|
||||||
|
target,
|
||||||
|
)
|
||||||
status, output = self.client.shcmd_result(cmd)
|
status, output = self.client.shcmd_result(cmd)
|
||||||
if status:
|
if status:
|
||||||
raise CoreCommandError(status, cmd, output)
|
raise CoreCommandError(status, cmd, output)
|
||||||
|
@ -663,19 +677,27 @@ class CoreNode(CoreNodeBase):
|
||||||
if len(name) >= 16:
|
if len(name) >= 16:
|
||||||
raise ValueError("interface name (%s) too long" % name)
|
raise ValueError("interface name (%s) too long" % name)
|
||||||
|
|
||||||
veth = Veth(node=self, name=name, localname=localname, net=net, start=self.up)
|
veth = Veth(
|
||||||
|
node=self, name=name, localname=localname, net=net, start=self.up
|
||||||
|
)
|
||||||
|
|
||||||
if self.up:
|
if self.up:
|
||||||
utils.check_cmd([constants.IP_BIN, "link", "set", veth.name, "netns", str(self.pid)])
|
utils.check_cmd(
|
||||||
self.check_cmd([constants.IP_BIN, "link", "set", veth.name, "name", ifname])
|
[constants.IP_BIN, "link", "set", veth.name, "netns", str(self.pid)]
|
||||||
self.check_cmd([constants.ETHTOOL_BIN, "-K", ifname, "rx", "off", "tx", "off"])
|
)
|
||||||
|
self.network_cmd(
|
||||||
|
[constants.IP_BIN, "link", "set", veth.name, "name", ifname]
|
||||||
|
)
|
||||||
|
self.network_cmd(
|
||||||
|
[constants.ETHTOOL_BIN, "-K", ifname, "rx", "off", "tx", "off"]
|
||||||
|
)
|
||||||
|
|
||||||
veth.name = ifname
|
veth.name = ifname
|
||||||
|
|
||||||
if self.up:
|
if self.up:
|
||||||
# TODO: potentially find better way to query interface ID
|
# TODO: potentially find better way to query interface ID
|
||||||
# retrieve interface information
|
# retrieve interface information
|
||||||
output = self.check_cmd(["ip", "link", "show", veth.name])
|
output = self.network_cmd([constants.IP_BIN, "link", "show", veth.name])
|
||||||
logging.debug("interface command output: %s", output)
|
logging.debug("interface command output: %s", output)
|
||||||
output = output.split("\n")
|
output = output.split("\n")
|
||||||
veth.flow_id = int(output[0].strip().split(":")[0]) + 1
|
veth.flow_id = int(output[0].strip().split(":")[0]) + 1
|
||||||
|
@ -685,6 +707,7 @@ class CoreNode(CoreNodeBase):
|
||||||
logging.debug("interface mac: %s - %s", veth.name, veth.hwaddr)
|
logging.debug("interface mac: %s - %s", veth.name, veth.hwaddr)
|
||||||
|
|
||||||
try:
|
try:
|
||||||
|
# add network interface to the node. If unsuccessful, destroy the network interface and raise exception.
|
||||||
self.addnetif(veth, ifindex)
|
self.addnetif(veth, ifindex)
|
||||||
except ValueError as e:
|
except ValueError as e:
|
||||||
veth.shutdown()
|
veth.shutdown()
|
||||||
|
@ -713,7 +736,9 @@ class CoreNode(CoreNodeBase):
|
||||||
sessionid = self.session.short_session_id()
|
sessionid = self.session.short_session_id()
|
||||||
localname = "tap%s.%s.%s" % (self.id, ifindex, sessionid)
|
localname = "tap%s.%s.%s" % (self.id, ifindex, sessionid)
|
||||||
name = ifname
|
name = ifname
|
||||||
tuntap = TunTap(node=self, name=name, localname=localname, net=net, start=self.up)
|
tuntap = TunTap(
|
||||||
|
node=self, name=name, localname=localname, net=net, start=self.up
|
||||||
|
)
|
||||||
|
|
||||||
try:
|
try:
|
||||||
self.addnetif(tuntap, ifindex)
|
self.addnetif(tuntap, ifindex)
|
||||||
|
@ -735,8 +760,16 @@ class CoreNode(CoreNodeBase):
|
||||||
"""
|
"""
|
||||||
self._netif[ifindex].sethwaddr(addr)
|
self._netif[ifindex].sethwaddr(addr)
|
||||||
if self.up:
|
if self.up:
|
||||||
args = [constants.IP_BIN, "link", "set", "dev", self.ifname(ifindex), "address", str(addr)]
|
args = [
|
||||||
self.check_cmd(args)
|
constants.IP_BIN,
|
||||||
|
"link",
|
||||||
|
"set",
|
||||||
|
"dev",
|
||||||
|
self.ifname(ifindex),
|
||||||
|
"address",
|
||||||
|
str(addr),
|
||||||
|
]
|
||||||
|
self.network_cmd(args)
|
||||||
|
|
||||||
def addaddr(self, ifindex, addr):
|
def addaddr(self, ifindex, addr):
|
||||||
"""
|
"""
|
||||||
|
@ -749,11 +782,27 @@ class CoreNode(CoreNodeBase):
|
||||||
if self.up:
|
if self.up:
|
||||||
# check if addr is ipv6
|
# check if addr is ipv6
|
||||||
if ":" in str(addr):
|
if ":" in str(addr):
|
||||||
args = [constants.IP_BIN, "addr", "add", str(addr), "dev", self.ifname(ifindex)]
|
args = [
|
||||||
self.check_cmd(args)
|
constants.IP_BIN,
|
||||||
|
"addr",
|
||||||
|
"add",
|
||||||
|
str(addr),
|
||||||
|
"dev",
|
||||||
|
self.ifname(ifindex),
|
||||||
|
]
|
||||||
|
self.network_cmd(args)
|
||||||
else:
|
else:
|
||||||
args = [constants.IP_BIN, "addr", "add", str(addr), "broadcast", "+", "dev", self.ifname(ifindex)]
|
args = [
|
||||||
self.check_cmd(args)
|
constants.IP_BIN,
|
||||||
|
"addr",
|
||||||
|
"add",
|
||||||
|
str(addr),
|
||||||
|
"broadcast",
|
||||||
|
"+",
|
||||||
|
"dev",
|
||||||
|
self.ifname(ifindex),
|
||||||
|
]
|
||||||
|
self.network_cmd(args)
|
||||||
|
|
||||||
self._netif[ifindex].addaddr(addr)
|
self._netif[ifindex].addaddr(addr)
|
||||||
|
|
||||||
|
@ -772,7 +821,16 @@ class CoreNode(CoreNodeBase):
|
||||||
logging.exception("trying to delete unknown address: %s" % addr)
|
logging.exception("trying to delete unknown address: %s" % addr)
|
||||||
|
|
||||||
if self.up:
|
if self.up:
|
||||||
self.check_cmd([constants.IP_BIN, "addr", "del", str(addr), "dev", self.ifname(ifindex)])
|
self.network_cmd(
|
||||||
|
[
|
||||||
|
constants.IP_BIN,
|
||||||
|
"addr",
|
||||||
|
"del",
|
||||||
|
str(addr),
|
||||||
|
"dev",
|
||||||
|
self.ifname(ifindex),
|
||||||
|
]
|
||||||
|
)
|
||||||
|
|
||||||
def delalladdr(self, ifindex, address_types=None):
|
def delalladdr(self, ifindex, address_types=None):
|
||||||
"""
|
"""
|
||||||
|
@ -791,7 +849,9 @@ class CoreNode(CoreNodeBase):
|
||||||
|
|
||||||
for address_type in address_types:
|
for address_type in address_types:
|
||||||
if address_type not in self.valid_address_types:
|
if address_type not in self.valid_address_types:
|
||||||
raise ValueError("addr type must be in: %s" % " ".join(self.valid_address_types))
|
raise ValueError(
|
||||||
|
"addr type must be in: %s" % " ".join(self.valid_address_types)
|
||||||
|
)
|
||||||
for address in addresses[address_type]:
|
for address in addresses[address_type]:
|
||||||
self.deladdr(ifindex, address)
|
self.deladdr(ifindex, address)
|
||||||
|
|
||||||
|
@ -806,7 +866,9 @@ class CoreNode(CoreNodeBase):
|
||||||
:return: nothing
|
:return: nothing
|
||||||
"""
|
"""
|
||||||
if self.up:
|
if self.up:
|
||||||
self.check_cmd([constants.IP_BIN, "link", "set", self.ifname(ifindex), "up"])
|
self.network_cmd(
|
||||||
|
[constants.IP_BIN, "link", "set", self.ifname(ifindex), "up"]
|
||||||
|
)
|
||||||
|
|
||||||
def newnetif(self, net=None, addrlist=None, hwaddr=None, ifindex=None, ifname=None):
|
def newnetif(self, net=None, addrlist=None, hwaddr=None, ifindex=None, ifname=None):
|
||||||
"""
|
"""
|
||||||
|
@ -862,18 +924,41 @@ class CoreNode(CoreNodeBase):
|
||||||
:return: nothing
|
:return: nothing
|
||||||
"""
|
"""
|
||||||
tmplen = 8
|
tmplen = 8
|
||||||
tmp1 = "tmp." + "".join([random.choice(string.ascii_lowercase) for _ in range(tmplen)])
|
tmp1 = "tmp." + "".join(
|
||||||
tmp2 = "tmp." + "".join([random.choice(string.ascii_lowercase) for _ in range(tmplen)])
|
[random.choice(string.ascii_lowercase) for _ in range(tmplen)]
|
||||||
utils.check_cmd([constants.IP_BIN, "link", "add", "name", tmp1, "type", "veth", "peer", "name", tmp2])
|
)
|
||||||
|
tmp2 = "tmp." + "".join(
|
||||||
|
[random.choice(string.ascii_lowercase) for _ in range(tmplen)]
|
||||||
|
)
|
||||||
|
utils.check_cmd(
|
||||||
|
[
|
||||||
|
constants.IP_BIN,
|
||||||
|
"link",
|
||||||
|
"add",
|
||||||
|
"name",
|
||||||
|
tmp1,
|
||||||
|
"type",
|
||||||
|
"veth",
|
||||||
|
"peer",
|
||||||
|
"name",
|
||||||
|
tmp2,
|
||||||
|
]
|
||||||
|
)
|
||||||
|
|
||||||
utils.check_cmd([constants.IP_BIN, "link", "set", tmp1, "netns", str(self.pid)])
|
utils.check_cmd([constants.IP_BIN, "link", "set", tmp1, "netns", str(self.pid)])
|
||||||
self.check_cmd([constants.IP_BIN, "link", "set", tmp1, "name", ifname])
|
self.network_cmd([constants.IP_BIN, "link", "set", tmp1, "name", ifname])
|
||||||
interface = CoreInterface(node=self, name=ifname, mtu=_DEFAULT_MTU)
|
interface = CoreInterface(node=self, name=ifname, mtu=_DEFAULT_MTU)
|
||||||
self.addnetif(interface, self.newifindex())
|
self.addnetif(interface, self.newifindex())
|
||||||
|
|
||||||
utils.check_cmd([constants.IP_BIN, "link", "set", tmp2, "netns", str(othernode.pid)])
|
utils.check_cmd(
|
||||||
othernode.check_cmd([constants.IP_BIN, "link", "set", tmp2, "name", otherifname])
|
[constants.IP_BIN, "link", "set", tmp2, "netns", str(othernode.pid)]
|
||||||
other_interface = CoreInterface(node=othernode, name=otherifname, mtu=_DEFAULT_MTU)
|
)
|
||||||
|
othernode.network_cmd(
|
||||||
|
[constants.IP_BIN, "link", "set", tmp2, "name", otherifname]
|
||||||
|
)
|
||||||
|
other_interface = CoreInterface(
|
||||||
|
node=othernode, name=otherifname, mtu=_DEFAULT_MTU
|
||||||
|
)
|
||||||
othernode.addnetif(other_interface, othernode.newifindex())
|
othernode.addnetif(other_interface, othernode.newifindex())
|
||||||
|
|
||||||
def addfile(self, srcname, filename):
|
def addfile(self, srcname, filename):
|
||||||
|
@ -936,7 +1021,9 @@ class CoreNode(CoreNodeBase):
|
||||||
with self.opennodefile(filename, "w") as open_file:
|
with self.opennodefile(filename, "w") as open_file:
|
||||||
open_file.write(contents)
|
open_file.write(contents)
|
||||||
os.chmod(open_file.name, mode)
|
os.chmod(open_file.name, mode)
|
||||||
logging.info("node(%s) added file: %s; mode: 0%o", self.name, open_file.name, mode)
|
logging.debug(
|
||||||
|
"node(%s) added file: %s; mode: 0%o", self.name, open_file.name, mode
|
||||||
|
)
|
||||||
|
|
||||||
def nodefilecopy(self, filename, srcfilename, mode=None):
|
def nodefilecopy(self, filename, srcfilename, mode=None):
|
||||||
"""
|
"""
|
||||||
|
@ -952,13 +1039,16 @@ class CoreNode(CoreNodeBase):
|
||||||
shutil.copy2(srcfilename, hostfilename)
|
shutil.copy2(srcfilename, hostfilename)
|
||||||
if mode is not None:
|
if mode is not None:
|
||||||
os.chmod(hostfilename, mode)
|
os.chmod(hostfilename, mode)
|
||||||
logging.info("node(%s) copied file: %s; mode: %s", self.name, hostfilename, mode)
|
logging.info(
|
||||||
|
"node(%s) copied file: %s; mode: %s", self.name, hostfilename, mode
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
class CoreNetworkBase(NodeBase):
|
class CoreNetworkBase(NodeBase):
|
||||||
"""
|
"""
|
||||||
Base class for networks
|
Base class for networks
|
||||||
"""
|
"""
|
||||||
|
|
||||||
linktype = LinkTypes.WIRED.value
|
linktype = LinkTypes.WIRED.value
|
||||||
|
|
||||||
def __init__(self, session, _id, name, start=True):
|
def __init__(self, session, _id, name, start=True):
|
||||||
|
@ -1019,6 +1109,11 @@ class CoreNetworkBase(NodeBase):
|
||||||
"""
|
"""
|
||||||
Build link data objects for this network. Each link object describes a link
|
Build link data objects for this network. Each link object describes a link
|
||||||
between this network and a node.
|
between this network and a node.
|
||||||
|
|
||||||
|
:param int flags: message type
|
||||||
|
:return: list of link data
|
||||||
|
:rtype: list[core.data.LinkData]
|
||||||
|
|
||||||
"""
|
"""
|
||||||
all_links = []
|
all_links = []
|
||||||
|
|
||||||
|
@ -1036,9 +1131,9 @@ class CoreNetworkBase(NodeBase):
|
||||||
linked_node = netif.othernet
|
linked_node = netif.othernet
|
||||||
if linked_node.id == self.id:
|
if linked_node.id == self.id:
|
||||||
continue
|
continue
|
||||||
netif.swapparams('_params_up')
|
netif.swapparams("_params_up")
|
||||||
upstream_params = netif.getparams()
|
upstream_params = netif.getparams()
|
||||||
netif.swapparams('_params_up')
|
netif.swapparams("_params_up")
|
||||||
if netif.getparams() != upstream_params:
|
if netif.getparams() != upstream_params:
|
||||||
uni = True
|
uni = True
|
||||||
|
|
||||||
|
@ -1080,7 +1175,7 @@ class CoreNetworkBase(NodeBase):
|
||||||
bandwidth=netif.getparam("bw"),
|
bandwidth=netif.getparam("bw"),
|
||||||
dup=netif.getparam("duplicate"),
|
dup=netif.getparam("duplicate"),
|
||||||
jitter=netif.getparam("jitter"),
|
jitter=netif.getparam("jitter"),
|
||||||
per=netif.getparam("loss")
|
per=netif.getparam("loss"),
|
||||||
)
|
)
|
||||||
|
|
||||||
all_links.append(link_data)
|
all_links.append(link_data)
|
||||||
|
@ -1088,7 +1183,7 @@ class CoreNetworkBase(NodeBase):
|
||||||
if not uni:
|
if not uni:
|
||||||
continue
|
continue
|
||||||
|
|
||||||
netif.swapparams('_params_up')
|
netif.swapparams("_params_up")
|
||||||
link_data = LinkData(
|
link_data = LinkData(
|
||||||
message_type=0,
|
message_type=0,
|
||||||
node1_id=linked_node.id,
|
node1_id=linked_node.id,
|
||||||
|
@ -1098,9 +1193,9 @@ class CoreNetworkBase(NodeBase):
|
||||||
bandwidth=netif.getparam("bw"),
|
bandwidth=netif.getparam("bw"),
|
||||||
dup=netif.getparam("duplicate"),
|
dup=netif.getparam("duplicate"),
|
||||||
jitter=netif.getparam("jitter"),
|
jitter=netif.getparam("jitter"),
|
||||||
per=netif.getparam("loss")
|
per=netif.getparam("loss"),
|
||||||
)
|
)
|
||||||
netif.swapparams('_params_up')
|
netif.swapparams("_params_up")
|
||||||
|
|
||||||
all_links.append(link_data)
|
all_links.append(link_data)
|
||||||
|
|
||||||
|
|
|
@ -6,11 +6,9 @@ The control channel can be accessed via calls using the vcmd shell.
|
||||||
|
|
||||||
import logging
|
import logging
|
||||||
import os
|
import os
|
||||||
|
from subprocess import PIPE, Popen
|
||||||
|
|
||||||
from subprocess import Popen, PIPE
|
from core import CoreCommandError, constants, utils
|
||||||
|
|
||||||
from core import CoreCommandError, utils
|
|
||||||
from core import constants
|
|
||||||
|
|
||||||
|
|
||||||
class VnodeClient(object):
|
class VnodeClient(object):
|
||||||
|
@ -137,7 +135,15 @@ class VnodeClient(object):
|
||||||
:rtype: int
|
:rtype: int
|
||||||
"""
|
"""
|
||||||
args = utils.split_args(args)
|
args = utils.split_args(args)
|
||||||
return os.spawnlp(os.P_WAIT, constants.VCMD_BIN, constants.VCMD_BIN, "-c", self.ctrlchnlname, "--", *args)
|
return os.spawnlp(
|
||||||
|
os.P_WAIT,
|
||||||
|
constants.VCMD_BIN,
|
||||||
|
constants.VCMD_BIN,
|
||||||
|
"-c",
|
||||||
|
self.ctrlchnlname,
|
||||||
|
"--",
|
||||||
|
*args
|
||||||
|
)
|
||||||
|
|
||||||
def redircmd(self, infd, outfd, errfd, args, wait=True):
|
def redircmd(self, infd, outfd, errfd, args, wait=True):
|
||||||
"""
|
"""
|
||||||
|
@ -177,11 +183,27 @@ class VnodeClient(object):
|
||||||
:return: terminal command result
|
:return: terminal command result
|
||||||
:rtype: int
|
:rtype: int
|
||||||
"""
|
"""
|
||||||
args = ("xterm", "-ut", "-title", self.name, "-e", constants.VCMD_BIN, "-c", self.ctrlchnlname, "--", sh)
|
args = (
|
||||||
|
"xterm",
|
||||||
|
"-ut",
|
||||||
|
"-title",
|
||||||
|
self.name,
|
||||||
|
"-e",
|
||||||
|
constants.VCMD_BIN,
|
||||||
|
"-c",
|
||||||
|
self.ctrlchnlname,
|
||||||
|
"--",
|
||||||
|
sh,
|
||||||
|
)
|
||||||
if "SUDO_USER" in os.environ:
|
if "SUDO_USER" in os.environ:
|
||||||
args = ("su", "-s", "/bin/sh", "-c",
|
args = (
|
||||||
"exec " + " ".join(map(lambda x: "'%s'" % x, args)),
|
"su",
|
||||||
os.environ["SUDO_USER"])
|
"-s",
|
||||||
|
"/bin/sh",
|
||||||
|
"-c",
|
||||||
|
"exec " + " ".join(map(lambda x: "'%s'" % x, args)),
|
||||||
|
os.environ["SUDO_USER"],
|
||||||
|
)
|
||||||
return os.spawnvp(os.P_NOWAIT, args[0], args)
|
return os.spawnvp(os.P_NOWAIT, args[0], args)
|
||||||
|
|
||||||
def termcmdstring(self, sh="/bin/sh"):
|
def termcmdstring(self, sh="/bin/sh"):
|
||||||
|
|
297
daemon/core/nodes/docker.py
Normal file
297
daemon/core/nodes/docker.py
Normal file
|
@ -0,0 +1,297 @@
|
||||||
|
import json
|
||||||
|
import logging
|
||||||
|
import os
|
||||||
|
|
||||||
|
from core import CoreCommandError, utils
|
||||||
|
from core.emulator.enumerations import NodeTypes
|
||||||
|
from core.nodes.base import CoreNode
|
||||||
|
|
||||||
|
|
||||||
|
class DockerClient(object):
|
||||||
|
def __init__(self, name, image):
|
||||||
|
self.name = name
|
||||||
|
self.image = image
|
||||||
|
self.pid = None
|
||||||
|
self._addr = {}
|
||||||
|
|
||||||
|
def create_container(self):
|
||||||
|
utils.check_cmd(
|
||||||
|
"docker run -td --init --net=none --hostname {name} --name {name} "
|
||||||
|
"--sysctl net.ipv6.conf.all.disable_ipv6=0 "
|
||||||
|
"{image} /bin/bash".format(
|
||||||
|
name=self.name,
|
||||||
|
image=self.image
|
||||||
|
))
|
||||||
|
self.pid = self.get_pid()
|
||||||
|
return self.pid
|
||||||
|
|
||||||
|
def get_info(self):
|
||||||
|
args = "docker inspect {name}".format(name=self.name)
|
||||||
|
status, output = utils.cmd_output(args)
|
||||||
|
if status:
|
||||||
|
raise CoreCommandError(status, args, output)
|
||||||
|
data = json.loads(output)
|
||||||
|
if not data:
|
||||||
|
raise CoreCommandError(status, args, "docker({name}) not present".format(name=self.name))
|
||||||
|
return data[0]
|
||||||
|
|
||||||
|
def is_alive(self):
|
||||||
|
try:
|
||||||
|
data = self.get_info()
|
||||||
|
return data["State"]["Running"]
|
||||||
|
except CoreCommandError:
|
||||||
|
return False
|
||||||
|
|
||||||
|
def stop_container(self):
|
||||||
|
utils.check_cmd("docker rm -f {name}".format(
|
||||||
|
name=self.name
|
||||||
|
))
|
||||||
|
|
||||||
|
def cmd(self, cmd, wait=True):
|
||||||
|
if isinstance(cmd, list):
|
||||||
|
cmd = " ".join(cmd)
|
||||||
|
logging.info("docker cmd wait(%s): %s", wait, cmd)
|
||||||
|
return utils.cmd("docker exec {name} {cmd}".format(
|
||||||
|
name=self.name,
|
||||||
|
cmd=cmd
|
||||||
|
), wait)
|
||||||
|
|
||||||
|
def cmd_output(self, cmd):
|
||||||
|
if isinstance(cmd, list):
|
||||||
|
cmd = " ".join(cmd)
|
||||||
|
logging.info("docker cmd output: %s", cmd)
|
||||||
|
return utils.cmd_output("docker exec {name} {cmd}".format(
|
||||||
|
name=self.name,
|
||||||
|
cmd=cmd
|
||||||
|
))
|
||||||
|
|
||||||
|
def ns_cmd(self, cmd):
|
||||||
|
if isinstance(cmd, list):
|
||||||
|
cmd = " ".join(cmd)
|
||||||
|
args = "nsenter -t {pid} -u -i -p -n {cmd}".format(
|
||||||
|
pid=self.pid,
|
||||||
|
cmd=cmd
|
||||||
|
)
|
||||||
|
logging.info("ns cmd: %s", args)
|
||||||
|
return utils.cmd_output(args)
|
||||||
|
|
||||||
|
def get_pid(self):
|
||||||
|
args = "docker inspect -f '{{{{.State.Pid}}}}' {name}".format(name=self.name)
|
||||||
|
status, output = utils.cmd_output(args)
|
||||||
|
if status:
|
||||||
|
raise CoreCommandError(status, args, output)
|
||||||
|
self.pid = output
|
||||||
|
logging.debug("node(%s) pid: %s", self.name, self.pid)
|
||||||
|
return output
|
||||||
|
|
||||||
|
def copy_file(self, source, destination):
|
||||||
|
args = "docker cp {source} {name}:{destination}".format(
|
||||||
|
source=source,
|
||||||
|
name=self.name,
|
||||||
|
destination=destination
|
||||||
|
)
|
||||||
|
status, output = utils.cmd_output(args)
|
||||||
|
if status:
|
||||||
|
raise CoreCommandError(status, args, output)
|
||||||
|
|
||||||
|
def getaddr(self, ifname, rescan=False):
|
||||||
|
"""
|
||||||
|
Get address for interface on node.
|
||||||
|
|
||||||
|
:param str ifname: interface name to get address for
|
||||||
|
:param bool rescan: rescan flag
|
||||||
|
:return: interface information
|
||||||
|
:rtype: dict
|
||||||
|
"""
|
||||||
|
if ifname in self._addr and not rescan:
|
||||||
|
return self._addr[ifname]
|
||||||
|
|
||||||
|
interface = {"ether": [], "inet": [], "inet6": [], "inet6link": []}
|
||||||
|
args = ["ip", "addr", "show", "dev", ifname]
|
||||||
|
status, output = self.ns_cmd(args)
|
||||||
|
for line in output:
|
||||||
|
line = line.strip().split()
|
||||||
|
if line[0] == "link/ether":
|
||||||
|
interface["ether"].append(line[1])
|
||||||
|
elif line[0] == "inet":
|
||||||
|
interface["inet"].append(line[1])
|
||||||
|
elif line[0] == "inet6":
|
||||||
|
if line[3] == "global":
|
||||||
|
interface["inet6"].append(line[1])
|
||||||
|
elif line[3] == "link":
|
||||||
|
interface["inet6link"].append(line[1])
|
||||||
|
else:
|
||||||
|
logging.warning("unknown scope: %s" % line[3])
|
||||||
|
|
||||||
|
if status:
|
||||||
|
logging.warning("nonzero exist status (%s) for cmd: %s", status, args)
|
||||||
|
self._addr[ifname] = interface
|
||||||
|
return interface
|
||||||
|
|
||||||
|
|
||||||
|
class DockerNode(CoreNode):
|
||||||
|
apitype = NodeTypes.DOCKER.value
|
||||||
|
|
||||||
|
def __init__(self, session, _id=None, name=None, nodedir=None, bootsh="boot.sh", start=True, image=None):
|
||||||
|
"""
|
||||||
|
Create a DockerNode instance.
|
||||||
|
|
||||||
|
:param core.emulator.session.Session session: core session instance
|
||||||
|
:param int _id: object id
|
||||||
|
:param str name: object name
|
||||||
|
:param str nodedir: node directory
|
||||||
|
:param str bootsh: boot shell to use
|
||||||
|
:param bool start: start flag
|
||||||
|
:param str image: image to start container with
|
||||||
|
"""
|
||||||
|
if image is None:
|
||||||
|
image = "ubuntu"
|
||||||
|
self.image = image
|
||||||
|
super(DockerNode, self).__init__(session, _id, name, nodedir, bootsh, start)
|
||||||
|
|
||||||
|
def alive(self):
|
||||||
|
"""
|
||||||
|
Check if the node is alive.
|
||||||
|
|
||||||
|
:return: True if node is alive, False otherwise
|
||||||
|
:rtype: bool
|
||||||
|
"""
|
||||||
|
return self.client.is_alive()
|
||||||
|
|
||||||
|
def startup(self):
|
||||||
|
"""
|
||||||
|
Start a new namespace node by invoking the vnoded process that
|
||||||
|
allocates a new namespace. Bring up the loopback device and set
|
||||||
|
the hostname.
|
||||||
|
|
||||||
|
:return: nothing
|
||||||
|
"""
|
||||||
|
with self.lock:
|
||||||
|
if self.up:
|
||||||
|
raise ValueError("starting a node that is already up")
|
||||||
|
self.makenodedir()
|
||||||
|
self.client = DockerClient(self.name, self.image)
|
||||||
|
self.pid = self.client.create_container()
|
||||||
|
self.up = True
|
||||||
|
|
||||||
|
def shutdown(self):
|
||||||
|
"""
|
||||||
|
Shutdown logic.
|
||||||
|
|
||||||
|
:return: nothing
|
||||||
|
"""
|
||||||
|
# nothing to do if node is not up
|
||||||
|
if not self.up:
|
||||||
|
return
|
||||||
|
|
||||||
|
with self.lock:
|
||||||
|
self._netif.clear()
|
||||||
|
self.client.stop_container()
|
||||||
|
self.up = False
|
||||||
|
|
||||||
|
def cmd(self, args, wait=True):
|
||||||
|
"""
|
||||||
|
Runs shell command on node, with option to not wait for a result.
|
||||||
|
|
||||||
|
:param list[str]|str args: command to run
|
||||||
|
:param bool wait: wait for command to exit, defaults to True
|
||||||
|
:return: exit status for command
|
||||||
|
:rtype: int
|
||||||
|
"""
|
||||||
|
return self.client.cmd(args, wait)
|
||||||
|
|
||||||
|
def cmd_output(self, args):
|
||||||
|
"""
|
||||||
|
Runs shell command on node and get exit status and output.
|
||||||
|
|
||||||
|
:param list[str]|str args: command to run
|
||||||
|
:return: exit status and combined stdout and stderr
|
||||||
|
:rtype: tuple[int, str]
|
||||||
|
"""
|
||||||
|
return self.client.cmd_output(args)
|
||||||
|
|
||||||
|
def check_cmd(self, args):
|
||||||
|
"""
|
||||||
|
Runs shell command on node.
|
||||||
|
|
||||||
|
:param list[str]|str args: command to run
|
||||||
|
:return: combined stdout and stderr
|
||||||
|
:rtype: str
|
||||||
|
:raises CoreCommandError: when a non-zero exit status occurs
|
||||||
|
"""
|
||||||
|
status, output = self.client.cmd_output(args)
|
||||||
|
if status:
|
||||||
|
raise CoreCommandError(status, args, output)
|
||||||
|
return output
|
||||||
|
|
||||||
|
def network_cmd(self, args):
|
||||||
|
if not self.up:
|
||||||
|
logging.debug("node down, not running network command: %s", args)
|
||||||
|
return 0
|
||||||
|
|
||||||
|
status, output = self.client.ns_cmd(args)
|
||||||
|
if status:
|
||||||
|
raise CoreCommandError(status, args, output)
|
||||||
|
return output
|
||||||
|
|
||||||
|
def termcmdstring(self, sh="/bin/sh"):
|
||||||
|
"""
|
||||||
|
Create a terminal command string.
|
||||||
|
|
||||||
|
:param str sh: shell to execute command in
|
||||||
|
:return: str
|
||||||
|
"""
|
||||||
|
return "docker exec -it {name} bash".format(name=self.name)
|
||||||
|
|
||||||
|
def privatedir(self, path):
|
||||||
|
"""
|
||||||
|
Create a private directory.
|
||||||
|
|
||||||
|
:param str path: path to create
|
||||||
|
:return: nothing
|
||||||
|
"""
|
||||||
|
logging.debug("creating node dir: %s", path)
|
||||||
|
args = "mkdir -p {path}".format(path=path)
|
||||||
|
self.check_cmd(args)
|
||||||
|
|
||||||
|
def mount(self, source, target):
|
||||||
|
"""
|
||||||
|
Create and mount a directory.
|
||||||
|
|
||||||
|
:param str source: source directory to mount
|
||||||
|
:param str target: target directory to create
|
||||||
|
:return: nothing
|
||||||
|
:raises CoreCommandError: when a non-zero exit status occurs
|
||||||
|
"""
|
||||||
|
logging.debug("mounting source(%s) target(%s)", source, target)
|
||||||
|
raise Exception("not supported")
|
||||||
|
|
||||||
|
def nodefile(self, filename, contents, mode=0o644):
|
||||||
|
"""
|
||||||
|
Create a node file with a given mode.
|
||||||
|
|
||||||
|
:param str filename: name of file to create
|
||||||
|
:param contents: contents of file
|
||||||
|
:param int mode: mode for file
|
||||||
|
:return: nothing
|
||||||
|
"""
|
||||||
|
logging.debug("node dir(%s) ctrlchannel(%s)", self.nodedir, self.ctrlchnlname)
|
||||||
|
logging.debug("nodefile filename(%s) mode(%s)", filename, mode)
|
||||||
|
file_path = os.path.join(self.nodedir, filename)
|
||||||
|
with open(file_path, "w") as f:
|
||||||
|
os.chmod(f.name, mode)
|
||||||
|
f.write(contents)
|
||||||
|
self.client.copy_file(file_path, filename)
|
||||||
|
|
||||||
|
def nodefilecopy(self, filename, srcfilename, mode=None):
|
||||||
|
"""
|
||||||
|
Copy a file to a node, following symlinks and preserving metadata.
|
||||||
|
Change file mode if specified.
|
||||||
|
|
||||||
|
:param str filename: file name to copy file to
|
||||||
|
:param str srcfilename: file to copy
|
||||||
|
:param int mode: mode to copy to
|
||||||
|
:return: nothing
|
||||||
|
"""
|
||||||
|
logging.info("node file copy file(%s) source(%s) mode(%s)", filename, srcfilename, mode)
|
||||||
|
raise Exception("not supported")
|
|
@ -4,11 +4,9 @@ virtual ethernet classes that implement the interfaces available under Linux.
|
||||||
|
|
||||||
import logging
|
import logging
|
||||||
import time
|
import time
|
||||||
from builtins import int
|
from builtins import int, range
|
||||||
from builtins import range
|
|
||||||
|
|
||||||
from core import CoreCommandError, utils
|
from core import CoreCommandError, constants, utils
|
||||||
from core import constants
|
|
||||||
from core.emulator.enumerations import NodeTypes
|
from core.emulator.enumerations import NodeTypes
|
||||||
from core.nodes import nodeutils
|
from core.nodes import nodeutils
|
||||||
|
|
||||||
|
@ -142,7 +140,7 @@ class CoreInterface(object):
|
||||||
"""
|
"""
|
||||||
# treat None and 0 as unchanged values
|
# treat None and 0 as unchanged values
|
||||||
logging.debug("setting param: %s - %s", key, value)
|
logging.debug("setting param: %s - %s", key, value)
|
||||||
if value is None or value <= 0:
|
if value is None or value < 0:
|
||||||
return False
|
return False
|
||||||
|
|
||||||
current_value = self._params.get(key)
|
current_value = self._params.get(key)
|
||||||
|
@ -221,8 +219,20 @@ class Veth(CoreInterface):
|
||||||
:return: nothing
|
:return: nothing
|
||||||
:raises CoreCommandError: when there is a command exception
|
:raises CoreCommandError: when there is a command exception
|
||||||
"""
|
"""
|
||||||
utils.check_cmd([constants.IP_BIN, "link", "add", "name", self.localname,
|
utils.check_cmd(
|
||||||
"type", "veth", "peer", "name", self.name])
|
[
|
||||||
|
constants.IP_BIN,
|
||||||
|
"link",
|
||||||
|
"add",
|
||||||
|
"name",
|
||||||
|
self.localname,
|
||||||
|
"type",
|
||||||
|
"veth",
|
||||||
|
"peer",
|
||||||
|
"name",
|
||||||
|
self.name,
|
||||||
|
]
|
||||||
|
)
|
||||||
utils.check_cmd([constants.IP_BIN, "link", "set", self.localname, "up"])
|
utils.check_cmd([constants.IP_BIN, "link", "set", self.localname, "up"])
|
||||||
self.up = True
|
self.up = True
|
||||||
|
|
||||||
|
@ -237,7 +247,9 @@ class Veth(CoreInterface):
|
||||||
|
|
||||||
if self.node:
|
if self.node:
|
||||||
try:
|
try:
|
||||||
self.node.check_cmd([constants.IP_BIN, "-6", "addr", "flush", "dev", self.name])
|
self.node.network_cmd(
|
||||||
|
[constants.IP_BIN, "-6", "addr", "flush", "dev", self.name]
|
||||||
|
)
|
||||||
except CoreCommandError:
|
except CoreCommandError:
|
||||||
logging.exception("error shutting down interface")
|
logging.exception("error shutting down interface")
|
||||||
|
|
||||||
|
@ -245,7 +257,7 @@ class Veth(CoreInterface):
|
||||||
try:
|
try:
|
||||||
utils.check_cmd([constants.IP_BIN, "link", "delete", self.localname])
|
utils.check_cmd([constants.IP_BIN, "link", "delete", self.localname])
|
||||||
except CoreCommandError:
|
except CoreCommandError:
|
||||||
logging.exception("error deleting link")
|
logging.info("link already removed: %s", self.localname)
|
||||||
|
|
||||||
self.up = False
|
self.up = False
|
||||||
|
|
||||||
|
@ -298,7 +310,9 @@ class TunTap(CoreInterface):
|
||||||
return
|
return
|
||||||
|
|
||||||
try:
|
try:
|
||||||
self.node.check_cmd([constants.IP_BIN, "-6", "addr", "flush", "dev", self.name])
|
self.node.network_cmd(
|
||||||
|
[constants.IP_BIN, "-6", "addr", "flush", "dev", self.name]
|
||||||
|
)
|
||||||
except CoreCommandError:
|
except CoreCommandError:
|
||||||
logging.exception("error shutting down tunnel tap")
|
logging.exception("error shutting down tunnel tap")
|
||||||
|
|
||||||
|
@ -361,7 +375,11 @@ class TunTap(CoreInterface):
|
||||||
|
|
||||||
def nodedevexists():
|
def nodedevexists():
|
||||||
args = [constants.IP_BIN, "link", "show", self.name]
|
args = [constants.IP_BIN, "link", "show", self.name]
|
||||||
return self.node.cmd(args)
|
try:
|
||||||
|
self.node.network_cmd(args)
|
||||||
|
return 0
|
||||||
|
except CoreCommandError:
|
||||||
|
return 1
|
||||||
|
|
||||||
count = 0
|
count = 0
|
||||||
while True:
|
while True:
|
||||||
|
@ -392,9 +410,13 @@ class TunTap(CoreInterface):
|
||||||
"""
|
"""
|
||||||
self.waitfordevicelocal()
|
self.waitfordevicelocal()
|
||||||
netns = str(self.node.pid)
|
netns = str(self.node.pid)
|
||||||
utils.check_cmd([constants.IP_BIN, "link", "set", self.localname, "netns", netns])
|
utils.check_cmd(
|
||||||
self.node.check_cmd([constants.IP_BIN, "link", "set", self.localname, "name", self.name])
|
[constants.IP_BIN, "link", "set", self.localname, "netns", netns]
|
||||||
self.node.check_cmd([constants.IP_BIN, "link", "set", self.name, "up"])
|
)
|
||||||
|
self.node.network_cmd(
|
||||||
|
[constants.IP_BIN, "link", "set", self.localname, "name", self.name]
|
||||||
|
)
|
||||||
|
self.node.network_cmd([constants.IP_BIN, "link", "set", self.name, "up"])
|
||||||
|
|
||||||
def setaddrs(self):
|
def setaddrs(self):
|
||||||
"""
|
"""
|
||||||
|
@ -404,7 +426,9 @@ class TunTap(CoreInterface):
|
||||||
"""
|
"""
|
||||||
self.waitfordevicenode()
|
self.waitfordevicenode()
|
||||||
for addr in self.addrlist:
|
for addr in self.addrlist:
|
||||||
self.node.check_cmd([constants.IP_BIN, "addr", "add", str(addr), "dev", self.name])
|
self.node.network_cmd(
|
||||||
|
[constants.IP_BIN, "addr", "add", str(addr), "dev", self.name]
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
class GreTap(CoreInterface):
|
class GreTap(CoreInterface):
|
||||||
|
@ -414,9 +438,19 @@ class GreTap(CoreInterface):
|
||||||
having a MAC address. The MAC address is required for bridging.
|
having a MAC address. The MAC address is required for bridging.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
def __init__(self, node=None, name=None, session=None, mtu=1458,
|
def __init__(
|
||||||
remoteip=None, _id=None, localip=None, ttl=255,
|
self,
|
||||||
key=None, start=True):
|
node=None,
|
||||||
|
name=None,
|
||||||
|
session=None,
|
||||||
|
mtu=1458,
|
||||||
|
remoteip=None,
|
||||||
|
_id=None,
|
||||||
|
localip=None,
|
||||||
|
ttl=255,
|
||||||
|
key=None,
|
||||||
|
start=True,
|
||||||
|
):
|
||||||
"""
|
"""
|
||||||
Creates a GreTap instance.
|
Creates a GreTap instance.
|
||||||
|
|
||||||
|
@ -436,7 +470,7 @@ class GreTap(CoreInterface):
|
||||||
self.session = session
|
self.session = session
|
||||||
if _id is None:
|
if _id is None:
|
||||||
# from PyCoreObj
|
# from PyCoreObj
|
||||||
_id = ((id(self) >> 16) ^ (id(self) & 0xffff)) & 0xffff
|
_id = ((id(self) >> 16) ^ (id(self) & 0xFFFF)) & 0xFFFF
|
||||||
self.id = _id
|
self.id = _id
|
||||||
sessionid = self.session.short_session_id()
|
sessionid = self.session.short_session_id()
|
||||||
# interface name on the local host machine
|
# interface name on the local host machine
|
||||||
|
@ -448,8 +482,16 @@ class GreTap(CoreInterface):
|
||||||
|
|
||||||
if remoteip is None:
|
if remoteip is None:
|
||||||
raise ValueError("missing remote IP required for GRE TAP device")
|
raise ValueError("missing remote IP required for GRE TAP device")
|
||||||
args = [constants.IP_BIN, "link", "add", self.localname, "type", "gretap",
|
args = [
|
||||||
"remote", str(remoteip)]
|
constants.IP_BIN,
|
||||||
|
"link",
|
||||||
|
"add",
|
||||||
|
self.localname,
|
||||||
|
"type",
|
||||||
|
"gretap",
|
||||||
|
"remote",
|
||||||
|
str(remoteip),
|
||||||
|
]
|
||||||
if localip:
|
if localip:
|
||||||
args += ["local", str(localip)]
|
args += ["local", str(localip)]
|
||||||
if ttl:
|
if ttl:
|
||||||
|
|
|
@ -6,10 +6,8 @@ import logging
|
||||||
import random
|
import random
|
||||||
import socket
|
import socket
|
||||||
import struct
|
import struct
|
||||||
from builtins import bytes
|
from builtins import bytes, int, range
|
||||||
from builtins import range
|
from socket import AF_INET, AF_INET6
|
||||||
from socket import AF_INET
|
|
||||||
from socket import AF_INET6
|
|
||||||
|
|
||||||
|
|
||||||
class MacAddress(object):
|
class MacAddress(object):
|
||||||
|
@ -45,13 +43,13 @@ class MacAddress(object):
|
||||||
if not self.addr:
|
if not self.addr:
|
||||||
return IpAddress.from_string("::")
|
return IpAddress.from_string("::")
|
||||||
tmp = struct.unpack("!Q", "\x00\x00" + self.addr)[0]
|
tmp = struct.unpack("!Q", "\x00\x00" + self.addr)[0]
|
||||||
nic = long(tmp) & 0x000000FFFFFF
|
nic = int(tmp) & 0x000000FFFFFF
|
||||||
oui = long(tmp) & 0xFFFFFF000000
|
oui = int(tmp) & 0xFFFFFF000000
|
||||||
# toggle U/L bit
|
# toggle U/L bit
|
||||||
oui ^= 0x020000000000
|
oui ^= 0x020000000000
|
||||||
# append EUI-48 octets
|
# append EUI-48 octets
|
||||||
oui = (oui << 16) | 0xFFFE000000
|
oui = (oui << 16) | 0xFFFE000000
|
||||||
return IpAddress(AF_INET6, struct.pack("!QQ", 0xfe80 << 48, oui | nic))
|
return IpAddress(AF_INET6, struct.pack("!QQ", 0xFE80 << 48, oui | nic))
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def from_string(cls, s):
|
def from_string(cls, s):
|
||||||
|
@ -158,7 +156,7 @@ class IpAddress(object):
|
||||||
tmp = [x for x in bytearray(self.addr)]
|
tmp = [x for x in bytearray(self.addr)]
|
||||||
for i in range(len(tmp) - 1, -1, -1):
|
for i in range(len(tmp) - 1, -1, -1):
|
||||||
x = tmp[i] + carry
|
x = tmp[i] + carry
|
||||||
tmp[i] = x & 0xff
|
tmp[i] = x & 0xFF
|
||||||
carry = x >> 8
|
carry = x >> 8
|
||||||
if carry == 0:
|
if carry == 0:
|
||||||
break
|
break
|
||||||
|
@ -239,7 +237,7 @@ class IpPrefix(object):
|
||||||
netmask = ((1 << self.prefixlen) - 1) << addrbits
|
netmask = ((1 << self.prefixlen) - 1) << addrbits
|
||||||
prefix = bytes(b"")
|
prefix = bytes(b"")
|
||||||
for i in range(-1, -(addrbits >> 3) - 2, -1):
|
for i in range(-1, -(addrbits >> 3) - 2, -1):
|
||||||
prefix = bytes([self.prefix[i] & (netmask & 0xff)]) + prefix
|
prefix = bytes([self.prefix[i] & (netmask & 0xFF)]) + prefix
|
||||||
netmask >>= 8
|
netmask >>= 8
|
||||||
self.prefix = self.prefix[:i] + prefix
|
self.prefix = self.prefix[:i] + prefix
|
||||||
|
|
||||||
|
@ -265,7 +263,11 @@ class IpPrefix(object):
|
||||||
elif self is other:
|
elif self is other:
|
||||||
return True
|
return True
|
||||||
else:
|
else:
|
||||||
return other.af == self.af and other.prefixlen == self.prefixlen and other.prefix == self.prefix
|
return (
|
||||||
|
other.af == self.af
|
||||||
|
and other.prefixlen == self.prefixlen
|
||||||
|
and other.prefix == self.prefix
|
||||||
|
)
|
||||||
|
|
||||||
def __add__(self, other):
|
def __add__(self, other):
|
||||||
"""
|
"""
|
||||||
|
@ -316,15 +318,20 @@ class IpPrefix(object):
|
||||||
if tmp in [-1, 0, 1] and self.addrlen == self.prefixlen:
|
if tmp in [-1, 0, 1] and self.addrlen == self.prefixlen:
|
||||||
return IpAddress(self.af, self.prefix)
|
return IpAddress(self.af, self.prefix)
|
||||||
|
|
||||||
if tmp == 0 or tmp > (1 << (self.addrlen - self.prefixlen)) - 1 or (
|
if (
|
||||||
self.af == AF_INET and tmp == (1 << (self.addrlen - self.prefixlen)) - 1):
|
tmp == 0
|
||||||
|
or tmp > (1 << (self.addrlen - self.prefixlen)) - 1
|
||||||
|
or (
|
||||||
|
self.af == AF_INET and tmp == (1 << (self.addrlen - self.prefixlen)) - 1
|
||||||
|
)
|
||||||
|
):
|
||||||
raise ValueError("invalid hostid for prefix %s: %s" % (self, hostid))
|
raise ValueError("invalid hostid for prefix %s: %s" % (self, hostid))
|
||||||
|
|
||||||
addr = bytes(b"")
|
addr = bytes(b"")
|
||||||
prefix_endpoint = -1
|
prefix_endpoint = -1
|
||||||
for i in range(-1, -(self.addrlen >> 3) - 1, -1):
|
for i in range(-1, -(self.addrlen >> 3) - 1, -1):
|
||||||
prefix_endpoint = i
|
prefix_endpoint = i
|
||||||
addr = bytes([self.prefix[i] | (tmp & 0xff)]) + addr
|
addr = bytes([self.prefix[i] | (tmp & 0xFF)]) + addr
|
||||||
tmp >>= 8
|
tmp >>= 8
|
||||||
if not tmp:
|
if not tmp:
|
||||||
break
|
break
|
||||||
|
|
303
daemon/core/nodes/lxd.py
Normal file
303
daemon/core/nodes/lxd.py
Normal file
|
@ -0,0 +1,303 @@
|
||||||
|
import json
|
||||||
|
import logging
|
||||||
|
import os
|
||||||
|
import time
|
||||||
|
|
||||||
|
from core import CoreCommandError, utils
|
||||||
|
from core.emulator.enumerations import NodeTypes
|
||||||
|
from core.nodes.base import CoreNode
|
||||||
|
|
||||||
|
|
||||||
|
class LxdClient(object):
|
||||||
|
def __init__(self, name, image):
|
||||||
|
self.name = name
|
||||||
|
self.image = image
|
||||||
|
self.pid = None
|
||||||
|
self._addr = {}
|
||||||
|
|
||||||
|
def create_container(self):
|
||||||
|
utils.check_cmd(
|
||||||
|
"lxc launch {image} {name}".format(name=self.name, image=self.image)
|
||||||
|
)
|
||||||
|
data = self.get_info()
|
||||||
|
self.pid = data["state"]["pid"]
|
||||||
|
return self.pid
|
||||||
|
|
||||||
|
def get_info(self):
|
||||||
|
args = "lxc list {name} --format json".format(name=self.name)
|
||||||
|
status, output = utils.cmd_output(args)
|
||||||
|
if status:
|
||||||
|
raise CoreCommandError(status, args, output)
|
||||||
|
data = json.loads(output)
|
||||||
|
if not data:
|
||||||
|
raise CoreCommandError(
|
||||||
|
status, args, "LXC({name}) not present".format(name=self.name)
|
||||||
|
)
|
||||||
|
return data[0]
|
||||||
|
|
||||||
|
def is_alive(self):
|
||||||
|
try:
|
||||||
|
data = self.get_info()
|
||||||
|
return data["state"]["status"] == "Running"
|
||||||
|
except CoreCommandError:
|
||||||
|
return False
|
||||||
|
|
||||||
|
def stop_container(self):
|
||||||
|
utils.check_cmd("lxc delete --force {name}".format(name=self.name))
|
||||||
|
|
||||||
|
def _cmd_args(self, cmd):
|
||||||
|
return "lxc exec -nT {name} -- {cmd}".format(name=self.name, cmd=cmd)
|
||||||
|
|
||||||
|
def cmd_output(self, cmd):
|
||||||
|
if isinstance(cmd, list):
|
||||||
|
cmd = " ".join(cmd)
|
||||||
|
args = self._cmd_args(cmd)
|
||||||
|
logging.info("lxc cmd output: %s", args)
|
||||||
|
return utils.cmd_output(args)
|
||||||
|
|
||||||
|
def cmd(self, cmd, wait=True):
|
||||||
|
if isinstance(cmd, list):
|
||||||
|
cmd = " ".join(cmd)
|
||||||
|
args = self._cmd_args(cmd)
|
||||||
|
logging.info("lxc cmd: %s", args)
|
||||||
|
return utils.cmd(args, wait)
|
||||||
|
|
||||||
|
def _ns_args(self, cmd):
|
||||||
|
return "nsenter -t {pid} -m -u -i -p -n {cmd}".format(pid=self.pid, cmd=cmd)
|
||||||
|
|
||||||
|
def ns_cmd_output(self, cmd):
|
||||||
|
if isinstance(cmd, list):
|
||||||
|
cmd = " ".join(cmd)
|
||||||
|
args = self._ns_args(cmd)
|
||||||
|
logging.info("ns cmd: %s", args)
|
||||||
|
return utils.cmd_output(args)
|
||||||
|
|
||||||
|
def ns_cmd(self, cmd, wait=True):
|
||||||
|
if isinstance(cmd, list):
|
||||||
|
cmd = " ".join(cmd)
|
||||||
|
args = self._ns_args(cmd)
|
||||||
|
logging.info("ns cmd: %s", args)
|
||||||
|
return utils.cmd(args, wait)
|
||||||
|
|
||||||
|
def copy_file(self, source, destination):
|
||||||
|
if destination[0] != "/":
|
||||||
|
destination = os.path.join("/root/", destination)
|
||||||
|
|
||||||
|
args = "lxc file push {source} {name}/{destination}".format(
|
||||||
|
source=source, name=self.name, destination=destination
|
||||||
|
)
|
||||||
|
status, output = utils.cmd_output(args)
|
||||||
|
if status:
|
||||||
|
raise CoreCommandError(status, args, output)
|
||||||
|
|
||||||
|
def getaddr(self, ifname, rescan=False):
|
||||||
|
"""
|
||||||
|
Get address for interface on node.
|
||||||
|
|
||||||
|
:param str ifname: interface name to get address for
|
||||||
|
:param bool rescan: rescan flag
|
||||||
|
:return: interface information
|
||||||
|
:rtype: dict
|
||||||
|
"""
|
||||||
|
if ifname in self._addr and not rescan:
|
||||||
|
return self._addr[ifname]
|
||||||
|
|
||||||
|
interface = {"ether": [], "inet": [], "inet6": [], "inet6link": []}
|
||||||
|
args = ["ip", "addr", "show", "dev", ifname]
|
||||||
|
status, output = self.ns_cmd_output(args)
|
||||||
|
for line in output:
|
||||||
|
line = line.strip().split()
|
||||||
|
if line[0] == "link/ether":
|
||||||
|
interface["ether"].append(line[1])
|
||||||
|
elif line[0] == "inet":
|
||||||
|
interface["inet"].append(line[1])
|
||||||
|
elif line[0] == "inet6":
|
||||||
|
if line[3] == "global":
|
||||||
|
interface["inet6"].append(line[1])
|
||||||
|
elif line[3] == "link":
|
||||||
|
interface["inet6link"].append(line[1])
|
||||||
|
else:
|
||||||
|
logging.warning("unknown scope: %s" % line[3])
|
||||||
|
|
||||||
|
if status:
|
||||||
|
logging.warning("nonzero exist status (%s) for cmd: %s", status, args)
|
||||||
|
self._addr[ifname] = interface
|
||||||
|
return interface
|
||||||
|
|
||||||
|
|
||||||
|
class LxcNode(CoreNode):
|
||||||
|
apitype = NodeTypes.LXC.value
|
||||||
|
|
||||||
|
def __init__(
|
||||||
|
self,
|
||||||
|
session,
|
||||||
|
_id=None,
|
||||||
|
name=None,
|
||||||
|
nodedir=None,
|
||||||
|
bootsh="boot.sh",
|
||||||
|
start=True,
|
||||||
|
image=None,
|
||||||
|
):
|
||||||
|
"""
|
||||||
|
Create a LxcNode instance.
|
||||||
|
|
||||||
|
:param core.emulator.session.Session session: core session instance
|
||||||
|
:param int _id: object id
|
||||||
|
:param str name: object name
|
||||||
|
:param str nodedir: node directory
|
||||||
|
:param str bootsh: boot shell to use
|
||||||
|
:param bool start: start flag
|
||||||
|
:param str image: image to start container with
|
||||||
|
"""
|
||||||
|
if image is None:
|
||||||
|
image = "ubuntu"
|
||||||
|
self.image = image
|
||||||
|
super(LxcNode, self).__init__(session, _id, name, nodedir, bootsh, start)
|
||||||
|
|
||||||
|
def alive(self):
|
||||||
|
"""
|
||||||
|
Check if the node is alive.
|
||||||
|
|
||||||
|
:return: True if node is alive, False otherwise
|
||||||
|
:rtype: bool
|
||||||
|
"""
|
||||||
|
return self.client.is_alive()
|
||||||
|
|
||||||
|
def startup(self):
|
||||||
|
"""
|
||||||
|
Startup logic.
|
||||||
|
|
||||||
|
:return: nothing
|
||||||
|
"""
|
||||||
|
with self.lock:
|
||||||
|
if self.up:
|
||||||
|
raise ValueError("starting a node that is already up")
|
||||||
|
self.makenodedir()
|
||||||
|
self.client = LxdClient(self.name, self.image)
|
||||||
|
self.pid = self.client.create_container()
|
||||||
|
self.up = True
|
||||||
|
|
||||||
|
def shutdown(self):
|
||||||
|
"""
|
||||||
|
Shutdown logic.
|
||||||
|
|
||||||
|
:return: nothing
|
||||||
|
"""
|
||||||
|
# nothing to do if node is not up
|
||||||
|
if not self.up:
|
||||||
|
return
|
||||||
|
|
||||||
|
with self.lock:
|
||||||
|
self._netif.clear()
|
||||||
|
self.client.stop_container()
|
||||||
|
self.up = False
|
||||||
|
|
||||||
|
def cmd(self, args, wait=True):
|
||||||
|
"""
|
||||||
|
Runs shell command on node, with option to not wait for a result.
|
||||||
|
|
||||||
|
:param list[str]|str args: command to run
|
||||||
|
:param bool wait: wait for command to exit, defaults to True
|
||||||
|
:return: exit status for command
|
||||||
|
:rtype: int
|
||||||
|
"""
|
||||||
|
return self.client.cmd(args, wait)
|
||||||
|
|
||||||
|
def cmd_output(self, args):
|
||||||
|
"""
|
||||||
|
Runs shell command on node and get exit status and output.
|
||||||
|
|
||||||
|
:param list[str]|str args: command to run
|
||||||
|
:return: exit status and combined stdout and stderr
|
||||||
|
:rtype: tuple[int, str]
|
||||||
|
"""
|
||||||
|
return self.client.cmd_output(args)
|
||||||
|
|
||||||
|
def check_cmd(self, args):
|
||||||
|
"""
|
||||||
|
Runs shell command on node.
|
||||||
|
|
||||||
|
:param list[str]|str args: command to run
|
||||||
|
:return: combined stdout and stderr
|
||||||
|
:rtype: str
|
||||||
|
:raises CoreCommandError: when a non-zero exit status occurs
|
||||||
|
"""
|
||||||
|
status, output = self.client.cmd_output(args)
|
||||||
|
if status:
|
||||||
|
raise CoreCommandError(status, args, output)
|
||||||
|
return output
|
||||||
|
|
||||||
|
def network_cmd(self, args):
|
||||||
|
if not self.up:
|
||||||
|
logging.debug("node down, not running network command: %s", args)
|
||||||
|
return 0
|
||||||
|
return self.check_cmd(args)
|
||||||
|
|
||||||
|
def termcmdstring(self, sh="/bin/sh"):
|
||||||
|
"""
|
||||||
|
Create a terminal command string.
|
||||||
|
|
||||||
|
:param str sh: shell to execute command in
|
||||||
|
:return: str
|
||||||
|
"""
|
||||||
|
return "lxc exec {name} -- bash".format(name=self.name)
|
||||||
|
|
||||||
|
def privatedir(self, path):
|
||||||
|
"""
|
||||||
|
Create a private directory.
|
||||||
|
|
||||||
|
:param str path: path to create
|
||||||
|
:return: nothing
|
||||||
|
"""
|
||||||
|
logging.info("creating node dir: %s", path)
|
||||||
|
args = "mkdir -p {path}".format(path=path)
|
||||||
|
self.check_cmd(args)
|
||||||
|
|
||||||
|
def mount(self, source, target):
|
||||||
|
"""
|
||||||
|
Create and mount a directory.
|
||||||
|
|
||||||
|
:param str source: source directory to mount
|
||||||
|
:param str target: target directory to create
|
||||||
|
:return: nothing
|
||||||
|
:raises CoreCommandError: when a non-zero exit status occurs
|
||||||
|
"""
|
||||||
|
logging.debug("mounting source(%s) target(%s)", source, target)
|
||||||
|
raise Exception("not supported")
|
||||||
|
|
||||||
|
def nodefile(self, filename, contents, mode=0o644):
|
||||||
|
"""
|
||||||
|
Create a node file with a given mode.
|
||||||
|
|
||||||
|
:param str filename: name of file to create
|
||||||
|
:param contents: contents of file
|
||||||
|
:param int mode: mode for file
|
||||||
|
:return: nothing
|
||||||
|
"""
|
||||||
|
logging.debug("node dir(%s) ctrlchannel(%s)", self.nodedir, self.ctrlchnlname)
|
||||||
|
logging.debug("nodefile filename(%s) mode(%s)", filename, mode)
|
||||||
|
file_path = os.path.join(self.nodedir, filename)
|
||||||
|
with open(file_path, "w") as f:
|
||||||
|
os.chmod(f.name, mode)
|
||||||
|
f.write(contents)
|
||||||
|
self.client.copy_file(file_path, filename)
|
||||||
|
|
||||||
|
def nodefilecopy(self, filename, srcfilename, mode=None):
|
||||||
|
"""
|
||||||
|
Copy a file to a node, following symlinks and preserving metadata.
|
||||||
|
Change file mode if specified.
|
||||||
|
|
||||||
|
:param str filename: file name to copy file to
|
||||||
|
:param str srcfilename: file to copy
|
||||||
|
:param int mode: mode to copy to
|
||||||
|
:return: nothing
|
||||||
|
"""
|
||||||
|
logging.info(
|
||||||
|
"node file copy file(%s) source(%s) mode(%s)", filename, srcfilename, mode
|
||||||
|
)
|
||||||
|
raise Exception("not supported")
|
||||||
|
|
||||||
|
def addnetif(self, netif, ifindex):
|
||||||
|
super(LxcNode, self).addnetif(netif, ifindex)
|
||||||
|
# adding small delay to allow time for adding addresses to work correctly
|
||||||
|
time.sleep(0.5)
|
|
@ -9,21 +9,16 @@ import threading
|
||||||
import time
|
import time
|
||||||
from socket import AF_INET, AF_INET6
|
from socket import AF_INET, AF_INET6
|
||||||
|
|
||||||
from core import CoreCommandError, utils
|
from core import CoreCommandError, constants, utils
|
||||||
from core import constants
|
|
||||||
from core.nodes.base import CoreNetworkBase
|
|
||||||
from core.emulator.data import LinkData
|
from core.emulator.data import LinkData
|
||||||
from core.emulator.enumerations import NodeTypes, LinkTypes, RegisterTlvs
|
from core.emulator.enumerations import LinkTypes, NodeTypes, RegisterTlvs
|
||||||
from core.nodes import ipaddress
|
from core.nodes import ipaddress
|
||||||
from core.nodes.interface import GreTap
|
from core.nodes.base import CoreNetworkBase
|
||||||
from core.nodes.interface import Veth
|
from core.nodes.interface import GreTap, Veth
|
||||||
|
|
||||||
utils.check_executables([
|
utils.check_executables(
|
||||||
constants.BRCTL_BIN,
|
[constants.BRCTL_BIN, constants.IP_BIN, constants.EBTABLES_BIN, constants.TC_BIN]
|
||||||
constants.IP_BIN,
|
)
|
||||||
constants.EBTABLES_BIN,
|
|
||||||
constants.TC_BIN
|
|
||||||
])
|
|
||||||
|
|
||||||
ebtables_lock = threading.Lock()
|
ebtables_lock = threading.Lock()
|
||||||
|
|
||||||
|
@ -34,6 +29,7 @@ class EbtablesQueue(object):
|
||||||
atomic commits. This improves performance and reliability when there are
|
atomic commits. This improves performance and reliability when there are
|
||||||
many WLAN link updates.
|
many WLAN link updates.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
# update rate is every 300ms
|
# update rate is every 300ms
|
||||||
rate = 0.3
|
rate = 0.3
|
||||||
# ebtables
|
# ebtables
|
||||||
|
@ -83,7 +79,9 @@ class EbtablesQueue(object):
|
||||||
try:
|
try:
|
||||||
del self.last_update_time[wlan]
|
del self.last_update_time[wlan]
|
||||||
except KeyError:
|
except KeyError:
|
||||||
logging.exception("error deleting last update time for wlan, ignored before: %s", wlan)
|
logging.exception(
|
||||||
|
"error deleting last update time for wlan, ignored before: %s", wlan
|
||||||
|
)
|
||||||
|
|
||||||
if len(self.last_update_time) > 0:
|
if len(self.last_update_time) > 0:
|
||||||
return
|
return
|
||||||
|
@ -149,7 +147,7 @@ class EbtablesQueue(object):
|
||||||
# TODO: if these are WlanNodes, this will never throw an exception
|
# TODO: if these are WlanNodes, this will never throw an exception
|
||||||
try:
|
try:
|
||||||
wlan.session
|
wlan.session
|
||||||
except:
|
except Exception:
|
||||||
# Just mark as updated to remove from self.updates.
|
# Just mark as updated to remove from self.updates.
|
||||||
self.updated(wlan)
|
self.updated(wlan)
|
||||||
continue
|
continue
|
||||||
|
@ -168,7 +166,7 @@ class EbtablesQueue(object):
|
||||||
:return: nothing
|
:return: nothing
|
||||||
"""
|
"""
|
||||||
# save kernel ebtables snapshot to a file
|
# save kernel ebtables snapshot to a file
|
||||||
args = self.ebatomiccmd(["--atomic-save", ])
|
args = self.ebatomiccmd(["--atomic-save"])
|
||||||
utils.check_cmd(args)
|
utils.check_cmd(args)
|
||||||
|
|
||||||
# modify the table file using queued ebtables commands
|
# modify the table file using queued ebtables commands
|
||||||
|
@ -178,7 +176,7 @@ class EbtablesQueue(object):
|
||||||
self.cmds = []
|
self.cmds = []
|
||||||
|
|
||||||
# commit the table file to the kernel
|
# commit the table file to the kernel
|
||||||
args = self.ebatomiccmd(["--atomic-commit", ])
|
args = self.ebatomiccmd(["--atomic-commit"])
|
||||||
utils.check_cmd(args)
|
utils.check_cmd(args)
|
||||||
|
|
||||||
try:
|
try:
|
||||||
|
@ -205,20 +203,60 @@ class EbtablesQueue(object):
|
||||||
"""
|
"""
|
||||||
with wlan._linked_lock:
|
with wlan._linked_lock:
|
||||||
# flush the chain
|
# flush the chain
|
||||||
self.cmds.extend([["-F", wlan.brname], ])
|
self.cmds.extend([["-F", wlan.brname]])
|
||||||
# rebuild the chain
|
# rebuild the chain
|
||||||
for netif1, v in wlan._linked.items():
|
for netif1, v in wlan._linked.items():
|
||||||
for netif2, linked in v.items():
|
for netif2, linked in v.items():
|
||||||
if wlan.policy == "DROP" and linked:
|
if wlan.policy == "DROP" and linked:
|
||||||
self.cmds.extend([["-A", wlan.brname, "-i", netif1.localname,
|
self.cmds.extend(
|
||||||
"-o", netif2.localname, "-j", "ACCEPT"],
|
[
|
||||||
["-A", wlan.brname, "-o", netif1.localname,
|
[
|
||||||
"-i", netif2.localname, "-j", "ACCEPT"]])
|
"-A",
|
||||||
|
wlan.brname,
|
||||||
|
"-i",
|
||||||
|
netif1.localname,
|
||||||
|
"-o",
|
||||||
|
netif2.localname,
|
||||||
|
"-j",
|
||||||
|
"ACCEPT",
|
||||||
|
],
|
||||||
|
[
|
||||||
|
"-A",
|
||||||
|
wlan.brname,
|
||||||
|
"-o",
|
||||||
|
netif1.localname,
|
||||||
|
"-i",
|
||||||
|
netif2.localname,
|
||||||
|
"-j",
|
||||||
|
"ACCEPT",
|
||||||
|
],
|
||||||
|
]
|
||||||
|
)
|
||||||
elif wlan.policy == "ACCEPT" and not linked:
|
elif wlan.policy == "ACCEPT" and not linked:
|
||||||
self.cmds.extend([["-A", wlan.brname, "-i", netif1.localname,
|
self.cmds.extend(
|
||||||
"-o", netif2.localname, "-j", "DROP"],
|
[
|
||||||
["-A", wlan.brname, "-o", netif1.localname,
|
[
|
||||||
"-i", netif2.localname, "-j", "DROP"]])
|
"-A",
|
||||||
|
wlan.brname,
|
||||||
|
"-i",
|
||||||
|
netif1.localname,
|
||||||
|
"-o",
|
||||||
|
netif2.localname,
|
||||||
|
"-j",
|
||||||
|
"DROP",
|
||||||
|
],
|
||||||
|
[
|
||||||
|
"-A",
|
||||||
|
wlan.brname,
|
||||||
|
"-o",
|
||||||
|
netif1.localname,
|
||||||
|
"-i",
|
||||||
|
netif2.localname,
|
||||||
|
"-j",
|
||||||
|
"DROP",
|
||||||
|
],
|
||||||
|
]
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
# a global object because all WLANs share the same queue
|
# a global object because all WLANs share the same queue
|
||||||
|
@ -243,6 +281,7 @@ class CoreNetwork(CoreNetworkBase):
|
||||||
"""
|
"""
|
||||||
Provides linux bridge network functionality for core nodes.
|
Provides linux bridge network functionality for core nodes.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
policy = "DROP"
|
policy = "DROP"
|
||||||
|
|
||||||
def __init__(self, session, _id=None, name=None, start=True, policy=None):
|
def __init__(self, session, _id=None, name=None, start=True, policy=None):
|
||||||
|
@ -282,10 +321,21 @@ class CoreNetwork(CoreNetworkBase):
|
||||||
utils.check_cmd([constants.BRCTL_BIN, "setfd", self.brname, "0"])
|
utils.check_cmd([constants.BRCTL_BIN, "setfd", self.brname, "0"])
|
||||||
utils.check_cmd([constants.IP_BIN, "link", "set", self.brname, "up"])
|
utils.check_cmd([constants.IP_BIN, "link", "set", self.brname, "up"])
|
||||||
# create a new ebtables chain for this bridge
|
# create a new ebtables chain for this bridge
|
||||||
ebtablescmds(utils.check_cmd, [
|
ebtablescmds(
|
||||||
[constants.EBTABLES_BIN, "-N", self.brname, "-P", self.policy],
|
utils.check_cmd,
|
||||||
[constants.EBTABLES_BIN, "-A", "FORWARD", "--logical-in", self.brname, "-j", self.brname]
|
[
|
||||||
])
|
[constants.EBTABLES_BIN, "-N", self.brname, "-P", self.policy],
|
||||||
|
[
|
||||||
|
constants.EBTABLES_BIN,
|
||||||
|
"-A",
|
||||||
|
"FORWARD",
|
||||||
|
"--logical-in",
|
||||||
|
self.brname,
|
||||||
|
"-j",
|
||||||
|
self.brname,
|
||||||
|
],
|
||||||
|
],
|
||||||
|
)
|
||||||
# turn off multicast snooping so mcast forwarding occurs w/o IGMP joins
|
# turn off multicast snooping so mcast forwarding occurs w/o IGMP joins
|
||||||
snoop = "/sys/devices/virtual/net/%s/bridge/multicast_snooping" % self.brname
|
snoop = "/sys/devices/virtual/net/%s/bridge/multicast_snooping" % self.brname
|
||||||
if os.path.exists(snoop):
|
if os.path.exists(snoop):
|
||||||
|
@ -308,10 +358,21 @@ class CoreNetwork(CoreNetworkBase):
|
||||||
try:
|
try:
|
||||||
utils.check_cmd([constants.IP_BIN, "link", "set", self.brname, "down"])
|
utils.check_cmd([constants.IP_BIN, "link", "set", self.brname, "down"])
|
||||||
utils.check_cmd([constants.BRCTL_BIN, "delbr", self.brname])
|
utils.check_cmd([constants.BRCTL_BIN, "delbr", self.brname])
|
||||||
ebtablescmds(utils.check_cmd, [
|
ebtablescmds(
|
||||||
[constants.EBTABLES_BIN, "-D", "FORWARD", "--logical-in", self.brname, "-j", self.brname],
|
utils.check_cmd,
|
||||||
[constants.EBTABLES_BIN, "-X", self.brname]
|
[
|
||||||
])
|
[
|
||||||
|
constants.EBTABLES_BIN,
|
||||||
|
"-D",
|
||||||
|
"FORWARD",
|
||||||
|
"--logical-in",
|
||||||
|
self.brname,
|
||||||
|
"-j",
|
||||||
|
self.brname,
|
||||||
|
],
|
||||||
|
[constants.EBTABLES_BIN, "-X", self.brname],
|
||||||
|
],
|
||||||
|
)
|
||||||
except CoreCommandError:
|
except CoreCommandError:
|
||||||
logging.exception("error during shutdown")
|
logging.exception("error during shutdown")
|
||||||
|
|
||||||
|
@ -333,7 +394,9 @@ class CoreNetwork(CoreNetworkBase):
|
||||||
:return: nothing
|
:return: nothing
|
||||||
"""
|
"""
|
||||||
if self.up:
|
if self.up:
|
||||||
utils.check_cmd([constants.BRCTL_BIN, "addif", self.brname, netif.localname])
|
utils.check_cmd(
|
||||||
|
[constants.BRCTL_BIN, "addif", self.brname, netif.localname]
|
||||||
|
)
|
||||||
utils.check_cmd([constants.IP_BIN, "link", "set", netif.localname, "up"])
|
utils.check_cmd([constants.IP_BIN, "link", "set", netif.localname, "up"])
|
||||||
|
|
||||||
CoreNetworkBase.attach(self, netif)
|
CoreNetworkBase.attach(self, netif)
|
||||||
|
@ -346,7 +409,9 @@ class CoreNetwork(CoreNetworkBase):
|
||||||
:return: nothing
|
:return: nothing
|
||||||
"""
|
"""
|
||||||
if self.up:
|
if self.up:
|
||||||
utils.check_cmd([constants.BRCTL_BIN, "delif", self.brname, netif.localname])
|
utils.check_cmd(
|
||||||
|
[constants.BRCTL_BIN, "delif", self.brname, netif.localname]
|
||||||
|
)
|
||||||
|
|
||||||
CoreNetworkBase.detach(self, netif)
|
CoreNetworkBase.detach(self, netif)
|
||||||
|
|
||||||
|
@ -411,8 +476,17 @@ class CoreNetwork(CoreNetworkBase):
|
||||||
|
|
||||||
ebq.ebchange(self)
|
ebq.ebchange(self)
|
||||||
|
|
||||||
def linkconfig(self, netif, bw=None, delay=None, loss=None, duplicate=None,
|
def linkconfig(
|
||||||
jitter=None, netif2=None, devname=None):
|
self,
|
||||||
|
netif,
|
||||||
|
bw=None,
|
||||||
|
delay=None,
|
||||||
|
loss=None,
|
||||||
|
duplicate=None,
|
||||||
|
jitter=None,
|
||||||
|
netif2=None,
|
||||||
|
devname=None,
|
||||||
|
):
|
||||||
"""
|
"""
|
||||||
Configure link parameters by applying tc queuing disciplines on the interface.
|
Configure link parameters by applying tc queuing disciplines on the interface.
|
||||||
|
|
||||||
|
@ -436,12 +510,13 @@ class CoreNetwork(CoreNetworkBase):
|
||||||
if bw is not None:
|
if bw is not None:
|
||||||
burst = max(2 * netif.mtu, bw / 1000)
|
burst = max(2 * netif.mtu, bw / 1000)
|
||||||
# max IP payload
|
# max IP payload
|
||||||
limit = 0xffff
|
limit = 0xFFFF
|
||||||
tbf = ["tbf", "rate", str(bw),
|
tbf = ["tbf", "rate", str(bw), "burst", str(burst), "limit", str(limit)]
|
||||||
"burst", str(burst), "limit", str(limit)]
|
|
||||||
if bw > 0:
|
if bw > 0:
|
||||||
if self.up:
|
if self.up:
|
||||||
logging.debug("linkconfig: %s" % ([tc + parent + ["handle", "1:"] + tbf],))
|
logging.debug(
|
||||||
|
"linkconfig: %s" % ([tc + parent + ["handle", "1:"] + tbf],)
|
||||||
|
)
|
||||||
utils.check_cmd(tc + parent + ["handle", "1:"] + tbf)
|
utils.check_cmd(tc + parent + ["handle", "1:"] + tbf)
|
||||||
netif.setparam("has_tbf", True)
|
netif.setparam("has_tbf", True)
|
||||||
changed = True
|
changed = True
|
||||||
|
@ -496,7 +571,9 @@ class CoreNetwork(CoreNetworkBase):
|
||||||
netif.setparam("has_netem", False)
|
netif.setparam("has_netem", False)
|
||||||
elif len(netem) > 1:
|
elif len(netem) > 1:
|
||||||
if self.up:
|
if self.up:
|
||||||
logging.debug("linkconfig: %s" % ([tc + parent + ["handle", "10:"] + netem],))
|
logging.debug(
|
||||||
|
"linkconfig: %s" % ([tc + parent + ["handle", "10:"] + netem],)
|
||||||
|
)
|
||||||
utils.check_cmd(tc + parent + ["handle", "10:"] + netem)
|
utils.check_cmd(tc + parent + ["handle", "10:"] + netem)
|
||||||
netif.setparam("has_netem", True)
|
netif.setparam("has_netem", True)
|
||||||
|
|
||||||
|
@ -528,7 +605,9 @@ class CoreNetwork(CoreNetworkBase):
|
||||||
if len(name) >= 16:
|
if len(name) >= 16:
|
||||||
raise ValueError("interface name %s too long" % name)
|
raise ValueError("interface name %s too long" % name)
|
||||||
|
|
||||||
netif = Veth(node=None, name=name, localname=localname, mtu=1500, net=self, start=self.up)
|
netif = Veth(
|
||||||
|
node=None, name=name, localname=localname, mtu=1500, net=self, start=self.up
|
||||||
|
)
|
||||||
self.attach(netif)
|
self.attach(netif)
|
||||||
if net.up:
|
if net.up:
|
||||||
# this is similar to net.attach() but uses netif.name instead
|
# this is similar to net.attach() but uses netif.name instead
|
||||||
|
@ -569,7 +648,9 @@ class CoreNetwork(CoreNetworkBase):
|
||||||
return
|
return
|
||||||
|
|
||||||
for addr in addrlist:
|
for addr in addrlist:
|
||||||
utils.check_cmd([constants.IP_BIN, "addr", "add", str(addr), "dev", self.brname])
|
utils.check_cmd(
|
||||||
|
[constants.IP_BIN, "addr", "add", str(addr), "dev", self.brname]
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
class GreTapBridge(CoreNetwork):
|
class GreTapBridge(CoreNetwork):
|
||||||
|
@ -578,8 +659,18 @@ class GreTapBridge(CoreNetwork):
|
||||||
another system.
|
another system.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
def __init__(self, session, remoteip=None, _id=None, name=None,
|
def __init__(
|
||||||
policy="ACCEPT", localip=None, ttl=255, key=None, start=True):
|
self,
|
||||||
|
session,
|
||||||
|
remoteip=None,
|
||||||
|
_id=None,
|
||||||
|
name=None,
|
||||||
|
policy="ACCEPT",
|
||||||
|
localip=None,
|
||||||
|
ttl=255,
|
||||||
|
key=None,
|
||||||
|
start=True,
|
||||||
|
):
|
||||||
"""
|
"""
|
||||||
Create a GreTapBridge instance.
|
Create a GreTapBridge instance.
|
||||||
|
|
||||||
|
@ -593,7 +684,9 @@ class GreTapBridge(CoreNetwork):
|
||||||
:param key: gre tap key
|
:param key: gre tap key
|
||||||
:param bool start: start flag
|
:param bool start: start flag
|
||||||
"""
|
"""
|
||||||
CoreNetwork.__init__(self, session=session, _id=_id, name=name, policy=policy, start=False)
|
CoreNetwork.__init__(
|
||||||
|
self, session=session, _id=_id, name=name, policy=policy, start=False
|
||||||
|
)
|
||||||
self.grekey = key
|
self.grekey = key
|
||||||
if self.grekey is None:
|
if self.grekey is None:
|
||||||
self.grekey = self.session.id ^ self.id
|
self.grekey = self.session.id ^ self.id
|
||||||
|
@ -605,8 +698,14 @@ class GreTapBridge(CoreNetwork):
|
||||||
if remoteip is None:
|
if remoteip is None:
|
||||||
self.gretap = None
|
self.gretap = None
|
||||||
else:
|
else:
|
||||||
self.gretap = GreTap(node=self, session=session, remoteip=remoteip,
|
self.gretap = GreTap(
|
||||||
localip=localip, ttl=ttl, key=self.grekey)
|
node=self,
|
||||||
|
session=session,
|
||||||
|
remoteip=remoteip,
|
||||||
|
localip=localip,
|
||||||
|
ttl=ttl,
|
||||||
|
key=self.grekey,
|
||||||
|
)
|
||||||
if start:
|
if start:
|
||||||
self.startup()
|
self.startup()
|
||||||
|
|
||||||
|
@ -648,8 +747,13 @@ class GreTapBridge(CoreNetwork):
|
||||||
localip = None
|
localip = None
|
||||||
if len(addrlist) > 1:
|
if len(addrlist) > 1:
|
||||||
localip = addrlist[1].split("/")[0]
|
localip = addrlist[1].split("/")[0]
|
||||||
self.gretap = GreTap(session=self.session, remoteip=remoteip,
|
self.gretap = GreTap(
|
||||||
localip=localip, ttl=self.ttl, key=self.grekey)
|
session=self.session,
|
||||||
|
remoteip=remoteip,
|
||||||
|
localip=localip,
|
||||||
|
ttl=self.ttl,
|
||||||
|
key=self.grekey,
|
||||||
|
)
|
||||||
self.attach(self.gretap)
|
self.attach(self.gretap)
|
||||||
|
|
||||||
def setkey(self, key):
|
def setkey(self, key):
|
||||||
|
@ -667,6 +771,7 @@ class CtrlNet(CoreNetwork):
|
||||||
"""
|
"""
|
||||||
Control network functionality.
|
Control network functionality.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
policy = "ACCEPT"
|
policy = "ACCEPT"
|
||||||
# base control interface index
|
# base control interface index
|
||||||
CTRLIF_IDX_BASE = 99
|
CTRLIF_IDX_BASE = 99
|
||||||
|
@ -674,12 +779,21 @@ class CtrlNet(CoreNetwork):
|
||||||
"172.16.0.0/24 172.16.1.0/24 172.16.2.0/24 172.16.3.0/24 172.16.4.0/24",
|
"172.16.0.0/24 172.16.1.0/24 172.16.2.0/24 172.16.3.0/24 172.16.4.0/24",
|
||||||
"172.17.0.0/24 172.17.1.0/24 172.17.2.0/24 172.17.3.0/24 172.17.4.0/24",
|
"172.17.0.0/24 172.17.1.0/24 172.17.2.0/24 172.17.3.0/24 172.17.4.0/24",
|
||||||
"172.18.0.0/24 172.18.1.0/24 172.18.2.0/24 172.18.3.0/24 172.18.4.0/24",
|
"172.18.0.0/24 172.18.1.0/24 172.18.2.0/24 172.18.3.0/24 172.18.4.0/24",
|
||||||
"172.19.0.0/24 172.19.1.0/24 172.19.2.0/24 172.19.3.0/24 172.19.4.0/24"
|
"172.19.0.0/24 172.19.1.0/24 172.19.2.0/24 172.19.3.0/24 172.19.4.0/24",
|
||||||
]
|
]
|
||||||
|
|
||||||
def __init__(self, session, _id="ctrlnet", name=None, prefix=None,
|
def __init__(
|
||||||
hostid=None, start=True, assign_address=True,
|
self,
|
||||||
updown_script=None, serverintf=None):
|
session,
|
||||||
|
_id="ctrlnet",
|
||||||
|
name=None,
|
||||||
|
prefix=None,
|
||||||
|
hostid=None,
|
||||||
|
start=True,
|
||||||
|
assign_address=True,
|
||||||
|
updown_script=None,
|
||||||
|
serverintf=None,
|
||||||
|
):
|
||||||
"""
|
"""
|
||||||
Creates a CtrlNet instance.
|
Creates a CtrlNet instance.
|
||||||
|
|
||||||
|
@ -726,12 +840,18 @@ class CtrlNet(CoreNetwork):
|
||||||
logging.info("address %s", addr)
|
logging.info("address %s", addr)
|
||||||
|
|
||||||
if self.updown_script:
|
if self.updown_script:
|
||||||
logging.info("interface %s updown script (%s startup) called", self.brname, self.updown_script)
|
logging.info(
|
||||||
|
"interface %s updown script (%s startup) called",
|
||||||
|
self.brname,
|
||||||
|
self.updown_script,
|
||||||
|
)
|
||||||
utils.check_cmd([self.updown_script, self.brname, "startup"])
|
utils.check_cmd([self.updown_script, self.brname, "startup"])
|
||||||
|
|
||||||
if self.serverintf:
|
if self.serverintf:
|
||||||
# sets the interface as a port of the bridge
|
# sets the interface as a port of the bridge
|
||||||
utils.check_cmd([constants.BRCTL_BIN, "addif", self.brname, self.serverintf])
|
utils.check_cmd(
|
||||||
|
[constants.BRCTL_BIN, "addif", self.brname, self.serverintf]
|
||||||
|
)
|
||||||
|
|
||||||
# bring interface up
|
# bring interface up
|
||||||
utils.check_cmd([constants.IP_BIN, "link", "set", self.serverintf, "up"])
|
utils.check_cmd([constants.IP_BIN, "link", "set", self.serverintf, "up"])
|
||||||
|
@ -758,7 +878,9 @@ class CtrlNet(CoreNetwork):
|
||||||
logging.error(
|
logging.error(
|
||||||
"error: An active control net bridge (%s) found. "
|
"error: An active control net bridge (%s) found. "
|
||||||
"An older session might still be running. "
|
"An older session might still be running. "
|
||||||
"Stop all sessions and, if needed, delete %s to continue.", oldbr, oldbr
|
"Stop all sessions and, if needed, delete %s to continue.",
|
||||||
|
oldbr,
|
||||||
|
oldbr,
|
||||||
)
|
)
|
||||||
return True
|
return True
|
||||||
return False
|
return False
|
||||||
|
@ -771,13 +893,23 @@ class CtrlNet(CoreNetwork):
|
||||||
"""
|
"""
|
||||||
if self.serverintf is not None:
|
if self.serverintf is not None:
|
||||||
try:
|
try:
|
||||||
utils.check_cmd([constants.BRCTL_BIN, "delif", self.brname, self.serverintf])
|
utils.check_cmd(
|
||||||
|
[constants.BRCTL_BIN, "delif", self.brname, self.serverintf]
|
||||||
|
)
|
||||||
except CoreCommandError:
|
except CoreCommandError:
|
||||||
logging.exception("error deleting server interface %s from bridge %s", self.serverintf, self.brname)
|
logging.exception(
|
||||||
|
"error deleting server interface %s from bridge %s",
|
||||||
|
self.serverintf,
|
||||||
|
self.brname,
|
||||||
|
)
|
||||||
|
|
||||||
if self.updown_script is not None:
|
if self.updown_script is not None:
|
||||||
try:
|
try:
|
||||||
logging.info("interface %s updown script (%s shutdown) called", self.brname, self.updown_script)
|
logging.info(
|
||||||
|
"interface %s updown script (%s shutdown) called",
|
||||||
|
self.brname,
|
||||||
|
self.updown_script,
|
||||||
|
)
|
||||||
utils.check_cmd([self.updown_script, self.brname, "shutdown"])
|
utils.check_cmd([self.updown_script, self.brname, "shutdown"])
|
||||||
except CoreCommandError:
|
except CoreCommandError:
|
||||||
logging.exception("error issuing shutdown script shutdown")
|
logging.exception("error issuing shutdown script shutdown")
|
||||||
|
@ -799,6 +931,7 @@ class PtpNet(CoreNetwork):
|
||||||
"""
|
"""
|
||||||
Peer to peer network node.
|
Peer to peer network node.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
policy = "ACCEPT"
|
policy = "ACCEPT"
|
||||||
|
|
||||||
def attach(self, netif):
|
def attach(self, netif):
|
||||||
|
@ -809,7 +942,9 @@ class PtpNet(CoreNetwork):
|
||||||
:return: nothing
|
:return: nothing
|
||||||
"""
|
"""
|
||||||
if len(self._netif) >= 2:
|
if len(self._netif) >= 2:
|
||||||
raise ValueError("Point-to-point links support at most 2 network interfaces")
|
raise ValueError(
|
||||||
|
"Point-to-point links support at most 2 network interfaces"
|
||||||
|
)
|
||||||
|
|
||||||
CoreNetwork.attach(self, netif)
|
CoreNetwork.attach(self, netif)
|
||||||
|
|
||||||
|
@ -924,7 +1059,7 @@ class PtpNet(CoreNetwork):
|
||||||
jitter=if2.getparam("jitter"),
|
jitter=if2.getparam("jitter"),
|
||||||
unidirectional=1,
|
unidirectional=1,
|
||||||
interface1_id=if2.node.getifindex(if2),
|
interface1_id=if2.node.getifindex(if2),
|
||||||
interface2_id=if1.node.getifindex(if1)
|
interface2_id=if1.node.getifindex(if1),
|
||||||
)
|
)
|
||||||
all_links.append(link_data)
|
all_links.append(link_data)
|
||||||
|
|
||||||
|
@ -935,6 +1070,7 @@ class SwitchNode(CoreNetwork):
|
||||||
"""
|
"""
|
||||||
Provides switch functionality within a core node.
|
Provides switch functionality within a core node.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
apitype = NodeTypes.SWITCH.value
|
apitype = NodeTypes.SWITCH.value
|
||||||
policy = "ACCEPT"
|
policy = "ACCEPT"
|
||||||
type = "lanswitch"
|
type = "lanswitch"
|
||||||
|
@ -945,6 +1081,7 @@ class HubNode(CoreNetwork):
|
||||||
Provides hub functionality within a core node, forwards packets to all bridge
|
Provides hub functionality within a core node, forwards packets to all bridge
|
||||||
ports by turning off MAC address learning.
|
ports by turning off MAC address learning.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
apitype = NodeTypes.HUB.value
|
apitype = NodeTypes.HUB.value
|
||||||
policy = "ACCEPT"
|
policy = "ACCEPT"
|
||||||
type = "hub"
|
type = "hub"
|
||||||
|
@ -970,6 +1107,7 @@ class WlanNode(CoreNetwork):
|
||||||
"""
|
"""
|
||||||
Provides wireless lan functionality within a core node.
|
Provides wireless lan functionality within a core node.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
apitype = NodeTypes.WIRELESS_LAN.value
|
apitype = NodeTypes.WIRELESS_LAN.value
|
||||||
linktype = LinkTypes.WIRELESS.value
|
linktype = LinkTypes.WIRELESS.value
|
||||||
policy = "DROP"
|
policy = "DROP"
|
||||||
|
@ -991,6 +1129,10 @@ class WlanNode(CoreNetwork):
|
||||||
# mobility model such as scripted
|
# mobility model such as scripted
|
||||||
self.mobility = None
|
self.mobility = None
|
||||||
|
|
||||||
|
# TODO: move to startup method
|
||||||
|
if start:
|
||||||
|
utils.check_cmd([constants.BRCTL_BIN, "setageing", self.brname, "0"])
|
||||||
|
|
||||||
def attach(self, netif):
|
def attach(self, netif):
|
||||||
"""
|
"""
|
||||||
Attach a network interface.
|
Attach a network interface.
|
||||||
|
@ -1015,9 +1157,14 @@ class WlanNode(CoreNetwork):
|
||||||
:param dict config: configuration for model being set
|
:param dict config: configuration for model being set
|
||||||
:return: nothing
|
:return: nothing
|
||||||
"""
|
"""
|
||||||
logging.info("adding model: %s", model.name)
|
logging.debug("node(%s) setting model: %s", self.name, model.name)
|
||||||
if model.config_type == RegisterTlvs.WIRELESS.value:
|
if model.config_type == RegisterTlvs.WIRELESS.value:
|
||||||
self.model = model(session=self.session, _id=self.id)
|
self.model = model(session=self.session, _id=self.id)
|
||||||
|
for netif in self.netifs():
|
||||||
|
netif.poshook = self.model.position_callback
|
||||||
|
if netif.poshook and netif.node:
|
||||||
|
x, y, z = netif.node.position.get()
|
||||||
|
netif.poshook(netif, x, y, z)
|
||||||
self.updatemodel(config)
|
self.updatemodel(config)
|
||||||
elif model.config_type == RegisterTlvs.MOBILITY.value:
|
elif model.config_type == RegisterTlvs.MOBILITY.value:
|
||||||
self.mobility = model(session=self.session, _id=self.id)
|
self.mobility = model(session=self.session, _id=self.id)
|
||||||
|
@ -1031,14 +1178,14 @@ class WlanNode(CoreNetwork):
|
||||||
def updatemodel(self, config):
|
def updatemodel(self, config):
|
||||||
if not self.model:
|
if not self.model:
|
||||||
raise ValueError("no model set to update for node(%s)", self.id)
|
raise ValueError("no model set to update for node(%s)", self.id)
|
||||||
logging.info("node(%s) updating model(%s): %s", self.id, self.model.name, config)
|
logging.debug(
|
||||||
|
"node(%s) updating model(%s): %s", self.id, self.model.name, config
|
||||||
|
)
|
||||||
self.model.update_config(config)
|
self.model.update_config(config)
|
||||||
if self.model.position_callback:
|
for netif in self.netifs():
|
||||||
for netif in self.netifs():
|
if netif.poshook and netif.node:
|
||||||
netif.poshook = self.model.position_callback
|
x, y, z = netif.node.position.get()
|
||||||
if netif.node is not None:
|
netif.poshook(netif, x, y, z)
|
||||||
x, y, z = netif.node.position.get()
|
|
||||||
netif.poshook(netif, x, y, z)
|
|
||||||
|
|
||||||
def all_link_data(self, flags):
|
def all_link_data(self, flags):
|
||||||
"""
|
"""
|
||||||
|
@ -1060,6 +1207,7 @@ class TunnelNode(GreTapBridge):
|
||||||
"""
|
"""
|
||||||
Provides tunnel functionality in a core node.
|
Provides tunnel functionality in a core node.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
apitype = NodeTypes.TUNNEL.value
|
apitype = NodeTypes.TUNNEL.value
|
||||||
policy = "ACCEPT"
|
policy = "ACCEPT"
|
||||||
type = "tunnel"
|
type = "tunnel"
|
||||||
|
|
|
@ -2,13 +2,14 @@
|
||||||
Provides default node maps that can be used to run core with.
|
Provides default node maps that can be used to run core with.
|
||||||
"""
|
"""
|
||||||
import core.nodes.base
|
import core.nodes.base
|
||||||
|
import core.nodes.docker
|
||||||
|
import core.nodes.lxd
|
||||||
import core.nodes.network
|
import core.nodes.network
|
||||||
import core.nodes.physical
|
import core.nodes.physical
|
||||||
from core.emane.nodes import EmaneNet
|
from core.emane.nodes import EmaneNet, EmaneNode
|
||||||
from core.emane.nodes import EmaneNode
|
|
||||||
from core.emulator.enumerations import NodeTypes
|
from core.emulator.enumerations import NodeTypes
|
||||||
from core.nodes.network import GreTapBridge
|
|
||||||
from core.nodes import physical
|
from core.nodes import physical
|
||||||
|
from core.nodes.network import GreTapBridge
|
||||||
|
|
||||||
# legacy core nodes, that leverage linux bridges
|
# legacy core nodes, that leverage linux bridges
|
||||||
NODES = {
|
NODES = {
|
||||||
|
@ -25,5 +26,7 @@ NODES = {
|
||||||
NodeTypes.EMANE_NET: EmaneNet,
|
NodeTypes.EMANE_NET: EmaneNet,
|
||||||
NodeTypes.TAP_BRIDGE: GreTapBridge,
|
NodeTypes.TAP_BRIDGE: GreTapBridge,
|
||||||
NodeTypes.PEER_TO_PEER: core.nodes.network.PtpNet,
|
NodeTypes.PEER_TO_PEER: core.nodes.network.PtpNet,
|
||||||
NodeTypes.CONTROL_NET: core.nodes.network.CtrlNet
|
NodeTypes.CONTROL_NET: core.nodes.network.CtrlNet,
|
||||||
|
NodeTypes.DOCKER: core.nodes.docker.DockerNode,
|
||||||
|
NodeTypes.LXC: core.nodes.lxd.LxcNode,
|
||||||
}
|
}
|
||||||
|
|
|
@ -5,21 +5,15 @@ TODO: probably goes away, or implement the usage of "unshare", or docker formal.
|
||||||
import logging
|
import logging
|
||||||
import socket
|
import socket
|
||||||
import threading
|
import threading
|
||||||
from socket import AF_INET
|
from socket import AF_INET, AF_INET6
|
||||||
from socket import AF_INET6
|
|
||||||
|
|
||||||
from core import CoreCommandError, utils
|
from core import CoreCommandError, constants, utils
|
||||||
from core import constants
|
|
||||||
from core.nodes.base import CoreNetworkBase
|
|
||||||
from core.emulator.data import LinkData
|
from core.emulator.data import LinkData
|
||||||
from core.emulator.enumerations import LinkTypes
|
from core.emulator.enumerations import LinkTypes, NodeTypes, RegisterTlvs
|
||||||
from core.emulator.enumerations import NodeTypes
|
|
||||||
from core.emulator.enumerations import RegisterTlvs
|
|
||||||
from core.nodes import ipaddress
|
from core.nodes import ipaddress
|
||||||
from core.nodes.interface import GreTap
|
from core.nodes.base import CoreNetworkBase
|
||||||
from core.nodes.interface import Veth
|
from core.nodes.interface import GreTap, Veth
|
||||||
from core.nodes.network import EbtablesQueue
|
from core.nodes.network import EbtablesQueue, GreTapBridge
|
||||||
from core.nodes.network import GreTapBridge
|
|
||||||
|
|
||||||
# a global object because all WLANs share the same queue
|
# a global object because all WLANs share the same queue
|
||||||
# cannot have multiple threads invoking the ebtables commnd
|
# cannot have multiple threads invoking the ebtables commnd
|
||||||
|
@ -27,11 +21,7 @@ ebtables_queue = EbtablesQueue()
|
||||||
|
|
||||||
ebtables_lock = threading.Lock()
|
ebtables_lock = threading.Lock()
|
||||||
|
|
||||||
utils.check_executables([
|
utils.check_executables([constants.IP_BIN, constants.EBTABLES_BIN, constants.TC_BIN])
|
||||||
constants.IP_BIN,
|
|
||||||
constants.EBTABLES_BIN,
|
|
||||||
constants.TC_BIN
|
|
||||||
])
|
|
||||||
|
|
||||||
|
|
||||||
def ebtables_commands(call, commands):
|
def ebtables_commands(call, commands):
|
||||||
|
@ -89,10 +79,21 @@ class OvsNet(CoreNetworkBase):
|
||||||
utils.check_cmd([constants.IP_BIN, "link", "set", self.bridge_name, "up"])
|
utils.check_cmd([constants.IP_BIN, "link", "set", self.bridge_name, "up"])
|
||||||
|
|
||||||
# create a new ebtables chain for this bridge
|
# create a new ebtables chain for this bridge
|
||||||
ebtables_commands(utils.check_cmd, [
|
ebtables_commands(
|
||||||
[constants.EBTABLES_BIN, "-N", self.bridge_name, "-P", self.policy],
|
utils.check_cmd,
|
||||||
[constants.EBTABLES_BIN, "-A", "FORWARD", "--logical-in", self.bridge_name, "-j", self.bridge_name]
|
[
|
||||||
])
|
[constants.EBTABLES_BIN, "-N", self.bridge_name, "-P", self.policy],
|
||||||
|
[
|
||||||
|
constants.EBTABLES_BIN,
|
||||||
|
"-A",
|
||||||
|
"FORWARD",
|
||||||
|
"--logical-in",
|
||||||
|
self.bridge_name,
|
||||||
|
"-j",
|
||||||
|
self.bridge_name,
|
||||||
|
],
|
||||||
|
],
|
||||||
|
)
|
||||||
|
|
||||||
self.up = True
|
self.up = True
|
||||||
|
|
||||||
|
@ -106,10 +107,21 @@ class OvsNet(CoreNetworkBase):
|
||||||
try:
|
try:
|
||||||
utils.check_cmd([constants.IP_BIN, "link", "set", self.bridge_name, "down"])
|
utils.check_cmd([constants.IP_BIN, "link", "set", self.bridge_name, "down"])
|
||||||
utils.check_cmd([constants.OVS_BIN, "del-br", self.bridge_name])
|
utils.check_cmd([constants.OVS_BIN, "del-br", self.bridge_name])
|
||||||
ebtables_commands(utils.check_cmd, [
|
ebtables_commands(
|
||||||
[constants.EBTABLES_BIN, "-D", "FORWARD", "--logical-in", self.bridge_name, "-j", self.bridge_name],
|
utils.check_cmd,
|
||||||
[constants.EBTABLES_BIN, "-X", self.bridge_name]
|
[
|
||||||
])
|
[
|
||||||
|
constants.EBTABLES_BIN,
|
||||||
|
"-D",
|
||||||
|
"FORWARD",
|
||||||
|
"--logical-in",
|
||||||
|
self.bridge_name,
|
||||||
|
"-j",
|
||||||
|
self.bridge_name,
|
||||||
|
],
|
||||||
|
[constants.EBTABLES_BIN, "-X", self.bridge_name],
|
||||||
|
],
|
||||||
|
)
|
||||||
except CoreCommandError:
|
except CoreCommandError:
|
||||||
logging.exception("error bringing bridge down and removing it")
|
logging.exception("error bringing bridge down and removing it")
|
||||||
|
|
||||||
|
@ -124,14 +136,20 @@ class OvsNet(CoreNetworkBase):
|
||||||
|
|
||||||
def attach(self, interface):
|
def attach(self, interface):
|
||||||
if self.up:
|
if self.up:
|
||||||
utils.check_cmd([constants.OVS_BIN, "add-port", self.bridge_name, interface.localname])
|
utils.check_cmd(
|
||||||
utils.check_cmd([constants.IP_BIN, "link", "set", interface.localname, "up"])
|
[constants.OVS_BIN, "add-port", self.bridge_name, interface.localname]
|
||||||
|
)
|
||||||
|
utils.check_cmd(
|
||||||
|
[constants.IP_BIN, "link", "set", interface.localname, "up"]
|
||||||
|
)
|
||||||
|
|
||||||
CoreNetworkBase.attach(self, interface)
|
CoreNetworkBase.attach(self, interface)
|
||||||
|
|
||||||
def detach(self, interface):
|
def detach(self, interface):
|
||||||
if self.up:
|
if self.up:
|
||||||
utils.check_cmd([constants.OVS_BIN, "del-port", self.bridge_name, interface.localname])
|
utils.check_cmd(
|
||||||
|
[constants.OVS_BIN, "del-port", self.bridge_name, interface.localname]
|
||||||
|
)
|
||||||
|
|
||||||
CoreNetworkBase.detach(self, interface)
|
CoreNetworkBase.detach(self, interface)
|
||||||
|
|
||||||
|
@ -183,8 +201,17 @@ class OvsNet(CoreNetworkBase):
|
||||||
|
|
||||||
ebtables_queue.ebchange(self)
|
ebtables_queue.ebchange(self)
|
||||||
|
|
||||||
def linkconfig(self, netif, bw=None, delay=None, loss=None, duplicate=None,
|
def linkconfig(
|
||||||
jitter=None, netif2=None, devname=None):
|
self,
|
||||||
|
netif,
|
||||||
|
bw=None,
|
||||||
|
delay=None,
|
||||||
|
loss=None,
|
||||||
|
duplicate=None,
|
||||||
|
jitter=None,
|
||||||
|
netif2=None,
|
||||||
|
devname=None,
|
||||||
|
):
|
||||||
"""
|
"""
|
||||||
Configure link parameters by applying tc queuing disciplines on the
|
Configure link parameters by applying tc queuing disciplines on the
|
||||||
interface.
|
interface.
|
||||||
|
@ -202,9 +229,19 @@ class OvsNet(CoreNetworkBase):
|
||||||
if bw > 0:
|
if bw > 0:
|
||||||
if self.up:
|
if self.up:
|
||||||
burst = max(2 * netif.mtu, bw / 1000)
|
burst = max(2 * netif.mtu, bw / 1000)
|
||||||
limit = 0xffff # max IP payload
|
limit = 0xFFFF # max IP payload
|
||||||
tbf = ["tbf", "rate", str(bw), "burst", str(burst), "limit", str(limit)]
|
tbf = [
|
||||||
logging.info("linkconfig: %s" % [tc + parent + ["handle", "1:"] + tbf])
|
"tbf",
|
||||||
|
"rate",
|
||||||
|
str(bw),
|
||||||
|
"burst",
|
||||||
|
str(burst),
|
||||||
|
"limit",
|
||||||
|
str(limit),
|
||||||
|
]
|
||||||
|
logging.info(
|
||||||
|
"linkconfig: %s" % [tc + parent + ["handle", "1:"] + tbf]
|
||||||
|
)
|
||||||
utils.check_cmd(tc + parent + ["handle", "1:"] + tbf)
|
utils.check_cmd(tc + parent + ["handle", "1:"] + tbf)
|
||||||
netif.setparam("has_tbf", True)
|
netif.setparam("has_tbf", True)
|
||||||
elif netif.getparam("has_tbf") and bw <= 0:
|
elif netif.getparam("has_tbf") and bw <= 0:
|
||||||
|
@ -234,7 +271,15 @@ class OvsNet(CoreNetworkBase):
|
||||||
jitter_changed = netif.setparam("jitter", jitter)
|
jitter_changed = netif.setparam("jitter", jitter)
|
||||||
|
|
||||||
# if nothing changed return
|
# if nothing changed return
|
||||||
if not any([bandwidth_changed, delay_changed, loss_changed, duplicate_changed, jitter_changed]):
|
if not any(
|
||||||
|
[
|
||||||
|
bandwidth_changed,
|
||||||
|
delay_changed,
|
||||||
|
loss_changed,
|
||||||
|
duplicate_changed,
|
||||||
|
jitter_changed,
|
||||||
|
]
|
||||||
|
):
|
||||||
return
|
return
|
||||||
|
|
||||||
# jitter and delay use the same delay statement
|
# jitter and delay use the same delay statement
|
||||||
|
@ -265,7 +310,9 @@ class OvsNet(CoreNetworkBase):
|
||||||
netif.setparam("has_netem", False)
|
netif.setparam("has_netem", False)
|
||||||
elif len(netem) > 1:
|
elif len(netem) > 1:
|
||||||
if self.up:
|
if self.up:
|
||||||
logging.info("linkconfig: %s" % ([tc + parent + ["handle", "10:"] + netem],))
|
logging.info(
|
||||||
|
"linkconfig: %s" % ([tc + parent + ["handle", "10:"] + netem],)
|
||||||
|
)
|
||||||
utils.check_cmd(tc + parent + ["handle", "10:"] + netem)
|
utils.check_cmd(tc + parent + ["handle", "10:"] + netem)
|
||||||
netif.setparam("has_netem", True)
|
netif.setparam("has_netem", True)
|
||||||
|
|
||||||
|
@ -295,11 +342,15 @@ class OvsNet(CoreNetworkBase):
|
||||||
if len(name) >= 16:
|
if len(name) >= 16:
|
||||||
raise ValueError("interface name %s too long" % name)
|
raise ValueError("interface name %s too long" % name)
|
||||||
|
|
||||||
interface = Veth(node=None, name=name, localname=localname, mtu=1500, net=self, start=self.up)
|
interface = Veth(
|
||||||
|
node=None, name=name, localname=localname, mtu=1500, net=self, start=self.up
|
||||||
|
)
|
||||||
self.attach(interface)
|
self.attach(interface)
|
||||||
if network.up:
|
if network.up:
|
||||||
# this is similar to net.attach() but uses netif.name instead of localname
|
# this is similar to net.attach() but uses netif.name instead of localname
|
||||||
utils.check_cmd([constants.OVS_BIN, "add-port", network.bridge_name, interface.name])
|
utils.check_cmd(
|
||||||
|
[constants.OVS_BIN, "add-port", network.bridge_name, interface.name]
|
||||||
|
)
|
||||||
utils.check_cmd([constants.IP_BIN, "link", "set", interface.name, "up"])
|
utils.check_cmd([constants.IP_BIN, "link", "set", interface.name, "up"])
|
||||||
|
|
||||||
network.attach(interface)
|
network.attach(interface)
|
||||||
|
@ -326,7 +377,9 @@ class OvsNet(CoreNetworkBase):
|
||||||
return
|
return
|
||||||
|
|
||||||
for address in addresses:
|
for address in addresses:
|
||||||
utils.check_cmd([constants.IP_BIN, "addr", "add", str(address), "dev", self.bridge_name])
|
utils.check_cmd(
|
||||||
|
[constants.IP_BIN, "addr", "add", str(address), "dev", self.bridge_name]
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
class OvsCtrlNet(OvsNet):
|
class OvsCtrlNet(OvsNet):
|
||||||
|
@ -336,11 +389,21 @@ class OvsCtrlNet(OvsNet):
|
||||||
"172.16.0.0/24 172.16.1.0/24 172.16.2.0/24 172.16.3.0/24 172.16.4.0/24",
|
"172.16.0.0/24 172.16.1.0/24 172.16.2.0/24 172.16.3.0/24 172.16.4.0/24",
|
||||||
"172.17.0.0/24 172.17.1.0/24 172.17.2.0/24 172.17.3.0/24 172.17.4.0/24",
|
"172.17.0.0/24 172.17.1.0/24 172.17.2.0/24 172.17.3.0/24 172.17.4.0/24",
|
||||||
"172.18.0.0/24 172.18.1.0/24 172.18.2.0/24 172.18.3.0/24 172.18.4.0/24",
|
"172.18.0.0/24 172.18.1.0/24 172.18.2.0/24 172.18.3.0/24 172.18.4.0/24",
|
||||||
"172.19.0.0/24 172.19.1.0/24 172.19.2.0/24 172.19.3.0/24 172.19.4.0/24"
|
"172.19.0.0/24 172.19.1.0/24 172.19.2.0/24 172.19.3.0/24 172.19.4.0/24",
|
||||||
]
|
]
|
||||||
|
|
||||||
def __init__(self, session, _id="ctrlnet", name=None, prefix=None, hostid=None,
|
def __init__(
|
||||||
start=True, assign_address=True, updown_script=None, serverintf=None):
|
self,
|
||||||
|
session,
|
||||||
|
_id="ctrlnet",
|
||||||
|
name=None,
|
||||||
|
prefix=None,
|
||||||
|
hostid=None,
|
||||||
|
start=True,
|
||||||
|
assign_address=True,
|
||||||
|
updown_script=None,
|
||||||
|
serverintf=None,
|
||||||
|
):
|
||||||
self.prefix = ipaddress.Ipv4Prefix(prefix)
|
self.prefix = ipaddress.Ipv4Prefix(prefix)
|
||||||
self.hostid = hostid
|
self.hostid = hostid
|
||||||
self.assign_address = assign_address
|
self.assign_address = assign_address
|
||||||
|
@ -358,7 +421,10 @@ class OvsCtrlNet(OvsNet):
|
||||||
else:
|
else:
|
||||||
addr = self.prefix.max_addr()
|
addr = self.prefix.max_addr()
|
||||||
|
|
||||||
message = "Added control network bridge: %s %s" % (self.bridge_name, self.prefix)
|
message = "Added control network bridge: %s %s" % (
|
||||||
|
self.bridge_name,
|
||||||
|
self.prefix,
|
||||||
|
)
|
||||||
addresses = ["%s/%s" % (addr, self.prefix.prefixlen)]
|
addresses = ["%s/%s" % (addr, self.prefix.prefixlen)]
|
||||||
if self.assign_address:
|
if self.assign_address:
|
||||||
self.addrconfig(addresses=addresses)
|
self.addrconfig(addresses=addresses)
|
||||||
|
@ -366,11 +432,16 @@ class OvsCtrlNet(OvsNet):
|
||||||
logging.info(message)
|
logging.info(message)
|
||||||
|
|
||||||
if self.updown_script:
|
if self.updown_script:
|
||||||
logging.info("interface %s updown script %s startup called" % (self.bridge_name, self.updown_script))
|
logging.info(
|
||||||
|
"interface %s updown script %s startup called"
|
||||||
|
% (self.bridge_name, self.updown_script)
|
||||||
|
)
|
||||||
utils.check_cmd([self.updown_script, self.bridge_name, "startup"])
|
utils.check_cmd([self.updown_script, self.bridge_name, "startup"])
|
||||||
|
|
||||||
if self.serverintf:
|
if self.serverintf:
|
||||||
utils.check_cmd([constants.OVS_BIN, "add-port", self.bridge_name, self.serverintf])
|
utils.check_cmd(
|
||||||
|
[constants.OVS_BIN, "add-port", self.bridge_name, self.serverintf]
|
||||||
|
)
|
||||||
utils.check_cmd([constants.IP_BIN, "link", "set", self.serverintf, "up"])
|
utils.check_cmd([constants.IP_BIN, "link", "set", self.serverintf, "up"])
|
||||||
|
|
||||||
def detectoldbridge(self):
|
def detectoldbridge(self):
|
||||||
|
@ -385,7 +456,10 @@ class OvsCtrlNet(OvsNet):
|
||||||
for line in output.split("\n"):
|
for line in output.split("\n"):
|
||||||
bride_name = line.split(".")
|
bride_name = line.split(".")
|
||||||
if bride_name[0] == "b" and bride_name[1] == self.id:
|
if bride_name[0] == "b" and bride_name[1] == self.id:
|
||||||
logging.error("older session may still be running with conflicting id for bridge: %s", line)
|
logging.error(
|
||||||
|
"older session may still be running with conflicting id for bridge: %s",
|
||||||
|
line,
|
||||||
|
)
|
||||||
return True
|
return True
|
||||||
|
|
||||||
return False
|
return False
|
||||||
|
@ -393,14 +467,23 @@ class OvsCtrlNet(OvsNet):
|
||||||
def shutdown(self):
|
def shutdown(self):
|
||||||
if self.serverintf:
|
if self.serverintf:
|
||||||
try:
|
try:
|
||||||
utils.check_cmd([constants.OVS_BIN, "del-port", self.bridge_name, self.serverintf])
|
utils.check_cmd(
|
||||||
|
[constants.OVS_BIN, "del-port", self.bridge_name, self.serverintf]
|
||||||
|
)
|
||||||
except CoreCommandError:
|
except CoreCommandError:
|
||||||
logging.exception("error deleting server interface %s to controlnet bridge %s",
|
logging.exception(
|
||||||
self.serverintf, self.bridge_name)
|
"error deleting server interface %s to controlnet bridge %s",
|
||||||
|
self.serverintf,
|
||||||
|
self.bridge_name,
|
||||||
|
)
|
||||||
|
|
||||||
if self.updown_script:
|
if self.updown_script:
|
||||||
try:
|
try:
|
||||||
logging.info("interface %s updown script (%s shutdown) called", self.bridge_name, self.updown_script)
|
logging.info(
|
||||||
|
"interface %s updown script (%s shutdown) called",
|
||||||
|
self.bridge_name,
|
||||||
|
self.updown_script,
|
||||||
|
)
|
||||||
utils.check_cmd([self.updown_script, self.bridge_name, "shutdown"])
|
utils.check_cmd([self.updown_script, self.bridge_name, "shutdown"])
|
||||||
except CoreCommandError:
|
except CoreCommandError:
|
||||||
logging.exception("error during updown script shutdown")
|
logging.exception("error during updown script shutdown")
|
||||||
|
@ -419,7 +502,9 @@ class OvsPtpNet(OvsNet):
|
||||||
|
|
||||||
def attach(self, interface):
|
def attach(self, interface):
|
||||||
if len(self._netif) >= 2:
|
if len(self._netif) >= 2:
|
||||||
raise ValueError("point-to-point links support at most 2 network interfaces")
|
raise ValueError(
|
||||||
|
"point-to-point links support at most 2 network interfaces"
|
||||||
|
)
|
||||||
OvsNet.attach(self, interface)
|
OvsNet.attach(self, interface)
|
||||||
|
|
||||||
def data(self, message_type, lat=None, lon=None, alt=None):
|
def data(self, message_type, lat=None, lon=None, alt=None):
|
||||||
|
@ -522,7 +607,7 @@ class OvsPtpNet(OvsNet):
|
||||||
jitter=if1.getparam("jitter"),
|
jitter=if1.getparam("jitter"),
|
||||||
unidirectional=1,
|
unidirectional=1,
|
||||||
interface1_id=if2.node.getifindex(if2),
|
interface1_id=if2.node.getifindex(if2),
|
||||||
interface2_id=if1.node.getifindex(if1)
|
interface2_id=if1.node.getifindex(if1),
|
||||||
)
|
)
|
||||||
all_links.append(link_data)
|
all_links.append(link_data)
|
||||||
|
|
||||||
|
@ -550,7 +635,9 @@ class OvsHubNode(OvsNet):
|
||||||
if start:
|
if start:
|
||||||
# TODO: verify that the below flow accomplishes what is desired for a "HUB"
|
# TODO: verify that the below flow accomplishes what is desired for a "HUB"
|
||||||
# TODO: replace "brctl setageing 0"
|
# TODO: replace "brctl setageing 0"
|
||||||
utils.check_cmd([constants.OVS_FLOW_BIN, "add-flow", self.bridge_name, "action=flood"])
|
utils.check_cmd(
|
||||||
|
[constants.OVS_FLOW_BIN, "add-flow", self.bridge_name, "action=flood"]
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
class OvsWlanNode(OvsNet):
|
class OvsWlanNode(OvsNet):
|
||||||
|
@ -602,7 +689,9 @@ class OvsWlanNode(OvsNet):
|
||||||
def updatemodel(self, config):
|
def updatemodel(self, config):
|
||||||
if not self.model:
|
if not self.model:
|
||||||
raise ValueError("no model set to update for node(%s)", self.id)
|
raise ValueError("no model set to update for node(%s)", self.id)
|
||||||
logging.info("node(%s) updating model(%s): %s", self.id, self.model.name, config)
|
logging.info(
|
||||||
|
"node(%s) updating model(%s): %s", self.id, self.model.name, config
|
||||||
|
)
|
||||||
self.model.set_configs(config, node_id=self.id)
|
self.model.set_configs(config, node_id=self.id)
|
||||||
if self.model.position_callback:
|
if self.model.position_callback:
|
||||||
for netif in self.netifs():
|
for netif in self.netifs():
|
||||||
|
@ -633,9 +722,21 @@ class OvsGreTapBridge(OvsNet):
|
||||||
another system.
|
another system.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
def __init__(self, session, remoteip=None, _id=None, name=None, policy="ACCEPT",
|
def __init__(
|
||||||
localip=None, ttl=255, key=None, start=True):
|
self,
|
||||||
OvsNet.__init__(self, session=session, _id=_id, name=name, policy=policy, start=False)
|
session,
|
||||||
|
remoteip=None,
|
||||||
|
_id=None,
|
||||||
|
name=None,
|
||||||
|
policy="ACCEPT",
|
||||||
|
localip=None,
|
||||||
|
ttl=255,
|
||||||
|
key=None,
|
||||||
|
start=True,
|
||||||
|
):
|
||||||
|
OvsNet.__init__(
|
||||||
|
self, session=session, _id=_id, name=name, policy=policy, start=False
|
||||||
|
)
|
||||||
self.grekey = key
|
self.grekey = key
|
||||||
if self.grekey is None:
|
if self.grekey is None:
|
||||||
self.grekey = self.session.id ^ self.id
|
self.grekey = self.session.id ^ self.id
|
||||||
|
@ -649,8 +750,14 @@ class OvsGreTapBridge(OvsNet):
|
||||||
if remoteip is None:
|
if remoteip is None:
|
||||||
self.gretap = None
|
self.gretap = None
|
||||||
else:
|
else:
|
||||||
self.gretap = GreTap(node=self, session=session, remoteip=remoteip,
|
self.gretap = GreTap(
|
||||||
localip=localip, ttl=ttl, key=self.grekey)
|
node=self,
|
||||||
|
session=session,
|
||||||
|
remoteip=remoteip,
|
||||||
|
localip=localip,
|
||||||
|
ttl=ttl,
|
||||||
|
key=self.grekey,
|
||||||
|
)
|
||||||
if start:
|
if start:
|
||||||
self.startup()
|
self.startup()
|
||||||
|
|
||||||
|
@ -690,8 +797,13 @@ class OvsGreTapBridge(OvsNet):
|
||||||
if len(addresses) > 1:
|
if len(addresses) > 1:
|
||||||
localip = addresses[1].split("/")[0]
|
localip = addresses[1].split("/")[0]
|
||||||
|
|
||||||
self.gretap = GreTap(session=self.session, remoteip=remoteip,
|
self.gretap = GreTap(
|
||||||
localip=localip, ttl=self.ttl, key=self.grekey)
|
session=self.session,
|
||||||
|
remoteip=remoteip,
|
||||||
|
localip=localip,
|
||||||
|
ttl=self.ttl,
|
||||||
|
key=self.grekey,
|
||||||
|
)
|
||||||
self.attach(self.gretap)
|
self.attach(self.gretap)
|
||||||
|
|
||||||
def setkey(self, key):
|
def setkey(self, key):
|
||||||
|
@ -709,5 +821,5 @@ OVS_NODES = {
|
||||||
NodeTypes.TUNNEL: OvsTunnelNode,
|
NodeTypes.TUNNEL: OvsTunnelNode,
|
||||||
NodeTypes.TAP_BRIDGE: OvsGreTapBridge,
|
NodeTypes.TAP_BRIDGE: OvsGreTapBridge,
|
||||||
NodeTypes.PEER_TO_PEER: OvsPtpNet,
|
NodeTypes.PEER_TO_PEER: OvsPtpNet,
|
||||||
NodeTypes.CONTROL_NET: OvsCtrlNet
|
NodeTypes.CONTROL_NET: OvsCtrlNet,
|
||||||
}
|
}
|
||||||
|
|
|
@ -7,13 +7,11 @@ import os
|
||||||
import subprocess
|
import subprocess
|
||||||
import threading
|
import threading
|
||||||
|
|
||||||
from core import CoreCommandError, utils
|
from core import CoreCommandError, constants, utils
|
||||||
from core import constants
|
from core.emulator.enumerations import NodeTypes
|
||||||
from core.nodes.base import CoreNodeBase
|
from core.nodes.base import CoreNodeBase
|
||||||
from core.nodes.interface import CoreInterface
|
from core.nodes.interface import CoreInterface
|
||||||
from core.emulator.enumerations import NodeTypes
|
from core.nodes.network import CoreNetwork, GreTap
|
||||||
from core.nodes.network import GreTap
|
|
||||||
from core.nodes.network import CoreNetwork
|
|
||||||
|
|
||||||
|
|
||||||
class PhysicalNode(CoreNodeBase):
|
class PhysicalNode(CoreNodeBase):
|
||||||
|
@ -104,14 +102,25 @@ class PhysicalNode(CoreNodeBase):
|
||||||
self._netif[ifindex].sethwaddr(addr)
|
self._netif[ifindex].sethwaddr(addr)
|
||||||
ifname = self.ifname(ifindex)
|
ifname = self.ifname(ifindex)
|
||||||
if self.up:
|
if self.up:
|
||||||
self.check_cmd([constants.IP_BIN, "link", "set", "dev", ifname, "address", str(addr)])
|
self.check_cmd(
|
||||||
|
[constants.IP_BIN, "link", "set", "dev", ifname, "address", str(addr)]
|
||||||
|
)
|
||||||
|
|
||||||
def addaddr(self, ifindex, addr):
|
def addaddr(self, ifindex, addr):
|
||||||
"""
|
"""
|
||||||
Add an address to an interface.
|
Add an address to an interface.
|
||||||
"""
|
"""
|
||||||
if self.up:
|
if self.up:
|
||||||
self.check_cmd([constants.IP_BIN, "addr", "add", str(addr), "dev", self.ifname(ifindex)])
|
self.check_cmd(
|
||||||
|
[
|
||||||
|
constants.IP_BIN,
|
||||||
|
"addr",
|
||||||
|
"add",
|
||||||
|
str(addr),
|
||||||
|
"dev",
|
||||||
|
self.ifname(ifindex),
|
||||||
|
]
|
||||||
|
)
|
||||||
|
|
||||||
self._netif[ifindex].addaddr(addr)
|
self._netif[ifindex].addaddr(addr)
|
||||||
|
|
||||||
|
@ -125,7 +134,16 @@ class PhysicalNode(CoreNodeBase):
|
||||||
logging.exception("trying to delete unknown address: %s", addr)
|
logging.exception("trying to delete unknown address: %s", addr)
|
||||||
|
|
||||||
if self.up:
|
if self.up:
|
||||||
self.check_cmd([constants.IP_BIN, "addr", "del", str(addr), "dev", self.ifname(ifindex)])
|
self.check_cmd(
|
||||||
|
[
|
||||||
|
constants.IP_BIN,
|
||||||
|
"addr",
|
||||||
|
"del",
|
||||||
|
str(addr),
|
||||||
|
"dev",
|
||||||
|
self.ifname(ifindex),
|
||||||
|
]
|
||||||
|
)
|
||||||
|
|
||||||
def adoptnetif(self, netif, ifindex, hwaddr, addrlist):
|
def adoptnetif(self, netif, ifindex, hwaddr, addrlist):
|
||||||
"""
|
"""
|
||||||
|
@ -140,8 +158,12 @@ class PhysicalNode(CoreNodeBase):
|
||||||
|
|
||||||
# use a more reasonable name, e.g. "gt0" instead of "gt.56286.150"
|
# use a more reasonable name, e.g. "gt0" instead of "gt.56286.150"
|
||||||
if self.up:
|
if self.up:
|
||||||
self.check_cmd([constants.IP_BIN, "link", "set", "dev", netif.localname, "down"])
|
self.check_cmd(
|
||||||
self.check_cmd([constants.IP_BIN, "link", "set", netif.localname, "name", netif.name])
|
[constants.IP_BIN, "link", "set", "dev", netif.localname, "down"]
|
||||||
|
)
|
||||||
|
self.check_cmd(
|
||||||
|
[constants.IP_BIN, "link", "set", netif.localname, "name", netif.name]
|
||||||
|
)
|
||||||
|
|
||||||
netif.localname = netif.name
|
netif.localname = netif.name
|
||||||
|
|
||||||
|
@ -152,16 +174,35 @@ class PhysicalNode(CoreNodeBase):
|
||||||
self.addaddr(ifindex, addr)
|
self.addaddr(ifindex, addr)
|
||||||
|
|
||||||
if self.up:
|
if self.up:
|
||||||
self.check_cmd([constants.IP_BIN, "link", "set", "dev", netif.localname, "up"])
|
self.check_cmd(
|
||||||
|
[constants.IP_BIN, "link", "set", "dev", netif.localname, "up"]
|
||||||
|
)
|
||||||
|
|
||||||
def linkconfig(self, netif, bw=None, delay=None, loss=None, duplicate=None, jitter=None, netif2=None):
|
def linkconfig(
|
||||||
|
self,
|
||||||
|
netif,
|
||||||
|
bw=None,
|
||||||
|
delay=None,
|
||||||
|
loss=None,
|
||||||
|
duplicate=None,
|
||||||
|
jitter=None,
|
||||||
|
netif2=None,
|
||||||
|
):
|
||||||
"""
|
"""
|
||||||
Apply tc queing disciplines using LxBrNet.linkconfig()
|
Apply tc queing disciplines using LxBrNet.linkconfig()
|
||||||
"""
|
"""
|
||||||
# borrow the tc qdisc commands from LxBrNet.linkconfig()
|
# borrow the tc qdisc commands from LxBrNet.linkconfig()
|
||||||
linux_bridge = CoreNetwork(session=self.session, start=False)
|
linux_bridge = CoreNetwork(session=self.session, start=False)
|
||||||
linux_bridge.up = True
|
linux_bridge.up = True
|
||||||
linux_bridge.linkconfig(netif, bw=bw, delay=delay, loss=loss, duplicate=duplicate, jitter=jitter, netif2=netif2)
|
linux_bridge.linkconfig(
|
||||||
|
netif,
|
||||||
|
bw=bw,
|
||||||
|
delay=delay,
|
||||||
|
loss=loss,
|
||||||
|
duplicate=duplicate,
|
||||||
|
jitter=jitter,
|
||||||
|
netif2=netif2,
|
||||||
|
)
|
||||||
del linux_bridge
|
del linux_bridge
|
||||||
|
|
||||||
def newifindex(self):
|
def newifindex(self):
|
||||||
|
@ -188,7 +229,9 @@ class PhysicalNode(CoreNodeBase):
|
||||||
# tunnel to net not built yet, so build it now and adopt it
|
# tunnel to net not built yet, so build it now and adopt it
|
||||||
gt = self.session.broker.addnettunnel(net.id)
|
gt = self.session.broker.addnettunnel(net.id)
|
||||||
if gt is None or len(gt) != 1:
|
if gt is None or len(gt) != 1:
|
||||||
raise ValueError("error building tunnel from adding a new network interface: %s" % gt)
|
raise ValueError(
|
||||||
|
"error building tunnel from adding a new network interface: %s" % gt
|
||||||
|
)
|
||||||
gt = gt[0]
|
gt = gt[0]
|
||||||
net.detach(gt)
|
net.detach(gt)
|
||||||
self.adoptnetif(gt, ifindex, hwaddr, addrlist)
|
self.adoptnetif(gt, ifindex, hwaddr, addrlist)
|
||||||
|
@ -205,7 +248,9 @@ class PhysicalNode(CoreNodeBase):
|
||||||
def privatedir(self, path):
|
def privatedir(self, path):
|
||||||
if path[0] != "/":
|
if path[0] != "/":
|
||||||
raise ValueError("path not fully qualified: %s" % path)
|
raise ValueError("path not fully qualified: %s" % path)
|
||||||
hostpath = os.path.join(self.nodedir, os.path.normpath(path).strip('/').replace('/', '.'))
|
hostpath = os.path.join(
|
||||||
|
self.nodedir, os.path.normpath(path).strip("/").replace("/", ".")
|
||||||
|
)
|
||||||
os.mkdir(hostpath)
|
os.mkdir(hostpath)
|
||||||
self.mount(hostpath, path)
|
self.mount(hostpath, path)
|
||||||
|
|
||||||
|
@ -251,6 +296,7 @@ class Rj45Node(CoreNodeBase, CoreInterface):
|
||||||
RJ45Node is a physical interface on the host linked to the emulated
|
RJ45Node is a physical interface on the host linked to the emulated
|
||||||
network.
|
network.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
apitype = NodeTypes.RJ45.value
|
apitype = NodeTypes.RJ45.value
|
||||||
type = "rj45"
|
type = "rj45"
|
||||||
|
|
||||||
|
@ -304,7 +350,9 @@ class Rj45Node(CoreNodeBase, CoreInterface):
|
||||||
try:
|
try:
|
||||||
utils.check_cmd([constants.IP_BIN, "link", "set", self.localname, "down"])
|
utils.check_cmd([constants.IP_BIN, "link", "set", self.localname, "down"])
|
||||||
utils.check_cmd([constants.IP_BIN, "addr", "flush", "dev", self.localname])
|
utils.check_cmd([constants.IP_BIN, "addr", "flush", "dev", self.localname])
|
||||||
utils.check_cmd([constants.TC_BIN, "qdisc", "del", "dev", self.localname, "root"])
|
utils.check_cmd(
|
||||||
|
[constants.TC_BIN, "qdisc", "del", "dev", self.localname, "root"]
|
||||||
|
)
|
||||||
except CoreCommandError:
|
except CoreCommandError:
|
||||||
logging.exception("error shutting down")
|
logging.exception("error shutting down")
|
||||||
|
|
||||||
|
@ -427,7 +475,9 @@ class Rj45Node(CoreNodeBase, CoreInterface):
|
||||||
:raises CoreCommandError: when there is a command exception
|
:raises CoreCommandError: when there is a command exception
|
||||||
"""
|
"""
|
||||||
if self.up:
|
if self.up:
|
||||||
utils.check_cmd([constants.IP_BIN, "addr", "add", str(addr), "dev", self.name])
|
utils.check_cmd(
|
||||||
|
[constants.IP_BIN, "addr", "add", str(addr), "dev", self.name]
|
||||||
|
)
|
||||||
|
|
||||||
CoreInterface.addaddr(self, addr)
|
CoreInterface.addaddr(self, addr)
|
||||||
|
|
||||||
|
@ -440,7 +490,9 @@ class Rj45Node(CoreNodeBase, CoreInterface):
|
||||||
:raises CoreCommandError: when there is a command exception
|
:raises CoreCommandError: when there is a command exception
|
||||||
"""
|
"""
|
||||||
if self.up:
|
if self.up:
|
||||||
utils.check_cmd([constants.IP_BIN, "addr", "del", str(addr), "dev", self.name])
|
utils.check_cmd(
|
||||||
|
[constants.IP_BIN, "addr", "del", str(addr), "dev", self.name]
|
||||||
|
)
|
||||||
|
|
||||||
CoreInterface.deladdr(self, addr)
|
CoreInterface.deladdr(self, addr)
|
||||||
|
|
||||||
|
@ -481,9 +533,22 @@ class Rj45Node(CoreNodeBase, CoreInterface):
|
||||||
"""
|
"""
|
||||||
for addr in self.old_addrs:
|
for addr in self.old_addrs:
|
||||||
if addr[1] is None:
|
if addr[1] is None:
|
||||||
utils.check_cmd([constants.IP_BIN, "addr", "add", addr[0], "dev", self.localname])
|
utils.check_cmd(
|
||||||
|
[constants.IP_BIN, "addr", "add", addr[0], "dev", self.localname]
|
||||||
|
)
|
||||||
else:
|
else:
|
||||||
utils.check_cmd([constants.IP_BIN, "addr", "add", addr[0], "brd", addr[1], "dev", self.localname])
|
utils.check_cmd(
|
||||||
|
[
|
||||||
|
constants.IP_BIN,
|
||||||
|
"addr",
|
||||||
|
"add",
|
||||||
|
addr[0],
|
||||||
|
"brd",
|
||||||
|
addr[1],
|
||||||
|
"dev",
|
||||||
|
self.localname,
|
||||||
|
]
|
||||||
|
)
|
||||||
|
|
||||||
if self.old_up:
|
if self.old_up:
|
||||||
utils.check_cmd([constants.IP_BIN, "link", "set", self.localname, "up"])
|
utils.check_cmd([constants.IP_BIN, "link", "set", self.localname, "up"])
|
||||||
|
|
|
@ -4,18 +4,21 @@ sdt.py: Scripted Display Tool (SDT3D) helper
|
||||||
|
|
||||||
import logging
|
import logging
|
||||||
import socket
|
import socket
|
||||||
|
|
||||||
from future.moves.urllib.parse import urlparse
|
from future.moves.urllib.parse import urlparse
|
||||||
|
|
||||||
from core import constants
|
from core import CoreError, constants
|
||||||
from core.emulator.enumerations import EventTypes
|
from core.emulator.enumerations import (
|
||||||
from core.emulator.enumerations import LinkTlvs
|
EventTypes,
|
||||||
from core.emulator.enumerations import LinkTypes
|
LinkTlvs,
|
||||||
from core.emulator.enumerations import MessageFlags
|
LinkTypes,
|
||||||
from core.emulator.enumerations import MessageTypes
|
MessageFlags,
|
||||||
from core.emulator.enumerations import NodeTlvs
|
MessageTypes,
|
||||||
from core.emulator.enumerations import NodeTypes
|
NodeTlvs,
|
||||||
|
NodeTypes,
|
||||||
|
)
|
||||||
from core.nodes import nodeutils
|
from core.nodes import nodeutils
|
||||||
from core.nodes.base import NodeBase, CoreNetworkBase
|
from core.nodes.base import CoreNetworkBase, NodeBase
|
||||||
|
|
||||||
|
|
||||||
# TODO: A named tuple may be more appropriate, than abusing a class dict like this
|
# TODO: A named tuple may be more appropriate, than abusing a class dict like this
|
||||||
|
@ -40,6 +43,7 @@ class Sdt(object):
|
||||||
The connect() method initializes the display, and can be invoked
|
The connect() method initializes the display, and can be invoked
|
||||||
when a node position or link has changed.
|
when a node position or link has changed.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
DEFAULT_SDT_URL = "tcp://127.0.0.1:50000/"
|
DEFAULT_SDT_URL = "tcp://127.0.0.1:50000/"
|
||||||
# default altitude (in meters) for flyto view
|
# default altitude (in meters) for flyto view
|
||||||
DEFAULT_ALT = 2500
|
DEFAULT_ALT = 2500
|
||||||
|
@ -93,7 +97,12 @@ class Sdt(object):
|
||||||
alt = node_data.altitude
|
alt = node_data.altitude
|
||||||
|
|
||||||
if all([lat, lon, alt]):
|
if all([lat, lon, alt]):
|
||||||
self.updatenodegeo(node_data.id, node_data.latitude, node_data.longitude, node_data.altitude)
|
self.updatenodegeo(
|
||||||
|
node_data.id,
|
||||||
|
node_data.latitude,
|
||||||
|
node_data.longitude,
|
||||||
|
node_data.altitude,
|
||||||
|
)
|
||||||
|
|
||||||
if node_data.message_type == 0:
|
if node_data.message_type == 0:
|
||||||
# TODO: z is not currently supported by node messages
|
# TODO: z is not currently supported by node messages
|
||||||
|
@ -107,7 +116,12 @@ class Sdt(object):
|
||||||
:return: nothing
|
:return: nothing
|
||||||
"""
|
"""
|
||||||
if link_data.link_type == LinkTypes.WIRELESS.value:
|
if link_data.link_type == LinkTypes.WIRELESS.value:
|
||||||
self.updatelink(link_data.node1_id, link_data.node2_id, link_data.message_type, wireless=True)
|
self.updatelink(
|
||||||
|
link_data.node1_id,
|
||||||
|
link_data.node2_id,
|
||||||
|
link_data.message_type,
|
||||||
|
wireless=True,
|
||||||
|
)
|
||||||
|
|
||||||
def is_enabled(self):
|
def is_enabled(self):
|
||||||
"""
|
"""
|
||||||
|
@ -179,7 +193,7 @@ class Sdt(object):
|
||||||
:return: initialize command status
|
:return: initialize command status
|
||||||
:rtype: bool
|
:rtype: bool
|
||||||
"""
|
"""
|
||||||
if not self.cmd("path \"%s/icons/normal\"" % constants.CORE_DATA_DIR):
|
if not self.cmd('path "%s/icons/normal"' % constants.CORE_DATA_DIR):
|
||||||
return False
|
return False
|
||||||
# send node type to icon mappings
|
# send node type to icon mappings
|
||||||
for node_type, icon in self.DEFAULT_SPRITES:
|
for node_type, icon in self.DEFAULT_SPRITES:
|
||||||
|
@ -210,7 +224,6 @@ class Sdt(object):
|
||||||
|
|
||||||
:return: nothing
|
:return: nothing
|
||||||
"""
|
"""
|
||||||
logging.info("SDT shutdown!")
|
|
||||||
self.cmd("clear all")
|
self.cmd("clear all")
|
||||||
self.disconnect()
|
self.disconnect()
|
||||||
self.showerror = True
|
self.showerror = True
|
||||||
|
@ -266,7 +279,9 @@ class Sdt(object):
|
||||||
icon = icon.replace("$CORE_DATA_DIR", constants.CORE_DATA_DIR)
|
icon = icon.replace("$CORE_DATA_DIR", constants.CORE_DATA_DIR)
|
||||||
icon = icon.replace("$CORE_CONF_DIR", constants.CORE_CONF_DIR)
|
icon = icon.replace("$CORE_CONF_DIR", constants.CORE_CONF_DIR)
|
||||||
self.cmd("sprite %s image %s" % (type, icon))
|
self.cmd("sprite %s image %s" % (type, icon))
|
||||||
self.cmd("node %d type %s label on,\"%s\" %s" % (nodenum, node_type, name, pos))
|
self.cmd(
|
||||||
|
'node %d type %s label on,"%s" %s' % (nodenum, node_type, name, pos)
|
||||||
|
)
|
||||||
else:
|
else:
|
||||||
self.cmd("node %d %s" % (nodenum, pos))
|
self.cmd("node %d %s" % (nodenum, pos))
|
||||||
|
|
||||||
|
@ -330,16 +345,29 @@ class Sdt(object):
|
||||||
(x, y, z) = node.getposition()
|
(x, y, z) = node.getposition()
|
||||||
if x is None or y is None:
|
if x is None or y is None:
|
||||||
continue
|
continue
|
||||||
self.updatenode(node.id, MessageFlags.ADD.value, x, y, z, node.name, node.type, node.icon)
|
self.updatenode(
|
||||||
|
node.id,
|
||||||
|
MessageFlags.ADD.value,
|
||||||
|
x,
|
||||||
|
y,
|
||||||
|
z,
|
||||||
|
node.name,
|
||||||
|
node.type,
|
||||||
|
node.icon,
|
||||||
|
)
|
||||||
for nodenum in sorted(self.remotes.keys()):
|
for nodenum in sorted(self.remotes.keys()):
|
||||||
r = self.remotes[nodenum]
|
r = self.remotes[nodenum]
|
||||||
x, y, z = r.pos
|
x, y, z = r.pos
|
||||||
self.updatenode(nodenum, MessageFlags.ADD.value, x, y, z, r.name, r.type, r.icon)
|
self.updatenode(
|
||||||
|
nodenum, MessageFlags.ADD.value, x, y, z, r.name, r.type, r.icon
|
||||||
|
)
|
||||||
|
|
||||||
for net in nets:
|
for net in nets:
|
||||||
all_links = net.all_link_data(flags=MessageFlags.ADD.value)
|
all_links = net.all_link_data(flags=MessageFlags.ADD.value)
|
||||||
for link_data in all_links:
|
for link_data in all_links:
|
||||||
is_wireless = nodeutils.is_node(net, (NodeTypes.WIRELESS_LAN, NodeTypes.EMANE))
|
is_wireless = nodeutils.is_node(
|
||||||
|
net, (NodeTypes.WIRELESS_LAN, NodeTypes.EMANE)
|
||||||
|
)
|
||||||
wireless_link = link_data.message_type == LinkTypes.WIRELESS.value
|
wireless_link = link_data.message_type == LinkTypes.WIRELESS.value
|
||||||
if is_wireless and link_data.node1_id == net.id:
|
if is_wireless and link_data.node1_id == net.id:
|
||||||
continue
|
continue
|
||||||
|
@ -348,7 +376,7 @@ class Sdt(object):
|
||||||
link_data.node1_id,
|
link_data.node1_id,
|
||||||
link_data.node2_id,
|
link_data.node2_id,
|
||||||
MessageFlags.ADD.value,
|
MessageFlags.ADD.value,
|
||||||
wireless_link
|
wireless_link,
|
||||||
)
|
)
|
||||||
|
|
||||||
for n1num in sorted(self.remotes.keys()):
|
for n1num in sorted(self.remotes.keys()):
|
||||||
|
@ -397,8 +425,7 @@ class Sdt(object):
|
||||||
icon = msg.get_tlv(NodeTlvs.ICON.value)
|
icon = msg.get_tlv(NodeTlvs.ICON.value)
|
||||||
|
|
||||||
net = False
|
net = False
|
||||||
if nodetype == NodeTypes.DEFAULT.value or \
|
if nodetype == NodeTypes.DEFAULT.value or nodetype == NodeTypes.PHYSICAL.value:
|
||||||
nodetype == NodeTypes.PHYSICAL.value:
|
|
||||||
if model is None:
|
if model is None:
|
||||||
model = "router"
|
model = "router"
|
||||||
nodetype = model
|
nodetype = model
|
||||||
|
@ -410,10 +437,12 @@ class Sdt(object):
|
||||||
|
|
||||||
try:
|
try:
|
||||||
node = self.session.get_node(nodenum)
|
node = self.session.get_node(nodenum)
|
||||||
except KeyError:
|
except CoreError:
|
||||||
node = None
|
node = None
|
||||||
if node:
|
if node:
|
||||||
self.updatenode(node.id, msg.flags, x, y, z, node.name, node.type, node.icon)
|
self.updatenode(
|
||||||
|
node.id, msg.flags, x, y, z, node.name, node.type, node.icon
|
||||||
|
)
|
||||||
else:
|
else:
|
||||||
if nodenum in self.remotes:
|
if nodenum in self.remotes:
|
||||||
remote = self.remotes[nodenum]
|
remote = self.remotes[nodenum]
|
||||||
|
@ -424,7 +453,14 @@ class Sdt(object):
|
||||||
if icon is None:
|
if icon is None:
|
||||||
icon = remote.icon
|
icon = remote.icon
|
||||||
else:
|
else:
|
||||||
remote = Bunch(_id=nodenum, type=nodetype, icon=icon, name=name, net=net, links=set())
|
remote = Bunch(
|
||||||
|
_id=nodenum,
|
||||||
|
type=nodetype,
|
||||||
|
icon=icon,
|
||||||
|
name=name,
|
||||||
|
net=net,
|
||||||
|
links=set(),
|
||||||
|
)
|
||||||
self.remotes[nodenum] = remote
|
self.remotes[nodenum] = remote
|
||||||
remote.pos = (x, y, z)
|
remote.pos = (x, y, z)
|
||||||
self.updatenode(nodenum, msg.flags, x, y, z, name, nodetype, icon)
|
self.updatenode(nodenum, msg.flags, x, y, z, name, nodetype, icon)
|
||||||
|
@ -471,7 +507,7 @@ class Sdt(object):
|
||||||
else:
|
else:
|
||||||
try:
|
try:
|
||||||
n = self.session.get_node(nodenum)
|
n = self.session.get_node(nodenum)
|
||||||
except KeyError:
|
except CoreError:
|
||||||
return False
|
return False
|
||||||
if nodeutils.is_node(n, (NodeTypes.WIRELESS_LAN, NodeTypes.EMANE)):
|
if nodeutils.is_node(n, (NodeTypes.WIRELESS_LAN, NodeTypes.EMANE)):
|
||||||
return True
|
return True
|
||||||
|
|
|
@ -9,6 +9,7 @@ class Bird(CoreService):
|
||||||
"""
|
"""
|
||||||
Bird router support
|
Bird router support
|
||||||
"""
|
"""
|
||||||
|
|
||||||
name = "bird"
|
name = "bird"
|
||||||
executables = ("bird",)
|
executables = ("bird",)
|
||||||
group = "BIRD"
|
group = "BIRD"
|
||||||
|
@ -34,11 +35,11 @@ class Bird(CoreService):
|
||||||
Helper to return the first IPv4 address of a node as its router ID.
|
Helper to return the first IPv4 address of a node as its router ID.
|
||||||
"""
|
"""
|
||||||
for ifc in node.netifs():
|
for ifc in node.netifs():
|
||||||
if hasattr(ifc, 'control') and ifc.control is True:
|
if hasattr(ifc, "control") and ifc.control is True:
|
||||||
continue
|
continue
|
||||||
for a in ifc.addrlist:
|
for a in ifc.addrlist:
|
||||||
if a.find(".") >= 0:
|
if a.find(".") >= 0:
|
||||||
return a.split('/')[0]
|
return a.split("/")[0]
|
||||||
# raise ValueError, "no IPv4 address found for router ID"
|
# raise ValueError, "no IPv4 address found for router ID"
|
||||||
return "0.0.0.0"
|
return "0.0.0.0"
|
||||||
|
|
||||||
|
@ -72,7 +73,10 @@ protocol device {
|
||||||
scan time 10; # Scan interfaces every 10 seconds
|
scan time 10; # Scan interfaces every 10 seconds
|
||||||
}
|
}
|
||||||
|
|
||||||
""" % (cls.name, cls.routerid(node))
|
""" % (
|
||||||
|
cls.name,
|
||||||
|
cls.routerid(node),
|
||||||
|
)
|
||||||
|
|
||||||
# Generate protocol specific configurations
|
# Generate protocol specific configurations
|
||||||
for s in node.services:
|
for s in node.services:
|
||||||
|
@ -113,7 +117,7 @@ class BirdService(CoreService):
|
||||||
cfg = ""
|
cfg = ""
|
||||||
|
|
||||||
for ifc in node.netifs():
|
for ifc in node.netifs():
|
||||||
if hasattr(ifc, 'control') and ifc.control is True:
|
if hasattr(ifc, "control") and ifc.control is True:
|
||||||
continue
|
continue
|
||||||
cfg += ' interface "%s";\n' % ifc.name
|
cfg += ' interface "%s";\n' % ifc.name
|
||||||
|
|
||||||
|
@ -160,18 +164,18 @@ class BirdOspf(BirdService):
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def generatebirdconfig(cls, node):
|
def generatebirdconfig(cls, node):
|
||||||
cfg = 'protocol ospf {\n'
|
cfg = "protocol ospf {\n"
|
||||||
cfg += ' export filter {\n'
|
cfg += " export filter {\n"
|
||||||
cfg += ' if source = RTS_BGP then {\n'
|
cfg += " if source = RTS_BGP then {\n"
|
||||||
cfg += ' ospf_metric1 = 100;\n'
|
cfg += " ospf_metric1 = 100;\n"
|
||||||
cfg += ' accept;\n'
|
cfg += " accept;\n"
|
||||||
cfg += ' }\n'
|
cfg += " }\n"
|
||||||
cfg += ' accept;\n'
|
cfg += " accept;\n"
|
||||||
cfg += ' };\n'
|
cfg += " };\n"
|
||||||
cfg += ' area 0.0.0.0 {\n'
|
cfg += " area 0.0.0.0 {\n"
|
||||||
cfg += cls.generatebirdifcconfig(node)
|
cfg += cls.generatebirdifcconfig(node)
|
||||||
cfg += ' };\n'
|
cfg += " };\n"
|
||||||
cfg += '}\n\n'
|
cfg += "}\n\n"
|
||||||
|
|
||||||
return cfg
|
return cfg
|
||||||
|
|
||||||
|
@ -185,21 +189,21 @@ class BirdRadv(BirdService):
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def generatebirdconfig(cls, node):
|
def generatebirdconfig(cls, node):
|
||||||
cfg = '/* This is a sample config that must be customized */\n'
|
cfg = "/* This is a sample config that must be customized */\n"
|
||||||
|
|
||||||
cfg += 'protocol radv {\n'
|
cfg += "protocol radv {\n"
|
||||||
cfg += ' # auto configuration on all interfaces\n'
|
cfg += " # auto configuration on all interfaces\n"
|
||||||
cfg += cls.generatebirdifcconfig(node)
|
cfg += cls.generatebirdifcconfig(node)
|
||||||
cfg += ' # Advertise DNS\n'
|
cfg += " # Advertise DNS\n"
|
||||||
cfg += ' rdnss {\n'
|
cfg += " rdnss {\n"
|
||||||
cfg += '# lifetime mult 10;\n'
|
cfg += "# lifetime mult 10;\n"
|
||||||
cfg += '# lifetime mult 10;\n'
|
cfg += "# lifetime mult 10;\n"
|
||||||
cfg += '# ns 2001:0DB8:1234::11;\n'
|
cfg += "# ns 2001:0DB8:1234::11;\n"
|
||||||
cfg += '# ns 2001:0DB8:1234::11;\n'
|
cfg += "# ns 2001:0DB8:1234::11;\n"
|
||||||
cfg += '# ns 2001:0DB8:1234::12;\n'
|
cfg += "# ns 2001:0DB8:1234::12;\n"
|
||||||
cfg += '# ns 2001:0DB8:1234::12;\n'
|
cfg += "# ns 2001:0DB8:1234::12;\n"
|
||||||
cfg += ' };\n'
|
cfg += " };\n"
|
||||||
cfg += '}\n\n'
|
cfg += "}\n\n"
|
||||||
|
|
||||||
return cfg
|
return cfg
|
||||||
|
|
||||||
|
@ -213,15 +217,15 @@ class BirdRip(BirdService):
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def generatebirdconfig(cls, node):
|
def generatebirdconfig(cls, node):
|
||||||
cfg = 'protocol rip {\n'
|
cfg = "protocol rip {\n"
|
||||||
cfg += ' period 10;\n'
|
cfg += " period 10;\n"
|
||||||
cfg += ' garbage time 60;\n'
|
cfg += " garbage time 60;\n"
|
||||||
cfg += cls.generatebirdifcconfig(node)
|
cfg += cls.generatebirdifcconfig(node)
|
||||||
cfg += ' honor neighbor;\n'
|
cfg += " honor neighbor;\n"
|
||||||
cfg += ' authentication none;\n'
|
cfg += " authentication none;\n"
|
||||||
cfg += ' import all;\n'
|
cfg += " import all;\n"
|
||||||
cfg += ' export all;\n'
|
cfg += " export all;\n"
|
||||||
cfg += '}\n\n'
|
cfg += "}\n\n"
|
||||||
|
|
||||||
return cfg
|
return cfg
|
||||||
|
|
||||||
|
@ -236,10 +240,10 @@ class BirdStatic(BirdService):
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def generatebirdconfig(cls, node):
|
def generatebirdconfig(cls, node):
|
||||||
cfg = '/* This is a sample config that must be customized */\n'
|
cfg = "/* This is a sample config that must be customized */\n"
|
||||||
cfg += 'protocol static {\n'
|
cfg += "protocol static {\n"
|
||||||
cfg += '# route 0.0.0.0/0 via 198.51.100.130; # Default route. Do NOT advertise on BGP !\n'
|
cfg += "# route 0.0.0.0/0 via 198.51.100.130; # Default route. Do NOT advertise on BGP !\n"
|
||||||
cfg += '# route 203.0.113.0/24 reject; # Sink route\n'
|
cfg += "# route 203.0.113.0/24 reject; # Sink route\n"
|
||||||
cfg += '# route 10.2.0.0/24 via "arc0"; # Secondary network\n'
|
cfg += '# route 10.2.0.0/24 via "arc0"; # Secondary network\n'
|
||||||
cfg += '}\n\n'
|
cfg += "}\n\n"
|
||||||
return cfg
|
return cfg
|
||||||
|
|
|
@ -15,8 +15,7 @@ from multiprocessing.pool import ThreadPool
|
||||||
from core import CoreCommandError, utils
|
from core import CoreCommandError, utils
|
||||||
from core.constants import which
|
from core.constants import which
|
||||||
from core.emulator.data import FileData
|
from core.emulator.data import FileData
|
||||||
from core.emulator.enumerations import MessageFlags
|
from core.emulator.enumerations import MessageFlags, RegisterTlvs
|
||||||
from core.emulator.enumerations import RegisterTlvs
|
|
||||||
|
|
||||||
|
|
||||||
class ServiceBootError(Exception):
|
class ServiceBootError(Exception):
|
||||||
|
@ -62,7 +61,9 @@ class ServiceDependencies(object):
|
||||||
for name in self.node_services:
|
for name in self.node_services:
|
||||||
service = self.node_services[name]
|
service = self.node_services[name]
|
||||||
if service.name in self.booted:
|
if service.name in self.booted:
|
||||||
logging.debug("skipping service that will already be booted: %s", service.name)
|
logging.debug(
|
||||||
|
"skipping service that will already be booted: %s", service.name
|
||||||
|
)
|
||||||
continue
|
continue
|
||||||
|
|
||||||
path = self._start(service)
|
path = self._start(service)
|
||||||
|
@ -70,7 +71,10 @@ class ServiceDependencies(object):
|
||||||
paths.append(path)
|
paths.append(path)
|
||||||
|
|
||||||
if self.booted != set(self.node_services):
|
if self.booted != set(self.node_services):
|
||||||
raise ValueError("failure to boot all services: %s != %s" % (self.booted, self.node_services.keys()))
|
raise ValueError(
|
||||||
|
"failure to boot all services: %s != %s"
|
||||||
|
% (self.booted, self.node_services.keys())
|
||||||
|
)
|
||||||
|
|
||||||
return paths
|
return paths
|
||||||
|
|
||||||
|
@ -92,10 +96,16 @@ class ServiceDependencies(object):
|
||||||
# dive down
|
# dive down
|
||||||
for service_name in current_service.dependencies:
|
for service_name in current_service.dependencies:
|
||||||
if service_name not in self.node_services:
|
if service_name not in self.node_services:
|
||||||
raise ValueError("required dependency was not included in node services: %s" % service_name)
|
raise ValueError(
|
||||||
|
"required dependency was not included in node services: %s"
|
||||||
|
% service_name
|
||||||
|
)
|
||||||
|
|
||||||
if service_name in self.visiting:
|
if service_name in self.visiting:
|
||||||
raise ValueError("cyclic dependency at service(%s): %s" % (current_service.name, service_name))
|
raise ValueError(
|
||||||
|
"cyclic dependency at service(%s): %s"
|
||||||
|
% (current_service.name, service_name)
|
||||||
|
)
|
||||||
|
|
||||||
if service_name not in self.visited:
|
if service_name not in self.visited:
|
||||||
service = self.node_services[service_name]
|
service = self.node_services[service_name]
|
||||||
|
@ -117,7 +127,16 @@ class ServiceDependencies(object):
|
||||||
|
|
||||||
|
|
||||||
class ServiceShim(object):
|
class ServiceShim(object):
|
||||||
keys = ["dirs", "files", "startidx", "cmdup", "cmddown", "cmdval", "meta", "starttime"]
|
keys = [
|
||||||
|
"dirs",
|
||||||
|
"files",
|
||||||
|
"startidx",
|
||||||
|
"cmdup",
|
||||||
|
"cmddown",
|
||||||
|
"cmdval",
|
||||||
|
"meta",
|
||||||
|
"starttime",
|
||||||
|
]
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def tovaluelist(cls, node, service):
|
def tovaluelist(cls, node, service):
|
||||||
|
@ -132,8 +151,16 @@ class ServiceShim(object):
|
||||||
"""
|
"""
|
||||||
start_time = 0
|
start_time = 0
|
||||||
start_index = 0
|
start_index = 0
|
||||||
valmap = [service.dirs, service.configs, start_index, service.startup,
|
valmap = [
|
||||||
service.shutdown, service.validate, service.meta, start_time]
|
service.dirs,
|
||||||
|
service.configs,
|
||||||
|
start_index,
|
||||||
|
service.startup,
|
||||||
|
service.shutdown,
|
||||||
|
service.validate,
|
||||||
|
service.meta,
|
||||||
|
start_time,
|
||||||
|
]
|
||||||
if not service.custom:
|
if not service.custom:
|
||||||
valmap[1] = service.get_configs(node)
|
valmap[1] = service.get_configs(node)
|
||||||
valmap[3] = service.get_startup(node)
|
valmap[3] = service.get_startup(node)
|
||||||
|
@ -201,16 +228,17 @@ class ServiceShim(object):
|
||||||
:return: services
|
:return: services
|
||||||
:rtype: list
|
:rtype: list
|
||||||
"""
|
"""
|
||||||
servicesstring = opaque.split(':')
|
servicesstring = opaque.split(":")
|
||||||
if servicesstring[0] != "service":
|
if servicesstring[0] != "service":
|
||||||
return []
|
return []
|
||||||
return servicesstring[1].split(',')
|
return servicesstring[1].split(",")
|
||||||
|
|
||||||
|
|
||||||
class ServiceManager(object):
|
class ServiceManager(object):
|
||||||
"""
|
"""
|
||||||
Manages services available for CORE nodes to use.
|
Manages services available for CORE nodes to use.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
services = {}
|
services = {}
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
|
@ -231,8 +259,12 @@ class ServiceManager(object):
|
||||||
# validate dependent executables are present
|
# validate dependent executables are present
|
||||||
for executable in service.executables:
|
for executable in service.executables:
|
||||||
if not which(executable):
|
if not which(executable):
|
||||||
logging.debug("service(%s) missing executable: %s", service.name, executable)
|
logging.debug(
|
||||||
raise ValueError("service(%s) missing executable: %s" % (service.name, executable))
|
"service(%s) missing executable: %s", service.name, executable
|
||||||
|
)
|
||||||
|
raise ValueError(
|
||||||
|
"service(%s) missing executable: %s" % (service.name, executable)
|
||||||
|
)
|
||||||
|
|
||||||
# make service available
|
# make service available
|
||||||
cls.services[name] = service
|
cls.services[name] = service
|
||||||
|
@ -280,6 +312,7 @@ class CoreServices(object):
|
||||||
the default services configured for each node type, and any
|
the default services configured for each node type, and any
|
||||||
custom service configuration. A CoreService is not a Configurable.
|
custom service configuration. A CoreService is not a Configurable.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
name = "services"
|
name = "services"
|
||||||
config_type = RegisterTlvs.UTILITY.value
|
config_type = RegisterTlvs.UTILITY.value
|
||||||
|
|
||||||
|
@ -368,17 +401,20 @@ class CoreServices(object):
|
||||||
:return: nothing
|
:return: nothing
|
||||||
"""
|
"""
|
||||||
if not services:
|
if not services:
|
||||||
logging.info("using default services for node(%s) type(%s)", node.name, node_type)
|
logging.info(
|
||||||
|
"using default services for node(%s) type(%s)", node.name, node_type
|
||||||
|
)
|
||||||
services = self.default_services.get(node_type, [])
|
services = self.default_services.get(node_type, [])
|
||||||
|
|
||||||
logging.info("setting services for node(%s): %s", node.name, services)
|
logging.info("setting services for node(%s): %s", node.name, services)
|
||||||
for service_name in services:
|
for service_name in services:
|
||||||
service = self.get_service(node.id, service_name, default_service=True)
|
service = self.get_service(node.id, service_name, default_service=True)
|
||||||
if not service:
|
if not service:
|
||||||
logging.warning("unknown service(%s) for node(%s)", service_name, node.name)
|
logging.warning(
|
||||||
|
"unknown service(%s) for node(%s)", service_name, node.name
|
||||||
|
)
|
||||||
continue
|
continue
|
||||||
logging.info("adding service to node(%s): %s", node.name, service_name)
|
node.services.append(service)
|
||||||
node.addservice(service)
|
|
||||||
|
|
||||||
def all_configs(self):
|
def all_configs(self):
|
||||||
"""
|
"""
|
||||||
|
@ -445,11 +481,15 @@ class CoreServices(object):
|
||||||
:param list[CoreService] boot_path: service to start in dependent order
|
:param list[CoreService] boot_path: service to start in dependent order
|
||||||
:return: nothing
|
:return: nothing
|
||||||
"""
|
"""
|
||||||
logging.info("booting node services: %s", " -> ".join([x.name for x in boot_path]))
|
logging.info(
|
||||||
|
"booting node(%s) services: %s",
|
||||||
|
node.name,
|
||||||
|
" -> ".join([x.name for x in boot_path]),
|
||||||
|
)
|
||||||
for service in boot_path:
|
for service in boot_path:
|
||||||
try:
|
try:
|
||||||
self.boot_service(node, service)
|
self.boot_service(node, service)
|
||||||
except:
|
except Exception:
|
||||||
logging.exception("exception booting service: %s", service.name)
|
logging.exception("exception booting service: %s", service.name)
|
||||||
raise
|
raise
|
||||||
|
|
||||||
|
@ -462,16 +502,24 @@ class CoreServices(object):
|
||||||
:param CoreService service: service to start
|
:param CoreService service: service to start
|
||||||
:return: nothing
|
:return: nothing
|
||||||
"""
|
"""
|
||||||
logging.info("starting node(%s) service(%s) validation(%s)", node.name, service.name,
|
logging.info(
|
||||||
service.validation_mode.name)
|
"starting node(%s) service(%s) validation(%s)",
|
||||||
|
node.name,
|
||||||
|
service.name,
|
||||||
|
service.validation_mode.name,
|
||||||
|
)
|
||||||
|
|
||||||
# create service directories
|
# create service directories
|
||||||
for directory in service.dirs:
|
for directory in service.dirs:
|
||||||
try:
|
try:
|
||||||
node.privatedir(directory)
|
node.privatedir(directory)
|
||||||
except (CoreCommandError, ValueError) as e:
|
except (CoreCommandError, ValueError) as e:
|
||||||
logging.warning("error mounting private dir '%s' for service '%s': %s",
|
logging.warning(
|
||||||
directory, service.name, e)
|
"error mounting private dir '%s' for service '%s': %s",
|
||||||
|
directory,
|
||||||
|
service.name,
|
||||||
|
e,
|
||||||
|
)
|
||||||
|
|
||||||
# create service files
|
# create service files
|
||||||
self.create_service_files(node, service)
|
self.create_service_files(node, service)
|
||||||
|
@ -480,7 +528,9 @@ class CoreServices(object):
|
||||||
wait = service.validation_mode == ServiceMode.BLOCKING
|
wait = service.validation_mode == ServiceMode.BLOCKING
|
||||||
status = self.startup_service(node, service, wait)
|
status = self.startup_service(node, service, wait)
|
||||||
if status:
|
if status:
|
||||||
raise ServiceBootError("node(%s) service(%s) error during startup" % (node.name, service.name))
|
raise ServiceBootError(
|
||||||
|
"node(%s) service(%s) error during startup" % (node.name, service.name)
|
||||||
|
)
|
||||||
|
|
||||||
# blocking mode is finished
|
# blocking mode is finished
|
||||||
if wait:
|
if wait:
|
||||||
|
@ -503,7 +553,9 @@ class CoreServices(object):
|
||||||
time.sleep(service.validation_period)
|
time.sleep(service.validation_period)
|
||||||
|
|
||||||
if status:
|
if status:
|
||||||
raise ServiceBootError("node(%s) service(%s) failed validation" % (node.name, service.name))
|
raise ServiceBootError(
|
||||||
|
"node(%s) service(%s) failed validation" % (node.name, service.name)
|
||||||
|
)
|
||||||
|
|
||||||
def copy_service_file(self, node, filename, cfg):
|
def copy_service_file(self, node, filename, cfg):
|
||||||
"""
|
"""
|
||||||
|
@ -517,9 +569,9 @@ class CoreServices(object):
|
||||||
:return: True if successful, False otherwise
|
:return: True if successful, False otherwise
|
||||||
:rtype: bool
|
:rtype: bool
|
||||||
"""
|
"""
|
||||||
if cfg[:7] == 'file://':
|
if cfg[:7] == "file://":
|
||||||
src = cfg[7:]
|
src = cfg[7:]
|
||||||
src = src.split('\n')[0]
|
src = src.split("\n")[0]
|
||||||
src = utils.expand_corepath(src, node.session, node)
|
src = utils.expand_corepath(src, node.session, node)
|
||||||
# TODO: glob here
|
# TODO: glob here
|
||||||
node.nodefilecopy(filename, src, mode=0o644)
|
node.nodefilecopy(filename, src, mode=0o644)
|
||||||
|
@ -535,7 +587,7 @@ class CoreServices(object):
|
||||||
:return: service validation status
|
:return: service validation status
|
||||||
:rtype: int
|
:rtype: int
|
||||||
"""
|
"""
|
||||||
logging.info("validating node(%s) service(%s)", node.name, service.name)
|
logging.debug("validating node(%s) service(%s)", node.name, service.name)
|
||||||
cmds = service.validate
|
cmds = service.validate
|
||||||
if not service.custom:
|
if not service.custom:
|
||||||
cmds = service.get_validate(node)
|
cmds = service.get_validate(node)
|
||||||
|
@ -546,8 +598,10 @@ class CoreServices(object):
|
||||||
try:
|
try:
|
||||||
node.check_cmd(cmd)
|
node.check_cmd(cmd)
|
||||||
except CoreCommandError as e:
|
except CoreCommandError as e:
|
||||||
logging.error("node(%s) service(%s) validate failed", node.name, service.name)
|
logging.debug(
|
||||||
logging.error("cmd(%s): %s", e.cmd, e.output)
|
"node(%s) service(%s) validate failed", node.name, service.name
|
||||||
|
)
|
||||||
|
logging.debug("cmd(%s): %s", e.cmd, e.output)
|
||||||
status = -1
|
status = -1
|
||||||
break
|
break
|
||||||
|
|
||||||
|
@ -603,7 +657,9 @@ class CoreServices(object):
|
||||||
config_files = service.get_configs(node)
|
config_files = service.get_configs(node)
|
||||||
|
|
||||||
if filename not in config_files:
|
if filename not in config_files:
|
||||||
raise ValueError("unknown service(%s) config file: %s", service_name, filename)
|
raise ValueError(
|
||||||
|
"unknown service(%s) config file: %s", service_name, filename
|
||||||
|
)
|
||||||
|
|
||||||
# get the file data
|
# get the file data
|
||||||
data = service.config_data.get(filename)
|
data = service.config_data.get(filename)
|
||||||
|
@ -618,7 +674,7 @@ class CoreServices(object):
|
||||||
node=node.id,
|
node=node.id,
|
||||||
name=filename,
|
name=filename,
|
||||||
type=filetypestr,
|
type=filetypestr,
|
||||||
data=data
|
data=data,
|
||||||
)
|
)
|
||||||
|
|
||||||
def set_service_file(self, node_id, service_name, file_name, data):
|
def set_service_file(self, node_id, service_name, file_name, data):
|
||||||
|
@ -645,7 +701,9 @@ class CoreServices(object):
|
||||||
# validate file being set is valid
|
# validate file being set is valid
|
||||||
config_files = service.configs
|
config_files = service.configs
|
||||||
if file_name not in config_files:
|
if file_name not in config_files:
|
||||||
logging.warning("received unknown file(%s) for service(%s)", file_name, service_name)
|
logging.warning(
|
||||||
|
"received unknown file(%s) for service(%s)", file_name, service_name
|
||||||
|
)
|
||||||
return
|
return
|
||||||
|
|
||||||
# set custom service file data
|
# set custom service file data
|
||||||
|
@ -686,7 +744,6 @@ class CoreServices(object):
|
||||||
:param CoreService service: service to reconfigure
|
:param CoreService service: service to reconfigure
|
||||||
:return: nothing
|
:return: nothing
|
||||||
"""
|
"""
|
||||||
logging.info("node(%s) service(%s) creating config files", node.name, service.name)
|
|
||||||
# get values depending on if custom or not
|
# get values depending on if custom or not
|
||||||
config_files = service.configs
|
config_files = service.configs
|
||||||
if not service.custom:
|
if not service.custom:
|
||||||
|
@ -739,6 +796,7 @@ class CoreService(object):
|
||||||
"""
|
"""
|
||||||
Parent class used for defining services.
|
Parent class used for defining services.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
# service name should not include spaces
|
# service name should not include spaces
|
||||||
name = None
|
name = None
|
||||||
|
|
||||||
|
|
|
@ -99,8 +99,7 @@ Limitations:
|
||||||
|
|
||||||
import logging
|
import logging
|
||||||
|
|
||||||
from core.services.coreservices import CoreService
|
from core.services.coreservices import CoreService, ServiceManager
|
||||||
from core.services.coreservices import ServiceManager
|
|
||||||
|
|
||||||
try:
|
try:
|
||||||
from docker import Client
|
from docker import Client
|
||||||
|
|
|
@ -23,15 +23,23 @@ class EmaneTransportService(CoreService):
|
||||||
for interface in node.netifs(sort=True):
|
for interface in node.netifs(sort=True):
|
||||||
network_node = node.session.get_node(interface.net.id)
|
network_node = node.session.get_node(interface.net.id)
|
||||||
if nodeutils.is_node(network_node, NodeTypes.EMANE):
|
if nodeutils.is_node(network_node, NodeTypes.EMANE):
|
||||||
config = node.session.emane.get_configs(network_node.id, network_node.model.name)
|
config = node.session.emane.get_configs(
|
||||||
|
network_node.id, network_node.model.name
|
||||||
|
)
|
||||||
if config and emanexml.is_external(config):
|
if config and emanexml.is_external(config):
|
||||||
nem_id = network_node.getnemid(interface)
|
nem_id = network_node.getnemid(interface)
|
||||||
command = "emanetransportd -r -l 0 -d ../transportdaemon%s.xml" % nem_id
|
command = (
|
||||||
|
"emanetransportd -r -l 0 -d ../transportdaemon%s.xml"
|
||||||
|
% nem_id
|
||||||
|
)
|
||||||
transport_commands.append(command)
|
transport_commands.append(command)
|
||||||
transport_commands = "\n".join(transport_commands)
|
transport_commands = "\n".join(transport_commands)
|
||||||
return """
|
return """
|
||||||
emanegentransportxml -o ../ ../platform%s.xml
|
emanegentransportxml -o ../ ../platform%s.xml
|
||||||
%s
|
%s
|
||||||
""" % (node.id, transport_commands)
|
""" % (
|
||||||
|
node.id,
|
||||||
|
transport_commands,
|
||||||
|
)
|
||||||
else:
|
else:
|
||||||
raise ValueError
|
raise ValueError
|
||||||
|
|
|
@ -5,18 +5,14 @@ Assumes installation of FRR via https://deb.frrouting.org/
|
||||||
|
|
||||||
from core import constants
|
from core import constants
|
||||||
from core.emulator.enumerations import LinkTypes, NodeTypes
|
from core.emulator.enumerations import LinkTypes, NodeTypes
|
||||||
from core.nodes import nodeutils, ipaddress
|
from core.nodes import ipaddress, nodeutils
|
||||||
from core.services.coreservices import CoreService
|
from core.services.coreservices import CoreService
|
||||||
|
|
||||||
|
|
||||||
class FRRZebra(CoreService):
|
class FRRZebra(CoreService):
|
||||||
name = "FRRzebra"
|
name = "FRRzebra"
|
||||||
group = "FRR"
|
group = "FRR"
|
||||||
dirs = (
|
dirs = ("/usr/local/etc/frr", "/var/run/frr", "/var/log/frr")
|
||||||
"/usr/local/etc/frr",
|
|
||||||
"/var/run/frr",
|
|
||||||
"/var/log/frr",
|
|
||||||
)
|
|
||||||
configs = (
|
configs = (
|
||||||
"/usr/local/etc/frr/frr.conf",
|
"/usr/local/etc/frr/frr.conf",
|
||||||
"frrboot.sh",
|
"frrboot.sh",
|
||||||
|
@ -41,7 +37,9 @@ class FRRZebra(CoreService):
|
||||||
elif filename == cls.configs[3]:
|
elif filename == cls.configs[3]:
|
||||||
return cls.generateFrrDaemons(node)
|
return cls.generateFrrDaemons(node)
|
||||||
else:
|
else:
|
||||||
raise ValueError("file name (%s) is not a known configuration: %s", filename, cls.configs)
|
raise ValueError(
|
||||||
|
"file name (%s) is not a known configuration: %s", filename, cls.configs
|
||||||
|
)
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def generateVtyshConf(cls, node):
|
def generateVtyshConf(cls, node):
|
||||||
|
@ -62,7 +60,7 @@ class FRRZebra(CoreService):
|
||||||
for ifc in node.netifs():
|
for ifc in node.netifs():
|
||||||
cfg += "interface %s\n" % ifc.name
|
cfg += "interface %s\n" % ifc.name
|
||||||
# include control interfaces in addressing but not routing daemons
|
# include control interfaces in addressing but not routing daemons
|
||||||
if hasattr(ifc, 'control') and ifc.control is True:
|
if hasattr(ifc, "control") and ifc.control is True:
|
||||||
cfg += " "
|
cfg += " "
|
||||||
cfg += "\n ".join(map(cls.addrstr, ifc.addrlist))
|
cfg += "\n ".join(map(cls.addrstr, ifc.addrlist))
|
||||||
cfg += "\n"
|
cfg += "\n"
|
||||||
|
@ -84,13 +82,17 @@ class FRRZebra(CoreService):
|
||||||
cfgv4 += ifccfg
|
cfgv4 += ifccfg
|
||||||
|
|
||||||
if want_ipv4:
|
if want_ipv4:
|
||||||
ipv4list = filter(lambda x: ipaddress.is_ipv4_address(x.split('/')[0]), ifc.addrlist)
|
ipv4list = filter(
|
||||||
|
lambda x: ipaddress.is_ipv4_address(x.split("/")[0]), ifc.addrlist
|
||||||
|
)
|
||||||
cfg += " "
|
cfg += " "
|
||||||
cfg += "\n ".join(map(cls.addrstr, ipv4list))
|
cfg += "\n ".join(map(cls.addrstr, ipv4list))
|
||||||
cfg += "\n"
|
cfg += "\n"
|
||||||
cfg += cfgv4
|
cfg += cfgv4
|
||||||
if want_ipv6:
|
if want_ipv6:
|
||||||
ipv6list = filter(lambda x: ipaddress.is_ipv6_address(x.split('/')[0]), ifc.addrlist)
|
ipv6list = filter(
|
||||||
|
lambda x: ipaddress.is_ipv6_address(x.split("/")[0]), ifc.addrlist
|
||||||
|
)
|
||||||
cfg += " "
|
cfg += " "
|
||||||
cfg += "\n ".join(map(cls.addrstr, ipv6list))
|
cfg += "\n ".join(map(cls.addrstr, ipv6list))
|
||||||
cfg += "\n"
|
cfg += "\n"
|
||||||
|
@ -120,10 +122,12 @@ class FRRZebra(CoreService):
|
||||||
"""
|
"""
|
||||||
Generate a shell script used to boot the FRR daemons.
|
Generate a shell script used to boot the FRR daemons.
|
||||||
"""
|
"""
|
||||||
frr_bin_search = node.session.options.get_config("frr_bin_search",
|
frr_bin_search = node.session.options.get_config(
|
||||||
default='"/usr/local/bin /usr/bin /usr/lib/frr"')
|
"frr_bin_search", default='"/usr/local/bin /usr/bin /usr/lib/frr"'
|
||||||
frr_sbin_search = node.session.options.get_config('frr_sbin_search',
|
)
|
||||||
default='"/usr/local/sbin /usr/sbin /usr/lib/frr"')
|
frr_sbin_search = node.session.options.get_config(
|
||||||
|
"frr_sbin_search", default='"/usr/local/sbin /usr/sbin /usr/lib/frr"'
|
||||||
|
)
|
||||||
return """\
|
return """\
|
||||||
#!/bin/sh
|
#!/bin/sh
|
||||||
# auto-generated by zebra service (frr.py)
|
# auto-generated by zebra service (frr.py)
|
||||||
|
@ -175,9 +179,8 @@ bootdaemon()
|
||||||
flags="$flags -6"
|
flags="$flags -6"
|
||||||
fi
|
fi
|
||||||
|
|
||||||
|
|
||||||
#force FRR to use CORE generated conf file
|
#force FRR to use CORE generated conf file
|
||||||
flags="$flags -d -f $FRR_CONF"
|
flags="$flags -d -f $FRR_CONF"
|
||||||
$FRR_SBIN_DIR/$1 $flags
|
$FRR_SBIN_DIR/$1 $flags
|
||||||
|
|
||||||
if [ "$?" != "0" ]; then
|
if [ "$?" != "0" ]; then
|
||||||
|
@ -203,7 +206,7 @@ bootfrr()
|
||||||
|
|
||||||
bootdaemon "zebra"
|
bootdaemon "zebra"
|
||||||
for r in rip ripng ospf6 ospf bgp babel; do
|
for r in rip ripng ospf6 ospf bgp babel; do
|
||||||
if grep -q "^router \<${r}\>" $FRR_CONF; then
|
if grep -q "^router \\<${r}\\>" $FRR_CONF; then
|
||||||
bootdaemon "${r}d"
|
bootdaemon "${r}d"
|
||||||
fi
|
fi
|
||||||
done
|
done
|
||||||
|
@ -221,7 +224,12 @@ if [ "$1" != "zebra" ]; then
|
||||||
fi
|
fi
|
||||||
confcheck
|
confcheck
|
||||||
bootfrr
|
bootfrr
|
||||||
""" % (cls.configs[0], frr_sbin_search, frr_bin_search, constants.FRR_STATE_DIR)
|
""" % (
|
||||||
|
cls.configs[0],
|
||||||
|
frr_sbin_search,
|
||||||
|
frr_bin_search,
|
||||||
|
constants.FRR_STATE_DIR,
|
||||||
|
)
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def generateFrrDaemons(cls, node):
|
def generateFrrDaemons(cls, node):
|
||||||
|
@ -291,12 +299,12 @@ fabricd_options="-A 127.0.0.1"
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
class FrrService(CoreService):
|
class FrrService(CoreService):
|
||||||
"""
|
"""
|
||||||
Parent class for FRR services. Defines properties and methods
|
Parent class for FRR services. Defines properties and methods
|
||||||
common to FRR's routing daemons.
|
common to FRR's routing daemons.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
name = None
|
name = None
|
||||||
group = "FRR"
|
group = "FRR"
|
||||||
dependencies = ("FRRzebra",)
|
dependencies = ("FRRzebra",)
|
||||||
|
@ -315,11 +323,11 @@ class FrrService(CoreService):
|
||||||
Helper to return the first IPv4 address of a node as its router ID.
|
Helper to return the first IPv4 address of a node as its router ID.
|
||||||
"""
|
"""
|
||||||
for ifc in node.netifs():
|
for ifc in node.netifs():
|
||||||
if hasattr(ifc, 'control') and ifc.control is True:
|
if hasattr(ifc, "control") and ifc.control is True:
|
||||||
continue
|
continue
|
||||||
for a in ifc.addrlist:
|
for a in ifc.addrlist:
|
||||||
if a.find(".") >= 0:
|
if a.find(".") >= 0:
|
||||||
return a.split('/')[0]
|
return a.split("/")[0]
|
||||||
# raise ValueError, "no IPv4 address found for router ID"
|
# raise ValueError, "no IPv4 address found for router ID"
|
||||||
return "0.0.0.0"
|
return "0.0.0.0"
|
||||||
|
|
||||||
|
@ -356,6 +364,7 @@ class FRROspfv2(FrrService):
|
||||||
not build its own configuration file but has hooks for adding to the
|
not build its own configuration file but has hooks for adding to the
|
||||||
unified frr.conf file.
|
unified frr.conf file.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
name = "FRROSPFv2"
|
name = "FRROSPFv2"
|
||||||
startup = ()
|
startup = ()
|
||||||
shutdown = ("killall ospfd",)
|
shutdown = ("killall ospfd",)
|
||||||
|
@ -397,7 +406,7 @@ class FRROspfv2(FrrService):
|
||||||
cfg += " router-id %s\n" % rtrid
|
cfg += " router-id %s\n" % rtrid
|
||||||
# network 10.0.0.0/24 area 0
|
# network 10.0.0.0/24 area 0
|
||||||
for ifc in node.netifs():
|
for ifc in node.netifs():
|
||||||
if hasattr(ifc, 'control') and ifc.control is True:
|
if hasattr(ifc, "control") and ifc.control is True:
|
||||||
continue
|
continue
|
||||||
for a in ifc.addrlist:
|
for a in ifc.addrlist:
|
||||||
if a.find(".") < 0:
|
if a.find(".") < 0:
|
||||||
|
@ -431,6 +440,7 @@ class FRROspfv3(FrrService):
|
||||||
not build its own configuration file but has hooks for adding to the
|
not build its own configuration file but has hooks for adding to the
|
||||||
unified frr.conf file.
|
unified frr.conf file.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
name = "FRROSPFv3"
|
name = "FRROSPFv3"
|
||||||
startup = ()
|
startup = ()
|
||||||
shutdown = ("killall ospf6d",)
|
shutdown = ("killall ospf6d",)
|
||||||
|
@ -481,7 +491,7 @@ class FRROspfv3(FrrService):
|
||||||
rtrid = cls.routerid(node)
|
rtrid = cls.routerid(node)
|
||||||
cfg += " router-id %s\n" % rtrid
|
cfg += " router-id %s\n" % rtrid
|
||||||
for ifc in node.netifs():
|
for ifc in node.netifs():
|
||||||
if hasattr(ifc, 'control') and ifc.control is True:
|
if hasattr(ifc, "control") and ifc.control is True:
|
||||||
continue
|
continue
|
||||||
cfg += " interface %s area 0.0.0.0\n" % ifc.name
|
cfg += " interface %s area 0.0.0.0\n" % ifc.name
|
||||||
cfg += "!\n"
|
cfg += "!\n"
|
||||||
|
@ -511,6 +521,7 @@ class FRRBgp(FrrService):
|
||||||
Peers must be manually configured, with a full mesh for those
|
Peers must be manually configured, with a full mesh for those
|
||||||
having the same AS number.
|
having the same AS number.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
name = "FRRBGP"
|
name = "FRRBGP"
|
||||||
startup = ()
|
startup = ()
|
||||||
shutdown = ("killall bgpd",)
|
shutdown = ("killall bgpd",)
|
||||||
|
@ -536,6 +547,7 @@ class FRRRip(FrrService):
|
||||||
"""
|
"""
|
||||||
The RIP service provides IPv4 routing for wired networks.
|
The RIP service provides IPv4 routing for wired networks.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
name = "FRRRIP"
|
name = "FRRRIP"
|
||||||
startup = ()
|
startup = ()
|
||||||
shutdown = ("killall ripd",)
|
shutdown = ("killall ripd",)
|
||||||
|
@ -559,6 +571,7 @@ class FRRRipng(FrrService):
|
||||||
"""
|
"""
|
||||||
The RIP NG service provides IPv6 routing for wired networks.
|
The RIP NG service provides IPv6 routing for wired networks.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
name = "FRRRIPNG"
|
name = "FRRRIPNG"
|
||||||
startup = ()
|
startup = ()
|
||||||
shutdown = ("killall ripngd",)
|
shutdown = ("killall ripngd",)
|
||||||
|
@ -583,6 +596,7 @@ class FRRBabel(FrrService):
|
||||||
The Babel service provides a loop-avoiding distance-vector routing
|
The Babel service provides a loop-avoiding distance-vector routing
|
||||||
protocol for IPv6 and IPv4 with fast convergence properties.
|
protocol for IPv6 and IPv4 with fast convergence properties.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
name = "FRRBabel"
|
name = "FRRBabel"
|
||||||
startup = ()
|
startup = ()
|
||||||
shutdown = ("killall babeld",)
|
shutdown = ("killall babeld",)
|
||||||
|
@ -611,28 +625,29 @@ class FRRpimd(FrrService):
|
||||||
"""
|
"""
|
||||||
PIM multicast routing based on XORP.
|
PIM multicast routing based on XORP.
|
||||||
"""
|
"""
|
||||||
name = 'FRRpimd'
|
|
||||||
|
name = "FRRpimd"
|
||||||
startup = ()
|
startup = ()
|
||||||
shutdown = ('killall pimd',)
|
shutdown = ("killall pimd",)
|
||||||
validate = ('pidof pimd',)
|
validate = ("pidof pimd",)
|
||||||
ipv4_routing = True
|
ipv4_routing = True
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def generatefrrconfig(cls, node):
|
def generatefrrconfig(cls, node):
|
||||||
ifname = 'eth0'
|
ifname = "eth0"
|
||||||
for ifc in node.netifs():
|
for ifc in node.netifs():
|
||||||
if ifc.name != 'lo':
|
if ifc.name != "lo":
|
||||||
ifname = ifc.name
|
ifname = ifc.name
|
||||||
break
|
break
|
||||||
cfg = 'router mfea\n!\n'
|
cfg = "router mfea\n!\n"
|
||||||
cfg += 'router igmp\n!\n'
|
cfg += "router igmp\n!\n"
|
||||||
cfg += 'router pim\n'
|
cfg += "router pim\n"
|
||||||
cfg += ' !ip pim rp-address 10.0.0.1\n'
|
cfg += " !ip pim rp-address 10.0.0.1\n"
|
||||||
cfg += ' ip pim bsr-candidate %s\n' % ifname
|
cfg += " ip pim bsr-candidate %s\n" % ifname
|
||||||
cfg += ' ip pim rp-candidate %s\n' % ifname
|
cfg += " ip pim rp-candidate %s\n" % ifname
|
||||||
cfg += ' !ip pim spt-threshold interval 10 bytes 80000\n'
|
cfg += " !ip pim spt-threshold interval 10 bytes 80000\n"
|
||||||
return cfg
|
return cfg
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def generatefrrifcconfig(cls, node, ifc):
|
def generatefrrifcconfig(cls, node, ifc):
|
||||||
return ' ip mfea\n ip igmp\n ip pim\n'
|
return " ip mfea\n ip igmp\n ip pim\n"
|
||||||
|
|
|
@ -12,7 +12,8 @@ class NrlService(CoreService):
|
||||||
"""
|
"""
|
||||||
Parent class for NRL services. Defines properties and methods
|
Parent class for NRL services. Defines properties and methods
|
||||||
common to NRL's routing daemons.
|
common to NRL's routing daemons.
|
||||||
"""""
|
""" ""
|
||||||
|
|
||||||
name = None
|
name = None
|
||||||
group = "ProtoSvc"
|
group = "ProtoSvc"
|
||||||
dirs = ()
|
dirs = ()
|
||||||
|
@ -32,11 +33,11 @@ class NrlService(CoreService):
|
||||||
interface's prefix length, so e.g. '/32' can turn into '/24'.
|
interface's prefix length, so e.g. '/32' can turn into '/24'.
|
||||||
"""
|
"""
|
||||||
for ifc in node.netifs():
|
for ifc in node.netifs():
|
||||||
if hasattr(ifc, 'control') and ifc.control is True:
|
if hasattr(ifc, "control") and ifc.control is True:
|
||||||
continue
|
continue
|
||||||
for a in ifc.addrlist:
|
for a in ifc.addrlist:
|
||||||
if a.find(".") >= 0:
|
if a.find(".") >= 0:
|
||||||
addr = a.split('/')[0]
|
addr = a.split("/")[0]
|
||||||
pre = Ipv4Prefix("%s/%s" % (addr, prefixlen))
|
pre = Ipv4Prefix("%s/%s" % (addr, prefixlen))
|
||||||
return str(pre)
|
return str(pre)
|
||||||
# raise ValueError, "no IPv4 address found"
|
# raise ValueError, "no IPv4 address found"
|
||||||
|
@ -63,13 +64,14 @@ class MgenSinkService(NrlService):
|
||||||
def get_startup(cls, node):
|
def get_startup(cls, node):
|
||||||
cmd = cls.startup[0]
|
cmd = cls.startup[0]
|
||||||
cmd += " output /tmp/mgen_%s.log" % node.name
|
cmd += " output /tmp/mgen_%s.log" % node.name
|
||||||
return cmd,
|
return (cmd,)
|
||||||
|
|
||||||
|
|
||||||
class NrlNhdp(NrlService):
|
class NrlNhdp(NrlService):
|
||||||
"""
|
"""
|
||||||
NeighborHood Discovery Protocol for MANET networks.
|
NeighborHood Discovery Protocol for MANET networks.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
name = "NHDP"
|
name = "NHDP"
|
||||||
executables = ("nrlnhdp",)
|
executables = ("nrlnhdp",)
|
||||||
startup = ("nrlnhdp",)
|
startup = ("nrlnhdp",)
|
||||||
|
@ -90,19 +92,20 @@ class NrlNhdp(NrlService):
|
||||||
cmd += " -flooding ecds"
|
cmd += " -flooding ecds"
|
||||||
cmd += " -smfClient %s_smf" % node.name
|
cmd += " -smfClient %s_smf" % node.name
|
||||||
|
|
||||||
netifs = filter(lambda x: not getattr(x, 'control', False), node.netifs())
|
netifs = filter(lambda x: not getattr(x, "control", False), node.netifs())
|
||||||
if len(netifs) > 0:
|
if len(netifs) > 0:
|
||||||
interfacenames = map(lambda x: x.name, netifs)
|
interfacenames = map(lambda x: x.name, netifs)
|
||||||
cmd += " -i "
|
cmd += " -i "
|
||||||
cmd += " -i ".join(interfacenames)
|
cmd += " -i ".join(interfacenames)
|
||||||
|
|
||||||
return cmd,
|
return (cmd,)
|
||||||
|
|
||||||
|
|
||||||
class NrlSmf(NrlService):
|
class NrlSmf(NrlService):
|
||||||
"""
|
"""
|
||||||
Simplified Multicast Forwarding for MANET networks.
|
Simplified Multicast Forwarding for MANET networks.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
name = "SMF"
|
name = "SMF"
|
||||||
executables = ("nrlsmf",)
|
executables = ("nrlsmf",)
|
||||||
startup = ("sh startsmf.sh",)
|
startup = ("sh startsmf.sh",)
|
||||||
|
@ -111,7 +114,7 @@ class NrlSmf(NrlService):
|
||||||
configs = ("startsmf.sh",)
|
configs = ("startsmf.sh",)
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def generate_config(cls, node, filename, ):
|
def generate_config(cls, node, filename):
|
||||||
"""
|
"""
|
||||||
Generate a startup script for SMF. Because nrlsmf does not
|
Generate a startup script for SMF. Because nrlsmf does not
|
||||||
daemonize, it can cause problems in some situations when launched
|
daemonize, it can cause problems in some situations when launched
|
||||||
|
@ -123,7 +126,7 @@ class NrlSmf(NrlService):
|
||||||
cmd = "nrlsmf instance %s_smf" % node.name
|
cmd = "nrlsmf instance %s_smf" % node.name
|
||||||
|
|
||||||
servicenames = map(lambda x: x.name, node.services)
|
servicenames = map(lambda x: x.name, node.services)
|
||||||
netifs = filter(lambda x: not getattr(x, 'control', False), node.netifs())
|
netifs = filter(lambda x: not getattr(x, "control", False), node.netifs())
|
||||||
if len(netifs) == 0:
|
if len(netifs) == 0:
|
||||||
return ""
|
return ""
|
||||||
|
|
||||||
|
@ -155,6 +158,7 @@ class NrlOlsr(NrlService):
|
||||||
"""
|
"""
|
||||||
Optimized Link State Routing protocol for MANET networks.
|
Optimized Link State Routing protocol for MANET networks.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
name = "OLSR"
|
name = "OLSR"
|
||||||
executables = ("nrlolsrd",)
|
executables = ("nrlolsrd",)
|
||||||
startup = ("nrlolsrd",)
|
startup = ("nrlolsrd",)
|
||||||
|
@ -176,19 +180,20 @@ class NrlOlsr(NrlService):
|
||||||
cmd += " -rpipe %s_olsr" % node.name
|
cmd += " -rpipe %s_olsr" % node.name
|
||||||
|
|
||||||
servicenames = map(lambda x: x.name, node.services)
|
servicenames = map(lambda x: x.name, node.services)
|
||||||
if "SMF" in servicenames and not "NHDP" in servicenames:
|
if "SMF" in servicenames and "NHDP" not in servicenames:
|
||||||
cmd += " -flooding s-mpr"
|
cmd += " -flooding s-mpr"
|
||||||
cmd += " -smfClient %s_smf" % node.name
|
cmd += " -smfClient %s_smf" % node.name
|
||||||
if "zebra" in servicenames:
|
if "zebra" in servicenames:
|
||||||
cmd += " -z"
|
cmd += " -z"
|
||||||
|
|
||||||
return cmd,
|
return (cmd,)
|
||||||
|
|
||||||
|
|
||||||
class NrlOlsrv2(NrlService):
|
class NrlOlsrv2(NrlService):
|
||||||
"""
|
"""
|
||||||
Optimized Link State Routing protocol version 2 for MANET networks.
|
Optimized Link State Routing protocol version 2 for MANET networks.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
name = "OLSRv2"
|
name = "OLSRv2"
|
||||||
executables = ("nrlolsrv2",)
|
executables = ("nrlolsrv2",)
|
||||||
startup = ("nrlolsrv2",)
|
startup = ("nrlolsrv2",)
|
||||||
|
@ -211,19 +216,20 @@ class NrlOlsrv2(NrlService):
|
||||||
|
|
||||||
cmd += " -p olsr"
|
cmd += " -p olsr"
|
||||||
|
|
||||||
netifs = filter(lambda x: not getattr(x, 'control', False), node.netifs())
|
netifs = filter(lambda x: not getattr(x, "control", False), node.netifs())
|
||||||
if len(netifs) > 0:
|
if len(netifs) > 0:
|
||||||
interfacenames = map(lambda x: x.name, netifs)
|
interfacenames = map(lambda x: x.name, netifs)
|
||||||
cmd += " -i "
|
cmd += " -i "
|
||||||
cmd += " -i ".join(interfacenames)
|
cmd += " -i ".join(interfacenames)
|
||||||
|
|
||||||
return cmd,
|
return (cmd,)
|
||||||
|
|
||||||
|
|
||||||
class OlsrOrg(NrlService):
|
class OlsrOrg(NrlService):
|
||||||
"""
|
"""
|
||||||
Optimized Link State Routing protocol from olsr.org for MANET networks.
|
Optimized Link State Routing protocol from olsr.org for MANET networks.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
name = "OLSRORG"
|
name = "OLSRORG"
|
||||||
executables = ("olsrd",)
|
executables = ("olsrd",)
|
||||||
configs = ("/etc/olsrd/olsrd.conf",)
|
configs = ("/etc/olsrd/olsrd.conf",)
|
||||||
|
@ -238,13 +244,13 @@ class OlsrOrg(NrlService):
|
||||||
Generate the appropriate command-line based on node interfaces.
|
Generate the appropriate command-line based on node interfaces.
|
||||||
"""
|
"""
|
||||||
cmd = cls.startup[0]
|
cmd = cls.startup[0]
|
||||||
netifs = filter(lambda x: not getattr(x, 'control', False), node.netifs())
|
netifs = filter(lambda x: not getattr(x, "control", False), node.netifs())
|
||||||
if len(netifs) > 0:
|
if len(netifs) > 0:
|
||||||
interfacenames = map(lambda x: x.name, netifs)
|
interfacenames = map(lambda x: x.name, netifs)
|
||||||
cmd += " -i "
|
cmd += " -i "
|
||||||
cmd += " -i ".join(interfacenames)
|
cmd += " -i ".join(interfacenames)
|
||||||
|
|
||||||
return cmd,
|
return (cmd,)
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def generate_config(cls, node, filename):
|
def generate_config(cls, node, filename):
|
||||||
|
@ -582,7 +588,7 @@ class MgenActor(NrlService):
|
||||||
dirs = ()
|
dirs = ()
|
||||||
# generated files (without a full path this file goes in the node's dir,
|
# generated files (without a full path this file goes in the node's dir,
|
||||||
# e.g. /tmp/pycore.12345/n1.conf/)
|
# e.g. /tmp/pycore.12345/n1.conf/)
|
||||||
configs = ('start_mgen_actor.sh',)
|
configs = ("start_mgen_actor.sh",)
|
||||||
# list of startup commands, also may be generated during startup
|
# list of startup commands, also may be generated during startup
|
||||||
startup = ("sh start_mgen_actor.sh",)
|
startup = ("sh start_mgen_actor.sh",)
|
||||||
# list of validation commands
|
# list of validation commands
|
||||||
|
@ -614,6 +620,7 @@ class Arouted(NrlService):
|
||||||
"""
|
"""
|
||||||
Adaptive Routing
|
Adaptive Routing
|
||||||
"""
|
"""
|
||||||
|
|
||||||
name = "arouted"
|
name = "arouted"
|
||||||
executables = ("arouted",)
|
executables = ("arouted",)
|
||||||
configs = ("startarouted.sh",)
|
configs = ("startarouted.sh",)
|
||||||
|
@ -626,7 +633,8 @@ class Arouted(NrlService):
|
||||||
"""
|
"""
|
||||||
Return the Quagga.conf or quaggaboot.sh file contents.
|
Return the Quagga.conf or quaggaboot.sh file contents.
|
||||||
"""
|
"""
|
||||||
cfg = """
|
cfg = (
|
||||||
|
"""
|
||||||
#!/bin/sh
|
#!/bin/sh
|
||||||
for f in "/tmp/%s_smf"; do
|
for f in "/tmp/%s_smf"; do
|
||||||
count=1
|
count=1
|
||||||
|
@ -640,7 +648,9 @@ for f in "/tmp/%s_smf"; do
|
||||||
done
|
done
|
||||||
done
|
done
|
||||||
|
|
||||||
""" % node.name
|
"""
|
||||||
|
% node.name
|
||||||
|
)
|
||||||
cfg += "ip route add %s dev lo\n" % cls.firstipv4prefix(node, 24)
|
cfg += "ip route add %s dev lo\n" % cls.firstipv4prefix(node, 24)
|
||||||
cfg += "arouted instance %s_smf tap %s_tap" % (node.name, node.name)
|
cfg += "arouted instance %s_smf tap %s_tap" % (node.name, node.name)
|
||||||
# seconds to consider a new route valid
|
# seconds to consider a new route valid
|
||||||
|
|
|
@ -4,7 +4,7 @@ quagga.py: defines routing services provided by Quagga.
|
||||||
|
|
||||||
from core import constants
|
from core import constants
|
||||||
from core.emulator.enumerations import LinkTypes, NodeTypes
|
from core.emulator.enumerations import LinkTypes, NodeTypes
|
||||||
from core.nodes import nodeutils, ipaddress
|
from core.nodes import ipaddress, nodeutils
|
||||||
from core.services.coreservices import CoreService
|
from core.services.coreservices import CoreService
|
||||||
|
|
||||||
|
|
||||||
|
@ -15,7 +15,7 @@ class Zebra(CoreService):
|
||||||
configs = (
|
configs = (
|
||||||
"/usr/local/etc/quagga/Quagga.conf",
|
"/usr/local/etc/quagga/Quagga.conf",
|
||||||
"quaggaboot.sh",
|
"quaggaboot.sh",
|
||||||
"/usr/local/etc/quagga/vtysh.conf"
|
"/usr/local/etc/quagga/vtysh.conf",
|
||||||
)
|
)
|
||||||
startup = ("sh quaggaboot.sh zebra",)
|
startup = ("sh quaggaboot.sh zebra",)
|
||||||
shutdown = ("killall zebra",)
|
shutdown = ("killall zebra",)
|
||||||
|
@ -33,7 +33,9 @@ class Zebra(CoreService):
|
||||||
elif filename == cls.configs[2]:
|
elif filename == cls.configs[2]:
|
||||||
return cls.generateVtyshConf(node)
|
return cls.generateVtyshConf(node)
|
||||||
else:
|
else:
|
||||||
raise ValueError("file name (%s) is not a known configuration: %s", filename, cls.configs)
|
raise ValueError(
|
||||||
|
"file name (%s) is not a known configuration: %s", filename, cls.configs
|
||||||
|
)
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def generateVtyshConf(cls, node):
|
def generateVtyshConf(cls, node):
|
||||||
|
@ -54,7 +56,7 @@ class Zebra(CoreService):
|
||||||
for ifc in node.netifs():
|
for ifc in node.netifs():
|
||||||
cfg += "interface %s\n" % ifc.name
|
cfg += "interface %s\n" % ifc.name
|
||||||
# include control interfaces in addressing but not routing daemons
|
# include control interfaces in addressing but not routing daemons
|
||||||
if hasattr(ifc, 'control') and ifc.control is True:
|
if hasattr(ifc, "control") and ifc.control is True:
|
||||||
cfg += " "
|
cfg += " "
|
||||||
cfg += "\n ".join(map(cls.addrstr, ifc.addrlist))
|
cfg += "\n ".join(map(cls.addrstr, ifc.addrlist))
|
||||||
cfg += "\n"
|
cfg += "\n"
|
||||||
|
@ -76,13 +78,17 @@ class Zebra(CoreService):
|
||||||
cfgv4 += ifccfg
|
cfgv4 += ifccfg
|
||||||
|
|
||||||
if want_ipv4:
|
if want_ipv4:
|
||||||
ipv4list = filter(lambda x: ipaddress.is_ipv4_address(x.split('/')[0]), ifc.addrlist)
|
ipv4list = filter(
|
||||||
|
lambda x: ipaddress.is_ipv4_address(x.split("/")[0]), ifc.addrlist
|
||||||
|
)
|
||||||
cfg += " "
|
cfg += " "
|
||||||
cfg += "\n ".join(map(cls.addrstr, ipv4list))
|
cfg += "\n ".join(map(cls.addrstr, ipv4list))
|
||||||
cfg += "\n"
|
cfg += "\n"
|
||||||
cfg += cfgv4
|
cfg += cfgv4
|
||||||
if want_ipv6:
|
if want_ipv6:
|
||||||
ipv6list = filter(lambda x: ipaddress.is_ipv6_address(x.split('/')[0]), ifc.addrlist)
|
ipv6list = filter(
|
||||||
|
lambda x: ipaddress.is_ipv6_address(x.split("/")[0]), ifc.addrlist
|
||||||
|
)
|
||||||
cfg += " "
|
cfg += " "
|
||||||
cfg += "\n ".join(map(cls.addrstr, ipv6list))
|
cfg += "\n ".join(map(cls.addrstr, ipv6list))
|
||||||
cfg += "\n"
|
cfg += "\n"
|
||||||
|
@ -112,10 +118,12 @@ class Zebra(CoreService):
|
||||||
"""
|
"""
|
||||||
Generate a shell script used to boot the Quagga daemons.
|
Generate a shell script used to boot the Quagga daemons.
|
||||||
"""
|
"""
|
||||||
quagga_bin_search = node.session.options.get_config("quagga_bin_search",
|
quagga_bin_search = node.session.options.get_config(
|
||||||
default='"/usr/local/bin /usr/bin /usr/lib/quagga"')
|
"quagga_bin_search", default='"/usr/local/bin /usr/bin /usr/lib/quagga"'
|
||||||
quagga_sbin_search = node.session.options.get_config('quagga_sbin_search',
|
)
|
||||||
default='"/usr/local/sbin /usr/sbin /usr/lib/quagga"')
|
quagga_sbin_search = node.session.options.get_config(
|
||||||
|
"quagga_sbin_search", default='"/usr/local/sbin /usr/sbin /usr/lib/quagga"'
|
||||||
|
)
|
||||||
return """\
|
return """\
|
||||||
#!/bin/sh
|
#!/bin/sh
|
||||||
# auto-generated by zebra service (quagga.py)
|
# auto-generated by zebra service (quagga.py)
|
||||||
|
@ -191,7 +199,7 @@ bootquagga()
|
||||||
|
|
||||||
bootdaemon "zebra"
|
bootdaemon "zebra"
|
||||||
for r in rip ripng ospf6 ospf bgp babel; do
|
for r in rip ripng ospf6 ospf bgp babel; do
|
||||||
if grep -q "^router \<${r}\>" $QUAGGA_CONF; then
|
if grep -q "^router \\<${r}\\>" $QUAGGA_CONF; then
|
||||||
bootdaemon "${r}d"
|
bootdaemon "${r}d"
|
||||||
fi
|
fi
|
||||||
done
|
done
|
||||||
|
@ -209,7 +217,12 @@ if [ "$1" != "zebra" ]; then
|
||||||
fi
|
fi
|
||||||
confcheck
|
confcheck
|
||||||
bootquagga
|
bootquagga
|
||||||
""" % (cls.configs[0], quagga_sbin_search, quagga_bin_search, constants.QUAGGA_STATE_DIR)
|
""" % (
|
||||||
|
cls.configs[0],
|
||||||
|
quagga_sbin_search,
|
||||||
|
quagga_bin_search,
|
||||||
|
constants.QUAGGA_STATE_DIR,
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
class QuaggaService(CoreService):
|
class QuaggaService(CoreService):
|
||||||
|
@ -217,6 +230,7 @@ class QuaggaService(CoreService):
|
||||||
Parent class for Quagga services. Defines properties and methods
|
Parent class for Quagga services. Defines properties and methods
|
||||||
common to Quagga's routing daemons.
|
common to Quagga's routing daemons.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
name = None
|
name = None
|
||||||
group = "Quagga"
|
group = "Quagga"
|
||||||
dependencies = ("zebra",)
|
dependencies = ("zebra",)
|
||||||
|
@ -235,11 +249,11 @@ class QuaggaService(CoreService):
|
||||||
Helper to return the first IPv4 address of a node as its router ID.
|
Helper to return the first IPv4 address of a node as its router ID.
|
||||||
"""
|
"""
|
||||||
for ifc in node.netifs():
|
for ifc in node.netifs():
|
||||||
if hasattr(ifc, 'control') and ifc.control is True:
|
if hasattr(ifc, "control") and ifc.control is True:
|
||||||
continue
|
continue
|
||||||
for a in ifc.addrlist:
|
for a in ifc.addrlist:
|
||||||
if a.find(".") >= 0:
|
if a.find(".") >= 0:
|
||||||
return a.split('/')[0]
|
return a.split("/")[0]
|
||||||
# raise ValueError, "no IPv4 address found for router ID"
|
# raise ValueError, "no IPv4 address found for router ID"
|
||||||
return "0.0.0.0"
|
return "0.0.0.0"
|
||||||
|
|
||||||
|
@ -276,6 +290,7 @@ class Ospfv2(QuaggaService):
|
||||||
not build its own configuration file but has hooks for adding to the
|
not build its own configuration file but has hooks for adding to the
|
||||||
unified Quagga.conf file.
|
unified Quagga.conf file.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
name = "OSPFv2"
|
name = "OSPFv2"
|
||||||
startup = ()
|
startup = ()
|
||||||
shutdown = ("killall ospfd",)
|
shutdown = ("killall ospfd",)
|
||||||
|
@ -317,7 +332,7 @@ class Ospfv2(QuaggaService):
|
||||||
cfg += " router-id %s\n" % rtrid
|
cfg += " router-id %s\n" % rtrid
|
||||||
# network 10.0.0.0/24 area 0
|
# network 10.0.0.0/24 area 0
|
||||||
for ifc in node.netifs():
|
for ifc in node.netifs():
|
||||||
if hasattr(ifc, 'control') and ifc.control is True:
|
if hasattr(ifc, "control") and ifc.control is True:
|
||||||
continue
|
continue
|
||||||
for a in ifc.addrlist:
|
for a in ifc.addrlist:
|
||||||
if a.find(".") < 0:
|
if a.find(".") < 0:
|
||||||
|
@ -351,6 +366,7 @@ class Ospfv3(QuaggaService):
|
||||||
not build its own configuration file but has hooks for adding to the
|
not build its own configuration file but has hooks for adding to the
|
||||||
unified Quagga.conf file.
|
unified Quagga.conf file.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
name = "OSPFv3"
|
name = "OSPFv3"
|
||||||
startup = ()
|
startup = ()
|
||||||
shutdown = ("killall ospf6d",)
|
shutdown = ("killall ospf6d",)
|
||||||
|
@ -401,7 +417,7 @@ class Ospfv3(QuaggaService):
|
||||||
rtrid = cls.routerid(node)
|
rtrid = cls.routerid(node)
|
||||||
cfg += " router-id %s\n" % rtrid
|
cfg += " router-id %s\n" % rtrid
|
||||||
for ifc in node.netifs():
|
for ifc in node.netifs():
|
||||||
if hasattr(ifc, 'control') and ifc.control is True:
|
if hasattr(ifc, "control") and ifc.control is True:
|
||||||
continue
|
continue
|
||||||
cfg += " interface %s area 0.0.0.0\n" % ifc.name
|
cfg += " interface %s area 0.0.0.0\n" % ifc.name
|
||||||
cfg += "!\n"
|
cfg += "!\n"
|
||||||
|
@ -432,6 +448,7 @@ class Ospfv3mdr(Ospfv3):
|
||||||
configuration file but has hooks for adding to the
|
configuration file but has hooks for adding to the
|
||||||
unified Quagga.conf file.
|
unified Quagga.conf file.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
name = "OSPFv3MDR"
|
name = "OSPFv3MDR"
|
||||||
ipv4_routing = True
|
ipv4_routing = True
|
||||||
|
|
||||||
|
@ -440,8 +457,12 @@ class Ospfv3mdr(Ospfv3):
|
||||||
cfg = cls.mtucheck(ifc)
|
cfg = cls.mtucheck(ifc)
|
||||||
# Uncomment the following line to use Address Family Translation for IPv4
|
# Uncomment the following line to use Address Family Translation for IPv4
|
||||||
cfg += " ipv6 ospf6 instance-id 65\n"
|
cfg += " ipv6 ospf6 instance-id 65\n"
|
||||||
if ifc.net is not None and nodeutils.is_node(ifc.net, (NodeTypes.WIRELESS_LAN, NodeTypes.EMANE)):
|
if ifc.net is not None and nodeutils.is_node(
|
||||||
return cfg + """\
|
ifc.net, (NodeTypes.WIRELESS_LAN, NodeTypes.EMANE)
|
||||||
|
):
|
||||||
|
return (
|
||||||
|
cfg
|
||||||
|
+ """\
|
||||||
ipv6 ospf6 hello-interval 2
|
ipv6 ospf6 hello-interval 2
|
||||||
ipv6 ospf6 dead-interval 6
|
ipv6 ospf6 dead-interval 6
|
||||||
ipv6 ospf6 retransmit-interval 5
|
ipv6 ospf6 retransmit-interval 5
|
||||||
|
@ -450,6 +471,7 @@ class Ospfv3mdr(Ospfv3):
|
||||||
ipv6 ospf6 adjacencyconnectivity uniconnected
|
ipv6 ospf6 adjacencyconnectivity uniconnected
|
||||||
ipv6 ospf6 lsafullness mincostlsa
|
ipv6 ospf6 lsafullness mincostlsa
|
||||||
"""
|
"""
|
||||||
|
)
|
||||||
else:
|
else:
|
||||||
return cfg
|
return cfg
|
||||||
|
|
||||||
|
@ -460,6 +482,7 @@ class Bgp(QuaggaService):
|
||||||
Peers must be manually configured, with a full mesh for those
|
Peers must be manually configured, with a full mesh for those
|
||||||
having the same AS number.
|
having the same AS number.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
name = "BGP"
|
name = "BGP"
|
||||||
startup = ()
|
startup = ()
|
||||||
shutdown = ("killall bgpd",)
|
shutdown = ("killall bgpd",)
|
||||||
|
@ -485,6 +508,7 @@ class Rip(QuaggaService):
|
||||||
"""
|
"""
|
||||||
The RIP service provides IPv4 routing for wired networks.
|
The RIP service provides IPv4 routing for wired networks.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
name = "RIP"
|
name = "RIP"
|
||||||
startup = ()
|
startup = ()
|
||||||
shutdown = ("killall ripd",)
|
shutdown = ("killall ripd",)
|
||||||
|
@ -508,6 +532,7 @@ class Ripng(QuaggaService):
|
||||||
"""
|
"""
|
||||||
The RIP NG service provides IPv6 routing for wired networks.
|
The RIP NG service provides IPv6 routing for wired networks.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
name = "RIPNG"
|
name = "RIPNG"
|
||||||
startup = ()
|
startup = ()
|
||||||
shutdown = ("killall ripngd",)
|
shutdown = ("killall ripngd",)
|
||||||
|
@ -532,6 +557,7 @@ class Babel(QuaggaService):
|
||||||
The Babel service provides a loop-avoiding distance-vector routing
|
The Babel service provides a loop-avoiding distance-vector routing
|
||||||
protocol for IPv6 and IPv4 with fast convergence properties.
|
protocol for IPv6 and IPv4 with fast convergence properties.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
name = "Babel"
|
name = "Babel"
|
||||||
startup = ()
|
startup = ()
|
||||||
shutdown = ("killall babeld",)
|
shutdown = ("killall babeld",)
|
||||||
|
@ -560,28 +586,29 @@ class Xpimd(QuaggaService):
|
||||||
"""
|
"""
|
||||||
PIM multicast routing based on XORP.
|
PIM multicast routing based on XORP.
|
||||||
"""
|
"""
|
||||||
name = 'Xpimd'
|
|
||||||
|
name = "Xpimd"
|
||||||
startup = ()
|
startup = ()
|
||||||
shutdown = ('killall xpimd',)
|
shutdown = ("killall xpimd",)
|
||||||
validate = ('pidof xpimd',)
|
validate = ("pidof xpimd",)
|
||||||
ipv4_routing = True
|
ipv4_routing = True
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def generatequaggaconfig(cls, node):
|
def generatequaggaconfig(cls, node):
|
||||||
ifname = 'eth0'
|
ifname = "eth0"
|
||||||
for ifc in node.netifs():
|
for ifc in node.netifs():
|
||||||
if ifc.name != 'lo':
|
if ifc.name != "lo":
|
||||||
ifname = ifc.name
|
ifname = ifc.name
|
||||||
break
|
break
|
||||||
cfg = 'router mfea\n!\n'
|
cfg = "router mfea\n!\n"
|
||||||
cfg += 'router igmp\n!\n'
|
cfg += "router igmp\n!\n"
|
||||||
cfg += 'router pim\n'
|
cfg += "router pim\n"
|
||||||
cfg += ' !ip pim rp-address 10.0.0.1\n'
|
cfg += " !ip pim rp-address 10.0.0.1\n"
|
||||||
cfg += ' ip pim bsr-candidate %s\n' % ifname
|
cfg += " ip pim bsr-candidate %s\n" % ifname
|
||||||
cfg += ' ip pim rp-candidate %s\n' % ifname
|
cfg += " ip pim rp-candidate %s\n" % ifname
|
||||||
cfg += ' !ip pim spt-threshold interval 10 bytes 80000\n'
|
cfg += " !ip pim spt-threshold interval 10 bytes 80000\n"
|
||||||
return cfg
|
return cfg
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def generatequaggaifcconfig(cls, node, ifc):
|
def generatequaggaifcconfig(cls, node, ifc):
|
||||||
return ' ip mfea\n ip igmp\n ip pim\n'
|
return " ip mfea\n ip igmp\n ip pim\n"
|
||||||
|
|
|
@ -11,6 +11,7 @@ class SdnService(CoreService):
|
||||||
"""
|
"""
|
||||||
Parent class for SDN services.
|
Parent class for SDN services.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
group = "SDN"
|
group = "SDN"
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
|
@ -23,9 +24,9 @@ class OvsService(SdnService):
|
||||||
executables = ("ovs-ofctl", "ovs-vsctl")
|
executables = ("ovs-ofctl", "ovs-vsctl")
|
||||||
group = "SDN"
|
group = "SDN"
|
||||||
dirs = ("/etc/openvswitch", "/var/run/openvswitch", "/var/log/openvswitch")
|
dirs = ("/etc/openvswitch", "/var/run/openvswitch", "/var/log/openvswitch")
|
||||||
configs = ('OvsService.sh',)
|
configs = ("OvsService.sh",)
|
||||||
startup = ('sh OvsService.sh',)
|
startup = ("sh OvsService.sh",)
|
||||||
shutdown = ('killall ovs-vswitchd', 'killall ovsdb-server')
|
shutdown = ("killall ovs-vswitchd", "killall ovsdb-server")
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def generate_config(cls, node, filename):
|
def generate_config(cls, node, filename):
|
||||||
|
@ -38,11 +39,11 @@ class OvsService(SdnService):
|
||||||
cfg = "#!/bin/sh\n"
|
cfg = "#!/bin/sh\n"
|
||||||
cfg += "# auto-generated by OvsService (OvsService.py)\n"
|
cfg += "# auto-generated by OvsService (OvsService.py)\n"
|
||||||
cfg += "/etc/init.d/openvswitch-switch start < /dev/null\n"
|
cfg += "/etc/init.d/openvswitch-switch start < /dev/null\n"
|
||||||
cfg += "ovs-vsctl add-br ovsbr0\n"
|
cfg += "ovs-vsctl add-br ovsbr0 -- set Bridge ovsbr0 fail-mode=secure\n"
|
||||||
cfg += "ifconfig ovsbr0 up\n"
|
cfg += "ifconfig ovsbr0 up\n"
|
||||||
|
|
||||||
for ifc in node.netifs():
|
for ifc in node.netifs():
|
||||||
if hasattr(ifc, 'control') and ifc.control is True:
|
if hasattr(ifc, "control") and ifc.control is True:
|
||||||
continue
|
continue
|
||||||
ifnumstr = re.findall(r"\d+", ifc.name)
|
ifnumstr = re.findall(r"\d+", ifc.name)
|
||||||
ifnum = ifnumstr[0]
|
ifnum = ifnumstr[0]
|
||||||
|
@ -76,10 +77,16 @@ class OvsService(SdnService):
|
||||||
# Setup default flows
|
# Setup default flows
|
||||||
portnum = 1
|
portnum = 1
|
||||||
for ifc in node.netifs():
|
for ifc in node.netifs():
|
||||||
if hasattr(ifc, 'control') and ifc.control is True:
|
if hasattr(ifc, "control") and ifc.control is True:
|
||||||
continue
|
continue
|
||||||
cfg += "ovs-ofctl add-flow ovsbr0 priority=1000,in_port=%d,action=output:%d\n" % (portnum, portnum + 1)
|
cfg += (
|
||||||
cfg += "ovs-ofctl add-flow ovsbr0 priority=1000,in_port=%d,action=output:%d\n" % (portnum + 1, portnum)
|
"ovs-ofctl add-flow ovsbr0 priority=1000,in_port=%d,action=output:%d\n"
|
||||||
|
% (portnum, portnum + 1)
|
||||||
|
)
|
||||||
|
cfg += (
|
||||||
|
"ovs-ofctl add-flow ovsbr0 priority=1000,in_port=%d,action=output:%d\n"
|
||||||
|
% (portnum + 1, portnum)
|
||||||
|
)
|
||||||
portnum += 2
|
portnum += 2
|
||||||
|
|
||||||
return cfg
|
return cfg
|
||||||
|
@ -90,9 +97,9 @@ class RyuService(SdnService):
|
||||||
executables = ("ryu-manager",)
|
executables = ("ryu-manager",)
|
||||||
group = "SDN"
|
group = "SDN"
|
||||||
dirs = ()
|
dirs = ()
|
||||||
configs = ('ryuService.sh',)
|
configs = ("ryuService.sh",)
|
||||||
startup = ('sh ryuService.sh',)
|
startup = ("sh ryuService.sh",)
|
||||||
shutdown = ('killall ryu-manager',)
|
shutdown = ("killall ryu-manager",)
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def generate_config(cls, node, filename):
|
def generate_config(cls, node, filename):
|
||||||
|
@ -100,8 +107,9 @@ class RyuService(SdnService):
|
||||||
Return a string that will be written to filename, or sent to the
|
Return a string that will be written to filename, or sent to the
|
||||||
GUI for user customization.
|
GUI for user customization.
|
||||||
"""
|
"""
|
||||||
app_path = "/usr/local/lib/python2.7/dist-packages/ryu/app"
|
|
||||||
cfg = "#!/bin/sh\n"
|
cfg = "#!/bin/sh\n"
|
||||||
cfg += "# auto-generated by ryuService (ryuService.py)\n"
|
cfg += "# auto-generated by ryuService (ryuService.py)\n"
|
||||||
cfg += '/usr/local/bin/ryu-manager --observe-links %s/ofctl_rest.py %s/rest_topology.py' % (app_path, app_path)
|
cfg += (
|
||||||
|
"ryu-manager --observe-links ryu.app.ofctl_rest ryu.app.rest_topology &\n"
|
||||||
|
)
|
||||||
return cfg
|
return cfg
|
||||||
|
|
|
@ -30,7 +30,9 @@ class VPNClient(CoreService):
|
||||||
try:
|
try:
|
||||||
cfg += open(fname, "rb").read()
|
cfg += open(fname, "rb").read()
|
||||||
except IOError:
|
except IOError:
|
||||||
logging.exception("Error opening VPN client configuration template (%s)", fname)
|
logging.exception(
|
||||||
|
"Error opening VPN client configuration template (%s)", fname
|
||||||
|
)
|
||||||
|
|
||||||
return cfg
|
return cfg
|
||||||
|
|
||||||
|
@ -57,7 +59,9 @@ class VPNServer(CoreService):
|
||||||
try:
|
try:
|
||||||
cfg += open(fname, "rb").read()
|
cfg += open(fname, "rb").read()
|
||||||
except IOError:
|
except IOError:
|
||||||
logging.exception("Error opening VPN server configuration template (%s)", fname)
|
logging.exception(
|
||||||
|
"Error opening VPN server configuration template (%s)", fname
|
||||||
|
)
|
||||||
|
|
||||||
return cfg
|
return cfg
|
||||||
|
|
||||||
|
@ -108,7 +112,9 @@ class Firewall(CoreService):
|
||||||
try:
|
try:
|
||||||
cfg += open(fname, "rb").read()
|
cfg += open(fname, "rb").read()
|
||||||
except IOError:
|
except IOError:
|
||||||
logging.exception("Error opening Firewall configuration template (%s)", fname)
|
logging.exception(
|
||||||
|
"Error opening Firewall configuration template (%s)", fname
|
||||||
|
)
|
||||||
|
|
||||||
return cfg
|
return cfg
|
||||||
|
|
||||||
|
@ -117,10 +123,11 @@ class Nat(CoreService):
|
||||||
"""
|
"""
|
||||||
IPv4 source NAT service.
|
IPv4 source NAT service.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
name = "NAT"
|
name = "NAT"
|
||||||
executables = ("iptables",)
|
executables = ("iptables",)
|
||||||
group = "Security"
|
group = "Security"
|
||||||
configs = ("nat.sh", )
|
configs = ("nat.sh",)
|
||||||
startup = ("sh nat.sh",)
|
startup = ("sh nat.sh",)
|
||||||
custom_needed = False
|
custom_needed = False
|
||||||
|
|
||||||
|
@ -130,7 +137,7 @@ class Nat(CoreService):
|
||||||
Generate a NAT line for one interface.
|
Generate a NAT line for one interface.
|
||||||
"""
|
"""
|
||||||
cfg = line_prefix + "iptables -t nat -A POSTROUTING -o "
|
cfg = line_prefix + "iptables -t nat -A POSTROUTING -o "
|
||||||
cfg +=ifc.name + " -j MASQUERADE\n"
|
cfg += ifc.name + " -j MASQUERADE\n"
|
||||||
|
|
||||||
cfg += line_prefix + "iptables -A FORWARD -i " + ifc.name
|
cfg += line_prefix + "iptables -A FORWARD -i " + ifc.name
|
||||||
cfg += " -m state --state RELATED,ESTABLISHED -j ACCEPT\n"
|
cfg += " -m state --state RELATED,ESTABLISHED -j ACCEPT\n"
|
||||||
|
@ -149,7 +156,7 @@ class Nat(CoreService):
|
||||||
cfg += "# NAT out the first interface by default\n"
|
cfg += "# NAT out the first interface by default\n"
|
||||||
have_nat = False
|
have_nat = False
|
||||||
for ifc in node.netifs():
|
for ifc in node.netifs():
|
||||||
if hasattr(ifc, 'control') and ifc.control == True:
|
if hasattr(ifc, "control") and ifc.control is True:
|
||||||
continue
|
continue
|
||||||
if have_nat:
|
if have_nat:
|
||||||
cfg += cls.generateifcnatrule(ifc, line_prefix="#")
|
cfg += cls.generateifcnatrule(ifc, line_prefix="#")
|
||||||
|
@ -159,4 +166,3 @@ class Nat(CoreService):
|
||||||
cfg += cls.generateifcnatrule(ifc)
|
cfg += cls.generateifcnatrule(ifc)
|
||||||
cfg += "\n"
|
cfg += "\n"
|
||||||
return cfg
|
return cfg
|
||||||
|
|
||||||
|
|
|
@ -12,7 +12,11 @@ class Ucarp(CoreService):
|
||||||
group = "Utility"
|
group = "Utility"
|
||||||
dirs = (UCARP_ETC,)
|
dirs = (UCARP_ETC,)
|
||||||
configs = (
|
configs = (
|
||||||
UCARP_ETC + "/default.sh", UCARP_ETC + "/default-up.sh", UCARP_ETC + "/default-down.sh", "ucarpboot.sh",)
|
UCARP_ETC + "/default.sh",
|
||||||
|
UCARP_ETC + "/default-up.sh",
|
||||||
|
UCARP_ETC + "/default-down.sh",
|
||||||
|
"ucarpboot.sh",
|
||||||
|
)
|
||||||
startup = ("sh ucarpboot.sh",)
|
startup = ("sh ucarpboot.sh",)
|
||||||
shutdown = ("killall ucarp",)
|
shutdown = ("killall ucarp",)
|
||||||
validate = ("pidof ucarp",)
|
validate = ("pidof ucarp",)
|
||||||
|
@ -39,7 +43,7 @@ class Ucarp(CoreService):
|
||||||
Returns configuration file text.
|
Returns configuration file text.
|
||||||
"""
|
"""
|
||||||
try:
|
try:
|
||||||
ucarp_bin = node.session.cfg['ucarp_bin']
|
ucarp_bin = node.session.cfg["ucarp_bin"]
|
||||||
except KeyError:
|
except KeyError:
|
||||||
ucarp_bin = "/usr/sbin/ucarp"
|
ucarp_bin = "/usr/sbin/ucarp"
|
||||||
|
|
||||||
|
@ -100,14 +104,18 @@ STOP_SCRIPT=${UCARP_CFGDIR}/default-down.sh
|
||||||
UCARP_OPTS="$OPTIONS -b $UCARP_BASE -k $SKEW -i $INTERFACE -v $INSTANCE_ID -p $PASSWORD -u $START_SCRIPT -d $STOP_SCRIPT -a $VIRTUAL_ADDRESS -s $SOURCE_ADDRESS -f $FACILITY $XPARAM"
|
UCARP_OPTS="$OPTIONS -b $UCARP_BASE -k $SKEW -i $INTERFACE -v $INSTANCE_ID -p $PASSWORD -u $START_SCRIPT -d $STOP_SCRIPT -a $VIRTUAL_ADDRESS -s $SOURCE_ADDRESS -f $FACILITY $XPARAM"
|
||||||
|
|
||||||
${UCARP_EXEC} -B ${UCARP_OPTS}
|
${UCARP_EXEC} -B ${UCARP_OPTS}
|
||||||
""" % (ucarp_bin, UCARP_ETC)
|
""" % (
|
||||||
|
ucarp_bin,
|
||||||
|
UCARP_ETC,
|
||||||
|
)
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def generateUcarpBoot(cls, node):
|
def generateUcarpBoot(cls, node):
|
||||||
"""
|
"""
|
||||||
Generate a shell script used to boot the Ucarp daemons.
|
Generate a shell script used to boot the Ucarp daemons.
|
||||||
"""
|
"""
|
||||||
return """\
|
return (
|
||||||
|
"""\
|
||||||
#!/bin/sh
|
#!/bin/sh
|
||||||
# Location of the UCARP config directory
|
# Location of the UCARP config directory
|
||||||
UCARP_CFGDIR=%s
|
UCARP_CFGDIR=%s
|
||||||
|
@ -117,7 +125,9 @@ chmod a+x ${UCARP_CFGDIR}/*.sh
|
||||||
# Start the default ucarp daemon configuration
|
# Start the default ucarp daemon configuration
|
||||||
${UCARP_CFGDIR}/default.sh
|
${UCARP_CFGDIR}/default.sh
|
||||||
|
|
||||||
""" % UCARP_ETC
|
"""
|
||||||
|
% UCARP_ETC
|
||||||
|
)
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def generateVipUp(cls, node):
|
def generateVipUp(cls, node):
|
||||||
|
@ -133,7 +143,7 @@ exec 2> /dev/null
|
||||||
IP="${2}"
|
IP="${2}"
|
||||||
NET="${3}"
|
NET="${3}"
|
||||||
if [ -z "$NET" ]; then
|
if [ -z "$NET" ]; then
|
||||||
NET="24"
|
NET="24"
|
||||||
fi
|
fi
|
||||||
|
|
||||||
/sbin/ip addr add ${IP}/${NET} dev "$1"
|
/sbin/ip addr add ${IP}/${NET} dev "$1"
|
||||||
|
@ -155,7 +165,7 @@ exec 2> /dev/null
|
||||||
IP="${2}"
|
IP="${2}"
|
||||||
NET="${3}"
|
NET="${3}"
|
||||||
if [ -z "$NET" ]; then
|
if [ -z "$NET" ]; then
|
||||||
NET="24"
|
NET="24"
|
||||||
fi
|
fi
|
||||||
|
|
||||||
/sbin/ip addr del ${IP}/${NET} dev "$1"
|
/sbin/ip addr del ${IP}/${NET} dev "$1"
|
||||||
|
|
|
@ -4,10 +4,8 @@ utility.py: defines miscellaneous utility services.
|
||||||
|
|
||||||
import os
|
import os
|
||||||
|
|
||||||
from core import CoreCommandError, utils
|
from core import CoreCommandError, constants, utils
|
||||||
from core import constants
|
from core.nodes.ipaddress import Ipv4Prefix, Ipv6Prefix
|
||||||
from core.nodes.ipaddress import Ipv4Prefix
|
|
||||||
from core.nodes.ipaddress import Ipv6Prefix
|
|
||||||
from core.services.coreservices import CoreService
|
from core.services.coreservices import CoreService
|
||||||
|
|
||||||
|
|
||||||
|
@ -15,6 +13,7 @@ class UtilService(CoreService):
|
||||||
"""
|
"""
|
||||||
Parent class for utility services.
|
Parent class for utility services.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
name = None
|
name = None
|
||||||
group = "Utility"
|
group = "Utility"
|
||||||
dirs = ()
|
dirs = ()
|
||||||
|
@ -52,12 +51,19 @@ class IPForwardService(UtilService):
|
||||||
%(sysctl)s -w net.ipv4.conf.default.send_redirects=0
|
%(sysctl)s -w net.ipv4.conf.default.send_redirects=0
|
||||||
%(sysctl)s -w net.ipv4.conf.all.rp_filter=0
|
%(sysctl)s -w net.ipv4.conf.all.rp_filter=0
|
||||||
%(sysctl)s -w net.ipv4.conf.default.rp_filter=0
|
%(sysctl)s -w net.ipv4.conf.default.rp_filter=0
|
||||||
""" % {'sysctl': constants.SYSCTL_BIN}
|
""" % {
|
||||||
|
"sysctl": constants.SYSCTL_BIN
|
||||||
|
}
|
||||||
for ifc in node.netifs():
|
for ifc in node.netifs():
|
||||||
name = utils.sysctl_devname(ifc.name)
|
name = utils.sysctl_devname(ifc.name)
|
||||||
cfg += "%s -w net.ipv4.conf.%s.forwarding=1\n" % (constants.SYSCTL_BIN, name)
|
cfg += "%s -w net.ipv4.conf.%s.forwarding=1\n" % (
|
||||||
cfg += "%s -w net.ipv4.conf.%s.send_redirects=0\n" % \
|
constants.SYSCTL_BIN,
|
||||||
(constants.SYSCTL_BIN, name)
|
name,
|
||||||
|
)
|
||||||
|
cfg += "%s -w net.ipv4.conf.%s.send_redirects=0\n" % (
|
||||||
|
constants.SYSCTL_BIN,
|
||||||
|
name,
|
||||||
|
)
|
||||||
cfg += "%s -w net.ipv4.conf.%s.rp_filter=0\n" % (constants.SYSCTL_BIN, name)
|
cfg += "%s -w net.ipv4.conf.%s.rp_filter=0\n" % (constants.SYSCTL_BIN, name)
|
||||||
return cfg
|
return cfg
|
||||||
|
|
||||||
|
@ -72,7 +78,7 @@ class DefaultRouteService(UtilService):
|
||||||
cfg = "#!/bin/sh\n"
|
cfg = "#!/bin/sh\n"
|
||||||
cfg += "# auto-generated by DefaultRoute service (utility.py)\n"
|
cfg += "# auto-generated by DefaultRoute service (utility.py)\n"
|
||||||
for ifc in node.netifs():
|
for ifc in node.netifs():
|
||||||
if hasattr(ifc, 'control') and ifc.control is True:
|
if hasattr(ifc, "control") and ifc.control is True:
|
||||||
continue
|
continue
|
||||||
cfg += "\n".join(map(cls.addrstr, ifc.addrlist))
|
cfg += "\n".join(map(cls.addrstr, ifc.addrlist))
|
||||||
cfg += "\n"
|
cfg += "\n"
|
||||||
|
@ -107,7 +113,7 @@ class DefaultMulticastRouteService(UtilService):
|
||||||
cfg += "as needed\n"
|
cfg += "as needed\n"
|
||||||
|
|
||||||
for ifc in node.netifs():
|
for ifc in node.netifs():
|
||||||
if hasattr(ifc, 'control') and ifc.control is True:
|
if hasattr(ifc, "control") and ifc.control is True:
|
||||||
continue
|
continue
|
||||||
if os.uname()[0] == "Linux":
|
if os.uname()[0] == "Linux":
|
||||||
rtcmd = "ip route add 224.0.0.0/4 dev"
|
rtcmd = "ip route add 224.0.0.0/4 dev"
|
||||||
|
@ -132,7 +138,7 @@ class StaticRouteService(UtilService):
|
||||||
cfg += "# NOTE: this service must be customized to be of any use\n"
|
cfg += "# NOTE: this service must be customized to be of any use\n"
|
||||||
cfg += "# Below are samples that you can uncomment and edit.\n#\n"
|
cfg += "# Below are samples that you can uncomment and edit.\n#\n"
|
||||||
for ifc in node.netifs():
|
for ifc in node.netifs():
|
||||||
if hasattr(ifc, 'control') and ifc.control is True:
|
if hasattr(ifc, "control") and ifc.control is True:
|
||||||
continue
|
continue
|
||||||
cfg += "\n".join(map(cls.routestr, ifc.addrlist))
|
cfg += "\n".join(map(cls.routestr, ifc.addrlist))
|
||||||
cfg += "\n"
|
cfg += "\n"
|
||||||
|
@ -158,8 +164,8 @@ class StaticRouteService(UtilService):
|
||||||
|
|
||||||
class SshService(UtilService):
|
class SshService(UtilService):
|
||||||
name = "SSH"
|
name = "SSH"
|
||||||
configs = ("startsshd.sh", "/etc/ssh/sshd_config",)
|
configs = ("startsshd.sh", "/etc/ssh/sshd_config")
|
||||||
dirs = ("/etc/ssh", "/var/run/sshd",)
|
dirs = ("/etc/ssh", "/var/run/sshd")
|
||||||
startup = ("sh startsshd.sh",)
|
startup = ("sh startsshd.sh",)
|
||||||
shutdown = ("killall sshd",)
|
shutdown = ("killall sshd",)
|
||||||
validate = ()
|
validate = ()
|
||||||
|
@ -181,7 +187,11 @@ ssh-keygen -q -t rsa -N "" -f %s/ssh_host_rsa_key
|
||||||
chmod 655 %s
|
chmod 655 %s
|
||||||
# wait until RSA host key has been generated to launch sshd
|
# wait until RSA host key has been generated to launch sshd
|
||||||
/usr/sbin/sshd -f %s/sshd_config
|
/usr/sbin/sshd -f %s/sshd_config
|
||||||
""" % (sshcfgdir, sshstatedir, sshcfgdir)
|
""" % (
|
||||||
|
sshcfgdir,
|
||||||
|
sshstatedir,
|
||||||
|
sshcfgdir,
|
||||||
|
)
|
||||||
else:
|
else:
|
||||||
return """\
|
return """\
|
||||||
# auto-generated by SSH service (utility.py)
|
# auto-generated by SSH service (utility.py)
|
||||||
|
@ -221,14 +231,18 @@ AcceptEnv LANG LC_*
|
||||||
Subsystem sftp %s/sftp-server
|
Subsystem sftp %s/sftp-server
|
||||||
UsePAM yes
|
UsePAM yes
|
||||||
UseDNS no
|
UseDNS no
|
||||||
""" % (sshcfgdir, sshstatedir, sshlibdir)
|
""" % (
|
||||||
|
sshcfgdir,
|
||||||
|
sshstatedir,
|
||||||
|
sshlibdir,
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
class DhcpService(UtilService):
|
class DhcpService(UtilService):
|
||||||
name = "DHCP"
|
name = "DHCP"
|
||||||
configs = ("/etc/dhcp/dhcpd.conf",)
|
configs = ("/etc/dhcp/dhcpd.conf",)
|
||||||
dirs = ("/etc/dhcp","/var/lib/dhcp")
|
dirs = ("/etc/dhcp", "/var/lib/dhcp")
|
||||||
startup = ("touch /var/lib/dhcp/dhcpd.leases","dhcpd")
|
startup = ("touch /var/lib/dhcp/dhcpd.leases", "dhcpd")
|
||||||
shutdown = ("killall dhcpd",)
|
shutdown = ("killall dhcpd",)
|
||||||
validate = ("pidof dhcpd",)
|
validate = ("pidof dhcpd",)
|
||||||
|
|
||||||
|
@ -253,7 +267,7 @@ max-lease-time 7200;
|
||||||
ddns-update-style none;
|
ddns-update-style none;
|
||||||
"""
|
"""
|
||||||
for ifc in node.netifs():
|
for ifc in node.netifs():
|
||||||
if hasattr(ifc, 'control') and ifc.control is True:
|
if hasattr(ifc, "control") and ifc.control is True:
|
||||||
continue
|
continue
|
||||||
cfg += "\n".join(map(cls.subnetentry, ifc.addrlist))
|
cfg += "\n".join(map(cls.subnetentry, ifc.addrlist))
|
||||||
cfg += "\n"
|
cfg += "\n"
|
||||||
|
@ -281,13 +295,20 @@ subnet %s netmask %s {
|
||||||
option routers %s;
|
option routers %s;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
""" % (net.prefix_str(), net.netmask_str(), rangelow, rangehigh, addr)
|
""" % (
|
||||||
|
net.prefix_str(),
|
||||||
|
net.netmask_str(),
|
||||||
|
rangelow,
|
||||||
|
rangehigh,
|
||||||
|
addr,
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
class DhcpClientService(UtilService):
|
class DhcpClientService(UtilService):
|
||||||
"""
|
"""
|
||||||
Use a DHCP client for all interfaces for addressing.
|
Use a DHCP client for all interfaces for addressing.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
name = "DHCPClient"
|
name = "DHCPClient"
|
||||||
configs = ("startdhcpclient.sh",)
|
configs = ("startdhcpclient.sh",)
|
||||||
startup = ("sh startdhcpclient.sh",)
|
startup = ("sh startdhcpclient.sh",)
|
||||||
|
@ -306,7 +327,7 @@ class DhcpClientService(UtilService):
|
||||||
cfg += "#mkdir -p /var/run/resolvconf/interface\n"
|
cfg += "#mkdir -p /var/run/resolvconf/interface\n"
|
||||||
|
|
||||||
for ifc in node.netifs():
|
for ifc in node.netifs():
|
||||||
if hasattr(ifc, 'control') and ifc.control is True:
|
if hasattr(ifc, "control") and ifc.control is True:
|
||||||
continue
|
continue
|
||||||
cfg += "#ln -s /var/run/resolvconf/interface/%s.dhclient" % ifc.name
|
cfg += "#ln -s /var/run/resolvconf/interface/%s.dhclient" % ifc.name
|
||||||
cfg += " /var/run/resolvconf/resolv.conf\n"
|
cfg += " /var/run/resolvconf/resolv.conf\n"
|
||||||
|
@ -319,9 +340,10 @@ class FtpService(UtilService):
|
||||||
"""
|
"""
|
||||||
Start a vsftpd server.
|
Start a vsftpd server.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
name = "FTP"
|
name = "FTP"
|
||||||
configs = ("vsftpd.conf",)
|
configs = ("vsftpd.conf",)
|
||||||
dirs = ("/var/run/vsftpd/empty", "/var/ftp",)
|
dirs = ("/var/run/vsftpd/empty", "/var/ftp")
|
||||||
startup = ("vsftpd ./vsftpd.conf",)
|
startup = ("vsftpd ./vsftpd.conf",)
|
||||||
shutdown = ("killall vsftpd",)
|
shutdown = ("killall vsftpd",)
|
||||||
validate = ("pidof vsftpd",)
|
validate = ("pidof vsftpd",)
|
||||||
|
@ -351,12 +373,22 @@ class HttpService(UtilService):
|
||||||
"""
|
"""
|
||||||
Start an apache server.
|
Start an apache server.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
name = "HTTP"
|
name = "HTTP"
|
||||||
configs = ("/etc/apache2/apache2.conf", "/etc/apache2/envvars",
|
configs = (
|
||||||
"/var/www/index.html",)
|
"/etc/apache2/apache2.conf",
|
||||||
dirs = ("/etc/apache2", "/var/run/apache2", "/var/log/apache2",
|
"/etc/apache2/envvars",
|
||||||
"/run/lock", "/var/lock/apache2", "/var/www",)
|
"/var/www/index.html",
|
||||||
startup = ("chown www-data /var/lock/apache2", "apache2ctl start",)
|
)
|
||||||
|
dirs = (
|
||||||
|
"/etc/apache2",
|
||||||
|
"/var/run/apache2",
|
||||||
|
"/var/log/apache2",
|
||||||
|
"/run/lock",
|
||||||
|
"/var/lock/apache2",
|
||||||
|
"/var/www",
|
||||||
|
)
|
||||||
|
startup = ("chown www-data /var/lock/apache2", "apache2ctl start")
|
||||||
shutdown = ("apache2ctl stop",)
|
shutdown = ("apache2ctl stop",)
|
||||||
validate = ("pidof apache2",)
|
validate = ("pidof apache2",)
|
||||||
|
|
||||||
|
@ -382,38 +414,40 @@ class HttpService(UtilService):
|
||||||
Detect the apache2 version using the 'a2query' command.
|
Detect the apache2 version using the 'a2query' command.
|
||||||
"""
|
"""
|
||||||
try:
|
try:
|
||||||
status, result = utils.cmd_output(['a2query', '-v'])
|
status, result = utils.cmd_output(["a2query", "-v"])
|
||||||
except CoreCommandError:
|
except CoreCommandError:
|
||||||
status = -1
|
status = -1
|
||||||
|
|
||||||
if status == 0 and result[:3] == '2.4':
|
if status == 0 and result[:3] == "2.4":
|
||||||
return cls.APACHEVER24
|
return cls.APACHEVER24
|
||||||
|
|
||||||
return cls.APACHEVER22
|
return cls.APACHEVER22
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def generateapache2conf(cls, node, filename):
|
def generateapache2conf(cls, node, filename):
|
||||||
lockstr = {cls.APACHEVER22:
|
lockstr = {
|
||||||
'LockFile ${APACHE_LOCK_DIR}/accept.lock\n',
|
cls.APACHEVER22: "LockFile ${APACHE_LOCK_DIR}/accept.lock\n",
|
||||||
cls.APACHEVER24:
|
cls.APACHEVER24: "Mutex file:${APACHE_LOCK_DIR} default\n",
|
||||||
'Mutex file:${APACHE_LOCK_DIR} default\n', }
|
}
|
||||||
mpmstr = {cls.APACHEVER22: '', cls.APACHEVER24:
|
mpmstr = {
|
||||||
'LoadModule mpm_worker_module /usr/lib/apache2/modules/mod_mpm_worker.so\n', }
|
cls.APACHEVER22: "",
|
||||||
|
cls.APACHEVER24: "LoadModule mpm_worker_module /usr/lib/apache2/modules/mod_mpm_worker.so\n",
|
||||||
|
}
|
||||||
|
|
||||||
permstr = {cls.APACHEVER22:
|
permstr = {
|
||||||
' Order allow,deny\n Deny from all\n Satisfy all\n',
|
cls.APACHEVER22: " Order allow,deny\n Deny from all\n Satisfy all\n",
|
||||||
cls.APACHEVER24:
|
cls.APACHEVER24: " Require all denied\n",
|
||||||
' Require all denied\n', }
|
}
|
||||||
|
|
||||||
authstr = {cls.APACHEVER22:
|
authstr = {
|
||||||
'LoadModule authz_default_module /usr/lib/apache2/modules/mod_authz_default.so\n',
|
cls.APACHEVER22: "LoadModule authz_default_module /usr/lib/apache2/modules/mod_authz_default.so\n",
|
||||||
cls.APACHEVER24:
|
cls.APACHEVER24: "LoadModule authz_core_module /usr/lib/apache2/modules/mod_authz_core.so\n",
|
||||||
'LoadModule authz_core_module /usr/lib/apache2/modules/mod_authz_core.so\n', }
|
}
|
||||||
|
|
||||||
permstr2 = {cls.APACHEVER22:
|
permstr2 = {
|
||||||
'\t\tOrder allow,deny\n\t\tallow from all\n',
|
cls.APACHEVER22: "\t\tOrder allow,deny\n\t\tallow from all\n",
|
||||||
cls.APACHEVER24:
|
cls.APACHEVER24: "\t\tRequire all granted\n",
|
||||||
'\t\tRequire all granted\n', }
|
}
|
||||||
|
|
||||||
version = cls.detectversionfromcmd()
|
version = cls.detectversionfromcmd()
|
||||||
cfg = "# apache2.conf generated by utility.py:HttpService\n"
|
cfg = "# apache2.conf generated by utility.py:HttpService\n"
|
||||||
|
@ -461,7 +495,7 @@ Group ${APACHE_RUN_GROUP}
|
||||||
|
|
||||||
AccessFileName .htaccess
|
AccessFileName .htaccess
|
||||||
|
|
||||||
<Files ~ "^\.ht">
|
<Files ~ "^\\.ht">
|
||||||
"""
|
"""
|
||||||
cfg += permstr[version]
|
cfg += permstr[version]
|
||||||
cfg += """\
|
cfg += """\
|
||||||
|
@ -508,22 +542,22 @@ ServerSignature On
|
||||||
TraceEnable Off
|
TraceEnable Off
|
||||||
|
|
||||||
<VirtualHost *:80>
|
<VirtualHost *:80>
|
||||||
ServerAdmin webmaster@localhost
|
ServerAdmin webmaster@localhost
|
||||||
DocumentRoot /var/www
|
DocumentRoot /var/www
|
||||||
<Directory />
|
<Directory />
|
||||||
Options FollowSymLinks
|
Options FollowSymLinks
|
||||||
AllowOverride None
|
AllowOverride None
|
||||||
</Directory>
|
</Directory>
|
||||||
<Directory /var/www/>
|
<Directory /var/www/>
|
||||||
Options Indexes FollowSymLinks MultiViews
|
Options Indexes FollowSymLinks MultiViews
|
||||||
AllowOverride None
|
AllowOverride None
|
||||||
"""
|
"""
|
||||||
cfg += permstr2[version]
|
cfg += permstr2[version]
|
||||||
cfg += """\
|
cfg += """\
|
||||||
</Directory>
|
</Directory>
|
||||||
ErrorLog ${APACHE_LOG_DIR}/error.log
|
ErrorLog ${APACHE_LOG_DIR}/error.log
|
||||||
LogLevel warn
|
LogLevel warn
|
||||||
CustomLog ${APACHE_LOG_DIR}/access.log combined
|
CustomLog ${APACHE_LOG_DIR}/access.log combined
|
||||||
</VirtualHost>
|
</VirtualHost>
|
||||||
|
|
||||||
"""
|
"""
|
||||||
|
@ -546,14 +580,17 @@ export LANG
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def generatehtml(cls, node, filename):
|
def generatehtml(cls, node, filename):
|
||||||
body = """\
|
body = (
|
||||||
|
"""\
|
||||||
<!-- generated by utility.py:HttpService -->
|
<!-- generated by utility.py:HttpService -->
|
||||||
<h1>%s web server</h1>
|
<h1>%s web server</h1>
|
||||||
<p>This is the default web page for this server.</p>
|
<p>This is the default web page for this server.</p>
|
||||||
<p>The web server software is running but no content has been added, yet.</p>
|
<p>The web server software is running but no content has been added, yet.</p>
|
||||||
""" % node.name
|
"""
|
||||||
|
% node.name
|
||||||
|
)
|
||||||
for ifc in node.netifs():
|
for ifc in node.netifs():
|
||||||
if hasattr(ifc, 'control') and ifc.control is True:
|
if hasattr(ifc, "control") and ifc.control is True:
|
||||||
continue
|
continue
|
||||||
body += "<li>%s - %s</li>\n" % (ifc.name, ifc.addrlist)
|
body += "<li>%s - %s</li>\n" % (ifc.name, ifc.addrlist)
|
||||||
return "<html><body>%s</body></html>" % body
|
return "<html><body>%s</body></html>" % body
|
||||||
|
@ -563,6 +600,7 @@ class PcapService(UtilService):
|
||||||
"""
|
"""
|
||||||
Pcap service for logging packets.
|
Pcap service for logging packets.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
name = "pcap"
|
name = "pcap"
|
||||||
configs = ("pcap.sh",)
|
configs = ("pcap.sh",)
|
||||||
dirs = ()
|
dirs = ()
|
||||||
|
@ -586,11 +624,15 @@ if [ "x$1" = "xstart" ]; then
|
||||||
|
|
||||||
"""
|
"""
|
||||||
for ifc in node.netifs():
|
for ifc in node.netifs():
|
||||||
if hasattr(ifc, 'control') and ifc.control is True:
|
if hasattr(ifc, "control") and ifc.control is True:
|
||||||
cfg += '# '
|
cfg += "# "
|
||||||
redir = "< /dev/null"
|
redir = "< /dev/null"
|
||||||
cfg += "tcpdump ${DUMPOPTS} -w %s.%s.pcap -i %s %s &\n" % \
|
cfg += "tcpdump ${DUMPOPTS} -w %s.%s.pcap -i %s %s &\n" % (
|
||||||
(node.name, ifc.name, ifc.name, redir)
|
node.name,
|
||||||
|
ifc.name,
|
||||||
|
ifc.name,
|
||||||
|
redir,
|
||||||
|
)
|
||||||
cfg += """
|
cfg += """
|
||||||
|
|
||||||
elif [ "x$1" = "xstop" ]; then
|
elif [ "x$1" = "xstop" ]; then
|
||||||
|
@ -617,12 +659,13 @@ class RadvdService(UtilService):
|
||||||
"""
|
"""
|
||||||
cfg = "# auto-generated by RADVD service (utility.py)\n"
|
cfg = "# auto-generated by RADVD service (utility.py)\n"
|
||||||
for ifc in node.netifs():
|
for ifc in node.netifs():
|
||||||
if hasattr(ifc, 'control') and ifc.control is True:
|
if hasattr(ifc, "control") and ifc.control is True:
|
||||||
continue
|
continue
|
||||||
prefixes = map(cls.subnetentry, ifc.addrlist)
|
prefixes = map(cls.subnetentry, ifc.addrlist)
|
||||||
if len(prefixes) < 1:
|
if len(prefixes) < 1:
|
||||||
continue
|
continue
|
||||||
cfg += """\
|
cfg += (
|
||||||
|
"""\
|
||||||
interface %s
|
interface %s
|
||||||
{
|
{
|
||||||
AdvSendAdvert on;
|
AdvSendAdvert on;
|
||||||
|
@ -630,18 +673,23 @@ interface %s
|
||||||
MaxRtrAdvInterval 10;
|
MaxRtrAdvInterval 10;
|
||||||
AdvDefaultPreference low;
|
AdvDefaultPreference low;
|
||||||
AdvHomeAgentFlag off;
|
AdvHomeAgentFlag off;
|
||||||
""" % ifc.name
|
"""
|
||||||
|
% ifc.name
|
||||||
|
)
|
||||||
for prefix in prefixes:
|
for prefix in prefixes:
|
||||||
if prefix == "":
|
if prefix == "":
|
||||||
continue
|
continue
|
||||||
cfg += """\
|
cfg += (
|
||||||
|
"""\
|
||||||
prefix %s
|
prefix %s
|
||||||
{
|
{
|
||||||
AdvOnLink on;
|
AdvOnLink on;
|
||||||
AdvAutonomous on;
|
AdvAutonomous on;
|
||||||
AdvRouterAddr on;
|
AdvRouterAddr on;
|
||||||
};
|
};
|
||||||
""" % prefix
|
"""
|
||||||
|
% prefix
|
||||||
|
)
|
||||||
cfg += "};\n"
|
cfg += "};\n"
|
||||||
return cfg
|
return cfg
|
||||||
|
|
||||||
|
@ -662,6 +710,7 @@ class AtdService(UtilService):
|
||||||
"""
|
"""
|
||||||
Atd service for scheduling at jobs
|
Atd service for scheduling at jobs
|
||||||
"""
|
"""
|
||||||
|
|
||||||
name = "atd"
|
name = "atd"
|
||||||
configs = ("startatd.sh",)
|
configs = ("startatd.sh",)
|
||||||
dirs = ("/var/spool/cron/atjobs", "/var/spool/cron/atspool")
|
dirs = ("/var/spool/cron/atjobs", "/var/spool/cron/atspool")
|
||||||
|
@ -683,5 +732,6 @@ class UserDefinedService(UtilService):
|
||||||
"""
|
"""
|
||||||
Dummy service allowing customization of anything.
|
Dummy service allowing customization of anything.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
name = "UserDefined"
|
name = "UserDefined"
|
||||||
meta = "Customize this service to do anything upon startup."
|
meta = "Customize this service to do anything upon startup."
|
||||||
|
|
|
@ -12,12 +12,16 @@ class XorpRtrmgr(CoreService):
|
||||||
XORP router manager service builds a config.boot file based on other
|
XORP router manager service builds a config.boot file based on other
|
||||||
enabled XORP services, and launches necessary daemons upon startup.
|
enabled XORP services, and launches necessary daemons upon startup.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
name = "xorp_rtrmgr"
|
name = "xorp_rtrmgr"
|
||||||
executables = ("xorp_rtrmgr",)
|
executables = ("xorp_rtrmgr",)
|
||||||
group = "XORP"
|
group = "XORP"
|
||||||
dirs = ("/etc/xorp",)
|
dirs = ("/etc/xorp",)
|
||||||
configs = ("/etc/xorp/config.boot",)
|
configs = ("/etc/xorp/config.boot",)
|
||||||
startup = ("xorp_rtrmgr -d -b %s -l /var/log/%s.log -P /var/run/%s.pid" % (configs[0], name, name),)
|
startup = (
|
||||||
|
"xorp_rtrmgr -d -b %s -l /var/log/%s.log -P /var/run/%s.pid"
|
||||||
|
% (configs[0], name, name),
|
||||||
|
)
|
||||||
shutdown = ("killall xorp_rtrmgr",)
|
shutdown = ("killall xorp_rtrmgr",)
|
||||||
validate = ("pidof xorp_rtrmgr",)
|
validate = ("pidof xorp_rtrmgr",)
|
||||||
|
|
||||||
|
@ -74,6 +78,7 @@ class XorpService(CoreService):
|
||||||
Parent class for XORP services. Defines properties and methods
|
Parent class for XORP services. Defines properties and methods
|
||||||
common to XORP's routing daemons.
|
common to XORP's routing daemons.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
name = None
|
name = None
|
||||||
executables = ("xorp_rtrmgr",)
|
executables = ("xorp_rtrmgr",)
|
||||||
group = "XORP"
|
group = "XORP"
|
||||||
|
@ -103,7 +108,7 @@ class XorpService(CoreService):
|
||||||
"""
|
"""
|
||||||
names = []
|
names = []
|
||||||
for ifc in ifcs:
|
for ifc in ifcs:
|
||||||
if hasattr(ifc, 'control') and ifc.control is True:
|
if hasattr(ifc, "control") and ifc.control is True:
|
||||||
continue
|
continue
|
||||||
names.append(ifc.name)
|
names.append(ifc.name)
|
||||||
names.append("register_vif")
|
names.append("register_vif")
|
||||||
|
@ -129,7 +134,7 @@ class XorpService(CoreService):
|
||||||
cfg += " policy-statement export-connected {\n"
|
cfg += " policy-statement export-connected {\n"
|
||||||
cfg += "\tterm 100 {\n"
|
cfg += "\tterm 100 {\n"
|
||||||
cfg += "\t from {\n"
|
cfg += "\t from {\n"
|
||||||
cfg += "\t\tprotocol: \"connected\"\n"
|
cfg += '\t\tprotocol: "connected"\n'
|
||||||
cfg += "\t }\n"
|
cfg += "\t }\n"
|
||||||
cfg += "\t}\n"
|
cfg += "\t}\n"
|
||||||
cfg += " }\n"
|
cfg += " }\n"
|
||||||
|
@ -142,11 +147,11 @@ class XorpService(CoreService):
|
||||||
Helper to return the first IPv4 address of a node as its router ID.
|
Helper to return the first IPv4 address of a node as its router ID.
|
||||||
"""
|
"""
|
||||||
for ifc in node.netifs():
|
for ifc in node.netifs():
|
||||||
if hasattr(ifc, 'control') and ifc.control is True:
|
if hasattr(ifc, "control") and ifc.control is True:
|
||||||
continue
|
continue
|
||||||
for a in ifc.addrlist:
|
for a in ifc.addrlist:
|
||||||
if a.find(".") >= 0:
|
if a.find(".") >= 0:
|
||||||
return a.split('/')[0]
|
return a.split("/")[0]
|
||||||
# raise ValueError, "no IPv4 address found for router ID"
|
# raise ValueError, "no IPv4 address found for router ID"
|
||||||
return "0.0.0.0"
|
return "0.0.0.0"
|
||||||
|
|
||||||
|
@ -165,6 +170,7 @@ class XorpOspfv2(XorpService):
|
||||||
not build its own configuration file but has hooks for adding to the
|
not build its own configuration file but has hooks for adding to the
|
||||||
unified XORP configuration file.
|
unified XORP configuration file.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
name = "XORP_OSPFv2"
|
name = "XORP_OSPFv2"
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
|
@ -176,7 +182,7 @@ class XorpOspfv2(XorpService):
|
||||||
cfg += "\trouter-id: %s\n" % rtrid
|
cfg += "\trouter-id: %s\n" % rtrid
|
||||||
cfg += "\tarea 0.0.0.0 {\n"
|
cfg += "\tarea 0.0.0.0 {\n"
|
||||||
for ifc in node.netifs():
|
for ifc in node.netifs():
|
||||||
if hasattr(ifc, 'control') and ifc.control is True:
|
if hasattr(ifc, "control") and ifc.control is True:
|
||||||
continue
|
continue
|
||||||
cfg += "\t interface %s {\n" % ifc.name
|
cfg += "\t interface %s {\n" % ifc.name
|
||||||
cfg += "\t\tvif %s {\n" % ifc.name
|
cfg += "\t\tvif %s {\n" % ifc.name
|
||||||
|
@ -200,6 +206,7 @@ class XorpOspfv3(XorpService):
|
||||||
not build its own configuration file but has hooks for adding to the
|
not build its own configuration file but has hooks for adding to the
|
||||||
unified XORP configuration file.
|
unified XORP configuration file.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
name = "XORP_OSPFv3"
|
name = "XORP_OSPFv3"
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
|
@ -211,7 +218,7 @@ class XorpOspfv3(XorpService):
|
||||||
cfg += "\trouter-id: %s\n" % rtrid
|
cfg += "\trouter-id: %s\n" % rtrid
|
||||||
cfg += "\tarea 0.0.0.0 {\n"
|
cfg += "\tarea 0.0.0.0 {\n"
|
||||||
for ifc in node.netifs():
|
for ifc in node.netifs():
|
||||||
if hasattr(ifc, 'control') and ifc.control is True:
|
if hasattr(ifc, "control") and ifc.control is True:
|
||||||
continue
|
continue
|
||||||
cfg += "\t interface %s {\n" % ifc.name
|
cfg += "\t interface %s {\n" % ifc.name
|
||||||
cfg += "\t\tvif %s {\n" % ifc.name
|
cfg += "\t\tvif %s {\n" % ifc.name
|
||||||
|
@ -227,6 +234,7 @@ class XorpBgp(XorpService):
|
||||||
"""
|
"""
|
||||||
IPv4 inter-domain routing. AS numbers and peers must be customized.
|
IPv4 inter-domain routing. AS numbers and peers must be customized.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
name = "XORP_BGP"
|
name = "XORP_BGP"
|
||||||
custom_needed = True
|
custom_needed = True
|
||||||
|
|
||||||
|
@ -241,7 +249,7 @@ class XorpBgp(XorpService):
|
||||||
cfg += " bgp {\n"
|
cfg += " bgp {\n"
|
||||||
cfg += "\tbgp-id: %s\n" % rtrid
|
cfg += "\tbgp-id: %s\n" % rtrid
|
||||||
cfg += "\tlocal-as: 65001 /* change this */\n"
|
cfg += "\tlocal-as: 65001 /* change this */\n"
|
||||||
cfg += "\texport: \"export-connected\"\n"
|
cfg += '\texport: "export-connected"\n'
|
||||||
cfg += "\tpeer 10.0.1.1 { /* change this */\n"
|
cfg += "\tpeer 10.0.1.1 { /* change this */\n"
|
||||||
cfg += "\t local-ip: 10.0.1.1\n"
|
cfg += "\t local-ip: 10.0.1.1\n"
|
||||||
cfg += "\t as: 65002\n"
|
cfg += "\t as: 65002\n"
|
||||||
|
@ -265,9 +273,9 @@ class XorpRip(XorpService):
|
||||||
cfg += cls.policyexportconnected()
|
cfg += cls.policyexportconnected()
|
||||||
cfg += "\nprotocols {\n"
|
cfg += "\nprotocols {\n"
|
||||||
cfg += " rip {\n"
|
cfg += " rip {\n"
|
||||||
cfg += "\texport: \"export-connected\"\n"
|
cfg += '\texport: "export-connected"\n'
|
||||||
for ifc in node.netifs():
|
for ifc in node.netifs():
|
||||||
if hasattr(ifc, 'control') and ifc.control is True:
|
if hasattr(ifc, "control") and ifc.control is True:
|
||||||
continue
|
continue
|
||||||
cfg += "\tinterface %s {\n" % ifc.name
|
cfg += "\tinterface %s {\n" % ifc.name
|
||||||
cfg += "\t vif %s {\n" % ifc.name
|
cfg += "\t vif %s {\n" % ifc.name
|
||||||
|
@ -289,6 +297,7 @@ class XorpRipng(XorpService):
|
||||||
"""
|
"""
|
||||||
RIP NG IPv6 unicast routing.
|
RIP NG IPv6 unicast routing.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
name = "XORP_RIPNG"
|
name = "XORP_RIPNG"
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
|
@ -297,9 +306,9 @@ class XorpRipng(XorpService):
|
||||||
cfg += cls.policyexportconnected()
|
cfg += cls.policyexportconnected()
|
||||||
cfg += "\nprotocols {\n"
|
cfg += "\nprotocols {\n"
|
||||||
cfg += " ripng {\n"
|
cfg += " ripng {\n"
|
||||||
cfg += "\texport: \"export-connected\"\n"
|
cfg += '\texport: "export-connected"\n'
|
||||||
for ifc in node.netifs():
|
for ifc in node.netifs():
|
||||||
if hasattr(ifc, 'control') and ifc.control is True:
|
if hasattr(ifc, "control") and ifc.control is True:
|
||||||
continue
|
continue
|
||||||
cfg += "\tinterface %s {\n" % ifc.name
|
cfg += "\tinterface %s {\n" % ifc.name
|
||||||
cfg += "\t vif %s {\n" % ifc.name
|
cfg += "\t vif %s {\n" % ifc.name
|
||||||
|
@ -324,6 +333,7 @@ class XorpPimSm4(XorpService):
|
||||||
"""
|
"""
|
||||||
PIM Sparse Mode IPv4 multicast routing.
|
PIM Sparse Mode IPv4 multicast routing.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
name = "XORP_PIMSM4"
|
name = "XORP_PIMSM4"
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
|
@ -334,7 +344,7 @@ class XorpPimSm4(XorpService):
|
||||||
cfg += " igmp {\n"
|
cfg += " igmp {\n"
|
||||||
names = []
|
names = []
|
||||||
for ifc in node.netifs():
|
for ifc in node.netifs():
|
||||||
if hasattr(ifc, 'control') and ifc.control is True:
|
if hasattr(ifc, "control") and ifc.control is True:
|
||||||
continue
|
continue
|
||||||
names.append(ifc.name)
|
names.append(ifc.name)
|
||||||
cfg += "\tinterface %s {\n" % ifc.name
|
cfg += "\tinterface %s {\n" % ifc.name
|
||||||
|
@ -358,12 +368,12 @@ class XorpPimSm4(XorpService):
|
||||||
cfg += "\tbootstrap {\n"
|
cfg += "\tbootstrap {\n"
|
||||||
cfg += "\t cand-bsr {\n"
|
cfg += "\t cand-bsr {\n"
|
||||||
cfg += "\t\tscope-zone 224.0.0.0/4 {\n"
|
cfg += "\t\tscope-zone 224.0.0.0/4 {\n"
|
||||||
cfg += "\t\t cand-bsr-by-vif-name: \"%s\"\n" % names[0]
|
cfg += '\t\t cand-bsr-by-vif-name: "%s"\n' % names[0]
|
||||||
cfg += "\t\t}\n"
|
cfg += "\t\t}\n"
|
||||||
cfg += "\t }\n"
|
cfg += "\t }\n"
|
||||||
cfg += "\t cand-rp {\n"
|
cfg += "\t cand-rp {\n"
|
||||||
cfg += "\t\tgroup-prefix 224.0.0.0/4 {\n"
|
cfg += "\t\tgroup-prefix 224.0.0.0/4 {\n"
|
||||||
cfg += "\t\t cand-rp-by-vif-name: \"%s\"\n" % names[0]
|
cfg += '\t\t cand-rp-by-vif-name: "%s"\n' % names[0]
|
||||||
cfg += "\t\t}\n"
|
cfg += "\t\t}\n"
|
||||||
cfg += "\t }\n"
|
cfg += "\t }\n"
|
||||||
cfg += "\t}\n"
|
cfg += "\t}\n"
|
||||||
|
@ -383,6 +393,7 @@ class XorpPimSm6(XorpService):
|
||||||
"""
|
"""
|
||||||
PIM Sparse Mode IPv6 multicast routing.
|
PIM Sparse Mode IPv6 multicast routing.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
name = "XORP_PIMSM6"
|
name = "XORP_PIMSM6"
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
|
@ -393,7 +404,7 @@ class XorpPimSm6(XorpService):
|
||||||
cfg += " mld {\n"
|
cfg += " mld {\n"
|
||||||
names = []
|
names = []
|
||||||
for ifc in node.netifs():
|
for ifc in node.netifs():
|
||||||
if hasattr(ifc, 'control') and ifc.control is True:
|
if hasattr(ifc, "control") and ifc.control is True:
|
||||||
continue
|
continue
|
||||||
names.append(ifc.name)
|
names.append(ifc.name)
|
||||||
cfg += "\tinterface %s {\n" % ifc.name
|
cfg += "\tinterface %s {\n" % ifc.name
|
||||||
|
@ -417,12 +428,12 @@ class XorpPimSm6(XorpService):
|
||||||
cfg += "\tbootstrap {\n"
|
cfg += "\tbootstrap {\n"
|
||||||
cfg += "\t cand-bsr {\n"
|
cfg += "\t cand-bsr {\n"
|
||||||
cfg += "\t\tscope-zone ff00::/8 {\n"
|
cfg += "\t\tscope-zone ff00::/8 {\n"
|
||||||
cfg += "\t\t cand-bsr-by-vif-name: \"%s\"\n" % names[0]
|
cfg += '\t\t cand-bsr-by-vif-name: "%s"\n' % names[0]
|
||||||
cfg += "\t\t}\n"
|
cfg += "\t\t}\n"
|
||||||
cfg += "\t }\n"
|
cfg += "\t }\n"
|
||||||
cfg += "\t cand-rp {\n"
|
cfg += "\t cand-rp {\n"
|
||||||
cfg += "\t\tgroup-prefix ff00::/8 {\n"
|
cfg += "\t\tgroup-prefix ff00::/8 {\n"
|
||||||
cfg += "\t\t cand-rp-by-vif-name: \"%s\"\n" % names[0]
|
cfg += '\t\t cand-rp-by-vif-name: "%s"\n' % names[0]
|
||||||
cfg += "\t\t}\n"
|
cfg += "\t\t}\n"
|
||||||
cfg += "\t }\n"
|
cfg += "\t }\n"
|
||||||
cfg += "\t}\n"
|
cfg += "\t}\n"
|
||||||
|
@ -442,6 +453,7 @@ class XorpOlsr(XorpService):
|
||||||
"""
|
"""
|
||||||
OLSR IPv4 unicast MANET routing.
|
OLSR IPv4 unicast MANET routing.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
name = "XORP_OLSR"
|
name = "XORP_OLSR"
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
|
@ -452,7 +464,7 @@ class XorpOlsr(XorpService):
|
||||||
cfg += " olsr4 {\n"
|
cfg += " olsr4 {\n"
|
||||||
cfg += "\tmain-address: %s\n" % rtrid
|
cfg += "\tmain-address: %s\n" % rtrid
|
||||||
for ifc in node.netifs():
|
for ifc in node.netifs():
|
||||||
if hasattr(ifc, 'control') and ifc.control is True:
|
if hasattr(ifc, "control") and ifc.control is True:
|
||||||
continue
|
continue
|
||||||
cfg += "\tinterface %s {\n" % ifc.name
|
cfg += "\tinterface %s {\n" % ifc.name
|
||||||
cfg += "\t vif %s {\n" % ifc.name
|
cfg += "\t vif %s {\n" % ifc.name
|
||||||
|
|
|
@ -11,6 +11,7 @@ import os
|
||||||
import shlex
|
import shlex
|
||||||
import subprocess
|
import subprocess
|
||||||
import sys
|
import sys
|
||||||
|
|
||||||
from past.builtins import basestring
|
from past.builtins import basestring
|
||||||
|
|
||||||
from core import CoreCommandError
|
from core import CoreCommandError
|
||||||
|
@ -30,10 +31,7 @@ def execute_file(path, exec_globals=None, exec_locals=None):
|
||||||
"""
|
"""
|
||||||
if exec_globals is None:
|
if exec_globals is None:
|
||||||
exec_globals = {}
|
exec_globals = {}
|
||||||
exec_globals.update({
|
exec_globals.update({"__file__": path, "__name__": "__main__"})
|
||||||
"__file__": path,
|
|
||||||
"__name__": "__main__"
|
|
||||||
})
|
|
||||||
with open(path, "rb") as f:
|
with open(path, "rb") as f:
|
||||||
data = compile(f.read(), path, "exec")
|
data = compile(f.read(), path, "exec")
|
||||||
exec(data, exec_globals, exec_locals)
|
exec(data, exec_globals, exec_locals)
|
||||||
|
@ -157,7 +155,7 @@ def make_tuple(obj):
|
||||||
if hasattr(obj, "__iter__"):
|
if hasattr(obj, "__iter__"):
|
||||||
return tuple(obj)
|
return tuple(obj)
|
||||||
else:
|
else:
|
||||||
return obj,
|
return (obj,)
|
||||||
|
|
||||||
|
|
||||||
def make_tuple_fromstr(s, value_type):
|
def make_tuple_fromstr(s, value_type):
|
||||||
|
@ -290,7 +288,10 @@ def hex_dump(s, bytes_per_word=2, words_per_line=8):
|
||||||
while s:
|
while s:
|
||||||
line = s[:total_bytes]
|
line = s[:total_bytes]
|
||||||
s = s[total_bytes:]
|
s = s[total_bytes:]
|
||||||
tmp = map(lambda x: ("%02x" * bytes_per_word) % x, zip(*[iter(map(ord, line))] * bytes_per_word))
|
tmp = map(
|
||||||
|
lambda x: ("%02x" * bytes_per_word) % x,
|
||||||
|
zip(*[iter(map(ord, line))] * bytes_per_word),
|
||||||
|
)
|
||||||
if len(line) % 2:
|
if len(line) % 2:
|
||||||
tmp.append("%x" % ord(line[-1]))
|
tmp.append("%x" % ord(line[-1]))
|
||||||
dump += "0x%08x: %s\n" % (count, " ".join(tmp))
|
dump += "0x%08x: %s\n" % (count, " ".join(tmp))
|
||||||
|
@ -437,7 +438,9 @@ def load_classes(path, clazz):
|
||||||
for member in members:
|
for member in members:
|
||||||
valid_class = member[1]
|
valid_class = member[1]
|
||||||
classes.append(valid_class)
|
classes.append(valid_class)
|
||||||
except:
|
except Exception:
|
||||||
logging.exception("unexpected error during import, skipping: %s", import_statement)
|
logging.exception(
|
||||||
|
"unexpected error during import, skipping: %s", import_statement
|
||||||
|
)
|
||||||
|
|
||||||
return classes
|
return classes
|
||||||
|
|
|
@ -1,19 +1,24 @@
|
||||||
import logging
|
import logging
|
||||||
from core.nodes.base import CoreNetworkBase
|
|
||||||
from lxml import etree
|
from lxml import etree
|
||||||
|
|
||||||
import core.nodes.base
|
import core.nodes.base
|
||||||
import core.nodes.physical
|
import core.nodes.physical
|
||||||
from core.emulator.emudata import InterfaceData
|
from core.emulator.emudata import InterfaceData, LinkOptions, NodeOptions
|
||||||
from core.emulator.emudata import LinkOptions
|
|
||||||
from core.emulator.emudata import NodeOptions
|
|
||||||
from core.emulator.enumerations import NodeTypes
|
from core.emulator.enumerations import NodeTypes
|
||||||
from core.nodes import nodeutils
|
from core.nodes import nodeutils
|
||||||
|
from core.nodes.base import CoreNetworkBase
|
||||||
from core.nodes.ipaddress import MacAddress
|
from core.nodes.ipaddress import MacAddress
|
||||||
|
|
||||||
|
|
||||||
def write_xml_file(xml_element, file_path, doctype=None):
|
def write_xml_file(xml_element, file_path, doctype=None):
|
||||||
xml_data = etree.tostring(xml_element, xml_declaration=True, pretty_print=True, encoding="UTF-8", doctype=doctype)
|
xml_data = etree.tostring(
|
||||||
|
xml_element,
|
||||||
|
xml_declaration=True,
|
||||||
|
pretty_print=True,
|
||||||
|
encoding="UTF-8",
|
||||||
|
doctype=doctype,
|
||||||
|
)
|
||||||
with open(file_path, "wb") as xml_file:
|
with open(file_path, "wb") as xml_file:
|
||||||
xml_file.write(xml_data)
|
xml_file.write(xml_data)
|
||||||
|
|
||||||
|
@ -252,7 +257,9 @@ class CoreXmlWriter(object):
|
||||||
|
|
||||||
# write out generated xml
|
# write out generated xml
|
||||||
xml_tree = etree.ElementTree(self.scenario)
|
xml_tree = etree.ElementTree(self.scenario)
|
||||||
xml_tree.write(file_name, xml_declaration=True, pretty_print=True, encoding="UTF-8")
|
xml_tree.write(
|
||||||
|
file_name, xml_declaration=True, pretty_print=True, encoding="UTF-8"
|
||||||
|
)
|
||||||
|
|
||||||
def write_session_origin(self):
|
def write_session_origin(self):
|
||||||
# origin: geolocation of cartesian coordinate 0,0,0
|
# origin: geolocation of cartesian coordinate 0,0,0
|
||||||
|
@ -327,12 +334,18 @@ class CoreXmlWriter(object):
|
||||||
|
|
||||||
for model_name in all_configs:
|
for model_name in all_configs:
|
||||||
config = all_configs[model_name]
|
config = all_configs[model_name]
|
||||||
logging.info("writing emane config node(%s) model(%s)", node_id, model_name)
|
logging.debug(
|
||||||
|
"writing emane config node(%s) model(%s)", node_id, model_name
|
||||||
|
)
|
||||||
if model_name == -1:
|
if model_name == -1:
|
||||||
emane_configuration = create_emane_config(node_id, self.session.emane.emane_config, config)
|
emane_configuration = create_emane_config(
|
||||||
|
node_id, self.session.emane.emane_config, config
|
||||||
|
)
|
||||||
else:
|
else:
|
||||||
model = self.session.emane.models[model_name]
|
model = self.session.emane.models[model_name]
|
||||||
emane_configuration = create_emane_model_config(node_id, model, config)
|
emane_configuration = create_emane_model_config(
|
||||||
|
node_id, model, config
|
||||||
|
)
|
||||||
emane_configurations.append(emane_configuration)
|
emane_configurations.append(emane_configuration)
|
||||||
|
|
||||||
if emane_configurations.getchildren():
|
if emane_configurations.getchildren():
|
||||||
|
@ -347,8 +360,12 @@ class CoreXmlWriter(object):
|
||||||
|
|
||||||
for model_name in all_configs:
|
for model_name in all_configs:
|
||||||
config = all_configs[model_name]
|
config = all_configs[model_name]
|
||||||
logging.info("writing mobility config node(%s) model(%s)", node_id, model_name)
|
logging.debug(
|
||||||
mobility_configuration = etree.SubElement(mobility_configurations, "mobility_configuration")
|
"writing mobility config node(%s) model(%s)", node_id, model_name
|
||||||
|
)
|
||||||
|
mobility_configuration = etree.SubElement(
|
||||||
|
mobility_configurations, "mobility_configuration"
|
||||||
|
)
|
||||||
add_attribute(mobility_configuration, "node", node_id)
|
add_attribute(mobility_configuration, "node", node_id)
|
||||||
add_attribute(mobility_configuration, "model", model_name)
|
add_attribute(mobility_configuration, "model", model_name)
|
||||||
for name in config:
|
for name in config:
|
||||||
|
@ -388,15 +405,15 @@ class CoreXmlWriter(object):
|
||||||
for node_id in self.session.nodes:
|
for node_id in self.session.nodes:
|
||||||
node = self.session.nodes[node_id]
|
node = self.session.nodes[node_id]
|
||||||
# network node
|
# network node
|
||||||
is_network_or_rj45 = isinstance(node, (core.nodes.base.CoreNetworkBase, core.nodes.physical.Rj45Node))
|
is_network_or_rj45 = isinstance(
|
||||||
|
node, (core.nodes.base.CoreNetworkBase, core.nodes.physical.Rj45Node)
|
||||||
|
)
|
||||||
is_controlnet = nodeutils.is_node(node, NodeTypes.CONTROL_NET)
|
is_controlnet = nodeutils.is_node(node, NodeTypes.CONTROL_NET)
|
||||||
if is_network_or_rj45 and not is_controlnet:
|
if is_network_or_rj45 and not is_controlnet:
|
||||||
self.write_network(node)
|
self.write_network(node)
|
||||||
# device node
|
# device node
|
||||||
elif isinstance(node, core.nodes.base.CoreNodeBase):
|
elif isinstance(node, core.nodes.base.CoreNodeBase):
|
||||||
self.write_device(node)
|
self.write_device(node)
|
||||||
else:
|
|
||||||
logging.error("unknown node: %s", node)
|
|
||||||
|
|
||||||
# add known links
|
# add known links
|
||||||
links.extend(node.all_link_data(0))
|
links.extend(node.all_link_data(0))
|
||||||
|
@ -406,7 +423,6 @@ class CoreXmlWriter(object):
|
||||||
def write_network(self, node):
|
def write_network(self, node):
|
||||||
# ignore p2p and other nodes that are not part of the api
|
# ignore p2p and other nodes that are not part of the api
|
||||||
if not node.apitype:
|
if not node.apitype:
|
||||||
logging.warning("ignoring node with no apitype: %s", node)
|
|
||||||
return
|
return
|
||||||
|
|
||||||
network = NetworkElement(self.session, node)
|
network = NetworkElement(self.session, node)
|
||||||
|
@ -430,7 +446,9 @@ class CoreXmlWriter(object):
|
||||||
device = DeviceElement(self.session, node)
|
device = DeviceElement(self.session, node)
|
||||||
self.devices.append(device.element)
|
self.devices.append(device.element)
|
||||||
|
|
||||||
def create_interface_element(self, element_name, node_id, interface_id, mac, ip4, ip4_mask, ip6, ip6_mask):
|
def create_interface_element(
|
||||||
|
self, element_name, node_id, interface_id, mac, ip4, ip4_mask, ip6, ip6_mask
|
||||||
|
):
|
||||||
interface = etree.Element(element_name)
|
interface = etree.Element(element_name)
|
||||||
node = self.session.get_node(node_id)
|
node = self.session.get_node(node_id)
|
||||||
interface_name = None
|
interface_name = None
|
||||||
|
@ -468,7 +486,7 @@ class CoreXmlWriter(object):
|
||||||
link_data.interface1_ip4,
|
link_data.interface1_ip4,
|
||||||
link_data.interface1_ip4_mask,
|
link_data.interface1_ip4_mask,
|
||||||
link_data.interface1_ip6,
|
link_data.interface1_ip6,
|
||||||
link_data.interface1_ip6_mask
|
link_data.interface1_ip6_mask,
|
||||||
)
|
)
|
||||||
link_element.append(interface_one)
|
link_element.append(interface_one)
|
||||||
|
|
||||||
|
@ -482,7 +500,7 @@ class CoreXmlWriter(object):
|
||||||
link_data.interface2_ip4,
|
link_data.interface2_ip4,
|
||||||
link_data.interface2_ip4_mask,
|
link_data.interface2_ip4_mask,
|
||||||
link_data.interface2_ip6,
|
link_data.interface2_ip6,
|
||||||
link_data.interface2_ip6_mask
|
link_data.interface2_ip6_mask,
|
||||||
)
|
)
|
||||||
link_element.append(interface_two)
|
link_element.append(interface_two)
|
||||||
|
|
||||||
|
@ -541,7 +559,9 @@ class CoreXmlReader(object):
|
||||||
services = []
|
services = []
|
||||||
for service in node.iterchildren():
|
for service in node.iterchildren():
|
||||||
services.append(service.get("name"))
|
services.append(service.get("name"))
|
||||||
logging.info("reading default services for nodes(%s): %s", node_type, services)
|
logging.info(
|
||||||
|
"reading default services for nodes(%s): %s", node_type, services
|
||||||
|
)
|
||||||
self.session.services.default_services[node_type] = services
|
self.session.services.default_services[node_type] = services
|
||||||
|
|
||||||
def read_session_metadata(self):
|
def read_session_metadata(self):
|
||||||
|
@ -581,7 +601,9 @@ class CoreXmlReader(object):
|
||||||
data = hook.text
|
data = hook.text
|
||||||
hook_type = "hook:%s" % state
|
hook_type = "hook:%s" % state
|
||||||
logging.info("reading hook: state(%s) name(%s)", state, name)
|
logging.info("reading hook: state(%s) name(%s)", state, name)
|
||||||
self.session.set_hook(hook_type, file_name=name, source_name=None, data=data)
|
self.session.set_hook(
|
||||||
|
hook_type, file_name=name, source_name=None, data=data
|
||||||
|
)
|
||||||
|
|
||||||
def read_session_origin(self):
|
def read_session_origin(self):
|
||||||
session_origin = self.scenario.find("session_origin")
|
session_origin = self.scenario.find("session_origin")
|
||||||
|
@ -615,7 +637,9 @@ class CoreXmlReader(object):
|
||||||
for service_configuration in service_configurations.iterchildren():
|
for service_configuration in service_configurations.iterchildren():
|
||||||
node_id = get_int(service_configuration, "node")
|
node_id = get_int(service_configuration, "node")
|
||||||
service_name = service_configuration.get("name")
|
service_name = service_configuration.get("name")
|
||||||
logging.info("reading custom service(%s) for node(%s)", service_name, node_id)
|
logging.info(
|
||||||
|
"reading custom service(%s) for node(%s)", service_name, node_id
|
||||||
|
)
|
||||||
self.session.services.set_service(node_id, service_name)
|
self.session.services.set_service(node_id, service_name)
|
||||||
service = self.session.services.get_service(node_id, service_name)
|
service = self.session.services.get_service(node_id, service_name)
|
||||||
|
|
||||||
|
@ -629,11 +653,15 @@ class CoreXmlReader(object):
|
||||||
|
|
||||||
validate_elements = service_configuration.find("validates")
|
validate_elements = service_configuration.find("validates")
|
||||||
if validate_elements is not None:
|
if validate_elements is not None:
|
||||||
service.validate = tuple(x.text for x in validate_elements.iterchildren())
|
service.validate = tuple(
|
||||||
|
x.text for x in validate_elements.iterchildren()
|
||||||
|
)
|
||||||
|
|
||||||
shutdown_elements = service_configuration.find("shutdowns")
|
shutdown_elements = service_configuration.find("shutdowns")
|
||||||
if shutdown_elements is not None:
|
if shutdown_elements is not None:
|
||||||
service.shutdown = tuple(x.text for x in shutdown_elements.iterchildren())
|
service.shutdown = tuple(
|
||||||
|
x.text for x in shutdown_elements.iterchildren()
|
||||||
|
)
|
||||||
|
|
||||||
file_elements = service_configuration.find("files")
|
file_elements = service_configuration.find("files")
|
||||||
if file_elements is not None:
|
if file_elements is not None:
|
||||||
|
@ -670,7 +698,9 @@ class CoreXmlReader(object):
|
||||||
value = config.get("value")
|
value = config.get("value")
|
||||||
configs[name] = value
|
configs[name] = value
|
||||||
|
|
||||||
logging.info("reading emane configuration node(%s) model(%s)", node_id, model_name)
|
logging.info(
|
||||||
|
"reading emane configuration node(%s) model(%s)", node_id, model_name
|
||||||
|
)
|
||||||
self.session.emane.set_model_config(node_id, model_name, configs)
|
self.session.emane.set_model_config(node_id, model_name, configs)
|
||||||
|
|
||||||
def read_mobility_configs(self):
|
def read_mobility_configs(self):
|
||||||
|
@ -688,7 +718,9 @@ class CoreXmlReader(object):
|
||||||
value = config.get("value")
|
value = config.get("value")
|
||||||
configs[name] = value
|
configs[name] = value
|
||||||
|
|
||||||
logging.info("reading mobility configuration node(%s) model(%s)", node_id, model_name)
|
logging.info(
|
||||||
|
"reading mobility configuration node(%s) model(%s)", node_id, model_name
|
||||||
|
)
|
||||||
self.session.mobility.set_model_config(node_id, model_name, configs)
|
self.session.mobility.set_model_config(node_id, model_name, configs)
|
||||||
|
|
||||||
def read_nodes(self):
|
def read_nodes(self):
|
||||||
|
@ -710,7 +742,9 @@ class CoreXmlReader(object):
|
||||||
|
|
||||||
service_elements = device_element.find("services")
|
service_elements = device_element.find("services")
|
||||||
if service_elements is not None:
|
if service_elements is not None:
|
||||||
node_options.services = [x.get("name") for x in service_elements.iterchildren()]
|
node_options.services = [
|
||||||
|
x.get("name") for x in service_elements.iterchildren()
|
||||||
|
]
|
||||||
|
|
||||||
position_element = device_element.find("position")
|
position_element = device_element.find("position")
|
||||||
if position_element is not None:
|
if position_element is not None:
|
||||||
|
@ -747,7 +781,9 @@ class CoreXmlReader(object):
|
||||||
if all([lat, lon, alt]):
|
if all([lat, lon, alt]):
|
||||||
node_options.set_location(lat, lon, alt)
|
node_options.set_location(lat, lon, alt)
|
||||||
|
|
||||||
logging.info("reading node id(%s) node_type(%s) name(%s)", node_id, node_type, name)
|
logging.info(
|
||||||
|
"reading node id(%s) node_type(%s) name(%s)", node_id, node_type, name
|
||||||
|
)
|
||||||
self.session.add_node(_type=node_type, _id=node_id, node_options=node_options)
|
self.session.add_node(_type=node_type, _id=node_id, node_options=node_options)
|
||||||
|
|
||||||
def read_links(self):
|
def read_links(self):
|
||||||
|
@ -791,10 +827,24 @@ class CoreXmlReader(object):
|
||||||
link_options.gui_attributes = options_element.get("gui_attributes")
|
link_options.gui_attributes = options_element.get("gui_attributes")
|
||||||
|
|
||||||
if link_options.unidirectional == 1 and node_set in node_sets:
|
if link_options.unidirectional == 1 and node_set in node_sets:
|
||||||
logging.info("updating link node_one(%s) node_two(%s): %s", node_one, node_two, link_options)
|
logging.info(
|
||||||
self.session.update_link(node_one, node_two, interface_one.id, interface_two.id, link_options)
|
"updating link node_one(%s) node_two(%s): %s",
|
||||||
|
node_one,
|
||||||
|
node_two,
|
||||||
|
link_options,
|
||||||
|
)
|
||||||
|
self.session.update_link(
|
||||||
|
node_one, node_two, interface_one.id, interface_two.id, link_options
|
||||||
|
)
|
||||||
else:
|
else:
|
||||||
logging.info("adding link node_one(%s) node_two(%s): %s", node_one, node_two, link_options)
|
logging.info(
|
||||||
self.session.add_link(node_one, node_two, interface_one, interface_two, link_options)
|
"adding link node_one(%s) node_two(%s): %s",
|
||||||
|
node_one,
|
||||||
|
node_two,
|
||||||
|
link_options,
|
||||||
|
)
|
||||||
|
self.session.add_link(
|
||||||
|
node_one, node_two, interface_one, interface_two, link_options
|
||||||
|
)
|
||||||
|
|
||||||
node_sets.add(node_set)
|
node_sets.add(node_set)
|
||||||
|
|
|
@ -4,9 +4,9 @@ import socket
|
||||||
from lxml import etree
|
from lxml import etree
|
||||||
|
|
||||||
from core import constants, utils
|
from core import constants, utils
|
||||||
from core.nodes.base import CoreNodeBase
|
|
||||||
from core.emulator.enumerations import NodeTypes
|
from core.emulator.enumerations import NodeTypes
|
||||||
from core.nodes import nodeutils, ipaddress
|
from core.nodes import ipaddress, nodeutils
|
||||||
|
from core.nodes.base import CoreNodeBase
|
||||||
|
|
||||||
|
|
||||||
def add_type(parent_element, name):
|
def add_type(parent_element, name):
|
||||||
|
@ -31,16 +31,22 @@ def add_emane_interface(host_element, netif, platform_name="p1", transport_name=
|
||||||
|
|
||||||
# platform data
|
# platform data
|
||||||
platform_id = "%s/%s" % (host_id, platform_name)
|
platform_id = "%s/%s" % (host_id, platform_name)
|
||||||
platform_element = etree.SubElement(host_element, "emanePlatform", id=platform_id, name=platform_name)
|
platform_element = etree.SubElement(
|
||||||
|
host_element, "emanePlatform", id=platform_id, name=platform_name
|
||||||
|
)
|
||||||
|
|
||||||
# transport data
|
# transport data
|
||||||
transport_id = "%s/%s" % (host_id, transport_name)
|
transport_id = "%s/%s" % (host_id, transport_name)
|
||||||
etree.SubElement(platform_element, "transport", id=transport_id, name=transport_name)
|
etree.SubElement(
|
||||||
|
platform_element, "transport", id=transport_id, name=transport_name
|
||||||
|
)
|
||||||
|
|
||||||
# nem data
|
# nem data
|
||||||
nem_name = "nem%s" % nem_id
|
nem_name = "nem%s" % nem_id
|
||||||
nem_element_id = "%s/%s" % (host_id, nem_name)
|
nem_element_id = "%s/%s" % (host_id, nem_name)
|
||||||
nem_element = etree.SubElement(platform_element, "nem", id=nem_element_id, name=nem_name)
|
nem_element = etree.SubElement(
|
||||||
|
platform_element, "nem", id=nem_element_id, name=nem_name
|
||||||
|
)
|
||||||
nem_id_element = etree.SubElement(nem_element, "parameter", name="nemid")
|
nem_id_element = etree.SubElement(nem_element, "parameter", name="nemid")
|
||||||
nem_id_element.text = str(nem_id)
|
nem_id_element.text = str(nem_id)
|
||||||
|
|
||||||
|
@ -81,7 +87,9 @@ class CoreXmlDeployment(object):
|
||||||
def __init__(self, session, scenario):
|
def __init__(self, session, scenario):
|
||||||
self.session = session
|
self.session = session
|
||||||
self.scenario = scenario
|
self.scenario = scenario
|
||||||
self.root = etree.SubElement(scenario, "container", id="TestBed", name="TestBed")
|
self.root = etree.SubElement(
|
||||||
|
scenario, "container", id="TestBed", name="TestBed"
|
||||||
|
)
|
||||||
self.add_deployment()
|
self.add_deployment()
|
||||||
|
|
||||||
def find_device(self, name):
|
def find_device(self, name):
|
||||||
|
@ -89,8 +97,10 @@ class CoreXmlDeployment(object):
|
||||||
return device
|
return device
|
||||||
|
|
||||||
def find_interface(self, device, name):
|
def find_interface(self, device, name):
|
||||||
interface = self.scenario.find("devices/device[@name='%s']/interfaces/interface[@name='%s']" % (
|
interface = self.scenario.find(
|
||||||
device.name, name))
|
"devices/device[@name='%s']/interfaces/interface[@name='%s']"
|
||||||
|
% (device.name, name)
|
||||||
|
)
|
||||||
return interface
|
return interface
|
||||||
|
|
||||||
def add_deployment(self):
|
def add_deployment(self):
|
||||||
|
@ -125,7 +135,9 @@ class CoreXmlDeployment(object):
|
||||||
|
|
||||||
# create virtual host element
|
# create virtual host element
|
||||||
host_id = "%s/%s" % (physical_host.get("id"), node.name)
|
host_id = "%s/%s" % (physical_host.get("id"), node.name)
|
||||||
host_element = etree.SubElement(physical_host, "testHost", id=host_id, name=node.name)
|
host_element = etree.SubElement(
|
||||||
|
physical_host, "testHost", id=host_id, name=node.name
|
||||||
|
)
|
||||||
|
|
||||||
# add host type
|
# add host type
|
||||||
add_type(host_element, "virtual")
|
add_type(host_element, "virtual")
|
||||||
|
|
|
@ -53,7 +53,10 @@ def create_file(xml_element, doc_name, file_path):
|
||||||
:param str file_path: file path to write xml file to
|
:param str file_path: file path to write xml file to
|
||||||
:return: nothing
|
:return: nothing
|
||||||
"""
|
"""
|
||||||
doctype = '<!DOCTYPE %(doc_name)s SYSTEM "file:///usr/share/emane/dtd/%(doc_name)s.dtd">' % {"doc_name": doc_name}
|
doctype = (
|
||||||
|
'<!DOCTYPE %(doc_name)s SYSTEM "file:///usr/share/emane/dtd/%(doc_name)s.dtd">'
|
||||||
|
% {"doc_name": doc_name}
|
||||||
|
)
|
||||||
corexml.write_xml_file(xml_element, file_path, doctype=doctype)
|
corexml.write_xml_file(xml_element, file_path, doctype=doctype)
|
||||||
|
|
||||||
|
|
||||||
|
@ -108,7 +111,12 @@ def build_node_platform_xml(emane_manager, control_net, node, nem_id, platform_x
|
||||||
:return: the next nem id that can be used for creating platform xml files
|
:return: the next nem id that can be used for creating platform xml files
|
||||||
:rtype: int
|
:rtype: int
|
||||||
"""
|
"""
|
||||||
logging.debug("building emane platform xml for node(%s) nem_id(%s): %s", node, nem_id, node.name)
|
logging.debug(
|
||||||
|
"building emane platform xml for node(%s) nem_id(%s): %s",
|
||||||
|
node,
|
||||||
|
nem_id,
|
||||||
|
node.name,
|
||||||
|
)
|
||||||
nem_entries = {}
|
nem_entries = {}
|
||||||
|
|
||||||
if node.model is None:
|
if node.model is None:
|
||||||
|
@ -116,10 +124,14 @@ def build_node_platform_xml(emane_manager, control_net, node, nem_id, platform_x
|
||||||
return nem_entries
|
return nem_entries
|
||||||
|
|
||||||
for netif in node.netifs():
|
for netif in node.netifs():
|
||||||
logging.debug("building platform xml for interface(%s) nem_id(%s)", netif.name, nem_id)
|
logging.debug(
|
||||||
|
"building platform xml for interface(%s) nem_id(%s)", netif.name, nem_id
|
||||||
|
)
|
||||||
# build nem xml
|
# build nem xml
|
||||||
nem_definition = nem_file_name(node.model, netif)
|
nem_definition = nem_file_name(node.model, netif)
|
||||||
nem_element = etree.Element("nem", id=str(nem_id), name=netif.localname, definition=nem_definition)
|
nem_element = etree.Element(
|
||||||
|
"nem", id=str(nem_id), name=netif.localname, definition=nem_definition
|
||||||
|
)
|
||||||
|
|
||||||
# check if this is an external transport, get default config if an interface specific one does not exist
|
# check if this is an external transport, get default config if an interface specific one does not exist
|
||||||
config = emane_manager.getifcconfig(node.model.id, netif, node.model.name)
|
config = emane_manager.getifcconfig(node.model.id, netif, node.model.name)
|
||||||
|
@ -137,7 +149,9 @@ def build_node_platform_xml(emane_manager, control_net, node, nem_id, platform_x
|
||||||
logging.info("warning: %s interface type unsupported!", netif.name)
|
logging.info("warning: %s interface type unsupported!", netif.name)
|
||||||
transport_type = "raw"
|
transport_type = "raw"
|
||||||
transport_file = transport_file_name(node.id, transport_type)
|
transport_file = transport_file_name(node.id, transport_type)
|
||||||
transport_element = etree.SubElement(nem_element, "transport", definition=transport_file)
|
transport_element = etree.SubElement(
|
||||||
|
nem_element, "transport", definition=transport_file
|
||||||
|
)
|
||||||
|
|
||||||
# add transport parameter
|
# add transport parameter
|
||||||
add_param(transport_element, "device", netif.name)
|
add_param(transport_element, "device", netif.name)
|
||||||
|
@ -261,7 +275,7 @@ def build_transport_xml(emane_manager, node, transport_type):
|
||||||
transport_element = etree.Element(
|
transport_element = etree.Element(
|
||||||
"transport",
|
"transport",
|
||||||
name="%s Transport" % transport_type.capitalize(),
|
name="%s Transport" % transport_type.capitalize(),
|
||||||
library="trans%s" % transport_type.lower()
|
library="trans%s" % transport_type.lower(),
|
||||||
)
|
)
|
||||||
|
|
||||||
# add bitrate
|
# add bitrate
|
||||||
|
@ -299,7 +313,9 @@ def create_phy_xml(emane_model, config, file_path):
|
||||||
if emane_model.phy_library:
|
if emane_model.phy_library:
|
||||||
phy_element.set("library", emane_model.phy_library)
|
phy_element.set("library", emane_model.phy_library)
|
||||||
|
|
||||||
add_configurations(phy_element, emane_model.phy_config, config, emane_model.config_ignore)
|
add_configurations(
|
||||||
|
phy_element, emane_model.phy_config, config, emane_model.config_ignore
|
||||||
|
)
|
||||||
create_file(phy_element, "phy", file_path)
|
create_file(phy_element, "phy", file_path)
|
||||||
|
|
||||||
|
|
||||||
|
@ -315,12 +331,18 @@ def create_mac_xml(emane_model, config, file_path):
|
||||||
if not emane_model.mac_library:
|
if not emane_model.mac_library:
|
||||||
raise ValueError("must define emane model library")
|
raise ValueError("must define emane model library")
|
||||||
|
|
||||||
mac_element = etree.Element("mac", name="%s MAC" % emane_model.name, library=emane_model.mac_library)
|
mac_element = etree.Element(
|
||||||
add_configurations(mac_element, emane_model.mac_config, config, emane_model.config_ignore)
|
"mac", name="%s MAC" % emane_model.name, library=emane_model.mac_library
|
||||||
|
)
|
||||||
|
add_configurations(
|
||||||
|
mac_element, emane_model.mac_config, config, emane_model.config_ignore
|
||||||
|
)
|
||||||
create_file(mac_element, "mac", file_path)
|
create_file(mac_element, "mac", file_path)
|
||||||
|
|
||||||
|
|
||||||
def create_nem_xml(emane_model, config, nem_file, transport_definition, mac_definition, phy_definition):
|
def create_nem_xml(
|
||||||
|
emane_model, config, nem_file, transport_definition, mac_definition, phy_definition
|
||||||
|
):
|
||||||
"""
|
"""
|
||||||
Create the nem xml document.
|
Create the nem xml document.
|
||||||
|
|
||||||
|
@ -353,7 +375,13 @@ def create_event_service_xml(group, port, device, file_directory):
|
||||||
:return: nothing
|
:return: nothing
|
||||||
"""
|
"""
|
||||||
event_element = etree.Element("emaneeventmsgsvc")
|
event_element = etree.Element("emaneeventmsgsvc")
|
||||||
for name, value in (("group", group), ("port", port), ("device", device), ("mcloop", "1"), ("ttl", "32")):
|
for name, value in (
|
||||||
|
("group", group),
|
||||||
|
("port", port),
|
||||||
|
("device", device),
|
||||||
|
("mcloop", "1"),
|
||||||
|
("ttl", "32"),
|
||||||
|
):
|
||||||
sub_element = etree.SubElement(event_element, name)
|
sub_element = etree.SubElement(event_element, name)
|
||||||
sub_element.text = value
|
sub_element.text = value
|
||||||
file_name = "libemaneeventservice.xml"
|
file_name = "libemaneeventservice.xml"
|
||||||
|
|
|
@ -3,9 +3,9 @@
|
||||||
# Example CORE Python script that attaches N nodes to an EMANE 802.11abg network.
|
# Example CORE Python script that attaches N nodes to an EMANE 802.11abg network.
|
||||||
|
|
||||||
import datetime
|
import datetime
|
||||||
|
import parser
|
||||||
from builtins import range
|
from builtins import range
|
||||||
|
|
||||||
import parser
|
|
||||||
from core import load_logging_config
|
from core import load_logging_config
|
||||||
from core.emane.ieee80211abg import EmaneIeee80211abgModel
|
from core.emane.ieee80211abg import EmaneIeee80211abgModel
|
||||||
from core.emulator.coreemu import CoreEmu
|
from core.emulator.coreemu import CoreEmu
|
||||||
|
@ -28,8 +28,7 @@ def example(options):
|
||||||
|
|
||||||
# create emane network node
|
# create emane network node
|
||||||
emane_network = session.create_emane_network(
|
emane_network = session.create_emane_network(
|
||||||
model=EmaneIeee80211abgModel,
|
model=EmaneIeee80211abgModel, geo_reference=(47.57917, -122.13232, 2.00000)
|
||||||
geo_reference=(47.57917, -122.13232, 2.00000)
|
|
||||||
)
|
)
|
||||||
emane_network.setposition(x=80, y=50)
|
emane_network.setposition(x=80, y=50)
|
||||||
|
|
||||||
|
@ -48,14 +47,17 @@ def example(options):
|
||||||
node.client.term("bash")
|
node.client.term("bash")
|
||||||
|
|
||||||
# shutdown session
|
# shutdown session
|
||||||
raw_input("press enter to exit...")
|
input("press enter to exit...")
|
||||||
coreemu.shutdown()
|
coreemu.shutdown()
|
||||||
|
|
||||||
|
|
||||||
def main():
|
def main():
|
||||||
options = parser.parse_options("emane80211")
|
options = parser.parse_options("emane80211")
|
||||||
start = datetime.datetime.now()
|
start = datetime.datetime.now()
|
||||||
print("running emane 80211 example: nodes(%s) time(%s)" % (options.nodes, options.time))
|
print(
|
||||||
|
"running emane 80211 example: nodes(%s) time(%s)"
|
||||||
|
% (options.nodes, options.time)
|
||||||
|
)
|
||||||
example(options)
|
example(options)
|
||||||
print("elapsed time: %s" % (datetime.datetime.now() - start))
|
print("elapsed time: %s" % (datetime.datetime.now() - start))
|
||||||
|
|
||||||
|
|
|
@ -7,10 +7,20 @@ DEFAULT_STEP = 1
|
||||||
|
|
||||||
def parse_options(name):
|
def parse_options(name):
|
||||||
parser = argparse.ArgumentParser(description="Run %s example" % name)
|
parser = argparse.ArgumentParser(description="Run %s example" % name)
|
||||||
parser.add_argument("-n", "--nodes", type=int, default=DEFAULT_NODES,
|
parser.add_argument(
|
||||||
help="number of nodes to create in this example")
|
"-n",
|
||||||
parser.add_argument("-t", "--time", type=int, default=DEFAULT_TIME,
|
"--nodes",
|
||||||
help="example iperf run time in seconds")
|
type=int,
|
||||||
|
default=DEFAULT_NODES,
|
||||||
|
help="number of nodes to create in this example",
|
||||||
|
)
|
||||||
|
parser.add_argument(
|
||||||
|
"-t",
|
||||||
|
"--time",
|
||||||
|
type=int,
|
||||||
|
default=DEFAULT_TIME,
|
||||||
|
help="example iperf run time in seconds",
|
||||||
|
)
|
||||||
|
|
||||||
options = parser.parse_args()
|
options = parser.parse_args()
|
||||||
|
|
||||||
|
|
|
@ -6,13 +6,13 @@
|
||||||
# nodestep
|
# nodestep
|
||||||
|
|
||||||
import datetime
|
import datetime
|
||||||
|
import parser
|
||||||
from builtins import range
|
from builtins import range
|
||||||
|
|
||||||
import parser
|
|
||||||
from core import load_logging_config
|
from core import load_logging_config
|
||||||
from core.emulator.coreemu import CoreEmu
|
from core.emulator.coreemu import CoreEmu
|
||||||
from core.emulator.emudata import IpPrefixes
|
from core.emulator.emudata import IpPrefixes
|
||||||
from core.emulator.enumerations import NodeTypes, EventTypes
|
from core.emulator.enumerations import EventTypes, NodeTypes
|
||||||
|
|
||||||
load_logging_config()
|
load_logging_config()
|
||||||
|
|
||||||
|
|
|
@ -8,7 +8,7 @@ from builtins import range
|
||||||
|
|
||||||
from core import load_logging_config
|
from core import load_logging_config
|
||||||
from core.emulator.emudata import IpPrefixes
|
from core.emulator.emudata import IpPrefixes
|
||||||
from core.emulator.enumerations import NodeTypes, EventTypes
|
from core.emulator.enumerations import EventTypes, NodeTypes
|
||||||
|
|
||||||
load_logging_config()
|
load_logging_config()
|
||||||
|
|
||||||
|
|
|
@ -6,13 +6,13 @@
|
||||||
# nodestep
|
# nodestep
|
||||||
|
|
||||||
import datetime
|
import datetime
|
||||||
|
import parser
|
||||||
from builtins import range
|
from builtins import range
|
||||||
|
|
||||||
import parser
|
|
||||||
from core import load_logging_config
|
from core import load_logging_config
|
||||||
from core.emulator.coreemu import CoreEmu
|
from core.emulator.coreemu import CoreEmu
|
||||||
from core.emulator.emudata import IpPrefixes, NodeOptions
|
from core.emulator.emudata import IpPrefixes, NodeOptions
|
||||||
from core.emulator.enumerations import NodeTypes, EventTypes
|
from core.emulator.enumerations import EventTypes, NodeTypes
|
||||||
from core.location.mobility import BasicRangeModel
|
from core.location.mobility import BasicRangeModel
|
||||||
|
|
||||||
load_logging_config()
|
load_logging_config()
|
||||||
|
|
30
daemon/examples/docker/README.md
Normal file
30
daemon/examples/docker/README.md
Normal file
|
@ -0,0 +1,30 @@
|
||||||
|
# Docker Support
|
||||||
|
|
||||||
|
Information on how Docker can be leveraged and included to create
|
||||||
|
nodes based on Docker containers and images to interface with
|
||||||
|
existing CORE nodes, when needed.
|
||||||
|
|
||||||
|
# Installation
|
||||||
|
|
||||||
|
```shell
|
||||||
|
sudo apt install docker.io
|
||||||
|
```
|
||||||
|
|
||||||
|
# Configuration
|
||||||
|
|
||||||
|
Custom configuration required to avoid iptable rules being added and removing
|
||||||
|
the need for the default docker network, since core will be orchestrating
|
||||||
|
connections between nodes.
|
||||||
|
|
||||||
|
Place the file below in **/etc/docker/**
|
||||||
|
* daemon.json
|
||||||
|
|
||||||
|
# Tools and Versions Tested With
|
||||||
|
|
||||||
|
* Docker version 18.09.5, build e8ff056
|
||||||
|
* nsenter from util-linux 2.31.1
|
||||||
|
|
||||||
|
# Examples
|
||||||
|
|
||||||
|
This directory provides a few small examples creating Docker nodes
|
||||||
|
and linking them to themselves or with standard CORE nodes.
|
5
daemon/examples/docker/daemon.json
Normal file
5
daemon/examples/docker/daemon.json
Normal file
|
@ -0,0 +1,5 @@
|
||||||
|
{
|
||||||
|
"bridge": "none",
|
||||||
|
"iptables": false
|
||||||
|
|
||||||
|
}
|
32
daemon/examples/docker/docker2core.py
Normal file
32
daemon/examples/docker/docker2core.py
Normal file
|
@ -0,0 +1,32 @@
|
||||||
|
import logging
|
||||||
|
|
||||||
|
from core.emulator.coreemu import CoreEmu
|
||||||
|
from core.emulator.emudata import IpPrefixes, NodeOptions
|
||||||
|
from core.emulator.enumerations import EventTypes, NodeTypes
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
logging.basicConfig(level=logging.DEBUG)
|
||||||
|
coreemu = CoreEmu()
|
||||||
|
session = coreemu.create_session()
|
||||||
|
session.set_state(EventTypes.CONFIGURATION_STATE)
|
||||||
|
|
||||||
|
try:
|
||||||
|
prefixes = IpPrefixes(ip4_prefix="10.83.0.0/16")
|
||||||
|
options = NodeOptions(model=None, image="ubuntu")
|
||||||
|
|
||||||
|
# create node one
|
||||||
|
node_one = session.add_node(_type=NodeTypes.DOCKER, node_options=options)
|
||||||
|
interface_one = prefixes.create_interface(node_one)
|
||||||
|
|
||||||
|
# create node two
|
||||||
|
node_two = session.add_node()
|
||||||
|
interface_two = prefixes.create_interface(node_two)
|
||||||
|
|
||||||
|
# add link
|
||||||
|
session.add_link(node_one.id, node_two.id, interface_one, interface_two)
|
||||||
|
|
||||||
|
# instantiate
|
||||||
|
session.instantiate()
|
||||||
|
finally:
|
||||||
|
input("continue to shutdown")
|
||||||
|
coreemu.shutdown()
|
34
daemon/examples/docker/docker2docker.py
Normal file
34
daemon/examples/docker/docker2docker.py
Normal file
|
@ -0,0 +1,34 @@
|
||||||
|
import logging
|
||||||
|
|
||||||
|
from core.emulator.coreemu import CoreEmu
|
||||||
|
from core.emulator.emudata import IpPrefixes, NodeOptions
|
||||||
|
from core.emulator.enumerations import EventTypes, NodeTypes
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
logging.basicConfig(level=logging.DEBUG)
|
||||||
|
|
||||||
|
coreemu = CoreEmu()
|
||||||
|
session = coreemu.create_session()
|
||||||
|
session.set_state(EventTypes.CONFIGURATION_STATE)
|
||||||
|
|
||||||
|
# create nodes and interfaces
|
||||||
|
try:
|
||||||
|
prefixes = IpPrefixes(ip4_prefix="10.83.0.0/16")
|
||||||
|
options = NodeOptions(model=None, image="ubuntu")
|
||||||
|
|
||||||
|
# create node one
|
||||||
|
node_one = session.add_node(_type=NodeTypes.DOCKER, node_options=options)
|
||||||
|
interface_one = prefixes.create_interface(node_one)
|
||||||
|
|
||||||
|
# create node two
|
||||||
|
node_two = session.add_node(_type=NodeTypes.DOCKER, node_options=options)
|
||||||
|
interface_two = prefixes.create_interface(node_two)
|
||||||
|
|
||||||
|
# add link
|
||||||
|
session.add_link(node_one.id, node_two.id, interface_one, interface_two)
|
||||||
|
|
||||||
|
# instantiate
|
||||||
|
session.instantiate()
|
||||||
|
finally:
|
||||||
|
input("continue to shutdown")
|
||||||
|
coreemu.shutdown()
|
42
daemon/examples/docker/switch.py
Normal file
42
daemon/examples/docker/switch.py
Normal file
|
@ -0,0 +1,42 @@
|
||||||
|
import logging
|
||||||
|
|
||||||
|
from core.emulator.coreemu import CoreEmu
|
||||||
|
from core.emulator.emudata import IpPrefixes, NodeOptions
|
||||||
|
from core.emulator.enumerations import EventTypes, NodeTypes
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
logging.basicConfig(level=logging.DEBUG)
|
||||||
|
|
||||||
|
coreemu = CoreEmu()
|
||||||
|
session = coreemu.create_session()
|
||||||
|
session.set_state(EventTypes.CONFIGURATION_STATE)
|
||||||
|
|
||||||
|
try:
|
||||||
|
prefixes = IpPrefixes(ip4_prefix="10.83.0.0/16")
|
||||||
|
options = NodeOptions(model=None, image="ubuntu")
|
||||||
|
|
||||||
|
# create switch
|
||||||
|
switch = session.add_node(_type=NodeTypes.SWITCH)
|
||||||
|
|
||||||
|
# node one
|
||||||
|
node_one = session.add_node(_type=NodeTypes.DOCKER, node_options=options)
|
||||||
|
interface_one = prefixes.create_interface(node_one)
|
||||||
|
|
||||||
|
# node two
|
||||||
|
node_two = session.add_node(_type=NodeTypes.DOCKER, node_options=options)
|
||||||
|
interface_two = prefixes.create_interface(node_two)
|
||||||
|
|
||||||
|
# node three
|
||||||
|
node_three = session.add_node()
|
||||||
|
interface_three = prefixes.create_interface(node_three)
|
||||||
|
|
||||||
|
# add links
|
||||||
|
session.add_link(node_one.id, switch.id, interface_one)
|
||||||
|
session.add_link(node_two.id, switch.id, interface_two)
|
||||||
|
session.add_link(node_three.id, switch.id, interface_three)
|
||||||
|
|
||||||
|
# instantiate
|
||||||
|
session.instantiate()
|
||||||
|
finally:
|
||||||
|
input("continue to shutdown")
|
||||||
|
coreemu.shutdown()
|
|
@ -21,7 +21,9 @@ def main():
|
||||||
core.events(session_id, log_event)
|
core.events(session_id, log_event)
|
||||||
|
|
||||||
# change session state
|
# change session state
|
||||||
response = core.set_session_state(session_id, core_pb2.SessionState.CONFIGURATION)
|
response = core.set_session_state(
|
||||||
|
session_id, core_pb2.SessionState.CONFIGURATION
|
||||||
|
)
|
||||||
logging.info("set session state: %s", response)
|
logging.info("set session state: %s", response)
|
||||||
|
|
||||||
# create switch node
|
# create switch node
|
||||||
|
@ -47,7 +49,9 @@ def main():
|
||||||
logging.info("created link: %s", response)
|
logging.info("created link: %s", response)
|
||||||
|
|
||||||
# change session state
|
# change session state
|
||||||
response = core.set_session_state(session_id, core_pb2.SessionState.INSTANTIATION)
|
response = core.set_session_state(
|
||||||
|
session_id, core_pb2.SessionState.INSTANTIATION
|
||||||
|
)
|
||||||
logging.info("set session state: %s", response)
|
logging.info("set session state: %s", response)
|
||||||
|
|
||||||
|
|
||||||
|
|
29
daemon/examples/lxd/README.md
Normal file
29
daemon/examples/lxd/README.md
Normal file
|
@ -0,0 +1,29 @@
|
||||||
|
# LXD Support
|
||||||
|
|
||||||
|
Information on how LXD can be leveraged and included to create
|
||||||
|
nodes based on LXC containers and images to interface with
|
||||||
|
existing CORE nodes, when needed.
|
||||||
|
|
||||||
|
# Installation
|
||||||
|
|
||||||
|
```shell
|
||||||
|
sudo snap install lxd
|
||||||
|
```
|
||||||
|
|
||||||
|
# Configuration
|
||||||
|
|
||||||
|
Initialize LXD and say no to adding a default bridge.
|
||||||
|
|
||||||
|
```shell
|
||||||
|
sudo lxd init
|
||||||
|
```
|
||||||
|
|
||||||
|
# Tools and Versions Tested With
|
||||||
|
|
||||||
|
* LXD 3.14
|
||||||
|
* nsenter from util-linux 2.31.1
|
||||||
|
|
||||||
|
# Examples
|
||||||
|
|
||||||
|
This directory provides a few small examples creating LXC nodes
|
||||||
|
using LXD and linking them to themselves or with standard CORE nodes.
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Reference in a new issue