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
PYTHON_DEB_DEP = python3 >= 3.0
PYTHON_RPM_DEP = python3 >= 3.0
PYTHON_DEB_DEP = python3 >= 3.6
PYTHON_RPM_DEP = python3 >= 3.6
else
PYTHON_DEB_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.
# 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
AC_CONFIG_SRCDIR([netns/version.h.in])
@ -236,7 +236,6 @@ AC_CONFIG_FILES([Makefile
gui/Makefile
gui/icons/Makefile
scripts/Makefile
scripts/perf/Makefile
man/Makefile
docs/Makefile
daemon/Makefile

View file

@ -13,7 +13,7 @@
<maven.compiler.source>1.8</maven.compiler.source>
<maven.compiler.target>1.8</maven.compiler.target>
<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>
<log4j.version>2.9.0</log4j.version>
</properties>

View file

@ -70,6 +70,7 @@ public class Controller implements Initializable {
private GraphToolbar graphToolbar = new GraphToolbar(this);
// dialogs
private Rj45Dialog rj45Dialog = new Rj45Dialog(this);
private SessionsDialog sessionsDialog = new SessionsDialog(this);
private ServiceDialog serviceDialog = new ServiceDialog(this);
private NodeServicesDialog nodeServicesDialog = new NodeServicesDialog(this);
@ -140,36 +141,15 @@ public class Controller implements Initializable {
Platform.runLater(() -> borderPane.setRight(null));
// get session to join
Session session = coreClient.getSession(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);
Session session = coreClient.joinSession(sessionId);
// display all nodes
logger.info("joining core session({}) state({}): {}", sessionId, sessionState, session);
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);
}
// display all links
for (CoreLink link : session.getLinks()) {
if (link.getInterfaceOne() != null || link.getInterfaceTwo() != null) {
link.setType(LinkTypes.WIRED.getValue());
}
networkGraph.addLink(link);
}
@ -191,7 +171,7 @@ public class Controller implements Initializable {
Platform.runLater(() -> decorator.setTitle(String.format("CORE (Session %s)", sessionId)));
}
public boolean startSession() throws IOException {
public boolean startSession() {
// force nodes to get latest positions
networkGraph.updatePositions();
@ -201,12 +181,18 @@ public class Controller implements Initializable {
List<Hook> hooks = hooksDialog.getHooks();
// start/create session
boolean result = false;
progressBar.setVisible(true);
boolean result = coreClient.start(nodes, links, hooks);
progressBar.setVisible(false);
if (result) {
showMobilityScriptDialogs();
saveXmlMenuItem.setDisable(false);
try {
result = coreClient.start(nodes, links, hooks);
if (result) {
showMobilityScriptDialogs();
saveXmlMenuItem.setDisable(false);
}
} catch (IOException ex) {
Toast.error("Failure Starting Session", ex);
} finally {
progressBar.setVisible(false);
}
return result;
}
@ -292,6 +278,7 @@ public class Controller implements Initializable {
connectDialog.setOwner(window);
guiPreferencesDialog.setOwner(window);
nodeTypeCreateDialog.setOwner(window);
rj45Dialog.setOwner(window);
}
private void showMobilityScriptDialogs() {
@ -448,6 +435,7 @@ public class Controller implements Initializable {
connectToCore(address, port);
logger.info("controller initialize");
coreClient.initialize(this);
swingNode.setContent(networkGraph.getGraphViewer());
// update graph preferences

View file

@ -1,8 +1,6 @@
package com.core.client;
import com.core.Controller;
import com.core.data.ServiceFile;
import com.core.data.WlanConfig;
import com.core.data.*;
import java.io.File;
@ -23,16 +21,14 @@ public interface ICoreClient {
boolean stopThroughput() throws IOException;
void updateSession(Integer sessionId);
void updateState(SessionState state);
SessionOverview createSession() throws IOException;
boolean deleteSession(Integer sessionId) throws IOException;
List<SessionOverview> getSessions() throws IOException;
Session joinSession(Integer sessionId) throws IOException;
Session getSession(Integer sessionId) 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;
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 boolean handlingEvents = false;
private boolean handlingThroughputs = false;
private Controller controller;
private CoreProto.Node nodeToProto(CoreNode node) {
CoreProto.Position position = CoreProto.Position.newBuilder()
@ -58,6 +59,9 @@ public class CoreGrpcClient implements ICoreClient {
if (node.getIcon() != null) {
builder.setIcon(node.getIcon());
}
if (node.getImage() != null) {
builder.setImage(node.getImage());
}
return builder.build();
}
@ -83,33 +87,32 @@ public class CoreGrpcClient implements ICoreClient {
private CoreProto.LinkOptions linkOptionsToProto(CoreLinkOptions options) {
CoreProto.LinkOptions.Builder builder = CoreProto.LinkOptions.newBuilder();
boolean unidirectional = false;
if (options.getUnidirectional() != null && options.getUnidirectional() == 1) {
unidirectional = true;
if (options.getUnidirectional() != null) {
builder.setUnidirectional(options.getUnidirectional());
}
if (options.getBandwidth() != null) {
builder.setBandwidth(options.getBandwidth().intValue());
builder.setBandwidth(options.getBandwidth());
}
if (options.getBurst() != null) {
builder.setBurst(options.getBurst().intValue());
builder.setBurst(options.getBurst());
}
if (options.getDelay() != null) {
builder.setDelay(options.getDelay().intValue());
builder.setDelay(options.getDelay());
}
if (options.getDup() != null) {
builder.setDup(options.getDup().intValue());
builder.setDup(options.getDup());
}
if (options.getJitter() != null) {
builder.setJitter(options.getJitter().intValue());
builder.setJitter(options.getJitter());
}
if (options.getMburst() != null) {
builder.setMburst(options.getMburst().intValue());
builder.setMburst(options.getMburst());
}
if (options.getMer() != null) {
builder.setMer(options.getMer().intValue());
builder.setMer(options.getMer());
}
if (options.getPer() != null) {
builder.setPer(options.getPer().intValue());
builder.setPer(options.getPer().floatValue());
}
if (options.getKey() != null) {
builder.setKey(options.getKey());
@ -117,7 +120,6 @@ public class CoreGrpcClient implements ICoreClient {
if (options.getOpaque() != null) {
builder.setOpaque(options.getOpaque());
}
builder.setUnidirectional(unidirectional);
return builder.build();
}
@ -157,14 +159,26 @@ public class CoreGrpcClient implements ICoreClient {
private CoreNode protoToNode(CoreProto.Node protoNode) {
CoreNode node = new CoreNode(protoNode.getId());
node.setType(protoNode.getTypeValue());
node.setName(protoNode.getName());
node.setEmane(protoNode.getEmane());
node.setIcon(protoNode.getIcon());
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.getPosition().setX((double) protoNode.getPosition().getX());
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;
}
@ -172,7 +186,9 @@ public class CoreGrpcClient implements ICoreClient {
CoreInterface coreInterface = new CoreInterface();
coreInterface.setId(protoInterface.getId());
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());
IPAddress ip4 = new IPAddressString(ip4String).getAddress();
coreInterface.setIp4(ip4);
@ -184,6 +200,7 @@ public class CoreGrpcClient implements ICoreClient {
private CoreLink protoToLink(CoreProto.Link linkProto) {
CoreLink link = new CoreLink();
link.setType(linkProto.getTypeValue());
link.setNodeOne(linkProto.getNodeOneId());
link.setNodeTwo(linkProto.getNodeTwoId());
CoreInterface interfaceOne = protoToInterface(linkProto.getInterfaceOne());
@ -193,24 +210,29 @@ public class CoreGrpcClient implements ICoreClient {
CoreLinkOptions options = new CoreLinkOptions();
CoreProto.LinkOptions protoOptions = linkProto.getOptions();
options.setBandwidth((double) protoOptions.getBandwidth());
options.setDelay((double) protoOptions.getDelay());
options.setDup((double) protoOptions.getDup());
options.setJitter((double) protoOptions.getJitter());
options.setBandwidth((int) protoOptions.getBandwidth());
options.setDelay((int) protoOptions.getDelay());
options.setDup((int) protoOptions.getDup());
options.setJitter((int) protoOptions.getJitter());
options.setPer((double) protoOptions.getPer());
options.setBurst((double) protoOptions.getBurst());
options.setBurst((int) protoOptions.getBurst());
if (protoOptions.hasField(CoreProto.LinkOptions.getDescriptor().findFieldByName("key"))) {
options.setKey(protoOptions.getKey());
}
options.setMburst((double) protoOptions.getMburst());
options.setMer((double) protoOptions.getMer());
options.setMburst((int) protoOptions.getMburst());
options.setMer((int) protoOptions.getMer());
options.setOpaque(protoOptions.getOpaque());
options.setUnidirectional(protoOptions.getUnidirectional() ? 1 : 0);
options.setUnidirectional(protoOptions.getUnidirectional());
link.setOptions(options);
return link;
}
@Override
public void initialize(Controller controller) {
this.controller = controller;
}
@Override
public void setConnection(String address, int port) {
this.address = address;
@ -283,16 +305,6 @@ public class CoreGrpcClient implements ICoreClient {
return true;
}
@Override
public void updateSession(Integer sessionId) {
this.sessionId = sessionId;
}
@Override
public void updateState(SessionState state) {
sessionState = state;
}
@Override
public SessionOverview createSession() throws IOException {
CoreProto.CreateSessionRequest request = CoreProto.CreateSessionRequest.newBuilder().build();
@ -345,6 +357,7 @@ public class CoreGrpcClient implements ICoreClient {
try {
CoreProto.GetSessionResponse response = blockingStub.getSession(request);
Session session = new Session();
session.setId(sessionId);
for (CoreProto.Node protoNode : response.getSession().getNodesList()) {
if (CoreProto.NodeType.Enum.PEER_TO_PEER == protoNode.getType()) {
continue;
@ -355,19 +368,43 @@ public class CoreGrpcClient implements ICoreClient {
session.getNodes().add(node);
}
for (CoreProto.Link linkProto : response.getSession().getLinksList()) {
logger.info("adding link: {} - {}", linkProto.getNodeOneId(), linkProto.getNodeTwoId());
logger.info("adding link: {}", linkProto);
CoreLink link = protoToLink(linkProto);
session.getLinks().add(link);
}
session.setState(response.getSession().getStateValue());
SessionState state = SessionState.get(response.getSession().getStateValue());
session.setState(state);
return session;
} catch (StatusRuntimeException 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
public boolean start(Collection<CoreNode> nodes, Collection<CoreLink> links, List<Hook> hooks) throws IOException {
setupEventHandlers();
boolean result = setState(SessionState.DEFINITION);
if (!result) {
return false;
@ -806,6 +843,8 @@ public class CoreGrpcClient implements ICoreClient {
@Override
public boolean deleteNode(CoreNode node) throws IOException {
CoreProto.DeleteNodeRequest request = CoreProto.DeleteNodeRequest.newBuilder()
.setSessionId(sessionId)
.setNodeId(node.getId())
.build();
try {
CoreProto.DeleteNodeResponse response = blockingStub.deleteNode(request);
@ -1132,8 +1171,7 @@ public class CoreGrpcClient implements ICoreClient {
}
}
@Override
public void setupEventHandlers(Controller controller) throws IOException {
private void setupEventHandlers() throws IOException {
logger.info("setting up event handlers");
handlingEvents = true;
try {
@ -1151,22 +1189,22 @@ public class CoreGrpcClient implements ICoreClient {
logger.info("handling event: {}", event);
switch (event.getEventTypeCase()) {
case SESSION_EVENT:
handleSessionEvents(controller, event.getSessionEvent());
handleSessionEvents(event.getSessionEvent());
break;
case NODE_EVENT:
handleNodeEvents(controller, event.getNodeEvent());
handleNodeEvents(event.getNodeEvent());
break;
case LINK_EVENT:
handleLinkEvents(controller, event.getLinkEvent());
handleLinkEvents(event.getLinkEvent());
break;
case CONFIG_EVENT:
handleConfigEvents(controller, event.getConfigEvent());
handleConfigEvents(event.getConfigEvent());
break;
case EXCEPTION_EVENT:
handleExceptionEvents(controller, event.getExceptionEvent());
handleExceptionEvents(event.getExceptionEvent());
break;
case FILE_EVENT:
handleFileEvents(controller, event.getFileEvent());
handleFileEvents(event.getFileEvent());
break;
default:
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);
SessionState state = SessionState.get(event.getEvent());
if (state == null) {
@ -1196,7 +1234,7 @@ public class CoreGrpcClient implements ICoreClient {
// session state event
if (state.getValue() <= 6) {
logger.info("event updating session state: {}", state);
updateState(state);
sessionState = state;
// mobility script event
} else if (state.getValue() <= 9) {
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);
CoreNode node = protoToNode(event.getNode());
controller.getNetworkGraph().setNodeLocation(node);
}
private void handleExceptionEvents(Controller controller, CoreProto.ExceptionEvent event) {
private void handleExceptionEvents(CoreProto.ExceptionEvent 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);
}
private void handleLinkEvents(Controller controller, CoreProto.LinkEvent event) {
private void handleLinkEvents(CoreProto.LinkEvent event) {
logger.info("link event: {}", event);
CoreLink link = protoToLink(event.getLink());
MessageFlags flag = MessageFlags.get(event.getMessageTypeValue());
@ -1239,7 +1277,18 @@ public class CoreGrpcClient implements ICoreClient {
controller.getNetworkGraph().getGraphViewer().repaint();
}
private void handleFileEvents(Controller controller, CoreProto.FileEvent event) {
private void handleFileEvents(CoreProto.FileEvent 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 boolean visible = true;
private Integer messageType;
private Integer type = 1;
private Integer type = LinkTypes.WIRED.getValue();
private Integer nodeOne;
private Integer nodeTwo;
private CoreInterface interfaceOne;

View file

@ -8,14 +8,14 @@ import lombok.NoArgsConstructor;
public class CoreLinkOptions {
private String opaque;
private Integer session;
private Double jitter;
private Integer jitter;
private Integer key;
private Double mburst;
private Double mer;
private Integer mburst;
private Integer mer;
private Double per;
private Double bandwidth;
private Double burst;
private Double delay;
private Double dup;
private Integer unidirectional;
private Integer bandwidth;
private Integer burst;
private Integer delay;
private Integer dup;
private Boolean unidirectional;
}

View file

@ -28,6 +28,7 @@ public class CoreNode {
private String url;
private NodeType nodeType;
private String icon;
private String image;
private boolean loaded = true;
private LayeredIcon graphIcon;
private RadioIcon radioIcon = new RadioIcon();

View file

@ -1,5 +1,7 @@
package com.core.data;
import javafx.scene.control.Label;
import javafx.scene.image.ImageView;
import lombok.Data;
import lombok.EqualsAndHashCode;
import org.apache.logging.log4j.LogManager;
@ -18,7 +20,10 @@ public class NodeType {
public static final int SWITCH = 4;
public static final int HUB = 5;
public static final int WLAN = 6;
public static final int RJ45 = 7;
public static final int EMANE = 10;
public static final int DOCKER = 15;
public static final int LXC = 16;
@EqualsAndHashCode.Include
private final int id;
private final int value;
@ -27,21 +32,14 @@ public class NodeType {
private String model;
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 {
add(new NodeType(SWITCH, "lanswitch", "Switch", "/icons/switch-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(RJ45, "rj45", "RJ45", "/icons/rj45-80.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;
}
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) {
ID_LOOKUP.put(nodeType.getId(), nodeType);
}
@ -74,7 +85,7 @@ public class NodeType {
.filter(nodeType -> {
boolean sameType = nodeType.getValue() == type;
boolean sameModel = true;
if (!model.isEmpty()) {
if (model != null && !model.isEmpty()) {
sameModel = model.equals(nodeType.getModel());
}
return sameType && sameModel;

View file

@ -9,7 +9,8 @@ import java.util.List;
@Data
@NoArgsConstructor
public class Session {
private Integer state;
private Integer id;
private SessionState state;
private List<CoreNode> nodes = 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.Logger;
import java.beans.IndexedPropertyDescriptor;
import java.util.Comparator;
import java.util.HashSet;
import java.util.Queue;
import java.util.Set;
import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.atomic.AtomicBoolean;
public class CoreAddresses {
private static final String ADDRESS = "10.0.0.0/24";
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 Set<IPAddress> usedSubnets = new HashSet<>();
@ -69,11 +67,6 @@ public class CoreAddresses {
public void reset() {
deleted.clear();
usedSubnets.clear();
currentSubnet = new IPAddressString("10.0.0.0/24").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)));
currentSubnet = new IPAddressString(ADDRESS).getAddress().toPrefixBlock();
}
}

View file

@ -60,6 +60,8 @@ public class CorePopupGraphMousePlugin<V, E> extends EditingPopupGraphMousePlugi
ContextMenu contextMenu = new ContextMenu();
switch (node.getType()) {
case NodeType.DEFAULT:
case NodeType.DOCKER:
case NodeType.LXC:
contextMenu = new NodeContextMenu(controller, node);
break;
case NodeType.WLAN:
@ -68,6 +70,9 @@ public class CorePopupGraphMousePlugin<V, E> extends EditingPopupGraphMousePlugi
case NodeType.EMANE:
contextMenu = new EmaneContextMenu(controller, node);
break;
case NodeType.RJ45:
contextMenu = new Rj45ContextMenu(controller, node);
break;
default:
logger.warn("no context menu for node: {}", node.getType());
break;

View file

@ -298,8 +298,8 @@ public class NetworkGraph {
Pair<CoreNode> endpoints = graph.getEndpoints(link);
CoreNode nodeOne = endpoints.getFirst();
CoreNode nodeTwo = endpoints.getSecond();
boolean nodeOneIsDefault = isNode(nodeOne);
boolean nodeTwoIsDefault = isNode(nodeTwo);
boolean nodeOneIsDefault = NodeType.isDefault(nodeOne.getNodeType());
boolean nodeTwoIsDefault = NodeType.isDefault(nodeTwo.getNodeType());
// check what we are linking together
IPAddress subnet = null;
@ -360,7 +360,7 @@ public class NetworkGraph {
currentInterface = link.getInterfaceTwo();
}
if (isNode(currentNode)) {
if (NodeType.isDefault(currentNode.getNodeType())) {
interfaces.add(currentInterface);
} else {
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) {
CoreInterface coreInterface = new CoreInterface();
coreInterface.setId(interfaceId);
@ -429,8 +425,8 @@ public class NetworkGraph {
CoreInterface interfaceOne = link.getInterfaceOne();
CoreNode nodeTwo = getVertex(link.getNodeTwo());
CoreInterface interfaceTwo = link.getInterfaceTwo();
boolean nodeOneIsDefault = isNode(nodeOne);
boolean nodeTwoIsDefault = isNode(nodeTwo);
boolean nodeOneIsDefault = NodeType.isDefault(nodeOne.getNodeType());
boolean nodeTwoIsDefault = NodeType.isDefault(nodeTwo.getNodeType());
// check what we are unlinking
Set<CoreInterface> interfaces;
@ -445,7 +441,7 @@ public class NetworkGraph {
logger.info("unlinking node one from network reuse subnet: {}", subnet);
}
} else if (nodeTwoIsDefault) {
interfaces = getNetworkInterfaces(nodeOne, new HashSet<>());
interfaces = getNetworkInterfaces(nodeOne, new HashSet<>());
if (interfaces.isEmpty()) {
subnet = interfaceTwo.getIp4().toPrefixBlock();
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) {
CoreNode node = vertexEvent.getVertex();
if (!node.isLoaded()) {
node.setNodeType(nodeType);
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);
if (node.isLoaded()) {
return;
}
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) {
@ -506,7 +508,7 @@ public class NetworkGraph {
try {
controller.getCoreClient().deleteNode(node);
} catch (IOException ex) {
logger.error("error deleting node");
logger.error("error deleting node", ex);
Toast.error(String.format("Error deleting node: %s", node.getName()));
}
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 JFXListView<Label> nodesList = new JFXListView<>();
private JFXListView<Label> devicesList = new JFXListView<>();
private JFXListView<Label> containersList = new JFXListView<>();
private JFXButton selectedEditButton;
private NodeType selectedNodeType;
private boolean isEditing = false;
@ -49,6 +50,7 @@ public class GraphToolbar extends VBox {
@FXML private ComboBox<String> graphModeCombo;
@FXML private JFXButton nodesButton;
@FXML private JFXButton devicesButton;
@FXML private JFXButton containersButton;
public GraphToolbar(Controller controller) {
this.controller = controller;
@ -63,6 +65,7 @@ public class GraphToolbar extends VBox {
setupDrawingButton();
setupNodesButton();
setupDevicesButton();
setupContainersButton();
// initial state
setSelected(true, pickingButton);
@ -113,28 +116,20 @@ public class GraphToolbar extends VBox {
public void setupNodeTypes() {
// clear existing configuration
labelMap.clear();
for (Label label : nodesList.getItems()) {
int id = (int) label.getUserData();
labelMap.remove(id);
}
nodesList.getItems().clear();
devicesList.getItems().clear();
// add current default nodes
for (NodeType nodeType : NodeType.getAll()) {
ImageView icon = new ImageView(nodeType.getIcon());
icon.setFitWidth(NODES_ICON_SIZE);
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);
if (NodeType.DEFAULT == nodeType.getValue()) {
addNodeType(nodeType.getValue(), nodeType.getModel(), nodesList);
}
}
Comparator<Label> comparator = Comparator.comparing(Label::getText);
nodesList.getItems().sort(comparator);
devicesList.getItems().sort(comparator);
// initial node
nodesList.getSelectionModel().selectFirst();
@ -143,9 +138,6 @@ public class GraphToolbar extends VBox {
selectedEditButton = nodesButton;
controller.getNetworkGraph().setNodeType(selectedNodeType);
updateButtonValues(nodesButton, selectedNodeLabel);
// initial device
updateButtonValues(devicesButton, devicesList.getItems().get(0));
}
private void updateButtonValues(JFXButton button, Label label) {
@ -167,49 +159,54 @@ public class GraphToolbar extends VBox {
}
private void setupNodesButton() {
nodesButton.setTooltip(new Tooltip("Network Nodes (host, pc, etc)"));
nodesList.setOnMouseClicked(event -> {
Label selectedLabel = nodesList.getSelectionModel().getSelectedItem();
setupButtonSelection("CORE Nodes", nodesButton, nodesList);
}
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) {
return;
}
updateButtonValues(nodesButton, selectedLabel);
updateButtonValues(button, selectedLabel);
selectedNodeType = NodeType.get((int) selectedLabel.getUserData());
logger.info("selected node type: {}", selectedNodeType);
setSelectedEditButton(nodesButton);
devicesList.getSelectionModel().clearSelection();
controller.getNetworkGraph().setNodeType(selectedNodeType);
setSelectedEditButton(button);
logger.info("node selected: {} - type: {}", selectedLabel, selectedNodeType);
setEditMode();
});
JFXPopup popup = new JFXPopup(nodesList);
nodesButton.setOnAction(event -> popup.show(nodesButton, JFXPopup.PopupVPosition.TOP,
JFXPopup.PopupHPosition.LEFT, nodesButton.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));
JFXPopup popup = new JFXPopup(list);
button.setOnAction(event -> popup.show(button, JFXPopup.PopupVPosition.TOP,
JFXPopup.PopupHPosition.LEFT, button.getWidth(), 0));
}
@FXML
@ -241,15 +238,11 @@ public class GraphToolbar extends VBox {
private void startSession() {
runButton.setDisable(true);
new Thread(() -> {
try {
boolean result = controller.startSession();
if (result) {
Toast.success("Session Started");
setRunButton(true);
}
} catch (IOException ex) {
Toast.error("Failure Starting Session", ex);
boolean result = controller.startSession();
if (result) {
Toast.success("Session Started");
}
setRunButton(result);
}).start();
}
@ -274,6 +267,7 @@ public class GraphToolbar extends VBox {
pickingButton.fire();
devicesButton.setDisable(true);
nodesButton.setDisable(true);
containersButton.setDisable(true);
runButton.pseudoClassStateChanged(START_CLASS, false);
runButton.pseudoClassStateChanged(STOP_CLASS, true);
if (runButton.getGraphic() != stopIcon) {
@ -285,6 +279,7 @@ public class GraphToolbar extends VBox {
Platform.runLater(() -> {
devicesButton.setDisable(false);
nodesButton.setDisable(false);
containersButton.setDisable(false);
runButton.pseudoClassStateChanged(START_CLASS, true);
runButton.pseudoClassStateChanged(STOP_CLASS, false);
if (runButton.getGraphic() != startIcon) {

View file

@ -7,47 +7,27 @@ import com.core.data.CoreLink;
import com.core.data.CoreLinkOptions;
import com.core.data.CoreNode;
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 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.Logger;
import java.io.IOException;
public class LinkDetails extends ScrollPane {
public class LinkDetails extends DetailsPanel {
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) {
this.controller = controller;
FxmlUtils.loadRootController(this, "/fxml/link_details.fxml");
setPrefWidth(400);
super(controller);
}
public void setLink(CoreLink link) {
NetworkGraph graph = controller.getNetworkGraph();
ICoreClient coreClient = controller.getCoreClient();
clear();
setTitle("Link Details");
addSeparator();
CoreNode nodeOne = graph.getVertex(link.getNodeOne());
CoreInterface interfaceOne = link.getInterfaceOne();
addLabel(nodeOne.getName());
@ -55,7 +35,6 @@ public class LinkDetails extends ScrollPane {
addInterface(interfaceOne);
}
addSeparator();
CoreNode nodeTwo = graph.getVertex(link.getNodeTwo());
CoreInterface interfaceTwo = link.getInterfaceTwo();
addLabel(nodeTwo.getName());
@ -63,20 +42,19 @@ public class LinkDetails extends ScrollPane {
addInterface(interfaceTwo);
}
addSeparator();
addLabel("Properties");
JFXTextField bandwidthField = addRow("Bandwidth (bps)", link.getOptions().getBandwidth());
JFXTextField delayField = addRow("Delay (us)", link.getOptions().getDelay());
JFXTextField jitterField = addRow("Jitter (us)", link.getOptions().getJitter());
JFXTextField lossField = addRow("Loss (%)", link.getOptions().getPer());
JFXTextField dupsField = addRow("Duplicate (%)", link.getOptions().getDup());
JFXTextField bandwidthField = addIntegerRow("Bandwidth (bps)", link.getOptions().getBandwidth());
JFXTextField delayField = addIntegerRow("Delay (us)", link.getOptions().getDelay());
JFXTextField jitterField = addIntegerRow("Jitter (us)", link.getOptions().getJitter());
JFXTextField lossField = addDoubleRow("Loss (%)", link.getOptions().getPer());
JFXTextField dupsField = addIntegerRow("Duplicate (%)", link.getOptions().getDup());
addButton("Update", event -> {
CoreLinkOptions options = link.getOptions();
options.setBandwidth(getDouble(bandwidthField));
options.setDelay(getDouble(delayField));
options.setJitter(getDouble(jitterField));
options.setBandwidth(getInteger(bandwidthField));
options.setDelay(getInteger(delayField));
options.setJitter(getInteger(jitterField));
options.setPer(getDouble(lossField));
options.setDup(getDouble(dupsField));
options.setDup(getInteger(dupsField));
if (coreClient.isRunning()) {
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.CoreNode;
import com.core.data.NodeType;
import com.core.utils.FxmlUtils;
import com.jfoenix.controls.JFXButton;
import com.jfoenix.controls.JFXListView;
import com.jfoenix.controls.JFXScrollPane;
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.Logger;
import java.util.Collections;
import java.util.Set;
public class NodeDetails extends ScrollPane {
public class NodeDetails extends DetailsPanel {
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) {
this.controller = controller;
FxmlUtils.loadRootController(this, "/fxml/node_details.fxml");
setPrefWidth(400);
super(controller);
}
public void setNode(CoreNode node) {
clear();
title.setText(node.getName());
boolean sessionRunning = controller.getCoreClient().isRunning();
setTitle(node.getName());
addSeparator();
addLabel("Properties");
addRow("ID", node.getId().toString(), true);
if (node.getType() == NodeType.DEFAULT) {
addRow("Model", node.getModel(), true);
} else {
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);
}
addSeparator();
addLabel("Position");
if (node.getPosition().getX() != null) {
addRow("X", node.getPosition().getX().toString(), true);
}
if (node.getPosition().getY() != null) {
addRow("Y", node.getPosition().getY().toString(), true);
if (node.getType() == NodeType.DOCKER || node.getType() == NodeType.LXC) {
setContainerDetails(node, sessionRunning);
}
addSeparator();
addLabel("Interfaces");
boolean firstInterface = true;
for (CoreLink link : controller.getNetworkGraph().getGraph().getIncidentEdges(node)) {
if (firstInterface) {
firstInterface = false;
addLabel("Interfaces");
} else {
addSeparator();
}
CoreNode linkedNode;
CoreInterface coreInterface;
if (node.getId().equals(link.getNodeOne())) {
@ -83,7 +69,6 @@ public class NodeDetails extends ScrollPane {
continue;
}
addSeparator();
if (linkedNode.getType() == NodeType.EMANE) {
String emaneModel = linkedNode.getEmane();
String linkedLabel = String.format("%s - %s", linkedNode.getName(), emaneModel);
@ -109,8 +94,8 @@ public class NodeDetails extends ScrollPane {
if (services.isEmpty()) {
services = controller.getDefaultServices().getOrDefault(node.getModel(), Collections.emptySet());
}
if (!services.isEmpty()) {
addSeparator();
addLabel("Services");
JFXListView<String> listView = new JFXListView<>();
listView.setMouseTransparent(true);
@ -122,61 +107,14 @@ public class NodeDetails extends ScrollPane {
JFXScrollPane.smoothScrolling(scrollPane);
}
private 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);
}
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);
private void setContainerDetails(CoreNode node, boolean sessionRunning) {
JFXTextField imageField = addRow("Image", node.getImage(), sessionRunning);
if (!sessionRunning) {
addButton("Update", event -> {
if (node.getType() == NodeType.DOCKER || node.getType() == NodeType.LXC) {
node.setImage(imageField.getText());
}
});
}
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();
if (nodeServices.isEmpty()) {
nodeServices = getController().getDefaultServices().get(node.getModel());
nodeServices = getController().getDefaultServices().getOrDefault(node.getModel(), new HashSet<>());
}
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
public class StageDialog {
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 Stage stage = new Stage(StageStyle.DECORATED);
private final Scene scene;
@ -35,10 +37,18 @@ public class StageDialog {
private final HBox buttonBar = new HBox();
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) {
this(controller, fxmlPath, modality, STAGE_WIDTH, STAGE_HEIGHT);
}
public StageDialog(Controller controller, String fxmlPath, Modality modality, int width, int height) {
this.controller = controller;
JFXDecorator decorator = new JFXDecorator(stage, gridPane);
@ -49,8 +59,8 @@ public class StageDialog {
scene = new Scene(decorator);
stage.setScene(scene);
stage.setWidth(800);
stage.setHeight(600);
stage.setWidth(width);
stage.setHeight(height);
scene.setOnKeyPressed(event -> {
if (KeyCode.ESCAPE == event.getCode()) {
stage.close();

View file

@ -19,13 +19,16 @@
<RowConstraints minHeight="10.0" prefHeight="30.0" vgrow="SOMETIMES" />
</rowConstraints>
<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>
<children>
<Label fx:id="title" styleClass="details-title" text="Node Details" GridPane.columnSpan="2147483647">
<font>
<Font name="System Bold" size="17.0" />
</font>
<GridPane.margin>
<Insets />
</GridPane.margin>
</Label>
</children>
</GridPane>

View file

@ -10,6 +10,7 @@
<JFXButton fx:id="pickingButton" styleClass="toolbar-button" />
<JFXButton fx:id="nodesButton" styleClass="toolbar-button" text="Nodes" />
<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" />
</children>
<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())
def load_logging_config():
def load_logging_config(config_path=None):
"""
Load CORE logging configuration file.
:param str config_path: path to logging config file,
when None defaults to /etc/core/logging.conf
:return: nothing
"""
log_config_path = os.path.join(constants.CORE_CONF_DIR, "logging.conf")
with open(log_config_path, "r") as log_config_file:
if not config_path:
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)
logging.config.dictConfig(log_config)
@ -28,3 +31,11 @@ class CoreCommandError(subprocess.CalledProcessError):
def __str__(self):
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
from core.api.grpc import core_pb2
from core.api.grpc import core_pb2_grpc
from core.api.grpc import core_pb2, core_pb2_grpc
from core.nodes.ipaddress import Ipv4Prefix, Ipv6Prefix, MacAddress
@ -99,7 +98,7 @@ class InterfaceHelper(object):
ip4mask=ip4_mask,
ip6=ip6,
ip6mask=ip6_mask,
mac=str(mac)
mac=str(mac),
)
@ -215,7 +214,9 @@ class CoreGrpcClient(object):
:rtype: core_pb2.SetSessionOptionsResponse
: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)
def get_session_location(self, session_id):
@ -230,7 +231,17 @@ class CoreGrpcClient(object):
request = core_pb2.GetSessionLocationRequest(session_id=session_id)
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.
@ -247,7 +258,9 @@ class CoreGrpcClient(object):
:raises grpc.RpcError: when session doesn't exist
"""
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)
def set_session_state(self, session_id, state):
@ -324,7 +337,9 @@ class CoreGrpcClient(object):
:rtype: core_pb2.EditNodeResponse
: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)
def delete_node(self, session_id, node_id):
@ -350,7 +365,9 @@ class CoreGrpcClient(object):
:rtype: core_pb2.NodeCommandResponse
: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)
def get_node_terminal(self, session_id, node_id):
@ -363,7 +380,9 @@ class CoreGrpcClient(object):
:rtype: core_pb2.GetNodeTerminalResponse
: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)
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)
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.
@ -394,12 +421,25 @@ class CoreGrpcClient(object):
:raises grpc.RpcError: when session or one of the nodes don't exist
"""
link = core_pb2.Link(
node_one_id=node_one_id, node_two_id=node_two_id, type=core_pb2.LinkType.WIRED,
interface_one=interface_one, interface_two=interface_two, options=options)
node_one_id=node_one_id,
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)
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.
@ -414,11 +454,23 @@ class CoreGrpcClient(object):
:raises grpc.RpcError: when session or one of the nodes don't exist
"""
request = core_pb2.EditLinkRequest(
session_id=session_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)
session_id=session_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)
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.
@ -432,8 +484,12 @@ class CoreGrpcClient(object):
:raises grpc.RpcError: when session doesn't exist
"""
request = core_pb2.DeleteLinkRequest(
session_id=session_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)
session_id=session_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)
def get_hooks(self, session_id):
@ -486,7 +542,9 @@ class CoreGrpcClient(object):
:rtype: core_pb2.GetMobilityConfigResponse
: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)
def set_mobility_config(self, session_id, node_id, config):
@ -500,7 +558,9 @@ class CoreGrpcClient(object):
:rtype: core_pb2.SetMobilityConfigResponse
: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)
def mobility_action(self, session_id, node_id, action):
@ -514,7 +574,9 @@ class CoreGrpcClient(object):
:rtype: core_pb2.MobilityActionResponse
: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)
def get_services(self):
@ -554,7 +616,9 @@ class CoreGrpcClient(object):
services = service_defaults[node_type]
default = core_pb2.ServiceDefaults(node_type=node_type, services=services)
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)
def get_node_service(self, session_id, node_id, service):
@ -568,7 +632,9 @@ class CoreGrpcClient(object):
:rtype: core_pb2.GetNodeServiceResponse
: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)
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
"""
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)
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.
@ -602,8 +671,13 @@ class CoreGrpcClient(object):
:raises grpc.RpcError: when session or node doesn't exist
"""
request = core_pb2.SetNodeServiceRequest(
session_id=session_id, node_id=node_id, service=service, startup=startup, validate=validate,
shutdown=shutdown)
session_id=session_id,
node_id=node_id,
service=service,
startup=startup,
validate=validate,
shutdown=shutdown,
)
return self.stub.SetNodeService(request)
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
"""
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)
def service_action(self, session_id, node_id, service, action):
@ -635,7 +714,9 @@ class CoreGrpcClient(object):
:rtype: core_pb2.ServiceActionResponse
: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)
def get_wlan_config(self, session_id, node_id):
@ -662,7 +743,9 @@ class CoreGrpcClient(object):
:rtype: core_pb2.SetWlanConfigResponse
: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)
def get_emane_config(self, session_id):
@ -715,10 +798,13 @@ class CoreGrpcClient(object):
:raises grpc.RpcError: when session doesn't exist
"""
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)
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.
@ -732,7 +818,12 @@ class CoreGrpcClient(object):
:raises grpc.RpcError: when session doesn't exist
"""
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)
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.api.tlv import coreapi
from core.nodes.base import CoreNodeBase, CoreNetworkBase
from core.emulator.enumerations import ConfigDataTypes
from core.emulator.enumerations import ConfigFlags
from core.emulator.enumerations import ConfigTlvs
from core.emulator.enumerations import EventTlvs
from core.emulator.enumerations import EventTypes
from core.emulator.enumerations import ExecuteTlvs
from core.emulator.enumerations import FileTlvs
from core.emulator.enumerations import LinkTlvs
from core.emulator.enumerations import MessageFlags
from core.emulator.enumerations import MessageTypes
from core.emulator.enumerations import NodeTlvs
from core.emulator.enumerations import NodeTypes
from core.emulator.enumerations import RegisterTlvs
from core.emulator.enumerations import (
ConfigDataTypes,
ConfigFlags,
ConfigTlvs,
EventTlvs,
EventTypes,
ExecuteTlvs,
FileTlvs,
LinkTlvs,
MessageFlags,
MessageTypes,
NodeTlvs,
NodeTypes,
RegisterTlvs,
)
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.ipaddress import IpAddress
from core.nodes.network import GreTapBridge
from core.nodes.physical import PhysicalNode
@ -147,7 +149,12 @@ class CoreBroker(object):
while len(self.servers) > 0:
name, server = self.servers.popitem()
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()
self.dorecvloop = False
if self.recvthread is not None:
@ -157,7 +164,7 @@ class CoreBroker(object):
"""
Reset to initial state.
"""
logging.info("clearing state")
logging.debug("broker reset")
self.nodemap_lock.acquire()
self.nodemap.clear()
for server in self.nodecounts:
@ -208,14 +215,22 @@ class CoreBroker(object):
r, _w, _x = select.select(rlist, [], [], 1.0)
for sock in r:
server = self.getserverbysock(sock)
logging.info("attempting to receive from server: peer:%s remote:%s",
server.sock.getpeername(), server.sock.getsockname())
logging.info(
"attempting to receive from server: peer:%s remote:%s",
server.sock.getpeername(),
server.sock.getsockname(),
)
if server is None:
# servers may have changed; loop again
continue
rcvlen = self.recv(server)
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):
"""
@ -236,7 +251,9 @@ class CoreBroker(object):
return 0
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)
msgtype, msgflags, msglen = coreapi.CoreMessage.unpack_header(msghdr)
@ -293,11 +310,17 @@ class CoreBroker(object):
with self.servers_lock:
server = self.servers.get(name)
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
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()
del self.servers[name]
@ -307,7 +330,9 @@ class CoreBroker(object):
try:
server.connect()
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:
self.startrecvloop()
self.servers[name] = server
@ -328,7 +353,12 @@ class CoreBroker(object):
logging.exception("error deleting server")
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()
def getserverbyname(self, name):
@ -414,16 +444,31 @@ class CoreBroker(object):
remotenum = n2num
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:
_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:
# no bridge is needed on physical nodes; use the GreTap directly
gt = GreTap(node=None, name=None, session=self.session,
remoteip=remoteip, key=key)
gt = GreTap(
node=None,
name=None,
session=self.session,
remoteip=remoteip,
key=key,
)
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.remotenum = remotenum
self.tunnels[key] = gt
@ -444,12 +489,10 @@ class CoreBroker(object):
:param int node_id: node id of network to add tunnel to
:return: list of gre taps
:rtype: list
:raises core.CoreError: when node to add net tunnel to does not exist
"""
try:
net = self.session.get_node(node_id)
logging.info("adding net tunnel for: id(%s) %s", node_id, net)
except KeyError:
raise KeyError("network node %s not found" % node_id)
net = self.session.get_node(node_id)
logging.info("adding net tunnel for: id(%s) %s", node_id, net)
# add other nets here that do not require tunnels
if nodeutils.is_node(net, NodeTypes.EMANE_NET):
@ -457,8 +500,13 @@ class CoreBroker(object):
return None
server_interface = getattr(net, "serverintf", None)
if 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")
if (
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
servers = self.getserversbynode(node_id)
@ -491,12 +539,18 @@ class CoreBroker(object):
myip = host
key = self.tunnelkey(node_id, IpAddress.to_int(myip))
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]
r.append(gt)
continue
logging.info("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)
logging.info(
"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
r.append(gt)
# attaching to net will later allow gt to be destroyed
@ -515,7 +569,9 @@ class CoreBroker(object):
"""
key = self.tunnelkey(n1num, n2num)
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)
except KeyError:
gt = None
@ -643,12 +699,19 @@ class CoreBroker(object):
elif message.message_type == MessageTypes.CONFIG.value:
# broadcast location and services configuration everywhere
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()
elif message.message_type == MessageTypes.FILE.value:
# broadcast hook scripts and custom service files everywhere
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()
if message.message_type == MessageTypes.LINK.value:
# prepare a server list from two node numbers in link message
@ -695,11 +758,19 @@ class CoreBroker(object):
# server of its local name
tlvdata = b""
tlvdata += coreapi.CoreConfigTlv.pack(ConfigTlvs.OBJECT.value, "broker")
tlvdata += coreapi.CoreConfigTlv.pack(ConfigTlvs.TYPE.value, ConfigFlags.UPDATE.value)
tlvdata += coreapi.CoreConfigTlv.pack(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)
tlvdata += coreapi.CoreConfigTlv.pack(
ConfigTlvs.TYPE.value, ConfigFlags.UPDATE.value
)
tlvdata += coreapi.CoreConfigTlv.pack(
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)
server.sock.send(msg)
@ -760,7 +831,10 @@ class CoreBroker(object):
if nodecls is None:
logging.warning("broker unimplemented node type %s", nodetype)
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
# don"t replicate WLANs, because ebtables rules won"t work
servers = self.getservers()
@ -810,7 +884,9 @@ class CoreBroker(object):
# determine link message destination using non-network nodes
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[1] in self.network_nodes:
# two network nodes linked together - prevent loops caused by
@ -854,7 +930,9 @@ class CoreBroker(object):
if host is None:
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:
message = self.addlinkendpoints(message, servers1, servers2)
elif message.flags & MessageFlags.ADD.value:
@ -889,10 +967,10 @@ class CoreBroker(object):
if server.host is not None:
ip2 = server.host
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))
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)
def getlinkendpoint(self, msg, first_is_local):
@ -934,10 +1012,12 @@ class CoreBroker(object):
:return: should handle locally or not
:rtype: bool
"""
hdr = msg[:coreapi.CoreMessage.header_len]
hdr = msg[: coreapi.CoreMessage.header_len]
msgtype, flags, _msglen = coreapi.CoreMessage.unpack_header(hdr)
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):
"""
@ -957,9 +1037,19 @@ class CoreBroker(object):
# local emulation server, handle this locally
handle_locally = True
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:
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)
server.sock.send(message.raw_message)
return handle_locally
@ -986,7 +1076,10 @@ class CoreBroker(object):
lhost, lport = None, None
if server.sock:
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:
logging.exception("error writing server list to the file: %s", filename)
@ -1015,7 +1108,9 @@ class CoreBroker(object):
with open(filename, "w") as f:
f.write("%s\n%s\n" % (serverstr, nodestr))
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):
"""
@ -1031,7 +1126,9 @@ class CoreBroker(object):
# broadcast out instantiate complete
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)
for session_client in self.session_clients:
session_client.sendall(message)

View file

@ -7,26 +7,27 @@ CORE API messaging is leveraged for communication with the GUI.
import socket
import struct
from past.builtins import basestring
from enum import Enum
from past.builtins import basestring
from core.api.tlv import structutils
from core.emulator.enumerations import ConfigTlvs
from core.emulator.enumerations import EventTlvs
from core.emulator.enumerations import EventTypes
from core.emulator.enumerations import ExceptionTlvs
from core.emulator.enumerations import ExecuteTlvs
from core.emulator.enumerations import FileTlvs
from core.emulator.enumerations import InterfaceTlvs
from core.emulator.enumerations import LinkTlvs
from core.emulator.enumerations import MessageFlags
from core.emulator.enumerations import MessageTypes
from core.emulator.enumerations import NodeTlvs
from core.emulator.enumerations import RegisterTlvs
from core.emulator.enumerations import SessionTlvs
from core.nodes.ipaddress import IpAddress
from core.nodes.ipaddress import MacAddress
from core.emulator.enumerations import (
ConfigTlvs,
EventTlvs,
EventTypes,
ExceptionTlvs,
ExecuteTlvs,
FileTlvs,
InterfaceTlvs,
LinkTlvs,
MessageFlags,
MessageTypes,
NodeTlvs,
RegisterTlvs,
SessionTlvs,
)
from core.nodes.ipaddress import IpAddress, MacAddress
class CoreTlvData(object):
@ -139,6 +140,7 @@ class CoreTlvDataUint16(CoreTlvData):
"""
Helper class for packing uint16 data.
"""
data_format = "!H"
data_type = int
pad_len = 0
@ -148,6 +150,7 @@ class CoreTlvDataUint32(CoreTlvData):
"""
Helper class for packing uint32 data.
"""
data_format = "!2xI"
data_type = int
pad_len = 2
@ -157,6 +160,7 @@ class CoreTlvDataUint64(CoreTlvData):
"""
Helper class for packing uint64 data.
"""
data_format = "!2xQ"
data_type = int
pad_len = 2
@ -166,6 +170,7 @@ class CoreTlvDataString(CoreTlvData):
"""
Helper class for packing string data.
"""
data_type = str
@classmethod
@ -204,6 +209,7 @@ class CoreTlvDataUint16List(CoreTlvData):
"""
List of unsigned 16-bit values.
"""
data_type = tuple
data_format = "!H"
@ -253,6 +259,7 @@ class CoreTlvDataIpv4Addr(CoreTlvDataObj):
"""
Utility class for packing/unpacking Ipv4 addresses.
"""
data_type = IpAddress.from_string
data_format = "!2x4s"
pad_len = 2
@ -283,6 +290,7 @@ class CoreTlvDataIPv6Addr(CoreTlvDataObj):
"""
Utility class for packing/unpacking Ipv6 addresses.
"""
data_format = "!16s2x"
data_type = IpAddress.from_string
pad_len = 2
@ -313,6 +321,7 @@ class CoreTlvDataMacAddr(CoreTlvDataObj):
"""
Utility class for packing/unpacking mac addresses.
"""
data_format = "!2x8s"
data_type = MacAddress.from_string
pad_len = 2
@ -345,6 +354,7 @@ class CoreTlv(object):
"""
Base class for representing CORE TLVs.
"""
header_format = "!BB"
header_len = struct.calcsize(header_format)
@ -379,10 +389,12 @@ class CoreTlv(object):
:param data: data to unpack
: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
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
tlv_size = header_len + tlv_len
# for 32-bit alignment
@ -435,7 +447,11 @@ class CoreTlv(object):
:return: string representation
: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):
@ -686,14 +702,16 @@ class CoreMessage(object):
:return: unpacked 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
@classmethod
def create(cls, flags, values):
tlv_data = structutils.pack_values(cls.tlv_class, values)
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)
@classmethod
@ -705,7 +723,9 @@ class CoreMessage(object):
:param tlv_data: data to get length from for packing
: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
def add_tlv_data(self, key, value):
@ -807,7 +827,11 @@ class CoreMessage(object):
:return: string representation
: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:
value = self.tlv_data[key]
@ -879,6 +903,7 @@ class CoreNodeMessage(CoreMessage):
"""
CORE node message class.
"""
message_type = MessageTypes.NODE.value
tlv_class = CoreNodeTlv
@ -887,6 +912,7 @@ class CoreLinkMessage(CoreMessage):
"""
CORE link message class.
"""
message_type = MessageTypes.LINK.value
tlv_class = CoreLinkTlv
@ -895,6 +921,7 @@ class CoreExecMessage(CoreMessage):
"""
CORE execute message class.
"""
message_type = MessageTypes.EXECUTE.value
tlv_class = CoreExecuteTlv
@ -903,6 +930,7 @@ class CoreRegMessage(CoreMessage):
"""
CORE register message class.
"""
message_type = MessageTypes.REGISTER.value
tlv_class = CoreRegisterTlv
@ -911,6 +939,7 @@ class CoreConfMessage(CoreMessage):
"""
CORE configuration message class.
"""
message_type = MessageTypes.CONFIG.value
tlv_class = CoreConfigTlv
@ -919,6 +948,7 @@ class CoreFileMessage(CoreMessage):
"""
CORE file message class.
"""
message_type = MessageTypes.FILE.value
tlv_class = CoreFileTlv
@ -927,6 +957,7 @@ class CoreIfaceMessage(CoreMessage):
"""
CORE interface message class.
"""
message_type = MessageTypes.INTERFACE.value
tlv_class = CoreInterfaceTlv
@ -935,6 +966,7 @@ class CoreEventMessage(CoreMessage):
"""
CORE event message class.
"""
message_type = MessageTypes.EVENT.value
tlv_class = CoreEventTlv
@ -943,6 +975,7 @@ class CoreSessionMessage(CoreMessage):
"""
CORE session message class.
"""
message_type = MessageTypes.SESSION.value
tlv_class = CoreSessionTlv
@ -951,6 +984,7 @@ class CoreExceptionMessage(CoreMessage):
"""
CORE exception message class.
"""
message_type = MessageTypes.EXCEPTION.value
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
incoming connections.
"""
daemon_threads = 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
incoming connections.
"""
daemon_threads = 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.emulator.enumerations import ConfigTlvs
from core.emulator.enumerations import NodeTlvs
from core.emulator.enumerations import ConfigTlvs, NodeTlvs
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
:return: packed node message
"""
tlv_data = structutils.pack_values(coreapi.CoreNodeTlv, [
(NodeTlvs.NUMBER, node_data.id),
(NodeTlvs.TYPE, node_data.node_type),
(NodeTlvs.NAME, node_data.name),
(NodeTlvs.IP_ADDRESS, node_data.ip_address),
(NodeTlvs.MAC_ADDRESS, node_data.mac_address),
(NodeTlvs.IP6_ADDRESS, node_data.ip6_address),
(NodeTlvs.MODEL, node_data.model),
(NodeTlvs.EMULATION_ID, node_data.emulation_id),
(NodeTlvs.EMULATION_SERVER, node_data.emulation_server),
(NodeTlvs.SESSION, node_data.session),
(NodeTlvs.X_POSITION, node_data.x_position),
(NodeTlvs.Y_POSITION, node_data.y_position),
(NodeTlvs.CANVAS, node_data.canvas),
(NodeTlvs.NETWORK_ID, node_data.network_id),
(NodeTlvs.SERVICES, node_data.services),
(NodeTlvs.LATITUDE, node_data.latitude),
(NodeTlvs.LONGITUDE, node_data.longitude),
(NodeTlvs.ALTITUDE, node_data.altitude),
(NodeTlvs.ICON, node_data.icon),
(NodeTlvs.OPAQUE, node_data.opaque)
])
tlv_data = structutils.pack_values(
coreapi.CoreNodeTlv,
[
(NodeTlvs.NUMBER, node_data.id),
(NodeTlvs.TYPE, node_data.node_type),
(NodeTlvs.NAME, node_data.name),
(NodeTlvs.IP_ADDRESS, node_data.ip_address),
(NodeTlvs.MAC_ADDRESS, node_data.mac_address),
(NodeTlvs.IP6_ADDRESS, node_data.ip6_address),
(NodeTlvs.MODEL, node_data.model),
(NodeTlvs.EMULATION_ID, node_data.emulation_id),
(NodeTlvs.EMULATION_SERVER, node_data.emulation_server),
(NodeTlvs.SESSION, node_data.session),
(NodeTlvs.X_POSITION, node_data.x_position),
(NodeTlvs.Y_POSITION, node_data.y_position),
(NodeTlvs.CANVAS, node_data.canvas),
(NodeTlvs.NETWORK_ID, node_data.network_id),
(NodeTlvs.SERVICES, node_data.services),
(NodeTlvs.LATITUDE, node_data.latitude),
(NodeTlvs.LONGITUDE, node_data.longitude),
(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)
@ -46,19 +48,22 @@ def convert_config(config_data):
:param core.emulator.data.ConfigData config_data: config data to convert
:return: packed message
"""
tlv_data = structutils.pack_values(coreapi.CoreConfigTlv, [
(ConfigTlvs.NODE, config_data.node),
(ConfigTlvs.OBJECT, config_data.object),
(ConfigTlvs.TYPE, config_data.type),
(ConfigTlvs.DATA_TYPES, config_data.data_types),
(ConfigTlvs.VALUES, config_data.data_values),
(ConfigTlvs.CAPTIONS, config_data.captions),
(ConfigTlvs.BITMAP, config_data.bitmap),
(ConfigTlvs.POSSIBLE_VALUES, config_data.possible_values),
(ConfigTlvs.GROUPS, config_data.groups),
(ConfigTlvs.SESSION, config_data.session),
(ConfigTlvs.INTERFACE_NUMBER, config_data.interface_number),
(ConfigTlvs.NETWORK_ID, config_data.network_id),
(ConfigTlvs.OPAQUE, config_data.opaque),
])
tlv_data = structutils.pack_values(
coreapi.CoreConfigTlv,
[
(ConfigTlvs.NODE, config_data.node),
(ConfigTlvs.OBJECT, config_data.object),
(ConfigTlvs.TYPE, config_data.type),
(ConfigTlvs.DATA_TYPES, config_data.data_types),
(ConfigTlvs.VALUES, config_data.data_values),
(ConfigTlvs.CAPTIONS, config_data.captions),
(ConfigTlvs.BITMAP, config_data.bitmap),
(ConfigTlvs.POSSIBLE_VALUES, config_data.possible_values),
(ConfigTlvs.GROUPS, config_data.groups),
(ConfigTlvs.SESSION, config_data.session),
(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)

View file

@ -3,6 +3,7 @@ Utilities for working with python struct data.
"""
import logging
from past.builtins import basestring

View file

@ -40,7 +40,11 @@ class ConfigShim(object):
"""
group_strings = []
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)
return "|".join(group_strings)
@ -96,7 +100,7 @@ class ConfigShim(object):
captions=captions,
possible_values="|".join(possible_values),
bitmap=configurable_options.bitmap,
groups=groups_str
groups=groups_str,
)
@ -127,13 +131,19 @@ class Configuration(object):
def __str__(self):
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):
"""
Provides convenience methods for storing and retrieving configuration options for nodes.
"""
_default_node = -1
_default_type = _default_node
@ -187,11 +197,15 @@ class ConfigurableManager(object):
:param str config_type: configuration type to store configuration for
: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[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.
@ -256,6 +270,7 @@ class ConfigurableOptions(object):
"""
Provides a base for defining configuration options within CORE.
"""
name = None
bitmap = None
options = []
@ -278,9 +293,7 @@ class ConfigurableOptions(object):
:return: configuration group definition
:rtype: list[ConfigGroup]
"""
return [
ConfigGroup("Options", 1, len(cls.configurations()))
]
return [ConfigGroup("Options", 1, len(cls.configurations()))]
@classmethod
def default_values(cls):
@ -290,7 +303,9 @@ class ConfigurableOptions(object):
:return: ordered configuration mapping default values
: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):
@ -336,7 +351,7 @@ class ModelManager(ConfigurableManager):
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 str model_name: model to set configuration for
@ -365,7 +380,9 @@ class ModelManager(ConfigurableManager):
:param dict config: model configuration, None for default configuration
: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)
config = self.get_model_config(node.id, model_class.name)
node.setmodel(model_class, config)

View file

@ -1,8 +1,7 @@
"""
EMANE Bypass model for CORE
"""
from core.config import ConfigGroup
from core.config import Configuration
from core.config import ConfigGroup, Configuration
from core.emane import emanemodel
from core.emulator.enumerations import ConfigDataTypes
@ -21,7 +20,7 @@ class EmaneBypassModel(emanemodel.EmaneModel):
_type=ConfigDataTypes.BOOL,
default="0",
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
@classmethod
def config_groups(cls):
return [
ConfigGroup("Bypass Parameters", 1, 1),
]
return [ConfigGroup("Bypass Parameters", 1, 1)]

View file

@ -4,12 +4,13 @@ commeffect.py: EMANE CommEffect model for CORE
import logging
import os
from builtins import int
from lxml import etree
from past.builtins import basestring
from core.config import ConfigGroup
from core.emane import emanemanifest
from core.emane import emanemodel
from core.emane import emanemanifest, emanemodel
from core.xml import emanexml
try:
@ -56,9 +57,7 @@ class EmaneCommEffectModel(emanemodel.EmaneModel):
@classmethod
def config_groups(cls):
return [
ConfigGroup("CommEffect SHIM Parameters", 1, len(cls.configurations()))
]
return [ConfigGroup("CommEffect SHIM Parameters", 1, len(cls.configurations()))]
def build_xml_files(self, config, interface=None):
"""
@ -76,7 +75,9 @@ class EmaneCommEffectModel(emanemodel.EmaneModel):
shim_name = emanexml.shim_file_name(self, interface)
# 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"
if interface and interface.transport_type == "raw":
transport_type = "raw"
@ -90,7 +91,9 @@ class EmaneCommEffectModel(emanemodel.EmaneModel):
emanexml.create_file(nem_element, "nem", nem_file)
# 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
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)
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
link parameters.
@ -136,7 +148,7 @@ class EmaneCommEffectModel(emanemodel.EmaneModel):
jitter=convert_none(jitter),
loss=convert_none(loss),
duplicate=convert_none(duplicate),
unicast=long(convert_none(bw)),
broadcast=long(convert_none(mbw))
unicast=int(convert_none(bw)),
broadcast=int(convert_none(mbw)),
)
service.publish(nemid2, event)

View file

@ -7,13 +7,9 @@ import logging
import os
import threading
from core import CoreCommandError, utils
from core import constants
from core import CoreCommandError, CoreError, constants, utils
from core.api.tlv import coreapi, dataconversion
from core.config import ConfigGroup
from core.config import ConfigShim
from core.config import Configuration
from core.config import ModelManager
from core.config import ConfigGroup, ConfigShim, Configuration, ModelManager
from core.emane import emanemanifest
from core.emane.bypass import EmaneBypassModel
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.rfpipe import EmaneRfPipeModel
from core.emane.tdma import EmaneTdmaModel
from core.emulator.enumerations import ConfigDataTypes
from core.emulator.enumerations import ConfigFlags
from core.emulator.enumerations import ConfigTlvs
from core.emulator.enumerations import MessageFlags
from core.emulator.enumerations import MessageTypes
from core.emulator.enumerations import NodeTypes
from core.emulator.enumerations import RegisterTlvs
from core.emulator.enumerations import (
ConfigDataTypes,
ConfigFlags,
ConfigTlvs,
MessageFlags,
MessageTypes,
NodeTypes,
RegisterTlvs,
)
from core.nodes import nodeutils
from core.xml import emanexml
@ -48,7 +46,7 @@ EMANE_MODELS = [
EmaneIeee80211abgModel,
EmaneCommEffectModel,
EmaneBypassModel,
EmaneTdmaModel
EmaneTdmaModel,
]
DEFAULT_EMANE_PREFIX = "/usr"
@ -59,6 +57,7 @@ class EmaneManager(ModelManager):
building EMANE config files from all of the EmaneNode objects in this
emulation, and for controlling the EMANE daemons.
"""
name = "emane"
config_type = RegisterTlvs.EMULATION_SERVER.value
SUCCESS, NOT_NEEDED, NOT_READY = (0, 1, 2)
@ -79,8 +78,12 @@ class EmaneManager(ModelManager):
self._ifccounts = {}
self._ifccountslock = threading.Lock()
# port numbers are allocated from these counters
self.platformport = self.session.options.get_config_int("emane_platform_port", 8100)
self.transformport = self.session.options.get_config_int("emane_transform_port", 8200)
self.platformport = self.session.options.get_config_int(
"emane_platform_port", 8100
)
self.transformport = self.session.options.get_config_int(
"emane_transform_port", 8200
)
self.doeventloop = False
self.eventmonthread = None
@ -124,7 +127,9 @@ class EmaneManager(ModelManager):
# otherwise retrieve the interfaces node configuration, avoid using defaults
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
if not config:
@ -186,11 +191,15 @@ class EmaneManager(ModelManager):
self.event_device = self.get_config("eventservicedevice")
eventnetidx = self.session.get_control_net_index(self.event_device)
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
# 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:
# direct EMANE events towards control net bridge
self.event_device = eventnet.brname
@ -211,8 +220,10 @@ class EmaneManager(ModelManager):
Load EMANE models and make them available.
"""
for emane_model in emane_models:
logging.info("loading emane model: %s", emane_model.__name__)
emane_prefix = self.session.options.get_config("emane_prefix", default=DEFAULT_EMANE_PREFIX)
logging.debug("loading emane model: %s", emane_model.__name__)
emane_prefix = self.session.options.get_config(
"emane_prefix", default=DEFAULT_EMANE_PREFIX
)
emane_model.load(emane_prefix)
self.models[emane_model.name] = emane_model
@ -225,7 +236,9 @@ class EmaneManager(ModelManager):
"""
with self._emane_node_lock:
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
def getnodes(self):
@ -254,7 +267,9 @@ class EmaneManager(ModelManager):
for node_id in self.session.nodes:
node = self.session.nodes[node_id]
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)
if not self._emane_nodes:
@ -267,12 +282,19 @@ class EmaneManager(ModelManager):
if self.session.master:
otadev = self.get_config("otamanagerdevice")
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:
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
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)
eventdev = self.get_config("eventservicedevice")
logging.debug("emane event service device: eventdev(%s)", eventdev)
@ -280,10 +302,15 @@ class EmaneManager(ModelManager):
netidx = self.session.get_control_net_index(eventdev)
logging.debug("emane event service device index: %s", netidx)
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
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)
if self.checkdistributed():
@ -323,7 +350,9 @@ class EmaneManager(ModelManager):
for node_id in self._emane_nodes:
emane_node = self._emane_nodes[node_id]
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:
emane_nems_filename = os.path.join(self.session.session_dir, "emane_nems")
@ -346,7 +375,11 @@ class EmaneManager(ModelManager):
with self._emane_node_lock:
for key in sorted(self._emane_nodes.keys()):
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()
for netif in emane_node.netifs():
x, y, z = netif.node.position.get()
@ -361,8 +394,12 @@ class EmaneManager(ModelManager):
self._emane_nodes.clear()
# 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.transformport = self.session.options.get_config_int("emane_transform_port", 8200)
self.platformport = self.session.options.get_config_int(
"emane_platform_port", 8100
)
self.transformport = self.session.options.get_config_int(
"emane_transform_port", 8200
)
def shutdown(self):
"""
@ -385,7 +422,10 @@ class EmaneManager(ModelManager):
received. This is used to snoop the Link add messages to get NEM
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()
# first node is always link layer node in Link add message
if nn[0] in self.session.broker.network_nodes:
@ -450,7 +490,9 @@ class EmaneManager(ModelManager):
config = copy.deepcopy(self.get_configs())
config["platform_id_start"] = str(platformid)
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)
server.sock.send(message)
# increment nemid for next server by number of interfaces
@ -469,7 +511,9 @@ class EmaneManager(ModelManager):
# assume self._objslock is already held here
logging.info("emane building xml...")
# 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.buildnemxml()
self.buildeventservicexml()
@ -495,7 +539,10 @@ class EmaneManager(ModelManager):
prefix = session.options.get_config("controlnet", default="")
prefixes = prefix.split()
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]
prefixes = prefix.split()
servers.remove("localhost")
@ -510,8 +557,10 @@ class EmaneManager(ModelManager):
tlvdata += coreapi.CoreConfigTlv.pack(ConfigTlvs.TYPE.value, 0)
tlvdata += coreapi.CoreConfigTlv.pack(ConfigTlvs.VALUES.value, vals)
rawmsg = coreapi.CoreConfMessage.pack(0, tlvdata)
msghdr = rawmsg[:coreapi.CoreMessage.header_len]
msg = coreapi.CoreConfMessage(flags=0, hdr=msghdr, data=rawmsg[coreapi.CoreMessage.header_len:])
msghdr = 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)
self.session.broker.handle_message(msg)
@ -526,7 +575,11 @@ class EmaneManager(ModelManager):
# skip nodes that already have a model set
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
# 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
for key in sorted(self._emane_nodes.keys()):
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):
"""
@ -593,7 +648,9 @@ class EmaneManager(ModelManager):
"""
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):
"""
@ -638,7 +695,7 @@ class EmaneManager(ModelManager):
emanecmd = ["emane", "-d", "-l", loglevel]
if realtime:
emanecmd += "-r",
emanecmd += ("-r",)
otagroup, _otaport = self.get_config("otamanagergroup").split(":")
otadev = self.get_config("otamanagerdevice")
@ -657,27 +714,37 @@ class EmaneManager(ModelManager):
n = node.id
# 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:
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:
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
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
if eventservicenetidx >= 0 and eventgroup != otagroup:
args = [constants.IP_BIN, "route", "add", eventgroup, "dev", eventdev]
node.check_cmd(args)
node.network_cmd(args)
# 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)
logging.info("node(%s) emane daemon running: %s", node.name, args)
logging.info("node(%s) emane daemon output: %s", node.name, output)
@ -760,10 +827,12 @@ class EmaneManager(ModelManager):
return
if self.service is None:
logging.error("Warning: EMANE events will not be generated "
"because the emaneeventservice\n binding was "
"unable to load "
"(install the python-emaneeventservice bindings)")
logging.error(
"Warning: EMANE events will not be generated "
"because the emaneeventservice\n binding was "
"unable to load "
"(install the python-emaneeventservice bindings)"
)
return
self.doeventloop = True
self.eventmonthread = threading.Thread(target=self.eventmonitorloop)
@ -792,7 +861,10 @@ class EmaneManager(ModelManager):
"""
if self.service is None:
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:
_uuid, _seq, events = self.service.nextEvent()
@ -805,7 +877,10 @@ class EmaneManager(ModelManager):
if eid == LocationEvent.IDENTIFIER:
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):
"""
@ -815,7 +890,11 @@ class EmaneManager(ModelManager):
events.restore(data)
for event in events:
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")
continue
@ -844,20 +923,37 @@ class EmaneManager(ModelManager):
x = int(x)
y = int(y)
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
ybit_check = y.bit_length() > 16 or y < 0
zbit_check = z.bit_length() > 16 or z < 0
if any([xbit_check, ybit_check, zbit_check]):
logging.error("Unable to build node location message, received lat/long/alt exceeds coordinate "
"space: NEM %s (%d, %d, %d)", nemid, x, y, z)
logging.error(
"Unable to build node location message, received lat/long/alt exceeds coordinate "
"space: NEM %s (%d, %d, %d)",
nemid,
x,
y,
z,
)
return False
# generate a node message for this location update
try:
node = self.session.get_node(n)
except KeyError:
logging.exception("location event NEM %s has no corresponding node %s" % (nemid, n))
except CoreError:
logging.exception(
"location event NEM %s has no corresponding node %s" % (nemid, n)
)
return False
# don"t use node.setposition(x,y,z) which generates an event
@ -889,18 +985,26 @@ class EmaneGlobalModel(EmaneModel):
"eventservicedevice": _DEFAULT_DEV,
"eventservicegroup": "224.1.2.8:45703",
"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.insert(
0,
Configuration(_id="platform_id_start", _type=ConfigDataTypes.INT32, default="1",
label="Starting Platform ID (core)")
Configuration(
_id="platform_id_start",
_type=ConfigDataTypes.INT32,
default="1",
label="Starting Platform ID (core)",
),
)
nem_config = [
Configuration(_id="nem_id_start", _type=ConfigDataTypes.INT32, default="1",
label="Starting NEM ID (core)")
Configuration(
_id="nem_id_start",
_type=ConfigDataTypes.INT32,
default="1",
label="Starting NEM ID (core)",
)
]
@classmethod
@ -913,7 +1017,7 @@ class EmaneGlobalModel(EmaneModel):
config_len = len(cls.configurations())
return [
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):

View file

@ -122,7 +122,7 @@ def parse(manifest_path, defaults):
_type=config_type_value,
default=config_default,
options=possible,
label=config_descriptions
label=config_descriptions,
)
configurations.append(configuration)

View file

@ -4,8 +4,8 @@ Defines Emane Models used within CORE.
import logging
import os
from core.config import ConfigGroup
from core.config import Configuration
from core import CoreError
from core.config import ConfigGroup, Configuration
from core.emane import emanemanifest
from core.emulator.enumerations import ConfigDataTypes
from core.location.mobility import WirelessModel
@ -18,6 +18,7 @@ class EmaneModel(WirelessModel):
handling configuration messages based on the list of
configurable parameters. Helper functions also live here.
"""
# default mac configuration settings
mac_library = None
mac_xml = None
@ -27,18 +28,18 @@ class EmaneModel(WirelessModel):
# default phy configuration settings, using the universal model
phy_library = None
phy_xml = "emanephy.xml"
phy_defaults = {
"subid": "1",
"propagationmodel": "2ray",
"noisemode": "none"
}
phy_defaults = {"subid": "1", "propagationmodel": "2ray", "noisemode": "none"}
phy_config = []
# support for external configurations
external_config = [
Configuration("external", ConfigDataTypes.BOOL, default="0"),
Configuration("platformendpoint", ConfigDataTypes.STRING, default="127.0.0.1:40001"),
Configuration("transportendpoint", ConfigDataTypes.STRING, default="127.0.0.1:50002")
Configuration(
"platformendpoint", ConfigDataTypes.STRING, default="127.0.0.1:40001"
),
Configuration(
"transportendpoint", ConfigDataTypes.STRING, default="127.0.0.1:50002"
),
]
config_ignore = set()
@ -85,7 +86,7 @@ class EmaneModel(WirelessModel):
return [
ConfigGroup("MAC Parameters", 1, mac_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):
@ -109,7 +110,9 @@ class EmaneModel(WirelessModel):
# create nem xml file
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
mac_file = os.path.join(self.session.session_dir, mac_name)
@ -125,7 +128,7 @@ class EmaneModel(WirelessModel):
: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):
"""
@ -140,10 +143,19 @@ class EmaneModel(WirelessModel):
try:
wlan = self.session.get_node(self.id)
wlan.setnempositions(moved_netifs)
except KeyError:
except CoreError:
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.
@ -156,4 +168,6 @@ class EmaneModel(WirelessModel):
:param core.netns.vif.Veth netif2: interface two
: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
def load(cls, emane_prefix):
cls.mac_defaults["pcrcurveuri"] = os.path.join(
emane_prefix,
"share/emane/xml/models/mac/ieee80211abg/ieee80211pcr.xml"
emane_prefix, "share/emane/xml/models/mac/ieee80211abg/ieee80211pcr.xml"
)
super(EmaneIeee80211abgModel, cls).load(emane_prefix)

View file

@ -6,10 +6,8 @@ share the same MAC+PHY model.
import logging
from core.emulator.enumerations import LinkTypes, NodeTypes, RegisterTlvs
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:
from emane.events import LocationEvent
@ -24,6 +22,7 @@ class EmaneNet(CoreNetworkBase):
"""
EMANE network base class.
"""
apitype = NodeTypes.EMANE.value
linktype = LinkTypes.WIRELESS.value
# icon used
@ -45,14 +44,30 @@ class EmaneNode(EmaneNet):
self.model = 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.
"""
if not self.model:
return
return self.model.linkconfig(netif=netif, bw=bw, delay=delay, loss=loss,
duplicate=duplicate, jitter=jitter, netif2=netif2)
return self.model.linkconfig(
netif=netif,
bw=bw,
delay=delay,
loss=loss,
duplicate=duplicate,
jitter=jitter,
netif2=netif2,
)
def config(self, conf):
self.conf = conf
@ -69,7 +84,9 @@ class EmaneNode(EmaneNet):
def updatemodel(self, config):
if not self.model:
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)
def setmodel(self, model, config):
@ -124,13 +141,18 @@ class EmaneNode(EmaneNet):
EMANE daemons have been started, because that is their only chance
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 += "Python bindings failed to load"
logging.error(warntxt)
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":
netif.setaddrs()
@ -168,7 +190,6 @@ class EmaneNode(EmaneNet):
logging.info("nemid for %s is unknown", ifname)
return
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()
# altitude must be an integer or warning is printed
@ -200,8 +221,6 @@ class EmaneNode(EmaneNet):
continue
x, y, z = netif.node.getposition()
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
alt = int(round(alt))
event.append(nemid, latitude=lat, longitude=lon, altitude=alt)

View file

@ -17,7 +17,6 @@ class EmaneRfPipeModel(emanemodel.EmaneModel):
@classmethod
def load(cls, emane_prefix):
cls.mac_defaults["pcrcurveuri"] = os.path.join(
emane_prefix,
"share/emane/xml/models/mac/rfpipe/rfpipepcr.xml"
emane_prefix, "share/emane/xml/models/mac/rfpipe/rfpipepcr.xml"
)
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
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}
@classmethod
def load(cls, emane_prefix):
cls.mac_defaults["pcrcurveuri"] = os.path.join(
emane_prefix,
"share/emane/xml/models/mac/tdmaeventscheduler/tdmabasemodelpcr.xml"
"share/emane/xml/models/mac/tdmaeventscheduler/tdmabasemodelpcr.xml",
)
super(EmaneTdmaModel, cls).load(emane_prefix)
cls.mac_config.insert(
@ -37,8 +39,8 @@ class EmaneTdmaModel(emanemodel.EmaneModel):
_id=cls.schedule_name,
_type=ConfigDataTypes.STRING,
default=cls.default_schedule,
label="TDMA schedule file (core)"
)
label="TDMA schedule file (core)",
),
)
def post_startup(self):
@ -57,5 +59,7 @@ class EmaneTdmaModel(emanemodel.EmaneModel):
event_device = self.session.emane.event_device
# 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])

View file

@ -7,8 +7,7 @@ import sys
import core.services
from core.emulator.emudata import IdGen
from core.emulator.session import Session
from core.nodes import nodemaps
from core.nodes import nodeutils
from core.nodes import nodemaps, nodeutils
from core.services.coreservices import ServiceManager
@ -73,7 +72,7 @@ class CoreEmu(object):
service_paths = self.config.get("custom_services_dir")
logging.debug("custom service paths: %s", service_paths)
if service_paths:
for service_path in service_paths.split(','):
for service_path in service_paths.split(","):
service_path = service_path.strip()
custom_service_errors = ServiceManager.add_services(service_path)
self.service_errors.extend(custom_service_errors)

View file

@ -4,117 +4,118 @@ CORE data objects.
import collections
ConfigData = collections.namedtuple("ConfigData", [
"message_type",
"node",
"object",
"type",
"data_types",
"data_values",
"captions",
"bitmap",
"possible_values",
"groups",
"session",
"interface_number",
"network_id",
"opaque"
])
ConfigData = collections.namedtuple(
"ConfigData",
[
"message_type",
"node",
"object",
"type",
"data_types",
"data_values",
"captions",
"bitmap",
"possible_values",
"groups",
"session",
"interface_number",
"network_id",
"opaque",
],
)
ConfigData.__new__.__defaults__ = (None,) * len(ConfigData._fields)
EventData = collections.namedtuple("EventData", [
"node",
"event_type",
"name",
"data",
"time",
"session"
])
EventData = collections.namedtuple(
"EventData", ["node", "event_type", "name", "data", "time", "session"]
)
EventData.__new__.__defaults__ = (None,) * len(EventData._fields)
ExceptionData = collections.namedtuple("ExceptionData", [
"node",
"session",
"level",
"source",
"date",
"text",
"opaque"
])
ExceptionData = collections.namedtuple(
"ExceptionData", ["node", "session", "level", "source", "date", "text", "opaque"]
)
ExceptionData.__new__.__defaults__ = (None,) * len(ExceptionData._fields)
FileData = collections.namedtuple("FileData", [
"message_type",
"node",
"name",
"mode",
"number",
"type",
"source",
"session",
"data",
"compressed_data"
])
FileData = collections.namedtuple(
"FileData",
[
"message_type",
"node",
"name",
"mode",
"number",
"type",
"source",
"session",
"data",
"compressed_data",
],
)
FileData.__new__.__defaults__ = (None,) * len(FileData._fields)
NodeData = collections.namedtuple("NodeData", [
"message_type",
"id",
"node_type",
"name",
"ip_address",
"mac_address",
"ip6_address",
"model",
"emulation_id",
"emulation_server",
"session",
"x_position",
"y_position",
"canvas",
"network_id",
"services",
"latitude",
"longitude",
"altitude",
"icon",
"opaque"
])
NodeData = collections.namedtuple(
"NodeData",
[
"message_type",
"id",
"node_type",
"name",
"ip_address",
"mac_address",
"ip6_address",
"model",
"emulation_id",
"emulation_server",
"session",
"x_position",
"y_position",
"canvas",
"network_id",
"services",
"latitude",
"longitude",
"altitude",
"icon",
"opaque",
],
)
NodeData.__new__.__defaults__ = (None,) * len(NodeData._fields)
LinkData = collections.namedtuple("LinkData", [
"message_type",
"node1_id",
"node2_id",
"delay",
"bandwidth",
"per",
"dup",
"jitter",
"mer",
"burst",
"session",
"mburst",
"link_type",
"gui_attributes",
"unidirectional",
"emulation_id",
"network_id",
"key",
"interface1_id",
"interface1_name",
"interface1_ip4",
"interface1_ip4_mask",
"interface1_mac",
"interface1_ip6",
"interface1_ip6_mask",
"interface2_id",
"interface2_name",
"interface2_ip4",
"interface2_ip4_mask",
"interface2_mac",
"interface2_ip6",
"interface2_ip6_mask",
"opaque"
])
LinkData = collections.namedtuple(
"LinkData",
[
"message_type",
"node1_id",
"node2_id",
"delay",
"bandwidth",
"per",
"dup",
"jitter",
"mer",
"burst",
"session",
"mburst",
"link_type",
"gui_attributes",
"unidirectional",
"emulation_id",
"network_id",
"key",
"interface1_id",
"interface1_name",
"interface1_ip4",
"interface1_ip4_mask",
"interface1_mac",
"interface1_ip6",
"interface1_ip6_mask",
"interface2_id",
"interface2_name",
"interface2_ip4",
"interface2_ip4_mask",
"interface2_mac",
"interface2_ip6",
"interface2_ip6_mask",
"opaque",
],
)
LinkData.__new__.__defaults__ = (None,) * len(LinkData._fields)

View file

@ -1,10 +1,7 @@
from core.emulator.enumerations import LinkTypes
from core.emulator.enumerations import NodeTypes
from core.emulator.enumerations import LinkTypes, NodeTypes
from core.nodes import nodeutils
from core.nodes.base import CoreNetworkBase
from core.nodes.ipaddress import Ipv4Prefix
from core.nodes.ipaddress import Ipv6Prefix
from core.nodes.ipaddress import MacAddress
from core.nodes.ipaddress import Ipv4Prefix, Ipv6Prefix, MacAddress
class IdGen(object):
@ -41,7 +38,7 @@ def create_interface(node, network, interface_data):
addrlist=interface_data.get_addresses(),
hwaddr=interface_data.mac,
ifindex=interface_data.id,
ifname=interface_data.name
ifname=interface_data.name,
)
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,
"duplicate": link_options.dup,
"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
@ -79,12 +76,13 @@ class NodeOptions(object):
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.
: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 image: image to use for docker nodes
"""
self.name = name
self.model = model
@ -99,6 +97,7 @@ class NodeOptions(object):
self.alt = None
self.emulation_id = None
self.emulation_server = None
self.image = image
def set_position(self, x, y):
"""
@ -240,7 +239,7 @@ class IpPrefixes(object):
ip4_mask=ip4_mask,
ip6=ip6,
ip6_mask=ip6_mask,
mac=mac
mac=mac,
)

View file

@ -12,6 +12,7 @@ class MessageTypes(Enum):
"""
CORE message types.
"""
NODE = 0x01
LINK = 0x02
EXECUTE = 0x03
@ -28,6 +29,7 @@ class MessageFlags(Enum):
"""
CORE message flags.
"""
ADD = 0x01
DELETE = 0x02
CRI = 0x04
@ -41,6 +43,7 @@ class NodeTlvs(Enum):
"""
Node type, length, value enumerations.
"""
NUMBER = 0x01
TYPE = 0x02
NAME = 0x03
@ -67,6 +70,7 @@ class NodeTypes(Enum):
"""
Node types.
"""
DEFAULT = 0
PHYSICAL = 1
TBD = 3
@ -81,12 +85,15 @@ class NodeTypes(Enum):
PEER_TO_PEER = 12
CONTROL_NET = 13
EMANE_NET = 14
DOCKER = 15
LXC = 16
class Rj45Models(Enum):
"""
RJ45 model types.
"""
LINKED = 0
WIRELESS = 1
INSTALLED = 2
@ -97,6 +104,7 @@ class LinkTlvs(Enum):
"""
Link type, length, value enumerations.
"""
N1_NUMBER = 0x01
N2_NUMBER = 0x02
DELAY = 0x03
@ -135,6 +143,7 @@ class LinkTypes(Enum):
"""
Link types.
"""
WIRELESS = 0
WIRED = 1
@ -143,6 +152,7 @@ class ExecuteTlvs(Enum):
"""
Execute type, length, value enumerations.
"""
NODE = 0x01
NUMBER = 0x02
TIME = 0x03
@ -156,6 +166,7 @@ class RegisterTlvs(Enum):
"""
Register type, length, value enumerations.
"""
WIRELESS = 0x01
MOBILITY = 0x02
UTILITY = 0x03
@ -169,6 +180,7 @@ class ConfigTlvs(Enum):
"""
Configuration type, length, value enumerations.
"""
NODE = 0x01
OBJECT = 0x02
TYPE = 0x03
@ -188,6 +200,7 @@ class ConfigFlags(Enum):
"""
Configuration flags.
"""
NONE = 0x00
REQUEST = 0x01
UPDATE = 0x02
@ -198,6 +211,7 @@ class ConfigDataTypes(Enum):
"""
Configuration data types.
"""
UINT8 = 0x01
UINT16 = 0x02
UINT32 = 0x03
@ -215,6 +229,7 @@ class FileTlvs(Enum):
"""
File type, length, value enumerations.
"""
NODE = 0x01
NAME = 0x02
MODE = 0x03
@ -230,6 +245,7 @@ class InterfaceTlvs(Enum):
"""
Interface type, length, value enumerations.
"""
NODE = 0x01
NUMBER = 0x02
NAME = 0x03
@ -249,6 +265,7 @@ class EventTlvs(Enum):
"""
Event type, length, value enumerations.
"""
NODE = 0x01
TYPE = 0x02
NAME = 0x03
@ -261,6 +278,7 @@ class EventTypes(Enum):
"""
Event types.
"""
NONE = 0
DEFINITION_STATE = 1
CONFIGURATION_STATE = 2
@ -283,6 +301,7 @@ class SessionTlvs(Enum):
"""
Session type, length, value enumerations.
"""
NUMBER = 0x01
NAME = 0x02
FILE = 0x03
@ -297,6 +316,7 @@ class ExceptionTlvs(Enum):
"""
Exception type, length, value enumerations.
"""
NODE = 0x01
SESSION = 0x02
LEVEL = 0x03
@ -310,6 +330,7 @@ class ExceptionLevels(Enum):
"""
Exception levels.
"""
NONE = 0
FATAL = 1
ERROR = 2

View file

@ -15,23 +15,21 @@ import time
from multiprocessing.pool import ThreadPool
import core.nodes.base
from core import constants
from core import utils
from core import CoreError, constants, utils
from core.api.tlv import coreapi
from core.api.tlv.broker import CoreBroker
from core.emane.emanemanager import EmaneManager
from core.emulator.data import EventData, NodeData
from core.emulator.data import ExceptionData
from core.emulator.emudata import IdGen
from core.emulator.emudata import LinkOptions, NodeOptions
from core.emulator.emudata import create_interface
from core.emulator.emudata import is_net_node
from core.emulator.emudata import link_config
from core.emulator.enumerations import EventTypes, LinkTypes
from core.emulator.enumerations import ExceptionLevels
from core.emulator.enumerations import NodeTypes
from core.emulator.sessionconfig import SessionConfig
from core.emulator.sessionconfig import SessionMetaData
from core.emulator.data import EventData, ExceptionData, NodeData
from core.emulator.emudata import (
IdGen,
LinkOptions,
NodeOptions,
create_interface,
is_net_node,
link_config,
)
from core.emulator.enumerations import EventTypes, ExceptionLevels, LinkTypes, NodeTypes
from core.emulator.sessionconfig import SessionConfig, SessionMetaData
from core.location.corelocation import CoreLocation
from core.location.event import EventLoop
from core.location.mobility import MobilityManager
@ -40,8 +38,7 @@ from core.nodes.base import CoreNodeBase
from core.nodes.ipaddress import MacAddress
from core.plugins.sdt import Sdt
from core.services.coreservices import CoreServices
from core.xml import corexml
from core.xml import corexmldeployment
from core.xml import corexml, corexmldeployment
from core.xml.corexml import CoreXmlReader, CoreXmlWriter
@ -85,7 +82,9 @@ class Session(object):
# hooks handlers
self._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
self.event_handlers = []
@ -131,7 +130,9 @@ class Session(object):
:return: nodes, network nodes if present, and tunnel if present
: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
net_one = None
@ -171,8 +172,14 @@ class Session(object):
net_two = node_two
node_two = None
logging.debug("link node types n1(%s) n2(%s) net1(%s) net2(%s) tunnel(%s)",
node_one, node_two, net_one, net_two, tunnel)
logging.debug(
"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
# 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 bool connect: link interfaces if True, unlink otherwise
: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]
if len(objects) < 2:
raise ValueError("wireless link failure: %s", objects)
logging.debug("handling wireless linking objects(%s) connect(%s)", objects, connect)
raise CoreError("wireless link failure: %s" % objects)
logging.debug(
"handling wireless linking objects(%s) connect(%s)", objects, connect
)
common_networks = objects[0].commonnets(objects[1])
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:
if not nodeutils.is_node(common_network, [NodeTypes.WIRELESS_LAN, NodeTypes.EMANE]):
logging.info("skipping common network that is not wireless/emane: %s", common_network)
if not nodeutils.is_node(
common_network, [NodeTypes.WIRELESS_LAN, NodeTypes.EMANE]
):
logging.info(
"skipping common network that is not wireless/emane: %s",
common_network,
)
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:
common_network.link(interface_one, interface_two)
else:
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.
@ -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_two: node two interface data, defaults to none
:param core.emulator.emudata.LinkOptions link_options: data for creating link, defaults to no options
:return:
:return: nothing
"""
if not link_options:
link_options = LinkOptions()
# 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:
node_one.lock.acquire()
@ -234,27 +263,43 @@ class Session(object):
else:
# 2 nodes being linked, ptp network
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)
start = self.state > EventTypes.DEFINITION_STATE.value
net_one = self.create_node(cls=ptp_class, start=start)
# node to network
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)
link_config(net_one, interface, link_options)
# network to node
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)
if not link_options.unidirectional:
link_config(net_one, interface, link_options)
# network to network
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):
interface = net_two.linknet(net_one)
else:
@ -264,7 +309,9 @@ class Session(object):
if not link_options.unidirectional:
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")
# 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):
logging.info("adding link for physical node: %s", node_one.name)
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)
elif node_two and nodeutils.is_node(node_two, NodeTypes.PHYSICAL):
logging.info("adding link for physical node: %s", node_two.name)
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)
finally:
if node_one:
@ -306,7 +357,14 @@ class Session(object):
if node_two:
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.
@ -316,9 +374,12 @@ class Session(object):
:param int interface_two_id: interface id for node two
:param core.emulator.enumerations.LinkTypes link_type: link type to delete
:return: nothing
:raises core.CoreError: when no common network is found for link being deleted
"""
# 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:
node_one.lock.acquire()
@ -342,18 +403,31 @@ class Session(object):
# otherwise get interfaces between a node and network
if not interface_one and not interface_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:
interface_one = common_interface_one
interface_two = common_interface_two
break
if all([interface_one, interface_two]) and any([interface_one.net, interface_two.net]):
if interface_one.net != interface_two.net and all([interface_one.up, interface_two.up]):
raise ValueError("no common network found")
if all([interface_one, interface_two]) and any(
[interface_one.net, interface_two.net]
):
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)",
node_one.name, interface_one.name, node_two.name, interface_two.name)
logging.info(
"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
interface_one.detachnet()
interface_two.detachnet()
@ -363,23 +437,40 @@ class Session(object):
node_two.delnetif(interface_two.netindex)
elif node_one and net_one:
interface = node_one.netif(interface_one_id)
logging.info("deleting link node(%s):interface(%s) node(%s)",
node_one.name, interface.name, net_one.name)
interface.detachnet()
node_one.delnetif(interface.netindex)
if interface:
logging.info(
"deleting link node(%s):interface(%s) node(%s)",
node_one.name,
interface.name,
net_one.name,
)
interface.detachnet()
node_one.delnetif(interface.netindex)
elif node_two and net_one:
interface = node_two.netif(interface_two_id)
logging.info("deleting link node(%s):interface(%s) node(%s)",
node_two.name, interface.name, net_one.name)
interface.detachnet()
node_two.delnetif(interface.netindex)
if interface:
logging.info(
"deleting link node(%s):interface(%s) node(%s)",
node_two.name,
interface.name,
net_one.name,
)
interface.detachnet()
node_two.delnetif(interface.netindex)
finally:
if node_one:
node_one.lock.release()
if node_two:
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.
@ -389,12 +480,16 @@ class Session(object):
:param int interface_two_id: interface id for node two
:param core.emulator.emudata.LinkOptions link_options: data to update link with
:return: nothing
:raises core.CoreError: when updating a wireless type link, when there is a unknown
link between networks
"""
if not link_options:
link_options = LinkOptions()
# 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:
node_one.lock.acquire()
@ -404,7 +499,7 @@ class Session(object):
try:
# wireless link
if link_options.type == LinkTypes.WIRELESS.value:
raise ValueError("cannot update wireless link")
raise CoreError("cannot update wireless link")
else:
if not node_one and not node_two:
if net_one and net_two:
@ -417,11 +512,13 @@ class Session(object):
interface = net_two.getlinknetif(net_one)
if not interface:
raise ValueError("modify unknown link between nets")
raise CoreError("modify unknown link between nets")
if upstream:
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")
else:
link_config(net_one, interface, link_options)
@ -431,10 +528,15 @@ class Session(object):
link_config(net_two, interface, link_options)
else:
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")
else:
raise ValueError("modify link for unknown nodes")
raise CoreError("modify link for unknown nodes")
elif not node_one:
# node1 = layer 2node, node2 = layer3 node
interface = node_two.netif(interface_two_id, net_one)
@ -446,15 +548,28 @@ class Session(object):
else:
common_networks = node_one.commonnets(node_two)
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:
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
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:
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:
if node_one:
node_one.lock.release()
@ -499,8 +614,23 @@ class Session(object):
name = "%s%s" % (node_class.__name__, _id)
# create node
logging.info("creating node(%s) id(%s) name(%s) start(%s)", node_class.__name__, _id, name, start)
node = self.create_node(cls=node_class, _id=_id, name=name, start=start)
logging.info(
"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
node.icon = node_options.icon
@ -511,13 +641,20 @@ class Session(object):
self.set_node_position(node, node_options)
# 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
logging.debug("set node type: %s", node.type)
self.services.add_services(node, node.type, node_options.services)
# 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:
self.write_nodes()
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
:return: True if node updated, False otherwise
:rtype: bool
:raises core.CoreError: when node to update does not exist
"""
result = False
try:
# get node to update
node = self.get_node(node_id)
# get node to update
node = self.get_node(node_id)
# set node position and broadcast it
self.set_node_position(node, node_options)
# set node position and broadcast it
self.set_node_position(node, node_options)
# update attributes
node.canvas = node_options.canvas
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
# update attributes
node.canvas = node_options.canvas
node.icon = node_options.icon
def set_node_position(self, node, node_options):
"""
@ -594,7 +723,7 @@ class Session(object):
message_type=0,
id=node.id,
x_position=node.position.x,
y_position=node.position.y
y_position=node.position.y,
)
self.broadcast_node(node_data)
@ -613,7 +742,10 @@ class Session(object):
: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)
return result
@ -721,9 +853,18 @@ class Session(object):
if not node_options:
node_options = NodeOptions()
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.
@ -847,7 +988,11 @@ class Session(object):
state_name = state.name
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
self.state = state_value
@ -908,9 +1053,11 @@ class Session(object):
:param str data: hook data
: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():
logging.error("error setting hook having state '%s'", state)
return
@ -964,8 +1111,14 @@ class Session(object):
# execute hook file
try:
args = ["/bin/sh", file_name]
subprocess.check_call(args, stdout=stdout, stderr=stderr,
close_fds=True, cwd=self.session_dir, env=self.get_environment())
subprocess.check_call(
args,
stdout=stdout,
stderr=stderr,
close_fds=True,
cwd=self.session_dir,
env=self.get_environment(),
)
except (OSError, subprocess.CalledProcessError):
logging.exception("error running hook: %s", file_name)
@ -979,10 +1132,15 @@ class Session(object):
for hook in self._state_hooks.get(state, []):
try:
hook(state)
except:
message = "exception occured when running %s state hook: %s" % (coreapi.state_name(state), hook)
except Exception:
message = "exception occured when running %s state hook: %s" % (
coreapi.state_name(state),
hook,
)
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):
"""
@ -994,7 +1152,7 @@ class Session(object):
"""
hooks = self._state_hooks.setdefault(state, [])
if hook in hooks:
raise ValueError("attempting to add duplicate state hook")
raise CoreError("attempting to add duplicate state hook")
hooks.append(hook)
if self.state == state:
@ -1055,15 +1213,23 @@ class Session(object):
if os.path.isfile(environment_config_file):
utils.load_config(environment_config_file, env)
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
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:
utils.load_config(environment_user_file, env)
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
@ -1113,21 +1279,22 @@ class Session(object):
return node_id
def create_node(self, cls, *clsargs, **clskwds):
def create_node(self, cls, *args, **kwargs):
"""
Create an emulation node.
:param class cls: node class to create
:param list clsargs: list of arguments for the class to create
:param dict clskwds: dictionary of arguments for the class to create
:param list args: list of arguments for the class to create
:param dict kwargs: dictionary of arguments for the class to create
: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:
if node.id in self.nodes:
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
return node
@ -1139,9 +1306,10 @@ class Session(object):
:param int _id: node id to retrieve
:return: node for the given id
:rtype: core.nodes.base.CoreNode
:raises core.CoreError: when node does not exist
"""
if _id not in self.nodes:
raise KeyError("unknown node id %s" % _id)
raise CoreError("unknown node id %s" % _id)
return self.nodes[_id]
def delete_node(self, _id):
@ -1153,6 +1321,7 @@ class Session(object):
:rtype: bool
"""
# delete node and check for session shutdown if a node was removed
logging.info("deleting node(%s)", _id)
result = False
with self._nodes_lock:
if _id in self.nodes:
@ -1185,7 +1354,9 @@ class Session(object):
with open(file_path, "w") as f:
for _id in self.nodes.keys():
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:
logging.exception("error writing nodes file")
@ -1194,8 +1365,13 @@ class Session(object):
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("file=%s thumbnail=%s node_count=%s/%s",
self.file_name, self.thumbnail, self.get_node_count(), len(self.nodes))
logging.info(
"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):
"""
@ -1214,7 +1390,7 @@ class Session(object):
level=level,
source=source,
date=time.ctime(),
text=text
text=text,
)
self.broadcast_exception(exception_data)
@ -1265,8 +1441,12 @@ class Session(object):
count = 0
for node_id in self.nodes:
node = self.nodes[node_id]
is_p2p_ctrlnet = nodeutils.is_node(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)
is_p2p_ctrlnet = nodeutils.is_node(
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:
continue
@ -1283,8 +1463,11 @@ class Session(object):
# this is called from instantiate() after receiving an event message
# for the instantiation state, and from the broker when distributed
# nodes have been started
logging.info("session(%s) checking if not in runtime state, current state: %s", self.id,
coreapi.state_name(self.state))
logging.debug(
"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:
logging.info("valid runtime state found, returning")
return
@ -1331,7 +1514,9 @@ class Session(object):
and links remain.
"""
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
if node_count == 0:
@ -1362,9 +1547,15 @@ class Session(object):
for _id in self.nodes:
node = self.nodes[_id]
# 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
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)
result = pool.apply_async(self.services.boot_services, (node,))
results.append(result)
@ -1444,7 +1635,12 @@ class Session(object):
:return: control net node
: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 = prefix_spec_list[net_index]
if not prefix_spec:
@ -1467,7 +1663,7 @@ class Session(object):
return None
return control_net
except KeyError:
except CoreError:
if remove:
return None
@ -1513,10 +1709,12 @@ class Session(object):
break
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
try:
prefix = prefixes[0].split(':', 1)[1]
prefix = prefixes[0].split(":", 1)[1]
except IndexError:
prefix = prefixes[0]
# len(prefixes) == 1
@ -1528,9 +1726,14 @@ class Session(object):
logging.info("controlnet prefix: %s - %s", type(prefix), prefix)
control_net_class = nodeutils.get_node_class(NodeTypes.CONTROL_NET)
control_net = self.create_node(cls=control_net_class, _id=_id, prefix=prefix,
assign_address=assign_address,
updown_script=updown_script, serverintf=server_interface)
control_net = self.create_node(
cls=control_net_class,
_id=_id,
prefix=prefix,
assign_address=assign_address,
updown_script=updown_script,
serverintf=server_interface,
)
# tunnels between controlnets will be built with Broker.addnettunnels()
# TODO: potentially remove documentation saying node ids are ints
@ -1541,7 +1744,9 @@ class Session(object):
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
listed in the config file or session options. Uses
@ -1569,7 +1774,10 @@ class Session(object):
control_ip = node.id
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:
msg = "Control interface not added to node %s. " % node.id
msg += "Invalid control network prefix (%s). " % control_net.prefix
@ -1577,10 +1785,13 @@ class Session(object):
logging.exception(msg)
return
interface1 = node.newnetif(net=control_net,
ifindex=control_net.CTRLIF_IDX_BASE + net_index,
ifname="ctrl%d" % net_index, hwaddr=MacAddress.random(),
addrlist=addrlist)
interface1 = node.newnetif(
net=control_net,
ifindex=control_net.CTRLIF_IDX_BASE + net_index,
ifname="ctrl%d" % net_index,
hwaddr=MacAddress.random(),
addrlist=addrlist,
)
node.netif(interface1).control = True
def update_control_interface_hosts(self, net_index=0, remove=False):
@ -1596,7 +1807,7 @@ class Session(object):
try:
control_net = self.get_control_net(net_index)
except KeyError:
except CoreError:
logging.exception("error retrieving control net node")
return
@ -1642,15 +1853,26 @@ class Session(object):
if current_time > 0:
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
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:
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
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 ConfigurableOptions
from core.config import Configuration
from core.emulator.enumerations import ConfigDataTypes
from core.emulator.enumerations import RegisterTlvs
from core.config import ConfigurableManager, ConfigurableOptions, Configuration
from core.emulator.enumerations import ConfigDataTypes, RegisterTlvs
from core.plugins.sdt import Sdt
@ -10,21 +7,56 @@ class SessionConfig(ConfigurableManager, ConfigurableOptions):
"""
Provides session configuration.
"""
name = "session"
options = [
Configuration(_id="controlnet", _type=ConfigDataTypes.STRING, label="Control Network"),
Configuration(_id="controlnet0", _type=ConfigDataTypes.STRING, label="Control Network 0"),
Configuration(_id="controlnet1", _type=ConfigDataTypes.STRING, label="Control Network 1"),
Configuration(_id="controlnet2", _type=ConfigDataTypes.STRING, label="Control Network 2"),
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")
Configuration(
_id="controlnet", _type=ConfigDataTypes.STRING, label="Control Network"
),
Configuration(
_id="controlnet0", _type=ConfigDataTypes.STRING, label="Control Network 0"
),
Configuration(
_id="controlnet1", _type=ConfigDataTypes.STRING, label="Control Network 1"
),
Configuration(
_id="controlnet2", _type=ConfigDataTypes.STRING, label="Control Network 2"
),
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
@ -32,9 +64,16 @@ class SessionConfig(ConfigurableManager, ConfigurableOptions):
super(SessionConfig, self).__init__()
self.set_configs(self.default_values())
def get_config(self, _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)
def get_config(
self,
_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 == "":
value = default
return value
@ -58,5 +97,6 @@ class SessionMetaData(ConfigurableManager):
passed in from configure messages destined to the "metadata" object.
The data is not otherwise interpreted or processed.
"""
name = "metadata"
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
order to convert between X,Y and geo coordinates.
"""
name = "location"
config_type = RegisterTlvs.UTILITY.value
@ -118,7 +119,14 @@ class CoreLocation(object):
try:
lat, lon = utm.to_latlon(e, n, zone[0], zone[1])
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]
# 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))
@ -265,9 +273,9 @@ class CoreLocation(object):
if n < 0:
# refpt in northern hemisphere and we crossed south of equator
n += 10000000
zone = (zone[0], 'M')
zone = (zone[0], "M")
elif n > 10000000:
# refpt in southern hemisphere and we crossed north of equator
n -= 10000000
zone = (zone[0], 'N')
zone = (zone[0], "N")
return e, n, zone

View file

@ -5,6 +5,7 @@ event.py: event loop implementation using a heap queue and threads.
import heapq
import threading
import time
from past.builtins import cmp

View file

@ -11,20 +11,18 @@ import time
from builtins import int
from functools import total_ordering
from core import utils
from core.config import ConfigGroup
from core.config import ConfigurableOptions
from core.config import Configuration
from core.config import ModelManager
from core.emulator.data import EventData
from core.emulator.data import LinkData
from core.emulator.enumerations import ConfigDataTypes
from core.emulator.enumerations import EventTypes
from core.emulator.enumerations import LinkTypes
from core.emulator.enumerations import MessageFlags
from core.emulator.enumerations import MessageTypes
from core.emulator.enumerations import NodeTlvs
from core.emulator.enumerations import RegisterTlvs
from core import CoreError, utils
from core.config import ConfigGroup, ConfigurableOptions, Configuration, ModelManager
from core.emulator.data import EventData, LinkData
from core.emulator.enumerations import (
ConfigDataTypes,
EventTypes,
LinkTypes,
MessageFlags,
MessageTypes,
NodeTlvs,
RegisterTlvs,
)
from core.nodes.base import CoreNodeBase
from core.nodes.ipaddress import IpAddress
@ -34,6 +32,7 @@ class MobilityManager(ModelManager):
Member of session class for handling configuration data for mobility and
range models.
"""
name = "MobilityManager"
config_type = RegisterTlvs.WIRELESS.value
@ -73,13 +72,17 @@ class MobilityManager(ModelManager):
node_ids = self.nodes()
for node_id in node_ids:
logging.info("checking mobility startup for node: %s", node_id)
logging.info("node mobility configurations: %s", self.get_all_configs(node_id))
logging.debug("checking mobility startup for node: %s", node_id)
logging.debug(
"node mobility configurations: %s", self.get_all_configs(node_id)
)
try:
node = self.session.get_node(node_id)
except KeyError:
logging.warning("skipping mobility configuration for unknown node: %s", node_id)
except CoreError:
logging.warning(
"skipping mobility configuration for unknown node: %s", node_id
)
continue
for model_name in self.models:
@ -109,12 +112,14 @@ class MobilityManager(ModelManager):
try:
node = self.session.get_node(node_id)
except KeyError:
logging.exception("Ignoring event for model '%s', unknown node '%s'", name, node_id)
except CoreError:
logging.exception(
"Ignoring event for model '%s', unknown node '%s'", name, node_id
)
return
# name is e.g. "mobility:ns2script"
models = name[9:].split(',')
models = name[9:].split(",")
for model in models:
try:
cls = self.models[model]
@ -122,7 +127,10 @@ class MobilityManager(ModelManager):
logging.warning("Ignoring event for unknown model '%s'", model)
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
else:
continue
@ -132,12 +140,23 @@ class MobilityManager(ModelManager):
continue
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
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)
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()
if event_type == EventTypes.PAUSE.value:
model.pause()
@ -166,7 +185,7 @@ class MobilityManager(ModelManager):
event_type=event_type,
name="mobility:%s" % model.name,
data=data,
time="%s" % time.time()
time="%s" % time.time(),
)
self.session.broadcast_event(event_data)
@ -184,7 +203,7 @@ class MobilityManager(ModelManager):
for node_id in self.nodes():
try:
node = self.session.get_node(node_id)
except KeyError:
except CoreError:
continue
if node.model:
node.model.update(moved, moved_netifs)
@ -200,7 +219,7 @@ class MobilityManager(ModelManager):
node_id = node.id
self.phys[node_id] = node
if netnum not in self.physnets:
self.physnets[netnum] = [node_id, ]
self.physnets[netnum] = [node_id]
else:
self.physnets[netnum].append(node_id)
@ -216,14 +235,19 @@ class MobilityManager(ModelManager):
:param message: link message to handle
: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()
# first node is always link layer node in Link add message
if nn[0] not in self.session.broker.network_nodes:
return
if nn[1] in self.session.broker.physical_nodes:
# 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)
# 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.
Used for managing arbitrary configuration parameters.
"""
config_type = RegisterTlvs.WIRELESS.value
bitmap = None
position_callback = None
@ -323,21 +348,44 @@ class BasicRangeModel(WirelessModel):
and unlinks nodes based on this distance. This was formerly done from
the GUI.
"""
name = "basic_range"
options = [
Configuration(_id="range", _type=ConfigDataTypes.UINT32, default="275", label="wireless range (pixels)"),
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 (%)")
Configuration(
_id="range",
_type=ConfigDataTypes.UINT32,
default="275",
label="wireless range (pixels)",
),
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
def config_groups(cls):
return [
ConfigGroup("Basic Range Parameters", 1, len(cls.configurations()))
]
return [ConfigGroup("Basic Range Parameters", 1, len(cls.configurations()))]
def __init__(self, session, _id):
"""
@ -345,7 +393,6 @@ class BasicRangeModel(WirelessModel):
:param core.session.Session session: related core session
:param int _id: object id
:param dict config: values
"""
super(BasicRangeModel, self).__init__(session=session, _id=_id)
self.session = session
@ -353,7 +400,7 @@ class BasicRangeModel(WirelessModel):
self._netifs = {}
self._netifslock = threading.Lock()
self.range = None
self.range = 0
self.bw = None
self.delay = None
self.loss = None
@ -367,7 +414,11 @@ class BasicRangeModel(WirelessModel):
:return: nothing
"""
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"])
if self.bw == 0:
self.bw = None
@ -388,8 +439,14 @@ class BasicRangeModel(WirelessModel):
"""
with self._netifslock:
for netif in self._netifs:
self.wlan.linkconfig(netif, bw=self.bw, delay=self.delay, loss=self.loss, duplicate=None,
jitter=self.jitter)
self.wlan.linkconfig(
netif,
bw=self.bw,
delay=self.delay,
loss=self.loss,
duplicate=None,
jitter=self.jitter,
)
def get_position(self, netif):
"""
@ -532,7 +589,7 @@ class BasicRangeModel(WirelessModel):
node1_id=interface1.node.id,
node2_id=interface2.node.id,
network_id=self.wlan.id,
link_type=LinkTypes.WIRELESS.value
link_type=LinkTypes.WIRELESS.value,
)
def sendlinkmsg(self, netif, netif2, unlink=False):
@ -606,6 +663,7 @@ class WayPointMobility(WirelessModel):
"""
Abstract class for mobility models that set node waypoints.
"""
name = "waypoint"
config_type = RegisterTlvs.MOBILITY.value
@ -666,7 +724,9 @@ class WayPointMobility(WirelessModel):
# no more waypoints or queued items, loop?
if not self.empty_queue_stop:
# 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
if not self.loopwaypoints():
return self.stop(move_initial=False)
@ -918,16 +978,50 @@ class Ns2ScriptedMobility(WayPointMobility):
Handles the ns-2 script format, generated by scengen/setdest or
BonnMotion.
"""
name = "ns2script"
options = [
Configuration(_id="file", _type=ConfigDataTypes.STRING, label="mobility script file"),
Configuration(_id="refresh_ms", _type=ConfigDataTypes.UINT32, default="50", 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")
Configuration(
_id="file", _type=ConfigDataTypes.STRING, label="mobility script file"
),
Configuration(
_id="refresh_ms",
_type=ConfigDataTypes.UINT32,
default="50",
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
@ -958,7 +1052,11 @@ class Ns2ScriptedMobility(WayPointMobility):
def update_config(self, config):
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.loop = config["loop"].lower() == "on"
self.autostart = config["autostart"]
@ -982,7 +1080,9 @@ class Ns2ScriptedMobility(WayPointMobility):
try:
f = open(filename, "r")
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
logging.info("reading ns-2 script file: %s" % filename)
ln = 0
@ -990,7 +1090,7 @@ class Ns2ScriptedMobility(WayPointMobility):
inodenum = None
for line in f:
ln += 1
if line[:2] != '$n':
if line[:2] != "$n":
continue
try:
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"
parts = line.split()
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])
y = float(parts[6])
z = None
@ -1012,15 +1112,15 @@ class Ns2ScriptedMobility(WayPointMobility):
# $node_(6) set X_ 780.0
parts = line.split()
time = 0.0
nodenum = parts[0][1 + parts[0].index('('):parts[0].index(')')]
if parts[2] == 'X_':
nodenum = parts[0][1 + parts[0].index("(") : parts[0].index(")")]
if parts[2] == "X_":
if ix is not None and iy is not None:
self.addinitial(self.map(inodenum), ix, iy, iz)
ix = iy = iz = None
ix = float(parts[3])
elif parts[2] == 'Y_':
elif parts[2] == "Y_":
iy = float(parts[3])
elif parts[2] == 'Z_':
elif parts[2] == "Z_":
iz = float(parts[3])
self.addinitial(self.map(nodenum), ix, iy, iz)
ix = iy = iz = None
@ -1028,7 +1128,9 @@ class Ns2ScriptedMobility(WayPointMobility):
else:
raise 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
if ix is not None and iy is not None:
self.addinitial(self.map(inodenum), ix, iy, iz)
@ -1054,7 +1156,9 @@ class Ns2ScriptedMobility(WayPointMobility):
return sessfn
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):
return userfn
@ -1100,16 +1204,22 @@ class Ns2ScriptedMobility(WayPointMobility):
:return: nothing
"""
if self.autostart == '':
if self.autostart == "":
logging.info("not auto-starting ns-2 script for %s" % self.wlan.name)
return
try:
t = float(self.autostart)
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
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.session.event_loop.add_event(t, self.run)
@ -1167,8 +1277,10 @@ class Ns2ScriptedMobility(WayPointMobility):
filename = self.script_pause
elif typestr == "stop":
filename = self.script_stop
if filename is None or filename == '':
if filename is None or filename == "":
return
filename = self.findfile(filename)
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 string
import threading
from builtins import range
from socket import AF_INET, AF_INET6
from builtins import range
from core import CoreCommandError, utils
from core import constants
from core.emulator.data import NodeData, LinkData
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
from core import CoreCommandError, constants, utils
from core.emulator.data import LinkData, NodeData
from core.emulator.enumerations import LinkTypes, NodeTypes
from core.nodes import client, ipaddress, nodeutils
from core.nodes.interface import CoreInterface, TunTap, Veth
_DEFAULT_MTU = 1500
@ -32,6 +29,7 @@ class NodeBase(object):
"""
Base class for CORE nodes (nodes and networks)
"""
apitype = None
# 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
:return: network interfaces
:rtype: list
:rtype: list[core.nodes.interfaces.CoreInterface]
"""
if sort:
return [self._netif[x] for x in sorted(self._netif)]
@ -170,7 +168,7 @@ class NodeBase(object):
:param str lon: longitude
:param str alt: altitude
:return: node data object
:rtype: core.data.NodeData
:rtype: core.emulator.data.NodeData
"""
if self.apitype is None:
return None
@ -199,7 +197,7 @@ class NodeBase(object):
altitude=alt,
model=model,
emulation_server=emulation_server,
services=services
services=services,
)
return node_data
@ -212,7 +210,7 @@ class NodeBase(object):
:param flags: message flags
:return: list of link data
:rtype: core.data.LinkData
:rtype: list[core.data.LinkData]
"""
return []
@ -236,16 +234,6 @@ class CoreNodeBase(NodeBase):
self.nodedir = None
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):
"""
Create the node directory.
@ -320,7 +308,7 @@ class CoreNodeBase(NodeBase):
:param int ifindex: interface of index to attach
:param core.nodes.interface.CoreInterface net: network to attach
:return:
:return: nothing
"""
if ifindex not in self._netif:
raise ValueError("ifindex %s does not exist" % ifindex)
@ -418,10 +406,13 @@ class CoreNode(CoreNodeBase):
"""
Provides standard core node logic.
"""
apitype = NodeTypes.DEFAULT.value
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.
@ -434,7 +425,9 @@ class CoreNode(CoreNodeBase):
"""
super(CoreNode, self).__init__(session, _id, name, start)
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.pid = None
self.up = False
@ -475,9 +468,12 @@ class CoreNode(CoreNodeBase):
vnoded = [
constants.VNODED_BIN,
"-v",
"-c", self.ctrlchnlname,
"-l", self.ctrlchnlname + ".log",
"-p", self.ctrlchnlname + ".pid"
"-c",
self.ctrlchnlname,
"-l",
self.ctrlchnlname + ".log",
"-p",
self.ctrlchnlname + ".pid",
]
if self.nodedir:
vnoded += ["-C", self.nodedir]
@ -493,11 +489,11 @@ class CoreNode(CoreNodeBase):
# bring up the 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
logging.debug("setting hostname: %s", self.name)
self.check_cmd(["hostname", self.name])
self.network_cmd(["hostname", self.name])
# mark node as up
self.up = True
@ -572,6 +568,17 @@ class CoreNode(CoreNodeBase):
"""
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):
"""
Runs shell command on node.
@ -601,7 +608,9 @@ class CoreNode(CoreNodeBase):
"""
if path[0] != "/":
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)
self.mount(hostpath, path)
@ -615,8 +624,13 @@ class CoreNode(CoreNodeBase):
:raises CoreCommandError: when a non-zero exit status occurs
"""
source = os.path.abspath(source)
logging.info("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)
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,
)
status, output = self.client.shcmd_result(cmd)
if status:
raise CoreCommandError(status, cmd, output)
@ -663,19 +677,27 @@ class CoreNode(CoreNodeBase):
if len(name) >= 16:
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:
utils.check_cmd([constants.IP_BIN, "link", "set", veth.name, "netns", str(self.pid)])
self.check_cmd([constants.IP_BIN, "link", "set", veth.name, "name", ifname])
self.check_cmd([constants.ETHTOOL_BIN, "-K", ifname, "rx", "off", "tx", "off"])
utils.check_cmd(
[constants.IP_BIN, "link", "set", veth.name, "netns", str(self.pid)]
)
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
if self.up:
# TODO: potentially find better way to query interface ID
# 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)
output = output.split("\n")
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)
try:
# add network interface to the node. If unsuccessful, destroy the network interface and raise exception.
self.addnetif(veth, ifindex)
except ValueError as e:
veth.shutdown()
@ -713,7 +736,9 @@ class CoreNode(CoreNodeBase):
sessionid = self.session.short_session_id()
localname = "tap%s.%s.%s" % (self.id, ifindex, sessionid)
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:
self.addnetif(tuntap, ifindex)
@ -735,8 +760,16 @@ class CoreNode(CoreNodeBase):
"""
self._netif[ifindex].sethwaddr(addr)
if self.up:
args = [constants.IP_BIN, "link", "set", "dev", self.ifname(ifindex), "address", str(addr)]
self.check_cmd(args)
args = [
constants.IP_BIN,
"link",
"set",
"dev",
self.ifname(ifindex),
"address",
str(addr),
]
self.network_cmd(args)
def addaddr(self, ifindex, addr):
"""
@ -749,11 +782,27 @@ class CoreNode(CoreNodeBase):
if self.up:
# check if addr is ipv6
if ":" in str(addr):
args = [constants.IP_BIN, "addr", "add", str(addr), "dev", self.ifname(ifindex)]
self.check_cmd(args)
args = [
constants.IP_BIN,
"addr",
"add",
str(addr),
"dev",
self.ifname(ifindex),
]
self.network_cmd(args)
else:
args = [constants.IP_BIN, "addr", "add", str(addr), "broadcast", "+", "dev", self.ifname(ifindex)]
self.check_cmd(args)
args = [
constants.IP_BIN,
"addr",
"add",
str(addr),
"broadcast",
"+",
"dev",
self.ifname(ifindex),
]
self.network_cmd(args)
self._netif[ifindex].addaddr(addr)
@ -772,7 +821,16 @@ class CoreNode(CoreNodeBase):
logging.exception("trying to delete unknown address: %s" % addr)
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):
"""
@ -791,7 +849,9 @@ class CoreNode(CoreNodeBase):
for address_type in 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]:
self.deladdr(ifindex, address)
@ -806,7 +866,9 @@ class CoreNode(CoreNodeBase):
:return: nothing
"""
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):
"""
@ -862,18 +924,41 @@ class CoreNode(CoreNodeBase):
:return: nothing
"""
tmplen = 8
tmp1 = "tmp." + "".join([random.choice(string.ascii_lowercase) for _ in range(tmplen)])
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])
tmp1 = "tmp." + "".join(
[random.choice(string.ascii_lowercase) for _ in range(tmplen)]
)
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)])
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)
self.addnetif(interface, self.newifindex())
utils.check_cmd([constants.IP_BIN, "link", "set", tmp2, "netns", str(othernode.pid)])
othernode.check_cmd([constants.IP_BIN, "link", "set", tmp2, "name", otherifname])
other_interface = CoreInterface(node=othernode, name=otherifname, mtu=_DEFAULT_MTU)
utils.check_cmd(
[constants.IP_BIN, "link", "set", tmp2, "netns", str(othernode.pid)]
)
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())
def addfile(self, srcname, filename):
@ -936,7 +1021,9 @@ class CoreNode(CoreNodeBase):
with self.opennodefile(filename, "w") as open_file:
open_file.write(contents)
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):
"""
@ -952,13 +1039,16 @@ class CoreNode(CoreNodeBase):
shutil.copy2(srcfilename, hostfilename)
if mode is not None:
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):
"""
Base class for networks
"""
linktype = LinkTypes.WIRED.value
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
between this network and a node.
:param int flags: message type
:return: list of link data
:rtype: list[core.data.LinkData]
"""
all_links = []
@ -1036,9 +1131,9 @@ class CoreNetworkBase(NodeBase):
linked_node = netif.othernet
if linked_node.id == self.id:
continue
netif.swapparams('_params_up')
netif.swapparams("_params_up")
upstream_params = netif.getparams()
netif.swapparams('_params_up')
netif.swapparams("_params_up")
if netif.getparams() != upstream_params:
uni = True
@ -1080,7 +1175,7 @@ class CoreNetworkBase(NodeBase):
bandwidth=netif.getparam("bw"),
dup=netif.getparam("duplicate"),
jitter=netif.getparam("jitter"),
per=netif.getparam("loss")
per=netif.getparam("loss"),
)
all_links.append(link_data)
@ -1088,7 +1183,7 @@ class CoreNetworkBase(NodeBase):
if not uni:
continue
netif.swapparams('_params_up')
netif.swapparams("_params_up")
link_data = LinkData(
message_type=0,
node1_id=linked_node.id,
@ -1098,9 +1193,9 @@ class CoreNetworkBase(NodeBase):
bandwidth=netif.getparam("bw"),
dup=netif.getparam("duplicate"),
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)

View file

@ -6,11 +6,9 @@ The control channel can be accessed via calls using the vcmd shell.
import logging
import os
from subprocess import PIPE, Popen
from subprocess import Popen, PIPE
from core import CoreCommandError, utils
from core import constants
from core import CoreCommandError, constants, utils
class VnodeClient(object):
@ -137,7 +135,15 @@ class VnodeClient(object):
:rtype: int
"""
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):
"""
@ -177,11 +183,27 @@ class VnodeClient(object):
:return: terminal command result
: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:
args = ("su", "-s", "/bin/sh", "-c",
"exec " + " ".join(map(lambda x: "'%s'" % x, args)),
os.environ["SUDO_USER"])
args = (
"su",
"-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)
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 time
from builtins import int
from builtins import range
from builtins import int, range
from core import CoreCommandError, utils
from core import constants
from core import CoreCommandError, constants, utils
from core.emulator.enumerations import NodeTypes
from core.nodes import nodeutils
@ -142,7 +140,7 @@ class CoreInterface(object):
"""
# treat None and 0 as unchanged values
logging.debug("setting param: %s - %s", key, value)
if value is None or value <= 0:
if value is None or value < 0:
return False
current_value = self._params.get(key)
@ -221,8 +219,20 @@ class Veth(CoreInterface):
:return: nothing
:raises CoreCommandError: when there is a command exception
"""
utils.check_cmd([constants.IP_BIN, "link", "add", "name", self.localname,
"type", "veth", "peer", "name", self.name])
utils.check_cmd(
[
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"])
self.up = True
@ -237,7 +247,9 @@ class Veth(CoreInterface):
if self.node:
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:
logging.exception("error shutting down interface")
@ -245,7 +257,7 @@ class Veth(CoreInterface):
try:
utils.check_cmd([constants.IP_BIN, "link", "delete", self.localname])
except CoreCommandError:
logging.exception("error deleting link")
logging.info("link already removed: %s", self.localname)
self.up = False
@ -298,7 +310,9 @@ class TunTap(CoreInterface):
return
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:
logging.exception("error shutting down tunnel tap")
@ -361,7 +375,11 @@ class TunTap(CoreInterface):
def nodedevexists():
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
while True:
@ -392,9 +410,13 @@ class TunTap(CoreInterface):
"""
self.waitfordevicelocal()
netns = str(self.node.pid)
utils.check_cmd([constants.IP_BIN, "link", "set", self.localname, "netns", netns])
self.node.check_cmd([constants.IP_BIN, "link", "set", self.localname, "name", self.name])
self.node.check_cmd([constants.IP_BIN, "link", "set", self.name, "up"])
utils.check_cmd(
[constants.IP_BIN, "link", "set", self.localname, "netns", netns]
)
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):
"""
@ -404,7 +426,9 @@ class TunTap(CoreInterface):
"""
self.waitfordevicenode()
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):
@ -414,9 +438,19 @@ class GreTap(CoreInterface):
having a MAC address. The MAC address is required for bridging.
"""
def __init__(self, node=None, name=None, session=None, mtu=1458,
remoteip=None, _id=None, localip=None, ttl=255,
key=None, start=True):
def __init__(
self,
node=None,
name=None,
session=None,
mtu=1458,
remoteip=None,
_id=None,
localip=None,
ttl=255,
key=None,
start=True,
):
"""
Creates a GreTap instance.
@ -436,7 +470,7 @@ class GreTap(CoreInterface):
self.session = session
if _id is None:
# from PyCoreObj
_id = ((id(self) >> 16) ^ (id(self) & 0xffff)) & 0xffff
_id = ((id(self) >> 16) ^ (id(self) & 0xFFFF)) & 0xFFFF
self.id = _id
sessionid = self.session.short_session_id()
# interface name on the local host machine
@ -448,8 +482,16 @@ class GreTap(CoreInterface):
if remoteip is None:
raise ValueError("missing remote IP required for GRE TAP device")
args = [constants.IP_BIN, "link", "add", self.localname, "type", "gretap",
"remote", str(remoteip)]
args = [
constants.IP_BIN,
"link",
"add",
self.localname,
"type",
"gretap",
"remote",
str(remoteip),
]
if localip:
args += ["local", str(localip)]
if ttl:

View file

@ -6,10 +6,8 @@ import logging
import random
import socket
import struct
from builtins import bytes
from builtins import range
from socket import AF_INET
from socket import AF_INET6
from builtins import bytes, int, range
from socket import AF_INET, AF_INET6
class MacAddress(object):
@ -45,13 +43,13 @@ class MacAddress(object):
if not self.addr:
return IpAddress.from_string("::")
tmp = struct.unpack("!Q", "\x00\x00" + self.addr)[0]
nic = long(tmp) & 0x000000FFFFFF
oui = long(tmp) & 0xFFFFFF000000
nic = int(tmp) & 0x000000FFFFFF
oui = int(tmp) & 0xFFFFFF000000
# toggle U/L bit
oui ^= 0x020000000000
# append EUI-48 octets
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
def from_string(cls, s):
@ -158,7 +156,7 @@ class IpAddress(object):
tmp = [x for x in bytearray(self.addr)]
for i in range(len(tmp) - 1, -1, -1):
x = tmp[i] + carry
tmp[i] = x & 0xff
tmp[i] = x & 0xFF
carry = x >> 8
if carry == 0:
break
@ -239,7 +237,7 @@ class IpPrefix(object):
netmask = ((1 << self.prefixlen) - 1) << addrbits
prefix = bytes(b"")
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
self.prefix = self.prefix[:i] + prefix
@ -265,7 +263,11 @@ class IpPrefix(object):
elif self is other:
return True
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):
"""
@ -316,15 +318,20 @@ class IpPrefix(object):
if tmp in [-1, 0, 1] and self.addrlen == self.prefixlen:
return IpAddress(self.af, self.prefix)
if tmp == 0 or tmp > (1 << (self.addrlen - self.prefixlen)) - 1 or (
self.af == AF_INET and tmp == (1 << (self.addrlen - self.prefixlen)) - 1):
if (
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))
addr = bytes(b"")
prefix_endpoint = -1
for i in range(-1, -(self.addrlen >> 3) - 1, -1):
prefix_endpoint = i
addr = bytes([self.prefix[i] | (tmp & 0xff)]) + addr
addr = bytes([self.prefix[i] | (tmp & 0xFF)]) + addr
tmp >>= 8
if not tmp:
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
from socket import AF_INET, AF_INET6
from core import CoreCommandError, utils
from core import constants
from core.nodes.base import CoreNetworkBase
from core import CoreCommandError, constants, utils
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.interface import GreTap
from core.nodes.interface import Veth
from core.nodes.base import CoreNetworkBase
from core.nodes.interface import GreTap, Veth
utils.check_executables([
constants.BRCTL_BIN,
constants.IP_BIN,
constants.EBTABLES_BIN,
constants.TC_BIN
])
utils.check_executables(
[constants.BRCTL_BIN, constants.IP_BIN, constants.EBTABLES_BIN, constants.TC_BIN]
)
ebtables_lock = threading.Lock()
@ -34,6 +29,7 @@ class EbtablesQueue(object):
atomic commits. This improves performance and reliability when there are
many WLAN link updates.
"""
# update rate is every 300ms
rate = 0.3
# ebtables
@ -83,7 +79,9 @@ class EbtablesQueue(object):
try:
del self.last_update_time[wlan]
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:
return
@ -149,7 +147,7 @@ class EbtablesQueue(object):
# TODO: if these are WlanNodes, this will never throw an exception
try:
wlan.session
except:
except Exception:
# Just mark as updated to remove from self.updates.
self.updated(wlan)
continue
@ -168,7 +166,7 @@ class EbtablesQueue(object):
:return: nothing
"""
# save kernel ebtables snapshot to a file
args = self.ebatomiccmd(["--atomic-save", ])
args = self.ebatomiccmd(["--atomic-save"])
utils.check_cmd(args)
# modify the table file using queued ebtables commands
@ -178,7 +176,7 @@ class EbtablesQueue(object):
self.cmds = []
# commit the table file to the kernel
args = self.ebatomiccmd(["--atomic-commit", ])
args = self.ebatomiccmd(["--atomic-commit"])
utils.check_cmd(args)
try:
@ -205,20 +203,60 @@ class EbtablesQueue(object):
"""
with wlan._linked_lock:
# flush the chain
self.cmds.extend([["-F", wlan.brname], ])
self.cmds.extend([["-F", wlan.brname]])
# rebuild the chain
for netif1, v in wlan._linked.items():
for netif2, linked in v.items():
if wlan.policy == "DROP" and linked:
self.cmds.extend([["-A", wlan.brname, "-i", netif1.localname,
"-o", netif2.localname, "-j", "ACCEPT"],
["-A", wlan.brname, "-o", netif1.localname,
"-i", netif2.localname, "-j", "ACCEPT"]])
self.cmds.extend(
[
[
"-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:
self.cmds.extend([["-A", wlan.brname, "-i", netif1.localname,
"-o", netif2.localname, "-j", "DROP"],
["-A", wlan.brname, "-o", netif1.localname,
"-i", netif2.localname, "-j", "DROP"]])
self.cmds.extend(
[
[
"-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
@ -243,6 +281,7 @@ class CoreNetwork(CoreNetworkBase):
"""
Provides linux bridge network functionality for core nodes.
"""
policy = "DROP"
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.IP_BIN, "link", "set", self.brname, "up"])
# create a new ebtables chain for this bridge
ebtablescmds(utils.check_cmd, [
[constants.EBTABLES_BIN, "-N", self.brname, "-P", self.policy],
[constants.EBTABLES_BIN, "-A", "FORWARD", "--logical-in", self.brname, "-j", self.brname]
])
ebtablescmds(
utils.check_cmd,
[
[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
snoop = "/sys/devices/virtual/net/%s/bridge/multicast_snooping" % self.brname
if os.path.exists(snoop):
@ -308,10 +358,21 @@ class CoreNetwork(CoreNetworkBase):
try:
utils.check_cmd([constants.IP_BIN, "link", "set", self.brname, "down"])
utils.check_cmd([constants.BRCTL_BIN, "delbr", self.brname])
ebtablescmds(utils.check_cmd, [
[constants.EBTABLES_BIN, "-D", "FORWARD", "--logical-in", self.brname, "-j", self.brname],
[constants.EBTABLES_BIN, "-X", self.brname]
])
ebtablescmds(
utils.check_cmd,
[
[
constants.EBTABLES_BIN,
"-D",
"FORWARD",
"--logical-in",
self.brname,
"-j",
self.brname,
],
[constants.EBTABLES_BIN, "-X", self.brname],
],
)
except CoreCommandError:
logging.exception("error during shutdown")
@ -333,7 +394,9 @@ class CoreNetwork(CoreNetworkBase):
:return: nothing
"""
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"])
CoreNetworkBase.attach(self, netif)
@ -346,7 +409,9 @@ class CoreNetwork(CoreNetworkBase):
:return: nothing
"""
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)
@ -411,8 +476,17 @@ class CoreNetwork(CoreNetworkBase):
ebq.ebchange(self)
def linkconfig(self, netif, bw=None, delay=None, loss=None, duplicate=None,
jitter=None, netif2=None, devname=None):
def linkconfig(
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.
@ -436,12 +510,13 @@ class CoreNetwork(CoreNetworkBase):
if bw is not None:
burst = max(2 * netif.mtu, bw / 1000)
# max IP payload
limit = 0xffff
tbf = ["tbf", "rate", str(bw),
"burst", str(burst), "limit", str(limit)]
limit = 0xFFFF
tbf = ["tbf", "rate", str(bw), "burst", str(burst), "limit", str(limit)]
if bw > 0:
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)
netif.setparam("has_tbf", True)
changed = True
@ -496,7 +571,9 @@ class CoreNetwork(CoreNetworkBase):
netif.setparam("has_netem", False)
elif len(netem) > 1:
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)
netif.setparam("has_netem", True)
@ -528,7 +605,9 @@ class CoreNetwork(CoreNetworkBase):
if len(name) >= 16:
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)
if net.up:
# this is similar to net.attach() but uses netif.name instead
@ -569,7 +648,9 @@ class CoreNetwork(CoreNetworkBase):
return
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):
@ -578,8 +659,18 @@ class GreTapBridge(CoreNetwork):
another system.
"""
def __init__(self, session, remoteip=None, _id=None, name=None,
policy="ACCEPT", localip=None, ttl=255, key=None, start=True):
def __init__(
self,
session,
remoteip=None,
_id=None,
name=None,
policy="ACCEPT",
localip=None,
ttl=255,
key=None,
start=True,
):
"""
Create a GreTapBridge instance.
@ -593,7 +684,9 @@ class GreTapBridge(CoreNetwork):
:param key: gre tap key
: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
if self.grekey is None:
self.grekey = self.session.id ^ self.id
@ -605,8 +698,14 @@ class GreTapBridge(CoreNetwork):
if remoteip is None:
self.gretap = None
else:
self.gretap = GreTap(node=self, session=session, remoteip=remoteip,
localip=localip, ttl=ttl, key=self.grekey)
self.gretap = GreTap(
node=self,
session=session,
remoteip=remoteip,
localip=localip,
ttl=ttl,
key=self.grekey,
)
if start:
self.startup()
@ -648,8 +747,13 @@ class GreTapBridge(CoreNetwork):
localip = None
if len(addrlist) > 1:
localip = addrlist[1].split("/")[0]
self.gretap = GreTap(session=self.session, remoteip=remoteip,
localip=localip, ttl=self.ttl, key=self.grekey)
self.gretap = GreTap(
session=self.session,
remoteip=remoteip,
localip=localip,
ttl=self.ttl,
key=self.grekey,
)
self.attach(self.gretap)
def setkey(self, key):
@ -667,6 +771,7 @@ class CtrlNet(CoreNetwork):
"""
Control network functionality.
"""
policy = "ACCEPT"
# base control interface index
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.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.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, start=True, assign_address=True,
updown_script=None, serverintf=None):
def __init__(
self,
session,
_id="ctrlnet",
name=None,
prefix=None,
hostid=None,
start=True,
assign_address=True,
updown_script=None,
serverintf=None,
):
"""
Creates a CtrlNet instance.
@ -726,12 +840,18 @@ class CtrlNet(CoreNetwork):
logging.info("address %s", addr)
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"])
if self.serverintf:
# 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
utils.check_cmd([constants.IP_BIN, "link", "set", self.serverintf, "up"])
@ -758,7 +878,9 @@ class CtrlNet(CoreNetwork):
logging.error(
"error: An active control net bridge (%s) found. "
"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 False
@ -771,13 +893,23 @@ class CtrlNet(CoreNetwork):
"""
if self.serverintf is not None:
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:
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:
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"])
except CoreCommandError:
logging.exception("error issuing shutdown script shutdown")
@ -799,6 +931,7 @@ class PtpNet(CoreNetwork):
"""
Peer to peer network node.
"""
policy = "ACCEPT"
def attach(self, netif):
@ -809,7 +942,9 @@ class PtpNet(CoreNetwork):
:return: nothing
"""
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)
@ -924,7 +1059,7 @@ class PtpNet(CoreNetwork):
jitter=if2.getparam("jitter"),
unidirectional=1,
interface1_id=if2.node.getifindex(if2),
interface2_id=if1.node.getifindex(if1)
interface2_id=if1.node.getifindex(if1),
)
all_links.append(link_data)
@ -935,6 +1070,7 @@ class SwitchNode(CoreNetwork):
"""
Provides switch functionality within a core node.
"""
apitype = NodeTypes.SWITCH.value
policy = "ACCEPT"
type = "lanswitch"
@ -945,6 +1081,7 @@ class HubNode(CoreNetwork):
Provides hub functionality within a core node, forwards packets to all bridge
ports by turning off MAC address learning.
"""
apitype = NodeTypes.HUB.value
policy = "ACCEPT"
type = "hub"
@ -970,6 +1107,7 @@ class WlanNode(CoreNetwork):
"""
Provides wireless lan functionality within a core node.
"""
apitype = NodeTypes.WIRELESS_LAN.value
linktype = LinkTypes.WIRELESS.value
policy = "DROP"
@ -991,6 +1129,10 @@ class WlanNode(CoreNetwork):
# mobility model such as scripted
self.mobility = None
# TODO: move to startup method
if start:
utils.check_cmd([constants.BRCTL_BIN, "setageing", self.brname, "0"])
def attach(self, netif):
"""
Attach a network interface.
@ -1015,9 +1157,14 @@ class WlanNode(CoreNetwork):
:param dict config: configuration for model being set
: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:
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)
elif model.config_type == RegisterTlvs.MOBILITY.value:
self.mobility = model(session=self.session, _id=self.id)
@ -1031,14 +1178,14 @@ class WlanNode(CoreNetwork):
def updatemodel(self, config):
if not self.model:
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)
if self.model.position_callback:
for netif in self.netifs():
netif.poshook = self.model.position_callback
if netif.node is not None:
x, y, z = netif.node.position.get()
netif.poshook(netif, x, y, z)
for netif in self.netifs():
if netif.poshook and netif.node:
x, y, z = netif.node.position.get()
netif.poshook(netif, x, y, z)
def all_link_data(self, flags):
"""
@ -1060,6 +1207,7 @@ class TunnelNode(GreTapBridge):
"""
Provides tunnel functionality in a core node.
"""
apitype = NodeTypes.TUNNEL.value
policy = "ACCEPT"
type = "tunnel"

View file

@ -2,13 +2,14 @@
Provides default node maps that can be used to run core with.
"""
import core.nodes.base
import core.nodes.docker
import core.nodes.lxd
import core.nodes.network
import core.nodes.physical
from core.emane.nodes import EmaneNet
from core.emane.nodes import EmaneNode
from core.emane.nodes import EmaneNet, EmaneNode
from core.emulator.enumerations import NodeTypes
from core.nodes.network import GreTapBridge
from core.nodes import physical
from core.nodes.network import GreTapBridge
# legacy core nodes, that leverage linux bridges
NODES = {
@ -25,5 +26,7 @@ NODES = {
NodeTypes.EMANE_NET: EmaneNet,
NodeTypes.TAP_BRIDGE: GreTapBridge,
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 socket
import threading
from socket import AF_INET
from socket import AF_INET6
from socket import AF_INET, AF_INET6
from core import CoreCommandError, utils
from core import constants
from core.nodes.base import CoreNetworkBase
from core import CoreCommandError, constants, utils
from core.emulator.data import LinkData
from core.emulator.enumerations import LinkTypes
from core.emulator.enumerations import NodeTypes
from core.emulator.enumerations import RegisterTlvs
from core.emulator.enumerations import LinkTypes, NodeTypes, RegisterTlvs
from core.nodes import ipaddress
from core.nodes.interface import GreTap
from core.nodes.interface import Veth
from core.nodes.network import EbtablesQueue
from core.nodes.network import GreTapBridge
from core.nodes.base import CoreNetworkBase
from core.nodes.interface import GreTap, Veth
from core.nodes.network import EbtablesQueue, GreTapBridge
# a global object because all WLANs share the same queue
# cannot have multiple threads invoking the ebtables commnd
@ -27,11 +21,7 @@ ebtables_queue = EbtablesQueue()
ebtables_lock = threading.Lock()
utils.check_executables([
constants.IP_BIN,
constants.EBTABLES_BIN,
constants.TC_BIN
])
utils.check_executables([constants.IP_BIN, constants.EBTABLES_BIN, constants.TC_BIN])
def ebtables_commands(call, commands):
@ -89,10 +79,21 @@ class OvsNet(CoreNetworkBase):
utils.check_cmd([constants.IP_BIN, "link", "set", self.bridge_name, "up"])
# create a new ebtables chain for this bridge
ebtables_commands(utils.check_cmd, [
[constants.EBTABLES_BIN, "-N", self.bridge_name, "-P", self.policy],
[constants.EBTABLES_BIN, "-A", "FORWARD", "--logical-in", self.bridge_name, "-j", self.bridge_name]
])
ebtables_commands(
utils.check_cmd,
[
[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
@ -106,10 +107,21 @@ class OvsNet(CoreNetworkBase):
try:
utils.check_cmd([constants.IP_BIN, "link", "set", self.bridge_name, "down"])
utils.check_cmd([constants.OVS_BIN, "del-br", self.bridge_name])
ebtables_commands(utils.check_cmd, [
[constants.EBTABLES_BIN, "-D", "FORWARD", "--logical-in", self.bridge_name, "-j", self.bridge_name],
[constants.EBTABLES_BIN, "-X", self.bridge_name]
])
ebtables_commands(
utils.check_cmd,
[
[
constants.EBTABLES_BIN,
"-D",
"FORWARD",
"--logical-in",
self.bridge_name,
"-j",
self.bridge_name,
],
[constants.EBTABLES_BIN, "-X", self.bridge_name],
],
)
except CoreCommandError:
logging.exception("error bringing bridge down and removing it")
@ -124,14 +136,20 @@ class OvsNet(CoreNetworkBase):
def attach(self, interface):
if self.up:
utils.check_cmd([constants.OVS_BIN, "add-port", self.bridge_name, interface.localname])
utils.check_cmd([constants.IP_BIN, "link", "set", interface.localname, "up"])
utils.check_cmd(
[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)
def detach(self, interface):
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)
@ -183,8 +201,17 @@ class OvsNet(CoreNetworkBase):
ebtables_queue.ebchange(self)
def linkconfig(self, netif, bw=None, delay=None, loss=None, duplicate=None,
jitter=None, netif2=None, devname=None):
def linkconfig(
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.
@ -202,9 +229,19 @@ class OvsNet(CoreNetworkBase):
if bw > 0:
if self.up:
burst = max(2 * netif.mtu, bw / 1000)
limit = 0xffff # max IP payload
tbf = ["tbf", "rate", str(bw), "burst", str(burst), "limit", str(limit)]
logging.info("linkconfig: %s" % [tc + parent + ["handle", "1:"] + tbf])
limit = 0xFFFF # max IP payload
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)
netif.setparam("has_tbf", True)
elif netif.getparam("has_tbf") and bw <= 0:
@ -234,7 +271,15 @@ class OvsNet(CoreNetworkBase):
jitter_changed = netif.setparam("jitter", jitter)
# 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
# jitter and delay use the same delay statement
@ -265,7 +310,9 @@ class OvsNet(CoreNetworkBase):
netif.setparam("has_netem", False)
elif len(netem) > 1:
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)
netif.setparam("has_netem", True)
@ -295,11 +342,15 @@ class OvsNet(CoreNetworkBase):
if len(name) >= 16:
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)
if network.up:
# 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"])
network.attach(interface)
@ -326,7 +377,9 @@ class OvsNet(CoreNetworkBase):
return
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):
@ -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.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.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,
start=True, assign_address=True, updown_script=None, serverintf=None):
def __init__(
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.hostid = hostid
self.assign_address = assign_address
@ -358,7 +421,10 @@ class OvsCtrlNet(OvsNet):
else:
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)]
if self.assign_address:
self.addrconfig(addresses=addresses)
@ -366,11 +432,16 @@ class OvsCtrlNet(OvsNet):
logging.info(message)
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"])
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"])
def detectoldbridge(self):
@ -385,7 +456,10 @@ class OvsCtrlNet(OvsNet):
for line in output.split("\n"):
bride_name = line.split(".")
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 False
@ -393,14 +467,23 @@ class OvsCtrlNet(OvsNet):
def shutdown(self):
if self.serverintf:
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:
logging.exception("error deleting server interface %s to controlnet bridge %s",
self.serverintf, self.bridge_name)
logging.exception(
"error deleting server interface %s to controlnet bridge %s",
self.serverintf,
self.bridge_name,
)
if self.updown_script:
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"])
except CoreCommandError:
logging.exception("error during updown script shutdown")
@ -419,7 +502,9 @@ class OvsPtpNet(OvsNet):
def attach(self, interface):
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)
def data(self, message_type, lat=None, lon=None, alt=None):
@ -522,7 +607,7 @@ class OvsPtpNet(OvsNet):
jitter=if1.getparam("jitter"),
unidirectional=1,
interface1_id=if2.node.getifindex(if2),
interface2_id=if1.node.getifindex(if1)
interface2_id=if1.node.getifindex(if1),
)
all_links.append(link_data)
@ -550,7 +635,9 @@ class OvsHubNode(OvsNet):
if start:
# TODO: verify that the below flow accomplishes what is desired for a "HUB"
# 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):
@ -602,7 +689,9 @@ class OvsWlanNode(OvsNet):
def updatemodel(self, config):
if not self.model:
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)
if self.model.position_callback:
for netif in self.netifs():
@ -633,9 +722,21 @@ class OvsGreTapBridge(OvsNet):
another system.
"""
def __init__(self, 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)
def __init__(
self,
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
if self.grekey is None:
self.grekey = self.session.id ^ self.id
@ -649,8 +750,14 @@ class OvsGreTapBridge(OvsNet):
if remoteip is None:
self.gretap = None
else:
self.gretap = GreTap(node=self, session=session, remoteip=remoteip,
localip=localip, ttl=ttl, key=self.grekey)
self.gretap = GreTap(
node=self,
session=session,
remoteip=remoteip,
localip=localip,
ttl=ttl,
key=self.grekey,
)
if start:
self.startup()
@ -690,8 +797,13 @@ class OvsGreTapBridge(OvsNet):
if len(addresses) > 1:
localip = addresses[1].split("/")[0]
self.gretap = GreTap(session=self.session, remoteip=remoteip,
localip=localip, ttl=self.ttl, key=self.grekey)
self.gretap = GreTap(
session=self.session,
remoteip=remoteip,
localip=localip,
ttl=self.ttl,
key=self.grekey,
)
self.attach(self.gretap)
def setkey(self, key):
@ -709,5 +821,5 @@ OVS_NODES = {
NodeTypes.TUNNEL: OvsTunnelNode,
NodeTypes.TAP_BRIDGE: OvsGreTapBridge,
NodeTypes.PEER_TO_PEER: OvsPtpNet,
NodeTypes.CONTROL_NET: OvsCtrlNet
NodeTypes.CONTROL_NET: OvsCtrlNet,
}

View file

@ -7,13 +7,11 @@ import os
import subprocess
import threading
from core import CoreCommandError, utils
from core import constants
from core import CoreCommandError, constants, utils
from core.emulator.enumerations import NodeTypes
from core.nodes.base import CoreNodeBase
from core.nodes.interface import CoreInterface
from core.emulator.enumerations import NodeTypes
from core.nodes.network import GreTap
from core.nodes.network import CoreNetwork
from core.nodes.network import CoreNetwork, GreTap
class PhysicalNode(CoreNodeBase):
@ -104,14 +102,25 @@ class PhysicalNode(CoreNodeBase):
self._netif[ifindex].sethwaddr(addr)
ifname = self.ifname(ifindex)
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):
"""
Add an address to an interface.
"""
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)
@ -125,7 +134,16 @@ class PhysicalNode(CoreNodeBase):
logging.exception("trying to delete unknown address: %s", addr)
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):
"""
@ -140,8 +158,12 @@ class PhysicalNode(CoreNodeBase):
# use a more reasonable name, e.g. "gt0" instead of "gt.56286.150"
if self.up:
self.check_cmd([constants.IP_BIN, "link", "set", "dev", netif.localname, "down"])
self.check_cmd([constants.IP_BIN, "link", "set", netif.localname, "name", netif.name])
self.check_cmd(
[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
@ -152,16 +174,35 @@ class PhysicalNode(CoreNodeBase):
self.addaddr(ifindex, addr)
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()
"""
# borrow the tc qdisc commands from LxBrNet.linkconfig()
linux_bridge = CoreNetwork(session=self.session, start=False)
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
def newifindex(self):
@ -188,7 +229,9 @@ class PhysicalNode(CoreNodeBase):
# tunnel to net not built yet, so build it now and adopt it
gt = self.session.broker.addnettunnel(net.id)
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]
net.detach(gt)
self.adoptnetif(gt, ifindex, hwaddr, addrlist)
@ -205,7 +248,9 @@ class PhysicalNode(CoreNodeBase):
def privatedir(self, path):
if path[0] != "/":
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)
self.mount(hostpath, path)
@ -251,6 +296,7 @@ class Rj45Node(CoreNodeBase, CoreInterface):
RJ45Node is a physical interface on the host linked to the emulated
network.
"""
apitype = NodeTypes.RJ45.value
type = "rj45"
@ -304,7 +350,9 @@ class Rj45Node(CoreNodeBase, CoreInterface):
try:
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.TC_BIN, "qdisc", "del", "dev", self.localname, "root"])
utils.check_cmd(
[constants.TC_BIN, "qdisc", "del", "dev", self.localname, "root"]
)
except CoreCommandError:
logging.exception("error shutting down")
@ -427,7 +475,9 @@ class Rj45Node(CoreNodeBase, CoreInterface):
:raises CoreCommandError: when there is a command exception
"""
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)
@ -440,7 +490,9 @@ class Rj45Node(CoreNodeBase, CoreInterface):
:raises CoreCommandError: when there is a command exception
"""
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)
@ -481,9 +533,22 @@ class Rj45Node(CoreNodeBase, CoreInterface):
"""
for addr in self.old_addrs:
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:
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:
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 socket
from future.moves.urllib.parse import urlparse
from core import constants
from core.emulator.enumerations import EventTypes
from core.emulator.enumerations import LinkTlvs
from core.emulator.enumerations import LinkTypes
from core.emulator.enumerations import MessageFlags
from core.emulator.enumerations import MessageTypes
from core.emulator.enumerations import NodeTlvs
from core.emulator.enumerations import NodeTypes
from core import CoreError, constants
from core.emulator.enumerations import (
EventTypes,
LinkTlvs,
LinkTypes,
MessageFlags,
MessageTypes,
NodeTlvs,
NodeTypes,
)
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
@ -40,6 +43,7 @@ class Sdt(object):
The connect() method initializes the display, and can be invoked
when a node position or link has changed.
"""
DEFAULT_SDT_URL = "tcp://127.0.0.1:50000/"
# default altitude (in meters) for flyto view
DEFAULT_ALT = 2500
@ -93,7 +97,12 @@ class Sdt(object):
alt = node_data.altitude
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:
# TODO: z is not currently supported by node messages
@ -107,7 +116,12 @@ class Sdt(object):
:return: nothing
"""
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):
"""
@ -179,7 +193,7 @@ class Sdt(object):
:return: initialize command status
: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
# send node type to icon mappings
for node_type, icon in self.DEFAULT_SPRITES:
@ -210,7 +224,6 @@ class Sdt(object):
:return: nothing
"""
logging.info("SDT shutdown!")
self.cmd("clear all")
self.disconnect()
self.showerror = True
@ -266,7 +279,9 @@ class Sdt(object):
icon = icon.replace("$CORE_DATA_DIR", constants.CORE_DATA_DIR)
icon = icon.replace("$CORE_CONF_DIR", constants.CORE_CONF_DIR)
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:
self.cmd("node %d %s" % (nodenum, pos))
@ -330,16 +345,29 @@ class Sdt(object):
(x, y, z) = node.getposition()
if x is None or y is None:
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()):
r = self.remotes[nodenum]
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:
all_links = net.all_link_data(flags=MessageFlags.ADD.value)
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
if is_wireless and link_data.node1_id == net.id:
continue
@ -348,7 +376,7 @@ class Sdt(object):
link_data.node1_id,
link_data.node2_id,
MessageFlags.ADD.value,
wireless_link
wireless_link,
)
for n1num in sorted(self.remotes.keys()):
@ -397,8 +425,7 @@ class Sdt(object):
icon = msg.get_tlv(NodeTlvs.ICON.value)
net = False
if nodetype == NodeTypes.DEFAULT.value or \
nodetype == NodeTypes.PHYSICAL.value:
if nodetype == NodeTypes.DEFAULT.value or nodetype == NodeTypes.PHYSICAL.value:
if model is None:
model = "router"
nodetype = model
@ -410,10 +437,12 @@ class Sdt(object):
try:
node = self.session.get_node(nodenum)
except KeyError:
except CoreError:
node = None
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:
if nodenum in self.remotes:
remote = self.remotes[nodenum]
@ -424,7 +453,14 @@ class Sdt(object):
if icon is None:
icon = remote.icon
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
remote.pos = (x, y, z)
self.updatenode(nodenum, msg.flags, x, y, z, name, nodetype, icon)
@ -471,7 +507,7 @@ class Sdt(object):
else:
try:
n = self.session.get_node(nodenum)
except KeyError:
except CoreError:
return False
if nodeutils.is_node(n, (NodeTypes.WIRELESS_LAN, NodeTypes.EMANE)):
return True

View file

@ -9,6 +9,7 @@ class Bird(CoreService):
"""
Bird router support
"""
name = "bird"
executables = ("bird",)
group = "BIRD"
@ -34,11 +35,11 @@ class Bird(CoreService):
Helper to return the first IPv4 address of a node as its router ID.
"""
for ifc in node.netifs():
if hasattr(ifc, 'control') and ifc.control is True:
if hasattr(ifc, "control") and ifc.control is True:
continue
for a in ifc.addrlist:
if a.find(".") >= 0:
return a.split('/')[0]
return a.split("/")[0]
# raise ValueError, "no IPv4 address found for router ID"
return "0.0.0.0"
@ -72,7 +73,10 @@ protocol device {
scan time 10; # Scan interfaces every 10 seconds
}
""" % (cls.name, cls.routerid(node))
""" % (
cls.name,
cls.routerid(node),
)
# Generate protocol specific configurations
for s in node.services:
@ -113,7 +117,7 @@ class BirdService(CoreService):
cfg = ""
for ifc in node.netifs():
if hasattr(ifc, 'control') and ifc.control is True:
if hasattr(ifc, "control") and ifc.control is True:
continue
cfg += ' interface "%s";\n' % ifc.name
@ -160,18 +164,18 @@ class BirdOspf(BirdService):
@classmethod
def generatebirdconfig(cls, node):
cfg = 'protocol ospf {\n'
cfg += ' export filter {\n'
cfg += ' if source = RTS_BGP then {\n'
cfg += ' ospf_metric1 = 100;\n'
cfg += ' accept;\n'
cfg += ' }\n'
cfg += ' accept;\n'
cfg += ' };\n'
cfg += ' area 0.0.0.0 {\n'
cfg = "protocol ospf {\n"
cfg += " export filter {\n"
cfg += " if source = RTS_BGP then {\n"
cfg += " ospf_metric1 = 100;\n"
cfg += " accept;\n"
cfg += " }\n"
cfg += " accept;\n"
cfg += " };\n"
cfg += " area 0.0.0.0 {\n"
cfg += cls.generatebirdifcconfig(node)
cfg += ' };\n'
cfg += '}\n\n'
cfg += " };\n"
cfg += "}\n\n"
return cfg
@ -185,21 +189,21 @@ class BirdRadv(BirdService):
@classmethod
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 += ' # auto configuration on all interfaces\n'
cfg += "protocol radv {\n"
cfg += " # auto configuration on all interfaces\n"
cfg += cls.generatebirdifcconfig(node)
cfg += ' # Advertise DNS\n'
cfg += ' rdnss {\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::12;\n'
cfg += '# ns 2001:0DB8:1234::12;\n'
cfg += ' };\n'
cfg += '}\n\n'
cfg += " # Advertise DNS\n"
cfg += " rdnss {\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::12;\n"
cfg += "# ns 2001:0DB8:1234::12;\n"
cfg += " };\n"
cfg += "}\n\n"
return cfg
@ -213,15 +217,15 @@ class BirdRip(BirdService):
@classmethod
def generatebirdconfig(cls, node):
cfg = 'protocol rip {\n'
cfg += ' period 10;\n'
cfg += ' garbage time 60;\n'
cfg = "protocol rip {\n"
cfg += " period 10;\n"
cfg += " garbage time 60;\n"
cfg += cls.generatebirdifcconfig(node)
cfg += ' honor neighbor;\n'
cfg += ' authentication none;\n'
cfg += ' import all;\n'
cfg += ' export all;\n'
cfg += '}\n\n'
cfg += " honor neighbor;\n"
cfg += " authentication none;\n"
cfg += " import all;\n"
cfg += " export all;\n"
cfg += "}\n\n"
return cfg
@ -236,10 +240,10 @@ class BirdStatic(BirdService):
@classmethod
def generatebirdconfig(cls, node):
cfg = '/* This is a sample config that must be customized */\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 203.0.113.0/24 reject; # Sink route\n'
cfg = "/* This is a sample config that must be customized */\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 203.0.113.0/24 reject; # Sink route\n"
cfg += '# route 10.2.0.0/24 via "arc0"; # Secondary network\n'
cfg += '}\n\n'
cfg += "}\n\n"
return cfg

View file

@ -15,8 +15,7 @@ from multiprocessing.pool import ThreadPool
from core import CoreCommandError, utils
from core.constants import which
from core.emulator.data import FileData
from core.emulator.enumerations import MessageFlags
from core.emulator.enumerations import RegisterTlvs
from core.emulator.enumerations import MessageFlags, RegisterTlvs
class ServiceBootError(Exception):
@ -62,7 +61,9 @@ class ServiceDependencies(object):
for name in self.node_services:
service = self.node_services[name]
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
path = self._start(service)
@ -70,7 +71,10 @@ class ServiceDependencies(object):
paths.append(path)
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
@ -92,10 +96,16 @@ class ServiceDependencies(object):
# dive down
for service_name in current_service.dependencies:
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:
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:
service = self.node_services[service_name]
@ -117,7 +127,16 @@ class ServiceDependencies(object):
class ServiceShim(object):
keys = ["dirs", "files", "startidx", "cmdup", "cmddown", "cmdval", "meta", "starttime"]
keys = [
"dirs",
"files",
"startidx",
"cmdup",
"cmddown",
"cmdval",
"meta",
"starttime",
]
@classmethod
def tovaluelist(cls, node, service):
@ -132,8 +151,16 @@ class ServiceShim(object):
"""
start_time = 0
start_index = 0
valmap = [service.dirs, service.configs, start_index, service.startup,
service.shutdown, service.validate, service.meta, start_time]
valmap = [
service.dirs,
service.configs,
start_index,
service.startup,
service.shutdown,
service.validate,
service.meta,
start_time,
]
if not service.custom:
valmap[1] = service.get_configs(node)
valmap[3] = service.get_startup(node)
@ -201,16 +228,17 @@ class ServiceShim(object):
:return: services
:rtype: list
"""
servicesstring = opaque.split(':')
servicesstring = opaque.split(":")
if servicesstring[0] != "service":
return []
return servicesstring[1].split(',')
return servicesstring[1].split(",")
class ServiceManager(object):
"""
Manages services available for CORE nodes to use.
"""
services = {}
@classmethod
@ -231,8 +259,12 @@ class ServiceManager(object):
# validate dependent executables are present
for executable in service.executables:
if not which(executable):
logging.debug("service(%s) missing executable: %s", service.name, executable)
raise ValueError("service(%s) missing executable: %s" % (service.name, executable))
logging.debug(
"service(%s) missing executable: %s", service.name, executable
)
raise ValueError(
"service(%s) missing executable: %s" % (service.name, executable)
)
# make service available
cls.services[name] = service
@ -280,6 +312,7 @@ class CoreServices(object):
the default services configured for each node type, and any
custom service configuration. A CoreService is not a Configurable.
"""
name = "services"
config_type = RegisterTlvs.UTILITY.value
@ -368,17 +401,20 @@ class CoreServices(object):
:return: nothing
"""
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, [])
logging.info("setting services for node(%s): %s", node.name, services)
for service_name in services:
service = self.get_service(node.id, service_name, default_service=True)
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
logging.info("adding service to node(%s): %s", node.name, service_name)
node.addservice(service)
node.services.append(service)
def all_configs(self):
"""
@ -445,11 +481,15 @@ class CoreServices(object):
:param list[CoreService] boot_path: service to start in dependent order
: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:
try:
self.boot_service(node, service)
except:
except Exception:
logging.exception("exception booting service: %s", service.name)
raise
@ -462,16 +502,24 @@ class CoreServices(object):
:param CoreService service: service to start
:return: nothing
"""
logging.info("starting node(%s) service(%s) validation(%s)", node.name, service.name,
service.validation_mode.name)
logging.info(
"starting node(%s) service(%s) validation(%s)",
node.name,
service.name,
service.validation_mode.name,
)
# create service directories
for directory in service.dirs:
try:
node.privatedir(directory)
except (CoreCommandError, ValueError) as e:
logging.warning("error mounting private dir '%s' for service '%s': %s",
directory, service.name, e)
logging.warning(
"error mounting private dir '%s' for service '%s': %s",
directory,
service.name,
e,
)
# create service files
self.create_service_files(node, service)
@ -480,7 +528,9 @@ class CoreServices(object):
wait = service.validation_mode == ServiceMode.BLOCKING
status = self.startup_service(node, service, wait)
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
if wait:
@ -503,7 +553,9 @@ class CoreServices(object):
time.sleep(service.validation_period)
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):
"""
@ -517,9 +569,9 @@ class CoreServices(object):
:return: True if successful, False otherwise
:rtype: bool
"""
if cfg[:7] == 'file://':
if cfg[:7] == "file://":
src = cfg[7:]
src = src.split('\n')[0]
src = src.split("\n")[0]
src = utils.expand_corepath(src, node.session, node)
# TODO: glob here
node.nodefilecopy(filename, src, mode=0o644)
@ -535,7 +587,7 @@ class CoreServices(object):
:return: service validation status
: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
if not service.custom:
cmds = service.get_validate(node)
@ -546,8 +598,10 @@ class CoreServices(object):
try:
node.check_cmd(cmd)
except CoreCommandError as e:
logging.error("node(%s) service(%s) validate failed", node.name, service.name)
logging.error("cmd(%s): %s", e.cmd, e.output)
logging.debug(
"node(%s) service(%s) validate failed", node.name, service.name
)
logging.debug("cmd(%s): %s", e.cmd, e.output)
status = -1
break
@ -603,7 +657,9 @@ class CoreServices(object):
config_files = service.get_configs(node)
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
data = service.config_data.get(filename)
@ -618,7 +674,7 @@ class CoreServices(object):
node=node.id,
name=filename,
type=filetypestr,
data=data
data=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
config_files = service.configs
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
# set custom service file data
@ -686,7 +744,6 @@ class CoreServices(object):
:param CoreService service: service to reconfigure
:return: nothing
"""
logging.info("node(%s) service(%s) creating config files", node.name, service.name)
# get values depending on if custom or not
config_files = service.configs
if not service.custom:
@ -739,6 +796,7 @@ class CoreService(object):
"""
Parent class used for defining services.
"""
# service name should not include spaces
name = None

View file

@ -99,8 +99,7 @@ Limitations:
import logging
from core.services.coreservices import CoreService
from core.services.coreservices import ServiceManager
from core.services.coreservices import CoreService, ServiceManager
try:
from docker import Client

View file

@ -23,15 +23,23 @@ class EmaneTransportService(CoreService):
for interface in node.netifs(sort=True):
network_node = node.session.get_node(interface.net.id)
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):
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 = "\n".join(transport_commands)
return """
emanegentransportxml -o ../ ../platform%s.xml
%s
""" % (node.id, transport_commands)
""" % (
node.id,
transport_commands,
)
else:
raise ValueError

View file

@ -5,18 +5,14 @@ Assumes installation of FRR via https://deb.frrouting.org/
from core import constants
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
class FRRZebra(CoreService):
name = "FRRzebra"
group = "FRR"
dirs = (
"/usr/local/etc/frr",
"/var/run/frr",
"/var/log/frr",
)
dirs = ("/usr/local/etc/frr", "/var/run/frr", "/var/log/frr")
configs = (
"/usr/local/etc/frr/frr.conf",
"frrboot.sh",
@ -41,7 +37,9 @@ class FRRZebra(CoreService):
elif filename == cls.configs[3]:
return cls.generateFrrDaemons(node)
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
def generateVtyshConf(cls, node):
@ -62,7 +60,7 @@ class FRRZebra(CoreService):
for ifc in node.netifs():
cfg += "interface %s\n" % ifc.name
# 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 += "\n ".join(map(cls.addrstr, ifc.addrlist))
cfg += "\n"
@ -84,13 +82,17 @@ class FRRZebra(CoreService):
cfgv4 += ifccfg
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 += "\n ".join(map(cls.addrstr, ipv4list))
cfg += "\n"
cfg += cfgv4
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 += "\n ".join(map(cls.addrstr, ipv6list))
cfg += "\n"
@ -120,10 +122,12 @@ class FRRZebra(CoreService):
"""
Generate a shell script used to boot the FRR daemons.
"""
frr_bin_search = node.session.options.get_config("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_bin_search = node.session.options.get_config(
"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"'
)
return """\
#!/bin/sh
# auto-generated by zebra service (frr.py)
@ -175,9 +179,8 @@ bootdaemon()
flags="$flags -6"
fi
#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
if [ "$?" != "0" ]; then
@ -203,7 +206,7 @@ bootfrr()
bootdaemon "zebra"
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"
fi
done
@ -221,7 +224,12 @@ if [ "$1" != "zebra" ]; then
fi
confcheck
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
def generateFrrDaemons(cls, node):
@ -291,12 +299,12 @@ fabricd_options="-A 127.0.0.1"
"""
class FrrService(CoreService):
"""
Parent class for FRR services. Defines properties and methods
common to FRR's routing daemons.
"""
name = None
group = "FRR"
dependencies = ("FRRzebra",)
@ -315,11 +323,11 @@ class FrrService(CoreService):
Helper to return the first IPv4 address of a node as its router ID.
"""
for ifc in node.netifs():
if hasattr(ifc, 'control') and ifc.control is True:
if hasattr(ifc, "control") and ifc.control is True:
continue
for a in ifc.addrlist:
if a.find(".") >= 0:
return a.split('/')[0]
return a.split("/")[0]
# raise ValueError, "no IPv4 address found for router ID"
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
unified frr.conf file.
"""
name = "FRROSPFv2"
startup = ()
shutdown = ("killall ospfd",)
@ -397,7 +406,7 @@ class FRROspfv2(FrrService):
cfg += " router-id %s\n" % rtrid
# network 10.0.0.0/24 area 0
for ifc in node.netifs():
if hasattr(ifc, 'control') and ifc.control is True:
if hasattr(ifc, "control") and ifc.control is True:
continue
for a in ifc.addrlist:
if a.find(".") < 0:
@ -431,6 +440,7 @@ class FRROspfv3(FrrService):
not build its own configuration file but has hooks for adding to the
unified frr.conf file.
"""
name = "FRROSPFv3"
startup = ()
shutdown = ("killall ospf6d",)
@ -481,7 +491,7 @@ class FRROspfv3(FrrService):
rtrid = cls.routerid(node)
cfg += " router-id %s\n" % rtrid
for ifc in node.netifs():
if hasattr(ifc, 'control') and ifc.control is True:
if hasattr(ifc, "control") and ifc.control is True:
continue
cfg += " interface %s area 0.0.0.0\n" % ifc.name
cfg += "!\n"
@ -511,6 +521,7 @@ class FRRBgp(FrrService):
Peers must be manually configured, with a full mesh for those
having the same AS number.
"""
name = "FRRBGP"
startup = ()
shutdown = ("killall bgpd",)
@ -536,6 +547,7 @@ class FRRRip(FrrService):
"""
The RIP service provides IPv4 routing for wired networks.
"""
name = "FRRRIP"
startup = ()
shutdown = ("killall ripd",)
@ -559,6 +571,7 @@ class FRRRipng(FrrService):
"""
The RIP NG service provides IPv6 routing for wired networks.
"""
name = "FRRRIPNG"
startup = ()
shutdown = ("killall ripngd",)
@ -583,6 +596,7 @@ class FRRBabel(FrrService):
The Babel service provides a loop-avoiding distance-vector routing
protocol for IPv6 and IPv4 with fast convergence properties.
"""
name = "FRRBabel"
startup = ()
shutdown = ("killall babeld",)
@ -611,28 +625,29 @@ class FRRpimd(FrrService):
"""
PIM multicast routing based on XORP.
"""
name = 'FRRpimd'
name = "FRRpimd"
startup = ()
shutdown = ('killall pimd',)
validate = ('pidof pimd',)
shutdown = ("killall pimd",)
validate = ("pidof pimd",)
ipv4_routing = True
@classmethod
def generatefrrconfig(cls, node):
ifname = 'eth0'
ifname = "eth0"
for ifc in node.netifs():
if ifc.name != 'lo':
if ifc.name != "lo":
ifname = ifc.name
break
cfg = 'router mfea\n!\n'
cfg += 'router igmp\n!\n'
cfg += 'router pim\n'
cfg += ' !ip pim rp-address 10.0.0.1\n'
cfg += ' ip pim bsr-candidate %s\n' % ifname
cfg += ' ip pim rp-candidate %s\n' % ifname
cfg += ' !ip pim spt-threshold interval 10 bytes 80000\n'
cfg = "router mfea\n!\n"
cfg += "router igmp\n!\n"
cfg += "router pim\n"
cfg += " !ip pim rp-address 10.0.0.1\n"
cfg += " ip pim bsr-candidate %s\n" % ifname
cfg += " ip pim rp-candidate %s\n" % ifname
cfg += " !ip pim spt-threshold interval 10 bytes 80000\n"
return cfg
@classmethod
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
common to NRL's routing daemons.
"""""
""" ""
name = None
group = "ProtoSvc"
dirs = ()
@ -32,11 +33,11 @@ class NrlService(CoreService):
interface's prefix length, so e.g. '/32' can turn into '/24'.
"""
for ifc in node.netifs():
if hasattr(ifc, 'control') and ifc.control is True:
if hasattr(ifc, "control") and ifc.control is True:
continue
for a in ifc.addrlist:
if a.find(".") >= 0:
addr = a.split('/')[0]
addr = a.split("/")[0]
pre = Ipv4Prefix("%s/%s" % (addr, prefixlen))
return str(pre)
# raise ValueError, "no IPv4 address found"
@ -63,13 +64,14 @@ class MgenSinkService(NrlService):
def get_startup(cls, node):
cmd = cls.startup[0]
cmd += " output /tmp/mgen_%s.log" % node.name
return cmd,
return (cmd,)
class NrlNhdp(NrlService):
"""
NeighborHood Discovery Protocol for MANET networks.
"""
name = "NHDP"
executables = ("nrlnhdp",)
startup = ("nrlnhdp",)
@ -90,19 +92,20 @@ class NrlNhdp(NrlService):
cmd += " -flooding ecds"
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:
interfacenames = map(lambda x: x.name, netifs)
cmd += " -i "
cmd += " -i ".join(interfacenames)
return cmd,
return (cmd,)
class NrlSmf(NrlService):
"""
Simplified Multicast Forwarding for MANET networks.
"""
name = "SMF"
executables = ("nrlsmf",)
startup = ("sh startsmf.sh",)
@ -111,7 +114,7 @@ class NrlSmf(NrlService):
configs = ("startsmf.sh",)
@classmethod
def generate_config(cls, node, filename, ):
def generate_config(cls, node, filename):
"""
Generate a startup script for SMF. Because nrlsmf does not
daemonize, it can cause problems in some situations when launched
@ -123,7 +126,7 @@ class NrlSmf(NrlService):
cmd = "nrlsmf instance %s_smf" % node.name
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:
return ""
@ -155,6 +158,7 @@ class NrlOlsr(NrlService):
"""
Optimized Link State Routing protocol for MANET networks.
"""
name = "OLSR"
executables = ("nrlolsrd",)
startup = ("nrlolsrd",)
@ -176,19 +180,20 @@ class NrlOlsr(NrlService):
cmd += " -rpipe %s_olsr" % node.name
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 += " -smfClient %s_smf" % node.name
if "zebra" in servicenames:
cmd += " -z"
return cmd,
return (cmd,)
class NrlOlsrv2(NrlService):
"""
Optimized Link State Routing protocol version 2 for MANET networks.
"""
name = "OLSRv2"
executables = ("nrlolsrv2",)
startup = ("nrlolsrv2",)
@ -211,19 +216,20 @@ class NrlOlsrv2(NrlService):
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:
interfacenames = map(lambda x: x.name, netifs)
cmd += " -i "
cmd += " -i ".join(interfacenames)
return cmd,
return (cmd,)
class OlsrOrg(NrlService):
"""
Optimized Link State Routing protocol from olsr.org for MANET networks.
"""
name = "OLSRORG"
executables = ("olsrd",)
configs = ("/etc/olsrd/olsrd.conf",)
@ -238,13 +244,13 @@ class OlsrOrg(NrlService):
Generate the appropriate command-line based on node interfaces.
"""
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:
interfacenames = map(lambda x: x.name, netifs)
cmd += " -i "
cmd += " -i ".join(interfacenames)
return cmd,
return (cmd,)
@classmethod
def generate_config(cls, node, filename):
@ -582,7 +588,7 @@ class MgenActor(NrlService):
dirs = ()
# generated files (without a full path this file goes in the node's dir,
# 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
startup = ("sh start_mgen_actor.sh",)
# list of validation commands
@ -614,6 +620,7 @@ class Arouted(NrlService):
"""
Adaptive Routing
"""
name = "arouted"
executables = ("arouted",)
configs = ("startarouted.sh",)
@ -626,7 +633,8 @@ class Arouted(NrlService):
"""
Return the Quagga.conf or quaggaboot.sh file contents.
"""
cfg = """
cfg = (
"""
#!/bin/sh
for f in "/tmp/%s_smf"; do
count=1
@ -640,7 +648,9 @@ for f in "/tmp/%s_smf"; do
done
done
""" % node.name
"""
% node.name
)
cfg += "ip route add %s dev lo\n" % cls.firstipv4prefix(node, 24)
cfg += "arouted instance %s_smf tap %s_tap" % (node.name, node.name)
# 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.emulator.enumerations import LinkTypes, NodeTypes
from core.nodes import nodeutils, ipaddress
from core.nodes import ipaddress, nodeutils
from core.services.coreservices import CoreService
@ -15,7 +15,7 @@ class Zebra(CoreService):
configs = (
"/usr/local/etc/quagga/Quagga.conf",
"quaggaboot.sh",
"/usr/local/etc/quagga/vtysh.conf"
"/usr/local/etc/quagga/vtysh.conf",
)
startup = ("sh quaggaboot.sh zebra",)
shutdown = ("killall zebra",)
@ -33,7 +33,9 @@ class Zebra(CoreService):
elif filename == cls.configs[2]:
return cls.generateVtyshConf(node)
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
def generateVtyshConf(cls, node):
@ -54,7 +56,7 @@ class Zebra(CoreService):
for ifc in node.netifs():
cfg += "interface %s\n" % ifc.name
# 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 += "\n ".join(map(cls.addrstr, ifc.addrlist))
cfg += "\n"
@ -76,13 +78,17 @@ class Zebra(CoreService):
cfgv4 += ifccfg
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 += "\n ".join(map(cls.addrstr, ipv4list))
cfg += "\n"
cfg += cfgv4
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 += "\n ".join(map(cls.addrstr, ipv6list))
cfg += "\n"
@ -112,10 +118,12 @@ class Zebra(CoreService):
"""
Generate a shell script used to boot the Quagga daemons.
"""
quagga_bin_search = node.session.options.get_config("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_bin_search = node.session.options.get_config(
"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"'
)
return """\
#!/bin/sh
# auto-generated by zebra service (quagga.py)
@ -191,7 +199,7 @@ bootquagga()
bootdaemon "zebra"
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"
fi
done
@ -209,7 +217,12 @@ if [ "$1" != "zebra" ]; then
fi
confcheck
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):
@ -217,6 +230,7 @@ class QuaggaService(CoreService):
Parent class for Quagga services. Defines properties and methods
common to Quagga's routing daemons.
"""
name = None
group = "Quagga"
dependencies = ("zebra",)
@ -235,11 +249,11 @@ class QuaggaService(CoreService):
Helper to return the first IPv4 address of a node as its router ID.
"""
for ifc in node.netifs():
if hasattr(ifc, 'control') and ifc.control is True:
if hasattr(ifc, "control") and ifc.control is True:
continue
for a in ifc.addrlist:
if a.find(".") >= 0:
return a.split('/')[0]
return a.split("/")[0]
# raise ValueError, "no IPv4 address found for router ID"
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
unified Quagga.conf file.
"""
name = "OSPFv2"
startup = ()
shutdown = ("killall ospfd",)
@ -317,7 +332,7 @@ class Ospfv2(QuaggaService):
cfg += " router-id %s\n" % rtrid
# network 10.0.0.0/24 area 0
for ifc in node.netifs():
if hasattr(ifc, 'control') and ifc.control is True:
if hasattr(ifc, "control") and ifc.control is True:
continue
for a in ifc.addrlist:
if a.find(".") < 0:
@ -351,6 +366,7 @@ class Ospfv3(QuaggaService):
not build its own configuration file but has hooks for adding to the
unified Quagga.conf file.
"""
name = "OSPFv3"
startup = ()
shutdown = ("killall ospf6d",)
@ -401,7 +417,7 @@ class Ospfv3(QuaggaService):
rtrid = cls.routerid(node)
cfg += " router-id %s\n" % rtrid
for ifc in node.netifs():
if hasattr(ifc, 'control') and ifc.control is True:
if hasattr(ifc, "control") and ifc.control is True:
continue
cfg += " interface %s area 0.0.0.0\n" % ifc.name
cfg += "!\n"
@ -432,6 +448,7 @@ class Ospfv3mdr(Ospfv3):
configuration file but has hooks for adding to the
unified Quagga.conf file.
"""
name = "OSPFv3MDR"
ipv4_routing = True
@ -440,8 +457,12 @@ class Ospfv3mdr(Ospfv3):
cfg = cls.mtucheck(ifc)
# Uncomment the following line to use Address Family Translation for IPv4
cfg += " ipv6 ospf6 instance-id 65\n"
if ifc.net is not None and nodeutils.is_node(ifc.net, (NodeTypes.WIRELESS_LAN, NodeTypes.EMANE)):
return cfg + """\
if ifc.net is not None and nodeutils.is_node(
ifc.net, (NodeTypes.WIRELESS_LAN, NodeTypes.EMANE)
):
return (
cfg
+ """\
ipv6 ospf6 hello-interval 2
ipv6 ospf6 dead-interval 6
ipv6 ospf6 retransmit-interval 5
@ -450,6 +471,7 @@ class Ospfv3mdr(Ospfv3):
ipv6 ospf6 adjacencyconnectivity uniconnected
ipv6 ospf6 lsafullness mincostlsa
"""
)
else:
return cfg
@ -460,6 +482,7 @@ class Bgp(QuaggaService):
Peers must be manually configured, with a full mesh for those
having the same AS number.
"""
name = "BGP"
startup = ()
shutdown = ("killall bgpd",)
@ -485,6 +508,7 @@ class Rip(QuaggaService):
"""
The RIP service provides IPv4 routing for wired networks.
"""
name = "RIP"
startup = ()
shutdown = ("killall ripd",)
@ -508,6 +532,7 @@ class Ripng(QuaggaService):
"""
The RIP NG service provides IPv6 routing for wired networks.
"""
name = "RIPNG"
startup = ()
shutdown = ("killall ripngd",)
@ -532,6 +557,7 @@ class Babel(QuaggaService):
The Babel service provides a loop-avoiding distance-vector routing
protocol for IPv6 and IPv4 with fast convergence properties.
"""
name = "Babel"
startup = ()
shutdown = ("killall babeld",)
@ -560,28 +586,29 @@ class Xpimd(QuaggaService):
"""
PIM multicast routing based on XORP.
"""
name = 'Xpimd'
name = "Xpimd"
startup = ()
shutdown = ('killall xpimd',)
validate = ('pidof xpimd',)
shutdown = ("killall xpimd",)
validate = ("pidof xpimd",)
ipv4_routing = True
@classmethod
def generatequaggaconfig(cls, node):
ifname = 'eth0'
ifname = "eth0"
for ifc in node.netifs():
if ifc.name != 'lo':
if ifc.name != "lo":
ifname = ifc.name
break
cfg = 'router mfea\n!\n'
cfg += 'router igmp\n!\n'
cfg += 'router pim\n'
cfg += ' !ip pim rp-address 10.0.0.1\n'
cfg += ' ip pim bsr-candidate %s\n' % ifname
cfg += ' ip pim rp-candidate %s\n' % ifname
cfg += ' !ip pim spt-threshold interval 10 bytes 80000\n'
cfg = "router mfea\n!\n"
cfg += "router igmp\n!\n"
cfg += "router pim\n"
cfg += " !ip pim rp-address 10.0.0.1\n"
cfg += " ip pim bsr-candidate %s\n" % ifname
cfg += " ip pim rp-candidate %s\n" % ifname
cfg += " !ip pim spt-threshold interval 10 bytes 80000\n"
return cfg
@classmethod
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.
"""
group = "SDN"
@classmethod
@ -23,9 +24,9 @@ class OvsService(SdnService):
executables = ("ovs-ofctl", "ovs-vsctl")
group = "SDN"
dirs = ("/etc/openvswitch", "/var/run/openvswitch", "/var/log/openvswitch")
configs = ('OvsService.sh',)
startup = ('sh OvsService.sh',)
shutdown = ('killall ovs-vswitchd', 'killall ovsdb-server')
configs = ("OvsService.sh",)
startup = ("sh OvsService.sh",)
shutdown = ("killall ovs-vswitchd", "killall ovsdb-server")
@classmethod
def generate_config(cls, node, filename):
@ -38,11 +39,11 @@ class OvsService(SdnService):
cfg = "#!/bin/sh\n"
cfg += "# auto-generated by OvsService (OvsService.py)\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"
for ifc in node.netifs():
if hasattr(ifc, 'control') and ifc.control is True:
if hasattr(ifc, "control") and ifc.control is True:
continue
ifnumstr = re.findall(r"\d+", ifc.name)
ifnum = ifnumstr[0]
@ -76,10 +77,16 @@ class OvsService(SdnService):
# Setup default flows
portnum = 1
for ifc in node.netifs():
if hasattr(ifc, 'control') and ifc.control is True:
if hasattr(ifc, "control") and ifc.control is True:
continue
cfg += "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)
cfg += (
"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
return cfg
@ -90,9 +97,9 @@ class RyuService(SdnService):
executables = ("ryu-manager",)
group = "SDN"
dirs = ()
configs = ('ryuService.sh',)
startup = ('sh ryuService.sh',)
shutdown = ('killall ryu-manager',)
configs = ("ryuService.sh",)
startup = ("sh ryuService.sh",)
shutdown = ("killall ryu-manager",)
@classmethod
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
GUI for user customization.
"""
app_path = "/usr/local/lib/python2.7/dist-packages/ryu/app"
cfg = "#!/bin/sh\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

View file

@ -30,7 +30,9 @@ class VPNClient(CoreService):
try:
cfg += open(fname, "rb").read()
except IOError:
logging.exception("Error opening VPN client configuration template (%s)", fname)
logging.exception(
"Error opening VPN client configuration template (%s)", fname
)
return cfg
@ -57,7 +59,9 @@ class VPNServer(CoreService):
try:
cfg += open(fname, "rb").read()
except IOError:
logging.exception("Error opening VPN server configuration template (%s)", fname)
logging.exception(
"Error opening VPN server configuration template (%s)", fname
)
return cfg
@ -108,7 +112,9 @@ class Firewall(CoreService):
try:
cfg += open(fname, "rb").read()
except IOError:
logging.exception("Error opening Firewall configuration template (%s)", fname)
logging.exception(
"Error opening Firewall configuration template (%s)", fname
)
return cfg
@ -117,10 +123,11 @@ class Nat(CoreService):
"""
IPv4 source NAT service.
"""
name = "NAT"
executables = ("iptables",)
group = "Security"
configs = ("nat.sh", )
configs = ("nat.sh",)
startup = ("sh nat.sh",)
custom_needed = False
@ -130,7 +137,7 @@ class Nat(CoreService):
Generate a NAT line for one interface.
"""
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 += " -m state --state RELATED,ESTABLISHED -j ACCEPT\n"
@ -149,7 +156,7 @@ class Nat(CoreService):
cfg += "# NAT out the first interface by default\n"
have_nat = False
for ifc in node.netifs():
if hasattr(ifc, 'control') and ifc.control == True:
if hasattr(ifc, "control") and ifc.control is True:
continue
if have_nat:
cfg += cls.generateifcnatrule(ifc, line_prefix="#")
@ -159,4 +166,3 @@ class Nat(CoreService):
cfg += cls.generateifcnatrule(ifc)
cfg += "\n"
return cfg

View file

@ -12,7 +12,11 @@ class Ucarp(CoreService):
group = "Utility"
dirs = (UCARP_ETC,)
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",)
shutdown = ("killall ucarp",)
validate = ("pidof ucarp",)
@ -39,7 +43,7 @@ class Ucarp(CoreService):
Returns configuration file text.
"""
try:
ucarp_bin = node.session.cfg['ucarp_bin']
ucarp_bin = node.session.cfg["ucarp_bin"]
except KeyError:
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_EXEC} -B ${UCARP_OPTS}
""" % (ucarp_bin, UCARP_ETC)
""" % (
ucarp_bin,
UCARP_ETC,
)
@classmethod
def generateUcarpBoot(cls, node):
"""
Generate a shell script used to boot the Ucarp daemons.
"""
return """\
return (
"""\
#!/bin/sh
# Location of the UCARP config directory
UCARP_CFGDIR=%s
@ -117,7 +125,9 @@ chmod a+x ${UCARP_CFGDIR}/*.sh
# Start the default ucarp daemon configuration
${UCARP_CFGDIR}/default.sh
""" % UCARP_ETC
"""
% UCARP_ETC
)
@classmethod
def generateVipUp(cls, node):
@ -133,7 +143,7 @@ exec 2> /dev/null
IP="${2}"
NET="${3}"
if [ -z "$NET" ]; then
NET="24"
NET="24"
fi
/sbin/ip addr add ${IP}/${NET} dev "$1"
@ -155,7 +165,7 @@ exec 2> /dev/null
IP="${2}"
NET="${3}"
if [ -z "$NET" ]; then
NET="24"
NET="24"
fi
/sbin/ip addr del ${IP}/${NET} dev "$1"

View file

@ -4,10 +4,8 @@ utility.py: defines miscellaneous utility services.
import os
from core import CoreCommandError, utils
from core import constants
from core.nodes.ipaddress import Ipv4Prefix
from core.nodes.ipaddress import Ipv6Prefix
from core import CoreCommandError, constants, utils
from core.nodes.ipaddress import Ipv4Prefix, Ipv6Prefix
from core.services.coreservices import CoreService
@ -15,6 +13,7 @@ class UtilService(CoreService):
"""
Parent class for utility services.
"""
name = None
group = "Utility"
dirs = ()
@ -52,12 +51,19 @@ class IPForwardService(UtilService):
%(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.default.rp_filter=0
""" % {'sysctl': constants.SYSCTL_BIN}
""" % {
"sysctl": constants.SYSCTL_BIN
}
for ifc in node.netifs():
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.send_redirects=0\n" % \
(constants.SYSCTL_BIN, name)
cfg += "%s -w net.ipv4.conf.%s.forwarding=1\n" % (
constants.SYSCTL_BIN,
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)
return cfg
@ -72,7 +78,7 @@ class DefaultRouteService(UtilService):
cfg = "#!/bin/sh\n"
cfg += "# auto-generated by DefaultRoute service (utility.py)\n"
for ifc in node.netifs():
if hasattr(ifc, 'control') and ifc.control is True:
if hasattr(ifc, "control") and ifc.control is True:
continue
cfg += "\n".join(map(cls.addrstr, ifc.addrlist))
cfg += "\n"
@ -107,7 +113,7 @@ class DefaultMulticastRouteService(UtilService):
cfg += "as needed\n"
for ifc in node.netifs():
if hasattr(ifc, 'control') and ifc.control is True:
if hasattr(ifc, "control") and ifc.control is True:
continue
if os.uname()[0] == "Linux":
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 += "# Below are samples that you can uncomment and edit.\n#\n"
for ifc in node.netifs():
if hasattr(ifc, 'control') and ifc.control is True:
if hasattr(ifc, "control") and ifc.control is True:
continue
cfg += "\n".join(map(cls.routestr, ifc.addrlist))
cfg += "\n"
@ -158,8 +164,8 @@ class StaticRouteService(UtilService):
class SshService(UtilService):
name = "SSH"
configs = ("startsshd.sh", "/etc/ssh/sshd_config",)
dirs = ("/etc/ssh", "/var/run/sshd",)
configs = ("startsshd.sh", "/etc/ssh/sshd_config")
dirs = ("/etc/ssh", "/var/run/sshd")
startup = ("sh startsshd.sh",)
shutdown = ("killall sshd",)
validate = ()
@ -181,7 +187,11 @@ ssh-keygen -q -t rsa -N "" -f %s/ssh_host_rsa_key
chmod 655 %s
# wait until RSA host key has been generated to launch sshd
/usr/sbin/sshd -f %s/sshd_config
""" % (sshcfgdir, sshstatedir, sshcfgdir)
""" % (
sshcfgdir,
sshstatedir,
sshcfgdir,
)
else:
return """\
# auto-generated by SSH service (utility.py)
@ -221,14 +231,18 @@ AcceptEnv LANG LC_*
Subsystem sftp %s/sftp-server
UsePAM yes
UseDNS no
""" % (sshcfgdir, sshstatedir, sshlibdir)
""" % (
sshcfgdir,
sshstatedir,
sshlibdir,
)
class DhcpService(UtilService):
name = "DHCP"
configs = ("/etc/dhcp/dhcpd.conf",)
dirs = ("/etc/dhcp","/var/lib/dhcp")
startup = ("touch /var/lib/dhcp/dhcpd.leases","dhcpd")
dirs = ("/etc/dhcp", "/var/lib/dhcp")
startup = ("touch /var/lib/dhcp/dhcpd.leases", "dhcpd")
shutdown = ("killall dhcpd",)
validate = ("pidof dhcpd",)
@ -253,7 +267,7 @@ max-lease-time 7200;
ddns-update-style none;
"""
for ifc in node.netifs():
if hasattr(ifc, 'control') and ifc.control is True:
if hasattr(ifc, "control") and ifc.control is True:
continue
cfg += "\n".join(map(cls.subnetentry, ifc.addrlist))
cfg += "\n"
@ -281,13 +295,20 @@ subnet %s netmask %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):
"""
Use a DHCP client for all interfaces for addressing.
"""
name = "DHCPClient"
configs = ("startdhcpclient.sh",)
startup = ("sh startdhcpclient.sh",)
@ -306,7 +327,7 @@ class DhcpClientService(UtilService):
cfg += "#mkdir -p /var/run/resolvconf/interface\n"
for ifc in node.netifs():
if hasattr(ifc, 'control') and ifc.control is True:
if hasattr(ifc, "control") and ifc.control is True:
continue
cfg += "#ln -s /var/run/resolvconf/interface/%s.dhclient" % ifc.name
cfg += " /var/run/resolvconf/resolv.conf\n"
@ -319,9 +340,10 @@ class FtpService(UtilService):
"""
Start a vsftpd server.
"""
name = "FTP"
configs = ("vsftpd.conf",)
dirs = ("/var/run/vsftpd/empty", "/var/ftp",)
dirs = ("/var/run/vsftpd/empty", "/var/ftp")
startup = ("vsftpd ./vsftpd.conf",)
shutdown = ("killall vsftpd",)
validate = ("pidof vsftpd",)
@ -351,12 +373,22 @@ class HttpService(UtilService):
"""
Start an apache server.
"""
name = "HTTP"
configs = ("/etc/apache2/apache2.conf", "/etc/apache2/envvars",
"/var/www/index.html",)
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",)
configs = (
"/etc/apache2/apache2.conf",
"/etc/apache2/envvars",
"/var/www/index.html",
)
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",)
validate = ("pidof apache2",)
@ -382,38 +414,40 @@ class HttpService(UtilService):
Detect the apache2 version using the 'a2query' command.
"""
try:
status, result = utils.cmd_output(['a2query', '-v'])
status, result = utils.cmd_output(["a2query", "-v"])
except CoreCommandError:
status = -1
if status == 0 and result[:3] == '2.4':
if status == 0 and result[:3] == "2.4":
return cls.APACHEVER24
return cls.APACHEVER22
@classmethod
def generateapache2conf(cls, node, filename):
lockstr = {cls.APACHEVER22:
'LockFile ${APACHE_LOCK_DIR}/accept.lock\n',
cls.APACHEVER24:
'Mutex file:${APACHE_LOCK_DIR} default\n', }
mpmstr = {cls.APACHEVER22: '', cls.APACHEVER24:
'LoadModule mpm_worker_module /usr/lib/apache2/modules/mod_mpm_worker.so\n', }
lockstr = {
cls.APACHEVER22: "LockFile ${APACHE_LOCK_DIR}/accept.lock\n",
cls.APACHEVER24: "Mutex file:${APACHE_LOCK_DIR} default\n",
}
mpmstr = {
cls.APACHEVER22: "",
cls.APACHEVER24: "LoadModule mpm_worker_module /usr/lib/apache2/modules/mod_mpm_worker.so\n",
}
permstr = {cls.APACHEVER22:
' Order allow,deny\n Deny from all\n Satisfy all\n',
cls.APACHEVER24:
' Require all denied\n', }
permstr = {
cls.APACHEVER22: " Order allow,deny\n Deny from all\n Satisfy all\n",
cls.APACHEVER24: " Require all denied\n",
}
authstr = {cls.APACHEVER22:
'LoadModule authz_default_module /usr/lib/apache2/modules/mod_authz_default.so\n',
cls.APACHEVER24:
'LoadModule authz_core_module /usr/lib/apache2/modules/mod_authz_core.so\n', }
authstr = {
cls.APACHEVER22: "LoadModule authz_default_module /usr/lib/apache2/modules/mod_authz_default.so\n",
cls.APACHEVER24: "LoadModule authz_core_module /usr/lib/apache2/modules/mod_authz_core.so\n",
}
permstr2 = {cls.APACHEVER22:
'\t\tOrder allow,deny\n\t\tallow from all\n',
cls.APACHEVER24:
'\t\tRequire all granted\n', }
permstr2 = {
cls.APACHEVER22: "\t\tOrder allow,deny\n\t\tallow from all\n",
cls.APACHEVER24: "\t\tRequire all granted\n",
}
version = cls.detectversionfromcmd()
cfg = "# apache2.conf generated by utility.py:HttpService\n"
@ -461,7 +495,7 @@ Group ${APACHE_RUN_GROUP}
AccessFileName .htaccess
<Files ~ "^\.ht">
<Files ~ "^\\.ht">
"""
cfg += permstr[version]
cfg += """\
@ -508,22 +542,22 @@ ServerSignature On
TraceEnable Off
<VirtualHost *:80>
ServerAdmin webmaster@localhost
DocumentRoot /var/www
<Directory />
Options FollowSymLinks
AllowOverride None
</Directory>
<Directory /var/www/>
Options Indexes FollowSymLinks MultiViews
AllowOverride None
ServerAdmin webmaster@localhost
DocumentRoot /var/www
<Directory />
Options FollowSymLinks
AllowOverride None
</Directory>
<Directory /var/www/>
Options Indexes FollowSymLinks MultiViews
AllowOverride None
"""
cfg += permstr2[version]
cfg += """\
</Directory>
ErrorLog ${APACHE_LOG_DIR}/error.log
LogLevel warn
CustomLog ${APACHE_LOG_DIR}/access.log combined
</Directory>
ErrorLog ${APACHE_LOG_DIR}/error.log
LogLevel warn
CustomLog ${APACHE_LOG_DIR}/access.log combined
</VirtualHost>
"""
@ -546,14 +580,17 @@ export LANG
@classmethod
def generatehtml(cls, node, filename):
body = """\
body = (
"""\
<!-- generated by utility.py:HttpService -->
<h1>%s web server</h1>
<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>
""" % node.name
"""
% node.name
)
for ifc in node.netifs():
if hasattr(ifc, 'control') and ifc.control is True:
if hasattr(ifc, "control") and ifc.control is True:
continue
body += "<li>%s - %s</li>\n" % (ifc.name, ifc.addrlist)
return "<html><body>%s</body></html>" % body
@ -563,6 +600,7 @@ class PcapService(UtilService):
"""
Pcap service for logging packets.
"""
name = "pcap"
configs = ("pcap.sh",)
dirs = ()
@ -586,11 +624,15 @@ if [ "x$1" = "xstart" ]; then
"""
for ifc in node.netifs():
if hasattr(ifc, 'control') and ifc.control is True:
cfg += '# '
if hasattr(ifc, "control") and ifc.control is True:
cfg += "# "
redir = "< /dev/null"
cfg += "tcpdump ${DUMPOPTS} -w %s.%s.pcap -i %s %s &\n" % \
(node.name, ifc.name, ifc.name, redir)
cfg += "tcpdump ${DUMPOPTS} -w %s.%s.pcap -i %s %s &\n" % (
node.name,
ifc.name,
ifc.name,
redir,
)
cfg += """
elif [ "x$1" = "xstop" ]; then
@ -617,12 +659,13 @@ class RadvdService(UtilService):
"""
cfg = "# auto-generated by RADVD service (utility.py)\n"
for ifc in node.netifs():
if hasattr(ifc, 'control') and ifc.control is True:
if hasattr(ifc, "control") and ifc.control is True:
continue
prefixes = map(cls.subnetentry, ifc.addrlist)
if len(prefixes) < 1:
continue
cfg += """\
cfg += (
"""\
interface %s
{
AdvSendAdvert on;
@ -630,18 +673,23 @@ interface %s
MaxRtrAdvInterval 10;
AdvDefaultPreference low;
AdvHomeAgentFlag off;
""" % ifc.name
"""
% ifc.name
)
for prefix in prefixes:
if prefix == "":
continue
cfg += """\
cfg += (
"""\
prefix %s
{
AdvOnLink on;
AdvAutonomous on;
AdvRouterAddr on;
};
""" % prefix
"""
% prefix
)
cfg += "};\n"
return cfg
@ -662,6 +710,7 @@ class AtdService(UtilService):
"""
Atd service for scheduling at jobs
"""
name = "atd"
configs = ("startatd.sh",)
dirs = ("/var/spool/cron/atjobs", "/var/spool/cron/atspool")
@ -683,5 +732,6 @@ class UserDefinedService(UtilService):
"""
Dummy service allowing customization of anything.
"""
name = "UserDefined"
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
enabled XORP services, and launches necessary daemons upon startup.
"""
name = "xorp_rtrmgr"
executables = ("xorp_rtrmgr",)
group = "XORP"
dirs = ("/etc/xorp",)
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",)
validate = ("pidof xorp_rtrmgr",)
@ -74,6 +78,7 @@ class XorpService(CoreService):
Parent class for XORP services. Defines properties and methods
common to XORP's routing daemons.
"""
name = None
executables = ("xorp_rtrmgr",)
group = "XORP"
@ -103,7 +108,7 @@ class XorpService(CoreService):
"""
names = []
for ifc in ifcs:
if hasattr(ifc, 'control') and ifc.control is True:
if hasattr(ifc, "control") and ifc.control is True:
continue
names.append(ifc.name)
names.append("register_vif")
@ -129,7 +134,7 @@ class XorpService(CoreService):
cfg += " policy-statement export-connected {\n"
cfg += "\tterm 100 {\n"
cfg += "\t from {\n"
cfg += "\t\tprotocol: \"connected\"\n"
cfg += '\t\tprotocol: "connected"\n'
cfg += "\t }\n"
cfg += "\t}\n"
cfg += " }\n"
@ -142,11 +147,11 @@ class XorpService(CoreService):
Helper to return the first IPv4 address of a node as its router ID.
"""
for ifc in node.netifs():
if hasattr(ifc, 'control') and ifc.control is True:
if hasattr(ifc, "control") and ifc.control is True:
continue
for a in ifc.addrlist:
if a.find(".") >= 0:
return a.split('/')[0]
return a.split("/")[0]
# raise ValueError, "no IPv4 address found for router ID"
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
unified XORP configuration file.
"""
name = "XORP_OSPFv2"
@classmethod
@ -176,7 +182,7 @@ class XorpOspfv2(XorpService):
cfg += "\trouter-id: %s\n" % rtrid
cfg += "\tarea 0.0.0.0 {\n"
for ifc in node.netifs():
if hasattr(ifc, 'control') and ifc.control is True:
if hasattr(ifc, "control") and ifc.control is True:
continue
cfg += "\t interface %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
unified XORP configuration file.
"""
name = "XORP_OSPFv3"
@classmethod
@ -211,7 +218,7 @@ class XorpOspfv3(XorpService):
cfg += "\trouter-id: %s\n" % rtrid
cfg += "\tarea 0.0.0.0 {\n"
for ifc in node.netifs():
if hasattr(ifc, 'control') and ifc.control is True:
if hasattr(ifc, "control") and ifc.control is True:
continue
cfg += "\t interface %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.
"""
name = "XORP_BGP"
custom_needed = True
@ -241,7 +249,7 @@ class XorpBgp(XorpService):
cfg += " bgp {\n"
cfg += "\tbgp-id: %s\n" % rtrid
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 += "\t local-ip: 10.0.1.1\n"
cfg += "\t as: 65002\n"
@ -265,9 +273,9 @@ class XorpRip(XorpService):
cfg += cls.policyexportconnected()
cfg += "\nprotocols {\n"
cfg += " rip {\n"
cfg += "\texport: \"export-connected\"\n"
cfg += '\texport: "export-connected"\n'
for ifc in node.netifs():
if hasattr(ifc, 'control') and ifc.control is True:
if hasattr(ifc, "control") and ifc.control is True:
continue
cfg += "\tinterface %s {\n" % ifc.name
cfg += "\t vif %s {\n" % ifc.name
@ -289,6 +297,7 @@ class XorpRipng(XorpService):
"""
RIP NG IPv6 unicast routing.
"""
name = "XORP_RIPNG"
@classmethod
@ -297,9 +306,9 @@ class XorpRipng(XorpService):
cfg += cls.policyexportconnected()
cfg += "\nprotocols {\n"
cfg += " ripng {\n"
cfg += "\texport: \"export-connected\"\n"
cfg += '\texport: "export-connected"\n'
for ifc in node.netifs():
if hasattr(ifc, 'control') and ifc.control is True:
if hasattr(ifc, "control") and ifc.control is True:
continue
cfg += "\tinterface %s {\n" % ifc.name
cfg += "\t vif %s {\n" % ifc.name
@ -324,6 +333,7 @@ class XorpPimSm4(XorpService):
"""
PIM Sparse Mode IPv4 multicast routing.
"""
name = "XORP_PIMSM4"
@classmethod
@ -334,7 +344,7 @@ class XorpPimSm4(XorpService):
cfg += " igmp {\n"
names = []
for ifc in node.netifs():
if hasattr(ifc, 'control') and ifc.control is True:
if hasattr(ifc, "control") and ifc.control is True:
continue
names.append(ifc.name)
cfg += "\tinterface %s {\n" % ifc.name
@ -358,12 +368,12 @@ class XorpPimSm4(XorpService):
cfg += "\tbootstrap {\n"
cfg += "\t cand-bsr {\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 }\n"
cfg += "\t cand-rp {\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 }\n"
cfg += "\t}\n"
@ -383,6 +393,7 @@ class XorpPimSm6(XorpService):
"""
PIM Sparse Mode IPv6 multicast routing.
"""
name = "XORP_PIMSM6"
@classmethod
@ -393,7 +404,7 @@ class XorpPimSm6(XorpService):
cfg += " mld {\n"
names = []
for ifc in node.netifs():
if hasattr(ifc, 'control') and ifc.control is True:
if hasattr(ifc, "control") and ifc.control is True:
continue
names.append(ifc.name)
cfg += "\tinterface %s {\n" % ifc.name
@ -417,12 +428,12 @@ class XorpPimSm6(XorpService):
cfg += "\tbootstrap {\n"
cfg += "\t cand-bsr {\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 }\n"
cfg += "\t cand-rp {\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 }\n"
cfg += "\t}\n"
@ -442,6 +453,7 @@ class XorpOlsr(XorpService):
"""
OLSR IPv4 unicast MANET routing.
"""
name = "XORP_OLSR"
@classmethod
@ -452,7 +464,7 @@ class XorpOlsr(XorpService):
cfg += " olsr4 {\n"
cfg += "\tmain-address: %s\n" % rtrid
for ifc in node.netifs():
if hasattr(ifc, 'control') and ifc.control is True:
if hasattr(ifc, "control") and ifc.control is True:
continue
cfg += "\tinterface %s {\n" % ifc.name
cfg += "\t vif %s {\n" % ifc.name

View file

@ -11,6 +11,7 @@ import os
import shlex
import subprocess
import sys
from past.builtins import basestring
from core import CoreCommandError
@ -30,10 +31,7 @@ def execute_file(path, exec_globals=None, exec_locals=None):
"""
if exec_globals is None:
exec_globals = {}
exec_globals.update({
"__file__": path,
"__name__": "__main__"
})
exec_globals.update({"__file__": path, "__name__": "__main__"})
with open(path, "rb") as f:
data = compile(f.read(), path, "exec")
exec(data, exec_globals, exec_locals)
@ -157,7 +155,7 @@ def make_tuple(obj):
if hasattr(obj, "__iter__"):
return tuple(obj)
else:
return obj,
return (obj,)
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:
line = 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:
tmp.append("%x" % ord(line[-1]))
dump += "0x%08x: %s\n" % (count, " ".join(tmp))
@ -437,7 +438,9 @@ def load_classes(path, clazz):
for member in members:
valid_class = member[1]
classes.append(valid_class)
except:
logging.exception("unexpected error during import, skipping: %s", import_statement)
except Exception:
logging.exception(
"unexpected error during import, skipping: %s", import_statement
)
return classes

View file

@ -1,19 +1,24 @@
import logging
from core.nodes.base import CoreNetworkBase
from lxml import etree
import core.nodes.base
import core.nodes.physical
from core.emulator.emudata import InterfaceData
from core.emulator.emudata import LinkOptions
from core.emulator.emudata import NodeOptions
from core.emulator.emudata import InterfaceData, LinkOptions, NodeOptions
from core.emulator.enumerations import NodeTypes
from core.nodes import nodeutils
from core.nodes.base import CoreNetworkBase
from core.nodes.ipaddress import MacAddress
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:
xml_file.write(xml_data)
@ -252,7 +257,9 @@ class CoreXmlWriter(object):
# write out generated xml
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):
# origin: geolocation of cartesian coordinate 0,0,0
@ -327,12 +334,18 @@ class CoreXmlWriter(object):
for model_name in all_configs:
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:
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:
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)
if emane_configurations.getchildren():
@ -347,8 +360,12 @@ class CoreXmlWriter(object):
for model_name in all_configs:
config = all_configs[model_name]
logging.info("writing mobility config node(%s) model(%s)", node_id, model_name)
mobility_configuration = etree.SubElement(mobility_configurations, "mobility_configuration")
logging.debug(
"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, "model", model_name)
for name in config:
@ -388,15 +405,15 @@ class CoreXmlWriter(object):
for node_id in self.session.nodes:
node = self.session.nodes[node_id]
# 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)
if is_network_or_rj45 and not is_controlnet:
self.write_network(node)
# device node
elif isinstance(node, core.nodes.base.CoreNodeBase):
self.write_device(node)
else:
logging.error("unknown node: %s", node)
# add known links
links.extend(node.all_link_data(0))
@ -406,7 +423,6 @@ class CoreXmlWriter(object):
def write_network(self, node):
# ignore p2p and other nodes that are not part of the api
if not node.apitype:
logging.warning("ignoring node with no apitype: %s", node)
return
network = NetworkElement(self.session, node)
@ -430,7 +446,9 @@ class CoreXmlWriter(object):
device = DeviceElement(self.session, node)
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)
node = self.session.get_node(node_id)
interface_name = None
@ -468,7 +486,7 @@ class CoreXmlWriter(object):
link_data.interface1_ip4,
link_data.interface1_ip4_mask,
link_data.interface1_ip6,
link_data.interface1_ip6_mask
link_data.interface1_ip6_mask,
)
link_element.append(interface_one)
@ -482,7 +500,7 @@ class CoreXmlWriter(object):
link_data.interface2_ip4,
link_data.interface2_ip4_mask,
link_data.interface2_ip6,
link_data.interface2_ip6_mask
link_data.interface2_ip6_mask,
)
link_element.append(interface_two)
@ -541,7 +559,9 @@ class CoreXmlReader(object):
services = []
for service in node.iterchildren():
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
def read_session_metadata(self):
@ -581,7 +601,9 @@ class CoreXmlReader(object):
data = hook.text
hook_type = "hook:%s" % state
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):
session_origin = self.scenario.find("session_origin")
@ -615,7 +637,9 @@ class CoreXmlReader(object):
for service_configuration in service_configurations.iterchildren():
node_id = get_int(service_configuration, "node")
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)
service = self.session.services.get_service(node_id, service_name)
@ -629,11 +653,15 @@ class CoreXmlReader(object):
validate_elements = service_configuration.find("validates")
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")
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")
if file_elements is not None:
@ -670,7 +698,9 @@ class CoreXmlReader(object):
value = config.get("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)
def read_mobility_configs(self):
@ -688,7 +718,9 @@ class CoreXmlReader(object):
value = config.get("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)
def read_nodes(self):
@ -710,7 +742,9 @@ class CoreXmlReader(object):
service_elements = device_element.find("services")
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")
if position_element is not None:
@ -747,7 +781,9 @@ class CoreXmlReader(object):
if all([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)
def read_links(self):
@ -791,10 +827,24 @@ class CoreXmlReader(object):
link_options.gui_attributes = options_element.get("gui_attributes")
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)
self.session.update_link(node_one, node_two, interface_one.id, interface_two.id, link_options)
logging.info(
"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:
logging.info("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)
logging.info(
"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)

View file

@ -4,9 +4,9 @@ import socket
from lxml import etree
from core import constants, utils
from core.nodes.base import CoreNodeBase
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):
@ -31,16 +31,22 @@ def add_emane_interface(host_element, netif, platform_name="p1", transport_name=
# platform data
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_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_name = "nem%s" % nem_id
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.text = str(nem_id)
@ -81,7 +87,9 @@ class CoreXmlDeployment(object):
def __init__(self, session, scenario):
self.session = session
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()
def find_device(self, name):
@ -89,8 +97,10 @@ class CoreXmlDeployment(object):
return device
def find_interface(self, device, name):
interface = self.scenario.find("devices/device[@name='%s']/interfaces/interface[@name='%s']" % (
device.name, name))
interface = self.scenario.find(
"devices/device[@name='%s']/interfaces/interface[@name='%s']"
% (device.name, name)
)
return interface
def add_deployment(self):
@ -125,7 +135,9 @@ class CoreXmlDeployment(object):
# create virtual host element
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_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
: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)
@ -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
: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 = {}
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
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
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
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)
transport_type = "raw"
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_param(transport_element, "device", netif.name)
@ -261,7 +275,7 @@ def build_transport_xml(emane_manager, node, transport_type):
transport_element = etree.Element(
"transport",
name="%s Transport" % transport_type.capitalize(),
library="trans%s" % transport_type.lower()
library="trans%s" % transport_type.lower(),
)
# add bitrate
@ -299,7 +313,9 @@ def create_phy_xml(emane_model, config, file_path):
if 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)
@ -315,12 +331,18 @@ def create_mac_xml(emane_model, config, file_path):
if not emane_model.mac_library:
raise ValueError("must define emane model library")
mac_element = etree.Element("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)
mac_element = etree.Element(
"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)
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.
@ -353,7 +375,13 @@ def create_event_service_xml(group, port, device, file_directory):
:return: nothing
"""
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.text = value
file_name = "libemaneeventservice.xml"

View file

@ -3,9 +3,9 @@
# Example CORE Python script that attaches N nodes to an EMANE 802.11abg network.
import datetime
import parser
from builtins import range
import parser
from core import load_logging_config
from core.emane.ieee80211abg import EmaneIeee80211abgModel
from core.emulator.coreemu import CoreEmu
@ -28,8 +28,7 @@ def example(options):
# create emane network node
emane_network = session.create_emane_network(
model=EmaneIeee80211abgModel,
geo_reference=(47.57917, -122.13232, 2.00000)
model=EmaneIeee80211abgModel, geo_reference=(47.57917, -122.13232, 2.00000)
)
emane_network.setposition(x=80, y=50)
@ -48,14 +47,17 @@ def example(options):
node.client.term("bash")
# shutdown session
raw_input("press enter to exit...")
input("press enter to exit...")
coreemu.shutdown()
def main():
options = parser.parse_options("emane80211")
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)
print("elapsed time: %s" % (datetime.datetime.now() - start))

View file

@ -7,10 +7,20 @@ DEFAULT_STEP = 1
def parse_options(name):
parser = argparse.ArgumentParser(description="Run %s example" % name)
parser.add_argument("-n", "--nodes", 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")
parser.add_argument(
"-n",
"--nodes",
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()

View file

@ -6,13 +6,13 @@
# nodestep
import datetime
import parser
from builtins import range
import parser
from core import load_logging_config
from core.emulator.coreemu import CoreEmu
from core.emulator.emudata import IpPrefixes
from core.emulator.enumerations import NodeTypes, EventTypes
from core.emulator.enumerations import EventTypes, NodeTypes
load_logging_config()

View file

@ -8,7 +8,7 @@ from builtins import range
from core import load_logging_config
from core.emulator.emudata import IpPrefixes
from core.emulator.enumerations import NodeTypes, EventTypes
from core.emulator.enumerations import EventTypes, NodeTypes
load_logging_config()

View file

@ -6,13 +6,13 @@
# nodestep
import datetime
import parser
from builtins import range
import parser
from core import load_logging_config
from core.emulator.coreemu import CoreEmu
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
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)
# 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)
# create switch node
@ -47,7 +49,9 @@ def main():
logging.info("created link: %s", response)
# 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)

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