Merge pull request #286 from coreemu/develop

Merging for 5.4.0
This commit is contained in:
bharnden 2019-09-23 23:39:17 -07:00 committed by GitHub
commit 87816748b3
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
151 changed files with 7535 additions and 5086 deletions

32
.github/workflows/daemon-checks.yml vendored Normal file
View 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

View file

@ -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

View file

@ -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

View file

@ -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>

View file

@ -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

View file

@ -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;
} }

View file

@ -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);
}
}
} }

View file

@ -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;

View file

@ -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;
} }

View file

@ -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();

View file

@ -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;

View file

@ -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<>();
} }

View file

@ -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)));
} }
} }

View file

@ -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;

View file

@ -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);

View 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));
}
}
}

View 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;
}
}

View file

@ -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) {

View file

@ -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;
}
} }

View file

@ -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;
} }
} }

View file

@ -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()) {

View 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);
}
}
}

View file

@ -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();

View file

@ -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>

View file

@ -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>

View file

@ -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>

View 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>

Binary file not shown.

After

Width:  |  Height:  |  Size: 362 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 394 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 162 B

View 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
View 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
View 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"
}
}
}

View file

@ -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

View file

@ -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

View file

@ -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)

View file

@ -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

View file

@ -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

View file

@ -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)

View file

@ -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

View file

@ -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)

View file

@ -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),
]

View file

@ -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)

View file

@ -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):

View file

@ -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)

View file

@ -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
)

View file

@ -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)

View file

@ -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)

View file

@ -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)

View file

@ -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])

View file

@ -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)

View file

@ -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)

View file

@ -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,
) )

View file

@ -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

View file

@ -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):

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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()
)

View file

@ -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)

View file

@ -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
View 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")

View file

@ -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:

View file

@ -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
View 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)

View file

@ -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"

View file

@ -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,
} }

View file

@ -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,
} }

View file

@ -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"])

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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"

View file

@ -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

View file

@ -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"

View file

@ -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

View file

@ -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

View file

@ -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"

View file

@ -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."

View file

@ -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

View file

@ -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

View file

@ -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)

View file

@ -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")

View file

@ -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"

View file

@ -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))

View file

@ -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()

View file

@ -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()

View file

@ -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()

View file

@ -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()

View 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.

View file

@ -0,0 +1,5 @@
{
"bridge": "none",
"iptables": false
}

View 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()

View 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()

View 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()

View file

@ -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)

View 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