merged cleanup branch with master
This commit is contained in:
parent
0a91fe7a3e
commit
55a6e2dcef
81 changed files with 11596 additions and 15021 deletions
4
.gitignore
vendored
4
.gitignore
vendored
|
|
@ -16,3 +16,7 @@ configure
|
||||||
core-*.tar.gz
|
core-*.tar.gz
|
||||||
debian
|
debian
|
||||||
stamp-h1
|
stamp-h1
|
||||||
|
|
||||||
|
# intelli
|
||||||
|
*.iml
|
||||||
|
.idea
|
||||||
|
|
|
||||||
|
|
@ -38,6 +38,9 @@ To build this software you should use:
|
||||||
make
|
make
|
||||||
sudo make install
|
sudo make install
|
||||||
|
|
||||||
|
Note: You may need to pass the proxy settings to sudo make install:
|
||||||
|
sudo make install HTTP_PROXY=<proxy>
|
||||||
|
|
||||||
Here is what is installed with 'make install':
|
Here is what is installed with 'make install':
|
||||||
|
|
||||||
/usr/local/bin/core-gui
|
/usr/local/bin/core-gui
|
||||||
|
|
|
||||||
|
|
@ -1,23 +1,17 @@
|
||||||
# Copyright (c)2010-2012 the Boeing Company.
|
# Copyright (c)2010-2012 the Boeing Company.
|
||||||
# See the LICENSE file included in this distribution.
|
# See the LICENSE file included in this distribution.
|
||||||
|
|
||||||
"""core
|
"""
|
||||||
|
core
|
||||||
|
|
||||||
Top-level Python package containing CORE components.
|
Top-level Python package containing CORE components.
|
||||||
|
|
||||||
See http://www.nrl.navy.mil/itd/ncs/products/core and
|
See http://www.nrl.navy.mil/itd/ncs/products/core for more information on CORE.
|
||||||
http://code.google.com/p/coreemu/ for more information on CORE.
|
|
||||||
|
|
||||||
Pieces can be imported individually, for example
|
Pieces can be imported individually, for example
|
||||||
|
|
||||||
import core.netns.vnode
|
from core.netns import vnode
|
||||||
|
|
||||||
or everything listed in __all__ can be imported using
|
|
||||||
|
|
||||||
from core import *
|
|
||||||
"""
|
"""
|
||||||
|
|
||||||
__all__ = []
|
|
||||||
|
|
||||||
# Automatically import all add-ons listed in addons.__all__
|
# Automatically import all add-ons listed in addons.__all__
|
||||||
from addons import *
|
from addons import *
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
"""Optional add-ons
|
|
||||||
|
|
||||||
Add on files can be put in this directory. Everything listed in
|
|
||||||
__all__ is automatically loaded by the main core module.
|
|
||||||
"""
|
"""
|
||||||
|
Optional add ons can be put in this directory. Everything listed in __all__ is automatically
|
||||||
|
loaded by the main core module.
|
||||||
|
"""
|
||||||
|
|
||||||
__all__ = []
|
__all__ = []
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,3 @@
|
||||||
|
"""
|
||||||
|
Contains code specific to the legacy TCP API for interacting with the TCL based GUI.
|
||||||
|
"""
|
||||||
File diff suppressed because it is too large
Load diff
|
|
@ -1,333 +0,0 @@
|
||||||
#
|
|
||||||
# CORE
|
|
||||||
# Copyright (c)2010-2013 the Boeing Company.
|
|
||||||
# See the LICENSE file included in this distribution.
|
|
||||||
#
|
|
||||||
# author: Tom Goff <thomas.goff@boeing.com>
|
|
||||||
#
|
|
||||||
'''
|
|
||||||
data.py: constant definitions for the CORE API, enumerating the
|
|
||||||
different message and TLV types (these constants are also found in coreapi.h)
|
|
||||||
'''
|
|
||||||
|
|
||||||
def enumdict(d):
|
|
||||||
for k, v in d.iteritems():
|
|
||||||
exec "%s = %s" % (v, k) in globals()
|
|
||||||
|
|
||||||
# Constants
|
|
||||||
|
|
||||||
CORE_API_VER = "1.23"
|
|
||||||
CORE_API_PORT = 4038
|
|
||||||
|
|
||||||
# Message types
|
|
||||||
|
|
||||||
message_types = {
|
|
||||||
0x01: "CORE_API_NODE_MSG",
|
|
||||||
0x02: "CORE_API_LINK_MSG",
|
|
||||||
0x03: "CORE_API_EXEC_MSG",
|
|
||||||
0x04: "CORE_API_REG_MSG",
|
|
||||||
0x05: "CORE_API_CONF_MSG",
|
|
||||||
0x06: "CORE_API_FILE_MSG",
|
|
||||||
0x07: "CORE_API_IFACE_MSG",
|
|
||||||
0x08: "CORE_API_EVENT_MSG",
|
|
||||||
0x09: "CORE_API_SESS_MSG",
|
|
||||||
0x0A: "CORE_API_EXCP_MSG",
|
|
||||||
0x0B: "CORE_API_MSG_MAX",
|
|
||||||
}
|
|
||||||
|
|
||||||
enumdict(message_types)
|
|
||||||
|
|
||||||
# Generic Message Flags
|
|
||||||
|
|
||||||
message_flags = {
|
|
||||||
0x01: "CORE_API_ADD_FLAG",
|
|
||||||
0x02: "CORE_API_DEL_FLAG",
|
|
||||||
0x04: "CORE_API_CRI_FLAG",
|
|
||||||
0x08: "CORE_API_LOC_FLAG",
|
|
||||||
0x10: "CORE_API_STR_FLAG",
|
|
||||||
0x20: "CORE_API_TXT_FLAG",
|
|
||||||
0x40: "CORE_API_TTY_FLAG",
|
|
||||||
}
|
|
||||||
|
|
||||||
enumdict(message_flags)
|
|
||||||
|
|
||||||
# Node Message TLV Types
|
|
||||||
|
|
||||||
node_tlvs = {
|
|
||||||
0x01: "CORE_TLV_NODE_NUMBER",
|
|
||||||
0x02: "CORE_TLV_NODE_TYPE",
|
|
||||||
0x03: "CORE_TLV_NODE_NAME",
|
|
||||||
0x04: "CORE_TLV_NODE_IPADDR",
|
|
||||||
0x05: "CORE_TLV_NODE_MACADDR",
|
|
||||||
0x06: "CORE_TLV_NODE_IP6ADDR",
|
|
||||||
0x07: "CORE_TLV_NODE_MODEL",
|
|
||||||
0x08: "CORE_TLV_NODE_EMUSRV",
|
|
||||||
0x0A: "CORE_TLV_NODE_SESSION",
|
|
||||||
0x20: "CORE_TLV_NODE_XPOS",
|
|
||||||
0x21: "CORE_TLV_NODE_YPOS",
|
|
||||||
0x22: "CORE_TLV_NODE_CANVAS",
|
|
||||||
0x23: "CORE_TLV_NODE_EMUID",
|
|
||||||
0x24: "CORE_TLV_NODE_NETID",
|
|
||||||
0x25: "CORE_TLV_NODE_SERVICES",
|
|
||||||
0x30: "CORE_TLV_NODE_LAT",
|
|
||||||
0x31: "CORE_TLV_NODE_LONG",
|
|
||||||
0x32: "CORE_TLV_NODE_ALT",
|
|
||||||
0x42: "CORE_TLV_NODE_ICON",
|
|
||||||
0x50: "CORE_TLV_NODE_OPAQUE",
|
|
||||||
}
|
|
||||||
|
|
||||||
enumdict(node_tlvs)
|
|
||||||
|
|
||||||
node_types = dict(enumerate([
|
|
||||||
"CORE_NODE_DEF",
|
|
||||||
"CORE_NODE_PHYS",
|
|
||||||
"CORE_NODE_XEN",
|
|
||||||
"CORE_NODE_TBD",
|
|
||||||
"CORE_NODE_SWITCH",
|
|
||||||
"CORE_NODE_HUB",
|
|
||||||
"CORE_NODE_WLAN",
|
|
||||||
"CORE_NODE_RJ45",
|
|
||||||
"CORE_NODE_TUNNEL",
|
|
||||||
"CORE_NODE_KTUNNEL",
|
|
||||||
"CORE_NODE_EMANE",
|
|
||||||
]))
|
|
||||||
|
|
||||||
enumdict(node_types)
|
|
||||||
|
|
||||||
rj45_models = dict(enumerate([
|
|
||||||
"RJ45_MODEL_LINKED",
|
|
||||||
"RJ45_MODEL_WIRELESS",
|
|
||||||
"RJ45_MODEL_INSTALLED",
|
|
||||||
]))
|
|
||||||
|
|
||||||
enumdict(rj45_models)
|
|
||||||
|
|
||||||
# Link Message TLV Types
|
|
||||||
|
|
||||||
link_tlvs = {
|
|
||||||
0x01: "CORE_TLV_LINK_N1NUMBER",
|
|
||||||
0x02: "CORE_TLV_LINK_N2NUMBER",
|
|
||||||
0x03: "CORE_TLV_LINK_DELAY",
|
|
||||||
0x04: "CORE_TLV_LINK_BW",
|
|
||||||
0x05: "CORE_TLV_LINK_PER",
|
|
||||||
0x06: "CORE_TLV_LINK_DUP",
|
|
||||||
0x07: "CORE_TLV_LINK_JITTER",
|
|
||||||
0x08: "CORE_TLV_LINK_MER",
|
|
||||||
0x09: "CORE_TLV_LINK_BURST",
|
|
||||||
CORE_TLV_NODE_SESSION: "CORE_TLV_LINK_SESSION",
|
|
||||||
0x10: "CORE_TLV_LINK_MBURST",
|
|
||||||
0x20: "CORE_TLV_LINK_TYPE",
|
|
||||||
0x21: "CORE_TLV_LINK_GUIATTR",
|
|
||||||
0x22: "CORE_TLV_LINK_UNI",
|
|
||||||
0x23: "CORE_TLV_LINK_EMUID",
|
|
||||||
0x24: "CORE_TLV_LINK_NETID",
|
|
||||||
0x25: "CORE_TLV_LINK_KEY",
|
|
||||||
0x30: "CORE_TLV_LINK_IF1NUM",
|
|
||||||
0x31: "CORE_TLV_LINK_IF1IP4",
|
|
||||||
0x32: "CORE_TLV_LINK_IF1IP4MASK",
|
|
||||||
0x33: "CORE_TLV_LINK_IF1MAC",
|
|
||||||
0x34: "CORE_TLV_LINK_IF1IP6",
|
|
||||||
0x35: "CORE_TLV_LINK_IF1IP6MASK",
|
|
||||||
0x36: "CORE_TLV_LINK_IF2NUM",
|
|
||||||
0x37: "CORE_TLV_LINK_IF2IP4",
|
|
||||||
0x38: "CORE_TLV_LINK_IF2IP4MASK",
|
|
||||||
0x39: "CORE_TLV_LINK_IF2MAC",
|
|
||||||
0x40: "CORE_TLV_LINK_IF2IP6",
|
|
||||||
0x41: "CORE_TLV_LINK_IF2IP6MASK",
|
|
||||||
0x42: "CORE_TLV_LINK_IF1NAME",
|
|
||||||
0x43: "CORE_TLV_LINK_IF2NAME",
|
|
||||||
0x50: "CORE_TLV_LINK_OPAQUE",
|
|
||||||
}
|
|
||||||
|
|
||||||
enumdict(link_tlvs)
|
|
||||||
|
|
||||||
link_types = dict(enumerate([
|
|
||||||
"CORE_LINK_WIRELESS",
|
|
||||||
"CORE_LINK_WIRED",
|
|
||||||
]))
|
|
||||||
|
|
||||||
enumdict(link_types)
|
|
||||||
|
|
||||||
# Execute Message TLV Types
|
|
||||||
|
|
||||||
exec_tlvs = {
|
|
||||||
0x01: "CORE_TLV_EXEC_NODE",
|
|
||||||
0x02: "CORE_TLV_EXEC_NUM",
|
|
||||||
0x03: "CORE_TLV_EXEC_TIME",
|
|
||||||
0x04: "CORE_TLV_EXEC_CMD",
|
|
||||||
0x05: "CORE_TLV_EXEC_RESULT",
|
|
||||||
0x06: "CORE_TLV_EXEC_STATUS",
|
|
||||||
CORE_TLV_NODE_SESSION: "CORE_TLV_EXEC_SESSION",
|
|
||||||
}
|
|
||||||
|
|
||||||
enumdict(exec_tlvs)
|
|
||||||
|
|
||||||
# Register Message TLV Types
|
|
||||||
|
|
||||||
reg_tlvs = {
|
|
||||||
0x01: "CORE_TLV_REG_WIRELESS",
|
|
||||||
0x02: "CORE_TLV_REG_MOBILITY",
|
|
||||||
0x03: "CORE_TLV_REG_UTILITY",
|
|
||||||
0x04: "CORE_TLV_REG_EXECSRV",
|
|
||||||
0x05: "CORE_TLV_REG_GUI",
|
|
||||||
0x06: "CORE_TLV_REG_EMULSRV",
|
|
||||||
CORE_TLV_NODE_SESSION: "CORE_TLV_REG_SESSION",
|
|
||||||
}
|
|
||||||
|
|
||||||
enumdict(reg_tlvs)
|
|
||||||
|
|
||||||
# Configuration Message TLV Types
|
|
||||||
|
|
||||||
conf_tlvs = {
|
|
||||||
0x01: "CORE_TLV_CONF_NODE",
|
|
||||||
0x02: "CORE_TLV_CONF_OBJ",
|
|
||||||
0x03: "CORE_TLV_CONF_TYPE",
|
|
||||||
0x04: "CORE_TLV_CONF_DATA_TYPES",
|
|
||||||
0x05: "CORE_TLV_CONF_VALUES",
|
|
||||||
0x06: "CORE_TLV_CONF_CAPTIONS",
|
|
||||||
0x07: "CORE_TLV_CONF_BITMAP",
|
|
||||||
0x08: "CORE_TLV_CONF_POSSIBLE_VALUES",
|
|
||||||
0x09: "CORE_TLV_CONF_GROUPS",
|
|
||||||
CORE_TLV_NODE_SESSION: "CORE_TLV_CONF_SESSION",
|
|
||||||
0x0B: "CORE_TLV_CONF_IFNUM",
|
|
||||||
CORE_TLV_NODE_NETID: "CORE_TLV_CONF_NETID",
|
|
||||||
0x50: "CORE_TLV_CONF_OPAQUE",
|
|
||||||
}
|
|
||||||
|
|
||||||
enumdict(conf_tlvs)
|
|
||||||
|
|
||||||
conf_flags = {
|
|
||||||
0x00: "CONF_TYPE_FLAGS_NONE",
|
|
||||||
0x01: "CONF_TYPE_FLAGS_REQUEST",
|
|
||||||
0x02: "CONF_TYPE_FLAGS_UPDATE",
|
|
||||||
0x03: "CONF_TYPE_FLAGS_RESET",
|
|
||||||
}
|
|
||||||
|
|
||||||
enumdict(conf_flags)
|
|
||||||
|
|
||||||
conf_data_types = {
|
|
||||||
0x01: "CONF_DATA_TYPE_UINT8",
|
|
||||||
0x02: "CONF_DATA_TYPE_UINT16",
|
|
||||||
0x03: "CONF_DATA_TYPE_UINT32",
|
|
||||||
0x04: "CONF_DATA_TYPE_UINT64",
|
|
||||||
0x05: "CONF_DATA_TYPE_INT8",
|
|
||||||
0x06: "CONF_DATA_TYPE_INT16",
|
|
||||||
0x07: "CONF_DATA_TYPE_INT32",
|
|
||||||
0x08: "CONF_DATA_TYPE_INT64",
|
|
||||||
0x09: "CONF_DATA_TYPE_FLOAT",
|
|
||||||
0x0A: "CONF_DATA_TYPE_STRING",
|
|
||||||
0x0B: "CONF_DATA_TYPE_BOOL",
|
|
||||||
}
|
|
||||||
|
|
||||||
enumdict(conf_data_types)
|
|
||||||
|
|
||||||
# File Message TLV Types
|
|
||||||
|
|
||||||
file_tlvs = {
|
|
||||||
0x01: "CORE_TLV_FILE_NODE",
|
|
||||||
0x02: "CORE_TLV_FILE_NAME",
|
|
||||||
0x03: "CORE_TLV_FILE_MODE",
|
|
||||||
0x04: "CORE_TLV_FILE_NUM",
|
|
||||||
0x05: "CORE_TLV_FILE_TYPE",
|
|
||||||
0x06: "CORE_TLV_FILE_SRCNAME",
|
|
||||||
CORE_TLV_NODE_SESSION: "CORE_TLV_FILE_SESSION",
|
|
||||||
0x10: "CORE_TLV_FILE_DATA",
|
|
||||||
0x11: "CORE_TLV_FILE_CMPDATA",
|
|
||||||
}
|
|
||||||
|
|
||||||
enumdict(file_tlvs)
|
|
||||||
|
|
||||||
# Interface Message TLV Types
|
|
||||||
|
|
||||||
iface_tlvs = {
|
|
||||||
0x01: "CORE_TLV_IFACE_NODE",
|
|
||||||
0x02: "CORE_TLV_IFACE_NUM",
|
|
||||||
0x03: "CORE_TLV_IFACE_NAME",
|
|
||||||
0x04: "CORE_TLV_IFACE_IPADDR",
|
|
||||||
0x05: "CORE_TLV_IFACE_MASK",
|
|
||||||
0x06: "CORE_TLV_IFACE_MACADDR",
|
|
||||||
0x07: "CORE_TLV_IFACE_IP6ADDR",
|
|
||||||
0x08: "CORE_TLV_IFACE_IP6MASK",
|
|
||||||
0x09: "CORE_TLV_IFACE_TYPE",
|
|
||||||
CORE_TLV_NODE_SESSION: "CORE_TLV_IFACE_SESSION",
|
|
||||||
0x0B: "CORE_TLV_IFACE_STATE",
|
|
||||||
CORE_TLV_NODE_EMUID: "CORE_TLV_IFACE_EMUID",
|
|
||||||
CORE_TLV_NODE_NETID: "CORE_TLV_IFACE_NETID",
|
|
||||||
}
|
|
||||||
|
|
||||||
enumdict(iface_tlvs)
|
|
||||||
|
|
||||||
# Event Message TLV Types
|
|
||||||
|
|
||||||
event_tlvs = {
|
|
||||||
0x01: "CORE_TLV_EVENT_NODE",
|
|
||||||
0x02: "CORE_TLV_EVENT_TYPE",
|
|
||||||
0x03: "CORE_TLV_EVENT_NAME",
|
|
||||||
0x04: "CORE_TLV_EVENT_DATA",
|
|
||||||
0x05: "CORE_TLV_EVENT_TIME",
|
|
||||||
CORE_TLV_NODE_SESSION: "CORE_TLV_EVENT_SESSION",
|
|
||||||
}
|
|
||||||
|
|
||||||
enumdict(event_tlvs)
|
|
||||||
|
|
||||||
event_types = dict(enumerate([
|
|
||||||
"CORE_EVENT_NONE",
|
|
||||||
"CORE_EVENT_DEFINITION_STATE",
|
|
||||||
"CORE_EVENT_CONFIGURATION_STATE",
|
|
||||||
"CORE_EVENT_INSTANTIATION_STATE",
|
|
||||||
"CORE_EVENT_RUNTIME_STATE",
|
|
||||||
"CORE_EVENT_DATACOLLECT_STATE",
|
|
||||||
"CORE_EVENT_SHUTDOWN_STATE",
|
|
||||||
"CORE_EVENT_START",
|
|
||||||
"CORE_EVENT_STOP",
|
|
||||||
"CORE_EVENT_PAUSE",
|
|
||||||
"CORE_EVENT_RESTART",
|
|
||||||
"CORE_EVENT_FILE_OPEN",
|
|
||||||
"CORE_EVENT_FILE_SAVE",
|
|
||||||
"CORE_EVENT_SCHEDULED",
|
|
||||||
"CORE_EVENT_RECONFIGURE",
|
|
||||||
"CORE_EVENT_INSTANTIATION_COMPLETE",
|
|
||||||
]))
|
|
||||||
|
|
||||||
enumdict(event_types)
|
|
||||||
|
|
||||||
# Session Message TLV Types
|
|
||||||
|
|
||||||
session_tlvs = {
|
|
||||||
0x01: "CORE_TLV_SESS_NUMBER",
|
|
||||||
0x02: "CORE_TLV_SESS_NAME",
|
|
||||||
0x03: "CORE_TLV_SESS_FILE",
|
|
||||||
0x04: "CORE_TLV_SESS_NODECOUNT",
|
|
||||||
0x05: "CORE_TLV_SESS_DATE",
|
|
||||||
0x06: "CORE_TLV_SESS_THUMB",
|
|
||||||
0x07: "CORE_TLV_SESS_USER",
|
|
||||||
0x0A: "CORE_TLV_SESS_OPAQUE",
|
|
||||||
}
|
|
||||||
|
|
||||||
enumdict(session_tlvs)
|
|
||||||
|
|
||||||
# Exception Message TLV Types
|
|
||||||
|
|
||||||
exception_tlvs = {
|
|
||||||
0x01: "CORE_TLV_EXCP_NODE",
|
|
||||||
0x02: "CORE_TLV_EXCP_SESSION",
|
|
||||||
0x03: "CORE_TLV_EXCP_LEVEL",
|
|
||||||
0x04: "CORE_TLV_EXCP_SOURCE",
|
|
||||||
0x05: "CORE_TLV_EXCP_DATE",
|
|
||||||
0x06: "CORE_TLV_EXCP_TEXT",
|
|
||||||
0x0A: "CORE_TLV_EXCP_OPAQUE",
|
|
||||||
}
|
|
||||||
|
|
||||||
enumdict(exception_tlvs)
|
|
||||||
|
|
||||||
exception_levels = dict(enumerate([
|
|
||||||
"CORE_EXCP_LEVEL_NONE",
|
|
||||||
"CORE_EXCP_LEVEL_FATAL",
|
|
||||||
"CORE_EXCP_LEVEL_ERROR",
|
|
||||||
"CORE_EXCP_LEVEL_WARNING",
|
|
||||||
"CORE_EXCP_LEVEL_NOTICE",
|
|
||||||
]))
|
|
||||||
|
|
||||||
enumdict(exception_levels)
|
|
||||||
|
|
||||||
del enumdict
|
|
||||||
File diff suppressed because it is too large
Load diff
|
|
@ -5,61 +5,69 @@
|
||||||
#
|
#
|
||||||
# authors: core-dev@pf.itd.nrl.navy.mil
|
# authors: core-dev@pf.itd.nrl.navy.mil
|
||||||
#
|
#
|
||||||
'''
|
|
||||||
|
"""
|
||||||
netgraph.py: Netgraph helper functions; for now these are wrappers around
|
netgraph.py: Netgraph helper functions; for now these are wrappers around
|
||||||
ngctl commands.
|
ngctl commands.
|
||||||
'''
|
"""
|
||||||
|
|
||||||
import subprocess
|
import subprocess
|
||||||
from core.misc.utils import *
|
|
||||||
from core.constants import *
|
|
||||||
|
|
||||||
checkexec([NGCTL_BIN])
|
from core import constants
|
||||||
|
from core.misc import utils
|
||||||
|
|
||||||
|
utils.checkexec([constants.NGCTL_BIN])
|
||||||
|
|
||||||
|
|
||||||
def createngnode(type, hookstr, name=None):
|
def createngnode(type, hookstr, name=None):
|
||||||
''' Create a new Netgraph node of type and optionally assign name. The
|
"""
|
||||||
|
Create a new Netgraph node of type and optionally assign name. The
|
||||||
hook string hookstr should contain two names. This is a string so
|
hook string hookstr should contain two names. This is a string so
|
||||||
other commands may be inserted after the two names.
|
other commands may be inserted after the two names.
|
||||||
Return the name and netgraph ID of the new node.
|
Return the name and netgraph ID of the new node.
|
||||||
'''
|
"""
|
||||||
hook1 = hookstr.split()[0]
|
hook1 = hookstr.split()[0]
|
||||||
ngcmd = "mkpeer %s %s \n show .%s" % (type, hookstr, hook1)
|
ngcmd = "mkpeer %s %s \n show .%s" % (type, hookstr, hook1)
|
||||||
cmd = [NGCTL_BIN, "-f", "-"]
|
cmd = [constants.NGCTL_BIN, "-f", "-"]
|
||||||
cmdid = subprocess.Popen(cmd, stdin=subprocess.PIPE,
|
cmdid = subprocess.Popen(cmd, stdin=subprocess.PIPE,
|
||||||
stdout=subprocess.PIPE,
|
stdout=subprocess.PIPE,
|
||||||
stderr=subprocess.STDOUT)
|
stderr=subprocess.STDOUT)
|
||||||
result, err = cmdid.communicate(input = ngcmd) # err will always be None
|
# err will always be None
|
||||||
|
result, err = cmdid.communicate(input=ngcmd)
|
||||||
status = cmdid.wait()
|
status = cmdid.wait()
|
||||||
if status > 0:
|
if status > 0:
|
||||||
raise Exception, "error creating Netgraph node %s (%s): %s" % \
|
raise Exception("error creating Netgraph node %s (%s): %s" % (type, ngcmd, result))
|
||||||
(type, ngcmd, result)
|
|
||||||
results = result.split()
|
results = result.split()
|
||||||
ngname = results[1]
|
ngname = results[1]
|
||||||
ngid = results[5]
|
ngid = results[5]
|
||||||
if name:
|
if name:
|
||||||
check_call([NGCTL_BIN, "name", "[0x%s]:" % ngid, name])
|
utils.check_call([constants.NGCTL_BIN, "name", "[0x%s]:" % ngid, name])
|
||||||
return (ngname, ngid)
|
return ngname, ngid
|
||||||
|
|
||||||
|
|
||||||
def destroyngnode(name):
|
def destroyngnode(name):
|
||||||
''' Shutdown a Netgraph node having the given name.
|
""" Shutdown a Netgraph node having the given name.
|
||||||
'''
|
"""
|
||||||
check_call([NGCTL_BIN, "shutdown", "%s:" % name])
|
utils.check_call([constants.NGCTL_BIN, "shutdown", "%s:" % name])
|
||||||
|
|
||||||
|
|
||||||
def connectngnodes(name1, name2, hook1, hook2):
|
def connectngnodes(name1, name2, hook1, hook2):
|
||||||
''' Connect two hooks of two Netgraph nodes given by their names.
|
""" Connect two hooks of two Netgraph nodes given by their names.
|
||||||
'''
|
"""
|
||||||
node1 = "%s:" % name1
|
node1 = "%s:" % name1
|
||||||
node2 = "%s:" % name2
|
node2 = "%s:" % name2
|
||||||
check_call([NGCTL_BIN, "connect", node1, node2, hook1, hook2])
|
utils.check_call([constants.NGCTL_BIN, "connect", node1, node2, hook1, hook2])
|
||||||
|
|
||||||
|
|
||||||
def ngmessage(name, msg):
|
def ngmessage(name, msg):
|
||||||
''' Send a Netgraph message to the node named name.
|
""" Send a Netgraph message to the node named name.
|
||||||
'''
|
"""
|
||||||
cmd = [NGCTL_BIN, "msg", "%s:" % name] + msg
|
cmd = [constants.NGCTL_BIN, "msg", "%s:" % name] + msg
|
||||||
check_call(cmd)
|
utils.check_call(cmd)
|
||||||
|
|
||||||
|
|
||||||
def ngloadkernelmodule(name):
|
def ngloadkernelmodule(name):
|
||||||
''' Load a kernel module by invoking kldstat. This is needed for the
|
""" Load a kernel module by invoking kldstat. This is needed for the
|
||||||
ng_ether module which automatically creates Netgraph nodes when loaded.
|
ng_ether module which automatically creates Netgraph nodes when loaded.
|
||||||
'''
|
"""
|
||||||
mutecall(["kldload", name])
|
utils.mutecall(["kldload", name])
|
||||||
|
|
|
||||||
|
|
@ -6,128 +6,129 @@
|
||||||
# author: core-dev@pf.itd.nrl.navy.mil
|
# author: core-dev@pf.itd.nrl.navy.mil
|
||||||
#
|
#
|
||||||
|
|
||||||
'''
|
"""
|
||||||
nodes.py: definition of CoreNode classes and other node classes that inherit
|
nodes.py: definition of CoreNode classes and other node classes that inherit
|
||||||
from the CoreNode, implementing specific node types.
|
from the CoreNode, implementing specific node types.
|
||||||
'''
|
"""
|
||||||
|
|
||||||
from vnode import *
|
import socket
|
||||||
from vnet import *
|
|
||||||
from core.constants import *
|
from core import constants
|
||||||
from core.misc.ipaddr import *
|
|
||||||
from core.api import coreapi
|
from core.api import coreapi
|
||||||
|
from core.bsd.netgraph import connectngnodes
|
||||||
from core.bsd.netgraph import ngloadkernelmodule
|
from core.bsd.netgraph import ngloadkernelmodule
|
||||||
|
from core.bsd.vnet import NetgraphNet
|
||||||
|
from core.bsd.vnet import NetgraphPipeNet
|
||||||
|
from core.bsd.vnode import JailNode
|
||||||
|
from core.enumerations import LinkTlvs
|
||||||
|
from core.enumerations import LinkTypes
|
||||||
|
from core.enumerations import NodeTypes
|
||||||
|
from core.enumerations import RegisterTlvs
|
||||||
|
from core.misc import ipaddress
|
||||||
|
from core.misc import utils
|
||||||
|
|
||||||
|
utils.checkexec([constants.IFCONFIG_BIN])
|
||||||
|
|
||||||
checkexec([IFCONFIG_BIN])
|
|
||||||
|
|
||||||
class CoreNode(JailNode):
|
class CoreNode(JailNode):
|
||||||
apitype = coreapi.CORE_NODE_DEF
|
apitype = NodeTypes.DEFAULT.value
|
||||||
|
|
||||||
|
|
||||||
class PtpNet(NetgraphPipeNet):
|
class PtpNet(NetgraphPipeNet):
|
||||||
def tonodemsg(self, flags):
|
def tonodemsg(self, flags):
|
||||||
''' Do not generate a Node Message for point-to-point links. They are
|
""" Do not generate a Node Message for point-to-point links. They are
|
||||||
built using a link message instead.
|
built using a link message instead.
|
||||||
'''
|
"""
|
||||||
pass
|
pass
|
||||||
|
|
||||||
def tolinkmsgs(self, flags):
|
def tolinkmsgs(self, flags):
|
||||||
''' Build CORE API TLVs for a point-to-point link. One Link message
|
""" Build CORE API TLVs for a point-to-point link. One Link message
|
||||||
describes this network.
|
describes this network.
|
||||||
'''
|
"""
|
||||||
tlvdata = ""
|
tlvdata = ""
|
||||||
if len(self._netif) != 2:
|
if len(self._netif) != 2:
|
||||||
return tlvdata
|
return tlvdata
|
||||||
(if1, if2) = self._netif.items()
|
(if1, if2) = self._netif.items()
|
||||||
if1 = if1[1]
|
if1 = if1[1]
|
||||||
if2 = if2[1]
|
if2 = if2[1]
|
||||||
tlvdata += coreapi.CoreLinkTlv.pack(coreapi.CORE_TLV_LINK_N1NUMBER,
|
tlvdata += coreapi.CoreLinkTlv.pack(LinkTlvs.N1_NUMBER.value, if1.node.objid)
|
||||||
if1.node.objid)
|
tlvdata += coreapi.CoreLinkTlv.pack(LinkTlvs.N2_NUMBER.value, if2.node.objid)
|
||||||
tlvdata += coreapi.CoreLinkTlv.pack(coreapi.CORE_TLV_LINK_N2NUMBER,
|
delay = if1.getparam("delay")
|
||||||
if2.node.objid)
|
bw = if1.getparam("bw")
|
||||||
delay = if1.getparam('delay')
|
loss = if1.getparam("loss")
|
||||||
bw = if1.getparam('bw')
|
duplicate = if1.getparam("duplicate")
|
||||||
loss = if1.getparam('loss')
|
jitter = if1.getparam("jitter")
|
||||||
duplicate = if1.getparam('duplicate')
|
|
||||||
jitter = if1.getparam('jitter')
|
|
||||||
if delay is not None:
|
if delay is not None:
|
||||||
tlvdata += coreapi.CoreLinkTlv.pack(coreapi.CORE_TLV_LINK_DELAY,
|
tlvdata += coreapi.CoreLinkTlv.pack(LinkTlvs.DELAY.value, delay)
|
||||||
delay)
|
|
||||||
if bw is not None:
|
if bw is not None:
|
||||||
tlvdata += coreapi.CoreLinkTlv.pack(coreapi.CORE_TLV_LINK_BW, bw)
|
tlvdata += coreapi.CoreLinkTlv.pack(LinkTlvs.BANDWIDTH.value, bw)
|
||||||
if loss is not None:
|
if loss is not None:
|
||||||
tlvdata += coreapi.CoreLinkTlv.pack(coreapi.CORE_TLV_LINK_PER,
|
tlvdata += coreapi.CoreLinkTlv.pack(LinkTlvs.PER.value, str(loss))
|
||||||
str(loss))
|
|
||||||
if duplicate is not None:
|
if duplicate is not None:
|
||||||
tlvdata += coreapi.CoreLinkTlv.pack(coreapi.CORE_TLV_LINK_DUP,
|
tlvdata += coreapi.CoreLinkTlv.pack(LinkTlvs.DUP.value, str(duplicate))
|
||||||
str(duplicate))
|
|
||||||
if jitter is not None:
|
if jitter is not None:
|
||||||
tlvdata += coreapi.CoreLinkTlv.pack(coreapi.CORE_TLV_LINK_JITTER,
|
tlvdata += coreapi.CoreLinkTlv.pack(LinkTlvs.JITTER.value, jitter)
|
||||||
jitter)
|
tlvdata += coreapi.CoreLinkTlv.pack(LinkTlvs.TYPE.value, self.linktype)
|
||||||
tlvdata += coreapi.CoreLinkTlv.pack(coreapi.CORE_TLV_LINK_TYPE,
|
|
||||||
self.linktype)
|
|
||||||
|
|
||||||
tlvdata += coreapi.CoreLinkTlv.pack(coreapi.CORE_TLV_LINK_IF1NUM, \
|
tlvdata += coreapi.CoreLinkTlv.pack(LinkTlvs.INTERFACE1_NUMBER.value, if1.node.getifindex(if1))
|
||||||
if1.node.getifindex(if1))
|
|
||||||
if if1.hwaddr:
|
if if1.hwaddr:
|
||||||
tlvdata += coreapi.CoreLinkTlv.pack(coreapi.CORE_TLV_LINK_IF1MAC,
|
tlvdata += coreapi.CoreLinkTlv.pack(LinkTlvs.INTERFACE1_MAC.value, if1.hwaddr)
|
||||||
if1.hwaddr)
|
|
||||||
for addr in if1.addrlist:
|
for addr in if1.addrlist:
|
||||||
(ip, sep, mask) = addr.partition('/')
|
(ip, sep, mask) = addr.partition("/")
|
||||||
mask = int(mask)
|
mask = int(mask)
|
||||||
if isIPv4Address(ip):
|
if ipaddress.is_ipv4_address(ip):
|
||||||
family = AF_INET
|
family = socket.AF_INET
|
||||||
tlvtypeip = coreapi.CORE_TLV_LINK_IF1IP4
|
tlvtypeip = LinkTlvs.INTERFACE1_IP4.value
|
||||||
tlvtypemask = coreapi.CORE_TLV_LINK_IF1IP4MASK
|
tlvtypemask = LinkTlvs.INTERFACE1_IP4_MASK
|
||||||
else:
|
else:
|
||||||
family = AF_INET6
|
family = socket.AF_INET6
|
||||||
tlvtypeip = coreapi.CORE_TLV_LINK_IF1IP6
|
tlvtypeip = LinkTlvs.INTERFACE1_IP6.value
|
||||||
tlvtypemask = coreapi.CORE_TLV_LINK_IF1IP6MASK
|
tlvtypemask = LinkTlvs.INTERFACE1_IP6_MASK.value
|
||||||
ipl = socket.inet_pton(family, ip)
|
ipl = socket.inet_pton(family, ip)
|
||||||
tlvdata += coreapi.CoreLinkTlv.pack(tlvtypeip,
|
tlvdata += coreapi.CoreLinkTlv.pack(tlvtypeip, ipaddress.IpAddress(af=family, address=ipl))
|
||||||
IPAddr(af=family, addr=ipl))
|
|
||||||
tlvdata += coreapi.CoreLinkTlv.pack(tlvtypemask, mask)
|
tlvdata += coreapi.CoreLinkTlv.pack(tlvtypemask, mask)
|
||||||
|
|
||||||
tlvdata += coreapi.CoreLinkTlv.pack(coreapi.CORE_TLV_LINK_IF2NUM, \
|
tlvdata += coreapi.CoreLinkTlv.pack(LinkTlvs.INTERFACE2_NUMBER.value, if2.node.getifindex(if2))
|
||||||
if2.node.getifindex(if2))
|
|
||||||
if if2.hwaddr:
|
if if2.hwaddr:
|
||||||
tlvdata += coreapi.CoreLinkTlv.pack(coreapi.CORE_TLV_LINK_IF2MAC,
|
tlvdata += coreapi.CoreLinkTlv.pack(LinkTlvs.INTERFACE2_MAC.value, if2.hwaddr)
|
||||||
if2.hwaddr)
|
|
||||||
for addr in if2.addrlist:
|
for addr in if2.addrlist:
|
||||||
(ip, sep, mask) = addr.partition('/')
|
(ip, sep, mask) = addr.partition("/")
|
||||||
mask = int(mask)
|
mask = int(mask)
|
||||||
if isIPv4Address(ip):
|
if ipaddress.is_ipv4_address(ip):
|
||||||
family = AF_INET
|
family = socket.AF_INET
|
||||||
tlvtypeip = coreapi.CORE_TLV_LINK_IF2IP4
|
tlvtypeip = LinkTlvs.INTERFACE2_IP4.value
|
||||||
tlvtypemask = coreapi.CORE_TLV_LINK_IF2IP4MASK
|
tlvtypemask = LinkTlvs.INTERFACE2_IP4_MASK
|
||||||
else:
|
else:
|
||||||
family = AF_INET6
|
family = socket.AF_INET6
|
||||||
tlvtypeip = coreapi.CORE_TLV_LINK_IF2IP6
|
tlvtypeip = LinkTlvs.INTERFACE2_IP6.value
|
||||||
tlvtypemask = coreapi.CORE_TLV_LINK_IF2IP6MASK
|
tlvtypemask = LinkTlvs.INTERFACE2_IP6_MASK.value
|
||||||
ipl = socket.inet_pton(family, ip)
|
ipl = socket.inet_pton(family, ip)
|
||||||
tlvdata += coreapi.CoreLinkTlv.pack(tlvtypeip,
|
tlvdata += coreapi.CoreLinkTlv.pack(tlvtypeip, ipaddress.IpAddress(af=family, address=ipl))
|
||||||
IPAddr(af=family, addr=ipl))
|
|
||||||
tlvdata += coreapi.CoreLinkTlv.pack(tlvtypemask, mask)
|
tlvdata += coreapi.CoreLinkTlv.pack(tlvtypemask, mask)
|
||||||
|
|
||||||
msg = coreapi.CoreLinkMessage.pack(flags, tlvdata)
|
msg = coreapi.CoreLinkMessage.pack(flags, tlvdata)
|
||||||
return [msg, ]
|
return [msg, ]
|
||||||
|
|
||||||
|
|
||||||
class SwitchNode(NetgraphNet):
|
class SwitchNode(NetgraphNet):
|
||||||
ngtype = "bridge"
|
ngtype = "bridge"
|
||||||
nghooks = "link0 link0\nmsg .link0 setpersistent"
|
nghooks = "link0 link0\nmsg .link0 setpersistent"
|
||||||
apitype = coreapi.CORE_NODE_SWITCH
|
apitype = NodeTypes.SWITCH.value
|
||||||
policy = "ACCEPT"
|
policy = "ACCEPT"
|
||||||
|
|
||||||
|
|
||||||
class HubNode(NetgraphNet):
|
class HubNode(NetgraphNet):
|
||||||
ngtype = "hub"
|
ngtype = "hub"
|
||||||
nghooks = "link0 link0\nmsg .link0 setpersistent"
|
nghooks = "link0 link0\nmsg .link0 setpersistent"
|
||||||
apitype = coreapi.CORE_NODE_HUB
|
apitype = NodeTypes.HUB.value
|
||||||
policy = "ACCEPT"
|
policy = "ACCEPT"
|
||||||
|
|
||||||
|
|
||||||
class WlanNode(NetgraphNet):
|
class WlanNode(NetgraphNet):
|
||||||
ngtype = "wlan"
|
ngtype = "wlan"
|
||||||
nghooks = "anchor anchor"
|
nghooks = "anchor anchor"
|
||||||
apitype = coreapi.CORE_NODE_WLAN
|
apitype = NodeTypes.WIRELESS_LAN.value
|
||||||
linktype = coreapi.CORE_LINK_WIRELESS
|
linktype = LinkTypes.WIRELESS.value
|
||||||
policy = "DROP"
|
policy = "DROP"
|
||||||
|
|
||||||
def __init__(self, session, objid=None, name=None, verbose=False,
|
def __init__(self, session, objid=None, name=None, verbose=False,
|
||||||
|
|
@ -144,15 +145,15 @@ class WlanNode(NetgraphNet):
|
||||||
netif.poshook = self.model._positioncallback
|
netif.poshook = self.model._positioncallback
|
||||||
if netif.node is None:
|
if netif.node is None:
|
||||||
return
|
return
|
||||||
(x,y,z) = netif.node.position.get()
|
x, y, z = netif.node.position.get()
|
||||||
netif.poshook(netif, x, y, z)
|
netif.poshook(netif, x, y, z)
|
||||||
|
|
||||||
def setmodel(self, model, config):
|
def setmodel(self, model, config):
|
||||||
''' Mobility and wireless model.
|
""" Mobility and wireless model.
|
||||||
'''
|
"""
|
||||||
if (self.verbose):
|
if self.verbose:
|
||||||
self.info("adding model %s" % model._name)
|
self.info("adding model %s" % model._name)
|
||||||
if model._type == coreapi.CORE_TLV_REG_WIRELESS:
|
if model._type == RegisterTlvs.WIRELESS.value:
|
||||||
self.model = model(session=self.session, objid=self.objid,
|
self.model = model(session=self.session, objid=self.objid,
|
||||||
verbose=self.verbose, values=config)
|
verbose=self.verbose, values=config)
|
||||||
if self.model._positioncallback:
|
if self.model._positioncallback:
|
||||||
|
|
@ -162,13 +163,13 @@ class WlanNode(NetgraphNet):
|
||||||
(x, y, z) = netif.node.position.get()
|
(x, y, z) = netif.node.position.get()
|
||||||
netif.poshook(netif, x, y, z)
|
netif.poshook(netif, x, y, z)
|
||||||
self.model.setlinkparams()
|
self.model.setlinkparams()
|
||||||
elif model._type == coreapi.CORE_TLV_REG_MOBILITY:
|
elif model._type == RegisterTlvs.MOBILITY.value:
|
||||||
self.mobility = model(session=self.session, objid=self.objid,
|
self.mobility = model(session=self.session, objid=self.objid,
|
||||||
verbose=self.verbose, values=config)
|
verbose=self.verbose, values=config)
|
||||||
|
|
||||||
|
|
||||||
class RJ45Node(NetgraphPipeNet):
|
class RJ45Node(NetgraphPipeNet):
|
||||||
apitype = coreapi.CORE_NODE_RJ45
|
apitype = NodeTypes.RJ45.value
|
||||||
policy = "ACCEPT"
|
policy = "ACCEPT"
|
||||||
|
|
||||||
def __init__(self, session, objid, name, verbose, start=True):
|
def __init__(self, session, objid, name, verbose, start=True):
|
||||||
|
|
@ -186,7 +187,7 @@ class RJ45Node(NetgraphPipeNet):
|
||||||
p = "promisc"
|
p = "promisc"
|
||||||
if not promisc:
|
if not promisc:
|
||||||
p = "-" + p
|
p = "-" + p
|
||||||
check_call([IFCONFIG_BIN, self.name, "up", p])
|
utils.check_call([constants.IFCONFIG_BIN, self.name, "up", p])
|
||||||
|
|
||||||
def attach(self, netif):
|
def attach(self, netif):
|
||||||
if len(self._netif) > 0:
|
if len(self._netif) > 0:
|
||||||
|
|
@ -195,9 +196,9 @@ class RJ45Node(NetgraphPipeNet):
|
||||||
NetgraphPipeNet.attach(self, netif)
|
NetgraphPipeNet.attach(self, netif)
|
||||||
connectngnodes(self.ngname, self.name, self.gethook(), "lower")
|
connectngnodes(self.ngname, self.name, self.gethook(), "lower")
|
||||||
|
|
||||||
|
|
||||||
class TunnelNode(NetgraphNet):
|
class TunnelNode(NetgraphNet):
|
||||||
ngtype = "pipe"
|
ngtype = "pipe"
|
||||||
nghooks = "upper lower"
|
nghooks = "upper lower"
|
||||||
apitype = coreapi.CORE_NODE_TUNNEL
|
apitype = NodeTypes.TUNNEL.value
|
||||||
policy = "ACCEPT"
|
policy = "ACCEPT"
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -5,18 +5,18 @@
|
||||||
#
|
#
|
||||||
# authors: core-dev@pf.itd.nrl.navy.mil
|
# authors: core-dev@pf.itd.nrl.navy.mil
|
||||||
#
|
#
|
||||||
'''
|
|
||||||
|
"""
|
||||||
vnet.py: NetgraphNet and NetgraphPipeNet classes that implement virtual networks
|
vnet.py: NetgraphNet and NetgraphPipeNet classes that implement virtual networks
|
||||||
using the FreeBSD Netgraph subsystem.
|
using the FreeBSD Netgraph subsystem.
|
||||||
'''
|
"""
|
||||||
|
|
||||||
import sys, threading
|
from core.bsd.netgraph import connectngnodes
|
||||||
|
from core.bsd.netgraph import createngnode
|
||||||
|
from core.bsd.netgraph import destroyngnode
|
||||||
|
from core.bsd.netgraph import ngmessage
|
||||||
|
from core.coreobj import PyCoreNet
|
||||||
|
|
||||||
from core.misc.utils import *
|
|
||||||
from core.constants import *
|
|
||||||
from core.coreobj import PyCoreNet, PyCoreObj
|
|
||||||
from core.bsd.netgraph import *
|
|
||||||
from core.bsd.vnode import VEth
|
|
||||||
|
|
||||||
class NetgraphNet(PyCoreNet):
|
class NetgraphNet(PyCoreNet):
|
||||||
ngtype = None
|
ngtype = None
|
||||||
|
|
@ -40,8 +40,7 @@ class NetgraphNet(PyCoreNet):
|
||||||
self.startup()
|
self.startup()
|
||||||
|
|
||||||
def startup(self):
|
def startup(self):
|
||||||
tmp, self.ngid = createngnode(type=self.ngtype, hookstr=self.nghooks,
|
tmp, self.ngid = createngnode(type=self.ngtype, hookstr=self.nghooks, name=self.ngname)
|
||||||
name=self.ngname)
|
|
||||||
self.up = True
|
self.up = True
|
||||||
|
|
||||||
def shutdown(self):
|
def shutdown(self):
|
||||||
|
|
@ -62,13 +61,12 @@ class NetgraphNet(PyCoreNet):
|
||||||
destroyngnode(self.ngname)
|
destroyngnode(self.ngname)
|
||||||
|
|
||||||
def attach(self, netif):
|
def attach(self, netif):
|
||||||
''' Attach an interface to this netgraph node. Create a pipe between
|
""" Attach an interface to this netgraph node. Create a pipe between
|
||||||
the interface and the hub/switch/wlan node.
|
the interface and the hub/switch/wlan node.
|
||||||
(Note that the PtpNet subclass overrides this method.)
|
(Note that the PtpNet subclass overrides this method.)
|
||||||
'''
|
"""
|
||||||
if self.up:
|
if self.up:
|
||||||
pipe = self.session.addobj(cls = NetgraphPipeNet,
|
pipe = self.session.addobj(cls=NetgraphPipeNet, verbose=self.verbose, start=True)
|
||||||
verbose = self.verbose, start = True)
|
|
||||||
pipe.attach(netif)
|
pipe.attach(netif)
|
||||||
hook = "link%d" % len(self._netif)
|
hook = "link%d" % len(self._netif)
|
||||||
pipe.attachnet(self, hook)
|
pipe.attachnet(self, hook)
|
||||||
|
|
@ -82,14 +80,16 @@ class NetgraphNet(PyCoreNet):
|
||||||
def linked(self, netif1, netif2):
|
def linked(self, netif1, netif2):
|
||||||
# check if the network interfaces are attached to this network
|
# check if the network interfaces are attached to this network
|
||||||
if self._netif[netif1] != netif1:
|
if self._netif[netif1] != netif1:
|
||||||
raise ValueError, "inconsistency for netif %s" % netif1.name
|
raise ValueError("inconsistency for netif %s" % netif1.name)
|
||||||
if self._netif[netif2] != netif2:
|
if self._netif[netif2] != netif2:
|
||||||
raise ValueError, "inconsistency for netif %s" % netif2.name
|
raise ValueError("inconsistency for netif %s" % netif2.name)
|
||||||
|
|
||||||
try:
|
try:
|
||||||
linked = self._linked[netif1][netif2]
|
linked = self._linked[netif1][netif2]
|
||||||
except KeyError:
|
except KeyError:
|
||||||
linked = False
|
linked = False
|
||||||
self._linked[netif1][netif2] = linked
|
self._linked[netif1][netif2] = linked
|
||||||
|
|
||||||
return linked
|
return linked
|
||||||
|
|
||||||
def unlink(self, netif1, netif2):
|
def unlink(self, netif1, netif2):
|
||||||
|
|
@ -109,21 +109,20 @@ class NetgraphNet(PyCoreNet):
|
||||||
self._linked[netif1][netif2] = True
|
self._linked[netif1][netif2] = True
|
||||||
|
|
||||||
def linknet(self, net):
|
def linknet(self, net):
|
||||||
''' Link this bridge with another by creating a veth pair and installing
|
""" Link this bridge with another by creating a veth pair and installing
|
||||||
each device into each bridge.
|
each device into each bridge.
|
||||||
'''
|
"""
|
||||||
raise NotImplementedError
|
raise NotImplementedError
|
||||||
|
|
||||||
def linkconfig(self, netif, bw=None, delay=None,
|
def linkconfig(self, netif, bw=None, delay=None,
|
||||||
loss=None, duplicate=None, jitter=None, netif2=None):
|
loss=None, duplicate=None, jitter=None, netif2=None):
|
||||||
''' Set link effects by modifying the pipe connected to an interface.
|
""" Set link effects by modifying the pipe connected to an interface.
|
||||||
'''
|
"""
|
||||||
if not netif.pipe:
|
if not netif.pipe:
|
||||||
self.warn("linkconfig for %s but interface %s has no pipe" % \
|
self.warn("linkconfig for %s but interface %s has no pipe" % (self.name, netif.name))
|
||||||
(self.name, netif.name))
|
|
||||||
return
|
return
|
||||||
return netif.pipe.linkconfig(netif, bw, delay, loss, duplicate, jitter,
|
return netif.pipe.linkconfig(netif, bw, delay, loss, duplicate, jitter, netif2)
|
||||||
netif2)
|
|
||||||
|
|
||||||
class NetgraphPipeNet(NetgraphNet):
|
class NetgraphPipeNet(NetgraphNet):
|
||||||
ngtype = "pipe"
|
ngtype = "pipe"
|
||||||
|
|
@ -137,10 +136,10 @@ class NetgraphPipeNet(NetgraphNet):
|
||||||
ngmessage(self.ngname, ["setcfg", "{", "header_offset=14", "}"])
|
ngmessage(self.ngname, ["setcfg", "{", "header_offset=14", "}"])
|
||||||
|
|
||||||
def attach(self, netif):
|
def attach(self, netif):
|
||||||
''' Attach an interface to this pipe node.
|
""" Attach an interface to this pipe node.
|
||||||
The first interface is connected to the "upper" hook, the second
|
The first interface is connected to the "upper" hook, the second
|
||||||
connected to the "lower" hook.
|
connected to the "lower" hook.
|
||||||
'''
|
"""
|
||||||
if len(self._netif) > 1:
|
if len(self._netif) > 1:
|
||||||
raise ValueError, \
|
raise ValueError, \
|
||||||
"Netgraph pipes support at most 2 network interfaces"
|
"Netgraph pipes support at most 2 network interfaces"
|
||||||
|
|
@ -156,15 +155,15 @@ class NetgraphPipeNet(NetgraphNet):
|
||||||
self._linked[netif] = {}
|
self._linked[netif] = {}
|
||||||
|
|
||||||
def attachnet(self, net, hook):
|
def attachnet(self, net, hook):
|
||||||
''' Attach another NetgraphNet to this pipe node.
|
""" Attach another NetgraphNet to this pipe node.
|
||||||
'''
|
"""
|
||||||
localhook = self.gethook()
|
localhook = self.gethook()
|
||||||
connectngnodes(self.ngname, net.ngname, localhook, hook)
|
connectngnodes(self.ngname, net.ngname, localhook, hook)
|
||||||
|
|
||||||
def gethook(self):
|
def gethook(self):
|
||||||
''' Returns the first hook (e.g. "upper") then the second hook
|
""" Returns the first hook (e.g. "upper") then the second hook
|
||||||
(e.g. "lower") based on the number of connections.
|
(e.g. "lower") based on the number of connections.
|
||||||
'''
|
"""
|
||||||
hooks = self.nghooks.split()
|
hooks = self.nghooks.split()
|
||||||
if len(self._netif) == 0:
|
if len(self._netif) == 0:
|
||||||
return hooks[0]
|
return hooks[0]
|
||||||
|
|
@ -173,13 +172,13 @@ class NetgraphPipeNet(NetgraphNet):
|
||||||
|
|
||||||
def linkconfig(self, netif, bw=None, delay=None,
|
def linkconfig(self, netif, bw=None, delay=None,
|
||||||
loss=None, duplicate=None, jitter=None, netif2=None):
|
loss=None, duplicate=None, jitter=None, netif2=None):
|
||||||
''' Set link effects by sending a Netgraph setcfg message to the pipe.
|
""" Set link effects by sending a Netgraph setcfg message to the pipe.
|
||||||
'''
|
"""
|
||||||
netif.setparam('bw', bw)
|
netif.setparam("bw", bw)
|
||||||
netif.setparam('delay', delay)
|
netif.setparam("delay", delay)
|
||||||
netif.setparam('loss', loss)
|
netif.setparam("loss", loss)
|
||||||
netif.setparam('duplicate', duplicate)
|
netif.setparam("duplicate", duplicate)
|
||||||
netif.setparam('jitter', jitter)
|
netif.setparam("jitter", jitter)
|
||||||
if not self.up:
|
if not self.up:
|
||||||
return
|
return
|
||||||
params = []
|
params = []
|
||||||
|
|
@ -213,4 +212,3 @@ class NetgraphPipeNet(NetgraphNet):
|
||||||
setcfg += ["downstream={", ] + downstream + ["}", ]
|
setcfg += ["downstream={", ] + downstream + ["}", ]
|
||||||
setcfg += ["}", ]
|
setcfg += ["}", ]
|
||||||
ngmessage(self.ngname, setcfg)
|
ngmessage(self.ngname, setcfg)
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -5,20 +5,25 @@
|
||||||
#
|
#
|
||||||
# authors: core-dev@pf.itd.nrl.navy.mil
|
# authors: core-dev@pf.itd.nrl.navy.mil
|
||||||
#
|
#
|
||||||
'''
|
|
||||||
|
"""
|
||||||
vnode.py: SimpleJailNode and JailNode classes that implement the FreeBSD
|
vnode.py: SimpleJailNode and JailNode classes that implement the FreeBSD
|
||||||
jail-based virtual node.
|
jail-based virtual node.
|
||||||
'''
|
"""
|
||||||
|
|
||||||
import os, signal, sys, subprocess, threading, string
|
import os
|
||||||
import random, time
|
import subprocess
|
||||||
from core.misc.utils import *
|
import threading
|
||||||
from core.constants import *
|
|
||||||
from core.coreobj import PyCoreObj, PyCoreNode, PyCoreNetIf, Position
|
from core import constants
|
||||||
from core.emane.nodes import EmaneNode
|
from core.bsd.netgraph import createngnode
|
||||||
from core.bsd.netgraph import *
|
from core.bsd.netgraph import destroyngnode
|
||||||
|
from core.coreobj import PyCoreNetIf
|
||||||
|
from core.coreobj import PyCoreNode
|
||||||
|
from core.misc import utils
|
||||||
|
|
||||||
|
utils.checkexec([constants.IFCONFIG_BIN, constants.VIMAGE_BIN])
|
||||||
|
|
||||||
checkexec([IFCONFIG_BIN, VIMAGE_BIN])
|
|
||||||
|
|
||||||
class VEth(PyCoreNetIf):
|
class VEth(PyCoreNetIf):
|
||||||
def __init__(self, node, name, localname, mtu=1500, net=None,
|
def __init__(self, node, name, localname, mtu=1500, net=None,
|
||||||
|
|
@ -45,7 +50,7 @@ class VEth(PyCoreNetIf):
|
||||||
name=self.localname)
|
name=self.localname)
|
||||||
self.name = ngname
|
self.name = ngname
|
||||||
self.ngid = ngid
|
self.ngid = ngid
|
||||||
check_call([IFCONFIG_BIN, ngname, "up"])
|
utils.check_call([constants.IFCONFIG_BIN, ngname, "up"])
|
||||||
self.up = True
|
self.up = True
|
||||||
|
|
||||||
def shutdown(self):
|
def shutdown(self):
|
||||||
|
|
@ -74,12 +79,15 @@ class VEth(PyCoreNetIf):
|
||||||
def sethwaddr(self, addr):
|
def sethwaddr(self, addr):
|
||||||
self.hwaddr = addr
|
self.hwaddr = addr
|
||||||
|
|
||||||
|
|
||||||
class TunTap(PyCoreNetIf):
|
class TunTap(PyCoreNetIf):
|
||||||
'''TUN/TAP virtual device in TAP mode'''
|
"""TUN/TAP virtual device in TAP mode"""
|
||||||
|
|
||||||
def __init__(self, node, name, localname, mtu=None, net=None,
|
def __init__(self, node, name, localname, mtu=None, net=None,
|
||||||
start=True):
|
start=True):
|
||||||
raise NotImplementedError
|
raise NotImplementedError
|
||||||
|
|
||||||
|
|
||||||
class SimpleJailNode(PyCoreNode):
|
class SimpleJailNode(PyCoreNode):
|
||||||
def __init__(self, session, objid=None, name=None, nodedir=None,
|
def __init__(self, session, objid=None, name=None, nodedir=None,
|
||||||
verbose=False):
|
verbose=False):
|
||||||
|
|
@ -94,17 +102,17 @@ class SimpleJailNode(PyCoreNode):
|
||||||
def startup(self):
|
def startup(self):
|
||||||
if self.up:
|
if self.up:
|
||||||
raise Exception, "already up"
|
raise Exception, "already up"
|
||||||
vimg = [VIMAGE_BIN, "-c", self.name]
|
vimg = [constants.VIMAGE_BIN, "-c", self.name]
|
||||||
try:
|
try:
|
||||||
os.spawnlp(os.P_WAIT, VIMAGE_BIN, *vimg)
|
os.spawnlp(os.P_WAIT, constants.VIMAGE_BIN, *vimg)
|
||||||
except OSError:
|
except OSError:
|
||||||
raise Exception, ("vimage command not found while running: %s" % \
|
raise Exception, ("vimage command not found while running: %s" % \
|
||||||
vimg)
|
vimg)
|
||||||
self.info("bringing up loopback interface")
|
self.info("bringing up loopback interface")
|
||||||
self.cmd([IFCONFIG_BIN, "lo0", "127.0.0.1"])
|
self.cmd([constants.IFCONFIG_BIN, "lo0", "127.0.0.1"])
|
||||||
self.info("setting hostname: %s" % self.name)
|
self.info("setting hostname: %s" % self.name)
|
||||||
self.cmd(["hostname", self.name])
|
self.cmd(["hostname", self.name])
|
||||||
self.cmd([SYSCTL_BIN, "vfs.morphing_symlinks=1"])
|
self.cmd([constants.SYSCTL_BIN, "vfs.morphing_symlinks=1"])
|
||||||
self.up = True
|
self.up = True
|
||||||
|
|
||||||
def shutdown(self):
|
def shutdown(self):
|
||||||
|
|
@ -114,12 +122,11 @@ class SimpleJailNode(PyCoreNode):
|
||||||
netif.shutdown()
|
netif.shutdown()
|
||||||
self._netif.clear()
|
self._netif.clear()
|
||||||
del self.session
|
del self.session
|
||||||
vimg = [VIMAGE_BIN, "-d", self.name]
|
vimg = [constants.VIMAGE_BIN, "-d", self.name]
|
||||||
try:
|
try:
|
||||||
os.spawnlp(os.P_WAIT, VIMAGE_BIN, *vimg)
|
os.spawnlp(os.P_WAIT, constants.VIMAGE_BIN, *vimg)
|
||||||
except OSError:
|
except OSError:
|
||||||
raise Exception, ("vimage command not found while running: %s" % \
|
raise Exception("vimage command not found while running: %s" % vimg)
|
||||||
vimg)
|
|
||||||
self.up = False
|
self.up = False
|
||||||
|
|
||||||
def cmd(self, args, wait=True):
|
def cmd(self, args, wait=True):
|
||||||
|
|
@ -127,7 +134,7 @@ class SimpleJailNode(PyCoreNode):
|
||||||
mode = os.P_WAIT
|
mode = os.P_WAIT
|
||||||
else:
|
else:
|
||||||
mode = os.P_NOWAIT
|
mode = os.P_NOWAIT
|
||||||
tmp = call([VIMAGE_BIN, self.name] + args, cwd=self.nodedir)
|
tmp = utils.call([constants.VIMAGE_BIN, self.name] + args, cwd=self.nodedir)
|
||||||
if not wait:
|
if not wait:
|
||||||
tmp = None
|
tmp = None
|
||||||
if tmp:
|
if tmp:
|
||||||
|
|
@ -145,10 +152,10 @@ class SimpleJailNode(PyCoreNode):
|
||||||
status = cmdid.wait()
|
status = cmdid.wait()
|
||||||
else:
|
else:
|
||||||
status = 0
|
status = 0
|
||||||
return (status, result)
|
return status, result
|
||||||
|
|
||||||
def popen(self, args):
|
def popen(self, args):
|
||||||
cmd = [VIMAGE_BIN, self.name]
|
cmd = [constants.VIMAGE_BIN, self.name]
|
||||||
cmd.extend(args)
|
cmd.extend(args)
|
||||||
tmp = subprocess.Popen(cmd, stdin=subprocess.PIPE,
|
tmp = subprocess.Popen(cmd, stdin=subprocess.PIPE,
|
||||||
stdout=subprocess.PIPE,
|
stdout=subprocess.PIPE,
|
||||||
|
|
@ -156,17 +163,17 @@ class SimpleJailNode(PyCoreNode):
|
||||||
return tmp, tmp.stdin, tmp.stdout, tmp.stderr
|
return tmp, tmp.stdin, tmp.stdout, tmp.stderr
|
||||||
|
|
||||||
def icmd(self, args):
|
def icmd(self, args):
|
||||||
return os.spawnlp(os.P_WAIT, VIMAGE_BIN, VIMAGE_BIN, self.name, *args)
|
return os.spawnlp(os.P_WAIT, constants.VIMAGE_BIN, constants.VIMAGE_BIN, self.name, *args)
|
||||||
|
|
||||||
def term(self, sh="/bin/sh"):
|
def term(self, sh="/bin/sh"):
|
||||||
return os.spawnlp(os.P_WAIT, "xterm", "xterm", "-ut",
|
return os.spawnlp(os.P_WAIT, "xterm", "xterm", "-ut",
|
||||||
"-title", self.name, "-e", VIMAGE_BIN, self.name, sh)
|
"-title", self.name, "-e", constants.VIMAGE_BIN, self.name, sh)
|
||||||
|
|
||||||
def termcmdstring(self, sh="/bin/sh"):
|
def termcmdstring(self, sh="/bin/sh"):
|
||||||
''' We add 'sudo' to the command string because the GUI runs as a
|
""" We add "sudo" to the command string because the GUI runs as a
|
||||||
normal user.
|
normal user.
|
||||||
'''
|
"""
|
||||||
return "cd %s && sudo %s %s %s" % (self.nodedir, VIMAGE_BIN, self.name, sh)
|
return "cd %s && sudo %s %s %s" % (self.nodedir, constants.VIMAGE_BIN, self.name, sh)
|
||||||
|
|
||||||
def shcmd(self, cmdstr, sh="/bin/sh"):
|
def shcmd(self, cmdstr, sh="/bin/sh"):
|
||||||
return self.cmd([sh, "-c", cmdstr])
|
return self.cmd([sh, "-c", cmdstr])
|
||||||
|
|
@ -180,7 +187,7 @@ class SimpleJailNode(PyCoreNode):
|
||||||
self.addsymlink(path=target, file=None)
|
self.addsymlink(path=target, file=None)
|
||||||
|
|
||||||
def umount(self, target):
|
def umount(self, target):
|
||||||
self.info("unmounting '%s'" % target)
|
self.info("unmounting %s" % target)
|
||||||
|
|
||||||
def newveth(self, ifindex=None, ifname=None, net=None):
|
def newveth(self, ifindex=None, ifname=None, net=None):
|
||||||
self.lock.acquire()
|
self.lock.acquire()
|
||||||
|
|
@ -197,9 +204,11 @@ class SimpleJailNode(PyCoreNode):
|
||||||
mtu=1500, net=net, start=self.up)
|
mtu=1500, net=net, start=self.up)
|
||||||
if self.up:
|
if self.up:
|
||||||
# install into jail
|
# install into jail
|
||||||
check_call([IFCONFIG_BIN, veth.name, "vnet", self.name])
|
utils.check_call([constants.IFCONFIG_BIN, veth.name, "vnet", self.name])
|
||||||
|
|
||||||
# rename from "ngeth0" to "eth0"
|
# rename from "ngeth0" to "eth0"
|
||||||
self.cmd([IFCONFIG_BIN, veth.name, "name", ifname])
|
self.cmd([constants.IFCONFIG_BIN, veth.name, "name", ifname])
|
||||||
|
|
||||||
veth.name = ifname
|
veth.name = ifname
|
||||||
try:
|
try:
|
||||||
self.addnetif(veth, ifindex)
|
self.addnetif(veth, ifindex)
|
||||||
|
|
@ -214,16 +223,16 @@ class SimpleJailNode(PyCoreNode):
|
||||||
def sethwaddr(self, ifindex, addr):
|
def sethwaddr(self, ifindex, addr):
|
||||||
self._netif[ifindex].sethwaddr(addr)
|
self._netif[ifindex].sethwaddr(addr)
|
||||||
if self.up:
|
if self.up:
|
||||||
self.cmd([IFCONFIG_BIN, self.ifname(ifindex), "link",
|
self.cmd([constants.IFCONFIG_BIN, self.ifname(ifindex), "link",
|
||||||
str(addr)])
|
str(addr)])
|
||||||
|
|
||||||
def addaddr(self, ifindex, addr):
|
def addaddr(self, ifindex, addr):
|
||||||
if self.up:
|
if self.up:
|
||||||
if ':' in addr:
|
if ":" in addr:
|
||||||
family = "inet6"
|
family = "inet6"
|
||||||
else:
|
else:
|
||||||
family = "inet"
|
family = "inet"
|
||||||
self.cmd([IFCONFIG_BIN, self.ifname(ifindex), family, "alias",
|
self.cmd([constants.IFCONFIG_BIN, self.ifname(ifindex), family, "alias",
|
||||||
str(addr)])
|
str(addr)])
|
||||||
self._netif[ifindex].addaddr(addr)
|
self._netif[ifindex].addaddr(addr)
|
||||||
|
|
||||||
|
|
@ -233,14 +242,15 @@ class SimpleJailNode(PyCoreNode):
|
||||||
except ValueError:
|
except ValueError:
|
||||||
self.warn("trying to delete unknown address: %s" % addr)
|
self.warn("trying to delete unknown address: %s" % addr)
|
||||||
if self.up:
|
if self.up:
|
||||||
if ':' in addr:
|
if ":" in addr:
|
||||||
family = "inet6"
|
family = "inet6"
|
||||||
else:
|
else:
|
||||||
family = "inet"
|
family = "inet"
|
||||||
self.cmd([IFCONFIG_BIN, self.ifname(ifindex), family, "-alias",
|
self.cmd([constants.IFCONFIG_BIN, self.ifname(ifindex), family, "-alias",
|
||||||
str(addr)])
|
str(addr)])
|
||||||
|
|
||||||
valid_deladdrtype = ("inet", "inet6", "inet6link")
|
valid_deladdrtype = ("inet", "inet6", "inet6link")
|
||||||
|
|
||||||
def delalladdr(self, ifindex, addrtypes=valid_deladdrtype):
|
def delalladdr(self, ifindex, addrtypes=valid_deladdrtype):
|
||||||
addr = self.getaddr(self.ifname(ifindex), rescan=True)
|
addr = self.getaddr(self.ifname(ifindex), rescan=True)
|
||||||
for t in addrtypes:
|
for t in addrtypes:
|
||||||
|
|
@ -254,19 +264,18 @@ class SimpleJailNode(PyCoreNode):
|
||||||
|
|
||||||
def ifup(self, ifindex):
|
def ifup(self, ifindex):
|
||||||
if self.up:
|
if self.up:
|
||||||
self.cmd([IFCONFIG_BIN, self.ifname(ifindex), "up"])
|
self.cmd([constants.IFCONFIG_BIN, self.ifname(ifindex), "up"])
|
||||||
|
|
||||||
def newnetif(self, net=None, addrlist=[], hwaddr=None,
|
def newnetif(self, net=None, addrlist=[], hwaddr=None,
|
||||||
ifindex=None, ifname=None):
|
ifindex=None, ifname=None):
|
||||||
self.lock.acquire()
|
self.lock.acquire()
|
||||||
try:
|
try:
|
||||||
ifindex = self.newveth(ifindex = ifindex, ifname = ifname,
|
ifindex = self.newveth(ifindex=ifindex, ifname=ifname, net=net)
|
||||||
net = net)
|
|
||||||
if net is not None:
|
if net is not None:
|
||||||
self.attachnet(ifindex, net)
|
self.attachnet(ifindex, net)
|
||||||
if hwaddr:
|
if hwaddr:
|
||||||
self.sethwaddr(ifindex, hwaddr)
|
self.sethwaddr(ifindex, hwaddr)
|
||||||
for addr in maketuple(addrlist):
|
for addr in utils.maketuple(addrlist):
|
||||||
self.addaddr(ifindex, addr)
|
self.addaddr(ifindex, addr)
|
||||||
self.ifup(ifindex)
|
self.ifup(ifindex)
|
||||||
return ifindex
|
return ifindex
|
||||||
|
|
@ -280,8 +289,7 @@ class SimpleJailNode(PyCoreNode):
|
||||||
self._netif[ifindex].detachnet()
|
self._netif[ifindex].detachnet()
|
||||||
|
|
||||||
def addfile(self, srcname, filename):
|
def addfile(self, srcname, filename):
|
||||||
shcmd = "mkdir -p $(dirname '%s') && mv '%s' '%s' && sync" % \
|
shcmd = 'mkdir -p $(dirname "%s") && mv "%s" "%s" && sync' % (filename, srcname, filename)
|
||||||
(filename, srcname, filename)
|
|
||||||
self.shcmd(shcmd)
|
self.shcmd(shcmd)
|
||||||
|
|
||||||
def getaddr(self, ifname, rescan=False):
|
def getaddr(self, ifname, rescan=False):
|
||||||
|
|
@ -289,9 +297,9 @@ class SimpleJailNode(PyCoreNode):
|
||||||
# return self.vnodeclient.getaddr(ifname = ifname, rescan = rescan)
|
# return self.vnodeclient.getaddr(ifname = ifname, rescan = rescan)
|
||||||
|
|
||||||
def addsymlink(self, path, file):
|
def addsymlink(self, path, file):
|
||||||
''' Create a symbolic link from /path/name/file ->
|
""" Create a symbolic link from /path/name/file ->
|
||||||
/tmp/pycore.nnnnn/@.conf/path.name/file
|
/tmp/pycore.nnnnn/@.conf/path.name/file
|
||||||
'''
|
"""
|
||||||
dirname = path
|
dirname = path
|
||||||
if dirname and dirname[0] == "/":
|
if dirname and dirname[0] == "/":
|
||||||
dirname = dirname[1:]
|
dirname = dirname[1:]
|
||||||
|
|
@ -316,8 +324,8 @@ class SimpleJailNode(PyCoreNode):
|
||||||
self.info("creating symlink %s -> %s" % (pathname, sym))
|
self.info("creating symlink %s -> %s" % (pathname, sym))
|
||||||
os.symlink(sym, pathname)
|
os.symlink(sym, pathname)
|
||||||
|
|
||||||
class JailNode(SimpleJailNode):
|
|
||||||
|
|
||||||
|
class JailNode(SimpleJailNode):
|
||||||
def __init__(self, session, objid=None, name=None,
|
def __init__(self, session, objid=None, name=None,
|
||||||
nodedir=None, bootsh="boot.sh", verbose=False,
|
nodedir=None, bootsh="boot.sh", verbose=False,
|
||||||
start=True):
|
start=True):
|
||||||
|
|
@ -362,7 +370,7 @@ class JailNode(SimpleJailNode):
|
||||||
if path[0] != "/":
|
if path[0] != "/":
|
||||||
raise ValueError, "path not fully qualified: " + path
|
raise ValueError, "path not fully qualified: " + path
|
||||||
hostpath = os.path.join(self.nodedir,
|
hostpath = os.path.join(self.nodedir,
|
||||||
os.path.normpath(path).strip('/').replace('/', '.'))
|
os.path.normpath(path).strip("/").replace("/", "."))
|
||||||
try:
|
try:
|
||||||
os.mkdir(hostpath)
|
os.mkdir(hostpath)
|
||||||
except OSError:
|
except OSError:
|
||||||
|
|
@ -390,5 +398,4 @@ class JailNode(SimpleJailNode):
|
||||||
f.write(contents)
|
f.write(contents)
|
||||||
os.chmod(f.name, mode)
|
os.chmod(f.name, mode)
|
||||||
f.close()
|
f.close()
|
||||||
self.info("created nodefile: '%s'; mode: 0%o" % (f.name, mode))
|
self.info("created nodefile: %s; mode: 0%o" % (f.name, mode))
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,35 +1,45 @@
|
||||||
#
|
"""
|
||||||
# CORE
|
Common support for configurable CORE objects.
|
||||||
# Copyright (c)2012 the Boeing Company.
|
"""
|
||||||
# See the LICENSE file included in this distribution.
|
|
||||||
#
|
|
||||||
# authors: Jeff Ahrenholz <jeffrey.m.ahrenholz@boeing.com>
|
|
||||||
#
|
|
||||||
'''
|
|
||||||
conf.py: common support for configurable objects
|
|
||||||
'''
|
|
||||||
import string
|
import string
|
||||||
from core.api import coreapi
|
|
||||||
|
from core.data import ConfigData
|
||||||
|
from core.enumerations import ConfigDataTypes
|
||||||
|
from core.enumerations import ConfigFlags
|
||||||
|
from core.misc import log
|
||||||
|
|
||||||
|
logger = log.get_logger(__name__)
|
||||||
|
|
||||||
|
|
||||||
class ConfigurableManager(object):
|
class ConfigurableManager(object):
|
||||||
''' A generic class for managing Configurables. This class can register
|
"""
|
||||||
|
A generic class for managing Configurables. This class can register
|
||||||
with a session to receive Config Messages for setting some parameters
|
with a session to receive Config Messages for setting some parameters
|
||||||
for itself or for the Configurables that it manages.
|
for itself or for the Configurables that it manages.
|
||||||
'''
|
"""
|
||||||
# name corresponds to configuration object field
|
# name corresponds to configuration object field
|
||||||
_name = ""
|
name = ""
|
||||||
# type corresponds with register message types
|
|
||||||
_type = None
|
|
||||||
|
|
||||||
def __init__(self, session=None):
|
# type corresponds with register message types
|
||||||
self.session = session
|
config_type = None
|
||||||
self.session.addconfobj(self._name, self._type, self.configure)
|
|
||||||
# Configurable key=values, indexed by node number
|
def __init__(self):
|
||||||
|
"""
|
||||||
|
Creates a ConfigurableManager instance.
|
||||||
|
|
||||||
|
:param core.session.Session session: session this manager is tied to
|
||||||
|
:return: nothing
|
||||||
|
"""
|
||||||
|
# configurable key=values, indexed by node number
|
||||||
self.configs = {}
|
self.configs = {}
|
||||||
|
|
||||||
|
# TODO: fix the need for this and isolate to the mobility class that wants it
|
||||||
|
self._modelclsmap = {}
|
||||||
|
|
||||||
def configure(self, session, msg):
|
def configure(self, session, config_data):
|
||||||
''' Handle configure messages. The configuration message sent to a
|
"""
|
||||||
|
Handle configure messages. The configuration message sent to a
|
||||||
ConfigurableManager usually is used to:
|
ConfigurableManager usually is used to:
|
||||||
1. Request a list of Configurables (request flag)
|
1. Request a list of Configurables (request flag)
|
||||||
2. Reset manager and clear configs (reset flag)
|
2. Reset manager and clear configs (reset flag)
|
||||||
|
|
@ -37,45 +47,66 @@ class ConfigurableManager(object):
|
||||||
Configurables
|
Configurables
|
||||||
|
|
||||||
Returns any reply messages.
|
Returns any reply messages.
|
||||||
'''
|
|
||||||
objname = msg.gettlv(coreapi.CORE_TLV_CONF_OBJ)
|
|
||||||
conftype = msg.gettlv(coreapi.CORE_TLV_CONF_TYPE)
|
|
||||||
if conftype == coreapi.CONF_TYPE_FLAGS_REQUEST:
|
|
||||||
return self.configure_request(msg)
|
|
||||||
elif conftype == coreapi.CONF_TYPE_FLAGS_RESET:
|
|
||||||
if objname == "all" or objname == self._name:
|
|
||||||
return self.configure_reset(msg)
|
|
||||||
else:
|
|
||||||
return self.configure_values(msg,
|
|
||||||
msg.gettlv(coreapi.CORE_TLV_CONF_VALUES))
|
|
||||||
|
|
||||||
def configure_request(self, msg):
|
:param core.session.Session session: CORE session object
|
||||||
''' Request configuration data.
|
:param ConfigData config_data: configuration data for carrying out a configuration
|
||||||
'''
|
:return: response messages
|
||||||
|
"""
|
||||||
|
|
||||||
|
if config_data.type == ConfigFlags.REQUEST.value:
|
||||||
|
return self.configure_request(config_data)
|
||||||
|
elif config_data.type == ConfigFlags.RESET.value:
|
||||||
|
return self.configure_reset(config_data)
|
||||||
|
else:
|
||||||
|
return self.configure_values(config_data)
|
||||||
|
|
||||||
|
def configure_request(self, config_data):
|
||||||
|
"""
|
||||||
|
Request configuration data.
|
||||||
|
|
||||||
|
:param ConfigData config_data: configuration data for carrying out a configuration
|
||||||
|
:return: nothing
|
||||||
|
"""
|
||||||
return None
|
return None
|
||||||
|
|
||||||
def configure_reset(self, msg):
|
def configure_reset(self, config_data):
|
||||||
''' By default, resets this manager to clear configs.
|
"""
|
||||||
'''
|
By default, resets this manager to clear configs.
|
||||||
|
|
||||||
|
:param ConfigData config_data: configuration data for carrying out a configuration
|
||||||
|
:return: reset response messages, or None
|
||||||
|
"""
|
||||||
return self.reset()
|
return self.reset()
|
||||||
|
|
||||||
def configure_values(self, msg, values):
|
def configure_values(self, config_data):
|
||||||
''' Values have been sent to this manager.
|
"""
|
||||||
'''
|
Values have been sent to this manager.
|
||||||
|
|
||||||
|
:param ConfigData config_data: configuration data for carrying out a configuration
|
||||||
|
:return: nothing
|
||||||
|
"""
|
||||||
return None
|
return None
|
||||||
|
|
||||||
def configure_values_keyvalues(self, msg, values, target, keys):
|
def configure_values_keyvalues(self, config_data, target, keys):
|
||||||
''' Helper that can be used for configure_values for parsing in
|
"""
|
||||||
|
Helper that can be used for configure_values for parsing in
|
||||||
'key=value' strings from a values field. The key name must be
|
'key=value' strings from a values field. The key name must be
|
||||||
in the keys list, and target.key=value is set.
|
in the keys list, and target.key=value is set.
|
||||||
'''
|
|
||||||
|
:param ConfigData config_data: configuration data for carrying out a configuration
|
||||||
|
:param target: target to set attribute values on
|
||||||
|
:param keys: list of keys to verify validity
|
||||||
|
:return: nothing
|
||||||
|
"""
|
||||||
|
values = config_data.data_values
|
||||||
|
|
||||||
if values is None:
|
if values is None:
|
||||||
return None
|
return None
|
||||||
|
|
||||||
kvs = values.split('|')
|
kvs = values.split('|')
|
||||||
for kv in kvs:
|
for kv in kvs:
|
||||||
try:
|
try:
|
||||||
# key=value
|
key, value = kv.split('=', 1)
|
||||||
(key, value) = kv.split('=', 1)
|
|
||||||
if value is not None and not value.strip():
|
if value is not None and not value.strip():
|
||||||
value = None
|
value = None
|
||||||
except ValueError:
|
except ValueError:
|
||||||
|
|
@ -83,25 +114,38 @@ class ConfigurableManager(object):
|
||||||
key = keys[kvs.index(kv)]
|
key = keys[kvs.index(kv)]
|
||||||
value = kv
|
value = kv
|
||||||
if key not in keys:
|
if key not in keys:
|
||||||
raise ValueError, "invalid key: %s" % key
|
raise ValueError("invalid key: %s" % key)
|
||||||
if value is not None:
|
if value is not None:
|
||||||
setattr(target, key, value)
|
setattr(target, key, value)
|
||||||
|
|
||||||
return None
|
return None
|
||||||
|
|
||||||
def reset(self):
|
def reset(self):
|
||||||
|
"""
|
||||||
|
Reset functionality for the configurable class.
|
||||||
|
|
||||||
|
:return: nothing
|
||||||
|
"""
|
||||||
return None
|
return None
|
||||||
|
|
||||||
def setconfig(self, nodenum, conftype, values):
|
def setconfig(self, nodenum, conftype, values):
|
||||||
''' add configuration values for a node to a dictionary; values are
|
"""
|
||||||
|
Add configuration values for a node to a dictionary; values are
|
||||||
usually received from a Configuration Message, and may refer to a
|
usually received from a Configuration Message, and may refer to a
|
||||||
node for which no object exists yet
|
node for which no object exists yet
|
||||||
'''
|
|
||||||
|
:param int nodenum: node id
|
||||||
|
:param conftype: configuration types
|
||||||
|
:param values: configuration values
|
||||||
|
:return: nothing
|
||||||
|
"""
|
||||||
|
logger.info("setting config for node(%s): %s - %s", nodenum, conftype, values)
|
||||||
conflist = []
|
conflist = []
|
||||||
if nodenum in self.configs:
|
if nodenum in self.configs:
|
||||||
oldlist = self.configs[nodenum]
|
oldlist = self.configs[nodenum]
|
||||||
found = False
|
found = False
|
||||||
for (t, v) in oldlist:
|
for t, v in oldlist:
|
||||||
if (t == conftype):
|
if t == conftype:
|
||||||
# replace existing config
|
# replace existing config
|
||||||
found = True
|
found = True
|
||||||
conflist.append((conftype, values))
|
conflist.append((conftype, values))
|
||||||
|
|
@ -114,34 +158,52 @@ class ConfigurableManager(object):
|
||||||
self.configs[nodenum] = conflist
|
self.configs[nodenum] = conflist
|
||||||
|
|
||||||
def getconfig(self, nodenum, conftype, defaultvalues):
|
def getconfig(self, nodenum, conftype, defaultvalues):
|
||||||
''' get configuration values for a node; if the values don't exist in
|
"""
|
||||||
|
Get configuration values for a node; if the values don't exist in
|
||||||
our dictionary then return the default values supplied
|
our dictionary then return the default values supplied
|
||||||
'''
|
|
||||||
|
:param int nodenum: node id
|
||||||
|
:param conftype: configuration type
|
||||||
|
:param defaultvalues: default values
|
||||||
|
:return: configuration type and default values
|
||||||
|
:type: tuple
|
||||||
|
"""
|
||||||
|
logger.info("getting config for node(%s): %s - default(%s)",
|
||||||
|
nodenum, conftype, defaultvalues)
|
||||||
if nodenum in self.configs:
|
if nodenum in self.configs:
|
||||||
# return configured values
|
# return configured values
|
||||||
conflist = self.configs[nodenum]
|
conflist = self.configs[nodenum]
|
||||||
for (t, v) in conflist:
|
for t, v in conflist:
|
||||||
if (conftype is None) or (t == conftype):
|
if conftype is None or t == conftype:
|
||||||
return (t, v)
|
return t, v
|
||||||
# return default values provided (may be None)
|
# return default values provided (may be None)
|
||||||
return (conftype, defaultvalues)
|
return conftype, defaultvalues
|
||||||
|
|
||||||
def getallconfigs(self, use_clsmap=True):
|
def getallconfigs(self, use_clsmap=True):
|
||||||
''' Return (nodenum, conftype, values) tuples for all stored configs.
|
"""
|
||||||
|
Return (nodenum, conftype, values) tuples for all stored configs.
|
||||||
Used when reconnecting to a session.
|
Used when reconnecting to a session.
|
||||||
'''
|
|
||||||
|
:param bool use_clsmap: should a class map be used, default to True
|
||||||
|
:return: list of all configurations
|
||||||
|
:rtype: list
|
||||||
|
"""
|
||||||
r = []
|
r = []
|
||||||
for nodenum in self.configs:
|
for nodenum in self.configs:
|
||||||
for (t, v) in self.configs[nodenum]:
|
for t, v in self.configs[nodenum]:
|
||||||
if use_clsmap:
|
if use_clsmap:
|
||||||
t = self._modelclsmap[t]
|
t = self._modelclsmap[t]
|
||||||
r.append((nodenum, t, v))
|
r.append((nodenum, t, v))
|
||||||
return r
|
return r
|
||||||
|
|
||||||
def clearconfig(self, nodenum):
|
def clearconfig(self, nodenum):
|
||||||
''' remove configuration values for the specified node;
|
"""
|
||||||
|
remove configuration values for the specified node;
|
||||||
when nodenum is None, remove all configuration values
|
when nodenum is None, remove all configuration values
|
||||||
'''
|
|
||||||
|
:param int nodenum: node id
|
||||||
|
:return: nothing
|
||||||
|
"""
|
||||||
if nodenum is None:
|
if nodenum is None:
|
||||||
self.configs = {}
|
self.configs = {}
|
||||||
return
|
return
|
||||||
|
|
@ -149,10 +211,16 @@ class ConfigurableManager(object):
|
||||||
self.configs.pop(nodenum)
|
self.configs.pop(nodenum)
|
||||||
|
|
||||||
def setconfig_keyvalues(self, nodenum, conftype, keyvalues):
|
def setconfig_keyvalues(self, nodenum, conftype, keyvalues):
|
||||||
''' keyvalues list of tuples
|
"""
|
||||||
'''
|
Key values list of tuples for a node.
|
||||||
|
|
||||||
|
:param int nodenum: node id
|
||||||
|
:param conftype: configuration type
|
||||||
|
:param keyvalues: key valyes
|
||||||
|
:return: nothing
|
||||||
|
"""
|
||||||
if conftype not in self._modelclsmap:
|
if conftype not in self._modelclsmap:
|
||||||
self.warn("Unknown model type '%s'" % (conftype))
|
logger.warn("Unknown model type '%s'" % conftype)
|
||||||
return
|
return
|
||||||
model = self._modelclsmap[conftype]
|
model = self._modelclsmap[conftype]
|
||||||
keys = model.getnames()
|
keys = model.getnames()
|
||||||
|
|
@ -160,7 +228,7 @@ class ConfigurableManager(object):
|
||||||
values = list(model.getdefaultvalues())
|
values = list(model.getdefaultvalues())
|
||||||
for key, value in keyvalues:
|
for key, value in keyvalues:
|
||||||
if key not in keys:
|
if key not in keys:
|
||||||
self.warn("Skipping unknown configuration key for %s: '%s'" % \
|
logger.warn("Skipping unknown configuration key for %s: '%s'" % \
|
||||||
(conftype, key))
|
(conftype, key))
|
||||||
continue
|
continue
|
||||||
i = keys.index(key)
|
i = keys.index(key)
|
||||||
|
|
@ -168,11 +236,16 @@ class ConfigurableManager(object):
|
||||||
self.setconfig(nodenum, conftype, values)
|
self.setconfig(nodenum, conftype, values)
|
||||||
|
|
||||||
def getmodels(self, n):
|
def getmodels(self, n):
|
||||||
''' Return a list of model classes and values for a net if one has been
|
"""
|
||||||
|
Return a list of model classes and values for a net if one has been
|
||||||
configured. This is invoked when exporting a session to XML.
|
configured. This is invoked when exporting a session to XML.
|
||||||
This assumes self.configs contains an iterable of (model-names, values)
|
This assumes self.configs contains an iterable of (model-names, values)
|
||||||
and a self._modelclsmapdict exists.
|
and a self._modelclsmapdict exists.
|
||||||
'''
|
|
||||||
|
:param n: network node to get models for
|
||||||
|
:return: list of model and values tuples for the network node
|
||||||
|
:rtype: list
|
||||||
|
"""
|
||||||
r = []
|
r = []
|
||||||
if n.objid in self.configs:
|
if n.objid in self.configs:
|
||||||
v = self.configs[n.objid]
|
v = self.configs[n.objid]
|
||||||
|
|
@ -183,92 +256,119 @@ class ConfigurableManager(object):
|
||||||
return r
|
return r
|
||||||
|
|
||||||
|
|
||||||
def info(self, msg):
|
|
||||||
self.session.info(msg)
|
|
||||||
|
|
||||||
def warn(self, msg):
|
|
||||||
self.session.warn(msg)
|
|
||||||
|
|
||||||
|
|
||||||
class Configurable(object):
|
class Configurable(object):
|
||||||
''' A generic class for managing configuration parameters.
|
"""
|
||||||
|
A generic class for managing configuration parameters.
|
||||||
Parameters are sent via Configuration Messages, which allow the GUI
|
Parameters are sent via Configuration Messages, which allow the GUI
|
||||||
to build dynamic dialogs depending on what is being configured.
|
to build dynamic dialogs depending on what is being configured.
|
||||||
'''
|
"""
|
||||||
_name = ""
|
name = ""
|
||||||
# Configuration items:
|
# Configuration items:
|
||||||
# ('name', 'type', 'default', 'possible-value-list', 'caption')
|
# ('name', 'type', 'default', 'possible-value-list', 'caption')
|
||||||
_confmatrix = []
|
config_matrix = []
|
||||||
_confgroups = None
|
config_groups = None
|
||||||
_bitmap = None
|
bitmap = None
|
||||||
|
|
||||||
def __init__(self, session=None, objid=None):
|
def __init__(self, session=None, object_id=None):
|
||||||
|
"""
|
||||||
|
Creates a Configurable instance.
|
||||||
|
|
||||||
|
:param core.session.Session session: session for this configurable
|
||||||
|
:param object_id:
|
||||||
|
"""
|
||||||
self.session = session
|
self.session = session
|
||||||
self.objid = objid
|
self.object_id = object_id
|
||||||
|
|
||||||
def reset(self):
|
def reset(self):
|
||||||
|
"""
|
||||||
|
Reset method.
|
||||||
|
|
||||||
|
:return: nothing
|
||||||
|
"""
|
||||||
pass
|
pass
|
||||||
|
|
||||||
def register(self):
|
def register(self):
|
||||||
|
"""
|
||||||
|
Register method.
|
||||||
|
|
||||||
|
:return: nothing
|
||||||
|
"""
|
||||||
pass
|
pass
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def getdefaultvalues(cls):
|
def getdefaultvalues(cls):
|
||||||
return tuple( map(lambda x: x[2], cls._confmatrix) )
|
"""
|
||||||
|
Retrieve default values from configuration matrix.
|
||||||
|
|
||||||
|
:return: tuple of default values
|
||||||
|
:rtype: tuple
|
||||||
|
"""
|
||||||
|
# TODO: why the need for a tuple?
|
||||||
|
return tuple(map(lambda x: x[2], cls.config_matrix))
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def getnames(cls):
|
def getnames(cls):
|
||||||
return tuple( map( lambda x: x[0], cls._confmatrix) )
|
"""
|
||||||
|
Retrieve name values from configuration matrix.
|
||||||
|
|
||||||
|
:return: tuple of name values
|
||||||
|
:rtype: tuple
|
||||||
|
"""
|
||||||
|
# TODO: why the need for a tuple?
|
||||||
|
return tuple(map(lambda x: x[0], cls.config_matrix))
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def configure(cls, mgr, msg):
|
def configure(cls, manager, config_data):
|
||||||
''' Handle configuration messages for this object.
|
"""
|
||||||
'''
|
Handle configuration messages for this object.
|
||||||
|
|
||||||
|
:param ConfigurableManager manager: configuration manager
|
||||||
|
:param config_data: configuration data
|
||||||
|
:return: configuration data object
|
||||||
|
:rtype: ConfigData
|
||||||
|
"""
|
||||||
reply = None
|
reply = None
|
||||||
nodenum = msg.gettlv(coreapi.CORE_TLV_CONF_NODE)
|
node_id = config_data.node
|
||||||
objname = msg.gettlv(coreapi.CORE_TLV_CONF_OBJ)
|
object_name = config_data.object
|
||||||
conftype = msg.gettlv(coreapi.CORE_TLV_CONF_TYPE)
|
config_type = config_data.type
|
||||||
|
interface_id = config_data.interface_number
|
||||||
|
values_str = config_data.data_values
|
||||||
|
|
||||||
ifacenum = msg.gettlv(coreapi.CORE_TLV_CONF_IFNUM)
|
if interface_id is not None:
|
||||||
if ifacenum is not None:
|
node_id = node_id * 1000 + interface_id
|
||||||
nodenum = nodenum*1000 + ifacenum
|
|
||||||
|
|
||||||
if mgr.verbose:
|
logger.info("received configure message for %s nodenum:%s", cls.name, str(node_id))
|
||||||
mgr.info("received configure message for %s nodenum:%s" % (cls._name, str(nodenum)))
|
if config_type == ConfigFlags.REQUEST.value:
|
||||||
if conftype == coreapi.CONF_TYPE_FLAGS_REQUEST:
|
logger.info("replying to configure request for %s model", cls.name)
|
||||||
if mgr.verbose:
|
|
||||||
mgr.info("replying to configure request for %s model" %
|
|
||||||
cls._name)
|
|
||||||
# when object name is "all", the reply to this request may be None
|
# when object name is "all", the reply to this request may be None
|
||||||
# if this node has not been configured for this model; otherwise we
|
# if this node has not been configured for this model; otherwise we
|
||||||
# reply with the defaults for this model
|
# reply with the defaults for this model
|
||||||
if objname == "all":
|
if object_name == "all":
|
||||||
defaults = None
|
defaults = None
|
||||||
typeflags = coreapi.CONF_TYPE_FLAGS_UPDATE
|
typeflags = ConfigFlags.UPDATE.value
|
||||||
else:
|
else:
|
||||||
defaults = cls.getdefaultvalues()
|
defaults = cls.getdefaultvalues()
|
||||||
typeflags = coreapi.CONF_TYPE_FLAGS_NONE
|
typeflags = ConfigFlags.coreapi.CONF_TYPE_FLAGS_NONE
|
||||||
values = mgr.getconfig(nodenum, cls._name, defaults)[1]
|
values = manager.getconfig(node_id, cls.name, defaults)[1]
|
||||||
if values is None:
|
if values is None:
|
||||||
# node has no active config for this model (don't send defaults)
|
# node has no active config for this model (don't send defaults)
|
||||||
return None
|
return None
|
||||||
# reply with config options
|
# reply with config options
|
||||||
reply = cls.toconfmsg(0, nodenum, typeflags, values)
|
reply = cls.config_data(0, node_id, typeflags, values)
|
||||||
elif conftype == coreapi.CONF_TYPE_FLAGS_RESET:
|
elif config_type == ConfigFlags.RESET.value:
|
||||||
if objname == "all":
|
if object_name == "all":
|
||||||
mgr.clearconfig(nodenum)
|
manager.clearconfig(node_id)
|
||||||
# elif conftype == coreapi.CONF_TYPE_FLAGS_UPDATE:
|
# elif conftype == coreapi.CONF_TYPE_FLAGS_UPDATE:
|
||||||
else:
|
else:
|
||||||
# store the configuration values for later use, when the node
|
# store the configuration values for later use, when the node
|
||||||
# object has been created
|
# object has been created
|
||||||
if objname is None:
|
if object_name is None:
|
||||||
mgr.info("no configuration object for node %s" % nodenum)
|
logger.info("no configuration object for node %s", node_id)
|
||||||
return None
|
return None
|
||||||
values_str = msg.gettlv(coreapi.CORE_TLV_CONF_VALUES)
|
|
||||||
defaults = cls.getdefaultvalues()
|
defaults = cls.getdefaultvalues()
|
||||||
if values_str is None:
|
if values_str is None:
|
||||||
# use default or preconfigured values
|
# use default or preconfigured values
|
||||||
values = mgr.getconfig(nodenum, cls._name, defaults)[1]
|
values = manager.getconfig(node_id, cls.name, defaults)[1]
|
||||||
else:
|
else:
|
||||||
# use new values supplied from the conf message
|
# use new values supplied from the conf message
|
||||||
values = values_str.split('|')
|
values = values_str.split('|')
|
||||||
|
|
@ -282,54 +382,55 @@ class Configurable(object):
|
||||||
try:
|
try:
|
||||||
new_values[keys.index(key)] = value
|
new_values[keys.index(key)] = value
|
||||||
except ValueError:
|
except ValueError:
|
||||||
mgr.info("warning: ignoring invalid key '%s'" % key)
|
logger.info("warning: ignoring invalid key '%s'" % key)
|
||||||
values = new_values
|
values = new_values
|
||||||
mgr.setconfig(nodenum, objname, values)
|
manager.setconfig(node_id, object_name, values)
|
||||||
|
|
||||||
return reply
|
return reply
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def toconfmsg(cls, flags, nodenum, typeflags, values):
|
def config_data(cls, flags, node_id, type_flags, values):
|
||||||
''' Convert this class to a Config API message. Some TLVs are defined
|
"""
|
||||||
|
Convert this class to a Config API message. Some TLVs are defined
|
||||||
by the class, but node number, conf type flags, and values must
|
by the class, but node number, conf type flags, and values must
|
||||||
be passed in.
|
be passed in.
|
||||||
'''
|
|
||||||
|
:param flags: message flags
|
||||||
|
:param int node_id: node id
|
||||||
|
:param type_flags: type flags
|
||||||
|
:param values: values
|
||||||
|
:return: configuration data object
|
||||||
|
:rtype: ConfigData
|
||||||
|
"""
|
||||||
keys = cls.getnames()
|
keys = cls.getnames()
|
||||||
keyvalues = map(lambda a, b: "%s=%s" % (a, b), keys, values)
|
keyvalues = map(lambda a, b: "%s=%s" % (a, b), keys, values)
|
||||||
values_str = string.join(keyvalues, '|')
|
values_str = string.join(keyvalues, '|')
|
||||||
tlvdata = ""
|
datatypes = tuple(map(lambda x: x[1], cls.config_matrix))
|
||||||
if nodenum is not None:
|
captions = reduce(lambda a, b: a + '|' + b, map(lambda x: x[4], cls.config_matrix))
|
||||||
tlvdata += coreapi.CoreConfTlv.pack(coreapi.CORE_TLV_CONF_NODE,
|
possible_valuess = reduce(lambda a, b: a + '|' + b, map(lambda x: x[3], cls.config_matrix))
|
||||||
nodenum)
|
|
||||||
tlvdata += coreapi.CoreConfTlv.pack(coreapi.CORE_TLV_CONF_OBJ,
|
return ConfigData(
|
||||||
cls._name)
|
message_type=flags,
|
||||||
tlvdata += coreapi.CoreConfTlv.pack(coreapi.CORE_TLV_CONF_TYPE,
|
node=node_id,
|
||||||
typeflags)
|
object=cls.name,
|
||||||
datatypes = tuple( map(lambda x: x[1], cls._confmatrix) )
|
type=type_flags,
|
||||||
tlvdata += coreapi.CoreConfTlv.pack(coreapi.CORE_TLV_CONF_DATA_TYPES,
|
data_types=datatypes,
|
||||||
datatypes)
|
data_values=values_str,
|
||||||
tlvdata += coreapi.CoreConfTlv.pack(coreapi.CORE_TLV_CONF_VALUES,
|
captions=captions,
|
||||||
values_str)
|
possible_values=possible_valuess,
|
||||||
captions = reduce( lambda a,b: a + '|' + b, \
|
bitmap=cls.bitmap,
|
||||||
map(lambda x: x[4], cls._confmatrix))
|
groups=cls.config_groups
|
||||||
tlvdata += coreapi.CoreConfTlv.pack(coreapi.CORE_TLV_CONF_CAPTIONS,
|
)
|
||||||
captions)
|
|
||||||
possiblevals = reduce( lambda a,b: a + '|' + b, \
|
|
||||||
map(lambda x: x[3], cls._confmatrix))
|
|
||||||
tlvdata += coreapi.CoreConfTlv.pack(
|
|
||||||
coreapi.CORE_TLV_CONF_POSSIBLE_VALUES, possiblevals)
|
|
||||||
if cls._bitmap is not None:
|
|
||||||
tlvdata += coreapi.CoreConfTlv.pack(coreapi.CORE_TLV_CONF_BITMAP,
|
|
||||||
cls._bitmap)
|
|
||||||
if cls._confgroups is not None:
|
|
||||||
tlvdata += coreapi.CoreConfTlv.pack(coreapi.CORE_TLV_CONF_GROUPS,
|
|
||||||
cls._confgroups)
|
|
||||||
msg = coreapi.CoreConfMessage.pack(flags, tlvdata)
|
|
||||||
return msg
|
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def booltooffon(value):
|
def booltooffon(value):
|
||||||
''' Convenience helper turns bool into on (True) or off (False) string.
|
"""
|
||||||
'''
|
Convenience helper turns bool into on (True) or off (False) string.
|
||||||
|
|
||||||
|
:param str value: value to retrieve on/off value for
|
||||||
|
:return: on or off string
|
||||||
|
:rtype: str
|
||||||
|
"""
|
||||||
if value == "1" or value == "true" or value == "on":
|
if value == "1" or value == "true" or value == "on":
|
||||||
return "on"
|
return "on"
|
||||||
else:
|
else:
|
||||||
|
|
@ -337,6 +438,13 @@ class Configurable(object):
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def offontobool(value):
|
def offontobool(value):
|
||||||
|
"""
|
||||||
|
Convenience helper for converting an on/off string to a integer.
|
||||||
|
|
||||||
|
:param str value: on/off string
|
||||||
|
:return: on/off integer value
|
||||||
|
:rtype: int
|
||||||
|
"""
|
||||||
if type(value) == str:
|
if type(value) == str:
|
||||||
if value.lower() == "on":
|
if value.lower() == "on":
|
||||||
return 1
|
return 1
|
||||||
|
|
@ -346,20 +454,30 @@ class Configurable(object):
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def valueof(cls, name, values):
|
def valueof(cls, name, values):
|
||||||
''' Helper to return a value by the name defined in confmatrix.
|
"""
|
||||||
Checks if it is boolean'''
|
Helper to return a value by the name defined in confmatrix.
|
||||||
|
Checks if it is boolean
|
||||||
|
|
||||||
|
:param str name: name to get value of
|
||||||
|
:param values: values to get value from
|
||||||
|
:return: value for name
|
||||||
|
"""
|
||||||
i = cls.getnames().index(name)
|
i = cls.getnames().index(name)
|
||||||
if cls._confmatrix[i][1] == coreapi.CONF_DATA_TYPE_BOOL and \
|
if cls.config_matrix[i][1] == ConfigDataTypes.BOOL.value and values[i] != "":
|
||||||
values[i] != "":
|
|
||||||
return cls.booltooffon(values[i])
|
return cls.booltooffon(values[i])
|
||||||
else:
|
else:
|
||||||
return values[i]
|
return values[i]
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def haskeyvalues(values):
|
def haskeyvalues(values):
|
||||||
''' Helper to check for list of key=value pairs versus a plain old
|
"""
|
||||||
|
Helper to check for list of key=value pairs versus a plain old
|
||||||
list of values. Returns True if all elements are "key=value".
|
list of values. Returns True if all elements are "key=value".
|
||||||
'''
|
|
||||||
|
:param values: items to check for key/value pairs
|
||||||
|
:return: True if all values are key/value pairs, False otherwise
|
||||||
|
:rtype: bool
|
||||||
|
"""
|
||||||
if len(values) == 0:
|
if len(values) == 0:
|
||||||
return False
|
return False
|
||||||
for v in values:
|
for v in values:
|
||||||
|
|
@ -368,13 +486,18 @@ class Configurable(object):
|
||||||
return True
|
return True
|
||||||
|
|
||||||
def getkeyvaluelist(self):
|
def getkeyvaluelist(self):
|
||||||
''' Helper to return a list of (key, value) tuples. Keys come from
|
"""
|
||||||
self._confmatrix and values are instance attributes.
|
Helper to return a list of (key, value) tuples. Keys come from
|
||||||
'''
|
configuration matrix and values are instance attributes.
|
||||||
r = []
|
|
||||||
for k in self.getnames():
|
|
||||||
if hasattr(self, k):
|
|
||||||
r.append((k, getattr(self, k)))
|
|
||||||
return r
|
|
||||||
|
|
||||||
|
:return: tuples of key value pairs
|
||||||
|
:rtype: list
|
||||||
|
"""
|
||||||
|
key_values = []
|
||||||
|
|
||||||
|
for name in self.getnames():
|
||||||
|
if hasattr(self, name):
|
||||||
|
value = getattr(self, name)
|
||||||
|
key_values.append((name, value))
|
||||||
|
|
||||||
|
return key_values
|
||||||
|
|
|
||||||
|
|
@ -1,32 +1,50 @@
|
||||||
#
|
"""
|
||||||
# CORE
|
Defines the basic objects for CORE emulation: the PyCoreObj base class, along with PyCoreNode,
|
||||||
# Copyright (c)2010-2013 the Boeing Company.
|
PyCoreNet, and PyCoreNetIf.
|
||||||
# See the LICENSE file included in this distribution.
|
"""
|
||||||
#
|
|
||||||
# authors: Tom Goff <thomas.goff@boeing.com>
|
import os
|
||||||
# Jeff Ahrenholz <jeffrey.m.ahrenholz@boeing.com>
|
import shutil
|
||||||
#
|
import socket
|
||||||
'''
|
import threading
|
||||||
coreobj.py: defines the basic objects for emulation: the PyCoreObj base class,
|
from socket import AF_INET
|
||||||
along with PyCoreNode, PyCoreNet, and PyCoreNetIf
|
from socket import AF_INET6
|
||||||
'''
|
|
||||||
import sys, threading, os, shutil
|
|
||||||
|
|
||||||
from core.api import coreapi
|
from core.api import coreapi
|
||||||
from core.misc.ipaddr import *
|
from core.data import NodeData, LinkData
|
||||||
|
from core.enumerations import LinkTlvs
|
||||||
|
from core.enumerations import LinkTypes
|
||||||
|
from core.misc import ipaddress
|
||||||
|
|
||||||
|
|
||||||
class Position(object):
|
class Position(object):
|
||||||
''' Helper class for Cartesian coordinate position
|
"""
|
||||||
'''
|
Helper class for Cartesian coordinate position
|
||||||
|
"""
|
||||||
|
|
||||||
def __init__(self, x=None, y=None, z=None):
|
def __init__(self, x=None, y=None, z=None):
|
||||||
self.x = None
|
"""
|
||||||
self.y = None
|
Creates a Position instance.
|
||||||
self.z = None
|
|
||||||
self.set(x, y, z)
|
:param x: x position
|
||||||
|
:param y: y position
|
||||||
|
:param z: z position
|
||||||
|
:return:
|
||||||
|
"""
|
||||||
|
self.x = x
|
||||||
|
self.y = y
|
||||||
|
self.z = z
|
||||||
|
|
||||||
def set(self, x=None, y=None, z=None):
|
def set(self, x=None, y=None, z=None):
|
||||||
''' Returns True if the position has actually changed.
|
"""
|
||||||
'''
|
Returns True if the position has actually changed.
|
||||||
|
|
||||||
|
:param x: x position
|
||||||
|
:param y: y position
|
||||||
|
:param z: z position
|
||||||
|
:return: True if position changed, False otherwise
|
||||||
|
:rtype: bool
|
||||||
|
"""
|
||||||
if self.x == x and self.y == y and self.z == z:
|
if self.x == x and self.y == y and self.z == z:
|
||||||
return False
|
return False
|
||||||
self.x = x
|
self.x = x
|
||||||
|
|
@ -35,20 +53,36 @@ class Position(object):
|
||||||
return True
|
return True
|
||||||
|
|
||||||
def get(self):
|
def get(self):
|
||||||
''' Fetch the (x,y,z) position tuple.
|
"""
|
||||||
'''
|
Retrieve x,y,z position.
|
||||||
return (self.x, self.y, self.z)
|
|
||||||
|
:return: x,y,z position tuple
|
||||||
|
:rtype: tuple
|
||||||
|
"""
|
||||||
|
return self.x, self.y, self.z
|
||||||
|
|
||||||
|
|
||||||
class PyCoreObj(object):
|
class PyCoreObj(object):
|
||||||
''' Base class for pycore objects (nodes and nets)
|
"""
|
||||||
'''
|
Base class for CORE objects (nodes and networks)
|
||||||
|
"""
|
||||||
apitype = None
|
apitype = None
|
||||||
|
|
||||||
def __init__(self, session, objid = None, name = None, verbose = False,
|
# TODO: appears start has no usage, verify and remove
|
||||||
start = True):
|
def __init__(self, session, objid=None, name=None, start=True):
|
||||||
|
"""
|
||||||
|
Creates a PyCoreObj instance.
|
||||||
|
|
||||||
|
:param core.session.Session session: CORE session object
|
||||||
|
:param int objid: object id
|
||||||
|
:param str name: object name
|
||||||
|
:param bool start: start value
|
||||||
|
:return:
|
||||||
|
"""
|
||||||
|
|
||||||
self.session = session
|
self.session = session
|
||||||
if objid is None:
|
if objid is None:
|
||||||
objid = session.getobjid()
|
objid = session.get_object_id()
|
||||||
self.objid = objid
|
self.objid = objid
|
||||||
if name is None:
|
if name is None:
|
||||||
name = "o%s" % self.objid
|
name = "o%s" % self.objid
|
||||||
|
|
@ -59,242 +93,360 @@ class PyCoreObj(object):
|
||||||
self.canvas = None
|
self.canvas = None
|
||||||
self.icon = None
|
self.icon = None
|
||||||
self.opaque = None
|
self.opaque = None
|
||||||
self.verbose = verbose
|
|
||||||
self.position = Position()
|
self.position = Position()
|
||||||
|
|
||||||
def startup(self):
|
def startup(self):
|
||||||
''' Each object implements its own startup method.
|
"""
|
||||||
'''
|
Each object implements its own startup method.
|
||||||
|
|
||||||
|
:return: nothing
|
||||||
|
"""
|
||||||
raise NotImplementedError
|
raise NotImplementedError
|
||||||
|
|
||||||
def shutdown(self):
|
def shutdown(self):
|
||||||
''' Each object implements its own shutdown method.
|
"""
|
||||||
'''
|
Each object implements its own shutdown method.
|
||||||
|
|
||||||
|
:return: nothing
|
||||||
|
"""
|
||||||
raise NotImplementedError
|
raise NotImplementedError
|
||||||
|
|
||||||
def setposition(self, x=None, y=None, z=None):
|
def setposition(self, x=None, y=None, z=None):
|
||||||
''' Set the (x,y,z) position of the object.
|
"""
|
||||||
'''
|
Set the (x,y,z) position of the object.
|
||||||
|
|
||||||
|
:param x: x position
|
||||||
|
:param y: y position
|
||||||
|
:param z: z position
|
||||||
|
:return: True if position changed, False otherwise
|
||||||
|
:rtype: bool
|
||||||
|
"""
|
||||||
return self.position.set(x=x, y=y, z=z)
|
return self.position.set(x=x, y=y, z=z)
|
||||||
|
|
||||||
def getposition(self):
|
def getposition(self):
|
||||||
''' Return an (x,y,z) tuple representing this object's position.
|
"""
|
||||||
'''
|
Return an (x,y,z) tuple representing this object's position.
|
||||||
|
|
||||||
|
:return: x,y,z position tuple
|
||||||
|
:rtype: tuple
|
||||||
|
"""
|
||||||
return self.position.get()
|
return self.position.get()
|
||||||
|
|
||||||
def ifname(self, ifindex):
|
def ifname(self, ifindex):
|
||||||
return self.netif(ifindex).name
|
"""
|
||||||
|
Retrieve interface name for index.
|
||||||
|
|
||||||
|
:param int ifindex: interface index
|
||||||
|
:return: interface name
|
||||||
|
:rtype: str
|
||||||
|
"""
|
||||||
|
return self._netif[ifindex].name
|
||||||
|
|
||||||
def netifs(self, sort=False):
|
def netifs(self, sort=False):
|
||||||
''' Iterate over attached network interfaces.
|
"""
|
||||||
'''
|
Retrieve network interfaces, sorted if desired.
|
||||||
|
|
||||||
|
:param bool sort: boolean used to determine if interfaces should be sorted
|
||||||
|
:return: network interfaces
|
||||||
|
:rtype: list
|
||||||
|
"""
|
||||||
if sort:
|
if sort:
|
||||||
return map(lambda k: self._netif[k], sorted(self._netif.keys()))
|
return map(lambda k: self._netif[k], sorted(self._netif.keys()))
|
||||||
else:
|
else:
|
||||||
return self._netif.itervalues()
|
return self._netif.itervalues()
|
||||||
|
|
||||||
def numnetif(self):
|
def numnetif(self):
|
||||||
''' Return the attached interface count.
|
"""
|
||||||
'''
|
Return the attached interface count.
|
||||||
|
|
||||||
|
:return: number of network interfaces
|
||||||
|
:rtype: int
|
||||||
|
"""
|
||||||
return len(self._netif)
|
return len(self._netif)
|
||||||
|
|
||||||
def getifindex(self, netif):
|
def getifindex(self, netif):
|
||||||
|
"""
|
||||||
|
Retrieve index for an interface.
|
||||||
|
|
||||||
|
:param PyCoreNetIf netif: interface to get index for
|
||||||
|
:return: interface index if found, -1 otherwise
|
||||||
|
:rtype: int
|
||||||
|
"""
|
||||||
|
|
||||||
for ifindex in self._netif:
|
for ifindex in self._netif:
|
||||||
if self._netif[ifindex] is netif:
|
if self._netif[ifindex] is netif:
|
||||||
return ifindex
|
return ifindex
|
||||||
|
|
||||||
return -1
|
return -1
|
||||||
|
|
||||||
def newifindex(self):
|
def newifindex(self):
|
||||||
|
"""
|
||||||
|
Create a new interface index.
|
||||||
|
|
||||||
|
:return: interface index
|
||||||
|
:rtype: int
|
||||||
|
"""
|
||||||
while self.ifindex in self._netif:
|
while self.ifindex in self._netif:
|
||||||
self.ifindex += 1
|
self.ifindex += 1
|
||||||
ifindex = self.ifindex
|
ifindex = self.ifindex
|
||||||
self.ifindex += 1
|
self.ifindex += 1
|
||||||
return ifindex
|
return ifindex
|
||||||
|
|
||||||
def tonodemsg(self, flags):
|
def data(self, message_type):
|
||||||
''' Build a CORE API Node Message for this object. Both nodes and
|
"""
|
||||||
networks can be represented by a Node Message.
|
Build a data object for this node.
|
||||||
'''
|
|
||||||
|
:param message_type: purpose for the data object we are creating
|
||||||
|
:return: node data object
|
||||||
|
:rtype: core.data.NodeData
|
||||||
|
"""
|
||||||
if self.apitype is None:
|
if self.apitype is None:
|
||||||
return None
|
return None
|
||||||
tlvdata = ""
|
|
||||||
(x, y, z) = self.getposition()
|
x, y, z = self.getposition()
|
||||||
tlvdata += coreapi.CoreNodeTlv.pack(coreapi.CORE_TLV_NODE_NUMBER,
|
|
||||||
self.objid)
|
model = None
|
||||||
tlvdata += coreapi.CoreNodeTlv.pack(coreapi.CORE_TLV_NODE_TYPE,
|
if hasattr(self, "type"):
|
||||||
self.apitype)
|
model = self.type
|
||||||
tlvdata += coreapi.CoreNodeTlv.pack(coreapi.CORE_TLV_NODE_NAME,
|
|
||||||
self.name)
|
emulation_server = None
|
||||||
if hasattr(self, "type") and self.type is not None:
|
if hasattr(self, "server"):
|
||||||
tlvdata += coreapi.CoreNodeTlv.pack(coreapi.CORE_TLV_NODE_MODEL,
|
emulation_server = self.server
|
||||||
self.type)
|
|
||||||
if hasattr(self, "server") and self.server is not None:
|
services = None
|
||||||
tlvdata += coreapi.CoreNodeTlv.pack(coreapi.CORE_TLV_NODE_EMUSRV,
|
|
||||||
self.server)
|
|
||||||
if hasattr(self, "services") and len(self.services) != 0:
|
if hasattr(self, "services") and len(self.services) != 0:
|
||||||
nodeservices = []
|
nodeservices = []
|
||||||
for s in self.services:
|
for s in self.services:
|
||||||
nodeservices.append(s._name)
|
nodeservices.append(s._name)
|
||||||
tlvdata += coreapi.CoreNodeTlv.pack(coreapi.CORE_TLV_NODE_SERVICES,
|
services = "|".join(nodeservices)
|
||||||
"|".join(nodeservices))
|
|
||||||
|
|
||||||
|
node_data = NodeData(
|
||||||
|
message_type=message_type,
|
||||||
|
id=self.objid,
|
||||||
|
node_type=self.apitype,
|
||||||
|
name=self.name,
|
||||||
|
emulation_id=self.objid,
|
||||||
|
canvas=self.canvas,
|
||||||
|
icon=self.icon,
|
||||||
|
opaque=self.opaque,
|
||||||
|
x_position=x,
|
||||||
|
y_position=y,
|
||||||
|
model=model,
|
||||||
|
emulation_server=emulation_server,
|
||||||
|
services=services
|
||||||
|
)
|
||||||
|
|
||||||
if x is not None:
|
return node_data
|
||||||
tlvdata += coreapi.CoreNodeTlv.pack(coreapi.CORE_TLV_NODE_XPOS, x)
|
|
||||||
if y is not None:
|
|
||||||
tlvdata += coreapi.CoreNodeTlv.pack(coreapi.CORE_TLV_NODE_YPOS, y)
|
|
||||||
if self.canvas is not None:
|
|
||||||
tlvdata += coreapi.CoreNodeTlv.pack(coreapi.CORE_TLV_NODE_CANVAS,
|
|
||||||
self.canvas)
|
|
||||||
tlvdata += coreapi.CoreNodeTlv.pack(coreapi.CORE_TLV_NODE_EMUID,
|
|
||||||
self.objid)
|
|
||||||
if self.icon is not None:
|
|
||||||
tlvdata += coreapi.CoreNodeTlv.pack(coreapi.CORE_TLV_NODE_ICON,
|
|
||||||
self.icon)
|
|
||||||
if self.opaque is not None:
|
|
||||||
tlvdata += coreapi.CoreNodeTlv.pack(coreapi.CORE_TLV_NODE_OPAQUE,
|
|
||||||
self.opaque)
|
|
||||||
msg = coreapi.CoreNodeMessage.pack(flags, tlvdata)
|
|
||||||
return msg
|
|
||||||
|
|
||||||
def tolinkmsgs(self, flags):
|
def all_link_data(self, flags):
|
||||||
''' Build CORE API Link Messages for this object. There is no default
|
"""
|
||||||
|
Build CORE Link data for this object. There is no default
|
||||||
method for PyCoreObjs as PyCoreNodes do not implement this but
|
method for PyCoreObjs as PyCoreNodes do not implement this but
|
||||||
PyCoreNets do.
|
PyCoreNets do.
|
||||||
'''
|
|
||||||
|
:param flags: message flags
|
||||||
|
:return: list of link data
|
||||||
|
:rtype: link
|
||||||
|
"""
|
||||||
return []
|
return []
|
||||||
|
|
||||||
def info(self, msg):
|
|
||||||
''' Utility method for printing informational messages when verbose
|
|
||||||
is turned on.
|
|
||||||
'''
|
|
||||||
if self.verbose:
|
|
||||||
print "%s: %s" % (self.name, msg)
|
|
||||||
sys.stdout.flush()
|
|
||||||
|
|
||||||
def warn(self, msg):
|
|
||||||
''' Utility method for printing warning/error messages
|
|
||||||
'''
|
|
||||||
print >> sys.stderr, "%s: %s" % (self.name, msg)
|
|
||||||
sys.stderr.flush()
|
|
||||||
|
|
||||||
def exception(self, level, source, text):
|
|
||||||
''' Generate an Exception Message for this session, providing this
|
|
||||||
object number.
|
|
||||||
'''
|
|
||||||
if self.session:
|
|
||||||
id = None
|
|
||||||
if isinstance(self.objid, int):
|
|
||||||
id = self.objid
|
|
||||||
elif isinstance(self.objid, str) and self.objid.isdigit():
|
|
||||||
id = int(self.objid)
|
|
||||||
self.session.exception(level, source, id, text)
|
|
||||||
|
|
||||||
|
|
||||||
class PyCoreNode(PyCoreObj):
|
class PyCoreNode(PyCoreObj):
|
||||||
''' Base class for nodes
|
"""
|
||||||
'''
|
Base class for CORE nodes.
|
||||||
def __init__(self, session, objid = None, name = None, verbose = False,
|
"""
|
||||||
start = True):
|
|
||||||
''' Initialization for node objects.
|
# TODO: start seems like it should go away
|
||||||
'''
|
def __init__(self, session, objid=None, name=None, start=True):
|
||||||
PyCoreObj.__init__(self, session, objid, name, verbose=verbose,
|
"""
|
||||||
start=start)
|
Create a PyCoreNode instance.
|
||||||
|
|
||||||
|
:param core.session.Session session: CORE session object
|
||||||
|
:param int objid: object id
|
||||||
|
:param str name: object name
|
||||||
|
:param bool start: boolean for starting
|
||||||
|
"""
|
||||||
|
PyCoreObj.__init__(self, session, objid, name, start=start)
|
||||||
self.services = []
|
self.services = []
|
||||||
if not hasattr(self, "type"):
|
if not hasattr(self, "type"):
|
||||||
self.type = None
|
self.type = None
|
||||||
self.nodedir = None
|
self.nodedir = None
|
||||||
|
self.tmpnodedir = False
|
||||||
|
|
||||||
|
# TODO: getter method that should not be needed
|
||||||
def nodeid(self):
|
def nodeid(self):
|
||||||
|
"""
|
||||||
|
Retrieve node id.
|
||||||
|
|
||||||
|
:return: node id
|
||||||
|
:rtype: int
|
||||||
|
"""
|
||||||
return self.objid
|
return self.objid
|
||||||
|
|
||||||
def addservice(self, service):
|
def addservice(self, service):
|
||||||
|
"""
|
||||||
|
Add a services to the service list.
|
||||||
|
|
||||||
|
:param core.service.CoreService service: service to add
|
||||||
|
:return: nothing
|
||||||
|
"""
|
||||||
if service is not None:
|
if service is not None:
|
||||||
self.services.append(service)
|
self.services.append(service)
|
||||||
|
|
||||||
def makenodedir(self):
|
def makenodedir(self):
|
||||||
|
"""
|
||||||
|
Create the node directory.
|
||||||
|
|
||||||
|
:return: nothing
|
||||||
|
"""
|
||||||
if self.nodedir is None:
|
if self.nodedir is None:
|
||||||
self.nodedir = \
|
self.nodedir = os.path.join(self.session.session_dir, self.name + ".conf")
|
||||||
os.path.join(self.session.sessiondir, self.name + ".conf")
|
|
||||||
os.makedirs(self.nodedir)
|
os.makedirs(self.nodedir)
|
||||||
self.tmpnodedir = True
|
self.tmpnodedir = True
|
||||||
else:
|
else:
|
||||||
self.tmpnodedir = False
|
self.tmpnodedir = False
|
||||||
|
|
||||||
def rmnodedir(self):
|
def rmnodedir(self):
|
||||||
if hasattr(self.session.options, 'preservedir'):
|
"""
|
||||||
if self.session.options.preservedir == '1':
|
Remove the node directory, unless preserve directory has been set.
|
||||||
|
|
||||||
|
:return: nothing
|
||||||
|
"""
|
||||||
|
preserve = getattr(self.session.options, "preservedir", None)
|
||||||
|
if preserve == "1":
|
||||||
return
|
return
|
||||||
|
|
||||||
if self.tmpnodedir:
|
if self.tmpnodedir:
|
||||||
shutil.rmtree(self.nodedir, ignore_errors=True)
|
shutil.rmtree(self.nodedir, ignore_errors=True)
|
||||||
|
|
||||||
def addnetif(self, netif, ifindex):
|
def addnetif(self, netif, ifindex):
|
||||||
|
"""
|
||||||
|
Add network interface to node and set the network interface index if successful.
|
||||||
|
|
||||||
|
:param PyCoreNetIf netif: network interface to add
|
||||||
|
:param int ifindex: interface index
|
||||||
|
:return: nothing
|
||||||
|
"""
|
||||||
if ifindex in self._netif:
|
if ifindex in self._netif:
|
||||||
raise ValueError, "ifindex %s already exists" % ifindex
|
raise ValueError("ifindex %s already exists" % ifindex)
|
||||||
self._netif[ifindex] = netif
|
self._netif[ifindex] = netif
|
||||||
|
# TODO: this hould have probably been set ahead, seems bad to me, check for failure and fix
|
||||||
netif.netindex = ifindex
|
netif.netindex = ifindex
|
||||||
|
|
||||||
def delnetif(self, ifindex):
|
def delnetif(self, ifindex):
|
||||||
|
"""
|
||||||
|
Delete a network interface
|
||||||
|
|
||||||
|
:param int ifindex: interface index to delete
|
||||||
|
:return: nothing
|
||||||
|
"""
|
||||||
if ifindex not in self._netif:
|
if ifindex not in self._netif:
|
||||||
raise ValueError, "ifindex %s does not exist" % ifindex
|
raise ValueError("ifindex %s does not exist" % ifindex)
|
||||||
netif = self._netif.pop(ifindex)
|
netif = self._netif.pop(ifindex)
|
||||||
netif.shutdown()
|
netif.shutdown()
|
||||||
del netif
|
del netif
|
||||||
|
|
||||||
|
# TODO: net parameter is not used, remove
|
||||||
def netif(self, ifindex, net=None):
|
def netif(self, ifindex, net=None):
|
||||||
|
"""
|
||||||
|
Retrieve network interface.
|
||||||
|
|
||||||
|
:param int ifindex: index of interface to retrieve
|
||||||
|
:param PyCoreNetIf net: network node
|
||||||
|
:return: network interface, or None if not found
|
||||||
|
:rtype: PyCoreNetIf
|
||||||
|
"""
|
||||||
if ifindex in self._netif:
|
if ifindex in self._netif:
|
||||||
return self._netif[ifindex]
|
return self._netif[ifindex]
|
||||||
else:
|
else:
|
||||||
return None
|
return None
|
||||||
|
|
||||||
def attachnet(self, ifindex, net):
|
def attachnet(self, ifindex, net):
|
||||||
|
"""
|
||||||
|
Attach a network.
|
||||||
|
|
||||||
|
:param int ifindex: interface of index to attach
|
||||||
|
:param PyCoreNetIf net: network to attach
|
||||||
|
:return:
|
||||||
|
"""
|
||||||
if ifindex not in self._netif:
|
if ifindex not in self._netif:
|
||||||
raise ValueError, "ifindex %s does not exist" % ifindex
|
raise ValueError("ifindex %s does not exist" % ifindex)
|
||||||
self._netif[ifindex].attachnet(net)
|
self._netif[ifindex].attachnet(net)
|
||||||
|
|
||||||
def detachnet(self, ifindex):
|
def detachnet(self, ifindex):
|
||||||
|
"""
|
||||||
|
Detach network interface.
|
||||||
|
|
||||||
|
:param int ifindex: interface index to detach
|
||||||
|
:return: nothing
|
||||||
|
"""
|
||||||
if ifindex not in self._netif:
|
if ifindex not in self._netif:
|
||||||
raise ValueError, "ifindex %s does not exist" % ifindex
|
raise ValueError("ifindex %s does not exist" % ifindex)
|
||||||
self._netif[ifindex].detachnet()
|
self._netif[ifindex].detachnet()
|
||||||
|
|
||||||
def setposition(self, x=None, y=None, z=None):
|
def setposition(self, x=None, y=None, z=None):
|
||||||
changed = PyCoreObj.setposition(self, x = x, y = y, z = z)
|
"""
|
||||||
if not changed:
|
Set position.
|
||||||
# save extra interface range calculations
|
|
||||||
return
|
:param x: x position
|
||||||
|
:param y: y position
|
||||||
|
:param z: z position
|
||||||
|
:return: nothing
|
||||||
|
"""
|
||||||
|
changed = super(PyCoreNode, self).setposition(x, y, z)
|
||||||
|
if changed:
|
||||||
for netif in self.netifs(sort=True):
|
for netif in self.netifs(sort=True):
|
||||||
netif.setposition(x, y, z)
|
netif.setposition(x, y, z)
|
||||||
|
|
||||||
def commonnets(self, obj, want_ctrl=False):
|
def commonnets(self, obj, want_ctrl=False):
|
||||||
''' Given another node or net object, return common networks between
|
"""
|
||||||
|
Given another node or net object, return common networks between
|
||||||
this node and that object. A list of tuples is returned, with each tuple
|
this node and that object. A list of tuples is returned, with each tuple
|
||||||
consisting of (network, interface1, interface2).
|
consisting of (network, interface1, interface2).
|
||||||
'''
|
|
||||||
r = []
|
:param obj: object to get common network with
|
||||||
|
:param want_ctrl: flag set to determine if control network are wanted
|
||||||
|
:return: tuples of common networks
|
||||||
|
:rtype: list
|
||||||
|
"""
|
||||||
|
common = []
|
||||||
for netif1 in self.netifs():
|
for netif1 in self.netifs():
|
||||||
if not want_ctrl and hasattr(netif1, 'control'):
|
if not want_ctrl and hasattr(netif1, "control"):
|
||||||
continue
|
continue
|
||||||
for netif2 in obj.netifs():
|
for netif2 in obj.netifs():
|
||||||
if netif1.net == netif2.net:
|
if netif1.net == netif2.net:
|
||||||
r += (netif1.net, netif1, netif2),
|
common.append((netif1.net, netif1, netif2))
|
||||||
return r
|
|
||||||
|
|
||||||
|
return common
|
||||||
|
|
||||||
|
|
||||||
class PyCoreNet(PyCoreObj):
|
class PyCoreNet(PyCoreObj):
|
||||||
''' Base class for networks
|
"""
|
||||||
'''
|
Base class for networks
|
||||||
linktype = coreapi.CORE_LINK_WIRED
|
"""
|
||||||
|
linktype = LinkTypes.WIRED.value
|
||||||
|
|
||||||
def __init__(self, session, objid, name, verbose = False, start = True):
|
# TODO: remove start if appropriate
|
||||||
''' Initialization for network objects.
|
def __init__(self, session, objid, name, start=True):
|
||||||
'''
|
"""
|
||||||
PyCoreObj.__init__(self, session, objid, name, verbose=verbose,
|
Create a PyCoreNet instance.
|
||||||
start=start)
|
|
||||||
|
:param core.session.Session session: CORE session object
|
||||||
|
:param int objid: object id
|
||||||
|
:param str name: object name
|
||||||
|
:param bool start: should object start
|
||||||
|
"""
|
||||||
|
PyCoreObj.__init__(self, session, objid, name, start=start)
|
||||||
self._linked = {}
|
self._linked = {}
|
||||||
self._linked_lock = threading.Lock()
|
self._linked_lock = threading.Lock()
|
||||||
|
|
||||||
def attach(self, netif):
|
def attach(self, netif):
|
||||||
|
"""
|
||||||
|
Attach network interface.
|
||||||
|
|
||||||
|
:param PyCoreNetIf netif: network interface to attach
|
||||||
|
:return: nothing
|
||||||
|
"""
|
||||||
i = self.newifindex()
|
i = self.newifindex()
|
||||||
self._netif[i] = netif
|
self._netif[i] = netif
|
||||||
netif.netifi = i
|
netif.netifi = i
|
||||||
|
|
@ -302,43 +454,53 @@ class PyCoreNet(PyCoreObj):
|
||||||
self._linked[netif] = {}
|
self._linked[netif] = {}
|
||||||
|
|
||||||
def detach(self, netif):
|
def detach(self, netif):
|
||||||
|
"""
|
||||||
|
Detach network interface.
|
||||||
|
|
||||||
|
:param PyCoreNetIf netif: network interface to detach
|
||||||
|
:return: nothing
|
||||||
|
"""
|
||||||
del self._netif[netif.netifi]
|
del self._netif[netif.netifi]
|
||||||
netif.netifi = None
|
netif.netifi = None
|
||||||
with self._linked_lock:
|
with self._linked_lock:
|
||||||
del self._linked[netif]
|
del self._linked[netif]
|
||||||
|
|
||||||
|
# TODO: needs to be abstracted out, seems like it may be ok to remove
|
||||||
def netifparamstolink(self, netif):
|
def netifparamstolink(self, netif):
|
||||||
''' Helper for tolinkmsgs() to build TLVs having link parameters
|
"""
|
||||||
from interface parameters.
|
Helper for tolinkmsgs() to build TLVs having link parameters from interface parameters.
|
||||||
'''
|
|
||||||
|
:param PyCoreNetIf netif: network interface to retrieve params from
|
||||||
|
:return: tlv data
|
||||||
|
"""
|
||||||
|
|
||||||
|
delay = netif.getparam("delay")
|
||||||
|
bw = netif.getparam("bw")
|
||||||
|
loss = netif.getparam("loss")
|
||||||
|
duplicate = netif.getparam("duplicate")
|
||||||
|
jitter = netif.getparam("jitter")
|
||||||
|
|
||||||
tlvdata = ""
|
tlvdata = ""
|
||||||
delay = netif.getparam('delay')
|
|
||||||
bw = netif.getparam('bw')
|
|
||||||
loss = netif.getparam('loss')
|
|
||||||
duplicate = netif.getparam('duplicate')
|
|
||||||
jitter = netif.getparam('jitter')
|
|
||||||
if delay is not None:
|
if delay is not None:
|
||||||
tlvdata += coreapi.CoreLinkTlv.pack(coreapi.CORE_TLV_LINK_DELAY,
|
tlvdata += coreapi.CoreLinkTlv.pack(LinkTlvs.DELAY.value, delay)
|
||||||
delay)
|
|
||||||
if bw is not None:
|
if bw is not None:
|
||||||
tlvdata += coreapi.CoreLinkTlv.pack(coreapi.CORE_TLV_LINK_BW, bw)
|
tlvdata += coreapi.CoreLinkTlv.pack(LinkTlvs.BANDWIDTH.value, bw)
|
||||||
if loss is not None:
|
if loss is not None:
|
||||||
tlvdata += coreapi.CoreLinkTlv.pack(coreapi.CORE_TLV_LINK_PER,
|
tlvdata += coreapi.CoreLinkTlv.pack(LinkTlvs.PER.value, str(loss))
|
||||||
str(loss))
|
|
||||||
if duplicate is not None:
|
if duplicate is not None:
|
||||||
tlvdata += coreapi.CoreLinkTlv.pack(coreapi.CORE_TLV_LINK_DUP,
|
tlvdata += coreapi.CoreLinkTlv.pack(LinkTlvs.DUP.value, str(duplicate))
|
||||||
str(duplicate))
|
|
||||||
if jitter is not None:
|
if jitter is not None:
|
||||||
tlvdata += coreapi.CoreLinkTlv.pack(coreapi.CORE_TLV_LINK_JITTER,
|
tlvdata += coreapi.CoreLinkTlv.pack(LinkTlvs.JITTER.value, jitter)
|
||||||
jitter)
|
|
||||||
return tlvdata
|
return tlvdata
|
||||||
|
|
||||||
|
def all_link_data(self, flags):
|
||||||
|
"""
|
||||||
|
Build link data objects for this network. Each link object describes a link
|
||||||
|
between this network and a node.
|
||||||
|
"""
|
||||||
|
all_links = []
|
||||||
|
|
||||||
def tolinkmsgs(self, flags):
|
|
||||||
''' Build CORE API Link Messages for this network. Each link message
|
|
||||||
describes a link between this network and a node.
|
|
||||||
'''
|
|
||||||
msgs = []
|
|
||||||
# build a link message from this network node to each node having a
|
# build a link message from this network node to each node having a
|
||||||
# connected interface
|
# connected interface
|
||||||
for netif in self.netifs(sort=True):
|
for netif in self.netifs(sort=True):
|
||||||
|
|
@ -359,61 +521,85 @@ class PyCoreNet(PyCoreObj):
|
||||||
if netif.getparams() != upstream_params:
|
if netif.getparams() != upstream_params:
|
||||||
uni = True
|
uni = True
|
||||||
|
|
||||||
tlvdata = ""
|
unidirectional = 0
|
||||||
tlvdata += coreapi.CoreLinkTlv.pack(coreapi.CORE_TLV_LINK_N1NUMBER,
|
|
||||||
self.objid)
|
|
||||||
tlvdata += coreapi.CoreLinkTlv.pack(coreapi.CORE_TLV_LINK_N2NUMBER,
|
|
||||||
otherobj.objid)
|
|
||||||
tlvdata += self.netifparamstolink(netif)
|
|
||||||
tlvdata += coreapi.CoreLinkTlv.pack(coreapi.CORE_TLV_LINK_TYPE,
|
|
||||||
self.linktype)
|
|
||||||
if uni:
|
if uni:
|
||||||
tlvdata += coreapi.CoreLinkTlv.pack(coreapi.CORE_TLV_LINK_UNI,
|
unidirectional = 1
|
||||||
1)
|
|
||||||
tlvdata += coreapi.CoreLinkTlv.pack(coreapi.CORE_TLV_LINK_IF2NUM,
|
interface2_ip4 = None
|
||||||
otherobj.getifindex(netif))
|
interface2_ip4_mask = None
|
||||||
if netif.hwaddr:
|
interface2_ip6 = None
|
||||||
tlvdata += \
|
interface2_ip6_mask = None
|
||||||
coreapi.CoreLinkTlv.pack(coreapi.CORE_TLV_LINK_IF2MAC,
|
for address in netif.addrlist:
|
||||||
netif.hwaddr)
|
ip, sep, mask = address.partition('/')
|
||||||
for addr in netif.addrlist:
|
|
||||||
(ip, sep, mask) = addr.partition('/')
|
|
||||||
mask = int(mask)
|
mask = int(mask)
|
||||||
if isIPv4Address(ip):
|
if ipaddress.is_ipv4_address(ip):
|
||||||
family = AF_INET
|
family = AF_INET
|
||||||
tlvtypeip = coreapi.CORE_TLV_LINK_IF2IP4
|
ipl = socket.inet_pton(family, ip)
|
||||||
tlvtypemask = coreapi.CORE_TLV_LINK_IF2IP4MASK
|
interface2_ip4 = ipaddress.IpAddress(af=family, address=ipl)
|
||||||
|
interface2_ip4_mask = mask
|
||||||
else:
|
else:
|
||||||
family = AF_INET6
|
family = AF_INET6
|
||||||
tlvtypeip = coreapi.CORE_TLV_LINK_IF2IP6
|
|
||||||
tlvtypemask = coreapi.CORE_TLV_LINK_IF2IP6MASK
|
|
||||||
ipl = socket.inet_pton(family, ip)
|
ipl = socket.inet_pton(family, ip)
|
||||||
tlvdata += coreapi.CoreLinkTlv.pack(tlvtypeip, \
|
interface2_ip6 = ipaddress.IpAddress(af=family, address=ipl)
|
||||||
IPAddr(af=family, addr=ipl))
|
interface2_ip6_mask = mask
|
||||||
tlvdata += coreapi.CoreLinkTlv.pack(tlvtypemask, mask)
|
|
||||||
|
# TODO: not currently used
|
||||||
|
# loss = netif.getparam('loss')
|
||||||
|
link_data = LinkData(
|
||||||
|
message_type=flags,
|
||||||
|
node1_id=self.objid,
|
||||||
|
node2_id=otherobj.objid,
|
||||||
|
link_type=self.linktype,
|
||||||
|
unidirectional=unidirectional,
|
||||||
|
interface2_id=otherobj.getifindex(netif),
|
||||||
|
interface2_mac=netif.hwaddr,
|
||||||
|
interface2_ip4=interface2_ip4,
|
||||||
|
interface2_ip4_mask=interface2_ip4_mask,
|
||||||
|
interface2_ip6=interface2_ip6,
|
||||||
|
interface2_ip6_mask=interface2_ip6_mask,
|
||||||
|
delay=netif.getparam("delay"),
|
||||||
|
bandwidth=netif.getparam("bw"),
|
||||||
|
dup=netif.getparam("duplicate"),
|
||||||
|
jitter=netif.getparam("jitter")
|
||||||
|
)
|
||||||
|
|
||||||
|
all_links.append(link_data)
|
||||||
|
|
||||||
msg = coreapi.CoreLinkMessage.pack(flags, tlvdata)
|
|
||||||
msgs.append(msg)
|
|
||||||
if not uni:
|
if not uni:
|
||||||
continue
|
continue
|
||||||
# build a 2nd link message for any upstream link parameters
|
|
||||||
tlvdata = ""
|
|
||||||
tlvdata += coreapi.CoreLinkTlv.pack(coreapi.CORE_TLV_LINK_N1NUMBER,
|
|
||||||
otherobj.objid)
|
|
||||||
tlvdata += coreapi.CoreLinkTlv.pack(coreapi.CORE_TLV_LINK_N2NUMBER,
|
|
||||||
self.objid)
|
|
||||||
netif.swapparams('_params_up')
|
netif.swapparams('_params_up')
|
||||||
tlvdata += self.netifparamstolink(netif)
|
link_data = LinkData(
|
||||||
|
message_type=0,
|
||||||
|
node1_id=otherobj.objid,
|
||||||
|
node2_id=self.objid,
|
||||||
|
unidirectional=1,
|
||||||
|
delay=netif.getparam("delay"),
|
||||||
|
bandwidth=netif.getparam("bw"),
|
||||||
|
dup=netif.getparam("duplicate"),
|
||||||
|
jitter=netif.getparam("jitter")
|
||||||
|
)
|
||||||
netif.swapparams('_params_up')
|
netif.swapparams('_params_up')
|
||||||
tlvdata += coreapi.CoreLinkTlv.pack(coreapi.CORE_TLV_LINK_UNI, 1)
|
|
||||||
msg = coreapi.CoreLinkMessage.pack(0, tlvdata)
|
all_links.append(link_data)
|
||||||
msgs.append(msg)
|
|
||||||
return msgs
|
return all_links
|
||||||
|
|
||||||
|
|
||||||
class PyCoreNetIf(object):
|
class PyCoreNetIf(object):
|
||||||
''' Base class for interfaces.
|
"""
|
||||||
'''
|
Base class for network interfaces.
|
||||||
|
"""
|
||||||
|
|
||||||
def __init__(self, node, name, mtu):
|
def __init__(self, node, name, mtu):
|
||||||
|
"""
|
||||||
|
Creates a PyCoreNetIf instance.
|
||||||
|
|
||||||
|
:param node: node for interface
|
||||||
|
:param str name: interface name
|
||||||
|
:param mtu: mtu value
|
||||||
|
"""
|
||||||
|
|
||||||
self.node = node
|
self.node = node
|
||||||
self.name = name
|
self.name = name
|
||||||
if not isinstance(mtu, (int, long)):
|
if not isinstance(mtu, (int, long)):
|
||||||
|
|
@ -428,67 +614,119 @@ class PyCoreNetIf(object):
|
||||||
self.transport_type = None
|
self.transport_type = None
|
||||||
# interface index on the network
|
# interface index on the network
|
||||||
self.netindex = None
|
self.netindex = None
|
||||||
|
# index used to find flow data
|
||||||
|
self.flow_id = None
|
||||||
|
|
||||||
def startup(self):
|
def startup(self):
|
||||||
|
"""
|
||||||
|
Startup method for the interface.
|
||||||
|
|
||||||
|
:return: nothing
|
||||||
|
"""
|
||||||
pass
|
pass
|
||||||
|
|
||||||
def shutdown(self):
|
def shutdown(self):
|
||||||
|
"""
|
||||||
|
Shutdown method for the interface.
|
||||||
|
|
||||||
|
:return: nothing
|
||||||
|
"""
|
||||||
pass
|
pass
|
||||||
|
|
||||||
def attachnet(self, net):
|
def attachnet(self, net):
|
||||||
|
"""
|
||||||
|
Attach network.
|
||||||
|
|
||||||
|
:param PyCoreNet net: network to attach to
|
||||||
|
:return:nothing
|
||||||
|
"""
|
||||||
if self.net:
|
if self.net:
|
||||||
self.detachnet()
|
self.detachnet()
|
||||||
self.net = None
|
self.net = None
|
||||||
|
|
||||||
net.attach(self)
|
net.attach(self)
|
||||||
self.net = net
|
self.net = net
|
||||||
|
|
||||||
def detachnet(self):
|
def detachnet(self):
|
||||||
|
"""
|
||||||
|
Detach from a network.
|
||||||
|
|
||||||
|
:return: nothing
|
||||||
|
"""
|
||||||
if self.net is not None:
|
if self.net is not None:
|
||||||
self.net.detach(self)
|
self.net.detach(self)
|
||||||
|
|
||||||
def addaddr(self, addr):
|
def addaddr(self, addr):
|
||||||
|
"""
|
||||||
|
Add address.
|
||||||
|
|
||||||
|
:param str addr: address to add
|
||||||
|
:return: nothing
|
||||||
|
"""
|
||||||
|
|
||||||
self.addrlist.append(addr)
|
self.addrlist.append(addr)
|
||||||
|
|
||||||
def deladdr(self, addr):
|
def deladdr(self, addr):
|
||||||
|
"""
|
||||||
|
Delete address.
|
||||||
|
|
||||||
|
:param str addr: address to delete
|
||||||
|
:return: nothing
|
||||||
|
"""
|
||||||
self.addrlist.remove(addr)
|
self.addrlist.remove(addr)
|
||||||
|
|
||||||
def sethwaddr(self, addr):
|
def sethwaddr(self, addr):
|
||||||
|
"""
|
||||||
|
Set hardware address.
|
||||||
|
|
||||||
|
:param core.misc.ipaddress.MacAddress addr: hardware address to set to.
|
||||||
|
:return: nothing
|
||||||
|
"""
|
||||||
self.hwaddr = addr
|
self.hwaddr = addr
|
||||||
|
|
||||||
def getparam(self, key):
|
def getparam(self, key):
|
||||||
''' Retrieve a parameter from the _params dict,
|
"""
|
||||||
or None if the parameter does not exist.
|
Retrieve a parameter from the, or None if the parameter does not exist.
|
||||||
'''
|
|
||||||
if key not in self._params:
|
:param key: parameter to get value for
|
||||||
return None
|
:return: parameter value
|
||||||
return self._params[key]
|
"""
|
||||||
|
return self._params.get(key)
|
||||||
|
|
||||||
def getparams(self):
|
def getparams(self):
|
||||||
''' Return (key, value) pairs from the _params dict.
|
"""
|
||||||
'''
|
Return (key, value) pairs for parameters.
|
||||||
r = []
|
"""
|
||||||
|
parameters = []
|
||||||
for k in sorted(self._params.keys()):
|
for k in sorted(self._params.keys()):
|
||||||
r.append((k, self._params[k]))
|
parameters.append((k, self._params[k]))
|
||||||
return r
|
return parameters
|
||||||
|
|
||||||
def setparam(self, key, value):
|
def setparam(self, key, value):
|
||||||
''' Set a parameter in the _params dict.
|
"""
|
||||||
Returns True if the parameter has changed.
|
Set a parameter value, returns True if the parameter has changed.
|
||||||
'''
|
|
||||||
if key in self._params:
|
:param key: parameter name to set
|
||||||
if self._params[key] == value:
|
:param value: parameter value
|
||||||
return False
|
:return: True if parameter changed, False otherwise
|
||||||
elif self._params[key] <= 0 and value <= 0:
|
"""
|
||||||
# treat None and 0 as unchanged values
|
# treat None and 0 as unchanged values
|
||||||
|
current_value = self._params.get(key)
|
||||||
|
if current_value == value or current_value <= 0 and value <= 0:
|
||||||
return False
|
return False
|
||||||
|
|
||||||
self._params[key] = value
|
self._params[key] = value
|
||||||
return True
|
return True
|
||||||
|
|
||||||
def swapparams(self, name):
|
def swapparams(self, name):
|
||||||
''' Swap out the _params dict for name. If name does not exist,
|
"""
|
||||||
|
Swap out parameters dict for name. If name does not exist,
|
||||||
intialize it. This is for supporting separate upstream/downstream
|
intialize it. This is for supporting separate upstream/downstream
|
||||||
parameters when two layer-2 nodes are linked together.
|
parameters when two layer-2 nodes are linked together.
|
||||||
'''
|
|
||||||
|
:param str name: name of parameter to swap
|
||||||
|
:return: nothing
|
||||||
|
"""
|
||||||
tmp = self._params
|
tmp = self._params
|
||||||
if not hasattr(self, name):
|
if not hasattr(self, name):
|
||||||
setattr(self, name, {})
|
setattr(self, name, {})
|
||||||
|
|
@ -496,8 +734,13 @@ class PyCoreNetIf(object):
|
||||||
setattr(self, name, tmp)
|
setattr(self, name, tmp)
|
||||||
|
|
||||||
def setposition(self, x, y, z):
|
def setposition(self, x, y, z):
|
||||||
''' Dispatch to any position hook (self.poshook) handler.
|
"""
|
||||||
'''
|
Dispatch position hook handler.
|
||||||
|
|
||||||
|
:param x: x position
|
||||||
|
:param y: y position
|
||||||
|
:param z: z position
|
||||||
|
:return: nothing
|
||||||
|
"""
|
||||||
if self.poshook is not None:
|
if self.poshook is not None:
|
||||||
self.poshook(self, x, y, z)
|
self.poshook(self, x, y, z)
|
||||||
|
|
||||||
|
|
|
||||||
File diff suppressed because it is too large
Load diff
|
|
@ -0,0 +1,54 @@
|
||||||
|
import subprocess
|
||||||
|
|
||||||
|
from core.misc import log
|
||||||
|
from core.misc import utils
|
||||||
|
|
||||||
|
logger = log.get_logger(__name__)
|
||||||
|
|
||||||
|
EMANEUNK = 0
|
||||||
|
EMANE074 = 7
|
||||||
|
EMANE081 = 8
|
||||||
|
EMANE091 = 91
|
||||||
|
EMANE092 = 92
|
||||||
|
EMANE093 = 93
|
||||||
|
EMANE101 = 101
|
||||||
|
|
||||||
|
VERSION = None
|
||||||
|
VERSIONSTR = None
|
||||||
|
|
||||||
|
|
||||||
|
def emane_version():
|
||||||
|
"""
|
||||||
|
Return the locally installed EMANE version identifier and string.
|
||||||
|
"""
|
||||||
|
global VERSION
|
||||||
|
global VERSIONSTR
|
||||||
|
cmd = ("emane", "--version")
|
||||||
|
|
||||||
|
try:
|
||||||
|
status, result = utils.cmdresult(cmd)
|
||||||
|
except (OSError, subprocess.CalledProcessError):
|
||||||
|
logger.exception("error checking emane version")
|
||||||
|
status = -1
|
||||||
|
result = ""
|
||||||
|
|
||||||
|
VERSION = EMANEUNK
|
||||||
|
if status == 0:
|
||||||
|
if result.startswith("0.7.4"):
|
||||||
|
VERSION = EMANE074
|
||||||
|
elif result.startswith("0.8.1"):
|
||||||
|
VERSION = EMANE081
|
||||||
|
elif result.startswith("0.9.1"):
|
||||||
|
VERSION = EMANE091
|
||||||
|
elif result.startswith("0.9.2"):
|
||||||
|
VERSION = EMANE092
|
||||||
|
elif result.startswith("0.9.3"):
|
||||||
|
VERSION = EMANE093
|
||||||
|
elif result.startswith("1.0.1"):
|
||||||
|
VERSION = EMANE101
|
||||||
|
|
||||||
|
VERSIONSTR = result.strip()
|
||||||
|
|
||||||
|
|
||||||
|
# set version variables for the Emane class
|
||||||
|
emane_version()
|
||||||
|
|
@ -1,48 +1,38 @@
|
||||||
#
|
"""
|
||||||
# CORE
|
|
||||||
# Copyright (c)2011-2012 the Boeing Company.
|
|
||||||
# See the LICENSE file included in this distribution.
|
|
||||||
#
|
|
||||||
# author: Jeff Ahrenholz <jeffrey.m.ahrenholz@boeing.com>
|
|
||||||
#
|
|
||||||
'''
|
|
||||||
bypass.py: EMANE Bypass model for CORE
|
bypass.py: EMANE Bypass model for CORE
|
||||||
'''
|
"""
|
||||||
|
|
||||||
import sys
|
from core.emane.emanemodel import EmaneModel
|
||||||
import string
|
from core.enumerations import ConfigDataTypes
|
||||||
from core.api import coreapi
|
|
||||||
|
|
||||||
from core.constants import *
|
|
||||||
from emane import EmaneModel
|
|
||||||
|
|
||||||
class EmaneBypassModel(EmaneModel):
|
class EmaneBypassModel(EmaneModel):
|
||||||
def __init__(self, session, objid = None, verbose = False):
|
def __init__(self, session, object_id=None):
|
||||||
EmaneModel.__init__(self, session, objid, verbose)
|
EmaneModel.__init__(self, session, object_id)
|
||||||
|
|
||||||
_name = "emane_bypass"
|
name = "emane_bypass"
|
||||||
_confmatrix = [
|
config_matrix = [
|
||||||
("none",coreapi.CONF_DATA_TYPE_BOOL, '0',
|
("none", ConfigDataTypes.BOOL.value, '0',
|
||||||
'True,False', 'There are no parameters for the bypass model.'),
|
'True,False', 'There are no parameters for the bypass model.'),
|
||||||
]
|
]
|
||||||
|
|
||||||
# value groupings
|
# value groupings
|
||||||
_confgroups = "Bypass Parameters:1-1"
|
config_groups = "Bypass Parameters:1-1"
|
||||||
|
|
||||||
def buildnemxmlfiles(self, e, ifc):
|
def buildnemxmlfiles(self, e, ifc):
|
||||||
''' Build the necessary nem, mac, and phy XMLs in the given path.
|
"""
|
||||||
|
Build the necessary nem, mac, and phy XMLs in the given path.
|
||||||
If an individual NEM has a nonstandard config, we need to build
|
If an individual NEM has a nonstandard config, we need to build
|
||||||
that file also. Otherwise the WLAN-wide nXXemane_bypassnem.xml,
|
that file also. Otherwise the WLAN-wide nXXemane_bypassnem.xml,
|
||||||
nXXemane_bypassmac.xml, nXXemane_bypassphy.xml are used.
|
nXXemane_bypassmac.xml, nXXemane_bypassphy.xml are used.
|
||||||
'''
|
"""
|
||||||
values = e.getifcconfig(self.objid, self._name,
|
values = e.getifcconfig(self.object_id, self.name, self.getdefaultvalues(), ifc)
|
||||||
self.getdefaultvalues(), ifc)
|
|
||||||
if values is None:
|
if values is None:
|
||||||
return
|
return
|
||||||
nemdoc = e.xmldoc("nem")
|
nemdoc = e.xmldoc("nem")
|
||||||
nem = nemdoc.getElementsByTagName("nem").pop()
|
nem = nemdoc.getElementsByTagName("nem").pop()
|
||||||
nem.setAttribute("name", "BYPASS NEM")
|
nem.setAttribute("name", "BYPASS NEM")
|
||||||
e.appendtransporttonem(nemdoc, nem, self.objid, ifc)
|
e.appendtransporttonem(nemdoc, nem, self.object_id, ifc)
|
||||||
mactag = nemdoc.createElement("mac")
|
mactag = nemdoc.createElement("mac")
|
||||||
mactag.setAttribute("definition", self.macxmlname(ifc))
|
mactag.setAttribute("definition", self.macxmlname(ifc))
|
||||||
nem.appendChild(mactag)
|
nem.appendChild(mactag)
|
||||||
|
|
@ -62,5 +52,3 @@ class EmaneBypassModel(EmaneModel):
|
||||||
phy.setAttribute("name", "BYPASS PHY")
|
phy.setAttribute("name", "BYPASS PHY")
|
||||||
phy.setAttribute("library", "bypassphylayer")
|
phy.setAttribute("library", "bypassphylayer")
|
||||||
e.xmlwrite(phydoc, self.phyxmlname(ifc))
|
e.xmlwrite(phydoc, self.phyxmlname(ifc))
|
||||||
|
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,76 +1,70 @@
|
||||||
#
|
"""
|
||||||
# CORE
|
|
||||||
# Copyright (c)2010-2014 the Boeing Company.
|
|
||||||
# See the LICENSE file included in this distribution.
|
|
||||||
#
|
|
||||||
# authors: Jeff Ahrenholz <jeffrey.m.ahrenholz@boeing.com>
|
|
||||||
# Randy Charland <rcharland@ll.mit.edu>
|
|
||||||
#
|
|
||||||
'''
|
|
||||||
commeffect.py: EMANE CommEffect model for CORE
|
commeffect.py: EMANE CommEffect model for CORE
|
||||||
'''
|
"""
|
||||||
|
|
||||||
|
from core import emane
|
||||||
|
from core.emane.emanemodel import EmaneModel
|
||||||
|
from core.enumerations import ConfigDataTypes
|
||||||
|
from core.misc import log
|
||||||
|
|
||||||
|
logger = log.get_logger(__name__)
|
||||||
|
|
||||||
import sys
|
|
||||||
import string
|
|
||||||
try:
|
try:
|
||||||
from emanesh.events import EventService
|
from emanesh.events import EventService
|
||||||
except:
|
except ImportError:
|
||||||
pass
|
logger.error("error importing emanesh")
|
||||||
from core.api import coreapi
|
|
||||||
from core.constants import *
|
|
||||||
from emane import Emane, EmaneModel
|
|
||||||
|
|
||||||
try:
|
try:
|
||||||
import emaneeventservice
|
import emaneeventservice
|
||||||
import emaneeventcommeffect
|
import emaneeventcommeffect
|
||||||
except Exception, e:
|
except ImportError:
|
||||||
pass
|
logger.error("error importing emaneeventservice and emaneeventcommeffect")
|
||||||
|
|
||||||
|
|
||||||
class EmaneCommEffectModel(EmaneModel):
|
class EmaneCommEffectModel(EmaneModel):
|
||||||
def __init__(self, session, objid = None, verbose = False):
|
def __init__(self, session, object_id=None):
|
||||||
EmaneModel.__init__(self, session, objid, verbose)
|
EmaneModel.__init__(self, session, object_id)
|
||||||
|
|
||||||
# model name
|
# model name
|
||||||
_name = "emane_commeffect"
|
name = "emane_commeffect"
|
||||||
# CommEffect parameters
|
# CommEffect parameters
|
||||||
_confmatrix_shim_base = [
|
_confmatrix_shim_base = [
|
||||||
("filterfile", coreapi.CONF_DATA_TYPE_STRING, '',
|
("filterfile", ConfigDataTypes.STRING.value, '',
|
||||||
'', 'filter file'),
|
'', 'filter file'),
|
||||||
("groupid", coreapi.CONF_DATA_TYPE_UINT32, '0',
|
("groupid", ConfigDataTypes.UINT32.value, '0',
|
||||||
'', 'NEM Group ID'),
|
'', 'NEM Group ID'),
|
||||||
("enablepromiscuousmode", coreapi.CONF_DATA_TYPE_BOOL, '0',
|
("enablepromiscuousmode", ConfigDataTypes.BOOL.value, '0',
|
||||||
'On,Off', 'enable promiscuous mode'),
|
'On,Off', 'enable promiscuous mode'),
|
||||||
("receivebufferperiod", coreapi.CONF_DATA_TYPE_FLOAT, '1.0',
|
("receivebufferperiod", ConfigDataTypes.FLOAT.value, '1.0',
|
||||||
'', 'receivebufferperiod'),
|
'', 'receivebufferperiod'),
|
||||||
]
|
]
|
||||||
_confmatrix_shim_081 = [
|
_confmatrix_shim_081 = [
|
||||||
("defaultconnectivity", coreapi.CONF_DATA_TYPE_BOOL, '0',
|
("defaultconnectivity", ConfigDataTypes.BOOL.value, '0',
|
||||||
'On,Off', 'defaultconnectivity'),
|
'On,Off', 'defaultconnectivity'),
|
||||||
("enabletighttimingmode", coreapi.CONF_DATA_TYPE_BOOL, '0',
|
("enabletighttimingmode", ConfigDataTypes.BOOL.value, '0',
|
||||||
'On,Off', 'enable tight timing mode'),
|
'On,Off', 'enable tight timing mode'),
|
||||||
]
|
]
|
||||||
_confmatrix_shim_091 = [
|
_confmatrix_shim_091 = [
|
||||||
("defaultconnectivitymode", coreapi.CONF_DATA_TYPE_BOOL, '0',
|
("defaultconnectivitymode", ConfigDataTypes.BOOL.value, '0',
|
||||||
'On,Off', 'defaultconnectivity'),
|
'On,Off', 'defaultconnectivity'),
|
||||||
]
|
]
|
||||||
if Emane.version >= Emane.EMANE091:
|
if emane.VERSION >= emane.EMANE091:
|
||||||
_confmatrix_shim = _confmatrix_shim_base + _confmatrix_shim_091
|
_confmatrix_shim = _confmatrix_shim_base + _confmatrix_shim_091
|
||||||
else:
|
else:
|
||||||
_confmatrix_shim = _confmatrix_shim_base + _confmatrix_shim_081
|
_confmatrix_shim = _confmatrix_shim_base + _confmatrix_shim_081
|
||||||
|
|
||||||
_confmatrix = _confmatrix_shim
|
config_matrix = _confmatrix_shim
|
||||||
# value groupings
|
# value groupings
|
||||||
_confgroups = "CommEffect SHIM Parameters:1-%d" \
|
config_groups = "CommEffect SHIM Parameters:1-%d" % len(_confmatrix_shim)
|
||||||
% len(_confmatrix_shim)
|
|
||||||
|
|
||||||
def buildnemxmlfiles(self, e, ifc):
|
def buildnemxmlfiles(self, e, ifc):
|
||||||
''' Build the necessary nem and commeffect XMLs in the given path.
|
"""
|
||||||
|
Build the necessary nem and commeffect XMLs in the given path.
|
||||||
If an individual NEM has a nonstandard config, we need to build
|
If an individual NEM has a nonstandard config, we need to build
|
||||||
that file also. Otherwise the WLAN-wide
|
that file also. Otherwise the WLAN-wide
|
||||||
nXXemane_commeffectnem.xml, nXXemane_commeffectshim.xml are used.
|
nXXemane_commeffectnem.xml, nXXemane_commeffectshim.xml are used.
|
||||||
'''
|
"""
|
||||||
values = e.getifcconfig(self.objid, self._name,
|
values = e.getifcconfig(self.object_id, self.name, self.getdefaultvalues(), ifc)
|
||||||
self.getdefaultvalues(), ifc)
|
|
||||||
if values is None:
|
if values is None:
|
||||||
return
|
return
|
||||||
shimdoc = e.xmldoc("shim")
|
shimdoc = e.xmldoc("shim")
|
||||||
|
|
@ -83,8 +77,7 @@ class EmaneCommEffectModel(EmaneModel):
|
||||||
shimnames.remove("filterfile")
|
shimnames.remove("filterfile")
|
||||||
|
|
||||||
# append all shim options (except filterfile) to shimdoc
|
# append all shim options (except filterfile) to shimdoc
|
||||||
map( lambda n: shim.appendChild(e.xmlparam(shimdoc, n, \
|
map(lambda n: shim.appendChild(e.xmlparam(shimdoc, n, self.valueof(n, values))), shimnames)
|
||||||
self.valueof(n, values))), shimnames)
|
|
||||||
# empty filterfile is not allowed
|
# empty filterfile is not allowed
|
||||||
ff = self.valueof("filterfile", values)
|
ff = self.valueof("filterfile", values)
|
||||||
if ff.strip() != '':
|
if ff.strip() != '':
|
||||||
|
|
@ -95,20 +88,24 @@ class EmaneCommEffectModel(EmaneModel):
|
||||||
nem = nemdoc.getElementsByTagName("nem").pop()
|
nem = nemdoc.getElementsByTagName("nem").pop()
|
||||||
nem.setAttribute("name", "commeffect NEM")
|
nem.setAttribute("name", "commeffect NEM")
|
||||||
nem.setAttribute("type", "unstructured")
|
nem.setAttribute("type", "unstructured")
|
||||||
e.appendtransporttonem(nemdoc, nem, self.objid, ifc)
|
e.appendtransporttonem(nemdoc, nem, self.object_id, ifc)
|
||||||
nem.appendChild(e.xmlshimdefinition(nemdoc, self.shimxmlname(ifc)))
|
nem.appendChild(e.xmlshimdefinition(nemdoc, self.shimxmlname(ifc)))
|
||||||
e.xmlwrite(nemdoc, self.nemxmlname(ifc))
|
e.xmlwrite(nemdoc, self.nemxmlname(ifc))
|
||||||
|
|
||||||
def linkconfig(self, netif, bw=None, delay=None,
|
def linkconfig(self, netif, bw=None, delay=None,
|
||||||
loss=None, duplicate=None, jitter=None, netif2=None):
|
loss=None, duplicate=None, jitter=None, netif2=None):
|
||||||
''' Generate CommEffect events when a Link Message is received having
|
"""
|
||||||
|
Generate CommEffect events when a Link Message is received having
|
||||||
link parameters.
|
link parameters.
|
||||||
'''
|
"""
|
||||||
if self.session.emane.version >= self.session.emane.EMANE091:
|
if emane.VERSION >= emane.EMANE091:
|
||||||
raise NotImplementedError, \
|
raise NotImplementedError, \
|
||||||
"CommEffect linkconfig() not implemented for EMANE 0.9.1+"
|
"CommEffect linkconfig() not implemented for EMANE 0.9.1+"
|
||||||
|
|
||||||
def z(x):
|
def z(x):
|
||||||
''' Helper to use 0 for None values. '''
|
"""
|
||||||
|
Helper to use 0 for None values.
|
||||||
|
"""
|
||||||
if type(x) is str:
|
if type(x) is str:
|
||||||
x = float(x)
|
x = float(x)
|
||||||
if x is None:
|
if x is None:
|
||||||
|
|
@ -118,17 +115,16 @@ class EmaneCommEffectModel(EmaneModel):
|
||||||
|
|
||||||
service = self.session.emane.service
|
service = self.session.emane.service
|
||||||
if service is None:
|
if service is None:
|
||||||
self.session.warn("%s: EMANE event service unavailable" % \
|
logger.warn("%s: EMANE event service unavailable" % self.name)
|
||||||
self._name)
|
|
||||||
return
|
return
|
||||||
if netif is None or netif2 is None:
|
if netif is None or netif2 is None:
|
||||||
self.session.warn("%s: missing NEM information" % self._name)
|
logger.warn("%s: missing NEM information" % self.name)
|
||||||
return
|
return
|
||||||
# TODO: batch these into multiple events per transmission
|
# TODO: batch these into multiple events per transmission
|
||||||
# TODO: may want to split out seconds portion of delay and jitter
|
# TODO: may want to split out seconds portion of delay and jitter
|
||||||
event = emaneeventcommeffect.EventCommEffect(1)
|
event = emaneeventcommeffect.EventCommEffect(1)
|
||||||
index = 0
|
index = 0
|
||||||
e = self.session.obj(self.objid)
|
e = self.session.get_object(self.object_id)
|
||||||
nemid = e.getnemid(netif)
|
nemid = e.getnemid(netif)
|
||||||
nemid2 = e.getnemid(netif2)
|
nemid2 = e.getnemid(netif2)
|
||||||
mbw = bw
|
mbw = bw
|
||||||
|
|
@ -139,6 +135,3 @@ class EmaneCommEffectModel(EmaneModel):
|
||||||
emaneeventservice.PLATFORMID_ANY,
|
emaneeventservice.PLATFORMID_ANY,
|
||||||
nemid2, emaneeventservice.COMPONENTID_ANY,
|
nemid2, emaneeventservice.COMPONENTID_ANY,
|
||||||
event.export())
|
event.export())
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
|
||||||
File diff suppressed because it is too large
Load diff
|
|
@ -1,60 +1,56 @@
|
||||||
#
|
"""
|
||||||
# CORE
|
|
||||||
# Copyright (c)2010-2014 the Boeing Company.
|
|
||||||
# See the LICENSE file included in this distribution.
|
|
||||||
#
|
|
||||||
# author: Jeff Ahrenholz <jeffrey.m.ahrenholz@boeing.com>
|
|
||||||
#
|
|
||||||
'''
|
|
||||||
ieee80211abg.py: EMANE IEEE 802.11abg model for CORE
|
ieee80211abg.py: EMANE IEEE 802.11abg model for CORE
|
||||||
'''
|
"""
|
||||||
|
|
||||||
|
from core import emane
|
||||||
|
from core.emane.emanemodel import EmaneModel
|
||||||
|
from core.emane.universal import EmaneUniversalModel
|
||||||
|
from core.enumerations import ConfigDataTypes
|
||||||
|
from core.misc import log
|
||||||
|
|
||||||
|
logger = log.get_logger(__name__)
|
||||||
|
|
||||||
import sys
|
|
||||||
import string
|
|
||||||
try:
|
try:
|
||||||
from emanesh.events import EventService
|
from emanesh.events import EventService
|
||||||
except:
|
except:
|
||||||
pass
|
logger.error("error importing emanesh")
|
||||||
from core.api import coreapi
|
|
||||||
from core.constants import *
|
|
||||||
from emane import Emane, EmaneModel
|
|
||||||
from universal import EmaneUniversalModel
|
|
||||||
|
|
||||||
class EmaneIeee80211abgModel(EmaneModel):
|
class EmaneIeee80211abgModel(EmaneModel):
|
||||||
def __init__(self, session, objid = None, verbose = False):
|
def __init__(self, session, object_id=None):
|
||||||
EmaneModel.__init__(self, session, objid, verbose)
|
EmaneModel.__init__(self, session, object_id)
|
||||||
|
|
||||||
# model name
|
# model name
|
||||||
_name = "emane_ieee80211abg"
|
name = "emane_ieee80211abg"
|
||||||
_80211rates = '1 1 Mbps,2 2 Mbps,3 5.5 Mbps,4 11 Mbps,5 6 Mbps,' + \
|
_80211rates = '1 1 Mbps,2 2 Mbps,3 5.5 Mbps,4 11 Mbps,5 6 Mbps,' + \
|
||||||
'6 9 Mbps,7 12 Mbps,8 18 Mbps,9 24 Mbps,10 36 Mbps,11 48 Mbps,' + \
|
'6 9 Mbps,7 12 Mbps,8 18 Mbps,9 24 Mbps,10 36 Mbps,11 48 Mbps,' + \
|
||||||
'12 54 Mbps'
|
'12 54 Mbps'
|
||||||
if Emane.version >= Emane.EMANE091:
|
if emane.VERSION >= emane.EMANE091:
|
||||||
xml_path = '/usr/share/emane/xml/models/mac/ieee80211abg'
|
xml_path = '/usr/share/emane/xml/models/mac/ieee80211abg'
|
||||||
else:
|
else:
|
||||||
xml_path = "/usr/share/emane/models/ieee80211abg/xml"
|
xml_path = "/usr/share/emane/models/ieee80211abg/xml"
|
||||||
|
|
||||||
# MAC parameters
|
# MAC parameters
|
||||||
_confmatrix_mac_base = [
|
_confmatrix_mac_base = [
|
||||||
("mode", coreapi.CONF_DATA_TYPE_UINT8, '0',
|
("mode", ConfigDataTypes.UINT8.value, '0',
|
||||||
'0 802.11b (DSSS only),1 802.11b (DSSS only),' + \
|
'0 802.11b (DSSS only),1 802.11b (DSSS only),' + \
|
||||||
'2 802.11a or g (OFDM),3 802.11b/g (DSSS and OFDM)', 'mode'),
|
'2 802.11a or g (OFDM),3 802.11b/g (DSSS and OFDM)', 'mode'),
|
||||||
("enablepromiscuousmode", coreapi.CONF_DATA_TYPE_BOOL, '0',
|
("enablepromiscuousmode", ConfigDataTypes.BOOL.value, '0',
|
||||||
'On,Off', 'enable promiscuous mode'),
|
'On,Off', 'enable promiscuous mode'),
|
||||||
("distance", coreapi.CONF_DATA_TYPE_UINT32, '1000',
|
("distance", ConfigDataTypes.UINT32.value, '1000',
|
||||||
'', 'max distance (m)'),
|
'', 'max distance (m)'),
|
||||||
("unicastrate", coreapi.CONF_DATA_TYPE_UINT8, '4', _80211rates,
|
("unicastrate", ConfigDataTypes.UINT8.value, '4', _80211rates,
|
||||||
'unicast rate (Mbps)'),
|
'unicast rate (Mbps)'),
|
||||||
("multicastrate", coreapi.CONF_DATA_TYPE_UINT8, '1', _80211rates,
|
("multicastrate", ConfigDataTypes.UINT8.value, '1', _80211rates,
|
||||||
'multicast rate (Mbps)'),
|
'multicast rate (Mbps)'),
|
||||||
("rtsthreshold", coreapi.CONF_DATA_TYPE_UINT16, '0',
|
("rtsthreshold", ConfigDataTypes.UINT16.value, '0',
|
||||||
'', 'RTS threshold (bytes)'),
|
'', 'RTS threshold (bytes)'),
|
||||||
("pcrcurveuri", coreapi.CONF_DATA_TYPE_STRING,
|
("pcrcurveuri", ConfigDataTypes.STRING.value,
|
||||||
'%s/ieee80211pcr.xml' % xml_path,
|
'%s/ieee80211pcr.xml' % xml_path,
|
||||||
'', 'SINR/PCR curve file'),
|
'', 'SINR/PCR curve file'),
|
||||||
("flowcontrolenable", coreapi.CONF_DATA_TYPE_BOOL, '0',
|
("flowcontrolenable", ConfigDataTypes.BOOL.value, '0',
|
||||||
'On,Off', 'enable traffic flow control'),
|
'On,Off', 'enable traffic flow control'),
|
||||||
("flowcontroltokens", coreapi.CONF_DATA_TYPE_UINT16, '10',
|
("flowcontroltokens", ConfigDataTypes.UINT16.value, '10',
|
||||||
'', 'number of flow control tokens'),
|
'', 'number of flow control tokens'),
|
||||||
]
|
]
|
||||||
# mac parameters introduced in EMANE 0.8.1
|
# mac parameters introduced in EMANE 0.8.1
|
||||||
|
|
@ -62,56 +58,56 @@ class EmaneIeee80211abgModel(EmaneModel):
|
||||||
# EMANE 9.x, but are being preserved for the time being due to space constraints in the
|
# EMANE 9.x, but are being preserved for the time being due to space constraints in the
|
||||||
# CORE GUI. A conversion function (get9xmacparamequivalent) has been defined to support this.
|
# CORE GUI. A conversion function (get9xmacparamequivalent) has been defined to support this.
|
||||||
_confmatrix_mac_extended = [
|
_confmatrix_mac_extended = [
|
||||||
("wmmenable", coreapi.CONF_DATA_TYPE_BOOL, '0',
|
("wmmenable", ConfigDataTypes.BOOL.value, '0',
|
||||||
'On,Off', 'WiFi Multimedia (WMM)'),
|
'On,Off', 'WiFi Multimedia (WMM)'),
|
||||||
("queuesize", coreapi.CONF_DATA_TYPE_STRING, '0:255 1:255 2:255 3:255',
|
("queuesize", ConfigDataTypes.STRING.value, '0:255 1:255 2:255 3:255',
|
||||||
'', 'queue size (0-4:size)'),
|
'', 'queue size (0-4:size)'),
|
||||||
("cwmin", coreapi.CONF_DATA_TYPE_STRING, '0:32 1:32 2:16 3:8',
|
("cwmin", ConfigDataTypes.STRING.value, '0:32 1:32 2:16 3:8',
|
||||||
'', 'min contention window (0-4:minw)'),
|
'', 'min contention window (0-4:minw)'),
|
||||||
("cwmax", coreapi.CONF_DATA_TYPE_STRING, '0:1024 1:1024 2:64 3:16',
|
("cwmax", ConfigDataTypes.STRING.value, '0:1024 1:1024 2:64 3:16',
|
||||||
'', 'max contention window (0-4:maxw)'),
|
'', 'max contention window (0-4:maxw)'),
|
||||||
("aifs", coreapi.CONF_DATA_TYPE_STRING, '0:2 1:2 2:2 3:1',
|
("aifs", ConfigDataTypes.STRING.value, '0:2 1:2 2:2 3:1',
|
||||||
'', 'arbitration inter frame space (0-4:aifs)'),
|
'', 'arbitration inter frame space (0-4:aifs)'),
|
||||||
("txop", coreapi.CONF_DATA_TYPE_STRING, '0:0 1:0 2:0 3:0',
|
("txop", ConfigDataTypes.STRING.value, '0:0 1:0 2:0 3:0',
|
||||||
'', 'txop (0-4:usec)'),
|
'', 'txop (0-4:usec)'),
|
||||||
("retrylimit", coreapi.CONF_DATA_TYPE_STRING, '0:3 1:3 2:3 3:3',
|
("retrylimit", ConfigDataTypes.STRING.value, '0:3 1:3 2:3 3:3',
|
||||||
'', 'retry limit (0-4:numretries)'),
|
'', 'retry limit (0-4:numretries)'),
|
||||||
]
|
]
|
||||||
_confmatrix_mac_091 = [
|
_confmatrix_mac_091 = [
|
||||||
('radiometricenable', coreapi.CONF_DATA_TYPE_BOOL, '0',
|
('radiometricenable', ConfigDataTypes.BOOL.value, '0',
|
||||||
'On,Off', 'report radio metrics via R2RI'),
|
'On,Off', 'report radio metrics via R2RI'),
|
||||||
('radiometricreportinterval', coreapi.CONF_DATA_TYPE_FLOAT, '1.0',
|
('radiometricreportinterval', ConfigDataTypes.FLOAT.value, '1.0',
|
||||||
'', 'R2RI radio metric report interval (sec)'),
|
'', 'R2RI radio metric report interval (sec)'),
|
||||||
('neighbormetricdeletetime', coreapi.CONF_DATA_TYPE_FLOAT, '60.0',
|
('neighbormetricdeletetime', ConfigDataTypes.FLOAT.value, '60.0',
|
||||||
'', 'R2RI neighbor table inactivity time (sec)'),
|
'', 'R2RI neighbor table inactivity time (sec)'),
|
||||||
]
|
]
|
||||||
_confmatrix_mac = _confmatrix_mac_base + _confmatrix_mac_extended
|
_confmatrix_mac = _confmatrix_mac_base + _confmatrix_mac_extended
|
||||||
if Emane.version >= Emane.EMANE091:
|
if emane.VERSION >= emane.EMANE091:
|
||||||
_confmatrix_mac += _confmatrix_mac_091
|
_confmatrix_mac += _confmatrix_mac_091
|
||||||
|
|
||||||
# PHY parameters from Universal PHY
|
# PHY parameters from Universal PHY
|
||||||
_confmatrix_phy = EmaneUniversalModel._confmatrix
|
_confmatrix_phy = EmaneUniversalModel.config_matrix
|
||||||
|
|
||||||
_confmatrix = _confmatrix_mac + _confmatrix_phy
|
config_matrix = _confmatrix_mac + _confmatrix_phy
|
||||||
# value groupings
|
# value groupings
|
||||||
_confgroups = "802.11 MAC Parameters:1-%d|Universal PHY Parameters:%d-%d" \
|
config_groups = "802.11 MAC Parameters:1-%d|Universal PHY Parameters:%d-%d" % (
|
||||||
% (len(_confmatrix_mac), len(_confmatrix_mac) + 1, len(_confmatrix))
|
len(_confmatrix_mac), len(_confmatrix_mac) + 1, len(config_matrix))
|
||||||
|
|
||||||
def buildnemxmlfiles(self, e, ifc):
|
def buildnemxmlfiles(self, e, ifc):
|
||||||
''' Build the necessary nem, mac, and phy XMLs in the given path.
|
"""
|
||||||
|
Build the necessary nem, mac, and phy XMLs in the given path.
|
||||||
If an individual NEM has a nonstandard config, we need to build
|
If an individual NEM has a nonstandard config, we need to build
|
||||||
that file also. Otherwise the WLAN-wide
|
that file also. Otherwise the WLAN-wide
|
||||||
nXXemane_ieee80211abgnem.xml, nXXemane_ieee80211abgemac.xml,
|
nXXemane_ieee80211abgnem.xml, nXXemane_ieee80211abgemac.xml,
|
||||||
nXXemane_ieee80211abgphy.xml are used.
|
nXXemane_ieee80211abgphy.xml are used.
|
||||||
'''
|
"""
|
||||||
values = e.getifcconfig(self.objid, self._name,
|
values = e.getifcconfig(self.object_id, self.name, self.getdefaultvalues(), ifc)
|
||||||
self.getdefaultvalues(), ifc)
|
|
||||||
if values is None:
|
if values is None:
|
||||||
return
|
return
|
||||||
nemdoc = e.xmldoc("nem")
|
nemdoc = e.xmldoc("nem")
|
||||||
nem = nemdoc.getElementsByTagName("nem").pop()
|
nem = nemdoc.getElementsByTagName("nem").pop()
|
||||||
nem.setAttribute("name", "ieee80211abg NEM")
|
nem.setAttribute("name", "ieee80211abg NEM")
|
||||||
e.appendtransporttonem(nemdoc, nem, self.objid, ifc)
|
e.appendtransporttonem(nemdoc, nem, self.object_id, ifc)
|
||||||
mactag = nemdoc.createElement("mac")
|
mactag = nemdoc.createElement("mac")
|
||||||
mactag.setAttribute("definition", self.macxmlname(ifc))
|
mactag.setAttribute("definition", self.macxmlname(ifc))
|
||||||
nem.appendChild(mactag)
|
nem.appendChild(mactag)
|
||||||
|
|
@ -130,14 +126,13 @@ class EmaneIeee80211abgModel(EmaneModel):
|
||||||
phynames = names[len(self._confmatrix_mac):]
|
phynames = names[len(self._confmatrix_mac):]
|
||||||
|
|
||||||
# append all MAC options to macdoc
|
# append all MAC options to macdoc
|
||||||
if Emane.version >= Emane.EMANE091:
|
if emane.VERSION >= emane.EMANE091:
|
||||||
for macname in macnames:
|
for macname in macnames:
|
||||||
mac9xnvpairlist = self.get9xmacparamequivalent(macname, values)
|
mac9xnvpairlist = self.get9xmacparamequivalent(macname, values)
|
||||||
for nvpair in mac9xnvpairlist:
|
for nvpair in mac9xnvpairlist:
|
||||||
mac.appendChild(e.xmlparam(macdoc, nvpair[0], nvpair[1]))
|
mac.appendChild(e.xmlparam(macdoc, nvpair[0], nvpair[1]))
|
||||||
else:
|
else:
|
||||||
map( lambda n: mac.appendChild(e.xmlparam(macdoc, n, \
|
map(lambda n: mac.appendChild(e.xmlparam(macdoc, n, self.valueof(n, values))), macnames)
|
||||||
self.valueof(n, values))), macnames)
|
|
||||||
|
|
||||||
e.xmlwrite(macdoc, self.macxmlname(ifc))
|
e.xmlwrite(macdoc, self.macxmlname(ifc))
|
||||||
|
|
||||||
|
|
@ -150,11 +145,12 @@ class EmaneIeee80211abgModel(EmaneModel):
|
||||||
# and work with EMANE 9.x onwards.
|
# and work with EMANE 9.x onwards.
|
||||||
#
|
#
|
||||||
def get9xmacparamequivalent(self, macname, values):
|
def get9xmacparamequivalent(self, macname, values):
|
||||||
''' Generate a list of 80211abg mac parameters in 0.9.x layout for a given mac parameter
|
"""
|
||||||
|
Generate a list of 80211abg mac parameters in 0.9.x layout for a given mac parameter
|
||||||
in 8.x layout.For mac category parameters, the list returned will contain the four
|
in 8.x layout.For mac category parameters, the list returned will contain the four
|
||||||
equivalent 9.x parameter and value pairs. Otherwise, the list returned will only
|
equivalent 9.x parameter and value pairs. Otherwise, the list returned will only
|
||||||
contain a single name and value pair.
|
contain a single name and value pair.
|
||||||
'''
|
"""
|
||||||
nvpairlist = []
|
nvpairlist = []
|
||||||
macparmval = self.valueof(macname, values)
|
macparmval = self.valueof(macname, values)
|
||||||
if macname in ["queuesize", "aifs", "cwmin", "cwmax", "txop", "retrylimit"]:
|
if macname in ["queuesize", "aifs", "cwmin", "cwmax", "txop", "retrylimit"]:
|
||||||
|
|
@ -164,10 +160,9 @@ class EmaneIeee80211abgModel(EmaneModel):
|
||||||
val = idx_and_val[1]
|
val = idx_and_val[1]
|
||||||
# aifs and tx are in microseconds. Convert to seconds.
|
# aifs and tx are in microseconds. Convert to seconds.
|
||||||
if macname in ["aifs", "txop"]:
|
if macname in ["aifs", "txop"]:
|
||||||
val = "%f" % (float(val)*(1e-6))
|
val = "%f" % (float(val) * 1e-6)
|
||||||
name9x = "%s%d" % (macname, idx)
|
name9x = "%s%d" % (macname, idx)
|
||||||
nvpairlist.append([name9x, val])
|
nvpairlist.append([name9x, val])
|
||||||
else:
|
else:
|
||||||
nvpairlist.append([macname, macparmval])
|
nvpairlist.append([macname, macparmval])
|
||||||
return nvpairlist
|
return nvpairlist
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,69 +1,72 @@
|
||||||
#
|
"""
|
||||||
# CORE
|
|
||||||
# Copyright (c)2010-2014 the Boeing Company.
|
|
||||||
# See the LICENSE file included in this distribution.
|
|
||||||
#
|
|
||||||
# author: Jeff Ahrenholz <jeffrey.m.ahrenholz@boeing.com>
|
|
||||||
#
|
|
||||||
'''
|
|
||||||
nodes.py: definition of an EmaneNode class for implementing configuration
|
nodes.py: definition of an EmaneNode class for implementing configuration
|
||||||
control of an EMANE emulation. An EmaneNode has several attached NEMs that
|
control of an EMANE emulation. An EmaneNode has several attached NEMs that
|
||||||
share the same MAC+PHY model.
|
share the same MAC+PHY model.
|
||||||
'''
|
"""
|
||||||
|
|
||||||
import sys
|
from os import path
|
||||||
import os.path
|
|
||||||
|
|
||||||
from core.api import coreapi
|
from core import emane
|
||||||
from core.coreobj import PyCoreNet
|
from core.coreobj import PyCoreNet
|
||||||
|
from core.enumerations import LinkTypes
|
||||||
|
from core.enumerations import NodeTypes
|
||||||
|
from core.enumerations import RegisterTlvs
|
||||||
|
from core.misc import log
|
||||||
|
|
||||||
|
logger = log.get_logger(__name__)
|
||||||
|
|
||||||
try:
|
try:
|
||||||
from emanesh.events import EventService
|
from emanesh.events import EventService
|
||||||
from emanesh.events import LocationEvent
|
from emanesh.events import LocationEvent
|
||||||
except Exception, e:
|
except ImportError:
|
||||||
pass
|
logger.error("error loading emanesh")
|
||||||
|
|
||||||
try:
|
try:
|
||||||
import emaneeventservice
|
import emaneeventservice
|
||||||
import emaneeventlocation
|
import emaneeventlocation
|
||||||
except Exception, e:
|
except ImportError:
|
||||||
''' Don't require all CORE users to have EMANE libeventservice and its
|
"""
|
||||||
|
Don't require all CORE users to have EMANE libeventservice and its
|
||||||
Python bindings installed.
|
Python bindings installed.
|
||||||
'''
|
"""
|
||||||
pass
|
logger.error("error loading emaneeventservice and emaneeventlocation")
|
||||||
|
|
||||||
|
|
||||||
class EmaneNet(PyCoreNet):
|
class EmaneNet(PyCoreNet):
|
||||||
''' EMANE network base class.
|
"""
|
||||||
'''
|
EMANE network base class.
|
||||||
apitype = coreapi.CORE_NODE_EMANE
|
"""
|
||||||
linktype = coreapi.CORE_LINK_WIRELESS
|
apitype = NodeTypes.EMANE.value
|
||||||
type = "wlan" # icon used
|
linktype = LinkTypes.WIRELESS.value
|
||||||
|
# icon used
|
||||||
|
type = "wlan"
|
||||||
|
|
||||||
|
|
||||||
class EmaneNode(EmaneNet):
|
class EmaneNode(EmaneNet):
|
||||||
''' EMANE node contains NEM configuration and causes connected nodes
|
"""
|
||||||
|
EMANE node contains NEM configuration and causes connected nodes
|
||||||
to have TAP interfaces (instead of VEth). These are managed by the
|
to have TAP interfaces (instead of VEth). These are managed by the
|
||||||
Emane controller object that exists in a session.
|
Emane controller object that exists in a session.
|
||||||
'''
|
"""
|
||||||
def __init__(self, session, objid = None, name = None, verbose = False,
|
|
||||||
start = True):
|
def __init__(self, session, objid=None, name=None, start=True):
|
||||||
PyCoreNet.__init__(self, session, objid, name, verbose, start)
|
PyCoreNet.__init__(self, session, objid, name, start)
|
||||||
self.verbose = verbose
|
|
||||||
self.conf = ""
|
self.conf = ""
|
||||||
self.up = False
|
self.up = False
|
||||||
self.nemidmap = {}
|
self.nemidmap = {}
|
||||||
self.model = None
|
self.model = None
|
||||||
self.mobility = None
|
self.mobility = None
|
||||||
|
|
||||||
def linkconfig(self, netif, bw = None, delay = None,
|
def linkconfig(self, netif, bw=None, delay=None, loss=None, duplicate=None, jitter=None, netif2=None):
|
||||||
loss = None, duplicate = None, jitter = None, netif2 = None):
|
"""
|
||||||
''' The CommEffect model supports link configuration.
|
The CommEffect model supports link configuration.
|
||||||
'''
|
"""
|
||||||
if not self.model:
|
if not self.model:
|
||||||
return
|
return
|
||||||
return self.model.linkconfig(netif=netif, bw=bw, delay=delay, loss=loss,
|
return self.model.linkconfig(netif=netif, bw=bw, delay=delay, loss=loss,
|
||||||
duplicate=duplicate, jitter=jitter, netif2=netif2)
|
duplicate=duplicate, jitter=jitter, netif2=netif2)
|
||||||
|
|
||||||
def config(self, conf):
|
def config(self, conf):
|
||||||
#print "emane", self.name, "got config:", conf
|
|
||||||
self.conf = conf
|
self.conf = conf
|
||||||
|
|
||||||
def shutdown(self):
|
def shutdown(self):
|
||||||
|
|
@ -76,55 +79,57 @@ class EmaneNode(EmaneNet):
|
||||||
pass
|
pass
|
||||||
|
|
||||||
def setmodel(self, model, config):
|
def setmodel(self, model, config):
|
||||||
''' set the EmaneModel associated with this node
|
"""
|
||||||
'''
|
set the EmaneModel associated with this node
|
||||||
if (self.verbose):
|
"""
|
||||||
self.info("adding model %s" % model._name)
|
logger.info("adding model: %s", model.name)
|
||||||
if model._type == coreapi.CORE_TLV_REG_WIRELESS:
|
if model.config_type == RegisterTlvs.WIRELESS.value:
|
||||||
# EmaneModel really uses values from ConfigurableManager
|
# EmaneModel really uses values from ConfigurableManager
|
||||||
# when buildnemxml() is called, not during init()
|
# when buildnemxml() is called, not during init()
|
||||||
self.model = model(session=self.session, objid=self.objid,
|
self.model = model(session=self.session, object_id=self.objid)
|
||||||
verbose=self.verbose)
|
elif model.config_type == RegisterTlvs.MOBILITY.value:
|
||||||
elif model._type == coreapi.CORE_TLV_REG_MOBILITY:
|
self.mobility = model(session=self.session, object_id=self.objid, values=config)
|
||||||
self.mobility = model(session=self.session, objid=self.objid,
|
|
||||||
verbose=self.verbose, values=config)
|
|
||||||
|
|
||||||
def setnemid(self, netif, nemid):
|
def setnemid(self, netif, nemid):
|
||||||
''' Record an interface to numerical ID mapping. The Emane controller
|
"""
|
||||||
|
Record an interface to numerical ID mapping. The Emane controller
|
||||||
object manages and assigns these IDs for all NEMs.
|
object manages and assigns these IDs for all NEMs.
|
||||||
'''
|
"""
|
||||||
self.nemidmap[netif] = nemid
|
self.nemidmap[netif] = nemid
|
||||||
|
|
||||||
def getnemid(self, netif):
|
def getnemid(self, netif):
|
||||||
''' Given an interface, return its numerical ID.
|
"""
|
||||||
'''
|
Given an interface, return its numerical ID.
|
||||||
|
"""
|
||||||
if netif not in self.nemidmap:
|
if netif not in self.nemidmap:
|
||||||
return None
|
return None
|
||||||
else:
|
else:
|
||||||
return self.nemidmap[netif]
|
return self.nemidmap[netif]
|
||||||
|
|
||||||
def getnemnetif(self, nemid):
|
def getnemnetif(self, nemid):
|
||||||
''' Given a numerical NEM ID, return its interface. This returns the
|
"""
|
||||||
|
Given a numerical NEM ID, return its interface. This returns the
|
||||||
first interface that matches the given NEM ID.
|
first interface that matches the given NEM ID.
|
||||||
'''
|
"""
|
||||||
for netif in self.nemidmap:
|
for netif in self.nemidmap:
|
||||||
if self.nemidmap[netif] == nemid:
|
if self.nemidmap[netif] == nemid:
|
||||||
return netif
|
return netif
|
||||||
return None
|
return None
|
||||||
|
|
||||||
def netifs(self, sort=True):
|
def netifs(self, sort=True):
|
||||||
''' Retrieve list of linked interfaces sorted by node number.
|
"""
|
||||||
'''
|
Retrieve list of linked interfaces sorted by node number.
|
||||||
|
"""
|
||||||
return sorted(self._netif.values(), key=lambda ifc: ifc.node.objid)
|
return sorted(self._netif.values(), key=lambda ifc: ifc.node.objid)
|
||||||
|
|
||||||
def buildplatformxmlentry(self, doc):
|
def buildplatformxmlentry(self, doc):
|
||||||
''' Return a dictionary of XML elements describing the NEMs
|
"""
|
||||||
|
Return a dictionary of XML elements describing the NEMs
|
||||||
connected to this EmaneNode for inclusion in the platform.xml file.
|
connected to this EmaneNode for inclusion in the platform.xml file.
|
||||||
'''
|
"""
|
||||||
ret = {}
|
ret = {}
|
||||||
if self.model is None:
|
if self.model is None:
|
||||||
self.info("warning: EmaneNode %s has no associated model" % \
|
logger.info("warning: EmaneNode %s has no associated model" % self.name)
|
||||||
self.name)
|
|
||||||
return ret
|
return ret
|
||||||
for netif in self.netifs():
|
for netif in self.netifs():
|
||||||
# <nem name="NODE-001" definition="rfpipenem.xml">
|
# <nem name="NODE-001" definition="rfpipenem.xml">
|
||||||
|
|
@ -139,9 +144,9 @@ class EmaneNode(EmaneNet):
|
||||||
return ret
|
return ret
|
||||||
|
|
||||||
def buildnemxmlfiles(self, emane):
|
def buildnemxmlfiles(self, emane):
|
||||||
''' Let the configured model build the necessary nem, mac, and phy
|
"""
|
||||||
XMLs.
|
Let the configured model build the necessary nem, mac, and phy XMLs.
|
||||||
'''
|
"""
|
||||||
if self.model is None:
|
if self.model is None:
|
||||||
return
|
return
|
||||||
# build XML for overall network (EmaneNode) configs
|
# build XML for overall network (EmaneNode) configs
|
||||||
|
|
@ -166,8 +171,9 @@ class EmaneNode(EmaneNet):
|
||||||
self.buildtransportxml(emane, rtype)
|
self.buildtransportxml(emane, rtype)
|
||||||
|
|
||||||
def buildtransportxml(self, emane, type):
|
def buildtransportxml(self, emane, type):
|
||||||
''' Write a transport XML file for the Virtual or Raw Transport.
|
"""
|
||||||
'''
|
Write a transport XML file for the Virtual or Raw Transport.
|
||||||
|
"""
|
||||||
transdoc = emane.xmldoc("transport")
|
transdoc = emane.xmldoc("transport")
|
||||||
trans = transdoc.getElementsByTagName("transport").pop()
|
trans = transdoc.getElementsByTagName("transport").pop()
|
||||||
trans.setAttribute("name", "%s Transport" % type.capitalize())
|
trans.setAttribute("name", "%s Transport" % type.capitalize())
|
||||||
|
|
@ -176,7 +182,7 @@ class EmaneNode(EmaneNet):
|
||||||
|
|
||||||
flowcontrol = False
|
flowcontrol = False
|
||||||
names = self.model.getnames()
|
names = self.model.getnames()
|
||||||
values = emane.getconfig(self.objid, self.model._name,
|
values = emane.getconfig(self.objid, self.model.name,
|
||||||
self.model.getdefaultvalues())[1]
|
self.model.getdefaultvalues())[1]
|
||||||
if "flowcontrolenable" in names and values:
|
if "flowcontrolenable" in names and values:
|
||||||
i = names.index("flowcontrolenable")
|
i = names.index("flowcontrolenable")
|
||||||
|
|
@ -184,35 +190,30 @@ class EmaneNode(EmaneNet):
|
||||||
flowcontrol = True
|
flowcontrol = True
|
||||||
|
|
||||||
if "virtual" in type.lower():
|
if "virtual" in type.lower():
|
||||||
if os.path.exists("/dev/net/tun_flowctl"):
|
if path.exists("/dev/net/tun_flowctl"):
|
||||||
trans.appendChild(emane.xmlparam(transdoc, "devicepath",
|
trans.appendChild(emane.xmlparam(transdoc, "devicepath", "/dev/net/tun_flowctl"))
|
||||||
"/dev/net/tun_flowctl"))
|
|
||||||
else:
|
else:
|
||||||
trans.appendChild(emane.xmlparam(transdoc, "devicepath",
|
trans.appendChild(emane.xmlparam(transdoc, "devicepath", "/dev/net/tun"))
|
||||||
"/dev/net/tun"))
|
|
||||||
if flowcontrol:
|
if flowcontrol:
|
||||||
trans.appendChild(emane.xmlparam(transdoc, "flowcontrolenable",
|
trans.appendChild(emane.xmlparam(transdoc, "flowcontrolenable", "on"))
|
||||||
"on"))
|
|
||||||
emane.xmlwrite(transdoc, self.transportxmlname(type.lower()))
|
emane.xmlwrite(transdoc, self.transportxmlname(type.lower()))
|
||||||
|
|
||||||
def transportxmlname(self, type):
|
def transportxmlname(self, type):
|
||||||
''' Return the string name for the Transport XML file,
|
"""
|
||||||
e.g. 'n3transvirtual.xml'
|
Return the string name for the Transport XML file, e.g. 'n3transvirtual.xml'
|
||||||
'''
|
"""
|
||||||
return "n%strans%s.xml" % (self.objid, type)
|
return "n%strans%s.xml" % (self.objid, type)
|
||||||
|
|
||||||
|
|
||||||
def installnetifs(self, do_netns=True):
|
def installnetifs(self, do_netns=True):
|
||||||
''' Install TAP devices into their namespaces. This is done after
|
"""
|
||||||
|
Install TAP devices into their namespaces. This is done after
|
||||||
EMANE daemons have been started, because that is their only chance
|
EMANE daemons have been started, because that is their only chance
|
||||||
to bind to the TAPs.
|
to bind to the TAPs.
|
||||||
'''
|
"""
|
||||||
if self.session.emane.genlocationevents() and \
|
if self.session.emane.genlocationevents() and self.session.emane.service is None:
|
||||||
self.session.emane.service is None:
|
|
||||||
warntxt = "unable to publish EMANE events because the eventservice "
|
warntxt = "unable to publish EMANE events because the eventservice "
|
||||||
warntxt += "Python bindings failed to load"
|
warntxt += "Python bindings failed to load"
|
||||||
self.session.exception(coreapi.CORE_EXCP_LEVEL_ERROR, self.name,
|
logger.error(warntxt)
|
||||||
self.objid, warntxt)
|
|
||||||
|
|
||||||
for netif in self.netifs():
|
for netif in self.netifs():
|
||||||
if do_netns and "virtual" in netif.transport_type.lower():
|
if do_netns and "virtual" in netif.transport_type.lower():
|
||||||
|
|
@ -228,63 +229,65 @@ class EmaneNode(EmaneNet):
|
||||||
self.setnemposition(netif, x, y, z)
|
self.setnemposition(netif, x, y, z)
|
||||||
|
|
||||||
def deinstallnetifs(self):
|
def deinstallnetifs(self):
|
||||||
''' Uninstall TAP devices. This invokes their shutdown method for
|
"""
|
||||||
|
Uninstall TAP devices. This invokes their shutdown method for
|
||||||
any required cleanup; the device may be actually removed when
|
any required cleanup; the device may be actually removed when
|
||||||
emanetransportd terminates.
|
emanetransportd terminates.
|
||||||
'''
|
"""
|
||||||
for netif in self.netifs():
|
for netif in self.netifs():
|
||||||
if "virtual" in netif.transport_type.lower():
|
if "virtual" in netif.transport_type.lower():
|
||||||
netif.shutdown()
|
netif.shutdown()
|
||||||
netif.poshook = None
|
netif.poshook = None
|
||||||
|
|
||||||
def setnemposition(self, netif, x, y, z):
|
def setnemposition(self, netif, x, y, z):
|
||||||
''' Publish a NEM location change event using the EMANE event service.
|
"""
|
||||||
'''
|
Publish a NEM location change event using the EMANE event service.
|
||||||
|
"""
|
||||||
if self.session.emane.service is None:
|
if self.session.emane.service is None:
|
||||||
if self.verbose:
|
logger.info("position service not available")
|
||||||
self.info("position service not available")
|
|
||||||
return
|
return
|
||||||
nemid = self.getnemid(netif)
|
nemid = self.getnemid(netif)
|
||||||
ifname = netif.localname
|
ifname = netif.localname
|
||||||
if nemid is None:
|
if nemid is None:
|
||||||
self.info("nemid for %s is unknown" % ifname)
|
logger.info("nemid for %s is unknown" % ifname)
|
||||||
return
|
return
|
||||||
(lat, long, alt) = self.session.location.getgeo(x, y, z)
|
(lat, long, alt) = self.session.location.getgeo(x, y, z)
|
||||||
if self.verbose:
|
logger.info("setnemposition %s (%s) x,y,z=(%d,%d,%s)"
|
||||||
self.info("setnemposition %s (%s) x,y,z=(%d,%d,%s)"
|
|
||||||
"(%.6f,%.6f,%.6f)" % \
|
"(%.6f,%.6f,%.6f)" % \
|
||||||
(ifname, nemid, x, y, z, lat, long, alt))
|
(ifname, nemid, x, y, z, lat, long, alt))
|
||||||
if self.session.emane.version >= self.session.emane.EMANE091:
|
if emane.VERSION >= emane.EMANE091:
|
||||||
event = LocationEvent()
|
event = LocationEvent()
|
||||||
else:
|
else:
|
||||||
event = emaneeventlocation.EventLocation(1)
|
event = emaneeventlocation.EventLocation(1)
|
||||||
# altitude must be an integer or warning is printed
|
# altitude must be an integer or warning is printed
|
||||||
# unused: yaw, pitch, roll, azimuth, elevation, velocity
|
# unused: yaw, pitch, roll, azimuth, elevation, velocity
|
||||||
alt = int(round(alt))
|
alt = int(round(alt))
|
||||||
if self.session.emane.version >= self.session.emane.EMANE091:
|
if emane.VERSION >= emane.EMANE091:
|
||||||
event.append(nemid, latitude=lat, longitude=long, altitude=alt)
|
event.append(nemid, latitude=lat, longitude=long, altitude=alt)
|
||||||
self.session.emane.service.publish(0, event)
|
self.session.emane.service.publish(0, event)
|
||||||
else:
|
else:
|
||||||
event.set(0, nemid, lat, long, alt)
|
event.set(0, nemid, lat, long, alt)
|
||||||
self.session.emane.service.publish(emaneeventlocation.EVENT_ID,
|
self.session.emane.service.publish(
|
||||||
|
emaneeventlocation.EVENT_ID,
|
||||||
emaneeventservice.PLATFORMID_ANY,
|
emaneeventservice.PLATFORMID_ANY,
|
||||||
emaneeventservice.NEMID_ANY,
|
emaneeventservice.NEMID_ANY,
|
||||||
emaneeventservice.COMPONENTID_ANY,
|
emaneeventservice.COMPONENTID_ANY,
|
||||||
event.export())
|
event.export()
|
||||||
|
)
|
||||||
|
|
||||||
def setnempositions(self, moved_netifs):
|
def setnempositions(self, moved_netifs):
|
||||||
''' Several NEMs have moved, from e.g. a WaypointMobilityModel
|
"""
|
||||||
|
Several NEMs have moved, from e.g. a WaypointMobilityModel
|
||||||
calculation. Generate an EMANE Location Event having several
|
calculation. Generate an EMANE Location Event having several
|
||||||
entries for each netif that has moved.
|
entries for each netif that has moved.
|
||||||
'''
|
"""
|
||||||
if len(moved_netifs) == 0:
|
if len(moved_netifs) == 0:
|
||||||
return
|
return
|
||||||
if self.session.emane.service is None:
|
if self.session.emane.service is None:
|
||||||
if self.verbose:
|
logger.info("position service not available")
|
||||||
self.info("position service not available")
|
|
||||||
return
|
return
|
||||||
|
|
||||||
if self.session.emane.version >= self.session.emane.EMANE091:
|
if emane.VERSION >= emane.EMANE091:
|
||||||
event = LocationEvent()
|
event = LocationEvent()
|
||||||
else:
|
else:
|
||||||
event = emaneeventlocation.EventLocation(len(moved_netifs))
|
event = emaneeventlocation.EventLocation(len(moved_netifs))
|
||||||
|
|
@ -293,29 +296,28 @@ class EmaneNode(EmaneNet):
|
||||||
nemid = self.getnemid(netif)
|
nemid = self.getnemid(netif)
|
||||||
ifname = netif.localname
|
ifname = netif.localname
|
||||||
if nemid is None:
|
if nemid is None:
|
||||||
self.info("nemid for %s is unknown" % ifname)
|
logger.info("nemid for %s is unknown" % ifname)
|
||||||
continue
|
continue
|
||||||
(x, y, z) = netif.node.getposition()
|
(x, y, z) = netif.node.getposition()
|
||||||
(lat, long, alt) = self.session.location.getgeo(x, y, z)
|
(lat, long, alt) = self.session.location.getgeo(x, y, z)
|
||||||
if self.verbose:
|
logger.info("setnempositions %d %s (%s) x,y,z=(%d,%d,%s)"
|
||||||
self.info("setnempositions %d %s (%s) x,y,z=(%d,%d,%s)"
|
"(%.6f,%.6f,%.6f)" %
|
||||||
"(%.6f,%.6f,%.6f)" % \
|
|
||||||
(i, ifname, nemid, x, y, z, lat, long, alt))
|
(i, ifname, nemid, x, y, z, lat, long, alt))
|
||||||
# altitude must be an integer or warning is printed
|
# altitude must be an integer or warning is printed
|
||||||
alt = int(round(alt))
|
alt = int(round(alt))
|
||||||
if self.session.emane.version >= self.session.emane.EMANE091:
|
if emane.VERSION >= emane.EMANE091:
|
||||||
event.append(nemid, latitude=lat, longitude=long, altitude=alt)
|
event.append(nemid, latitude=lat, longitude=long, altitude=alt)
|
||||||
else:
|
else:
|
||||||
event.set(i, nemid, lat, long, alt)
|
event.set(i, nemid, lat, long, alt)
|
||||||
i += 1
|
i += 1
|
||||||
|
|
||||||
if self.session.emane.version >= self.session.emane.EMANE091:
|
if emane.VERSION >= emane.EMANE091:
|
||||||
self.session.emane.service.publish(0, event)
|
self.session.emane.service.publish(0, event)
|
||||||
else:
|
else:
|
||||||
self.session.emane.service.publish(emaneeventlocation.EVENT_ID,
|
self.session.emane.service.publish(
|
||||||
|
emaneeventlocation.EVENT_ID,
|
||||||
emaneeventservice.PLATFORMID_ANY,
|
emaneeventservice.PLATFORMID_ANY,
|
||||||
emaneeventservice.NEMID_ANY,
|
emaneeventservice.NEMID_ANY,
|
||||||
emaneeventservice.COMPONENTID_ANY,
|
emaneeventservice.COMPONENTID_ANY,
|
||||||
event.export())
|
event.export()
|
||||||
|
)
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,33 +1,28 @@
|
||||||
#
|
"""
|
||||||
# CORE
|
|
||||||
# Copyright (c)2010-2014 the Boeing Company.
|
|
||||||
# See the LICENSE file included in this distribution.
|
|
||||||
#
|
|
||||||
# authors: Jeff Ahrenholz <jeffrey.m.ahrenholz@boeing.com>
|
|
||||||
# Harry Bullen <hbullen@i-a-i.com>
|
|
||||||
#
|
|
||||||
'''
|
|
||||||
rfpipe.py: EMANE RF-PIPE model for CORE
|
rfpipe.py: EMANE RF-PIPE model for CORE
|
||||||
'''
|
"""
|
||||||
|
|
||||||
|
from core import emane
|
||||||
|
from core.emane.emanemodel import EmaneModel
|
||||||
|
from core.emane.universal import EmaneUniversalModel
|
||||||
|
from core.enumerations import ConfigDataTypes
|
||||||
|
from core.misc import log
|
||||||
|
|
||||||
|
logger = log.get_logger(__name__)
|
||||||
|
|
||||||
import sys
|
|
||||||
import string
|
|
||||||
try:
|
try:
|
||||||
from emanesh.events import EventService
|
from emanesh.events import EventService
|
||||||
except:
|
except ImportError:
|
||||||
pass
|
logger.error("error importing emanesh")
|
||||||
from core.api import coreapi
|
|
||||||
from core.constants import *
|
|
||||||
from emane import Emane, EmaneModel
|
|
||||||
from universal import EmaneUniversalModel
|
|
||||||
|
|
||||||
class EmaneRfPipeModel(EmaneModel):
|
class EmaneRfPipeModel(EmaneModel):
|
||||||
def __init__(self, session, objid = None, verbose = False):
|
def __init__(self, session, object_id=None):
|
||||||
EmaneModel.__init__(self, session, objid, verbose)
|
EmaneModel.__init__(self, session, object_id)
|
||||||
|
|
||||||
# model name
|
# model name
|
||||||
_name = "emane_rfpipe"
|
name = "emane_rfpipe"
|
||||||
if Emane.version >= Emane.EMANE091:
|
if emane.VERSION >= emane.EMANE091:
|
||||||
xml_path = '/usr/share/emane/xml/models/mac/rfpipe'
|
xml_path = '/usr/share/emane/xml/models/mac/rfpipe'
|
||||||
else:
|
else:
|
||||||
xml_path = "/usr/share/emane/models/rfpipe/xml"
|
xml_path = "/usr/share/emane/models/rfpipe/xml"
|
||||||
|
|
@ -36,68 +31,69 @@ class EmaneRfPipeModel(EmaneModel):
|
||||||
# ( 'name', 'type', 'default', 'possible-value-list', 'caption')
|
# ( 'name', 'type', 'default', 'possible-value-list', 'caption')
|
||||||
# MAC parameters
|
# MAC parameters
|
||||||
_confmatrix_mac_base = [
|
_confmatrix_mac_base = [
|
||||||
("enablepromiscuousmode", coreapi.CONF_DATA_TYPE_BOOL, '0',
|
("enablepromiscuousmode", ConfigDataTypes.BOOL.value, '0',
|
||||||
'True,False', 'enable promiscuous mode'),
|
'True,False', 'enable promiscuous mode'),
|
||||||
("datarate", coreapi.CONF_DATA_TYPE_UINT32, '1M',
|
("datarate", ConfigDataTypes.UINT32.value, '1M',
|
||||||
'', 'data rate (bps)'),
|
'', 'data rate (bps)'),
|
||||||
("flowcontrolenable", coreapi.CONF_DATA_TYPE_BOOL, '0',
|
("flowcontrolenable", ConfigDataTypes.BOOL.value, '0',
|
||||||
'On,Off', 'enable traffic flow control'),
|
'On,Off', 'enable traffic flow control'),
|
||||||
("flowcontroltokens", coreapi.CONF_DATA_TYPE_UINT16, '10',
|
("flowcontroltokens", ConfigDataTypes.UINT16.value, '10',
|
||||||
'', 'number of flow control tokens'),
|
'', 'number of flow control tokens'),
|
||||||
("pcrcurveuri", coreapi.CONF_DATA_TYPE_STRING,
|
("pcrcurveuri", ConfigDataTypes.STRING.value,
|
||||||
'%s/rfpipepcr.xml' % xml_path,
|
'%s/rfpipepcr.xml' % xml_path,
|
||||||
'', 'SINR/PCR curve file'),
|
'', 'SINR/PCR curve file'),
|
||||||
]
|
]
|
||||||
_confmatrix_mac_081 = [
|
_confmatrix_mac_081 = [
|
||||||
("jitter", coreapi.CONF_DATA_TYPE_FLOAT, '0.0',
|
("jitter", ConfigDataTypes.FLOAT.value, '0.0',
|
||||||
'', 'transmission jitter (usec)'),
|
'', 'transmission jitter (usec)'),
|
||||||
("delay", coreapi.CONF_DATA_TYPE_FLOAT, '0.0',
|
("delay", ConfigDataTypes.FLOAT.value, '0.0',
|
||||||
'', 'transmission delay (usec)'),
|
'', 'transmission delay (usec)'),
|
||||||
("transmissioncontrolmap", coreapi.CONF_DATA_TYPE_STRING, '',
|
("transmissioncontrolmap", ConfigDataTypes.STRING.value, '',
|
||||||
'', 'tx control map (nem:rate:freq:tx_dBm)'),
|
'', 'tx control map (nem:rate:freq:tx_dBm)'),
|
||||||
("enabletighttiming", coreapi.CONF_DATA_TYPE_BOOL, '0',
|
("enabletighttiming", ConfigDataTypes.BOOL.value, '0',
|
||||||
'On,Off', 'enable tight timing for pkt delay'),
|
'On,Off', 'enable tight timing for pkt delay'),
|
||||||
]
|
]
|
||||||
_confmatrix_mac_091 = [
|
_confmatrix_mac_091 = [
|
||||||
("jitter", coreapi.CONF_DATA_TYPE_FLOAT, '0.0',
|
("jitter", ConfigDataTypes.FLOAT.value, '0.0',
|
||||||
'', 'transmission jitter (sec)'),
|
'', 'transmission jitter (sec)'),
|
||||||
("delay", coreapi.CONF_DATA_TYPE_FLOAT, '0.0',
|
("delay", ConfigDataTypes.FLOAT.value, '0.0',
|
||||||
'', 'transmission delay (sec)'),
|
'', 'transmission delay (sec)'),
|
||||||
('radiometricenable', coreapi.CONF_DATA_TYPE_BOOL, '0',
|
('radiometricenable', ConfigDataTypes.BOOL.value, '0',
|
||||||
'On,Off', 'report radio metrics via R2RI'),
|
'On,Off', 'report radio metrics via R2RI'),
|
||||||
('radiometricreportinterval', coreapi.CONF_DATA_TYPE_FLOAT, '1.0',
|
('radiometricreportinterval', ConfigDataTypes.FLOAT.value, '1.0',
|
||||||
'', 'R2RI radio metric report interval (sec)'),
|
'', 'R2RI radio metric report interval (sec)'),
|
||||||
('neighbormetricdeletetime', coreapi.CONF_DATA_TYPE_FLOAT, '60.0',
|
('neighbormetricdeletetime', ConfigDataTypes.FLOAT.value, '60.0',
|
||||||
'', 'R2RI neighbor table inactivity time (sec)'),
|
'', 'R2RI neighbor table inactivity time (sec)'),
|
||||||
]
|
]
|
||||||
if Emane.version >= Emane.EMANE091:
|
if emane.VERSION >= emane.EMANE091:
|
||||||
_confmatrix_mac = _confmatrix_mac_base + _confmatrix_mac_091
|
_confmatrix_mac = _confmatrix_mac_base + _confmatrix_mac_091
|
||||||
else:
|
else:
|
||||||
_confmatrix_mac = _confmatrix_mac_base + _confmatrix_mac_081
|
_confmatrix_mac = _confmatrix_mac_base + _confmatrix_mac_081
|
||||||
|
|
||||||
# PHY parameters from Universal PHY
|
# PHY parameters from Universal PHY
|
||||||
_confmatrix_phy = EmaneUniversalModel._confmatrix
|
_confmatrix_phy = EmaneUniversalModel.config_matrix
|
||||||
|
|
||||||
_confmatrix = _confmatrix_mac + _confmatrix_phy
|
config_matrix = _confmatrix_mac + _confmatrix_phy
|
||||||
|
|
||||||
# value groupings
|
# value groupings
|
||||||
_confgroups = "RF-PIPE MAC Parameters:1-%d|Universal PHY Parameters:%d-%d" \
|
config_groups = "RF-PIPE MAC Parameters:1-%d|Universal PHY Parameters:%d-%d" % (
|
||||||
% ( len(_confmatrix_mac), len(_confmatrix_mac) + 1, len(_confmatrix))
|
len(_confmatrix_mac), len(_confmatrix_mac) + 1, len(config_matrix))
|
||||||
|
|
||||||
def buildnemxmlfiles(self, e, ifc):
|
def buildnemxmlfiles(self, e, ifc):
|
||||||
''' Build the necessary nem, mac, and phy XMLs in the given path.
|
"""
|
||||||
|
Build the necessary nem, mac, and phy XMLs in the given path.
|
||||||
If an individual NEM has a nonstandard config, we need to build
|
If an individual NEM has a nonstandard config, we need to build
|
||||||
that file also. Otherwise the WLAN-wide nXXemane_rfpipenem.xml,
|
that file also. Otherwise the WLAN-wide nXXemane_rfpipenem.xml,
|
||||||
nXXemane_rfpipemac.xml, nXXemane_rfpipephy.xml are used.
|
nXXemane_rfpipemac.xml, nXXemane_rfpipephy.xml are used.
|
||||||
'''
|
"""
|
||||||
values = e.getifcconfig(self.objid, self._name,
|
values = e.getifcconfig(self.object_id, self.name,
|
||||||
self.getdefaultvalues(), ifc)
|
self.getdefaultvalues(), ifc)
|
||||||
if values is None:
|
if values is None:
|
||||||
return
|
return
|
||||||
nemdoc = e.xmldoc("nem")
|
nemdoc = e.xmldoc("nem")
|
||||||
nem = nemdoc.getElementsByTagName("nem").pop()
|
nem = nemdoc.getElementsByTagName("nem").pop()
|
||||||
nem.setAttribute("name", "RF-PIPE NEM")
|
nem.setAttribute("name", "RF-PIPE NEM")
|
||||||
e.appendtransporttonem(nemdoc, nem, self.objid, ifc)
|
e.appendtransporttonem(nemdoc, nem, self.object_id, ifc)
|
||||||
mactag = nemdoc.createElement("mac")
|
mactag = nemdoc.createElement("mac")
|
||||||
mactag.setAttribute("definition", self.macxmlname(ifc))
|
mactag.setAttribute("definition", self.macxmlname(ifc))
|
||||||
nem.appendChild(mactag)
|
nem.appendChild(mactag)
|
||||||
|
|
@ -124,10 +120,8 @@ class EmaneRfPipeModel(EmaneModel):
|
||||||
values = list(values)
|
values = list(values)
|
||||||
values[i] = self.emane074_fixup(values[i], 1000)
|
values[i] = self.emane074_fixup(values[i], 1000)
|
||||||
# append MAC options to macdoc
|
# append MAC options to macdoc
|
||||||
map(lambda n: mac.appendChild(e.xmlparam(macdoc, n, \
|
map(lambda n: mac.appendChild(e.xmlparam(macdoc, n, self.valueof(n, values))), macnames)
|
||||||
self.valueof(n, values))), macnames)
|
|
||||||
e.xmlwrite(macdoc, self.macxmlname(ifc))
|
e.xmlwrite(macdoc, self.macxmlname(ifc))
|
||||||
|
|
||||||
phydoc = EmaneUniversalModel.getphydoc(e, self, values, phynames)
|
phydoc = EmaneUniversalModel.getphydoc(e, self, values, phynames)
|
||||||
e.xmlwrite(phydoc, self.phyxmlname(ifc))
|
e.xmlwrite(phydoc, self.phyxmlname(ifc))
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,91 +1,85 @@
|
||||||
|
"""
|
||||||
#
|
|
||||||
# CORE
|
|
||||||
# Copyright (c)2013 Company.
|
|
||||||
# See the LICENSE file included in this distribution.
|
|
||||||
#
|
|
||||||
# author: Name <email@company.com>
|
|
||||||
#
|
|
||||||
'''
|
|
||||||
tdma.py: EMANE TDMA model bindings for CORE
|
tdma.py: EMANE TDMA model bindings for CORE
|
||||||
'''
|
"""
|
||||||
|
|
||||||
|
from core import emane
|
||||||
|
from core.emane.emanemodel import EmaneModel
|
||||||
|
from core.emane.universal import EmaneUniversalModel
|
||||||
|
from core.enumerations import ConfigDataTypes
|
||||||
|
from core.misc import log
|
||||||
|
|
||||||
|
logger = log.get_logger(__name__)
|
||||||
|
|
||||||
import sys
|
|
||||||
import string
|
|
||||||
try:
|
try:
|
||||||
from emanesh.events import EventService
|
from emanesh.events import EventService
|
||||||
except:
|
except:
|
||||||
pass
|
logger.error("error importing emanesh")
|
||||||
from core.api import coreapi
|
|
||||||
from core.constants import *
|
|
||||||
from emane import Emane, EmaneModel
|
|
||||||
from universal import EmaneUniversalModel
|
|
||||||
|
|
||||||
class EmaneTdmaModel(EmaneModel):
|
class EmaneTdmaModel(EmaneModel):
|
||||||
def __init__(self, session, objid = None, verbose = False):
|
def __init__(self, session, object_id=None):
|
||||||
EmaneModel.__init__(self, session, objid, verbose)
|
EmaneModel.__init__(self, session, object_id)
|
||||||
|
|
||||||
# model name
|
# model name
|
||||||
_name = "emane_tdma"
|
name = "emane_tdma"
|
||||||
if Emane.version >= Emane.EMANE101:
|
if emane.VERSION >= emane.EMANE101:
|
||||||
xml_path = '/usr/share/emane/xml/models/mac/tdmaeventscheduler'
|
xml_path = '/usr/share/emane/xml/models/mac/tdmaeventscheduler'
|
||||||
else:
|
else:
|
||||||
raise Exception("EMANE TDMA requires EMANE 1.0.1 or greater")
|
raise Exception("EMANE TDMA requires EMANE 1.0.1 or greater")
|
||||||
|
|
||||||
|
|
||||||
# MAC parameters
|
# MAC parameters
|
||||||
_confmatrix_mac = [
|
_confmatrix_mac = [
|
||||||
("enablepromiscuousmode", coreapi.CONF_DATA_TYPE_BOOL, '0',
|
("enablepromiscuousmode", ConfigDataTypes.BOOL.value, '0',
|
||||||
'True,False', 'enable promiscuous mode'),
|
'True,False', 'enable promiscuous mode'),
|
||||||
("flowcontrolenable", coreapi.CONF_DATA_TYPE_BOOL, '0',
|
("flowcontrolenable", ConfigDataTypes.BOOL.value, '0',
|
||||||
'On,Off', 'enable traffic flow control'),
|
'On,Off', 'enable traffic flow control'),
|
||||||
("flowcontroltokens", coreapi.CONF_DATA_TYPE_UINT16, '10',
|
("flowcontroltokens", ConfigDataTypes.UINT16.value, '10',
|
||||||
'', 'number of flow control tokens'),
|
'', 'number of flow control tokens'),
|
||||||
("fragmentcheckthreshold", coreapi.CONF_DATA_TYPE_UINT16, '2',
|
("fragmentcheckthreshold", ConfigDataTypes.UINT16.value, '2',
|
||||||
'', 'rate in seconds for check if fragment reassembly efforts should be abandoned'),
|
'', 'rate in seconds for check if fragment reassembly efforts should be abandoned'),
|
||||||
("fragmenttimeoutthreshold", coreapi.CONF_DATA_TYPE_UINT16, '5',
|
("fragmenttimeoutthreshold", ConfigDataTypes.UINT16.value, '5',
|
||||||
'', 'threshold in seconds to wait for another packet fragment for reassembly'),
|
'', 'threshold in seconds to wait for another packet fragment for reassembly'),
|
||||||
('neighbormetricdeletetime', coreapi.CONF_DATA_TYPE_FLOAT, '60.0',
|
('neighbormetricdeletetime', ConfigDataTypes.FLOAT.value, '60.0',
|
||||||
'', 'neighbor RF reception timeout for removal from neighbor table (sec)'),
|
'', 'neighbor RF reception timeout for removal from neighbor table (sec)'),
|
||||||
('neighbormetricupdateinterval', coreapi.CONF_DATA_TYPE_FLOAT, '1.0',
|
('neighbormetricupdateinterval', ConfigDataTypes.FLOAT.value, '1.0',
|
||||||
'', 'neighbor table update interval (sec)'),
|
'', 'neighbor table update interval (sec)'),
|
||||||
("pcrcurveuri", coreapi.CONF_DATA_TYPE_STRING, '%s/tdmabasemodelpcr.xml' % xml_path,
|
("pcrcurveuri", ConfigDataTypes.STRING.value, '%s/tdmabasemodelpcr.xml' % xml_path,
|
||||||
'', 'SINR/PCR curve file'),
|
'', 'SINR/PCR curve file'),
|
||||||
("queue.aggregationenable", coreapi.CONF_DATA_TYPE_BOOL, '1',
|
("queue.aggregationenable", ConfigDataTypes.BOOL.value, '1',
|
||||||
'On,Off', 'enable transmit packet aggregation'),
|
'On,Off', 'enable transmit packet aggregation'),
|
||||||
('queue.aggregationslotthreshold', coreapi.CONF_DATA_TYPE_FLOAT, '90.0',
|
('queue.aggregationslotthreshold', ConfigDataTypes.FLOAT.value, '90.0',
|
||||||
'', 'percentage of a slot that must be filled in order to conclude aggregation'),
|
'', 'percentage of a slot that must be filled in order to conclude aggregation'),
|
||||||
("queue.depth", coreapi.CONF_DATA_TYPE_UINT16, '256',
|
("queue.depth", ConfigDataTypes.UINT16.value, '256',
|
||||||
'', 'size of the per service class downstream packet queues (packets)'),
|
'', 'size of the per service class downstream packet queues (packets)'),
|
||||||
("queue.fragmentationenable", coreapi.CONF_DATA_TYPE_BOOL, '1',
|
("queue.fragmentationenable", ConfigDataTypes.BOOL.value, '1',
|
||||||
'On,Off', 'enable packet fragmentation (over multiple slots)'),
|
'On,Off', 'enable packet fragmentation (over multiple slots)'),
|
||||||
("queue.strictdequeueenable", coreapi.CONF_DATA_TYPE_BOOL, '0',
|
("queue.strictdequeueenable", ConfigDataTypes.BOOL.value, '0',
|
||||||
'On,Off', 'enable strict dequeueing to specified queues only'),
|
'On,Off', 'enable strict dequeueing to specified queues only'),
|
||||||
]
|
]
|
||||||
|
|
||||||
# PHY parameters from Universal PHY
|
# PHY parameters from Universal PHY
|
||||||
_confmatrix_phy = EmaneUniversalModel._confmatrix
|
_confmatrix_phy = EmaneUniversalModel.config_matrix
|
||||||
|
|
||||||
_confmatrix = _confmatrix_mac + _confmatrix_phy
|
config_matrix = _confmatrix_mac + _confmatrix_phy
|
||||||
|
|
||||||
# value groupings
|
# value groupings
|
||||||
_confgroups = "TDMA MAC Parameters:1-%d|Universal PHY Parameters:%d-%d" % \
|
config_groups = "TDMA MAC Parameters:1-%d|Universal PHY Parameters:%d-%d" % (
|
||||||
(len(_confmatrix_mac), len(_confmatrix_mac) + 1, len(_confmatrix))
|
len(_confmatrix_mac), len(_confmatrix_mac) + 1, len(config_matrix))
|
||||||
|
|
||||||
def buildnemxmlfiles(self, e, ifc):
|
def buildnemxmlfiles(self, e, ifc):
|
||||||
''' Build the necessary nem, mac, and phy XMLs in the given path.
|
"""
|
||||||
|
Build the necessary nem, mac, and phy XMLs in the given path.
|
||||||
If an individual NEM has a nonstandard config, we need to build
|
If an individual NEM has a nonstandard config, we need to build
|
||||||
that file also. Otherwise the WLAN-wide nXXemane_tdmanem.xml,
|
that file also. Otherwise the WLAN-wide nXXemane_tdmanem.xml,
|
||||||
nXXemane_tdmamac.xml, nXXemane_tdmaphy.xml are used.
|
nXXemane_tdmamac.xml, nXXemane_tdmaphy.xml are used.
|
||||||
'''
|
"""
|
||||||
values = e.getifcconfig(self.objid, self._name,
|
values = e.getifcconfig(self.object_id, self.name, self.getdefaultvalues(), ifc)
|
||||||
self.getdefaultvalues(), ifc)
|
|
||||||
if values is None:
|
if values is None:
|
||||||
return
|
return
|
||||||
nemdoc = e.xmldoc("nem")
|
nemdoc = e.xmldoc("nem")
|
||||||
nem = nemdoc.getElementsByTagName("nem").pop()
|
nem = nemdoc.getElementsByTagName("nem").pop()
|
||||||
nem.setAttribute("name", "TDMA NEM")
|
nem.setAttribute("name", "TDMA NEM")
|
||||||
e.appendtransporttonem(nemdoc, nem, self.objid, ifc)
|
e.appendtransporttonem(nemdoc, nem, self.object_id, ifc)
|
||||||
mactag = nemdoc.createElement("mac")
|
mactag = nemdoc.createElement("mac")
|
||||||
mactag.setAttribute("definition", self.macxmlname(ifc))
|
mactag.setAttribute("definition", self.macxmlname(ifc))
|
||||||
nem.appendChild(mactag)
|
nem.appendChild(mactag)
|
||||||
|
|
@ -105,10 +99,8 @@ class EmaneTdmaModel(EmaneModel):
|
||||||
mac.setAttribute("name", "TDMA MAC")
|
mac.setAttribute("name", "TDMA MAC")
|
||||||
mac.setAttribute("library", "tdmaeventschedulerradiomodel")
|
mac.setAttribute("library", "tdmaeventschedulerradiomodel")
|
||||||
# append MAC options to macdoc
|
# append MAC options to macdoc
|
||||||
map(lambda n: mac.appendChild(e.xmlparam(macdoc, n, \
|
map(lambda n: mac.appendChild(e.xmlparam(macdoc, n, self.valueof(n, values))), macnames)
|
||||||
self.valueof(n, values))), macnames)
|
|
||||||
e.xmlwrite(macdoc, self.macxmlname(ifc))
|
e.xmlwrite(macdoc, self.macxmlname(ifc))
|
||||||
|
|
||||||
phydoc = EmaneUniversalModel.getphydoc(e, self, values, phynames)
|
phydoc = EmaneUniversalModel.getphydoc(e, self, values, phynames)
|
||||||
e.xmlwrite(phydoc, self.phyxmlname(ifc))
|
e.xmlwrite(phydoc, self.phyxmlname(ifc))
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,97 +1,95 @@
|
||||||
#
|
"""
|
||||||
# CORE
|
|
||||||
# Copyright (c)2010-2014 the Boeing Company.
|
|
||||||
# See the LICENSE file included in this distribution.
|
|
||||||
#
|
|
||||||
# author: Jeff Ahrenholz <jeffrey.m.ahrenholz@boeing.com>
|
|
||||||
#
|
|
||||||
'''
|
|
||||||
universal.py: EMANE Universal PHY model for CORE. Enumerates configuration items
|
universal.py: EMANE Universal PHY model for CORE. Enumerates configuration items
|
||||||
used for the Universal PHY.
|
used for the Universal PHY.
|
||||||
'''
|
"""
|
||||||
|
|
||||||
|
from core import emane
|
||||||
|
from core.emane.emanemodel import EmaneModel
|
||||||
|
from core.enumerations import ConfigDataTypes
|
||||||
|
from core.misc import log
|
||||||
|
|
||||||
|
logger = log.get_logger(__name__)
|
||||||
|
|
||||||
import sys
|
|
||||||
import string
|
|
||||||
try:
|
try:
|
||||||
from emanesh.events import EventService
|
from emanesh.events import EventService
|
||||||
except:
|
except ImportError:
|
||||||
pass
|
logger.error("error importing emanesh")
|
||||||
from core.api import coreapi
|
|
||||||
from core.constants import *
|
|
||||||
from emane import Emane, EmaneModel
|
|
||||||
|
|
||||||
class EmaneUniversalModel(EmaneModel):
|
class EmaneUniversalModel(EmaneModel):
|
||||||
''' This Univeral PHY model is meant to be imported by other models,
|
"""
|
||||||
|
This Univeral PHY model is meant to be imported by other models,
|
||||||
not instantiated.
|
not instantiated.
|
||||||
'''
|
"""
|
||||||
def __init__(self, session, objid = None, verbose = False):
|
|
||||||
raise SyntaxError
|
|
||||||
|
|
||||||
_name = "emane_universal"
|
def __init__(self, session, object_id=None):
|
||||||
|
raise NotImplemented("Cannot use this class directly")
|
||||||
|
|
||||||
|
name = "emane_universal"
|
||||||
_xmlname = "universalphy"
|
_xmlname = "universalphy"
|
||||||
_xmllibrary = "universalphylayer"
|
_xmllibrary = "universalphylayer"
|
||||||
|
|
||||||
# universal PHY parameters
|
# universal PHY parameters
|
||||||
_confmatrix_base = [
|
_confmatrix_base = [
|
||||||
("bandwidth", coreapi.CONF_DATA_TYPE_UINT64, '1M',
|
("bandwidth", ConfigDataTypes.UINT64.value, '1M',
|
||||||
'', 'rf bandwidth (hz)'),
|
'', 'rf bandwidth (hz)'),
|
||||||
("frequency", coreapi.CONF_DATA_TYPE_UINT64, '2.347G',
|
("frequency", ConfigDataTypes.UINT64.value, '2.347G',
|
||||||
'', 'frequency (Hz)'),
|
'', 'frequency (Hz)'),
|
||||||
("frequencyofinterest", coreapi.CONF_DATA_TYPE_UINT64, '2.347G',
|
("frequencyofinterest", ConfigDataTypes.UINT64.value, '2.347G',
|
||||||
'', 'frequency of interest (Hz)'),
|
'', 'frequency of interest (Hz)'),
|
||||||
("subid", coreapi.CONF_DATA_TYPE_UINT16, '1',
|
("subid", ConfigDataTypes.UINT16.value, '1',
|
||||||
'', 'subid'),
|
'', 'subid'),
|
||||||
("systemnoisefigure", coreapi.CONF_DATA_TYPE_FLOAT, '4.0',
|
("systemnoisefigure", ConfigDataTypes.FLOAT.value, '4.0',
|
||||||
'', 'system noise figure (dB)'),
|
'', 'system noise figure (dB)'),
|
||||||
("txpower", coreapi.CONF_DATA_TYPE_FLOAT, '0.0',
|
("txpower", ConfigDataTypes.FLOAT.value, '0.0',
|
||||||
'', 'transmit power (dBm)'),
|
'', 'transmit power (dBm)'),
|
||||||
]
|
]
|
||||||
_confmatrix_081 = [
|
_confmatrix_081 = [
|
||||||
("antennagain", coreapi.CONF_DATA_TYPE_FLOAT, '0.0',
|
("antennagain", ConfigDataTypes.FLOAT.value, '0.0',
|
||||||
'', 'antenna gain (dBi)'),
|
'', 'antenna gain (dBi)'),
|
||||||
("antennaazimuth", coreapi.CONF_DATA_TYPE_FLOAT, '0.0',
|
("antennaazimuth", ConfigDataTypes.FLOAT.value, '0.0',
|
||||||
'', 'antenna azimuth (deg)'),
|
'', 'antenna azimuth (deg)'),
|
||||||
("antennaelevation", coreapi.CONF_DATA_TYPE_FLOAT, '0.0',
|
("antennaelevation", ConfigDataTypes.FLOAT.value, '0.0',
|
||||||
'', 'antenna elevation (deg)'),
|
'', 'antenna elevation (deg)'),
|
||||||
("antennaprofileid", coreapi.CONF_DATA_TYPE_STRING, '1',
|
("antennaprofileid", ConfigDataTypes.STRING.value, '1',
|
||||||
'', 'antenna profile ID'),
|
'', 'antenna profile ID'),
|
||||||
("antennaprofilemanifesturi", coreapi.CONF_DATA_TYPE_STRING, '',
|
("antennaprofilemanifesturi", ConfigDataTypes.STRING.value, '',
|
||||||
'', 'antenna profile manifest URI'),
|
'', 'antenna profile manifest URI'),
|
||||||
("antennaprofileenable", coreapi.CONF_DATA_TYPE_BOOL, '0',
|
("antennaprofileenable", ConfigDataTypes.BOOL.value, '0',
|
||||||
'On,Off', 'antenna profile mode'),
|
'On,Off', 'antenna profile mode'),
|
||||||
("defaultconnectivitymode", coreapi.CONF_DATA_TYPE_BOOL, '1',
|
("defaultconnectivitymode", ConfigDataTypes.BOOL.value, '1',
|
||||||
'On,Off', 'default connectivity'),
|
'On,Off', 'default connectivity'),
|
||||||
("frequencyofinterestfilterenable", coreapi.CONF_DATA_TYPE_BOOL, '1',
|
("frequencyofinterestfilterenable", ConfigDataTypes.BOOL.value, '1',
|
||||||
'On,Off', 'frequency of interest filter enable'),
|
'On,Off', 'frequency of interest filter enable'),
|
||||||
("noiseprocessingmode", coreapi.CONF_DATA_TYPE_BOOL, '0',
|
("noiseprocessingmode", ConfigDataTypes.BOOL.value, '0',
|
||||||
'On,Off', 'enable noise processing'),
|
'On,Off', 'enable noise processing'),
|
||||||
("pathlossmode", coreapi.CONF_DATA_TYPE_STRING, '2ray',
|
("pathlossmode", ConfigDataTypes.STRING.value, '2ray',
|
||||||
'pathloss,2ray,freespace', 'path loss mode'),
|
'pathloss,2ray,freespace', 'path loss mode'),
|
||||||
]
|
]
|
||||||
_confmatrix_091 = [
|
_confmatrix_091 = [
|
||||||
("fixedantennagain", coreapi.CONF_DATA_TYPE_FLOAT, '0.0',
|
("fixedantennagain", ConfigDataTypes.FLOAT.value, '0.0',
|
||||||
'', 'antenna gain (dBi)'),
|
'', 'antenna gain (dBi)'),
|
||||||
("fixedantennagainenable", coreapi.CONF_DATA_TYPE_BOOL, '1',
|
("fixedantennagainenable", ConfigDataTypes.BOOL.value, '1',
|
||||||
'On,Off', 'enable fixed antenna gain'),
|
'On,Off', 'enable fixed antenna gain'),
|
||||||
("noisemode", coreapi.CONF_DATA_TYPE_STRING, 'none',
|
("noisemode", ConfigDataTypes.STRING.value, 'none',
|
||||||
'none,all,outofband', 'noise processing mode'),
|
'none,all,outofband', 'noise processing mode'),
|
||||||
("noisebinsize", coreapi.CONF_DATA_TYPE_UINT64, '20',
|
("noisebinsize", ConfigDataTypes.UINT64.value, '20',
|
||||||
'', 'noise bin size in microseconds'),
|
'', 'noise bin size in microseconds'),
|
||||||
("propagationmodel", coreapi.CONF_DATA_TYPE_STRING, '2ray',
|
("propagationmodel", ConfigDataTypes.STRING.value, '2ray',
|
||||||
'precomputed,2ray,freespace', 'path loss mode'),
|
'precomputed,2ray,freespace', 'path loss mode'),
|
||||||
]
|
]
|
||||||
if Emane.version >= Emane.EMANE091:
|
if emane.VERSION >= emane.EMANE091:
|
||||||
_confmatrix = _confmatrix_base + _confmatrix_091
|
config_matrix = _confmatrix_base + _confmatrix_091
|
||||||
else:
|
else:
|
||||||
_confmatrix = _confmatrix_base + _confmatrix_081
|
config_matrix = _confmatrix_base + _confmatrix_081
|
||||||
|
|
||||||
# old parameters
|
# old parameters
|
||||||
_confmatrix_ver074 = [
|
_confmatrix_ver074 = [
|
||||||
("antennaazimuthbeamwidth", coreapi.CONF_DATA_TYPE_FLOAT, '360.0',
|
("antennaazimuthbeamwidth", ConfigDataTypes.FLOAT.value, '360.0',
|
||||||
'', 'azimith beam width (deg)'),
|
'', 'azimith beam width (deg)'),
|
||||||
("antennaelevationbeamwidth", coreapi.CONF_DATA_TYPE_FLOAT, '180.0',
|
("antennaelevationbeamwidth", ConfigDataTypes.FLOAT.value, '180.0',
|
||||||
'', 'elevation beam width (deg)'),
|
'', 'elevation beam width (deg)'),
|
||||||
("antennatype", coreapi.CONF_DATA_TYPE_STRING, 'omnidirectional',
|
("antennatype", ConfigDataTypes.STRING.value, 'omnidirectional',
|
||||||
'omnidirectional,unidirectional', 'antenna type'),
|
'omnidirectional,unidirectional', 'antenna type'),
|
||||||
]
|
]
|
||||||
|
|
||||||
|
|
@ -102,16 +100,15 @@ class EmaneUniversalModel(EmaneModel):
|
||||||
"antennaprofilemanifesturi",
|
"antennaprofilemanifesturi",
|
||||||
"frequencyofinterestfilterenable")
|
"frequencyofinterestfilterenable")
|
||||||
|
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def getphydoc(cls, e, mac, values, phynames):
|
def getphydoc(cls, e, mac, values, phynames):
|
||||||
phydoc = e.xmldoc("phy")
|
phydoc = e.xmldoc("phy")
|
||||||
phy = phydoc.getElementsByTagName("phy").pop()
|
phy = phydoc.getElementsByTagName("phy").pop()
|
||||||
phy.setAttribute("name", cls._xmlname)
|
phy.setAttribute("name", cls._xmlname)
|
||||||
if e.version < e.EMANE091:
|
if emane.VERSION < emane.EMANE091:
|
||||||
phy.setAttribute("library", cls._xmllibrary)
|
phy.setAttribute("library", cls._xmllibrary)
|
||||||
# EMANE 0.7.4 suppport - to be removed when 0.7.4 support is deprecated
|
# EMANE 0.7.4 suppport - to be removed when 0.7.4 support is deprecated
|
||||||
if e.version == e.EMANE074:
|
if emane.VERSION == emane.EMANE074:
|
||||||
names = mac.getnames()
|
names = mac.getnames()
|
||||||
values = list(values)
|
values = list(values)
|
||||||
phynames = list(phynames)
|
phynames = list(phynames)
|
||||||
|
|
@ -128,7 +125,7 @@ class EmaneUniversalModel(EmaneModel):
|
||||||
phy.appendChild(e.xmlparam(phydoc, old[0], old[2]))
|
phy.appendChild(e.xmlparam(phydoc, old[0], old[2]))
|
||||||
|
|
||||||
frequencies = None
|
frequencies = None
|
||||||
if e.version >= e.EMANE091:
|
if emane.VERSION >= emane.EMANE091:
|
||||||
name = "frequencyofinterest"
|
name = "frequencyofinterest"
|
||||||
value = mac.valueof(name, values)
|
value = mac.valueof(name, values)
|
||||||
frequencies = cls.valuestrtoparamlist(phydoc, name, value)
|
frequencies = cls.valuestrtoparamlist(phydoc, name, value)
|
||||||
|
|
@ -137,10 +134,7 @@ class EmaneUniversalModel(EmaneModel):
|
||||||
phynames.remove("frequencyofinterest")
|
phynames.remove("frequencyofinterest")
|
||||||
|
|
||||||
# append all PHY options to phydoc
|
# append all PHY options to phydoc
|
||||||
map( lambda n: phy.appendChild(e.xmlparam(phydoc, n, \
|
map(lambda n: phy.appendChild(e.xmlparam(phydoc, n, mac.valueof(n, values))), phynames)
|
||||||
mac.valueof(n, values))), phynames)
|
|
||||||
if frequencies:
|
if frequencies:
|
||||||
phy.appendChild(frequencies)
|
phy.appendChild(frequencies)
|
||||||
return phydoc
|
return phydoc
|
||||||
|
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,42 +1,50 @@
|
||||||
#
|
"""
|
||||||
# CORE
|
|
||||||
# Copyright (c)2010-2013 the Boeing Company.
|
|
||||||
# See the LICENSE file included in this distribution.
|
|
||||||
#
|
|
||||||
# author: Jeff Ahrenholz <jeffrey.m.ahrenholz@boeing.com>
|
|
||||||
#
|
|
||||||
'''
|
|
||||||
location.py: definition of CoreLocation class that is a member of the
|
location.py: definition of CoreLocation class that is a member of the
|
||||||
Session object. Provides conversions between Cartesian and geographic coordinate
|
Session object. Provides conversions between Cartesian and geographic coordinate
|
||||||
systems. Depends on utm contributed module, from
|
systems. Depends on utm contributed module, from
|
||||||
https://pypi.python.org/pypi/utm (version 0.3.0).
|
https://pypi.python.org/pypi/utm (version 0.3.0).
|
||||||
'''
|
"""
|
||||||
|
|
||||||
from core.conf import ConfigurableManager
|
from core.conf import ConfigurableManager
|
||||||
from core.api import coreapi
|
from core.enumerations import RegisterTlvs
|
||||||
|
from core.misc import log
|
||||||
from core.misc import utm
|
from core.misc import utm
|
||||||
|
|
||||||
|
logger = log.get_logger(__name__)
|
||||||
|
|
||||||
|
|
||||||
class CoreLocation(ConfigurableManager):
|
class CoreLocation(ConfigurableManager):
|
||||||
''' Member of session class for handling global location data. This keeps
|
"""
|
||||||
|
Member of session class for handling global location data. This keeps
|
||||||
track of a latitude/longitude/altitude reference point and scale in
|
track of a latitude/longitude/altitude reference point and scale in
|
||||||
order to convert between X,Y and geo coordinates.
|
order to convert between X,Y and geo coordinates.
|
||||||
|
|
||||||
TODO: this could be updated to use more generic
|
TODO: this could be updated to use more generic
|
||||||
Configurable/ConfigurableManager code like other Session objects
|
Configurable/ConfigurableManager code like other Session objects
|
||||||
'''
|
"""
|
||||||
_name = "location"
|
name = "location"
|
||||||
_type = coreapi.CORE_TLV_REG_UTILITY
|
config_type = RegisterTlvs.UTILITY.value
|
||||||
|
|
||||||
def __init__(self, session):
|
def __init__(self):
|
||||||
ConfigurableManager.__init__(self, session)
|
"""
|
||||||
|
Creates a MobilityManager instance.
|
||||||
|
|
||||||
|
:return: nothing
|
||||||
|
"""
|
||||||
|
ConfigurableManager.__init__(self)
|
||||||
self.reset()
|
self.reset()
|
||||||
self.zonemap = {}
|
self.zonemap = {}
|
||||||
|
self.refxyz = (0.0, 0.0, 0.0)
|
||||||
|
self.refscale = 1.0
|
||||||
|
self.zoneshifts = {}
|
||||||
|
self.refgeo = (0.0, 0.0, 0.0)
|
||||||
for n, l in utm.ZONE_LETTERS:
|
for n, l in utm.ZONE_LETTERS:
|
||||||
self.zonemap[l] = n
|
self.zonemap[l] = n
|
||||||
|
|
||||||
def reset(self):
|
def reset(self):
|
||||||
''' Reset to initial state.
|
"""
|
||||||
'''
|
Reset to initial state.
|
||||||
|
"""
|
||||||
# (x, y, z) coordinates of the point given by self.refgeo
|
# (x, y, z) coordinates of the point given by self.refgeo
|
||||||
self.refxyz = (0.0, 0.0, 0.0)
|
self.refxyz = (0.0, 0.0, 0.0)
|
||||||
# decimal latitude, longitude, and altitude at the point (x, y, z)
|
# decimal latitude, longitude, and altitude at the point (x, y, z)
|
||||||
|
|
@ -46,70 +54,97 @@ class CoreLocation(ConfigurableManager):
|
||||||
# cached distance to refpt in other zones
|
# cached distance to refpt in other zones
|
||||||
self.zoneshifts = {}
|
self.zoneshifts = {}
|
||||||
|
|
||||||
def configure_values(self, msg, values):
|
def configure_values(self, config_data):
|
||||||
''' Receive configuration message for setting the reference point
|
"""
|
||||||
|
Receive configuration message for setting the reference point
|
||||||
and scale.
|
and scale.
|
||||||
'''
|
|
||||||
|
:param core.conf.ConfigData config_data: configuration data for carrying out a configuration
|
||||||
|
:return: nothing
|
||||||
|
"""
|
||||||
|
values = config_data.data_values
|
||||||
|
|
||||||
if values is None:
|
if values is None:
|
||||||
self.session.info("location data missing")
|
logger.info("location data missing")
|
||||||
return None
|
return None
|
||||||
values = values.split('|')
|
values = values.split('|')
|
||||||
|
|
||||||
# Cartesian coordinate reference point
|
# Cartesian coordinate reference point
|
||||||
refx, refy = map(lambda x: float(x), values[0:2])
|
refx, refy = map(lambda x: float(x), values[0:2])
|
||||||
refz = 0.0
|
refz = 0.0
|
||||||
self.refxyz = (refx, refy, refz)
|
self.refxyz = (refx, refy, refz)
|
||||||
# Geographic reference point
|
# Geographic reference point
|
||||||
lat,long,alt = map(lambda x: float(x), values[2:5])
|
lat, lon, alt = map(lambda x: float(x), values[2:5])
|
||||||
self.setrefgeo(lat, long, alt)
|
self.setrefgeo(lat, lon, alt)
|
||||||
self.refscale = float(values[5])
|
self.refscale = float(values[5])
|
||||||
self.session.info("location configured: (%.2f,%.2f,%.2f) = "
|
logger.info("location configured: (%.2f,%.2f,%.2f) = (%.5f,%.5f,%.5f) scale=%.2f" %
|
||||||
"(%.5f,%.5f,%.5f) scale=%.2f" %
|
|
||||||
(self.refxyz[0], self.refxyz[1], self.refxyz[2], self.refgeo[0],
|
(self.refxyz[0], self.refxyz[1], self.refxyz[2], self.refgeo[0],
|
||||||
self.refgeo[1], self.refgeo[2], self.refscale))
|
self.refgeo[1], self.refgeo[2], self.refscale))
|
||||||
self.session.info("location configured: UTM(%.5f,%.5f,%.5f)" %
|
logger.info("location configured: UTM(%.5f,%.5f,%.5f)" %
|
||||||
(self.refutm[1], self.refutm[2], self.refutm[3]))
|
(self.refutm[1], self.refutm[2], self.refutm[3]))
|
||||||
|
|
||||||
def px2m(self, val):
|
def px2m(self, val):
|
||||||
''' Convert the specified value in pixels to meters using the
|
"""
|
||||||
|
Convert the specified value in pixels to meters using the
|
||||||
configured scale. The scale is given as s, where
|
configured scale. The scale is given as s, where
|
||||||
100 pixels = s meters.
|
100 pixels = s meters.
|
||||||
'''
|
|
||||||
|
:param val: value to use in converting to meters
|
||||||
|
:return: value converted to meters
|
||||||
|
"""
|
||||||
return (val / 100.0) * self.refscale
|
return (val / 100.0) * self.refscale
|
||||||
|
|
||||||
def m2px(self, val):
|
def m2px(self, val):
|
||||||
''' Convert the specified value in meters to pixels using the
|
"""
|
||||||
|
Convert the specified value in meters to pixels using the
|
||||||
configured scale. The scale is given as s, where
|
configured scale. The scale is given as s, where
|
||||||
100 pixels = s meters.
|
100 pixels = s meters.
|
||||||
'''
|
|
||||||
|
:param val: value to convert to pixels
|
||||||
|
:return: value converted to pixels
|
||||||
|
"""
|
||||||
if self.refscale == 0.0:
|
if self.refscale == 0.0:
|
||||||
return 0.0
|
return 0.0
|
||||||
return 100.0 * (val / self.refscale)
|
return 100.0 * (val / self.refscale)
|
||||||
|
|
||||||
def setrefgeo(self, lat, lon, alt):
|
def setrefgeo(self, lat, lon, alt):
|
||||||
''' Record the geographical reference point decimal (lat, lon, alt)
|
"""
|
||||||
|
Record the geographical reference point decimal (lat, lon, alt)
|
||||||
and convert and store its UTM equivalent for later use.
|
and convert and store its UTM equivalent for later use.
|
||||||
'''
|
|
||||||
|
:param lat: latitude
|
||||||
|
:param lon: longitude
|
||||||
|
:param alt: altitude
|
||||||
|
:return: nothing
|
||||||
|
"""
|
||||||
self.refgeo = (lat, lon, alt)
|
self.refgeo = (lat, lon, alt)
|
||||||
# easting, northing, zone
|
# easting, northing, zone
|
||||||
(e, n, zonen, zonel) = utm.from_latlon(lat, lon)
|
e, n, zonen, zonel = utm.from_latlon(lat, lon)
|
||||||
self.refutm = ((zonen, zonel), e, n, alt)
|
self.refutm = ((zonen, zonel), e, n, alt)
|
||||||
|
|
||||||
def getgeo(self, x, y, z):
|
def getgeo(self, x, y, z):
|
||||||
''' Given (x, y, z) Cartesian coordinates, convert them to latitude,
|
"""
|
||||||
|
Given (x, y, z) Cartesian coordinates, convert them to latitude,
|
||||||
longitude, and altitude based on the configured reference point
|
longitude, and altitude based on the configured reference point
|
||||||
and scale.
|
and scale.
|
||||||
'''
|
|
||||||
|
:param x: x value
|
||||||
|
:param y: y value
|
||||||
|
:param z: z value
|
||||||
|
:return: lat, lon, alt values for provided coordinates
|
||||||
|
:rtype: tuple
|
||||||
|
"""
|
||||||
# shift (x,y,z) over to reference point (x,y,z)
|
# shift (x,y,z) over to reference point (x,y,z)
|
||||||
x = x - self.refxyz[0]
|
x -= self.refxyz[0]
|
||||||
y = -(y - self.refxyz[1])
|
y = -(y - self.refxyz[1])
|
||||||
if z is None:
|
if z is None:
|
||||||
z = self.refxyz[2]
|
z = self.refxyz[2]
|
||||||
else:
|
else:
|
||||||
z = z - self.refxyz[2]
|
z -= self.refxyz[2]
|
||||||
# use UTM coordinates since unit is meters
|
# use UTM coordinates since unit is meters
|
||||||
zone = self.refutm[0]
|
zone = self.refutm[0]
|
||||||
if zone == "":
|
if zone == "":
|
||||||
raise ValueError, "reference point not configured"
|
raise ValueError("reference point not configured")
|
||||||
e = self.refutm[1] + self.px2m(x)
|
e = self.refutm[1] + self.px2m(x)
|
||||||
n = self.refutm[2] + self.px2m(y)
|
n = self.refutm[2] + self.px2m(y)
|
||||||
alt = self.refutm[3] + self.px2m(z)
|
alt = self.refutm[3] + self.px2m(z)
|
||||||
|
|
@ -117,23 +152,29 @@ class CoreLocation(ConfigurableManager):
|
||||||
try:
|
try:
|
||||||
lat, lon = utm.to_latlon(e, n, zone[0], zone[1])
|
lat, lon = utm.to_latlon(e, n, zone[0], zone[1])
|
||||||
except utm.OutOfRangeError:
|
except utm.OutOfRangeError:
|
||||||
self.info("UTM out of range error for e=%s n=%s zone=%s" \
|
logger.exception("UTM out of range error for n=%s zone=%s xyz=(%s,%s,%s)", n, zone, x, y, z)
|
||||||
"xyz=(%s,%s,%s)" % (e, n, zone, x, y, z))
|
lat, lon = self.refgeo[:2]
|
||||||
(lat, lon) = self.refgeo[:2]
|
|
||||||
# self.info("getgeo(%s,%s,%s) e=%s n=%s zone=%s lat,lon,alt=" \
|
# self.info("getgeo(%s,%s,%s) e=%s n=%s zone=%s lat,lon,alt=" \
|
||||||
# "%.3f,%.3f,%.3f" % (x, y, z, e, n, zone, lat, lon, alt))
|
# "%.3f,%.3f,%.3f" % (x, y, z, e, n, zone, lat, lon, alt))
|
||||||
return (lat, lon, alt)
|
return lat, lon, alt
|
||||||
|
|
||||||
def getxyz(self, lat, lon, alt):
|
def getxyz(self, lat, lon, alt):
|
||||||
''' Given latitude, longitude, and altitude location data, convert them
|
"""
|
||||||
|
Given latitude, longitude, and altitude location data, convert them
|
||||||
to (x, y, z) Cartesian coordinates based on the configured
|
to (x, y, z) Cartesian coordinates based on the configured
|
||||||
reference point and scale. Lat/lon is converted to UTM meter
|
reference point and scale. Lat/lon is converted to UTM meter
|
||||||
coordinates, UTM zones are accounted for, and the scale turns
|
coordinates, UTM zones are accounted for, and the scale turns
|
||||||
meters to pixels.
|
meters to pixels.
|
||||||
'''
|
|
||||||
|
:param lat: latitude
|
||||||
|
:param lon: longitude
|
||||||
|
:param alt: altitude
|
||||||
|
:return: converted x, y, z coordinates
|
||||||
|
:rtype: tuple
|
||||||
|
"""
|
||||||
# convert lat/lon to UTM coordinates in meters
|
# convert lat/lon to UTM coordinates in meters
|
||||||
(e, n, zonen, zonel) = utm.from_latlon(lat, lon)
|
e, n, zonen, zonel = utm.from_latlon(lat, lon)
|
||||||
(rlat, rlon, ralt) = self.refgeo
|
rlat, rlon, ralt = self.refgeo
|
||||||
xshift = self.geteastingshift(zonen, zonel)
|
xshift = self.geteastingshift(zonen, zonel)
|
||||||
if xshift is None:
|
if xshift is None:
|
||||||
xm = e - self.refutm[1]
|
xm = e - self.refutm[1]
|
||||||
|
|
@ -150,26 +191,35 @@ class CoreLocation(ConfigurableManager):
|
||||||
x = self.m2px(xm) + self.refxyz[0]
|
x = self.m2px(xm) + self.refxyz[0]
|
||||||
y = -(self.m2px(ym) + self.refxyz[1])
|
y = -(self.m2px(ym) + self.refxyz[1])
|
||||||
z = self.m2px(zm) + self.refxyz[2]
|
z = self.m2px(zm) + self.refxyz[2]
|
||||||
return (x, y, z)
|
return x, y, z
|
||||||
|
|
||||||
def geteastingshift(self, zonen, zonel):
|
def geteastingshift(self, zonen, zonel):
|
||||||
''' If the lat, lon coordinates being converted are located in a
|
"""
|
||||||
|
If the lat, lon coordinates being converted are located in a
|
||||||
different UTM zone than the canvas reference point, the UTM meters
|
different UTM zone than the canvas reference point, the UTM meters
|
||||||
may need to be shifted.
|
may need to be shifted.
|
||||||
This picks a reference point in the same longitudinal band
|
This picks a reference point in the same longitudinal band
|
||||||
(UTM zone number) as the provided zone, to calculate the shift in
|
(UTM zone number) as the provided zone, to calculate the shift in
|
||||||
meters for the x coordinate.
|
meters for the x coordinate.
|
||||||
'''
|
|
||||||
rzonen = int(self.refutm[0][0])
|
|
||||||
if zonen == rzonen:
|
|
||||||
return None # same zone number, no x shift required
|
|
||||||
z = (zonen, zonel)
|
|
||||||
if z in self.zoneshifts and self.zoneshifts[z][0] is not None:
|
|
||||||
return self.zoneshifts[z][0] # x shift already calculated, cached
|
|
||||||
|
|
||||||
(rlat, rlon, ralt) = self.refgeo
|
:param zonen: zonen
|
||||||
lon2 = rlon + 6*(zonen - rzonen) # ea. zone is 6deg band
|
:param zonel: zone1
|
||||||
(e2, n2, zonen2, zonel2) = utm.from_latlon(rlat, lon2) # ignore northing
|
:return: the x shift value
|
||||||
|
"""
|
||||||
|
rzonen = int(self.refutm[0][0])
|
||||||
|
# same zone number, no x shift required
|
||||||
|
if zonen == rzonen:
|
||||||
|
return None
|
||||||
|
z = (zonen, zonel)
|
||||||
|
# x shift already calculated, cached
|
||||||
|
if z in self.zoneshifts and self.zoneshifts[z][0] is not None:
|
||||||
|
return self.zoneshifts[z][0]
|
||||||
|
|
||||||
|
rlat, rlon, ralt = self.refgeo
|
||||||
|
# ea. zone is 6deg band
|
||||||
|
lon2 = rlon + 6 * (zonen - rzonen)
|
||||||
|
# ignore northing
|
||||||
|
e2, n2, zonen2, zonel2 = utm.from_latlon(rlat, lon2)
|
||||||
# NOTE: great circle distance used here, not reference ellipsoid!
|
# NOTE: great circle distance used here, not reference ellipsoid!
|
||||||
xshift = utm.haversine(rlon, rlat, lon2, rlat) - e2
|
xshift = utm.haversine(rlon, rlat, lon2, rlat) - e2
|
||||||
# cache the return value
|
# cache the return value
|
||||||
|
|
@ -180,25 +230,33 @@ class CoreLocation(ConfigurableManager):
|
||||||
return xshift
|
return xshift
|
||||||
|
|
||||||
def getnorthingshift(self, zonen, zonel):
|
def getnorthingshift(self, zonen, zonel):
|
||||||
''' If the lat, lon coordinates being converted are located in a
|
"""
|
||||||
|
If the lat, lon coordinates being converted are located in a
|
||||||
different UTM zone than the canvas reference point, the UTM meters
|
different UTM zone than the canvas reference point, the UTM meters
|
||||||
may need to be shifted.
|
may need to be shifted.
|
||||||
This picks a reference point in the same latitude band (UTM zone letter)
|
This picks a reference point in the same latitude band (UTM zone letter)
|
||||||
as the provided zone, to calculate the shift in meters for the
|
as the provided zone, to calculate the shift in meters for the
|
||||||
y coordinate.
|
y coordinate.
|
||||||
'''
|
|
||||||
rzonel = self.refutm[0][1]
|
|
||||||
if zonel == rzonel:
|
|
||||||
return None # same zone letter, no y shift required
|
|
||||||
z = (zonen, zonel)
|
|
||||||
if z in self.zoneshifts and self.zoneshifts[z][1] is not None:
|
|
||||||
return self.zoneshifts[z][1] # y shift already calculated, cached
|
|
||||||
|
|
||||||
(rlat, rlon, ralt) = self.refgeo
|
:param zonen: zonen
|
||||||
|
:param zonel: zone1
|
||||||
|
:return: calculated y shift
|
||||||
|
"""
|
||||||
|
rzonel = self.refutm[0][1]
|
||||||
|
# same zone letter, no y shift required
|
||||||
|
if zonel == rzonel:
|
||||||
|
return None
|
||||||
|
z = (zonen, zonel)
|
||||||
|
# y shift already calculated, cached
|
||||||
|
if z in self.zoneshifts and self.zoneshifts[z][1] is not None:
|
||||||
|
return self.zoneshifts[z][1]
|
||||||
|
|
||||||
|
rlat, rlon, ralt = self.refgeo
|
||||||
# zonemap is used to calculate degrees difference between zone letters
|
# zonemap is used to calculate degrees difference between zone letters
|
||||||
latshift = self.zonemap[zonel] - self.zonemap[rzonel]
|
latshift = self.zonemap[zonel] - self.zonemap[rzonel]
|
||||||
lat2 = rlat + latshift # ea. latitude band is 8deg high
|
# ea. latitude band is 8deg high
|
||||||
(e2, n2, zonen2, zonel2) = utm.from_latlon(lat2, rlon)
|
lat2 = rlat + latshift
|
||||||
|
e2, n2, zonen2, zonel2 = utm.from_latlon(lat2, rlon)
|
||||||
# NOTE: great circle distance used here, not reference ellipsoid
|
# NOTE: great circle distance used here, not reference ellipsoid
|
||||||
yshift = -(utm.haversine(rlon, rlat, rlon, lat2) + n2)
|
yshift = -(utm.haversine(rlon, rlat, rlon, lat2) + n2)
|
||||||
# cache the return value
|
# cache the return value
|
||||||
|
|
@ -209,18 +267,24 @@ class CoreLocation(ConfigurableManager):
|
||||||
return yshift
|
return yshift
|
||||||
|
|
||||||
def getutmzoneshift(self, e, n):
|
def getutmzoneshift(self, e, n):
|
||||||
''' Given UTM easting and northing values, check if they fall outside
|
"""
|
||||||
|
Given UTM easting and northing values, check if they fall outside
|
||||||
the reference point's zone boundary. Return the UTM coordinates in a
|
the reference point's zone boundary. Return the UTM coordinates in a
|
||||||
different zone and the new zone if they do. Zone lettering is only
|
different zone and the new zone if they do. Zone lettering is only
|
||||||
changed when the reference point is in the opposite hemisphere.
|
changed when the reference point is in the opposite hemisphere.
|
||||||
'''
|
|
||||||
|
:param e: easting value
|
||||||
|
:param n: northing value
|
||||||
|
:return: modified easting, northing, and zone values
|
||||||
|
:rtype: tuple
|
||||||
|
"""
|
||||||
zone = self.refutm[0]
|
zone = self.refutm[0]
|
||||||
(rlat, rlon, ralt) = self.refgeo
|
rlat, rlon, ralt = self.refgeo
|
||||||
if e > 834000 or e < 166000:
|
if e > 834000 or e < 166000:
|
||||||
num_zones = (int(e) - 166000) / (utm.R / 10)
|
num_zones = (int(e) - 166000) / (utm.R / 10)
|
||||||
# estimate number of zones to shift, E (positive) or W (negative)
|
# estimate number of zones to shift, E (positive) or W (negative)
|
||||||
rlon2 = self.refgeo[1] + (num_zones * 6)
|
rlon2 = self.refgeo[1] + (num_zones * 6)
|
||||||
(e2, n2, zonen2, zonel2) = utm.from_latlon(rlat, rlon2)
|
e2, n2, zonen2, zonel2 = utm.from_latlon(rlat, rlon2)
|
||||||
xshift = utm.haversine(rlon, rlat, rlon2, rlat)
|
xshift = utm.haversine(rlon, rlat, rlon2, rlat)
|
||||||
# after >3 zones away from refpt, the above estimate won't work
|
# after >3 zones away from refpt, the above estimate won't work
|
||||||
# (the above estimate could be improved)
|
# (the above estimate could be improved)
|
||||||
|
|
@ -228,7 +292,7 @@ class CoreLocation(ConfigurableManager):
|
||||||
# move one more zone away
|
# move one more zone away
|
||||||
num_zones = (abs(num_zones) + 1) * (abs(num_zones) / num_zones)
|
num_zones = (abs(num_zones) + 1) * (abs(num_zones) / num_zones)
|
||||||
rlon2 = self.refgeo[1] + (num_zones * 6)
|
rlon2 = self.refgeo[1] + (num_zones * 6)
|
||||||
(e2, n2, zonen2, zonel2) = utm.from_latlon(rlat, rlon2)
|
e2, n2, zonen2, zonel2 = utm.from_latlon(rlat, rlon2)
|
||||||
xshift = utm.haversine(rlon, rlat, rlon2, rlat)
|
xshift = utm.haversine(rlon, rlat, rlon2, rlat)
|
||||||
e = e - xshift
|
e = e - xshift
|
||||||
zone = (zonen2, zonel2)
|
zone = (zonen2, zonel2)
|
||||||
|
|
@ -240,7 +304,4 @@ class CoreLocation(ConfigurableManager):
|
||||||
# refpt in southern hemisphere and we crossed north of equator
|
# refpt in southern hemisphere and we crossed north of equator
|
||||||
n -= 10000000
|
n -= 10000000
|
||||||
zone = (zone[0], 'N')
|
zone = (zone[0], 'N')
|
||||||
return (e, n, zone)
|
return e, n, zone
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -44,6 +44,7 @@ _ellipsoid = [
|
||||||
[23, "WGS-84", 6378137, 0.00669438]
|
[23, "WGS-84", 6378137, 0.00669438]
|
||||||
]
|
]
|
||||||
|
|
||||||
|
|
||||||
# Reference ellipsoids derived from Peter H. Dana's website-
|
# Reference ellipsoids derived from Peter H. Dana's website-
|
||||||
# http://www.utexas.edu/depts/grg/gcraft/notes/datum/elist.html
|
# http://www.utexas.edu/depts/grg/gcraft/notes/datum/elist.html
|
||||||
# Department of Geography, University of Texas at Austin
|
# Department of Geography, University of Texas at Austin
|
||||||
|
|
@ -84,10 +85,14 @@ def LLtoUTM(ReferenceEllipsoid, Lat, Long, zone = None):
|
||||||
|
|
||||||
# Special zones for Svalbard
|
# Special zones for Svalbard
|
||||||
if Lat >= 72.0 and Lat < 84.0:
|
if Lat >= 72.0 and Lat < 84.0:
|
||||||
if LongTemp >= 0.0 and LongTemp < 9.0:ZoneNumber = 31
|
if LongTemp >= 0.0 and LongTemp < 9.0:
|
||||||
elif LongTemp >= 9.0 and LongTemp < 21.0: ZoneNumber = 33
|
ZoneNumber = 31
|
||||||
elif LongTemp >= 21.0 and LongTemp < 33.0: ZoneNumber = 35
|
elif LongTemp >= 9.0 and LongTemp < 21.0:
|
||||||
elif LongTemp >= 33.0 and LongTemp < 42.0: ZoneNumber = 37
|
ZoneNumber = 33
|
||||||
|
elif LongTemp >= 21.0 and LongTemp < 33.0:
|
||||||
|
ZoneNumber = 35
|
||||||
|
elif LongTemp >= 33.0 and LongTemp < 42.0:
|
||||||
|
ZoneNumber = 37
|
||||||
|
|
||||||
LongOrigin = (ZoneNumber - 1) * 6 - 180 + 3 # +3 puts origin in middle of zone
|
LongOrigin = (ZoneNumber - 1) * 6 - 180 + 3 # +3 puts origin in middle of zone
|
||||||
LongOriginRad = LongOrigin * _deg2rad
|
LongOriginRad = LongOrigin * _deg2rad
|
||||||
|
|
@ -132,27 +137,49 @@ def _UTMLetterDesignator(Lat):
|
||||||
latitude returns 'Z' if latitude is outside the UTM limits of 84N to 80S
|
latitude returns 'Z' if latitude is outside the UTM limits of 84N to 80S
|
||||||
Written by Chuck Gantz- chuck.gantz@globalstar.com"""
|
Written by Chuck Gantz- chuck.gantz@globalstar.com"""
|
||||||
|
|
||||||
if 84 >= Lat >= 72: return 'X'
|
if 84 >= Lat >= 72:
|
||||||
elif 72 > Lat >= 64: return 'W'
|
return 'X'
|
||||||
elif 64 > Lat >= 56: return 'V'
|
elif 72 > Lat >= 64:
|
||||||
elif 56 > Lat >= 48: return 'U'
|
return 'W'
|
||||||
elif 48 > Lat >= 40: return 'T'
|
elif 64 > Lat >= 56:
|
||||||
elif 40 > Lat >= 32: return 'S'
|
return 'V'
|
||||||
elif 32 > Lat >= 24: return 'R'
|
elif 56 > Lat >= 48:
|
||||||
elif 24 > Lat >= 16: return 'Q'
|
return 'U'
|
||||||
elif 16 > Lat >= 8: return 'P'
|
elif 48 > Lat >= 40:
|
||||||
elif 8 > Lat >= 0: return 'N'
|
return 'T'
|
||||||
elif 0 > Lat >= -8: return 'M'
|
elif 40 > Lat >= 32:
|
||||||
elif -8> Lat >= -16: return 'L'
|
return 'S'
|
||||||
elif -16 > Lat >= -24: return 'K'
|
elif 32 > Lat >= 24:
|
||||||
elif -24 > Lat >= -32: return 'J'
|
return 'R'
|
||||||
elif -32 > Lat >= -40: return 'H'
|
elif 24 > Lat >= 16:
|
||||||
elif -40 > Lat >= -48: return 'G'
|
return 'Q'
|
||||||
elif -48 > Lat >= -56: return 'F'
|
elif 16 > Lat >= 8:
|
||||||
elif -56 > Lat >= -64: return 'E'
|
return 'P'
|
||||||
elif -64 > Lat >= -72: return 'D'
|
elif 8 > Lat >= 0:
|
||||||
elif -72 > Lat >= -80: return 'C'
|
return 'N'
|
||||||
else: return 'Z' # if the Latitude is outside the UTM limits
|
elif 0 > Lat >= -8:
|
||||||
|
return 'M'
|
||||||
|
elif -8 > Lat >= -16:
|
||||||
|
return 'L'
|
||||||
|
elif -16 > Lat >= -24:
|
||||||
|
return 'K'
|
||||||
|
elif -24 > Lat >= -32:
|
||||||
|
return 'J'
|
||||||
|
elif -32 > Lat >= -40:
|
||||||
|
return 'H'
|
||||||
|
elif -40 > Lat >= -48:
|
||||||
|
return 'G'
|
||||||
|
elif -48 > Lat >= -56:
|
||||||
|
return 'F'
|
||||||
|
elif -56 > Lat >= -64:
|
||||||
|
return 'E'
|
||||||
|
elif -64 > Lat >= -72:
|
||||||
|
return 'D'
|
||||||
|
elif -72 > Lat >= -80:
|
||||||
|
return 'C'
|
||||||
|
else:
|
||||||
|
return 'Z' # if the Latitude is outside the UTM limits
|
||||||
|
|
||||||
|
|
||||||
# void UTMtoLL(int ReferenceEllipsoid, const double UTMNorthing, const double UTMEasting, const char* UTMZone,
|
# void UTMtoLL(int ReferenceEllipsoid, const double UTMNorthing, const double UTMEasting, const char* UTMZone,
|
||||||
# double& Lat, double& Long )
|
# double& Lat, double& Long )
|
||||||
|
|
@ -187,7 +214,8 @@ Converted to Python by Russ Nelson <nelson@crynwr.com>"""
|
||||||
eccPrimeSquared = (eccSquared) / (1 - eccSquared)
|
eccPrimeSquared = (eccSquared) / (1 - eccSquared)
|
||||||
|
|
||||||
M = y / k0
|
M = y / k0
|
||||||
mu = M/(a*(1-eccSquared/4-3*eccSquared*eccSquared/64-5*eccSquared*eccSquared*eccSquared/256))
|
mu = M / (
|
||||||
|
a * (1 - eccSquared / 4 - 3 * eccSquared * eccSquared / 64 - 5 * eccSquared * eccSquared * eccSquared / 256))
|
||||||
|
|
||||||
phi1Rad = (mu + (3 * e1 / 2 - 27 * e1 * e1 * e1 / 32) * sin(2 * mu)
|
phi1Rad = (mu + (3 * e1 / 2 - 27 * e1 * e1 * e1 / 32) * sin(2 * mu)
|
||||||
+ (21 * e1 * e1 / 16 - 55 * e1 * e1 * e1 * e1 / 32) * sin(4 * mu)
|
+ (21 * e1 * e1 / 16 - 55 * e1 * e1 * e1 * e1 / 32) * sin(4 * mu)
|
||||||
|
|
@ -200,17 +228,19 @@ Converted to Python by Russ Nelson <nelson@crynwr.com>"""
|
||||||
R1 = a * (1 - eccSquared) / pow(1 - eccSquared * sin(phi1Rad) * sin(phi1Rad), 1.5)
|
R1 = a * (1 - eccSquared) / pow(1 - eccSquared * sin(phi1Rad) * sin(phi1Rad), 1.5)
|
||||||
D = x / (N1 * k0)
|
D = x / (N1 * k0)
|
||||||
|
|
||||||
Lat = phi1Rad - (N1*tan(phi1Rad)/R1)*(D*D/2-(5+3*T1+10*C1-4*C1*C1-9*eccPrimeSquared)*D*D*D*D/24
|
Lat = phi1Rad - (N1 * tan(phi1Rad) / R1) * (
|
||||||
|
D * D / 2 - (5 + 3 * T1 + 10 * C1 - 4 * C1 * C1 - 9 * eccPrimeSquared) * D * D * D * D / 24
|
||||||
+ (61 + 90 * T1 + 298 * C1 + 45 * T1 * T1 - 252 * eccPrimeSquared - 3 * C1 * C1) * D * D * D * D * D * D / 720)
|
+ (61 + 90 * T1 + 298 * C1 + 45 * T1 * T1 - 252 * eccPrimeSquared - 3 * C1 * C1) * D * D * D * D * D * D / 720)
|
||||||
Lat = Lat * _rad2deg
|
Lat = Lat * _rad2deg
|
||||||
|
|
||||||
Long = (D-(1+2*T1+C1)*D*D*D/6+(5-2*C1+28*T1-3*C1*C1+8*eccPrimeSquared+24*T1*T1)
|
Long = (D - (1 + 2 * T1 + C1) * D * D * D / 6 + (
|
||||||
|
5 - 2 * C1 + 28 * T1 - 3 * C1 * C1 + 8 * eccPrimeSquared + 24 * T1 * T1)
|
||||||
* D * D * D * D * D / 120) / cos(phi1Rad)
|
* D * D * D * D * D / 120) / cos(phi1Rad)
|
||||||
Long = LongOrigin + Long * _rad2deg
|
Long = LongOrigin + Long * _rad2deg
|
||||||
return (Lat, Long)
|
return (Lat, Long)
|
||||||
|
|
||||||
|
|
||||||
if __name__ == '__main__':
|
if __name__ == '__main__':
|
||||||
(z, e, n) = LLtoUTM(23, 45.00, -75.00)
|
(z, e, n) = LLtoUTM(23, 45.00, -75.00)
|
||||||
print z, e, n
|
print z, e, n
|
||||||
print UTMtoLL(23, n, e, z)
|
print UTMtoLL(23, n, e, z)
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,39 +1,58 @@
|
||||||
#
|
"""
|
||||||
# CORE
|
|
||||||
# Copyright (c)2012 the Boeing Company.
|
|
||||||
# See the LICENSE file included in this distribution.
|
|
||||||
#
|
|
||||||
# authors: Tom Goff <thomas.goff@boeing.com>
|
|
||||||
#
|
|
||||||
'''
|
|
||||||
event.py: event loop implementation using a heap queue and threads.
|
event.py: event loop implementation using a heap queue and threads.
|
||||||
'''
|
"""
|
||||||
import time
|
|
||||||
import threading
|
import heapq
|
||||||
import heapq
|
import threading
|
||||||
|
import time
|
||||||
|
|
||||||
|
from core.misc import log
|
||||||
|
|
||||||
|
logger = log.get_logger(__name__)
|
||||||
|
|
||||||
class EventLoop(object):
|
|
||||||
|
|
||||||
class Timer(threading.Thread):
|
class Timer(threading.Thread):
|
||||||
'''\
|
"""
|
||||||
Based on threading.Timer but cancel() returns if the timer was
|
Based on threading.Timer but cancel() returns if the timer was
|
||||||
already running.
|
already running.
|
||||||
'''
|
"""
|
||||||
|
|
||||||
def __init__(self, interval, function, args=[], kwargs={}):
|
def __init__(self, interval, function, args=None, kwargs=None):
|
||||||
super(EventLoop.Timer, self).__init__()
|
"""
|
||||||
|
Create a Timer instance.
|
||||||
|
|
||||||
|
:param interval: time interval
|
||||||
|
:param function: function to call when timer finishes
|
||||||
|
:param args: function arguments
|
||||||
|
:param kwargs: function keyword arguments
|
||||||
|
"""
|
||||||
|
super(Timer, self).__init__()
|
||||||
self.interval = interval
|
self.interval = interval
|
||||||
self.function = function
|
self.function = function
|
||||||
self.args = args
|
|
||||||
self.kwargs = kwargs
|
|
||||||
self.finished = threading.Event()
|
self.finished = threading.Event()
|
||||||
self._running = threading.Lock()
|
self._running = threading.Lock()
|
||||||
|
|
||||||
|
# validate arguments were provided
|
||||||
|
if args:
|
||||||
|
self.args = args
|
||||||
|
else:
|
||||||
|
self.args = []
|
||||||
|
|
||||||
|
# validate keyword arguments were provided
|
||||||
|
if kwargs:
|
||||||
|
self.kwargs = kwargs
|
||||||
|
else:
|
||||||
|
self.kwargs = {}
|
||||||
|
|
||||||
def cancel(self):
|
def cancel(self):
|
||||||
'''\
|
"""
|
||||||
Stop the timer if it hasn't finished yet. Return False if
|
Stop the timer if it hasn't finished yet. Return False if
|
||||||
the timer was already running.
|
the timer was already running.
|
||||||
'''
|
|
||||||
|
:return: True if canceled, False otherwise
|
||||||
|
:rtype: bool
|
||||||
|
"""
|
||||||
locked = self._running.acquire(False)
|
locked = self._running.acquire(False)
|
||||||
if locked:
|
if locked:
|
||||||
self.finished.set()
|
self.finished.set()
|
||||||
|
|
@ -41,36 +60,82 @@ class EventLoop(object):
|
||||||
return locked
|
return locked
|
||||||
|
|
||||||
def run(self):
|
def run(self):
|
||||||
|
"""
|
||||||
|
Run the timer.
|
||||||
|
|
||||||
|
:return: nothing
|
||||||
|
"""
|
||||||
self.finished.wait(self.interval)
|
self.finished.wait(self.interval)
|
||||||
with self._running:
|
with self._running:
|
||||||
if not self.finished.is_set():
|
if not self.finished.is_set():
|
||||||
self.function(*self.args, **self.kwargs)
|
self.function(*self.args, **self.kwargs)
|
||||||
self.finished.set()
|
self.finished.set()
|
||||||
|
|
||||||
|
|
||||||
class Event(object):
|
class Event(object):
|
||||||
def __init__(self, eventnum, time, func, *args, **kwds):
|
"""
|
||||||
|
Provides event objects that can be used within the EventLoop class.
|
||||||
|
"""
|
||||||
|
|
||||||
|
def __init__(self, eventnum, event_time, func, *args, **kwds):
|
||||||
|
"""
|
||||||
|
Create an Event instance.
|
||||||
|
|
||||||
|
:param eventnum: event number
|
||||||
|
:param event_time: event time
|
||||||
|
:param func: event function
|
||||||
|
:param args: function arguments
|
||||||
|
:param kwds: function keyword arguments
|
||||||
|
"""
|
||||||
self.eventnum = eventnum
|
self.eventnum = eventnum
|
||||||
self.time = time
|
self.time = event_time
|
||||||
self.func = func
|
self.func = func
|
||||||
self.args = args
|
self.args = args
|
||||||
self.kwds = kwds
|
self.kwds = kwds
|
||||||
self.canceled = False
|
self.canceled = False
|
||||||
|
|
||||||
def __cmp__(self, other):
|
def __cmp__(self, other):
|
||||||
|
"""
|
||||||
|
Comparison function.
|
||||||
|
|
||||||
|
:param Event other: event to compare with
|
||||||
|
:return: comparison result
|
||||||
|
:rtype: int
|
||||||
|
"""
|
||||||
tmp = cmp(self.time, other.time)
|
tmp = cmp(self.time, other.time)
|
||||||
if tmp == 0:
|
if tmp == 0:
|
||||||
tmp = cmp(self.eventnum, other.eventnum)
|
tmp = cmp(self.eventnum, other.eventnum)
|
||||||
return tmp
|
return tmp
|
||||||
|
|
||||||
def run(self):
|
def run(self):
|
||||||
|
"""
|
||||||
|
Run an event.
|
||||||
|
|
||||||
|
:return: nothing
|
||||||
|
"""
|
||||||
if self.canceled:
|
if self.canceled:
|
||||||
return
|
return
|
||||||
self.func(*self.args, **self.kwds)
|
self.func(*self.args, **self.kwds)
|
||||||
|
|
||||||
def cancel(self):
|
def cancel(self):
|
||||||
self.canceled = True # XXX not thread-safe
|
"""
|
||||||
|
Cancel event.
|
||||||
|
|
||||||
|
:return: nothing
|
||||||
|
"""
|
||||||
|
# XXX not thread-safe
|
||||||
|
self.canceled = True
|
||||||
|
|
||||||
|
|
||||||
|
class EventLoop(object):
|
||||||
|
"""
|
||||||
|
Provides an event loop for running events.
|
||||||
|
"""
|
||||||
|
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
|
"""
|
||||||
|
Creates a EventLoop instance.
|
||||||
|
"""
|
||||||
self.lock = threading.RLock()
|
self.lock = threading.RLock()
|
||||||
self.queue = []
|
self.queue = []
|
||||||
self.eventnum = 0
|
self.eventnum = 0
|
||||||
|
|
@ -79,6 +144,11 @@ class EventLoop(object):
|
||||||
self.start = None
|
self.start = None
|
||||||
|
|
||||||
def __run_events(self):
|
def __run_events(self):
|
||||||
|
"""
|
||||||
|
Run events.
|
||||||
|
|
||||||
|
:return: nothing
|
||||||
|
"""
|
||||||
schedule = False
|
schedule = False
|
||||||
while True:
|
while True:
|
||||||
with self.lock:
|
with self.lock:
|
||||||
|
|
@ -91,23 +161,34 @@ class EventLoop(object):
|
||||||
event = heapq.heappop(self.queue)
|
event = heapq.heappop(self.queue)
|
||||||
assert event.time <= now
|
assert event.time <= now
|
||||||
event.run()
|
event.run()
|
||||||
|
|
||||||
with self.lock:
|
with self.lock:
|
||||||
self.timer = None
|
self.timer = None
|
||||||
if schedule:
|
if schedule:
|
||||||
self.__schedule_event()
|
self.__schedule_event()
|
||||||
|
|
||||||
def __schedule_event(self):
|
def __schedule_event(self):
|
||||||
|
"""
|
||||||
|
Schedule event.
|
||||||
|
|
||||||
|
:return: nothing
|
||||||
|
"""
|
||||||
with self.lock:
|
with self.lock:
|
||||||
assert self.running
|
assert self.running
|
||||||
if not self.queue:
|
if not self.queue:
|
||||||
return
|
return
|
||||||
delay = self.queue[0].time - time.time()
|
delay = self.queue[0].time - time.time()
|
||||||
assert self.timer is None
|
assert self.timer is None
|
||||||
self.timer = EventLoop.Timer(delay, self.__run_events)
|
self.timer = Timer(delay, self.__run_events)
|
||||||
self.timer.daemon = True
|
self.timer.daemon = True
|
||||||
self.timer.start()
|
self.timer.start()
|
||||||
|
|
||||||
def run(self):
|
def run(self):
|
||||||
|
"""
|
||||||
|
Start event loop.
|
||||||
|
|
||||||
|
:return: nothing
|
||||||
|
"""
|
||||||
with self.lock:
|
with self.lock:
|
||||||
if self.running:
|
if self.running:
|
||||||
return
|
return
|
||||||
|
|
@ -118,6 +199,11 @@ class EventLoop(object):
|
||||||
self.__schedule_event()
|
self.__schedule_event()
|
||||||
|
|
||||||
def stop(self):
|
def stop(self):
|
||||||
|
"""
|
||||||
|
Stop event loop.
|
||||||
|
|
||||||
|
:return: nothing
|
||||||
|
"""
|
||||||
with self.lock:
|
with self.lock:
|
||||||
if not self.running:
|
if not self.running:
|
||||||
return
|
return
|
||||||
|
|
@ -130,13 +216,23 @@ class EventLoop(object):
|
||||||
self.start = None
|
self.start = None
|
||||||
|
|
||||||
def add_event(self, delaysec, func, *args, **kwds):
|
def add_event(self, delaysec, func, *args, **kwds):
|
||||||
|
"""
|
||||||
|
Add an event to the event loop.
|
||||||
|
|
||||||
|
:param int delaysec: delay in seconds for event
|
||||||
|
:param func: event function
|
||||||
|
:param args: event arguments
|
||||||
|
:param kwds: event keyword arguments
|
||||||
|
:return: created event
|
||||||
|
:rtype: Event
|
||||||
|
"""
|
||||||
with self.lock:
|
with self.lock:
|
||||||
eventnum = self.eventnum
|
eventnum = self.eventnum
|
||||||
self.eventnum += 1
|
self.eventnum += 1
|
||||||
evtime = float(delaysec)
|
evtime = float(delaysec)
|
||||||
if self.running:
|
if self.running:
|
||||||
evtime += time.time()
|
evtime += time.time()
|
||||||
event = self.Event(eventnum, evtime, func, *args, **kwds)
|
event = Event(eventnum, evtime, func, *args, **kwds)
|
||||||
|
|
||||||
if self.queue:
|
if self.queue:
|
||||||
prevhead = self.queue[0]
|
prevhead = self.queue[0]
|
||||||
|
|
@ -152,12 +248,14 @@ class EventLoop(object):
|
||||||
self.__schedule_event()
|
self.__schedule_event()
|
||||||
return event
|
return event
|
||||||
|
|
||||||
|
|
||||||
|
# TODO: move example to documentation
|
||||||
def example():
|
def example():
|
||||||
loop = EventLoop()
|
loop = EventLoop()
|
||||||
|
|
||||||
def msg(arg):
|
def msg(arg):
|
||||||
delta = time.time() - loop.start
|
delta = time.time() - loop.start
|
||||||
print delta, 'arg:', arg
|
logger.debug("%s arg: %s", delta, arg)
|
||||||
|
|
||||||
def repeat(interval, count):
|
def repeat(interval, count):
|
||||||
count -= 1
|
count -= 1
|
||||||
|
|
|
||||||
|
|
@ -1,230 +0,0 @@
|
||||||
#
|
|
||||||
# CORE
|
|
||||||
# Copyright (c)2010-2012 the Boeing Company.
|
|
||||||
# See the LICENSE file included in this distribution.
|
|
||||||
#
|
|
||||||
# author: Tom Goff <thomas.goff@boeing.com>
|
|
||||||
#
|
|
||||||
'''
|
|
||||||
ipaddr.py: helper objects for dealing with IPv4/v6 addresses.
|
|
||||||
'''
|
|
||||||
|
|
||||||
import socket
|
|
||||||
import struct
|
|
||||||
import random
|
|
||||||
|
|
||||||
AF_INET = socket.AF_INET
|
|
||||||
AF_INET6 = socket.AF_INET6
|
|
||||||
|
|
||||||
class MacAddr(object):
|
|
||||||
def __init__(self, addr):
|
|
||||||
self.addr = addr
|
|
||||||
|
|
||||||
def __str__(self):
|
|
||||||
return ":".join(map(lambda x: ("%02x" % ord(x)), self.addr))
|
|
||||||
|
|
||||||
def tolinklocal(self):
|
|
||||||
''' Convert the MAC address to a IPv6 link-local address, using EUI 48
|
|
||||||
to EUI 64 conversion process per RFC 5342.
|
|
||||||
'''
|
|
||||||
if not self.addr:
|
|
||||||
return IPAddr.fromstring("::")
|
|
||||||
tmp = struct.unpack("!Q", '\x00\x00' + self.addr)[0]
|
|
||||||
nic = long(tmp) & 0x000000FFFFFFL
|
|
||||||
oui = long(tmp) & 0xFFFFFF000000L
|
|
||||||
# toggle U/L bit
|
|
||||||
oui ^= 0x020000000000L
|
|
||||||
# append EUI-48 octets
|
|
||||||
oui = (oui << 16) | 0xFFFE000000L
|
|
||||||
return IPAddr(AF_INET6, struct.pack("!QQ", 0xfe80 << 48, oui | nic))
|
|
||||||
|
|
||||||
@classmethod
|
|
||||||
def fromstring(cls, s):
|
|
||||||
addr = "".join(map(lambda x: chr(int(x, 16)), s.split(":")))
|
|
||||||
return cls(addr)
|
|
||||||
|
|
||||||
@classmethod
|
|
||||||
def random(cls):
|
|
||||||
tmp = random.randint(0, 0xFFFFFF)
|
|
||||||
tmp |= 0x00163E << 24 # use the Xen OID 00:16:3E
|
|
||||||
tmpbytes = struct.pack("!Q", tmp)
|
|
||||||
return cls(tmpbytes[2:])
|
|
||||||
|
|
||||||
class IPAddr(object):
|
|
||||||
def __init__(self, af, addr):
|
|
||||||
# check if (af, addr) is valid
|
|
||||||
if not socket.inet_ntop(af, addr):
|
|
||||||
raise ValueError, "invalid af/addr"
|
|
||||||
self.af = af
|
|
||||||
self.addr = addr
|
|
||||||
|
|
||||||
def isIPv4(self):
|
|
||||||
return self.af == AF_INET
|
|
||||||
|
|
||||||
def isIPv6(self):
|
|
||||||
return self.af == AF_INET6
|
|
||||||
|
|
||||||
def __str__(self):
|
|
||||||
return socket.inet_ntop(self.af, self.addr)
|
|
||||||
|
|
||||||
def __eq__(self, other):
|
|
||||||
try:
|
|
||||||
return other.af == self.af and other.addr == self.addr
|
|
||||||
except:
|
|
||||||
return False
|
|
||||||
|
|
||||||
def __add__(self, other):
|
|
||||||
try:
|
|
||||||
carry = int(other)
|
|
||||||
except:
|
|
||||||
return NotImplemented
|
|
||||||
tmp = map(lambda x: ord(x), self.addr)
|
|
||||||
for i in xrange(len(tmp) - 1, -1, -1):
|
|
||||||
x = tmp[i] + carry
|
|
||||||
tmp[i] = x & 0xff
|
|
||||||
carry = x >> 8
|
|
||||||
if carry == 0:
|
|
||||||
break
|
|
||||||
addr = "".join(map(lambda x: chr(x), tmp))
|
|
||||||
return self.__class__(self.af, addr)
|
|
||||||
|
|
||||||
def __sub__(self, other):
|
|
||||||
try:
|
|
||||||
tmp = -int(other)
|
|
||||||
except:
|
|
||||||
return NotImplemented
|
|
||||||
return self.__add__(tmp)
|
|
||||||
|
|
||||||
@classmethod
|
|
||||||
def fromstring(cls, s):
|
|
||||||
for af in AF_INET, AF_INET6:
|
|
||||||
try:
|
|
||||||
return cls(af, socket.inet_pton(af, s))
|
|
||||||
except Exception, e:
|
|
||||||
pass
|
|
||||||
raise e
|
|
||||||
|
|
||||||
@staticmethod
|
|
||||||
def toint(s):
|
|
||||||
''' convert IPv4 string to 32-bit integer
|
|
||||||
'''
|
|
||||||
bin = socket.inet_pton(AF_INET, s)
|
|
||||||
return(struct.unpack('!I', bin)[0])
|
|
||||||
|
|
||||||
class IPPrefix(object):
|
|
||||||
def __init__(self, af, prefixstr):
|
|
||||||
"prefixstr format: address/prefixlen"
|
|
||||||
tmp = prefixstr.split("/")
|
|
||||||
if len(tmp) > 2:
|
|
||||||
raise ValueError, "invalid prefix: '%s'" % prefixstr
|
|
||||||
self.af = af
|
|
||||||
if self.af == AF_INET:
|
|
||||||
self.addrlen = 32
|
|
||||||
elif self.af == AF_INET6:
|
|
||||||
self.addrlen = 128
|
|
||||||
else:
|
|
||||||
raise ValueError, "invalid address family: '%s'" % self.af
|
|
||||||
if len(tmp) == 2:
|
|
||||||
self.prefixlen = int(tmp[1])
|
|
||||||
else:
|
|
||||||
self.prefixlen = self.addrlen
|
|
||||||
self.prefix = socket.inet_pton(self.af, tmp[0])
|
|
||||||
if self.addrlen > self.prefixlen:
|
|
||||||
addrbits = self.addrlen - self.prefixlen
|
|
||||||
netmask = ((1L << self.prefixlen) - 1) << addrbits
|
|
||||||
prefix = ""
|
|
||||||
for i in xrange(-1, -(addrbits >> 3) - 2, -1):
|
|
||||||
prefix = chr(ord(self.prefix[i]) & (netmask & 0xff)) + prefix
|
|
||||||
netmask >>= 8
|
|
||||||
self.prefix = self.prefix[:i] + prefix
|
|
||||||
|
|
||||||
def __str__(self):
|
|
||||||
return "%s/%s" % (socket.inet_ntop(self.af, self.prefix),
|
|
||||||
self.prefixlen)
|
|
||||||
|
|
||||||
def __eq__(self, other):
|
|
||||||
try:
|
|
||||||
return other.af == self.af and \
|
|
||||||
other.prefixlen == self.prefixlen and \
|
|
||||||
other.prefix == self.prefix
|
|
||||||
except:
|
|
||||||
return False
|
|
||||||
|
|
||||||
def __add__(self, other):
|
|
||||||
try:
|
|
||||||
tmp = int(other)
|
|
||||||
except:
|
|
||||||
return NotImplemented
|
|
||||||
a = IPAddr(self.af, self.prefix) + \
|
|
||||||
(tmp << (self.addrlen - self.prefixlen))
|
|
||||||
prefixstr = "%s/%s" % (a, self.prefixlen)
|
|
||||||
if self.__class__ == IPPrefix:
|
|
||||||
return self.__class__(self.af, prefixstr)
|
|
||||||
else:
|
|
||||||
return self.__class__(prefixstr)
|
|
||||||
|
|
||||||
def __sub__(self, other):
|
|
||||||
try:
|
|
||||||
tmp = -int(other)
|
|
||||||
except:
|
|
||||||
return NotImplemented
|
|
||||||
return self.__add__(tmp)
|
|
||||||
|
|
||||||
def addr(self, hostid):
|
|
||||||
tmp = int(hostid)
|
|
||||||
if (tmp == 1 or tmp == 0 or tmp == -1) and self.addrlen == self.prefixlen:
|
|
||||||
return IPAddr(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):
|
|
||||||
raise ValueError, "invalid hostid for prefix %s: %s" % (self, hostid)
|
|
||||||
addr = ""
|
|
||||||
for i in xrange(-1, -(self.addrlen >> 3) - 1, -1):
|
|
||||||
addr = chr(ord(self.prefix[i]) | (tmp & 0xff)) + addr
|
|
||||||
tmp >>= 8
|
|
||||||
if not tmp:
|
|
||||||
break
|
|
||||||
addr = self.prefix[:i] + addr
|
|
||||||
return IPAddr(self.af, addr)
|
|
||||||
|
|
||||||
def minaddr(self):
|
|
||||||
return self.addr(1)
|
|
||||||
|
|
||||||
def maxaddr(self):
|
|
||||||
if self.af == AF_INET:
|
|
||||||
return self.addr((1 << (self.addrlen - self.prefixlen)) - 2)
|
|
||||||
else:
|
|
||||||
return self.addr((1 << (self.addrlen - self.prefixlen)) - 1)
|
|
||||||
|
|
||||||
def numaddr(self):
|
|
||||||
return max(0, (1 << (self.addrlen - self.prefixlen)) - 2)
|
|
||||||
|
|
||||||
def prefixstr(self):
|
|
||||||
return "%s" % socket.inet_ntop(self.af, self.prefix)
|
|
||||||
|
|
||||||
def netmaskstr(self):
|
|
||||||
addrbits = self.addrlen - self.prefixlen
|
|
||||||
netmask = ((1L << self.prefixlen) - 1) << addrbits
|
|
||||||
netmaskbytes = struct.pack("!L", netmask)
|
|
||||||
return IPAddr(af=AF_INET, addr=netmaskbytes).__str__()
|
|
||||||
|
|
||||||
class IPv4Prefix(IPPrefix):
|
|
||||||
def __init__(self, prefixstr):
|
|
||||||
IPPrefix.__init__(self, AF_INET, prefixstr)
|
|
||||||
|
|
||||||
class IPv6Prefix(IPPrefix):
|
|
||||||
def __init__(self, prefixstr):
|
|
||||||
IPPrefix.__init__(self, AF_INET6, prefixstr)
|
|
||||||
|
|
||||||
def isIPAddress(af, addrstr):
|
|
||||||
try:
|
|
||||||
tmp = socket.inet_pton(af, addrstr)
|
|
||||||
return True
|
|
||||||
except:
|
|
||||||
return False
|
|
||||||
|
|
||||||
def isIPv4Address(addrstr):
|
|
||||||
return isIPAddress(AF_INET, addrstr)
|
|
||||||
|
|
||||||
def isIPv6Address(addrstr):
|
|
||||||
return isIPAddress(AF_INET6, addrstr)
|
|
||||||
|
|
@ -5,35 +5,76 @@
|
||||||
#
|
#
|
||||||
# author: Tom Goff <thomas.goff@boeing.com>
|
# author: Tom Goff <thomas.goff@boeing.com>
|
||||||
#
|
#
|
||||||
'''
|
|
||||||
quagga.py: helper class for generating Quagga configuration.
|
|
||||||
'''
|
|
||||||
|
|
||||||
import os.path
|
"""
|
||||||
|
quagga.py: helper class for generating Quagga configuration.
|
||||||
|
"""
|
||||||
|
|
||||||
from string import Template
|
from string import Template
|
||||||
|
|
||||||
def maketuple(obj):
|
from core.misc import utils
|
||||||
if hasattr(obj, "__iter__"):
|
|
||||||
return tuple(obj)
|
|
||||||
|
def addrstr(x):
|
||||||
|
if x.find(".") >= 0:
|
||||||
|
return "ip address %s" % x
|
||||||
|
elif x.find(":") >= 0:
|
||||||
|
return "ipv6 address %s" % x
|
||||||
else:
|
else:
|
||||||
return (obj,)
|
raise ValueError("invalid address: %s" % x)
|
||||||
|
|
||||||
|
|
||||||
class NetIf(object):
|
class NetIf(object):
|
||||||
def __init__(self, name, addrlist = []):
|
"""
|
||||||
|
Represents a network interface.
|
||||||
|
"""
|
||||||
|
|
||||||
|
def __init__(self, name, addrlist=None):
|
||||||
|
"""
|
||||||
|
Create a NetIf instance.
|
||||||
|
|
||||||
|
:param str name: interface name
|
||||||
|
:param addrlist: address list for the interface
|
||||||
|
"""
|
||||||
self.name = name
|
self.name = name
|
||||||
|
|
||||||
|
if addrlist:
|
||||||
self.addrlist = addrlist
|
self.addrlist = addrlist
|
||||||
|
else:
|
||||||
|
self.addrlist = []
|
||||||
|
|
||||||
|
|
||||||
class Conf(object):
|
class Conf(object):
|
||||||
def __init__(self, **kwds):
|
"""
|
||||||
self.kwds = kwds
|
Provides a configuration object.
|
||||||
|
"""
|
||||||
|
|
||||||
|
def __init__(self, **kwargs):
|
||||||
|
"""
|
||||||
|
Create a Conf instance.
|
||||||
|
|
||||||
|
:param dict kwargs: configuration keyword arguments
|
||||||
|
"""
|
||||||
|
self.kwargs = kwargs
|
||||||
|
|
||||||
def __str__(self):
|
def __str__(self):
|
||||||
tmp = self.template.substitute(**self.kwds)
|
"""
|
||||||
if tmp[-1] == '\n':
|
Provides a string representation of a configuration object.
|
||||||
|
|
||||||
|
:return: string representation
|
||||||
|
:rtype: str
|
||||||
|
"""
|
||||||
|
# TODO: seems like an error here
|
||||||
|
tmp = self.template.substitute(**self.kwargs)
|
||||||
|
if tmp[-1] == "\n":
|
||||||
tmp = tmp[:-1]
|
tmp = tmp[:-1]
|
||||||
return tmp
|
return tmp
|
||||||
|
|
||||||
|
|
||||||
class QuaggaOSPF6Interface(Conf):
|
class QuaggaOSPF6Interface(Conf):
|
||||||
|
"""
|
||||||
|
Provides quagga ospf6 interface functionality.
|
||||||
|
"""
|
||||||
AF_IPV6_ID = 0
|
AF_IPV6_ID = 0
|
||||||
AF_IPV4_ID = 65
|
AF_IPV4_ID = 65
|
||||||
|
|
||||||
|
|
@ -54,28 +95,36 @@ interface $interface
|
||||||
# ipv6 ospf6 simhelloLLtoULRecv :$simhelloport
|
# ipv6 ospf6 simhelloLLtoULRecv :$simhelloport
|
||||||
# !$ipaddr:$simhelloport
|
# !$ipaddr:$simhelloport
|
||||||
|
|
||||||
def __init__(self, netif, instanceid = AF_IPV4_ID,
|
def __init__(self, netif, instanceid=AF_IPV4_ID, network="manet-designated-router", **kwargs):
|
||||||
network = "manet-designated-router", **kwds):
|
"""
|
||||||
self.netif = netif
|
Create a QuaggaOSPF6Interface instance.
|
||||||
def addrstr(x):
|
|
||||||
if x.find(".") >= 0:
|
|
||||||
return "ip address %s" % x
|
|
||||||
elif x.find(":") >= 0:
|
|
||||||
return "ipv6 address %s" % x
|
|
||||||
else:
|
|
||||||
raise Value, "invalid address: %s", x
|
|
||||||
addr = "\n ".join(map(addrstr, netif.addrlist))
|
|
||||||
|
|
||||||
|
:param netif: network interface
|
||||||
|
:param int instanceid: instance id
|
||||||
|
:param network: network
|
||||||
|
:param dict kwargs: keyword arguments
|
||||||
|
"""
|
||||||
|
self.netif = netif
|
||||||
|
addr = "\n ".join(map(addrstr, netif.addrlist))
|
||||||
self.instanceid = instanceid
|
self.instanceid = instanceid
|
||||||
self.network = network
|
self.network = network
|
||||||
Conf.__init__(self, interface=netif.name, addr=addr,
|
Conf.__init__(self, interface=netif.name, addr=addr,
|
||||||
instanceid = instanceid, network = network, **kwds)
|
instanceid=instanceid, network=network, **kwargs)
|
||||||
|
|
||||||
def name(self):
|
def name(self):
|
||||||
|
"""
|
||||||
|
Retrieve network interface name.
|
||||||
|
|
||||||
|
:return: network interface name
|
||||||
|
:rtype: str
|
||||||
|
"""
|
||||||
return self.netif.name
|
return self.netif.name
|
||||||
|
|
||||||
class QuaggaOSPF6(Conf):
|
|
||||||
|
|
||||||
|
class QuaggaOSPF6(Conf):
|
||||||
|
"""
|
||||||
|
Provides quagga ospf6 functionality.
|
||||||
|
"""
|
||||||
template = Template("""\
|
template = Template("""\
|
||||||
$interfaces
|
$interfaces
|
||||||
!
|
!
|
||||||
|
|
@ -85,17 +134,25 @@ router ospf6
|
||||||
$redistribute
|
$redistribute
|
||||||
""")
|
""")
|
||||||
|
|
||||||
def __init__(self, ospf6ifs, area, routerid,
|
def __init__(self, ospf6ifs, area, routerid, redistribute="! no redistribute"):
|
||||||
redistribute = "! no redistribute"):
|
"""
|
||||||
ospf6ifs = maketuple(ospf6ifs)
|
Create a QuaggaOSPF6 instance.
|
||||||
|
|
||||||
|
:param list ospf6ifs: ospf6 interfaces
|
||||||
|
:param area: area
|
||||||
|
:param routerid: router id
|
||||||
|
:param str redistribute: redistribute value
|
||||||
|
"""
|
||||||
|
ospf6ifs = utils.maketuple(ospf6ifs)
|
||||||
interfaces = "\n!\n".join(map(str, ospf6ifs))
|
interfaces = "\n!\n".join(map(str, ospf6ifs))
|
||||||
ospfifs = "\n ".join(map(lambda x: "interface %s area %s" % \
|
ospfifs = "\n ".join(map(lambda x: "interface %s area %s" % (x.name(), area), ospf6ifs))
|
||||||
(x.name(), area), ospf6ifs))
|
Conf.__init__(self, interfaces=interfaces, routerid=routerid, ospfifs=ospfifs, redistribute=redistribute)
|
||||||
Conf.__init__(self, interfaces = interfaces, routerid = routerid,
|
|
||||||
ospfifs = ospfifs, redistribute = redistribute)
|
|
||||||
|
|
||||||
|
|
||||||
class QuaggaConf(Conf):
|
class QuaggaConf(Conf):
|
||||||
|
"""
|
||||||
|
Provides quagga configuration functionality.
|
||||||
|
"""
|
||||||
template = Template("""\
|
template = Template("""\
|
||||||
log file $logfile
|
log file $logfile
|
||||||
$debugs
|
$debugs
|
||||||
|
|
@ -106,11 +163,17 @@ $forwarding
|
||||||
""")
|
""")
|
||||||
|
|
||||||
def __init__(self, routers, logfile, debugs=()):
|
def __init__(self, routers, logfile, debugs=()):
|
||||||
routers = "\n!\n".join(map(str, maketuple(routers)))
|
"""
|
||||||
|
Create a QuaggaConf instance.
|
||||||
|
|
||||||
|
:param list routers: routers
|
||||||
|
:param str logfile: log file name
|
||||||
|
:param debugs: debug options
|
||||||
|
"""
|
||||||
|
routers = "\n!\n".join(map(str, utils.maketuple(routers)))
|
||||||
if debugs:
|
if debugs:
|
||||||
debugs = "\n".join(maketuple(debugs))
|
debugs = "\n".join(utils.maketuple(debugs))
|
||||||
else:
|
else:
|
||||||
debugs = "! no debugs"
|
debugs = "! no debugs"
|
||||||
forwarding = "ip forwarding\nipv6 forwarding"
|
forwarding = "ip forwarding\nipv6 forwarding"
|
||||||
Conf.__init__(self, logfile = logfile, debugs = debugs,
|
Conf.__init__(self, logfile=logfile, debugs=debugs, routers=routers, forwarding=forwarding)
|
||||||
routers = routers, forwarding = forwarding)
|
|
||||||
|
|
|
||||||
|
|
@ -1,123 +1,235 @@
|
||||||
#
|
"""
|
||||||
# CORE
|
Miscellaneous utility functions, wrappers around some subprocess procedures.
|
||||||
# Copyright (c)2010-2012 the Boeing Company.
|
"""
|
||||||
# See the LICENSE file included in this distribution.
|
|
||||||
#
|
import ast
|
||||||
# authors: Tom Goff <thomas.goff@boeing.com>
|
import os
|
||||||
# Jeff Ahrenholz <jeffrey.m.ahrenholz@boeing.com>
|
import subprocess
|
||||||
#
|
|
||||||
'''
|
|
||||||
utils.py: miscellaneous utility functions, wrappers around some subprocess
|
|
||||||
procedures.
|
|
||||||
'''
|
|
||||||
|
|
||||||
import subprocess, os, ast
|
|
||||||
import fcntl
|
import fcntl
|
||||||
|
import resource
|
||||||
|
|
||||||
|
from core.misc import log
|
||||||
|
|
||||||
|
logger = log.get_logger(__name__)
|
||||||
|
|
||||||
|
|
||||||
def closeonexec(fd):
|
def closeonexec(fd):
|
||||||
|
"""
|
||||||
|
Close on execution of a shell process.
|
||||||
|
|
||||||
|
:param fd: file descriptor to close
|
||||||
|
:return: nothing
|
||||||
|
"""
|
||||||
fdflags = fcntl.fcntl(fd, fcntl.F_GETFD)
|
fdflags = fcntl.fcntl(fd, fcntl.F_GETFD)
|
||||||
fcntl.fcntl(fd, fcntl.F_SETFD, fdflags | fcntl.FD_CLOEXEC)
|
fcntl.fcntl(fd, fcntl.F_SETFD, fdflags | fcntl.FD_CLOEXEC)
|
||||||
|
|
||||||
def checkexec(execlist):
|
|
||||||
for bin in execlist:
|
def check_executables(executables):
|
||||||
if which(bin) is None:
|
"""
|
||||||
raise EnvironmentError, "executable not found: %s" % bin
|
Check executables, verify they exist and are executable.
|
||||||
|
|
||||||
|
:param list[str] executables: executable to check
|
||||||
|
:return: nothing
|
||||||
|
:raises EnvironmentError: when an executable doesn't exist or is not executable
|
||||||
|
"""
|
||||||
|
for executable in executables:
|
||||||
|
if not is_exe(executable):
|
||||||
|
raise EnvironmentError("executable not found: %s" % executable)
|
||||||
|
|
||||||
|
|
||||||
|
def is_exe(file_path):
|
||||||
|
"""
|
||||||
|
Check if a given file path exists and is an executable file.
|
||||||
|
|
||||||
|
:param str file_path: file path to check
|
||||||
|
:return: True if the file is considered and executable file, False otherwise
|
||||||
|
:rtype: bool
|
||||||
|
"""
|
||||||
|
return os.path.isfile(file_path) and os.access(file_path, os.X_OK)
|
||||||
|
|
||||||
|
|
||||||
def which(program):
|
def which(program):
|
||||||
''' From: http://stackoverflow.com/questions/377017/test-if-executable-exists-in-python
|
"""
|
||||||
'''
|
From: http://stackoverflow.com/questions/377017/test-if-executable-exists-in-python
|
||||||
def is_exe(fpath):
|
|
||||||
return os.path.isfile(fpath) and os.access(fpath, os.X_OK)
|
|
||||||
|
|
||||||
|
:param str program: program to check for
|
||||||
|
:return: path if it exists, none otherwise
|
||||||
|
"""
|
||||||
fpath, fname = os.path.split(program)
|
fpath, fname = os.path.split(program)
|
||||||
if fpath:
|
if fpath:
|
||||||
if is_exe(program):
|
if is_exe(program):
|
||||||
return program
|
return program
|
||||||
else:
|
else:
|
||||||
for path in os.environ["PATH"].split(os.pathsep):
|
for path in os.environ["PATH"].split(os.pathsep):
|
||||||
path = path.strip('"')
|
path = path.strip("\"")
|
||||||
exe_file = os.path.join(path, program)
|
exe_file = os.path.join(path, program)
|
||||||
if is_exe(exe_file):
|
if is_exe(exe_file):
|
||||||
return exe_file
|
return exe_file
|
||||||
|
|
||||||
return None
|
return None
|
||||||
|
|
||||||
|
|
||||||
def ensurepath(pathlist):
|
def ensurepath(pathlist):
|
||||||
|
"""
|
||||||
|
Checks a list of paths are contained within the environment path, if not add it to the path.
|
||||||
|
|
||||||
|
:param list[str] pathlist: list of paths to check
|
||||||
|
:return: nothing
|
||||||
|
"""
|
||||||
searchpath = os.environ["PATH"].split(":")
|
searchpath = os.environ["PATH"].split(":")
|
||||||
for p in set(pathlist):
|
for p in set(pathlist):
|
||||||
if p not in searchpath:
|
if p not in searchpath:
|
||||||
os.environ["PATH"] += ":" + p
|
os.environ["PATH"] += ":" + p
|
||||||
|
|
||||||
|
|
||||||
def maketuple(obj):
|
def maketuple(obj):
|
||||||
|
"""
|
||||||
|
Create a tuple from an object, or return the object itself.
|
||||||
|
|
||||||
|
:param obj: object to convert to a tuple
|
||||||
|
:return: converted tuple or the object itself
|
||||||
|
:rtype: tuple
|
||||||
|
"""
|
||||||
if hasattr(obj, "__iter__"):
|
if hasattr(obj, "__iter__"):
|
||||||
return tuple(obj)
|
return tuple(obj)
|
||||||
else:
|
else:
|
||||||
return (obj,)
|
return obj,
|
||||||
|
|
||||||
|
|
||||||
|
# TODO: remove unused parameter type
|
||||||
def maketuplefromstr(s, type):
|
def maketuplefromstr(s, type):
|
||||||
s.replace('\\', '\\\\')
|
"""
|
||||||
|
Create a tuple from a string.
|
||||||
|
|
||||||
|
:param str s: string to convert to a tuple
|
||||||
|
:param type: type of tuple to convert to
|
||||||
|
:return: tuple from string
|
||||||
|
:rtype: tuple
|
||||||
|
"""
|
||||||
|
s.replace("\\", "\\\\")
|
||||||
return ast.literal_eval(s)
|
return ast.literal_eval(s)
|
||||||
#return tuple(type(i) for i in s[1:-1].split(','))
|
|
||||||
#r = ()
|
|
||||||
#for i in s.strip("()").split(','):
|
|
||||||
# r += (i.strip("' "), )
|
|
||||||
# chop empty last element from "('a',)" strings
|
|
||||||
#if r[-1] == '':
|
|
||||||
# r = r[:-1]
|
|
||||||
#return r
|
|
||||||
|
|
||||||
def call(*args, **kwds):
|
|
||||||
return subprocess.call(*args, **kwds)
|
|
||||||
|
|
||||||
def mutecall(*args, **kwds):
|
def mutecall(*args, **kwargs):
|
||||||
kwds["stdout"] = open(os.devnull, "w")
|
"""
|
||||||
kwds["stderr"] = subprocess.STDOUT
|
Run a muted call command.
|
||||||
return call(*args, **kwds)
|
|
||||||
|
|
||||||
def check_call(*args, **kwds):
|
:param list args: arguments for the command
|
||||||
return subprocess.check_call(*args, **kwds)
|
:param dict kwargs: keyword arguments for the command
|
||||||
|
:return: command result
|
||||||
|
:rtype: int
|
||||||
|
"""
|
||||||
|
kwargs["stdout"] = open(os.devnull, "w")
|
||||||
|
kwargs["stderr"] = subprocess.STDOUT
|
||||||
|
return subprocess.call(*args, **kwargs)
|
||||||
|
|
||||||
def mutecheck_call(*args, **kwds):
|
|
||||||
kwds["stdout"] = open(os.devnull, "w")
|
|
||||||
kwds["stderr"] = subprocess.STDOUT
|
|
||||||
return subprocess.check_call(*args, **kwds)
|
|
||||||
|
|
||||||
def spawn(*args, **kwds):
|
def mutecheck_call(*args, **kwargs):
|
||||||
return subprocess.Popen(*args, **kwds).pid
|
"""
|
||||||
|
Run a muted check call command.
|
||||||
|
|
||||||
|
:param list args: arguments for the command
|
||||||
|
:param dict kwargs: keyword arguments for the command
|
||||||
|
:return: command result
|
||||||
|
:rtype: int
|
||||||
|
"""
|
||||||
|
kwargs["stdout"] = open(os.devnull, "w")
|
||||||
|
kwargs["stderr"] = subprocess.STDOUT
|
||||||
|
return subprocess.check_call(*args, **kwargs)
|
||||||
|
|
||||||
|
|
||||||
|
def spawn(*args, **kwargs):
|
||||||
|
"""
|
||||||
|
Wrapper for running a spawn command and returning the process id.
|
||||||
|
|
||||||
|
:param list args: arguments for the command
|
||||||
|
:param dict kwargs: keyword arguments for the command
|
||||||
|
:return: process id of the command
|
||||||
|
:rtype: int
|
||||||
|
"""
|
||||||
|
return subprocess.Popen(*args, **kwargs).pid
|
||||||
|
|
||||||
|
|
||||||
|
def mutespawn(*args, **kwargs):
|
||||||
|
"""
|
||||||
|
Wrapper for running a muted spawned command.
|
||||||
|
|
||||||
|
:param list args: arguments for the command
|
||||||
|
:param dict kwargs: keyword arguments for the command
|
||||||
|
:return: process id of the command
|
||||||
|
:rtype: int
|
||||||
|
"""
|
||||||
|
kwargs["stdout"] = open(os.devnull, "w")
|
||||||
|
kwargs["stderr"] = subprocess.STDOUT
|
||||||
|
return subprocess.Popen(*args, **kwargs).pid
|
||||||
|
|
||||||
def mutespawn(*args, **kwds):
|
|
||||||
kwds["stdout"] = open(os.devnull, "w")
|
|
||||||
kwds["stderr"] = subprocess.STDOUT
|
|
||||||
return subprocess.Popen(*args, **kwds).pid
|
|
||||||
|
|
||||||
def detachinit():
|
def detachinit():
|
||||||
|
"""
|
||||||
|
Fork a child process and exit.
|
||||||
|
|
||||||
|
:return: nothing
|
||||||
|
"""
|
||||||
if os.fork():
|
if os.fork():
|
||||||
os._exit(0) # parent exits
|
# parent exits
|
||||||
|
os._exit(0)
|
||||||
os.setsid()
|
os.setsid()
|
||||||
|
|
||||||
def detach(*args, **kwds):
|
|
||||||
kwds["preexec_fn"] = detachinit
|
|
||||||
return subprocess.Popen(*args, **kwds).pid
|
|
||||||
|
|
||||||
def mutedetach(*args, **kwds):
|
def detach(*args, **kwargs):
|
||||||
kwds["preexec_fn"] = detachinit
|
"""
|
||||||
kwds["stdout"] = open(os.devnull, "w")
|
Run a detached process by forking it.
|
||||||
kwds["stderr"] = subprocess.STDOUT
|
|
||||||
return subprocess.Popen(*args, **kwds).pid
|
:param list args: arguments for the command
|
||||||
|
:param dict kwargs: keyword arguments for the command
|
||||||
|
:return: process id of the command
|
||||||
|
:rtype: int
|
||||||
|
"""
|
||||||
|
kwargs["preexec_fn"] = detachinit
|
||||||
|
return subprocess.Popen(*args, **kwargs).pid
|
||||||
|
|
||||||
|
|
||||||
|
def mutedetach(*args, **kwargs):
|
||||||
|
"""
|
||||||
|
Run a muted detached process by forking it.
|
||||||
|
|
||||||
|
:param list args: arguments for the command
|
||||||
|
:param dict kwargs: keyword arguments for the command
|
||||||
|
:return: process id of the command
|
||||||
|
:rtype: int
|
||||||
|
"""
|
||||||
|
kwargs["preexec_fn"] = detachinit
|
||||||
|
kwargs["stdout"] = open(os.devnull, "w")
|
||||||
|
kwargs["stderr"] = subprocess.STDOUT
|
||||||
|
return subprocess.Popen(*args, **kwargs).pid
|
||||||
|
|
||||||
|
|
||||||
def cmdresult(args):
|
def cmdresult(args):
|
||||||
''' Execute a command on the host and return a tuple containing the
|
"""
|
||||||
exit status and result string. stderr output
|
Execute a command on the host and return a tuple containing the exit status and result string. stderr output
|
||||||
is folded into the stdout result string.
|
is folded into the stdout result string.
|
||||||
'''
|
|
||||||
cmdid = subprocess.Popen(args, stdin = open(os.devnull, 'r'),
|
:param list args: command arguments
|
||||||
stdout = subprocess.PIPE,
|
:return: command status and stdout
|
||||||
stderr = subprocess.STDOUT)
|
:rtype: tuple[int, str]
|
||||||
result, err = cmdid.communicate() # err will always be None
|
"""
|
||||||
|
cmdid = subprocess.Popen(args, stdin=open(os.devnull, "r"), stdout=subprocess.PIPE, stderr=subprocess.STDOUT)
|
||||||
|
# err will always be None
|
||||||
|
result, err = cmdid.communicate()
|
||||||
status = cmdid.wait()
|
status = cmdid.wait()
|
||||||
return (status, result)
|
return status, result
|
||||||
|
|
||||||
|
|
||||||
def hexdump(s, bytes_per_word=2, words_per_line=8):
|
def hexdump(s, bytes_per_word=2, words_per_line=8):
|
||||||
|
"""
|
||||||
|
Hex dump of a string.
|
||||||
|
|
||||||
|
:param str s: string to hex dump
|
||||||
|
:param bytes_per_word: number of bytes per word
|
||||||
|
:param words_per_line: number of words per line
|
||||||
|
:return: hex dump of string
|
||||||
|
"""
|
||||||
dump = ""
|
dump = ""
|
||||||
count = 0
|
count = 0
|
||||||
bytes = bytes_per_word * words_per_line
|
bytes = bytes_per_word * words_per_line
|
||||||
|
|
@ -132,20 +244,34 @@ def hexdump(s, bytes_per_word = 2, words_per_line = 8):
|
||||||
count += len(line)
|
count += len(line)
|
||||||
return dump[:-1]
|
return dump[:-1]
|
||||||
|
|
||||||
|
|
||||||
def filemunge(pathname, header, text):
|
def filemunge(pathname, header, text):
|
||||||
''' Insert text at the end of a file, surrounded by header comments.
|
"""
|
||||||
'''
|
Insert text at the end of a file, surrounded by header comments.
|
||||||
filedemunge(pathname, header) # prevent duplicates
|
|
||||||
f = open(pathname, 'a')
|
:param str pathname: file path to add text to
|
||||||
|
:param str header: header text comments
|
||||||
|
:param str text: text to append to file
|
||||||
|
:return: nothing
|
||||||
|
"""
|
||||||
|
# prevent duplicates
|
||||||
|
filedemunge(pathname, header)
|
||||||
|
f = open(pathname, "a")
|
||||||
f.write("# BEGIN %s\n" % header)
|
f.write("# BEGIN %s\n" % header)
|
||||||
f.write(text)
|
f.write(text)
|
||||||
f.write("# END %s\n" % header)
|
f.write("# END %s\n" % header)
|
||||||
f.close()
|
f.close()
|
||||||
|
|
||||||
|
|
||||||
def filedemunge(pathname, header):
|
def filedemunge(pathname, header):
|
||||||
''' Remove text that was inserted in a file surrounded by header comments.
|
"""
|
||||||
'''
|
Remove text that was inserted in a file surrounded by header comments.
|
||||||
f = open(pathname, 'r')
|
|
||||||
|
:param str pathname: file path to open for removing a header
|
||||||
|
:param str header: header text to target for removal
|
||||||
|
:return: nothing
|
||||||
|
"""
|
||||||
|
f = open(pathname, "r")
|
||||||
lines = f.readlines()
|
lines = f.readlines()
|
||||||
f.close()
|
f.close()
|
||||||
start = None
|
start = None
|
||||||
|
|
@ -157,66 +283,101 @@ def filedemunge(pathname, header):
|
||||||
end = i + 1
|
end = i + 1
|
||||||
if start is None or end is None:
|
if start is None or end is None:
|
||||||
return
|
return
|
||||||
f = open(pathname, 'w')
|
f = open(pathname, "w")
|
||||||
lines = lines[:start] + lines[end:]
|
lines = lines[:start] + lines[end:]
|
||||||
f.write("".join(lines))
|
f.write("".join(lines))
|
||||||
f.close()
|
f.close()
|
||||||
|
|
||||||
|
|
||||||
def expandcorepath(pathname, session=None, node=None):
|
def expandcorepath(pathname, session=None, node=None):
|
||||||
''' Expand a file path given session information.
|
"""
|
||||||
'''
|
Expand a file path given session information.
|
||||||
|
|
||||||
|
:param str pathname: file path to expand
|
||||||
|
:param core.session.Session session: core session object to expand path with
|
||||||
|
:param core.netns.LxcNode node: node to expand path with
|
||||||
|
:return: expanded path
|
||||||
|
:rtype: str
|
||||||
|
"""
|
||||||
if session is not None:
|
if session is not None:
|
||||||
pathname = pathname.replace('~', "/home/%s" % session.user)
|
pathname = pathname.replace("~", "/home/%s" % session.user)
|
||||||
pathname = pathname.replace('%SESSION%', str(session.sessionid))
|
pathname = pathname.replace("%SESSION%", str(session.session_id))
|
||||||
pathname = pathname.replace('%SESSION_DIR%', session.sessiondir)
|
pathname = pathname.replace("%SESSION_DIR%", session.session_dir)
|
||||||
pathname = pathname.replace('%SESSION_USER%', session.user)
|
pathname = pathname.replace("%SESSION_USER%", session.user)
|
||||||
if node is not None:
|
if node is not None:
|
||||||
pathname = pathname.replace('%NODE%', str(node.objid))
|
pathname = pathname.replace("%NODE%", str(node.objid))
|
||||||
pathname = pathname.replace('%NODENAME%', node.name)
|
pathname = pathname.replace("%NODENAME%", node.name)
|
||||||
return pathname
|
return pathname
|
||||||
|
|
||||||
|
|
||||||
def sysctldevname(devname):
|
def sysctldevname(devname):
|
||||||
''' Translate a device name to the name used with sysctl.
|
"""
|
||||||
'''
|
Translate a device name to the name used with sysctl.
|
||||||
|
|
||||||
|
:param str devname: device name to translate
|
||||||
|
:return: translated device name
|
||||||
|
:rtype: str
|
||||||
|
"""
|
||||||
if devname is None:
|
if devname is None:
|
||||||
return None
|
return None
|
||||||
return devname.replace(".", "/")
|
return devname.replace(".", "/")
|
||||||
|
|
||||||
|
|
||||||
def daemonize(rootdir="/", umask=0, close_fds=False, dontclose=(),
|
def daemonize(rootdir="/", umask=0, close_fds=False, dontclose=(),
|
||||||
stdin=os.devnull, stdout=os.devnull, stderr=os.devnull,
|
stdin=os.devnull, stdout=os.devnull, stderr=os.devnull,
|
||||||
stdoutmode=0644, stderrmode=0644, pidfilename=None,
|
stdoutmode=0644, stderrmode=0644, pidfilename=None,
|
||||||
defaultmaxfd=1024):
|
defaultmaxfd=1024):
|
||||||
''' Run the background process as a daemon.
|
"""
|
||||||
'''
|
Run the background process as a daemon.
|
||||||
|
|
||||||
|
:param str rootdir: root directory for daemon
|
||||||
|
:param int umask: umask for daemon
|
||||||
|
:param bool close_fds: flag to close file descriptors
|
||||||
|
:param dontclose: dont close options
|
||||||
|
:param stdin: stdin for daemon
|
||||||
|
:param stdout: stdout for daemon
|
||||||
|
:param stderr: stderr for daemon
|
||||||
|
:param int stdoutmode: stdout mode
|
||||||
|
:param int stderrmode: stderr mode
|
||||||
|
:param str pidfilename: pid file name
|
||||||
|
:param int defaultmaxfd: default max file descriptors
|
||||||
|
:return: nothing
|
||||||
|
"""
|
||||||
if not hasattr(dontclose, "__contains__"):
|
if not hasattr(dontclose, "__contains__"):
|
||||||
if not isinstance(dontclose, int):
|
if not isinstance(dontclose, int):
|
||||||
raise TypeError, "dontclose must be an integer"
|
raise TypeError("dontclose must be an integer")
|
||||||
dontclose = (int(dontclose),)
|
dontclose = (int(dontclose),)
|
||||||
else:
|
else:
|
||||||
for fd in dontclose:
|
for fd in dontclose:
|
||||||
if not isinstance(fd, int):
|
if not isinstance(fd, int):
|
||||||
raise TypeError, "dontclose must contain only integers"
|
raise TypeError("dontclose must contain only integers")
|
||||||
|
|
||||||
# redirect stdin
|
# redirect stdin
|
||||||
if stdin:
|
if stdin:
|
||||||
fd = os.open(stdin, os.O_RDONLY)
|
fd = os.open(stdin, os.O_RDONLY)
|
||||||
os.dup2(fd, 0)
|
os.dup2(fd, 0)
|
||||||
os.close(fd)
|
os.close(fd)
|
||||||
|
|
||||||
# redirect stdout
|
# redirect stdout
|
||||||
if stdout:
|
if stdout:
|
||||||
fd = os.open(stdout, os.O_WRONLY | os.O_CREAT | os.O_APPEND,
|
fd = os.open(stdout, os.O_WRONLY | os.O_CREAT | os.O_APPEND,
|
||||||
stdoutmode)
|
stdoutmode)
|
||||||
os.dup2(fd, 1)
|
os.dup2(fd, 1)
|
||||||
if (stdout == stderr):
|
if stdout == stderr:
|
||||||
os.dup2(1, 2)
|
os.dup2(1, 2)
|
||||||
os.close(fd)
|
os.close(fd)
|
||||||
|
|
||||||
# redirect stderr
|
# redirect stderr
|
||||||
if stderr and (stderr != stdout):
|
if stderr and (stderr != stdout):
|
||||||
fd = os.open(stderr, os.O_WRONLY | os.O_CREAT | os.O_APPEND,
|
fd = os.open(stderr, os.O_WRONLY | os.O_CREAT | os.O_APPEND,
|
||||||
stderrmode)
|
stderrmode)
|
||||||
os.dup2(fd, 2)
|
os.dup2(fd, 2)
|
||||||
os.close(fd)
|
os.close(fd)
|
||||||
|
|
||||||
if os.fork():
|
if os.fork():
|
||||||
os._exit(0) # parent exits
|
# parent exits
|
||||||
|
os._exit(0)
|
||||||
|
|
||||||
os.setsid()
|
os.setsid()
|
||||||
pid = os.fork()
|
pid = os.fork()
|
||||||
if pid:
|
if pid:
|
||||||
|
|
@ -225,11 +386,14 @@ def daemonize(rootdir = "/", umask = 0, close_fds = False, dontclose = (),
|
||||||
f = open(pidfilename, "w")
|
f = open(pidfilename, "w")
|
||||||
f.write("%s\n" % pid)
|
f.write("%s\n" % pid)
|
||||||
f.close()
|
f.close()
|
||||||
except:
|
except IOError:
|
||||||
pass
|
logger.exception("error writing to file: %s", pidfilename)
|
||||||
os._exit(0) # parent exits
|
# parent exits
|
||||||
|
os._exit(0)
|
||||||
|
|
||||||
if rootdir:
|
if rootdir:
|
||||||
os.chdir(rootdir)
|
os.chdir(rootdir)
|
||||||
|
|
||||||
os.umask(umask)
|
os.umask(umask)
|
||||||
if close_fds:
|
if close_fds:
|
||||||
try:
|
try:
|
||||||
|
|
@ -238,38 +402,49 @@ def daemonize(rootdir = "/", umask = 0, close_fds = False, dontclose = (),
|
||||||
raise ValueError
|
raise ValueError
|
||||||
except:
|
except:
|
||||||
maxfd = defaultmaxfd
|
maxfd = defaultmaxfd
|
||||||
|
|
||||||
for fd in xrange(3, maxfd):
|
for fd in xrange(3, maxfd):
|
||||||
if fd in dontclose:
|
if fd in dontclose:
|
||||||
continue
|
continue
|
||||||
try:
|
try:
|
||||||
os.close(fd)
|
os.close(fd)
|
||||||
except:
|
except IOError:
|
||||||
pass
|
logger.exception("error closing file descriptor")
|
||||||
|
|
||||||
|
|
||||||
def readfileintodict(filename, d):
|
def readfileintodict(filename, d):
|
||||||
''' Read key=value pairs from a file, into a dict.
|
"""
|
||||||
Skip comments; strip newline characters and spacing.
|
Read key=value pairs from a file, into a dict. Skip comments; strip newline characters and spacing.
|
||||||
'''
|
|
||||||
with open(filename, 'r') as f:
|
:param str filename: file to read into a dictionary
|
||||||
|
:param dict d: dictionary to read file into
|
||||||
|
:return: nothing
|
||||||
|
"""
|
||||||
|
with open(filename, "r") as f:
|
||||||
lines = f.readlines()
|
lines = f.readlines()
|
||||||
for l in lines:
|
for l in lines:
|
||||||
if l[:1] == '#':
|
if l[:1] == "#":
|
||||||
continue
|
continue
|
||||||
try:
|
try:
|
||||||
key, value = l.split('=', 1)
|
key, value = l.split("=", 1)
|
||||||
d[key] = value.strip()
|
d[key] = value.strip()
|
||||||
except ValueError:
|
except ValueError:
|
||||||
pass
|
logger.exception("error reading file to dict: %s", filename)
|
||||||
|
|
||||||
|
|
||||||
def checkforkernelmodule(name):
|
def checkforkernelmodule(name):
|
||||||
''' Return a string if a Linux kernel module is loaded, None otherwise.
|
"""
|
||||||
|
Return a string if a Linux kernel module is loaded, None otherwise.
|
||||||
The string is the line from /proc/modules containing the module name,
|
The string is the line from /proc/modules containing the module name,
|
||||||
memory size (bytes), number of loaded instances, dependencies, state,
|
memory size (bytes), number of loaded instances, dependencies, state,
|
||||||
and kernel memory offset.
|
and kernel memory offset.
|
||||||
'''
|
|
||||||
with open('/proc/modules', 'r') as f:
|
:param str name: name of kernel module to check for
|
||||||
|
:return: kernel module line, None otherwise
|
||||||
|
:rtype: str
|
||||||
|
"""
|
||||||
|
with open("/proc/modules", "r") as f:
|
||||||
for line in f:
|
for line in f:
|
||||||
if line.startswith(name + ' '):
|
if line.startswith(name + " "):
|
||||||
return line.rstrip()
|
return line.rstrip()
|
||||||
return None
|
return None
|
||||||
|
|
|
||||||
|
|
@ -66,6 +66,7 @@ import math
|
||||||
|
|
||||||
__all__ = ['to_latlon', 'from_latlon']
|
__all__ = ['to_latlon', 'from_latlon']
|
||||||
|
|
||||||
|
|
||||||
class OutOfRangeError(ValueError):
|
class OutOfRangeError(ValueError):
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
|
@ -256,4 +257,3 @@ def haversine(lon1, lat1, lon2, lat2):
|
||||||
c = 2 * math.asin(math.sqrt(a))
|
c = 2 * math.asin(math.sqrt(a))
|
||||||
m = 6367000 * c
|
m = 6367000 * c
|
||||||
return m
|
return m
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,205 +0,0 @@
|
||||||
import socket
|
|
||||||
import subprocess
|
|
||||||
import os
|
|
||||||
import xmlutils
|
|
||||||
|
|
||||||
from core.netns import nodes
|
|
||||||
from core.misc import ipaddr
|
|
||||||
from core import constants
|
|
||||||
|
|
||||||
class CoreDeploymentWriter(object):
|
|
||||||
def __init__(self, dom, root, session):
|
|
||||||
self.dom = dom
|
|
||||||
self.root = root
|
|
||||||
self.session = session
|
|
||||||
self.hostname = socket.gethostname()
|
|
||||||
if self.session.emane.version < self.session.emane.EMANE092:
|
|
||||||
self.transport = None
|
|
||||||
self.platform = None
|
|
||||||
|
|
||||||
@staticmethod
|
|
||||||
def get_ipv4_addresses(hostname):
|
|
||||||
if hostname == 'localhost':
|
|
||||||
addr_list = []
|
|
||||||
cmd = (constants.IP_BIN, '-o', '-f', 'inet', 'addr', 'show')
|
|
||||||
output = subprocess.check_output(cmd)
|
|
||||||
for line in output.split(os.linesep):
|
|
||||||
split = line.split()
|
|
||||||
if not split:
|
|
||||||
continue
|
|
||||||
addr = split[3]
|
|
||||||
if not addr.startswith('127.'):
|
|
||||||
addr_list.append(addr)
|
|
||||||
return addr_list
|
|
||||||
else:
|
|
||||||
# TODO: handle other hosts
|
|
||||||
raise NotImplementedError
|
|
||||||
|
|
||||||
@staticmethod
|
|
||||||
def get_interface_names(hostname):
|
|
||||||
'''Uses same methodology of get_ipv4_addresses() to get
|
|
||||||
parallel list of interface names to go with ...'''
|
|
||||||
if hostname == 'localhost':
|
|
||||||
iface_list = []
|
|
||||||
cmd = (constants.IP_BIN, '-o', '-f', 'inet', 'addr', 'show')
|
|
||||||
output = subprocess.check_output(cmd)
|
|
||||||
for line in output.split(os.linesep):
|
|
||||||
split = line.split()
|
|
||||||
if not split:
|
|
||||||
continue
|
|
||||||
ifaceName = split[1]
|
|
||||||
addr = split[3]
|
|
||||||
if not addr.startswith('127.'):
|
|
||||||
iface_list.append(ifaceName)
|
|
||||||
return iface_list
|
|
||||||
else:
|
|
||||||
# TODO: handle other hosts
|
|
||||||
raise NotImplementedError
|
|
||||||
|
|
||||||
@staticmethod
|
|
||||||
def find_device(scenario, name):
|
|
||||||
tagName = ('device', 'host', 'router')
|
|
||||||
for d in xmlutils.iterDescendantsWithAttribute(scenario, tagName,
|
|
||||||
'name', name):
|
|
||||||
return d
|
|
||||||
return None
|
|
||||||
|
|
||||||
@staticmethod
|
|
||||||
def find_interface(device, name):
|
|
||||||
for i in xmlutils.iterDescendantsWithAttribute(device, 'interface',
|
|
||||||
'name', name):
|
|
||||||
return i
|
|
||||||
return None
|
|
||||||
|
|
||||||
def add_deployment(self):
|
|
||||||
testbed = self.dom.createElement('container')
|
|
||||||
testbed.setAttribute('name', 'TestBed')
|
|
||||||
testbed.setAttribute('id', 'TestBed')
|
|
||||||
self.root.baseEle.appendChild(testbed)
|
|
||||||
nodelist = []
|
|
||||||
for obj in self.session.objs():
|
|
||||||
if isinstance(obj, nodes.PyCoreNode):
|
|
||||||
nodelist.append(obj)
|
|
||||||
name = self.hostname
|
|
||||||
ipv4_addresses = self.get_ipv4_addresses('localhost')
|
|
||||||
iface_names = self.get_interface_names('localhost')
|
|
||||||
testhost = self.add_physical_host(testbed, name, ipv4_addresses, iface_names)
|
|
||||||
for n in nodelist:
|
|
||||||
self.add_virtual_host(testhost, n)
|
|
||||||
# TODO: handle other servers
|
|
||||||
# servers = self.session.broker.getservernames()
|
|
||||||
# servers.remove('localhost')
|
|
||||||
|
|
||||||
def add_child_element(self, parent, tagName):
|
|
||||||
el = self.dom.createElement(tagName)
|
|
||||||
parent.appendChild(el)
|
|
||||||
return el
|
|
||||||
|
|
||||||
def add_child_element_with_nameattr(self, parent, tagName,
|
|
||||||
name, setid = True):
|
|
||||||
el = self.add_child_element(parent, tagName)
|
|
||||||
el.setAttribute('name', name)
|
|
||||||
if setid:
|
|
||||||
el.setAttribute('id', '%s/%s' % (parent.getAttribute('id'), name))
|
|
||||||
return el
|
|
||||||
|
|
||||||
def add_address(self, parent, address_type, address_str, address_iface=None):
|
|
||||||
el = self.add_child_element(parent, 'address')
|
|
||||||
el.setAttribute('type', address_type)
|
|
||||||
if address_iface is not None:
|
|
||||||
el.setAttribute('iface', address_iface)
|
|
||||||
el.appendChild(self.dom.createTextNode(address_str))
|
|
||||||
return el
|
|
||||||
|
|
||||||
def add_type(self, parent, type_str):
|
|
||||||
el = self.add_child_element(parent, 'type')
|
|
||||||
el.appendChild(self.dom.createTextNode(type_str))
|
|
||||||
return el
|
|
||||||
|
|
||||||
def add_platform(self, parent, name):
|
|
||||||
el = self.add_child_element_with_nameattr(parent,
|
|
||||||
'emanePlatform', name)
|
|
||||||
return el
|
|
||||||
|
|
||||||
def add_transport(self, parent, name):
|
|
||||||
el = self.add_child_element_with_nameattr(parent, 'transport', name)
|
|
||||||
return el
|
|
||||||
|
|
||||||
def add_nem(self, parent, name):
|
|
||||||
el = self.add_child_element_with_nameattr(parent, 'nem', name)
|
|
||||||
return el
|
|
||||||
|
|
||||||
def add_parameter(self, parent, name, val):
|
|
||||||
el = self.add_child_element_with_nameattr(parent, 'parameter',
|
|
||||||
name, False)
|
|
||||||
el.appendChild(self.dom.createTextNode(val))
|
|
||||||
return el
|
|
||||||
|
|
||||||
def add_mapping(self, parent, maptype, mapref):
|
|
||||||
el = self.add_child_element(parent, 'mapping')
|
|
||||||
el.setAttribute('type', maptype)
|
|
||||||
el.setAttribute('ref', mapref)
|
|
||||||
return el
|
|
||||||
|
|
||||||
def add_host(self, parent, name):
|
|
||||||
el = self.add_child_element_with_nameattr(parent, 'testHost', name)
|
|
||||||
return el
|
|
||||||
|
|
||||||
def add_physical_host(self, parent, name, ipv4_addresses, iface_names):
|
|
||||||
el = self.add_host(parent, name)
|
|
||||||
self.add_type(el, 'physical')
|
|
||||||
for i in range(0, len(ipv4_addresses)):
|
|
||||||
addr = ipv4_addresses[i]
|
|
||||||
if iface_names:
|
|
||||||
ifaceName = iface_names[i]
|
|
||||||
else:
|
|
||||||
ifaceName = None
|
|
||||||
self.add_address(el, 'IPv4', addr, ifaceName)
|
|
||||||
return el
|
|
||||||
|
|
||||||
def add_virtual_host(self, parent, obj):
|
|
||||||
assert isinstance(obj, nodes.PyCoreNode)
|
|
||||||
el = self.add_host(parent, obj.name)
|
|
||||||
device = self.find_device(self.root.baseEle, obj.name)
|
|
||||||
if device is None:
|
|
||||||
self.session.warn('corresponding XML device not found for %s' %
|
|
||||||
(obj.name))
|
|
||||||
return
|
|
||||||
self.add_mapping(device, 'testHost', el.getAttribute('id'))
|
|
||||||
self.add_type(el, 'virtual')
|
|
||||||
for netif in obj.netifs():
|
|
||||||
for address in netif.addrlist:
|
|
||||||
addr, slash, prefixlen= address.partition('/')
|
|
||||||
if ipaddr.isIPv4Address(addr):
|
|
||||||
addr_type = 'IPv4'
|
|
||||||
elif ipaddr.isIPv6Address(addr):
|
|
||||||
addr_type = 'IPv6'
|
|
||||||
else:
|
|
||||||
raise NotImplementedError
|
|
||||||
self.add_address(el, addr_type, address, netif.name)
|
|
||||||
if isinstance(netif.net, nodes.EmaneNode):
|
|
||||||
nem = self.add_emane_interface(parent, el, netif)
|
|
||||||
interface = self.find_interface(device, netif.name)
|
|
||||||
self.add_mapping(interface, 'nem', nem.getAttribute('id'))
|
|
||||||
return el
|
|
||||||
|
|
||||||
def add_emane_interface(self, physical_host, virtual_host, netif,
|
|
||||||
platform_name = 'p1', transport_name = 't1'):
|
|
||||||
nemid = netif.net.nemidmap[netif]
|
|
||||||
if self.session.emane.version < self.session.emane.EMANE092:
|
|
||||||
if self.platform is None:
|
|
||||||
self.platform = \
|
|
||||||
self.add_platform(physical_host, name = platform_name)
|
|
||||||
platform = self.platform
|
|
||||||
if self.transport is None:
|
|
||||||
self.transport = \
|
|
||||||
self.add_transport(physical_host, name = transport_name)
|
|
||||||
transport = self.transport
|
|
||||||
else:
|
|
||||||
platform = self.add_platform(virtual_host, name = platform_name)
|
|
||||||
transport = self.add_transport(virtual_host, name = transport_name)
|
|
||||||
nem_name = 'nem%s' % nemid
|
|
||||||
nem = self.add_nem(platform, nem_name)
|
|
||||||
self.add_parameter(nem, 'nemid', str(nemid))
|
|
||||||
self.add_mapping(transport, 'nem', nem.getAttribute('id'))
|
|
||||||
return nem
|
|
||||||
|
|
@ -1,46 +0,0 @@
|
||||||
# CORE
|
|
||||||
# Copyright (c) 2014 The Boeing Company.
|
|
||||||
# See the LICENSE file included in this distribution.
|
|
||||||
|
|
||||||
from xml.dom.minidom import parse
|
|
||||||
from xmlutils import getFirstChildByTagName
|
|
||||||
from xmlparser0 import CoreDocumentParser0
|
|
||||||
from xmlparser1 import CoreDocumentParser1
|
|
||||||
|
|
||||||
class CoreVersionParser(object):
|
|
||||||
DEFAULT_SCENARIO_VERSION = '1.0'
|
|
||||||
|
|
||||||
'''\
|
|
||||||
Helper class to check the version of Network Plan document. This
|
|
||||||
simply looks for a "Scenario" element; when present, this
|
|
||||||
indicates a 0.0 version document. The dom member is set in order
|
|
||||||
to prevent parsing a file twice (it can be passed to the
|
|
||||||
appropriate CoreDocumentParser class.)
|
|
||||||
'''
|
|
||||||
def __init__(self, filename, options={}):
|
|
||||||
if 'dom' in options:
|
|
||||||
self.dom = options['dom']
|
|
||||||
else:
|
|
||||||
self.dom = parse(filename)
|
|
||||||
scenario = getFirstChildByTagName(self.dom, 'scenario')
|
|
||||||
if scenario:
|
|
||||||
version = scenario.getAttribute('version')
|
|
||||||
if not version:
|
|
||||||
version = self.DEFAULT_SCENARIO_VERSION
|
|
||||||
self.version = version
|
|
||||||
elif getFirstChildByTagName(self.dom, 'Scenario'):
|
|
||||||
self.version = '0.0'
|
|
||||||
else:
|
|
||||||
self.version = 'unknown'
|
|
||||||
|
|
||||||
def core_document_parser(session, filename, options):
|
|
||||||
vp = CoreVersionParser(filename, options)
|
|
||||||
if 'dom' not in options:
|
|
||||||
options['dom'] = vp.dom
|
|
||||||
if vp.version == '0.0':
|
|
||||||
doc = CoreDocumentParser0(session, filename, options)
|
|
||||||
elif vp.version == '1.0':
|
|
||||||
doc = CoreDocumentParser1(session, filename, options)
|
|
||||||
else:
|
|
||||||
raise ValueError, 'unsupported document version: %s' % vp.version
|
|
||||||
return doc
|
|
||||||
|
|
@ -1,420 +0,0 @@
|
||||||
#
|
|
||||||
# CORE
|
|
||||||
# Copyright (c)2011-2014 the Boeing Company.
|
|
||||||
# See the LICENSE file included in this distribution.
|
|
||||||
#
|
|
||||||
# author: Jeff Ahrenholz <jeffrey.m.ahrenholz@boeing.com>
|
|
||||||
#
|
|
||||||
|
|
||||||
from core.netns import nodes
|
|
||||||
from xml.dom.minidom import parse
|
|
||||||
from xmlutils import *
|
|
||||||
|
|
||||||
class CoreDocumentParser0(object):
|
|
||||||
def __init__(self, session, filename, options):
|
|
||||||
self.session = session
|
|
||||||
self.verbose = self.session.getcfgitembool('verbose', False)
|
|
||||||
self.filename = filename
|
|
||||||
if 'dom' in options:
|
|
||||||
# this prevents parsing twice when detecting file versions
|
|
||||||
self.dom = options['dom']
|
|
||||||
else:
|
|
||||||
self.dom = parse(filename)
|
|
||||||
self.start = options['start']
|
|
||||||
self.nodecls = options['nodecls']
|
|
||||||
|
|
||||||
self.np = getoneelement(self.dom, "NetworkPlan")
|
|
||||||
if self.np is None:
|
|
||||||
raise ValueError, "missing NetworkPlan!"
|
|
||||||
self.mp = getoneelement(self.dom, "MotionPlan")
|
|
||||||
self.sp = getoneelement(self.dom, "ServicePlan")
|
|
||||||
self.meta = getoneelement(self.dom, "CoreMetaData")
|
|
||||||
|
|
||||||
self.coords = self.getmotiondict(self.mp)
|
|
||||||
# link parameters parsed in parsenets(), applied in parsenodes()
|
|
||||||
self.linkparams = {}
|
|
||||||
|
|
||||||
self.parsedefaultservices()
|
|
||||||
self.parseorigin()
|
|
||||||
self.parsenets()
|
|
||||||
self.parsenodes()
|
|
||||||
self.parseservices()
|
|
||||||
self.parsemeta()
|
|
||||||
|
|
||||||
|
|
||||||
def warn(self, msg):
|
|
||||||
if self.session:
|
|
||||||
warnstr = "XML parsing '%s':" % (self.filename)
|
|
||||||
self.session.warn("%s %s" % (warnstr, msg))
|
|
||||||
|
|
||||||
def getmotiondict(self, mp):
|
|
||||||
''' Parse a MotionPlan into a dict with node names for keys and coordinates
|
|
||||||
for values.
|
|
||||||
'''
|
|
||||||
if mp is None:
|
|
||||||
return {}
|
|
||||||
coords = {}
|
|
||||||
for node in mp.getElementsByTagName("Node"):
|
|
||||||
nodename = str(node.getAttribute("name"))
|
|
||||||
if nodename == '':
|
|
||||||
continue
|
|
||||||
for m in node.getElementsByTagName("motion"):
|
|
||||||
if m.getAttribute("type") != "stationary":
|
|
||||||
continue
|
|
||||||
point = m.getElementsByTagName("point")
|
|
||||||
if len(point) == 0:
|
|
||||||
continue
|
|
||||||
txt = point[0].firstChild
|
|
||||||
if txt is None:
|
|
||||||
continue
|
|
||||||
xyz = map(int, txt.nodeValue.split(','))
|
|
||||||
z = None
|
|
||||||
x, y = xyz[0:2]
|
|
||||||
if (len(xyz) == 3):
|
|
||||||
z = xyz[2]
|
|
||||||
coords[nodename] = (x, y, z)
|
|
||||||
return coords
|
|
||||||
|
|
||||||
@staticmethod
|
|
||||||
def getcommonattributes(obj):
|
|
||||||
''' Helper to return tuple of attributes common to nodes and nets.
|
|
||||||
'''
|
|
||||||
id = int(obj.getAttribute("id"))
|
|
||||||
name = str(obj.getAttribute("name"))
|
|
||||||
type = str(obj.getAttribute("type"))
|
|
||||||
return(id, name, type)
|
|
||||||
|
|
||||||
def parsenets(self):
|
|
||||||
linkednets = []
|
|
||||||
for net in self.np.getElementsByTagName("NetworkDefinition"):
|
|
||||||
id, name, type = self.getcommonattributes(net)
|
|
||||||
nodecls = xmltypetonodeclass(self.session, type)
|
|
||||||
if not nodecls:
|
|
||||||
self.warn("skipping unknown network node '%s' type '%s'" % \
|
|
||||||
(name, type))
|
|
||||||
continue
|
|
||||||
n = self.session.addobj(cls = nodecls, objid = id, name = name,
|
|
||||||
start = self.start)
|
|
||||||
if name in self.coords:
|
|
||||||
x, y, z = self.coords[name]
|
|
||||||
n.setposition(x, y, z)
|
|
||||||
getparamssetattrs(net, ("icon", "canvas", "opaque"), n)
|
|
||||||
if hasattr(n, "canvas") and n.canvas is not None:
|
|
||||||
n.canvas = int(n.canvas)
|
|
||||||
# links between two nets (e.g. switch-switch)
|
|
||||||
for ifc in net.getElementsByTagName("interface"):
|
|
||||||
netid = str(ifc.getAttribute("net"))
|
|
||||||
ifcname = str(ifc.getAttribute("name"))
|
|
||||||
linkednets.append((n, netid, ifcname))
|
|
||||||
self.parsemodels(net, n)
|
|
||||||
# link networks together now that they all have been parsed
|
|
||||||
for (n, netid, ifcname) in linkednets:
|
|
||||||
try:
|
|
||||||
n2 = n.session.objbyname(netid)
|
|
||||||
except KeyError:
|
|
||||||
n.warn("skipping net %s interface: unknown net %s" % \
|
|
||||||
(n.name, netid))
|
|
||||||
continue
|
|
||||||
upstream = False
|
|
||||||
netif = n.getlinknetif(n2)
|
|
||||||
if netif is None:
|
|
||||||
netif = n2.linknet(n)
|
|
||||||
else:
|
|
||||||
netif.swapparams('_params_up')
|
|
||||||
upstream = True
|
|
||||||
key = (n2.name, ifcname)
|
|
||||||
if key in self.linkparams:
|
|
||||||
for (k, v) in self.linkparams[key]:
|
|
||||||
netif.setparam(k, v)
|
|
||||||
if upstream:
|
|
||||||
netif.swapparams('_params_up')
|
|
||||||
|
|
||||||
def parsenodes(self):
|
|
||||||
for node in self.np.getElementsByTagName("Node"):
|
|
||||||
id, name, type = self.getcommonattributes(node)
|
|
||||||
if type == "rj45":
|
|
||||||
nodecls = nodes.RJ45Node
|
|
||||||
else:
|
|
||||||
nodecls = self.nodecls
|
|
||||||
n = self.session.addobj(cls = nodecls, objid = id, name = name,
|
|
||||||
start = self.start)
|
|
||||||
if name in self.coords:
|
|
||||||
x, y, z = self.coords[name]
|
|
||||||
n.setposition(x, y, z)
|
|
||||||
n.type = type
|
|
||||||
getparamssetattrs(node, ("icon", "canvas", "opaque"), n)
|
|
||||||
if hasattr(n, "canvas") and n.canvas is not None:
|
|
||||||
n.canvas = int(n.canvas)
|
|
||||||
for ifc in node.getElementsByTagName("interface"):
|
|
||||||
self.parseinterface(n, ifc)
|
|
||||||
|
|
||||||
def parseinterface(self, n, ifc):
|
|
||||||
''' Parse a interface block such as:
|
|
||||||
<interface name="eth0" net="37278">
|
|
||||||
<address type="mac">00:00:00:aa:00:01</address>
|
|
||||||
<address>10.0.0.2/24</address>
|
|
||||||
<address>2001::2/64</address>
|
|
||||||
</interface>
|
|
||||||
'''
|
|
||||||
name = str(ifc.getAttribute("name"))
|
|
||||||
netid = str(ifc.getAttribute("net"))
|
|
||||||
hwaddr = None
|
|
||||||
addrlist = []
|
|
||||||
try:
|
|
||||||
net = n.session.objbyname(netid)
|
|
||||||
except KeyError:
|
|
||||||
n.warn("skipping node %s interface %s: unknown net %s" % \
|
|
||||||
(n.name, name, netid))
|
|
||||||
return
|
|
||||||
for addr in ifc.getElementsByTagName("address"):
|
|
||||||
addrstr = gettextchild(addr)
|
|
||||||
if addrstr is None:
|
|
||||||
continue
|
|
||||||
if addr.getAttribute("type") == "mac":
|
|
||||||
hwaddr = addrstr
|
|
||||||
else:
|
|
||||||
addrlist.append(addrstr)
|
|
||||||
i = n.newnetif(net, addrlist = addrlist, hwaddr = hwaddr,
|
|
||||||
ifindex = None, ifname = name)
|
|
||||||
for model in ifc.getElementsByTagName("model"):
|
|
||||||
self.parsemodel(model, n, n.objid)
|
|
||||||
key = (n.name, name)
|
|
||||||
if key in self.linkparams:
|
|
||||||
netif = n.netif(i)
|
|
||||||
for (k, v) in self.linkparams[key]:
|
|
||||||
netif.setparam(k, v)
|
|
||||||
|
|
||||||
def parsemodels(self, dom, obj):
|
|
||||||
''' Mobility/wireless model config is stored in a ConfigurableManager's
|
|
||||||
config dict.
|
|
||||||
'''
|
|
||||||
nodenum = int(dom.getAttribute("id"))
|
|
||||||
for model in dom.getElementsByTagName("model"):
|
|
||||||
self.parsemodel(model, obj, nodenum)
|
|
||||||
|
|
||||||
def parsemodel(self, model, obj, nodenum):
|
|
||||||
''' Mobility/wireless model config is stored in a ConfigurableManager's
|
|
||||||
config dict.
|
|
||||||
'''
|
|
||||||
name = model.getAttribute("name")
|
|
||||||
if name == '':
|
|
||||||
return
|
|
||||||
type = model.getAttribute("type")
|
|
||||||
# convert child text nodes into key=value pairs
|
|
||||||
kvs = gettextelementstolist(model)
|
|
||||||
|
|
||||||
mgr = self.session.mobility
|
|
||||||
# TODO: the session.confobj() mechanism could be more generic;
|
|
||||||
# it only allows registering Conf Message callbacks, but here
|
|
||||||
# we want access to the ConfigurableManager, not the callback
|
|
||||||
if name[:5] == "emane":
|
|
||||||
mgr = self.session.emane
|
|
||||||
elif name[:5] == "netem":
|
|
||||||
mgr = None
|
|
||||||
self.parsenetem(model, obj, kvs)
|
|
||||||
|
|
||||||
elif name[:3] == "xen":
|
|
||||||
mgr = self.session.xen
|
|
||||||
# TODO: assign other config managers here
|
|
||||||
if mgr:
|
|
||||||
mgr.setconfig_keyvalues(nodenum, name, kvs)
|
|
||||||
|
|
||||||
def parsenetem(self, model, obj, kvs):
|
|
||||||
''' Determine interface and invoke setparam() using the parsed
|
|
||||||
(key, value) pairs.
|
|
||||||
'''
|
|
||||||
ifname = model.getAttribute("netif")
|
|
||||||
peer = model.getAttribute("peer")
|
|
||||||
key = (peer, ifname)
|
|
||||||
# nodes and interfaces do not exist yet, at this point of the parsing,
|
|
||||||
# save (key, value) pairs for later
|
|
||||||
try:
|
|
||||||
#kvs = map(lambda(k, v): (int(v)), kvs)
|
|
||||||
kvs = map(self.numericvalue, kvs)
|
|
||||||
except ValueError:
|
|
||||||
self.warn("error parsing link parameters for '%s' on '%s'" % \
|
|
||||||
(ifname, peer))
|
|
||||||
self.linkparams[key] = kvs
|
|
||||||
|
|
||||||
@staticmethod
|
|
||||||
def numericvalue(keyvalue):
|
|
||||||
(key, value) = keyvalue
|
|
||||||
if '.' in str(value):
|
|
||||||
value = float(value)
|
|
||||||
else:
|
|
||||||
value = int(value)
|
|
||||||
return (key, value)
|
|
||||||
|
|
||||||
def parseorigin(self):
|
|
||||||
''' Parse any origin tag from the Mobility Plan and set the CoreLocation
|
|
||||||
reference point appropriately.
|
|
||||||
'''
|
|
||||||
origin = getoneelement(self.mp, "origin")
|
|
||||||
if not origin:
|
|
||||||
return
|
|
||||||
location = self.session.location
|
|
||||||
geo = []
|
|
||||||
attrs = ("lat","lon","alt")
|
|
||||||
for i in xrange(3):
|
|
||||||
a = origin.getAttribute(attrs[i])
|
|
||||||
if a is not None:
|
|
||||||
a = float(a)
|
|
||||||
geo.append(a)
|
|
||||||
location.setrefgeo(geo[0], geo[1], geo[2])
|
|
||||||
scale = origin.getAttribute("scale100")
|
|
||||||
if scale is not None:
|
|
||||||
location.refscale = float(scale)
|
|
||||||
point = getoneelement(origin, "point")
|
|
||||||
if point is not None and point.firstChild is not None:
|
|
||||||
xyz = point.firstChild.nodeValue.split(',')
|
|
||||||
if len(xyz) == 2:
|
|
||||||
xyz.append('0.0')
|
|
||||||
if len(xyz) == 3:
|
|
||||||
xyz = map(lambda(x): float(x), xyz)
|
|
||||||
location.refxyz = (xyz[0], xyz[1], xyz[2])
|
|
||||||
|
|
||||||
def parsedefaultservices(self):
|
|
||||||
''' Prior to parsing nodes, use session.services manager to store
|
|
||||||
default services for node types
|
|
||||||
'''
|
|
||||||
for node in self.sp.getElementsByTagName("Node"):
|
|
||||||
type = node.getAttribute("type")
|
|
||||||
if type == '':
|
|
||||||
continue # node-specific service config
|
|
||||||
services = []
|
|
||||||
for service in node.getElementsByTagName("Service"):
|
|
||||||
services.append(str(service.getAttribute("name")))
|
|
||||||
self.session.services.defaultservices[type] = services
|
|
||||||
self.session.info("default services for type %s set to %s" % \
|
|
||||||
(type, services))
|
|
||||||
|
|
||||||
def parseservices(self):
|
|
||||||
''' After node objects exist, parse service customizations and add them
|
|
||||||
to the nodes.
|
|
||||||
'''
|
|
||||||
svclists = {}
|
|
||||||
# parse services and store configs into session.services.configs
|
|
||||||
for node in self.sp.getElementsByTagName("Node"):
|
|
||||||
name = node.getAttribute("name")
|
|
||||||
if name == '':
|
|
||||||
continue # node type without name
|
|
||||||
n = self.session.objbyname(name)
|
|
||||||
if n is None:
|
|
||||||
self.warn("skipping service config for unknown node '%s'" % \
|
|
||||||
name)
|
|
||||||
continue
|
|
||||||
for service in node.getElementsByTagName("Service"):
|
|
||||||
svcname = service.getAttribute("name")
|
|
||||||
if self.parseservice(service, n):
|
|
||||||
if n.objid in svclists:
|
|
||||||
svclists[n.objid] += "|" + svcname
|
|
||||||
else:
|
|
||||||
svclists[n.objid] = svcname
|
|
||||||
# nodes in NetworkPlan but not in ServicePlan use the
|
|
||||||
# default services for their type
|
|
||||||
for node in self.np.getElementsByTagName("Node"):
|
|
||||||
id, name, type = self.getcommonattributes(node)
|
|
||||||
if id in svclists:
|
|
||||||
continue # custom config exists
|
|
||||||
else:
|
|
||||||
svclists[int(id)] = None # use defaults
|
|
||||||
|
|
||||||
# associate nodes with services
|
|
||||||
for objid in sorted(svclists.keys()):
|
|
||||||
n = self.session.obj(objid)
|
|
||||||
self.session.services.addservicestonode(node=n, nodetype=n.type,
|
|
||||||
services_str=svclists[objid],
|
|
||||||
verbose=self.verbose)
|
|
||||||
|
|
||||||
def parseservice(self, service, n):
|
|
||||||
''' Use session.services manager to store service customizations before
|
|
||||||
they are added to a node.
|
|
||||||
'''
|
|
||||||
name = service.getAttribute("name")
|
|
||||||
svc = self.session.services.getservicebyname(name)
|
|
||||||
if svc is None:
|
|
||||||
return False
|
|
||||||
values = []
|
|
||||||
startup_idx = service.getAttribute("startup_idx")
|
|
||||||
if startup_idx is not None:
|
|
||||||
values.append("startidx=%s" % startup_idx)
|
|
||||||
startup_time = service.getAttribute("start_time")
|
|
||||||
if startup_time is not None:
|
|
||||||
values.append("starttime=%s" % startup_time)
|
|
||||||
dirs = []
|
|
||||||
for dir in service.getElementsByTagName("Directory"):
|
|
||||||
dirname = dir.getAttribute("name")
|
|
||||||
dirs.append(dirname)
|
|
||||||
if len(dirs):
|
|
||||||
values.append("dirs=%s" % dirs)
|
|
||||||
|
|
||||||
startup = []
|
|
||||||
shutdown = []
|
|
||||||
validate = []
|
|
||||||
for cmd in service.getElementsByTagName("Command"):
|
|
||||||
type = cmd.getAttribute("type")
|
|
||||||
cmdstr = gettextchild(cmd)
|
|
||||||
if cmdstr is None:
|
|
||||||
continue
|
|
||||||
if type == "start":
|
|
||||||
startup.append(cmdstr)
|
|
||||||
elif type == "stop":
|
|
||||||
shutdown.append(cmdstr)
|
|
||||||
elif type == "validate":
|
|
||||||
validate.append(cmdstr)
|
|
||||||
if len(startup):
|
|
||||||
values.append("cmdup=%s" % startup)
|
|
||||||
if len(shutdown):
|
|
||||||
values.append("cmddown=%s" % shutdown)
|
|
||||||
if len(validate):
|
|
||||||
values.append("cmdval=%s" % validate)
|
|
||||||
|
|
||||||
files = []
|
|
||||||
for file in service.getElementsByTagName("File"):
|
|
||||||
filename = file.getAttribute("name")
|
|
||||||
files.append(filename)
|
|
||||||
data = gettextchild(file)
|
|
||||||
typestr = "service:%s:%s" % (name, filename)
|
|
||||||
self.session.services.setservicefile(nodenum=n.objid, type=typestr,
|
|
||||||
filename=filename,
|
|
||||||
srcname=None, data=data)
|
|
||||||
if len(files):
|
|
||||||
values.append("files=%s" % files)
|
|
||||||
if not bool(service.getAttribute("custom")):
|
|
||||||
return True
|
|
||||||
self.session.services.setcustomservice(n.objid, svc, values)
|
|
||||||
return True
|
|
||||||
|
|
||||||
def parsehooks(self, hooks):
|
|
||||||
''' Parse hook scripts from XML into session._hooks.
|
|
||||||
'''
|
|
||||||
for hook in hooks.getElementsByTagName("Hook"):
|
|
||||||
filename = hook.getAttribute("name")
|
|
||||||
state = hook.getAttribute("state")
|
|
||||||
data = gettextchild(hook)
|
|
||||||
if data is None:
|
|
||||||
data = "" # allow for empty file
|
|
||||||
type = "hook:%s" % state
|
|
||||||
self.session.sethook(type, filename=filename,
|
|
||||||
srcname=None, data=data)
|
|
||||||
|
|
||||||
def parsemeta(self):
|
|
||||||
opt = getoneelement(self.meta, "SessionOptions")
|
|
||||||
if opt:
|
|
||||||
for param in opt.getElementsByTagName("param"):
|
|
||||||
k = str(param.getAttribute("name"))
|
|
||||||
v = str(param.getAttribute("value"))
|
|
||||||
if v == '':
|
|
||||||
v = gettextchild(param) # allow attribute/text for newlines
|
|
||||||
setattr(self.session.options, k, v)
|
|
||||||
hooks = getoneelement(self.meta, "Hooks")
|
|
||||||
if hooks:
|
|
||||||
self.parsehooks(hooks)
|
|
||||||
meta = getoneelement(self.meta, "MetaData")
|
|
||||||
if meta:
|
|
||||||
for param in meta.getElementsByTagName("param"):
|
|
||||||
k = str(param.getAttribute("name"))
|
|
||||||
v = str(param.getAttribute("value"))
|
|
||||||
if v == '':
|
|
||||||
v = gettextchild(param)
|
|
||||||
self.session.metadata.additem(k, v)
|
|
||||||
|
|
@ -1,942 +0,0 @@
|
||||||
#
|
|
||||||
# CORE
|
|
||||||
# Copyright (c) 2015 the Boeing Company.
|
|
||||||
# See the LICENSE file included in this distribution.
|
|
||||||
#
|
|
||||||
|
|
||||||
import sys
|
|
||||||
import random
|
|
||||||
from core.netns import nodes
|
|
||||||
from core import constants
|
|
||||||
from core.misc.ipaddr import MacAddr
|
|
||||||
from xml.dom.minidom import parse
|
|
||||||
from xmlutils import *
|
|
||||||
|
|
||||||
class CoreDocumentParser1(object):
|
|
||||||
|
|
||||||
layer2_device_types = 'hub', 'switch'
|
|
||||||
layer3_device_types = 'host', 'router'
|
|
||||||
device_types = layer2_device_types + layer3_device_types
|
|
||||||
|
|
||||||
# TODO: support CORE interface classes:
|
|
||||||
# RJ45Node
|
|
||||||
# TunnelNode
|
|
||||||
|
|
||||||
def __init__(self, session, filename, options):
|
|
||||||
self.session = session
|
|
||||||
self.verbose = self.session.getcfgitembool('verbose', False)
|
|
||||||
self.filename = filename
|
|
||||||
if 'dom' in options:
|
|
||||||
# this prevents parsing twice when detecting file versions
|
|
||||||
self.dom = options['dom']
|
|
||||||
else:
|
|
||||||
self.dom = parse(filename)
|
|
||||||
self.start = options['start']
|
|
||||||
self.nodecls = options['nodecls']
|
|
||||||
self.scenario = self.get_scenario(self.dom)
|
|
||||||
self.location_refgeo_set = False
|
|
||||||
self.location_refxyz_set = False
|
|
||||||
# saved link parameters saved when parsing networks and applied later
|
|
||||||
self.link_params = {}
|
|
||||||
# map from id-string to objid, for files having node names but
|
|
||||||
# not node numbers
|
|
||||||
self.objidmap = {}
|
|
||||||
self.objids = set()
|
|
||||||
self.default_services = {}
|
|
||||||
if self.scenario:
|
|
||||||
self.parse_scenario()
|
|
||||||
|
|
||||||
def info(self, msg):
|
|
||||||
s = 'XML parsing \'%s\': %s' % (self.filename, msg)
|
|
||||||
if self.session:
|
|
||||||
self.session.info(s)
|
|
||||||
else:
|
|
||||||
sys.stdout.write(s + '\n')
|
|
||||||
|
|
||||||
def warn(self, msg):
|
|
||||||
s = 'WARNING XML parsing \'%s\': %s' % (self.filename, msg)
|
|
||||||
if self.session:
|
|
||||||
self.session.warn(s)
|
|
||||||
else:
|
|
||||||
sys.stderr.write(s + '\n')
|
|
||||||
|
|
||||||
@staticmethod
|
|
||||||
def get_scenario(dom):
|
|
||||||
scenario = getFirstChildByTagName(dom, 'scenario')
|
|
||||||
if not scenario:
|
|
||||||
raise ValueError, 'no scenario element found'
|
|
||||||
version = scenario.getAttribute('version')
|
|
||||||
if version and version != '1.0':
|
|
||||||
raise ValueError, \
|
|
||||||
'unsupported scenario version found: \'%s\'' % version
|
|
||||||
return scenario
|
|
||||||
|
|
||||||
def parse_scenario(self):
|
|
||||||
self.parse_default_services()
|
|
||||||
self.parse_session_config()
|
|
||||||
self.parse_network_plan()
|
|
||||||
|
|
||||||
def assign_id(self, idstr, idval):
|
|
||||||
if idstr in self.objidmap:
|
|
||||||
assert self.objidmap[idstr] == idval and idval in self.objids
|
|
||||||
return
|
|
||||||
self.objidmap[idstr] = idval
|
|
||||||
self.objids.add(idval)
|
|
||||||
|
|
||||||
def rand_id(self):
|
|
||||||
while True:
|
|
||||||
x = random.randint(0, 0xffff)
|
|
||||||
if x not in self.objids:
|
|
||||||
return x
|
|
||||||
|
|
||||||
def get_id(self, idstr):
|
|
||||||
'''\
|
|
||||||
Get a, possibly new, object id (node number) corresponding to
|
|
||||||
the given XML string id.
|
|
||||||
'''
|
|
||||||
if not idstr:
|
|
||||||
idn = self.rand_id()
|
|
||||||
self.objids.add(idn)
|
|
||||||
return idn
|
|
||||||
elif idstr in self.objidmap:
|
|
||||||
return self.objidmap[idstr]
|
|
||||||
else:
|
|
||||||
try:
|
|
||||||
idn = int(idstr)
|
|
||||||
except ValueError:
|
|
||||||
idn = self.rand_id()
|
|
||||||
self.assign_id(idstr, idn)
|
|
||||||
return idn
|
|
||||||
|
|
||||||
def get_common_attributes(self, node):
|
|
||||||
'''\
|
|
||||||
Return id, name attributes for the given XML element. These
|
|
||||||
attributes are common to nodes and networks.
|
|
||||||
'''
|
|
||||||
idstr = node.getAttribute('id')
|
|
||||||
# use an explicit set COREID if it exists
|
|
||||||
coreid = self.find_core_id(node)
|
|
||||||
if coreid:
|
|
||||||
idn = int(coreid)
|
|
||||||
if idstr:
|
|
||||||
self.assign_id(idstr, idn)
|
|
||||||
else:
|
|
||||||
idn = self.get_id(idstr)
|
|
||||||
# TODO: consider supporting unicode; for now convert to an
|
|
||||||
# ascii string
|
|
||||||
namestr = str(node.getAttribute('name'))
|
|
||||||
return idn, namestr
|
|
||||||
|
|
||||||
def iter_network_member_devices(self, element):
|
|
||||||
# element can be a network or a channel
|
|
||||||
for interface in iterChildrenWithAttribute(element, 'member',
|
|
||||||
'type', 'interface'):
|
|
||||||
if_id = getChildTextTrim(interface)
|
|
||||||
assert if_id # XXX for testing
|
|
||||||
if not if_id:
|
|
||||||
continue
|
|
||||||
device, if_name = self.find_device_with_interface(if_id)
|
|
||||||
assert device, 'no device for if_id: %s' % if_id # XXX for testing
|
|
||||||
if device:
|
|
||||||
yield device, if_name
|
|
||||||
|
|
||||||
def network_class(self, network, network_type):
|
|
||||||
'''\
|
|
||||||
Return the corresponding CORE network class for the given
|
|
||||||
network/network_type.
|
|
||||||
'''
|
|
||||||
if network_type == 'ethernet':
|
|
||||||
return nodes.PtpNet
|
|
||||||
elif network_type == 'satcom':
|
|
||||||
return nodes.PtpNet
|
|
||||||
elif network_type == 'wireless':
|
|
||||||
channel = getFirstChildByTagName(network, 'channel')
|
|
||||||
if channel:
|
|
||||||
# use an explicit CORE type if it exists
|
|
||||||
coretype = getFirstChildTextTrimWithAttribute(channel, 'type',
|
|
||||||
'domain', 'CORE')
|
|
||||||
if coretype:
|
|
||||||
if coretype == 'basic_range':
|
|
||||||
return nodes.WlanNode
|
|
||||||
elif coretype.startswith('emane'):
|
|
||||||
return nodes.EmaneNode
|
|
||||||
else:
|
|
||||||
self.warn('unknown network type: \'%s\'' % coretype)
|
|
||||||
return xmltypetonodeclass(self.session, coretype)
|
|
||||||
return nodes.WlanNode
|
|
||||||
self.warn('unknown network type: \'%s\'' % network_type)
|
|
||||||
return None
|
|
||||||
|
|
||||||
def create_core_object(self, objcls, objid, objname, element, node_type):
|
|
||||||
obj = self.session.addobj(cls = objcls, objid = objid,
|
|
||||||
name = objname, start = self.start)
|
|
||||||
if self.verbose:
|
|
||||||
self.info('added object objid=%s name=%s cls=%s' % \
|
|
||||||
(objid, objname, objcls))
|
|
||||||
self.set_object_position(obj, element)
|
|
||||||
self.set_object_presentation(obj, element, node_type)
|
|
||||||
return obj
|
|
||||||
|
|
||||||
def get_core_object(self, idstr):
|
|
||||||
if idstr and idstr in self.objidmap:
|
|
||||||
objid = self.objidmap[idstr]
|
|
||||||
return self.session.obj(objid)
|
|
||||||
return None
|
|
||||||
|
|
||||||
def parse_network_plan(self):
|
|
||||||
# parse the scenario in the following order:
|
|
||||||
# 1. layer-2 devices
|
|
||||||
# 2. other networks (ptp/wlan)
|
|
||||||
# 3. layer-3 devices
|
|
||||||
self.parse_layer2_devices()
|
|
||||||
self.parse_networks()
|
|
||||||
self.parse_layer3_devices()
|
|
||||||
|
|
||||||
def set_ethernet_link_parameters(self, channel, link_params,
|
|
||||||
mobility_model_name, mobility_params):
|
|
||||||
# save link parameters for later use, indexed by the tuple
|
|
||||||
# (device_id, interface_name)
|
|
||||||
for dev, if_name in self.iter_network_member_devices(channel):
|
|
||||||
if self.device_type(dev) in self.device_types:
|
|
||||||
dev_id = dev.getAttribute('id')
|
|
||||||
key = (dev_id, if_name)
|
|
||||||
self.link_params[key] = link_params
|
|
||||||
if mobility_model_name or mobility_params:
|
|
||||||
raise NotImplementedError
|
|
||||||
|
|
||||||
def set_wireless_link_parameters(self, channel, link_params,
|
|
||||||
mobility_model_name, mobility_params):
|
|
||||||
network = self.find_channel_network(channel)
|
|
||||||
network_id = network.getAttribute('id')
|
|
||||||
if network_id in self.objidmap:
|
|
||||||
nodenum = self.objidmap[network_id]
|
|
||||||
else:
|
|
||||||
self.warn('unknown network: %s' % network.toxml('utf-8'))
|
|
||||||
assert False # XXX for testing
|
|
||||||
return
|
|
||||||
model_name = getFirstChildTextTrimWithAttribute(channel, 'type',
|
|
||||||
'domain', 'CORE')
|
|
||||||
if not model_name:
|
|
||||||
model_name = 'basic_range'
|
|
||||||
if model_name == 'basic_range':
|
|
||||||
mgr = self.session.mobility
|
|
||||||
elif model_name.startswith('emane'):
|
|
||||||
mgr = self.session.emane
|
|
||||||
elif model_name.startswith('xen'):
|
|
||||||
mgr = self.session.xen
|
|
||||||
else:
|
|
||||||
# TODO: any other config managers?
|
|
||||||
raise NotImplementedError
|
|
||||||
mgr.setconfig_keyvalues(nodenum, model_name, link_params.items())
|
|
||||||
if mobility_model_name and mobility_params:
|
|
||||||
mgr.setconfig_keyvalues(nodenum, mobility_model_name,
|
|
||||||
mobility_params.items())
|
|
||||||
|
|
||||||
def link_layer2_devices(self, device1, ifname1, device2, ifname2):
|
|
||||||
'''\
|
|
||||||
Link two layer-2 devices together.
|
|
||||||
'''
|
|
||||||
devid1 = device1.getAttribute('id')
|
|
||||||
dev1 = self.get_core_object(devid1)
|
|
||||||
devid2 = device2.getAttribute('id')
|
|
||||||
dev2 = self.get_core_object(devid2)
|
|
||||||
assert dev1 and dev2 # XXX for testing
|
|
||||||
if dev1 and dev2:
|
|
||||||
# TODO: review this
|
|
||||||
if isinstance(dev2, nodes.RJ45Node):
|
|
||||||
# RJ45 nodes have different linknet()
|
|
||||||
netif = dev2.linknet(dev1)
|
|
||||||
else:
|
|
||||||
netif = dev1.linknet(dev2)
|
|
||||||
self.set_wired_link_parameters(dev1, netif, devid1, ifname1)
|
|
||||||
|
|
||||||
@classmethod
|
|
||||||
def parse_xml_value(cls, valtext):
|
|
||||||
if not valtext:
|
|
||||||
return None
|
|
||||||
try:
|
|
||||||
if not valtext.translate(None, '0123456789'):
|
|
||||||
val = int(valtext)
|
|
||||||
else:
|
|
||||||
val = float(valtext)
|
|
||||||
except ValueError:
|
|
||||||
val = str(valtext)
|
|
||||||
return val
|
|
||||||
|
|
||||||
@classmethod
|
|
||||||
def parse_parameter_children(cls, parent):
|
|
||||||
params = {}
|
|
||||||
for parameter in iterChildrenWithName(parent, 'parameter'):
|
|
||||||
param_name = parameter.getAttribute('name')
|
|
||||||
assert param_name # XXX for testing
|
|
||||||
if not param_name:
|
|
||||||
continue
|
|
||||||
# TODO: consider supporting unicode; for now convert
|
|
||||||
# to an ascii string
|
|
||||||
param_name = str(param_name)
|
|
||||||
param_val = cls.parse_xml_value(getChildTextTrim(parameter))
|
|
||||||
# TODO: check if the name already exists?
|
|
||||||
if param_name and param_val:
|
|
||||||
params[param_name] = param_val
|
|
||||||
return params
|
|
||||||
|
|
||||||
def parse_network_channel(self, channel):
|
|
||||||
element = self.search_for_element(channel, 'type',
|
|
||||||
lambda x: not x.hasAttributes())
|
|
||||||
channel_type = getChildTextTrim(element)
|
|
||||||
link_params = self.parse_parameter_children(channel)
|
|
||||||
|
|
||||||
mobility = getFirstChildByTagName(channel, 'CORE:mobility')
|
|
||||||
if mobility:
|
|
||||||
mobility_model_name = \
|
|
||||||
getFirstChildTextTrimByTagName(mobility, 'type')
|
|
||||||
mobility_params = self.parse_parameter_children(mobility)
|
|
||||||
else:
|
|
||||||
mobility_model_name = None
|
|
||||||
mobility_params = None
|
|
||||||
if channel_type == 'wireless':
|
|
||||||
self.set_wireless_link_parameters(channel, link_params,
|
|
||||||
mobility_model_name,
|
|
||||||
mobility_params)
|
|
||||||
elif channel_type == 'ethernet':
|
|
||||||
# TODO: maybe this can be done in the loop below to avoid
|
|
||||||
# iterating through channel members multiple times
|
|
||||||
self.set_ethernet_link_parameters(channel, link_params,
|
|
||||||
mobility_model_name,
|
|
||||||
mobility_params)
|
|
||||||
else:
|
|
||||||
raise NotImplementedError
|
|
||||||
layer2_device = []
|
|
||||||
for dev, if_name in self.iter_network_member_devices(channel):
|
|
||||||
if self.device_type(dev) in self.layer2_device_types:
|
|
||||||
layer2_device.append((dev, if_name))
|
|
||||||
assert len(layer2_device) <= 2
|
|
||||||
if len(layer2_device) == 2:
|
|
||||||
self.link_layer2_devices(layer2_device[0][0], layer2_device[0][1],
|
|
||||||
layer2_device[1][0], layer2_device[1][1])
|
|
||||||
|
|
||||||
def parse_network(self, network):
|
|
||||||
'''\
|
|
||||||
Each network element should have an 'id' and 'name' attribute
|
|
||||||
and include the following child elements:
|
|
||||||
|
|
||||||
type (one)
|
|
||||||
member (zero or more with type="interface" or type="channel")
|
|
||||||
channel (zero or more)
|
|
||||||
'''
|
|
||||||
layer2_members = set()
|
|
||||||
layer3_members = 0
|
|
||||||
for dev, if_name in self.iter_network_member_devices(network):
|
|
||||||
if not dev:
|
|
||||||
continue
|
|
||||||
devtype = self.device_type(dev)
|
|
||||||
if devtype in self.layer2_device_types:
|
|
||||||
layer2_members.add(dev)
|
|
||||||
elif devtype in self.layer3_device_types:
|
|
||||||
layer3_members += 1
|
|
||||||
else:
|
|
||||||
raise NotImplementedError
|
|
||||||
if len(layer2_members) == 0:
|
|
||||||
net_type = getFirstChildTextTrimByTagName(network, 'type')
|
|
||||||
if not net_type:
|
|
||||||
msg = 'no network type found for network: \'%s\'' % \
|
|
||||||
network.toxml('utf-8')
|
|
||||||
self.warn(msg)
|
|
||||||
assert False # XXX for testing
|
|
||||||
return
|
|
||||||
net_cls = self.network_class(network, net_type)
|
|
||||||
objid, net_name = self.get_common_attributes(network)
|
|
||||||
if self.verbose:
|
|
||||||
self.info('parsing network: %s %s' % (net_name, objid))
|
|
||||||
if objid in self.session._objs:
|
|
||||||
return
|
|
||||||
n = self.create_core_object(net_cls, objid, net_name,
|
|
||||||
network, None)
|
|
||||||
# handle channel parameters
|
|
||||||
for channel in iterChildrenWithName(network, 'channel'):
|
|
||||||
self.parse_network_channel(channel)
|
|
||||||
|
|
||||||
def parse_networks(self):
|
|
||||||
'''\
|
|
||||||
Parse all 'network' elements.
|
|
||||||
'''
|
|
||||||
for network in iterDescendantsWithName(self.scenario, 'network'):
|
|
||||||
self.parse_network(network)
|
|
||||||
|
|
||||||
def parse_addresses(self, interface):
|
|
||||||
mac = []
|
|
||||||
ipv4 = []
|
|
||||||
ipv6= []
|
|
||||||
hostname = []
|
|
||||||
for address in iterChildrenWithName(interface, 'address'):
|
|
||||||
addr_type = address.getAttribute('type')
|
|
||||||
if not addr_type:
|
|
||||||
msg = 'no type attribute found for address ' \
|
|
||||||
'in interface: \'%s\'' % interface.toxml('utf-8')
|
|
||||||
self.warn(msg)
|
|
||||||
assert False # XXX for testing
|
|
||||||
continue
|
|
||||||
addr_text = getChildTextTrim(address)
|
|
||||||
if not addr_text:
|
|
||||||
msg = 'no text found for address ' \
|
|
||||||
'in interface: \'%s\'' % interface.toxml('utf-8')
|
|
||||||
self.warn(msg)
|
|
||||||
assert False # XXX for testing
|
|
||||||
continue
|
|
||||||
if addr_type == 'mac':
|
|
||||||
mac.append(addr_text)
|
|
||||||
elif addr_type == 'IPv4':
|
|
||||||
ipv4.append(addr_text)
|
|
||||||
elif addr_type == 'IPv6':
|
|
||||||
ipv6.append(addr_text)
|
|
||||||
elif addr_type == 'hostname':
|
|
||||||
hostname.append(addr_text)
|
|
||||||
else:
|
|
||||||
msg = 'skipping unknown address type \'%s\' in ' \
|
|
||||||
'interface: \'%s\'' % (addr_type, interface.toxml('utf-8'))
|
|
||||||
self.warn(msg)
|
|
||||||
assert False # XXX for testing
|
|
||||||
continue
|
|
||||||
return mac, ipv4, ipv6, hostname
|
|
||||||
|
|
||||||
def parse_interface(self, node, device_id, interface):
|
|
||||||
'''\
|
|
||||||
Each interface can have multiple 'address' elements.
|
|
||||||
'''
|
|
||||||
if_name = interface.getAttribute('name')
|
|
||||||
network = self.find_interface_network_object(interface)
|
|
||||||
if not network:
|
|
||||||
msg = 'skipping node \'%s\' interface \'%s\': ' \
|
|
||||||
'unknown network' % (node.name, if_name)
|
|
||||||
self.warn(msg)
|
|
||||||
assert False # XXX for testing
|
|
||||||
return
|
|
||||||
mac, ipv4, ipv6, hostname = self.parse_addresses(interface)
|
|
||||||
if mac:
|
|
||||||
hwaddr = MacAddr.fromstring(mac[0])
|
|
||||||
else:
|
|
||||||
hwaddr = None
|
|
||||||
ifindex = node.newnetif(network, addrlist = ipv4 + ipv6,
|
|
||||||
hwaddr = hwaddr, ifindex = None,
|
|
||||||
ifname = if_name)
|
|
||||||
# TODO: 'hostname' addresses are unused
|
|
||||||
if self.verbose:
|
|
||||||
msg = 'node \'%s\' interface \'%s\' connected ' \
|
|
||||||
'to network \'%s\'' % (node.name, if_name, network.name)
|
|
||||||
self.info(msg)
|
|
||||||
# set link parameters for wired links
|
|
||||||
if isinstance(network,
|
|
||||||
(nodes.HubNode, nodes.PtpNet, nodes.SwitchNode)):
|
|
||||||
netif = node.netif(ifindex)
|
|
||||||
self.set_wired_link_parameters(network, netif, device_id)
|
|
||||||
|
|
||||||
def set_wired_link_parameters(self, network, netif,
|
|
||||||
device_id, netif_name = None):
|
|
||||||
if netif_name is None:
|
|
||||||
netif_name = netif.name
|
|
||||||
key = (device_id, netif_name)
|
|
||||||
if key in self.link_params:
|
|
||||||
link_params = self.link_params[key]
|
|
||||||
if self.start:
|
|
||||||
bw = link_params.get('bw')
|
|
||||||
delay = link_params.get('delay')
|
|
||||||
loss = link_params.get('loss')
|
|
||||||
duplicate = link_params.get('duplicate')
|
|
||||||
jitter = link_params.get('jitter')
|
|
||||||
network.linkconfig(netif, bw = bw, delay = delay, loss = loss,
|
|
||||||
duplicate = duplicate, jitter = jitter)
|
|
||||||
else:
|
|
||||||
for k, v in link_params.iteritems():
|
|
||||||
netif.setparam(k, v)
|
|
||||||
|
|
||||||
@staticmethod
|
|
||||||
def search_for_element(node, tagName, match = None):
|
|
||||||
'''\
|
|
||||||
Search the given node and all ancestors for an element named
|
|
||||||
tagName that satisfies the given matching function.
|
|
||||||
'''
|
|
||||||
while True:
|
|
||||||
for child in iterChildren(node, Node.ELEMENT_NODE):
|
|
||||||
if child.tagName == tagName and \
|
|
||||||
(match is None or match(child)):
|
|
||||||
return child
|
|
||||||
node = node.parentNode
|
|
||||||
if not node:
|
|
||||||
break
|
|
||||||
return None
|
|
||||||
|
|
||||||
@classmethod
|
|
||||||
def find_core_id(cls, node):
|
|
||||||
def match(x):
|
|
||||||
domain = x.getAttribute('domain')
|
|
||||||
return domain == 'COREID'
|
|
||||||
alias = cls.search_for_element(node, 'alias', match)
|
|
||||||
if alias:
|
|
||||||
return getChildTextTrim(alias)
|
|
||||||
return None
|
|
||||||
|
|
||||||
@classmethod
|
|
||||||
def find_point(cls, node):
|
|
||||||
return cls.search_for_element(node, 'point')
|
|
||||||
|
|
||||||
@staticmethod
|
|
||||||
def find_channel_network(channel):
|
|
||||||
p = channel.parentNode
|
|
||||||
if p and p.tagName == 'network':
|
|
||||||
return p
|
|
||||||
return None
|
|
||||||
|
|
||||||
def find_interface_network_object(self, interface):
|
|
||||||
network_id = getFirstChildTextTrimWithAttribute(interface, 'member',
|
|
||||||
'type', 'network')
|
|
||||||
if not network_id:
|
|
||||||
# support legacy notation: <interface net="netid" ...
|
|
||||||
network_id = interface.getAttribute('net')
|
|
||||||
obj = self.get_core_object(network_id)
|
|
||||||
if obj:
|
|
||||||
# the network_id should exist for ptp or wlan/emane networks
|
|
||||||
return obj
|
|
||||||
# the network should correspond to a layer-2 device if the
|
|
||||||
# network_id does not exist
|
|
||||||
channel_id = getFirstChildTextTrimWithAttribute(interface, 'member',
|
|
||||||
'type', 'channel')
|
|
||||||
if not network_id or not channel_id:
|
|
||||||
return None
|
|
||||||
network = getFirstChildWithAttribute(self.scenario, 'network',
|
|
||||||
'id', network_id)
|
|
||||||
if not network:
|
|
||||||
return None
|
|
||||||
channel = getFirstChildWithAttribute(network, 'channel',
|
|
||||||
'id', channel_id)
|
|
||||||
if not channel:
|
|
||||||
return None
|
|
||||||
device = None
|
|
||||||
for dev, if_name in self.iter_network_member_devices(channel):
|
|
||||||
if self.device_type(dev) in self.layer2_device_types:
|
|
||||||
assert not device # XXX
|
|
||||||
device = dev
|
|
||||||
if device:
|
|
||||||
obj = self.get_core_object(device.getAttribute('id'))
|
|
||||||
if obj:
|
|
||||||
return obj
|
|
||||||
return None
|
|
||||||
|
|
||||||
def set_object_position_pixel(self, obj, point):
|
|
||||||
x = float(point.getAttribute('x'))
|
|
||||||
y = float(point.getAttribute('y'))
|
|
||||||
z = point.getAttribute('z')
|
|
||||||
if z:
|
|
||||||
z = float(z)
|
|
||||||
else:
|
|
||||||
z = 0.0
|
|
||||||
# TODO: zMode is unused
|
|
||||||
# z_mode = point.getAttribute('zMode'))
|
|
||||||
if x < 0.0:
|
|
||||||
self.warn('limiting negative x position of \'%s\' to zero: %s' %
|
|
||||||
(obj.name, x))
|
|
||||||
x = 0.0
|
|
||||||
if y < 0.0:
|
|
||||||
self.warn('limiting negative y position of \'%s\' to zero: %s' %
|
|
||||||
(obj.name, y))
|
|
||||||
y = 0.0
|
|
||||||
obj.setposition(x, y, z)
|
|
||||||
|
|
||||||
def set_object_position_gps(self, obj, point):
|
|
||||||
lat = float(point.getAttribute('lat'))
|
|
||||||
lon = float(point.getAttribute('lon'))
|
|
||||||
zalt = point.getAttribute('z')
|
|
||||||
if zalt:
|
|
||||||
zalt = float(zalt)
|
|
||||||
else:
|
|
||||||
zalt = 0.0
|
|
||||||
# TODO: zMode is unused
|
|
||||||
# z_mode = point.getAttribute('zMode'))
|
|
||||||
if not self.location_refgeo_set:
|
|
||||||
# for x,y,z conversion, we need a reasonable refpt; this
|
|
||||||
# picks the first coordinates as the origin
|
|
||||||
self.session.location.setrefgeo(lat, lon, zalt)
|
|
||||||
self.location_refgeo_set = True
|
|
||||||
x, y, z = self.session.location.getxyz(lat, lon, zalt)
|
|
||||||
if x < 0.0:
|
|
||||||
self.warn('limiting negative x position of \'%s\' to zero: %s' %
|
|
||||||
(obj.name, x))
|
|
||||||
x = 0.0
|
|
||||||
if y < 0.0:
|
|
||||||
self.warn('limiting negative y position of \'%s\' to zero: %s' %
|
|
||||||
(obj.name, y))
|
|
||||||
y = 0.0
|
|
||||||
obj.setposition(x, y, z)
|
|
||||||
|
|
||||||
def set_object_position_cartesian(self, obj, point):
|
|
||||||
# TODO: review this
|
|
||||||
xm = float(point.getAttribute('x'))
|
|
||||||
ym = float(point.getAttribute('y'))
|
|
||||||
zm = point.getAttribute('z')
|
|
||||||
if zm:
|
|
||||||
zm = float(zm)
|
|
||||||
else:
|
|
||||||
zm = 0.0
|
|
||||||
# TODO: zMode is unused
|
|
||||||
# z_mode = point.getAttribute('zMode'))
|
|
||||||
if not self.location_refxyz_set:
|
|
||||||
self.session.location.refxyz = xm, ym, zm
|
|
||||||
self.location_refxyz_set = True
|
|
||||||
# need to convert meters to pixels
|
|
||||||
x = self.session.location.m2px(xm) + self.session.location.refxyz[0]
|
|
||||||
y = self.session.location.m2px(ym) + self.session.location.refxyz[1]
|
|
||||||
z = self.session.location.m2px(zm) + self.session.location.refxyz[2]
|
|
||||||
if x < 0.0:
|
|
||||||
self.warn('limiting negative x position of \'%s\' to zero: %s' %
|
|
||||||
(obj.name, x))
|
|
||||||
x = 0.0
|
|
||||||
if y < 0.0:
|
|
||||||
self.warn('limiting negative y position of \'%s\' to zero: %s' %
|
|
||||||
(obj.name, y))
|
|
||||||
y = 0.0
|
|
||||||
obj.setposition(x, y, z)
|
|
||||||
|
|
||||||
def set_object_position(self, obj, element):
|
|
||||||
'''\
|
|
||||||
Set the x,y,x position of obj from the point associated with
|
|
||||||
the given element.
|
|
||||||
'''
|
|
||||||
point = self.find_point(element)
|
|
||||||
if not point:
|
|
||||||
return False
|
|
||||||
point_type = point.getAttribute('type')
|
|
||||||
if not point_type:
|
|
||||||
msg = 'no type attribute found for point: \'%s\'' % \
|
|
||||||
point.toxml('utf-8')
|
|
||||||
self.warn(msg)
|
|
||||||
assert False # XXX for testing
|
|
||||||
return False
|
|
||||||
elif point_type == 'pixel':
|
|
||||||
self.set_object_position_pixel(obj, point)
|
|
||||||
elif point_type == 'gps':
|
|
||||||
self.set_object_position_gps(obj, point)
|
|
||||||
elif point_type == 'cart':
|
|
||||||
self.set_object_position_cartesian(obj, point)
|
|
||||||
else:
|
|
||||||
self.warn("skipping unknown point type: '%s'" % point_type)
|
|
||||||
assert False # XXX for testing
|
|
||||||
return False
|
|
||||||
if self.verbose:
|
|
||||||
msg = 'set position of %s from point element: \'%s\'' % \
|
|
||||||
(obj.name, point.toxml('utf-8'))
|
|
||||||
self.info(msg)
|
|
||||||
return True
|
|
||||||
|
|
||||||
def parse_device_service(self, service, node):
|
|
||||||
name = service.getAttribute('name')
|
|
||||||
session_service = self.session.services.getservicebyname(name)
|
|
||||||
if not session_service:
|
|
||||||
assert False # XXX for testing
|
|
||||||
return None
|
|
||||||
values = []
|
|
||||||
startup_idx = service.getAttribute('startup_idx')
|
|
||||||
if startup_idx:
|
|
||||||
values.append('startidx=%s' % startup_idx)
|
|
||||||
startup_time = service.getAttribute('start_time')
|
|
||||||
if startup_time:
|
|
||||||
values.append('starttime=%s' % startup_time)
|
|
||||||
dirs = []
|
|
||||||
for directory in iterChildrenWithName(service, 'directory'):
|
|
||||||
dirname = directory.getAttribute('name')
|
|
||||||
dirs.append(str(dirname))
|
|
||||||
if dirs:
|
|
||||||
values.append("dirs=%s" % dirs)
|
|
||||||
startup = []
|
|
||||||
shutdown = []
|
|
||||||
validate = []
|
|
||||||
for command in iterChildrenWithName(service, 'command'):
|
|
||||||
command_type = command.getAttribute('type')
|
|
||||||
command_text = getChildTextTrim(command)
|
|
||||||
if not command_text:
|
|
||||||
continue
|
|
||||||
if command_type == 'start':
|
|
||||||
startup.append(str(command_text))
|
|
||||||
elif command_type == 'stop':
|
|
||||||
shutdown.append(str(command_text))
|
|
||||||
elif command_type == 'validate':
|
|
||||||
validate.append(str(command_text))
|
|
||||||
if startup:
|
|
||||||
values.append('cmdup=%s' % startup)
|
|
||||||
if shutdown:
|
|
||||||
values.append('cmddown=%s' % shutdown)
|
|
||||||
if validate:
|
|
||||||
values.append('cmdval=%s' % validate)
|
|
||||||
filenames = []
|
|
||||||
files = []
|
|
||||||
for f in iterChildrenWithName(service, 'file'):
|
|
||||||
filename = f.getAttribute('name')
|
|
||||||
if not filename:
|
|
||||||
continue;
|
|
||||||
filenames.append(filename)
|
|
||||||
data = getChildTextTrim(f)
|
|
||||||
if data:
|
|
||||||
data = str(data)
|
|
||||||
else:
|
|
||||||
data = None
|
|
||||||
typestr = 'service:%s:%s' % (name, filename)
|
|
||||||
files.append((typestr, filename, data))
|
|
||||||
if filenames:
|
|
||||||
values.append('files=%s' % filenames)
|
|
||||||
custom = service.getAttribute('custom')
|
|
||||||
if custom and custom.lower() == 'true':
|
|
||||||
self.session.services.setcustomservice(node.objid,
|
|
||||||
session_service, values)
|
|
||||||
# NOTE: if a custom service is used, setservicefile() must be
|
|
||||||
# called after the custom service exists
|
|
||||||
for typestr, filename, data in files:
|
|
||||||
self.session.services.setservicefile(nodenum = node.objid,
|
|
||||||
type = typestr,
|
|
||||||
filename = filename,
|
|
||||||
srcname = None,
|
|
||||||
data = data)
|
|
||||||
return str(name)
|
|
||||||
|
|
||||||
def parse_device_services(self, services, node):
|
|
||||||
'''\
|
|
||||||
Use session.services manager to store service customizations
|
|
||||||
before they are added to a node.
|
|
||||||
'''
|
|
||||||
service_names = []
|
|
||||||
for service in iterChildrenWithName(services, 'service'):
|
|
||||||
name = self.parse_device_service(service, node)
|
|
||||||
if name:
|
|
||||||
service_names.append(name)
|
|
||||||
return '|'.join(service_names)
|
|
||||||
|
|
||||||
def add_device_services(self, node, device, node_type):
|
|
||||||
'''\
|
|
||||||
Add services to the given node.
|
|
||||||
'''
|
|
||||||
services = getFirstChildByTagName(device, 'CORE:services')
|
|
||||||
if services:
|
|
||||||
services_str = self.parse_device_services(services, node)
|
|
||||||
if self.verbose:
|
|
||||||
self.info('services for node \'%s\': %s' % \
|
|
||||||
(node.name, services_str))
|
|
||||||
elif node_type in self.default_services:
|
|
||||||
services_str = None # default services will be added
|
|
||||||
else:
|
|
||||||
return
|
|
||||||
self.session.services.addservicestonode(node = node,
|
|
||||||
nodetype = node_type,
|
|
||||||
services_str = services_str,
|
|
||||||
verbose = self.verbose)
|
|
||||||
|
|
||||||
def set_object_presentation(self, obj, element, node_type):
|
|
||||||
# defaults from the CORE GUI
|
|
||||||
default_icons = {
|
|
||||||
'router': 'router.gif',
|
|
||||||
'host': 'host.gif',
|
|
||||||
'PC': 'pc.gif',
|
|
||||||
'mdr': 'mdr.gif',
|
|
||||||
# 'prouter': 'router_green.gif',
|
|
||||||
# 'xen': 'xen.gif'
|
|
||||||
}
|
|
||||||
icon_set = False
|
|
||||||
for child in iterChildrenWithName(element, 'CORE:presentation'):
|
|
||||||
canvas = child.getAttribute('canvas')
|
|
||||||
if canvas:
|
|
||||||
obj.canvas = int(canvas)
|
|
||||||
icon = child.getAttribute('icon')
|
|
||||||
if icon:
|
|
||||||
icon = str(icon).replace("$CORE_DATA_DIR",
|
|
||||||
constants.CORE_DATA_DIR)
|
|
||||||
obj.icon = icon
|
|
||||||
icon_set = True
|
|
||||||
if not icon_set and node_type in default_icons:
|
|
||||||
obj.icon = default_icons[node_type]
|
|
||||||
|
|
||||||
def device_type(self, device):
|
|
||||||
if device.tagName in self.device_types:
|
|
||||||
return device.tagName
|
|
||||||
return None
|
|
||||||
|
|
||||||
def core_node_type(self, device):
|
|
||||||
# use an explicit CORE type if it exists
|
|
||||||
coretype = getFirstChildTextTrimWithAttribute(device, 'type',
|
|
||||||
'domain', 'CORE')
|
|
||||||
if coretype:
|
|
||||||
return coretype
|
|
||||||
return self.device_type(device)
|
|
||||||
|
|
||||||
def find_device_with_interface(self, interface_id):
|
|
||||||
# TODO: suport generic 'device' elements
|
|
||||||
for device in iterDescendantsWithName(self.scenario,
|
|
||||||
self.device_types):
|
|
||||||
interface = getFirstChildWithAttribute(device, 'interface',
|
|
||||||
'id', interface_id)
|
|
||||||
if interface:
|
|
||||||
if_name = interface.getAttribute('name')
|
|
||||||
return device, if_name
|
|
||||||
return None, None
|
|
||||||
|
|
||||||
def parse_layer2_device(self, device):
|
|
||||||
objid, device_name = self.get_common_attributes(device)
|
|
||||||
if self.verbose:
|
|
||||||
self.info('parsing layer-2 device: %s %s' % (device_name, objid))
|
|
||||||
try:
|
|
||||||
return self.session.obj(objid)
|
|
||||||
except KeyError:
|
|
||||||
pass
|
|
||||||
device_type = self.device_type(device)
|
|
||||||
if device_type == 'hub':
|
|
||||||
device_class = nodes.HubNode
|
|
||||||
elif device_type == 'switch':
|
|
||||||
device_class = nodes.SwitchNode
|
|
||||||
else:
|
|
||||||
self.warn('unknown layer-2 device type: \'%s\'' % device_type)
|
|
||||||
assert False # XXX for testing
|
|
||||||
return None
|
|
||||||
n = self.create_core_object(device_class, objid, device_name,
|
|
||||||
device, None)
|
|
||||||
return n
|
|
||||||
|
|
||||||
def parse_layer3_device(self, device):
|
|
||||||
objid, device_name = self.get_common_attributes(device)
|
|
||||||
if self.verbose:
|
|
||||||
self.info('parsing layer-3 device: %s %s' % (device_name, objid))
|
|
||||||
try:
|
|
||||||
return self.session.obj(objid)
|
|
||||||
except KeyError:
|
|
||||||
pass
|
|
||||||
device_cls = self.nodecls
|
|
||||||
core_node_type = self.core_node_type(device)
|
|
||||||
n = self.create_core_object(device_cls, objid, device_name,
|
|
||||||
device, core_node_type)
|
|
||||||
n.type = core_node_type
|
|
||||||
self.add_device_services(n, device, core_node_type)
|
|
||||||
for interface in iterChildrenWithName(device, 'interface'):
|
|
||||||
self.parse_interface(n, device.getAttribute('id'), interface)
|
|
||||||
return n
|
|
||||||
|
|
||||||
def parse_layer2_devices(self):
|
|
||||||
'''\
|
|
||||||
Parse all layer-2 device elements. A device can be: 'switch',
|
|
||||||
'hub'.
|
|
||||||
'''
|
|
||||||
# TODO: suport generic 'device' elements
|
|
||||||
for device in iterDescendantsWithName(self.scenario,
|
|
||||||
self.layer2_device_types):
|
|
||||||
self.parse_layer2_device(device)
|
|
||||||
|
|
||||||
def parse_layer3_devices(self):
|
|
||||||
'''\
|
|
||||||
Parse all layer-3 device elements. A device can be: 'host',
|
|
||||||
'router'.
|
|
||||||
'''
|
|
||||||
# TODO: suport generic 'device' elements
|
|
||||||
for device in iterDescendantsWithName(self.scenario,
|
|
||||||
self.layer3_device_types):
|
|
||||||
self.parse_layer3_device(device)
|
|
||||||
|
|
||||||
def parse_session_origin(self, session_config):
|
|
||||||
'''\
|
|
||||||
Parse the first origin tag and set the CoreLocation reference
|
|
||||||
point appropriately.
|
|
||||||
'''
|
|
||||||
# defaults from the CORE GUI
|
|
||||||
self.session.location.setrefgeo(47.5791667, -122.132322, 2.0)
|
|
||||||
self.session.location.refscale = 150.0
|
|
||||||
origin = getFirstChildByTagName(session_config, 'origin')
|
|
||||||
if not origin:
|
|
||||||
return
|
|
||||||
lat = origin.getAttribute('lat')
|
|
||||||
lon = origin.getAttribute('lon')
|
|
||||||
alt = origin.getAttribute('alt')
|
|
||||||
if lat and lon and alt:
|
|
||||||
self.session.location.setrefgeo(float(lat), float(lon), float(alt))
|
|
||||||
self.location_refgeo_set = True
|
|
||||||
scale100 = origin.getAttribute("scale100")
|
|
||||||
if scale100:
|
|
||||||
self.session.location.refscale = float(scale100)
|
|
||||||
point = getFirstChildTextTrimByTagName(origin, 'point')
|
|
||||||
if point:
|
|
||||||
xyz = point.split(',')
|
|
||||||
if len(xyz) == 2:
|
|
||||||
xyz.append('0.0')
|
|
||||||
if len(xyz) == 3:
|
|
||||||
self.session.location.refxyz = \
|
|
||||||
(float(xyz[0]), float(xyz[1]), float(xyz[2]))
|
|
||||||
self.location_refxyz_set = True
|
|
||||||
|
|
||||||
def parse_session_options(self, session_config):
|
|
||||||
options = getFirstChildByTagName(session_config, 'options')
|
|
||||||
if not options:
|
|
||||||
return
|
|
||||||
params = self.parse_parameter_children(options)
|
|
||||||
for name, value in params.iteritems():
|
|
||||||
if name and value:
|
|
||||||
setattr(self.session.options, str(name), str(value))
|
|
||||||
|
|
||||||
def parse_session_hooks(self, session_config):
|
|
||||||
'''\
|
|
||||||
Parse hook scripts.
|
|
||||||
'''
|
|
||||||
hooks = getFirstChildByTagName(session_config, 'hooks')
|
|
||||||
if not hooks:
|
|
||||||
return
|
|
||||||
for hook in iterChildrenWithName(hooks, 'hook'):
|
|
||||||
filename = hook.getAttribute('name')
|
|
||||||
state = hook.getAttribute('state')
|
|
||||||
data = getChildTextTrim(hook)
|
|
||||||
if data is None:
|
|
||||||
data = '' # allow for empty file
|
|
||||||
hook_type = "hook:%s" % state
|
|
||||||
self.session.sethook(hook_type, filename = str(filename),
|
|
||||||
srcname = None, data = str(data))
|
|
||||||
|
|
||||||
def parse_session_metadata(self, session_config):
|
|
||||||
metadata = getFirstChildByTagName(session_config, 'metadata')
|
|
||||||
if not metadata:
|
|
||||||
return
|
|
||||||
params = self.parse_parameter_children(metadata)
|
|
||||||
for name, value in params.iteritems():
|
|
||||||
if name and value:
|
|
||||||
self.session.metadata.additem(str(name), str(value))
|
|
||||||
|
|
||||||
def parse_session_config(self):
|
|
||||||
session_config = \
|
|
||||||
getFirstChildByTagName(self.scenario, 'CORE:sessionconfig')
|
|
||||||
if not session_config:
|
|
||||||
return
|
|
||||||
self.parse_session_origin(session_config)
|
|
||||||
self.parse_session_options(session_config)
|
|
||||||
self.parse_session_hooks(session_config)
|
|
||||||
self.parse_session_metadata(session_config)
|
|
||||||
|
|
||||||
def parse_default_services(self):
|
|
||||||
# defaults from the CORE GUI
|
|
||||||
self.default_services = {
|
|
||||||
'router': ['zebra', 'OSPFv2', 'OSPFv3', 'IPForward'],
|
|
||||||
'host': ['DefaultRoute', 'SSH'],
|
|
||||||
'PC': ['DefaultRoute',],
|
|
||||||
'mdr': ['zebra', 'OSPFv3MDR', 'IPForward'],
|
|
||||||
# 'prouter': ['zebra', 'OSPFv2', 'OSPFv3', 'IPForward'],
|
|
||||||
# 'xen': ['zebra', 'OSPFv2', 'OSPFv3', 'IPForward'],
|
|
||||||
}
|
|
||||||
default_services = \
|
|
||||||
getFirstChildByTagName(self.scenario, 'CORE:defaultservices')
|
|
||||||
if not default_services:
|
|
||||||
return
|
|
||||||
for device in iterChildrenWithName(default_services, 'device'):
|
|
||||||
device_type = device.getAttribute('type')
|
|
||||||
if not device_type:
|
|
||||||
self.warn('parse_default_services: no type attribute ' \
|
|
||||||
'found for device')
|
|
||||||
continue
|
|
||||||
services = []
|
|
||||||
for service in iterChildrenWithName(device, 'service'):
|
|
||||||
name = service.getAttribute('name')
|
|
||||||
if name:
|
|
||||||
services.append(str(name))
|
|
||||||
self.default_services[device_type] = services
|
|
||||||
# store default services for the session
|
|
||||||
for t, s in self.default_services.iteritems():
|
|
||||||
self.session.services.defaultservices[t] = s
|
|
||||||
if self.verbose:
|
|
||||||
self.info('default services for node type \'%s\' ' \
|
|
||||||
'set to: %s' % (t, s))
|
|
||||||
|
|
@ -1,34 +0,0 @@
|
||||||
#
|
|
||||||
# CORE
|
|
||||||
# Copyright (c)2011-2014 the Boeing Company.
|
|
||||||
# See the LICENSE file included in this distribution.
|
|
||||||
#
|
|
||||||
# author: Jeff Ahrenholz <jeffrey.m.ahrenholz@boeing.com>
|
|
||||||
#
|
|
||||||
|
|
||||||
'''
|
|
||||||
Helpers for loading and saving XML files. savesessionxml(session, filename) is
|
|
||||||
the main public interface here.
|
|
||||||
'''
|
|
||||||
|
|
||||||
import os.path
|
|
||||||
from core.netns import nodes
|
|
||||||
from xmlparser import core_document_parser
|
|
||||||
from xmlwriter import core_document_writer
|
|
||||||
|
|
||||||
def opensessionxml(session, filename, start=False, nodecls=nodes.CoreNode):
|
|
||||||
''' Import a session from the EmulationScript XML format.
|
|
||||||
'''
|
|
||||||
options = {'start': start, 'nodecls': nodecls}
|
|
||||||
doc = core_document_parser(session, filename, options)
|
|
||||||
if start:
|
|
||||||
session.name = os.path.basename(filename)
|
|
||||||
session.filename = filename
|
|
||||||
session.node_count = str(session.getnodecount())
|
|
||||||
session.instantiate()
|
|
||||||
|
|
||||||
def savesessionxml(session, filename, version):
|
|
||||||
''' Export a session to the EmulationScript XML format.
|
|
||||||
'''
|
|
||||||
doc = core_document_writer(session, version)
|
|
||||||
doc.writexml(filename)
|
|
||||||
|
|
@ -1,303 +0,0 @@
|
||||||
#
|
|
||||||
# CORE
|
|
||||||
# Copyright (c)2011-2013 the Boeing Company.
|
|
||||||
# See the LICENSE file included in this distribution.
|
|
||||||
#
|
|
||||||
# author: Jeff Ahrenholz <jeffrey.m.ahrenholz@boeing.com>
|
|
||||||
#
|
|
||||||
|
|
||||||
from core.netns import nodes
|
|
||||||
from xml.dom.minidom import Node
|
|
||||||
|
|
||||||
def addelementsfromlist(dom, parent, iterable, name, attr_name):
|
|
||||||
''' XML helper to iterate through a list and add items to parent using tags
|
|
||||||
of the given name and the item value as an attribute named attr_name.
|
|
||||||
Example: addelementsfromlist(dom, parent, ('a','b','c'), "letter", "value")
|
|
||||||
<parent>
|
|
||||||
<letter value="a"/>
|
|
||||||
<letter value="b"/>
|
|
||||||
<letter value="c"/>
|
|
||||||
</parent>
|
|
||||||
'''
|
|
||||||
for item in iterable:
|
|
||||||
element = dom.createElement(name)
|
|
||||||
element.setAttribute(attr_name, item)
|
|
||||||
parent.appendChild(element)
|
|
||||||
|
|
||||||
def addtextelementsfromlist(dom, parent, iterable, name, attrs):
|
|
||||||
''' XML helper to iterate through a list and add items to parent using tags
|
|
||||||
of the given name, attributes specified in the attrs tuple, and having the
|
|
||||||
text of the item within the tags.
|
|
||||||
Example: addtextelementsfromlist(dom, parent, ('a','b','c'), "letter",
|
|
||||||
(('show','True'),))
|
|
||||||
<parent>
|
|
||||||
<letter show="True">a</letter>
|
|
||||||
<letter show="True">b</letter>
|
|
||||||
<letter show="True">c</letter>
|
|
||||||
</parent>
|
|
||||||
'''
|
|
||||||
for item in iterable:
|
|
||||||
element = dom.createElement(name)
|
|
||||||
for k,v in attrs:
|
|
||||||
element.setAttribute(k, v)
|
|
||||||
parent.appendChild(element)
|
|
||||||
txt = dom.createTextNode(item)
|
|
||||||
element.appendChild(txt)
|
|
||||||
|
|
||||||
def addtextelementsfromtuples(dom, parent, iterable, attrs=()):
|
|
||||||
''' XML helper to iterate through a list of tuples and add items to
|
|
||||||
parent using tags named for the first tuple element,
|
|
||||||
attributes specified in the attrs tuple, and having the
|
|
||||||
text of second tuple element.
|
|
||||||
Example: addtextelementsfromtuples(dom, parent,
|
|
||||||
(('first','a'),('second','b'),('third','c')),
|
|
||||||
(('show','True'),))
|
|
||||||
<parent>
|
|
||||||
<first show="True">a</first>
|
|
||||||
<second show="True">b</second>
|
|
||||||
<third show="True">c</third>
|
|
||||||
</parent>
|
|
||||||
'''
|
|
||||||
for name, value in iterable:
|
|
||||||
element = dom.createElement(name)
|
|
||||||
for k,v in attrs:
|
|
||||||
element.setAttribute(k, v)
|
|
||||||
parent.appendChild(element)
|
|
||||||
txt = dom.createTextNode(value)
|
|
||||||
element.appendChild(txt)
|
|
||||||
|
|
||||||
def gettextelementstolist(parent):
|
|
||||||
''' XML helper to parse child text nodes from the given parent and return
|
|
||||||
a list of (key, value) tuples.
|
|
||||||
'''
|
|
||||||
r = []
|
|
||||||
for n in parent.childNodes:
|
|
||||||
if n.nodeType != Node.ELEMENT_NODE:
|
|
||||||
continue
|
|
||||||
k = str(n.nodeName)
|
|
||||||
v = '' # sometimes want None here?
|
|
||||||
for c in n.childNodes:
|
|
||||||
if c.nodeType != Node.TEXT_NODE:
|
|
||||||
continue
|
|
||||||
v = str(c.nodeValue)
|
|
||||||
break
|
|
||||||
r.append((k,v))
|
|
||||||
return r
|
|
||||||
|
|
||||||
def addparamtoparent(dom, parent, name, value):
|
|
||||||
''' XML helper to add a <param name="name" value="value"/> tag to the parent
|
|
||||||
element, when value is not None.
|
|
||||||
'''
|
|
||||||
if value is None:
|
|
||||||
return None
|
|
||||||
p = dom.createElement("param")
|
|
||||||
parent.appendChild(p)
|
|
||||||
p.setAttribute("name", name)
|
|
||||||
p.setAttribute("value", "%s" % value)
|
|
||||||
return p
|
|
||||||
|
|
||||||
def addtextparamtoparent(dom, parent, name, value):
|
|
||||||
''' XML helper to add a <param name="name">value</param> tag to the parent
|
|
||||||
element, when value is not None.
|
|
||||||
'''
|
|
||||||
if value is None:
|
|
||||||
return None
|
|
||||||
p = dom.createElement("param")
|
|
||||||
parent.appendChild(p)
|
|
||||||
p.setAttribute("name", name)
|
|
||||||
txt = dom.createTextNode(value)
|
|
||||||
p.appendChild(txt)
|
|
||||||
return p
|
|
||||||
|
|
||||||
def addparamlisttoparent(dom, parent, name, values):
|
|
||||||
''' XML helper to return a parameter list and optionally add it to the
|
|
||||||
parent element:
|
|
||||||
<paramlist name="name">
|
|
||||||
<item value="123">
|
|
||||||
<item value="456">
|
|
||||||
</paramlist>
|
|
||||||
'''
|
|
||||||
if values is None:
|
|
||||||
return None
|
|
||||||
p = dom.createElement("paramlist")
|
|
||||||
if parent:
|
|
||||||
parent.appendChild(p)
|
|
||||||
p.setAttribute("name", name)
|
|
||||||
for v in values:
|
|
||||||
item = dom.createElement("item")
|
|
||||||
item.setAttribute("value", str(v))
|
|
||||||
p.appendChild(item)
|
|
||||||
return p
|
|
||||||
|
|
||||||
def getoneelement(dom, name):
|
|
||||||
e = dom.getElementsByTagName(name)
|
|
||||||
if len(e) == 0:
|
|
||||||
return None
|
|
||||||
return e[0]
|
|
||||||
|
|
||||||
def iterDescendants(dom, max_depth = 0):
|
|
||||||
'''\
|
|
||||||
Iterate over all descendant element nodes in breadth first order.
|
|
||||||
Only consider nodes up to max_depth deep when max_depth is greater
|
|
||||||
than zero.
|
|
||||||
'''
|
|
||||||
nodes = [dom]
|
|
||||||
depth = 0
|
|
||||||
current_depth_nodes = 1
|
|
||||||
next_depth_nodes = 0
|
|
||||||
while nodes:
|
|
||||||
n = nodes.pop(0)
|
|
||||||
for child in n.childNodes:
|
|
||||||
if child.nodeType == Node.ELEMENT_NODE:
|
|
||||||
yield child
|
|
||||||
nodes.append(child)
|
|
||||||
next_depth_nodes += 1
|
|
||||||
current_depth_nodes -= 1
|
|
||||||
if current_depth_nodes == 0:
|
|
||||||
depth += 1
|
|
||||||
if max_depth > 0 and depth == max_depth:
|
|
||||||
return
|
|
||||||
current_depth_nodes = next_depth_nodes
|
|
||||||
next_depth_nodes = 0
|
|
||||||
|
|
||||||
def iterMatchingDescendants(dom, matchFunction, max_depth = 0):
|
|
||||||
'''\
|
|
||||||
Iterate over descendant elements where matchFunction(descendant)
|
|
||||||
returns true. Only consider nodes up to max_depth deep when
|
|
||||||
max_depth is greater than zero.
|
|
||||||
'''
|
|
||||||
for d in iterDescendants(dom, max_depth):
|
|
||||||
if matchFunction(d):
|
|
||||||
yield d
|
|
||||||
|
|
||||||
def iterDescendantsWithName(dom, tagName, max_depth = 0):
|
|
||||||
'''\
|
|
||||||
Iterate over descendant elements whose name is contained in
|
|
||||||
tagName (or is named tagName if tagName is a string). Only
|
|
||||||
consider nodes up to max_depth deep when max_depth is greater than
|
|
||||||
zero.
|
|
||||||
'''
|
|
||||||
if isinstance(tagName, basestring):
|
|
||||||
tagName = (tagName,)
|
|
||||||
def match(d):
|
|
||||||
return d.tagName in tagName
|
|
||||||
return iterMatchingDescendants(dom, match, max_depth)
|
|
||||||
|
|
||||||
def iterDescendantsWithAttribute(dom, tagName, attrName, attrValue,
|
|
||||||
max_depth = 0):
|
|
||||||
'''\
|
|
||||||
Iterate over descendant elements whose name is contained in
|
|
||||||
tagName (or is named tagName if tagName is a string) and have an
|
|
||||||
attribute named attrName with value attrValue. Only consider
|
|
||||||
nodes up to max_depth deep when max_depth is greater than zero.
|
|
||||||
'''
|
|
||||||
if isinstance(tagName, basestring):
|
|
||||||
tagName = (tagName,)
|
|
||||||
def match(d):
|
|
||||||
return d.tagName in tagName and \
|
|
||||||
d.getAttribute(attrName) == attrValue
|
|
||||||
return iterMatchingDescendants(dom, match, max_depth)
|
|
||||||
|
|
||||||
def iterChildren(dom, nodeType):
|
|
||||||
'''\
|
|
||||||
Iterate over all child elements of the given type.
|
|
||||||
'''
|
|
||||||
for child in dom.childNodes:
|
|
||||||
if child.nodeType == nodeType:
|
|
||||||
yield child
|
|
||||||
|
|
||||||
def gettextchild(dom):
|
|
||||||
'''\
|
|
||||||
Return the text node of the given element.
|
|
||||||
'''
|
|
||||||
for child in iterChildren(dom, Node.TEXT_NODE):
|
|
||||||
return str(child.nodeValue)
|
|
||||||
return None
|
|
||||||
|
|
||||||
def getChildTextTrim(dom):
|
|
||||||
text = gettextchild(dom)
|
|
||||||
if text:
|
|
||||||
text = text.strip()
|
|
||||||
return text
|
|
||||||
|
|
||||||
def getparamssetattrs(dom, param_names, target):
|
|
||||||
''' XML helper to get <param name="name" value="value"/> tags and set
|
|
||||||
the attribute in the target object. String type is used. Target object
|
|
||||||
attribute is unchanged if the XML attribute is not present.
|
|
||||||
'''
|
|
||||||
params = dom.getElementsByTagName("param")
|
|
||||||
for param in params:
|
|
||||||
param_name = param.getAttribute("name")
|
|
||||||
value = param.getAttribute("value")
|
|
||||||
if value is None:
|
|
||||||
continue # never reached?
|
|
||||||
if param_name in param_names:
|
|
||||||
setattr(target, param_name, str(value))
|
|
||||||
|
|
||||||
def xmltypetonodeclass(session, type):
|
|
||||||
''' Helper to convert from a type string to a class name in nodes.*.
|
|
||||||
'''
|
|
||||||
if hasattr(nodes, type):
|
|
||||||
return eval("nodes.%s" % type)
|
|
||||||
else:
|
|
||||||
return None
|
|
||||||
|
|
||||||
def iterChildrenWithName(dom, tagName):
|
|
||||||
return iterDescendantsWithName(dom, tagName, 1)
|
|
||||||
|
|
||||||
def iterChildrenWithAttribute(dom, tagName, attrName, attrValue):
|
|
||||||
return iterDescendantsWithAttribute(dom, tagName, attrName, attrValue, 1)
|
|
||||||
|
|
||||||
def getFirstChildByTagName(dom, tagName):
|
|
||||||
'''\
|
|
||||||
Return the first child element whose name is contained in tagName
|
|
||||||
(or is named tagName if tagName is a string).
|
|
||||||
'''
|
|
||||||
for child in iterChildrenWithName(dom, tagName):
|
|
||||||
return child
|
|
||||||
return None
|
|
||||||
|
|
||||||
def getFirstChildTextByTagName(dom, tagName):
|
|
||||||
'''\
|
|
||||||
Return the corresponding text of the first child element whose
|
|
||||||
name is contained in tagName (or is named tagName if tagName is a
|
|
||||||
string).
|
|
||||||
'''
|
|
||||||
child = getFirstChildByTagName(dom, tagName)
|
|
||||||
if child:
|
|
||||||
return gettextchild(child)
|
|
||||||
return None
|
|
||||||
|
|
||||||
def getFirstChildTextTrimByTagName(dom, tagName):
|
|
||||||
text = getFirstChildTextByTagName(dom, tagName)
|
|
||||||
if text:
|
|
||||||
text = text.strip()
|
|
||||||
return text
|
|
||||||
|
|
||||||
def getFirstChildWithAttribute(dom, tagName, attrName, attrValue):
|
|
||||||
'''\
|
|
||||||
Return the first child element whose name is contained in tagName
|
|
||||||
(or is named tagName if tagName is a string) that has an attribute
|
|
||||||
named attrName with value attrValue.
|
|
||||||
'''
|
|
||||||
for child in \
|
|
||||||
iterChildrenWithAttribute(dom, tagName, attrName, attrValue):
|
|
||||||
return child
|
|
||||||
return None
|
|
||||||
|
|
||||||
def getFirstChildTextWithAttribute(dom, tagName, attrName, attrValue):
|
|
||||||
'''\
|
|
||||||
Return the corresponding text of the first child element whose
|
|
||||||
name is contained in tagName (or is named tagName if tagName is a
|
|
||||||
string) that has an attribute named attrName with value attrValue.
|
|
||||||
'''
|
|
||||||
child = getFirstChildWithAttribute(dom, tagName, attrName, attrValue)
|
|
||||||
if child:
|
|
||||||
return gettextchild(child)
|
|
||||||
return None
|
|
||||||
|
|
||||||
def getFirstChildTextTrimWithAttribute(dom, tagName, attrName, attrValue):
|
|
||||||
text = getFirstChildTextWithAttribute(dom, tagName, attrName, attrValue)
|
|
||||||
if text:
|
|
||||||
text = text.strip()
|
|
||||||
return text
|
|
||||||
|
|
@ -1,15 +0,0 @@
|
||||||
# CORE
|
|
||||||
# Copyright (c) 2015 The Boeing Company.
|
|
||||||
# See the LICENSE file included in this distribution.
|
|
||||||
|
|
||||||
from xmlwriter0 import CoreDocumentWriter0
|
|
||||||
from xmlwriter1 import CoreDocumentWriter1
|
|
||||||
|
|
||||||
def core_document_writer(session, version):
|
|
||||||
if version == '0.0':
|
|
||||||
doc = CoreDocumentWriter0(session)
|
|
||||||
elif version == '1.0':
|
|
||||||
doc = CoreDocumentWriter1(session)
|
|
||||||
else:
|
|
||||||
raise ValueError, 'unsupported document version: %s' % version
|
|
||||||
return doc
|
|
||||||
|
|
@ -1,377 +0,0 @@
|
||||||
#
|
|
||||||
# CORE
|
|
||||||
# Copyright (c)2011-2013 the Boeing Company.
|
|
||||||
# See the LICENSE file included in this distribution.
|
|
||||||
#
|
|
||||||
# author: Jeff Ahrenholz <jeffrey.m.ahrenholz@boeing.com>
|
|
||||||
#
|
|
||||||
|
|
||||||
import os
|
|
||||||
import pwd
|
|
||||||
from core.netns import nodes
|
|
||||||
from core.api import coreapi
|
|
||||||
from xml.dom.minidom import Document
|
|
||||||
from xmlutils import *
|
|
||||||
|
|
||||||
class CoreDocumentWriter0(Document):
|
|
||||||
''' Utility class for writing a CoreSession to XML. The init method builds
|
|
||||||
an xml.dom.minidom.Document, and the writexml() method saves the XML file.
|
|
||||||
'''
|
|
||||||
def __init__(self, session):
|
|
||||||
''' Create an empty Scenario XML Document, then populate it with
|
|
||||||
objects from the given session.
|
|
||||||
'''
|
|
||||||
Document.__init__(self)
|
|
||||||
self.session = session
|
|
||||||
self.scenario = self.createElement("Scenario")
|
|
||||||
self.np = self.createElement("NetworkPlan")
|
|
||||||
self.mp = self.createElement("MotionPlan")
|
|
||||||
self.sp = self.createElement("ServicePlan")
|
|
||||||
self.meta = self.createElement("CoreMetaData")
|
|
||||||
|
|
||||||
self.appendChild(self.scenario)
|
|
||||||
self.scenario.appendChild(self.np)
|
|
||||||
self.scenario.appendChild(self.mp)
|
|
||||||
self.scenario.appendChild(self.sp)
|
|
||||||
self.scenario.appendChild(self.meta)
|
|
||||||
|
|
||||||
self.populatefromsession()
|
|
||||||
|
|
||||||
def populatefromsession(self):
|
|
||||||
self.session.emane.setup() # not during runtime?
|
|
||||||
self.addorigin()
|
|
||||||
self.adddefaultservices()
|
|
||||||
self.addnets()
|
|
||||||
self.addnodes()
|
|
||||||
self.addmetadata()
|
|
||||||
|
|
||||||
def writexml(self, filename):
|
|
||||||
self.session.info("saving session XML file %s" % filename)
|
|
||||||
f = open(filename, "w")
|
|
||||||
Document.writexml(self, writer=f, indent="", addindent=" ", newl="\n", \
|
|
||||||
encoding="UTF-8")
|
|
||||||
f.close()
|
|
||||||
if self.session.user is not None:
|
|
||||||
uid = pwd.getpwnam(self.session.user).pw_uid
|
|
||||||
gid = os.stat(self.session.sessiondir).st_gid
|
|
||||||
os.chown(filename, uid, gid)
|
|
||||||
|
|
||||||
def addnets(self):
|
|
||||||
''' Add PyCoreNet objects as NetworkDefinition XML elements.
|
|
||||||
'''
|
|
||||||
with self.session._objslock:
|
|
||||||
for net in self.session.objs():
|
|
||||||
if not isinstance(net, nodes.PyCoreNet):
|
|
||||||
continue
|
|
||||||
self.addnet(net)
|
|
||||||
|
|
||||||
def addnet(self, net):
|
|
||||||
''' Add one PyCoreNet object as a NetworkDefinition XML element.
|
|
||||||
'''
|
|
||||||
n = self.createElement("NetworkDefinition")
|
|
||||||
self.np.appendChild(n)
|
|
||||||
n.setAttribute("name", net.name)
|
|
||||||
# could use net.brname
|
|
||||||
n.setAttribute("id", "%s" % net.objid)
|
|
||||||
n.setAttribute("type", "%s" % net.__class__.__name__)
|
|
||||||
self.addnetinterfaces(n, net)
|
|
||||||
# key used with tunnel node
|
|
||||||
if hasattr(net, 'grekey') and net.grekey is not None:
|
|
||||||
n.setAttribute("key", "%s" % net.grekey)
|
|
||||||
# link parameters
|
|
||||||
for netif in net.netifs(sort=True):
|
|
||||||
self.addnetem(n, netif)
|
|
||||||
# wireless/mobility models
|
|
||||||
modelconfigs = net.session.mobility.getmodels(net)
|
|
||||||
modelconfigs += net.session.emane.getmodels(net)
|
|
||||||
self.addmodels(n, modelconfigs)
|
|
||||||
self.addposition(net)
|
|
||||||
|
|
||||||
def addnetem(self, n, netif):
|
|
||||||
''' Similar to addmodels(); used for writing netem link effects
|
|
||||||
parameters. TODO: Interface parameters should be moved to the model
|
|
||||||
construct, then this separate method shouldn't be required.
|
|
||||||
'''
|
|
||||||
params = netif.getparams()
|
|
||||||
if len(params) == 0:
|
|
||||||
return
|
|
||||||
model = self.createElement("model")
|
|
||||||
model.setAttribute("name", "netem")
|
|
||||||
model.setAttribute("netif", netif.name)
|
|
||||||
if hasattr(netif, "node") and netif.node is not None:
|
|
||||||
model.setAttribute("peer", netif.node.name)
|
|
||||||
# link between switches uses one veth interface
|
|
||||||
elif hasattr(netif, "othernet") and netif.othernet is not None:
|
|
||||||
if netif.othernet.name == n.getAttribute("name"):
|
|
||||||
model.setAttribute("peer", netif.net.name)
|
|
||||||
else:
|
|
||||||
model.setAttribute("peer", netif.othernet.name)
|
|
||||||
model.setAttribute("netif", netif.localname)
|
|
||||||
# hack used for upstream parameters for link between switches
|
|
||||||
# (see LxBrNet.linknet())
|
|
||||||
if netif.othernet.objid == int(n.getAttribute("id")):
|
|
||||||
netif.swapparams('_params_up')
|
|
||||||
params = netif.getparams()
|
|
||||||
netif.swapparams('_params_up')
|
|
||||||
has_params = False
|
|
||||||
for k, v in params:
|
|
||||||
# default netem parameters are 0 or None
|
|
||||||
if v is None or v == 0:
|
|
||||||
continue
|
|
||||||
if k == "has_netem" or k == "has_tbf":
|
|
||||||
continue
|
|
||||||
key = self.createElement(k)
|
|
||||||
key.appendChild(self.createTextNode("%s" % v))
|
|
||||||
model.appendChild(key)
|
|
||||||
has_params = True
|
|
||||||
if has_params:
|
|
||||||
n.appendChild(model)
|
|
||||||
|
|
||||||
def addmodels(self, n, configs):
|
|
||||||
''' Add models from a list of model-class, config values tuples.
|
|
||||||
'''
|
|
||||||
for (m, conf) in configs:
|
|
||||||
model = self.createElement("model")
|
|
||||||
n.appendChild(model)
|
|
||||||
model.setAttribute("name", m._name)
|
|
||||||
type = "wireless"
|
|
||||||
if m._type == coreapi.CORE_TLV_REG_MOBILITY:
|
|
||||||
type = "mobility"
|
|
||||||
model.setAttribute("type", type)
|
|
||||||
for i, k in enumerate(m.getnames()):
|
|
||||||
key = self.createElement(k)
|
|
||||||
value = conf[i]
|
|
||||||
if value is None:
|
|
||||||
value = ""
|
|
||||||
key.appendChild(self.createTextNode("%s" % value))
|
|
||||||
model.appendChild(key)
|
|
||||||
|
|
||||||
def addnodes(self):
|
|
||||||
''' Add PyCoreNode objects as node XML elements.
|
|
||||||
'''
|
|
||||||
with self.session._objslock:
|
|
||||||
for node in self.session.objs():
|
|
||||||
if not isinstance(node, nodes.PyCoreNode):
|
|
||||||
continue
|
|
||||||
self.addnode(node)
|
|
||||||
|
|
||||||
def addnode(self, node):
|
|
||||||
''' Add a PyCoreNode object as node XML elements.
|
|
||||||
'''
|
|
||||||
n = self.createElement("Node")
|
|
||||||
self.np.appendChild(n)
|
|
||||||
n.setAttribute("name", node.name)
|
|
||||||
n.setAttribute("id", "%s" % node.nodeid())
|
|
||||||
if node.type:
|
|
||||||
n.setAttribute("type", node.type)
|
|
||||||
self.addinterfaces(n, node)
|
|
||||||
self.addposition(node)
|
|
||||||
addparamtoparent(self, n, "icon", node.icon)
|
|
||||||
addparamtoparent(self, n, "canvas", node.canvas)
|
|
||||||
self.addservices(node)
|
|
||||||
|
|
||||||
def addinterfaces(self, n, node):
|
|
||||||
''' Add PyCoreNetIfs to node XML elements.
|
|
||||||
'''
|
|
||||||
for ifc in node.netifs(sort=True):
|
|
||||||
i = self.createElement("interface")
|
|
||||||
n.appendChild(i)
|
|
||||||
i.setAttribute("name", ifc.name)
|
|
||||||
netmodel = None
|
|
||||||
if ifc.net:
|
|
||||||
i.setAttribute("net", ifc.net.name)
|
|
||||||
if hasattr(ifc.net, "model"):
|
|
||||||
netmodel = ifc.net.model
|
|
||||||
if ifc.mtu and ifc.mtu != 1500:
|
|
||||||
i.setAttribute("mtu", "%s" % ifc.mtu)
|
|
||||||
# could use ifc.params, transport_type
|
|
||||||
self.addaddresses(i, ifc)
|
|
||||||
# per-interface models
|
|
||||||
if netmodel and netmodel._name[:6] == "emane_":
|
|
||||||
cfg = self.session.emane.getifcconfig(node.objid, netmodel._name,
|
|
||||||
None, ifc)
|
|
||||||
if cfg:
|
|
||||||
self.addmodels(i, ((netmodel, cfg),) )
|
|
||||||
|
|
||||||
|
|
||||||
def addnetinterfaces(self, n, net):
|
|
||||||
''' Similar to addinterfaces(), but only adds interface elements to the
|
|
||||||
supplied XML node that would not otherwise appear in the Node elements.
|
|
||||||
These are any interfaces that link two switches/hubs together.
|
|
||||||
'''
|
|
||||||
for ifc in net.netifs(sort=True):
|
|
||||||
if not hasattr(ifc, "othernet") or not ifc.othernet:
|
|
||||||
continue
|
|
||||||
i = self.createElement("interface")
|
|
||||||
n.appendChild(i)
|
|
||||||
if net.objid == ifc.net.objid:
|
|
||||||
i.setAttribute("name", ifc.localname)
|
|
||||||
i.setAttribute("net", ifc.othernet.name)
|
|
||||||
else:
|
|
||||||
i.setAttribute("name", ifc.name)
|
|
||||||
i.setAttribute("net", ifc.net.name)
|
|
||||||
|
|
||||||
def addposition(self, node):
|
|
||||||
''' Add object coordinates as location XML element.
|
|
||||||
'''
|
|
||||||
(x,y,z) = node.position.get()
|
|
||||||
if x is None or y is None:
|
|
||||||
return
|
|
||||||
# <Node name="n1">
|
|
||||||
mpn = self.createElement("Node")
|
|
||||||
mpn.setAttribute("name", node.name)
|
|
||||||
self.mp.appendChild(mpn)
|
|
||||||
|
|
||||||
# <motion type="stationary">
|
|
||||||
motion = self.createElement("motion")
|
|
||||||
motion.setAttribute("type", "stationary")
|
|
||||||
mpn.appendChild(motion)
|
|
||||||
|
|
||||||
# <point>$X$,$Y$,$Z$</point>
|
|
||||||
pt = self.createElement("point")
|
|
||||||
motion.appendChild(pt)
|
|
||||||
coordstxt = "%s,%s" % (x,y)
|
|
||||||
if z:
|
|
||||||
coordstxt += ",%s" % z
|
|
||||||
coords = self.createTextNode(coordstxt)
|
|
||||||
pt.appendChild(coords)
|
|
||||||
|
|
||||||
def addorigin(self):
|
|
||||||
''' Add origin to Motion Plan using canvas reference point.
|
|
||||||
The CoreLocation class maintains this reference point.
|
|
||||||
'''
|
|
||||||
refgeo = self.session.location.refgeo
|
|
||||||
origin = self.createElement("origin")
|
|
||||||
attrs = ("lat","lon","alt")
|
|
||||||
have_origin = False
|
|
||||||
for i in xrange(3):
|
|
||||||
if refgeo[i] is not None:
|
|
||||||
origin.setAttribute(attrs[i], str(refgeo[i]))
|
|
||||||
have_origin = True
|
|
||||||
if not have_origin:
|
|
||||||
return
|
|
||||||
if self.session.location.refscale != 1.0: # 100 pixels = refscale m
|
|
||||||
origin.setAttribute("scale100", str(self.session.location.refscale))
|
|
||||||
if self.session.location.refxyz != (0.0, 0.0, 0.0):
|
|
||||||
pt = self.createElement("point")
|
|
||||||
origin.appendChild(pt)
|
|
||||||
x,y,z = self.session.location.refxyz
|
|
||||||
coordstxt = "%s,%s" % (x,y)
|
|
||||||
if z:
|
|
||||||
coordstxt += ",%s" % z
|
|
||||||
coords = self.createTextNode(coordstxt)
|
|
||||||
pt.appendChild(coords)
|
|
||||||
|
|
||||||
self.mp.appendChild(origin)
|
|
||||||
|
|
||||||
def adddefaultservices(self):
|
|
||||||
''' Add default services and node types to the ServicePlan.
|
|
||||||
'''
|
|
||||||
for type in self.session.services.defaultservices:
|
|
||||||
defaults = self.session.services.getdefaultservices(type)
|
|
||||||
spn = self.createElement("Node")
|
|
||||||
spn.setAttribute("type", type)
|
|
||||||
self.sp.appendChild(spn)
|
|
||||||
for svc in defaults:
|
|
||||||
s = self.createElement("Service")
|
|
||||||
spn.appendChild(s)
|
|
||||||
s.setAttribute("name", str(svc._name))
|
|
||||||
|
|
||||||
def addservices(self, node):
|
|
||||||
''' Add services and their customizations to the ServicePlan.
|
|
||||||
'''
|
|
||||||
if len(node.services) == 0:
|
|
||||||
return
|
|
||||||
defaults = self.session.services.getdefaultservices(node.type)
|
|
||||||
if node.services == defaults:
|
|
||||||
return
|
|
||||||
spn = self.createElement("Node")
|
|
||||||
spn.setAttribute("name", node.name)
|
|
||||||
self.sp.appendChild(spn)
|
|
||||||
|
|
||||||
for svc in node.services:
|
|
||||||
s = self.createElement("Service")
|
|
||||||
spn.appendChild(s)
|
|
||||||
s.setAttribute("name", str(svc._name))
|
|
||||||
s.setAttribute("startup_idx", str(svc._startindex))
|
|
||||||
if svc._starttime != "":
|
|
||||||
s.setAttribute("start_time", str(svc._starttime))
|
|
||||||
# only record service names if not a customized service
|
|
||||||
if not svc._custom:
|
|
||||||
continue
|
|
||||||
s.setAttribute("custom", str(svc._custom))
|
|
||||||
addelementsfromlist(self, s, svc._dirs, "Directory", "name")
|
|
||||||
|
|
||||||
for fn in svc._configs:
|
|
||||||
if len(fn) == 0:
|
|
||||||
continue
|
|
||||||
f = self.createElement("File")
|
|
||||||
f.setAttribute("name", fn)
|
|
||||||
# all file names are added to determine when a file has been deleted
|
|
||||||
s.appendChild(f)
|
|
||||||
data = self.session.services.getservicefiledata(svc, fn)
|
|
||||||
if data is None:
|
|
||||||
# this includes only customized file contents and skips
|
|
||||||
# the auto-generated files
|
|
||||||
continue
|
|
||||||
txt = self.createTextNode(data)
|
|
||||||
f.appendChild(txt)
|
|
||||||
|
|
||||||
addtextelementsfromlist(self, s, svc._startup, "Command",
|
|
||||||
(("type","start"),))
|
|
||||||
addtextelementsfromlist(self, s, svc._shutdown, "Command",
|
|
||||||
(("type","stop"),))
|
|
||||||
addtextelementsfromlist(self, s, svc._validate, "Command",
|
|
||||||
(("type","validate"),))
|
|
||||||
|
|
||||||
def addaddresses(self, i, netif):
|
|
||||||
''' Add MAC and IP addresses to interface XML elements.
|
|
||||||
'''
|
|
||||||
if netif.hwaddr:
|
|
||||||
h = self.createElement("address")
|
|
||||||
i.appendChild(h)
|
|
||||||
h.setAttribute("type", "mac")
|
|
||||||
htxt = self.createTextNode("%s" % netif.hwaddr)
|
|
||||||
h.appendChild(htxt)
|
|
||||||
for addr in netif.addrlist:
|
|
||||||
a = self.createElement("address")
|
|
||||||
i.appendChild(a)
|
|
||||||
# a.setAttribute("type", )
|
|
||||||
atxt = self.createTextNode("%s" % addr)
|
|
||||||
a.appendChild(atxt)
|
|
||||||
|
|
||||||
def addhooks(self):
|
|
||||||
''' Add hook script XML elements to the metadata tag.
|
|
||||||
'''
|
|
||||||
hooks = self.createElement("Hooks")
|
|
||||||
for state in sorted(self.session._hooks.keys()):
|
|
||||||
for (filename, data) in self.session._hooks[state]:
|
|
||||||
hook = self.createElement("Hook")
|
|
||||||
hook.setAttribute("name", filename)
|
|
||||||
hook.setAttribute("state", str(state))
|
|
||||||
txt = self.createTextNode(data)
|
|
||||||
hook.appendChild(txt)
|
|
||||||
hooks.appendChild(hook)
|
|
||||||
if hooks.hasChildNodes():
|
|
||||||
self.meta.appendChild(hooks)
|
|
||||||
|
|
||||||
def addmetadata(self):
|
|
||||||
''' Add CORE-specific session meta-data XML elements.
|
|
||||||
'''
|
|
||||||
# options
|
|
||||||
options = self.createElement("SessionOptions")
|
|
||||||
defaults = self.session.options.getdefaultvalues()
|
|
||||||
for i, (k, v) in enumerate(self.session.options.getkeyvaluelist()):
|
|
||||||
if str(v) != str(defaults[i]):
|
|
||||||
addtextparamtoparent(self, options, k, v)
|
|
||||||
#addparamtoparent(self, options, k, v)
|
|
||||||
if options.hasChildNodes():
|
|
||||||
self.meta.appendChild(options)
|
|
||||||
# hook scripts
|
|
||||||
self.addhooks()
|
|
||||||
# meta
|
|
||||||
meta = self.createElement("MetaData")
|
|
||||||
self.meta.appendChild(meta)
|
|
||||||
for (k, v) in self.session.metadata.items():
|
|
||||||
addtextparamtoparent(self, meta, k, v)
|
|
||||||
#addparamtoparent(self, meta, k, v)
|
|
||||||
|
|
@ -1,989 +0,0 @@
|
||||||
#
|
|
||||||
# CORE
|
|
||||||
# Copyright (c)2011-2015 the Boeing Company.
|
|
||||||
# See the LICENSE file included in this distribution.
|
|
||||||
#
|
|
||||||
# Created on Dec 18, 2014
|
|
||||||
#
|
|
||||||
# @author: santiago
|
|
||||||
#
|
|
||||||
|
|
||||||
import os
|
|
||||||
import pwd
|
|
||||||
import collections
|
|
||||||
from core.netns import nodes
|
|
||||||
from core.api import coreapi
|
|
||||||
from core.misc.ipaddr import *
|
|
||||||
|
|
||||||
from xml.dom.minidom import Document
|
|
||||||
from xmlutils import *
|
|
||||||
from xmldeployment import CoreDeploymentWriter
|
|
||||||
|
|
||||||
def enum(**enums):
|
|
||||||
return type('Enum', (), enums)
|
|
||||||
|
|
||||||
class Attrib(object):
|
|
||||||
''' NMF scenario plan attribute constants
|
|
||||||
'''
|
|
||||||
NetType = enum(WIRELESS = 'wireless', ETHERNET = 'ethernet',
|
|
||||||
PTP_WIRED = 'point-to-point-wired',
|
|
||||||
PTP_WIRELESS = 'point-to-point-wireless')
|
|
||||||
MembType = enum(INTERFACE = 'interface', CHANNEL = 'channel',
|
|
||||||
SWITCH = 'switch', HUB = 'hub', TUNNEL = 'tunnel',
|
|
||||||
NETWORK = "network")
|
|
||||||
DevType = enum(HOST = 'host', ROUTER = 'router', SWITCH = 'switch',
|
|
||||||
HUB = 'hub')
|
|
||||||
''' Node types in CORE
|
|
||||||
'''
|
|
||||||
NodeType = enum(ROUTER = 'router', HOST = 'host', MDR = 'mdr',
|
|
||||||
PC = 'PC', RJ45 = 'rj45', SWITCH = 'lanswitch',
|
|
||||||
HUB = 'hub')
|
|
||||||
Alias = enum(ID = "COREID")
|
|
||||||
|
|
||||||
''' A link endpoint in CORE
|
|
||||||
net: the network that the endpoint belongs to
|
|
||||||
netif: the network interface at this end
|
|
||||||
id: the identifier for the endpoint
|
|
||||||
l2devport: if the other end is a layer 2 device, this is the assigned port in that device
|
|
||||||
params: link/interface parameters
|
|
||||||
'''
|
|
||||||
Endpoint = collections.namedtuple('Endpoint',
|
|
||||||
['net', 'netif', 'type', 'id', 'l2devport', 'params'])
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
class CoreDocumentWriter1(Document):
|
|
||||||
''' Utility class for writing a CoreSession to XML in the NMF scenPlan schema. The init
|
|
||||||
method builds an xml.dom.minidom.Document, and the writexml() method saves the XML file.
|
|
||||||
'''
|
|
||||||
|
|
||||||
def __init__(self, session):
|
|
||||||
''' Create an empty Scenario XML Document, then populate it with
|
|
||||||
objects from the given session.
|
|
||||||
'''
|
|
||||||
Document.__init__(self)
|
|
||||||
session.info('Exporting to NMF XML version 1.0')
|
|
||||||
with session._objslock:
|
|
||||||
self.scenarioPlan = ScenarioPlan(self, session)
|
|
||||||
if session.getstate() == coreapi.CORE_EVENT_RUNTIME_STATE:
|
|
||||||
deployment = CoreDeploymentWriter(self, self.scenarioPlan,
|
|
||||||
session)
|
|
||||||
deployment.add_deployment()
|
|
||||||
self.scenarioPlan.setAttribute('deployed', 'true')
|
|
||||||
|
|
||||||
def writexml(self, filename):
|
|
||||||
''' Commit to file
|
|
||||||
'''
|
|
||||||
self.scenarioPlan.coreSession.info("saving session XML file %s" % filename)
|
|
||||||
f = open(filename, "w")
|
|
||||||
Document.writexml(self, writer=f, indent="", addindent=" ", newl="\n", \
|
|
||||||
encoding="UTF-8")
|
|
||||||
f.close()
|
|
||||||
if self.scenarioPlan.coreSession.user is not None:
|
|
||||||
uid = pwd.getpwnam(self.scenarioPlan.coreSession.user).pw_uid
|
|
||||||
gid = os.stat(self.scenarioPlan.coreSession.sessiondir).st_gid
|
|
||||||
os.chown(filename, uid, gid)
|
|
||||||
|
|
||||||
|
|
||||||
class XmlElement(object):
|
|
||||||
''' The base class for all XML elements in the scenario plan. Includes
|
|
||||||
convenience functions.
|
|
||||||
'''
|
|
||||||
def __init__(self, document, parent, elementType):
|
|
||||||
self.document = document
|
|
||||||
self.parent = parent
|
|
||||||
self.baseEle = document.createElement("%s" % elementType)
|
|
||||||
if self.parent is not None:
|
|
||||||
self.parent.appendChild(self.baseEle)
|
|
||||||
|
|
||||||
def createElement(self, elementTag):
|
|
||||||
return self.document.createElement(elementTag)
|
|
||||||
|
|
||||||
def getTagName(self):
|
|
||||||
return self.baseEle.tagName
|
|
||||||
|
|
||||||
def createTextNode(self, nodeTag):
|
|
||||||
return self.document.createTextNode(nodeTag)
|
|
||||||
|
|
||||||
def appendChild(self, child):
|
|
||||||
if isinstance(child, XmlElement):
|
|
||||||
self.baseEle.appendChild(child.baseEle)
|
|
||||||
else:
|
|
||||||
self.baseEle.appendChild(child)
|
|
||||||
|
|
||||||
@staticmethod
|
|
||||||
def add_parameter(doc, parent, key, value):
|
|
||||||
if key and value:
|
|
||||||
parm = doc.createElement("parameter")
|
|
||||||
parm.setAttribute("name", str(key))
|
|
||||||
parm.appendChild(doc.createTextNode(str(value)))
|
|
||||||
parent.appendChild(parm)
|
|
||||||
|
|
||||||
def addParameter(self, key, value):
|
|
||||||
'''
|
|
||||||
Add a parameter to the xml element
|
|
||||||
'''
|
|
||||||
self.add_parameter(self.document, self, key, value)
|
|
||||||
|
|
||||||
def setAttribute(self, name, val):
|
|
||||||
self.baseEle.setAttribute(name, val)
|
|
||||||
|
|
||||||
def getAttribute(self, name):
|
|
||||||
return self.baseEle.getAttribute(name)
|
|
||||||
|
|
||||||
|
|
||||||
class NamedXmlElement(XmlElement):
|
|
||||||
''' The base class for all "named" xml elements. Named elements are
|
|
||||||
xml elements in the scenario plan that have an id and a name attribute.
|
|
||||||
'''
|
|
||||||
def __init__(self, scenPlan, parent, elementType, elementName):
|
|
||||||
XmlElement.__init__(self, scenPlan.document, parent, elementType)
|
|
||||||
|
|
||||||
self.scenPlan = scenPlan
|
|
||||||
self.coreSession = scenPlan.coreSession
|
|
||||||
|
|
||||||
elementPath = ''
|
|
||||||
self.id=None
|
|
||||||
if self.parent is not None and isinstance(self.parent, XmlElement) and self.parent.getTagName() != "scenario":
|
|
||||||
elementPath="%s/" % self.parent.getAttribute("id")
|
|
||||||
|
|
||||||
self.id = "%s%s" % (elementPath,elementName)
|
|
||||||
self.setAttribute("name", elementName)
|
|
||||||
self.setAttribute("id", self.id)
|
|
||||||
|
|
||||||
|
|
||||||
def addPoint(self, coreObj):
|
|
||||||
''' Add position to an object
|
|
||||||
'''
|
|
||||||
(x,y,z) = coreObj.position.get()
|
|
||||||
if x is None or y is None:
|
|
||||||
return
|
|
||||||
lat, lon, alt = self.coreSession.location.getgeo(x, y, z)
|
|
||||||
|
|
||||||
pt = self.createElement("point")
|
|
||||||
pt.setAttribute("type", "gps")
|
|
||||||
pt.setAttribute("lat", "%s" % lat)
|
|
||||||
pt.setAttribute("lon", "%s" % lon)
|
|
||||||
if z:
|
|
||||||
pt.setAttribute("z", "%s" % alt)
|
|
||||||
self.appendChild(pt)
|
|
||||||
|
|
||||||
def createAlias(self, domain, valueStr):
|
|
||||||
''' Create an alias element for CORE specific information
|
|
||||||
'''
|
|
||||||
a = self.createElement("alias")
|
|
||||||
a.setAttribute("domain", "%s" % domain)
|
|
||||||
a.appendChild(self.createTextNode(valueStr))
|
|
||||||
return a
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
class ScenarioPlan(XmlElement):
|
|
||||||
''' Container class for ScenarioPlan.
|
|
||||||
'''
|
|
||||||
def __init__(self, document, session):
|
|
||||||
XmlElement.__init__(self, document, parent=document, elementType='scenario')
|
|
||||||
|
|
||||||
self.coreSession = session
|
|
||||||
|
|
||||||
self.setAttribute('version', '1.0')
|
|
||||||
self.setAttribute("name", "%s" % session.name)
|
|
||||||
|
|
||||||
self.setAttribute('xmlns', 'nmfPlan')
|
|
||||||
self.setAttribute('xmlns:CORE', 'coreSpecific')
|
|
||||||
self.setAttribute('compiled', 'true')
|
|
||||||
|
|
||||||
self.allChannelMembers = dict()
|
|
||||||
self.lastNetIdx = 0
|
|
||||||
self.addNetworks()
|
|
||||||
self.addDevices()
|
|
||||||
|
|
||||||
# XXX Do we need these?
|
|
||||||
#self.session.emane.setup() # not during runtime?
|
|
||||||
#self.addorigin()
|
|
||||||
|
|
||||||
self.addDefaultServices()
|
|
||||||
|
|
||||||
self.addSessionConfiguration()
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
def addNetworks(self):
|
|
||||||
''' Add networks in the session to the scenPlan.
|
|
||||||
'''
|
|
||||||
for net in self.coreSession.objs():
|
|
||||||
if not isinstance(net, nodes.PyCoreNet):
|
|
||||||
continue
|
|
||||||
|
|
||||||
if isinstance(net, nodes.CtrlNet):
|
|
||||||
continue
|
|
||||||
|
|
||||||
# Do not add switches and hubs that belong to another network
|
|
||||||
if isinstance(net, (nodes.SwitchNode, nodes.HubNode)):
|
|
||||||
if inOtherNetwork(net):
|
|
||||||
continue
|
|
||||||
|
|
||||||
try:
|
|
||||||
NetworkElement(self, self, net)
|
|
||||||
except:
|
|
||||||
if hasattr(net, "name") and net.name:
|
|
||||||
self.coreSession.warn('Unsupported net: %s' % net.name)
|
|
||||||
else:
|
|
||||||
self.coreSession.warn('Unsupported net: %s' % net.__class__.__name__)
|
|
||||||
|
|
||||||
|
|
||||||
def addDevices(self):
|
|
||||||
''' Add device elements to the scenario plan.
|
|
||||||
'''
|
|
||||||
for node in self.coreSession.objs():
|
|
||||||
if not isinstance(node, (nodes.PyCoreNode)):
|
|
||||||
continue
|
|
||||||
try:
|
|
||||||
DeviceElement(self, self, node)
|
|
||||||
except:
|
|
||||||
if hasattr(node, "name") and node.name:
|
|
||||||
self.coreSession.warn('Unsupported device: %s' % node.name)
|
|
||||||
else:
|
|
||||||
self.coreSession.warn('Unsupported device: %s' % node.__class__.__name__)
|
|
||||||
|
|
||||||
|
|
||||||
def addDefaultServices(self):
|
|
||||||
''' Add default services and node types to the ServicePlan.
|
|
||||||
'''
|
|
||||||
defaultservices = self.createElement("CORE:defaultservices")
|
|
||||||
for type in self.coreSession.services.defaultservices:
|
|
||||||
defaults = self.coreSession.services.getdefaultservices(type)
|
|
||||||
spn = self.createElement("device")
|
|
||||||
spn.setAttribute("type", type)
|
|
||||||
defaultservices.appendChild(spn)
|
|
||||||
for svc in defaults:
|
|
||||||
s = self.createElement("service")
|
|
||||||
spn.appendChild(s)
|
|
||||||
s.setAttribute("name", str(svc._name))
|
|
||||||
if defaultservices.hasChildNodes():
|
|
||||||
self.appendChild(defaultservices)
|
|
||||||
|
|
||||||
def addSessionConfiguration(self):
|
|
||||||
''' Add CORE-specific session configuration XML elements.
|
|
||||||
'''
|
|
||||||
config = self.createElement("CORE:sessionconfig")
|
|
||||||
|
|
||||||
# origin: geolocation of cartesian coordinate 0,0,0
|
|
||||||
refgeo = self.coreSession.location.refgeo
|
|
||||||
origin = self.createElement("origin")
|
|
||||||
attrs = ("lat","lon","alt")
|
|
||||||
have_origin = False
|
|
||||||
for i in xrange(3):
|
|
||||||
if refgeo[i] is not None:
|
|
||||||
origin.setAttribute(attrs[i], str(refgeo[i]))
|
|
||||||
have_origin = True
|
|
||||||
if have_origin:
|
|
||||||
if self.coreSession.location.refscale != 1.0: # 100 pixels = refscale m
|
|
||||||
origin.setAttribute("scale100", str(self.coreSession.location.refscale))
|
|
||||||
if self.coreSession.location.refxyz != (0.0, 0.0, 0.0):
|
|
||||||
pt = self.createElement("point")
|
|
||||||
origin.appendChild(pt)
|
|
||||||
x,y,z = self.coreSession.location.refxyz
|
|
||||||
coordstxt = "%s,%s" % (x,y)
|
|
||||||
if z:
|
|
||||||
coordstxt += ",%s" % z
|
|
||||||
coords = self.createTextNode(coordstxt)
|
|
||||||
pt.appendChild(coords)
|
|
||||||
config.appendChild(origin)
|
|
||||||
|
|
||||||
|
|
||||||
# options
|
|
||||||
options = self.createElement("options")
|
|
||||||
defaults = self.coreSession.options.getdefaultvalues()
|
|
||||||
for i, (k, v) in enumerate(self.coreSession.options.getkeyvaluelist()):
|
|
||||||
if str(v) != str(defaults[i]):
|
|
||||||
XmlElement.add_parameter(self.document, options, k, v)
|
|
||||||
if options.hasChildNodes():
|
|
||||||
config.appendChild(options)
|
|
||||||
|
|
||||||
# hook scripts
|
|
||||||
hooks = self.createElement("hooks")
|
|
||||||
for state in sorted(self.coreSession._hooks.keys()):
|
|
||||||
for (filename, data) in self.coreSession._hooks[state]:
|
|
||||||
hook = self.createElement("hook")
|
|
||||||
hook.setAttribute("name", filename)
|
|
||||||
hook.setAttribute("state", str(state))
|
|
||||||
txt = self.createTextNode(data)
|
|
||||||
hook.appendChild(txt)
|
|
||||||
hooks.appendChild(hook)
|
|
||||||
if hooks.hasChildNodes():
|
|
||||||
config.appendChild(hooks)
|
|
||||||
|
|
||||||
# metadata
|
|
||||||
meta = self.createElement("metadata")
|
|
||||||
for (k, v) in self.coreSession.metadata.items():
|
|
||||||
XmlElement.add_parameter(self.document, meta, k, v)
|
|
||||||
if meta.hasChildNodes():
|
|
||||||
config.appendChild(meta)
|
|
||||||
|
|
||||||
if config.hasChildNodes():
|
|
||||||
self.appendChild(config)
|
|
||||||
|
|
||||||
|
|
||||||
class NetworkElement(NamedXmlElement):
|
|
||||||
def __init__(self, scenPlan, parent, netObj):
|
|
||||||
''' Add one PyCoreNet object as one network XML element.
|
|
||||||
'''
|
|
||||||
elementName = self.getNetworkName(scenPlan, netObj)
|
|
||||||
NamedXmlElement.__init__(self, scenPlan, parent, "network", elementName)
|
|
||||||
|
|
||||||
self.scenPlan = scenPlan
|
|
||||||
|
|
||||||
self.addPoint(netObj)
|
|
||||||
|
|
||||||
netType = None
|
|
||||||
if isinstance(netObj, (nodes.WlanNode, nodes.EmaneNode)):
|
|
||||||
netType = Attrib.NetType.WIRELESS
|
|
||||||
elif isinstance(netObj, (nodes.SwitchNode, nodes.HubNode,
|
|
||||||
nodes.PtpNet, nodes.TunnelNode)):
|
|
||||||
netType = Attrib.NetType.ETHERNET
|
|
||||||
else:
|
|
||||||
netType ="%s" % netObj.__class__.__name__
|
|
||||||
|
|
||||||
typeEle = self.createElement("type")
|
|
||||||
typeEle.appendChild(self.createTextNode(netType))
|
|
||||||
self.appendChild(typeEle)
|
|
||||||
|
|
||||||
# Gather all endpoints belonging to this network
|
|
||||||
self.endpoints = getEndpoints(netObj)
|
|
||||||
|
|
||||||
# Special case for a network of switches and hubs
|
|
||||||
createAlias = True
|
|
||||||
self.l2devices = []
|
|
||||||
if isinstance(netObj, (nodes.SwitchNode, nodes.HubNode)):
|
|
||||||
createAlias = False
|
|
||||||
self.appendChild(typeEle)
|
|
||||||
self.addL2Devices(netObj)
|
|
||||||
|
|
||||||
if createAlias:
|
|
||||||
a = self.createAlias(Attrib.Alias.ID, "%d" % int(netObj.objid))
|
|
||||||
self.appendChild(a)
|
|
||||||
|
|
||||||
# XXXX TODO: Move this to channel?
|
|
||||||
# key used with tunnel node
|
|
||||||
if hasattr(netObj, 'grekey') and netObj.grekey is not None:
|
|
||||||
a = self.createAlias("COREGREKEY", "%s" % netObj.grekey)
|
|
||||||
self.appendChild(a)
|
|
||||||
|
|
||||||
self.addNetMembers(netObj)
|
|
||||||
self.addChannels(netObj)
|
|
||||||
|
|
||||||
presentationEle = self.createElement("CORE:presentation")
|
|
||||||
addPresentationEle = False
|
|
||||||
if netObj.icon and not netObj.icon.isspace():
|
|
||||||
presentationEle.setAttribute("icon", netObj.icon)
|
|
||||||
addPresentationEle = True
|
|
||||||
if netObj.canvas:
|
|
||||||
presentationEle.setAttribute("canvas", str(netObj.canvas))
|
|
||||||
addPresentationEle = True
|
|
||||||
if addPresentationEle:
|
|
||||||
self.appendChild(presentationEle)
|
|
||||||
|
|
||||||
def getNetworkName(self, scenPlan, netObj):
|
|
||||||
''' Determine the name to use for this network element
|
|
||||||
'''
|
|
||||||
if isinstance(netObj, (nodes.PtpNet, nodes.TunnelNode)):
|
|
||||||
name = "net%s" % scenPlan.lastNetIdx
|
|
||||||
scenPlan.lastNetIdx += 1
|
|
||||||
elif netObj.name:
|
|
||||||
name = str(netObj.name) # could use net.brname for bridges?
|
|
||||||
elif isinstance(netObj, (nodes.SwitchNode, nodes.HubNode)):
|
|
||||||
name = "lan%s" % netObj.objid
|
|
||||||
else:
|
|
||||||
name = ''
|
|
||||||
return name
|
|
||||||
|
|
||||||
|
|
||||||
def addL2Devices(self, netObj):
|
|
||||||
''' Add switches and hubs
|
|
||||||
'''
|
|
||||||
|
|
||||||
# Add the netObj as a device
|
|
||||||
self.l2devices.append(DeviceElement(self.scenPlan, self, netObj))
|
|
||||||
|
|
||||||
# Add downstream switches/hubs
|
|
||||||
l2devs = []
|
|
||||||
neweps = []
|
|
||||||
for ep in self.endpoints:
|
|
||||||
if ep.type and ep.net.objid != netObj.objid:
|
|
||||||
l2s, eps = getDowmstreamL2Devices(ep.net)
|
|
||||||
l2devs.extend(l2s)
|
|
||||||
neweps.extend(eps)
|
|
||||||
|
|
||||||
for l2dev in l2devs:
|
|
||||||
self.l2devices.append(DeviceElement(self.scenPlan, self, l2dev))
|
|
||||||
|
|
||||||
self.endpoints.extend(neweps)
|
|
||||||
|
|
||||||
# XXX: Optimize later
|
|
||||||
def addNetMembers(self, netObj):
|
|
||||||
''' Add members to a network XML element.
|
|
||||||
'''
|
|
||||||
|
|
||||||
for ep in self.endpoints:
|
|
||||||
if ep.type:
|
|
||||||
MemberElement(self.scenPlan, self, referencedType=ep.type, referencedId=ep.id)
|
|
||||||
|
|
||||||
if ep.l2devport:
|
|
||||||
MemberElement(self.scenPlan,
|
|
||||||
self,
|
|
||||||
referencedType=Attrib.MembType.INTERFACE,
|
|
||||||
referencedId="%s/%s" % (self.id,ep.l2devport))
|
|
||||||
|
|
||||||
# XXX Revisit this
|
|
||||||
# Create implied members given the network type
|
|
||||||
if isinstance(netObj, nodes.TunnelNode):
|
|
||||||
MemberElement(self.scenPlan,
|
|
||||||
self,
|
|
||||||
referencedType=Attrib.MembType.TUNNEL,
|
|
||||||
referencedId="%s/%s" % (netObj.name, netObj.name))
|
|
||||||
|
|
||||||
# XXX: Optimize later
|
|
||||||
def addChannels(self, netObj):
|
|
||||||
''' Add channels to a network XML element
|
|
||||||
'''
|
|
||||||
|
|
||||||
if isinstance(netObj, (nodes.WlanNode, nodes.EmaneNode)):
|
|
||||||
modelconfigs = netObj.session.mobility.getmodels(netObj)
|
|
||||||
modelconfigs += netObj.session.emane.getmodels(netObj)
|
|
||||||
chan = None
|
|
||||||
for (model, conf) in modelconfigs:
|
|
||||||
# Handle mobility parameters below
|
|
||||||
if model._type == coreapi.CORE_TLV_REG_MOBILITY:
|
|
||||||
continue
|
|
||||||
|
|
||||||
# Create the channel
|
|
||||||
if chan is None:
|
|
||||||
name = "wireless"
|
|
||||||
chan = ChannelElement(self.scenPlan, self, netObj,
|
|
||||||
channelType=model._name,
|
|
||||||
channelName=name,
|
|
||||||
channelDomain="CORE")
|
|
||||||
|
|
||||||
# Add wireless model parameters
|
|
||||||
for i, key in enumerate(model.getnames()):
|
|
||||||
value = conf[i]
|
|
||||||
if value is not None:
|
|
||||||
chan.addParameter(key, model.valueof(key, conf))
|
|
||||||
|
|
||||||
for (model, conf) in modelconfigs:
|
|
||||||
if model._type == coreapi.CORE_TLV_REG_MOBILITY:
|
|
||||||
# Add wireless mobility parameters
|
|
||||||
mobility = XmlElement(self.scenPlan, chan, "CORE:mobility")
|
|
||||||
# Add a type child
|
|
||||||
typeEle = self.createElement("type")
|
|
||||||
typeEle.appendChild(self.createTextNode(model._name))
|
|
||||||
mobility.appendChild(typeEle)
|
|
||||||
for i, key in enumerate(model.getnames()):
|
|
||||||
value = conf[i]
|
|
||||||
if value is not None:
|
|
||||||
mobility.addParameter(key, value)
|
|
||||||
|
|
||||||
# Add members to the channel
|
|
||||||
if chan is not None:
|
|
||||||
chan.addChannelMembers(self.endpoints)
|
|
||||||
self.appendChild(chan.baseEle)
|
|
||||||
elif isinstance(netObj, nodes.PtpNet) :
|
|
||||||
if len(self.endpoints) < 2:
|
|
||||||
if len(self.endpoints) == 1:
|
|
||||||
self.coreSession.warn('Pt2Pt network with only 1 endpoint: %s' % self.endpoints[0].id)
|
|
||||||
else:
|
|
||||||
self.coreSession.warn('Pt2Pt network with no endpoints encountered in %s' % netObj.name)
|
|
||||||
return
|
|
||||||
name = "chan%d" % (0)
|
|
||||||
chan = ChannelElement(self.scenPlan, self, netObj,
|
|
||||||
channelType=Attrib.NetType.ETHERNET,
|
|
||||||
channelName=name)
|
|
||||||
|
|
||||||
# Add interface parameters
|
|
||||||
if self.endpoints[0].params != self.endpoints[1].params:
|
|
||||||
self.coreSession.warn('Pt2Pt Endpoint parameters do not match in %s' % netObj.name)
|
|
||||||
for key, value in self.endpoints[0].params:
|
|
||||||
# XXX lifted from original addnetem function. revisit this.
|
|
||||||
# default netem parameters are 0 or None
|
|
||||||
if value is None or value == 0:
|
|
||||||
continue
|
|
||||||
if key == "has_netem" or key == "has_tbf":
|
|
||||||
continue
|
|
||||||
chan.addParameter(key, value)
|
|
||||||
|
|
||||||
# Add members to the channel
|
|
||||||
chan.addChannelMembers(self.endpoints)
|
|
||||||
self.appendChild(chan)
|
|
||||||
|
|
||||||
elif isinstance(netObj, (nodes.SwitchNode,
|
|
||||||
nodes.HubNode, nodes.TunnelNode)):
|
|
||||||
cidx=0
|
|
||||||
channels = []
|
|
||||||
for ep in self.endpoints:
|
|
||||||
# Create one channel member per ep
|
|
||||||
if ep.type:
|
|
||||||
name = "chan%d" % (cidx)
|
|
||||||
chan = ChannelElement(self.scenPlan, self, netObj,
|
|
||||||
channelType=Attrib.NetType.ETHERNET,
|
|
||||||
channelName=name)
|
|
||||||
|
|
||||||
# Add interface parameters
|
|
||||||
for key, value in ep.params:
|
|
||||||
# XXX lifted from original addnetem function. revisit this.
|
|
||||||
# default netem parameters are 0 or None
|
|
||||||
if value is None or value == 0:
|
|
||||||
continue
|
|
||||||
if key == "has_netem" or key == "has_tbf":
|
|
||||||
continue
|
|
||||||
chan.addParameter(key, value)
|
|
||||||
|
|
||||||
# Add members to the channel
|
|
||||||
chan.addChannelMembers(ep)
|
|
||||||
channels.append(chan)
|
|
||||||
cidx += 1
|
|
||||||
|
|
||||||
for chan in channels:
|
|
||||||
self.appendChild(chan)
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
class DeviceElement(NamedXmlElement):
|
|
||||||
''' A device element in the scenario plan.
|
|
||||||
'''
|
|
||||||
def __init__(self, scenPlan, parent, devObj):
|
|
||||||
''' Add a PyCoreNode object as a device element.
|
|
||||||
'''
|
|
||||||
|
|
||||||
devType = None
|
|
||||||
coreDevType = None
|
|
||||||
if hasattr(devObj, "type") and devObj.type:
|
|
||||||
coreDevType = devObj.type
|
|
||||||
if devObj.type == Attrib.NodeType.ROUTER:
|
|
||||||
devType = Attrib.DevType.ROUTER
|
|
||||||
elif devObj.type == Attrib.NodeType.MDR:
|
|
||||||
devType = Attrib.DevType.ROUTER
|
|
||||||
elif devObj.type == Attrib.NodeType.HOST:
|
|
||||||
devType = Attrib.DevType.HOST
|
|
||||||
elif devObj.type == Attrib.NodeType.PC:
|
|
||||||
devType = Attrib.DevType.HOST
|
|
||||||
elif devObj.type == Attrib.NodeType.RJ45:
|
|
||||||
devType = Attrib.DevType.HOST
|
|
||||||
nodeId = "EMULATOR-HOST"
|
|
||||||
elif devObj.type == Attrib.NodeType.HUB:
|
|
||||||
devType = Attrib.DevType.HUB
|
|
||||||
elif devObj.type == Attrib.NodeType.SWITCH:
|
|
||||||
devType = Attrib.DevType.SWITCH
|
|
||||||
else:
|
|
||||||
# Default custom types (defined in ~/.core/nodes.conf) to HOST
|
|
||||||
devType = Attrib.DevType.HOST
|
|
||||||
|
|
||||||
|
|
||||||
if devType is None:
|
|
||||||
raise Exception
|
|
||||||
|
|
||||||
|
|
||||||
NamedXmlElement.__init__(self, scenPlan, parent, devType, devObj.name)
|
|
||||||
|
|
||||||
if coreDevType is not None:
|
|
||||||
typeEle = self.createElement("type")
|
|
||||||
typeEle.setAttribute("domain", "CORE")
|
|
||||||
typeEle.appendChild(self.createTextNode("%s" % coreDevType))
|
|
||||||
self.appendChild(typeEle)
|
|
||||||
|
|
||||||
self.interfaces = []
|
|
||||||
self.addInterfaces(devObj)
|
|
||||||
alias = self.createAlias(Attrib.Alias.ID, "%s" % devObj.objid)
|
|
||||||
self.appendChild(alias)
|
|
||||||
self.addPoint(devObj)
|
|
||||||
self.addServices(devObj)
|
|
||||||
|
|
||||||
|
|
||||||
presentationEle = self.createElement("CORE:presentation")
|
|
||||||
addPresentationEle = False
|
|
||||||
if devObj.icon and not devObj.icon.isspace():
|
|
||||||
presentationEle.setAttribute("icon", devObj.icon)
|
|
||||||
addPresentationEle = True
|
|
||||||
if devObj.canvas:
|
|
||||||
presentationEle.setAttribute("canvas", str(devObj.canvas))
|
|
||||||
addPresentationEle = True
|
|
||||||
if addPresentationEle:
|
|
||||||
self.appendChild(presentationEle)
|
|
||||||
|
|
||||||
def addInterfaces(self, devObj):
|
|
||||||
''' Add interfaces to a device element.
|
|
||||||
'''
|
|
||||||
idx=0
|
|
||||||
for ifcObj in devObj.netifs(sort=True):
|
|
||||||
if ifcObj.net and isinstance(ifcObj.net, nodes.CtrlNet):
|
|
||||||
continue
|
|
||||||
if isinstance(devObj, nodes.PyCoreNode):
|
|
||||||
ifcEle = InterfaceElement(self.scenPlan, self, devObj, ifcObj)
|
|
||||||
else: # isinstance(node, (nodes.HubNode nodes.SwitchNode)):
|
|
||||||
ifcEle = InterfaceElement(self.scenPlan, self, devObj, ifcObj, idx)
|
|
||||||
idx += 1
|
|
||||||
|
|
||||||
netmodel = None
|
|
||||||
if ifcObj.net:
|
|
||||||
if hasattr(ifcObj.net, "model"):
|
|
||||||
netmodel = ifcObj.net.model
|
|
||||||
if ifcObj.mtu and ifcObj.mtu != 1500:
|
|
||||||
ifcEle.setAttribute("mtu", "%s" % ifcObj.mtu)
|
|
||||||
|
|
||||||
# The interfaces returned for Switches and Hubs are the interfaces of the nodes connected to them.
|
|
||||||
# The addresses are for those interfaces. Don't include them here.
|
|
||||||
if isinstance(devObj, nodes.PyCoreNode):
|
|
||||||
# could use ifcObj.params, transport_type
|
|
||||||
ifcEle.addAddresses(ifcObj)
|
|
||||||
# per-interface models
|
|
||||||
# XXX Remove???
|
|
||||||
if netmodel and netmodel._name[:6] == "emane_":
|
|
||||||
cfg = self.coreSession.emane.getifcconfig(devObj.objid, netmodel._name,
|
|
||||||
None, ifcObj)
|
|
||||||
if cfg:
|
|
||||||
ifcEle.addModels(((netmodel, cfg),) )
|
|
||||||
|
|
||||||
self.interfaces.append(ifcEle)
|
|
||||||
|
|
||||||
|
|
||||||
def addServices(self, devObj):
|
|
||||||
''' Add services and their customizations to the ServicePlan.
|
|
||||||
'''
|
|
||||||
if not hasattr(devObj, "services") :
|
|
||||||
return
|
|
||||||
|
|
||||||
if len(devObj.services) == 0:
|
|
||||||
return
|
|
||||||
|
|
||||||
defaults = self.coreSession.services.getdefaultservices(devObj.type)
|
|
||||||
if devObj.services == defaults:
|
|
||||||
return
|
|
||||||
spn = self.createElement("CORE:services")
|
|
||||||
spn.setAttribute("name", devObj.name)
|
|
||||||
self.appendChild(spn)
|
|
||||||
|
|
||||||
for svc in devObj.services:
|
|
||||||
s = self.createElement("service")
|
|
||||||
spn.appendChild(s)
|
|
||||||
s.setAttribute("name", str(svc._name))
|
|
||||||
s.setAttribute("startup_idx", str(svc._startindex))
|
|
||||||
if svc._starttime != "":
|
|
||||||
s.setAttribute("start_time", str(svc._starttime))
|
|
||||||
# only record service names if not a customized service
|
|
||||||
if not svc._custom:
|
|
||||||
continue
|
|
||||||
s.setAttribute("custom", str(svc._custom))
|
|
||||||
addelementsfromlist(self, s, svc._dirs, "directory", "name")
|
|
||||||
|
|
||||||
for fn in svc._configs:
|
|
||||||
if len(fn) == 0:
|
|
||||||
continue
|
|
||||||
f = self.createElement("file")
|
|
||||||
f.setAttribute("name", fn)
|
|
||||||
# all file names are added to determine when a file has been deleted
|
|
||||||
s.appendChild(f)
|
|
||||||
data = self.coreSession.services.getservicefiledata(svc, fn)
|
|
||||||
if data is None:
|
|
||||||
# this includes only customized file contents and skips
|
|
||||||
# the auto-generated files
|
|
||||||
continue
|
|
||||||
txt = self.createTextNode("\n" + data)
|
|
||||||
f.appendChild(txt)
|
|
||||||
|
|
||||||
addtextelementsfromlist(self, s, svc._startup, "command",
|
|
||||||
(("type","start"),))
|
|
||||||
addtextelementsfromlist(self, s, svc._shutdown, "command",
|
|
||||||
(("type","stop"),))
|
|
||||||
addtextelementsfromlist(self, s, svc._validate, "command",
|
|
||||||
(("type","validate"),))
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
class ChannelElement(NamedXmlElement):
|
|
||||||
''' A channel element in the scenario plan
|
|
||||||
'''
|
|
||||||
def __init__(self, scenPlan, parent, netObj, channelType, channelName, channelDomain=None):
|
|
||||||
NamedXmlElement.__init__(self, scenPlan, parent, "channel", channelName)
|
|
||||||
'''
|
|
||||||
Create a channel element and append a member child referencing this channel element
|
|
||||||
in the parent element.
|
|
||||||
'''
|
|
||||||
# Create a member element for this channel in the parent
|
|
||||||
MemberElement(self.scenPlan,
|
|
||||||
parent,
|
|
||||||
referencedType=Attrib.MembType.CHANNEL,
|
|
||||||
referencedId=self.id)
|
|
||||||
|
|
||||||
# Add a type child
|
|
||||||
typeEle = self.createElement("type")
|
|
||||||
if channelDomain is not None:
|
|
||||||
typeEle.setAttribute("domain", "%s" % channelDomain)
|
|
||||||
typeEle.appendChild(self.createTextNode(channelType))
|
|
||||||
self.appendChild(typeEle)
|
|
||||||
|
|
||||||
|
|
||||||
def addChannelMembers(self, endpoints):
|
|
||||||
'''
|
|
||||||
Add network channel members referencing interfaces in the channel
|
|
||||||
'''
|
|
||||||
if isinstance(endpoints, list):
|
|
||||||
# A list of endpoints is given. Create one channel member per endpoint
|
|
||||||
idx = 0
|
|
||||||
for ep in endpoints:
|
|
||||||
self.addChannelMember(ep.type, ep.id, idx)
|
|
||||||
idx += 1
|
|
||||||
else:
|
|
||||||
# A single endpoint is given. Create one channel member for the endpoint,
|
|
||||||
# and if the endpoint is associated with a Layer 2 device port, add the
|
|
||||||
# port as a second member
|
|
||||||
ep = endpoints
|
|
||||||
self.addChannelMember(ep.type, ep.id, 0)
|
|
||||||
if ep.l2devport is not None:
|
|
||||||
memId = "%s/%s" % (self.parent.getAttribute("id"), ep.l2devport)
|
|
||||||
self.addChannelMember(ep.type, memId, 1)
|
|
||||||
|
|
||||||
|
|
||||||
def addChannelMember(self, memIfcType, memIfcId, memIdx):
|
|
||||||
'''
|
|
||||||
add a member to a given channel
|
|
||||||
'''
|
|
||||||
|
|
||||||
m = MemberElement(self.scenPlan,
|
|
||||||
self,
|
|
||||||
referencedType=memIfcType,
|
|
||||||
referencedId=memIfcId,
|
|
||||||
index=memIdx)
|
|
||||||
self.scenPlan.allChannelMembers[memIfcId] = m
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
class InterfaceElement(NamedXmlElement):
|
|
||||||
'''
|
|
||||||
A network interface element
|
|
||||||
'''
|
|
||||||
def __init__(self, scenPlan, parent, devObj, ifcObj, ifcIdx=None):
|
|
||||||
'''
|
|
||||||
Create a network interface element with references to channel that this
|
|
||||||
interface is used.
|
|
||||||
'''
|
|
||||||
elementName=None
|
|
||||||
if ifcIdx is not None:
|
|
||||||
elementName = "e%d" % ifcIdx
|
|
||||||
else:
|
|
||||||
elementName = ifcObj.name
|
|
||||||
NamedXmlElement.__init__(self, scenPlan, parent, "interface", elementName)
|
|
||||||
self.ifcObj = ifcObj
|
|
||||||
self.addChannelReference()
|
|
||||||
|
|
||||||
def addChannelReference(self):
|
|
||||||
'''
|
|
||||||
Add a reference to the channel that uses this interface
|
|
||||||
'''
|
|
||||||
try:
|
|
||||||
cm = self.scenPlan.allChannelMembers[self.id]
|
|
||||||
if cm is not None:
|
|
||||||
ch = cm.baseEle.parentNode
|
|
||||||
if ch is not None:
|
|
||||||
net = ch.parentNode
|
|
||||||
if net is not None:
|
|
||||||
MemberElement(self.scenPlan,
|
|
||||||
self,
|
|
||||||
referencedType=Attrib.MembType.CHANNEL,
|
|
||||||
referencedId=ch.getAttribute("id"),
|
|
||||||
index=int(cm.getAttribute("index")))
|
|
||||||
MemberElement(self.scenPlan,
|
|
||||||
self,
|
|
||||||
referencedType=Attrib.MembType.NETWORK,
|
|
||||||
referencedId=net.getAttribute("id"))
|
|
||||||
except KeyError:
|
|
||||||
pass # Not an error. This occurs when an interface belongs to a switch or a hub within a network and the channel is yet to be defined
|
|
||||||
|
|
||||||
|
|
||||||
def addAddresses(self, ifcObj):
|
|
||||||
'''
|
|
||||||
Add MAC and IP addresses to interface XML elements.
|
|
||||||
'''
|
|
||||||
if ifcObj.hwaddr:
|
|
||||||
h = self.createElement("address")
|
|
||||||
self.appendChild(h)
|
|
||||||
h.setAttribute("type", "mac")
|
|
||||||
htxt = self.createTextNode("%s" % ifcObj.hwaddr)
|
|
||||||
h.appendChild(htxt)
|
|
||||||
for addr in ifcObj.addrlist:
|
|
||||||
a = self.createElement("address")
|
|
||||||
self.appendChild(a)
|
|
||||||
(ip, sep, mask) = addr.partition('/')
|
|
||||||
# mask = int(mask) XXX?
|
|
||||||
if isIPv4Address(ip):
|
|
||||||
a.setAttribute("type", "IPv4")
|
|
||||||
else:
|
|
||||||
a.setAttribute("type", "IPv6")
|
|
||||||
|
|
||||||
# a.setAttribute("type", )
|
|
||||||
atxt = self.createTextNode("%s" % addr)
|
|
||||||
a.appendChild(atxt)
|
|
||||||
|
|
||||||
|
|
||||||
# XXX Remove?
|
|
||||||
def addModels(self, configs):
|
|
||||||
'''
|
|
||||||
Add models from a list of model-class, config values tuples.
|
|
||||||
'''
|
|
||||||
for (m, conf) in configs:
|
|
||||||
modelEle = self.createElement("model")
|
|
||||||
modelEle.setAttribute("name", m._name)
|
|
||||||
typeStr = "wireless"
|
|
||||||
if m._type == coreapi.CORE_TLV_REG_MOBILITY:
|
|
||||||
typeStr = "mobility"
|
|
||||||
modelEle.setAttribute("type", typeStr)
|
|
||||||
for i, k in enumerate(m.getnames()):
|
|
||||||
key = self.createElement(k)
|
|
||||||
value = conf[i]
|
|
||||||
if value is None:
|
|
||||||
value = ""
|
|
||||||
key.appendChild(self.createTextNode("%s" % value))
|
|
||||||
modelEle.appendChild(key)
|
|
||||||
self.appendChild(modelEle)
|
|
||||||
|
|
||||||
|
|
||||||
class MemberElement(XmlElement):
|
|
||||||
'''
|
|
||||||
Member elements are references to other elements in the network plan elements of the scenario.
|
|
||||||
They are used in networks to reference channels, in channels to reference interfaces,
|
|
||||||
and in interfaces to reference networks/channels. Member elements provided allow bi-directional
|
|
||||||
traversal of network plan components.
|
|
||||||
'''
|
|
||||||
def __init__(self, scenPlan, parent, referencedType, referencedId, index=None):
|
|
||||||
'''
|
|
||||||
Create a member element
|
|
||||||
'''
|
|
||||||
XmlElement.__init__(self, scenPlan.document, parent, "member")
|
|
||||||
self.setAttribute("type", "%s" % referencedType)
|
|
||||||
# See'Understanding the Network Modeling Framework document'
|
|
||||||
if index is not None:
|
|
||||||
self.setAttribute("index", "%d" % index)
|
|
||||||
self.appendChild(self.createTextNode("%s" % referencedId))
|
|
||||||
|
|
||||||
|
|
||||||
#
|
|
||||||
# =======================================================================================
|
|
||||||
# Helpers
|
|
||||||
# =======================================================================================
|
|
||||||
def getEndpoint(netObj, ifcObj):
|
|
||||||
'''
|
|
||||||
Create an Endpoint object given the network and the interface of interest
|
|
||||||
'''
|
|
||||||
ep = None
|
|
||||||
l2devport=None
|
|
||||||
|
|
||||||
# if ifcObj references an interface of a node and is part of this network
|
|
||||||
if ifcObj.net.objid == netObj.objid and hasattr(ifcObj,'node') and ifcObj.node:
|
|
||||||
params = ifcObj.getparams()
|
|
||||||
if isinstance(ifcObj.net, (nodes.HubNode, nodes.SwitchNode)):
|
|
||||||
l2devport="%s/e%d" % (ifcObj.net.name, ifcObj.net.getifindex(ifcObj))
|
|
||||||
ep = Endpoint(netObj,
|
|
||||||
ifcObj,
|
|
||||||
type = Attrib.MembType.INTERFACE,
|
|
||||||
id="%s/%s" % (ifcObj.node.name, ifcObj.name),
|
|
||||||
l2devport=l2devport,
|
|
||||||
params=params)
|
|
||||||
|
|
||||||
# else if ifcObj references another node and is connected to this network
|
|
||||||
elif hasattr(ifcObj,"othernet"):
|
|
||||||
if ifcObj.othernet.objid == netObj.objid:
|
|
||||||
# #hack used for upstream parameters for link between switches
|
|
||||||
# #(see LxBrNet.linknet())
|
|
||||||
ifcObj.swapparams('_params_up')
|
|
||||||
params = ifcObj.getparams()
|
|
||||||
ifcObj.swapparams('_params_up')
|
|
||||||
owner = ifcObj.net
|
|
||||||
l2devport="%s/e%d" % (ifcObj.othernet.name, ifcObj.othernet.getifindex(ifcObj))
|
|
||||||
|
|
||||||
# Create the endpoint.
|
|
||||||
# XXX the interface index might not match what is shown in the gui. For switches and hubs,
|
|
||||||
# The gui assigns its index but doesn't pass it to the daemon and vice versa.
|
|
||||||
# The gui stores it's index in the IMN file, which it reads and writes without daemon intervention.
|
|
||||||
# Fix this!
|
|
||||||
ep = Endpoint(owner,
|
|
||||||
ifcObj,
|
|
||||||
type = Attrib.MembType.INTERFACE,
|
|
||||||
id="%s/%s/e%d" % (netObj.name, owner.name, owner.getifindex(ifcObj)),
|
|
||||||
l2devport=l2devport,
|
|
||||||
params=params)
|
|
||||||
# else this node has an interface that belongs to another network
|
|
||||||
# i.e. a switch/hub interface connected to another switch/hub and CORE has the other switch/hub
|
|
||||||
# as the containing network
|
|
||||||
else :
|
|
||||||
ep = Endpoint(netObj, ifcObj,type=None, id=None, l2devport=None, params=None)
|
|
||||||
|
|
||||||
|
|
||||||
return ep
|
|
||||||
|
|
||||||
def getEndpoints(netObj):
|
|
||||||
'''
|
|
||||||
Gather all endpoints of the given network
|
|
||||||
'''
|
|
||||||
# Get all endpoints
|
|
||||||
endpoints = []
|
|
||||||
|
|
||||||
# XXX TODO: How to represent physical interfaces.
|
|
||||||
#
|
|
||||||
# NOTE: The following code works except it would be missing physical (rj45) interfaces from Pt2pt links
|
|
||||||
# TODO: Fix data in net.netifs to include Pt2Pt physical interfaces
|
|
||||||
#
|
|
||||||
# Iterate through all the nodes in the scenario, then iterate through all the interface for each node,
|
|
||||||
# and check if the interface is connected to this network.
|
|
||||||
|
|
||||||
for ifcObj in netObj.netifs(sort=True):
|
|
||||||
try:
|
|
||||||
ep = getEndpoint(netObj, ifcObj)
|
|
||||||
if ep is not None:
|
|
||||||
endpoints.append(ep)
|
|
||||||
except Exception:
|
|
||||||
pass
|
|
||||||
return endpoints
|
|
||||||
|
|
||||||
def getDowmstreamL2Devices(netObj):
|
|
||||||
'''
|
|
||||||
Helper function for getting a list of all downstream layer 2 devices from the given netObj
|
|
||||||
'''
|
|
||||||
l2devObjs = [netObj]
|
|
||||||
allendpoints = []
|
|
||||||
myendpoints = getEndpoints(netObj)
|
|
||||||
allendpoints.extend(myendpoints)
|
|
||||||
for ep in myendpoints:
|
|
||||||
if ep.type and ep.net.objid != netObj.objid:
|
|
||||||
l2s, eps = getDowmstreamL2Devices(ep.net)
|
|
||||||
l2devObjs.extend(l2s)
|
|
||||||
allendpoints.extend(eps)
|
|
||||||
|
|
||||||
return l2devObjs, allendpoints
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
def getAllNetworkInterfaces(session):
|
|
||||||
'''
|
|
||||||
Gather all network interfacecs in the session
|
|
||||||
'''
|
|
||||||
netifs = []
|
|
||||||
for node in session.objs():
|
|
||||||
for netif in node.netifs(sort=True):
|
|
||||||
if netif not in netifs:
|
|
||||||
netifs.append(netif)
|
|
||||||
return netifs
|
|
||||||
|
|
||||||
def inOtherNetwork(netObj):
|
|
||||||
'''
|
|
||||||
Determine if CORE considers a given network object to be part of another network.
|
|
||||||
Note: CORE considers layer 2 devices to be their own networks. However, if a l2 device
|
|
||||||
is connected to another device, it is possible that one of its ports belong to the other
|
|
||||||
l2 device's network (thus, "othernet").
|
|
||||||
'''
|
|
||||||
for netif in netObj.netifs(sort=True):
|
|
||||||
if hasattr(netif,"othernet"):
|
|
||||||
if netif.othernet.objid != netObj.objid:
|
|
||||||
return True
|
|
||||||
return False
|
|
||||||
File diff suppressed because it is too large
Load diff
|
|
@ -1,43 +1,76 @@
|
||||||
#
|
"""
|
||||||
# CORE
|
Definition of LxcNode, CoreNode, and other node classes that inherit from the CoreNode,
|
||||||
# Copyright (c)2010-2013 the Boeing Company.
|
implementing specific node types.
|
||||||
# See the LICENSE file included in this distribution.
|
"""
|
||||||
#
|
|
||||||
# authors: Tom Goff <thomas.goff@boeing.com>
|
|
||||||
# Jeff Ahrenholz <jeffrey.m.ahrenholz@boeing.com>
|
|
||||||
#
|
|
||||||
'''
|
|
||||||
nodes.py: definition of an LxcNode and CoreNode classes, and other node classes
|
|
||||||
that inherit from the CoreNode, implementing specific node types.
|
|
||||||
'''
|
|
||||||
|
|
||||||
from vnode import *
|
import socket
|
||||||
from vnet import *
|
import subprocess
|
||||||
from core.misc.ipaddr import *
|
import threading
|
||||||
from core.api import coreapi
|
from socket import AF_INET
|
||||||
|
from socket import AF_INET6
|
||||||
|
|
||||||
|
from core import constants
|
||||||
|
from core.coreobj import PyCoreNetIf
|
||||||
from core.coreobj import PyCoreNode
|
from core.coreobj import PyCoreNode
|
||||||
|
from core.coreobj import PyCoreObj
|
||||||
|
from core.data import LinkData
|
||||||
|
from core.enumerations import LinkTypes
|
||||||
|
from core.enumerations import NodeTypes
|
||||||
|
from core.enumerations import RegisterTlvs
|
||||||
|
from core.misc import ipaddress
|
||||||
|
from core.misc import log
|
||||||
|
from core.misc import utils
|
||||||
|
from core.netns.vnet import GreTapBridge
|
||||||
|
from core.netns.vnet import LxBrNet
|
||||||
|
from core.netns.vnode import LxcNode
|
||||||
|
|
||||||
|
logger = log.get_logger(__name__)
|
||||||
|
|
||||||
|
|
||||||
class CtrlNet(LxBrNet):
|
class CtrlNet(LxBrNet):
|
||||||
|
"""
|
||||||
|
Control network functionality.
|
||||||
|
"""
|
||||||
policy = "ACCEPT"
|
policy = "ACCEPT"
|
||||||
CTRLIF_IDX_BASE = 99 # base control interface index
|
# base control interface index
|
||||||
DEFAULT_PREFIX_LIST = ["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",
|
CTRLIF_IDX_BASE = 99
|
||||||
|
DEFAULT_PREFIX_LIST = [
|
||||||
|
"172.16.0.0/24 172.16.1.0/24 172.16.2.0/24 172.16.3.0/24 172.16.4.0/24",
|
||||||
"172.17.0.0/24 172.17.1.0/24 172.17.2.0/24 172.17.3.0/24 172.17.4.0/24",
|
"172.17.0.0/24 172.17.1.0/24 172.17.2.0/24 172.17.3.0/24 172.17.4.0/24",
|
||||||
"172.18.0.0/24 172.18.1.0/24 172.18.2.0/24 172.18.3.0/24 172.18.4.0/24",
|
"172.18.0.0/24 172.18.1.0/24 172.18.2.0/24 172.18.3.0/24 172.18.4.0/24",
|
||||||
"172.19.0.0/24 172.19.1.0/24 172.19.2.0/24 172.19.3.0/24 172.19.4.0/24"]
|
"172.19.0.0/24 172.19.1.0/24 172.19.2.0/24 172.19.3.0/24 172.19.4.0/24"
|
||||||
|
]
|
||||||
|
|
||||||
def __init__(self, session, objid = "ctrlnet", name = None,
|
def __init__(self, session, objid="ctrlnet", name=None, prefix=None,
|
||||||
verbose = False, prefix = None,
|
|
||||||
hostid=None, start=True, assign_address=True,
|
hostid=None, start=True, assign_address=True,
|
||||||
updown_script=None, serverintf=None):
|
updown_script=None, serverintf=None):
|
||||||
self.prefix = IPv4Prefix(prefix)
|
"""
|
||||||
|
Creates a CtrlNet instance.
|
||||||
|
|
||||||
|
:param core.session.Session session: core session instance
|
||||||
|
:param int objid: node id
|
||||||
|
:param str name: node namee
|
||||||
|
:param prefix: control network ipv4 prefix
|
||||||
|
:param hostid: host id
|
||||||
|
:param bool start: start flag
|
||||||
|
:param str assign_address: assigned address
|
||||||
|
:param str updown_script: updown script
|
||||||
|
:param serverintf: server interface
|
||||||
|
:return:
|
||||||
|
"""
|
||||||
|
self.prefix = ipaddress.Ipv4Prefix(prefix)
|
||||||
self.hostid = hostid
|
self.hostid = hostid
|
||||||
self.assign_address = assign_address
|
self.assign_address = assign_address
|
||||||
self.updown_script = updown_script
|
self.updown_script = updown_script
|
||||||
self.serverintf = serverintf
|
self.serverintf = serverintf
|
||||||
LxBrNet.__init__(self, session, objid = objid, name = name,
|
LxBrNet.__init__(self, session, objid=objid, name=name, start=start)
|
||||||
verbose = verbose, start = start)
|
|
||||||
|
|
||||||
def startup(self):
|
def startup(self):
|
||||||
|
"""
|
||||||
|
Startup functionality for the control network.
|
||||||
|
|
||||||
|
:return: nothing
|
||||||
|
"""
|
||||||
if self.detectoldbridge():
|
if self.detectoldbridge():
|
||||||
return
|
return
|
||||||
|
|
||||||
|
|
@ -45,36 +78,37 @@ class CtrlNet(LxBrNet):
|
||||||
if self.hostid:
|
if self.hostid:
|
||||||
addr = self.prefix.addr(self.hostid)
|
addr = self.prefix.addr(self.hostid)
|
||||||
else:
|
else:
|
||||||
addr = self.prefix.maxaddr()
|
addr = self.prefix.max_addr()
|
||||||
msg = "Added control network bridge: %s %s" % \
|
msg = "Added control network bridge: %s %s" % \
|
||||||
(self.brname, self.prefix)
|
(self.brname, self.prefix)
|
||||||
addrlist = ["%s/%s" % (addr, self.prefix.prefixlen)]
|
addrlist = ["%s/%s" % (addr, self.prefix.prefixlen)]
|
||||||
if self.assign_address:
|
if self.assign_address:
|
||||||
self.addrconfig(addrlist=addrlist)
|
self.addrconfig(addrlist=addrlist)
|
||||||
msg += " address %s" % addr
|
msg += " address %s" % addr
|
||||||
self.session.info(msg)
|
logger.info(msg)
|
||||||
if self.updown_script is not None:
|
if self.updown_script is not None:
|
||||||
self.info("interface %s updown script '%s startup' called" % \
|
logger.info("interface %s updown script '%s startup' called" % \
|
||||||
(self.brname, self.updown_script))
|
(self.brname, self.updown_script))
|
||||||
check_call([self.updown_script, self.brname, "startup"])
|
subprocess.check_call([self.updown_script, self.brname, "startup"])
|
||||||
if self.serverintf is not None:
|
if self.serverintf is not None:
|
||||||
try:
|
try:
|
||||||
check_call([BRCTL_BIN, "addif", self.brname, self.serverintf])
|
subprocess.check_call([constants.BRCTL_BIN, "addif", self.brname, self.serverintf])
|
||||||
check_call([IP_BIN, "link", "set", self.serverintf, "up"])
|
subprocess.check_call([constants.IP_BIN, "link", "set", self.serverintf, "up"])
|
||||||
except Exception, e:
|
except subprocess.CalledProcessError:
|
||||||
self.exception(coreapi.CORE_EXCP_LEVEL_FATAL, self.brname,
|
logger.exception("Error joining server interface %s to controlnet bridge %s",
|
||||||
"Error joining server interface %s to controlnet bridge %s: %s" % \
|
self.serverintf, self.brname)
|
||||||
(self.serverintf, self.brname, e))
|
|
||||||
|
|
||||||
|
|
||||||
def detectoldbridge(self):
|
def detectoldbridge(self):
|
||||||
''' Occassionally, control net bridges from previously closed sessions are not cleaned up.
|
"""
|
||||||
|
Occassionally, control net bridges from previously closed sessions are not cleaned up.
|
||||||
Check if there are old control net bridges and delete them
|
Check if there are old control net bridges and delete them
|
||||||
'''
|
|
||||||
retstat, retstr = cmdresult([BRCTL_BIN,'show'])
|
:return: True if an old bridge was detected, False otherwise
|
||||||
|
:rtype: bool
|
||||||
|
"""
|
||||||
|
retstat, retstr = utils.cmdresult([constants.BRCTL_BIN, 'show'])
|
||||||
if retstat != 0:
|
if retstat != 0:
|
||||||
self.exception(coreapi.CORE_EXCP_LEVEL_FATAL, None,
|
logger.error("Unable to retrieve list of installed bridges")
|
||||||
"Unable to retrieve list of installed bridges")
|
|
||||||
lines = retstr.split('\n')
|
lines = retstr.split('\n')
|
||||||
for line in lines[1:]:
|
for line in lines[1:]:
|
||||||
cols = line.split('\t')
|
cols = line.split('\t')
|
||||||
|
|
@ -82,246 +116,344 @@ class CtrlNet(LxBrNet):
|
||||||
flds = cols[0].split('.')
|
flds = cols[0].split('.')
|
||||||
if len(flds) == 3:
|
if len(flds) == 3:
|
||||||
if flds[0] == 'b' and flds[1] == self.objid:
|
if flds[0] == 'b' and flds[1] == self.objid:
|
||||||
self.session.exception(coreapi.CORE_EXCP_LEVEL_FATAL, "CtrlNet.startup()", None,
|
logger.error(
|
||||||
"Error: An active control net bridge (%s) found. " \
|
"Error: An active control net bridge (%s) found. " \
|
||||||
"An older session might still be running. " \
|
"An older session might still be running. " \
|
||||||
"Stop all sessions and, if needed, delete %s to continue." % \
|
"Stop all sessions and, if needed, delete %s to continue." % \
|
||||||
(oldbr, oldbr))
|
(oldbr, oldbr)
|
||||||
|
)
|
||||||
return True
|
return True
|
||||||
'''
|
"""
|
||||||
# Do this if we want to delete the old bridge
|
# Do this if we want to delete the old bridge
|
||||||
self.warn("Warning: Old %s bridge found: %s" % (self.objid, oldbr))
|
logger.warn("Warning: Old %s bridge found: %s" % (self.objid, oldbr))
|
||||||
try:
|
try:
|
||||||
check_call([BRCTL_BIN, 'delbr', oldbr])
|
check_call([BRCTL_BIN, 'delbr', oldbr])
|
||||||
except Exception, e:
|
except subprocess.CalledProcessError as e:
|
||||||
self.exception(coreapi.CORE_EXCP_LEVEL_ERROR, oldbr,
|
logger.exception("Error deleting old bridge %s", oldbr, e)
|
||||||
"Error deleting old bridge %s" % oldbr)
|
logger.info("Deleted %s", oldbr)
|
||||||
self.info("Deleted %s" % oldbr)
|
"""
|
||||||
'''
|
|
||||||
return False
|
return False
|
||||||
|
|
||||||
def shutdown(self):
|
def shutdown(self):
|
||||||
|
"""
|
||||||
|
Control network shutdown.
|
||||||
|
|
||||||
|
:return: nothing
|
||||||
|
"""
|
||||||
if self.serverintf is not None:
|
if self.serverintf is not None:
|
||||||
try:
|
try:
|
||||||
check_call([BRCTL_BIN, "delif", self.brname, self.serverintf])
|
subprocess.check_call([constants.BRCTL_BIN, "delif", self.brname, self.serverintf])
|
||||||
except Exception, e:
|
except subprocess.CalledProcessError:
|
||||||
self.exception(coreapi.CORE_EXCP_LEVEL_ERROR, self.brname,
|
logger.exception("Error deleting server interface %s to controlnet bridge %s",
|
||||||
"Error deleting server interface %s to controlnet bridge %s: %s" % \
|
self.serverintf, self.brname)
|
||||||
(self.serverintf, self.brname, e))
|
|
||||||
|
|
||||||
if self.updown_script is not None:
|
if self.updown_script is not None:
|
||||||
self.info("interface %s updown script '%s shutdown' called" % \
|
logger.info("interface %s updown script '%s shutdown' called" % (self.brname, self.updown_script))
|
||||||
(self.brname, self.updown_script))
|
subprocess.check_call([self.updown_script, self.brname, "shutdown"])
|
||||||
check_call([self.updown_script, self.brname, "shutdown"])
|
|
||||||
LxBrNet.shutdown(self)
|
LxBrNet.shutdown(self)
|
||||||
|
|
||||||
def tolinkmsgs(self, flags):
|
def all_link_data(self, flags):
|
||||||
''' Do not include CtrlNet in link messages describing this session.
|
"""
|
||||||
'''
|
Do not include CtrlNet in link messages describing this session.
|
||||||
|
|
||||||
|
:return: nothing
|
||||||
|
"""
|
||||||
return []
|
return []
|
||||||
|
|
||||||
|
|
||||||
class CoreNode(LxcNode):
|
class CoreNode(LxcNode):
|
||||||
apitype = coreapi.CORE_NODE_DEF
|
"""
|
||||||
|
Basic core node class for nodes to extend.
|
||||||
|
"""
|
||||||
|
apitype = NodeTypes.DEFAULT.value
|
||||||
|
|
||||||
|
|
||||||
class PtpNet(LxBrNet):
|
class PtpNet(LxBrNet):
|
||||||
|
"""
|
||||||
|
Peer to peer network node.
|
||||||
|
"""
|
||||||
policy = "ACCEPT"
|
policy = "ACCEPT"
|
||||||
|
|
||||||
def attach(self, netif):
|
def attach(self, netif):
|
||||||
if len(self._netif) > 1:
|
"""
|
||||||
raise ValueError, \
|
Attach a network interface, but limit attachment to two interfaces.
|
||||||
"Point-to-point links support at most 2 network interfaces"
|
|
||||||
|
:param core.coreobj.PyCoreNetIf netif: network interface
|
||||||
|
:return: nothing
|
||||||
|
"""
|
||||||
|
if len(self._netif) >= 2:
|
||||||
|
raise ValueError("Point-to-point links support at most 2 network interfaces")
|
||||||
LxBrNet.attach(self, netif)
|
LxBrNet.attach(self, netif)
|
||||||
|
|
||||||
def tonodemsg(self, flags):
|
def data(self, message_type):
|
||||||
''' Do not generate a Node Message for point-to-point links. They are
|
"""
|
||||||
|
Do not generate a Node Message for point-to-point links. They are
|
||||||
built using a link message instead.
|
built using a link message instead.
|
||||||
'''
|
|
||||||
|
:return: nothing
|
||||||
|
"""
|
||||||
pass
|
pass
|
||||||
|
|
||||||
def tolinkmsgs(self, flags):
|
def all_link_data(self, flags):
|
||||||
''' Build CORE API TLVs for a point-to-point link. One Link message
|
"""
|
||||||
|
Build CORE API TLVs for a point-to-point link. One Link message
|
||||||
describes this network.
|
describes this network.
|
||||||
'''
|
|
||||||
tlvdata = ""
|
:return: all link data
|
||||||
|
:rtype: list[LinkData]
|
||||||
|
"""
|
||||||
|
|
||||||
|
all_links = []
|
||||||
|
|
||||||
if len(self._netif) != 2:
|
if len(self._netif) != 2:
|
||||||
return tlvdata
|
return all_links
|
||||||
(if1, if2) = self._netif.items()
|
|
||||||
|
if1, if2 = self._netif.items()
|
||||||
if1 = if1[1]
|
if1 = if1[1]
|
||||||
if2 = if2[1]
|
if2 = if2[1]
|
||||||
tlvdata += coreapi.CoreLinkTlv.pack(coreapi.CORE_TLV_LINK_N1NUMBER,
|
|
||||||
if1.node.objid)
|
unidirectional = 0
|
||||||
tlvdata += coreapi.CoreLinkTlv.pack(coreapi.CORE_TLV_LINK_N2NUMBER,
|
|
||||||
if2.node.objid)
|
|
||||||
uni = False
|
|
||||||
if if1.getparams() != if2.getparams():
|
if if1.getparams() != if2.getparams():
|
||||||
uni = True
|
unidirectional = 1
|
||||||
tlvdata += self.netifparamstolink(if1)
|
|
||||||
tlvdata += coreapi.CoreLinkTlv.pack(coreapi.CORE_TLV_LINK_TYPE,
|
|
||||||
self.linktype)
|
|
||||||
if uni:
|
|
||||||
tlvdata += coreapi.CoreLinkTlv.pack(coreapi.CORE_TLV_LINK_UNI, 1)
|
|
||||||
|
|
||||||
tlvdata += coreapi.CoreLinkTlv.pack(coreapi.CORE_TLV_LINK_IF1NUM, \
|
interface1_ip4 = None
|
||||||
if1.node.getifindex(if1))
|
interface1_ip4_mask = None
|
||||||
if if1.hwaddr:
|
interface1_ip6 = None
|
||||||
tlvdata += coreapi.CoreLinkTlv.pack(coreapi.CORE_TLV_LINK_IF1MAC,
|
interface1_ip6_mask = None
|
||||||
if1.hwaddr)
|
for address in if1.addrlist:
|
||||||
for addr in if1.addrlist:
|
ip, sep, mask = address.partition('/')
|
||||||
(ip, sep, mask) = addr.partition('/')
|
|
||||||
mask = int(mask)
|
mask = int(mask)
|
||||||
if isIPv4Address(ip):
|
if ipaddress.is_ipv4_address(ip):
|
||||||
family = AF_INET
|
family = AF_INET
|
||||||
tlvtypeip = coreapi.CORE_TLV_LINK_IF1IP4
|
ipl = socket.inet_pton(family, ip)
|
||||||
tlvtypemask = coreapi.CORE_TLV_LINK_IF1IP4MASK
|
interface1_ip4 = ipaddress.IpAddress(af=family, address=ipl)
|
||||||
|
interface1_ip4_mask = mask
|
||||||
else:
|
else:
|
||||||
family = AF_INET6
|
family = AF_INET6
|
||||||
tlvtypeip = coreapi.CORE_TLV_LINK_IF1IP6
|
|
||||||
tlvtypemask = coreapi.CORE_TLV_LINK_IF1IP6MASK
|
|
||||||
ipl = socket.inet_pton(family, ip)
|
ipl = socket.inet_pton(family, ip)
|
||||||
tlvdata += coreapi.CoreLinkTlv.pack(tlvtypeip,
|
interface1_ip6 = ipaddress.IpAddress(af=family, address=ipl)
|
||||||
IPAddr(af=family, addr=ipl))
|
interface1_ip6_mask = mask
|
||||||
tlvdata += coreapi.CoreLinkTlv.pack(tlvtypemask, mask)
|
|
||||||
|
|
||||||
tlvdata += coreapi.CoreLinkTlv.pack(coreapi.CORE_TLV_LINK_IF2NUM, \
|
interface2_ip4 = None
|
||||||
if2.node.getifindex(if2))
|
interface2_ip4_mask = None
|
||||||
if if2.hwaddr:
|
interface2_ip6 = None
|
||||||
tlvdata += coreapi.CoreLinkTlv.pack(coreapi.CORE_TLV_LINK_IF2MAC,
|
interface2_ip6_mask = None
|
||||||
if2.hwaddr)
|
for address in if2.addrlist:
|
||||||
for addr in if2.addrlist:
|
ip, sep, mask = address.partition('/')
|
||||||
(ip, sep, mask) = addr.partition('/')
|
|
||||||
mask = int(mask)
|
mask = int(mask)
|
||||||
if isIPv4Address(ip):
|
if ipaddress.is_ipv4_address(ip):
|
||||||
family = AF_INET
|
family = AF_INET
|
||||||
tlvtypeip = coreapi.CORE_TLV_LINK_IF2IP4
|
ipl = socket.inet_pton(family, ip)
|
||||||
tlvtypemask = coreapi.CORE_TLV_LINK_IF2IP4MASK
|
interface2_ip4 = ipaddress.IpAddress(af=family, address=ipl)
|
||||||
|
interface2_ip4_mask = mask
|
||||||
else:
|
else:
|
||||||
family = AF_INET6
|
family = AF_INET6
|
||||||
tlvtypeip = coreapi.CORE_TLV_LINK_IF2IP6
|
|
||||||
tlvtypemask = coreapi.CORE_TLV_LINK_IF2IP6MASK
|
|
||||||
ipl = socket.inet_pton(family, ip)
|
ipl = socket.inet_pton(family, ip)
|
||||||
tlvdata += coreapi.CoreLinkTlv.pack(tlvtypeip,
|
interface2_ip6 = ipaddress.IpAddress(af=family, address=ipl)
|
||||||
IPAddr(af=family, addr=ipl))
|
interface2_ip6_mask = mask
|
||||||
tlvdata += coreapi.CoreLinkTlv.pack(tlvtypemask, mask)
|
|
||||||
msg = coreapi.CoreLinkMessage.pack(flags, tlvdata)
|
# TODO: not currently used
|
||||||
if not uni:
|
# loss=netif.getparam('loss')
|
||||||
return [msg,]
|
link_data = LinkData(
|
||||||
|
message_type=flags,
|
||||||
|
node1_id=if1.node.objid,
|
||||||
|
node2_id=if2.node.objid,
|
||||||
|
link_type=self.linktype,
|
||||||
|
unidirectional=unidirectional,
|
||||||
|
delay=if1.getparam("delay"),
|
||||||
|
bandwidth=if1.getparam("bw"),
|
||||||
|
dup=if1.getparam("duplicate"),
|
||||||
|
jitter=if1.getparam("jitter"),
|
||||||
|
interface1_id=if1.node.getifindex(if1),
|
||||||
|
interface1_mac=if1.hwaddr,
|
||||||
|
interface1_ip4=interface1_ip4,
|
||||||
|
interface1_ip4_mask=interface1_ip4_mask,
|
||||||
|
interface1_ip6=interface1_ip6,
|
||||||
|
interface1_ip6_mask=interface1_ip6_mask,
|
||||||
|
interface2_id=if2.node.getifindex(if2),
|
||||||
|
interface2_mac=if2.hwaddr,
|
||||||
|
interface2_ip4=interface2_ip4,
|
||||||
|
interface2_ip4_mask=interface2_ip4_mask,
|
||||||
|
interface2_ip6=interface2_ip6,
|
||||||
|
interface2_ip6_mask=interface2_ip6_mask,
|
||||||
|
)
|
||||||
|
|
||||||
|
all_links.append(link_data)
|
||||||
|
|
||||||
# build a 2nd link message for the upstream link parameters
|
# build a 2nd link message for the upstream link parameters
|
||||||
# (swap if1 and if2)
|
# (swap if1 and if2)
|
||||||
tlvdata = ""
|
if unidirectional:
|
||||||
tlvdata += coreapi.CoreLinkTlv.pack(coreapi.CORE_TLV_LINK_N1NUMBER,
|
link_data = LinkData(
|
||||||
if2.node.objid)
|
message_type=0,
|
||||||
tlvdata += coreapi.CoreLinkTlv.pack(coreapi.CORE_TLV_LINK_N2NUMBER,
|
node1_id=if2.node.objid,
|
||||||
if1.node.objid)
|
node2_id=if1.node.objid,
|
||||||
tlvdata += self.netifparamstolink(if2)
|
delay=if1.getparam("delay"),
|
||||||
tlvdata += coreapi.CoreLinkTlv.pack(coreapi.CORE_TLV_LINK_UNI, 1)
|
bandwidth=if1.getparam("bw"),
|
||||||
tlvdata += coreapi.CoreLinkTlv.pack(coreapi.CORE_TLV_LINK_IF1NUM, \
|
dup=if1.getparam("duplicate"),
|
||||||
if2.node.getifindex(if2))
|
jitter=if1.getparam("jitter"),
|
||||||
tlvdata += coreapi.CoreLinkTlv.pack(coreapi.CORE_TLV_LINK_IF2NUM, \
|
unidirectional=1,
|
||||||
if1.node.getifindex(if1))
|
interface1_id=if2.node.getifindex(if2),
|
||||||
msg2 = coreapi.CoreLinkMessage.pack(0, tlvdata)
|
interface2_id=if1.node.getifindex(if1)
|
||||||
return [msg, msg2]
|
)
|
||||||
|
all_links.append(link_data)
|
||||||
|
|
||||||
|
return all_links
|
||||||
|
|
||||||
|
|
||||||
class SwitchNode(LxBrNet):
|
class SwitchNode(LxBrNet):
|
||||||
apitype = coreapi.CORE_NODE_SWITCH
|
"""
|
||||||
|
Provides switch functionality within a core node.
|
||||||
|
"""
|
||||||
|
apitype = NodeTypes.SWITCH.value
|
||||||
policy = "ACCEPT"
|
policy = "ACCEPT"
|
||||||
type = "lanswitch"
|
type = "lanswitch"
|
||||||
|
|
||||||
|
|
||||||
class HubNode(LxBrNet):
|
class HubNode(LxBrNet):
|
||||||
apitype = coreapi.CORE_NODE_HUB
|
"""
|
||||||
|
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"
|
policy = "ACCEPT"
|
||||||
type = "hub"
|
type = "hub"
|
||||||
|
|
||||||
def __init__(self, session, objid = None, name = None, verbose = False,
|
def __init__(self, session, objid=None, name=None, start=True):
|
||||||
start = True):
|
"""
|
||||||
''' the Hub node forwards packets to all bridge ports by turning off
|
Creates a HubNode instance.
|
||||||
the MAC address learning
|
|
||||||
'''
|
:param core.session.Session session: core session instance
|
||||||
LxBrNet.__init__(self, session, objid, name, verbose, start)
|
:param int objid: node id
|
||||||
|
:param str name: node namee
|
||||||
|
:param bool start: start flag
|
||||||
|
"""
|
||||||
|
LxBrNet.__init__(self, session, objid, name, start)
|
||||||
if start:
|
if start:
|
||||||
check_call([BRCTL_BIN, "setageing", self.brname, "0"])
|
subprocess.check_call([constants.BRCTL_BIN, "setageing", self.brname, "0"])
|
||||||
|
|
||||||
|
|
||||||
class WlanNode(LxBrNet):
|
class WlanNode(LxBrNet):
|
||||||
apitype = coreapi.CORE_NODE_WLAN
|
"""
|
||||||
linktype = coreapi.CORE_LINK_WIRELESS
|
Provides wireless lan functionality within a core node.
|
||||||
|
"""
|
||||||
|
apitype = NodeTypes.WIRELESS_LAN.value
|
||||||
|
linktype = LinkTypes.WIRELESS.value
|
||||||
policy = "DROP"
|
policy = "DROP"
|
||||||
type = "wlan"
|
type = "wlan"
|
||||||
|
|
||||||
def __init__(self, session, objid = None, name = None, verbose = False,
|
def __init__(self, session, objid=None, name=None, start=True, policy=None):
|
||||||
start = True, policy = None):
|
"""
|
||||||
LxBrNet.__init__(self, session, objid, name, verbose, start, policy)
|
Create a WlanNode instance.
|
||||||
|
|
||||||
|
:param core.session.Session session: core session instance
|
||||||
|
:param int objid: node id
|
||||||
|
:param str name: node name
|
||||||
|
:param bool start: start flag
|
||||||
|
:param policy: wlan policy
|
||||||
|
"""
|
||||||
|
LxBrNet.__init__(self, session, objid, name, start, policy)
|
||||||
# wireless model such as basic range
|
# wireless model such as basic range
|
||||||
self.model = None
|
self.model = None
|
||||||
# mobility model such as scripted
|
# mobility model such as scripted
|
||||||
self.mobility = None
|
self.mobility = None
|
||||||
|
|
||||||
def attach(self, netif):
|
def attach(self, netif):
|
||||||
|
"""
|
||||||
|
Attach a network interface.
|
||||||
|
|
||||||
|
:param core.coreobj.PyCoreNetIf netif: network interface
|
||||||
|
:return: nothing
|
||||||
|
"""
|
||||||
LxBrNet.attach(self, netif)
|
LxBrNet.attach(self, netif)
|
||||||
if self.model:
|
if self.model:
|
||||||
netif.poshook = self.model._positioncallback
|
netif.poshook = self.model.position_callback
|
||||||
if netif.node is None:
|
if netif.node is None:
|
||||||
return
|
return
|
||||||
(x,y,z) = netif.node.position.get()
|
x, y, z = netif.node.position.get()
|
||||||
# invokes any netif.poshook
|
# invokes any netif.poshook
|
||||||
netif.setposition(x, y, z)
|
netif.setposition(x, y, z)
|
||||||
# self.model.setlinkparams()
|
# self.model.setlinkparams()
|
||||||
|
|
||||||
def setmodel(self, model, config):
|
def setmodel(self, model, config):
|
||||||
''' Mobility and wireless model.
|
"""
|
||||||
'''
|
Sets the mobility and wireless model.
|
||||||
if (self.verbose):
|
|
||||||
self.info("adding model %s" % model._name)
|
:param core.mobility.WirelessModel.cls model: wireless model to set to
|
||||||
if model._type == coreapi.CORE_TLV_REG_WIRELESS:
|
:param config: model configuration
|
||||||
self.model = model(session=self.session, objid=self.objid,
|
:return: nothing
|
||||||
verbose=self.verbose, values=config)
|
"""
|
||||||
if self.model._positioncallback:
|
logger.info("adding model %s" % model.name)
|
||||||
|
if model.config_type == RegisterTlvs.WIRELESS.value:
|
||||||
|
self.model = model(session=self.session, object_id=self.objid, values=config)
|
||||||
|
if self.model.position_callback:
|
||||||
for netif in self.netifs():
|
for netif in self.netifs():
|
||||||
netif.poshook = self.model._positioncallback
|
netif.poshook = self.model.position_callback
|
||||||
if netif.node is not None:
|
if netif.node is not None:
|
||||||
(x,y,z) = netif.node.position.get()
|
x, y, z = netif.node.position.get()
|
||||||
netif.poshook(netif, x, y, z)
|
netif.poshook(netif, x, y, z)
|
||||||
self.model.setlinkparams()
|
self.model.setlinkparams()
|
||||||
elif model._type == coreapi.CORE_TLV_REG_MOBILITY:
|
elif model.config_type == RegisterTlvs.MOBILITY.value:
|
||||||
self.mobility = model(session=self.session, objid=self.objid,
|
self.mobility = model(session=self.session, object_id=self.objid, values=config)
|
||||||
verbose=self.verbose, values=config)
|
|
||||||
|
|
||||||
def updatemodel(self, model_name, values):
|
def updatemodel(self, model_name, values):
|
||||||
''' Allow for model updates during runtime (similar to setmodel().)
|
"""
|
||||||
'''
|
Allow for model updates during runtime (similar to setmodel().)
|
||||||
if (self.verbose):
|
|
||||||
self.info("updating model %s" % model_name)
|
:param model_name: model name to update
|
||||||
if self.model is None or self.model._name != model_name:
|
:param values: values to update model with
|
||||||
|
:return: nothing
|
||||||
|
"""
|
||||||
|
logger.info("updating model %s" % model_name)
|
||||||
|
if self.model is None or self.model.name != model_name:
|
||||||
return
|
return
|
||||||
model = self.model
|
model = self.model
|
||||||
if model._type == coreapi.CORE_TLV_REG_WIRELESS:
|
if model.config_type == RegisterTlvs.WIRELESS.value:
|
||||||
if not model.updateconfig(values):
|
if not model.updateconfig(values):
|
||||||
return
|
return
|
||||||
if self.model._positioncallback:
|
if self.model.position_callback:
|
||||||
for netif in self.netifs():
|
for netif in self.netifs():
|
||||||
netif.poshook = self.model._positioncallback
|
netif.poshook = self.model.position_callback
|
||||||
if netif.node is not None:
|
if netif.node is not None:
|
||||||
(x, y, z) = netif.node.position.get()
|
(x, y, z) = netif.node.position.get()
|
||||||
netif.poshook(netif, x, y, z)
|
netif.poshook(netif, x, y, z)
|
||||||
self.model.setlinkparams()
|
self.model.setlinkparams()
|
||||||
|
|
||||||
def tolinkmsgs(self, flags):
|
def all_link_data(self, flags):
|
||||||
msgs = LxBrNet.tolinkmsgs(self, flags)
|
"""
|
||||||
|
Retrieve all link data.
|
||||||
|
|
||||||
|
:param flags: link flags
|
||||||
|
:return: all link data
|
||||||
|
:rtype: list[LinkData]
|
||||||
|
"""
|
||||||
|
all_links = LxBrNet.all_link_data(self, flags)
|
||||||
|
|
||||||
if self.model:
|
if self.model:
|
||||||
msgs += self.model.tolinkmsgs(flags)
|
all_links.extend(self.model.all_link_data(flags))
|
||||||
return msgs
|
|
||||||
|
return all_links
|
||||||
|
|
||||||
|
|
||||||
class RJ45Node(PyCoreNode, PyCoreNetIf):
|
class RJ45Node(PyCoreNode, PyCoreNetIf):
|
||||||
''' RJ45Node is a physical interface on the host linked to the emulated
|
"""
|
||||||
|
RJ45Node is a physical interface on the host linked to the emulated
|
||||||
network.
|
network.
|
||||||
'''
|
"""
|
||||||
apitype = coreapi.CORE_NODE_RJ45
|
apitype = NodeTypes.RJ45.value
|
||||||
type = "rj45"
|
type = "rj45"
|
||||||
|
|
||||||
def __init__(self, session, objid = None, name = None, mtu = 1500,
|
def __init__(self, session, objid=None, name=None, mtu=1500, start=True):
|
||||||
verbose = False, start = True):
|
"""
|
||||||
PyCoreNode.__init__(self, session, objid, name, verbose=verbose,
|
Create an RJ45Node instance.
|
||||||
start=start)
|
|
||||||
|
:param core.session.Session session: core session instance
|
||||||
|
:param int objid: node id
|
||||||
|
:param str name: node name
|
||||||
|
:param mtu: rj45 mtu
|
||||||
|
:param bool start: start flag
|
||||||
|
:return:
|
||||||
|
"""
|
||||||
|
PyCoreNode.__init__(self, session, objid, name, start=start)
|
||||||
# this initializes net, params, poshook
|
# this initializes net, params, poshook
|
||||||
PyCoreNetIf.__init__(self, node=self, name=name, mtu=mtu)
|
PyCoreNetIf.__init__(self, node=self, name=name, mtu=mtu)
|
||||||
self.up = False
|
self.up = False
|
||||||
|
|
@ -334,65 +466,101 @@ class RJ45Node(PyCoreNode, PyCoreNetIf):
|
||||||
self.startup()
|
self.startup()
|
||||||
|
|
||||||
def startup(self):
|
def startup(self):
|
||||||
''' Set the interface in the up state.
|
"""
|
||||||
'''
|
Set the interface in the up state.
|
||||||
|
|
||||||
|
:return: nothing
|
||||||
|
"""
|
||||||
# interface will also be marked up during net.attach()
|
# interface will also be marked up during net.attach()
|
||||||
self.savestate()
|
self.savestate()
|
||||||
|
|
||||||
try:
|
try:
|
||||||
check_call([IP_BIN, "link", "set", self.localname, "up"])
|
subprocess.check_call([constants.IP_BIN, "link", "set", self.localname, "up"])
|
||||||
except:
|
|
||||||
self.warn("Failed to run command: %s link set %s up" % \
|
|
||||||
(IP_BIN, self.localname))
|
|
||||||
return
|
|
||||||
self.up = True
|
self.up = True
|
||||||
|
except subprocess.CalledProcessError:
|
||||||
|
logger.exception("failed to run command: %s link set %s up", constants.IP_BIN, self.localname)
|
||||||
|
|
||||||
def shutdown(self):
|
def shutdown(self):
|
||||||
''' Bring the interface down. Remove any addresses and queuing
|
"""
|
||||||
|
Bring the interface down. Remove any addresses and queuing
|
||||||
disciplines.
|
disciplines.
|
||||||
'''
|
|
||||||
|
:return: nothing
|
||||||
|
"""
|
||||||
if not self.up:
|
if not self.up:
|
||||||
return
|
return
|
||||||
check_call([IP_BIN, "link", "set", self.localname, "down"])
|
subprocess.check_call([constants.IP_BIN, "link", "set", self.localname, "down"])
|
||||||
check_call([IP_BIN, "addr", "flush", "dev", self.localname])
|
subprocess.check_call([constants.IP_BIN, "addr", "flush", "dev", self.localname])
|
||||||
mutecall([TC_BIN, "qdisc", "del", "dev", self.localname, "root"])
|
utils.mutecall([constants.TC_BIN, "qdisc", "del", "dev", self.localname, "root"])
|
||||||
self.up = False
|
self.up = False
|
||||||
self.restorestate()
|
self.restorestate()
|
||||||
|
|
||||||
|
# TODO: issue in that both classes inherited from provide the same method with different signatures
|
||||||
def attachnet(self, net):
|
def attachnet(self, net):
|
||||||
|
"""
|
||||||
|
Attach a network.
|
||||||
|
|
||||||
|
:param core.coreobj.PyCoreNet net: network to attach
|
||||||
|
:return: nothing
|
||||||
|
"""
|
||||||
PyCoreNetIf.attachnet(self, net)
|
PyCoreNetIf.attachnet(self, net)
|
||||||
|
|
||||||
def detachnet(self):
|
def detachnet(self):
|
||||||
|
"""
|
||||||
|
Detach a network.
|
||||||
|
|
||||||
|
:return: nothing
|
||||||
|
"""
|
||||||
PyCoreNetIf.detachnet(self)
|
PyCoreNetIf.detachnet(self)
|
||||||
|
|
||||||
def newnetif(self, net = None, addrlist = [], hwaddr = None,
|
# TODO: parameters are not used
|
||||||
ifindex = None, ifname = None):
|
def newnetif(self, net=None, addrlist=None, hwaddr=None, ifindex=None, ifname=None):
|
||||||
''' This is called when linking with another node. Since this node
|
"""
|
||||||
|
This is called when linking with another node. Since this node
|
||||||
represents an interface, we do not create another object here,
|
represents an interface, we do not create another object here,
|
||||||
but attach ourselves to the given network.
|
but attach ourselves to the given network.
|
||||||
'''
|
|
||||||
self.lock.acquire()
|
:param core.coreobj.PyCoreNet net: new network instance
|
||||||
try:
|
:param list[str] addrlist: address list
|
||||||
|
:param str hwaddr: hardware address
|
||||||
|
:param int ifindex: interface index
|
||||||
|
:param str ifname: interface name
|
||||||
|
:return:
|
||||||
|
"""
|
||||||
|
with self.lock:
|
||||||
if ifindex is None:
|
if ifindex is None:
|
||||||
ifindex = 0
|
ifindex = 0
|
||||||
|
|
||||||
if self.net is not None:
|
if self.net is not None:
|
||||||
raise ValueError, \
|
raise ValueError("RJ45 nodes support at most 1 network interface")
|
||||||
"RJ45 nodes support at most 1 network interface"
|
|
||||||
self._netif[ifindex] = self
|
self._netif[ifindex] = self
|
||||||
self.node = self # PyCoreNetIf.node is self
|
# PyCoreNetIf.node is self
|
||||||
|
self.node = self
|
||||||
self.ifindex = ifindex
|
self.ifindex = ifindex
|
||||||
|
|
||||||
if net is not None:
|
if net is not None:
|
||||||
self.attachnet(net)
|
self.attachnet(net)
|
||||||
for addr in maketuple(addrlist):
|
|
||||||
|
if addrlist:
|
||||||
|
for addr in utils.maketuple(addrlist):
|
||||||
self.addaddr(addr)
|
self.addaddr(addr)
|
||||||
|
|
||||||
return ifindex
|
return ifindex
|
||||||
finally:
|
|
||||||
self.lock.release()
|
|
||||||
|
|
||||||
def delnetif(self, ifindex):
|
def delnetif(self, ifindex):
|
||||||
|
"""
|
||||||
|
Delete a network interface.
|
||||||
|
|
||||||
|
:param int ifindex: interface index to delete
|
||||||
|
:return: nothing
|
||||||
|
"""
|
||||||
if ifindex is None:
|
if ifindex is None:
|
||||||
ifindex = 0
|
ifindex = 0
|
||||||
|
|
||||||
if ifindex not in self._netif:
|
if ifindex not in self._netif:
|
||||||
raise ValueError, "ifindex %s does not exist" % ifindex
|
raise ValueError, "ifindex %s does not exist" % ifindex
|
||||||
|
|
||||||
self._netif.pop(ifindex)
|
self._netif.pop(ifindex)
|
||||||
if ifindex == self.ifindex:
|
if ifindex == self.ifindex:
|
||||||
self.shutdown()
|
self.shutdown()
|
||||||
|
|
@ -400,46 +568,78 @@ class RJ45Node(PyCoreNode, PyCoreNetIf):
|
||||||
raise ValueError, "ifindex %s does not exist" % ifindex
|
raise ValueError, "ifindex %s does not exist" % ifindex
|
||||||
|
|
||||||
def netif(self, ifindex, net=None):
|
def netif(self, ifindex, net=None):
|
||||||
''' This object is considered the network interface, so we only
|
"""
|
||||||
|
This object is considered the network interface, so we only
|
||||||
return self here. This keeps the RJ45Node compatible with
|
return self here. This keeps the RJ45Node compatible with
|
||||||
real nodes.
|
real nodes.
|
||||||
'''
|
|
||||||
|
:param int ifindex: interface index to retrieve
|
||||||
|
:param net: network to retrieve
|
||||||
|
:return: a network interface
|
||||||
|
:rtype: core.coreobj.PyCoreNetIf
|
||||||
|
"""
|
||||||
if net is not None and net == self.net:
|
if net is not None and net == self.net:
|
||||||
return self
|
return self
|
||||||
|
|
||||||
if ifindex is None:
|
if ifindex is None:
|
||||||
ifindex = 0
|
ifindex = 0
|
||||||
|
|
||||||
if ifindex == self.ifindex:
|
if ifindex == self.ifindex:
|
||||||
return self
|
return self
|
||||||
|
|
||||||
return None
|
return None
|
||||||
|
|
||||||
def getifindex(self, netif):
|
def getifindex(self, netif):
|
||||||
|
"""
|
||||||
|
Retrieve network interface index.
|
||||||
|
|
||||||
|
:param core.coreobj.PyCoreNetIf netif: network interface to retrieve index for
|
||||||
|
:return: interface index, None otherwise
|
||||||
|
:rtype: int
|
||||||
|
"""
|
||||||
if netif != self:
|
if netif != self:
|
||||||
return None
|
return None
|
||||||
|
|
||||||
return self.ifindex
|
return self.ifindex
|
||||||
|
|
||||||
def addaddr(self, addr):
|
def addaddr(self, addr):
|
||||||
|
"""
|
||||||
|
Add address to to network interface.
|
||||||
|
|
||||||
|
:param str addr: address to add
|
||||||
|
:return: nothing
|
||||||
|
"""
|
||||||
if self.up:
|
if self.up:
|
||||||
check_call([IP_BIN, "addr", "add", str(addr), "dev", self.name])
|
subprocess.check_call([constants.IP_BIN, "addr", "add", str(addr), "dev", self.name])
|
||||||
PyCoreNetIf.addaddr(self, addr)
|
PyCoreNetIf.addaddr(self, addr)
|
||||||
|
|
||||||
def deladdr(self, addr):
|
def deladdr(self, addr):
|
||||||
|
"""
|
||||||
|
Delete address from network interface.
|
||||||
|
|
||||||
|
:param str addr: address to delete
|
||||||
|
:return: nothing
|
||||||
|
"""
|
||||||
if self.up:
|
if self.up:
|
||||||
check_call([IP_BIN, "addr", "del", str(addr), "dev", self.name])
|
subprocess.check_call([constants.IP_BIN, "addr", "del", str(addr), "dev", self.name])
|
||||||
PyCoreNetIf.deladdr(self, addr)
|
PyCoreNetIf.deladdr(self, addr)
|
||||||
|
|
||||||
def savestate(self):
|
def savestate(self):
|
||||||
''' Save the addresses and other interface state before using the
|
"""
|
||||||
|
Save the addresses and other interface state before using the
|
||||||
interface for emulation purposes. TODO: save/restore the PROMISC flag
|
interface for emulation purposes. TODO: save/restore the PROMISC flag
|
||||||
'''
|
|
||||||
|
:return: nothing
|
||||||
|
"""
|
||||||
self.old_up = False
|
self.old_up = False
|
||||||
self.old_addrs = []
|
self.old_addrs = []
|
||||||
cmd = [IP_BIN, "addr", "show", "dev", self.localname]
|
cmd = [constants.IP_BIN, "addr", "show", "dev", self.localname]
|
||||||
try:
|
try:
|
||||||
tmp = subprocess.Popen(cmd, stdout=subprocess.PIPE)
|
tmp = subprocess.Popen(cmd, stdout=subprocess.PIPE)
|
||||||
except OSError:
|
except OSError:
|
||||||
self.warn("Failed to run %s command: %s" % (IP_BIN, cmd))
|
logger.exception("Failed to run %s command: %s", constants.IP_BIN, cmd)
|
||||||
if tmp.wait():
|
if tmp.wait():
|
||||||
self.warn("Command failed: %s" % cmd)
|
logger.warn("Command failed: %s", cmd)
|
||||||
return
|
return
|
||||||
lines = tmp.stdout.read()
|
lines = tmp.stdout.read()
|
||||||
tmp.stdout.close()
|
tmp.stdout.close()
|
||||||
|
|
@ -459,31 +659,34 @@ class RJ45Node(PyCoreNode, PyCoreNetIf):
|
||||||
self.old_addrs.append((items[1], None))
|
self.old_addrs.append((items[1], None))
|
||||||
|
|
||||||
def restorestate(self):
|
def restorestate(self):
|
||||||
''' Restore the addresses and other interface state after using it.
|
"""
|
||||||
'''
|
Restore the addresses and other interface state after using it.
|
||||||
|
|
||||||
|
:return: nothing
|
||||||
|
"""
|
||||||
for addr in self.old_addrs:
|
for addr in self.old_addrs:
|
||||||
if addr[1] is None:
|
if addr[1] is None:
|
||||||
check_call([IP_BIN, "addr", "add", addr[0], "dev",
|
subprocess.check_call([constants.IP_BIN, "addr", "add", addr[0], "dev", self.localname])
|
||||||
self.localname])
|
|
||||||
else:
|
else:
|
||||||
check_call([IP_BIN, "addr", "add", addr[0], "brd", addr[1],
|
subprocess.check_call([constants.IP_BIN, "addr", "add", addr[0], "brd", addr[1], "dev", self.localname])
|
||||||
"dev", self.localname])
|
|
||||||
if self.old_up:
|
if self.old_up:
|
||||||
check_call([IP_BIN, "link", "set", self.localname, "up"])
|
subprocess.check_call([constants.IP_BIN, "link", "set", self.localname, "up"])
|
||||||
|
|
||||||
def setposition(self, x=None, y=None, z=None):
|
def setposition(self, x=None, y=None, z=None):
|
||||||
''' Use setposition() from both parent classes.
|
"""
|
||||||
'''
|
Use setposition() from both parent classes.
|
||||||
|
|
||||||
|
:return: nothing
|
||||||
|
"""
|
||||||
PyCoreObj.setposition(self, x, y, z)
|
PyCoreObj.setposition(self, x, y, z)
|
||||||
# invoke any poshook
|
# invoke any poshook
|
||||||
PyCoreNetIf.setposition(self, x, y, z)
|
PyCoreNetIf.setposition(self, x, y, z)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
class TunnelNode(GreTapBridge):
|
class TunnelNode(GreTapBridge):
|
||||||
apitype = coreapi.CORE_NODE_TUNNEL
|
"""
|
||||||
|
Provides tunnel functionality in a core node.
|
||||||
|
"""
|
||||||
|
apitype = NodeTypes.TUNNEL.value
|
||||||
policy = "ACCEPT"
|
policy = "ACCEPT"
|
||||||
type = "tunnel"
|
type = "tunnel"
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,29 +1,40 @@
|
||||||
#
|
"""
|
||||||
# CORE
|
virtual ethernet classes that implement the interfaces available under Linux.
|
||||||
# Copyright (c)2011-2014 the Boeing Company.
|
"""
|
||||||
# See the LICENSE file included in this distribution.
|
|
||||||
#
|
|
||||||
# authors: Tom Goff <thomas.goff@boeing.com>
|
|
||||||
# Jeff Ahrenholz <jeffrey.m.ahrenholz@boeing.com>
|
|
||||||
#
|
|
||||||
'''
|
|
||||||
vif.py: PyCoreNetIf classes that implement the interfaces available
|
|
||||||
under Linux.
|
|
||||||
'''
|
|
||||||
|
|
||||||
import os, signal, shutil, sys, subprocess, vnodeclient, threading, string
|
import subprocess
|
||||||
import random, time
|
import time
|
||||||
from core.api import coreapi
|
|
||||||
from core.misc.utils import *
|
from core import constants
|
||||||
from core.constants import *
|
from core.coreobj import PyCoreNetIf
|
||||||
from core.coreobj import PyCoreObj, PyCoreNode, PyCoreNetIf, Position
|
from core.enumerations import NodeTypes
|
||||||
from core.emane.nodes import EmaneNode
|
from core.misc import log
|
||||||
|
from core.misc import nodeutils
|
||||||
|
from core.misc import utils
|
||||||
|
|
||||||
|
logger = log.get_logger(__name__)
|
||||||
|
|
||||||
|
utils.check_executables([constants.IP_BIN])
|
||||||
|
|
||||||
checkexec([IP_BIN])
|
|
||||||
|
|
||||||
class VEth(PyCoreNetIf):
|
class VEth(PyCoreNetIf):
|
||||||
def __init__(self, node, name, localname, mtu = 1500, net = None,
|
"""
|
||||||
start = True):
|
Provides virtual ethernet functionality for core nodes.
|
||||||
|
"""
|
||||||
|
|
||||||
|
# TODO: network is not used, why was it needed?
|
||||||
|
def __init__(self, node, name, localname, mtu=1500, net=None, start=True):
|
||||||
|
"""
|
||||||
|
Creates a VEth instance.
|
||||||
|
|
||||||
|
:param core.netns.nodes.CoreNode node: related core node
|
||||||
|
:param str name: interface name
|
||||||
|
:param str localname: interface local name
|
||||||
|
:param mtu: interface mtu
|
||||||
|
:param net: network
|
||||||
|
:param bool start: start flag
|
||||||
|
:return:
|
||||||
|
"""
|
||||||
# note that net arg is ignored
|
# note that net arg is ignored
|
||||||
PyCoreNetIf.__init__(self, node=node, name=name, mtu=mtu)
|
PyCoreNetIf.__init__(self, node=node, name=name, mtu=mtu)
|
||||||
self.localname = localname
|
self.localname = localname
|
||||||
|
|
@ -32,26 +43,48 @@ class VEth(PyCoreNetIf):
|
||||||
self.startup()
|
self.startup()
|
||||||
|
|
||||||
def startup(self):
|
def startup(self):
|
||||||
check_call([IP_BIN, "link", "add", "name", self.localname,
|
"""
|
||||||
|
Interface startup logic.
|
||||||
|
|
||||||
|
:return: nothing
|
||||||
|
"""
|
||||||
|
subprocess.check_call([constants.IP_BIN, "link", "add", "name", self.localname,
|
||||||
"type", "veth", "peer", "name", self.name])
|
"type", "veth", "peer", "name", self.name])
|
||||||
check_call([IP_BIN, "link", "set", self.localname, "up"])
|
subprocess.check_call([constants.IP_BIN, "link", "set", self.localname, "up"])
|
||||||
self.up = True
|
self.up = True
|
||||||
|
|
||||||
def shutdown(self):
|
def shutdown(self):
|
||||||
|
"""
|
||||||
|
Interface shutdown logic.
|
||||||
|
|
||||||
|
:return: nothing
|
||||||
|
"""
|
||||||
if not self.up:
|
if not self.up:
|
||||||
return
|
return
|
||||||
if self.node:
|
if self.node:
|
||||||
self.node.cmd([IP_BIN, "-6", "addr", "flush", "dev", self.name])
|
self.node.cmd([constants.IP_BIN, "-6", "addr", "flush", "dev", self.name])
|
||||||
if self.localname:
|
if self.localname:
|
||||||
mutedetach([IP_BIN, "link", "delete", self.localname])
|
utils.mutedetach([constants.IP_BIN, "link", "delete", self.localname])
|
||||||
self.up = False
|
self.up = False
|
||||||
|
|
||||||
|
|
||||||
class TunTap(PyCoreNetIf):
|
class TunTap(PyCoreNetIf):
|
||||||
''' TUN/TAP virtual device in TAP mode
|
"""
|
||||||
'''
|
TUN/TAP virtual device in TAP mode
|
||||||
def __init__(self, node, name, localname, mtu = 1500, net = None,
|
"""
|
||||||
start = True):
|
|
||||||
|
# TODO: network is not used, why was it needed?
|
||||||
|
def __init__(self, node, name, localname, mtu=1500, net=None, start=True):
|
||||||
|
"""
|
||||||
|
Create a TunTap instance.
|
||||||
|
|
||||||
|
:param core.netns.nodes.CoreNode node: related core node
|
||||||
|
:param str name: interface name
|
||||||
|
:param str localname: local interface name
|
||||||
|
:param mtu: interface mtu
|
||||||
|
:param net: related network
|
||||||
|
:param bool start: start flag
|
||||||
|
"""
|
||||||
PyCoreNetIf.__init__(self, node=node, name=name, mtu=mtu)
|
PyCoreNetIf.__init__(self, node=node, name=name, mtu=mtu)
|
||||||
self.localname = localname
|
self.localname = localname
|
||||||
self.up = False
|
self.up = False
|
||||||
|
|
@ -60,6 +93,11 @@ class TunTap(PyCoreNetIf):
|
||||||
self.startup()
|
self.startup()
|
||||||
|
|
||||||
def startup(self):
|
def startup(self):
|
||||||
|
"""
|
||||||
|
Startup logic for a tunnel tap.
|
||||||
|
|
||||||
|
:return: nothing
|
||||||
|
"""
|
||||||
# TODO: more sophisticated TAP creation here
|
# TODO: more sophisticated TAP creation here
|
||||||
# Debian does not support -p (tap) option, RedHat does.
|
# Debian does not support -p (tap) option, RedHat does.
|
||||||
# For now, this is disabled to allow the TAP to be created by another
|
# For now, this is disabled to allow the TAP to be created by another
|
||||||
|
|
@ -69,17 +107,27 @@ class TunTap(PyCoreNetIf):
|
||||||
self.up = True
|
self.up = True
|
||||||
|
|
||||||
def shutdown(self):
|
def shutdown(self):
|
||||||
|
"""
|
||||||
|
Shutdown functionality for a tunnel tap.
|
||||||
|
|
||||||
|
:return: nothing
|
||||||
|
"""
|
||||||
if not self.up:
|
if not self.up:
|
||||||
return
|
return
|
||||||
self.node.cmd([IP_BIN, "-6", "addr", "flush", "dev", self.name])
|
self.node.cmd([constants.IP_BIN, "-6", "addr", "flush", "dev", self.name])
|
||||||
# if self.name:
|
# if self.name:
|
||||||
# mutedetach(["tunctl", "-d", self.localname])
|
# mutedetach(["tunctl", "-d", self.localname])
|
||||||
self.up = False
|
self.up = False
|
||||||
|
|
||||||
def waitfor(self, func, attempts=10, maxretrydelay=0.25):
|
def waitfor(self, func, attempts=10, maxretrydelay=0.25):
|
||||||
'''\
|
"""
|
||||||
Wait for func() to return zero with exponential backoff
|
Wait for func() to return zero with exponential backoff.
|
||||||
'''
|
|
||||||
|
:param func: function to wait for a result of zero
|
||||||
|
:param int attempts: number of attempts to wait for a zero result
|
||||||
|
:param float maxretrydelay: maximum retry delay
|
||||||
|
:return: nothing
|
||||||
|
"""
|
||||||
delay = 0.01
|
delay = 0.01
|
||||||
for i in xrange(1, attempts + 1):
|
for i in xrange(1, attempts + 1):
|
||||||
r = func()
|
r = func()
|
||||||
|
|
@ -88,91 +136,123 @@ class TunTap(PyCoreNetIf):
|
||||||
msg = 'attempt %s failed with nonzero exit status %s' % (i, r)
|
msg = 'attempt %s failed with nonzero exit status %s' % (i, r)
|
||||||
if i < attempts + 1:
|
if i < attempts + 1:
|
||||||
msg += ', retrying...'
|
msg += ', retrying...'
|
||||||
self.node.info(msg)
|
logger.info(msg)
|
||||||
time.sleep(delay)
|
time.sleep(delay)
|
||||||
delay = delay + delay
|
delay = delay + delay
|
||||||
if delay > maxretrydelay:
|
if delay > maxretrydelay:
|
||||||
delay = maxretrydelay
|
delay = maxretrydelay
|
||||||
else:
|
else:
|
||||||
msg += ', giving up'
|
msg += ', giving up'
|
||||||
self.node.info(msg)
|
logger.info(msg)
|
||||||
raise RuntimeError, 'command failed after %s attempts' % attempts
|
|
||||||
|
raise RuntimeError('command failed after %s attempts' % attempts)
|
||||||
|
|
||||||
def waitfordevicelocal(self):
|
def waitfordevicelocal(self):
|
||||||
'''\
|
"""
|
||||||
Check for presence of a local device - tap device may not
|
Check for presence of a local device - tap device may not
|
||||||
appear right away waits
|
appear right away waits
|
||||||
'''
|
|
||||||
|
:return: wait for device local response
|
||||||
|
:rtype: int
|
||||||
|
"""
|
||||||
|
|
||||||
def localdevexists():
|
def localdevexists():
|
||||||
cmd = (IP_BIN, 'link', 'show', self.localname)
|
cmd = (constants.IP_BIN, 'link', 'show', self.localname)
|
||||||
return mutecall(cmd)
|
return utils.mutecall(cmd)
|
||||||
|
|
||||||
self.waitfor(localdevexists)
|
self.waitfor(localdevexists)
|
||||||
|
|
||||||
def waitfordevicenode(self):
|
def waitfordevicenode(self):
|
||||||
'''\
|
"""
|
||||||
Check for presence of a node device - tap device may not
|
Check for presence of a node device - tap device may not appear right away waits.
|
||||||
appear right away waits
|
|
||||||
'''
|
:return: nothing
|
||||||
|
"""
|
||||||
|
|
||||||
def nodedevexists():
|
def nodedevexists():
|
||||||
cmd = (IP_BIN, 'link', 'show', self.name)
|
cmd = (constants.IP_BIN, 'link', 'show', self.name)
|
||||||
return self.node.cmd(cmd)
|
return self.node.cmd(cmd)
|
||||||
|
|
||||||
count = 0
|
count = 0
|
||||||
while True:
|
while True:
|
||||||
try:
|
try:
|
||||||
self.waitfor(nodedevexists)
|
self.waitfor(nodedevexists)
|
||||||
break
|
break
|
||||||
except RuntimeError:
|
except RuntimeError as e:
|
||||||
# check if this is an EMANE interface; if so, continue
|
# check if this is an EMANE interface; if so, continue
|
||||||
# waiting if EMANE is still running
|
# waiting if EMANE is still running
|
||||||
if count < 5 and isinstance(self.net, EmaneNode) and \
|
# TODO: remove emane code
|
||||||
|
if count < 5 and nodeutils.is_node(self.net, NodeTypes.EMANE) and \
|
||||||
self.node.session.emane.emanerunning(self.node):
|
self.node.session.emane.emanerunning(self.node):
|
||||||
count += 1
|
count += 1
|
||||||
else:
|
else:
|
||||||
raise
|
raise e
|
||||||
|
|
||||||
def install(self):
|
def install(self):
|
||||||
''' Install this TAP into its namespace. This is not done from the
|
"""
|
||||||
|
Install this TAP into its namespace. This is not done from the
|
||||||
startup() method but called at a later time when a userspace
|
startup() method but called at a later time when a userspace
|
||||||
program (running on the host) has had a chance to open the socket
|
program (running on the host) has had a chance to open the socket
|
||||||
end of the TAP.
|
end of the TAP.
|
||||||
'''
|
|
||||||
|
:return: nothing
|
||||||
|
"""
|
||||||
self.waitfordevicelocal()
|
self.waitfordevicelocal()
|
||||||
netns = str(self.node.pid)
|
netns = str(self.node.pid)
|
||||||
|
|
||||||
try:
|
try:
|
||||||
check_call([IP_BIN, "link", "set", self.localname, "netns", netns])
|
subprocess.check_call([constants.IP_BIN, "link", "set", self.localname, "netns", netns])
|
||||||
except Exception, e:
|
except subprocess.CalledProcessError:
|
||||||
msg = "error installing TAP interface %s, command:" % self.localname
|
msg = "error installing TAP interface %s, command:" % self.localname
|
||||||
msg += "ip link set %s netns %s" % (self.localname, netns)
|
msg += "ip link set %s netns %s" % (self.localname, netns)
|
||||||
self.node.exception(coreapi.CORE_EXCP_LEVEL_ERROR, self.localname, msg)
|
logger.exception(msg)
|
||||||
self.node.warn(msg)
|
|
||||||
return
|
return
|
||||||
self.node.cmd([IP_BIN, "link", "set", self.localname,
|
|
||||||
"name", self.name])
|
self.node.cmd([constants.IP_BIN, "link", "set", self.localname, "name", self.name])
|
||||||
self.node.cmd([IP_BIN, "link", "set", self.name, "up"])
|
self.node.cmd([constants.IP_BIN, "link", "set", self.name, "up"])
|
||||||
|
|
||||||
def setaddrs(self):
|
def setaddrs(self):
|
||||||
''' Set interface addresses based on self.addrlist.
|
"""
|
||||||
'''
|
Set interface addresses based on self.addrlist.
|
||||||
|
|
||||||
|
:return: nothing
|
||||||
|
"""
|
||||||
self.waitfordevicenode()
|
self.waitfordevicenode()
|
||||||
for addr in self.addrlist:
|
for addr in self.addrlist:
|
||||||
self.node.cmd([IP_BIN, "addr", "add", str(addr),
|
self.node.cmd([constants.IP_BIN, "addr", "add", str(addr), "dev", self.name])
|
||||||
"dev", self.name])
|
|
||||||
|
|
||||||
class GreTap(PyCoreNetIf):
|
class GreTap(PyCoreNetIf):
|
||||||
''' GRE TAP device for tunneling between emulation servers.
|
"""
|
||||||
|
GRE TAP device for tunneling between emulation servers.
|
||||||
Uses the "gretap" tunnel device type from Linux which is a GRE device
|
Uses the "gretap" tunnel device type from Linux which is a GRE device
|
||||||
having a MAC address. The MAC address is required for bridging.
|
having a MAC address. The MAC address is required for bridging.
|
||||||
'''
|
"""
|
||||||
|
|
||||||
def __init__(self, node=None, name=None, session=None, mtu=1458,
|
def __init__(self, node=None, name=None, session=None, mtu=1458,
|
||||||
remoteip=None, objid=None, localip=None, ttl=255,
|
remoteip=None, objid=None, localip=None, ttl=255,
|
||||||
key=None, start=True):
|
key=None, start=True):
|
||||||
|
"""
|
||||||
|
Creates a GreTap instance.
|
||||||
|
|
||||||
|
:param core.netns.nodes.CoreNode node: related core node
|
||||||
|
:param str name: interface name
|
||||||
|
:param core.session.Session session: core session instance
|
||||||
|
:param mtu: interface mtu
|
||||||
|
:param str remoteip: remote address
|
||||||
|
:param int objid: object id
|
||||||
|
:param str localip: local address
|
||||||
|
:param ttl: ttl value
|
||||||
|
:param key: gre tap key
|
||||||
|
:param bool start: start flag
|
||||||
|
"""
|
||||||
PyCoreNetIf.__init__(self, node=node, name=name, mtu=mtu)
|
PyCoreNetIf.__init__(self, node=node, name=name, mtu=mtu)
|
||||||
self.session = session
|
self.session = session
|
||||||
if objid is None:
|
if objid is None:
|
||||||
# from PyCoreObj
|
# from PyCoreObj
|
||||||
objid = (((id(self) >> 16) ^ (id(self) & 0xffff)) & 0xffff)
|
objid = ((id(self) >> 16) ^ (id(self) & 0xffff)) & 0xffff
|
||||||
self.objid = objid
|
self.objid = objid
|
||||||
sessionid = self.session.shortsessionid()
|
sessionid = self.session.short_session_id()
|
||||||
# interface name on the local host machine
|
# interface name on the local host machine
|
||||||
self.localname = "gt.%s.%s" % (self.objid, sessionid)
|
self.localname = "gt.%s.%s" % (self.objid, sessionid)
|
||||||
self.transport_type = "raw"
|
self.transport_type = "raw"
|
||||||
|
|
@ -190,21 +270,39 @@ class GreTap(PyCoreNetIf):
|
||||||
cmd += ("ttl", str(ttl))
|
cmd += ("ttl", str(ttl))
|
||||||
if key:
|
if key:
|
||||||
cmd += ("key", str(key))
|
cmd += ("key", str(key))
|
||||||
check_call(cmd)
|
subprocess.check_call(cmd)
|
||||||
cmd = ("ip", "link", "set", self.localname, "up")
|
cmd = ("ip", "link", "set", self.localname, "up")
|
||||||
check_call(cmd)
|
subprocess.check_call(cmd)
|
||||||
self.up = True
|
self.up = True
|
||||||
|
|
||||||
def shutdown(self):
|
def shutdown(self):
|
||||||
|
"""
|
||||||
|
Shutdown logic for a GreTap.
|
||||||
|
|
||||||
|
:return: nothing
|
||||||
|
"""
|
||||||
if self.localname:
|
if self.localname:
|
||||||
cmd = ("ip", "link", "set", self.localname, "down")
|
cmd = ("ip", "link", "set", self.localname, "down")
|
||||||
check_call(cmd)
|
subprocess.check_call(cmd)
|
||||||
cmd = ("ip", "link", "del", self.localname)
|
cmd = ("ip", "link", "del", self.localname)
|
||||||
check_call(cmd)
|
subprocess.check_call(cmd)
|
||||||
self.localname = None
|
self.localname = None
|
||||||
|
|
||||||
def tonodemsg(self, flags):
|
def data(self, message_type):
|
||||||
|
"""
|
||||||
|
Data for a gre tap.
|
||||||
|
|
||||||
|
:param message_type: message type for data
|
||||||
|
:return: None
|
||||||
|
"""
|
||||||
return None
|
return None
|
||||||
|
|
||||||
def tolinkmsgs(self, flags):
|
def all_link_data(self, flags):
|
||||||
|
"""
|
||||||
|
Retrieve link data.
|
||||||
|
|
||||||
|
:param flags: link flags
|
||||||
|
:return: link data
|
||||||
|
:rtype: list[core.data.LinkData]
|
||||||
|
"""
|
||||||
return []
|
return []
|
||||||
|
|
|
||||||
|
|
@ -1,42 +1,48 @@
|
||||||
#
|
"""
|
||||||
# CORE
|
PyCoreNet and LxBrNet classes that implement virtual networks using
|
||||||
# Copyright (c)2010-2016 the Boeing Company.
|
|
||||||
# See the LICENSE file included in this distribution.
|
|
||||||
#
|
|
||||||
# authors: Tom Goff <thomas.goff@boeing.com>
|
|
||||||
# Jeff Ahrenholz <jeffrey.m.ahrenholz@boeing.com>
|
|
||||||
#
|
|
||||||
'''
|
|
||||||
vnet.py: PyCoreNet and LxBrNet classes that implement virtual networks using
|
|
||||||
Linux Ethernet bridging and ebtables rules.
|
Linux Ethernet bridging and ebtables rules.
|
||||||
'''
|
"""
|
||||||
|
|
||||||
import os, sys, threading, time, subprocess
|
import os
|
||||||
|
import subprocess
|
||||||
|
import threading
|
||||||
|
import time
|
||||||
|
|
||||||
from core.api import coreapi
|
from core import constants
|
||||||
from core.misc.utils import *
|
from core.coreobj import PyCoreNet
|
||||||
from core.constants import *
|
from core.misc import log
|
||||||
from core.coreobj import PyCoreNet, PyCoreObj
|
from core.misc import utils
|
||||||
from core.netns.vif import VEth, GreTap
|
from core.netns.vif import GreTap
|
||||||
|
from core.netns.vif import VEth
|
||||||
|
|
||||||
checkexec([BRCTL_BIN, IP_BIN, EBTABLES_BIN, TC_BIN])
|
logger = log.get_logger(__name__)
|
||||||
|
|
||||||
|
utils.check_executables([
|
||||||
|
constants.BRCTL_BIN,
|
||||||
|
constants.IP_BIN,
|
||||||
|
constants.EBTABLES_BIN,
|
||||||
|
constants.TC_BIN
|
||||||
|
])
|
||||||
|
|
||||||
ebtables_lock = threading.Lock()
|
ebtables_lock = threading.Lock()
|
||||||
|
|
||||||
|
|
||||||
class EbtablesQueue(object):
|
class EbtablesQueue(object):
|
||||||
''' Helper class for queuing up ebtables commands into rate-limited
|
"""
|
||||||
|
Helper class for queuing up ebtables commands into rate-limited
|
||||||
atomic commits. This improves performance and reliability when there are
|
atomic commits. This improves performance and reliability when there are
|
||||||
many WLAN link updates.
|
many WLAN link updates.
|
||||||
'''
|
"""
|
||||||
# update rate is every 300ms
|
# update rate is every 300ms
|
||||||
rate = 0.3
|
rate = 0.3
|
||||||
# ebtables
|
# ebtables
|
||||||
atomic_file = "/tmp/pycore.ebtables.atomic"
|
atomic_file = "/tmp/pycore.ebtables.atomic"
|
||||||
|
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
''' Initialize the helper class, but don't start the update thread
|
"""
|
||||||
|
Initialize the helper class, but don't start the update thread
|
||||||
until a WLAN is instantiated.
|
until a WLAN is instantiated.
|
||||||
'''
|
"""
|
||||||
self.doupdateloop = False
|
self.doupdateloop = False
|
||||||
self.updatethread = None
|
self.updatethread = None
|
||||||
# this lock protects cmds and updates lists
|
# this lock protects cmds and updates lists
|
||||||
|
|
@ -50,8 +56,11 @@ class EbtablesQueue(object):
|
||||||
self.last_update_time = {}
|
self.last_update_time = {}
|
||||||
|
|
||||||
def startupdateloop(self, wlan):
|
def startupdateloop(self, wlan):
|
||||||
''' Kick off the update loop; only needs to be invoked once.
|
"""
|
||||||
'''
|
Kick off the update loop; only needs to be invoked once.
|
||||||
|
|
||||||
|
:return: nothing
|
||||||
|
"""
|
||||||
self.updatelock.acquire()
|
self.updatelock.acquire()
|
||||||
self.last_update_time[wlan] = time.time()
|
self.last_update_time[wlan] = time.time()
|
||||||
self.updatelock.release()
|
self.updatelock.release()
|
||||||
|
|
@ -63,13 +72,17 @@ class EbtablesQueue(object):
|
||||||
self.updatethread.start()
|
self.updatethread.start()
|
||||||
|
|
||||||
def stopupdateloop(self, wlan):
|
def stopupdateloop(self, wlan):
|
||||||
''' Kill the update loop thread if there are no more WLANs using it.
|
"""
|
||||||
'''
|
Kill the update loop thread if there are no more WLANs using it.
|
||||||
|
|
||||||
|
:return: nothing
|
||||||
|
"""
|
||||||
self.updatelock.acquire()
|
self.updatelock.acquire()
|
||||||
try:
|
try:
|
||||||
del self.last_update_time[wlan]
|
del self.last_update_time[wlan]
|
||||||
except KeyError:
|
except KeyError:
|
||||||
pass
|
logger.exception("error deleting last update time for wlan: %s", wlan)
|
||||||
|
|
||||||
self.updatelock.release()
|
self.updatelock.release()
|
||||||
if len(self.last_update_time) > 0:
|
if len(self.last_update_time) > 0:
|
||||||
return
|
return
|
||||||
|
|
@ -79,42 +92,60 @@ class EbtablesQueue(object):
|
||||||
self.updatethread = None
|
self.updatethread = None
|
||||||
|
|
||||||
def ebatomiccmd(self, cmd):
|
def ebatomiccmd(self, cmd):
|
||||||
''' Helper for building ebtables atomic file command list.
|
"""
|
||||||
'''
|
Helper for building ebtables atomic file command list.
|
||||||
r = [EBTABLES_BIN, "--atomic-file", self.atomic_file]
|
|
||||||
|
:param list[str] cmd: ebtable command
|
||||||
|
:return: ebtable atomic command
|
||||||
|
:rtype: list[str]
|
||||||
|
"""
|
||||||
|
r = [constants.EBTABLES_BIN, "--atomic-file", self.atomic_file]
|
||||||
if cmd:
|
if cmd:
|
||||||
r.extend(cmd)
|
r.extend(cmd)
|
||||||
return r
|
return r
|
||||||
|
|
||||||
def lastupdate(self, wlan):
|
def lastupdate(self, wlan):
|
||||||
''' Return the time elapsed since this WLAN was last updated.
|
"""
|
||||||
'''
|
Return the time elapsed since this WLAN was last updated.
|
||||||
|
|
||||||
|
:param wlan: wlan entity
|
||||||
|
:return: elpased time
|
||||||
|
:rtype: float
|
||||||
|
"""
|
||||||
try:
|
try:
|
||||||
elapsed = time.time() - self.last_update_time[wlan]
|
elapsed = time.time() - self.last_update_time[wlan]
|
||||||
except KeyError:
|
except KeyError:
|
||||||
self.last_update_time[wlan] = time.time()
|
self.last_update_time[wlan] = time.time()
|
||||||
elapsed = 0.0
|
elapsed = 0.0
|
||||||
|
|
||||||
return elapsed
|
return elapsed
|
||||||
|
|
||||||
def updated(self, wlan):
|
def updated(self, wlan):
|
||||||
''' Keep track of when this WLAN was last updated.
|
"""
|
||||||
'''
|
Keep track of when this WLAN was last updated.
|
||||||
|
|
||||||
|
:param wlan: wlan entity
|
||||||
|
:return: nothing
|
||||||
|
"""
|
||||||
self.last_update_time[wlan] = time.time()
|
self.last_update_time[wlan] = time.time()
|
||||||
self.updates.remove(wlan)
|
self.updates.remove(wlan)
|
||||||
|
|
||||||
def updateloop(self):
|
def updateloop(self):
|
||||||
''' Thread target that looks for WLANs needing update, and
|
"""
|
||||||
|
Thread target that looks for WLANs needing update, and
|
||||||
rate limits the amount of ebtables activity. Only one userspace program
|
rate limits the amount of ebtables activity. Only one userspace program
|
||||||
should use ebtables at any given time, or results can be unpredictable.
|
should use ebtables at any given time, or results can be unpredictable.
|
||||||
'''
|
|
||||||
|
:return: nothing
|
||||||
|
"""
|
||||||
while self.doupdateloop:
|
while self.doupdateloop:
|
||||||
self.updatelock.acquire()
|
self.updatelock.acquire()
|
||||||
for wlan in self.updates:
|
for wlan in self.updates:
|
||||||
'''
|
"""
|
||||||
Check if wlan is from a previously closed session. Because of the
|
Check if wlan is from a previously closed session. Because of the
|
||||||
rate limiting scheme employed here, this may happen if a new session
|
rate limiting scheme employed here, this may happen if a new session
|
||||||
is started soon after closing a previous session.
|
is started soon after closing a previous session.
|
||||||
'''
|
"""
|
||||||
try:
|
try:
|
||||||
wlan.session
|
wlan.session
|
||||||
except:
|
except:
|
||||||
|
|
@ -130,53 +161,61 @@ class EbtablesQueue(object):
|
||||||
time.sleep(self.rate)
|
time.sleep(self.rate)
|
||||||
|
|
||||||
def ebcommit(self, wlan):
|
def ebcommit(self, wlan):
|
||||||
''' Perform ebtables atomic commit using commands built in the
|
"""
|
||||||
self.cmds list.
|
Perform ebtables atomic commit using commands built in the self.cmds list.
|
||||||
'''
|
|
||||||
|
:return: nothing
|
||||||
|
"""
|
||||||
# save kernel ebtables snapshot to a file
|
# save kernel ebtables snapshot to a file
|
||||||
cmd = self.ebatomiccmd(["--atomic-save", ])
|
cmd = self.ebatomiccmd(["--atomic-save", ])
|
||||||
try:
|
try:
|
||||||
check_call(cmd)
|
subprocess.check_call(cmd)
|
||||||
except Exception, e:
|
except subprocess.CalledProcessError:
|
||||||
self.eberror(wlan, "atomic-save (%s)" % cmd, e)
|
logger.exception("atomic-save (%s)", cmd)
|
||||||
# no atomic file, exit
|
# no atomic file, exit
|
||||||
return
|
return
|
||||||
# modify the table file using queued ebtables commands
|
# modify the table file using queued ebtables commands
|
||||||
for c in self.cmds:
|
for c in self.cmds:
|
||||||
cmd = self.ebatomiccmd(c)
|
cmd = self.ebatomiccmd(c)
|
||||||
try:
|
try:
|
||||||
check_call(cmd)
|
subprocess.check_call(cmd)
|
||||||
except Exception, e:
|
except subprocess.CalledProcessError:
|
||||||
self.eberror(wlan, "cmd=%s" % cmd, e)
|
logger.exception("cmd=%s", cmd)
|
||||||
pass
|
|
||||||
self.cmds = []
|
self.cmds = []
|
||||||
# commit the table file to the kernel
|
# commit the table file to the kernel
|
||||||
cmd = self.ebatomiccmd(["--atomic-commit", ])
|
cmd = self.ebatomiccmd(["--atomic-commit", ])
|
||||||
|
|
||||||
try:
|
try:
|
||||||
check_call(cmd)
|
subprocess.check_call(cmd)
|
||||||
os.unlink(self.atomic_file)
|
os.unlink(self.atomic_file)
|
||||||
except Exception, e:
|
except OSError:
|
||||||
self.eberror(wlan, "atomic-commit (%s)" % cmd, e)
|
logger.exception("atomic-commit (%s)", cmd)
|
||||||
|
|
||||||
def ebchange(self, wlan):
|
def ebchange(self, wlan):
|
||||||
''' Flag a change to the given WLAN's _linked dict, so the ebtables
|
"""
|
||||||
|
Flag a change to the given WLAN's _linked dict, so the ebtables
|
||||||
chain will be rebuilt at the next interval.
|
chain will be rebuilt at the next interval.
|
||||||
'''
|
|
||||||
|
:return: nothing
|
||||||
|
"""
|
||||||
self.updatelock.acquire()
|
self.updatelock.acquire()
|
||||||
if wlan not in self.updates:
|
if wlan not in self.updates:
|
||||||
self.updates.append(wlan)
|
self.updates.append(wlan)
|
||||||
self.updatelock.release()
|
self.updatelock.release()
|
||||||
|
|
||||||
def buildcmds(self, wlan):
|
def buildcmds(self, wlan):
|
||||||
''' Inspect a _linked dict from a wlan, and rebuild the ebtables chain
|
"""
|
||||||
for that WLAN.
|
Inspect a _linked dict from a wlan, and rebuild the ebtables chain for that WLAN.
|
||||||
'''
|
|
||||||
|
:return: nothing
|
||||||
|
"""
|
||||||
wlan._linked_lock.acquire()
|
wlan._linked_lock.acquire()
|
||||||
# flush the chain
|
# flush the chain
|
||||||
self.cmds.extend([["-F", wlan.brname], ])
|
self.cmds.extend([["-F", wlan.brname], ])
|
||||||
# rebuild the chain
|
# rebuild the chain
|
||||||
for (netif1, v) in wlan._linked.items():
|
for netif1, v in wlan._linked.items():
|
||||||
for (netif2, linked) in v.items():
|
for netif2, linked in v.items():
|
||||||
if wlan.policy == "DROP" and linked:
|
if wlan.policy == "DROP" and linked:
|
||||||
self.cmds.extend([["-A", wlan.brname, "-i", netif1.localname,
|
self.cmds.extend([["-A", wlan.brname, "-i", netif1.localname,
|
||||||
"-o", netif2.localname, "-j", "ACCEPT"],
|
"-o", netif2.localname, "-j", "ACCEPT"],
|
||||||
|
|
@ -189,40 +228,48 @@ class EbtablesQueue(object):
|
||||||
"-i", netif2.localname, "-j", "DROP"]])
|
"-i", netif2.localname, "-j", "DROP"]])
|
||||||
wlan._linked_lock.release()
|
wlan._linked_lock.release()
|
||||||
|
|
||||||
def eberror(self, wlan, source, error):
|
|
||||||
''' Log an ebtables command error and send an exception.
|
|
||||||
'''
|
|
||||||
if not wlan:
|
|
||||||
return
|
|
||||||
wlan.exception(coreapi.CORE_EXCP_LEVEL_ERROR, wlan.brname,
|
|
||||||
"ebtables command error: %s\n%s\n" % (source, error))
|
|
||||||
|
|
||||||
|
|
||||||
# a global object because all WLANs share the same queue
|
# a global object because all WLANs share the same queue
|
||||||
# cannot have multiple threads invoking the ebtables commnd
|
# cannot have multiple threads invoking the ebtables commnd
|
||||||
ebq = EbtablesQueue()
|
ebq = EbtablesQueue()
|
||||||
|
|
||||||
|
|
||||||
def ebtablescmds(call, cmds):
|
def ebtablescmds(call, cmds):
|
||||||
ebtables_lock.acquire()
|
"""
|
||||||
try:
|
Run ebtable commands.
|
||||||
|
|
||||||
|
:param func call: function to call commands
|
||||||
|
:param list cmds: commands to call
|
||||||
|
:return: nothing
|
||||||
|
"""
|
||||||
|
with ebtables_lock:
|
||||||
for cmd in cmds:
|
for cmd in cmds:
|
||||||
call(cmd)
|
call(cmd)
|
||||||
finally:
|
|
||||||
ebtables_lock.release()
|
|
||||||
|
|
||||||
class LxBrNet(PyCoreNet):
|
class LxBrNet(PyCoreNet):
|
||||||
|
"""
|
||||||
|
Provides linux bridge network functionlity for core nodes.
|
||||||
|
"""
|
||||||
policy = "DROP"
|
policy = "DROP"
|
||||||
|
|
||||||
def __init__(self, session, objid = None, name = None, verbose = False,
|
def __init__(self, session, objid=None, name=None, start=True, policy=None):
|
||||||
start = True, policy = None):
|
"""
|
||||||
PyCoreNet.__init__(self, session, objid, name, verbose, start)
|
Creates a LxBrNet instance.
|
||||||
|
|
||||||
|
:param core.session.Session session: core session instance
|
||||||
|
:param int objid: object id
|
||||||
|
:param str name: object name
|
||||||
|
:param bool start: start flag
|
||||||
|
:param policy: network policy
|
||||||
|
"""
|
||||||
|
PyCoreNet.__init__(self, session, objid, name, start)
|
||||||
if name is None:
|
if name is None:
|
||||||
name = str(self.objid)
|
name = str(self.objid)
|
||||||
if policy is not None:
|
if policy is not None:
|
||||||
self.policy = policy
|
self.policy = policy
|
||||||
self.name = name
|
self.name = name
|
||||||
sessionid = self.session.shortsessionid()
|
sessionid = self.session.short_session_id()
|
||||||
self.brname = "b.%s.%s" % (str(self.objid), sessionid)
|
self.brname = "b.%s.%s" % (str(self.objid), sessionid)
|
||||||
self.up = False
|
self.up = False
|
||||||
if start:
|
if start:
|
||||||
|
|
@ -230,42 +277,50 @@ class LxBrNet(PyCoreNet):
|
||||||
ebq.startupdateloop(self)
|
ebq.startupdateloop(self)
|
||||||
|
|
||||||
def startup(self):
|
def startup(self):
|
||||||
|
"""
|
||||||
|
Linux bridge starup logic.
|
||||||
|
|
||||||
|
:return: nothing
|
||||||
|
"""
|
||||||
try:
|
try:
|
||||||
check_call([BRCTL_BIN, "addbr", self.brname])
|
subprocess.check_call([constants.BRCTL_BIN, "addbr", self.brname])
|
||||||
except Exception, e:
|
except subprocess.CalledProcessError:
|
||||||
self.exception(coreapi.CORE_EXCP_LEVEL_FATAL, self.brname,
|
logger.exception("Error adding bridge")
|
||||||
"Error adding bridge: %s" % e)
|
|
||||||
try:
|
try:
|
||||||
# turn off spanning tree protocol and forwarding delay
|
# turn off spanning tree protocol and forwarding delay
|
||||||
check_call([BRCTL_BIN, "stp", self.brname, "off"])
|
subprocess.check_call([constants.BRCTL_BIN, "stp", self.brname, "off"])
|
||||||
check_call([BRCTL_BIN, "setfd", self.brname, "0"])
|
subprocess.check_call([constants.BRCTL_BIN, "setfd", self.brname, "0"])
|
||||||
check_call([IP_BIN, "link", "set", self.brname, "up"])
|
subprocess.check_call([constants.IP_BIN, "link", "set", self.brname, "up"])
|
||||||
# create a new ebtables chain for this bridge
|
# create a new ebtables chain for this bridge
|
||||||
ebtablescmds(check_call, [
|
ebtablescmds(subprocess.check_call, [
|
||||||
[EBTABLES_BIN, "-N", self.brname, "-P", self.policy],
|
[constants.EBTABLES_BIN, "-N", self.brname, "-P", self.policy],
|
||||||
[EBTABLES_BIN, "-A", "FORWARD",
|
[constants.EBTABLES_BIN, "-A", "FORWARD", "--logical-in", self.brname, "-j", self.brname]
|
||||||
"--logical-in", self.brname, "-j", self.brname]])
|
])
|
||||||
# turn off multicast snooping so mcast forwarding occurs w/o IGMP joins
|
# turn off multicast snooping so mcast forwarding occurs w/o IGMP joins
|
||||||
snoop = "/sys/devices/virtual/net/%s/bridge/multicast_snooping" % \
|
snoop = "/sys/devices/virtual/net/%s/bridge/multicast_snooping" % self.brname
|
||||||
self.brname
|
|
||||||
if os.path.exists(snoop):
|
if os.path.exists(snoop):
|
||||||
open(snoop, "w").write('0')
|
open(snoop, "w").write('0')
|
||||||
except Exception, e:
|
except subprocess.CalledProcessError:
|
||||||
self.exception(coreapi.CORE_EXCP_LEVEL_WARNING, self.brname,
|
logger.exception("Error setting bridge parameters")
|
||||||
"Error setting bridge parameters: %s" % e)
|
|
||||||
|
|
||||||
self.up = True
|
self.up = True
|
||||||
|
|
||||||
def shutdown(self):
|
def shutdown(self):
|
||||||
|
"""
|
||||||
|
Linux bridge shutdown logic.
|
||||||
|
|
||||||
|
:return: nothing
|
||||||
|
"""
|
||||||
if not self.up:
|
if not self.up:
|
||||||
return
|
return
|
||||||
ebq.stopupdateloop(self)
|
ebq.stopupdateloop(self)
|
||||||
mutecall([IP_BIN, "link", "set", self.brname, "down"])
|
utils.mutecall([constants.IP_BIN, "link", "set", self.brname, "down"])
|
||||||
mutecall([BRCTL_BIN, "delbr", self.brname])
|
utils.mutecall([constants.BRCTL_BIN, "delbr", self.brname])
|
||||||
ebtablescmds(mutecall, [
|
ebtablescmds(utils.mutecall, [
|
||||||
[EBTABLES_BIN, "-D", "FORWARD",
|
[constants.EBTABLES_BIN, "-D", "FORWARD",
|
||||||
"--logical-in", self.brname, "-j", self.brname],
|
"--logical-in", self.brname, "-j", self.brname],
|
||||||
[EBTABLES_BIN, "-X", self.brname]])
|
[constants.EBTABLES_BIN, "-X", self.brname]])
|
||||||
for netif in self.netifs():
|
for netif in self.netifs():
|
||||||
# removes veth pairs used for bridge-to-bridge connections
|
# removes veth pairs used for bridge-to-bridge connections
|
||||||
netif.shutdown()
|
netif.shutdown()
|
||||||
|
|
@ -275,34 +330,52 @@ class LxBrNet(PyCoreNet):
|
||||||
self.up = False
|
self.up = False
|
||||||
|
|
||||||
def attach(self, netif):
|
def attach(self, netif):
|
||||||
|
"""
|
||||||
|
Attach a network interface.
|
||||||
|
|
||||||
|
:param core.netns.vif.VEth netif: network interface to attach
|
||||||
|
:return: nothing
|
||||||
|
"""
|
||||||
if self.up:
|
if self.up:
|
||||||
try:
|
try:
|
||||||
check_call([BRCTL_BIN, "addif", self.brname, netif.localname])
|
subprocess.check_call([constants.BRCTL_BIN, "addif", self.brname, netif.localname])
|
||||||
check_call([IP_BIN, "link", "set", netif.localname, "up"])
|
subprocess.check_call([constants.IP_BIN, "link", "set", netif.localname, "up"])
|
||||||
except Exception, e:
|
except subprocess.CalledProcessError:
|
||||||
self.exception(coreapi.CORE_EXCP_LEVEL_ERROR, self.brname,
|
logger.exception("Error joining interface %s to bridge %s", netif.localname, self.brname)
|
||||||
"Error joining interface %s to bridge %s: %s" % \
|
|
||||||
(netif.localname, self.brname, e))
|
|
||||||
return
|
return
|
||||||
PyCoreNet.attach(self, netif)
|
PyCoreNet.attach(self, netif)
|
||||||
|
|
||||||
def detach(self, netif):
|
def detach(self, netif):
|
||||||
|
"""
|
||||||
|
Detach a network interface.
|
||||||
|
|
||||||
|
:param core.netns.vif.Veth netif: network interface to detach
|
||||||
|
:return: nothing
|
||||||
|
"""
|
||||||
if self.up:
|
if self.up:
|
||||||
try:
|
try:
|
||||||
check_call([BRCTL_BIN, "delif", self.brname, netif.localname])
|
subprocess.check_call([constants.BRCTL_BIN, "delif", self.brname, netif.localname])
|
||||||
except Exception, e:
|
except subprocess.CalledProcessError:
|
||||||
self.exception(coreapi.CORE_EXCP_LEVEL_ERROR, self.brname,
|
logger.exception("Error removing interface %s from bridge %s", netif.localname, self.brname)
|
||||||
"Error removing interface %s from bridge %s: %s" % \
|
|
||||||
(netif.localname, self.brname, e))
|
|
||||||
return
|
return
|
||||||
PyCoreNet.detach(self, netif)
|
PyCoreNet.detach(self, netif)
|
||||||
|
|
||||||
def linked(self, netif1, netif2):
|
def linked(self, netif1, netif2):
|
||||||
|
"""
|
||||||
|
Determine if the provided network interfaces are linked.
|
||||||
|
|
||||||
|
:param core.netns.vif.Veth netif1: interface one
|
||||||
|
:param core.netns.vif.Veth netif2: interface two
|
||||||
|
:return: True if interfaces are linked, False otherwise
|
||||||
|
:rtype: bool
|
||||||
|
"""
|
||||||
# check if the network interfaces are attached to this network
|
# check if the network interfaces are attached to this network
|
||||||
if self._netif[netif1.netifi] != netif1:
|
if self._netif[netif1.netifi] != netif1:
|
||||||
raise ValueError, "inconsistency for netif %s" % netif1.name
|
raise ValueError("inconsistency for netif %s" % netif1.name)
|
||||||
|
|
||||||
if self._netif[netif2.netifi] != netif2:
|
if self._netif[netif2.netifi] != netif2:
|
||||||
raise ValueError, "inconsistency for netif %s" % netif2.name
|
raise ValueError("inconsistency for netif %s" % netif2.name)
|
||||||
|
|
||||||
try:
|
try:
|
||||||
linked = self._linked[netif1][netif2]
|
linked = self._linked[netif1][netif2]
|
||||||
except KeyError:
|
except KeyError:
|
||||||
|
|
@ -311,14 +384,20 @@ class LxBrNet(PyCoreNet):
|
||||||
elif self.policy == "DROP":
|
elif self.policy == "DROP":
|
||||||
linked = False
|
linked = False
|
||||||
else:
|
else:
|
||||||
raise Exception, "unknown policy: %s" % self.policy
|
raise Exception("unknown policy: %s" % self.policy)
|
||||||
self._linked[netif1][netif2] = linked
|
self._linked[netif1][netif2] = linked
|
||||||
|
|
||||||
return linked
|
return linked
|
||||||
|
|
||||||
def unlink(self, netif1, netif2):
|
def unlink(self, netif1, netif2):
|
||||||
''' Unlink two PyCoreNetIfs, resulting in adding or removing ebtables
|
"""
|
||||||
|
Unlink two PyCoreNetIfs, resulting in adding or removing ebtables
|
||||||
filtering rules.
|
filtering rules.
|
||||||
'''
|
|
||||||
|
:param core.netns.vif.Veth netif1: interface one
|
||||||
|
:param core.netns.vif.Veth netif2: interface two
|
||||||
|
:return: nothing
|
||||||
|
"""
|
||||||
self._linked_lock.acquire()
|
self._linked_lock.acquire()
|
||||||
if not self.linked(netif1, netif2):
|
if not self.linked(netif1, netif2):
|
||||||
self._linked_lock.release()
|
self._linked_lock.release()
|
||||||
|
|
@ -328,9 +407,14 @@ class LxBrNet(PyCoreNet):
|
||||||
ebq.ebchange(self)
|
ebq.ebchange(self)
|
||||||
|
|
||||||
def link(self, netif1, netif2):
|
def link(self, netif1, netif2):
|
||||||
''' Link two PyCoreNetIfs together, resulting in adding or removing
|
"""
|
||||||
|
Link two PyCoreNetIfs together, resulting in adding or removing
|
||||||
ebtables filtering rules.
|
ebtables filtering rules.
|
||||||
'''
|
|
||||||
|
:param core.netns.vif.Veth netif1: interface one
|
||||||
|
:param core.netns.vif.Veth netif2: interface two
|
||||||
|
:return: nothing
|
||||||
|
"""
|
||||||
self._linked_lock.acquire()
|
self._linked_lock.acquire()
|
||||||
if self.linked(netif1, netif2):
|
if self.linked(netif1, netif2):
|
||||||
self._linked_lock.release()
|
self._linked_lock.release()
|
||||||
|
|
@ -339,37 +423,45 @@ class LxBrNet(PyCoreNet):
|
||||||
self._linked_lock.release()
|
self._linked_lock.release()
|
||||||
ebq.ebchange(self)
|
ebq.ebchange(self)
|
||||||
|
|
||||||
def linkconfig(self, netif, bw = None, delay = None,
|
def linkconfig(self, netif, bw=None, delay=None, loss=None, duplicate=None,
|
||||||
loss = None, duplicate = None, jitter = None, netif2 = None,
|
jitter=None, netif2=None, devname=None):
|
||||||
devname = None):
|
"""
|
||||||
''' Configure link parameters by applying tc queuing disciplines on the
|
Configure link parameters by applying tc queuing disciplines on the interface.
|
||||||
interface.
|
|
||||||
'''
|
:param core.netns.vif.Veth netif: interface one
|
||||||
|
:param bw: bandwidth to set to
|
||||||
|
:param delay: packet delay to set to
|
||||||
|
:param loss: packet loss to set to
|
||||||
|
:param duplicate: duplicate percentage to set to
|
||||||
|
:param jitter: jitter to set to
|
||||||
|
:param core.netns.vif.Veth netif2: interface two
|
||||||
|
:param devname: device name
|
||||||
|
:return: nothing
|
||||||
|
"""
|
||||||
if devname is None:
|
if devname is None:
|
||||||
devname = netif.localname
|
devname = netif.localname
|
||||||
tc = [TC_BIN, "qdisc", "replace", "dev", devname]
|
tc = [constants.TC_BIN, "qdisc", "replace", "dev", devname]
|
||||||
parent = ["root"]
|
parent = ["root"]
|
||||||
changed = False
|
changed = False
|
||||||
if netif.setparam('bw', bw):
|
if netif.setparam('bw', bw):
|
||||||
# from tc-tbf(8): minimum value for burst is rate / kernel_hz
|
# from tc-tbf(8): minimum value for burst is rate / kernel_hz
|
||||||
if bw is not None:
|
if bw is not None:
|
||||||
burst = max(2 * netif.mtu, bw / 1000)
|
burst = max(2 * netif.mtu, bw / 1000)
|
||||||
limit = 0xffff # max IP payload
|
# max IP payload
|
||||||
|
limit = 0xffff
|
||||||
tbf = ["tbf", "rate", str(bw),
|
tbf = ["tbf", "rate", str(bw),
|
||||||
"burst", str(burst), "limit", str(limit)]
|
"burst", str(burst), "limit", str(limit)]
|
||||||
if bw > 0:
|
if bw > 0:
|
||||||
if self.up:
|
if self.up:
|
||||||
if (self.verbose):
|
logger.info("linkconfig: %s" % ([tc + parent + ["handle", "1:"] + tbf],))
|
||||||
self.info("linkconfig: %s" % \
|
subprocess.check_call(tc + parent + ["handle", "1:"] + tbf)
|
||||||
([tc + parent + ["handle", "1:"] + tbf],))
|
|
||||||
check_call(tc + parent + ["handle", "1:"] + tbf)
|
|
||||||
netif.setparam('has_tbf', True)
|
netif.setparam('has_tbf', True)
|
||||||
changed = True
|
changed = True
|
||||||
elif netif.getparam('has_tbf') and bw <= 0:
|
elif netif.getparam('has_tbf') and bw <= 0:
|
||||||
tcd = [] + tc
|
tcd = [] + tc
|
||||||
tcd[2] = "delete"
|
tcd[2] = "delete"
|
||||||
if self.up:
|
if self.up:
|
||||||
check_call(tcd + parent)
|
subprocess.check_call(tcd + parent)
|
||||||
netif.setparam('has_tbf', False)
|
netif.setparam('has_tbf', False)
|
||||||
# removing the parent removes the child
|
# removing the parent removes the child
|
||||||
netif.setparam('has_netem', False)
|
netif.setparam('has_netem', False)
|
||||||
|
|
@ -406,47 +498,47 @@ class LxBrNet(PyCoreNet):
|
||||||
return
|
return
|
||||||
tc[2] = "delete"
|
tc[2] = "delete"
|
||||||
if self.up:
|
if self.up:
|
||||||
if self.verbose:
|
logger.info("linkconfig: %s" % ([tc + parent + ["handle", "10:"]],))
|
||||||
self.info("linkconfig: %s" % \
|
subprocess.check_call(tc + parent + ["handle", "10:"])
|
||||||
([tc + parent + ["handle", "10:"]],))
|
|
||||||
check_call(tc + parent + ["handle", "10:"])
|
|
||||||
netif.setparam('has_netem', False)
|
netif.setparam('has_netem', False)
|
||||||
elif len(netem) > 1:
|
elif len(netem) > 1:
|
||||||
if self.up:
|
if self.up:
|
||||||
if self.verbose:
|
logger.info("linkconfig: %s" % ([tc + parent + ["handle", "10:"] + netem],))
|
||||||
self.info("linkconfig: %s" % \
|
subprocess.check_call(tc + parent + ["handle", "10:"] + netem)
|
||||||
([tc + parent + ["handle", "10:"] + netem],))
|
|
||||||
check_call(tc + parent + ["handle", "10:"] + netem)
|
|
||||||
netif.setparam('has_netem', True)
|
netif.setparam('has_netem', True)
|
||||||
|
|
||||||
def linknet(self, net):
|
def linknet(self, net):
|
||||||
''' Link this bridge with another by creating a veth pair and installing
|
"""
|
||||||
|
Link this bridge with another by creating a veth pair and installing
|
||||||
each device into each bridge.
|
each device into each bridge.
|
||||||
'''
|
|
||||||
sessionid = self.session.shortsessionid()
|
:param core.netns.vnet.LxBrNet net: network to link with
|
||||||
|
:return: created interface
|
||||||
|
:rtype: Veth
|
||||||
|
"""
|
||||||
|
sessionid = self.session.short_session_id()
|
||||||
try:
|
try:
|
||||||
self_objid = '%x' % self.objid
|
self_objid = "%x" % self.objid
|
||||||
except TypeError:
|
except TypeError:
|
||||||
self_objid = '%s' % self.objid
|
self_objid = "%s" % self.objid
|
||||||
try:
|
try:
|
||||||
net_objid = '%x' % net.objid
|
net_objid = "%x" % net.objid
|
||||||
except TypeError:
|
except TypeError:
|
||||||
net_objid = '%s' % net.objid
|
net_objid = "%s" % net.objid
|
||||||
localname = 'veth%s.%s.%s' % (self_objid, net_objid, sessionid)
|
localname = "veth%s.%s.%s" % (self_objid, net_objid, sessionid)
|
||||||
if len(localname) >= 16:
|
if len(localname) >= 16:
|
||||||
raise ValueError, "interface local name '%s' too long" % \
|
raise ValueError("interface local name %s too long" % localname)
|
||||||
localname
|
name = "veth%s.%s.%s" % (net_objid, self_objid, sessionid)
|
||||||
name = 'veth%s.%s.%s' % (net_objid, self_objid, sessionid)
|
|
||||||
if len(name) >= 16:
|
if len(name) >= 16:
|
||||||
raise ValueError, "interface name '%s' too long" % name
|
raise ValueError("interface name %s too long" % name)
|
||||||
netif = VEth(node=None, name=name, localname=localname,
|
netif = VEth(node=None, name=name, localname=localname,
|
||||||
mtu=1500, net=self, start=self.up)
|
mtu=1500, net=self, start=self.up)
|
||||||
self.attach(netif)
|
self.attach(netif)
|
||||||
if net.up:
|
if net.up:
|
||||||
# this is similar to net.attach() but uses netif.name instead
|
# this is similar to net.attach() but uses netif.name instead
|
||||||
# of localname
|
# of localname
|
||||||
check_call([BRCTL_BIN, "addif", net.brname, netif.name])
|
subprocess.check_call([constants.BRCTL_BIN, "addif", net.brname, netif.name])
|
||||||
check_call([IP_BIN, "link", "set", netif.name, "up"])
|
subprocess.check_call([constants.IP_BIN, "link", "set", netif.name, "up"])
|
||||||
i = net.newifindex()
|
i = net.newifindex()
|
||||||
net._netif[i] = netif
|
net._netif[i] = netif
|
||||||
with net._linked_lock:
|
with net._linked_lock:
|
||||||
|
|
@ -456,36 +548,58 @@ class LxBrNet(PyCoreNet):
|
||||||
return netif
|
return netif
|
||||||
|
|
||||||
def getlinknetif(self, net):
|
def getlinknetif(self, net):
|
||||||
''' Return the interface of that links this net with another net
|
"""
|
||||||
|
Return the interface of that links this net with another net
|
||||||
(that were linked using linknet()).
|
(that were linked using linknet()).
|
||||||
'''
|
|
||||||
|
:param core.netns.vnet.LxBrNet net: interface to get link for
|
||||||
|
:return: interface the provided network is linked to
|
||||||
|
:rtype: core.netns.vnet.LxBrNet
|
||||||
|
"""
|
||||||
for netif in self.netifs():
|
for netif in self.netifs():
|
||||||
if hasattr(netif, 'othernet') and netif.othernet == net:
|
if hasattr(netif, "othernet") and netif.othernet == net:
|
||||||
return netif
|
return netif
|
||||||
return None
|
return None
|
||||||
|
|
||||||
def addrconfig(self, addrlist):
|
def addrconfig(self, addrlist):
|
||||||
''' Set addresses on the bridge.
|
"""
|
||||||
'''
|
Set addresses on the bridge.
|
||||||
|
|
||||||
|
:param list[str] addrlist: address list
|
||||||
|
:return: nothing
|
||||||
|
"""
|
||||||
if not self.up:
|
if not self.up:
|
||||||
return
|
return
|
||||||
for addr in addrlist:
|
for addr in addrlist:
|
||||||
try:
|
try:
|
||||||
check_call([IP_BIN, "addr", "add", str(addr), "dev", self.brname])
|
subprocess.check_call([constants.IP_BIN, "addr", "add", str(addr), "dev", self.brname])
|
||||||
except Exception, e:
|
except subprocess.CalledProcessError:
|
||||||
self.exception(coreapi.CORE_EXCP_LEVEL_ERROR, self.brname,
|
logger.exception("Error adding IP address")
|
||||||
"Error adding IP address: %s" % e)
|
|
||||||
|
|
||||||
class GreTapBridge(LxBrNet):
|
class GreTapBridge(LxBrNet):
|
||||||
''' A network consisting of a bridge with a gretap device for tunneling to
|
"""
|
||||||
|
A network consisting of a bridge with a gretap device for tunneling to
|
||||||
another system.
|
another system.
|
||||||
'''
|
"""
|
||||||
|
|
||||||
def __init__(self, session, remoteip=None, objid=None, name=None,
|
def __init__(self, session, remoteip=None, objid=None, name=None,
|
||||||
policy = "ACCEPT", localip = None, ttl = 255, key = None,
|
policy="ACCEPT", localip=None, ttl=255, key=None, start=True):
|
||||||
verbose = False, start = True):
|
"""
|
||||||
LxBrNet.__init__(self, session = session, objid = objid,
|
Create a GreTapBridge instance.
|
||||||
name = name, verbose = verbose, policy = policy,
|
|
||||||
start = False)
|
:param core.session.Session session: core session instance
|
||||||
|
:param str remoteip: remote address
|
||||||
|
:param int objid: object id
|
||||||
|
:param str name: object name
|
||||||
|
:param policy: network policy
|
||||||
|
:param str localip: local address
|
||||||
|
:param ttl: ttl value
|
||||||
|
:param key: gre tap key
|
||||||
|
:param bool start: start flag
|
||||||
|
:return:
|
||||||
|
"""
|
||||||
|
LxBrNet.__init__(self, session=session, objid=objid, name=name, policy=policy, start=False)
|
||||||
self.grekey = key
|
self.grekey = key
|
||||||
if self.grekey is None:
|
if self.grekey is None:
|
||||||
self.grekey = self.session.sessionid ^ self.objid
|
self.grekey = self.session.sessionid ^ self.objid
|
||||||
|
|
@ -497,22 +611,27 @@ class GreTapBridge(LxBrNet):
|
||||||
if remoteip is None:
|
if remoteip is None:
|
||||||
self.gretap = None
|
self.gretap = None
|
||||||
else:
|
else:
|
||||||
self.gretap = GreTap(node = self, name = None, session = session,
|
self.gretap = GreTap(node=self, name=None, session=session, remoteip=remoteip,
|
||||||
remoteip = remoteip, objid = None, localip = localip, ttl = ttl,
|
objid=None, localip=localip, ttl=ttl, key=self.grekey)
|
||||||
key = self.grekey)
|
|
||||||
if start:
|
if start:
|
||||||
self.startup()
|
self.startup()
|
||||||
|
|
||||||
def startup(self):
|
def startup(self):
|
||||||
''' Creates a bridge and adds the gretap device to it.
|
"""
|
||||||
'''
|
Creates a bridge and adds the gretap device to it.
|
||||||
|
|
||||||
|
:return: nothing
|
||||||
|
"""
|
||||||
LxBrNet.startup(self)
|
LxBrNet.startup(self)
|
||||||
if self.gretap:
|
if self.gretap:
|
||||||
self.attach(self.gretap)
|
self.attach(self.gretap)
|
||||||
|
|
||||||
def shutdown(self):
|
def shutdown(self):
|
||||||
''' Detach the gretap device and remove the bridge.
|
"""
|
||||||
'''
|
Detach the gretap device and remove the bridge.
|
||||||
|
|
||||||
|
:return: nothing
|
||||||
|
"""
|
||||||
if self.gretap:
|
if self.gretap:
|
||||||
self.detach(self.gretap)
|
self.detach(self.gretap)
|
||||||
self.gretap.shutdown()
|
self.gretap.shutdown()
|
||||||
|
|
@ -520,24 +639,31 @@ class GreTapBridge(LxBrNet):
|
||||||
LxBrNet.shutdown(self)
|
LxBrNet.shutdown(self)
|
||||||
|
|
||||||
def addrconfig(self, addrlist):
|
def addrconfig(self, addrlist):
|
||||||
''' Set the remote tunnel endpoint. This is a one-time method for
|
"""
|
||||||
|
Set the remote tunnel endpoint. This is a one-time method for
|
||||||
creating the GreTap device, which requires the remoteip at startup.
|
creating the GreTap device, which requires the remoteip at startup.
|
||||||
The 1st address in the provided list is remoteip, 2nd optionally
|
The 1st address in the provided list is remoteip, 2nd optionally
|
||||||
specifies localip.
|
specifies localip.
|
||||||
'''
|
|
||||||
|
:param list addrlist: address list
|
||||||
|
:return: nothing
|
||||||
|
"""
|
||||||
if self.gretap:
|
if self.gretap:
|
||||||
raise ValueError, "gretap already exists for %s" % self.name
|
raise ValueError("gretap already exists for %s" % self.name)
|
||||||
remoteip = addrlist[0].split('/')[0]
|
remoteip = addrlist[0].split('/')[0]
|
||||||
localip = None
|
localip = None
|
||||||
if len(addrlist) > 1:
|
if len(addrlist) > 1:
|
||||||
localip = addrlist[1].split('/')[0]
|
localip = addrlist[1].split('/')[0]
|
||||||
self.gretap = GreTap(session = self.session, remoteip = remoteip,
|
self.gretap = GreTap(session=self.session, remoteip=remoteip, objid=None, name=None,
|
||||||
objid = None, name = None,
|
|
||||||
localip=localip, ttl=self.ttl, key=self.grekey)
|
localip=localip, ttl=self.ttl, key=self.grekey)
|
||||||
self.attach(self.gretap)
|
self.attach(self.gretap)
|
||||||
|
|
||||||
def setkey(self, key):
|
def setkey(self, key):
|
||||||
''' Set the GRE key used for the GreTap device. This needs to be set
|
"""
|
||||||
|
Set the GRE key used for the GreTap device. This needs to be set
|
||||||
prior to instantiating the GreTap device (before addrconfig).
|
prior to instantiating the GreTap device (before addrconfig).
|
||||||
'''
|
|
||||||
|
:param key: gre key
|
||||||
|
:return: nothing
|
||||||
|
"""
|
||||||
self.grekey = key
|
self.grekey = key
|
||||||
|
|
|
||||||
|
|
@ -1,35 +1,50 @@
|
||||||
#
|
"""
|
||||||
# CORE
|
PyCoreNode and LxcNode classes that implement the network namespac virtual node.
|
||||||
# Copyright (c)2010-2012 the Boeing Company.
|
"""
|
||||||
# See the LICENSE file included in this distribution.
|
|
||||||
#
|
|
||||||
# authors: Tom Goff <thomas.goff@boeing.com>
|
|
||||||
# Jeff Ahrenholz <jeffrey.m.ahrenholz@boeing.com>
|
|
||||||
#
|
|
||||||
'''
|
|
||||||
vnode.py: PyCoreNode and LxcNode classes that implement the network namespace
|
|
||||||
virtual node.
|
|
||||||
'''
|
|
||||||
|
|
||||||
import os, signal, sys, subprocess, vnodeclient, threading, string, shutil
|
import os
|
||||||
import random, time
|
import random
|
||||||
from core.api import coreapi
|
import shutil
|
||||||
from core.misc.utils import *
|
import signal
|
||||||
from core.constants import *
|
import string
|
||||||
from core.coreobj import PyCoreObj, PyCoreNode, PyCoreNetIf, Position
|
import subprocess
|
||||||
from core.netns.vif import VEth, TunTap
|
import threading
|
||||||
from core.emane.nodes import EmaneNode
|
|
||||||
|
from core import constants
|
||||||
|
from core.coreobj import PyCoreNetIf
|
||||||
|
from core.coreobj import PyCoreNode
|
||||||
|
from core.enumerations import NodeTypes
|
||||||
|
from core.misc import log
|
||||||
|
from core.misc import nodeutils
|
||||||
|
from core.misc import utils
|
||||||
|
from core.netns import vnodeclient
|
||||||
|
from core.netns.vif import TunTap
|
||||||
|
from core.netns.vif import VEth
|
||||||
|
|
||||||
|
logger = log.get_logger(__name__)
|
||||||
|
|
||||||
|
utils.check_executables([constants.IP_BIN])
|
||||||
|
|
||||||
checkexec([IP_BIN])
|
|
||||||
|
|
||||||
class SimpleLxcNode(PyCoreNode):
|
class SimpleLxcNode(PyCoreNode):
|
||||||
def __init__(self, session, objid = None, name = None, nodedir = None,
|
"""
|
||||||
verbose = False, start = True):
|
Provides simple lxc functionality for core nodes.
|
||||||
PyCoreNode.__init__(self, session, objid, name, verbose=verbose,
|
"""
|
||||||
start=start)
|
valid_deladdrtype = ("inet", "inet6", "inet6link")
|
||||||
|
|
||||||
|
def __init__(self, session, objid=None, name=None, nodedir=None, start=True):
|
||||||
|
"""
|
||||||
|
Create a SimpleLxcNode instance.
|
||||||
|
|
||||||
|
:param core.session.Session session: core session instance
|
||||||
|
:param int objid: object id
|
||||||
|
:param str name: object name
|
||||||
|
:param str nodedir: node directory
|
||||||
|
:param bool start: start flag
|
||||||
|
"""
|
||||||
|
PyCoreNode.__init__(self, session, objid, name, start=start)
|
||||||
self.nodedir = nodedir
|
self.nodedir = nodedir
|
||||||
self.ctrlchnlname = \
|
self.ctrlchnlname = os.path.abspath(os.path.join(self.session.session_dir, self.name))
|
||||||
os.path.abspath(os.path.join(self.session.sessiondir, self.name))
|
|
||||||
self.vnodeclient = None
|
self.vnodeclient = None
|
||||||
self.pid = None
|
self.pid = None
|
||||||
self.up = False
|
self.up = False
|
||||||
|
|
@ -37,168 +52,304 @@ class SimpleLxcNode(PyCoreNode):
|
||||||
self._mounts = []
|
self._mounts = []
|
||||||
|
|
||||||
def alive(self):
|
def alive(self):
|
||||||
|
"""
|
||||||
|
Check if the node is alive.
|
||||||
|
|
||||||
|
:return: True if node is alive, False otherwise
|
||||||
|
:rtype: bool
|
||||||
|
"""
|
||||||
try:
|
try:
|
||||||
os.kill(self.pid, 0)
|
os.kill(self.pid, 0)
|
||||||
except OSError:
|
except OSError:
|
||||||
return False
|
return False
|
||||||
|
|
||||||
return True
|
return True
|
||||||
|
|
||||||
def startup(self):
|
def startup(self):
|
||||||
''' Start a new namespace node by invoking the vnoded process that
|
"""
|
||||||
|
Start a new namespace node by invoking the vnoded process that
|
||||||
allocates a new namespace. Bring up the loopback device and set
|
allocates a new namespace. Bring up the loopback device and set
|
||||||
the hostname.
|
the hostname.
|
||||||
'''
|
|
||||||
|
:return: nothing
|
||||||
|
"""
|
||||||
if self.up:
|
if self.up:
|
||||||
raise Exception, "already up"
|
raise Exception("already up")
|
||||||
vnoded = ["%s/vnoded" % CORE_SBIN_DIR, "-v", "-c", self.ctrlchnlname,
|
vnoded = ["%s/vnoded" % constants.CORE_SBIN_DIR, "-v", "-c", self.ctrlchnlname,
|
||||||
"-l", self.ctrlchnlname + ".log",
|
"-l", self.ctrlchnlname + ".log",
|
||||||
"-p", self.ctrlchnlname + ".pid"]
|
"-p", self.ctrlchnlname + ".pid"]
|
||||||
if self.nodedir:
|
if self.nodedir:
|
||||||
vnoded += ["-C", self.nodedir]
|
vnoded += ["-C", self.nodedir]
|
||||||
env = self.session.getenviron(state=False)
|
env = self.session.get_environment(state=False)
|
||||||
env['NODE_NUMBER'] = str(self.objid)
|
env['NODE_NUMBER'] = str(self.objid)
|
||||||
env['NODE_NAME'] = str(self.name)
|
env['NODE_NAME'] = str(self.name)
|
||||||
|
|
||||||
try:
|
try:
|
||||||
tmp = subprocess.Popen(vnoded, stdout=subprocess.PIPE, env=env)
|
tmp = subprocess.Popen(vnoded, stdout=subprocess.PIPE, env=env)
|
||||||
except OSError, e:
|
except OSError:
|
||||||
msg = "error running vnoded command: %s (%s)" % (vnoded, e)
|
msg = "error running vnoded command: %s" % vnoded
|
||||||
self.exception(coreapi.CORE_EXCP_LEVEL_FATAL,
|
logger.exception("SimpleLxcNode.startup(): %s", msg)
|
||||||
"SimpleLxcNode.startup()", msg)
|
raise Exception(msg)
|
||||||
raise Exception, msg
|
|
||||||
try:
|
try:
|
||||||
self.pid = int(tmp.stdout.read())
|
self.pid = int(tmp.stdout.read())
|
||||||
tmp.stdout.close()
|
tmp.stdout.close()
|
||||||
except Exception:
|
except ValueError:
|
||||||
msg = "vnoded failed to create a namespace; "
|
msg = "vnoded failed to create a namespace; "
|
||||||
msg += "check kernel support and user priveleges"
|
msg += "check kernel support and user priveleges"
|
||||||
self.exception(coreapi.CORE_EXCP_LEVEL_FATAL,
|
logger.exception("SimpleLxcNode.startup(): %s", msg)
|
||||||
"SimpleLxcNode.startup()", msg)
|
|
||||||
if tmp.wait():
|
if tmp.wait():
|
||||||
raise Exception, ("command failed: %s" % vnoded)
|
raise Exception("command failed: %s" % vnoded)
|
||||||
self.vnodeclient = vnodeclient.VnodeClient(self.name,
|
|
||||||
self.ctrlchnlname)
|
self.vnodeclient = vnodeclient.VnodeClient(self.name, self.ctrlchnlname)
|
||||||
self.info("bringing up loopback interface")
|
logger.info("bringing up loopback interface")
|
||||||
self.cmd([IP_BIN, "link", "set", "lo", "up"])
|
self.cmd([constants.IP_BIN, "link", "set", "lo", "up"])
|
||||||
self.info("setting hostname: %s" % self.name)
|
logger.info("setting hostname: %s" % self.name)
|
||||||
self.cmd(["hostname", self.name])
|
self.cmd(["hostname", self.name])
|
||||||
self.up = True
|
self.up = True
|
||||||
|
|
||||||
def shutdown(self):
|
def shutdown(self):
|
||||||
|
"""
|
||||||
|
Shutdown logic for simple lxc nodes.
|
||||||
|
|
||||||
|
:return: nothing
|
||||||
|
"""
|
||||||
|
# nothing to do if node is not up
|
||||||
if not self.up:
|
if not self.up:
|
||||||
return
|
return
|
||||||
|
|
||||||
|
# unmount all targets
|
||||||
while self._mounts:
|
while self._mounts:
|
||||||
source, target = self._mounts.pop(-1)
|
source, target = self._mounts.pop(-1)
|
||||||
self.umount(target)
|
self.umount(target)
|
||||||
|
|
||||||
|
# shutdown all interfaces
|
||||||
for netif in self.netifs():
|
for netif in self.netifs():
|
||||||
netif.shutdown()
|
netif.shutdown()
|
||||||
|
|
||||||
|
# attempt to kill node process and wait for termination of children
|
||||||
try:
|
try:
|
||||||
os.kill(self.pid, signal.SIGTERM)
|
os.kill(self.pid, signal.SIGTERM)
|
||||||
os.waitpid(self.pid, 0)
|
os.waitpid(self.pid, 0)
|
||||||
except OSError:
|
except OSError:
|
||||||
pass
|
logger.exception("error killing process")
|
||||||
|
|
||||||
|
# remove node directory if present
|
||||||
try:
|
try:
|
||||||
|
if os.path.exists(self.ctrlchnlname):
|
||||||
os.unlink(self.ctrlchnlname)
|
os.unlink(self.ctrlchnlname)
|
||||||
except OSError:
|
except OSError:
|
||||||
pass
|
logger.exception("error removing file")
|
||||||
|
|
||||||
|
# clear interface data, close client, and mark self and not up
|
||||||
self._netif.clear()
|
self._netif.clear()
|
||||||
self.vnodeclient.close()
|
self.vnodeclient.close()
|
||||||
self.up = False
|
self.up = False
|
||||||
|
|
||||||
|
# TODO: potentially remove all these wrapper methods, just make use of object itself.
|
||||||
def cmd(self, args, wait=True):
|
def cmd(self, args, wait=True):
|
||||||
|
"""
|
||||||
|
Wrapper around vnodeclient cmd.
|
||||||
|
|
||||||
|
:param args: arguments for ocmmand
|
||||||
|
:param wait: wait or not
|
||||||
|
:return:
|
||||||
|
"""
|
||||||
return self.vnodeclient.cmd(args, wait)
|
return self.vnodeclient.cmd(args, wait)
|
||||||
|
|
||||||
def cmdresult(self, args):
|
def cmdresult(self, args):
|
||||||
|
"""
|
||||||
|
Wrapper around vnodeclient cmdresult.
|
||||||
|
|
||||||
|
:param args: arguments for ocmmand
|
||||||
|
:return:
|
||||||
|
"""
|
||||||
return self.vnodeclient.cmdresult(args)
|
return self.vnodeclient.cmdresult(args)
|
||||||
|
|
||||||
def popen(self, args):
|
def popen(self, args):
|
||||||
|
"""
|
||||||
|
Wrapper around vnodeclient popen.
|
||||||
|
|
||||||
|
:param args: arguments for ocmmand
|
||||||
|
:return:
|
||||||
|
"""
|
||||||
return self.vnodeclient.popen(args)
|
return self.vnodeclient.popen(args)
|
||||||
|
|
||||||
def icmd(self, args):
|
def icmd(self, args):
|
||||||
|
"""
|
||||||
|
Wrapper around vnodeclient icmd.
|
||||||
|
|
||||||
|
:param args: arguments for ocmmand
|
||||||
|
:return:
|
||||||
|
"""
|
||||||
return self.vnodeclient.icmd(args)
|
return self.vnodeclient.icmd(args)
|
||||||
|
|
||||||
def redircmd(self, infd, outfd, errfd, args, wait=True):
|
def redircmd(self, infd, outfd, errfd, args, wait=True):
|
||||||
|
"""
|
||||||
|
Wrapper around vnodeclient redircmd.
|
||||||
|
|
||||||
|
:param infd: input file descriptor
|
||||||
|
:param outfd: output file descriptor
|
||||||
|
:param errfd: err file descriptor
|
||||||
|
:param args: command arguments
|
||||||
|
:param wait: wait or not
|
||||||
|
:return:
|
||||||
|
"""
|
||||||
return self.vnodeclient.redircmd(infd, outfd, errfd, args, wait)
|
return self.vnodeclient.redircmd(infd, outfd, errfd, args, wait)
|
||||||
|
|
||||||
def term(self, sh="/bin/sh"):
|
def term(self, sh="/bin/sh"):
|
||||||
|
"""
|
||||||
|
Wrapper around vnodeclient term.
|
||||||
|
|
||||||
|
:param sh: shell to create terminal for
|
||||||
|
:return:
|
||||||
|
"""
|
||||||
return self.vnodeclient.term(sh=sh)
|
return self.vnodeclient.term(sh=sh)
|
||||||
|
|
||||||
def termcmdstring(self, sh="/bin/sh"):
|
def termcmdstring(self, sh="/bin/sh"):
|
||||||
|
"""
|
||||||
|
Wrapper around vnodeclient termcmdstring.
|
||||||
|
|
||||||
|
:param sh: shell to run command in
|
||||||
|
:return:
|
||||||
|
"""
|
||||||
return self.vnodeclient.termcmdstring(sh=sh)
|
return self.vnodeclient.termcmdstring(sh=sh)
|
||||||
|
|
||||||
def shcmd(self, cmdstr, sh="/bin/sh"):
|
def shcmd(self, cmdstr, sh="/bin/sh"):
|
||||||
|
"""
|
||||||
|
Wrapper around vnodeclient shcmd.
|
||||||
|
|
||||||
|
:param str cmdstr: command string
|
||||||
|
:param sh: shell to run command in
|
||||||
|
:return:
|
||||||
|
"""
|
||||||
return self.vnodeclient.shcmd(cmdstr, sh=sh)
|
return self.vnodeclient.shcmd(cmdstr, sh=sh)
|
||||||
|
|
||||||
def boot(self):
|
def boot(self):
|
||||||
|
"""
|
||||||
|
Boot logic.
|
||||||
|
|
||||||
|
:return: nothing
|
||||||
|
"""
|
||||||
pass
|
pass
|
||||||
|
|
||||||
def mount(self, source, target):
|
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
|
||||||
|
"""
|
||||||
source = os.path.abspath(source)
|
source = os.path.abspath(source)
|
||||||
self.info("mounting %s at %s" % (source, target))
|
logger.info("mounting %s at %s" % (source, target))
|
||||||
try:
|
try:
|
||||||
shcmd = "mkdir -p '%s' && %s -n --bind '%s' '%s'" % \
|
shcmd = "mkdir -p '%s' && %s -n --bind '%s' '%s'" % (
|
||||||
(target, MOUNT_BIN, source, target)
|
target, constants.MOUNT_BIN, source, target)
|
||||||
self.shcmd(shcmd)
|
self.shcmd(shcmd)
|
||||||
self._mounts.append((source, target))
|
self._mounts.append((source, target))
|
||||||
except:
|
except IOError:
|
||||||
self.warn("mounting failed for %s at %s" % (source, target))
|
logger.exception("mounting failed for %s at %s", source, target)
|
||||||
|
|
||||||
def umount(self, target):
|
def umount(self, target):
|
||||||
self.info("unmounting '%s'" % target)
|
"""
|
||||||
|
Unmount a target directory.
|
||||||
|
|
||||||
|
:param str target: target directory to unmount
|
||||||
|
:return: nothing
|
||||||
|
"""
|
||||||
|
logger.info("unmounting '%s'" % target)
|
||||||
try:
|
try:
|
||||||
self.cmd([UMOUNT_BIN, "-n", "-l", target])
|
self.cmd([constants.UMOUNT_BIN, "-n", "-l", target])
|
||||||
except:
|
except IOError:
|
||||||
self.warn("unmounting failed for %s" % target)
|
logger.exception("unmounting failed for %s" % target)
|
||||||
|
|
||||||
def newifindex(self):
|
def newifindex(self):
|
||||||
|
"""
|
||||||
|
Retrieve a new interface index.
|
||||||
|
|
||||||
|
:return: new interface index
|
||||||
|
:rtype: int
|
||||||
|
"""
|
||||||
with self.lock:
|
with self.lock:
|
||||||
return PyCoreNode.newifindex(self)
|
return super(SimpleLxcNode, self).newifindex()
|
||||||
|
|
||||||
def newveth(self, ifindex=None, ifname=None, net=None):
|
def newveth(self, ifindex=None, ifname=None, net=None):
|
||||||
|
"""
|
||||||
|
Create a new interface.
|
||||||
|
|
||||||
|
:param int ifindex: index for the new interface
|
||||||
|
:param str ifname: name for the new interface
|
||||||
|
:param net: network to associate interface with
|
||||||
|
:return: nothing
|
||||||
|
"""
|
||||||
self.lock.acquire()
|
self.lock.acquire()
|
||||||
try:
|
try:
|
||||||
if ifindex is None:
|
if ifindex is None:
|
||||||
ifindex = self.newifindex()
|
ifindex = self.newifindex()
|
||||||
|
|
||||||
if ifname is None:
|
if ifname is None:
|
||||||
ifname = "eth%d" % ifindex
|
ifname = "eth%d" % ifindex
|
||||||
sessionid = self.session.shortsessionid()
|
|
||||||
|
sessionid = self.session.short_session_id()
|
||||||
|
|
||||||
try:
|
try:
|
||||||
suffix = '%x.%s.%s' % (self.objid, ifindex, sessionid)
|
suffix = '%x.%s.%s' % (self.objid, ifindex, sessionid)
|
||||||
except TypeError:
|
except TypeError:
|
||||||
suffix = '%s.%s.%s' % (self.objid, ifindex, sessionid)
|
suffix = '%s.%s.%s' % (self.objid, ifindex, sessionid)
|
||||||
|
|
||||||
localname = 'veth' + suffix
|
localname = 'veth' + suffix
|
||||||
if len(localname) >= 16:
|
if len(localname) >= 16:
|
||||||
raise ValueError, "interface local name '%s' too long" % \
|
raise ValueError("interface local name '%s' too long" % localname)
|
||||||
localname
|
|
||||||
name = localname + 'p'
|
name = localname + 'p'
|
||||||
if len(name) >= 16:
|
if len(name) >= 16:
|
||||||
raise ValueError, "interface name '%s' too long" % name
|
raise ValueError, "interface name '%s' too long" % name
|
||||||
ifclass = VEth
|
veth = VEth(node=self, name=name, localname=localname, mtu=1500, net=net, start=self.up)
|
||||||
veth = ifclass(node = self, name = name, localname = localname,
|
|
||||||
mtu = 1500, net = net, start = self.up)
|
|
||||||
if self.up:
|
if self.up:
|
||||||
check_call([IP_BIN, "link", "set", veth.name,
|
subprocess.check_call([constants.IP_BIN, "link", "set", veth.name, "netns", str(self.pid)])
|
||||||
"netns", str(self.pid)])
|
self.cmd([constants.IP_BIN, "link", "set", veth.name, "name", ifname])
|
||||||
self.cmd([IP_BIN, "link", "set", veth.name, "name", ifname])
|
|
||||||
veth.name = ifname
|
veth.name = ifname
|
||||||
|
|
||||||
|
# retrieve interface information
|
||||||
|
result, output = self.cmdresult(["ip", "link", "show", veth.name])
|
||||||
|
logger.info("interface command output: %s", output)
|
||||||
|
output = output.split("\n")
|
||||||
|
veth.flow_id = int(output[0].strip().split(":")[0]) + 1
|
||||||
|
logger.info("interface flow index: %s - %s", veth.name, veth.flow_id)
|
||||||
|
veth.hwaddr = output[1].strip().split()[1]
|
||||||
|
logger.info("interface mac: %s - %s", veth.name, veth.hwaddr)
|
||||||
|
|
||||||
try:
|
try:
|
||||||
self.addnetif(veth, ifindex)
|
self.addnetif(veth, ifindex)
|
||||||
except:
|
except:
|
||||||
veth.shutdown()
|
veth.shutdown()
|
||||||
del veth
|
del veth
|
||||||
raise
|
raise
|
||||||
|
|
||||||
return ifindex
|
return ifindex
|
||||||
finally:
|
finally:
|
||||||
self.lock.release()
|
self.lock.release()
|
||||||
|
|
||||||
def newtuntap(self, ifindex=None, ifname=None, net=None):
|
def newtuntap(self, ifindex=None, ifname=None, net=None):
|
||||||
|
"""
|
||||||
|
Create a new tunnel tap.
|
||||||
|
|
||||||
|
:param int ifindex: interface index
|
||||||
|
:param str ifname: interface name
|
||||||
|
:param net: network to associate with
|
||||||
|
:return: interface index
|
||||||
|
:rtype: int
|
||||||
|
"""
|
||||||
self.lock.acquire()
|
self.lock.acquire()
|
||||||
try:
|
try:
|
||||||
if ifindex is None:
|
if ifindex is None:
|
||||||
ifindex = self.newifindex()
|
ifindex = self.newifindex()
|
||||||
if ifname is None:
|
if ifname is None:
|
||||||
ifname = "eth%d" % ifindex
|
ifname = "eth%d" % ifindex
|
||||||
sessionid = self.session.shortsessionid()
|
sessionid = self.session.short_session_id()
|
||||||
localname = "tap%s.%s.%s" % (self.objid, ifindex, sessionid)
|
localname = "tap%s.%s.%s" % (self.objid, ifindex, sessionid)
|
||||||
name = ifname
|
name = ifname
|
||||||
ifclass = TunTap
|
ifclass = TunTap
|
||||||
|
|
@ -206,65 +357,106 @@ class SimpleLxcNode(PyCoreNode):
|
||||||
mtu=1500, net=net, start=self.up)
|
mtu=1500, net=net, start=self.up)
|
||||||
try:
|
try:
|
||||||
self.addnetif(tuntap, ifindex)
|
self.addnetif(tuntap, ifindex)
|
||||||
except:
|
except Exception as e:
|
||||||
tuntap.shutdown()
|
tuntap.shutdown()
|
||||||
del tuntap
|
del tuntap
|
||||||
raise
|
raise e
|
||||||
return ifindex
|
return ifindex
|
||||||
finally:
|
finally:
|
||||||
self.lock.release()
|
self.lock.release()
|
||||||
|
|
||||||
def sethwaddr(self, ifindex, addr):
|
def sethwaddr(self, ifindex, addr):
|
||||||
|
"""
|
||||||
|
Set hardware addres for an interface.
|
||||||
|
|
||||||
|
:param int ifindex: index of interface to set hardware address for
|
||||||
|
:param core.misc.ipaddress.MacAddress addr: hardware address to set
|
||||||
|
:return: mothing
|
||||||
|
"""
|
||||||
self._netif[ifindex].sethwaddr(addr)
|
self._netif[ifindex].sethwaddr(addr)
|
||||||
if self.up:
|
if self.up:
|
||||||
(status, result) = self.cmdresult([IP_BIN, "link", "set", "dev",
|
(status, result) = self.cmdresult([constants.IP_BIN, "link", "set", "dev",
|
||||||
self.ifname(ifindex), "address", str(addr)])
|
self.ifname(ifindex), "address", str(addr)])
|
||||||
if status:
|
if status:
|
||||||
self.exception(coreapi.CORE_EXCP_LEVEL_ERROR,
|
logger.error("error setting MAC address %s", str(addr))
|
||||||
"SimpleLxcNode.sethwaddr()",
|
|
||||||
"error setting MAC address %s" % str(addr))
|
|
||||||
def addaddr(self, ifindex, addr):
|
def addaddr(self, ifindex, addr):
|
||||||
|
"""
|
||||||
|
Add interface address.
|
||||||
|
|
||||||
|
:param int ifindex: index of interface to add address to
|
||||||
|
:param str addr: address to add to interface
|
||||||
|
:return: nothing
|
||||||
|
"""
|
||||||
if self.up:
|
if self.up:
|
||||||
if ":" in str(addr): # check if addr is ipv6
|
if ":" in str(addr): # check if addr is ipv6
|
||||||
self.cmd([IP_BIN, "addr", "add", str(addr),
|
self.cmd([constants.IP_BIN, "addr", "add", str(addr),
|
||||||
"dev", self.ifname(ifindex)])
|
"dev", self.ifname(ifindex)])
|
||||||
else:
|
else:
|
||||||
self.cmd([IP_BIN, "addr", "add", str(addr), "broadcast", "+",
|
self.cmd([constants.IP_BIN, "addr", "add", str(addr), "broadcast", "+",
|
||||||
"dev", self.ifname(ifindex)])
|
"dev", self.ifname(ifindex)])
|
||||||
self._netif[ifindex].addaddr(addr)
|
self._netif[ifindex].addaddr(addr)
|
||||||
|
|
||||||
def deladdr(self, ifindex, addr):
|
def deladdr(self, ifindex, addr):
|
||||||
|
"""
|
||||||
|
Delete address from an interface.
|
||||||
|
|
||||||
|
:param int ifindex: index of interface to delete address from
|
||||||
|
:param str addr: address to delete from interface
|
||||||
|
:return: nothing
|
||||||
|
"""
|
||||||
try:
|
try:
|
||||||
self._netif[ifindex].deladdr(addr)
|
self._netif[ifindex].deladdr(addr)
|
||||||
except ValueError:
|
except ValueError:
|
||||||
self.warn("trying to delete unknown address: %s" % addr)
|
logger.exception("trying to delete unknown address: %s" % addr)
|
||||||
if self.up:
|
|
||||||
self.cmd([IP_BIN, "addr", "del", str(addr),
|
if self.up:
|
||||||
"dev", self.ifname(ifindex)])
|
self.cmd([constants.IP_BIN, "addr", "del", str(addr), "dev", self.ifname(ifindex)])
|
||||||
|
|
||||||
valid_deladdrtype = ("inet", "inet6", "inet6link")
|
|
||||||
def delalladdr(self, ifindex, addrtypes=valid_deladdrtype):
|
def delalladdr(self, ifindex, addrtypes=valid_deladdrtype):
|
||||||
|
"""
|
||||||
|
Delete all addresses from an interface.
|
||||||
|
|
||||||
|
:param int ifindex: index of interface to delete all addresses from
|
||||||
|
:param tuple addrtypes: address types to delete
|
||||||
|
:return: nothing
|
||||||
|
"""
|
||||||
addr = self.getaddr(self.ifname(ifindex), rescan=True)
|
addr = self.getaddr(self.ifname(ifindex), rescan=True)
|
||||||
for t in addrtypes:
|
for t in addrtypes:
|
||||||
if t not in self.valid_deladdrtype:
|
if t not in self.valid_deladdrtype:
|
||||||
raise ValueError, "addr type must be in: " + \
|
raise ValueError("addr type must be in: " + " ".join(self.valid_deladdrtype))
|
||||||
" ".join(self.valid_deladdrtype)
|
|
||||||
for a in addr[t]:
|
for a in addr[t]:
|
||||||
self.deladdr(ifindex, a)
|
self.deladdr(ifindex, a)
|
||||||
# update cached information
|
# update cached information
|
||||||
self.getaddr(self.ifname(ifindex), rescan=True)
|
self.getaddr(self.ifname(ifindex), rescan=True)
|
||||||
|
|
||||||
def ifup(self, ifindex):
|
def ifup(self, ifindex):
|
||||||
if self.up:
|
"""
|
||||||
self.cmd([IP_BIN, "link", "set", self.ifname(ifindex), "up"])
|
Bring an interface up.
|
||||||
|
|
||||||
def newnetif(self, net = None, addrlist = [], hwaddr = None,
|
:param int ifindex: index of interface to bring up
|
||||||
ifindex = None, ifname = None):
|
:return: nothing
|
||||||
|
"""
|
||||||
|
if self.up:
|
||||||
|
self.cmd([constants.IP_BIN, "link", "set", self.ifname(ifindex), "up"])
|
||||||
|
|
||||||
|
def newnetif(self, net=None, addrlist=None, hwaddr=None, ifindex=None, ifname=None):
|
||||||
|
"""
|
||||||
|
Create a new network interface.
|
||||||
|
|
||||||
|
:param net: network to associate with
|
||||||
|
:param list addrlist: addresses to add on the interface
|
||||||
|
:param core.misc.ipaddress.MacAddress hwaddr: hardware address to set for interface
|
||||||
|
:param int ifindex: index of interface to create
|
||||||
|
:param str ifname: name for interface
|
||||||
|
:return: interface index
|
||||||
|
:rtype: int
|
||||||
|
"""
|
||||||
self.lock.acquire()
|
self.lock.acquire()
|
||||||
try:
|
try:
|
||||||
if isinstance(net, EmaneNode):
|
# TODO: see if you can move this to emane specific code
|
||||||
ifindex = self.newtuntap(ifindex = ifindex, ifname = ifname,
|
if nodeutils.is_node(net, NodeTypes.EMANE):
|
||||||
net = net)
|
ifindex = self.newtuntap(ifindex=ifindex, ifname=ifname, net=net)
|
||||||
# TUN/TAP is not ready for addressing yet; the device may
|
# TUN/TAP is not ready for addressing yet; the device may
|
||||||
# take some time to appear, and installing it into a
|
# take some time to appear, and installing it into a
|
||||||
# namespace after it has been bound removes addressing;
|
# namespace after it has been bound removes addressing;
|
||||||
|
|
@ -272,85 +464,146 @@ class SimpleLxcNode(PyCoreNode):
|
||||||
self.attachnet(ifindex, net)
|
self.attachnet(ifindex, net)
|
||||||
netif = self.netif(ifindex)
|
netif = self.netif(ifindex)
|
||||||
netif.sethwaddr(hwaddr)
|
netif.sethwaddr(hwaddr)
|
||||||
for addr in maketuple(addrlist):
|
for addr in utils.maketuple(addrlist):
|
||||||
netif.addaddr(addr)
|
netif.addaddr(addr)
|
||||||
return ifindex
|
return ifindex
|
||||||
else:
|
else:
|
||||||
ifindex = self.newveth(ifindex = ifindex, ifname = ifname,
|
ifindex = self.newveth(ifindex=ifindex, ifname=ifname, net=net)
|
||||||
net = net)
|
|
||||||
if net is not None:
|
if net is not None:
|
||||||
self.attachnet(ifindex, net)
|
self.attachnet(ifindex, net)
|
||||||
|
|
||||||
if hwaddr:
|
if hwaddr:
|
||||||
self.sethwaddr(ifindex, hwaddr)
|
self.sethwaddr(ifindex, hwaddr)
|
||||||
for addr in maketuple(addrlist):
|
|
||||||
|
if addrlist:
|
||||||
|
for addr in utils.maketuple(addrlist):
|
||||||
self.addaddr(ifindex, addr)
|
self.addaddr(ifindex, addr)
|
||||||
|
|
||||||
self.ifup(ifindex)
|
self.ifup(ifindex)
|
||||||
return ifindex
|
return ifindex
|
||||||
finally:
|
finally:
|
||||||
self.lock.release()
|
self.lock.release()
|
||||||
|
|
||||||
def connectnode(self, ifname, othernode, otherifname):
|
def connectnode(self, ifname, othernode, otherifname):
|
||||||
|
"""
|
||||||
|
Connect a node.
|
||||||
|
|
||||||
|
:param str ifname: name of interface to connect
|
||||||
|
:param core.netns.nodes.LxcNode othernode: node to connect to
|
||||||
|
:param str otherifname: interface name to connect to
|
||||||
|
:return: nothing
|
||||||
|
"""
|
||||||
tmplen = 8
|
tmplen = 8
|
||||||
tmp1 = "tmp." + "".join([random.choice(string.ascii_lowercase)
|
tmp1 = "tmp." + "".join([random.choice(string.ascii_lowercase)
|
||||||
for x in xrange(tmplen)])
|
for x in xrange(tmplen)])
|
||||||
tmp2 = "tmp." + "".join([random.choice(string.ascii_lowercase)
|
tmp2 = "tmp." + "".join([random.choice(string.ascii_lowercase)
|
||||||
for x in xrange(tmplen)])
|
for x in xrange(tmplen)])
|
||||||
check_call([IP_BIN, "link", "add", "name", tmp1,
|
subprocess.check_call([constants.IP_BIN, "link", "add", "name", tmp1,
|
||||||
"type", "veth", "peer", "name", tmp2])
|
"type", "veth", "peer", "name", tmp2])
|
||||||
|
|
||||||
check_call([IP_BIN, "link", "set", tmp1, "netns", str(self.pid)])
|
subprocess.call([constants.IP_BIN, "link", "set", tmp1, "netns", str(self.pid)])
|
||||||
self.cmd([IP_BIN, "link", "set", tmp1, "name", ifname])
|
self.cmd([constants.IP_BIN, "link", "set", tmp1, "name", ifname])
|
||||||
self.addnetif(PyCoreNetIf(self, ifname), self.newifindex())
|
self.addnetif(PyCoreNetIf(self, ifname), self.newifindex())
|
||||||
|
|
||||||
check_call([IP_BIN, "link", "set", tmp2, "netns", str(othernode.pid)])
|
subprocess.check_call([constants.IP_BIN, "link", "set", tmp2, "netns", str(othernode.pid)])
|
||||||
othernode.cmd([IP_BIN, "link", "set", tmp2, "name", otherifname])
|
othernode.cmd([constants.IP_BIN, "link", "set", tmp2, "name", otherifname])
|
||||||
othernode.addnetif(PyCoreNetIf(othernode, otherifname),
|
othernode.addnetif(PyCoreNetIf(othernode, otherifname),
|
||||||
othernode.newifindex())
|
othernode.newifindex())
|
||||||
|
|
||||||
def addfile(self, srcname, filename):
|
def addfile(self, srcname, filename):
|
||||||
shcmd = "mkdir -p $(dirname '%s') && mv '%s' '%s' && sync" % \
|
"""
|
||||||
(filename, srcname, filename)
|
Add a file.
|
||||||
|
|
||||||
|
:param str srcname: source file name
|
||||||
|
:param str filename: file name to add
|
||||||
|
:return: nothing
|
||||||
|
"""
|
||||||
|
shcmd = "mkdir -p $(dirname '%s') && mv '%s' '%s' && sync" % (filename, srcname, filename)
|
||||||
self.shcmd(shcmd)
|
self.shcmd(shcmd)
|
||||||
|
|
||||||
def getaddr(self, ifname, rescan=False):
|
def getaddr(self, ifname, rescan=False):
|
||||||
|
"""
|
||||||
|
Wrapper around vnodeclient getaddr.
|
||||||
|
|
||||||
|
:param str ifname: interface name to get address for
|
||||||
|
:param bool rescan: rescan flag
|
||||||
|
:return:
|
||||||
|
"""
|
||||||
return self.vnodeclient.getaddr(ifname=ifname, rescan=rescan)
|
return self.vnodeclient.getaddr(ifname=ifname, rescan=rescan)
|
||||||
|
|
||||||
def netifstats(self, ifname=None):
|
def netifstats(self, ifname=None):
|
||||||
|
"""
|
||||||
|
Wrapper around vnodeclient netifstate.
|
||||||
|
|
||||||
|
:param str ifname: interface name to get state for
|
||||||
|
:return:
|
||||||
|
"""
|
||||||
return self.vnodeclient.netifstats(ifname=ifname)
|
return self.vnodeclient.netifstats(ifname=ifname)
|
||||||
|
|
||||||
|
|
||||||
class LxcNode(SimpleLxcNode):
|
class LxcNode(SimpleLxcNode):
|
||||||
|
"""
|
||||||
|
Provides lcx node functionality for core nodes.
|
||||||
|
"""
|
||||||
|
|
||||||
def __init__(self, session, objid=None, name=None,
|
def __init__(self, session, objid=None, name=None,
|
||||||
nodedir = None, bootsh = "boot.sh", verbose = False,
|
nodedir=None, bootsh="boot.sh", start=True):
|
||||||
start = True):
|
"""
|
||||||
|
Create a LxcNode instance.
|
||||||
|
|
||||||
|
:param core.session.Session session: core session instance
|
||||||
|
:param int objid: object id
|
||||||
|
:param str name: object name
|
||||||
|
:param str nodedir: node directory
|
||||||
|
:param bootsh: boot shell
|
||||||
|
:param bool start: start flag
|
||||||
|
"""
|
||||||
super(LxcNode, self).__init__(session=session, objid=objid,
|
super(LxcNode, self).__init__(session=session, objid=objid,
|
||||||
name = name, nodedir = nodedir,
|
name=name, nodedir=nodedir, start=start)
|
||||||
verbose = verbose, start = start)
|
|
||||||
self.bootsh = bootsh
|
self.bootsh = bootsh
|
||||||
if start:
|
if start:
|
||||||
self.startup()
|
self.startup()
|
||||||
|
|
||||||
def boot(self):
|
def boot(self):
|
||||||
|
"""
|
||||||
|
Boot the node.
|
||||||
|
|
||||||
|
:return: nothing
|
||||||
|
"""
|
||||||
self.session.services.bootnodeservices(self)
|
self.session.services.bootnodeservices(self)
|
||||||
|
|
||||||
def validate(self):
|
def validate(self):
|
||||||
|
"""
|
||||||
|
Validate the node.
|
||||||
|
|
||||||
|
:return: nothing
|
||||||
|
"""
|
||||||
self.session.services.validatenodeservices(self)
|
self.session.services.validatenodeservices(self)
|
||||||
|
|
||||||
def startup(self):
|
def startup(self):
|
||||||
|
"""
|
||||||
|
Startup logic for the node.
|
||||||
|
|
||||||
|
:return: nothing
|
||||||
|
"""
|
||||||
self.lock.acquire()
|
self.lock.acquire()
|
||||||
try:
|
try:
|
||||||
self.makenodedir()
|
self.makenodedir()
|
||||||
super(LxcNode, self).startup()
|
super(LxcNode, self).startup()
|
||||||
self.privatedir("/var/run")
|
self.privatedir("/var/run")
|
||||||
self.privatedir("/var/log")
|
self.privatedir("/var/log")
|
||||||
except OSError, e:
|
except OSError:
|
||||||
self.warn("Error with LxcNode.startup(): %s" % e)
|
logger.exception("error during LxcNode.startup()")
|
||||||
self.exception(coreapi.CORE_EXCP_LEVEL_ERROR,
|
|
||||||
"LxcNode.startup()", "%s" % e)
|
|
||||||
finally:
|
finally:
|
||||||
self.lock.release()
|
self.lock.release()
|
||||||
|
|
||||||
def shutdown(self):
|
def shutdown(self):
|
||||||
|
"""
|
||||||
|
Shutdown logic for the node.
|
||||||
|
|
||||||
|
:return: nothing
|
||||||
|
"""
|
||||||
if not self.up:
|
if not self.up:
|
||||||
return
|
return
|
||||||
self.lock.acquire()
|
self.lock.acquire()
|
||||||
|
|
@ -358,29 +611,40 @@ class LxcNode(SimpleLxcNode):
|
||||||
# self.session.services.stopnodeservices(self)
|
# self.session.services.stopnodeservices(self)
|
||||||
try:
|
try:
|
||||||
super(LxcNode, self).shutdown()
|
super(LxcNode, self).shutdown()
|
||||||
|
except:
|
||||||
|
logger.exception("error during shutdown")
|
||||||
finally:
|
finally:
|
||||||
self.rmnodedir()
|
self.rmnodedir()
|
||||||
self.lock.release()
|
self.lock.release()
|
||||||
|
|
||||||
def privatedir(self, path):
|
def privatedir(self, path):
|
||||||
|
"""
|
||||||
|
Create a private directory.
|
||||||
|
|
||||||
|
:param str path: path to create
|
||||||
|
:return: nothing
|
||||||
|
"""
|
||||||
if path[0] != "/":
|
if path[0] != "/":
|
||||||
raise ValueError, "path not fully qualified: " + path
|
raise ValueError("path not fully qualified: %s" % path)
|
||||||
hostpath = os.path.join(self.nodedir,
|
hostpath = os.path.join(self.nodedir, os.path.normpath(path).strip('/').replace('/', '.'))
|
||||||
os.path.normpath(path).strip('/').replace('/', '.'))
|
|
||||||
try:
|
try:
|
||||||
os.mkdir(hostpath)
|
os.mkdir(hostpath)
|
||||||
except OSError:
|
except OSError:
|
||||||
pass
|
logger.exception("error creating directory: %s", hostpath)
|
||||||
except Exception, e:
|
|
||||||
raise Exception, e
|
|
||||||
self.mount(hostpath, path)
|
self.mount(hostpath, path)
|
||||||
|
|
||||||
def hostfilename(self, filename):
|
def hostfilename(self, filename):
|
||||||
''' Return the name of a node's file on the host filesystem.
|
"""
|
||||||
'''
|
Return the name of a node's file on the host filesystem.
|
||||||
|
|
||||||
|
:param str filename: host file name
|
||||||
|
:return: path to file
|
||||||
|
"""
|
||||||
dirname, basename = os.path.split(filename)
|
dirname, basename = os.path.split(filename)
|
||||||
if not basename:
|
if not basename:
|
||||||
raise ValueError, "no basename for filename: " + filename
|
raise ValueError("no basename for filename: " + filename)
|
||||||
if dirname and dirname[0] == "/":
|
if dirname and dirname[0] == "/":
|
||||||
dirname = dirname[1:]
|
dirname = dirname[1:]
|
||||||
dirname = dirname.replace("/", ".")
|
dirname = dirname.replace("/", ".")
|
||||||
|
|
@ -388,6 +652,14 @@ class LxcNode(SimpleLxcNode):
|
||||||
return os.path.join(dirname, basename)
|
return os.path.join(dirname, basename)
|
||||||
|
|
||||||
def opennodefile(self, filename, mode="w"):
|
def opennodefile(self, filename, mode="w"):
|
||||||
|
"""
|
||||||
|
Open a node file, within it's directory.
|
||||||
|
|
||||||
|
:param str filename: file name to open
|
||||||
|
:param str mode: mode to open file in
|
||||||
|
:return: open file
|
||||||
|
:rtype: file
|
||||||
|
"""
|
||||||
hostfilename = self.hostfilename(filename)
|
hostfilename = self.hostfilename(filename)
|
||||||
dirname, basename = os.path.split(hostfilename)
|
dirname, basename = os.path.split(hostfilename)
|
||||||
if not os.path.isdir(dirname):
|
if not os.path.isdir(dirname):
|
||||||
|
|
@ -395,20 +667,32 @@ class LxcNode(SimpleLxcNode):
|
||||||
return open(hostfilename, mode)
|
return open(hostfilename, mode)
|
||||||
|
|
||||||
def nodefile(self, filename, contents, mode=0644):
|
def nodefile(self, filename, contents, mode=0644):
|
||||||
|
"""
|
||||||
|
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
|
||||||
|
"""
|
||||||
f = self.opennodefile(filename, "w")
|
f = self.opennodefile(filename, "w")
|
||||||
f.write(contents)
|
f.write(contents)
|
||||||
os.chmod(f.name, mode)
|
os.chmod(f.name, mode)
|
||||||
f.close()
|
f.close()
|
||||||
self.info("created nodefile: '%s'; mode: 0%o" % (f.name, mode))
|
logger.info("created nodefile: '%s'; mode: 0%o" % (f.name, mode))
|
||||||
|
|
||||||
def nodefilecopy(self, filename, srcfilename, mode=None):
|
def nodefilecopy(self, filename, srcfilename, mode=None):
|
||||||
''' Copy a file to a node, following symlinks and preserving metadata.
|
"""
|
||||||
|
Copy a file to a node, following symlinks and preserving metadata.
|
||||||
Change file mode if specified.
|
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
|
||||||
|
"""
|
||||||
hostfilename = self.hostfilename(filename)
|
hostfilename = self.hostfilename(filename)
|
||||||
shutil.copy2(srcfilename, hostfilename)
|
shutil.copy2(srcfilename, hostfilename)
|
||||||
if mode is not None:
|
if mode is not None:
|
||||||
os.chmod(hostfilename, mode)
|
os.chmod(hostfilename, mode)
|
||||||
self.info("copied nodefile: '%s'; mode: %s" % (hostfilename, mode))
|
logger.info("copied nodefile: '%s'; mode: %s" % (hostfilename, mode))
|
||||||
|
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,19 +1,17 @@
|
||||||
#
|
"""
|
||||||
# CORE
|
|
||||||
# Copyright (c)2010-2012 the Boeing Company.
|
|
||||||
# See the LICENSE file included in this distribution.
|
|
||||||
#
|
|
||||||
# author: Tom Goff <thomas.goff@boeing.com>
|
|
||||||
#
|
|
||||||
'''
|
|
||||||
vnodeclient.py: implementation of the VnodeClient class for issuing commands
|
vnodeclient.py: implementation of the VnodeClient class for issuing commands
|
||||||
over a control channel to the vnoded process running in a network namespace.
|
over a control channel to the vnoded process running in a network namespace.
|
||||||
The control channel can be accessed via calls to the vcmd Python module or
|
The control channel can be accessed via calls to the vcmd Python module or
|
||||||
by invoking the vcmd shell command.
|
by invoking the vcmd shell command.
|
||||||
'''
|
"""
|
||||||
|
|
||||||
import os, stat, sys
|
import os
|
||||||
from core.constants import *
|
import stat
|
||||||
|
|
||||||
|
from core import constants
|
||||||
|
from core.misc import log
|
||||||
|
|
||||||
|
logger = log.get_logger(__name__)
|
||||||
|
|
||||||
USE_VCMD_MODULE = True
|
USE_VCMD_MODULE = True
|
||||||
|
|
||||||
|
|
@ -22,10 +20,21 @@ if USE_VCMD_MODULE:
|
||||||
else:
|
else:
|
||||||
import subprocess
|
import subprocess
|
||||||
|
|
||||||
VCMD = os.path.join(CORE_SBIN_DIR, "vcmd")
|
VCMD = os.path.join(constants.CORE_SBIN_DIR, "vcmd")
|
||||||
|
|
||||||
|
|
||||||
class VnodeClient(object):
|
class VnodeClient(object):
|
||||||
|
"""
|
||||||
|
Provides client functionality for interacting with a virtual node.
|
||||||
|
"""
|
||||||
|
|
||||||
def __init__(self, name, ctrlchnlname):
|
def __init__(self, name, ctrlchnlname):
|
||||||
|
"""
|
||||||
|
Create a VnodeClient instance.
|
||||||
|
|
||||||
|
:param str name: name for client
|
||||||
|
:param str ctrlchnlname: control channel name
|
||||||
|
"""
|
||||||
self.name = name
|
self.name = name
|
||||||
self.ctrlchnlname = ctrlchnlname
|
self.ctrlchnlname = ctrlchnlname
|
||||||
if USE_VCMD_MODULE:
|
if USE_VCMD_MODULE:
|
||||||
|
|
@ -34,25 +43,39 @@ class VnodeClient(object):
|
||||||
self.cmdchnl = None
|
self.cmdchnl = None
|
||||||
self._addr = {}
|
self._addr = {}
|
||||||
|
|
||||||
def warn(self, msg):
|
|
||||||
print >> sys.stderr, "%s: %s" % (self.name, msg)
|
|
||||||
|
|
||||||
def connected(self):
|
def connected(self):
|
||||||
|
"""
|
||||||
|
Check if node is connected or not.
|
||||||
|
|
||||||
|
:return: True if connected, False otherwise
|
||||||
|
:rtype: bool
|
||||||
|
"""
|
||||||
if USE_VCMD_MODULE:
|
if USE_VCMD_MODULE:
|
||||||
return self.cmdchnl.connected()
|
return self.cmdchnl.connected()
|
||||||
else:
|
else:
|
||||||
return True
|
return True
|
||||||
|
|
||||||
def close(self):
|
def close(self):
|
||||||
|
"""
|
||||||
|
Close the client connection.
|
||||||
|
|
||||||
|
:return: nothing
|
||||||
|
"""
|
||||||
if USE_VCMD_MODULE:
|
if USE_VCMD_MODULE:
|
||||||
self.cmdchnl.close()
|
self.cmdchnl.close()
|
||||||
|
|
||||||
def cmd(self, args, wait=True):
|
def cmd(self, args, wait=True):
|
||||||
''' Execute a command on a node and return the status (return code).
|
"""
|
||||||
'''
|
Execute a command on a node and return the status (return code).
|
||||||
|
|
||||||
|
:param list args: command arguments
|
||||||
|
:param bool wait: wait for command to end or not
|
||||||
|
:return: command status
|
||||||
|
:rtype: int
|
||||||
|
"""
|
||||||
if USE_VCMD_MODULE:
|
if USE_VCMD_MODULE:
|
||||||
if not self.cmdchnl.connected():
|
if not self.cmdchnl.connected():
|
||||||
raise ValueError, "self.cmdchnl not connected"
|
raise ValueError("self.cmdchnl not connected")
|
||||||
tmp = self.cmdchnl.qcmd(args)
|
tmp = self.cmdchnl.qcmd(args)
|
||||||
if not wait:
|
if not wait:
|
||||||
return tmp
|
return tmp
|
||||||
|
|
@ -62,19 +85,25 @@ class VnodeClient(object):
|
||||||
mode = os.P_WAIT
|
mode = os.P_WAIT
|
||||||
else:
|
else:
|
||||||
mode = os.P_NOWAIT
|
mode = os.P_NOWAIT
|
||||||
tmp = os.spawnlp(mode, VCMD, VCMD, "-c",
|
tmp = os.spawnlp(mode, VCMD, VCMD, "-c", self.ctrlchnlname, "-q", "--", *args)
|
||||||
self.ctrlchnlname, "-q", "--", *args)
|
|
||||||
if not wait:
|
if not wait:
|
||||||
return tmp
|
return tmp
|
||||||
|
|
||||||
if tmp:
|
if tmp:
|
||||||
self.warn("cmd exited with status %s: %s" % (tmp, str(args)))
|
logger.warn("cmd exited with status %s: %s" % (tmp, str(args)))
|
||||||
|
|
||||||
return tmp
|
return tmp
|
||||||
|
|
||||||
def cmdresult(self, args):
|
def cmdresult(self, args):
|
||||||
''' Execute a command on a node and return a tuple containing the
|
"""
|
||||||
|
Execute a command on a node and return a tuple containing the
|
||||||
exit status and result string. stderr output
|
exit status and result string. stderr output
|
||||||
is folded into the stdout result string.
|
is folded into the stdout result string.
|
||||||
'''
|
|
||||||
|
:param list args: command arguments
|
||||||
|
:return: command status and combined stdout and stderr output
|
||||||
|
:rtype: tuple[int, str]
|
||||||
|
"""
|
||||||
cmdid, cmdin, cmdout, cmderr = self.popen(args)
|
cmdid, cmdin, cmdout, cmderr = self.popen(args)
|
||||||
result = cmdout.read()
|
result = cmdout.read()
|
||||||
result += cmderr.read()
|
result += cmderr.read()
|
||||||
|
|
@ -82,43 +111,69 @@ class VnodeClient(object):
|
||||||
cmdout.close()
|
cmdout.close()
|
||||||
cmderr.close()
|
cmderr.close()
|
||||||
status = cmdid.wait()
|
status = cmdid.wait()
|
||||||
return (status, result)
|
return status, result
|
||||||
|
|
||||||
def popen(self, args):
|
def popen(self, args):
|
||||||
|
"""
|
||||||
|
Execute a popen command against the node.
|
||||||
|
|
||||||
|
:param list args: command arguments
|
||||||
|
:return: popen object, stdin, stdout, and stderr
|
||||||
|
:rtype: tuple
|
||||||
|
"""
|
||||||
if USE_VCMD_MODULE:
|
if USE_VCMD_MODULE:
|
||||||
if not self.cmdchnl.connected():
|
if not self.cmdchnl.connected():
|
||||||
raise ValueError, "self.cmdchnl not connected"
|
raise ValueError("self.cmdchnl not connected")
|
||||||
return self.cmdchnl.popen(args)
|
return self.cmdchnl.popen(args)
|
||||||
else:
|
else:
|
||||||
cmd = [VCMD, "-c", self.ctrlchnlname, "--"]
|
cmd = [VCMD, "-c", self.ctrlchnlname, "--"]
|
||||||
cmd.extend(args)
|
cmd.extend(args)
|
||||||
tmp = subprocess.Popen(cmd, stdin = subprocess.PIPE,
|
tmp = subprocess.Popen(cmd, stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
|
||||||
stdout = subprocess.PIPE,
|
|
||||||
stderr = subprocess.PIPE)
|
|
||||||
return tmp, tmp.stdin, tmp.stdout, tmp.stderr
|
return tmp, tmp.stdin, tmp.stdout, tmp.stderr
|
||||||
|
|
||||||
def icmd(self, args):
|
def icmd(self, args):
|
||||||
return os.spawnlp(os.P_WAIT, VCMD, VCMD, "-c", self.ctrlchnlname,
|
"""
|
||||||
"--", *args)
|
Execute an icmd against a node.
|
||||||
|
|
||||||
|
:param list args: command arguments
|
||||||
|
:return: command result
|
||||||
|
:rtype: int
|
||||||
|
"""
|
||||||
|
return os.spawnlp(os.P_WAIT, VCMD, VCMD, "-c", self.ctrlchnlname, "--", *args)
|
||||||
|
|
||||||
def redircmd(self, infd, outfd, errfd, args, wait=True):
|
def redircmd(self, infd, outfd, errfd, args, wait=True):
|
||||||
'''
|
"""
|
||||||
Execute a command on a node with standard input, output, and
|
Execute a command on a node with standard input, output, and
|
||||||
error redirected according to the given file descriptors.
|
error redirected according to the given file descriptors.
|
||||||
'''
|
|
||||||
|
:param infd: stdin file descriptor
|
||||||
|
:param outfd: stdout file descriptor
|
||||||
|
:param errfd: stderr file descriptor
|
||||||
|
:param list args: command arguments
|
||||||
|
:param bool wait: wait flag
|
||||||
|
:return: command status
|
||||||
|
:rtype: int
|
||||||
|
"""
|
||||||
if not USE_VCMD_MODULE:
|
if not USE_VCMD_MODULE:
|
||||||
raise NotImplementedError
|
raise NotImplementedError
|
||||||
if not self.cmdchnl.connected():
|
if not self.cmdchnl.connected():
|
||||||
raise ValueError, "self.cmdchnl not connected"
|
raise ValueError("self.cmdchnl not connected")
|
||||||
tmp = self.cmdchnl.redircmd(infd, outfd, errfd, args)
|
tmp = self.cmdchnl.redircmd(infd, outfd, errfd, args)
|
||||||
if not wait:
|
if not wait:
|
||||||
return tmp
|
return tmp
|
||||||
tmp = tmp.wait()
|
tmp = tmp.wait()
|
||||||
if tmp:
|
if tmp:
|
||||||
self.warn("cmd exited with status %s: %s" % (tmp, str(args)))
|
logger.warn("cmd exited with status %s: %s" % (tmp, str(args)))
|
||||||
return tmp
|
return tmp
|
||||||
|
|
||||||
def term(self, sh="/bin/sh"):
|
def term(self, sh="/bin/sh"):
|
||||||
|
"""
|
||||||
|
Open a terminal on a node.
|
||||||
|
|
||||||
|
:param str sh: shell to open terminal with
|
||||||
|
:return: terminal command result
|
||||||
|
:rtype: int
|
||||||
|
"""
|
||||||
cmd = ("xterm", "-ut", "-title", self.name, "-e",
|
cmd = ("xterm", "-ut", "-title", self.name, "-e",
|
||||||
VCMD, "-c", self.ctrlchnlname, "--", sh)
|
VCMD, "-c", self.ctrlchnlname, "--", sh)
|
||||||
if "SUDO_USER" in os.environ:
|
if "SUDO_USER" in os.environ:
|
||||||
|
|
@ -128,18 +183,41 @@ class VnodeClient(object):
|
||||||
return os.spawnvp(os.P_NOWAIT, cmd[0], cmd)
|
return os.spawnvp(os.P_NOWAIT, cmd[0], cmd)
|
||||||
|
|
||||||
def termcmdstring(self, sh="/bin/sh"):
|
def termcmdstring(self, sh="/bin/sh"):
|
||||||
|
"""
|
||||||
|
Create a terminal command string.
|
||||||
|
|
||||||
|
:param str sh: shell to execute command in
|
||||||
|
:return: str
|
||||||
|
"""
|
||||||
return "%s -c %s -- %s" % (VCMD, self.ctrlchnlname, sh)
|
return "%s -c %s -- %s" % (VCMD, self.ctrlchnlname, sh)
|
||||||
|
|
||||||
def shcmd(self, cmdstr, sh="/bin/sh"):
|
def shcmd(self, cmdstr, sh="/bin/sh"):
|
||||||
|
"""
|
||||||
|
Execute a shell command.
|
||||||
|
|
||||||
|
:param str cmdstr: command string
|
||||||
|
:param str sh: shell to run command in
|
||||||
|
:return: command result
|
||||||
|
:rtype: int
|
||||||
|
"""
|
||||||
return self.cmd([sh, "-c", cmdstr])
|
return self.cmd([sh, "-c", cmdstr])
|
||||||
|
|
||||||
def getaddr(self, ifname, rescan=False):
|
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:
|
if ifname in self._addr and not rescan:
|
||||||
return self._addr[ifname]
|
return self._addr[ifname]
|
||||||
tmp = {"ether": [], "inet": [], "inet6": [], "inet6link": []}
|
tmp = {"ether": [], "inet": [], "inet6": [], "inet6link": []}
|
||||||
cmd = [IP_BIN, "addr", "show", "dev", ifname]
|
cmd = [constants.IP_BIN, "addr", "show", "dev", ifname]
|
||||||
cmdid, cmdin, cmdout, cmderr = self.popen(cmd)
|
cmdid, cmdin, cmdout, cmderr = self.popen(cmd)
|
||||||
cmdin.close()
|
cmdin.close()
|
||||||
|
|
||||||
for line in cmdout:
|
for line in cmdout:
|
||||||
line = line.strip().split()
|
line = line.strip().split()
|
||||||
if line[0] == "link/ether":
|
if line[0] == "link/ether":
|
||||||
|
|
@ -152,21 +230,27 @@ class VnodeClient(object):
|
||||||
elif line[3] == "link":
|
elif line[3] == "link":
|
||||||
tmp["inet6link"].append(line[1])
|
tmp["inet6link"].append(line[1])
|
||||||
else:
|
else:
|
||||||
self.warn("unknown scope: %s" % line[3])
|
logger.warn("unknown scope: %s" % line[3])
|
||||||
else:
|
|
||||||
pass
|
|
||||||
err = cmderr.read()
|
err = cmderr.read()
|
||||||
cmdout.close()
|
cmdout.close()
|
||||||
cmderr.close()
|
cmderr.close()
|
||||||
status = cmdid.wait()
|
status = cmdid.wait()
|
||||||
if status:
|
if status:
|
||||||
self.warn("nonzero exist status (%s) for cmd: %s" % (status, cmd))
|
logger.warn("nonzero exist status (%s) for cmd: %s" % (status, cmd))
|
||||||
if err:
|
if err:
|
||||||
self.warn("error output: %s" % err)
|
logger.warn("error output: %s" % err)
|
||||||
self._addr[ifname] = tmp
|
self._addr[ifname] = tmp
|
||||||
return tmp
|
return tmp
|
||||||
|
|
||||||
def netifstats(self, ifname=None):
|
def netifstats(self, ifname=None):
|
||||||
|
"""
|
||||||
|
Retrieve network interface state.
|
||||||
|
|
||||||
|
:param str ifname: name of interface to get state for
|
||||||
|
:return: interface state information
|
||||||
|
:rtype: dict
|
||||||
|
"""
|
||||||
stats = {}
|
stats = {}
|
||||||
cmd = ["cat", "/proc/net/dev"]
|
cmd = ["cat", "/proc/net/dev"]
|
||||||
cmdid, cmdin, cmdout, cmderr = self.popen(cmd)
|
cmdid, cmdin, cmdout, cmderr = self.popen(cmd)
|
||||||
|
|
@ -195,34 +279,47 @@ class VnodeClient(object):
|
||||||
cmderr.close()
|
cmderr.close()
|
||||||
status = cmdid.wait()
|
status = cmdid.wait()
|
||||||
if status:
|
if status:
|
||||||
self.warn("nonzero exist status (%s) for cmd: %s" % (status, cmd))
|
logger.warn("nonzero exist status (%s) for cmd: %s" % (status, cmd))
|
||||||
if err:
|
if err:
|
||||||
self.warn("error output: %s" % err)
|
logger.warn("error output: %s" % err)
|
||||||
if ifname is not None:
|
if ifname is not None:
|
||||||
return stats[ifname]
|
return stats[ifname]
|
||||||
else:
|
else:
|
||||||
return stats
|
return stats
|
||||||
|
|
||||||
def createclients(sessiondir, clientcls = VnodeClient,
|
|
||||||
cmdchnlfilterfunc = None):
|
def createclients(sessiondir, clientcls=VnodeClient, cmdchnlfilterfunc=None):
|
||||||
direntries = map(lambda x: os.path.join(sessiondir, x),
|
"""
|
||||||
os.listdir(sessiondir))
|
Create clients
|
||||||
|
|
||||||
|
:param str sessiondir: session directory to create clients
|
||||||
|
:param class clientcls: class to create clients from
|
||||||
|
:param func cmdchnlfilterfunc: command channel filter function
|
||||||
|
:return: list of created clients
|
||||||
|
:rtype: list
|
||||||
|
"""
|
||||||
|
direntries = map(lambda x: os.path.join(sessiondir, x), os.listdir(sessiondir))
|
||||||
cmdchnls = filter(lambda x: stat.S_ISSOCK(os.stat(x).st_mode), direntries)
|
cmdchnls = filter(lambda x: stat.S_ISSOCK(os.stat(x).st_mode), direntries)
|
||||||
if cmdchnlfilterfunc:
|
if cmdchnlfilterfunc:
|
||||||
cmdchnls = filter(cmdchnlfilterfunc, cmdchnls)
|
cmdchnls = filter(cmdchnlfilterfunc, cmdchnls)
|
||||||
cmdchnls.sort()
|
cmdchnls.sort()
|
||||||
return map(lambda x: clientcls(os.path.basename(x), x), cmdchnls)
|
return map(lambda x: clientcls(os.path.basename(x), x), cmdchnls)
|
||||||
|
|
||||||
def createremoteclients(sessiondir, clientcls = VnodeClient,
|
|
||||||
filterfunc = None):
|
def createremoteclients(sessiondir, clientcls=VnodeClient, filterfunc=None):
|
||||||
''' Creates remote VnodeClients, for nodes emulated on other machines. The
|
"""
|
||||||
|
Creates remote VnodeClients, for nodes emulated on other machines. The
|
||||||
session.Broker writes a n1.conf/server file having the server's info.
|
session.Broker writes a n1.conf/server file having the server's info.
|
||||||
'''
|
|
||||||
direntries = map(lambda x: os.path.join(sessiondir, x),
|
:param str sessiondir: session directory to create clients
|
||||||
os.listdir(sessiondir))
|
:param class clientcls: class to create clients from
|
||||||
|
:param func filterfunc: filter function
|
||||||
|
:return: list of remove clients
|
||||||
|
:rtype: list
|
||||||
|
"""
|
||||||
|
direntries = map(lambda x: os.path.join(sessiondir, x), os.listdir(sessiondir))
|
||||||
nodedirs = filter(lambda x: stat.S_ISDIR(os.stat(x).st_mode), direntries)
|
nodedirs = filter(lambda x: stat.S_ISDIR(os.stat(x).st_mode), direntries)
|
||||||
nodedirs = filter(lambda x: os.path.exists(os.path.join(x, "server")),
|
nodedirs = filter(lambda x: os.path.exists(os.path.join(x, "server")), nodedirs)
|
||||||
nodedirs)
|
|
||||||
if filterfunc:
|
if filterfunc:
|
||||||
nodedirs = filter(filterfunc, nodedirs)
|
nodedirs = filter(filterfunc, nodedirs)
|
||||||
nodedirs.sort()
|
nodedirs.sort()
|
||||||
|
|
|
||||||
|
|
@ -1,32 +1,24 @@
|
||||||
#
|
"""
|
||||||
# CORE
|
PhysicalNode class for including real systems in the emulated network.
|
||||||
# Copyright (c)2011-2012 the Boeing Company.
|
"""
|
||||||
# See the LICENSE file included in this distribution.
|
|
||||||
#
|
|
||||||
# author: Jeff Ahrenholz <jeffrey.m.ahrenholz@boeing.com>
|
|
||||||
#
|
|
||||||
''' PhysicalNode class for including real systems in the emulated network.
|
|
||||||
'''
|
|
||||||
import os, threading, subprocess
|
|
||||||
|
|
||||||
from core.misc.ipaddr import *
|
import os
|
||||||
from core.misc.utils import *
|
import subprocess
|
||||||
from core.constants import *
|
import threading
|
||||||
from core.api import coreapi
|
|
||||||
from core.coreobj import PyCoreNode, PyCoreNetIf
|
from core import constants
|
||||||
from core.emane.nodes import EmaneNode
|
from core.coreobj import PyCoreNode
|
||||||
if os.uname()[0] == "Linux":
|
from core.misc import log
|
||||||
|
from core.misc import utils
|
||||||
|
from core.netns.vnet import GreTap
|
||||||
from core.netns.vnet import LxBrNet
|
from core.netns.vnet import LxBrNet
|
||||||
from core.netns.vif import GreTap
|
|
||||||
elif os.uname()[0] == "FreeBSD":
|
logger = log.get_logger(__name__)
|
||||||
from core.bsd.vnet import NetgraphNet
|
|
||||||
|
|
||||||
|
|
||||||
class PhysicalNode(PyCoreNode):
|
class PhysicalNode(PyCoreNode):
|
||||||
def __init__(self, session, objid = None, name = None,
|
def __init__(self, session, objid=None, name=None, nodedir=None, start=True):
|
||||||
nodedir = None, verbose = False, start = True):
|
PyCoreNode.__init__(self, session, objid, name, start=start)
|
||||||
PyCoreNode.__init__(self, session, objid, name, verbose=verbose,
|
|
||||||
start=start)
|
|
||||||
self.nodedir = nodedir
|
self.nodedir = nodedir
|
||||||
self.up = start
|
self.up = start
|
||||||
self.lock = threading.RLock()
|
self.lock = threading.RLock()
|
||||||
|
|
@ -46,9 +38,8 @@ class PhysicalNode(PyCoreNode):
|
||||||
self.makenodedir()
|
self.makenodedir()
|
||||||
# self.privatedir("/var/run")
|
# self.privatedir("/var/run")
|
||||||
# self.privatedir("/var/log")
|
# self.privatedir("/var/log")
|
||||||
except OSError, e:
|
except OSError:
|
||||||
self.exception(coreapi.CORE_EXCP_LEVEL_ERROR,
|
logger.exception("startup error")
|
||||||
"PhysicalNode.startup()", e)
|
|
||||||
finally:
|
finally:
|
||||||
self.lock.release()
|
self.lock.release()
|
||||||
|
|
||||||
|
|
@ -64,16 +55,17 @@ class PhysicalNode(PyCoreNode):
|
||||||
self.rmnodedir()
|
self.rmnodedir()
|
||||||
self.lock.release()
|
self.lock.release()
|
||||||
|
|
||||||
|
|
||||||
def termcmdstring(self, sh="/bin/sh"):
|
def termcmdstring(self, sh="/bin/sh"):
|
||||||
''' The broker will add the appropriate SSH command to open a terminal
|
"""
|
||||||
|
The broker will add the appropriate SSH command to open a terminal
|
||||||
on this physical node.
|
on this physical node.
|
||||||
'''
|
"""
|
||||||
return sh
|
return sh
|
||||||
|
|
||||||
def cmd(self, args, wait=True):
|
def cmd(self, args, wait=True):
|
||||||
''' run a command on the physical node
|
"""
|
||||||
'''
|
run a command on the physical node
|
||||||
|
"""
|
||||||
os.chdir(self.nodedir)
|
os.chdir(self.nodedir)
|
||||||
try:
|
try:
|
||||||
if wait:
|
if wait:
|
||||||
|
|
@ -82,109 +74,105 @@ class PhysicalNode(PyCoreNode):
|
||||||
else:
|
else:
|
||||||
# os.spawnlp(os.P_NOWAIT, args)
|
# os.spawnlp(os.P_NOWAIT, args)
|
||||||
subprocess.Popen(args)
|
subprocess.Popen(args)
|
||||||
except CalledProcessError, e:
|
except subprocess.CalledProcessError:
|
||||||
self.warn("cmd exited with status %s: %s" % (e, str(args)))
|
logger.exception("cmd exited with status: %s", str(args))
|
||||||
|
|
||||||
def cmdresult(self, args):
|
def cmdresult(self, args):
|
||||||
''' run a command on the physical node and get the result
|
"""
|
||||||
'''
|
run a command on the physical node and get the result
|
||||||
|
"""
|
||||||
os.chdir(self.nodedir)
|
os.chdir(self.nodedir)
|
||||||
# in Python 2.7 we can use subprocess.check_output() here
|
# in Python 2.7 we can use subprocess.check_output() here
|
||||||
tmp = subprocess.Popen(args, stdin=open(os.devnull, 'r'),
|
tmp = subprocess.Popen(args, stdin=open(os.devnull, 'r'),
|
||||||
stdout=subprocess.PIPE,
|
stdout=subprocess.PIPE,
|
||||||
stderr=subprocess.STDOUT)
|
stderr=subprocess.STDOUT)
|
||||||
result, err = tmp.communicate() # err will always be None
|
# err will always be None
|
||||||
|
result, err = tmp.communicate()
|
||||||
status = tmp.wait()
|
status = tmp.wait()
|
||||||
return (status, result)
|
return status, result
|
||||||
|
|
||||||
def shcmd(self, cmdstr, sh="/bin/sh"):
|
def shcmd(self, cmdstr, sh="/bin/sh"):
|
||||||
return self.cmd([sh, "-c", cmdstr])
|
return self.cmd([sh, "-c", cmdstr])
|
||||||
|
|
||||||
def sethwaddr(self, ifindex, addr):
|
def sethwaddr(self, ifindex, addr):
|
||||||
''' same as SimpleLxcNode.sethwaddr()
|
"""
|
||||||
'''
|
same as SimpleLxcNode.sethwaddr()
|
||||||
|
"""
|
||||||
self._netif[ifindex].sethwaddr(addr)
|
self._netif[ifindex].sethwaddr(addr)
|
||||||
ifname = self.ifname(ifindex)
|
ifname = self.ifname(ifindex)
|
||||||
if self.up:
|
if self.up:
|
||||||
(status, result) = self.cmdresult([IP_BIN, "link", "set", "dev",
|
(status, result) = self.cmdresult(
|
||||||
ifname, "address", str(addr)])
|
[constants.IP_BIN, "link", "set", "dev", ifname, "address", str(addr)])
|
||||||
if status:
|
if status:
|
||||||
self.exception(coreapi.CORE_EXCP_LEVEL_ERROR,
|
logger.error("error setting MAC address %s", str(addr))
|
||||||
"PhysicalNode.sethwaddr()",
|
|
||||||
"error setting MAC address %s" % str(addr))
|
|
||||||
|
|
||||||
def addaddr(self, ifindex, addr):
|
def addaddr(self, ifindex, addr):
|
||||||
''' same as SimpleLxcNode.addaddr()
|
"""
|
||||||
'''
|
same as SimpleLxcNode.addaddr()
|
||||||
|
"""
|
||||||
if self.up:
|
if self.up:
|
||||||
self.cmd([IP_BIN, "addr", "add", str(addr),
|
self.cmd([constants.IP_BIN, "addr", "add", str(addr), "dev", self.ifname(ifindex)])
|
||||||
"dev", self.ifname(ifindex)])
|
|
||||||
self._netif[ifindex].addaddr(addr)
|
self._netif[ifindex].addaddr(addr)
|
||||||
|
|
||||||
def deladdr(self, ifindex, addr):
|
def deladdr(self, ifindex, addr):
|
||||||
''' same as SimpleLxcNode.deladdr()
|
"""
|
||||||
'''
|
same as SimpleLxcNode.deladdr()
|
||||||
|
"""
|
||||||
try:
|
try:
|
||||||
self._netif[ifindex].deladdr(addr)
|
self._netif[ifindex].deladdr(addr)
|
||||||
except ValueError:
|
except ValueError:
|
||||||
self.warn("trying to delete unknown address: %s" % addr)
|
logger.exception("trying to delete unknown address: %s", addr)
|
||||||
|
|
||||||
if self.up:
|
if self.up:
|
||||||
self.cmd([IP_BIN, "addr", "del", str(addr),
|
self.cmd([constants.IP_BIN, "addr", "del", str(addr), "dev", self.ifname(ifindex)])
|
||||||
"dev", self.ifname(ifindex)])
|
|
||||||
|
|
||||||
def adoptnetif(self, netif, ifindex, hwaddr, addrlist):
|
def adoptnetif(self, netif, ifindex, hwaddr, addrlist):
|
||||||
''' The broker builds a GreTap tunnel device to this physical node.
|
"""
|
||||||
|
The broker builds a GreTap tunnel device to this physical node.
|
||||||
When a link message is received linking this node to another part of
|
When a link message is received linking this node to another part of
|
||||||
the emulation, no new interface is created; instead, adopt the
|
the emulation, no new interface is created; instead, adopt the
|
||||||
GreTap netif as the node interface.
|
GreTap netif as the node interface.
|
||||||
'''
|
"""
|
||||||
netif.name = "gt%d" % ifindex
|
netif.name = "gt%d" % ifindex
|
||||||
netif.node = self
|
netif.node = self
|
||||||
self.addnetif(netif, ifindex)
|
self.addnetif(netif, ifindex)
|
||||||
# use a more reasonable name, e.g. "gt0" instead of "gt.56286.150"
|
# use a more reasonable name, e.g. "gt0" instead of "gt.56286.150"
|
||||||
if self.up:
|
if self.up:
|
||||||
self.cmd([IP_BIN, "link", "set", "dev", netif.localname, "down"])
|
self.cmd([constants.IP_BIN, "link", "set", "dev", netif.localname, "down"])
|
||||||
self.cmd([IP_BIN, "link", "set", netif.localname, "name", netif.name])
|
self.cmd([constants.IP_BIN, "link", "set", netif.localname, "name", netif.name])
|
||||||
netif.localname = netif.name
|
netif.localname = netif.name
|
||||||
if hwaddr:
|
if hwaddr:
|
||||||
self.sethwaddr(ifindex, hwaddr)
|
self.sethwaddr(ifindex, hwaddr)
|
||||||
for addr in maketuple(addrlist):
|
for addr in utils.maketuple(addrlist):
|
||||||
self.addaddr(ifindex, addr)
|
self.addaddr(ifindex, addr)
|
||||||
if self.up:
|
if self.up:
|
||||||
self.cmd([IP_BIN, "link", "set", "dev", netif.localname, "up"])
|
self.cmd([constants.IP_BIN, "link", "set", "dev", netif.localname, "up"])
|
||||||
|
|
||||||
def linkconfig(self, netif, bw=None, delay=None,
|
def linkconfig(self, netif, bw=None, delay=None,
|
||||||
loss=None, duplicate=None, jitter=None, netif2=None):
|
loss=None, duplicate=None, jitter=None, netif2=None):
|
||||||
''' Apply tc queing disciplines using LxBrNet.linkconfig()
|
"""
|
||||||
'''
|
Apply tc queing disciplines using LxBrNet.linkconfig()
|
||||||
if os.uname()[0] == "Linux":
|
"""
|
||||||
netcls = LxBrNet
|
|
||||||
elif os.uname()[0] == "FreeBSD":
|
|
||||||
netcls = NetgraphNet
|
|
||||||
else:
|
|
||||||
raise NotImplementedError, "unsupported platform"
|
|
||||||
# borrow the tc qdisc commands from LxBrNet.linkconfig()
|
# borrow the tc qdisc commands from LxBrNet.linkconfig()
|
||||||
tmp = netcls(session=self.session, start=False)
|
linux_bridge = LxBrNet(session=self.session, start=False)
|
||||||
tmp.up = True
|
linux_bridge.up = True
|
||||||
tmp.linkconfig(netif, bw=bw, delay=delay, loss=loss,
|
linux_bridge.linkconfig(netif, bw=bw, delay=delay, loss=loss, duplicate=duplicate,
|
||||||
duplicate=duplicate, jitter=jitter, netif2=netif2)
|
jitter=jitter, netif2=netif2)
|
||||||
del tmp
|
del linux_bridge
|
||||||
|
|
||||||
def newifindex(self):
|
def newifindex(self):
|
||||||
self.lock.acquire()
|
with self.lock:
|
||||||
try:
|
|
||||||
while self.ifindex in self._netif:
|
while self.ifindex in self._netif:
|
||||||
self.ifindex += 1
|
self.ifindex += 1
|
||||||
ifindex = self.ifindex
|
ifindex = self.ifindex
|
||||||
self.ifindex += 1
|
self.ifindex += 1
|
||||||
return ifindex
|
return ifindex
|
||||||
finally:
|
|
||||||
self.lock.release()
|
|
||||||
|
|
||||||
def newnetif(self, net = None, addrlist = [], hwaddr = None,
|
def newnetif(self, net=None, addrlist=[], hwaddr=None, ifindex=None, ifname=None):
|
||||||
ifindex = None, ifname = None):
|
|
||||||
if self.up and net is None:
|
if self.up and net is None:
|
||||||
raise NotImplementedError
|
raise NotImplementedError
|
||||||
|
|
||||||
if ifindex is None:
|
if ifindex is None:
|
||||||
ifindex = self.newifindex()
|
ifindex = self.newifindex()
|
||||||
|
|
||||||
|
|
@ -193,8 +181,7 @@ class PhysicalNode(PyCoreNode):
|
||||||
# tunnel to net not built yet, so build it now and adopt it
|
# tunnel to net not built yet, so build it now and adopt it
|
||||||
gt = self.session.broker.addnettunnel(net.objid)
|
gt = self.session.broker.addnettunnel(net.objid)
|
||||||
if gt is None or len(gt) != 1:
|
if gt is None or len(gt) != 1:
|
||||||
self.session.warn("Error building tunnel from PhysicalNode."
|
raise ValueError("error building tunnel from adding a new network interface: %s" % gt)
|
||||||
"newnetif()")
|
|
||||||
gt = gt[0]
|
gt = gt[0]
|
||||||
net.detach(gt)
|
net.detach(gt)
|
||||||
self.adoptnetif(gt, ifindex, hwaddr, addrlist)
|
self.adoptnetif(gt, ifindex, hwaddr, addrlist)
|
||||||
|
|
@ -203,44 +190,41 @@ class PhysicalNode(PyCoreNode):
|
||||||
# this is reached when configuring services (self.up=False)
|
# this is reached when configuring services (self.up=False)
|
||||||
if ifname is None:
|
if ifname is None:
|
||||||
ifname = "gt%d" % ifindex
|
ifname = "gt%d" % ifindex
|
||||||
netif = GreTap(node = self, name = ifname, session = self.session,
|
|
||||||
start = False)
|
netif = GreTap(node=self, name=ifname, session=self.session, start=False)
|
||||||
self.adoptnetif(netif, ifindex, hwaddr, addrlist)
|
self.adoptnetif(netif, ifindex, hwaddr, addrlist)
|
||||||
return ifindex
|
return ifindex
|
||||||
|
|
||||||
|
|
||||||
def privatedir(self, path):
|
def privatedir(self, path):
|
||||||
if path[0] != "/":
|
if path[0] != "/":
|
||||||
raise ValueError, "path not fully qualified: " + path
|
raise ValueError, "path not fully qualified: " + path
|
||||||
hostpath = os.path.join(self.nodedir,
|
hostpath = os.path.join(self.nodedir, os.path.normpath(path).strip('/').replace('/', '.'))
|
||||||
os.path.normpath(path).strip('/').replace('/', '.'))
|
|
||||||
try:
|
try:
|
||||||
os.mkdir(hostpath)
|
os.mkdir(hostpath)
|
||||||
except OSError:
|
except OSError:
|
||||||
pass
|
logger.exception("error creating directory: %s", hostpath)
|
||||||
except Exception, e:
|
|
||||||
raise Exception, e
|
|
||||||
self.mount(hostpath, path)
|
self.mount(hostpath, path)
|
||||||
|
|
||||||
def mount(self, source, target):
|
def mount(self, source, target):
|
||||||
source = os.path.abspath(source)
|
source = os.path.abspath(source)
|
||||||
self.info("mounting %s at %s" % (source, target))
|
logger.info("mounting %s at %s" % (source, target))
|
||||||
|
|
||||||
try:
|
try:
|
||||||
os.makedirs(target)
|
os.makedirs(target)
|
||||||
except OSError:
|
self.cmd([constants.MOUNT_BIN, "--bind", source, target])
|
||||||
pass
|
|
||||||
try:
|
|
||||||
self.cmd([MOUNT_BIN, "--bind", source, target])
|
|
||||||
self._mounts.append((source, target))
|
self._mounts.append((source, target))
|
||||||
|
except OSError:
|
||||||
|
logger.exception("error making directories")
|
||||||
except:
|
except:
|
||||||
self.warn("mounting failed for %s at %s" % (source, target))
|
logger.exception("mounting failed for %s at %s", source, target)
|
||||||
|
|
||||||
def umount(self, target):
|
def umount(self, target):
|
||||||
self.info("unmounting '%s'" % target)
|
logger.info("unmounting '%s'" % target)
|
||||||
try:
|
try:
|
||||||
self.cmd([UMOUNT_BIN, "-l", target])
|
self.cmd([constants.UMOUNT_BIN, "-l", target])
|
||||||
except:
|
except:
|
||||||
self.warn("unmounting failed for %s" % target)
|
logger.exception("unmounting failed for %s", target)
|
||||||
|
|
||||||
def opennodefile(self, filename, mode="w"):
|
def opennodefile(self, filename, mode="w"):
|
||||||
dirname, basename = os.path.split(filename)
|
dirname, basename = os.path.split(filename)
|
||||||
|
|
@ -260,6 +244,4 @@ class PhysicalNode(PyCoreNode):
|
||||||
f.write(contents)
|
f.write(contents)
|
||||||
os.chmod(f.name, mode)
|
os.chmod(f.name, mode)
|
||||||
f.close()
|
f.close()
|
||||||
self.info("created nodefile: '%s'; mode: 0%o" % (f.name, mode))
|
logger.info("created nodefile: '%s'; mode: 0%o" % (f.name, mode))
|
||||||
|
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,27 +0,0 @@
|
||||||
# Copyright (c)2010-2012 the Boeing Company.
|
|
||||||
# See the LICENSE file included in this distribution.
|
|
||||||
|
|
||||||
"""
|
|
||||||
This is a convenience module that imports a set of platform-dependent
|
|
||||||
defaults.
|
|
||||||
"""
|
|
||||||
|
|
||||||
from misc.utils import ensurepath
|
|
||||||
ensurepath(["/sbin", "/bin", "/usr/sbin", "/usr/bin"])
|
|
||||||
del ensurepath
|
|
||||||
|
|
||||||
from session import Session
|
|
||||||
|
|
||||||
import os
|
|
||||||
|
|
||||||
if os.uname()[0] == "Linux":
|
|
||||||
from netns import nodes
|
|
||||||
try:
|
|
||||||
from xen import xen
|
|
||||||
except ImportError:
|
|
||||||
#print "Xen support disabled."
|
|
||||||
pass
|
|
||||||
elif os.uname()[0] == "FreeBSD":
|
|
||||||
from bsd import nodes
|
|
||||||
from phys import pnodes
|
|
||||||
del os
|
|
||||||
|
|
@ -1,74 +1,102 @@
|
||||||
#
|
"""
|
||||||
# CORE
|
|
||||||
# Copyright (c)2012-2013 the Boeing Company.
|
|
||||||
# See the LICENSE file included in this distribution.
|
|
||||||
#
|
|
||||||
# author: Jeff Ahrenholz <jeffrey.m.ahrenholz@boeing.com>
|
|
||||||
#
|
|
||||||
'''
|
|
||||||
sdt.py: Scripted Display Tool (SDT3D) helper
|
sdt.py: Scripted Display Tool (SDT3D) helper
|
||||||
'''
|
"""
|
||||||
|
|
||||||
from core.constants import *
|
|
||||||
from core.api import coreapi
|
|
||||||
from coreobj import PyCoreNet, PyCoreObj
|
|
||||||
from core.netns import nodes
|
|
||||||
from urlparse import urlparse
|
|
||||||
import socket
|
import socket
|
||||||
|
from urlparse import urlparse
|
||||||
|
|
||||||
|
from core import constants
|
||||||
|
from core.api import coreapi
|
||||||
|
from core.coreobj import PyCoreNet
|
||||||
|
from core.coreobj import PyCoreObj
|
||||||
|
from core.enumerations import EventTypes
|
||||||
|
from core.enumerations import LinkTlvs
|
||||||
|
from core.enumerations import LinkTypes
|
||||||
|
from core.enumerations import MessageFlags
|
||||||
|
from core.enumerations import MessageTypes
|
||||||
|
from core.enumerations import NodeTlvs
|
||||||
|
from core.enumerations import NodeTypes
|
||||||
|
from core.misc import log
|
||||||
|
from core.misc import nodeutils
|
||||||
|
|
||||||
|
logger = log.get_logger(__name__)
|
||||||
|
|
||||||
|
|
||||||
|
# TODO: A named tuple may be more appropriate, than abusing a class dict like this
|
||||||
|
class Bunch(object):
|
||||||
|
"""
|
||||||
|
Helper class for recording a collection of attributes.
|
||||||
|
"""
|
||||||
|
|
||||||
|
def __init__(self, **kwds):
|
||||||
|
"""
|
||||||
|
Create a Bunch instance.
|
||||||
|
|
||||||
|
:param dict kwds: keyword arguments
|
||||||
|
:return:
|
||||||
|
"""
|
||||||
|
self.__dict__.update(kwds)
|
||||||
|
|
||||||
|
|
||||||
class Sdt(object):
|
class Sdt(object):
|
||||||
''' Helper class for exporting session objects to NRL's SDT3D.
|
"""
|
||||||
|
Helper class for exporting session objects to NRL"s SDT3D.
|
||||||
The connect() method initializes the display, and can be invoked
|
The connect() method initializes the display, and can be invoked
|
||||||
when a node position or link has changed.
|
when a node position or link has changed.
|
||||||
'''
|
"""
|
||||||
DEFAULT_SDT_URL = "tcp://127.0.0.1:50000/"
|
DEFAULT_SDT_URL = "tcp://127.0.0.1:50000/"
|
||||||
# default altitude (in meters) for flyto view
|
# default altitude (in meters) for flyto view
|
||||||
DEFAULT_ALT = 2500
|
DEFAULT_ALT = 2500
|
||||||
# TODO: read in user's nodes.conf here; below are default node types
|
# TODO: read in user"s nodes.conf here; below are default node types from the GUI
|
||||||
# from the GUI
|
DEFAULT_SPRITES = [
|
||||||
DEFAULT_SPRITES = [('router', 'router.gif'), ('host', 'host.gif'),
|
("router", "router.gif"), ("host", "host.gif"),
|
||||||
('PC', 'pc.gif'), ('mdr', 'mdr.gif'),
|
("PC", "pc.gif"), ("mdr", "mdr.gif"),
|
||||||
('prouter', 'router_green.gif'), ('xen', 'xen.gif'),
|
("prouter", "router_green.gif"), ("xen", "xen.gif"),
|
||||||
('hub', 'hub.gif'), ('lanswitch','lanswitch.gif'),
|
("hub", "hub.gif"), ("lanswitch", "lanswitch.gif"),
|
||||||
('wlan', 'wlan.gif'), ('rj45','rj45.gif'),
|
("wlan", "wlan.gif"), ("rj45", "rj45.gif"),
|
||||||
('tunnel','tunnel.gif'),
|
("tunnel", "tunnel.gif"),
|
||||||
]
|
]
|
||||||
|
|
||||||
class Bunch:
|
|
||||||
''' Helper class for recording a collection of attributes.
|
|
||||||
'''
|
|
||||||
def __init__(self, **kwds):
|
|
||||||
self.__dict__.update(kwds)
|
|
||||||
|
|
||||||
def __init__(self, session):
|
def __init__(self, session):
|
||||||
|
"""
|
||||||
|
Creates a Sdt instance.
|
||||||
|
|
||||||
|
:param core.session.Session session: session this manager is tied to
|
||||||
|
"""
|
||||||
self.session = session
|
self.session = session
|
||||||
self.sock = None
|
self.sock = None
|
||||||
self.connected = False
|
self.connected = False
|
||||||
self.showerror = True
|
self.showerror = True
|
||||||
self.url = self.DEFAULT_SDT_URL
|
self.url = self.DEFAULT_SDT_URL
|
||||||
self.verbose = self.session.getcfgitembool('verbose', False)
|
|
||||||
# node information for remote nodes not in session._objs
|
# node information for remote nodes not in session._objs
|
||||||
# local nodes also appear here since their obj may not exist yet
|
# local nodes also appear here since their obj may not exist yet
|
||||||
self.remotes = {}
|
self.remotes = {}
|
||||||
session.broker.handlers.add(self.handledistributed)
|
session.broker.handlers.add(self.handledistributed)
|
||||||
|
|
||||||
def is_enabled(self):
|
def is_enabled(self):
|
||||||
''' Check for 'enablesdt' session option. Return False by default if
|
"""
|
||||||
|
Check for "enablesdt" session option. Return False by default if
|
||||||
the option is missing.
|
the option is missing.
|
||||||
'''
|
|
||||||
if not hasattr(self.session.options, 'enablesdt'):
|
:return: True if enabled, False otherwise
|
||||||
|
:rtype: bool
|
||||||
|
"""
|
||||||
|
if not hasattr(self.session.options, "enablesdt"):
|
||||||
return False
|
return False
|
||||||
enabled = self.session.options.enablesdt
|
enabled = self.session.options.enablesdt
|
||||||
if enabled in ('1', 'true', 1, True):
|
if enabled in ("1", "true", 1, True):
|
||||||
return True
|
return True
|
||||||
return False
|
return False
|
||||||
|
|
||||||
def seturl(self):
|
def seturl(self):
|
||||||
''' Read 'sdturl' from session options, or use the default value.
|
"""
|
||||||
|
Read "sdturl" from session options, or use the default value.
|
||||||
Set self.url, self.address, self.protocol
|
Set self.url, self.address, self.protocol
|
||||||
'''
|
|
||||||
|
:return: nothing
|
||||||
|
"""
|
||||||
url = None
|
url = None
|
||||||
if hasattr(self.session.options,'sdturl'):
|
if hasattr(self.session.options, "sdturl"):
|
||||||
if self.session.options.sdturl != "":
|
if self.session.options.sdturl != "":
|
||||||
url = self.session.options.sdturl
|
url = self.session.options.sdturl
|
||||||
if url is None or url == "":
|
if url is None or url == "":
|
||||||
|
|
@ -78,147 +106,193 @@ class Sdt(object):
|
||||||
self.protocol = self.url.scheme
|
self.protocol = self.url.scheme
|
||||||
|
|
||||||
def connect(self, flags=0):
|
def connect(self, flags=0):
|
||||||
''' Connect to the SDT address/port if enabled.
|
"""
|
||||||
'''
|
Connect to the SDT address/port if enabled.
|
||||||
|
|
||||||
|
:return: True if connected, False otherwise
|
||||||
|
:rtype: bool
|
||||||
|
"""
|
||||||
if not self.is_enabled():
|
if not self.is_enabled():
|
||||||
return False
|
return False
|
||||||
if self.connected:
|
if self.connected:
|
||||||
return True
|
return True
|
||||||
if self.session.getstate() == coreapi.CORE_EVENT_SHUTDOWN_STATE:
|
if self.session.state == EventTypes.SHUTDOWN_STATE.value:
|
||||||
return False
|
return False
|
||||||
|
|
||||||
self.seturl()
|
self.seturl()
|
||||||
if self.showerror:
|
logger.info("connecting to SDT at %s://%s" % (self.protocol, self.address))
|
||||||
self.session.info("connecting to SDT at %s://%s" \
|
|
||||||
% (self.protocol, self.address))
|
|
||||||
if self.sock is None:
|
if self.sock is None:
|
||||||
try:
|
try:
|
||||||
if (self.protocol.lower() == 'udp'):
|
if self.protocol.lower() == "udp":
|
||||||
self.sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
|
self.sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
|
||||||
self.sock.connect(self.address)
|
self.sock.connect(self.address)
|
||||||
else:
|
else:
|
||||||
# Default to tcp
|
# Default to tcp
|
||||||
self.sock = socket.create_connection(self.address, 5)
|
self.sock = socket.create_connection(self.address, 5)
|
||||||
except Exception, e:
|
except IOError:
|
||||||
if self.showerror:
|
logger.exception("SDT socket connect error")
|
||||||
self.session.warn("SDT socket connect error: %s" % e)
|
|
||||||
self.showerror = False
|
|
||||||
return False
|
return False
|
||||||
|
|
||||||
if not self.initialize():
|
if not self.initialize():
|
||||||
return False
|
return False
|
||||||
|
|
||||||
self.connected = True
|
self.connected = True
|
||||||
# refresh all objects in SDT3D when connecting after session start
|
# refresh all objects in SDT3D when connecting after session start
|
||||||
if not flags & coreapi.CORE_API_ADD_FLAG:
|
if not flags & MessageFlags.ADD.value and not self.sendobjs():
|
||||||
if not self.sendobjs():
|
|
||||||
return False
|
return False
|
||||||
|
|
||||||
return True
|
return True
|
||||||
|
|
||||||
def initialize(self):
|
def initialize(self):
|
||||||
''' Load icon sprites, and fly to the reference point location on
|
"""
|
||||||
|
Load icon sprites, and fly to the reference point location on
|
||||||
the virtual globe.
|
the virtual globe.
|
||||||
'''
|
|
||||||
if not self.cmd('path "%s/icons/normal"' % CORE_DATA_DIR):
|
:return: initialize command status
|
||||||
|
:rtype: bool
|
||||||
|
"""
|
||||||
|
if not self.cmd("path \"%s/icons/normal\"" % constants.CORE_DATA_DIR):
|
||||||
return False
|
return False
|
||||||
# send node type to icon mappings
|
# send node type to icon mappings
|
||||||
for (type, icon) in self.DEFAULT_SPRITES:
|
for type, icon in self.DEFAULT_SPRITES:
|
||||||
if not self.cmd('sprite %s image %s' % (type, icon)):
|
if not self.cmd("sprite %s image %s" % (type, icon)):
|
||||||
return False
|
return False
|
||||||
(lat, long) = self.session.location.refgeo[:2]
|
(lat, long) = self.session.location.refgeo[:2]
|
||||||
return self.cmd('flyto %.6f,%.6f,%d' % (long, lat, self.DEFAULT_ALT))
|
return self.cmd("flyto %.6f,%.6f,%d" % (long, lat, self.DEFAULT_ALT))
|
||||||
|
|
||||||
def disconnect(self):
|
def disconnect(self):
|
||||||
|
"""
|
||||||
|
Disconnect from SDT.
|
||||||
|
|
||||||
|
:return: nothing
|
||||||
|
"""
|
||||||
|
if self.sock:
|
||||||
try:
|
try:
|
||||||
self.sock.close()
|
self.sock.close()
|
||||||
except:
|
except IOError:
|
||||||
pass
|
logger.error("error closing socket")
|
||||||
|
finally:
|
||||||
self.sock = None
|
self.sock = None
|
||||||
|
|
||||||
self.connected = False
|
self.connected = False
|
||||||
|
|
||||||
def shutdown(self):
|
def shutdown(self):
|
||||||
''' Invoked from Session.shutdown() and Session.checkshutdown().
|
"""
|
||||||
'''
|
Invoked from Session.shutdown() and Session.checkshutdown().
|
||||||
self.cmd('clear all')
|
|
||||||
|
:return: nothing
|
||||||
|
"""
|
||||||
|
self.cmd("clear all")
|
||||||
self.disconnect()
|
self.disconnect()
|
||||||
self.showerror = True
|
self.showerror = True
|
||||||
|
|
||||||
def cmd(self, cmdstr):
|
def cmd(self, cmdstr):
|
||||||
''' Send an SDT command over a UDP socket. socket.sendall() is used
|
"""
|
||||||
|
Send an SDT command over a UDP socket. socket.sendall() is used
|
||||||
as opposed to socket.sendto() because an exception is raised when
|
as opposed to socket.sendto() because an exception is raised when
|
||||||
there is no socket listener.
|
there is no socket listener.
|
||||||
'''
|
|
||||||
|
:param str cmdstr: command to send
|
||||||
|
:return: True if command was successful, False otherwise
|
||||||
|
:rtype: bool
|
||||||
|
"""
|
||||||
if self.sock is None:
|
if self.sock is None:
|
||||||
return False
|
return False
|
||||||
try:
|
try:
|
||||||
if self.verbose:
|
logger.info("sdt: %s" % cmdstr)
|
||||||
self.session.info("sdt: %s" % cmdstr)
|
|
||||||
self.sock.sendall("%s\n" % cmdstr)
|
self.sock.sendall("%s\n" % cmdstr)
|
||||||
return True
|
return True
|
||||||
except Exception, e:
|
except IOError:
|
||||||
if self.showerror:
|
logger.exception("SDT connection error")
|
||||||
self.session.warn("SDT connection error: %s" % e)
|
|
||||||
self.showerror = False
|
|
||||||
self.sock = None
|
self.sock = None
|
||||||
self.connected = False
|
self.connected = False
|
||||||
return False
|
return False
|
||||||
|
|
||||||
def updatenode(self, nodenum, flags, x, y, z,
|
def updatenode(self, nodenum, flags, x, y, z, name=None, type=None, icon=None):
|
||||||
name=None, type=None, icon=None):
|
"""
|
||||||
''' Node is updated from a Node Message or mobility script.
|
Node is updated from a Node Message or mobility script.
|
||||||
'''
|
|
||||||
|
:param int nodenum: node id to update
|
||||||
|
:param flags: update flags
|
||||||
|
:param x: x position
|
||||||
|
:param y: y position
|
||||||
|
:param z: z position
|
||||||
|
:param str name: node name
|
||||||
|
:param type: node type
|
||||||
|
:param icon: node icon
|
||||||
|
:return: nothing
|
||||||
|
"""
|
||||||
if not self.connect():
|
if not self.connect():
|
||||||
return
|
return
|
||||||
if flags & coreapi.CORE_API_DEL_FLAG:
|
if flags & MessageFlags.DELETE.value:
|
||||||
self.cmd('delete node,%d' % nodenum)
|
self.cmd("delete node,%d" % nodenum)
|
||||||
return
|
return
|
||||||
if x is None or y is None:
|
if x is None or y is None:
|
||||||
return
|
return
|
||||||
(lat, long, alt) = self.session.location.getgeo(x, y, z)
|
lat, long, alt = self.session.location.getgeo(x, y, z)
|
||||||
pos = "pos %.6f,%.6f,%.6f" % (long, lat, alt)
|
pos = "pos %.6f,%.6f,%.6f" % (long, lat, alt)
|
||||||
if flags & coreapi.CORE_API_ADD_FLAG:
|
if flags & MessageFlags.ADD.value:
|
||||||
if icon is not None:
|
if icon is not None:
|
||||||
type = name
|
type = name
|
||||||
icon = icon.replace("$CORE_DATA_DIR", CORE_DATA_DIR)
|
icon = icon.replace("$CORE_DATA_DIR", constants.CORE_DATA_DIR)
|
||||||
icon = icon.replace("$CORE_CONF_DIR", CORE_CONF_DIR)
|
icon = icon.replace("$CORE_CONF_DIR", constants.CORE_CONF_DIR)
|
||||||
self.cmd('sprite %s image %s' % (type, icon))
|
self.cmd("sprite %s image %s" % (type, icon))
|
||||||
self.cmd('node %d type %s label on,"%s" %s' % \
|
self.cmd("node %d type %s label on,\"%s\" %s" % (nodenum, type, name, pos))
|
||||||
(nodenum, type, name, pos))
|
|
||||||
else:
|
else:
|
||||||
self.cmd('node %d %s' % (nodenum, pos))
|
self.cmd("node %d %s" % (nodenum, pos))
|
||||||
|
|
||||||
def updatenodegeo(self, nodenum, lat, long, alt):
|
def updatenodegeo(self, nodenum, lat, long, alt):
|
||||||
''' Node is updated upon receiving an EMANE Location Event.
|
"""
|
||||||
TODO: received Node Message with lat/long/alt.
|
Node is updated upon receiving an EMANE Location Event.
|
||||||
'''
|
|
||||||
|
:param int nodenum: node id to update geospatial for
|
||||||
|
:param lat: latitude
|
||||||
|
:param long: longitude
|
||||||
|
:param alt: altitude
|
||||||
|
:return: nothing
|
||||||
|
"""
|
||||||
|
|
||||||
|
# TODO: received Node Message with lat/long/alt.
|
||||||
if not self.connect():
|
if not self.connect():
|
||||||
return
|
return
|
||||||
pos = "pos %.6f,%.6f,%.6f" % (long, lat, alt)
|
pos = "pos %.6f,%.6f,%.6f" % (long, lat, alt)
|
||||||
self.cmd('node %d %s' % (nodenum, pos))
|
self.cmd("node %d %s" % (nodenum, pos))
|
||||||
|
|
||||||
def updatelink(self, node1num, node2num, flags, wireless=False):
|
def updatelink(self, node1num, node2num, flags, wireless=False):
|
||||||
''' Link is updated from a Link Message or by a wireless model.
|
"""
|
||||||
'''
|
Link is updated from a Link Message or by a wireless model.
|
||||||
|
|
||||||
|
:param int node1num: node one id
|
||||||
|
:param int node2num: node two id
|
||||||
|
:param flags: link flags
|
||||||
|
:param bool wireless: flag to check if wireless or not
|
||||||
|
:return: nothing
|
||||||
|
"""
|
||||||
if node1num is None or node2num is None:
|
if node1num is None or node2num is None:
|
||||||
return
|
return
|
||||||
if not self.connect():
|
if not self.connect():
|
||||||
return
|
return
|
||||||
if flags & coreapi.CORE_API_DEL_FLAG:
|
if flags & MessageFlags.DELETE.value:
|
||||||
self.cmd('delete link,%s,%s' % (node1num, node2num))
|
self.cmd("delete link,%s,%s" % (node1num, node2num))
|
||||||
elif flags & coreapi.CORE_API_ADD_FLAG:
|
elif flags & MessageFlags.ADD.value:
|
||||||
attr = ""
|
attr = ""
|
||||||
if wireless:
|
if wireless:
|
||||||
attr = " line green,2"
|
attr = " line green,2"
|
||||||
else:
|
else:
|
||||||
attr = " line red,2"
|
attr = " line red,2"
|
||||||
self.cmd('link %s,%s%s' % (node1num, node2num, attr))
|
self.cmd("link %s,%s%s" % (node1num, node2num, attr))
|
||||||
|
|
||||||
def sendobjs(self):
|
def sendobjs(self):
|
||||||
''' Session has already started, and the SDT3D GUI later connects.
|
"""
|
||||||
|
Session has already started, and the SDT3D GUI later connects.
|
||||||
Send all node and link objects for display. Otherwise, nodes and
|
Send all node and link objects for display. Otherwise, nodes and
|
||||||
links will only be drawn when they have been updated (e.g. moved).
|
links will only be drawn when they have been updated (e.g. moved).
|
||||||
'''
|
|
||||||
|
:return: nothing
|
||||||
|
"""
|
||||||
nets = []
|
nets = []
|
||||||
with self.session._objslock:
|
with self.session._objects_lock:
|
||||||
for obj in self.session.objs():
|
for obj in self.session.objects.itervalues():
|
||||||
if isinstance(obj, PyCoreNet):
|
if isinstance(obj, PyCoreNet):
|
||||||
nets.append(obj)
|
nets.append(obj)
|
||||||
if not isinstance(obj, PyCoreObj):
|
if not isinstance(obj, PyCoreObj):
|
||||||
|
|
@ -226,88 +300,96 @@ class Sdt(object):
|
||||||
(x, y, z) = obj.getposition()
|
(x, y, z) = obj.getposition()
|
||||||
if x is None or y is None:
|
if x is None or y is None:
|
||||||
continue
|
continue
|
||||||
self.updatenode(obj.objid, coreapi.CORE_API_ADD_FLAG, x, y, z,
|
self.updatenode(obj.objid, MessageFlags.ADD.value, x, y, z,
|
||||||
obj.name, obj.type, obj.icon)
|
obj.name, obj.type, obj.icon)
|
||||||
for nodenum in sorted(self.remotes.keys()):
|
for nodenum in sorted(self.remotes.keys()):
|
||||||
r = self.remotes[nodenum]
|
r = self.remotes[nodenum]
|
||||||
(x, y, z) = r.pos
|
x, y, z = r.pos
|
||||||
self.updatenode(nodenum, coreapi.CORE_API_ADD_FLAG, x, y, z,
|
self.updatenode(nodenum, MessageFlags.ADD.value, x, y, z,
|
||||||
r.name, r.type, r.icon)
|
r.name, r.type, r.icon)
|
||||||
|
|
||||||
for net in nets:
|
for net in nets:
|
||||||
# use tolinkmsgs() to handle various types of links
|
# use tolinkmsgs() to handle various types of links
|
||||||
msgs = net.tolinkmsgs(flags = coreapi.CORE_API_ADD_FLAG)
|
messages = net.all_link_data(flags=MessageFlags.ADD.value)
|
||||||
for msg in msgs:
|
for message in messages:
|
||||||
msghdr = msg[:coreapi.CoreMessage.hdrsiz]
|
msghdr = message[:coreapi.CoreMessage.header_len]
|
||||||
flags = coreapi.CoreMessage.unpackhdr(msghdr)[1]
|
flags = coreapi.CoreMessage.unpack_header(msghdr)[1]
|
||||||
m = coreapi.CoreLinkMessage(flags, msghdr,
|
m = coreapi.CoreLinkMessage(flags, msghdr, message[coreapi.CoreMessage.header_len:])
|
||||||
msg[coreapi.CoreMessage.hdrsiz:])
|
n1num = m.get_tlv(LinkTlvs.N1_NUMBER.value)
|
||||||
n1num = m.gettlv(coreapi.CORE_TLV_LINK_N1NUMBER)
|
n2num = m.get_tlv(LinkTlvs.N2_NUMBER.value)
|
||||||
n2num = m.gettlv(coreapi.CORE_TLV_LINK_N2NUMBER)
|
link_msg_type = m.get_tlv(LinkTlvs.TYPE.value)
|
||||||
link_msg_type = m.gettlv(coreapi.CORE_TLV_LINK_TYPE)
|
if nodeutils.is_node(net, (NodeTypes.WIRELESS_LAN, NodeTypes.EMANE)):
|
||||||
if isinstance(net, nodes.WlanNode) or \
|
if n1num == net.objid:
|
||||||
isinstance(net, nodes.EmaneNode):
|
|
||||||
if (n1num == net.objid):
|
|
||||||
continue
|
continue
|
||||||
wl = (link_msg_type == coreapi.CORE_LINK_WIRELESS)
|
wl = link_msg_type == LinkTypes.WIRELESS.value
|
||||||
self.updatelink(n1num, n2num, coreapi.CORE_API_ADD_FLAG, wl)
|
self.updatelink(n1num, n2num, MessageFlags.ADD.value, wl)
|
||||||
|
|
||||||
for n1num in sorted(self.remotes.keys()):
|
for n1num in sorted(self.remotes.keys()):
|
||||||
r = self.remotes[n1num]
|
r = self.remotes[n1num]
|
||||||
for (n2num, wl) in r.links:
|
for n2num, wl in r.links:
|
||||||
self.updatelink(n1num, n2num, coreapi.CORE_API_ADD_FLAG, wl)
|
self.updatelink(n1num, n2num, MessageFlags.ADD.value, wl)
|
||||||
|
|
||||||
def handledistributed(self, msg):
|
# TODO: remove the need for this
|
||||||
''' Broker handler for processing CORE API messages as they are
|
def handledistributed(self, message):
|
||||||
|
"""
|
||||||
|
Broker handler for processing CORE API messages as they are
|
||||||
received. This is used to snoop the Node messages and update
|
received. This is used to snoop the Node messages and update
|
||||||
node positions.
|
node positions.
|
||||||
'''
|
|
||||||
if msg.msgtype == coreapi.CORE_API_LINK_MSG:
|
|
||||||
return self.handlelinkmsg(msg)
|
|
||||||
elif msg.msgtype == coreapi.CORE_API_NODE_MSG:
|
|
||||||
return self.handlenodemsg(msg)
|
|
||||||
|
|
||||||
|
:param message: message to handle
|
||||||
|
:return: replies
|
||||||
|
"""
|
||||||
|
if message.message_type == MessageTypes.LINK.value:
|
||||||
|
return self.handlelinkmsg(message)
|
||||||
|
elif message.message_type == MessageTypes.NODE.value:
|
||||||
|
return self.handlenodemsg(message)
|
||||||
|
|
||||||
|
# TODO: remove the need for this
|
||||||
def handlenodemsg(self, msg):
|
def handlenodemsg(self, msg):
|
||||||
''' Process a Node Message to add/delete or move a node on
|
"""
|
||||||
|
Process a Node Message to add/delete or move a node on
|
||||||
the SDT display. Node properties are found in session._objs or
|
the SDT display. Node properties are found in session._objs or
|
||||||
self.remotes for remote nodes (or those not yet instantiated).
|
self.remotes for remote nodes (or those not yet instantiated).
|
||||||
'''
|
|
||||||
|
:param msg: node message to handle
|
||||||
|
:return: nothing
|
||||||
|
"""
|
||||||
# for distributed sessions to work properly, the SDT option should be
|
# for distributed sessions to work properly, the SDT option should be
|
||||||
# enabled prior to starting the session
|
# enabled prior to starting the session
|
||||||
if not self.is_enabled():
|
if not self.is_enabled():
|
||||||
return False
|
return False
|
||||||
# node.(objid, type, icon, name) are used.
|
# node.(objid, type, icon, name) are used.
|
||||||
nodenum = msg.gettlv(coreapi.CORE_TLV_NODE_NUMBER)
|
nodenum = msg.get_tlv(NodeTlvs.NUMBER.value)
|
||||||
if not nodenum:
|
if not nodenum:
|
||||||
return
|
return
|
||||||
x = msg.gettlv(coreapi.CORE_TLV_NODE_XPOS)
|
x = msg.get_tlv(NodeTlvs.X_POSITION.value)
|
||||||
y = msg.gettlv(coreapi.CORE_TLV_NODE_YPOS)
|
y = msg.get_tlv(NodeTlvs.Y_POSITION.value)
|
||||||
z = None
|
z = None
|
||||||
name = msg.gettlv(coreapi.CORE_TLV_NODE_NAME)
|
name = msg.get_tlv(NodeTlvs.NAME.value)
|
||||||
|
|
||||||
nodetype = msg.gettlv(coreapi.CORE_TLV_NODE_TYPE)
|
nodetype = msg.get_tlv(NodeTlvs.TYPE.value)
|
||||||
model = msg.gettlv(coreapi.CORE_TLV_NODE_MODEL)
|
model = msg.get_tlv(NodeTlvs.MODEL.value)
|
||||||
icon = msg.gettlv(coreapi.CORE_TLV_NODE_ICON)
|
icon = msg.get_tlv(NodeTlvs.ICON.value)
|
||||||
|
|
||||||
net = False
|
net = False
|
||||||
if nodetype == coreapi.CORE_NODE_DEF or \
|
if nodetype == NodeTypes.DEFAULT.value or \
|
||||||
nodetype == coreapi.CORE_NODE_PHYS or \
|
nodetype == NodeTypes.PHYSICAL.value or \
|
||||||
nodetype == coreapi.CORE_NODE_XEN:
|
nodetype == NodeTypes.XEN.value:
|
||||||
if model is None:
|
if model is None:
|
||||||
model = "router"
|
model = "router"
|
||||||
type = model
|
type = model
|
||||||
elif nodetype != None:
|
elif nodetype is not None:
|
||||||
type = coreapi.node_class(nodetype).type
|
type = nodeutils.get_node_class(NodeTypes(nodetype)).type
|
||||||
net = True
|
net = True
|
||||||
else:
|
else:
|
||||||
type = None
|
type = None
|
||||||
|
|
||||||
try:
|
try:
|
||||||
node = self.session.obj(nodenum)
|
node = self.session.get_object(nodenum)
|
||||||
except KeyError:
|
except KeyError:
|
||||||
node = None
|
node = None
|
||||||
if node:
|
if node:
|
||||||
self.updatenode(node.objid, msg.flags, x, y, z,
|
self.updatenode(node.objid, msg.flags, x, y, z, node.name, node.type, node.icon)
|
||||||
node.name, node.type, node.icon)
|
|
||||||
else:
|
else:
|
||||||
if nodenum in self.remotes:
|
if nodenum in self.remotes:
|
||||||
remote = self.remotes[nodenum]
|
remote = self.remotes[nodenum]
|
||||||
|
|
@ -318,29 +400,33 @@ class Sdt(object):
|
||||||
if icon is None:
|
if icon is None:
|
||||||
icon = remote.icon
|
icon = remote.icon
|
||||||
else:
|
else:
|
||||||
remote = self.Bunch(objid=nodenum, type=type, icon=icon,
|
remote = Bunch(objid=nodenum, type=type, icon=icon, name=name, net=net, links=set())
|
||||||
name=name, net=net, links=set())
|
|
||||||
self.remotes[nodenum] = remote
|
self.remotes[nodenum] = remote
|
||||||
remote.pos = (x, y, z)
|
remote.pos = (x, y, z)
|
||||||
self.updatenode(nodenum, msg.flags, x, y, z, name, type, icon)
|
self.updatenode(nodenum, msg.flags, x, y, z, name, type, icon)
|
||||||
|
|
||||||
|
# TODO: remove the need for this
|
||||||
def handlelinkmsg(self, msg):
|
def handlelinkmsg(self, msg):
|
||||||
''' Process a Link Message to add/remove links on the SDT display.
|
"""
|
||||||
|
Process a Link Message to add/remove links on the SDT display.
|
||||||
Links are recorded in the remotes[nodenum1].links set for updating
|
Links are recorded in the remotes[nodenum1].links set for updating
|
||||||
the SDT display at a later time.
|
the SDT display at a later time.
|
||||||
'''
|
|
||||||
|
:param msg: link message to handle
|
||||||
|
:return: nothing
|
||||||
|
"""
|
||||||
if not self.is_enabled():
|
if not self.is_enabled():
|
||||||
return False
|
return False
|
||||||
nodenum1 = msg.gettlv(coreapi.CORE_TLV_LINK_N1NUMBER)
|
nodenum1 = msg.get_tlv(LinkTlvs.N1_NUMBER.value)
|
||||||
nodenum2 = msg.gettlv(coreapi.CORE_TLV_LINK_N2NUMBER)
|
nodenum2 = msg.get_tlv(LinkTlvs.N2_NUMBER.value)
|
||||||
link_msg_type = msg.gettlv(coreapi.CORE_TLV_LINK_TYPE)
|
link_msg_type = msg.get_tlv(LinkTlvs.TYPE.value)
|
||||||
# this filters out links to WLAN and EMANE nodes which are not drawn
|
# this filters out links to WLAN and EMANE nodes which are not drawn
|
||||||
if self.wlancheck(nodenum1):
|
if self.wlancheck(nodenum1):
|
||||||
return
|
return
|
||||||
wl = (link_msg_type == coreapi.CORE_LINK_WIRELESS)
|
wl = link_msg_type == LinkTypes.WIRELESS.value
|
||||||
if nodenum1 in self.remotes:
|
if nodenum1 in self.remotes:
|
||||||
r = self.remotes[nodenum1]
|
r = self.remotes[nodenum1]
|
||||||
if msg.flags & coreapi.CORE_API_DEL_FLAG:
|
if msg.flags & MessageFlags.DELETE.value:
|
||||||
if (nodenum2, wl) in r.links:
|
if (nodenum2, wl) in r.links:
|
||||||
r.links.remove((nodenum2, wl))
|
r.links.remove((nodenum2, wl))
|
||||||
else:
|
else:
|
||||||
|
|
@ -348,18 +434,22 @@ class Sdt(object):
|
||||||
self.updatelink(nodenum1, nodenum2, msg.flags, wireless=wl)
|
self.updatelink(nodenum1, nodenum2, msg.flags, wireless=wl)
|
||||||
|
|
||||||
def wlancheck(self, nodenum):
|
def wlancheck(self, nodenum):
|
||||||
''' Helper returns True if a node number corresponds to a WlanNode
|
"""
|
||||||
or EmaneNode.
|
Helper returns True if a node number corresponds to a WlanNode or EmaneNode.
|
||||||
'''
|
|
||||||
|
:param int nodenum: node id to check
|
||||||
|
:return: True if node is wlan or emane, False otherwise
|
||||||
|
:rtype: bool
|
||||||
|
"""
|
||||||
if nodenum in self.remotes:
|
if nodenum in self.remotes:
|
||||||
type = self.remotes[nodenum].type
|
type = self.remotes[nodenum].type
|
||||||
if type in ("wlan", "emane"):
|
if type in ("wlan", "emane"):
|
||||||
return True
|
return True
|
||||||
else:
|
else:
|
||||||
try:
|
try:
|
||||||
n = self.session.obj(nodenum)
|
n = self.session.get_object(nodenum)
|
||||||
except KeyError:
|
except KeyError:
|
||||||
return False
|
return False
|
||||||
if isinstance(n, (nodes.WlanNode, nodes.EmaneNode)):
|
if nodeutils.is_node(n, (NodeTypes.WIRELESS_LAN, NodeTypes.EMANE)):
|
||||||
return True
|
return True
|
||||||
return False
|
return False
|
||||||
|
|
|
||||||
File diff suppressed because it is too large
Load diff
|
|
@ -1,6 +1,6 @@
|
||||||
"""Services
|
"""
|
||||||
|
Services
|
||||||
|
|
||||||
Services available to nodes can be put in this directory. Everything listed in
|
Services available to nodes can be put in this directory. Everything listed in
|
||||||
__all__ is automatically loaded by the main core module.
|
__all__ is automatically loaded by the main core module.
|
||||||
"""
|
"""
|
||||||
__all__ = ["quagga", "nrl", "xorp", "bird", "utility", "security", "ucarp", "dockersvc", 'startup']
|
|
||||||
|
|
|
||||||
|
|
@ -1,24 +1,15 @@
|
||||||
#
|
"""
|
||||||
# CORE
|
|
||||||
# Copyright (c)2012 Jean-Tiare Le Bigot.
|
|
||||||
# See the LICENSE file included in this distribution.
|
|
||||||
#
|
|
||||||
# authors: Jean-Tiare Le Bigot <admin@jtlebi.fr>
|
|
||||||
# Jeff Ahrenholz <jeffrey.m.ahrenholz@boeing.com>
|
|
||||||
#
|
|
||||||
'''
|
|
||||||
bird.py: defines routing services provided by the BIRD Internet Routing Daemon.
|
bird.py: defines routing services provided by the BIRD Internet Routing Daemon.
|
||||||
'''
|
"""
|
||||||
|
|
||||||
import os
|
from core.service import CoreService
|
||||||
|
from core.service import ServiceManager
|
||||||
|
|
||||||
from core.service import CoreService, addservice
|
|
||||||
from core.misc.ipaddr import IPv4Prefix
|
|
||||||
from core.constants import *
|
|
||||||
|
|
||||||
class Bird(CoreService):
|
class Bird(CoreService):
|
||||||
''' Bird router support
|
"""
|
||||||
'''
|
Bird router support
|
||||||
|
"""
|
||||||
_name = "bird"
|
_name = "bird"
|
||||||
_group = "BIRD"
|
_group = "BIRD"
|
||||||
_depends = ()
|
_depends = ()
|
||||||
|
|
@ -31,8 +22,9 @@ class Bird(CoreService):
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def generateconfig(cls, node, filename, services):
|
def generateconfig(cls, node, filename, services):
|
||||||
''' Return the bird.conf file contents.
|
"""
|
||||||
'''
|
Return the bird.conf file contents.
|
||||||
|
"""
|
||||||
if filename == cls._configs[0]:
|
if filename == cls._configs[0]:
|
||||||
return cls.generateBirdConf(node, services)
|
return cls.generateBirdConf(node, services)
|
||||||
else:
|
else:
|
||||||
|
|
@ -40,8 +32,9 @@ class Bird(CoreService):
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def routerid(node):
|
def routerid(node):
|
||||||
''' Helper to return the first IPv4 address of a node as its router ID.
|
"""
|
||||||
'''
|
Helper to return the first IPv4 address of a node as its router ID.
|
||||||
|
"""
|
||||||
for ifc in node.netifs():
|
for ifc in node.netifs():
|
||||||
if hasattr(ifc, 'control') and ifc.control == True:
|
if hasattr(ifc, 'control') and ifc.control == True:
|
||||||
continue
|
continue
|
||||||
|
|
@ -53,10 +46,11 @@ class Bird(CoreService):
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def generateBirdConf(cls, node, services):
|
def generateBirdConf(cls, node, services):
|
||||||
''' Returns configuration file text. Other services that depend on bird
|
"""
|
||||||
|
Returns configuration file text. Other services that depend on bird
|
||||||
will have generatebirdifcconfig() and generatebirdconfig()
|
will have generatebirdifcconfig() and generatebirdconfig()
|
||||||
hooks that are invoked here.
|
hooks that are invoked here.
|
||||||
'''
|
"""
|
||||||
cfg = """\
|
cfg = """\
|
||||||
/* Main configuration file for BIRD. This is ony a template,
|
/* Main configuration file for BIRD. This is ony a template,
|
||||||
* you will *need* to customize it according to your needs
|
* you will *need* to customize it according to your needs
|
||||||
|
|
@ -90,10 +84,12 @@ protocol device {
|
||||||
|
|
||||||
return cfg
|
return cfg
|
||||||
|
|
||||||
|
|
||||||
class BirdService(CoreService):
|
class BirdService(CoreService):
|
||||||
''' Parent class for Bird services. Defines properties and methods
|
"""
|
||||||
|
Parent class for Bird services. Defines properties and methods
|
||||||
common to Bird's routing daemons.
|
common to Bird's routing daemons.
|
||||||
'''
|
"""
|
||||||
|
|
||||||
_name = "BirdDaemon"
|
_name = "BirdDaemon"
|
||||||
_group = "BIRD"
|
_group = "BIRD"
|
||||||
|
|
@ -118,14 +114,17 @@ class BirdService(CoreService):
|
||||||
cfg = ""
|
cfg = ""
|
||||||
|
|
||||||
for ifc in node.netifs():
|
for ifc in node.netifs():
|
||||||
if hasattr(ifc, 'control') and ifc.control == True: continue
|
if hasattr(ifc, 'control') and ifc.control == True:
|
||||||
|
continue
|
||||||
cfg += ' interface "%s";\n' % ifc.name
|
cfg += ' interface "%s";\n' % ifc.name
|
||||||
|
|
||||||
return cfg
|
return cfg
|
||||||
|
|
||||||
|
|
||||||
class BirdBgp(BirdService):
|
class BirdBgp(BirdService):
|
||||||
'''BGP BIRD Service (configuration generation)'''
|
"""
|
||||||
|
BGP BIRD Service (configuration generation)
|
||||||
|
"""
|
||||||
|
|
||||||
_name = "BIRD_BGP"
|
_name = "BIRD_BGP"
|
||||||
_custom_needed = True
|
_custom_needed = True
|
||||||
|
|
@ -152,8 +151,11 @@ protocol bgp {
|
||||||
|
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
|
||||||
class BirdOspf(BirdService):
|
class BirdOspf(BirdService):
|
||||||
'''OSPF BIRD Service (configuration generation)'''
|
"""
|
||||||
|
OSPF BIRD Service (configuration generation)
|
||||||
|
"""
|
||||||
|
|
||||||
_name = "BIRD_OSPFv2"
|
_name = "BIRD_OSPFv2"
|
||||||
|
|
||||||
|
|
@ -176,7 +178,9 @@ class BirdOspf(BirdService):
|
||||||
|
|
||||||
|
|
||||||
class BirdRadv(BirdService):
|
class BirdRadv(BirdService):
|
||||||
'''RADV BIRD Service (configuration generation)'''
|
"""
|
||||||
|
RADV BIRD Service (configuration generation)
|
||||||
|
"""
|
||||||
|
|
||||||
_name = "BIRD_RADV"
|
_name = "BIRD_RADV"
|
||||||
|
|
||||||
|
|
@ -202,7 +206,9 @@ class BirdRadv(BirdService):
|
||||||
|
|
||||||
|
|
||||||
class BirdRip(BirdService):
|
class BirdRip(BirdService):
|
||||||
'''RIP BIRD Service (configuration generation)'''
|
"""
|
||||||
|
RIP BIRD Service (configuration generation)
|
||||||
|
"""
|
||||||
|
|
||||||
_name = "BIRD_RIP"
|
_name = "BIRD_RIP"
|
||||||
|
|
||||||
|
|
@ -222,7 +228,9 @@ class BirdRip(BirdService):
|
||||||
|
|
||||||
|
|
||||||
class BirdStatic(BirdService):
|
class BirdStatic(BirdService):
|
||||||
'''Static Bird Service (configuration generation)'''
|
"""
|
||||||
|
Static Bird Service (configuration generation)
|
||||||
|
"""
|
||||||
|
|
||||||
_name = "BIRD_static"
|
_name = "BIRD_static"
|
||||||
_custom_needed = True
|
_custom_needed = True
|
||||||
|
|
@ -240,10 +248,11 @@ class BirdStatic(BirdService):
|
||||||
return cfg
|
return cfg
|
||||||
|
|
||||||
|
|
||||||
|
def load_services():
|
||||||
# Register all protocols
|
# Register all protocols
|
||||||
addservice(Bird)
|
ServiceManager.add(Bird)
|
||||||
addservice(BirdOspf)
|
ServiceManager.add(BirdOspf)
|
||||||
addservice(BirdBgp)
|
ServiceManager.add(BirdBgp)
|
||||||
#addservice(BirdRadv) # untested
|
# ServiceManager.add(BirdRadv) # untested
|
||||||
addservice(BirdRip)
|
ServiceManager.add(BirdRip)
|
||||||
addservice(BirdStatic)
|
ServiceManager.add(BirdStatic)
|
||||||
|
|
|
||||||
|
|
@ -1,12 +1,5 @@
|
||||||
#
|
"""
|
||||||
# CORE
|
Docker service allows running docker containers within CORE nodes.
|
||||||
# Copyright (c)2014 the Boeing Company.
|
|
||||||
# See the LICENSE file included in this distribution.
|
|
||||||
#
|
|
||||||
# authors: Stuart Marsden
|
|
||||||
# Jeff Ahrenholz <jeffrey.m.ahrenholz@boeing.com>
|
|
||||||
#
|
|
||||||
''' Docker service allows running docker containers within CORE nodes.
|
|
||||||
|
|
||||||
The running of Docker within a CORE node allows for additional extensibility to
|
The running of Docker within a CORE node allows for additional extensibility to
|
||||||
the CORE services. This allows network applications and protocols to be easily
|
the CORE services. This allows network applications and protocols to be easily
|
||||||
|
|
@ -102,23 +95,25 @@ Limitations:
|
||||||
for most due to contention. The service just does a hackish wait for 1 second
|
for most due to contention. The service just does a hackish wait for 1 second
|
||||||
and retry. This means all the docker containers can take a while to come up
|
and retry. This means all the docker containers can take a while to come up
|
||||||
depending on how many nodes you have.
|
depending on how many nodes you have.
|
||||||
|
"""
|
||||||
|
|
||||||
'''
|
from core.misc import log
|
||||||
|
from core.service import CoreService
|
||||||
|
from core.service import ServiceManager
|
||||||
|
|
||||||
|
logger = log.get_logger(__name__)
|
||||||
|
|
||||||
import os
|
|
||||||
import sys
|
|
||||||
try:
|
try:
|
||||||
from docker import Client
|
from docker import Client
|
||||||
except Exception:
|
except ImportError:
|
||||||
pass
|
logger.error("failure to import docker")
|
||||||
|
|
||||||
from core.service import CoreService, addservice
|
|
||||||
from core.misc.ipaddr import IPv4Prefix, IPv6Prefix
|
|
||||||
|
|
||||||
class DockerService(CoreService):
|
class DockerService(CoreService):
|
||||||
''' This is a service which will allow running docker containers in a CORE
|
"""
|
||||||
|
This is a service which will allow running docker containers in a CORE
|
||||||
node.
|
node.
|
||||||
'''
|
"""
|
||||||
_name = "Docker"
|
_name = "Docker"
|
||||||
_group = "Docker"
|
_group = "Docker"
|
||||||
_depends = ()
|
_depends = ()
|
||||||
|
|
@ -132,9 +127,10 @@ class DockerService(CoreService):
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def generateconfig(cls, node, filename, services):
|
def generateconfig(cls, node, filename, services):
|
||||||
''' Returns a string having contents of a docker.sh script that
|
"""
|
||||||
|
Returns a string having contents of a docker.sh script that
|
||||||
can be modified to start a specific docker image.
|
can be modified to start a specific docker image.
|
||||||
'''
|
"""
|
||||||
cfg = "#!/bin/sh\n"
|
cfg = "#!/bin/sh\n"
|
||||||
cfg += "# auto-generated by Docker (docker.py)\n"
|
cfg += "# auto-generated by Docker (docker.py)\n"
|
||||||
# Docker likes to think it has DNS set up or it complains.
|
# Docker likes to think it has DNS set up or it complains.
|
||||||
|
|
@ -159,10 +155,14 @@ done
|
||||||
""" % (cls._image,)
|
""" % (cls._image,)
|
||||||
return cfg
|
return cfg
|
||||||
|
|
||||||
addservice(DockerService)
|
|
||||||
|
def load_services():
|
||||||
|
ServiceManager.add(DockerService)
|
||||||
|
|
||||||
# This auto-loads Docker images having a :core tag, adding them to the list
|
# This auto-loads Docker images having a :core tag, adding them to the list
|
||||||
# of services under the "Docker" group.
|
# of services under the "Docker" group.
|
||||||
|
# TODO: change this logic, should be a proper configurable, or docker needs to be a required library
|
||||||
|
# TODO: also should make this call possible real time for reloading removing "magic" auto loading on import
|
||||||
if 'Client' in globals():
|
if 'Client' in globals():
|
||||||
client = Client(version='1.10')
|
client = Client(version='1.10')
|
||||||
images = client.images()
|
images = client.images()
|
||||||
|
|
@ -176,7 +176,6 @@ for image in images:
|
||||||
if u':core' not in repo:
|
if u':core' not in repo:
|
||||||
continue
|
continue
|
||||||
dockerid = repo.encode('ascii', 'ignore').split(':')[0]
|
dockerid = repo.encode('ascii', 'ignore').split(':')[0]
|
||||||
SubClass = type('SubClass', (DockerService,),
|
sub_class = type('SubClass', (DockerService,), {'_name': dockerid, '_image': dockerid})
|
||||||
{'_name': dockerid, '_image': dockerid})
|
ServiceManager.add(sub_class)
|
||||||
addservice(SubClass)
|
|
||||||
del images
|
del images
|
||||||
|
|
|
||||||
|
|
@ -1,24 +1,19 @@
|
||||||
#
|
"""
|
||||||
# CORE
|
|
||||||
# Copyright (c)2010-2012 the Boeing Company.
|
|
||||||
# See the LICENSE file included in this distribution.
|
|
||||||
#
|
|
||||||
# author: Jeff Ahrenholz <jeffrey.m.ahrenholz@boeing.com>
|
|
||||||
#
|
|
||||||
'''
|
|
||||||
nrl.py: defines services provided by NRL protolib tools hosted here:
|
nrl.py: defines services provided by NRL protolib tools hosted here:
|
||||||
http://www.nrl.navy.mil/itd/ncs/products
|
http://www.nrl.navy.mil/itd/ncs/products
|
||||||
'''
|
"""
|
||||||
|
|
||||||
|
from core.misc import utils
|
||||||
|
from core.misc.ipaddress import Ipv4Prefix
|
||||||
|
from core.service import CoreService
|
||||||
|
from core.service import ServiceManager
|
||||||
|
|
||||||
from core.service import CoreService, addservice
|
|
||||||
from core.misc.ipaddr import IPv4Prefix, IPv6Prefix
|
|
||||||
from core.misc.utils import *
|
|
||||||
from core.constants import *
|
|
||||||
|
|
||||||
class NrlService(CoreService):
|
class NrlService(CoreService):
|
||||||
''' Parent class for NRL services. Defines properties and methods
|
"""
|
||||||
|
Parent class for NRL services. Defines properties and methods
|
||||||
common to NRL's routing daemons.
|
common to NRL's routing daemons.
|
||||||
'''
|
"""""
|
||||||
_name = "Protean"
|
_name = "Protean"
|
||||||
_group = "ProtoSvc"
|
_group = "ProtoSvc"
|
||||||
_depends = ()
|
_depends = ()
|
||||||
|
|
@ -34,21 +29,23 @@ class NrlService(CoreService):
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def firstipv4prefix(node, prefixlen=24):
|
def firstipv4prefix(node, prefixlen=24):
|
||||||
''' Similar to QuaggaService.routerid(). Helper to return the first IPv4
|
"""
|
||||||
|
Similar to QuaggaService.routerid(). Helper to return the first IPv4
|
||||||
prefix of a node, using the supplied prefix length. This ignores the
|
prefix of a node, using the supplied prefix length. This ignores the
|
||||||
interface's prefix length, so e.g. '/32' can turn into '/24'.
|
interface's prefix length, so e.g. '/32' can turn into '/24'.
|
||||||
'''
|
"""
|
||||||
for ifc in node.netifs():
|
for ifc in node.netifs():
|
||||||
if hasattr(ifc, 'control') and ifc.control == True:
|
if hasattr(ifc, 'control') and ifc.control == True:
|
||||||
continue
|
continue
|
||||||
for a in ifc.addrlist:
|
for a in ifc.addrlist:
|
||||||
if a.find(".") >= 0:
|
if a.find(".") >= 0:
|
||||||
addr = a.split('/')[0]
|
addr = a.split('/')[0]
|
||||||
pre = IPv4Prefix("%s/%s" % (addr, prefixlen))
|
pre = Ipv4Prefix("%s/%s" % (addr, prefixlen))
|
||||||
return str(pre)
|
return str(pre)
|
||||||
# raise ValueError, "no IPv4 address found"
|
# raise ValueError, "no IPv4 address found"
|
||||||
return "0.0.0.0/%s" % prefixlen
|
return "0.0.0.0/%s" % prefixlen
|
||||||
|
|
||||||
|
|
||||||
class MgenSinkService(NrlService):
|
class MgenSinkService(NrlService):
|
||||||
_name = "MGEN_Sink"
|
_name = "MGEN_Sink"
|
||||||
_configs = ("sink.mgen",)
|
_configs = ("sink.mgen",)
|
||||||
|
|
@ -61,7 +58,7 @@ class MgenSinkService(NrlService):
|
||||||
def generateconfig(cls, node, filename, services):
|
def generateconfig(cls, node, filename, services):
|
||||||
cfg = "0.0 LISTEN UDP 5000\n"
|
cfg = "0.0 LISTEN UDP 5000\n"
|
||||||
for ifc in node.netifs():
|
for ifc in node.netifs():
|
||||||
name = sysctldevname(ifc.name)
|
name = utils.sysctldevname(ifc.name)
|
||||||
cfg += "0.0 Join 224.225.1.2 INTERFACE %s\n" % name
|
cfg += "0.0 Join 224.225.1.2 INTERFACE %s\n" % name
|
||||||
return cfg
|
return cfg
|
||||||
|
|
||||||
|
|
@ -69,13 +66,13 @@ class MgenSinkService(NrlService):
|
||||||
def getstartup(cls, node, services):
|
def getstartup(cls, node, services):
|
||||||
cmd = cls._startup[0]
|
cmd = cls._startup[0]
|
||||||
cmd += " output /tmp/mgen_%s.log" % node.name
|
cmd += " output /tmp/mgen_%s.log" % node.name
|
||||||
return (cmd, )
|
return cmd,
|
||||||
|
|
||||||
addservice(MgenSinkService)
|
|
||||||
|
|
||||||
class NrlNhdp(NrlService):
|
class NrlNhdp(NrlService):
|
||||||
''' NeighborHood Discovery Protocol for MANET networks.
|
"""
|
||||||
'''
|
NeighborHood Discovery Protocol for MANET networks.
|
||||||
|
"""
|
||||||
_name = "NHDP"
|
_name = "NHDP"
|
||||||
_startup = ("nrlnhdp",)
|
_startup = ("nrlnhdp",)
|
||||||
_shutdown = ("killall nrlnhdp",)
|
_shutdown = ("killall nrlnhdp",)
|
||||||
|
|
@ -83,8 +80,9 @@ class NrlNhdp(NrlService):
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def getstartup(cls, node, services):
|
def getstartup(cls, node, services):
|
||||||
''' Generate the appropriate command-line based on node interfaces.
|
"""
|
||||||
'''
|
Generate the appropriate command-line based on node interfaces.
|
||||||
|
"""
|
||||||
cmd = cls._startup[0]
|
cmd = cls._startup[0]
|
||||||
cmd += " -l /var/log/nrlnhdp.log"
|
cmd += " -l /var/log/nrlnhdp.log"
|
||||||
cmd += " -rpipe %s_nhdp" % node.name
|
cmd += " -rpipe %s_nhdp" % node.name
|
||||||
|
|
@ -101,13 +99,13 @@ class NrlNhdp(NrlService):
|
||||||
cmd += " -i "
|
cmd += " -i "
|
||||||
cmd += " -i ".join(interfacenames)
|
cmd += " -i ".join(interfacenames)
|
||||||
|
|
||||||
return (cmd, )
|
return cmd,
|
||||||
|
|
||||||
addservice(NrlNhdp)
|
|
||||||
|
|
||||||
class NrlSmf(NrlService):
|
class NrlSmf(NrlService):
|
||||||
''' Simplified Multicast Forwarding for MANET networks.
|
"""
|
||||||
'''
|
Simplified Multicast Forwarding for MANET networks.
|
||||||
|
"""
|
||||||
_name = "SMF"
|
_name = "SMF"
|
||||||
_startup = ("sh startsmf.sh",)
|
_startup = ("sh startsmf.sh",)
|
||||||
_shutdown = ("killall nrlsmf",)
|
_shutdown = ("killall nrlsmf",)
|
||||||
|
|
@ -116,18 +114,18 @@ class NrlSmf(NrlService):
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def generateconfig(cls, node, filename, services):
|
def generateconfig(cls, node, filename, services):
|
||||||
''' Generate a startup script for SMF. Because nrlsmf does not
|
"""
|
||||||
|
Generate a startup script for SMF. Because nrlsmf does not
|
||||||
daemonize, it can cause problems in some situations when launched
|
daemonize, it can cause problems in some situations when launched
|
||||||
directly using vcmd.
|
directly using vcmd.
|
||||||
'''
|
"""
|
||||||
cfg = "#!/bin/sh\n"
|
cfg = "#!/bin/sh\n"
|
||||||
cfg += "# auto-generated by nrl.py:NrlSmf.generateconfig()\n"
|
cfg += "# auto-generated by nrl.py:NrlSmf.generateconfig()\n"
|
||||||
comments = ""
|
comments = ""
|
||||||
cmd = "nrlsmf instance %s_smf" % (node.name)
|
cmd = "nrlsmf instance %s_smf" % node.name
|
||||||
|
|
||||||
servicenames = map(lambda x: x._name, services)
|
servicenames = map(lambda x: x._name, services)
|
||||||
netifs = filter(lambda x: not getattr(x, 'control', False), \
|
netifs = filter(lambda x: not getattr(x, 'control', False), node.netifs())
|
||||||
node.netifs())
|
|
||||||
if len(netifs) == 0:
|
if len(netifs) == 0:
|
||||||
return ()
|
return ()
|
||||||
|
|
||||||
|
|
@ -154,11 +152,11 @@ class NrlSmf(NrlService):
|
||||||
cfg += comments + cmd + " < /dev/null > /dev/null 2>&1 &\n\n"
|
cfg += comments + cmd + " < /dev/null > /dev/null 2>&1 &\n\n"
|
||||||
return cfg
|
return cfg
|
||||||
|
|
||||||
addservice(NrlSmf)
|
|
||||||
|
|
||||||
class NrlOlsr(NrlService):
|
class NrlOlsr(NrlService):
|
||||||
''' Optimized Link State Routing protocol for MANET networks.
|
"""
|
||||||
'''
|
Optimized Link State Routing protocol for MANET networks.
|
||||||
|
"""
|
||||||
_name = "OLSR"
|
_name = "OLSR"
|
||||||
_startup = ("nrlolsrd",)
|
_startup = ("nrlolsrd",)
|
||||||
_shutdown = ("killall nrlolsrd",)
|
_shutdown = ("killall nrlolsrd",)
|
||||||
|
|
@ -166,8 +164,9 @@ class NrlOlsr(NrlService):
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def getstartup(cls, node, services):
|
def getstartup(cls, node, services):
|
||||||
''' Generate the appropriate command-line based on node interfaces.
|
"""
|
||||||
'''
|
Generate the appropriate command-line based on node interfaces.
|
||||||
|
"""
|
||||||
cmd = cls._startup[0]
|
cmd = cls._startup[0]
|
||||||
# are multiple interfaces supported? No.
|
# are multiple interfaces supported? No.
|
||||||
netifs = list(node.netifs())
|
netifs = list(node.netifs())
|
||||||
|
|
@ -184,13 +183,13 @@ class NrlOlsr(NrlService):
|
||||||
if "zebra" in servicenames:
|
if "zebra" in servicenames:
|
||||||
cmd += " -z"
|
cmd += " -z"
|
||||||
|
|
||||||
return (cmd, )
|
return cmd,
|
||||||
|
|
||||||
addservice(NrlOlsr)
|
|
||||||
|
|
||||||
class NrlOlsrv2(NrlService):
|
class NrlOlsrv2(NrlService):
|
||||||
''' Optimized Link State Routing protocol version 2 for MANET networks.
|
"""
|
||||||
'''
|
Optimized Link State Routing protocol version 2 for MANET networks.
|
||||||
|
"""
|
||||||
_name = "OLSRv2"
|
_name = "OLSRv2"
|
||||||
_startup = ("nrlolsrv2",)
|
_startup = ("nrlolsrv2",)
|
||||||
_shutdown = ("killall nrlolsrv2",)
|
_shutdown = ("killall nrlolsrv2",)
|
||||||
|
|
@ -198,8 +197,9 @@ class NrlOlsrv2(NrlService):
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def getstartup(cls, node, services):
|
def getstartup(cls, node, services):
|
||||||
''' Generate the appropriate command-line based on node interfaces.
|
"""
|
||||||
'''
|
Generate the appropriate command-line based on node interfaces.
|
||||||
|
"""
|
||||||
cmd = cls._startup[0]
|
cmd = cls._startup[0]
|
||||||
cmd += " -l /var/log/nrlolsrv2.log"
|
cmd += " -l /var/log/nrlolsrv2.log"
|
||||||
cmd += " -rpipe %s_olsrv2" % node.name
|
cmd += " -rpipe %s_olsrv2" % node.name
|
||||||
|
|
@ -211,20 +211,19 @@ class NrlOlsrv2(NrlService):
|
||||||
|
|
||||||
cmd += " -p olsr"
|
cmd += " -p olsr"
|
||||||
|
|
||||||
netifs = filter(lambda x: not getattr(x, 'control', False), \
|
netifs = filter(lambda x: not getattr(x, 'control', False), node.netifs())
|
||||||
node.netifs())
|
|
||||||
if len(netifs) > 0:
|
if len(netifs) > 0:
|
||||||
interfacenames = map(lambda x: x.name, netifs)
|
interfacenames = map(lambda x: x.name, netifs)
|
||||||
cmd += " -i "
|
cmd += " -i "
|
||||||
cmd += " -i ".join(interfacenames)
|
cmd += " -i ".join(interfacenames)
|
||||||
|
|
||||||
return (cmd, )
|
return cmd,
|
||||||
|
|
||||||
addservice(NrlOlsrv2)
|
|
||||||
|
|
||||||
class OlsrOrg(NrlService):
|
class OlsrOrg(NrlService):
|
||||||
''' Optimized Link State Routing protocol from olsr.org for MANET networks.
|
"""
|
||||||
'''
|
Optimized Link State Routing protocol from olsr.org for MANET networks.
|
||||||
|
"""
|
||||||
_name = "OLSRORG"
|
_name = "OLSRORG"
|
||||||
_configs = ("/etc/olsrd/olsrd.conf",)
|
_configs = ("/etc/olsrd/olsrd.conf",)
|
||||||
_dirs = ("/etc/olsrd",)
|
_dirs = ("/etc/olsrd",)
|
||||||
|
|
@ -234,21 +233,23 @@ class OlsrOrg(NrlService):
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def getstartup(cls, node, services):
|
def getstartup(cls, node, services):
|
||||||
''' Generate the appropriate command-line based on node interfaces.
|
"""
|
||||||
'''
|
Generate the appropriate command-line based on node interfaces.
|
||||||
|
"""
|
||||||
cmd = cls._startup[0]
|
cmd = cls._startup[0]
|
||||||
netifs = filter(lambda x: not getattr(x, 'control', False), \
|
netifs = filter(lambda x: not getattr(x, 'control', False), node.netifs())
|
||||||
node.netifs())
|
|
||||||
if len(netifs) > 0:
|
if len(netifs) > 0:
|
||||||
interfacenames = map(lambda x: x.name, netifs)
|
interfacenames = map(lambda x: x.name, netifs)
|
||||||
cmd += " -i "
|
cmd += " -i "
|
||||||
cmd += " -i ".join(interfacenames)
|
cmd += " -i ".join(interfacenames)
|
||||||
|
|
||||||
return (cmd, )
|
return cmd,
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def generateconfig(cls, node, filename, services):
|
def generateconfig(cls, node, filename, services):
|
||||||
''' Generate a default olsrd config file to use the broadcast address of 255.255.255.255.
|
"""
|
||||||
'''
|
Generate a default olsrd config file to use the broadcast address of 255.255.255.255.
|
||||||
|
"""
|
||||||
cfg = """\
|
cfg = """\
|
||||||
#
|
#
|
||||||
# OLSR.org routing daemon config file
|
# OLSR.org routing daemon config file
|
||||||
|
|
@ -566,11 +567,11 @@ InterfaceDefaults {
|
||||||
"""
|
"""
|
||||||
return cfg
|
return cfg
|
||||||
|
|
||||||
addservice(OlsrOrg)
|
|
||||||
|
|
||||||
class MgenActor(NrlService):
|
class MgenActor(NrlService):
|
||||||
''' ZpcMgenActor.
|
"""
|
||||||
'''
|
ZpcMgenActor.
|
||||||
|
"""
|
||||||
|
|
||||||
# a unique name is required, without spaces
|
# a unique name is required, without spaces
|
||||||
_name = "MgenActor"
|
_name = "MgenActor"
|
||||||
|
|
@ -594,30 +595,29 @@ class MgenActor(NrlService):
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def generateconfig(cls, node, filename, services):
|
def generateconfig(cls, node, filename, services):
|
||||||
''' Generate a startup script for MgenActor. Because mgenActor does not
|
"""
|
||||||
|
Generate a startup script for MgenActor. Because mgenActor does not
|
||||||
daemonize, it can cause problems in some situations when launched
|
daemonize, it can cause problems in some situations when launched
|
||||||
directly using vcmd.
|
directly using vcmd.
|
||||||
'''
|
"""
|
||||||
cfg = "#!/bin/sh\n"
|
cfg = "#!/bin/sh\n"
|
||||||
cfg += "# auto-generated by nrl.py:MgenActor.generateconfig()\n"
|
cfg += "# auto-generated by nrl.py:MgenActor.generateconfig()\n"
|
||||||
comments = ""
|
comments = ""
|
||||||
cmd = "mgenBasicActor.py -n %s -a 0.0.0.0" % (node.name)
|
cmd = "mgenBasicActor.py -n %s -a 0.0.0.0" % node.name
|
||||||
|
|
||||||
servicenames = map(lambda x: x._name, services)
|
servicenames = map(lambda x: x._name, services)
|
||||||
netifs = filter(lambda x: not getattr(x, 'control', False), \
|
netifs = filter(lambda x: not getattr(x, 'control', False), node.netifs())
|
||||||
node.netifs())
|
|
||||||
if len(netifs) == 0:
|
if len(netifs) == 0:
|
||||||
return ()
|
return ()
|
||||||
|
|
||||||
cfg += comments + cmd + " < /dev/null > /dev/null 2>&1 &\n\n"
|
cfg += comments + cmd + " < /dev/null > /dev/null 2>&1 &\n\n"
|
||||||
return cfg
|
return cfg
|
||||||
|
|
||||||
# this line is required to add the above class to the list of available services
|
|
||||||
addservice(MgenActor)
|
|
||||||
|
|
||||||
class Arouted(NrlService):
|
class Arouted(NrlService):
|
||||||
''' Adaptive Routing
|
"""
|
||||||
'''
|
Adaptive Routing
|
||||||
|
"""
|
||||||
_name = "arouted"
|
_name = "arouted"
|
||||||
_configs = ("startarouted.sh",)
|
_configs = ("startarouted.sh",)
|
||||||
_startindex = NrlService._startindex + 10
|
_startindex = NrlService._startindex + 10
|
||||||
|
|
@ -627,8 +627,9 @@ class Arouted(NrlService):
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def generateconfig(cls, node, filename, services):
|
def generateconfig(cls, node, filename, services):
|
||||||
''' Return the Quagga.conf or quaggaboot.sh file contents.
|
"""
|
||||||
'''
|
Return the Quagga.conf or quaggaboot.sh file contents.
|
||||||
|
"""
|
||||||
cfg = """
|
cfg = """
|
||||||
#!/bin/sh
|
#!/bin/sh
|
||||||
for f in "/tmp/%s_smf"; do
|
for f in "/tmp/%s_smf"; do
|
||||||
|
|
@ -643,12 +644,23 @@ for f in "/tmp/%s_smf"; do
|
||||||
done
|
done
|
||||||
done
|
done
|
||||||
|
|
||||||
""" % (node.name)
|
""" % node.name
|
||||||
cfg += "ip route add %s dev lo\n" % cls.firstipv4prefix(node, 24)
|
cfg += "ip route add %s dev lo\n" % cls.firstipv4prefix(node, 24)
|
||||||
cfg += "arouted instance %s_smf tap %s_tap" % (node.name, node.name)
|
cfg += "arouted instance %s_smf tap %s_tap" % (node.name, node.name)
|
||||||
cfg += " stability 10" # seconds to consider a new route valid
|
# seconds to consider a new route valid
|
||||||
|
cfg += " stability 10"
|
||||||
cfg += " 2>&1 > /var/log/arouted.log &\n\n"
|
cfg += " 2>&1 > /var/log/arouted.log &\n\n"
|
||||||
return cfg
|
return cfg
|
||||||
|
|
||||||
|
|
||||||
|
def load_services():
|
||||||
|
ServiceManager.add(MgenSinkService)
|
||||||
|
ServiceManager.add(NrlNhdp)
|
||||||
|
ServiceManager.add(NrlSmf)
|
||||||
|
ServiceManager.add(NrlOlsr)
|
||||||
|
ServiceManager.add(NrlOlsrv2)
|
||||||
|
ServiceManager.add(OlsrOrg)
|
||||||
|
# this line is required to add the above class to the list of available services
|
||||||
|
ServiceManager.add(MgenActor)
|
||||||
# experimental
|
# experimental
|
||||||
#addservice(Arouted)
|
# ServiceManager.add(Arouted)
|
||||||
|
|
|
||||||
|
|
@ -1,28 +1,18 @@
|
||||||
#
|
"""
|
||||||
# CORE
|
|
||||||
# Copyright (c)2010-2012 the Boeing Company.
|
|
||||||
# See the LICENSE file included in this distribution.
|
|
||||||
#
|
|
||||||
# author: Jeff Ahrenholz <jeffrey.m.ahrenholz@boeing.com>
|
|
||||||
#
|
|
||||||
'''
|
|
||||||
quagga.py: defines routing services provided by Quagga.
|
quagga.py: defines routing services provided by Quagga.
|
||||||
'''
|
"""
|
||||||
|
|
||||||
import os
|
import os
|
||||||
|
|
||||||
if os.uname()[0] == "Linux":
|
from core import constants
|
||||||
from core.netns import nodes
|
from core.enumerations import LinkTypes, NodeTypes
|
||||||
elif os.uname()[0] == "FreeBSD":
|
from core.misc import ipaddress
|
||||||
from core.bsd import nodes
|
from core.misc import nodeutils
|
||||||
from core.service import CoreService, addservice
|
from core.service import CoreService
|
||||||
from core.misc.ipaddr import IPv4Prefix, isIPv4Address, isIPv6Address
|
from core.service import ServiceManager
|
||||||
from core.api import coreapi
|
|
||||||
from core.constants import *
|
|
||||||
|
|
||||||
class Zebra(CoreService):
|
class Zebra(CoreService):
|
||||||
'''
|
|
||||||
'''
|
|
||||||
_name = "zebra"
|
_name = "zebra"
|
||||||
_group = "Quagga"
|
_group = "Quagga"
|
||||||
_dirs = ("/usr/local/etc/quagga", "/var/run/quagga")
|
_dirs = ("/usr/local/etc/quagga", "/var/run/quagga")
|
||||||
|
|
@ -35,8 +25,9 @@ class Zebra(CoreService):
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def generateconfig(cls, node, filename, services):
|
def generateconfig(cls, node, filename, services):
|
||||||
''' Return the Quagga.conf or quaggaboot.sh file contents.
|
"""
|
||||||
'''
|
Return the Quagga.conf or quaggaboot.sh file contents.
|
||||||
|
"""
|
||||||
if filename == cls._configs[0]:
|
if filename == cls._configs[0]:
|
||||||
return cls.generateQuaggaConf(node, services)
|
return cls.generateQuaggaConf(node, services)
|
||||||
elif filename == cls._configs[1]:
|
elif filename == cls._configs[1]:
|
||||||
|
|
@ -48,16 +39,18 @@ class Zebra(CoreService):
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def generateVtyshConf(cls, node, services):
|
def generateVtyshConf(cls, node, services):
|
||||||
''' Returns configuration file text.
|
"""
|
||||||
'''
|
Returns configuration file text.
|
||||||
|
"""
|
||||||
return "service integrated-vtysh-config\n"
|
return "service integrated-vtysh-config\n"
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def generateQuaggaConf(cls, node, services):
|
def generateQuaggaConf(cls, node, services):
|
||||||
''' Returns configuration file text. Other services that depend on zebra
|
"""
|
||||||
|
Returns configuration file text. Other services that depend on zebra
|
||||||
will have generatequaggaifcconfig() and generatequaggaconfig()
|
will have generatequaggaifcconfig() and generatequaggaconfig()
|
||||||
hooks that are invoked here.
|
hooks that are invoked here.
|
||||||
'''
|
"""
|
||||||
# we could verify here that filename == Quagga.conf
|
# we could verify here that filename == Quagga.conf
|
||||||
cfg = ""
|
cfg = ""
|
||||||
for ifc in node.netifs():
|
for ifc in node.netifs():
|
||||||
|
|
@ -85,15 +78,13 @@ class Zebra(CoreService):
|
||||||
cfgv4 += ifccfg
|
cfgv4 += ifccfg
|
||||||
|
|
||||||
if want_ipv4:
|
if want_ipv4:
|
||||||
ipv4list = filter(lambda x: isIPv4Address(x.split('/')[0]),
|
ipv4list = filter(lambda x: ipaddress.is_ipv4_address(x.split('/')[0]), ifc.addrlist)
|
||||||
ifc.addrlist)
|
|
||||||
cfg += " "
|
cfg += " "
|
||||||
cfg += "\n ".join(map(cls.addrstr, ipv4list))
|
cfg += "\n ".join(map(cls.addrstr, ipv4list))
|
||||||
cfg += "\n"
|
cfg += "\n"
|
||||||
cfg += cfgv4
|
cfg += cfgv4
|
||||||
if want_ipv6:
|
if want_ipv6:
|
||||||
ipv6list = filter(lambda x: isIPv6Address(x.split('/')[0]),
|
ipv6list = filter(lambda x: ipaddress.is_ipv6_address(x.split('/')[0]), ifc.addrlist)
|
||||||
ifc.addrlist)
|
|
||||||
cfg += " "
|
cfg += " "
|
||||||
cfg += "\n ".join(map(cls.addrstr, ipv6list))
|
cfg += "\n ".join(map(cls.addrstr, ipv6list))
|
||||||
cfg += "\n"
|
cfg += "\n"
|
||||||
|
|
@ -108,22 +99,24 @@ class Zebra(CoreService):
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def addrstr(x):
|
def addrstr(x):
|
||||||
''' helper for mapping IP addresses to zebra config statements
|
"""
|
||||||
'''
|
helper for mapping IP addresses to zebra config statements
|
||||||
|
"""
|
||||||
if x.find(".") >= 0:
|
if x.find(".") >= 0:
|
||||||
return "ip address %s" % x
|
return "ip address %s" % x
|
||||||
elif x.find(":") >= 0:
|
elif x.find(":") >= 0:
|
||||||
return "ipv6 address %s" % x
|
return "ipv6 address %s" % x
|
||||||
else:
|
else:
|
||||||
raise Value, "invalid address: %s", x
|
raise ValueError("invalid address: %s", x)
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def generateQuaggaBoot(cls, node, services):
|
def generateQuaggaBoot(cls, node, services):
|
||||||
''' Generate a shell script used to boot the Quagga daemons.
|
"""
|
||||||
'''
|
Generate a shell script used to boot the Quagga daemons.
|
||||||
|
"""
|
||||||
try:
|
try:
|
||||||
quagga_bin_search = node.session.cfg['quagga_bin_search']
|
quagga_bin_search = node.session.config['quagga_bin_search']
|
||||||
quagga_sbin_search = node.session.cfg['quagga_sbin_search']
|
quagga_sbin_search = node.session.config['quagga_sbin_search']
|
||||||
except KeyError:
|
except KeyError:
|
||||||
quagga_bin_search = '"/usr/local/bin /usr/bin /usr/lib/quagga"'
|
quagga_bin_search = '"/usr/local/bin /usr/bin /usr/lib/quagga"'
|
||||||
quagga_sbin_search = '"/usr/local/sbin /usr/sbin /usr/lib/quagga"'
|
quagga_sbin_search = '"/usr/local/sbin /usr/sbin /usr/lib/quagga"'
|
||||||
|
|
@ -220,15 +213,14 @@ if [ "$1" != "zebra" ]; then
|
||||||
fi
|
fi
|
||||||
confcheck
|
confcheck
|
||||||
bootquagga
|
bootquagga
|
||||||
""" % (cls._configs[0], quagga_sbin_search, quagga_bin_search, \
|
""" % (cls._configs[0], quagga_sbin_search, quagga_bin_search, constants.QUAGGA_STATE_DIR)
|
||||||
QUAGGA_STATE_DIR)
|
|
||||||
|
|
||||||
addservice(Zebra)
|
|
||||||
|
|
||||||
class QuaggaService(CoreService):
|
class QuaggaService(CoreService):
|
||||||
''' Parent class for Quagga services. Defines properties and methods
|
"""
|
||||||
|
Parent class for Quagga services. Defines properties and methods
|
||||||
common to Quagga's routing daemons.
|
common to Quagga's routing daemons.
|
||||||
'''
|
"""
|
||||||
_name = "QuaggaDaemon"
|
_name = "QuaggaDaemon"
|
||||||
_group = "Quagga"
|
_group = "Quagga"
|
||||||
_depends = ("zebra",)
|
_depends = ("zebra",)
|
||||||
|
|
@ -244,8 +236,9 @@ class QuaggaService(CoreService):
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def routerid(node):
|
def routerid(node):
|
||||||
''' Helper to return the first IPv4 address of a node as its router ID.
|
"""
|
||||||
'''
|
Helper to return the first IPv4 address of a node as its router ID.
|
||||||
|
"""
|
||||||
for ifc in node.netifs():
|
for ifc in node.netifs():
|
||||||
if hasattr(ifc, 'control') and ifc.control == True:
|
if hasattr(ifc, 'control') and ifc.control == True:
|
||||||
continue
|
continue
|
||||||
|
|
@ -257,14 +250,15 @@ class QuaggaService(CoreService):
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def rj45check(ifc):
|
def rj45check(ifc):
|
||||||
''' Helper to detect whether interface is connected an external RJ45
|
"""
|
||||||
|
Helper to detect whether interface is connected an external RJ45
|
||||||
link.
|
link.
|
||||||
'''
|
"""
|
||||||
if ifc.net:
|
if ifc.net:
|
||||||
for peerifc in ifc.net.netifs():
|
for peerifc in ifc.net.netifs():
|
||||||
if peerifc == ifc:
|
if peerifc == ifc:
|
||||||
continue
|
continue
|
||||||
if isinstance(peerifc, nodes.RJ45Node):
|
if nodeutils.is_node(peerifc, NodeTypes.RJ45):
|
||||||
return True
|
return True
|
||||||
return False
|
return False
|
||||||
|
|
||||||
|
|
@ -281,12 +275,12 @@ class QuaggaService(CoreService):
|
||||||
return ""
|
return ""
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
class Ospfv2(QuaggaService):
|
class Ospfv2(QuaggaService):
|
||||||
''' The OSPFv2 service provides IPv4 routing for wired networks. It does
|
"""
|
||||||
|
The OSPFv2 service provides IPv4 routing for wired networks. It does
|
||||||
not build its own configuration file but has hooks for adding to the
|
not build its own configuration file but has hooks for adding to the
|
||||||
unified Quagga.conf file.
|
unified Quagga.conf file.
|
||||||
'''
|
"""
|
||||||
_name = "OSPFv2"
|
_name = "OSPFv2"
|
||||||
_startup = ()
|
_startup = ()
|
||||||
_shutdown = ("killall ospfd", )
|
_shutdown = ("killall ospfd", )
|
||||||
|
|
@ -295,10 +289,11 @@ class Ospfv2(QuaggaService):
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def mtucheck(ifc):
|
def mtucheck(ifc):
|
||||||
''' Helper to detect MTU mismatch and add the appropriate OSPF
|
"""
|
||||||
|
Helper to detect MTU mismatch and add the appropriate OSPF
|
||||||
mtu-ignore command. This is needed when e.g. a node is linked via a
|
mtu-ignore command. This is needed when e.g. a node is linked via a
|
||||||
GreTap device.
|
GreTap device.
|
||||||
'''
|
"""
|
||||||
if ifc.mtu != 1500:
|
if ifc.mtu != 1500:
|
||||||
# a workaround for PhysicalNode GreTap, which has no knowledge of
|
# a workaround for PhysicalNode GreTap, which has no knowledge of
|
||||||
# the other nodes/nets
|
# the other nodes/nets
|
||||||
|
|
@ -312,10 +307,11 @@ class Ospfv2(QuaggaService):
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def ptpcheck(ifc):
|
def ptpcheck(ifc):
|
||||||
''' Helper to detect whether interface is connected to a notional
|
"""
|
||||||
|
Helper to detect whether interface is connected to a notional
|
||||||
point-to-point link.
|
point-to-point link.
|
||||||
'''
|
"""
|
||||||
if isinstance(ifc.net, nodes.PtpNet):
|
if nodeutils.is_node(ifc.net, NodeTypes.PEER_TO_PEER):
|
||||||
return " ip ospf network point-to-point\n"
|
return " ip ospf network point-to-point\n"
|
||||||
return ""
|
return ""
|
||||||
|
|
||||||
|
|
@ -326,12 +322,12 @@ class Ospfv2(QuaggaService):
|
||||||
cfg += " router-id %s\n" % rtrid
|
cfg += " router-id %s\n" % rtrid
|
||||||
# network 10.0.0.0/24 area 0
|
# network 10.0.0.0/24 area 0
|
||||||
for ifc in node.netifs():
|
for ifc in node.netifs():
|
||||||
if hasattr(ifc, 'control') and ifc.control == True:
|
if hasattr(ifc, 'control') and ifc.control is True:
|
||||||
continue
|
continue
|
||||||
for a in ifc.addrlist:
|
for a in ifc.addrlist:
|
||||||
if a.find(".") < 0:
|
if a.find(".") < 0:
|
||||||
continue
|
continue
|
||||||
net = IPv4Prefix(a)
|
net = ipaddress.Ipv4Prefix(a)
|
||||||
cfg += " network %s area 0\n" % net
|
cfg += " network %s area 0\n" % net
|
||||||
cfg += "!\n"
|
cfg += "!\n"
|
||||||
return cfg
|
return cfg
|
||||||
|
|
@ -346,18 +342,20 @@ class Ospfv2(QuaggaService):
|
||||||
# cfg += cls.ptpcheck(ifc)
|
# cfg += cls.ptpcheck(ifc)
|
||||||
|
|
||||||
# return cfg + """\
|
# return cfg + """\
|
||||||
|
|
||||||
|
|
||||||
# ip ospf hello-interval 2
|
# ip ospf hello-interval 2
|
||||||
# ip ospf dead-interval 6
|
# ip ospf dead-interval 6
|
||||||
# ip ospf retransmit-interval 5
|
# ip ospf retransmit-interval 5
|
||||||
# """
|
# """
|
||||||
|
|
||||||
addservice(Ospfv2)
|
|
||||||
|
|
||||||
class Ospfv3(QuaggaService):
|
class Ospfv3(QuaggaService):
|
||||||
''' The OSPFv3 service provides IPv6 routing for wired networks. It does
|
"""
|
||||||
|
The OSPFv3 service provides IPv6 routing for wired networks. It does
|
||||||
not build its own configuration file but has hooks for adding to the
|
not build its own configuration file but has hooks for adding to the
|
||||||
unified Quagga.conf file.
|
unified Quagga.conf file.
|
||||||
'''
|
"""
|
||||||
_name = "OSPFv3"
|
_name = "OSPFv3"
|
||||||
_startup = ()
|
_startup = ()
|
||||||
_shutdown = ("killall ospf6d", )
|
_shutdown = ("killall ospf6d", )
|
||||||
|
|
@ -367,9 +365,10 @@ class Ospfv3(QuaggaService):
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def minmtu(ifc):
|
def minmtu(ifc):
|
||||||
''' Helper to discover the minimum MTU of interfaces linked with the
|
"""
|
||||||
|
Helper to discover the minimum MTU of interfaces linked with the
|
||||||
given interface.
|
given interface.
|
||||||
'''
|
"""
|
||||||
mtu = ifc.mtu
|
mtu = ifc.mtu
|
||||||
if not ifc.net:
|
if not ifc.net:
|
||||||
return mtu
|
return mtu
|
||||||
|
|
@ -380,10 +379,11 @@ class Ospfv3(QuaggaService):
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def mtucheck(cls, ifc):
|
def mtucheck(cls, ifc):
|
||||||
''' Helper to detect MTU mismatch and add the appropriate OSPFv3
|
"""
|
||||||
|
Helper to detect MTU mismatch and add the appropriate OSPFv3
|
||||||
ifmtu command. This is needed when e.g. a node is linked via a
|
ifmtu command. This is needed when e.g. a node is linked via a
|
||||||
GreTap device.
|
GreTap device.
|
||||||
'''
|
"""
|
||||||
minmtu = cls.minmtu(ifc)
|
minmtu = cls.minmtu(ifc)
|
||||||
if minmtu < ifc.mtu:
|
if minmtu < ifc.mtu:
|
||||||
return " ipv6 ospf6 ifmtu %d\n" % minmtu
|
return " ipv6 ospf6 ifmtu %d\n" % minmtu
|
||||||
|
|
@ -392,10 +392,11 @@ class Ospfv3(QuaggaService):
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def ptpcheck(ifc):
|
def ptpcheck(ifc):
|
||||||
''' Helper to detect whether interface is connected to a notional
|
"""
|
||||||
|
Helper to detect whether interface is connected to a notional
|
||||||
point-to-point link.
|
point-to-point link.
|
||||||
'''
|
"""
|
||||||
if isinstance(ifc.net, nodes.PtpNet):
|
if nodeutils.is_node(ifc.net, NodeTypes.PEER_TO_PEER):
|
||||||
return " ipv6 ospf6 network point-to-point\n"
|
return " ipv6 ospf6 network point-to-point\n"
|
||||||
return ""
|
return ""
|
||||||
|
|
||||||
|
|
@ -405,7 +406,7 @@ class Ospfv3(QuaggaService):
|
||||||
rtrid = cls.routerid(node)
|
rtrid = cls.routerid(node)
|
||||||
cfg += " router-id %s\n" % rtrid
|
cfg += " router-id %s\n" % rtrid
|
||||||
for ifc in node.netifs():
|
for ifc in node.netifs():
|
||||||
if hasattr(ifc, 'control') and ifc.control == True:
|
if hasattr(ifc, 'control') and ifc.control is True:
|
||||||
continue
|
continue
|
||||||
cfg += " interface %s area 0.0.0.0\n" % ifc.name
|
cfg += " interface %s area 0.0.0.0\n" % ifc.name
|
||||||
cfg += "!\n"
|
cfg += "!\n"
|
||||||
|
|
@ -421,19 +422,21 @@ class Ospfv3(QuaggaService):
|
||||||
# cfg += cls.ptpcheck(ifc)
|
# cfg += cls.ptpcheck(ifc)
|
||||||
|
|
||||||
# return cfg + """\
|
# return cfg + """\
|
||||||
|
|
||||||
|
|
||||||
# ipv6 ospf6 hello-interval 2
|
# ipv6 ospf6 hello-interval 2
|
||||||
# ipv6 ospf6 dead-interval 6
|
# ipv6 ospf6 dead-interval 6
|
||||||
# ipv6 ospf6 retransmit-interval 5
|
# ipv6 ospf6 retransmit-interval 5
|
||||||
# """
|
# """
|
||||||
|
|
||||||
addservice(Ospfv3)
|
|
||||||
|
|
||||||
class Ospfv3mdr(Ospfv3):
|
class Ospfv3mdr(Ospfv3):
|
||||||
''' The OSPFv3 MANET Designated Router (MDR) service provides IPv6
|
"""
|
||||||
|
The OSPFv3 MANET Designated Router (MDR) service provides IPv6
|
||||||
routing for wireless networks. It does not build its own
|
routing for wireless networks. It does not build its own
|
||||||
configuration file but has hooks for adding to the
|
configuration file but has hooks for adding to the
|
||||||
unified Quagga.conf file.
|
unified Quagga.conf file.
|
||||||
'''
|
"""
|
||||||
_name = "OSPFv3MDR"
|
_name = "OSPFv3MDR"
|
||||||
_ipv4_routing = True
|
_ipv4_routing = True
|
||||||
|
|
||||||
|
|
@ -441,8 +444,7 @@ class Ospfv3mdr(Ospfv3):
|
||||||
def generatequaggaifcconfig(cls, node, ifc):
|
def generatequaggaifcconfig(cls, node, ifc):
|
||||||
cfg = cls.mtucheck(ifc)
|
cfg = cls.mtucheck(ifc)
|
||||||
cfg += " ipv6 ospf6 instance-id 65\n"
|
cfg += " ipv6 ospf6 instance-id 65\n"
|
||||||
if ifc.net is not None and \
|
if ifc.net is not None and nodeutils.is_node(ifc.net, (NodeTypes.WIRELESS_LAN, NodeTypes.EMANE)):
|
||||||
isinstance(ifc.net, (nodes.WlanNode, nodes.EmaneNode)):
|
|
||||||
return cfg + """\
|
return cfg + """\
|
||||||
ipv6 ospf6 hello-interval 2
|
ipv6 ospf6 hello-interval 2
|
||||||
ipv6 ospf6 dead-interval 6
|
ipv6 ospf6 dead-interval 6
|
||||||
|
|
@ -455,13 +457,13 @@ class Ospfv3mdr(Ospfv3):
|
||||||
else:
|
else:
|
||||||
return cfg
|
return cfg
|
||||||
|
|
||||||
addservice(Ospfv3mdr)
|
|
||||||
|
|
||||||
class Bgp(QuaggaService):
|
class Bgp(QuaggaService):
|
||||||
'''' The BGP service provides interdomain routing.
|
"""
|
||||||
|
The BGP service provides interdomain routing.
|
||||||
Peers must be manually configured, with a full mesh for those
|
Peers must be manually configured, with a full mesh for those
|
||||||
having the same AS number.
|
having the same AS number.
|
||||||
'''
|
"""
|
||||||
_name = "BGP"
|
_name = "BGP"
|
||||||
_startup = ()
|
_startup = ()
|
||||||
_shutdown = ("killall bgpd", )
|
_shutdown = ("killall bgpd", )
|
||||||
|
|
@ -482,11 +484,11 @@ class Bgp(QuaggaService):
|
||||||
cfg += "! neighbor 1.2.3.4 remote-as 555\n!\n"
|
cfg += "! neighbor 1.2.3.4 remote-as 555\n!\n"
|
||||||
return cfg
|
return cfg
|
||||||
|
|
||||||
addservice(Bgp)
|
|
||||||
|
|
||||||
class Rip(QuaggaService):
|
class Rip(QuaggaService):
|
||||||
''' The RIP service provides IPv4 routing for wired networks.
|
"""
|
||||||
'''
|
The RIP service provides IPv4 routing for wired networks.
|
||||||
|
"""
|
||||||
_name = "RIP"
|
_name = "RIP"
|
||||||
_startup = ()
|
_startup = ()
|
||||||
_shutdown = ("killall ripd", )
|
_shutdown = ("killall ripd", )
|
||||||
|
|
@ -505,11 +507,11 @@ router rip
|
||||||
"""
|
"""
|
||||||
return cfg
|
return cfg
|
||||||
|
|
||||||
addservice(Rip)
|
|
||||||
|
|
||||||
class Ripng(QuaggaService):
|
class Ripng(QuaggaService):
|
||||||
''' The RIP NG service provides IPv6 routing for wired networks.
|
"""
|
||||||
'''
|
The RIP NG service provides IPv6 routing for wired networks.
|
||||||
|
"""
|
||||||
_name = "RIPNG"
|
_name = "RIPNG"
|
||||||
_startup = ()
|
_startup = ()
|
||||||
_shutdown = ("killall ripngd", )
|
_shutdown = ("killall ripngd", )
|
||||||
|
|
@ -528,12 +530,12 @@ router ripng
|
||||||
"""
|
"""
|
||||||
return cfg
|
return cfg
|
||||||
|
|
||||||
addservice(Ripng)
|
|
||||||
|
|
||||||
class Babel(QuaggaService):
|
class Babel(QuaggaService):
|
||||||
''' The Babel service provides a loop-avoiding distance-vector routing
|
"""
|
||||||
|
The Babel service provides a loop-avoiding distance-vector routing
|
||||||
protocol for IPv6 and IPv4 with fast convergence properties.
|
protocol for IPv6 and IPv4 with fast convergence properties.
|
||||||
'''
|
"""
|
||||||
_name = "Babel"
|
_name = "Babel"
|
||||||
_startup = ()
|
_startup = ()
|
||||||
_shutdown = ("killall babeld", )
|
_shutdown = ("killall babeld", )
|
||||||
|
|
@ -544,7 +546,7 @@ class Babel(QuaggaService):
|
||||||
def generatequaggaconfig(cls, node):
|
def generatequaggaconfig(cls, node):
|
||||||
cfg = "router babel\n"
|
cfg = "router babel\n"
|
||||||
for ifc in node.netifs():
|
for ifc in node.netifs():
|
||||||
if hasattr(ifc, 'control') and ifc.control == True:
|
if hasattr(ifc, 'control') and ifc.control is True:
|
||||||
continue
|
continue
|
||||||
cfg += " network %s\n" % ifc.name
|
cfg += " network %s\n" % ifc.name
|
||||||
cfg += " redistribute static\n redistribute connected\n"
|
cfg += " redistribute static\n redistribute connected\n"
|
||||||
|
|
@ -553,17 +555,16 @@ class Babel(QuaggaService):
|
||||||
@classmethod
|
@classmethod
|
||||||
def generatequaggaifcconfig(cls, node, ifc):
|
def generatequaggaifcconfig(cls, node, ifc):
|
||||||
type = "wired"
|
type = "wired"
|
||||||
if ifc.net and ifc.net.linktype == coreapi.CORE_LINK_WIRELESS:
|
if ifc.net and ifc.net.linktype == LinkTypes.WIRELESS.value:
|
||||||
return " babel wireless\n no babel split-horizon\n"
|
return " babel wireless\n no babel split-horizon\n"
|
||||||
else:
|
else:
|
||||||
return " babel wired\n babel split-horizon\n"
|
return " babel wired\n babel split-horizon\n"
|
||||||
|
|
||||||
addservice(Babel)
|
|
||||||
|
|
||||||
class Xpimd(QuaggaService):
|
class Xpimd(QuaggaService):
|
||||||
'''\
|
"""
|
||||||
PIM multicast routing based on XORP.
|
PIM multicast routing based on XORP.
|
||||||
'''
|
"""
|
||||||
_name = 'Xpimd'
|
_name = 'Xpimd'
|
||||||
_startup = ()
|
_startup = ()
|
||||||
_shutdown = ('killall xpimd', )
|
_shutdown = ('killall xpimd', )
|
||||||
|
|
@ -590,4 +591,14 @@ class Xpimd(QuaggaService):
|
||||||
def generatequaggaifcconfig(cls, node, ifc):
|
def generatequaggaifcconfig(cls, node, ifc):
|
||||||
return ' ip mfea\n ip igmp\n ip pim\n'
|
return ' ip mfea\n ip igmp\n ip pim\n'
|
||||||
|
|
||||||
addservice(Xpimd)
|
|
||||||
|
def load_services():
|
||||||
|
ServiceManager.add(Zebra)
|
||||||
|
ServiceManager.add(Ospfv2)
|
||||||
|
ServiceManager.add(Ospfv3)
|
||||||
|
ServiceManager.add(Ospfv3mdr)
|
||||||
|
ServiceManager.add(Bgp)
|
||||||
|
ServiceManager.add(Rip)
|
||||||
|
ServiceManager.add(Ripng)
|
||||||
|
ServiceManager.add(Babel)
|
||||||
|
ServiceManager.add(Xpimd)
|
||||||
|
|
|
||||||
|
|
@ -1,22 +1,17 @@
|
||||||
#
|
"""
|
||||||
# CORE - define security services : vpnclient, vpnserver, ipsec and firewall
|
|
||||||
#
|
|
||||||
# Copyright (c)2011-2012 the Boeing Company.
|
|
||||||
# See the LICENSE file included in this distribution.
|
|
||||||
#
|
|
||||||
'''
|
|
||||||
security.py: defines security services (vpnclient, vpnserver, ipsec and
|
security.py: defines security services (vpnclient, vpnserver, ipsec and
|
||||||
firewall)
|
firewall)
|
||||||
'''
|
"""
|
||||||
|
|
||||||
import os
|
from core import constants
|
||||||
|
from core.misc import log
|
||||||
|
from core.service import CoreService
|
||||||
|
from core.service import ServiceManager
|
||||||
|
|
||||||
|
logger = log.get_logger(__name__)
|
||||||
|
|
||||||
from core.service import CoreService, addservice
|
|
||||||
from core.constants import *
|
|
||||||
|
|
||||||
class VPNClient(CoreService):
|
class VPNClient(CoreService):
|
||||||
'''
|
|
||||||
'''
|
|
||||||
_name = "VPNClient"
|
_name = "VPNClient"
|
||||||
_group = "Security"
|
_group = "Security"
|
||||||
_configs = ('vpnclient.sh',)
|
_configs = ('vpnclient.sh',)
|
||||||
|
|
@ -28,24 +23,22 @@ class VPNClient(CoreService):
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def generateconfig(cls, node, filename, services):
|
def generateconfig(cls, node, filename, services):
|
||||||
''' Return the client.conf and vpnclient.sh file contents to
|
"""
|
||||||
'''
|
Return the client.conf and vpnclient.sh file contents to
|
||||||
|
"""
|
||||||
cfg = "#!/bin/sh\n"
|
cfg = "#!/bin/sh\n"
|
||||||
cfg += "# custom VPN Client configuration for service (security.py)\n"
|
cfg += "# custom VPN Client configuration for service (security.py)\n"
|
||||||
fname = "%s/examples/services/sampleVPNClient" % CORE_DATA_DIR
|
fname = "%s/examples/services/sampleVPNClient" % constants.CORE_DATA_DIR
|
||||||
|
|
||||||
try:
|
try:
|
||||||
cfg += open(fname, "rb").read()
|
cfg += open(fname, "rb").read()
|
||||||
except e:
|
except IOError:
|
||||||
print "Error opening VPN client configuration template (%s): %s" % \
|
logger.exception("Error opening VPN client configuration template (%s)", fname)
|
||||||
(fname, e)
|
|
||||||
return cfg
|
return cfg
|
||||||
|
|
||||||
# this line is required to add the above class to the list of available services
|
|
||||||
addservice(VPNClient)
|
|
||||||
|
|
||||||
class VPNServer(CoreService):
|
class VPNServer(CoreService):
|
||||||
'''
|
|
||||||
'''
|
|
||||||
_name = "VPNServer"
|
_name = "VPNServer"
|
||||||
_group = "Security"
|
_group = "Security"
|
||||||
_configs = ('vpnserver.sh',)
|
_configs = ('vpnserver.sh',)
|
||||||
|
|
@ -57,24 +50,23 @@ class VPNServer(CoreService):
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def generateconfig(cls, node, filename, services):
|
def generateconfig(cls, node, filename, services):
|
||||||
''' Return the sample server.conf and vpnserver.sh file contents to
|
"""
|
||||||
|
Return the sample server.conf and vpnserver.sh file contents to
|
||||||
GUI for user customization.
|
GUI for user customization.
|
||||||
'''
|
"""
|
||||||
cfg = "#!/bin/sh\n"
|
cfg = "#!/bin/sh\n"
|
||||||
cfg += "# custom VPN Server Configuration for service (security.py)\n"
|
cfg += "# custom VPN Server Configuration for service (security.py)\n"
|
||||||
fname = "%s/examples/services/sampleVPNServer" % CORE_DATA_DIR
|
fname = "%s/examples/services/sampleVPNServer" % constants.CORE_DATA_DIR
|
||||||
|
|
||||||
try:
|
try:
|
||||||
cfg += open(fname, "rb").read()
|
cfg += open(fname, "rb").read()
|
||||||
except e:
|
except IOError:
|
||||||
print "Error opening VPN server configuration template (%s): %s" % \
|
logger.exception("Error opening VPN server configuration template (%s)", fname)
|
||||||
(fname, e)
|
|
||||||
return cfg
|
return cfg
|
||||||
|
|
||||||
addservice(VPNServer)
|
|
||||||
|
|
||||||
class IPsec(CoreService):
|
class IPsec(CoreService):
|
||||||
'''
|
|
||||||
'''
|
|
||||||
_name = "IPsec"
|
_name = "IPsec"
|
||||||
_group = "Security"
|
_group = "Security"
|
||||||
_configs = ('ipsec.sh',)
|
_configs = ('ipsec.sh',)
|
||||||
|
|
@ -85,25 +77,24 @@ class IPsec(CoreService):
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def generateconfig(cls, node, filename, services):
|
def generateconfig(cls, node, filename, services):
|
||||||
''' Return the ipsec.conf and racoon.conf file contents to
|
"""
|
||||||
|
Return the ipsec.conf and racoon.conf file contents to
|
||||||
GUI for user customization.
|
GUI for user customization.
|
||||||
'''
|
"""
|
||||||
cfg = "#!/bin/sh\n"
|
cfg = "#!/bin/sh\n"
|
||||||
cfg += "# set up static tunnel mode security assocation for service "
|
cfg += "# set up static tunnel mode security assocation for service "
|
||||||
cfg += "(security.py)\n"
|
cfg += "(security.py)\n"
|
||||||
fname = "%s/examples/services/sampleIPsec" % CORE_DATA_DIR
|
fname = "%s/examples/services/sampleIPsec" % constants.CORE_DATA_DIR
|
||||||
|
|
||||||
try:
|
try:
|
||||||
cfg += open(fname, "rb").read()
|
cfg += open(fname, "rb").read()
|
||||||
except e:
|
except IOError:
|
||||||
print "Error opening IPsec configuration template (%s): %s" % \
|
logger.exception("Error opening IPsec configuration template (%s)", fname)
|
||||||
(fname, e)
|
|
||||||
return cfg
|
return cfg
|
||||||
|
|
||||||
addservice(IPsec)
|
|
||||||
|
|
||||||
class Firewall(CoreService):
|
class Firewall(CoreService):
|
||||||
'''
|
|
||||||
'''
|
|
||||||
_name = "Firewall"
|
_name = "Firewall"
|
||||||
_group = "Security"
|
_group = "Security"
|
||||||
_configs = ('firewall.sh',)
|
_configs = ('firewall.sh',)
|
||||||
|
|
@ -113,17 +104,24 @@ class Firewall(CoreService):
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def generateconfig(cls, node, filename, services):
|
def generateconfig(cls, node, filename, services):
|
||||||
''' Return the firewall rule examples to GUI for user customization.
|
"""
|
||||||
'''
|
Return the firewall rule examples to GUI for user customization.
|
||||||
|
"""
|
||||||
cfg = "#!/bin/sh\n"
|
cfg = "#!/bin/sh\n"
|
||||||
cfg += "# custom node firewall rules for service (security.py)\n"
|
cfg += "# custom node firewall rules for service (security.py)\n"
|
||||||
fname = "%s/examples/services/sampleFirewall" % CORE_DATA_DIR
|
fname = "%s/examples/services/sampleFirewall" % constants.CORE_DATA_DIR
|
||||||
|
|
||||||
try:
|
try:
|
||||||
cfg += open(fname, "rb").read()
|
cfg += open(fname, "rb").read()
|
||||||
except e:
|
except IOError:
|
||||||
print "Error opening Firewall configuration template (%s): %s" % \
|
logger.exception("Error opening Firewall configuration template (%s)", fname)
|
||||||
(fname, e)
|
|
||||||
return cfg
|
return cfg
|
||||||
|
|
||||||
addservice(Firewall)
|
|
||||||
|
|
||||||
|
def load_services():
|
||||||
|
# this line is required to add the above class to the list of available services
|
||||||
|
ServiceManager.add(VPNClient)
|
||||||
|
ServiceManager.add(VPNServer)
|
||||||
|
ServiceManager.add(IPsec)
|
||||||
|
ServiceManager.add(Firewall)
|
||||||
|
|
|
||||||
|
|
@ -1,9 +1,14 @@
|
||||||
from core.service import CoreService, addservice
|
|
||||||
from sys import maxint
|
|
||||||
from inspect import isclass
|
from inspect import isclass
|
||||||
|
from sys import maxint
|
||||||
|
|
||||||
|
from core.service import CoreService
|
||||||
|
from core.service import ServiceManager
|
||||||
|
|
||||||
|
|
||||||
class Startup(CoreService):
|
class Startup(CoreService):
|
||||||
'A CORE service to start other services in order, serially'
|
"""
|
||||||
|
A CORE service to start other services in order, serially
|
||||||
|
"""
|
||||||
_name = 'startup'
|
_name = 'startup'
|
||||||
_group = 'Utility'
|
_group = 'Utility'
|
||||||
_depends = ()
|
_depends = ()
|
||||||
|
|
@ -15,9 +20,8 @@ class Startup(CoreService):
|
||||||
_validate = ()
|
_validate = ()
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def isStartupService(s):
|
def is_startup_service(s):
|
||||||
return isinstance(s, Startup) or \
|
return isinstance(s, Startup) or (isclass(s) and issubclass(s, Startup))
|
||||||
(isclass(s) and issubclass(s, Startup))
|
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def generateconfig(cls, node, filename, services):
|
def generateconfig(cls, node, filename, services):
|
||||||
|
|
@ -27,11 +31,13 @@ class Startup(CoreService):
|
||||||
'# auto-generated by Startup (startup.py)\n\n' \
|
'# auto-generated by Startup (startup.py)\n\n' \
|
||||||
'exec > startup.log 2>&1\n\n'
|
'exec > startup.log 2>&1\n\n'
|
||||||
for s in sorted(services, key=lambda x: x._startindex):
|
for s in sorted(services, key=lambda x: x._startindex):
|
||||||
if cls.isStartupService(s) or len(str(s._starttime)) > 0:
|
if cls.is_startup_service(s) or len(str(s._starttime)) > 0:
|
||||||
continue
|
continue
|
||||||
start = '\n'.join(s.getstartup(node, services))
|
start = '\n'.join(s.getstartup(node, services))
|
||||||
if start:
|
if start:
|
||||||
script += start + '\n'
|
script += start + '\n'
|
||||||
return script
|
return script
|
||||||
|
|
||||||
addservice(Startup)
|
|
||||||
|
def load_services():
|
||||||
|
ServiceManager.add(Startup)
|
||||||
|
|
|
||||||
|
|
@ -1,32 +1,20 @@
|
||||||
#
|
"""
|
||||||
# CORE configuration for UCARP
|
|
||||||
# Copyright (c) 2012 Jonathan deBoer
|
|
||||||
# See the LICENSE file included in this distribution.
|
|
||||||
#
|
|
||||||
#
|
|
||||||
# author: Jonathan deBoer <jdccdevel@gmail.com>
|
|
||||||
#
|
|
||||||
'''
|
|
||||||
ucarp.py: defines high-availability IP address controlled by ucarp
|
ucarp.py: defines high-availability IP address controlled by ucarp
|
||||||
'''
|
"""
|
||||||
|
|
||||||
import os
|
|
||||||
|
|
||||||
from core.service import CoreService, addservice
|
|
||||||
from core.misc.ipaddr import IPv4Prefix
|
|
||||||
from core.constants import *
|
|
||||||
|
|
||||||
|
from core.service import CoreService
|
||||||
|
from core.service import ServiceManager
|
||||||
|
|
||||||
UCARP_ETC = "/usr/local/etc/ucarp"
|
UCARP_ETC = "/usr/local/etc/ucarp"
|
||||||
|
|
||||||
|
|
||||||
class Ucarp(CoreService):
|
class Ucarp(CoreService):
|
||||||
'''
|
|
||||||
'''
|
|
||||||
_name = "ucarp"
|
_name = "ucarp"
|
||||||
_group = "Utility"
|
_group = "Utility"
|
||||||
_depends = ( )
|
_depends = ( )
|
||||||
_dirs = (UCARP_ETC,)
|
_dirs = (UCARP_ETC,)
|
||||||
_configs = (UCARP_ETC + "/default.sh", UCARP_ETC + "/default-up.sh", UCARP_ETC + "/default-down.sh", "ucarpboot.sh",)
|
_configs = (
|
||||||
|
UCARP_ETC + "/default.sh", UCARP_ETC + "/default-up.sh", UCARP_ETC + "/default-down.sh", "ucarpboot.sh",)
|
||||||
_startindex = 65
|
_startindex = 65
|
||||||
_startup = ("sh ucarpboot.sh",)
|
_startup = ("sh ucarpboot.sh",)
|
||||||
_shutdown = ("killall ucarp",)
|
_shutdown = ("killall ucarp",)
|
||||||
|
|
@ -34,8 +22,9 @@ class Ucarp(CoreService):
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def generateconfig(cls, node, filename, services):
|
def generateconfig(cls, node, filename, services):
|
||||||
''' Return the default file contents
|
"""
|
||||||
'''
|
Return the default file contents
|
||||||
|
"""
|
||||||
if filename == cls._configs[0]:
|
if filename == cls._configs[0]:
|
||||||
return cls.generateUcarpConf(node, services)
|
return cls.generateUcarpConf(node, services)
|
||||||
elif filename == cls._configs[1]:
|
elif filename == cls._configs[1]:
|
||||||
|
|
@ -49,12 +38,14 @@ class Ucarp(CoreService):
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def generateUcarpConf(cls, node, services):
|
def generateUcarpConf(cls, node, services):
|
||||||
''' Returns configuration file text.
|
"""
|
||||||
'''
|
Returns configuration file text.
|
||||||
|
"""
|
||||||
try:
|
try:
|
||||||
ucarp_bin = node.session.cfg['ucarp_bin']
|
ucarp_bin = node.session.cfg['ucarp_bin']
|
||||||
except KeyError:
|
except KeyError:
|
||||||
ucarp_bin = "/usr/sbin/ucarp"
|
ucarp_bin = "/usr/sbin/ucarp"
|
||||||
|
|
||||||
return """\
|
return """\
|
||||||
#!/bin/sh
|
#!/bin/sh
|
||||||
# Location of UCARP executable
|
# Location of UCARP executable
|
||||||
|
|
@ -116,8 +107,10 @@ ${UCARP_EXEC} -B ${UCARP_OPTS}
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def generateUcarpBoot(cls, node, services):
|
def generateUcarpBoot(cls, node, services):
|
||||||
''' Generate a shell script used to boot the Ucarp daemons.
|
"""
|
||||||
'''
|
Generate a shell script used to boot the Ucarp daemons.
|
||||||
|
"""
|
||||||
|
|
||||||
try:
|
try:
|
||||||
ucarp_bin = node.session.cfg['ucarp_bin']
|
ucarp_bin = node.session.cfg['ucarp_bin']
|
||||||
except KeyError:
|
except KeyError:
|
||||||
|
|
@ -132,16 +125,18 @@ chmod a+x ${UCARP_CFGDIR}/*.sh
|
||||||
# Start the default ucarp daemon configuration
|
# Start the default ucarp daemon configuration
|
||||||
${UCARP_CFGDIR}/default.sh
|
${UCARP_CFGDIR}/default.sh
|
||||||
|
|
||||||
""" % (UCARP_ETC)
|
""" % UCARP_ETC
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def generateVipUp(cls, node, services):
|
def generateVipUp(cls, node, services):
|
||||||
''' Generate a shell script used to start the virtual ip
|
"""
|
||||||
'''
|
Generate a shell script used to start the virtual ip
|
||||||
|
"""
|
||||||
try:
|
try:
|
||||||
ucarp_bin = node.session.cfg['ucarp_bin']
|
ucarp_bin = node.session.cfg['ucarp_bin']
|
||||||
except KeyError:
|
except KeyError:
|
||||||
ucarp_bin = "/usr/sbin/ucarp"
|
ucarp_bin = "/usr/sbin/ucarp"
|
||||||
|
|
||||||
return """\
|
return """\
|
||||||
#!/bin/bash
|
#!/bin/bash
|
||||||
|
|
||||||
|
|
@ -161,8 +156,9 @@ fi
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def generateVipDown(cls, node, services):
|
def generateVipDown(cls, node, services):
|
||||||
''' Generate a shell script used to stop the virtual ip
|
"""
|
||||||
'''
|
Generate a shell script used to stop the virtual ip
|
||||||
|
"""
|
||||||
try:
|
try:
|
||||||
ucarp_bin = node.session.cfg['ucarp_bin']
|
ucarp_bin = node.session.cfg['ucarp_bin']
|
||||||
except KeyError:
|
except KeyError:
|
||||||
|
|
@ -185,5 +181,5 @@ fi
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
|
||||||
addservice(Ucarp)
|
def load_services():
|
||||||
|
ServiceManager.add(Ucarp)
|
||||||
|
|
|
||||||
|
|
@ -1,24 +1,22 @@
|
||||||
#
|
"""
|
||||||
# CORE
|
|
||||||
# Copyright (c)2010-2014 the Boeing Company.
|
|
||||||
# See the LICENSE file included in this distribution.
|
|
||||||
#
|
|
||||||
# author: Jeff Ahrenholz <jeffrey.m.ahrenholz@boeing.com>
|
|
||||||
#
|
|
||||||
'''
|
|
||||||
utility.py: defines miscellaneous utility services.
|
utility.py: defines miscellaneous utility services.
|
||||||
'''
|
"""
|
||||||
|
|
||||||
import os
|
import os
|
||||||
|
import subprocess
|
||||||
|
|
||||||
|
from core import constants
|
||||||
|
from core.misc import utils
|
||||||
|
from core.misc.ipaddress import Ipv4Prefix
|
||||||
|
from core.misc.ipaddress import Ipv6Prefix
|
||||||
|
from core.service import CoreService
|
||||||
|
from core.service import ServiceManager
|
||||||
|
|
||||||
from core.service import CoreService, addservice
|
|
||||||
from core.misc.ipaddr import IPv4Prefix, IPv6Prefix
|
|
||||||
from core.misc.utils import *
|
|
||||||
from core.constants import *
|
|
||||||
|
|
||||||
class UtilService(CoreService):
|
class UtilService(CoreService):
|
||||||
''' Parent class for utility services.
|
"""
|
||||||
'''
|
Parent class for utility services.
|
||||||
|
"""
|
||||||
_name = "UtilityProcess"
|
_name = "UtilityProcess"
|
||||||
_group = "Utility"
|
_group = "Utility"
|
||||||
_depends = ()
|
_depends = ()
|
||||||
|
|
@ -32,6 +30,7 @@ class UtilService(CoreService):
|
||||||
def generateconfig(cls, node, filename, services):
|
def generateconfig(cls, node, filename, services):
|
||||||
return ""
|
return ""
|
||||||
|
|
||||||
|
|
||||||
class IPForwardService(UtilService):
|
class IPForwardService(UtilService):
|
||||||
_name = "IPForward"
|
_name = "IPForward"
|
||||||
_configs = ("ipforward.sh",)
|
_configs = ("ipforward.sh",)
|
||||||
|
|
@ -60,13 +59,13 @@ class IPForwardService(UtilService):
|
||||||
%(sysctl)s -w net.ipv4.conf.default.send_redirects=0
|
%(sysctl)s -w net.ipv4.conf.default.send_redirects=0
|
||||||
%(sysctl)s -w net.ipv4.conf.all.rp_filter=0
|
%(sysctl)s -w net.ipv4.conf.all.rp_filter=0
|
||||||
%(sysctl)s -w net.ipv4.conf.default.rp_filter=0
|
%(sysctl)s -w net.ipv4.conf.default.rp_filter=0
|
||||||
""" % {'sysctl': SYSCTL_BIN}
|
""" % {'sysctl': constants.SYSCTL_BIN}
|
||||||
for ifc in node.netifs():
|
for ifc in node.netifs():
|
||||||
name = sysctldevname(ifc.name)
|
name = utils.sysctldevname(ifc.name)
|
||||||
cfg += "%s -w net.ipv4.conf.%s.forwarding=1\n" % (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" % \
|
cfg += "%s -w net.ipv4.conf.%s.send_redirects=0\n" % \
|
||||||
(SYSCTL_BIN, name)
|
(constants.SYSCTL_BIN, name)
|
||||||
cfg += "%s -w net.ipv4.conf.%s.rp_filter=0\n" % (SYSCTL_BIN, name)
|
cfg += "%s -w net.ipv4.conf.%s.rp_filter=0\n" % (constants.SYSCTL_BIN, name)
|
||||||
return cfg
|
return cfg
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
|
|
@ -78,9 +77,8 @@ class IPForwardService(UtilService):
|
||||||
%s -w net.inet6.ip6.forwarding=1
|
%s -w net.inet6.ip6.forwarding=1
|
||||||
%s -w net.inet.icmp.bmcastecho=1
|
%s -w net.inet.icmp.bmcastecho=1
|
||||||
%s -w net.inet.icmp.icmplim=0
|
%s -w net.inet.icmp.icmplim=0
|
||||||
""" % (SYSCTL_BIN, SYSCTL_BIN, SYSCTL_BIN, SYSCTL_BIN)
|
""" % (constants.SYSCTL_BIN, constants.SYSCTL_BIN, constants.SYSCTL_BIN, constants.SYSCTL_BIN)
|
||||||
|
|
||||||
addservice(IPForwardService)
|
|
||||||
|
|
||||||
class DefaultRouteService(UtilService):
|
class DefaultRouteService(UtilService):
|
||||||
_name = "DefaultRoute"
|
_name = "DefaultRoute"
|
||||||
|
|
@ -92,7 +90,7 @@ class DefaultRouteService(UtilService):
|
||||||
cfg = "#!/bin/sh\n"
|
cfg = "#!/bin/sh\n"
|
||||||
cfg += "# auto-generated by DefaultRoute service (utility.py)\n"
|
cfg += "# auto-generated by DefaultRoute service (utility.py)\n"
|
||||||
for ifc in node.netifs():
|
for ifc in node.netifs():
|
||||||
if hasattr(ifc, 'control') and ifc.control == True:
|
if hasattr(ifc, 'control') and ifc.control is True:
|
||||||
continue
|
continue
|
||||||
cfg += "\n".join(map(cls.addrstr, ifc.addrlist))
|
cfg += "\n".join(map(cls.addrstr, ifc.addrlist))
|
||||||
cfg += "\n"
|
cfg += "\n"
|
||||||
|
|
@ -101,12 +99,12 @@ class DefaultRouteService(UtilService):
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def addrstr(x):
|
def addrstr(x):
|
||||||
if x.find(":") >= 0:
|
if x.find(":") >= 0:
|
||||||
net = IPv6Prefix(x)
|
net = Ipv6Prefix(x)
|
||||||
fam = "inet6 ::"
|
fam = "inet6 ::"
|
||||||
else:
|
else:
|
||||||
net = IPv4Prefix(x)
|
net = Ipv4Prefix(x)
|
||||||
fam = "inet 0.0.0.0"
|
fam = "inet 0.0.0.0"
|
||||||
if net.maxaddr() == net.minaddr():
|
if net.max_addr() == net.min_addr():
|
||||||
return ""
|
return ""
|
||||||
else:
|
else:
|
||||||
if os.uname()[0] == "Linux":
|
if os.uname()[0] == "Linux":
|
||||||
|
|
@ -115,9 +113,8 @@ class DefaultRouteService(UtilService):
|
||||||
rtcmd = "route add -%s" % fam
|
rtcmd = "route add -%s" % fam
|
||||||
else:
|
else:
|
||||||
raise Exception, "unknown platform"
|
raise Exception, "unknown platform"
|
||||||
return "%s %s" % (rtcmd, net.minaddr())
|
return "%s %s" % (rtcmd, net.min_addr())
|
||||||
|
|
||||||
addservice(DefaultRouteService)
|
|
||||||
|
|
||||||
class DefaultMulticastRouteService(UtilService):
|
class DefaultMulticastRouteService(UtilService):
|
||||||
_name = "DefaultMulticastRoute"
|
_name = "DefaultMulticastRoute"
|
||||||
|
|
@ -145,7 +142,6 @@ class DefaultMulticastRouteService(UtilService):
|
||||||
break
|
break
|
||||||
return cfg
|
return cfg
|
||||||
|
|
||||||
addservice(DefaultMulticastRouteService)
|
|
||||||
|
|
||||||
class StaticRouteService(UtilService):
|
class StaticRouteService(UtilService):
|
||||||
_name = "StaticRoute"
|
_name = "StaticRoute"
|
||||||
|
|
@ -160,7 +156,7 @@ class StaticRouteService(UtilService):
|
||||||
cfg += "# NOTE: this service must be customized to be of any use\n"
|
cfg += "# NOTE: this service must be customized to be of any use\n"
|
||||||
cfg += "# Below are samples that you can uncomment and edit.\n#\n"
|
cfg += "# Below are samples that you can uncomment and edit.\n#\n"
|
||||||
for ifc in node.netifs():
|
for ifc in node.netifs():
|
||||||
if hasattr(ifc, 'control') and ifc.control == True:
|
if hasattr(ifc, 'control') and ifc.control is True:
|
||||||
continue
|
continue
|
||||||
cfg += "\n".join(map(cls.routestr, ifc.addrlist))
|
cfg += "\n".join(map(cls.routestr, ifc.addrlist))
|
||||||
cfg += "\n"
|
cfg += "\n"
|
||||||
|
|
@ -169,14 +165,14 @@ class StaticRouteService(UtilService):
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def routestr(x):
|
def routestr(x):
|
||||||
if x.find(":") >= 0:
|
if x.find(":") >= 0:
|
||||||
net = IPv6Prefix(x)
|
net = Ipv6Prefix(x)
|
||||||
fam = "inet6"
|
fam = "inet6"
|
||||||
dst = "3ffe:4::/64"
|
dst = "3ffe:4::/64"
|
||||||
else:
|
else:
|
||||||
net = IPv4Prefix(x)
|
net = Ipv4Prefix(x)
|
||||||
fam = "inet"
|
fam = "inet"
|
||||||
dst = "10.9.8.0/24"
|
dst = "10.9.8.0/24"
|
||||||
if net.maxaddr() == net.minaddr():
|
if net.max_addr() == net.min_addr():
|
||||||
return ""
|
return ""
|
||||||
else:
|
else:
|
||||||
if os.uname()[0] == "Linux":
|
if os.uname()[0] == "Linux":
|
||||||
|
|
@ -185,9 +181,8 @@ class StaticRouteService(UtilService):
|
||||||
rtcmd = "#/sbin/route add -%s %s" % (fam, dst)
|
rtcmd = "#/sbin/route add -%s %s" % (fam, dst)
|
||||||
else:
|
else:
|
||||||
raise Exception, "unknown platform"
|
raise Exception, "unknown platform"
|
||||||
return "%s %s" % (rtcmd, net.minaddr())
|
return "%s %s" % (rtcmd, net.min_addr())
|
||||||
|
|
||||||
addservice(StaticRouteService)
|
|
||||||
|
|
||||||
class SshService(UtilService):
|
class SshService(UtilService):
|
||||||
_name = "SSH"
|
_name = "SSH"
|
||||||
|
|
@ -203,9 +198,10 @@ class SshService(UtilService):
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def generateconfig(cls, node, filename, services):
|
def generateconfig(cls, node, filename, services):
|
||||||
''' Use a startup script for launching sshd in order to wait for host
|
"""
|
||||||
|
Use a startup script for launching sshd in order to wait for host
|
||||||
key generation.
|
key generation.
|
||||||
'''
|
"""
|
||||||
if os.uname()[0] == "FreeBSD":
|
if os.uname()[0] == "FreeBSD":
|
||||||
sshcfgdir = node.nodedir
|
sshcfgdir = node.nodedir
|
||||||
sshstatedir = node.nodedir
|
sshstatedir = node.nodedir
|
||||||
|
|
@ -264,7 +260,6 @@ UsePAM yes
|
||||||
UseDNS no
|
UseDNS no
|
||||||
""" % (sshcfgdir, sshstatedir, sshlibdir)
|
""" % (sshcfgdir, sshstatedir, sshlibdir)
|
||||||
|
|
||||||
addservice(SshService)
|
|
||||||
|
|
||||||
class DhcpService(UtilService):
|
class DhcpService(UtilService):
|
||||||
_name = "DHCP"
|
_name = "DHCP"
|
||||||
|
|
@ -276,9 +271,10 @@ class DhcpService(UtilService):
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def generateconfig(cls, node, filename, services):
|
def generateconfig(cls, node, filename, services):
|
||||||
''' Generate a dhcpd config file using the network address of
|
"""
|
||||||
|
Generate a dhcpd config file using the network address of
|
||||||
each interface.
|
each interface.
|
||||||
'''
|
"""
|
||||||
cfg = """\
|
cfg = """\
|
||||||
# auto-generated by DHCP service (utility.py)
|
# auto-generated by DHCP service (utility.py)
|
||||||
# NOTE: move these option lines into the desired pool { } block(s) below
|
# NOTE: move these option lines into the desired pool { } block(s) below
|
||||||
|
|
@ -294,7 +290,7 @@ max-lease-time 7200;
|
||||||
ddns-update-style none;
|
ddns-update-style none;
|
||||||
"""
|
"""
|
||||||
for ifc in node.netifs():
|
for ifc in node.netifs():
|
||||||
if hasattr(ifc, 'control') and ifc.control == True:
|
if hasattr(ifc, 'control') and ifc.control is True:
|
||||||
continue
|
continue
|
||||||
cfg += "\n".join(map(cls.subnetentry, ifc.addrlist))
|
cfg += "\n".join(map(cls.subnetentry, ifc.addrlist))
|
||||||
cfg += "\n"
|
cfg += "\n"
|
||||||
|
|
@ -302,17 +298,18 @@ ddns-update-style none;
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def subnetentry(x):
|
def subnetentry(x):
|
||||||
''' Generate a subnet declaration block given an IPv4 prefix string
|
"""
|
||||||
|
Generate a subnet declaration block given an IPv4 prefix string
|
||||||
for inclusion in the dhcpd3 config file.
|
for inclusion in the dhcpd3 config file.
|
||||||
'''
|
"""
|
||||||
if x.find(":") >= 0:
|
if x.find(":") >= 0:
|
||||||
return ""
|
return ""
|
||||||
else:
|
else:
|
||||||
addr = x.split("/")[0]
|
addr = x.split("/")[0]
|
||||||
net = IPv4Prefix(x)
|
net = Ipv4Prefix(x)
|
||||||
# divide the address space in half
|
# divide the address space in half
|
||||||
rangelow = net.addr(net.numaddr() / 2)
|
rangelow = net.addr(net.num_addr() / 2)
|
||||||
rangehigh = net.maxaddr()
|
rangehigh = net.max_addr()
|
||||||
return """
|
return """
|
||||||
subnet %s netmask %s {
|
subnet %s netmask %s {
|
||||||
pool {
|
pool {
|
||||||
|
|
@ -321,13 +318,13 @@ subnet %s netmask %s {
|
||||||
option routers %s;
|
option routers %s;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
""" % (net.prefixstr(), net.netmaskstr(), rangelow, rangehigh, addr)
|
""" % (net.prefix_str(), net.netmask_str(), rangelow, rangehigh, addr)
|
||||||
|
|
||||||
addservice(DhcpService)
|
|
||||||
|
|
||||||
class DhcpClientService(UtilService):
|
class DhcpClientService(UtilService):
|
||||||
''' Use a DHCP client for all interfaces for addressing.
|
"""
|
||||||
'''
|
Use a DHCP client for all interfaces for addressing.
|
||||||
|
"""
|
||||||
_name = "DHCPClient"
|
_name = "DHCPClient"
|
||||||
_configs = ("startdhcpclient.sh",)
|
_configs = ("startdhcpclient.sh",)
|
||||||
_startup = ("sh startdhcpclient.sh",)
|
_startup = ("sh startdhcpclient.sh",)
|
||||||
|
|
@ -336,8 +333,9 @@ class DhcpClientService(UtilService):
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def generateconfig(cls, node, filename, services):
|
def generateconfig(cls, node, filename, services):
|
||||||
''' Generate a script to invoke dhclient on all interfaces.
|
"""
|
||||||
'''
|
Generate a script to invoke dhclient on all interfaces.
|
||||||
|
"""
|
||||||
cfg = "#!/bin/sh\n"
|
cfg = "#!/bin/sh\n"
|
||||||
cfg += "# auto-generated by DHCPClient service (utility.py)\n"
|
cfg += "# auto-generated by DHCPClient service (utility.py)\n"
|
||||||
cfg += "# uncomment this mkdir line and symlink line to enable client-"
|
cfg += "# uncomment this mkdir line and symlink line to enable client-"
|
||||||
|
|
@ -353,11 +351,11 @@ class DhcpClientService(UtilService):
|
||||||
cfg += " -lf /var/run/dhclient-%s.lease %s\n" % (ifc.name, ifc.name)
|
cfg += " -lf /var/run/dhclient-%s.lease %s\n" % (ifc.name, ifc.name)
|
||||||
return cfg
|
return cfg
|
||||||
|
|
||||||
addservice(DhcpClientService)
|
|
||||||
|
|
||||||
class FtpService(UtilService):
|
class FtpService(UtilService):
|
||||||
''' Start a vsftpd server.
|
"""
|
||||||
'''
|
Start a vsftpd server.
|
||||||
|
"""
|
||||||
_name = "FTP"
|
_name = "FTP"
|
||||||
_configs = ("vsftpd.conf",)
|
_configs = ("vsftpd.conf",)
|
||||||
_dirs = ("/var/run/vsftpd/empty", "/var/ftp",)
|
_dirs = ("/var/run/vsftpd/empty", "/var/ftp",)
|
||||||
|
|
@ -367,8 +365,9 @@ class FtpService(UtilService):
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def generateconfig(cls, node, filename, services):
|
def generateconfig(cls, node, filename, services):
|
||||||
''' Generate a vsftpd.conf configuration file.
|
"""
|
||||||
'''
|
Generate a vsftpd.conf configuration file.
|
||||||
|
"""
|
||||||
return """\
|
return """\
|
||||||
# vsftpd.conf auto-generated by FTP service (utility.py)
|
# vsftpd.conf auto-generated by FTP service (utility.py)
|
||||||
listen=YES
|
listen=YES
|
||||||
|
|
@ -384,11 +383,11 @@ secure_chroot_dir=/var/run/vsftpd/empty
|
||||||
anon_root=/var/ftp
|
anon_root=/var/ftp
|
||||||
"""
|
"""
|
||||||
|
|
||||||
addservice(FtpService)
|
|
||||||
|
|
||||||
class HttpService(UtilService):
|
class HttpService(UtilService):
|
||||||
''' Start an apache server.
|
"""
|
||||||
'''
|
Start an apache server.
|
||||||
|
"""
|
||||||
_name = "HTTP"
|
_name = "HTTP"
|
||||||
_configs = ("/etc/apache2/apache2.conf", "/etc/apache2/envvars",
|
_configs = ("/etc/apache2/apache2.conf", "/etc/apache2/envvars",
|
||||||
"/var/www/index.html",)
|
"/var/www/index.html",)
|
||||||
|
|
@ -402,8 +401,9 @@ class HttpService(UtilService):
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def generateconfig(cls, node, filename, services):
|
def generateconfig(cls, node, filename, services):
|
||||||
''' Generate an apache2.conf configuration file.
|
"""
|
||||||
'''
|
Generate an apache2.conf configuration file.
|
||||||
|
"""
|
||||||
if filename == cls._configs[0]:
|
if filename == cls._configs[0]:
|
||||||
return cls.generateapache2conf(node, filename, services)
|
return cls.generateapache2conf(node, filename, services)
|
||||||
elif filename == cls._configs[1]:
|
elif filename == cls._configs[1]:
|
||||||
|
|
@ -415,16 +415,18 @@ class HttpService(UtilService):
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def detectversionfromcmd(cls):
|
def detectversionfromcmd(cls):
|
||||||
''' Detect the apache2 version using the 'a2query' command.
|
"""
|
||||||
'''
|
Detect the apache2 version using the 'a2query' command.
|
||||||
|
"""
|
||||||
try:
|
try:
|
||||||
status, result = cmdresult(['a2query', '-v'])
|
status, result = utils.cmdresult(['a2query', '-v'])
|
||||||
except Exception:
|
except subprocess.CalledProcessError:
|
||||||
status = -1
|
status = -1
|
||||||
|
|
||||||
if status == 0 and result[:3] == '2.4':
|
if status == 0 and result[:3] == '2.4':
|
||||||
return cls.APACHEVER24
|
return cls.APACHEVER24
|
||||||
return cls.APACHEVER22
|
|
||||||
|
|
||||||
|
return cls.APACHEVER22
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def generateapache2conf(cls, node, filename, services):
|
def generateapache2conf(cls, node, filename, services):
|
||||||
|
|
@ -593,11 +595,11 @@ export LANG
|
||||||
body += "<li>%s - %s</li>\n" % (ifc.name, ifc.addrlist)
|
body += "<li>%s - %s</li>\n" % (ifc.name, ifc.addrlist)
|
||||||
return "<html><body>%s</body></html>" % body
|
return "<html><body>%s</body></html>" % body
|
||||||
|
|
||||||
addservice(HttpService)
|
|
||||||
|
|
||||||
class PcapService(UtilService):
|
class PcapService(UtilService):
|
||||||
''' Pcap service for logging packets.
|
"""
|
||||||
'''
|
Pcap service for logging packets.
|
||||||
|
"""
|
||||||
_name = "pcap"
|
_name = "pcap"
|
||||||
_configs = ("pcap.sh",)
|
_configs = ("pcap.sh",)
|
||||||
_dirs = ()
|
_dirs = ()
|
||||||
|
|
@ -609,8 +611,9 @@ class PcapService(UtilService):
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def generateconfig(cls, node, filename, services):
|
def generateconfig(cls, node, filename, services):
|
||||||
''' Generate a startpcap.sh traffic logging script.
|
"""
|
||||||
'''
|
Generate a startpcap.sh traffic logging script.
|
||||||
|
"""
|
||||||
cfg = """
|
cfg = """
|
||||||
#!/bin/sh
|
#!/bin/sh
|
||||||
# set tcpdump options here (see 'man tcpdump' for help)
|
# set tcpdump options here (see 'man tcpdump' for help)
|
||||||
|
|
@ -635,7 +638,6 @@ fi;
|
||||||
"""
|
"""
|
||||||
return cfg
|
return cfg
|
||||||
|
|
||||||
addservice(PcapService)
|
|
||||||
|
|
||||||
class RadvdService(UtilService):
|
class RadvdService(UtilService):
|
||||||
_name = "radvd"
|
_name = "radvd"
|
||||||
|
|
@ -647,9 +649,10 @@ class RadvdService(UtilService):
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def generateconfig(cls, node, filename, services):
|
def generateconfig(cls, node, filename, services):
|
||||||
''' Generate a RADVD router advertisement daemon config file
|
"""
|
||||||
|
Generate a RADVD router advertisement daemon config file
|
||||||
using the network address of each interface.
|
using the network address of each interface.
|
||||||
'''
|
"""
|
||||||
cfg = "# auto-generated by RADVD service (utility.py)\n"
|
cfg = "# auto-generated by RADVD service (utility.py)\n"
|
||||||
for ifc in node.netifs():
|
for ifc in node.netifs():
|
||||||
if hasattr(ifc, 'control') and ifc.control == True:
|
if hasattr(ifc, 'control') and ifc.control == True:
|
||||||
|
|
@ -682,20 +685,21 @@ interface %s
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def subnetentry(x):
|
def subnetentry(x):
|
||||||
''' Generate a subnet declaration block given an IPv6 prefix string
|
"""
|
||||||
|
Generate a subnet declaration block given an IPv6 prefix string
|
||||||
for inclusion in the RADVD config file.
|
for inclusion in the RADVD config file.
|
||||||
'''
|
"""
|
||||||
if x.find(":") >= 0:
|
if x.find(":") >= 0:
|
||||||
net = IPv6Prefix(x)
|
net = Ipv6Prefix(x)
|
||||||
return str(net)
|
return str(net)
|
||||||
else:
|
else:
|
||||||
return ""
|
return ""
|
||||||
|
|
||||||
addservice(RadvdService)
|
|
||||||
|
|
||||||
class AtdService(UtilService):
|
class AtdService(UtilService):
|
||||||
''' Atd service for scheduling at jobs
|
"""
|
||||||
'''
|
Atd service for scheduling at jobs
|
||||||
|
"""
|
||||||
_name = "atd"
|
_name = "atd"
|
||||||
_configs = ("startatd.sh",)
|
_configs = ("startatd.sh",)
|
||||||
_dirs = ("/var/spool/cron/atjobs", "/var/spool/cron/atspool")
|
_dirs = ("/var/spool/cron/atjobs", "/var/spool/cron/atspool")
|
||||||
|
|
@ -712,13 +716,27 @@ chmod -R 700 /var/spool/cron/*
|
||||||
atd
|
atd
|
||||||
"""
|
"""
|
||||||
|
|
||||||
addservice(AtdService)
|
|
||||||
|
|
||||||
class UserDefinedService(UtilService):
|
class UserDefinedService(UtilService):
|
||||||
''' Dummy service allowing customization of anything.
|
"""
|
||||||
'''
|
Dummy service allowing customization of anything.
|
||||||
|
"""
|
||||||
_name = "UserDefined"
|
_name = "UserDefined"
|
||||||
_startindex = 50
|
_startindex = 50
|
||||||
_meta = "Customize this service to do anything upon startup."
|
_meta = "Customize this service to do anything upon startup."
|
||||||
|
|
||||||
addservice(UserDefinedService)
|
|
||||||
|
def load_services():
|
||||||
|
ServiceManager.add(IPForwardService)
|
||||||
|
ServiceManager.add(DefaultRouteService)
|
||||||
|
ServiceManager.add(DefaultMulticastRouteService)
|
||||||
|
ServiceManager.add(StaticRouteService)
|
||||||
|
ServiceManager.add(SshService)
|
||||||
|
ServiceManager.add(DhcpService)
|
||||||
|
ServiceManager.add(DhcpClientService)
|
||||||
|
ServiceManager.add(FtpService)
|
||||||
|
ServiceManager.add(HttpService)
|
||||||
|
ServiceManager.add(PcapService)
|
||||||
|
ServiceManager.add(RadvdService)
|
||||||
|
ServiceManager.add(AtdService)
|
||||||
|
ServiceManager.add(UserDefinedService)
|
||||||
|
|
|
||||||
|
|
@ -1,24 +1,19 @@
|
||||||
#
|
"""
|
||||||
# CORE
|
|
||||||
# Copyright (c)2011-2012 the Boeing Company.
|
|
||||||
# See the LICENSE file included in this distribution.
|
|
||||||
#
|
|
||||||
# author: Jeff Ahrenholz <jeffrey.m.ahrenholz@boeing.com>
|
|
||||||
#
|
|
||||||
'''
|
|
||||||
xorp.py: defines routing services provided by the XORP routing suite.
|
xorp.py: defines routing services provided by the XORP routing suite.
|
||||||
'''
|
"""
|
||||||
|
|
||||||
import os
|
from core.misc import log
|
||||||
|
from core.service import CoreService
|
||||||
|
from core.service import ServiceManager
|
||||||
|
|
||||||
|
logger = log.get_logger(__name__)
|
||||||
|
|
||||||
from core.service import CoreService, addservice
|
|
||||||
from core.misc.ipaddr import IPv4Prefix
|
|
||||||
from core.constants import *
|
|
||||||
|
|
||||||
class XorpRtrmgr(CoreService):
|
class XorpRtrmgr(CoreService):
|
||||||
''' XORP router manager service builds a config.boot file based on other
|
"""
|
||||||
|
XORP router manager service builds a config.boot file based on other
|
||||||
enabled XORP services, and launches necessary daemons upon startup.
|
enabled XORP services, and launches necessary daemons upon startup.
|
||||||
'''
|
"""
|
||||||
_name = "xorp_rtrmgr"
|
_name = "xorp_rtrmgr"
|
||||||
_group = "XORP"
|
_group = "XORP"
|
||||||
_depends = ()
|
_depends = ()
|
||||||
|
|
@ -31,10 +26,11 @@ class XorpRtrmgr(CoreService):
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def generateconfig(cls, node, filename, services):
|
def generateconfig(cls, node, filename, services):
|
||||||
''' Returns config.boot configuration file text. Other services that
|
"""
|
||||||
|
Returns config.boot configuration file text. Other services that
|
||||||
depend on this will have generatexorpconfig() hooks that are
|
depend on this will have generatexorpconfig() hooks that are
|
||||||
invoked here. Filename currently ignored.
|
invoked here. Filename currently ignored.
|
||||||
'''
|
"""
|
||||||
cfg = "interfaces {\n"
|
cfg = "interfaces {\n"
|
||||||
for ifc in node.netifs():
|
for ifc in node.netifs():
|
||||||
cfg += " interface %s {\n" % ifc.name
|
cfg += " interface %s {\n" % ifc.name
|
||||||
|
|
@ -50,17 +46,16 @@ class XorpRtrmgr(CoreService):
|
||||||
s._depends.index(cls._name)
|
s._depends.index(cls._name)
|
||||||
cfg += s.generatexorpconfig(node)
|
cfg += s.generatexorpconfig(node)
|
||||||
except ValueError:
|
except ValueError:
|
||||||
pass
|
logger.exception("error getting value from service: %s", cls._name)
|
||||||
|
|
||||||
return cfg
|
return cfg
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def addrstr(x):
|
def addrstr(x):
|
||||||
''' helper for mapping IP addresses to XORP config statements
|
"""
|
||||||
'''
|
helper for mapping IP addresses to XORP config statements
|
||||||
try:
|
"""
|
||||||
(addr, plen) = x.split("/")
|
addr, plen = x.split("/")
|
||||||
except Exception:
|
|
||||||
raise ValueError, "invalid address"
|
|
||||||
cfg = "\t address %s {\n" % addr
|
cfg = "\t address %s {\n" % addr
|
||||||
cfg += "\t\tprefix-length: %s\n" % plen
|
cfg += "\t\tprefix-length: %s\n" % plen
|
||||||
cfg += "\t }\n"
|
cfg += "\t }\n"
|
||||||
|
|
@ -68,19 +63,20 @@ class XorpRtrmgr(CoreService):
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def lladdrstr(ifc):
|
def lladdrstr(ifc):
|
||||||
''' helper for adding link-local address entries (required by OSPFv3)
|
"""
|
||||||
'''
|
helper for adding link-local address entries (required by OSPFv3)
|
||||||
|
"""
|
||||||
cfg = "\t address %s {\n" % ifc.hwaddr.tolinklocal()
|
cfg = "\t address %s {\n" % ifc.hwaddr.tolinklocal()
|
||||||
cfg += "\t\tprefix-length: 64\n"
|
cfg += "\t\tprefix-length: 64\n"
|
||||||
cfg += "\t }\n"
|
cfg += "\t }\n"
|
||||||
return cfg
|
return cfg
|
||||||
|
|
||||||
addservice(XorpRtrmgr)
|
|
||||||
|
|
||||||
class XorpService(CoreService):
|
class XorpService(CoreService):
|
||||||
''' Parent class for XORP services. Defines properties and methods
|
"""
|
||||||
|
Parent class for XORP services. Defines properties and methods
|
||||||
common to XORP's routing daemons.
|
common to XORP's routing daemons.
|
||||||
'''
|
"""
|
||||||
_name = "XorpDaemon"
|
_name = "XorpDaemon"
|
||||||
_group = "XORP"
|
_group = "XORP"
|
||||||
_depends = ("xorp_rtrmgr",)
|
_depends = ("xorp_rtrmgr",)
|
||||||
|
|
@ -93,8 +89,9 @@ class XorpService(CoreService):
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def fea(forwarding):
|
def fea(forwarding):
|
||||||
''' Helper to add a forwarding engine entry to the config file.
|
"""
|
||||||
'''
|
Helper to add a forwarding engine entry to the config file.
|
||||||
|
"""
|
||||||
cfg = "fea {\n"
|
cfg = "fea {\n"
|
||||||
cfg += " %s {\n" % forwarding
|
cfg += " %s {\n" % forwarding
|
||||||
cfg += "\tdisable:false\n"
|
cfg += "\tdisable:false\n"
|
||||||
|
|
@ -104,11 +101,12 @@ class XorpService(CoreService):
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def mfea(forwarding, ifcs):
|
def mfea(forwarding, ifcs):
|
||||||
''' Helper to add a multicast forwarding engine entry to the config file.
|
"""
|
||||||
'''
|
Helper to add a multicast forwarding engine entry to the config file.
|
||||||
|
"""
|
||||||
names = []
|
names = []
|
||||||
for ifc in ifcs:
|
for ifc in ifcs:
|
||||||
if hasattr(ifc, 'control') and ifc.control == True:
|
if hasattr(ifc, 'control') and ifc.control is True:
|
||||||
continue
|
continue
|
||||||
names.append(ifc.name)
|
names.append(ifc.name)
|
||||||
names.append("register_vif")
|
names.append("register_vif")
|
||||||
|
|
@ -125,11 +123,11 @@ class XorpService(CoreService):
|
||||||
cfg += "}\n"
|
cfg += "}\n"
|
||||||
return cfg
|
return cfg
|
||||||
|
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def policyexportconnected():
|
def policyexportconnected():
|
||||||
''' Helper to add a policy statement for exporting connected routes.
|
"""
|
||||||
'''
|
Helper to add a policy statement for exporting connected routes.
|
||||||
|
"""
|
||||||
cfg = "policy {\n"
|
cfg = "policy {\n"
|
||||||
cfg += " policy-statement export-connected {\n"
|
cfg += " policy-statement export-connected {\n"
|
||||||
cfg += "\tterm 100 {\n"
|
cfg += "\tterm 100 {\n"
|
||||||
|
|
@ -143,10 +141,11 @@ class XorpService(CoreService):
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def routerid(node):
|
def routerid(node):
|
||||||
''' Helper to return the first IPv4 address of a node as its router ID.
|
"""
|
||||||
'''
|
Helper to return the first IPv4 address of a node as its router ID.
|
||||||
|
"""
|
||||||
for ifc in node.netifs():
|
for ifc in node.netifs():
|
||||||
if hasattr(ifc, 'control') and ifc.control == True:
|
if hasattr(ifc, 'control') and ifc.control is True:
|
||||||
continue
|
continue
|
||||||
for a in ifc.addrlist:
|
for a in ifc.addrlist:
|
||||||
if a.find(".") >= 0:
|
if a.find(".") >= 0:
|
||||||
|
|
@ -162,11 +161,13 @@ class XorpService(CoreService):
|
||||||
def generatexorpconfig(cls, node):
|
def generatexorpconfig(cls, node):
|
||||||
return ""
|
return ""
|
||||||
|
|
||||||
|
|
||||||
class XorpOspfv2(XorpService):
|
class XorpOspfv2(XorpService):
|
||||||
''' The OSPFv2 service provides IPv4 routing for wired networks. It does
|
"""
|
||||||
|
The OSPFv2 service provides IPv4 routing for wired networks. It does
|
||||||
not build its own configuration file but has hooks for adding to the
|
not build its own configuration file but has hooks for adding to the
|
||||||
unified XORP configuration file.
|
unified XORP configuration file.
|
||||||
'''
|
"""
|
||||||
_name = "XORP_OSPFv2"
|
_name = "XORP_OSPFv2"
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
|
|
@ -178,7 +179,7 @@ class XorpOspfv2(XorpService):
|
||||||
cfg += "\trouter-id: %s\n" % rtrid
|
cfg += "\trouter-id: %s\n" % rtrid
|
||||||
cfg += "\tarea 0.0.0.0 {\n"
|
cfg += "\tarea 0.0.0.0 {\n"
|
||||||
for ifc in node.netifs():
|
for ifc in node.netifs():
|
||||||
if hasattr(ifc, 'control') and ifc.control == True:
|
if hasattr(ifc, 'control') and ifc.control is True:
|
||||||
continue
|
continue
|
||||||
cfg += "\t interface %s {\n" % ifc.name
|
cfg += "\t interface %s {\n" % ifc.name
|
||||||
cfg += "\t\tvif %s {\n" % ifc.name
|
cfg += "\t\tvif %s {\n" % ifc.name
|
||||||
|
|
@ -195,13 +196,13 @@ class XorpOspfv2(XorpService):
|
||||||
cfg += "}\n"
|
cfg += "}\n"
|
||||||
return cfg
|
return cfg
|
||||||
|
|
||||||
addservice(XorpOspfv2)
|
|
||||||
|
|
||||||
class XorpOspfv3(XorpService):
|
class XorpOspfv3(XorpService):
|
||||||
''' The OSPFv3 service provides IPv6 routing. It does
|
"""
|
||||||
|
The OSPFv3 service provides IPv6 routing. It does
|
||||||
not build its own configuration file but has hooks for adding to the
|
not build its own configuration file but has hooks for adding to the
|
||||||
unified XORP configuration file.
|
unified XORP configuration file.
|
||||||
'''
|
"""
|
||||||
_name = "XORP_OSPFv3"
|
_name = "XORP_OSPFv3"
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
|
|
@ -213,7 +214,7 @@ class XorpOspfv3(XorpService):
|
||||||
cfg += "\trouter-id: %s\n" % rtrid
|
cfg += "\trouter-id: %s\n" % rtrid
|
||||||
cfg += "\tarea 0.0.0.0 {\n"
|
cfg += "\tarea 0.0.0.0 {\n"
|
||||||
for ifc in node.netifs():
|
for ifc in node.netifs():
|
||||||
if hasattr(ifc, 'control') and ifc.control == True:
|
if hasattr(ifc, 'control') and ifc.control is True:
|
||||||
continue
|
continue
|
||||||
cfg += "\t interface %s {\n" % ifc.name
|
cfg += "\t interface %s {\n" % ifc.name
|
||||||
cfg += "\t\tvif %s {\n" % ifc.name
|
cfg += "\t\tvif %s {\n" % ifc.name
|
||||||
|
|
@ -224,11 +225,11 @@ class XorpOspfv3(XorpService):
|
||||||
cfg += "}\n"
|
cfg += "}\n"
|
||||||
return cfg
|
return cfg
|
||||||
|
|
||||||
addservice(XorpOspfv3)
|
|
||||||
|
|
||||||
class XorpBgp(XorpService):
|
class XorpBgp(XorpService):
|
||||||
''' IPv4 inter-domain routing. AS numbers and peers must be customized.
|
"""
|
||||||
'''
|
IPv4 inter-domain routing. AS numbers and peers must be customized.
|
||||||
|
"""
|
||||||
_name = "XORP_BGP"
|
_name = "XORP_BGP"
|
||||||
_custom_needed = True
|
_custom_needed = True
|
||||||
|
|
||||||
|
|
@ -253,11 +254,12 @@ class XorpBgp(XorpService):
|
||||||
cfg += "}\n"
|
cfg += "}\n"
|
||||||
return cfg
|
return cfg
|
||||||
|
|
||||||
addservice(XorpBgp)
|
|
||||||
|
|
||||||
class XorpRip(XorpService):
|
class XorpRip(XorpService):
|
||||||
''' RIP IPv4 unicast routing.
|
"""
|
||||||
'''
|
RIP IPv4 unicast routing.
|
||||||
|
"""
|
||||||
|
|
||||||
_name = "XORP_RIP"
|
_name = "XORP_RIP"
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
|
|
@ -268,7 +270,7 @@ class XorpRip(XorpService):
|
||||||
cfg += " rip {\n"
|
cfg += " rip {\n"
|
||||||
cfg += "\texport: \"export-connected\"\n"
|
cfg += "\texport: \"export-connected\"\n"
|
||||||
for ifc in node.netifs():
|
for ifc in node.netifs():
|
||||||
if hasattr(ifc, 'control') and ifc.control == True:
|
if hasattr(ifc, 'control') and ifc.control is True:
|
||||||
continue
|
continue
|
||||||
cfg += "\tinterface %s {\n" % ifc.name
|
cfg += "\tinterface %s {\n" % ifc.name
|
||||||
cfg += "\t vif %s {\n" % ifc.name
|
cfg += "\t vif %s {\n" % ifc.name
|
||||||
|
|
@ -285,11 +287,11 @@ class XorpRip(XorpService):
|
||||||
cfg += "}\n"
|
cfg += "}\n"
|
||||||
return cfg
|
return cfg
|
||||||
|
|
||||||
addservice(XorpRip)
|
|
||||||
|
|
||||||
class XorpRipng(XorpService):
|
class XorpRipng(XorpService):
|
||||||
''' RIP NG IPv6 unicast routing.
|
"""
|
||||||
'''
|
RIP NG IPv6 unicast routing.
|
||||||
|
"""
|
||||||
_name = "XORP_RIPNG"
|
_name = "XORP_RIPNG"
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
|
|
@ -300,7 +302,7 @@ class XorpRipng(XorpService):
|
||||||
cfg += " ripng {\n"
|
cfg += " ripng {\n"
|
||||||
cfg += "\texport: \"export-connected\"\n"
|
cfg += "\texport: \"export-connected\"\n"
|
||||||
for ifc in node.netifs():
|
for ifc in node.netifs():
|
||||||
if hasattr(ifc, 'control') and ifc.control == True:
|
if hasattr(ifc, 'control') and ifc.control is True:
|
||||||
continue
|
continue
|
||||||
cfg += "\tinterface %s {\n" % ifc.name
|
cfg += "\tinterface %s {\n" % ifc.name
|
||||||
cfg += "\t vif %s {\n" % ifc.name
|
cfg += "\t vif %s {\n" % ifc.name
|
||||||
|
|
@ -320,11 +322,11 @@ class XorpRipng(XorpService):
|
||||||
cfg += "}\n"
|
cfg += "}\n"
|
||||||
return cfg
|
return cfg
|
||||||
|
|
||||||
addservice(XorpRipng)
|
|
||||||
|
|
||||||
class XorpPimSm4(XorpService):
|
class XorpPimSm4(XorpService):
|
||||||
''' PIM Sparse Mode IPv4 multicast routing.
|
"""
|
||||||
'''
|
PIM Sparse Mode IPv4 multicast routing.
|
||||||
|
"""
|
||||||
_name = "XORP_PIMSM4"
|
_name = "XORP_PIMSM4"
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
|
|
@ -335,7 +337,7 @@ class XorpPimSm4(XorpService):
|
||||||
cfg += " igmp {\n"
|
cfg += " igmp {\n"
|
||||||
names = []
|
names = []
|
||||||
for ifc in node.netifs():
|
for ifc in node.netifs():
|
||||||
if hasattr(ifc, 'control') and ifc.control == True:
|
if hasattr(ifc, 'control') and ifc.control is True:
|
||||||
continue
|
continue
|
||||||
names.append(ifc.name)
|
names.append(ifc.name)
|
||||||
cfg += "\tinterface %s {\n" % ifc.name
|
cfg += "\tinterface %s {\n" % ifc.name
|
||||||
|
|
@ -379,11 +381,11 @@ class XorpPimSm4(XorpService):
|
||||||
cfg += "}\n"
|
cfg += "}\n"
|
||||||
return cfg
|
return cfg
|
||||||
|
|
||||||
addservice(XorpPimSm4)
|
|
||||||
|
|
||||||
class XorpPimSm6(XorpService):
|
class XorpPimSm6(XorpService):
|
||||||
''' PIM Sparse Mode IPv6 multicast routing.
|
"""
|
||||||
'''
|
PIM Sparse Mode IPv6 multicast routing.
|
||||||
|
"""
|
||||||
_name = "XORP_PIMSM6"
|
_name = "XORP_PIMSM6"
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
|
|
@ -394,7 +396,7 @@ class XorpPimSm6(XorpService):
|
||||||
cfg += " mld {\n"
|
cfg += " mld {\n"
|
||||||
names = []
|
names = []
|
||||||
for ifc in node.netifs():
|
for ifc in node.netifs():
|
||||||
if hasattr(ifc, 'control') and ifc.control == True:
|
if hasattr(ifc, 'control') and ifc.control is True:
|
||||||
continue
|
continue
|
||||||
names.append(ifc.name)
|
names.append(ifc.name)
|
||||||
cfg += "\tinterface %s {\n" % ifc.name
|
cfg += "\tinterface %s {\n" % ifc.name
|
||||||
|
|
@ -438,11 +440,11 @@ class XorpPimSm6(XorpService):
|
||||||
cfg += "}\n"
|
cfg += "}\n"
|
||||||
return cfg
|
return cfg
|
||||||
|
|
||||||
addservice(XorpPimSm6)
|
|
||||||
|
|
||||||
class XorpOlsr(XorpService):
|
class XorpOlsr(XorpService):
|
||||||
''' OLSR IPv4 unicast MANET routing.
|
"""
|
||||||
'''
|
OLSR IPv4 unicast MANET routing.
|
||||||
|
"""
|
||||||
_name = "XORP_OLSR"
|
_name = "XORP_OLSR"
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
|
|
@ -453,7 +455,7 @@ class XorpOlsr(XorpService):
|
||||||
cfg += " olsr4 {\n"
|
cfg += " olsr4 {\n"
|
||||||
cfg += "\tmain-address: %s\n" % rtrid
|
cfg += "\tmain-address: %s\n" % rtrid
|
||||||
for ifc in node.netifs():
|
for ifc in node.netifs():
|
||||||
if hasattr(ifc, 'control') and ifc.control == True:
|
if hasattr(ifc, 'control') and ifc.control is True:
|
||||||
continue
|
continue
|
||||||
cfg += "\tinterface %s {\n" % ifc.name
|
cfg += "\tinterface %s {\n" % ifc.name
|
||||||
cfg += "\t vif %s {\n" % ifc.name
|
cfg += "\t vif %s {\n" % ifc.name
|
||||||
|
|
@ -469,4 +471,14 @@ class XorpOlsr(XorpService):
|
||||||
cfg += "}\n"
|
cfg += "}\n"
|
||||||
return cfg
|
return cfg
|
||||||
|
|
||||||
addservice(XorpOlsr)
|
|
||||||
|
def load_services():
|
||||||
|
ServiceManager.add(XorpRtrmgr)
|
||||||
|
ServiceManager.add(XorpOspfv2)
|
||||||
|
ServiceManager.add(XorpOspfv3)
|
||||||
|
ServiceManager.add(XorpBgp)
|
||||||
|
ServiceManager.add(XorpRip)
|
||||||
|
ServiceManager.add(XorpRipng)
|
||||||
|
ServiceManager.add(XorpPimSm4)
|
||||||
|
ServiceManager.add(XorpPimSm6)
|
||||||
|
ServiceManager.add(XorpOlsr)
|
||||||
|
|
|
||||||
File diff suppressed because it is too large
Load diff
|
|
@ -1,51 +1,43 @@
|
||||||
#
|
"""
|
||||||
# CORE
|
|
||||||
# Copyright (c)2011-2012 the Boeing Company.
|
|
||||||
# See the LICENSE file included in this distribution.
|
|
||||||
#
|
|
||||||
'''
|
|
||||||
xen.py: implementation of the XenNode and XenVEth classes that support
|
xen.py: implementation of the XenNode and XenVEth classes that support
|
||||||
generating Xen domUs based on an ISO image and persistent configuration area
|
generating Xen domUs based on an ISO image and persistent configuration area
|
||||||
'''
|
"""
|
||||||
|
|
||||||
from core.netns.vnet import *
|
import base64
|
||||||
|
import os
|
||||||
|
import shutil
|
||||||
|
import string
|
||||||
|
import subprocess
|
||||||
|
import sys
|
||||||
|
import threading
|
||||||
|
|
||||||
|
import crypt
|
||||||
|
|
||||||
|
from core import constants
|
||||||
|
from core.coreobj import PyCoreNetIf
|
||||||
|
from core.coreobj import PyCoreNode
|
||||||
|
from core.enumerations import NodeTypes
|
||||||
|
from core.misc import log
|
||||||
|
from core.misc import nodeutils
|
||||||
|
from core.misc import utils
|
||||||
from core.netns.vnode import LxcNode
|
from core.netns.vnode import LxcNode
|
||||||
from core.coreobj import PyCoreObj, PyCoreNode, PyCoreNetIf
|
|
||||||
from core.misc.ipaddr import *
|
logger = log.get_logger(__name__)
|
||||||
from core.misc.utils import *
|
|
||||||
from core.constants import *
|
|
||||||
from core.api import coreapi
|
|
||||||
from core.netns.vif import TunTap
|
|
||||||
from core.emane.nodes import EmaneNode
|
|
||||||
|
|
||||||
try:
|
try:
|
||||||
import parted
|
import parted
|
||||||
except ImportError, e:
|
except ImportError:
|
||||||
#print "Failed to load parted Python module required by Xen support."
|
logger.error("failed to import parted for xen nodes")
|
||||||
#print "Error was:", e
|
|
||||||
raise ImportError
|
|
||||||
|
|
||||||
import base64
|
|
||||||
import crypt
|
|
||||||
import subprocess
|
|
||||||
try:
|
try:
|
||||||
import fsimage
|
import fsimage
|
||||||
except ImportError, e:
|
except ImportError:
|
||||||
# fix for fsimage under Ubuntu
|
# fix for fsimage under Ubuntu
|
||||||
sys.path.append("/usr/lib/xen-default/lib/python")
|
sys.path.append("/usr/lib/xen-default/lib/python")
|
||||||
try:
|
try:
|
||||||
import fsimage
|
import fsimage
|
||||||
except ImportError, e:
|
except ImportError:
|
||||||
#print "Failed to load fsimage Python module required by Xen support."
|
logger.error("failed to import fsimage for xen nodes")
|
||||||
#print "Error was:", e
|
|
||||||
raise ImportError
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
import os
|
|
||||||
import time
|
|
||||||
import shutil
|
|
||||||
import string
|
|
||||||
|
|
||||||
# XXX move these out to config file
|
# XXX move these out to config file
|
||||||
AWK_PATH = "/bin/awk"
|
AWK_PATH = "/bin/awk"
|
||||||
|
|
@ -60,6 +52,7 @@ SED_PATH = "/bin/sed"
|
||||||
XM_PATH = "/usr/sbin/xm"
|
XM_PATH = "/usr/sbin/xm"
|
||||||
UDEVADM_PATH = "/sbin/udevadm"
|
UDEVADM_PATH = "/sbin/udevadm"
|
||||||
|
|
||||||
|
|
||||||
class XenVEth(PyCoreNetIf):
|
class XenVEth(PyCoreNetIf):
|
||||||
def __init__(self, node, name, localname, mtu=1500, net=None,
|
def __init__(self, node, name, localname, mtu=1500, net=None,
|
||||||
start=True, hwaddr=None):
|
start=True, hwaddr=None):
|
||||||
|
|
@ -76,8 +69,8 @@ class XenVEth(PyCoreNetIf):
|
||||||
'vifname=%s' % self.localname, 'script=vif-core']
|
'vifname=%s' % self.localname, 'script=vif-core']
|
||||||
if self.hwaddr is not None:
|
if self.hwaddr is not None:
|
||||||
cmd.append('mac=%s' % self.hwaddr)
|
cmd.append('mac=%s' % self.hwaddr)
|
||||||
check_call(cmd)
|
subprocess.check_call(cmd)
|
||||||
check_call([IP_BIN, "link", "set", self.localname, "up"])
|
subprocess.check_call([constants.IP_BIN, "link", "set", self.localname, "up"])
|
||||||
self.up = True
|
self.up = True
|
||||||
|
|
||||||
def shutdown(self):
|
def shutdown(self):
|
||||||
|
|
@ -93,7 +86,7 @@ class XenVEth(PyCoreNetIf):
|
||||||
|
|
||||||
|
|
||||||
class XenNode(PyCoreNode):
|
class XenNode(PyCoreNode):
|
||||||
apitype = coreapi.CORE_NODE_XEN
|
apitype = NodeTypes.XEN.value
|
||||||
|
|
||||||
FilesToIgnore = frozenset([
|
FilesToIgnore = frozenset([
|
||||||
# 'ipforward.sh',
|
# 'ipforward.sh',
|
||||||
|
|
@ -116,24 +109,22 @@ class XenNode(PyCoreNode):
|
||||||
])
|
])
|
||||||
|
|
||||||
def RedirCmd_ipforward(self):
|
def RedirCmd_ipforward(self):
|
||||||
sysctlFile = open(os.path.join(self.mountdir, self.etcdir,
|
sysctlFile = open(os.path.join(self.mountdir, self.etcdir, 'sysctl.conf'), 'a')
|
||||||
'sysctl.conf'), 'a')
|
p1 = subprocess.Popen([AWK_PATH, '/^\/sbin\/sysctl -w/ {print $NF}',
|
||||||
p1 = subprocess.Popen([AWK_PATH,
|
os.path.join(self.nodedir, 'core-tmp/ipforward.sh')], stdout=sysctlFile)
|
||||||
'/^\/sbin\/sysctl -w/ {print $NF}',
|
|
||||||
os.path.join(self.nodedir,
|
|
||||||
'core-tmp/ipforward.sh') ],
|
|
||||||
stdout=sysctlFile)
|
|
||||||
p1.wait()
|
p1.wait()
|
||||||
sysctlFile.close()
|
sysctlFile.close()
|
||||||
|
|
||||||
def RedirCmd_zebra(self):
|
def RedirCmd_zebra(self):
|
||||||
check_call([SED_PATH, '-i', '-e', 's/^zebra=no/zebra=yes/',
|
subprocess.check_call([SED_PATH, '-i', '-e', 's/^zebra=no/zebra=yes/',
|
||||||
os.path.join(self.mountdir, self.etcdir, 'quagga/daemons')])
|
os.path.join(self.mountdir, self.etcdir, 'quagga/daemons')])
|
||||||
|
|
||||||
def RedirCmd_ospfd(self):
|
def RedirCmd_ospfd(self):
|
||||||
check_call([SED_PATH, '-i', '-e', 's/^ospfd=no/ospfd=yes/',
|
subprocess.check_call([SED_PATH, '-i', '-e', 's/^ospfd=no/ospfd=yes/',
|
||||||
os.path.join(self.mountdir, self.etcdir, 'quagga/daemons')])
|
os.path.join(self.mountdir, self.etcdir, 'quagga/daemons')])
|
||||||
|
|
||||||
def RedirCmd_ospf6d(self):
|
def RedirCmd_ospf6d(self):
|
||||||
check_call([SED_PATH, '-i', '-e',
|
subprocess.check_call([SED_PATH, '-i', '-e',
|
||||||
's/^ospf6d=no/ospf6d=yes/',
|
's/^ospf6d=no/ospf6d=yes/',
|
||||||
os.path.join(self.mountdir, self.etcdir, 'quagga/daemons')])
|
os.path.join(self.mountdir, self.etcdir, 'quagga/daemons')])
|
||||||
|
|
||||||
|
|
@ -146,13 +137,11 @@ class XenNode(PyCoreNode):
|
||||||
|
|
||||||
# CoreNode: no __init__, take from LxcNode & SimpleLxcNode
|
# CoreNode: no __init__, take from LxcNode & SimpleLxcNode
|
||||||
def __init__(self, session, objid=None, name=None,
|
def __init__(self, session, objid=None, name=None,
|
||||||
nodedir = None, bootsh = "boot.sh", verbose = False,
|
nodedir=None, bootsh="boot.sh", start=True, model=None,
|
||||||
start = True, model = None,
|
|
||||||
vgname=None, ramsize=None, disksize=None,
|
vgname=None, ramsize=None, disksize=None,
|
||||||
isofile=None):
|
isofile=None):
|
||||||
# SimpleLxcNode initialization
|
# SimpleLxcNode initialization
|
||||||
PyCoreNode.__init__(self, session = session, objid = objid, name = name,
|
PyCoreNode.__init__(self, session=session, objid=objid, name=name)
|
||||||
verbose = verbose)
|
|
||||||
self.nodedir = nodedir
|
self.nodedir = nodedir
|
||||||
self.model = model
|
self.model = model
|
||||||
# indicates startup() has been invoked and disk has been initialized
|
# indicates startup() has been invoked and disk has been initialized
|
||||||
|
|
@ -185,31 +174,30 @@ class XenNode(PyCoreNode):
|
||||||
# LxcNode initialization
|
# LxcNode initialization
|
||||||
# self.makenodedir()
|
# self.makenodedir()
|
||||||
if self.nodedir is None:
|
if self.nodedir is None:
|
||||||
self.nodedir = \
|
self.nodedir = os.path.join(session.sessiondir, self.name + ".conf")
|
||||||
os.path.join(session.sessiondir, self.name + ".conf")
|
|
||||||
self.mountdir = self.nodedir + self.getconfigitem('mount_path')
|
self.mountdir = self.nodedir + self.getconfigitem('mount_path')
|
||||||
if not os.path.isdir(self.mountdir):
|
if not os.path.isdir(self.mountdir):
|
||||||
os.makedirs(self.mountdir)
|
os.makedirs(self.mountdir)
|
||||||
self.tmpnodedir = True
|
self.tmpnodedir = True
|
||||||
else:
|
else:
|
||||||
raise Exception, "Xen PVM node requires a temporary nodedir"
|
raise Exception("Xen PVM node requires a temporary nodedir")
|
||||||
self.tmpnodedir = False
|
self.tmpnodedir = False
|
||||||
self.bootsh = bootsh
|
self.bootsh = bootsh
|
||||||
if start:
|
if start:
|
||||||
self.startup()
|
self.startup()
|
||||||
|
|
||||||
def getconfigitem(self, name, default=None):
|
def getconfigitem(self, name, default=None):
|
||||||
''' Configuration items come from the xen.conf file and/or input from
|
"""
|
||||||
|
Configuration items come from the xen.conf file and/or input from
|
||||||
the GUI, and are stored in the session using the XenConfigManager
|
the GUI, and are stored in the session using the XenConfigManager
|
||||||
object. self.model is used to identify particular profiles
|
object. self.model is used to identify particular profiles
|
||||||
associated with a node type in the GUI.
|
associated with a node type in the GUI.
|
||||||
'''
|
"""
|
||||||
return self.session.xen.getconfigitem(name=name, model=self.model,
|
return self.session.xen.getconfigitem(name=name, model=self.model, node=self, value=default)
|
||||||
node=self, value=default)
|
|
||||||
|
|
||||||
# from class LxcNode (also SimpleLxcNode)
|
# from class LxcNode (also SimpleLxcNode)
|
||||||
def startup(self):
|
def startup(self):
|
||||||
self.warn("XEN PVM startup() called: preparing disk for %s" % self.name)
|
logger.warn("XEN PVM startup() called: preparing disk for %s" % self.name)
|
||||||
self.lock.acquire()
|
self.lock.acquire()
|
||||||
try:
|
try:
|
||||||
if self.up:
|
if self.up:
|
||||||
|
|
@ -217,7 +205,7 @@ class XenNode(PyCoreNode):
|
||||||
self.createlogicalvolume()
|
self.createlogicalvolume()
|
||||||
self.createpartitions()
|
self.createpartitions()
|
||||||
persistdev = self.createfilesystems()
|
persistdev = self.createfilesystems()
|
||||||
check_call([MOUNT_BIN, '-t', 'ext4', persistdev, self.mountdir])
|
subprocess.check_call([constants.MOUNT_BIN, '-t', 'ext4', persistdev, self.mountdir])
|
||||||
self.untarpersistent(tarname=self.getconfigitem('persist_tar_iso'),
|
self.untarpersistent(tarname=self.getconfigitem('persist_tar_iso'),
|
||||||
iso=True)
|
iso=True)
|
||||||
self.setrootpassword(pw=self.getconfigitem('root_password'))
|
self.setrootpassword(pw=self.getconfigitem('root_password'))
|
||||||
|
|
@ -230,11 +218,11 @@ class XenNode(PyCoreNode):
|
||||||
|
|
||||||
# from class LxcNode (also SimpleLxcNode)
|
# from class LxcNode (also SimpleLxcNode)
|
||||||
def boot(self):
|
def boot(self):
|
||||||
self.warn("XEN PVM boot() called")
|
logger.warn("XEN PVM boot() called")
|
||||||
|
|
||||||
self.lock.acquire()
|
self.lock.acquire()
|
||||||
if not self.up:
|
if not self.up:
|
||||||
raise Exception, "Can't boot VM without initialized disk"
|
raise Exception("Can't boot VM without initialized disk")
|
||||||
|
|
||||||
if self.booted:
|
if self.booted:
|
||||||
self.lock.release()
|
self.lock.release()
|
||||||
|
|
@ -246,18 +234,17 @@ class XenNode(PyCoreNode):
|
||||||
self.untarpersistent(tarname=tarname, iso=False)
|
self.untarpersistent(tarname=tarname, iso=False)
|
||||||
|
|
||||||
try:
|
try:
|
||||||
check_call([UMOUNT_BIN, self.mountdir])
|
subprocess.check_call([constants.UMOUNT_BIN, self.mountdir])
|
||||||
self.unmount_all(self.mountdir)
|
self.unmount_all(self.mountdir)
|
||||||
check_call([UDEVADM_PATH, 'settle'])
|
subprocess.check_call([UDEVADM_PATH, 'settle'])
|
||||||
check_call([KPARTX_PATH, '-d', self.lvpath])
|
subprocess.check_call([KPARTX_PATH, '-d', self.lvpath])
|
||||||
|
|
||||||
# time.sleep(5)
|
# time.sleep(5)
|
||||||
# time.sleep(1)
|
# time.sleep(1)
|
||||||
|
|
||||||
# unpause VM
|
# unpause VM
|
||||||
if self.verbose:
|
logger.warn("XEN PVM boot() unpause domU %s" % self.vmname)
|
||||||
self.warn("XEN PVM boot() unpause domU %s" % self.vmname)
|
utils.mutecheck_call([XM_PATH, 'unpause', self.vmname])
|
||||||
mutecheck_call([XM_PATH, 'unpause', self.vmname])
|
|
||||||
|
|
||||||
self.booted = True
|
self.booted = True
|
||||||
finally:
|
finally:
|
||||||
|
|
@ -268,7 +255,7 @@ class XenNode(PyCoreNode):
|
||||||
|
|
||||||
# from class LxcNode (also SimpleLxcNode)
|
# from class LxcNode (also SimpleLxcNode)
|
||||||
def shutdown(self):
|
def shutdown(self):
|
||||||
self.warn("XEN PVM shutdown() called")
|
logger.warn("XEN PVM shutdown() called")
|
||||||
if not self.up:
|
if not self.up:
|
||||||
return
|
return
|
||||||
self.lock.acquire()
|
self.lock.acquire()
|
||||||
|
|
@ -281,27 +268,25 @@ class XenNode(PyCoreNode):
|
||||||
try:
|
try:
|
||||||
# RJE XXX what to do here
|
# RJE XXX what to do here
|
||||||
if self.booted:
|
if self.booted:
|
||||||
mutecheck_call([XM_PATH, 'destroy', self.vmname])
|
utils.mutecheck_call([XM_PATH, 'destroy', self.vmname])
|
||||||
self.booted = False
|
self.booted = False
|
||||||
except OSError:
|
except (OSError, subprocess.CalledProcessError):
|
||||||
pass
|
|
||||||
except subprocess.CalledProcessError:
|
|
||||||
# ignore this error too, the VM may have exited already
|
# ignore this error too, the VM may have exited already
|
||||||
pass
|
logger.exception("error during shutdown")
|
||||||
|
|
||||||
# discard LVM volume
|
# discard LVM volume
|
||||||
lvmRemoveCount = 0
|
lvmRemoveCount = 0
|
||||||
while os.path.exists(self.lvpath):
|
while os.path.exists(self.lvpath):
|
||||||
try:
|
try:
|
||||||
check_call([UDEVADM_PATH, 'settle'])
|
subprocess.check_call([UDEVADM_PATH, 'settle'])
|
||||||
mutecall([LVCHANGE_PATH, '-an', self.lvpath])
|
utils.mutecall([LVCHANGE_PATH, '-an', self.lvpath])
|
||||||
lvmRemoveCount += 1
|
lvmRemoveCount += 1
|
||||||
mutecall([LVREMOVE_PATH, '-f', self.lvpath])
|
utils.mutecall([LVREMOVE_PATH, '-f', self.lvpath])
|
||||||
except OSError:
|
except OSError:
|
||||||
pass
|
logger.exception("error during shutdown")
|
||||||
if (lvmRemoveCount > 1):
|
|
||||||
self.warn("XEN PVM shutdown() required %d lvremove " \
|
if lvmRemoveCount > 1:
|
||||||
"executions." % lvmRemoveCount)
|
logger.warn("XEN PVM shutdown() required %d lvremove executions." % lvmRemoveCount)
|
||||||
|
|
||||||
self._netif.clear()
|
self._netif.clear()
|
||||||
del self.session
|
del self.session
|
||||||
|
|
@ -313,78 +298,82 @@ class XenNode(PyCoreNode):
|
||||||
self.lock.release()
|
self.lock.release()
|
||||||
|
|
||||||
def createlogicalvolume(self):
|
def createlogicalvolume(self):
|
||||||
''' Create a logical volume for this Xen domU. Called from startup().
|
"""
|
||||||
'''
|
Create a logical volume for this Xen domU. Called from startup().
|
||||||
|
"""
|
||||||
if os.path.exists(self.lvpath):
|
if os.path.exists(self.lvpath):
|
||||||
raise Exception, "LVM volume already exists"
|
raise Exception, "LVM volume already exists"
|
||||||
mutecheck_call([LVCREATE_PATH, '--size', self.disksize,
|
utils.mutecheck_call([LVCREATE_PATH, '--size', self.disksize,
|
||||||
'--name', self.lvname, self.vgname])
|
'--name', self.lvname, self.vgname])
|
||||||
|
|
||||||
def createpartitions(self):
|
def createpartitions(self):
|
||||||
''' Partition the LVM volume into persistent and swap partitions
|
"""
|
||||||
|
Partition the LVM volume into persistent and swap partitions
|
||||||
using the parted module.
|
using the parted module.
|
||||||
'''
|
"""
|
||||||
dev = parted.Device(path=self.lvpath)
|
dev = parted.Device(path=self.lvpath)
|
||||||
dev.removeFromCache()
|
dev.removeFromCache()
|
||||||
disk = parted.freshDisk(dev, 'msdos')
|
disk = parted.freshDisk(dev, 'msdos')
|
||||||
constraint = parted.Constraint(device=dev)
|
constraint = parted.Constraint(device=dev)
|
||||||
persist_size = int(0.75 * constraint.maxSize);
|
persist_size = int(0.75 * constraint.maxSize)
|
||||||
self.createpartition(device=dev, disk=disk, start=1,
|
self.createpartition(device=dev, disk=disk, start=1,
|
||||||
end=(persist_size - 1) , type="ext4")
|
end=persist_size - 1, type="ext4")
|
||||||
self.createpartition(device=dev, disk=disk, start=persist_size,
|
self.createpartition(device=dev, disk=disk, start=persist_size,
|
||||||
end=(constraint.maxSize - 1) , type="linux-swap(v1)")
|
end=constraint.maxSize - 1, type="linux-swap(v1)")
|
||||||
disk.commit()
|
disk.commit()
|
||||||
|
|
||||||
def createpartition(self, device, disk, start, end, type):
|
def createpartition(self, device, disk, start, end, type):
|
||||||
''' Create a single partition of the specified type and size and add
|
"""
|
||||||
|
Create a single partition of the specified type and size and add
|
||||||
it to the disk object, using the parted module.
|
it to the disk object, using the parted module.
|
||||||
'''
|
"""
|
||||||
geo = parted.Geometry(device=device, start=start, end=end)
|
geo = parted.Geometry(device=device, start=start, end=end)
|
||||||
fs = parted.FileSystem(type=type, geometry=geo)
|
fs = parted.FileSystem(type=type, geometry=geo)
|
||||||
part = parted.Partition(disk=disk, fs=fs, type=parted.PARTITION_NORMAL,
|
part = parted.Partition(disk=disk, fs=fs, type=parted.PARTITION_NORMAL, geometry=geo)
|
||||||
geometry=geo)
|
|
||||||
constraint = parted.Constraint(exactGeom=geo)
|
constraint = parted.Constraint(exactGeom=geo)
|
||||||
disk.addPartition(partition=part, constraint=constraint)
|
disk.addPartition(partition=part, constraint=constraint)
|
||||||
|
|
||||||
def createfilesystems(self):
|
def createfilesystems(self):
|
||||||
''' Make an ext4 filesystem and swap space. Return the device name for
|
"""
|
||||||
|
Make an ext4 filesystem and swap space. Return the device name for
|
||||||
the persistent partition so we can mount it.
|
the persistent partition so we can mount it.
|
||||||
'''
|
"""
|
||||||
output = subprocess.Popen([KPARTX_PATH, '-l', self.lvpath],
|
output = subprocess.Popen([KPARTX_PATH, '-l', self.lvpath],
|
||||||
stdout=subprocess.PIPE).communicate()[0]
|
stdout=subprocess.PIPE).communicate()[0]
|
||||||
lines = output.splitlines()
|
lines = output.splitlines()
|
||||||
persistdev = '/dev/mapper/' + lines[0].strip().split(' ')[0].strip()
|
persistdev = '/dev/mapper/' + lines[0].strip().split(' ')[0].strip()
|
||||||
swapdev = '/dev/mapper/' + lines[1].strip().split(' ')[0].strip()
|
swapdev = '/dev/mapper/' + lines[1].strip().split(' ')[0].strip()
|
||||||
check_call([KPARTX_PATH, '-a', self.lvpath])
|
subprocess.check_call([KPARTX_PATH, '-a', self.lvpath])
|
||||||
mutecheck_call([MKFSEXT4_PATH, '-L', 'persist', persistdev])
|
utils.mutecheck_call([MKFSEXT4_PATH, '-L', 'persist', persistdev])
|
||||||
mutecheck_call([MKSWAP_PATH, '-f', '-L', 'swap', swapdev])
|
utils.mutecheck_call([MKSWAP_PATH, '-f', '-L', 'swap', swapdev])
|
||||||
return persistdev
|
return persistdev
|
||||||
|
|
||||||
def untarpersistent(self, tarname, iso):
|
def untarpersistent(self, tarname, iso):
|
||||||
''' Unpack a persistent template tar file to the mounted mount dir.
|
"""
|
||||||
|
Unpack a persistent template tar file to the mounted mount dir.
|
||||||
Uses fsimage library to read from an ISO file.
|
Uses fsimage library to read from an ISO file.
|
||||||
'''
|
"""
|
||||||
tarname = tarname.replace('%h', self.name) # filename may use hostname
|
tarname = tarname.replace('%h', self.name) # filename may use hostname
|
||||||
if iso:
|
if iso:
|
||||||
try:
|
try:
|
||||||
fs = fsimage.open(self.isofile, 0)
|
fs = fsimage.open(self.isofile, 0)
|
||||||
except IOError, e:
|
except IOError:
|
||||||
self.warn("Failed to open ISO file: %s (%s)" % (self.isofile,e))
|
logger.exception("Failed to open ISO file: %s", self.isofile)
|
||||||
return
|
return
|
||||||
try:
|
try:
|
||||||
tardata = fs.open_file(tarname).read();
|
tardata = fs.open_file(tarname).read()
|
||||||
except IOError, e:
|
except IOError:
|
||||||
self.warn("Failed to open tar file: %s (%s)" % (tarname, e))
|
logger.exception("Failed to open tar file: %s", tarname)
|
||||||
return
|
return
|
||||||
finally:
|
finally:
|
||||||
del fs;
|
del fs
|
||||||
else:
|
else:
|
||||||
try:
|
try:
|
||||||
f = open(tarname)
|
f = open(tarname)
|
||||||
tardata = f.read()
|
tardata = f.read()
|
||||||
f.close()
|
f.close()
|
||||||
except IOError, e:
|
except IOError:
|
||||||
self.warn("Failed to open tar file: %s (%s)" % (tarname, e))
|
logger.exception("Failed to open tar file: %s", tarname)
|
||||||
return
|
return
|
||||||
p = subprocess.Popen([TAR_PATH, '-C', self.mountdir, '--numeric-owner',
|
p = subprocess.Popen([TAR_PATH, '-C', self.mountdir, '--numeric-owner',
|
||||||
'-xf', '-'], stdin=subprocess.PIPE)
|
'-xf', '-'], stdin=subprocess.PIPE)
|
||||||
|
|
@ -392,38 +381,41 @@ class XenNode(PyCoreNode):
|
||||||
p.wait()
|
p.wait()
|
||||||
|
|
||||||
def setrootpassword(self, pw):
|
def setrootpassword(self, pw):
|
||||||
''' Set the root password by updating the shadow password file that
|
"""
|
||||||
|
Set the root password by updating the shadow password file that
|
||||||
is on the filesystem mounted in the temporary area.
|
is on the filesystem mounted in the temporary area.
|
||||||
'''
|
"""
|
||||||
saltedpw = crypt.crypt(pw, '$6$' + base64.b64encode(os.urandom(12)))
|
saltedpw = crypt.crypt(pw, '$6$' + base64.b64encode(os.urandom(12)))
|
||||||
check_call([SED_PATH, '-i', '-e',
|
subprocess.check_call([SED_PATH, '-i', '-e',
|
||||||
'/^root:/s_^root:\([^:]*\):_root:' + saltedpw + ':_',
|
'/^root:/s_^root:\([^:]*\):_root:' + saltedpw + ':_',
|
||||||
os.path.join(self.mountdir, self.etcdir, 'shadow')])
|
os.path.join(self.mountdir, self.etcdir, 'shadow')])
|
||||||
|
|
||||||
def sethostname(self, old, new):
|
def sethostname(self, old, new):
|
||||||
''' Set the hostname by updating the hostname and hosts files that
|
"""
|
||||||
|
Set the hostname by updating the hostname and hosts files that
|
||||||
reside on the filesystem mounted in the temporary area.
|
reside on the filesystem mounted in the temporary area.
|
||||||
'''
|
"""
|
||||||
check_call([SED_PATH, '-i', '-e', 's/%s/%s/' % (old, new),
|
subprocess.check_call([SED_PATH, '-i', '-e', 's/%s/%s/' % (old, new),
|
||||||
os.path.join(self.mountdir, self.etcdir, 'hostname')])
|
os.path.join(self.mountdir, self.etcdir, 'hostname')])
|
||||||
check_call([SED_PATH, '-i', '-e', 's/%s/%s/' % (old, new),
|
subprocess.check_call([SED_PATH, '-i', '-e', 's/%s/%s/' % (old, new),
|
||||||
os.path.join(self.mountdir, self.etcdir, 'hosts')])
|
os.path.join(self.mountdir, self.etcdir, 'hosts')])
|
||||||
|
|
||||||
def setupssh(self, keypath):
|
def setupssh(self, keypath):
|
||||||
''' Configure SSH access by installing host keys and a system-wide
|
"""
|
||||||
|
Configure SSH access by installing host keys and a system-wide
|
||||||
authorized_keys file.
|
authorized_keys file.
|
||||||
'''
|
"""
|
||||||
sshdcfg = os.path.join(self.mountdir, self.etcdir, 'ssh/sshd_config')
|
sshdcfg = os.path.join(self.mountdir, self.etcdir, 'ssh/sshd_config')
|
||||||
check_call([SED_PATH, '-i', '-e',
|
subprocess.check_call([SED_PATH, '-i', '-e',
|
||||||
's/PermitRootLogin no/PermitRootLogin yes/', sshdcfg])
|
's/PermitRootLogin no/PermitRootLogin yes/', sshdcfg])
|
||||||
sshdir = os.path.join(self.getconfigitem('mount_path'), self.etcdir,
|
sshdir = os.path.join(self.getconfigitem('mount_path'), self.etcdir,
|
||||||
'ssh')
|
'ssh')
|
||||||
sshdir = sshdir.replace('/', '\\/') # backslash slashes for use in sed
|
sshdir = sshdir.replace('/', '\\/') # backslash slashes for use in sed
|
||||||
check_call([SED_PATH, '-i', '-e',
|
subprocess.check_call([SED_PATH, '-i', '-e',
|
||||||
's/#AuthorizedKeysFile %h\/.ssh\/authorized_keys/' + \
|
's/#AuthorizedKeysFile %h\/.ssh\/authorized_keys/' + \
|
||||||
'AuthorizedKeysFile ' + sshdir + '\/authorized_keys/',
|
'AuthorizedKeysFile ' + sshdir + '\/authorized_keys/',
|
||||||
sshdcfg])
|
sshdcfg])
|
||||||
for f in ('ssh_host_rsa_key','ssh_host_rsa_key.pub','authorized_keys'):
|
for f in 'ssh_host_rsa_key', 'ssh_host_rsa_key.pub', 'authorized_keys':
|
||||||
src = os.path.join(keypath, f)
|
src = os.path.join(keypath, f)
|
||||||
dst = os.path.join(self.mountdir, self.etcdir, 'ssh', f)
|
dst = os.path.join(self.mountdir, self.etcdir, 'ssh', f)
|
||||||
shutil.copy(src, dst)
|
shutil.copy(src, dst)
|
||||||
|
|
@ -431,10 +423,11 @@ class XenNode(PyCoreNode):
|
||||||
os.chmod(dst, 0600)
|
os.chmod(dst, 0600)
|
||||||
|
|
||||||
def createvm(self):
|
def createvm(self):
|
||||||
''' Instantiate a *paused* domU VM
|
"""
|
||||||
|
Instantiate a *paused* domU VM
|
||||||
Instantiate it now, so we can add network interfaces,
|
Instantiate it now, so we can add network interfaces,
|
||||||
pause it so we can have the filesystem open for configuration.
|
pause it so we can have the filesystem open for configuration.
|
||||||
'''
|
"""
|
||||||
args = [XM_PATH, 'create', os.devnull, '--paused']
|
args = [XM_PATH, 'create', os.devnull, '--paused']
|
||||||
args.extend(['name=' + self.vmname, 'memory=' + str(self.ramsize)])
|
args.extend(['name=' + self.vmname, 'memory=' + str(self.ramsize)])
|
||||||
args.append('disk=tap:aio:' + self.isofile + ',hda,r')
|
args.append('disk=tap:aio:' + self.isofile + ',hda,r')
|
||||||
|
|
@ -445,7 +438,7 @@ class XenNode(PyCoreNode):
|
||||||
for action in ('poweroff', 'reboot', 'suspend', 'crash', 'halt'):
|
for action in ('poweroff', 'reboot', 'suspend', 'crash', 'halt'):
|
||||||
args.append('on_%s=destroy' % action)
|
args.append('on_%s=destroy' % action)
|
||||||
args.append('extra=' + self.getconfigitem('xm_create_extra'))
|
args.append('extra=' + self.getconfigitem('xm_create_extra'))
|
||||||
mutecheck_call(args)
|
utils.mutecheck_call(args)
|
||||||
|
|
||||||
# from class LxcNode
|
# from class LxcNode
|
||||||
def privatedir(self, path):
|
def privatedir(self, path):
|
||||||
|
|
@ -455,8 +448,8 @@ class XenNode(PyCoreNode):
|
||||||
|
|
||||||
# from class LxcNode
|
# from class LxcNode
|
||||||
def opennodefile(self, filename, mode="w"):
|
def opennodefile(self, filename, mode="w"):
|
||||||
self.warn("XEN PVM opennodefile() called")
|
logger.warn("XEN PVM opennodefile() called")
|
||||||
raise Exception, "Can't open VM file with opennodefile()"
|
raise Exception("Can't open VM file with opennodefile()")
|
||||||
|
|
||||||
# from class LxcNode
|
# from class LxcNode
|
||||||
# open a file on a paused Xen node
|
# open a file on a paused Xen node
|
||||||
|
|
@ -481,27 +474,25 @@ class XenNode(PyCoreNode):
|
||||||
|
|
||||||
if filename in self.FilesRedirection:
|
if filename in self.FilesRedirection:
|
||||||
redirFilename = self.FilesRedirection[filename]
|
redirFilename = self.FilesRedirection[filename]
|
||||||
self.warn("XEN PVM nodefile(filename=%s) redirected to %s" % (filename, redirFilename))
|
logger.warn("XEN PVM nodefile(filename=%s) redirected to %s" % (filename, redirFilename))
|
||||||
filename = redirFilename
|
filename = redirFilename
|
||||||
|
|
||||||
self.warn("XEN PVM nodefile(filename=%s) called" % [filename])
|
logger.warn("XEN PVM nodefile(filename=%s) called" % [filename])
|
||||||
self.lock.acquire()
|
self.lock.acquire()
|
||||||
if not self.up:
|
if not self.up:
|
||||||
self.lock.release()
|
self.lock.release()
|
||||||
raise Exception, "Can't access VM file as VM disk isn't ready"
|
raise Exception("Can't access VM file as VM disk isn't ready")
|
||||||
return
|
|
||||||
|
|
||||||
if self.booted:
|
if self.booted:
|
||||||
self.lock.release()
|
self.lock.release()
|
||||||
raise Exception, "Can't access VM file as VM is already running"
|
raise Exception("Can't access VM file as VM is already running")
|
||||||
return
|
|
||||||
|
|
||||||
try:
|
try:
|
||||||
f = self.openpausednodefile(filename, "w")
|
f = self.openpausednodefile(filename, "w")
|
||||||
f.write(contents)
|
f.write(contents)
|
||||||
os.chmod(f.name, mode)
|
os.chmod(f.name, mode)
|
||||||
f.close()
|
f.close()
|
||||||
self.info("created nodefile: '%s'; mode: 0%o" % (f.name, mode))
|
logger.info("created nodefile: '%s'; mode: 0%o" % (f.name, mode))
|
||||||
finally:
|
finally:
|
||||||
self.lock.release()
|
self.lock.release()
|
||||||
|
|
||||||
|
|
@ -519,36 +510,37 @@ class XenNode(PyCoreNode):
|
||||||
self.CmdsRedirection[cmdAsString](self)
|
self.CmdsRedirection[cmdAsString](self)
|
||||||
return 0
|
return 0
|
||||||
|
|
||||||
self.warn("XEN PVM cmd(args=[%s]) called, but not yet implemented" % cmdAsString)
|
logger("XEN PVM cmd(args=[%s]) called, but not yet implemented" % cmdAsString)
|
||||||
return 0
|
return 0
|
||||||
|
|
||||||
def cmdresult(self, args):
|
def cmdresult(self, args):
|
||||||
cmdAsString = string.join(args, ' ')
|
cmdAsString = string.join(args, ' ')
|
||||||
if cmdAsString in self.CmdsToIgnore:
|
if cmdAsString in self.CmdsToIgnore:
|
||||||
# self.warn("XEN PVM cmd(args=[%s]) called and ignored" % cmdAsString)
|
# self.warn("XEN PVM cmd(args=[%s]) called and ignored" % cmdAsString)
|
||||||
return (0, "")
|
return 0, ""
|
||||||
self.warn("XEN PVM cmdresult(args=[%s]) called, but not yet implemented" % cmdAsString)
|
logger.warn("XEN PVM cmdresult(args=[%s]) called, but not yet implemented" % cmdAsString)
|
||||||
return (0, "")
|
return 0, ""
|
||||||
|
|
||||||
def popen(self, args):
|
def popen(self, args):
|
||||||
cmdAsString = string.join(args, ' ')
|
cmdAsString = string.join(args, ' ')
|
||||||
self.warn("XEN PVM popen(args=[%s]) called, but not yet implemented" % cmdAsString)
|
logger.warn("XEN PVM popen(args=[%s]) called, but not yet implemented" % cmdAsString)
|
||||||
return
|
return
|
||||||
|
|
||||||
def icmd(self, args):
|
def icmd(self, args):
|
||||||
cmdAsString = string.join(args, ' ')
|
cmdAsString = string.join(args, ' ')
|
||||||
self.warn("XEN PVM icmd(args=[%s]) called, but not yet implemented" % cmdAsString)
|
logger.warn("XEN PVM icmd(args=[%s]) called, but not yet implemented" % cmdAsString)
|
||||||
return
|
return
|
||||||
|
|
||||||
def term(self, sh="/bin/sh"):
|
def term(self, sh="/bin/sh"):
|
||||||
self.warn("XEN PVM term() called, but not yet implemented")
|
logger.warn("XEN PVM term() called, but not yet implemented")
|
||||||
return
|
return
|
||||||
|
|
||||||
def termcmdstring(self, sh="/bin/sh"):
|
def termcmdstring(self, sh="/bin/sh"):
|
||||||
''' We may add 'sudo' to the command string because the GUI runs as a
|
"""
|
||||||
|
We may add 'sudo' to the command string because the GUI runs as a
|
||||||
normal user. Use SSH if control interface is available, otherwise
|
normal user. Use SSH if control interface is available, otherwise
|
||||||
use Xen console with a keymapping for easy login.
|
use Xen console with a keymapping for easy login.
|
||||||
'''
|
"""
|
||||||
controlifc = None
|
controlifc = None
|
||||||
for ifc in self.netifs():
|
for ifc in self.netifs():
|
||||||
if hasattr(ifc, 'control') and ifc.control == True:
|
if hasattr(ifc, 'control') and ifc.control == True:
|
||||||
|
|
@ -568,25 +560,14 @@ class XenNode(PyCoreNode):
|
||||||
return cmd
|
return cmd
|
||||||
|
|
||||||
def shcmd(self, cmdstr, sh="/bin/sh"):
|
def shcmd(self, cmdstr, sh="/bin/sh"):
|
||||||
self.warn("XEN PVM shcmd(args=[%s]) called, but not yet implemented" % cmdstr)
|
logger("XEN PVM shcmd(args=[%s]) called, but not yet implemented" % cmdstr)
|
||||||
return
|
return
|
||||||
|
|
||||||
# from class SimpleLxcNode
|
|
||||||
def info(self, msg):
|
|
||||||
if self.verbose:
|
|
||||||
print "%s: %s" % (self.name, msg)
|
|
||||||
sys.stdout.flush()
|
|
||||||
|
|
||||||
# from class SimpleLxcNode
|
|
||||||
def warn(self, msg):
|
|
||||||
print >> sys.stderr, "%s: %s" % (self.name, msg)
|
|
||||||
sys.stderr.flush()
|
|
||||||
|
|
||||||
def mount(self, source, target):
|
def mount(self, source, target):
|
||||||
self.warn("XEN PVM Nodes can't bind-mount filesystems")
|
logger.warn("XEN PVM Nodes can't bind-mount filesystems")
|
||||||
|
|
||||||
def umount(self, target):
|
def umount(self, target):
|
||||||
self.warn("XEN PVM Nodes can't bind-mount filesystems")
|
logger.warn("XEN PVM Nodes can't bind-mount filesystems")
|
||||||
|
|
||||||
def newifindex(self):
|
def newifindex(self):
|
||||||
self.lock.acquire()
|
self.lock.acquire()
|
||||||
|
|
@ -606,15 +587,15 @@ class XenNode(PyCoreNode):
|
||||||
return -1
|
return -1
|
||||||
|
|
||||||
def addnetif(self, netif, ifindex):
|
def addnetif(self, netif, ifindex):
|
||||||
self.warn("XEN PVM addnetif() called")
|
logger.warn("XEN PVM addnetif() called")
|
||||||
PyCoreNode.addnetif(self, netif, ifindex)
|
PyCoreNode.addnetif(self, netif, ifindex)
|
||||||
|
|
||||||
def delnetif(self, ifindex):
|
def delnetif(self, ifindex):
|
||||||
self.warn("XEN PVM delnetif() called")
|
logger.warn("XEN PVM delnetif() called")
|
||||||
PyCoreNode.delnetif(self, ifindex)
|
PyCoreNode.delnetif(self, ifindex)
|
||||||
|
|
||||||
def newveth(self, ifindex=None, ifname=None, net=None, hwaddr=None):
|
def newveth(self, ifindex=None, ifname=None, net=None, hwaddr=None):
|
||||||
self.warn("XEN PVM newveth(ifindex=%s, ifname=%s) called" %
|
logger.warn("XEN PVM newveth(ifindex=%s, ifname=%s) called" %
|
||||||
(ifindex, ifname))
|
(ifindex, ifname))
|
||||||
|
|
||||||
self.lock.acquire()
|
self.lock.acquire()
|
||||||
|
|
@ -623,7 +604,7 @@ class XenNode(PyCoreNode):
|
||||||
ifindex = self.newifindex()
|
ifindex = self.newifindex()
|
||||||
if ifname is None:
|
if ifname is None:
|
||||||
ifname = "eth%d" % ifindex
|
ifname = "eth%d" % ifindex
|
||||||
sessionid = self.session.shortsessionid()
|
sessionid = self.session.short_session_id()
|
||||||
name = "n%s.%s.%s" % (self.objid, ifindex, sessionid)
|
name = "n%s.%s.%s" % (self.objid, ifindex, sessionid)
|
||||||
localname = "n%s.%s.%s" % (self.objid, ifname, sessionid)
|
localname = "n%s.%s.%s" % (self.objid, ifname, sessionid)
|
||||||
ifclass = XenVEth
|
ifclass = XenVEth
|
||||||
|
|
@ -642,7 +623,7 @@ class XenNode(PyCoreNode):
|
||||||
self.lock.release()
|
self.lock.release()
|
||||||
|
|
||||||
def newtuntap(self, ifindex=None, ifname=None, net=None):
|
def newtuntap(self, ifindex=None, ifname=None, net=None):
|
||||||
self.warn("XEN PVM newtuntap() called but not implemented")
|
logger.warn("XEN PVM newtuntap() called but not implemented")
|
||||||
|
|
||||||
def sethwaddr(self, ifindex, addr):
|
def sethwaddr(self, ifindex, addr):
|
||||||
self._netif[ifindex].sethwaddr(addr)
|
self._netif[ifindex].sethwaddr(addr)
|
||||||
|
|
@ -662,13 +643,15 @@ class XenNode(PyCoreNode):
|
||||||
try:
|
try:
|
||||||
self._netif[ifindex].deladdr(addr)
|
self._netif[ifindex].deladdr(addr)
|
||||||
except ValueError:
|
except ValueError:
|
||||||
self.warn("trying to delete unknown address: %s" % addr)
|
logger.exception("trying to delete unknown address: %s", addr)
|
||||||
|
|
||||||
if self.up:
|
if self.up:
|
||||||
pass
|
pass
|
||||||
# self.cmd([IP_BIN, "addr", "del", str(addr),
|
# self.cmd([IP_BIN, "addr", "del", str(addr),
|
||||||
# "dev", self.ifname(ifindex)])
|
# "dev", self.ifname(ifindex)])
|
||||||
|
|
||||||
valid_deladdrtype = ("inet", "inet6", "inet6link")
|
valid_deladdrtype = ("inet", "inet6", "inet6link")
|
||||||
|
|
||||||
def delalladdr(self, ifindex, addrtypes=valid_deladdrtype):
|
def delalladdr(self, ifindex, addrtypes=valid_deladdrtype):
|
||||||
addr = self.getaddr(self.ifname(ifindex), rescan=True)
|
addr = self.getaddr(self.ifname(ifindex), rescan=True)
|
||||||
for t in addrtypes:
|
for t in addrtypes:
|
||||||
|
|
@ -687,24 +670,22 @@ class XenNode(PyCoreNode):
|
||||||
|
|
||||||
def newnetif(self, net=None, addrlist=[], hwaddr=None,
|
def newnetif(self, net=None, addrlist=[], hwaddr=None,
|
||||||
ifindex=None, ifname=None):
|
ifindex=None, ifname=None):
|
||||||
self.warn("XEN PVM newnetif(ifindex=%s, ifname=%s) called" %
|
logger.warn("XEN PVM newnetif(ifindex=%s, ifname=%s) called" %
|
||||||
(ifindex, ifname))
|
(ifindex, ifname))
|
||||||
|
|
||||||
self.lock.acquire()
|
self.lock.acquire()
|
||||||
|
|
||||||
if not self.up:
|
if not self.up:
|
||||||
self.lock.release()
|
self.lock.release()
|
||||||
raise Exception, "Can't access add veth as VM disk isn't ready"
|
raise Exception("Can't access add veth as VM disk isn't ready")
|
||||||
return
|
|
||||||
|
|
||||||
if self.booted:
|
if self.booted:
|
||||||
self.lock.release()
|
self.lock.release()
|
||||||
raise Exception, "Can't access add veth as VM is already running"
|
raise Exception("Can't access add veth as VM is already running")
|
||||||
return
|
|
||||||
|
|
||||||
try:
|
try:
|
||||||
if isinstance(net, EmaneNode):
|
if nodeutils.is_node(net, NodeTypes.EMANE):
|
||||||
raise Exception, "Xen PVM doesn't yet support Emane nets"
|
raise Exception("Xen PVM doesn't yet support Emane nets")
|
||||||
|
|
||||||
# ifindex = self.newtuntap(ifindex = ifindex, ifname = ifname,
|
# ifindex = self.newtuntap(ifindex = ifindex, ifname = ifname,
|
||||||
# net = net)
|
# net = net)
|
||||||
|
|
@ -728,16 +709,19 @@ class XenNode(PyCoreNode):
|
||||||
self.etcdir,
|
self.etcdir,
|
||||||
'udev/rules.d/70-persistent-net.rules')
|
'udev/rules.d/70-persistent-net.rules')
|
||||||
f = self.openpausednodefile(rulefile, "a")
|
f = self.openpausednodefile(rulefile, "a")
|
||||||
f.write('\n# Xen PVM virtual interface #%s %s with MAC address %s\n' % (ifindex, self.ifname(ifindex), hwaddr))
|
f.write(
|
||||||
|
'\n# Xen PVM virtual interface #%s %s with MAC address %s\n' % (ifindex, self.ifname(ifindex), hwaddr))
|
||||||
# Using MAC address as we're now loading PVM net driver "early"
|
# Using MAC address as we're now loading PVM net driver "early"
|
||||||
# OLD: Would like to use MAC address, but udev isn't working with paravirtualized NICs. Perhaps the "set hw address" isn't triggering a rescan.
|
# OLD: Would like to use MAC address, but udev isn't working with paravirtualized NICs. Perhaps the "set hw address" isn't triggering a rescan.
|
||||||
f.write('SUBSYSTEM=="net", ACTION=="add", DRIVERS=="?*", ATTR{address}=="%s", KERNEL=="eth*", NAME="%s"\n' % (hwaddr, self.ifname(ifindex)))
|
f.write(
|
||||||
|
'SUBSYSTEM=="net", ACTION=="add", DRIVERS=="?*", ATTR{address}=="%s", KERNEL=="eth*", NAME="%s"\n' % (
|
||||||
|
hwaddr, self.ifname(ifindex)))
|
||||||
# f.write('SUBSYSTEM=="net", ACTION=="add", DRIVERS=="?*", DEVPATH=="/devices/vif-%s/?*", KERNEL=="eth*", NAME="%s"\n' % (ifindex, self.ifname(ifindex)))
|
# f.write('SUBSYSTEM=="net", ACTION=="add", DRIVERS=="?*", DEVPATH=="/devices/vif-%s/?*", KERNEL=="eth*", NAME="%s"\n' % (ifindex, self.ifname(ifindex)))
|
||||||
f.close()
|
f.close()
|
||||||
|
|
||||||
if hwaddr:
|
if hwaddr:
|
||||||
self.sethwaddr(ifindex, hwaddr)
|
self.sethwaddr(ifindex, hwaddr)
|
||||||
for addr in maketuple(addrlist):
|
for addr in utils.maketuple(addrlist):
|
||||||
self.addaddr(ifindex, addr)
|
self.addaddr(ifindex, addr)
|
||||||
# self.ifup(ifindex)
|
# self.ifup(ifindex)
|
||||||
return ifindex
|
return ifindex
|
||||||
|
|
@ -745,7 +729,7 @@ class XenNode(PyCoreNode):
|
||||||
self.lock.release()
|
self.lock.release()
|
||||||
|
|
||||||
def connectnode(self, ifname, othernode, otherifname):
|
def connectnode(self, ifname, othernode, otherifname):
|
||||||
self.warn("XEN PVM connectnode() called")
|
logger.warn("XEN PVM connectnode() called")
|
||||||
|
|
||||||
# tmplen = 8
|
# tmplen = 8
|
||||||
# tmp1 = "tmp." + "".join([random.choice(string.ascii_lowercase)
|
# tmp1 = "tmp." + "".join([random.choice(string.ascii_lowercase)
|
||||||
|
|
@ -768,13 +752,11 @@ class XenNode(PyCoreNode):
|
||||||
self.lock.acquire()
|
self.lock.acquire()
|
||||||
if not self.up:
|
if not self.up:
|
||||||
self.lock.release()
|
self.lock.release()
|
||||||
raise Exception, "Can't access VM file as VM disk isn't ready"
|
raise Exception("Can't access VM file as VM disk isn't ready")
|
||||||
return
|
|
||||||
|
|
||||||
if self.booted:
|
if self.booted:
|
||||||
self.lock.release()
|
self.lock.release()
|
||||||
raise Exception, "Can't access VM file as VM is already running"
|
raise Exception("Can't access VM file as VM is already running")
|
||||||
return
|
|
||||||
|
|
||||||
if filename in self.FilesToIgnore:
|
if filename in self.FilesToIgnore:
|
||||||
# self.warn("XEN PVM addfile(filename=%s) ignored" % [filename])
|
# self.warn("XEN PVM addfile(filename=%s) ignored" % [filename])
|
||||||
|
|
@ -782,7 +764,7 @@ class XenNode(PyCoreNode):
|
||||||
|
|
||||||
if filename in self.FilesRedirection:
|
if filename in self.FilesRedirection:
|
||||||
redirFilename = self.FilesRedirection[filename]
|
redirFilename = self.FilesRedirection[filename]
|
||||||
self.warn("XEN PVM addfile(filename=%s) redirected to %s" % (filename, redirFilename))
|
logger.warn("XEN PVM addfile(filename=%s) redirected to %s" % (filename, redirFilename))
|
||||||
filename = redirFilename
|
filename = redirFilename
|
||||||
|
|
||||||
try:
|
try:
|
||||||
|
|
@ -794,24 +776,24 @@ class XenNode(PyCoreNode):
|
||||||
fout.write(contents)
|
fout.write(contents)
|
||||||
os.chmod(fout.name, mode)
|
os.chmod(fout.name, mode)
|
||||||
fout.close()
|
fout.close()
|
||||||
self.info("created nodefile: '%s'; mode: 0%o" % (fout.name, mode))
|
logger.info("created nodefile: '%s'; mode: 0%o" % (fout.name, mode))
|
||||||
finally:
|
finally:
|
||||||
self.lock.release()
|
self.lock.release()
|
||||||
|
|
||||||
self.warn("XEN PVM addfile(filename=%s) called" % [filename])
|
logger.warn("XEN PVM addfile(filename=%s) called" % [filename])
|
||||||
|
|
||||||
# shcmd = "mkdir -p $(dirname '%s') && mv '%s' '%s' && sync" % \
|
# shcmd = "mkdir -p $(dirname '%s') && mv '%s' '%s' && sync" % \
|
||||||
# (filename, srcname, filename)
|
# (filename, srcname, filename)
|
||||||
# self.shcmd(shcmd)
|
# self.shcmd(shcmd)
|
||||||
|
|
||||||
def unmount_all(self, path):
|
def unmount_all(self, path):
|
||||||
''' Namespaces inherit the host mounts, so we need to ensure that all
|
"""
|
||||||
|
Namespaces inherit the host mounts, so we need to ensure that all
|
||||||
namespaces have unmounted our temporary mount area so that the
|
namespaces have unmounted our temporary mount area so that the
|
||||||
kpartx command will succeed.
|
kpartx command will succeed.
|
||||||
'''
|
"""
|
||||||
# Session.bootnodes() already has self.session._objslock
|
# Session.bootnodes() already has self.session._objslock
|
||||||
for o in self.session.objs():
|
for o in self.session.objects.itervalues():
|
||||||
if not isinstance(o, LxcNode):
|
if not isinstance(o, LxcNode):
|
||||||
continue
|
continue
|
||||||
o.umount(path)
|
o.umount(path)
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,11 +1,4 @@
|
||||||
#
|
"""
|
||||||
# CORE
|
|
||||||
# Copyright (c)2011-2012 the Boeing Company.
|
|
||||||
# See the LICENSE file included in this distribution.
|
|
||||||
#
|
|
||||||
# author: Jeff Ahrenholz <jeffrey.m.ahrenholz@boeing.com>
|
|
||||||
#
|
|
||||||
'''
|
|
||||||
xenconfig.py: Implementation of the XenConfigManager class for managing
|
xenconfig.py: Implementation of the XenConfigManager class for managing
|
||||||
configurable items for XenNodes.
|
configurable items for XenNodes.
|
||||||
|
|
||||||
|
|
@ -17,72 +10,93 @@ Node type config: XenConfigManager.configs[0] = (type='mytype', values)
|
||||||
All nodes of this type have this config.
|
All nodes of this type have this config.
|
||||||
Node-specific config: XenConfigManager.configs[nodenumber] = (type, values)
|
Node-specific config: XenConfigManager.configs[nodenumber] = (type, values)
|
||||||
The node having this specific number has this config.
|
The node having this specific number has this config.
|
||||||
'''
|
"""
|
||||||
|
|
||||||
import sys, os, threading, subprocess, time, string
|
|
||||||
import ConfigParser
|
import ConfigParser
|
||||||
from xml.dom.minidom import parseString, Document
|
import os
|
||||||
from core.constants import *
|
import string
|
||||||
|
|
||||||
|
from core import constants
|
||||||
from core.api import coreapi
|
from core.api import coreapi
|
||||||
from core.conf import ConfigurableManager, Configurable
|
from core.conf import Configurable
|
||||||
|
from core.conf import ConfigurableManager
|
||||||
|
from core.enumerations import ConfigDataTypes
|
||||||
|
from core.enumerations import ConfigFlags
|
||||||
|
from core.enumerations import ConfigTlvs
|
||||||
|
from core.enumerations import RegisterTlvs
|
||||||
|
from core.misc import log
|
||||||
|
|
||||||
|
logger = log.get_logger(__name__)
|
||||||
|
|
||||||
|
|
||||||
class XenConfigManager(ConfigurableManager):
|
class XenConfigManager(ConfigurableManager):
|
||||||
''' Xen controller object. Lives in a Session instance and is used for
|
"""
|
||||||
|
Xen controller object. Lives in a Session instance and is used for
|
||||||
building Xen profiles.
|
building Xen profiles.
|
||||||
'''
|
"""
|
||||||
_name = "xen"
|
name = "xen"
|
||||||
_type = coreapi.CORE_TLV_REG_EMULSRV
|
config_type = RegisterTlvs.EMULATION_SERVER.value
|
||||||
|
|
||||||
def __init__(self, session):
|
def __init__(self, session):
|
||||||
ConfigurableManager.__init__(self, session)
|
"""
|
||||||
self.verbose = self.session.getcfgitembool('verbose', False)
|
Creates a XenConfigManager instance.
|
||||||
self.default_config = XenDefaultConfig(session, objid=None)
|
|
||||||
|
:param core.session.Session session: session this manager is tied to
|
||||||
|
:return: nothing
|
||||||
|
"""
|
||||||
|
ConfigurableManager.__init__(self)
|
||||||
|
self.default_config = XenDefaultConfig(session, object_id=None)
|
||||||
self.loadconfigfile()
|
self.loadconfigfile()
|
||||||
|
|
||||||
def setconfig(self, nodenum, conftype, values):
|
def setconfig(self, nodenum, conftype, values):
|
||||||
''' add configuration values for a node to a dictionary; values are
|
"""
|
||||||
|
add configuration values for a node to a dictionary; values are
|
||||||
usually received from a Configuration Message, and may refer to a
|
usually received from a Configuration Message, and may refer to a
|
||||||
node for which no object exists yet
|
node for which no object exists yet
|
||||||
'''
|
"""
|
||||||
if nodenum is None:
|
if nodenum is None:
|
||||||
nodenum = 0 # used for storing the global default config
|
nodenum = 0 # used for storing the global default config
|
||||||
return ConfigurableManager.setconfig(self, nodenum, conftype, values)
|
return ConfigurableManager.setconfig(self, nodenum, conftype, values)
|
||||||
|
|
||||||
def getconfig(self, nodenum, conftype, defaultvalues):
|
def getconfig(self, nodenum, conftype, defaultvalues):
|
||||||
''' get configuration values for a node; if the values don't exist in
|
"""
|
||||||
|
get configuration values for a node; if the values don't exist in
|
||||||
our dictionary then return the default values supplied; if conftype
|
our dictionary then return the default values supplied; if conftype
|
||||||
is None then we return a match on any conftype.
|
is None then we return a match on any conftype.
|
||||||
'''
|
"""
|
||||||
if nodenum is None:
|
if nodenum is None:
|
||||||
nodenum = 0 # used for storing the global default config
|
nodenum = 0 # used for storing the global default config
|
||||||
return ConfigurableManager.getconfig(self, nodenum, conftype,
|
return ConfigurableManager.getconfig(self, nodenum, conftype, defaultvalues)
|
||||||
defaultvalues)
|
|
||||||
|
|
||||||
def clearconfig(self, nodenum):
|
def clearconfig(self, nodenum):
|
||||||
''' remove configuration values for a node
|
"""
|
||||||
'''
|
remove configuration values for a node
|
||||||
|
"""
|
||||||
ConfigurableManager.clearconfig(self, nodenum)
|
ConfigurableManager.clearconfig(self, nodenum)
|
||||||
if 0 in self.configs:
|
if 0 in self.configs:
|
||||||
self.configs.pop(0)
|
self.configs.pop(0)
|
||||||
|
|
||||||
def configure(self, session, msg):
|
def configure(self, session, config_data):
|
||||||
''' Handle configuration messages for global Xen config.
|
"""
|
||||||
'''
|
Handle configuration messages for global Xen config.
|
||||||
return self.default_config.configure(self, msg)
|
|
||||||
|
:param core.conf.ConfigData config_data: configuration data for carrying out a configuration
|
||||||
|
"""
|
||||||
|
return self.default_config.configure(self, config_data)
|
||||||
|
|
||||||
def loadconfigfile(self, filename=None):
|
def loadconfigfile(self, filename=None):
|
||||||
''' Load defaults from the /etc/core/xen.conf file into dict object.
|
"""
|
||||||
'''
|
Load defaults from the /etc/core/xen.conf file into dict object.
|
||||||
|
"""
|
||||||
if filename is None:
|
if filename is None:
|
||||||
filename = os.path.join(CORE_CONF_DIR, 'xen.conf')
|
filename = os.path.join(constants.CORE_CONF_DIR, 'xen.conf')
|
||||||
cfg = ConfigParser.SafeConfigParser()
|
cfg = ConfigParser.SafeConfigParser()
|
||||||
if filename not in cfg.read(filename):
|
if filename not in cfg.read(filename):
|
||||||
self.session.warn("unable to read Xen config file: %s" % filename)
|
logger.warn("unable to read Xen config file: %s" % filename)
|
||||||
return
|
return
|
||||||
section = "xen"
|
section = "xen"
|
||||||
if not cfg.has_section(section):
|
if not cfg.has_section(section):
|
||||||
self.session.warn("%s is missing a xen section!" % filename)
|
logger.warn("%s is missing a xen section!" % filename)
|
||||||
return
|
return
|
||||||
self.configfile = dict(cfg.items(section))
|
self.configfile = dict(cfg.items(section))
|
||||||
# populate default config items from config file entries
|
# populate default config items from config file entries
|
||||||
|
|
@ -92,13 +106,14 @@ class XenConfigManager(ConfigurableManager):
|
||||||
if names[i] in self.configfile:
|
if names[i] in self.configfile:
|
||||||
vals[i] = self.configfile[names[i]]
|
vals[i] = self.configfile[names[i]]
|
||||||
# this sets XenConfigManager.configs[0] = (type='xen', vals)
|
# this sets XenConfigManager.configs[0] = (type='xen', vals)
|
||||||
self.setconfig(None, self.default_config._name, vals)
|
self.setconfig(None, self.default_config.name, vals)
|
||||||
|
|
||||||
def getconfigitem(self, name, model=None, node=None, value=None):
|
def getconfigitem(self, name, model=None, node=None, value=None):
|
||||||
''' Get a config item of the given name, first looking for node-specific
|
"""
|
||||||
|
Get a config item of the given name, first looking for node-specific
|
||||||
configuration, then model specific, and finally global defaults.
|
configuration, then model specific, and finally global defaults.
|
||||||
If a value is supplied, it will override any stored config.
|
If a value is supplied, it will override any stored config.
|
||||||
'''
|
"""
|
||||||
if value is not None:
|
if value is not None:
|
||||||
return value
|
return value
|
||||||
n = None
|
n = None
|
||||||
|
|
@ -112,7 +127,7 @@ class XenConfigManager(ConfigurableManager):
|
||||||
if v is None:
|
if v is None:
|
||||||
# get item from default config for the machine type
|
# get item from default config for the machine type
|
||||||
(t, v) = self.getconfig(nodenum=None,
|
(t, v) = self.getconfig(nodenum=None,
|
||||||
conftype=self.default_config._name,
|
conftype=self.default_config.name,
|
||||||
defaultvalues=None)
|
defaultvalues=None)
|
||||||
|
|
||||||
confignames = self.default_config.getnames()
|
confignames = self.default_config.getnames()
|
||||||
|
|
@ -124,142 +139,136 @@ class XenConfigManager(ConfigurableManager):
|
||||||
if name in self.configfile:
|
if name in self.configfile:
|
||||||
return self.configfile[name]
|
return self.configfile[name]
|
||||||
else:
|
else:
|
||||||
#self.warn("missing config item '%s'" % name)
|
# logger.warn("missing config item '%s'" % name)
|
||||||
return None
|
return None
|
||||||
|
|
||||||
|
|
||||||
class XenConfig(Configurable):
|
class XenConfig(Configurable):
|
||||||
''' Manage Xen configuration profiles.
|
"""
|
||||||
'''
|
Manage Xen configuration profiles.
|
||||||
|
"""
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def configure(cls, xen, msg):
|
def configure(cls, xen, config_data):
|
||||||
''' Handle configuration messages for setting up a model.
|
"""
|
||||||
|
Handle configuration messages for setting up a model.
|
||||||
Similar to Configurable.configure(), but considers opaque data
|
Similar to Configurable.configure(), but considers opaque data
|
||||||
for indicating node types.
|
for indicating node types.
|
||||||
'''
|
|
||||||
reply = None
|
|
||||||
nodenum = msg.gettlv(coreapi.CORE_TLV_CONF_NODE)
|
|
||||||
objname = msg.gettlv(coreapi.CORE_TLV_CONF_OBJ)
|
|
||||||
conftype = msg.gettlv(coreapi.CORE_TLV_CONF_TYPE)
|
|
||||||
opaque = msg.gettlv(coreapi.CORE_TLV_CONF_OPAQUE)
|
|
||||||
|
|
||||||
nodetype = objname
|
:param xen: xen instance to configure
|
||||||
|
:param core.conf.ConfigData config_data: configuration data for carrying out a configuration
|
||||||
|
"""
|
||||||
|
reply = None
|
||||||
|
node_id = config_data.node
|
||||||
|
object_name = config_data.object
|
||||||
|
config_type = config_data.type
|
||||||
|
opaque = config_data.opaque
|
||||||
|
values_str = config_data.data_values
|
||||||
|
|
||||||
|
nodetype = object_name
|
||||||
if opaque is not None:
|
if opaque is not None:
|
||||||
opaque_items = opaque.split(':')
|
opaque_items = opaque.split(':')
|
||||||
if len(opaque_items) != 2:
|
if len(opaque_items) != 2:
|
||||||
xen.warn("xen config: invalid opaque data in conf message")
|
logger.warn("xen config: invalid opaque data in conf message")
|
||||||
return None
|
return None
|
||||||
nodetype = opaque_items[1]
|
nodetype = opaque_items[1]
|
||||||
|
|
||||||
if xen.verbose:
|
logger.info("received configure message for %s", nodetype)
|
||||||
xen.info("received configure message for %s" % nodetype)
|
if config_type == ConfigFlags.REQUEST.value:
|
||||||
if conftype == coreapi.CONF_TYPE_FLAGS_REQUEST:
|
logger.info("replying to configure request for %s " % nodetype)
|
||||||
if xen.verbose:
|
|
||||||
xen.info("replying to configure request for %s " % nodetype)
|
|
||||||
# when object name is "all", the reply to this request may be None
|
# when object name is "all", the reply to this request may be None
|
||||||
# if this node has not been configured for this model; otherwise we
|
# if this node has not been configured for this model; otherwise we
|
||||||
# reply with the defaults for this model
|
# reply with the defaults for this model
|
||||||
if objname == "all":
|
if object_name == "all":
|
||||||
typeflags = coreapi.CONF_TYPE_FLAGS_UPDATE
|
typeflags = ConfigFlags.UPDATE.value
|
||||||
else:
|
else:
|
||||||
typeflags = coreapi.CONF_TYPE_FLAGS_NONE
|
typeflags = ConfigFlags.NONE.value
|
||||||
values = xen.getconfig(nodenum, nodetype, defaultvalues=None)[1]
|
values = xen.getconfig(node_id, nodetype, defaultvalues=None)[1]
|
||||||
if values is None:
|
if values is None:
|
||||||
# get defaults from default "xen" config which includes
|
# get defaults from default "xen" config which includes
|
||||||
# settings from both cls._confdefaultvalues and xen.conf
|
# settings from both cls._confdefaultvalues and xen.conf
|
||||||
defaults = cls.getdefaultvalues()
|
defaults = cls.getdefaultvalues()
|
||||||
values = xen.getconfig(nodenum, cls._name, defaults)[1]
|
values = xen.getconfig(node_id, cls.name, defaults)[1]
|
||||||
if values is None:
|
if values is None:
|
||||||
return None
|
return None
|
||||||
# reply with config options
|
# reply with config options
|
||||||
if nodenum is None:
|
if node_id is None:
|
||||||
nodenum = 0
|
node_id = 0
|
||||||
reply = cls.toconfmsg(0, nodenum, typeflags, nodetype, values)
|
reply = cls.config_data(0, node_id, typeflags, nodetype, values)
|
||||||
elif conftype == coreapi.CONF_TYPE_FLAGS_RESET:
|
elif config_type == ConfigFlags.RESET.value:
|
||||||
if objname == "all":
|
if object_name == "all":
|
||||||
xen.clearconfig(nodenum)
|
xen.clearconfig(node_id)
|
||||||
# elif conftype == coreapi.CONF_TYPE_FLAGS_UPDATE:
|
# elif conftype == coreapi.CONF_TYPE_FLAGS_UPDATE:
|
||||||
else:
|
else:
|
||||||
# store the configuration values for later use, when the XenNode
|
# store the configuration values for later use, when the XenNode
|
||||||
# object has been created
|
# object has been created
|
||||||
if objname is None:
|
if object_name is None:
|
||||||
xen.info("no configuration object for node %s" % nodenum)
|
logger.info("no configuration object for node %s" % node_id)
|
||||||
return None
|
return None
|
||||||
values_str = msg.gettlv(coreapi.CORE_TLV_CONF_VALUES)
|
|
||||||
if values_str is None:
|
if values_str is None:
|
||||||
# use default or preconfigured values
|
# use default or preconfigured values
|
||||||
defaults = cls.getdefaultvalues()
|
defaults = cls.getdefaultvalues()
|
||||||
values = xen.getconfig(nodenum, cls._name, defaults)[1]
|
values = xen.getconfig(node_id, cls.name, defaults)[1]
|
||||||
else:
|
else:
|
||||||
# use new values supplied from the conf message
|
# use new values supplied from the conf message
|
||||||
values = values_str.split('|')
|
values = values_str.split('|')
|
||||||
xen.setconfig(nodenum, nodetype, values)
|
xen.setconfig(node_id, nodetype, values)
|
||||||
|
|
||||||
return reply
|
return reply
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def toconfmsg(cls, flags, nodenum, typeflags, nodetype, values):
|
def config_data(cls, flags, node_id, type_flags, nodetype, values):
|
||||||
''' Convert this class to a Config API message. Some TLVs are defined
|
"""
|
||||||
|
Convert this class to a Config API message. Some TLVs are defined
|
||||||
by the class, but node number, conf type flags, and values must
|
by the class, but node number, conf type flags, and values must
|
||||||
be passed in.
|
be passed in.
|
||||||
'''
|
"""
|
||||||
values_str = string.join(values, '|')
|
values_str = string.join(values, '|')
|
||||||
tlvdata = ""
|
tlvdata = ""
|
||||||
tlvdata += coreapi.CoreConfTlv.pack(coreapi.CORE_TLV_CONF_NODE, nodenum)
|
tlvdata += coreapi.CoreConfigTlv.pack(ConfigTlvs.NODE.value, node_id)
|
||||||
tlvdata += coreapi.CoreConfTlv.pack(coreapi.CORE_TLV_CONF_OBJ,
|
tlvdata += coreapi.CoreConfigTlv.pack(ConfigTlvs.OBJECT.value, cls.name)
|
||||||
cls._name)
|
tlvdata += coreapi.CoreConfigTlv.pack(ConfigTlvs.TYPE.value, type_flags)
|
||||||
tlvdata += coreapi.CoreConfTlv.pack(coreapi.CORE_TLV_CONF_TYPE,
|
datatypes = tuple(map(lambda x: x[1], cls.config_matrix))
|
||||||
typeflags)
|
tlvdata += coreapi.CoreConfigTlv.pack(ConfigTlvs.DATA_TYPES.value, datatypes)
|
||||||
datatypes = tuple( map(lambda x: x[1], cls._confmatrix) )
|
tlvdata += coreapi.CoreConfigTlv.pack(ConfigTlvs.VALUES.value, values_str)
|
||||||
tlvdata += coreapi.CoreConfTlv.pack(coreapi.CORE_TLV_CONF_DATA_TYPES,
|
captions = reduce(lambda a, b: a + '|' + b, map(lambda x: x[4], cls.config_matrix))
|
||||||
datatypes)
|
tlvdata += coreapi.CoreConfigTlv.pack(ConfigTlvs.CAPTIONS, captions)
|
||||||
tlvdata += coreapi.CoreConfTlv.pack(coreapi.CORE_TLV_CONF_VALUES,
|
possiblevals = reduce(lambda a, b: a + '|' + b, map(lambda x: x[3], cls.config_matrix))
|
||||||
values_str)
|
tlvdata += coreapi.CoreConfigTlv.pack(ConfigTlvs.POSSIBLE_VALUES.value, possiblevals)
|
||||||
captions = reduce( lambda a,b: a + '|' + b, \
|
if cls.bitmap is not None:
|
||||||
map(lambda x: x[4], cls._confmatrix))
|
tlvdata += coreapi.CoreConfigTlv.pack(ConfigTlvs.BITMAP.value, cls.bitmap)
|
||||||
tlvdata += coreapi.CoreConfTlv.pack(coreapi.CORE_TLV_CONF_CAPTIONS,
|
if cls.config_groups is not None:
|
||||||
captions)
|
tlvdata += coreapi.CoreConfigTlv.pack(ConfigTlvs.GROUPS.value, cls.config_groups)
|
||||||
possiblevals = reduce( lambda a,b: a + '|' + b, \
|
opaque = "%s:%s" % (cls.name, nodetype)
|
||||||
map(lambda x: x[3], cls._confmatrix))
|
tlvdata += coreapi.CoreConfigTlv.pack(ConfigTlvs.OPAQUE.value, opaque)
|
||||||
tlvdata += coreapi.CoreConfTlv.pack(
|
|
||||||
coreapi.CORE_TLV_CONF_POSSIBLE_VALUES, possiblevals)
|
|
||||||
if cls._bitmap is not None:
|
|
||||||
tlvdata += coreapi.CoreConfTlv.pack(coreapi.CORE_TLV_CONF_BITMAP,
|
|
||||||
cls._bitmap)
|
|
||||||
if cls._confgroups is not None:
|
|
||||||
tlvdata += coreapi.CoreConfTlv.pack(coreapi.CORE_TLV_CONF_GROUPS,
|
|
||||||
cls._confgroups)
|
|
||||||
opaque = "%s:%s" % (cls._name, nodetype)
|
|
||||||
tlvdata += coreapi.CoreConfTlv.pack(coreapi.CORE_TLV_CONF_OPAQUE,
|
|
||||||
opaque)
|
|
||||||
msg = coreapi.CoreConfMessage.pack(flags, tlvdata)
|
msg = coreapi.CoreConfMessage.pack(flags, tlvdata)
|
||||||
return msg
|
return msg
|
||||||
|
|
||||||
|
|
||||||
class XenDefaultConfig(XenConfig):
|
class XenDefaultConfig(XenConfig):
|
||||||
''' Global default Xen configuration options.
|
"""
|
||||||
'''
|
Global default Xen configuration options.
|
||||||
_name = "xen"
|
"""
|
||||||
|
name = "xen"
|
||||||
# Configuration items:
|
# Configuration items:
|
||||||
# ('name', 'type', 'default', 'possible-value-list', 'caption')
|
# ('name', 'type', 'default', 'possible-value-list', 'caption')
|
||||||
_confmatrix = [
|
config_matrix = [
|
||||||
('ram_size', coreapi.CONF_DATA_TYPE_STRING, '256', '',
|
('ram_size', ConfigDataTypes.STRING.value, '256', '',
|
||||||
'ram size (MB)'),
|
'ram size (MB)'),
|
||||||
('disk_size', coreapi.CONF_DATA_TYPE_STRING, '256M', '',
|
('disk_size', ConfigDataTypes.STRING.value, '256M', '',
|
||||||
'disk size (use K/M/G suffix)'),
|
'disk size (use K/M/G suffix)'),
|
||||||
('iso_file', coreapi.CONF_DATA_TYPE_STRING, '', '',
|
('iso_file', ConfigDataTypes.STRING.value, '', '',
|
||||||
'iso file'),
|
'iso file'),
|
||||||
('mount_path', coreapi.CONF_DATA_TYPE_STRING, '', '',
|
('mount_path', ConfigDataTypes.STRING.value, '', '',
|
||||||
'mount path'),
|
'mount path'),
|
||||||
('etc_path', coreapi.CONF_DATA_TYPE_STRING, '', '',
|
('etc_path', ConfigDataTypes.STRING.value, '', '',
|
||||||
'etc path'),
|
'etc path'),
|
||||||
('persist_tar_iso', coreapi.CONF_DATA_TYPE_STRING, '', '',
|
('persist_tar_iso', ConfigDataTypes.STRING.value, '', '',
|
||||||
'iso persist tar file'),
|
'iso persist tar file'),
|
||||||
('persist_tar', coreapi.CONF_DATA_TYPE_STRING, '', '',
|
('persist_tar', ConfigDataTypes.STRING.value, '', '',
|
||||||
'persist tar file'),
|
'persist tar file'),
|
||||||
('root_password', coreapi.CONF_DATA_TYPE_STRING, 'password', '',
|
('root_password', ConfigDataTypes.STRING.value, 'password', '',
|
||||||
'root password'),
|
'root password'),
|
||||||
]
|
]
|
||||||
|
|
||||||
_confgroups = "domU properties:1-%d" % len(_confmatrix)
|
config_groups = "domU properties:1-%d" % len(config_matrix)
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -29,12 +29,12 @@ quagga_sbin_search = "/usr/local/sbin /usr/sbin /usr/lib/quagga"
|
||||||
#
|
#
|
||||||
#
|
#
|
||||||
# uncomment and edit to establish a distributed control backchannel
|
# uncomment and edit to establish a distributed control backchannel
|
||||||
#controlnet = core1:172.16.1.0/24 core2:172.16.2.0/24 core3:172.16.3.0/24 core4 :172.16.4.0/24 core5:172.16.5.0/24
|
#controlnet = core1:172.16.1.0/24 core:172.16.2.0/24 core3:172.16.3.0/24 core4 :172.16.4.0/24 core5:172.16.5.0/24
|
||||||
|
|
||||||
# uncomment and edit to establish distributed auxiliary control channels.
|
# uncomment and edit to establish distributed auxiliary control channels.
|
||||||
#controlnet1 = core1:172.17.1.0/24 core2:172.17.2.0/24 core3:172.17.3.0/24 core4 :172.17.4.0/24 core5:172.17.5.0/24
|
#controlnet1 = core1:172.17.1.0/24 core:172.17.2.0/24 core3:172.17.3.0/24 core4 :172.17.4.0/24 core5:172.17.5.0/24
|
||||||
#controlnet2 = core1:172.18.1.0/24 core2:172.18.2.0/24 core3:172.18.3.0/24 core4 :172.18.4.0/24 core5:172.18.5.0/24
|
#controlnet2 = core1:172.18.1.0/24 core:172.18.2.0/24 core3:172.18.3.0/24 core4 :172.18.4.0/24 core5:172.18.5.0/24
|
||||||
#controlnet3 = core1:172.19.1.0/24 core2:172.19.2.0/24 core3:172.19.3.0/24 core4 :172.19.4.0/24 core5:172.19.5.0/24
|
#controlnet3 = core1:172.19.1.0/24 core:172.19.2.0/24 core3:172.19.3.0/24 core4 :172.19.4.0/24 core5:172.19.5.0/24
|
||||||
|
|
||||||
# uncomment and edit to assign host interfaces to auxilary control channels
|
# uncomment and edit to assign host interfaces to auxilary control channels
|
||||||
# for use in connecting with other servers in a distributed environments.
|
# for use in connecting with other servers in a distributed environments.
|
||||||
|
|
@ -62,4 +62,4 @@ emane_models = RfPipe, Ieee80211abg, CommEffect, Bypass, Tdma
|
||||||
#emane_log_level = 2
|
#emane_log_level = 2
|
||||||
emane_realtime = True
|
emane_realtime = True
|
||||||
|
|
||||||
aux_request_handler = core.addons.api2handler.CoreApi2RequestHandler:12222
|
#aux_request_handler = core.addons.api2handler.CoreApi2RequestHandler:12222
|
||||||
|
|
|
||||||
|
|
@ -8,63 +8,81 @@
|
||||||
#
|
#
|
||||||
|
|
||||||
|
|
||||||
import optparse, sys, os, datetime, time
|
import datetime
|
||||||
|
import optparse
|
||||||
|
import sys
|
||||||
|
import time
|
||||||
|
|
||||||
from core import pycore
|
from core.misc import ipaddress, nodeutils
|
||||||
from core.misc import ipaddr
|
from core.misc import nodemaps
|
||||||
from core.misc.utils import mutecall
|
|
||||||
from core.mobility import BasicRangeModel
|
from core.mobility import BasicRangeModel
|
||||||
|
from core.netns.nodes import WlanNode
|
||||||
from core.netns.vnet import EbtablesQueue
|
from core.netns.vnet import EbtablesQueue
|
||||||
|
from core.netns.vnode import LxcNode
|
||||||
|
from core.session import Session
|
||||||
|
|
||||||
# node list - global so you can play using 'python -i'
|
# node list - global so you can play using 'python -i'
|
||||||
# e.g. >>> n[0].session.shutdown()
|
# e.g. >>> n[0].session.shutdown()
|
||||||
n = []
|
n = []
|
||||||
|
|
||||||
|
|
||||||
def test(options):
|
def test(options):
|
||||||
prefix = ipaddr.IPv4Prefix("10.83.0.0/16")
|
prefix = ipaddress.Ipv4Prefix("10.83.0.0/16")
|
||||||
session = pycore.Session(persistent = True)
|
session = Session(1, persistent=True)
|
||||||
if options.enablesdt:
|
if options.enablesdt:
|
||||||
session.location.setrefgeo(47.57917,-122.13232,50.0) # GUI default
|
# GUI default
|
||||||
|
session.location.setrefgeo(47.57917, -122.13232, 50.0)
|
||||||
session.location.refscale = 100.0
|
session.location.refscale = 100.0
|
||||||
session.options.enablesdt = True
|
session.options.enablesdt = True
|
||||||
session.options.sdturl = options.sdturl
|
session.options.sdturl = options.sdturl
|
||||||
wlanid = options.numnodes + 1
|
wlanid = options.numnodes + 1
|
||||||
net = session.addobj(cls = pycore.nodes.WlanNode, name = "wlan%d" % wlanid,
|
net = session.add_object(
|
||||||
objid = wlanid, verbose = True)
|
cls=WlanNode,
|
||||||
|
name="wlan%d" % wlanid,
|
||||||
|
objid=wlanid
|
||||||
|
)
|
||||||
|
|
||||||
values = list(BasicRangeModel.getdefaultvalues())
|
values = list(BasicRangeModel.getdefaultvalues())
|
||||||
# values[0] = 5000000 # 5000km range
|
# values[0] = 5000000 # 5000km range
|
||||||
net.setmodel(BasicRangeModel, values)
|
net.setmodel(BasicRangeModel, values)
|
||||||
for i in xrange(1, options.numnodes + 1):
|
for i in xrange(1, options.numnodes + 1):
|
||||||
tmp = session.addobj(cls = pycore.nodes.LxcNode, name = "n%d" % i,
|
node = session.add_object(cls=LxcNode, name="n%d" % i, objid=i)
|
||||||
objid = i)
|
address = "%s/%s" % (prefix.addr(i), prefix.prefixlen)
|
||||||
tmp.newnetif(net, ["%s/%s" % (prefix.addr(i), prefix.prefixlen)])
|
print "setting node address: %s - %s" % (node.objid, address)
|
||||||
|
node.newnetif(net, [address])
|
||||||
# set increasing Z coordinates
|
# set increasing Z coordinates
|
||||||
tmp.setposition(10, 10, 100*i)
|
node.setposition(10, 10, 100)
|
||||||
n.append(tmp)
|
n.append(node)
|
||||||
|
|
||||||
# example setting node n2 to a high altitude
|
# example setting node n2 to a high altitude
|
||||||
# n[1].setposition(10, 10, 2000000) # 2000km
|
# n[1].setposition(10, 10, 2000000) # 2000km
|
||||||
# session.sdt.updatenode(n[1].objid, 0, 10, 10, 2000000)
|
# session.sdt.updatenode(n[1].objid, 0, 10, 10, 2000000)
|
||||||
|
|
||||||
n[0].term("bash")
|
# launches terminal for the first node
|
||||||
|
# n[0].term("bash")
|
||||||
|
n[0].icmd(["ping", "-c", "5", "127.0.0.1"])
|
||||||
|
|
||||||
# wait for rate seconds to allow ebtables commands to commit
|
# wait for rate seconds to allow ebtables commands to commit
|
||||||
time.sleep(EbtablesQueue.rate)
|
time.sleep(EbtablesQueue.rate)
|
||||||
#session.shutdown()
|
|
||||||
|
raw_input("press enter to exit")
|
||||||
|
session.shutdown()
|
||||||
|
|
||||||
|
|
||||||
def main():
|
def main():
|
||||||
usagestr = "usage: %prog [-h] [options] [args]"
|
usagestr = "usage: %prog [-h] [options] [args]"
|
||||||
parser = optparse.OptionParser(usage=usagestr)
|
parser = optparse.OptionParser(usage=usagestr)
|
||||||
|
|
||||||
parser.set_defaults(numnodes = 2, enablesdt = False,
|
parser.set_defaults(numnodes=2, enablesdt=False, sdturl="tcp://127.0.0.1:50000/")
|
||||||
sdturl = "tcp://127.0.0.1:50000/")
|
parser.add_option(
|
||||||
parser.add_option("-n", "--numnodes", dest = "numnodes", type = int,
|
"-n", "--numnodes", dest="numnodes", type=int,
|
||||||
help = "number of nodes to test; default = %s" %
|
help="number of nodes to test; default = %s" % parser.defaults["numnodes"]
|
||||||
parser.defaults["numnodes"])
|
)
|
||||||
parser.add_option("-s", "--sdt", dest = "enablesdt", action = "store_true",
|
parser.add_option("-s", "--sdt", dest="enablesdt", action="store_true", help="enable SDT output")
|
||||||
help = "enable SDT output")
|
parser.add_option(
|
||||||
parser.add_option("-u", "--sdturl", dest = "sdturl", type = "string",
|
"-u", "--sdturl", dest="sdturl", type="string",
|
||||||
help = "URL for SDT connection, default = %s" % \
|
help="URL for SDT connection, default = %s" % parser.defaults["sdturl"]
|
||||||
parser.defaults["sdturl"])
|
)
|
||||||
|
|
||||||
def usage(msg=None, err=0):
|
def usage(msg=None, err=0):
|
||||||
sys.stdout.write("\n")
|
sys.stdout.write("\n")
|
||||||
|
|
@ -86,8 +104,12 @@ def main():
|
||||||
|
|
||||||
test(options)
|
test(options)
|
||||||
|
|
||||||
print >> sys.stderr, \
|
print >> sys.stderr, "elapsed time: %s" % (datetime.datetime.now() - start)
|
||||||
"elapsed time: %s" % (datetime.datetime.now() - start)
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
|
# configure nodes to use
|
||||||
|
node_map = nodemaps.CLASSIC_NODES
|
||||||
|
nodeutils.set_node_map(node_map)
|
||||||
|
|
||||||
main()
|
main()
|
||||||
|
|
|
||||||
|
|
@ -5,64 +5,79 @@
|
||||||
|
|
||||||
# A distributed example where CORE API messaging is used to create a session
|
# A distributed example where CORE API messaging is used to create a session
|
||||||
# on a daemon server. The daemon server defaults to 127.0.0.1:4038
|
# on a daemon server. The daemon server defaults to 127.0.0.1:4038
|
||||||
# to target a remote machine specify '-d <ip address>' parameter, it needs to be
|
# to target a remote machine specify "-d <ip address>" parameter, it needs to be
|
||||||
# running the daemon with listenaddr=0.0.0.0 in the core.conf file.
|
# running the daemon with listenaddr=0.0.0.0 in the core.conf file.
|
||||||
# This script creates no nodes locally and therefore can be run as an
|
# This script creates no nodes locally and therefore can be run as an
|
||||||
# unprivileged user.
|
# unprivileged user.
|
||||||
|
|
||||||
import sys, datetime, optparse, time
|
import datetime
|
||||||
|
import optparse
|
||||||
|
import sys
|
||||||
|
|
||||||
from core import pycore
|
|
||||||
from core.misc import ipaddr
|
|
||||||
from core.constants import *
|
|
||||||
from core.api import coreapi
|
from core.api import coreapi
|
||||||
|
from core.api import dataconversion
|
||||||
|
from core.api.coreapi import CoreExecuteTlv
|
||||||
|
from core.enumerations import CORE_API_PORT
|
||||||
|
from core.enumerations import EventTlvs
|
||||||
|
from core.enumerations import EventTypes
|
||||||
|
from core.enumerations import ExecuteTlvs
|
||||||
|
from core.enumerations import LinkTlvs
|
||||||
|
from core.enumerations import LinkTypes
|
||||||
|
from core.enumerations import MessageFlags
|
||||||
|
from core.enumerations import MessageTypes
|
||||||
|
from core.misc import ipaddress, nodeutils, nodemaps
|
||||||
|
from core.netns import nodes
|
||||||
|
|
||||||
# declare classes for use with Broker
|
# declare classes for use with Broker
|
||||||
import select
|
|
||||||
|
|
||||||
coreapi.add_node_class("CORE_NODE_DEF",
|
from core.session import Session
|
||||||
coreapi.CORE_NODE_DEF, pycore.nodes.CoreNode)
|
|
||||||
coreapi.add_node_class("CORE_NODE_SWITCH",
|
|
||||||
coreapi.CORE_NODE_SWITCH, pycore.nodes.SwitchNode)
|
|
||||||
|
|
||||||
# node list (count from 1)
|
# node list (count from 1)
|
||||||
n = [None]
|
n = [None]
|
||||||
exec_num = 1
|
exec_num = 1
|
||||||
|
|
||||||
|
|
||||||
def cmd(node, exec_cmd):
|
def cmd(node, exec_cmd):
|
||||||
'''
|
"""
|
||||||
:param node: The node the command should be issued too
|
:param node: The node the command should be issued too
|
||||||
:param exec_cmd: A string with the command to be run
|
:param exec_cmd: A string with the command to be run
|
||||||
:return: Returns the result of the command
|
:return: Returns the result of the command
|
||||||
'''
|
"""
|
||||||
global exec_num
|
global exec_num
|
||||||
|
|
||||||
# Set up the command api message
|
# Set up the command api message
|
||||||
tlvdata = coreapi.CoreExecTlv.pack(coreapi.CORE_TLV_EXEC_NODE, node.objid)
|
tlvdata = CoreExecuteTlv.pack(ExecuteTlvs.NODE.value, node.objid)
|
||||||
tlvdata += coreapi.CoreExecTlv.pack(coreapi.CORE_TLV_EXEC_NUM, exec_num)
|
tlvdata += CoreExecuteTlv.pack(ExecuteTlvs.NUMBER.value, exec_num)
|
||||||
tlvdata += coreapi.CoreExecTlv.pack(coreapi.CORE_TLV_EXEC_CMD, exec_cmd)
|
tlvdata += CoreExecuteTlv.pack(ExecuteTlvs.COMMAND.value, exec_cmd)
|
||||||
msg = coreapi.CoreExecMessage.pack(coreapi.CORE_API_STR_FLAG | coreapi.CORE_API_TXT_FLAG, tlvdata)
|
msg = coreapi.CoreExecMessage.pack(MessageFlags.STRING.value | MessageFlags.TEXT.value, tlvdata)
|
||||||
node.session.broker.handlerawmsg(msg)
|
node.session.broker.handlerawmsg(msg)
|
||||||
exec_num += 1
|
exec_num += 1
|
||||||
|
|
||||||
# Now wait for the response
|
# Now wait for the response
|
||||||
(h, p, sock) = node.session.broker.servers['localhost']
|
server = node.session.broker.servers["localhost"]
|
||||||
sock.settimeout(50.0)
|
server.sock.settimeout(50.0)
|
||||||
msghdr = sock.recv(coreapi.CoreMessage.hdrsiz)
|
|
||||||
msgtype, msgflags, msglen = coreapi.CoreMessage.unpackhdr(msghdr)
|
# receive messages until we get our execute response
|
||||||
msgdata = sock.recv(msglen)
|
result = None
|
||||||
|
while True:
|
||||||
|
msghdr = server.sock.recv(coreapi.CoreMessage.header_len)
|
||||||
|
msgtype, msgflags, msglen = coreapi.CoreMessage.unpack_header(msghdr)
|
||||||
|
msgdata = server.sock.recv(msglen)
|
||||||
|
|
||||||
# If we get the right response return the results
|
# If we get the right response return the results
|
||||||
if msgtype == coreapi.CORE_API_EXEC_MSG:
|
print "received response message: %s" % MessageTypes(msgtype)
|
||||||
|
if msgtype == MessageTypes.EXECUTE.value:
|
||||||
msg = coreapi.CoreExecMessage(msgflags, msghdr, msgdata)
|
msg = coreapi.CoreExecMessage(msgflags, msghdr, msgdata)
|
||||||
return msg.gettlv(coreapi.CORE_TLV_EXEC_RESULT)
|
result = msg.get_tlv(ExecuteTlvs.RESULT.value)
|
||||||
else:
|
break
|
||||||
return None
|
|
||||||
|
return result
|
||||||
|
|
||||||
|
|
||||||
def main():
|
def main():
|
||||||
usagestr = "usage: %prog [-n] number of nodes [-d] daemon address"
|
usagestr = "usage: %prog [-n] number of nodes [-d] daemon address"
|
||||||
parser = optparse.OptionParser(usage=usagestr)
|
parser = optparse.OptionParser(usage=usagestr)
|
||||||
parser.set_defaults(numnodes = 5, daemon = '127.0.0.1:'+str(coreapi.CORE_API_PORT))
|
parser.set_defaults(numnodes=5, daemon="127.0.0.1:" + str(CORE_API_PORT))
|
||||||
|
|
||||||
parser.add_option("-n", "--numnodes", dest="numnodes", type=int,
|
parser.add_option("-n", "--numnodes", dest="numnodes", type=int,
|
||||||
help="number of nodes")
|
help="number of nodes")
|
||||||
|
|
@ -85,94 +100,95 @@ def main():
|
||||||
usage("daemon server IP address (-d) is a required argument")
|
usage("daemon server IP address (-d) is a required argument")
|
||||||
|
|
||||||
for a in args:
|
for a in args:
|
||||||
sys.stderr.write("ignoring command line argument: '%s'\n" % a)
|
sys.stderr.write("ignoring command line argument: %s\n" % a)
|
||||||
|
|
||||||
start = datetime.datetime.now()
|
start = datetime.datetime.now()
|
||||||
|
|
||||||
prefix = ipaddr.IPv4Prefix("10.83.0.0/16")
|
prefix = ipaddress.Ipv4Prefix("10.83.0.0/16")
|
||||||
session = pycore.Session(persistent=True)
|
session = Session(1, persistent=True)
|
||||||
if 'server' in globals():
|
if "server" in globals():
|
||||||
server.addsession(session)
|
server.addsession(session)
|
||||||
|
|
||||||
# distributed setup - connect to daemon server
|
# distributed setup - connect to daemon server
|
||||||
daemonport = options.daemon.split(':')
|
daemonport = options.daemon.split(":")
|
||||||
daemonip = daemonport[0]
|
daemonip = daemonport[0]
|
||||||
|
|
||||||
# Localhost is already set in the session but we change it to be the remote daemon
|
# Localhost is already set in the session but we change it to be the remote daemon
|
||||||
# This stops the remote daemon trying to build a tunnel back which would fail
|
# This stops the remote daemon trying to build a tunnel back which would fail
|
||||||
daemon = 'localhost'
|
daemon = "localhost"
|
||||||
if len(daemonport) > 1:
|
if len(daemonport) > 1:
|
||||||
port = int(daemonport[1])
|
port = int(daemonport[1])
|
||||||
else:
|
else:
|
||||||
port = coreapi.CORE_API_PORT
|
port = CORE_API_PORT
|
||||||
print "connecting to daemon at %s:%d" % (daemon, port)
|
print "connecting to daemon at %s:%d" % (daemon, port)
|
||||||
session.broker.addserver(daemon, daemonip, port)
|
session.broker.addserver(daemon, daemonip, port)
|
||||||
|
|
||||||
# Set the local session id to match the port.
|
# Set the local session id to match the port.
|
||||||
# Not necessary but seems neater.
|
# Not necessary but seems neater.
|
||||||
session.sessionid = session.broker.getserver('localhost')[2].getsockname()[1]
|
# session.sessionid = session.broker.getserver("localhost")[2].getsockname()[1]
|
||||||
session.broker.setupserver(daemon)
|
session.broker.setupserver(daemon)
|
||||||
|
|
||||||
# We do not want the recvloop running as we will deal ourselves
|
# We do not want the recvloop running as we will deal ourselves
|
||||||
session.broker.dorecvloop = False
|
session.broker.dorecvloop = False
|
||||||
|
|
||||||
# Change to configuration state on both machines
|
# Change to configuration state on both machines
|
||||||
session.setstate(coreapi.CORE_EVENT_CONFIGURATION_STATE)
|
session.set_state(EventTypes.CONFIGURATION_STATE.value)
|
||||||
tlvdata = coreapi.CoreEventTlv.pack(coreapi.CORE_TLV_EVENT_TYPE,
|
tlvdata = coreapi.CoreEventTlv.pack(EventTlvs.TYPE.value, EventTypes.CONFIGURATION_STATE.value)
|
||||||
coreapi.CORE_EVENT_CONFIGURATION_STATE)
|
|
||||||
session.broker.handlerawmsg(coreapi.CoreEventMessage.pack(0, tlvdata))
|
session.broker.handlerawmsg(coreapi.CoreEventMessage.pack(0, tlvdata))
|
||||||
|
|
||||||
flags = coreapi.CORE_API_ADD_FLAG
|
flags = MessageFlags.ADD.value
|
||||||
switch = pycore.nodes.SwitchNode(session = session, name='switch', start=False)
|
switch = nodes.SwitchNode(session=session, name="switch", start=False)
|
||||||
switch.setposition(x=80, y=50)
|
switch.setposition(x=80, y=50)
|
||||||
switch.server = daemon
|
switch.server = daemon
|
||||||
session.broker.handlerawmsg(switch.tonodemsg(flags=flags))
|
switch_data = switch.data(flags)
|
||||||
|
switch_message = dataconversion.convert_node(switch_data)
|
||||||
|
session.broker.handlerawmsg(switch_message)
|
||||||
|
|
||||||
numberOfNodes = options.numnodes
|
number_of_nodes = options.numnodes
|
||||||
|
|
||||||
print "creating %d remote nodes with addresses from %s" % \
|
print "creating %d remote nodes with addresses from %s" % (options.numnodes, prefix)
|
||||||
(options.numnodes, prefix)
|
|
||||||
|
|
||||||
# create remote nodes via API
|
# create remote nodes via API
|
||||||
for i in xrange(1, numberOfNodes + 1):
|
for i in xrange(1, number_of_nodes + 1):
|
||||||
tmp = pycore.nodes.CoreNode(session = session, objid = i,
|
node = nodes.CoreNode(session=session, objid=i, name="n%d" % i, start=False)
|
||||||
name = "n%d" % i, start=False)
|
node.setposition(x=150 * i, y=150)
|
||||||
tmp.setposition(x=150*i,y=150)
|
node.server = daemon
|
||||||
tmp.server = daemon
|
node_data = node.data(flags)
|
||||||
session.broker.handlerawmsg(tmp.tonodemsg(flags=flags))
|
node_message = dataconversion.convert_node(node_data)
|
||||||
n.append(tmp)
|
session.broker.handlerawmsg(node_message)
|
||||||
|
n.append(node)
|
||||||
|
|
||||||
# create remote links via API
|
# create remote links via API
|
||||||
for i in xrange(1, numberOfNodes + 1):
|
for i in xrange(1, number_of_nodes + 1):
|
||||||
tlvdata = coreapi.CoreLinkTlv.pack(coreapi.CORE_TLV_LINK_N1NUMBER,
|
tlvdata = coreapi.CoreLinkTlv.pack(LinkTlvs.N1_NUMBER.value, switch.objid)
|
||||||
switch.objid)
|
tlvdata += coreapi.CoreLinkTlv.pack(LinkTlvs.N2_NUMBER.value, i)
|
||||||
tlvdata += coreapi.CoreLinkTlv.pack(coreapi.CORE_TLV_LINK_N2NUMBER, i)
|
tlvdata += coreapi.CoreLinkTlv.pack(LinkTlvs.TYPE.value, LinkTypes.WIRED.value)
|
||||||
tlvdata += coreapi.CoreLinkTlv.pack(coreapi.CORE_TLV_LINK_TYPE,
|
tlvdata += coreapi.CoreLinkTlv.pack(LinkTlvs.INTERFACE2_NUMBER.value, 0)
|
||||||
coreapi.CORE_LINK_WIRED)
|
tlvdata += coreapi.CoreLinkTlv.pack(LinkTlvs.INTERFACE2_IP4.value, prefix.addr(i))
|
||||||
tlvdata += coreapi.CoreLinkTlv.pack(coreapi.CORE_TLV_LINK_IF2NUM, 0)
|
tlvdata += coreapi.CoreLinkTlv.pack(LinkTlvs.INTERFACE2_IP4_MASK.value, prefix.prefixlen)
|
||||||
tlvdata += coreapi.CoreLinkTlv.pack(coreapi.CORE_TLV_LINK_IF2IP4,
|
|
||||||
prefix.addr(i))
|
|
||||||
tlvdata += coreapi.CoreLinkTlv.pack(coreapi.CORE_TLV_LINK_IF2IP4MASK,
|
|
||||||
prefix.prefixlen)
|
|
||||||
msg = coreapi.CoreLinkMessage.pack(flags, tlvdata)
|
msg = coreapi.CoreLinkMessage.pack(flags, tlvdata)
|
||||||
session.broker.handlerawmsg(msg)
|
session.broker.handlerawmsg(msg)
|
||||||
|
|
||||||
# We change the daemon to Instantiation state
|
# We change the daemon to Instantiation state
|
||||||
# We do not change the local session as it would try and build a tunnel and fail
|
# We do not change the local session as it would try and build a tunnel and fail
|
||||||
tlvdata = coreapi.CoreEventTlv.pack(coreapi.CORE_TLV_EVENT_TYPE,
|
tlvdata = coreapi.CoreEventTlv.pack(EventTlvs.TYPE.value, EventTypes.INSTANTIATION_STATE.value)
|
||||||
coreapi.CORE_EVENT_INSTANTIATION_STATE)
|
|
||||||
msg = coreapi.CoreEventMessage.pack(0, tlvdata)
|
msg = coreapi.CoreEventMessage.pack(0, tlvdata)
|
||||||
session.broker.handlerawmsg(msg)
|
session.broker.handlerawmsg(msg)
|
||||||
|
|
||||||
# Get the ip or last node and ping it from the first
|
# Get the ip or last node and ping it from the first
|
||||||
print 'Pinging from the first to the last node'
|
print "Pinging from the first to the last node"
|
||||||
pingip = cmd(n[-1], 'ip -4 -o addr show dev eth0').split()[3].split('/')[0]
|
pingip = cmd(n[-1], "ip -4 -o addr show dev eth0").split()[3].split("/")[0]
|
||||||
print cmd(n[1], 'ping -c 5 ' + pingip)
|
print cmd(n[1], "ping -c 5 " + pingip)
|
||||||
|
|
||||||
print "elapsed time: %s" % (datetime.datetime.now() - start)
|
print "elapsed time: %s" % (datetime.datetime.now() - start)
|
||||||
|
|
||||||
print "To stop this session, use the 'core-cleanup' script on the remote daemon server."
|
print "To stop this session, use the core-cleanup script on the remote daemon server."
|
||||||
|
raw_input("press enter to exit")
|
||||||
|
|
||||||
|
|
||||||
if __name__ == "__main__" or __name__ == "__builtin__":
|
if __name__ == "__main__" or __name__ == "__builtin__":
|
||||||
main()
|
# configure nodes to use
|
||||||
|
node_map = nodemaps.CLASSIC_NODES
|
||||||
|
nodeutils.set_node_map(node_map)
|
||||||
|
|
||||||
|
main()
|
||||||
|
|
|
||||||
|
|
@ -9,22 +9,21 @@
|
||||||
# running the daemon with listenaddr=0.0.0.0 in the core.conf file.
|
# running the daemon with listenaddr=0.0.0.0 in the core.conf file.
|
||||||
#
|
#
|
||||||
|
|
||||||
import sys, datetime, optparse, time
|
import datetime
|
||||||
|
import optparse
|
||||||
|
import sys
|
||||||
|
|
||||||
from core import pycore
|
from core import constants
|
||||||
from core.misc import ipaddr
|
from core.api import coreapi, dataconversion
|
||||||
from core.constants import *
|
from core.enumerations import CORE_API_PORT, EventTypes, EventTlvs, LinkTlvs, LinkTypes, MessageFlags
|
||||||
from core.api import coreapi
|
from core.misc import ipaddress, nodeutils, nodemaps
|
||||||
|
from core.netns import nodes
|
||||||
# declare classes for use with Broker
|
from core.session import Session
|
||||||
coreapi.add_node_class("CORE_NODE_DEF",
|
|
||||||
coreapi.CORE_NODE_DEF, pycore.nodes.CoreNode)
|
|
||||||
coreapi.add_node_class("CORE_NODE_SWITCH",
|
|
||||||
coreapi.CORE_NODE_SWITCH, pycore.nodes.SwitchNode)
|
|
||||||
|
|
||||||
# node list (count from 1)
|
# node list (count from 1)
|
||||||
n = [None]
|
n = [None]
|
||||||
|
|
||||||
|
|
||||||
def main():
|
def main():
|
||||||
usagestr = "usage: %prog [-h] [options] [args]"
|
usagestr = "usage: %prog [-h] [options] [args]"
|
||||||
parser = optparse.OptionParser(usage=usagestr)
|
parser = optparse.OptionParser(usage=usagestr)
|
||||||
|
|
@ -55,8 +54,8 @@ def main():
|
||||||
|
|
||||||
start = datetime.datetime.now()
|
start = datetime.datetime.now()
|
||||||
|
|
||||||
prefix = ipaddr.IPv4Prefix("10.83.0.0/16")
|
prefix = ipaddress.Ipv4Prefix("10.83.0.0/16")
|
||||||
session = pycore.Session(persistent=True)
|
session = Session(1, persistent=True)
|
||||||
if 'server' in globals():
|
if 'server' in globals():
|
||||||
server.addsession(session)
|
server.addsession(session)
|
||||||
|
|
||||||
|
|
@ -66,59 +65,53 @@ def main():
|
||||||
if len(slaveport) > 1:
|
if len(slaveport) > 1:
|
||||||
port = int(slaveport[1])
|
port = int(slaveport[1])
|
||||||
else:
|
else:
|
||||||
port = coreapi.CORE_API_PORT
|
port = CORE_API_PORT
|
||||||
print "connecting to slave at %s:%d" % (slave, port)
|
print "connecting to slave at %s:%d" % (slave, port)
|
||||||
session.broker.addserver(slave, slave, port)
|
session.broker.addserver(slave, slave, port)
|
||||||
session.broker.setupserver(slave)
|
session.broker.setupserver(slave)
|
||||||
session.setstate(coreapi.CORE_EVENT_CONFIGURATION_STATE)
|
session.set_state(EventTypes.CONFIGURATION_STATE.value)
|
||||||
tlvdata = coreapi.CoreEventTlv.pack(coreapi.CORE_TLV_EVENT_TYPE,
|
tlvdata = coreapi.CoreEventTlv.pack(EventTlvs.TYPE.value, EventTypes.CONFIGURATION_STATE.value)
|
||||||
coreapi.CORE_EVENT_CONFIGURATION_STATE)
|
|
||||||
session.broker.handlerawmsg(coreapi.CoreEventMessage.pack(0, tlvdata))
|
session.broker.handlerawmsg(coreapi.CoreEventMessage.pack(0, tlvdata))
|
||||||
|
|
||||||
switch = session.addobj(cls = pycore.nodes.SwitchNode, name = "switch")
|
switch = session.add_object(cls=nodes.SwitchNode, name="switch")
|
||||||
switch.setposition(x=80, y=50)
|
switch.setposition(x=80, y=50)
|
||||||
num_local = options.numnodes / 2
|
num_local = options.numnodes / 2
|
||||||
num_remote = options.numnodes / 2 + options.numnodes % 2
|
num_remote = options.numnodes / 2 + options.numnodes % 2
|
||||||
print "creating %d (%d local / %d remote) nodes with addresses from %s" % \
|
print "creating %d (%d local / %d remote) nodes with addresses from %s" % \
|
||||||
(options.numnodes, num_local, num_remote, prefix)
|
(options.numnodes, num_local, num_remote, prefix)
|
||||||
for i in xrange(1, num_local + 1):
|
for i in xrange(1, num_local + 1):
|
||||||
tmp = session.addobj(cls = pycore.nodes.CoreNode, name = "n%d" % i,
|
node = session.add_object(cls=nodes.CoreNode, name="n%d" % i, objid=i)
|
||||||
objid=i)
|
node.newnetif(switch, ["%s/%s" % (prefix.addr(i), prefix.prefixlen)])
|
||||||
tmp.newnetif(switch, ["%s/%s" % (prefix.addr(i), prefix.prefixlen)])
|
node.cmd([constants.SYSCTL_BIN, "net.ipv4.icmp_echo_ignore_broadcasts=0"])
|
||||||
tmp.cmd([SYSCTL_BIN, "net.ipv4.icmp_echo_ignore_broadcasts=0"])
|
node.setposition(x=150 * i, y=150)
|
||||||
tmp.setposition(x=150*i,y=150)
|
n.append(node)
|
||||||
n.append(tmp)
|
|
||||||
|
|
||||||
flags = coreapi.CORE_API_ADD_FLAG
|
flags = MessageFlags.ADD.value
|
||||||
session.broker.handlerawmsg(switch.tonodemsg(flags=flags))
|
session.broker.handlerawmsg(switch.tonodemsg(flags=flags))
|
||||||
|
|
||||||
# create remote nodes via API
|
# create remote nodes via API
|
||||||
for i in xrange(num_local + 1, options.numnodes + 1):
|
for i in xrange(num_local + 1, options.numnodes + 1):
|
||||||
tmp = pycore.nodes.CoreNode(session = session, objid = i,
|
node = nodes.CoreNode(session=session, objid=i, name="n%d" % i, start=False)
|
||||||
name = "n%d" % i, start=False)
|
node.setposition(x=150 * i, y=150)
|
||||||
tmp.setposition(x=150*i,y=150)
|
node.server = slave
|
||||||
tmp.server = slave
|
n.append(node)
|
||||||
n.append(tmp)
|
node_data = node.data(flags)
|
||||||
session.broker.handlerawmsg(tmp.tonodemsg(flags=flags))
|
node_message = dataconversion.convert_node(node_data)
|
||||||
|
session.broker.handlerawmsg(node_message)
|
||||||
|
|
||||||
# create remote links via API
|
# create remote links via API
|
||||||
for i in xrange(num_local + 1, options.numnodes + 1):
|
for i in xrange(num_local + 1, options.numnodes + 1):
|
||||||
tlvdata = coreapi.CoreLinkTlv.pack(coreapi.CORE_TLV_LINK_N1NUMBER,
|
tlvdata = coreapi.CoreLinkTlv.pack(LinkTlvs.N1_NUMBER.value, switch.objid)
|
||||||
switch.objid)
|
tlvdata += coreapi.CoreLinkTlv.pack(LinkTlvs.N2_NUMBER.value, i)
|
||||||
tlvdata += coreapi.CoreLinkTlv.pack(coreapi.CORE_TLV_LINK_N2NUMBER, i)
|
tlvdata += coreapi.CoreLinkTlv.pack(LinkTlvs.TYPE.value, LinkTypes.WIRED.value)
|
||||||
tlvdata += coreapi.CoreLinkTlv.pack(coreapi.CORE_TLV_LINK_TYPE,
|
tlvdata += coreapi.CoreLinkTlv.pack(LinkTlvs.INTERFACE2_NUMBER.value, 0)
|
||||||
coreapi.CORE_LINK_WIRED)
|
tlvdata += coreapi.CoreLinkTlv.pack(LinkTlvs.INTERFACE2_IP4.value, prefix.addr(i))
|
||||||
tlvdata += coreapi.CoreLinkTlv.pack(coreapi.CORE_TLV_LINK_IF2NUM, 0)
|
tlvdata += coreapi.CoreLinkTlv.pack(LinkTlvs.INTERFACE2_IP4_MASK.value, prefix.prefixlen)
|
||||||
tlvdata += coreapi.CoreLinkTlv.pack(coreapi.CORE_TLV_LINK_IF2IP4,
|
|
||||||
prefix.addr(i))
|
|
||||||
tlvdata += coreapi.CoreLinkTlv.pack(coreapi.CORE_TLV_LINK_IF2IP4MASK,
|
|
||||||
prefix.prefixlen)
|
|
||||||
msg = coreapi.CoreLinkMessage.pack(flags, tlvdata)
|
msg = coreapi.CoreLinkMessage.pack(flags, tlvdata)
|
||||||
session.broker.handlerawmsg(msg)
|
session.broker.handlerawmsg(msg)
|
||||||
|
|
||||||
session.instantiate()
|
session.instantiate()
|
||||||
tlvdata = coreapi.CoreEventTlv.pack(coreapi.CORE_TLV_EVENT_TYPE,
|
tlvdata = coreapi.CoreEventTlv.pack(EventTlvs.TYPE.value, EventTypes.INSTANTIATION_STATE.value)
|
||||||
coreapi.CORE_EVENT_INSTANTIATION_STATE)
|
|
||||||
msg = coreapi.CoreEventMessage.pack(0, tlvdata)
|
msg = coreapi.CoreEventMessage.pack(0, tlvdata)
|
||||||
session.broker.handlerawmsg(msg)
|
session.broker.handlerawmsg(msg)
|
||||||
|
|
||||||
|
|
@ -132,6 +125,10 @@ def main():
|
||||||
print "To stop this session, use the 'core-cleanup' script on this server"
|
print "To stop this session, use the 'core-cleanup' script on this server"
|
||||||
print "and on the remote slave server."
|
print "and on the remote slave server."
|
||||||
|
|
||||||
if __name__ == "__main__" or __name__ == "__builtin__":
|
|
||||||
main()
|
|
||||||
|
|
||||||
|
if __name__ == "__main__" or __name__ == "__builtin__":
|
||||||
|
# configure nodes to use
|
||||||
|
node_map = nodemaps.CLASSIC_NODES
|
||||||
|
nodeutils.set_node_map(node_map)
|
||||||
|
|
||||||
|
main()
|
||||||
|
|
|
||||||
|
|
@ -6,16 +6,22 @@
|
||||||
# Example CORE Python script that attaches N nodes to an EMANE 802.11abg
|
# Example CORE Python script that attaches N nodes to an EMANE 802.11abg
|
||||||
# network. One of the parameters is changed, the pathloss mode.
|
# network. One of the parameters is changed, the pathloss mode.
|
||||||
|
|
||||||
import sys, datetime, optparse
|
import datetime
|
||||||
|
import optparse
|
||||||
|
import sys
|
||||||
|
|
||||||
from core import pycore
|
from core import constants
|
||||||
from core.misc import ipaddr
|
|
||||||
from core.constants import *
|
|
||||||
from core.emane.ieee80211abg import EmaneIeee80211abgModel
|
from core.emane.ieee80211abg import EmaneIeee80211abgModel
|
||||||
|
from core.emane.nodes import EmaneNode
|
||||||
|
from core.misc import ipaddress, nodeutils, nodemaps
|
||||||
|
from core.netns import nodes
|
||||||
|
|
||||||
# node list (count from 1)
|
# node list (count from 1)
|
||||||
|
from core.session import Session
|
||||||
|
|
||||||
n = [None]
|
n = [None]
|
||||||
|
|
||||||
|
|
||||||
def main():
|
def main():
|
||||||
usagestr = "usage: %prog [-h] [options] [args]"
|
usagestr = "usage: %prog [-h] [options] [args]"
|
||||||
parser = optparse.OptionParser(usage=usagestr)
|
parser = optparse.OptionParser(usage=usagestr)
|
||||||
|
|
@ -43,21 +49,21 @@ def main():
|
||||||
start = datetime.datetime.now()
|
start = datetime.datetime.now()
|
||||||
|
|
||||||
# IP subnet
|
# IP subnet
|
||||||
prefix = ipaddr.IPv4Prefix("10.83.0.0/16")
|
prefix = ipaddress.Ipv4Prefix("10.83.0.0/16")
|
||||||
# session with some EMANE initialization
|
# session with some EMANE initialization
|
||||||
cfg = {'verbose': 'false'}
|
cfg = {'verbose': 'false'}
|
||||||
session = pycore.Session(cfg = cfg, persistent = True)
|
session = Session(1, config=cfg, persistent=True)
|
||||||
session.master = True
|
session.master = True
|
||||||
session.location.setrefgeo(47.57917, -122.13232, 2.00000)
|
session.location.setrefgeo(47.57917, -122.13232, 2.00000)
|
||||||
session.location.refscale = 150.0
|
session.location.refscale = 150.0
|
||||||
session.cfg['emane_models'] = "RfPipe, Ieee80211abg, Bypass"
|
session.config['emane_models'] = "RfPipe, Ieee80211abg, Bypass"
|
||||||
session.emane.loadmodels()
|
session.emane.loadmodels()
|
||||||
if 'server' in globals():
|
if 'server' in globals():
|
||||||
server.addsession(session)
|
server.addsession(session)
|
||||||
|
|
||||||
# EMANE WLAN
|
# EMANE WLAN
|
||||||
print "creating EMANE WLAN wlan1"
|
print "creating EMANE WLAN wlan1"
|
||||||
wlan = session.addobj(cls = pycore.nodes.EmaneNode, name = "wlan1")
|
wlan = session.add_object(cls=EmaneNode, name="wlan1")
|
||||||
wlan.setposition(x=80, y=50)
|
wlan.setposition(x=80, y=50)
|
||||||
names = EmaneIeee80211abgModel.getnames()
|
names = EmaneIeee80211abgModel.getnames()
|
||||||
values = list(EmaneIeee80211abgModel.getdefaultvalues())
|
values = list(EmaneIeee80211abgModel.getdefaultvalues())
|
||||||
|
|
@ -69,18 +75,18 @@ def main():
|
||||||
except ValueError:
|
except ValueError:
|
||||||
values[names.index('propagationmodel')] = '2ray'
|
values[names.index('propagationmodel')] = '2ray'
|
||||||
|
|
||||||
session.emane.setconfig(wlan.objid, EmaneIeee80211abgModel._name, values)
|
session.emane.setconfig(wlan.objid, EmaneIeee80211abgModel.name, values)
|
||||||
services_str = "zebra|OSPFv3MDR|IPForward"
|
services_str = "zebra|OSPFv3MDR|IPForward"
|
||||||
|
|
||||||
print "creating %d nodes with addresses from %s" % \
|
print "creating %d nodes with addresses from %s" % \
|
||||||
(options.numnodes, prefix)
|
(options.numnodes, prefix)
|
||||||
for i in xrange(1, options.numnodes + 1):
|
for i in xrange(1, options.numnodes + 1):
|
||||||
tmp = session.addobj(cls = pycore.nodes.CoreNode, name = "n%d" % i,
|
tmp = session.add_object(cls=nodes.CoreNode, name="n%d" % i,
|
||||||
objid=i)
|
objid=i)
|
||||||
tmp.newnetif(wlan, ["%s/%s" % (prefix.addr(i), prefix.prefixlen)])
|
tmp.newnetif(wlan, ["%s/%s" % (prefix.addr(i), prefix.prefixlen)])
|
||||||
tmp.cmd([SYSCTL_BIN, "net.ipv4.icmp_echo_ignore_broadcasts=0"])
|
tmp.cmd([constants.SYSCTL_BIN, "net.ipv4.icmp_echo_ignore_broadcasts=0"])
|
||||||
tmp.setposition(x=150 * i, y=150)
|
tmp.setposition(x=150 * i, y=150)
|
||||||
session.services.addservicestonode(tmp, "", services_str, verbose=False)
|
session.services.addservicestonode(tmp, "", services_str)
|
||||||
n.append(tmp)
|
n.append(tmp)
|
||||||
|
|
||||||
# this starts EMANE, etc.
|
# this starts EMANE, etc.
|
||||||
|
|
@ -92,6 +98,10 @@ def main():
|
||||||
|
|
||||||
print "elapsed time: %s" % (datetime.datetime.now() - start)
|
print "elapsed time: %s" % (datetime.datetime.now() - start)
|
||||||
|
|
||||||
if __name__ == "__main__" or __name__ == "__builtin__":
|
|
||||||
main()
|
|
||||||
|
|
||||||
|
if __name__ == "__main__" or __name__ == "__builtin__":
|
||||||
|
# configure nodes to use
|
||||||
|
node_map = nodemaps.CLASSIC_NODES
|
||||||
|
nodeutils.set_node_map(node_map)
|
||||||
|
|
||||||
|
main()
|
||||||
|
|
|
||||||
|
|
@ -5,49 +5,48 @@
|
||||||
#
|
#
|
||||||
# author: Jeff Ahrenholz <jeffrey.m.ahrenholz@boeing.com>
|
# author: Jeff Ahrenholz <jeffrey.m.ahrenholz@boeing.com>
|
||||||
#
|
#
|
||||||
'''
|
|
||||||
|
"""
|
||||||
howmanynodes.py - This is a CORE script that creates network namespace nodes
|
howmanynodes.py - This is a CORE script that creates network namespace nodes
|
||||||
having one virtual Ethernet interface connected to a bridge. It continues to
|
having one virtual Ethernet interface connected to a bridge. It continues to
|
||||||
add nodes until an exception occurs. The number of nodes per bridge can be
|
add nodes until an exception occurs. The number of nodes per bridge can be
|
||||||
specified.
|
specified.
|
||||||
'''
|
"""
|
||||||
|
|
||||||
import optparse, sys, os, datetime, time, shutil
|
import datetime
|
||||||
try:
|
import optparse
|
||||||
from core import pycore
|
import shutil
|
||||||
except ImportError:
|
import sys
|
||||||
# hack for Fedora autoconf that uses the following pythondir:
|
import time
|
||||||
if "/usr/lib/python2.6/site-packages" in sys.path:
|
|
||||||
sys.path.append("/usr/local/lib/python2.6/site-packages")
|
from core import constants
|
||||||
if "/usr/lib64/python2.6/site-packages" in sys.path:
|
from core.misc import ipaddress, nodeutils, nodemaps
|
||||||
sys.path.append("/usr/local/lib64/python2.6/site-packages")
|
from core.netns import nodes
|
||||||
if "/usr/lib/python2.7/site-packages" in sys.path:
|
from core.session import Session
|
||||||
sys.path.append("/usr/local/lib/python2.7/site-packages")
|
|
||||||
if "/usr/lib64/python2.7/site-packages" in sys.path:
|
|
||||||
sys.path.append("/usr/local/lib64/python2.7/site-packages")
|
|
||||||
from core import pycore
|
|
||||||
from core.misc import ipaddr
|
|
||||||
from core.constants import *
|
|
||||||
|
|
||||||
GBD = 1024.0 * 1024.0
|
GBD = 1024.0 * 1024.0
|
||||||
|
|
||||||
|
|
||||||
def linuxversion():
|
def linuxversion():
|
||||||
''' Return a string having the Linux kernel version.
|
""" Return a string having the Linux kernel version.
|
||||||
'''
|
"""
|
||||||
f = open('/proc/version', 'r')
|
f = open("/proc/version", "r")
|
||||||
v = f.readline().split()
|
v = f.readline().split()
|
||||||
version_str = ' '.join(v[:3])
|
version_str = " ".join(v[:3])
|
||||||
f.close()
|
f.close()
|
||||||
return version_str
|
return version_str
|
||||||
|
|
||||||
MEMKEYS = ('total', 'free', 'buff', 'cached', 'stotal', 'sfree')
|
|
||||||
|
MEMKEYS = ("total", "free", "buff", "cached", "stotal", "sfree")
|
||||||
|
|
||||||
|
|
||||||
def memfree():
|
def memfree():
|
||||||
''' Returns kilobytes memory [total, free, buff, cached, stotal, sfree].
|
""" Returns kilobytes memory [total, free, buff, cached, stotal, sfree].
|
||||||
useful stats are:
|
useful stats are:
|
||||||
free memory = free + buff + cached
|
free memory = free + buff + cached
|
||||||
swap used = stotal - sfree
|
swap used = stotal - sfree
|
||||||
'''
|
"""
|
||||||
f = open('/proc/meminfo', 'r')
|
f = open("/proc/meminfo", "r")
|
||||||
lines = f.readlines()
|
lines = f.readlines()
|
||||||
f.close()
|
f.close()
|
||||||
kbs = {}
|
kbs = {}
|
||||||
|
|
@ -55,20 +54,21 @@ def memfree():
|
||||||
kbs[k] = 0
|
kbs[k] = 0
|
||||||
for l in lines:
|
for l in lines:
|
||||||
if l[:9] == "MemTotal:":
|
if l[:9] == "MemTotal:":
|
||||||
kbs['total'] = int(l.split()[1])
|
kbs["total"] = int(l.split()[1])
|
||||||
elif l[:8] == "MemFree:":
|
elif l[:8] == "MemFree:":
|
||||||
kbs['free'] = int(l.split()[1])
|
kbs["free"] = int(l.split()[1])
|
||||||
elif l[:8] == "Buffers:":
|
elif l[:8] == "Buffers:":
|
||||||
kbs['buff'] = int(l.split()[1])
|
kbs["buff"] = int(l.split()[1])
|
||||||
elif l[:8] == "Cached:":
|
elif l[:8] == "Cached:":
|
||||||
kbs['cache'] = int(l.split()[1])
|
kbs["cache"] = int(l.split()[1])
|
||||||
elif l[:10] == "SwapTotal:":
|
elif l[:10] == "SwapTotal:":
|
||||||
kbs['stotal'] = int(l.split()[1])
|
kbs["stotal"] = int(l.split()[1])
|
||||||
elif l[:9] == "SwapFree:":
|
elif l[:9] == "SwapFree:":
|
||||||
kbs['sfree'] = int(l.split()[1])
|
kbs["sfree"] = int(l.split()[1])
|
||||||
break
|
break
|
||||||
return kbs
|
return kbs
|
||||||
|
|
||||||
|
|
||||||
# node list (count from 1)
|
# node list (count from 1)
|
||||||
nodelist = [None]
|
nodelist = [None]
|
||||||
switchlist = []
|
switchlist = []
|
||||||
|
|
@ -95,9 +95,9 @@ def main():
|
||||||
help="log memory usage to this file (default = %s)" % \
|
help="log memory usage to this file (default = %s)" % \
|
||||||
parser.defaults["logfile"])
|
parser.defaults["logfile"])
|
||||||
parser.add_option("-s", "--services", dest="services", type=str,
|
parser.add_option("-s", "--services", dest="services", type=str,
|
||||||
help = "pipe-delimited list of services added to each " \
|
help="pipe-delimited list of services added to each "
|
||||||
"node (default = %s)\n(Example: 'zebra|OSPFv2|OSPFv3|" \
|
"node (default = %s)\n(Example: zebra|OSPFv2|OSPFv3|"
|
||||||
"IPForward')" % parser.defaults["services"])
|
"IPForward)" % parser.defaults["services"])
|
||||||
|
|
||||||
def usage(msg=None, err=0):
|
def usage(msg=None, err=0):
|
||||||
sys.stdout.write("\n")
|
sys.stdout.write("\n")
|
||||||
|
|
@ -109,16 +109,16 @@ def main():
|
||||||
(options, args) = parser.parse_args()
|
(options, args) = parser.parse_args()
|
||||||
|
|
||||||
for a in args:
|
for a in args:
|
||||||
sys.stderr.write("ignoring command line argument: '%s'\n" % a)
|
sys.stderr.write("ignoring command line argument: %s\n" % a)
|
||||||
|
|
||||||
start = datetime.datetime.now()
|
start = datetime.datetime.now()
|
||||||
prefix = ipaddr.IPv4Prefix("10.83.0.0/16")
|
prefix = ipaddress.Ipv4Prefix("10.83.0.0/16")
|
||||||
|
|
||||||
print "Testing how many network namespace nodes this machine can create."
|
print "Testing how many network namespace nodes this machine can create."
|
||||||
print " - %s" % linuxversion()
|
print " - %s" % linuxversion()
|
||||||
mem = memfree()
|
mem = memfree()
|
||||||
print " - %.02f GB total memory (%.02f GB swap)" % \
|
print " - %.02f GB total memory (%.02f GB swap)" % \
|
||||||
(mem['total']/GBD, mem['stotal']/GBD)
|
(mem["total"] / GBD, mem["stotal"] / GBD)
|
||||||
print " - using IPv4 network prefix %s" % prefix
|
print " - using IPv4 network prefix %s" % prefix
|
||||||
print " - using wait time of %s" % options.waittime
|
print " - using wait time of %s" % options.waittime
|
||||||
print " - using %d nodes per bridge" % options.bridges
|
print " - using %d nodes per bridge" % options.bridges
|
||||||
|
|
@ -132,11 +132,11 @@ def main():
|
||||||
lfp = open(options.logfile, "a")
|
lfp = open(options.logfile, "a")
|
||||||
lfp.write("# log from howmanynodes.py %s\n" % time.ctime())
|
lfp.write("# log from howmanynodes.py %s\n" % time.ctime())
|
||||||
lfp.write("# options = %s\n#\n" % options)
|
lfp.write("# options = %s\n#\n" % options)
|
||||||
lfp.write("# numnodes,%s\n" % ','.join(MEMKEYS))
|
lfp.write("# numnodes,%s\n" % ",".join(MEMKEYS))
|
||||||
lfp.flush()
|
lfp.flush()
|
||||||
|
|
||||||
session = pycore.Session(persistent=True)
|
session = Session(1, persistent=True)
|
||||||
switch = session.addobj(cls = pycore.nodes.SwitchNode)
|
switch = session.add_object(cls=nodes.SwitchNode)
|
||||||
switchlist.append(switch)
|
switchlist.append(switch)
|
||||||
print "Added bridge %s (%d)." % (switch.brname, len(switchlist))
|
print "Added bridge %s (%d)." % (switch.brname, len(switchlist))
|
||||||
|
|
||||||
|
|
@ -147,7 +147,7 @@ def main():
|
||||||
# optionally add a bridge (options.bridges nodes per bridge)
|
# optionally add a bridge (options.bridges nodes per bridge)
|
||||||
try:
|
try:
|
||||||
if options.bridges > 0 and switch.numnetif() >= options.bridges:
|
if options.bridges > 0 and switch.numnetif() >= options.bridges:
|
||||||
switch = session.addobj(cls = pycore.nodes.SwitchNode)
|
switch = session.add_object(cls=nodes.SwitchNode)
|
||||||
switchlist.append(switch)
|
switchlist.append(switch)
|
||||||
print "\nAdded bridge %s (%d) for node %d." % \
|
print "\nAdded bridge %s (%d) for node %d." % \
|
||||||
(switch.brname, len(switchlist), i)
|
(switch.brname, len(switchlist), i)
|
||||||
|
|
@ -157,23 +157,22 @@ def main():
|
||||||
break
|
break
|
||||||
# create a node
|
# create a node
|
||||||
try:
|
try:
|
||||||
n = session.addobj(cls = pycore.nodes.LxcNode, name = "n%d" % i)
|
n = session.add_object(cls=nodes.LxcNode, name="n%d" % i)
|
||||||
n.newnetif(switch, ["%s/%s" % (prefix.addr(i), prefix.prefixlen)])
|
n.newnetif(switch, ["%s/%s" % (prefix.addr(i), prefix.prefixlen)])
|
||||||
n.cmd([SYSCTL_BIN, "net.ipv4.icmp_echo_ignore_broadcasts=0"])
|
n.cmd([constants.SYSCTL_BIN, "net.ipv4.icmp_echo_ignore_broadcasts=0"])
|
||||||
if options.services is not None:
|
if options.services is not None:
|
||||||
session.services.addservicestonode(n, "", options.services,
|
session.services.addservicestonode(n, "", options.services)
|
||||||
verbose=False)
|
|
||||||
n.boot()
|
n.boot()
|
||||||
nodelist.append(n)
|
nodelist.append(n)
|
||||||
if i % 25 == 0:
|
if i % 25 == 0:
|
||||||
print "\n%s nodes created " % i,
|
print "\n%s nodes created " % i,
|
||||||
mem = memfree()
|
mem = memfree()
|
||||||
free = mem['free'] + mem['buff'] + mem['cached']
|
free = mem["free"] + mem["buff"] + mem["cached"]
|
||||||
swap = mem['stotal'] - mem['sfree']
|
swap = mem["stotal"] - mem["sfree"]
|
||||||
print "(%.02f/%.02f GB free/swap)" % (free / GBD, swap / GBD),
|
print "(%.02f/%.02f GB free/swap)" % (free / GBD, swap / GBD),
|
||||||
if lfp:
|
if lfp:
|
||||||
lfp.write("%d," % i)
|
lfp.write("%d," % i)
|
||||||
lfp.write("%s\n" % ','.join(str(mem[x]) for x in MEMKEYS))
|
lfp.write("%s\n" % ",".join(str(mem[x]) for x in MEMKEYS))
|
||||||
lfp.flush()
|
lfp.flush()
|
||||||
else:
|
else:
|
||||||
sys.stdout.write(".")
|
sys.stdout.write(".")
|
||||||
|
|
@ -205,5 +204,10 @@ def main():
|
||||||
print "elapsed time: %s" % (datetime.datetime.now() - start)
|
print "elapsed time: %s" % (datetime.datetime.now() - start)
|
||||||
print "Use the core-cleanup script to remove nodes and bridges."
|
print "Use the core-cleanup script to remove nodes and bridges."
|
||||||
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
|
# configure nodes to use
|
||||||
|
node_map = nodemaps.CLASSIC_NODES
|
||||||
|
nodeutils.set_node_map(node_map)
|
||||||
|
|
||||||
main()
|
main()
|
||||||
|
|
|
||||||
|
|
@ -14,15 +14,20 @@
|
||||||
# Use core-cleanup to clean up after this script as the session is left running.
|
# Use core-cleanup to clean up after this script as the session is left running.
|
||||||
#
|
#
|
||||||
|
|
||||||
import sys, datetime, optparse
|
import datetime
|
||||||
|
import optparse
|
||||||
|
import sys
|
||||||
|
|
||||||
from core import pycore
|
from core import constants
|
||||||
from core.misc import ipaddr
|
from core.misc import ipaddress, nodeutils, nodemaps
|
||||||
from core.constants import *
|
from core.netns import nodes
|
||||||
|
|
||||||
# node list (count from 1)
|
# node list (count from 1)
|
||||||
|
from core.session import Session
|
||||||
|
|
||||||
n = [None]
|
n = [None]
|
||||||
|
|
||||||
|
|
||||||
def main():
|
def main():
|
||||||
usagestr = "usage: %prog [-h] [options] [args]"
|
usagestr = "usage: %prog [-h] [options] [args]"
|
||||||
parser = optparse.OptionParser(usage=usagestr)
|
parser = optparse.OptionParser(usage=usagestr)
|
||||||
|
|
@ -52,29 +57,29 @@ def main():
|
||||||
|
|
||||||
start = datetime.datetime.now()
|
start = datetime.datetime.now()
|
||||||
|
|
||||||
session = pycore.Session(persistent=True)
|
session = Session(1, persistent=True)
|
||||||
if 'server' in globals():
|
if 'server' in globals():
|
||||||
server.addsession(session)
|
server.addsession(session)
|
||||||
print "creating %d nodes" % options.numnodes
|
print "creating %d nodes" % options.numnodes
|
||||||
left = None
|
left = None
|
||||||
prefix = None
|
prefix = None
|
||||||
for i in xrange(1, options.numnodes + 1):
|
for i in xrange(1, options.numnodes + 1):
|
||||||
tmp = session.addobj(cls = pycore.nodes.CoreNode, name = "n%d" % i,
|
tmp = session.add_object(cls=nodes.CoreNode, name="n%d" % i, objid=i)
|
||||||
objid=i)
|
|
||||||
if left:
|
if left:
|
||||||
tmp.newnetif(left, ["%s/%s" % (prefix.addr(2), prefix.prefixlen)])
|
tmp.newnetif(left, ["%s/%s" % (prefix.addr(2), prefix.prefixlen)])
|
||||||
|
|
||||||
prefix = ipaddr.IPv4Prefix("10.83.%d.0/24" % i) # limit: i < 255
|
# limit: i < 255
|
||||||
right = session.addobj(cls = pycore.nodes.PtpNet)
|
prefix = ipaddress.Ipv4Prefix("10.83.%d.0/24" % i)
|
||||||
|
right = session.add_object(cls=nodes.PtpNet)
|
||||||
tmp.newnetif(right, ["%s/%s" % (prefix.addr(1), prefix.prefixlen)])
|
tmp.newnetif(right, ["%s/%s" % (prefix.addr(1), prefix.prefixlen)])
|
||||||
tmp.cmd([SYSCTL_BIN, "net.ipv4.icmp_echo_ignore_broadcasts=0"])
|
tmp.cmd([constants.SYSCTL_BIN, "net.ipv4.icmp_echo_ignore_broadcasts=0"])
|
||||||
tmp.cmd([SYSCTL_BIN, "net.ipv4.conf.all.forwarding=1"])
|
tmp.cmd([constants.SYSCTL_BIN, "net.ipv4.conf.all.forwarding=1"])
|
||||||
tmp.cmd([SYSCTL_BIN, "net.ipv4.conf.default.rp_filter=0"])
|
tmp.cmd([constants.SYSCTL_BIN, "net.ipv4.conf.default.rp_filter=0"])
|
||||||
tmp.setposition(x=100 * i, y=150)
|
tmp.setposition(x=100 * i, y=150)
|
||||||
n.append(tmp)
|
n.append(tmp)
|
||||||
left = right
|
left = right
|
||||||
|
|
||||||
prefixes = map(lambda(x): ipaddr.IPv4Prefix("10.83.%d.0/24" % x),
|
prefixes = map(lambda (x): ipaddress.Ipv4Prefix("10.83.%d.0/24" % x),
|
||||||
xrange(1, options.numnodes + 1))
|
xrange(1, options.numnodes + 1))
|
||||||
|
|
||||||
# set up static routing in the chain
|
# set up static routing in the chain
|
||||||
|
|
@ -89,11 +94,14 @@ def main():
|
||||||
else:
|
else:
|
||||||
continue
|
continue
|
||||||
net = prefixes[j - 1]
|
net = prefixes[j - 1]
|
||||||
n[i].cmd([IP_BIN, "route", "add", str(net), "via", str(gw)])
|
n[i].cmd([constants.IP_BIN, "route", "add", str(net), "via", str(gw)])
|
||||||
|
|
||||||
|
|
||||||
print "elapsed time: %s" % (datetime.datetime.now() - start)
|
print "elapsed time: %s" % (datetime.datetime.now() - start)
|
||||||
|
|
||||||
if __name__ == "__main__" or __name__ == "__builtin__":
|
|
||||||
main()
|
|
||||||
|
|
||||||
|
if __name__ == "__main__" or __name__ == "__builtin__":
|
||||||
|
# configure nodes to use
|
||||||
|
node_map = nodemaps.CLASSIC_NODES
|
||||||
|
nodeutils.set_node_map(node_map)
|
||||||
|
|
||||||
|
main()
|
||||||
|
|
|
||||||
|
|
@ -7,26 +7,22 @@
|
||||||
# that all neighbor states are either full or two-way, and check the routes
|
# that all neighbor states are either full or two-way, and check the routes
|
||||||
# in zebra vs those installed in the kernel.
|
# in zebra vs those installed in the kernel.
|
||||||
|
|
||||||
import os, sys, random, time, optparse, datetime
|
import datetime
|
||||||
|
import optparse
|
||||||
|
import os
|
||||||
|
import random
|
||||||
|
import sys
|
||||||
|
import time
|
||||||
from string import Template
|
from string import Template
|
||||||
try:
|
|
||||||
from core import pycore
|
|
||||||
except ImportError:
|
|
||||||
# hack for Fedora autoconf that uses the following pythondir:
|
|
||||||
if "/usr/lib/python2.6/site-packages" in sys.path:
|
|
||||||
sys.path.append("/usr/local/lib/python2.6/site-packages")
|
|
||||||
if "/usr/lib64/python2.6/site-packages" in sys.path:
|
|
||||||
sys.path.append("/usr/local/lib64/python2.6/site-packages")
|
|
||||||
if "/usr/lib/python2.7/site-packages" in sys.path:
|
|
||||||
sys.path.append("/usr/local/lib/python2.7/site-packages")
|
|
||||||
if "/usr/lib64/python2.7/site-packages" in sys.path:
|
|
||||||
sys.path.append("/usr/local/lib64/python2.7/site-packages")
|
|
||||||
from core import pycore
|
|
||||||
from core.misc import ipaddr
|
|
||||||
from core.misc.utils import mutecall
|
|
||||||
from core.constants import QUAGGA_STATE_DIR
|
from core.constants import QUAGGA_STATE_DIR
|
||||||
|
from core.misc import ipaddress, nodeutils, nodemaps
|
||||||
|
from core.misc.utils import mutecall
|
||||||
|
from core.netns import nodes
|
||||||
|
|
||||||
# this is the /etc/core/core.conf default
|
# this is the /etc/core/core.conf default
|
||||||
|
from core.session import Session
|
||||||
|
|
||||||
quagga_sbin_search = ("/usr/local/sbin", "/usr/sbin", "/usr/lib/quagga")
|
quagga_sbin_search = ("/usr/local/sbin", "/usr/sbin", "/usr/lib/quagga")
|
||||||
quagga_path = "zebra"
|
quagga_path = "zebra"
|
||||||
|
|
||||||
|
|
@ -42,7 +38,8 @@ except OSError:
|
||||||
sys.stderr.write("ERROR: running zebra failed\n")
|
sys.stderr.write("ERROR: running zebra failed\n")
|
||||||
sys.exit(1)
|
sys.exit(1)
|
||||||
|
|
||||||
class ManetNode(pycore.nodes.LxcNode):
|
|
||||||
|
class ManetNode(nodes.LxcNode):
|
||||||
""" An Lxc namespace node configured for Quagga OSPFv3 MANET MDR
|
""" An Lxc namespace node configured for Quagga OSPFv3 MANET MDR
|
||||||
"""
|
"""
|
||||||
conftemp = Template("""\
|
conftemp = Template("""\
|
||||||
|
|
@ -72,7 +69,7 @@ ip forwarding
|
||||||
routerid = ipaddr.split("/")[0]
|
routerid = ipaddr.split("/")[0]
|
||||||
self.ipaddr = ipaddr
|
self.ipaddr = ipaddr
|
||||||
self.routerid = routerid
|
self.routerid = routerid
|
||||||
pycore.nodes.LxcNode.__init__(self, core, objid, name, nodedir)
|
nodes.LxcNode.__init__(self, core, objid, name, nodedir)
|
||||||
self.privatedir(self.confdir)
|
self.privatedir(self.confdir)
|
||||||
self.privatedir(QUAGGA_STATE_DIR)
|
self.privatedir(QUAGGA_STATE_DIR)
|
||||||
|
|
||||||
|
|
@ -125,11 +122,13 @@ waitfile $STATEDIR/ospf6d.vty
|
||||||
vtysh -b
|
vtysh -b
|
||||||
""" % (QUAGGA_STATE_DIR, quagga_path, quagga_path)
|
""" % (QUAGGA_STATE_DIR, quagga_path, quagga_path)
|
||||||
|
|
||||||
|
|
||||||
class Route(object):
|
class Route(object):
|
||||||
""" Helper class for organzing routing table entries. """
|
""" Helper class for organzing routing table entries. """
|
||||||
|
|
||||||
def __init__(self, prefix=None, gw=None, metric=None):
|
def __init__(self, prefix=None, gw=None, metric=None):
|
||||||
try:
|
try:
|
||||||
self.prefix = ipaddr.IPv4Prefix(prefix)
|
self.prefix = ipaddress.Ipv4Prefix(prefix)
|
||||||
except Exception, e:
|
except Exception, e:
|
||||||
raise ValueError, "Invalid prefix given to Route object: %s\n%s" % \
|
raise ValueError, "Invalid prefix given to Route object: %s\n%s" % \
|
||||||
(prefix, e)
|
(prefix, e)
|
||||||
|
|
@ -156,6 +155,7 @@ class Route(object):
|
||||||
class ManetExperiment(object):
|
class ManetExperiment(object):
|
||||||
""" A class for building an MDR network and checking and logging its state.
|
""" A class for building an MDR network and checking and logging its state.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
def __init__(self, options, start):
|
def __init__(self, options, start):
|
||||||
""" Initialize with options and start time. """
|
""" Initialize with options and start time. """
|
||||||
self.session = None
|
self.session = None
|
||||||
|
|
@ -240,13 +240,13 @@ class ManetExperiment(object):
|
||||||
the session, WLAN, and node list objects.
|
the session, WLAN, and node list objects.
|
||||||
"""
|
"""
|
||||||
# IP subnet
|
# IP subnet
|
||||||
prefix = ipaddr.IPv4Prefix("10.14.0.0/16")
|
prefix = ipaddress.Ipv4Prefix("10.14.0.0/16")
|
||||||
self.session = pycore.Session()
|
self.session = Session(1)
|
||||||
# emulated network
|
# emulated network
|
||||||
self.net = self.session.addobj(cls = pycore.nodes.WlanNode)
|
self.net = self.session.add_object(cls=nodes.WlanNode)
|
||||||
for i in xrange(1, numnodes + 1):
|
for i in xrange(1, numnodes + 1):
|
||||||
addr = "%s/%s" % (prefix.addr(i), 32)
|
addr = "%s/%s" % (prefix.addr(i), 32)
|
||||||
tmp = self.session.addobj(cls = ManetNode, ipaddr = addr, objid= "%d" % i, name = "n%d" % i)
|
tmp = self.session.add_object(cls=ManetNode, ipaddr=addr, objid="%d" % i, name="n%d" % i)
|
||||||
tmp.newnetif(self.net, [addr])
|
tmp.newnetif(self.net, [addr])
|
||||||
self.nodes.append(tmp)
|
self.nodes.append(tmp)
|
||||||
# connect nodes with probability linkprob
|
# connect nodes with probability linkprob
|
||||||
|
|
@ -353,9 +353,11 @@ class ManetExperiment(object):
|
||||||
self.comparelsdbs(lsdbs)
|
self.comparelsdbs(lsdbs)
|
||||||
self.logdata(nbrs, mdrs, lsdbs, krs, zrs)
|
self.logdata(nbrs, mdrs, lsdbs, krs, zrs)
|
||||||
|
|
||||||
|
|
||||||
class Cmd:
|
class Cmd:
|
||||||
""" Helper class for running a command on a node and parsing the result. """
|
""" Helper class for running a command on a node and parsing the result. """
|
||||||
args = ""
|
args = ""
|
||||||
|
|
||||||
def __init__(self, node, verbose=False):
|
def __init__(self, node, verbose=False):
|
||||||
""" Initialize with a CoreNode (LxcNode) """
|
""" Initialize with a CoreNode (LxcNode) """
|
||||||
self.id = None
|
self.id = None
|
||||||
|
|
@ -384,7 +386,7 @@ class Cmd:
|
||||||
def open(self):
|
def open(self):
|
||||||
""" Exceute call to node.popen(). """
|
""" Exceute call to node.popen(). """
|
||||||
self.id, self.stdin, self.out, self.err = \
|
self.id, self.stdin, self.out, self.err = \
|
||||||
self.node.popen((self.args))
|
self.node.popen(self.args)
|
||||||
|
|
||||||
def parse(self):
|
def parse(self):
|
||||||
""" This method is overloaded by child classes and should return some
|
""" This method is overloaded by child classes and should return some
|
||||||
|
|
@ -401,18 +403,22 @@ class Cmd:
|
||||||
if tmp:
|
if tmp:
|
||||||
self.warn("nonzero exit status:", tmp)
|
self.warn("nonzero exit status:", tmp)
|
||||||
|
|
||||||
|
|
||||||
class VtyshCmd(Cmd):
|
class VtyshCmd(Cmd):
|
||||||
""" Runs a vtysh command. """
|
""" Runs a vtysh command. """
|
||||||
|
|
||||||
def open(self):
|
def open(self):
|
||||||
args = ("vtysh", "-c", self.args)
|
args = ("vtysh", "-c", self.args)
|
||||||
self.id, self.stdin, self.out, self.err = self.node.popen((args))
|
self.id, self.stdin, self.out, self.err = self.node.popen(args)
|
||||||
|
|
||||||
|
|
||||||
class Ospf6NeighState(VtyshCmd):
|
class Ospf6NeighState(VtyshCmd):
|
||||||
""" Check a node for OSPFv3 neighbors in the full/two-way states. """
|
""" Check a node for OSPFv3 neighbors in the full/two-way states. """
|
||||||
args = "show ipv6 ospf6 neighbor"
|
args = "show ipv6 ospf6 neighbor"
|
||||||
|
|
||||||
def parse(self):
|
def parse(self):
|
||||||
self.out.readline() # skip first line
|
# skip first line
|
||||||
|
self.out.readline()
|
||||||
nbrlist = []
|
nbrlist = []
|
||||||
for line in self.out:
|
for line in self.out:
|
||||||
field = line.split()
|
field = line.split()
|
||||||
|
|
@ -428,6 +434,7 @@ class Ospf6NeighState(VtyshCmd):
|
||||||
self.info(" %s has %d neighbors" % (self.node.routerid, len(nbrlist)))
|
self.info(" %s has %d neighbors" % (self.node.routerid, len(nbrlist)))
|
||||||
return nbrlist
|
return nbrlist
|
||||||
|
|
||||||
|
|
||||||
class Ospf6MdrLevel(VtyshCmd):
|
class Ospf6MdrLevel(VtyshCmd):
|
||||||
""" Retrieve the OSPFv3 MDR level for a node. """
|
""" Retrieve the OSPFv3 MDR level for a node. """
|
||||||
args = "show ipv6 ospf6 mdrlevel"
|
args = "show ipv6 ospf6 mdrlevel"
|
||||||
|
|
@ -443,6 +450,7 @@ class Ospf6MdrLevel(VtyshCmd):
|
||||||
self.info(" %s is %s" % (self.node.routerid, mdrlevel))
|
self.info(" %s is %s" % (self.node.routerid, mdrlevel))
|
||||||
return mdrlevel
|
return mdrlevel
|
||||||
|
|
||||||
|
|
||||||
class Ospf6Database(VtyshCmd):
|
class Ospf6Database(VtyshCmd):
|
||||||
""" Retrieve the OSPFv3 LSDB summary for a node. """
|
""" Retrieve the OSPFv3 LSDB summary for a node. """
|
||||||
args = "show ipv6 ospf6 database"
|
args = "show ipv6 ospf6 database"
|
||||||
|
|
@ -458,6 +466,7 @@ class Ospf6Database(VtyshCmd):
|
||||||
db += " ".join(filtered) + "\n"
|
db += " ".join(filtered) + "\n"
|
||||||
return db
|
return db
|
||||||
|
|
||||||
|
|
||||||
class ZebraRoutes(VtyshCmd):
|
class ZebraRoutes(VtyshCmd):
|
||||||
""" Return a list of Route objects for a node based on its zebra
|
""" Return a list of Route objects for a node based on its zebra
|
||||||
routing table.
|
routing table.
|
||||||
|
|
@ -466,7 +475,8 @@ class ZebraRoutes(VtyshCmd):
|
||||||
|
|
||||||
def parse(self):
|
def parse(self):
|
||||||
for i in xrange(0, 3):
|
for i in xrange(0, 3):
|
||||||
self.out.readline() # skip first three lines
|
# skip first three lines
|
||||||
|
self.out.readline()
|
||||||
r = []
|
r = []
|
||||||
prefix = None
|
prefix = None
|
||||||
for line in self.out:
|
for line in self.out:
|
||||||
|
|
@ -497,6 +507,7 @@ class ZebraRoutes(VtyshCmd):
|
||||||
self.info(" %s has %d zebra routes" % (self.node.routerid, len(r)))
|
self.info(" %s has %d zebra routes" % (self.node.routerid, len(r)))
|
||||||
return r
|
return r
|
||||||
|
|
||||||
|
|
||||||
class KernelRoutes(Cmd):
|
class KernelRoutes(Cmd):
|
||||||
""" Return a list of Route objects for a node based on its kernel
|
""" Return a list of Route objects for a node based on its kernel
|
||||||
routing table.
|
routing table.
|
||||||
|
|
@ -521,7 +532,8 @@ class KernelRoutes(Cmd):
|
||||||
if field[1] == "proto":
|
if field[1] == "proto":
|
||||||
# nexthop entry is on the next line
|
# nexthop entry is on the next line
|
||||||
continue
|
continue
|
||||||
gw = field[2] # nexthop IP or interface
|
# nexthop IP or interface
|
||||||
|
gw = field[2]
|
||||||
r.append(Route(prefix, gw, metric))
|
r.append(Route(prefix, gw, metric))
|
||||||
prefix = None
|
prefix = None
|
||||||
|
|
||||||
|
|
@ -531,6 +543,7 @@ class KernelRoutes(Cmd):
|
||||||
self.info(" %s has %d kernel routes" % (self.node.routerid, len(r)))
|
self.info(" %s has %d kernel routes" % (self.node.routerid, len(r)))
|
||||||
return r
|
return r
|
||||||
|
|
||||||
|
|
||||||
def main():
|
def main():
|
||||||
usagestr = "usage: %prog [-h] [options] [args]"
|
usagestr = "usage: %prog [-h] [options] [args]"
|
||||||
parser = optparse.OptionParser(usage=usagestr)
|
parser = optparse.OptionParser(usage=usagestr)
|
||||||
|
|
@ -587,5 +600,10 @@ def main():
|
||||||
|
|
||||||
return me
|
return me
|
||||||
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
|
# configure nodes to use
|
||||||
|
node_map = nodemaps.CLASSIC_NODES
|
||||||
|
nodeutils.set_node_map(node_map)
|
||||||
|
|
||||||
me = main()
|
me = main()
|
||||||
|
|
|
||||||
|
|
@ -5,15 +5,20 @@
|
||||||
|
|
||||||
# connect n nodes to a virtual switch/hub
|
# connect n nodes to a virtual switch/hub
|
||||||
|
|
||||||
import sys, datetime, optparse
|
import datetime
|
||||||
|
import optparse
|
||||||
|
import sys
|
||||||
|
|
||||||
from core import pycore
|
from core import constants
|
||||||
from core.misc import ipaddr
|
from core.misc import ipaddress, nodeutils, nodemaps
|
||||||
from core.constants import *
|
from core.netns import nodes
|
||||||
|
|
||||||
# node list (count from 1)
|
# node list (count from 1)
|
||||||
|
from core.session import Session
|
||||||
|
|
||||||
n = [None]
|
n = [None]
|
||||||
|
|
||||||
|
|
||||||
def main():
|
def main():
|
||||||
usagestr = "usage: %prog [-h] [options] [args]"
|
usagestr = "usage: %prog [-h] [options] [args]"
|
||||||
parser = optparse.OptionParser(usage=usagestr)
|
parser = optparse.OptionParser(usage=usagestr)
|
||||||
|
|
@ -41,30 +46,35 @@ def main():
|
||||||
start = datetime.datetime.now()
|
start = datetime.datetime.now()
|
||||||
|
|
||||||
# IP subnet
|
# IP subnet
|
||||||
prefix = ipaddr.IPv4Prefix("10.83.0.0/16")
|
prefix = ipaddress.Ipv4Prefix("10.83.0.0/16")
|
||||||
session = pycore.Session(persistent=True)
|
session = Session(1, persistent=True)
|
||||||
if 'server' in globals():
|
if 'server' in globals():
|
||||||
server.addsession(session)
|
server.addsession(session)
|
||||||
# emulated Ethernet switch
|
# emulated Ethernet switch
|
||||||
switch = session.addobj(cls = pycore.nodes.SwitchNode, name = "switch")
|
switch = session.add_object(cls=nodes.SwitchNode, name="switch")
|
||||||
switch.setposition(x=80, y=50)
|
switch.setposition(x=80, y=50)
|
||||||
print "creating %d nodes with addresses from %s" % \
|
print "creating %d nodes with addresses from %s" % (options.numnodes, prefix)
|
||||||
(options.numnodes, prefix)
|
|
||||||
for i in xrange(1, options.numnodes + 1):
|
for i in xrange(1, options.numnodes + 1):
|
||||||
tmp = session.addobj(cls = pycore.nodes.CoreNode, name = "n%d" % i,
|
tmp = session.add_object(cls=nodes.CoreNode, name="n%d" % i, objid=i)
|
||||||
objid=i)
|
|
||||||
tmp.newnetif(switch, ["%s/%s" % (prefix.addr(i), prefix.prefixlen)])
|
tmp.newnetif(switch, ["%s/%s" % (prefix.addr(i), prefix.prefixlen)])
|
||||||
tmp.cmd([SYSCTL_BIN, "net.ipv4.icmp_echo_ignore_broadcasts=0"])
|
tmp.cmd([constants.SYSCTL_BIN, "net.ipv4.icmp_echo_ignore_broadcasts=0"])
|
||||||
tmp.setposition(x=150 * i, y=150)
|
tmp.setposition(x=150 * i, y=150)
|
||||||
n.append(tmp)
|
n.append(tmp)
|
||||||
|
|
||||||
session.node_count = str(options.numnodes + 1)
|
session.node_count = str(options.numnodes + 1)
|
||||||
session.instantiate()
|
session.instantiate()
|
||||||
|
print "elapsed time: %s" % (datetime.datetime.now() - start)
|
||||||
|
|
||||||
# start a shell on node 1
|
# start a shell on node 1
|
||||||
n[1].term("bash")
|
n[1].term("bash")
|
||||||
|
|
||||||
print "elapsed time: %s" % (datetime.datetime.now() - start)
|
raw_input("press enter to exit")
|
||||||
|
session.shutdown()
|
||||||
|
|
||||||
|
|
||||||
if __name__ == "__main__" or __name__ == "__builtin__":
|
if __name__ == "__main__" or __name__ == "__builtin__":
|
||||||
main()
|
# configure nodes to use
|
||||||
|
node_map = nodemaps.CLASSIC_NODES
|
||||||
|
nodeutils.set_node_map(node_map)
|
||||||
|
|
||||||
|
main()
|
||||||
|
|
|
||||||
|
|
@ -8,11 +8,14 @@
|
||||||
# and repeat for minnodes <= n <= maxnodes with a step size of
|
# and repeat for minnodes <= n <= maxnodes with a step size of
|
||||||
# nodestep
|
# nodestep
|
||||||
|
|
||||||
import optparse, sys, os, datetime
|
import datetime
|
||||||
|
import optparse
|
||||||
|
import sys
|
||||||
|
|
||||||
from core import pycore
|
from core.misc import ipaddress, nodeutils, nodemaps
|
||||||
from core.misc import ipaddr
|
|
||||||
from core.misc.utils import mutecall
|
from core.misc.utils import mutecall
|
||||||
|
from core.netns import nodes
|
||||||
|
from core.session import Session
|
||||||
|
|
||||||
try:
|
try:
|
||||||
mutecall(["iperf", "-v"])
|
mutecall(["iperf", "-v"])
|
||||||
|
|
@ -20,23 +23,27 @@ except OSError:
|
||||||
sys.stderr.write("ERROR: running iperf failed\n")
|
sys.stderr.write("ERROR: running iperf failed\n")
|
||||||
sys.exit(1)
|
sys.exit(1)
|
||||||
|
|
||||||
|
|
||||||
def test(numnodes, testsec):
|
def test(numnodes, testsec):
|
||||||
# node list
|
# node list
|
||||||
n = []
|
n = []
|
||||||
# IP subnet
|
# IP subnet
|
||||||
prefix = ipaddr.IPv4Prefix("10.83.0.0/16")
|
prefix = ipaddress.Ipv4Prefix("10.83.0.0/16")
|
||||||
session = pycore.Session()
|
session = Session(1)
|
||||||
# emulated network
|
# emulated network
|
||||||
net = session.addobj(cls = pycore.nodes.SwitchNode)
|
net = session.add_object(cls=nodes.SwitchNode)
|
||||||
for i in xrange(1, numnodes + 1):
|
for i in xrange(1, numnodes + 1):
|
||||||
tmp = session.addobj(cls = pycore.nodes.LxcNode, name = "n%d" % i)
|
tmp = session.add_object(cls=nodes.LxcNode, name="n%d" % i)
|
||||||
tmp.newnetif(net, ["%s/%s" % (prefix.addr(i), prefix.prefixlen)])
|
tmp.newnetif(net, ["%s/%s" % (prefix.addr(i), prefix.prefixlen)])
|
||||||
n.append(tmp)
|
n.append(tmp)
|
||||||
n[0].cmd(["iperf", "-s", "-D"])
|
n[0].cmd(["iperf", "-s", "-D"])
|
||||||
n[-1].icmd(["iperf", "-t", str(int(testsec)), "-c", str(prefix.addr(1))])
|
n[-1].icmd(["iperf", "-t", str(int(testsec)), "-c", str(prefix.addr(1))])
|
||||||
n[0].cmd(["killall", "-9", "iperf"])
|
n[0].cmd(["killall", "-9", "iperf"])
|
||||||
|
|
||||||
|
raw_input("press enter to exit")
|
||||||
session.shutdown()
|
session.shutdown()
|
||||||
|
|
||||||
|
|
||||||
def main():
|
def main():
|
||||||
usagestr = "usage: %prog [-h] [options] [args]"
|
usagestr = "usage: %prog [-h] [options] [args]"
|
||||||
parser = optparse.OptionParser(usage=usagestr)
|
parser = optparse.OptionParser(usage=usagestr)
|
||||||
|
|
@ -90,8 +97,12 @@ def main():
|
||||||
test(i, options.testsec)
|
test(i, options.testsec)
|
||||||
print >> sys.stderr, ""
|
print >> sys.stderr, ""
|
||||||
|
|
||||||
print >> sys.stderr, \
|
print >> sys.stderr, "elapsed time: %s" % (datetime.datetime.now() - start)
|
||||||
"elapsed time: %s" % (datetime.datetime.now() - start)
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
|
# configure nodes to use
|
||||||
|
node_map = nodemaps.CLASSIC_NODES
|
||||||
|
nodeutils.set_node_map(node_map)
|
||||||
|
|
||||||
main()
|
main()
|
||||||
|
|
|
||||||
|
|
@ -5,7 +5,7 @@
|
||||||
#
|
#
|
||||||
# author: Jeff Ahrenholz <jeffrey.m.ahrenholz@boeing.com>
|
# author: Jeff Ahrenholz <jeffrey.m.ahrenholz@boeing.com>
|
||||||
#
|
#
|
||||||
'''
|
"""
|
||||||
wlanemanetests.py - This script tests the performance of the WLAN device in
|
wlanemanetests.py - This script tests the performance of the WLAN device in
|
||||||
CORE by measuring various metrics:
|
CORE by measuring various metrics:
|
||||||
- delay experienced when pinging end-to-end
|
- delay experienced when pinging end-to-end
|
||||||
|
|
@ -28,29 +28,22 @@ Various underlying network types are tested:
|
||||||
|
|
||||||
Results are printed/logged in CSV format.
|
Results are printed/logged in CSV format.
|
||||||
|
|
||||||
'''
|
"""
|
||||||
|
|
||||||
import os, sys, time, optparse, datetime, math
|
import datetime
|
||||||
from string import Template
|
import math
|
||||||
try:
|
import optparse
|
||||||
from core import pycore
|
import os
|
||||||
except ImportError:
|
import sys
|
||||||
# hack for Fedora autoconf that uses the following pythondir:
|
import time
|
||||||
if "/usr/lib/python2.6/site-packages" in sys.path:
|
|
||||||
sys.path.append("/usr/local/lib/python2.6/site-packages")
|
from core import emane
|
||||||
if "/usr/lib64/python2.6/site-packages" in sys.path:
|
|
||||||
sys.path.append("/usr/local/lib64/python2.6/site-packages")
|
|
||||||
if "/usr/lib/python2.7/site-packages" in sys.path:
|
|
||||||
sys.path.append("/usr/local/lib/python2.7/site-packages")
|
|
||||||
if "/usr/lib64/python2.7/site-packages" in sys.path:
|
|
||||||
sys.path.append("/usr/local/lib64/python2.7/site-packages")
|
|
||||||
from core import pycore
|
|
||||||
from core.misc import ipaddr
|
|
||||||
from core.misc.utils import mutecall
|
|
||||||
from core.constants import QUAGGA_STATE_DIR
|
|
||||||
from core.emane.emane import Emane
|
|
||||||
from core.emane.bypass import EmaneBypassModel
|
from core.emane.bypass import EmaneBypassModel
|
||||||
|
from core.emane.nodes import EmaneNode
|
||||||
from core.emane.rfpipe import EmaneRfPipeModel
|
from core.emane.rfpipe import EmaneRfPipeModel
|
||||||
|
from core.misc import ipaddress, nodemaps, nodeutils
|
||||||
|
from core.netns import nodes
|
||||||
|
from core.session import Session
|
||||||
|
|
||||||
try:
|
try:
|
||||||
import emaneeventservice
|
import emaneeventservice
|
||||||
|
|
@ -60,12 +53,12 @@ except Exception, e:
|
||||||
from emanesh.events import EventService
|
from emanesh.events import EventService
|
||||||
from emanesh.events import PathlossEvent
|
from emanesh.events import PathlossEvent
|
||||||
except Exception, e2:
|
except Exception, e2:
|
||||||
raise ImportError, "failed to import EMANE Python bindings:\n%s\n%s" % \
|
raise ImportError("failed to import EMANE Python bindings:\n%s\n%s" % (e, e2))
|
||||||
(e, e2)
|
|
||||||
|
|
||||||
# global Experiment object (for interaction with 'python -i')
|
# global Experiment object (for interaction with "python -i")
|
||||||
exp = None
|
exp = None
|
||||||
|
|
||||||
|
|
||||||
# move these to core.misc.utils
|
# move these to core.misc.utils
|
||||||
def readstat():
|
def readstat():
|
||||||
f = open("/proc/stat", "r")
|
f = open("/proc/stat", "r")
|
||||||
|
|
@ -73,6 +66,7 @@ def readstat():
|
||||||
f.close()
|
f.close()
|
||||||
return lines
|
return lines
|
||||||
|
|
||||||
|
|
||||||
def numcpus():
|
def numcpus():
|
||||||
lines = readstat()
|
lines = readstat()
|
||||||
n = 0
|
n = 0
|
||||||
|
|
@ -82,6 +76,7 @@ def numcpus():
|
||||||
n += 1
|
n += 1
|
||||||
return n
|
return n
|
||||||
|
|
||||||
|
|
||||||
def getcputimes(line):
|
def getcputimes(line):
|
||||||
# return (user, nice, sys, idle) from a /proc/stat cpu line
|
# return (user, nice, sys, idle) from a /proc/stat cpu line
|
||||||
# assume columns are:
|
# assume columns are:
|
||||||
|
|
@ -90,6 +85,7 @@ def getcputimes(line):
|
||||||
(user, nice, sys, idle) = map(lambda (x): int(x), items[1:5])
|
(user, nice, sys, idle) = map(lambda (x): int(x), items[1:5])
|
||||||
return [user, nice, sys, idle]
|
return [user, nice, sys, idle]
|
||||||
|
|
||||||
|
|
||||||
def calculatecpu(timesa, timesb):
|
def calculatecpu(timesa, timesb):
|
||||||
for i in range(len(timesa)):
|
for i in range(len(timesa)):
|
||||||
timesb[i] -= timesa[i]
|
timesb[i] -= timesa[i]
|
||||||
|
|
@ -100,13 +96,15 @@ def calculatecpu(timesa, timesb):
|
||||||
# subtract % time spent in idle time
|
# subtract % time spent in idle time
|
||||||
return 100 - ((100.0 * timesb[-1]) / total)
|
return 100 - ((100.0 * timesb[-1]) / total)
|
||||||
|
|
||||||
|
|
||||||
# end move these to core.misc.utils
|
# end move these to core.misc.utils
|
||||||
|
|
||||||
class Cmd(object):
|
class Cmd(object):
|
||||||
''' Helper class for running a command on a node and parsing the result. '''
|
""" Helper class for running a command on a node and parsing the result. """
|
||||||
args = ""
|
args = ""
|
||||||
|
|
||||||
def __init__(self, node, verbose=False):
|
def __init__(self, node, verbose=False):
|
||||||
''' Initialize with a CoreNode (LxcNode) '''
|
""" Initialize with a CoreNode (LxcNode) """
|
||||||
self.id = None
|
self.id = None
|
||||||
self.stdin = None
|
self.stdin = None
|
||||||
self.out = None
|
self.out = None
|
||||||
|
|
@ -114,17 +112,17 @@ class Cmd(object):
|
||||||
self.verbose = verbose
|
self.verbose = verbose
|
||||||
|
|
||||||
def info(self, msg):
|
def info(self, msg):
|
||||||
''' Utility method for writing output to stdout.'''
|
""" Utility method for writing output to stdout."""
|
||||||
print msg
|
print msg
|
||||||
sys.stdout.flush()
|
sys.stdout.flush()
|
||||||
|
|
||||||
def warn(self, msg):
|
def warn(self, msg):
|
||||||
''' Utility method for writing output to stderr. '''
|
""" Utility method for writing output to stderr. """
|
||||||
print >> sys.stderr, "XXX %s:" % self.node.name, msg
|
print >> sys.stderr, "XXX %s:" % self.node.name, msg
|
||||||
sys.stderr.flush()
|
sys.stderr.flush()
|
||||||
|
|
||||||
def run(self):
|
def run(self):
|
||||||
''' This is the primary method used for running this command. '''
|
""" This is the primary method used for running this command. """
|
||||||
self.open()
|
self.open()
|
||||||
status = self.id.wait()
|
status = self.id.wait()
|
||||||
r = self.parse()
|
r = self.parse()
|
||||||
|
|
@ -132,18 +130,17 @@ class Cmd(object):
|
||||||
return r
|
return r
|
||||||
|
|
||||||
def open(self):
|
def open(self):
|
||||||
''' Exceute call to node.popen(). '''
|
""" Exceute call to node.popen(). """
|
||||||
self.id, self.stdin, self.out, self.err = \
|
self.id, self.stdin, self.out, self.err = self.node.popen(self.args)
|
||||||
self.node.popen((self.args))
|
|
||||||
|
|
||||||
def parse(self):
|
def parse(self):
|
||||||
''' This method is overloaded by child classes and should return some
|
""" This method is overloaded by child classes and should return some
|
||||||
result.
|
result.
|
||||||
'''
|
"""
|
||||||
return None
|
return None
|
||||||
|
|
||||||
def cleanup(self):
|
def cleanup(self):
|
||||||
''' Close the Popen channels.'''
|
""" Close the Popen channels."""
|
||||||
self.stdin.close()
|
self.stdin.close()
|
||||||
self.out.close()
|
self.out.close()
|
||||||
self.err.close()
|
self.err.close()
|
||||||
|
|
@ -153,69 +150,70 @@ class Cmd(object):
|
||||||
|
|
||||||
|
|
||||||
class ClientServerCmd(Cmd):
|
class ClientServerCmd(Cmd):
|
||||||
''' Helper class for running a command on a node and parsing the result. '''
|
""" Helper class for running a command on a node and parsing the result. """
|
||||||
args = ""
|
args = ""
|
||||||
client_args = ""
|
client_args = ""
|
||||||
|
|
||||||
def __init__(self, node, client_node, verbose=False):
|
def __init__(self, node, client_node, verbose=False):
|
||||||
''' Initialize with two CoreNodes, node is the server '''
|
""" Initialize with two CoreNodes, node is the server """
|
||||||
Cmd.__init__(self, node, verbose)
|
Cmd.__init__(self, node, verbose)
|
||||||
self.client_node = client_node
|
self.client_node = client_node
|
||||||
|
|
||||||
def run(self):
|
def run(self):
|
||||||
''' Run the server command, then the client command, then
|
""" Run the server command, then the client command, then
|
||||||
kill the server '''
|
kill the server """
|
||||||
self.open() # server
|
self.open() # server
|
||||||
self.client_open() # client
|
self.client_open() # client
|
||||||
status = self.client_id.wait()
|
status = self.client_id.wait()
|
||||||
self.node.cmdresult(['killall', self.args[0]]) # stop the server
|
# stop the server
|
||||||
|
self.node.cmdresult(["killall", self.args[0]])
|
||||||
r = self.parse()
|
r = self.parse()
|
||||||
self.cleanup()
|
self.cleanup()
|
||||||
return r
|
return r
|
||||||
|
|
||||||
def client_open(self):
|
def client_open(self):
|
||||||
''' Exceute call to client_node.popen(). '''
|
""" Exceute call to client_node.popen(). """
|
||||||
self.client_id, self.client_stdin, self.client_out, self.client_err = \
|
self.client_id, self.client_stdin, self.client_out, self.client_err = \
|
||||||
self.client_node.popen((self.client_args))
|
self.client_node.popen(self.client_args)
|
||||||
|
|
||||||
def parse(self):
|
def parse(self):
|
||||||
''' This method is overloaded by child classes and should return some
|
""" This method is overloaded by child classes and should return some
|
||||||
result.
|
result.
|
||||||
'''
|
"""
|
||||||
return None
|
return None
|
||||||
|
|
||||||
def cleanup(self):
|
def cleanup(self):
|
||||||
''' Close the Popen channels.'''
|
""" Close the Popen channels."""
|
||||||
self.stdin.close()
|
self.stdin.close()
|
||||||
self.out.close()
|
self.out.close()
|
||||||
self.err.close()
|
self.err.close()
|
||||||
tmp = self.id.wait()
|
tmp = self.id.wait()
|
||||||
if tmp:
|
if tmp:
|
||||||
self.warn("nonzero exit status: %s" % tmp)
|
self.warn("nonzero exit status: %s" % tmp)
|
||||||
self.warn("command was: %s" % ((self.args, )))
|
self.warn("command was: %s" % (self.args,))
|
||||||
|
|
||||||
|
|
||||||
class PingCmd(Cmd):
|
class PingCmd(Cmd):
|
||||||
''' Test latency using ping.
|
""" Test latency using ping.
|
||||||
'''
|
"""
|
||||||
|
|
||||||
def __init__(self, node, verbose=False, addr=None, count=50, interval=0.1, ):
|
def __init__(self, node, verbose=False, addr=None, count=50, interval=0.1, ):
|
||||||
Cmd.__init__(self, node, verbose)
|
Cmd.__init__(self, node, verbose)
|
||||||
self.addr = addr
|
self.addr = addr
|
||||||
self.count = count
|
self.count = count
|
||||||
self.interval = interval
|
self.interval = interval
|
||||||
self.args = ['ping', '-q', '-c', '%s' % count, '-i', '%s' % interval,
|
self.args = ["ping", "-q", "-c", "%s" % count, "-i", "%s" % interval, addr]
|
||||||
addr]
|
|
||||||
|
|
||||||
def run(self):
|
def run(self):
|
||||||
if self.verbose:
|
if self.verbose:
|
||||||
self.info("%s initial test ping (max 1 second)..." % self.node.name)
|
self.info("%s initial test ping (max 1 second)..." % self.node.name)
|
||||||
(status, result) = self.node.cmdresult(["ping", "-q", "-c", "1", "-w",
|
(status, result) = self.node.cmdresult(["ping", "-q", "-c", "1", "-w", "1", self.addr])
|
||||||
"1", self.addr])
|
|
||||||
if status != 0:
|
if status != 0:
|
||||||
self.warn("initial ping from %s to %s failed! result:\n%s" % \
|
self.warn("initial ping from %s to %s failed! result:\n%s" %
|
||||||
(self.node.name, self.addr, result))
|
(self.node.name, self.addr, result))
|
||||||
return (0.0, 0.0)
|
return 0.0, 0.0
|
||||||
if self.verbose:
|
if self.verbose:
|
||||||
self.info("%s pinging %s (%d seconds)..." % \
|
self.info("%s pinging %s (%d seconds)..." %
|
||||||
(self.node.name, self.addr, self.count * self.interval))
|
(self.node.name, self.addr, self.count * self.interval))
|
||||||
return Cmd.run(self)
|
return Cmd.run(self)
|
||||||
|
|
||||||
|
|
@ -224,17 +222,19 @@ class PingCmd(Cmd):
|
||||||
avg_latency = 0
|
avg_latency = 0
|
||||||
mdev = 0
|
mdev = 0
|
||||||
try:
|
try:
|
||||||
stats_str = lines[-1].split('=')[1]
|
stats_str = lines[-1].split("=")[1]
|
||||||
stats = stats_str.split('/')
|
stats = stats_str.split("/")
|
||||||
avg_latency = float(stats[1])
|
avg_latency = float(stats[1])
|
||||||
mdev = float(stats[3].split(' ')[0])
|
mdev = float(stats[3].split(" ")[0])
|
||||||
except Exception, e:
|
except Exception, e:
|
||||||
self.warn("ping parsing exception: %s" % e)
|
self.warn("ping parsing exception: %s" % e)
|
||||||
return (avg_latency, mdev)
|
return avg_latency, mdev
|
||||||
|
|
||||||
|
|
||||||
class IperfCmd(ClientServerCmd):
|
class IperfCmd(ClientServerCmd):
|
||||||
''' Test throughput using iperf.
|
""" Test throughput using iperf.
|
||||||
'''
|
"""
|
||||||
|
|
||||||
def __init__(self, node, client_node, verbose=False, addr=None, time=10):
|
def __init__(self, node, client_node, verbose=False, addr=None, time=10):
|
||||||
# node is the server
|
# node is the server
|
||||||
ClientServerCmd.__init__(self, node, client_node, verbose)
|
ClientServerCmd.__init__(self, node, client_node, verbose)
|
||||||
|
|
@ -254,35 +254,37 @@ class IperfCmd(ClientServerCmd):
|
||||||
def parse(self):
|
def parse(self):
|
||||||
lines = self.out.readlines()
|
lines = self.out.readlines()
|
||||||
try:
|
try:
|
||||||
bps = int(lines[-1].split(',')[-1].strip('\n'))
|
bps = int(lines[-1].split(",")[-1].strip("\n"))
|
||||||
except Exception, e:
|
except Exception, e:
|
||||||
self.warn("iperf parsing exception: %s" % e)
|
self.warn("iperf parsing exception: %s" % e)
|
||||||
bps = 0
|
bps = 0
|
||||||
return bps
|
return bps
|
||||||
|
|
||||||
|
|
||||||
class MgenCmd(ClientServerCmd):
|
class MgenCmd(ClientServerCmd):
|
||||||
''' Run a test traffic flow using an MGEN sender and receiver.
|
""" Run a test traffic flow using an MGEN sender and receiver.
|
||||||
'''
|
"""
|
||||||
|
|
||||||
def __init__(self, node, client_node, verbose=False, addr=None, time=10,
|
def __init__(self, node, client_node, verbose=False, addr=None, time=10,
|
||||||
rate=512):
|
rate=512):
|
||||||
ClientServerCmd.__init__(self, node, client_node, verbose)
|
ClientServerCmd.__init__(self, node, client_node, verbose)
|
||||||
self.addr = addr
|
self.addr = addr
|
||||||
self.time = time
|
self.time = time
|
||||||
self.args = ['mgen', 'event', 'listen udp 5000', 'output',
|
self.args = ["mgen", "event", "listen udp 5000", "output",
|
||||||
'/var/log/mgen.log']
|
"/var/log/mgen.log"]
|
||||||
self.rate = rate
|
self.rate = rate
|
||||||
sendevent = "ON 1 UDP DST %s/5000 PERIODIC [%s]" % \
|
sendevent = "ON 1 UDP DST %s/5000 PERIODIC [%s]" % \
|
||||||
(addr, self.mgenrate(self.rate))
|
(addr, self.mgenrate(self.rate))
|
||||||
stopevent = "%s OFF 1" % time
|
stopevent = "%s OFF 1" % time
|
||||||
self.client_args = ['mgen', 'event', sendevent, 'event', stopevent,
|
self.client_args = ["mgen", "event", sendevent, "event", stopevent,
|
||||||
'output', '/var/log/mgen.log']
|
"output", "/var/log/mgen.log"]
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def mgenrate(kbps):
|
def mgenrate(kbps):
|
||||||
''' Return a MGEN periodic rate string for the given kilobits-per-sec.
|
""" Return a MGEN periodic rate string for the given kilobits-per-sec.
|
||||||
Assume 1500 byte MTU, 20-byte IP + 8-byte UDP headers, leaving
|
Assume 1500 byte MTU, 20-byte IP + 8-byte UDP headers, leaving
|
||||||
1472 bytes for data.
|
1472 bytes for data.
|
||||||
'''
|
"""
|
||||||
bps = (kbps / 8) * 1000.0
|
bps = (kbps / 8) * 1000.0
|
||||||
maxdata = 1472
|
maxdata = 1472
|
||||||
pps = math.ceil(bps / maxdata)
|
pps = math.ceil(bps / maxdata)
|
||||||
|
|
@ -296,26 +298,27 @@ class MgenCmd(ClientServerCmd):
|
||||||
return ClientServerCmd.run(self)
|
return ClientServerCmd.run(self)
|
||||||
|
|
||||||
def cleanup(self):
|
def cleanup(self):
|
||||||
''' Close the Popen channels.'''
|
""" Close the Popen channels."""
|
||||||
self.stdin.close()
|
self.stdin.close()
|
||||||
self.out.close()
|
self.out.close()
|
||||||
self.err.close()
|
self.err.close()
|
||||||
tmp = self.id.wait() # non-zero mgen exit status OK
|
# non-zero mgen exit status OK
|
||||||
|
tmp = self.id.wait()
|
||||||
|
|
||||||
def parse(self):
|
def parse(self):
|
||||||
''' Check MGEN receiver's log file for packet sequence numbers, and
|
""" Check MGEN receiver"s log file for packet sequence numbers, and
|
||||||
return the percentage of lost packets.
|
return the percentage of lost packets.
|
||||||
'''
|
"""
|
||||||
logfile = os.path.join(self.node.nodedir, 'var.log/mgen.log')
|
logfile = os.path.join(self.node.nodedir, "var.log/mgen.log")
|
||||||
f = open(logfile, 'r')
|
f = open(logfile, "r")
|
||||||
numlost = 0
|
numlost = 0
|
||||||
lastseq = 0
|
lastseq = 0
|
||||||
for line in f.readlines():
|
for line in f.readlines():
|
||||||
fields = line.split()
|
fields = line.split()
|
||||||
if fields[1] != 'RECV':
|
if fields[1] != "RECV":
|
||||||
continue
|
continue
|
||||||
try:
|
try:
|
||||||
seq = int(fields[4].split('>')[1])
|
seq = int(fields[4].split(">")[1])
|
||||||
except:
|
except:
|
||||||
self.info("Unexpected MGEN line:\n%s" % fields)
|
self.info("Unexpected MGEN line:\n%s" % fields)
|
||||||
if seq > (lastseq + 1):
|
if seq > (lastseq + 1):
|
||||||
|
|
@ -333,10 +336,11 @@ class MgenCmd(ClientServerCmd):
|
||||||
|
|
||||||
|
|
||||||
class Experiment(object):
|
class Experiment(object):
|
||||||
''' Experiment object to organize tests.
|
""" Experiment object to organize tests.
|
||||||
'''
|
"""
|
||||||
|
|
||||||
def __init__(self, opt, start):
|
def __init__(self, opt, start):
|
||||||
''' Initialize with opt and start time. '''
|
""" Initialize with opt and start time. """
|
||||||
self.session = None
|
self.session = None
|
||||||
# node list
|
# node list
|
||||||
self.nodes = []
|
self.nodes = []
|
||||||
|
|
@ -352,19 +356,19 @@ class Experiment(object):
|
||||||
self.logbegin()
|
self.logbegin()
|
||||||
|
|
||||||
def info(self, msg):
|
def info(self, msg):
|
||||||
''' Utility method for writing output to stdout. '''
|
""" Utility method for writing output to stdout. """
|
||||||
print msg
|
print msg
|
||||||
sys.stdout.flush()
|
sys.stdout.flush()
|
||||||
self.log(msg)
|
self.log(msg)
|
||||||
|
|
||||||
def warn(self, msg):
|
def warn(self, msg):
|
||||||
''' Utility method for writing output to stderr. '''
|
""" Utility method for writing output to stderr. """
|
||||||
print >> sys.stderr, msg
|
print >> sys.stderr, msg
|
||||||
sys.stderr.flush()
|
sys.stderr.flush()
|
||||||
self.log(msg)
|
self.log(msg)
|
||||||
|
|
||||||
def logbegin(self):
|
def logbegin(self):
|
||||||
''' Start logging. '''
|
""" Start logging. """
|
||||||
self.logfp = None
|
self.logfp = None
|
||||||
if not self.opt.logfile:
|
if not self.opt.logfile:
|
||||||
return
|
return
|
||||||
|
|
@ -375,7 +379,7 @@ class Experiment(object):
|
||||||
self.log("%s %s %s %s on %s" % (sysname, rel, ver, machine, nodename))
|
self.log("%s %s %s %s on %s" % (sysname, rel, ver, machine, nodename))
|
||||||
|
|
||||||
def logend(self):
|
def logend(self):
|
||||||
''' End logging. '''
|
""" End logging. """
|
||||||
if not self.logfp:
|
if not self.logfp:
|
||||||
return
|
return
|
||||||
end = datetime.datetime.now()
|
end = datetime.datetime.now()
|
||||||
|
|
@ -386,14 +390,14 @@ class Experiment(object):
|
||||||
self.logfp = None
|
self.logfp = None
|
||||||
|
|
||||||
def log(self, msg):
|
def log(self, msg):
|
||||||
''' Write to the log file, if any. '''
|
""" Write to the log file, if any. """
|
||||||
if not self.logfp:
|
if not self.logfp:
|
||||||
return
|
return
|
||||||
print >> self.logfp, msg
|
print >> self.logfp, msg
|
||||||
|
|
||||||
def reset(self):
|
def reset(self):
|
||||||
''' Prepare for another experiment run.
|
""" Prepare for another experiment run.
|
||||||
'''
|
"""
|
||||||
if self.session:
|
if self.session:
|
||||||
self.session.shutdown()
|
self.session.shutdown()
|
||||||
del self.session
|
del self.session
|
||||||
|
|
@ -402,24 +406,21 @@ class Experiment(object):
|
||||||
self.net = None
|
self.net = None
|
||||||
|
|
||||||
def createbridgedsession(self, numnodes, verbose=False):
|
def createbridgedsession(self, numnodes, verbose=False):
|
||||||
''' Build a topology consisting of the given number of LxcNodes
|
""" Build a topology consisting of the given number of LxcNodes
|
||||||
connected to a WLAN.
|
connected to a WLAN.
|
||||||
'''
|
"""
|
||||||
# IP subnet
|
# IP subnet
|
||||||
prefix = ipaddr.IPv4Prefix("10.0.0.0/16")
|
prefix = ipaddress.Ipv4Prefix("10.0.0.0/16")
|
||||||
self.session = pycore.Session()
|
self.session = Session(1)
|
||||||
# emulated network
|
# emulated network
|
||||||
self.net = self.session.addobj(cls = pycore.nodes.WlanNode,
|
self.net = self.session.add_object(cls=nodes.WlanNode, name="wlan1")
|
||||||
name = "wlan1")
|
|
||||||
prev = None
|
prev = None
|
||||||
for i in xrange(1, numnodes + 1):
|
for i in xrange(1, numnodes + 1):
|
||||||
addr = "%s/%s" % (prefix.addr(i), 32)
|
addr = "%s/%s" % (prefix.addr(i), 32)
|
||||||
tmp = self.session.addobj(cls = pycore.nodes.CoreNode, objid = i,
|
tmp = self.session.add_object(cls=nodes.CoreNode, objid=i, name="n%d" % i)
|
||||||
name = "n%d" % i)
|
|
||||||
tmp.newnetif(self.net, [addr])
|
tmp.newnetif(self.net, [addr])
|
||||||
self.nodes.append(tmp)
|
self.nodes.append(tmp)
|
||||||
self.session.services.addservicestonode(tmp, "router",
|
self.session.services.addservicestonode(tmp, "router", "IPForward")
|
||||||
"IPForward", self.verbose)
|
|
||||||
self.session.services.bootnodeservices(tmp)
|
self.session.services.bootnodeservices(tmp)
|
||||||
self.staticroutes(i, prefix, numnodes)
|
self.staticroutes(i, prefix, numnodes)
|
||||||
|
|
||||||
|
|
@ -430,35 +431,33 @@ class Experiment(object):
|
||||||
|
|
||||||
def createemanesession(self, numnodes, verbose=False, cls=None,
|
def createemanesession(self, numnodes, verbose=False, cls=None,
|
||||||
values=None):
|
values=None):
|
||||||
''' Build a topology consisting of the given number of LxcNodes
|
""" Build a topology consisting of the given number of LxcNodes
|
||||||
connected to an EMANE WLAN.
|
connected to an EMANE WLAN.
|
||||||
'''
|
"""
|
||||||
prefix = ipaddr.IPv4Prefix("10.0.0.0/16")
|
prefix = ipaddress.Ipv4Prefix("10.0.0.0/16")
|
||||||
self.session = pycore.Session()
|
self.session = Session(2)
|
||||||
self.session.node_count = str(numnodes + 1)
|
self.session.node_count = str(numnodes + 1)
|
||||||
self.session.master = True
|
self.session.master = True
|
||||||
self.session.location.setrefgeo(47.57917, -122.13232, 2.00000)
|
self.session.location.setrefgeo(47.57917, -122.13232, 2.00000)
|
||||||
self.session.location.refscale = 150.0
|
self.session.location.refscale = 150.0
|
||||||
self.session.cfg['emane_models'] = "RfPipe, Ieee80211abg, Bypass"
|
self.session.config["emane_models"] = "RfPipe, Ieee80211abg, Bypass"
|
||||||
self.session.emane.loadmodels()
|
self.session.emane.loadmodels()
|
||||||
self.net = self.session.addobj(cls = pycore.nodes.EmaneNode,
|
self.net = self.session.add_object(cls=EmaneNode, objid=numnodes + 1, name="wlan1")
|
||||||
objid = numnodes + 1, name = "wlan1")
|
|
||||||
self.net.verbose = verbose
|
self.net.verbose = verbose
|
||||||
# self.session.emane.addobj(self.net)
|
# self.session.emane.addobj(self.net)
|
||||||
for i in xrange(1, numnodes + 1):
|
for i in xrange(1, numnodes + 1):
|
||||||
addr = "%s/%s" % (prefix.addr(i), 32)
|
addr = "%s/%s" % (prefix.addr(i), 32)
|
||||||
tmp = self.session.addobj(cls = pycore.nodes.CoreNode, objid = i,
|
tmp = self.session.add_object(cls=nodes.CoreNode, objid=i,
|
||||||
name="n%d" % i)
|
name="n%d" % i)
|
||||||
# tmp.setposition(i * 20, 50, None)
|
# tmp.setposition(i * 20, 50, None)
|
||||||
tmp.setposition(50, 50, None)
|
tmp.setposition(50, 50, None)
|
||||||
tmp.newnetif(self.net, [addr])
|
tmp.newnetif(self.net, [addr])
|
||||||
self.nodes.append(tmp)
|
self.nodes.append(tmp)
|
||||||
self.session.services.addservicestonode(tmp, "router",
|
self.session.services.addservicestonode(tmp, "router", "IPForward")
|
||||||
"IPForward", self.verbose)
|
|
||||||
|
|
||||||
if values is None:
|
if values is None:
|
||||||
values = cls.getdefaultvalues()
|
values = cls.getdefaultvalues()
|
||||||
self.session.emane.setconfig(self.net.objid, cls._name, values)
|
self.session.emane.setconfig(self.net.objid, cls.name, values)
|
||||||
self.session.instantiate()
|
self.session.instantiate()
|
||||||
|
|
||||||
self.info("waiting %s sec (TAP bring-up)" % 2)
|
self.info("waiting %s sec (TAP bring-up)" % 2)
|
||||||
|
|
@ -469,19 +468,17 @@ class Experiment(object):
|
||||||
self.session.services.bootnodeservices(tmp)
|
self.session.services.bootnodeservices(tmp)
|
||||||
self.staticroutes(i, prefix, numnodes)
|
self.staticroutes(i, prefix, numnodes)
|
||||||
|
|
||||||
|
|
||||||
def setnodes(self):
|
def setnodes(self):
|
||||||
''' Set the sender and receiver nodes for use in this experiment,
|
""" Set the sender and receiver nodes for use in this experiment,
|
||||||
along with the address of the receiver to be used.
|
along with the address of the receiver to be used.
|
||||||
'''
|
"""
|
||||||
self.firstnode = self.nodes[0]
|
self.firstnode = self.nodes[0]
|
||||||
self.lastnode = self.nodes[-1]
|
self.lastnode = self.nodes[-1]
|
||||||
self.lastaddr = self.lastnode.netif(0).addrlist[0].split('/')[0]
|
self.lastaddr = self.lastnode.netif(0).addrlist[0].split("/")[0]
|
||||||
|
|
||||||
|
|
||||||
def staticroutes(self, i, prefix, numnodes):
|
def staticroutes(self, i, prefix, numnodes):
|
||||||
''' Add static routes on node number i to the other nodes in the chain.
|
""" Add static routes on node number i to the other nodes in the chain.
|
||||||
'''
|
"""
|
||||||
routecmd = ["/sbin/ip", "route", "add"]
|
routecmd = ["/sbin/ip", "route", "add"]
|
||||||
node = self.nodes[i - 1]
|
node = self.nodes[i - 1]
|
||||||
neigh_left = ""
|
neigh_left = ""
|
||||||
|
|
@ -515,17 +512,17 @@ class Experiment(object):
|
||||||
self.warn("failed to add route: %s" % cmd)
|
self.warn("failed to add route: %s" % cmd)
|
||||||
|
|
||||||
def setpathloss(self, numnodes):
|
def setpathloss(self, numnodes):
|
||||||
''' Send EMANE pathloss events to connect all NEMs in a chain.
|
""" Send EMANE pathloss events to connect all NEMs in a chain.
|
||||||
'''
|
"""
|
||||||
if self.session.emane.version < self.session.emane.EMANE091:
|
if self.session.emane.version < self.session.emane.EMANE091:
|
||||||
service = emaneeventservice.EventService()
|
service = emaneeventservice.EventService()
|
||||||
e = emaneeventpathloss.EventPathloss(1)
|
e = emaneeventpathloss.EventPathloss(1)
|
||||||
old = True
|
old = True
|
||||||
else:
|
else:
|
||||||
if self.session.emane.version == self.session.emane.EMANE091:
|
if self.session.emane.version == self.session.emane.EMANE091:
|
||||||
dev = 'lo'
|
dev = "lo"
|
||||||
else:
|
else:
|
||||||
dev = self.session.obj('ctrlnet').brname
|
dev = self.session.obj("ctrlnet").brname
|
||||||
service = EventService(eventchannel=("224.1.2.8", 45703, dev),
|
service = EventService(eventchannel=("224.1.2.8", 45703, dev),
|
||||||
otachannel=None)
|
otachannel=None)
|
||||||
old = False
|
old = False
|
||||||
|
|
@ -559,8 +556,8 @@ class Experiment(object):
|
||||||
service.publish(rxnem, e)
|
service.publish(rxnem, e)
|
||||||
|
|
||||||
def setneteffects(self, bw=None, delay=None):
|
def setneteffects(self, bw=None, delay=None):
|
||||||
''' Set link effects for all interfaces attached to the network node.
|
""" Set link effects for all interfaces attached to the network node.
|
||||||
'''
|
"""
|
||||||
if not self.net:
|
if not self.net:
|
||||||
self.warn("failed to set effects: no network node")
|
self.warn("failed to set effects: no network node")
|
||||||
return
|
return
|
||||||
|
|
@ -568,10 +565,10 @@ class Experiment(object):
|
||||||
self.net.linkconfig(netif, bw=bw, delay=delay)
|
self.net.linkconfig(netif, bw=bw, delay=delay)
|
||||||
|
|
||||||
def runalltests(self, title=""):
|
def runalltests(self, title=""):
|
||||||
''' Convenience helper to run all defined experiment tests.
|
""" Convenience helper to run all defined experiment tests.
|
||||||
If tests are run multiple times, this returns the average of
|
If tests are run multiple times, this returns the average of
|
||||||
those runs.
|
those runs.
|
||||||
'''
|
"""
|
||||||
duration = self.opt.duration
|
duration = self.opt.duration
|
||||||
rate = self.opt.rate
|
rate = self.opt.rate
|
||||||
if len(title) > 0:
|
if len(title) > 0:
|
||||||
|
|
@ -591,7 +588,8 @@ class Experiment(object):
|
||||||
throughput = self.iperftest(time=duration)
|
throughput = self.iperftest(time=duration)
|
||||||
if self.numiperf > 1:
|
if self.numiperf > 1:
|
||||||
throughputs += throughput
|
throughputs += throughput
|
||||||
time.sleep(1) # iperf is very CPU intensive
|
# iperf is very CPU intensive
|
||||||
|
time.sleep(1)
|
||||||
if self.numiperf > 1:
|
if self.numiperf > 1:
|
||||||
throughput = sum(throughputs) / len(throughputs)
|
throughput = sum(throughputs) / len(throughputs)
|
||||||
self.info("throughputs=%s" % ["%.2f" % v for v in throughputs])
|
self.info("throughputs=%s" % ["%.2f" % v for v in throughputs])
|
||||||
|
|
@ -610,11 +608,11 @@ class Experiment(object):
|
||||||
self.info("cpus=%s" % ["%.2f" % v for v in cpus])
|
self.info("cpus=%s" % ["%.2f" % v for v in cpus])
|
||||||
self.info("losses=%s" % ["%.2f" % v for v in losses])
|
self.info("losses=%s" % ["%.2f" % v for v in losses])
|
||||||
|
|
||||||
return (latency, mdev, throughput, cpu, loss)
|
return latency, mdev, throughput, cpu, loss
|
||||||
|
|
||||||
def pingtest(self, count=50):
|
def pingtest(self, count=50):
|
||||||
''' Ping through a chain of nodes and report the average latency.
|
""" Ping through a chain of nodes and report the average latency.
|
||||||
'''
|
"""
|
||||||
p = PingCmd(node=self.firstnode, verbose=self.verbose,
|
p = PingCmd(node=self.firstnode, verbose=self.verbose,
|
||||||
addr=self.lastaddr, count=count, interval=0.1).run()
|
addr=self.lastaddr, count=count, interval=0.1).run()
|
||||||
(latency, mdev) = p
|
(latency, mdev) = p
|
||||||
|
|
@ -622,18 +620,18 @@ class Experiment(object):
|
||||||
return p
|
return p
|
||||||
|
|
||||||
def iperftest(self, time=10):
|
def iperftest(self, time=10):
|
||||||
''' Run iperf through a chain of nodes and report the maximum
|
""" Run iperf through a chain of nodes and report the maximum
|
||||||
throughput.
|
throughput.
|
||||||
'''
|
"""
|
||||||
bps = IperfCmd(node=self.lastnode, client_node=self.firstnode,
|
bps = IperfCmd(node=self.lastnode, client_node=self.firstnode,
|
||||||
verbose=False, addr=self.lastaddr, time=time).run()
|
verbose=False, addr=self.lastaddr, time=time).run()
|
||||||
self.info("throughput (bps): %s" % bps)
|
self.info("throughput (bps): %s" % bps)
|
||||||
return bps
|
return bps
|
||||||
|
|
||||||
def cputest(self, time=10, rate=512):
|
def cputest(self, time=10, rate=512):
|
||||||
''' Run MGEN through a chain of nodes and report the CPU usage and
|
""" Run MGEN through a chain of nodes and report the CPU usage and
|
||||||
percent of lost packets. Rate is in kbps.
|
percent of lost packets. Rate is in kbps.
|
||||||
'''
|
"""
|
||||||
if self.verbose:
|
if self.verbose:
|
||||||
self.info("%s initial test ping (max 1 second)..." % \
|
self.info("%s initial test ping (max 1 second)..." % \
|
||||||
self.firstnode.name)
|
self.firstnode.name)
|
||||||
|
|
@ -642,7 +640,7 @@ class Experiment(object):
|
||||||
if status != 0:
|
if status != 0:
|
||||||
self.warn("initial ping from %s to %s failed! result:\n%s" % \
|
self.warn("initial ping from %s to %s failed! result:\n%s" % \
|
||||||
(self.firstnode.name, self.lastaddr, result))
|
(self.firstnode.name, self.lastaddr, result))
|
||||||
return (0.0, 0.0)
|
return 0.0, 0.0
|
||||||
lines = readstat()
|
lines = readstat()
|
||||||
cpustart = getcputimes(lines[0])
|
cpustart = getcputimes(lines[0])
|
||||||
loss = MgenCmd(node=self.lastnode, client_node=self.firstnode,
|
loss = MgenCmd(node=self.lastnode, client_node=self.firstnode,
|
||||||
|
|
@ -654,9 +652,10 @@ class Experiment(object):
|
||||||
self.info("CPU usage (%%): %.02f, %.02f loss" % (percent, loss))
|
self.info("CPU usage (%%): %.02f, %.02f loss" % (percent, loss))
|
||||||
return percent, loss
|
return percent, loss
|
||||||
|
|
||||||
|
|
||||||
def main():
|
def main():
|
||||||
''' Main routine when running from command-line.
|
""" Main routine when running from command-line.
|
||||||
'''
|
"""
|
||||||
usagestr = "usage: %prog [-h] [options] [args]"
|
usagestr = "usage: %prog [-h] [options] [args]"
|
||||||
parser = optparse.OptionParser(usage=usagestr)
|
parser = optparse.OptionParser(usage=usagestr)
|
||||||
parser.set_defaults(numnodes=10, delay=3, duration=10, rate=512,
|
parser.set_defaults(numnodes=10, delay=3, duration=10, rate=512,
|
||||||
|
|
@ -700,7 +699,7 @@ def main():
|
||||||
usage("invalid rate: %s" % opt.rate)
|
usage("invalid rate: %s" % opt.rate)
|
||||||
|
|
||||||
for a in args:
|
for a in args:
|
||||||
sys.stderr.write("ignoring command line argument: '%s'\n" % a)
|
sys.stderr.write("ignoring command line argument: %s\n" % a)
|
||||||
|
|
||||||
results = {}
|
results = {}
|
||||||
starttime = datetime.datetime.now()
|
starttime = datetime.datetime.now()
|
||||||
|
|
@ -708,51 +707,49 @@ def main():
|
||||||
exp.info("Starting wlanemanetests.py tests %s" % starttime.ctime())
|
exp.info("Starting wlanemanetests.py tests %s" % starttime.ctime())
|
||||||
|
|
||||||
# system sanity checks here
|
# system sanity checks here
|
||||||
emanever, emaneverstr = Emane.detectversionfromcmd()
|
emanever, emaneverstr = emane.VERSION, emane.VERSIONSTR
|
||||||
if opt.verbose:
|
if opt.verbose:
|
||||||
exp.info("Detected EMANE version %s" % (emaneverstr,))
|
exp.info("Detected EMANE version %s" % (emaneverstr,))
|
||||||
|
|
||||||
# bridged
|
# bridged
|
||||||
exp.info("setting up bridged tests 1/2 no link effects")
|
exp.info("setting up bridged tests 1/2 no link effects")
|
||||||
exp.info("creating topology: numnodes = %s" % \
|
exp.info("creating topology: numnodes = %s" % (opt.numnodes,))
|
||||||
(opt.numnodes, ))
|
|
||||||
exp.createbridgedsession(numnodes=opt.numnodes, verbose=opt.verbose)
|
exp.createbridgedsession(numnodes=opt.numnodes, verbose=opt.verbose)
|
||||||
exp.setnodes()
|
exp.setnodes()
|
||||||
exp.info("waiting %s sec (node/route bring-up)" % opt.delay)
|
exp.info("waiting %s sec (node/route bring-up)" % opt.delay)
|
||||||
time.sleep(opt.delay)
|
time.sleep(opt.delay)
|
||||||
results['0 bridged'] = exp.runalltests("bridged")
|
results["0 bridged"] = exp.runalltests("bridged")
|
||||||
exp.info("done; elapsed time: %s" % (datetime.datetime.now() - exp.start))
|
exp.info("done; elapsed time: %s" % (datetime.datetime.now() - exp.start))
|
||||||
|
|
||||||
# bridged with netem
|
# bridged with netem
|
||||||
exp.info("setting up bridged tests 2/2 with netem")
|
exp.info("setting up bridged tests 2/2 with netem")
|
||||||
exp.setneteffects(bw=54000000, delay=0)
|
exp.setneteffects(bw=54000000, delay=0)
|
||||||
exp.info("waiting %s sec (queue bring-up)" % opt.delay)
|
exp.info("waiting %s sec (queue bring-up)" % opt.delay)
|
||||||
results['1.0 netem'] = exp.runalltests("netem")
|
results["1.0 netem"] = exp.runalltests("netem")
|
||||||
exp.info("shutting down bridged session")
|
exp.info("shutting down bridged session")
|
||||||
|
|
||||||
# bridged with netem (1 Mbps,200ms)
|
# bridged with netem (1 Mbps,200ms)
|
||||||
exp.info("setting up bridged tests 3/2 with netem")
|
exp.info("setting up bridged tests 3/2 with netem")
|
||||||
exp.setneteffects(bw=1000000, delay=20000)
|
exp.setneteffects(bw=1000000, delay=20000)
|
||||||
exp.info("waiting %s sec (queue bring-up)" % opt.delay)
|
exp.info("waiting %s sec (queue bring-up)" % opt.delay)
|
||||||
results['1.2 netem_1M'] = exp.runalltests("netem_1M")
|
results["1.2 netem_1M"] = exp.runalltests("netem_1M")
|
||||||
exp.info("shutting down bridged session")
|
exp.info("shutting down bridged session")
|
||||||
|
|
||||||
# bridged with netem (54 kbps,500ms)
|
# bridged with netem (54 kbps,500ms)
|
||||||
exp.info("setting up bridged tests 3/2 with netem")
|
exp.info("setting up bridged tests 3/2 with netem")
|
||||||
exp.setneteffects(bw=54000, delay=100000)
|
exp.setneteffects(bw=54000, delay=100000)
|
||||||
exp.info("waiting %s sec (queue bring-up)" % opt.delay)
|
exp.info("waiting %s sec (queue bring-up)" % opt.delay)
|
||||||
results['1.4 netem_54K'] = exp.runalltests("netem_54K")
|
results["1.4 netem_54K"] = exp.runalltests("netem_54K")
|
||||||
exp.info("shutting down bridged session")
|
exp.info("shutting down bridged session")
|
||||||
exp.reset()
|
exp.reset()
|
||||||
|
|
||||||
# EMANE bypass model
|
# EMANE bypass model
|
||||||
exp.info("setting up EMANE tests 1/2 with bypass model")
|
exp.info("setting up EMANE tests 1/2 with bypass model")
|
||||||
exp.createemanesession(numnodes=opt.numnodes, verbose=opt.verbose,
|
exp.createemanesession(numnodes=opt.numnodes, verbose=opt.verbose, cls=EmaneBypassModel, values=None)
|
||||||
cls=EmaneBypassModel, values=None)
|
|
||||||
exp.setnodes()
|
exp.setnodes()
|
||||||
exp.info("waiting %s sec (node/route bring-up)" % opt.delay)
|
exp.info("waiting %s sec (node/route bring-up)" % opt.delay)
|
||||||
time.sleep(opt.delay)
|
time.sleep(opt.delay)
|
||||||
results['2.0 bypass'] = exp.runalltests("bypass")
|
results["2.0 bypass"] = exp.runalltests("bypass")
|
||||||
exp.info("shutting down bypass session")
|
exp.info("shutting down bypass session")
|
||||||
exp.reset()
|
exp.reset()
|
||||||
|
|
||||||
|
|
@ -763,18 +760,18 @@ def main():
|
||||||
exp.info("setting up EMANE tests 2/4 with RF-PIPE model")
|
exp.info("setting up EMANE tests 2/4 with RF-PIPE model")
|
||||||
rfpipevals = list(EmaneRfPipeModel.getdefaultvalues())
|
rfpipevals = list(EmaneRfPipeModel.getdefaultvalues())
|
||||||
rfpnames = EmaneRfPipeModel.getnames()
|
rfpnames = EmaneRfPipeModel.getnames()
|
||||||
rfpipevals[ rfpnames.index('datarate') ] = '4294967295' # max value
|
# max value
|
||||||
if emanever < Emane.EMANE091:
|
rfpipevals[rfpnames.index("datarate")] = "4294967295"
|
||||||
rfpipevals[ rfpnames.index('pathlossmode') ] = '2ray'
|
if emanever < emane.EMANE091:
|
||||||
rfpipevals[ rfpnames.index('defaultconnectivitymode') ] = '1'
|
rfpipevals[rfpnames.index("pathlossmode")] = "2ray"
|
||||||
|
rfpipevals[rfpnames.index("defaultconnectivitymode")] = "1"
|
||||||
else:
|
else:
|
||||||
rfpipevals[ rfpnames.index('propagationmodel') ] = '2ray'
|
rfpipevals[rfpnames.index("propagationmodel")] = "2ray"
|
||||||
exp.createemanesession(numnodes=opt.numnodes, verbose=opt.verbose,
|
exp.createemanesession(numnodes=opt.numnodes, verbose=opt.verbose, cls=EmaneRfPipeModel, values=rfpipevals)
|
||||||
cls=EmaneRfPipeModel, values=rfpipevals)
|
|
||||||
exp.setnodes()
|
exp.setnodes()
|
||||||
exp.info("waiting %s sec (node/route bring-up)" % opt.delay)
|
exp.info("waiting %s sec (node/route bring-up)" % opt.delay)
|
||||||
time.sleep(opt.delay)
|
time.sleep(opt.delay)
|
||||||
results['3.0 rfpipe'] = exp.runalltests("rfpipe")
|
results["3.0 rfpipe"] = exp.runalltests("rfpipe")
|
||||||
exp.info("shutting down RF-PIPE session")
|
exp.info("shutting down RF-PIPE session")
|
||||||
exp.reset()
|
exp.reset()
|
||||||
|
|
||||||
|
|
@ -782,20 +779,20 @@ def main():
|
||||||
exp.info("setting up EMANE tests 3/4 with RF-PIPE model 54M")
|
exp.info("setting up EMANE tests 3/4 with RF-PIPE model 54M")
|
||||||
rfpipevals = list(EmaneRfPipeModel.getdefaultvalues())
|
rfpipevals = list(EmaneRfPipeModel.getdefaultvalues())
|
||||||
rfpnames = EmaneRfPipeModel.getnames()
|
rfpnames = EmaneRfPipeModel.getnames()
|
||||||
rfpipevals[ rfpnames.index('datarate') ] = '54000000'
|
rfpipevals[rfpnames.index("datarate")] = "54000000"
|
||||||
# TX delay != propagation delay
|
# TX delay != propagation delay
|
||||||
#rfpipevals[ rfpnames.index('delay') ] = '5000'
|
# rfpipevals[ rfpnames.index("delay") ] = "5000"
|
||||||
if emanever < Emane.EMANE091:
|
if emanever < emane.EMANE091:
|
||||||
rfpipevals[ rfpnames.index('pathlossmode') ] = '2ray'
|
rfpipevals[rfpnames.index("pathlossmode")] = "2ray"
|
||||||
rfpipevals[ rfpnames.index('defaultconnectivitymode') ] = '1'
|
rfpipevals[rfpnames.index("defaultconnectivitymode")] = "1"
|
||||||
else:
|
else:
|
||||||
rfpipevals[ rfpnames.index('propagationmodel') ] = '2ray'
|
rfpipevals[rfpnames.index("propagationmodel")] = "2ray"
|
||||||
exp.createemanesession(numnodes=opt.numnodes, verbose=opt.verbose,
|
exp.createemanesession(numnodes=opt.numnodes, verbose=opt.verbose,
|
||||||
cls=EmaneRfPipeModel, values=rfpipevals)
|
cls=EmaneRfPipeModel, values=rfpipevals)
|
||||||
exp.setnodes()
|
exp.setnodes()
|
||||||
exp.info("waiting %s sec (node/route bring-up)" % opt.delay)
|
exp.info("waiting %s sec (node/route bring-up)" % opt.delay)
|
||||||
time.sleep(opt.delay)
|
time.sleep(opt.delay)
|
||||||
results['4.0 rfpipe54m'] = exp.runalltests("rfpipe54m")
|
results["4.0 rfpipe54m"] = exp.runalltests("rfpipe54m")
|
||||||
exp.info("shutting down RF-PIPE session")
|
exp.info("shutting down RF-PIPE session")
|
||||||
exp.reset()
|
exp.reset()
|
||||||
|
|
||||||
|
|
@ -803,12 +800,12 @@ def main():
|
||||||
exp.info("setting up EMANE tests 4/4 with RF-PIPE model pathloss")
|
exp.info("setting up EMANE tests 4/4 with RF-PIPE model pathloss")
|
||||||
rfpipevals = list(EmaneRfPipeModel.getdefaultvalues())
|
rfpipevals = list(EmaneRfPipeModel.getdefaultvalues())
|
||||||
rfpnames = EmaneRfPipeModel.getnames()
|
rfpnames = EmaneRfPipeModel.getnames()
|
||||||
rfpipevals[ rfpnames.index('datarate') ] = '54000'
|
rfpipevals[rfpnames.index("datarate")] = "54000"
|
||||||
if emanever < Emane.EMANE091:
|
if emanever < emane.EMANE091:
|
||||||
rfpipevals[ rfpnames.index('pathlossmode') ] = 'pathloss'
|
rfpipevals[rfpnames.index("pathlossmode")] = "pathloss"
|
||||||
rfpipevals[ rfpnames.index('defaultconnectivitymode') ] = '0'
|
rfpipevals[rfpnames.index("defaultconnectivitymode")] = "0"
|
||||||
else:
|
else:
|
||||||
rfpipevals[ rfpnames.index('propagationmodel') ] = 'precomputed'
|
rfpipevals[rfpnames.index("propagationmodel")] = "precomputed"
|
||||||
exp.createemanesession(numnodes=opt.numnodes, verbose=opt.verbose,
|
exp.createemanesession(numnodes=opt.numnodes, verbose=opt.verbose,
|
||||||
cls=EmaneRfPipeModel, values=rfpipevals)
|
cls=EmaneRfPipeModel, values=rfpipevals)
|
||||||
exp.setnodes()
|
exp.setnodes()
|
||||||
|
|
@ -816,7 +813,7 @@ def main():
|
||||||
time.sleep(opt.delay)
|
time.sleep(opt.delay)
|
||||||
exp.info("sending pathloss events to govern connectivity")
|
exp.info("sending pathloss events to govern connectivity")
|
||||||
exp.setpathloss(opt.numnodes)
|
exp.setpathloss(opt.numnodes)
|
||||||
results['5.0 pathloss'] = exp.runalltests("pathloss")
|
results["5.0 pathloss"] = exp.runalltests("pathloss")
|
||||||
exp.info("shutting down RF-PIPE session")
|
exp.info("shutting down RF-PIPE session")
|
||||||
exp.reset()
|
exp.reset()
|
||||||
|
|
||||||
|
|
@ -824,10 +821,10 @@ def main():
|
||||||
exp.info("setting up EMANE tests 4/4 with RF-PIPE model pathloss")
|
exp.info("setting up EMANE tests 4/4 with RF-PIPE model pathloss")
|
||||||
rfpipevals = list(EmaneRfPipeModel.getdefaultvalues())
|
rfpipevals = list(EmaneRfPipeModel.getdefaultvalues())
|
||||||
rfpnames = EmaneRfPipeModel.getnames()
|
rfpnames = EmaneRfPipeModel.getnames()
|
||||||
rfpipevals[ rfpnames.index('datarate') ] = '512000'
|
rfpipevals[rfpnames.index("datarate")] = "512000"
|
||||||
rfpipevals[ rfpnames.index('delay') ] = '200'
|
rfpipevals[rfpnames.index("delay")] = "200"
|
||||||
rfpipevals[ rfpnames.index('pathlossmode') ] = 'pathloss'
|
rfpipevals[rfpnames.index("pathlossmode")] = "pathloss"
|
||||||
rfpipevals[ rfpnames.index('defaultconnectivitymode') ] = '0'
|
rfpipevals[rfpnames.index("defaultconnectivitymode")] = "0"
|
||||||
exp.createemanesession(numnodes=opt.numnodes, verbose=opt.verbose,
|
exp.createemanesession(numnodes=opt.numnodes, verbose=opt.verbose,
|
||||||
cls=EmaneRfPipeModel, values=rfpipevals)
|
cls=EmaneRfPipeModel, values=rfpipevals)
|
||||||
exp.setnodes()
|
exp.setnodes()
|
||||||
|
|
@ -835,7 +832,7 @@ def main():
|
||||||
time.sleep(opt.delay)
|
time.sleep(opt.delay)
|
||||||
exp.info("sending pathloss events to govern connectivity")
|
exp.info("sending pathloss events to govern connectivity")
|
||||||
exp.setpathloss(opt.numnodes)
|
exp.setpathloss(opt.numnodes)
|
||||||
results['5.1 pathloss'] = exp.runalltests("pathloss")
|
results["5.1 pathloss"] = exp.runalltests("pathloss")
|
||||||
exp.info("shutting down RF-PIPE session")
|
exp.info("shutting down RF-PIPE session")
|
||||||
exp.reset()
|
exp.reset()
|
||||||
|
|
||||||
|
|
@ -852,5 +849,10 @@ def main():
|
||||||
exp.logend()
|
exp.logend()
|
||||||
return exp
|
return exp
|
||||||
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
exp = main()
|
# configure nodes to use
|
||||||
|
node_map = nodemaps.CLASSIC_NODES
|
||||||
|
nodeutils.set_node_map(node_map)
|
||||||
|
|
||||||
|
main()
|
||||||
|
|
|
||||||
|
|
@ -8,11 +8,14 @@
|
||||||
# and repeat for minnodes <= n <= maxnodes with a step size of
|
# and repeat for minnodes <= n <= maxnodes with a step size of
|
||||||
# nodestep
|
# nodestep
|
||||||
|
|
||||||
import optparse, sys, os, datetime
|
import datetime
|
||||||
|
import optparse
|
||||||
|
import sys
|
||||||
|
|
||||||
from core import pycore
|
from core.misc import ipaddress, nodeutils, nodemaps
|
||||||
from core.misc import ipaddr
|
|
||||||
from core.misc.utils import mutecall
|
from core.misc.utils import mutecall
|
||||||
|
from core.netns import nodes
|
||||||
|
from core.session import Session
|
||||||
|
|
||||||
try:
|
try:
|
||||||
mutecall(["iperf", "-v"])
|
mutecall(["iperf", "-v"])
|
||||||
|
|
@ -20,16 +23,17 @@ except OSError:
|
||||||
sys.stderr.write("ERROR: running iperf failed\n")
|
sys.stderr.write("ERROR: running iperf failed\n")
|
||||||
sys.exit(1)
|
sys.exit(1)
|
||||||
|
|
||||||
|
|
||||||
def test(numnodes, testsec):
|
def test(numnodes, testsec):
|
||||||
# node list
|
# node list
|
||||||
n = []
|
n = []
|
||||||
# IP subnet
|
# IP subnet
|
||||||
prefix = ipaddr.IPv4Prefix("10.83.0.0/16")
|
prefix = ipaddress.Ipv4Prefix("10.83.0.0/16")
|
||||||
session = pycore.Session()
|
session = Session(1)
|
||||||
# emulated network
|
# emulated network
|
||||||
net = session.addobj(cls = pycore.nodes.WlanNode)
|
net = session.add_object(cls=nodes.WlanNode)
|
||||||
for i in xrange(1, numnodes + 1):
|
for i in xrange(1, numnodes + 1):
|
||||||
tmp = session.addobj(cls = pycore.nodes.LxcNode, objid= "%d" % i, name = "n%d" % i)
|
tmp = session.add_object(cls=nodes.LxcNode, objid="%d" % i, name="n%d" % i)
|
||||||
tmp.newnetif(net, ["%s/%s" % (prefix.addr(i), prefix.prefixlen)])
|
tmp.newnetif(net, ["%s/%s" % (prefix.addr(i), prefix.prefixlen)])
|
||||||
n.append(tmp)
|
n.append(tmp)
|
||||||
net.link(n[0].netif(0), n[-1].netif(0))
|
net.link(n[0].netif(0), n[-1].netif(0))
|
||||||
|
|
@ -38,14 +42,14 @@ def test(numnodes, testsec):
|
||||||
n[0].cmd(["killall", "-9", "iperf"])
|
n[0].cmd(["killall", "-9", "iperf"])
|
||||||
session.shutdown()
|
session.shutdown()
|
||||||
|
|
||||||
|
|
||||||
def main():
|
def main():
|
||||||
usagestr = "usage: %prog [-h] [options] [args]"
|
usagestr = "usage: %prog [-h] [options] [args]"
|
||||||
parser = optparse.OptionParser(usage=usagestr)
|
parser = optparse.OptionParser(usage=usagestr)
|
||||||
|
|
||||||
parser.set_defaults(minnodes=2)
|
parser.set_defaults(minnodes=2)
|
||||||
parser.add_option("-m", "--minnodes", dest="minnodes", type=int,
|
parser.add_option("-m", "--minnodes", dest="minnodes", type=int,
|
||||||
help = "min number of nodes to test; default = %s" %
|
help="min number of nodes to test; default = %s" % parser.defaults["minnodes"])
|
||||||
parser.defaults["minnodes"])
|
|
||||||
|
|
||||||
parser.set_defaults(maxnodes=2)
|
parser.set_defaults(maxnodes=2)
|
||||||
parser.add_option("-n", "--maxnodes", dest="maxnodes", type=int,
|
parser.add_option("-n", "--maxnodes", dest="maxnodes", type=int,
|
||||||
|
|
@ -91,8 +95,12 @@ def main():
|
||||||
test(i, options.testsec)
|
test(i, options.testsec)
|
||||||
print >> sys.stderr, ""
|
print >> sys.stderr, ""
|
||||||
|
|
||||||
print >> sys.stderr, \
|
print >> sys.stderr, "elapsed time: %s" % (datetime.datetime.now() - start)
|
||||||
"elapsed time: %s" % (datetime.datetime.now() - start)
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
|
# configure nodes to use
|
||||||
|
node_map = nodemaps.CLASSIC_NODES
|
||||||
|
nodeutils.set_node_map(node_map)
|
||||||
|
|
||||||
main()
|
main()
|
||||||
|
|
|
||||||
|
|
@ -5,9 +5,12 @@
|
||||||
# List and stop CORE sessions from the command line.
|
# List and stop CORE sessions from the command line.
|
||||||
#
|
#
|
||||||
|
|
||||||
import socket, optparse
|
import optparse
|
||||||
from core.constants import *
|
import socket
|
||||||
|
|
||||||
from core.api import coreapi
|
from core.api import coreapi
|
||||||
|
from core.enumerations import MessageFlags, SessionTlvs, CORE_API_PORT
|
||||||
|
|
||||||
|
|
||||||
def main():
|
def main():
|
||||||
parser = optparse.OptionParser(usage="usage: %prog [-l] <sessionid>")
|
parser = optparse.OptionParser(usage="usage: %prog [-l] <sessionid>")
|
||||||
|
|
@ -17,29 +20,30 @@ def main():
|
||||||
|
|
||||||
if options.list is True:
|
if options.list is True:
|
||||||
num = '0'
|
num = '0'
|
||||||
flags = coreapi.CORE_API_STR_FLAG
|
flags = MessageFlags.STRING.value
|
||||||
else:
|
else:
|
||||||
num = args[0]
|
num = args[0]
|
||||||
flags = coreapi.CORE_API_DEL_FLAG
|
flags = MessageFlags.DELETE.value
|
||||||
tlvdata = coreapi.CoreSessionTlv.pack(coreapi.CORE_TLV_SESS_NUMBER, num)
|
tlvdata = coreapi.CoreSessionTlv.pack(SessionTlvs.NUMBER.value, num)
|
||||||
msg = coreapi.CoreSessionMessage.pack(flags, tlvdata)
|
message = coreapi.CoreSessionMessage.pack(flags, tlvdata)
|
||||||
|
|
||||||
sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
|
sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
|
||||||
sock.connect(('localhost', coreapi.CORE_API_PORT))
|
sock.connect(('localhost', CORE_API_PORT))
|
||||||
sock.send(msg)
|
sock.send(message)
|
||||||
|
|
||||||
# receive and print a session list
|
# receive and print a session list
|
||||||
if options.list is True:
|
if options.list is True:
|
||||||
hdr = sock.recv(coreapi.CoreMessage.hdrsiz)
|
hdr = sock.recv(coreapi.CoreMessage.header_len)
|
||||||
msgtype, msgflags, msglen = coreapi.CoreMessage.unpackhdr(hdr)
|
msgtype, msgflags, msglen = coreapi.CoreMessage.unpack_header(hdr)
|
||||||
data = ""
|
data = ""
|
||||||
if msglen:
|
if msglen:
|
||||||
data = sock.recv(msglen)
|
data = sock.recv(msglen)
|
||||||
msg = coreapi.CoreMessage(msgflags, hdr, data)
|
message = coreapi.CoreMessage(msgflags, hdr, data)
|
||||||
sessions = msg.gettlv(coreapi.CORE_TLV_SESS_NUMBER)
|
sessions = message.get_tlv(coreapi.SessionTlvs.NUMBER.value)
|
||||||
print "sessions:", sessions
|
print "sessions:", sessions
|
||||||
|
|
||||||
sock.close()
|
sock.close()
|
||||||
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
main()
|
main()
|
||||||
|
|
|
||||||
|
|
@ -7,160 +7,177 @@
|
||||||
# authors: Tom Goff <thomas.goff@boeing.com>
|
# authors: Tom Goff <thomas.goff@boeing.com>
|
||||||
# Jeff Ahrenholz <jeffrey.m.ahrenholz@boeing.com>
|
# Jeff Ahrenholz <jeffrey.m.ahrenholz@boeing.com>
|
||||||
#
|
#
|
||||||
'''
|
|
||||||
|
"""
|
||||||
core-daemon: the CORE daemon is a server process that receives CORE API
|
core-daemon: the CORE daemon is a server process that receives CORE API
|
||||||
messages and instantiates emulated nodes and networks within the kernel. Various
|
messages and instantiates emulated nodes and networks within the kernel. Various
|
||||||
message handlers are defined and some support for sending messages.
|
message handlers are defined and some support for sending messages.
|
||||||
'''
|
"""
|
||||||
|
|
||||||
import os, optparse, ConfigParser, gc, shlex, socket, shutil
|
import ConfigParser
|
||||||
import atexit
|
import atexit
|
||||||
import signal
|
|
||||||
import importlib
|
import importlib
|
||||||
|
import logging
|
||||||
|
import optparse
|
||||||
|
import os
|
||||||
|
import signal
|
||||||
|
import socket
|
||||||
|
import sys
|
||||||
|
import threading
|
||||||
|
import time
|
||||||
|
|
||||||
try:
|
from core import constants
|
||||||
from core import pycore
|
from core import corehandlers
|
||||||
except ImportError:
|
from core import coreserver
|
||||||
# hack for Fedora autoconf that uses the following pythondir:
|
from core import enumerations
|
||||||
if "/usr/lib/python2.6/site-packages" in sys.path:
|
|
||||||
sys.path.append("/usr/local/lib/python2.6/site-packages")
|
|
||||||
if "/usr/lib64/python2.6/site-packages" in sys.path:
|
|
||||||
sys.path.append("/usr/local/lib64/python2.6/site-packages")
|
|
||||||
if "/usr/lib/python2.7/site-packages" in sys.path:
|
|
||||||
sys.path.append("/usr/local/lib/python2.7/site-packages")
|
|
||||||
if "/usr/lib64/python2.7/site-packages" in sys.path:
|
|
||||||
sys.path.append("/usr/local/lib64/python2.7/site-packages")
|
|
||||||
from core import pycore
|
|
||||||
from core.coreserver import *
|
|
||||||
from core.constants import *
|
|
||||||
from core.api import coreapi
|
from core.api import coreapi
|
||||||
from core.misc.utils import daemonize, closeonexec
|
from core.corehandlers import CoreDatagramRequestHandler
|
||||||
|
from core.enumerations import MessageFlags
|
||||||
|
from core.enumerations import RegisterTlvs
|
||||||
|
from core.misc import log
|
||||||
|
from core.misc import nodemaps
|
||||||
|
from core.misc import nodeutils
|
||||||
|
from core.misc.utils import closeonexec
|
||||||
|
from core.misc.utils import daemonize
|
||||||
|
from core.services import bird
|
||||||
|
from core.services import dockersvc
|
||||||
|
from core.services import nrl
|
||||||
|
from core.services import quagga
|
||||||
|
from core.services import security
|
||||||
|
from core.services import startup
|
||||||
|
from core.services import ucarp
|
||||||
|
from core.services import utility
|
||||||
|
from core.services import xorp
|
||||||
|
|
||||||
|
logger = log.get_logger(__name__)
|
||||||
|
|
||||||
DEFAULT_MAXFD = 1024
|
DEFAULT_MAXFD = 1024
|
||||||
|
|
||||||
# garbage collection debugging
|
|
||||||
# gc.set_debug(gc.DEBUG_STATS | gc.DEBUG_LEAK)
|
def startudp(core_server, server_address):
|
||||||
|
"""
|
||||||
|
Start a thread running a UDP server on the same host,port for connectionless requests.
|
||||||
|
|
||||||
|
:param core.coreserver.CoreServer core_server: core server instance
|
||||||
|
:param tuple[str, int] server_address: server address
|
||||||
|
:return: created core udp server
|
||||||
|
:rtype: core.coreserver.CoreUdpServer
|
||||||
|
"""
|
||||||
|
core_server.udpserver = coreserver.CoreUdpServer(server_address, CoreDatagramRequestHandler, core_server)
|
||||||
|
core_server.udpthread = threading.Thread(target=core_server.udpserver.start)
|
||||||
|
core_server.udpthread.daemon = True
|
||||||
|
core_server.udpthread.start()
|
||||||
|
return core_server.udpserver
|
||||||
|
|
||||||
|
|
||||||
coreapi.add_node_class("CORE_NODE_DEF",
|
def startaux(core_server, aux_address, aux_handler):
|
||||||
coreapi.CORE_NODE_DEF, pycore.nodes.CoreNode)
|
"""
|
||||||
coreapi.add_node_class("CORE_NODE_PHYS",
|
Start a thread running an auxiliary TCP server on the given address.
|
||||||
coreapi.CORE_NODE_PHYS, pycore.pnodes.PhysicalNode)
|
|
||||||
try:
|
|
||||||
coreapi.add_node_class("CORE_NODE_XEN",
|
|
||||||
coreapi.CORE_NODE_XEN, pycore.xen.XenNode)
|
|
||||||
except Exception:
|
|
||||||
#print "XenNode class unavailable."
|
|
||||||
pass
|
|
||||||
coreapi.add_node_class("CORE_NODE_TBD",
|
|
||||||
coreapi.CORE_NODE_TBD, None)
|
|
||||||
coreapi.add_node_class("CORE_NODE_SWITCH",
|
|
||||||
coreapi.CORE_NODE_SWITCH, pycore.nodes.SwitchNode)
|
|
||||||
coreapi.add_node_class("CORE_NODE_HUB",
|
|
||||||
coreapi.CORE_NODE_HUB, pycore.nodes.HubNode)
|
|
||||||
coreapi.add_node_class("CORE_NODE_WLAN",
|
|
||||||
coreapi.CORE_NODE_WLAN, pycore.nodes.WlanNode)
|
|
||||||
coreapi.add_node_class("CORE_NODE_RJ45",
|
|
||||||
coreapi.CORE_NODE_RJ45, pycore.nodes.RJ45Node)
|
|
||||||
coreapi.add_node_class("CORE_NODE_TUNNEL",
|
|
||||||
coreapi.CORE_NODE_TUNNEL, pycore.nodes.TunnelNode)
|
|
||||||
coreapi.add_node_class("CORE_NODE_EMANE",
|
|
||||||
coreapi.CORE_NODE_EMANE, pycore.nodes.EmaneNode)
|
|
||||||
|
|
||||||
#
|
|
||||||
# UDP server startup
|
|
||||||
#
|
|
||||||
def startudp(mainserver, server_address):
|
|
||||||
''' Start a thread running a UDP server on the same host,port for
|
|
||||||
connectionless requests.
|
|
||||||
'''
|
|
||||||
mainserver.udpserver = CoreUdpServer(server_address,
|
|
||||||
CoreDatagramRequestHandler, mainserver)
|
|
||||||
mainserver.udpthread = threading.Thread(target = mainserver.udpserver.start)
|
|
||||||
mainserver.udpthread.daemon = True
|
|
||||||
mainserver.udpthread.start()
|
|
||||||
return mainserver.udpserver
|
|
||||||
|
|
||||||
|
|
||||||
#
|
|
||||||
# Auxiliary server startup
|
|
||||||
#
|
|
||||||
def startaux(mainserver, aux_address, aux_handler):
|
|
||||||
''' Start a thread running an auxiliary TCP server on the given address.
|
|
||||||
This server will communicate with client requests using a handler
|
This server will communicate with client requests using a handler
|
||||||
using the aux_handler class. The aux_handler can provide an alternative
|
using the aux_handler class. The aux_handler can provide an alternative
|
||||||
API to CORE.
|
API to CORE.
|
||||||
'''
|
|
||||||
handlermodname,dot,handlerclassname = aux_handler.rpartition('.')
|
:param core.coreserver.CoreServer core_server: core server instance
|
||||||
|
:param tuple[str, int] aux_address: auxiliary server address
|
||||||
|
:param str aux_handler: auxiliary handler string to import
|
||||||
|
:return: auxiliary server
|
||||||
|
"""
|
||||||
|
handlermodname, dot, handlerclassname = aux_handler.rpartition(".")
|
||||||
handlermod = importlib.import_module(handlermodname)
|
handlermod = importlib.import_module(handlermodname)
|
||||||
handlerclass = getattr(handlermod, handlerclassname)
|
handlerclass = getattr(handlermod, handlerclassname)
|
||||||
mainserver.auxserver = CoreAuxServer(aux_address,
|
core_server.auxserver = coreserver.CoreAuxServer(aux_address, handlerclass, core_server)
|
||||||
handlerclass,
|
core_server.auxthread = threading.Thread(target=core_server.auxserver.start)
|
||||||
mainserver)
|
core_server.auxthread.daemon = True
|
||||||
mainserver.auxthread = threading.Thread(target = mainserver.auxserver.start)
|
core_server.auxthread.start()
|
||||||
mainserver.auxthread.daemon = True
|
return core_server.auxserver
|
||||||
mainserver.auxthread.start()
|
|
||||||
return mainserver.auxserver
|
|
||||||
|
|
||||||
|
|
||||||
def banner():
|
def banner():
|
||||||
''' Output the program banner printed to the terminal or log file.
|
"""
|
||||||
'''
|
Output the program banner printed to the terminal or log file.
|
||||||
sys.stdout.write("CORE daemon v.%s started %s\n" % \
|
|
||||||
(COREDPY_VERSION, time.ctime()))
|
:return: nothing
|
||||||
sys.stdout.flush()
|
"""
|
||||||
|
logger.info("CORE daemon v.%s started %s\n" % (constants.COREDPY_VERSION, time.ctime()))
|
||||||
|
|
||||||
|
|
||||||
def cored(cfg=None):
|
def cored(cfg=None):
|
||||||
''' Start the CoreServer object and enter the server loop.
|
"""
|
||||||
'''
|
Start the CoreServer object and enter the server loop.
|
||||||
host = cfg['listenaddr']
|
|
||||||
port = int(cfg['port'])
|
:param dict cfg: core configuration
|
||||||
if host == '' or host is None:
|
:return: nothing
|
||||||
|
"""
|
||||||
|
host = cfg["listenaddr"]
|
||||||
|
port = int(cfg["port"])
|
||||||
|
if host == "" or host is None:
|
||||||
host = "localhost"
|
host = "localhost"
|
||||||
try:
|
try:
|
||||||
server = CoreServer((host, port), CoreRequestHandler, cfg)
|
server = coreserver.CoreServer((host, port), corehandlers.CoreRequestHandler, cfg)
|
||||||
except Exception, e:
|
except:
|
||||||
sys.stderr.write("error starting main server on: %s:%s\n\t%s\n" % \
|
logger.exception("error starting main server on: %s:%s", host, port)
|
||||||
(host, port, e))
|
|
||||||
sys.stderr.flush()
|
|
||||||
sys.exit(1)
|
sys.exit(1)
|
||||||
|
|
||||||
closeonexec(server.fileno())
|
closeonexec(server.fileno())
|
||||||
sys.stdout.write("main server started, listening on: %s:%s\n" % (host, port))
|
logger.info("main server started, listening on: %s:%s\n" % (host, port))
|
||||||
sys.stdout.flush()
|
|
||||||
|
|
||||||
udpserver = startudp(server, (host, port))
|
udpserver = startudp(server, (host, port))
|
||||||
closeonexec(udpserver.fileno())
|
closeonexec(udpserver.fileno())
|
||||||
|
|
||||||
auxreqhandler = cfg['aux_request_handler']
|
auxreqhandler = cfg["aux_request_handler"]
|
||||||
if auxreqhandler:
|
if auxreqhandler:
|
||||||
try:
|
handler, auxport = auxreqhandler.rsplit(":")
|
||||||
handler, auxport = auxreqhandler.rsplit(':')
|
|
||||||
auxserver = startaux(server, (host, int(auxport)), handler)
|
auxserver = startaux(server, (host, int(auxport)), handler)
|
||||||
closeonexec(auxserver.fileno())
|
closeonexec(auxserver.fileno())
|
||||||
except Exception as e:
|
|
||||||
raise ValueError, "invalid auxreqhandler:(%s)\nError: %s" % (auxreqhandler, e)
|
|
||||||
|
|
||||||
server.serve_forever()
|
server.serve_forever()
|
||||||
|
|
||||||
|
|
||||||
|
# TODO: should sessions and the main core daemon both catch at exist to shutdown independently?
|
||||||
def cleanup():
|
def cleanup():
|
||||||
while CoreServer.servers:
|
"""
|
||||||
server = CoreServer.servers.pop()
|
Runs server shutdown and cleanup when catching an exit signal.
|
||||||
|
|
||||||
|
:return: nothing
|
||||||
|
"""
|
||||||
|
while coreserver.CoreServer.servers:
|
||||||
|
server = coreserver.CoreServer.servers.pop()
|
||||||
server.shutdown()
|
server.shutdown()
|
||||||
|
|
||||||
|
|
||||||
atexit.register(cleanup)
|
atexit.register(cleanup)
|
||||||
|
|
||||||
|
|
||||||
def sighandler(signum, stackframe):
|
def sighandler(signum, stackframe):
|
||||||
print >> sys.stderr, "terminated by signal:", signum
|
"""
|
||||||
|
Signal handler when different signals are sent.
|
||||||
|
|
||||||
|
:param int signum: singal number sent
|
||||||
|
:param stackframe: stack frame sent
|
||||||
|
:return: nothing
|
||||||
|
"""
|
||||||
|
logger.error("terminated by signal: %s", signum)
|
||||||
sys.exit(signum)
|
sys.exit(signum)
|
||||||
|
|
||||||
|
|
||||||
signal.signal(signal.SIGHUP, sighandler)
|
signal.signal(signal.SIGHUP, sighandler)
|
||||||
signal.signal(signal.SIGINT, sighandler)
|
signal.signal(signal.SIGINT, sighandler)
|
||||||
signal.signal(signal.SIGTERM, sighandler)
|
signal.signal(signal.SIGTERM, sighandler)
|
||||||
signal.signal(signal.SIGUSR1, sighandler)
|
signal.signal(signal.SIGUSR1, sighandler)
|
||||||
signal.signal(signal.SIGUSR2, sighandler)
|
signal.signal(signal.SIGUSR2, sighandler)
|
||||||
|
|
||||||
|
|
||||||
def logrotate(stdout, stderr, stdoutmode=0644, stderrmode=0644):
|
def logrotate(stdout, stderr, stdoutmode=0644, stderrmode=0644):
|
||||||
|
"""
|
||||||
|
Log rotation method.
|
||||||
|
|
||||||
|
:param stdout: stdout
|
||||||
|
:param stderr: stderr
|
||||||
|
:param int stdoutmode: stdout mode
|
||||||
|
:param int stderrmode: stderr mode
|
||||||
|
:return:
|
||||||
|
"""
|
||||||
|
|
||||||
def reopen(fileno, filename, mode):
|
def reopen(fileno, filename, mode):
|
||||||
err = 0
|
err = 0
|
||||||
fd = -1
|
fd = -1
|
||||||
|
|
@ -174,6 +191,7 @@ def logrotate(stdout, stderr, stdoutmode = 0644, stderrmode = 0644):
|
||||||
if fd >= 0:
|
if fd >= 0:
|
||||||
os.close(fd)
|
os.close(fd)
|
||||||
return err
|
return err
|
||||||
|
|
||||||
if stdout:
|
if stdout:
|
||||||
err = reopen(1, stdout, stdoutmode)
|
err = reopen(1, stdout, stdoutmode)
|
||||||
if stderr:
|
if stderr:
|
||||||
|
|
@ -185,27 +203,32 @@ def logrotate(stdout, stderr, stdoutmode = 0644, stderrmode = 0644):
|
||||||
else:
|
else:
|
||||||
reopen(2, stderr, stderrmode)
|
reopen(2, stderr, stderrmode)
|
||||||
|
|
||||||
def getMergedConfig(filename):
|
|
||||||
''' Return a configuration after merging config file and command-line
|
def get_merged_config(filename):
|
||||||
arguments.
|
"""
|
||||||
'''
|
Return a configuration after merging config file and command-line arguments.
|
||||||
|
|
||||||
|
:param str filename: file name to merge configuration settings with
|
||||||
|
:return: merged configuration
|
||||||
|
:rtype: dict
|
||||||
|
"""
|
||||||
# these are the defaults used in the config file
|
# these are the defaults used in the config file
|
||||||
defaults = { 'port' : '%d' % coreapi.CORE_API_PORT,
|
defaults = {"port": "%d" % enumerations.CORE_API_PORT,
|
||||||
'listenaddr' : 'localhost',
|
"listenaddr": "localhost",
|
||||||
'pidfile' : '%s/run/core-daemon.pid' % CORE_STATE_DIR,
|
"pidfile": "%s/run/core-daemon.pid" % constants.CORE_STATE_DIR,
|
||||||
'logfile' : '%s/log/core-daemon.log' % CORE_STATE_DIR,
|
"logfile": "%s/log/core-daemon.log" % constants.CORE_STATE_DIR,
|
||||||
'xmlfilever' : '1.0',
|
"xmlfilever": "1.0",
|
||||||
'numthreads' : '1',
|
"numthreads": "1",
|
||||||
'verbose' : 'False',
|
"verbose": "False",
|
||||||
'daemonize' : 'False',
|
"daemonize": "False",
|
||||||
'debug' : 'False',
|
"debug": "False",
|
||||||
'execfile' : None,
|
"execfile": None,
|
||||||
'aux_request_handler' : None,
|
"aux_request_handler": None,
|
||||||
}
|
}
|
||||||
|
|
||||||
usagestr = "usage: %prog [-h] [options] [args]\n\n" + \
|
usagestr = "usage: %prog [-h] [options] [args]\n\n" + \
|
||||||
"CORE daemon v.%s instantiates Linux network namespace " \
|
"CORE daemon v.%s instantiates Linux network namespace " \
|
||||||
"nodes." % COREDPY_VERSION
|
"nodes." % constants.COREDPY_VERSION
|
||||||
parser = optparse.OptionParser(usage=usagestr)
|
parser = optparse.OptionParser(usage=usagestr)
|
||||||
parser.add_option("-f", "--configfile", dest="configfile",
|
parser.add_option("-f", "--configfile", dest="configfile",
|
||||||
type="string",
|
type="string",
|
||||||
|
|
@ -237,7 +260,7 @@ def getMergedConfig(filename):
|
||||||
defaults["debug"])
|
defaults["debug"])
|
||||||
|
|
||||||
# parse command line options
|
# parse command line options
|
||||||
(options, args) = parser.parse_args()
|
options, args = parser.parse_args()
|
||||||
|
|
||||||
# read the config file
|
# read the config file
|
||||||
if options.configfile is not None:
|
if options.configfile is not None:
|
||||||
|
|
@ -252,14 +275,14 @@ def getMergedConfig(filename):
|
||||||
# gracefully support legacy configs (cored.py/cored now core-daemon)
|
# gracefully support legacy configs (cored.py/cored now core-daemon)
|
||||||
if cfg.has_section("cored.py"):
|
if cfg.has_section("cored.py"):
|
||||||
for name, val in cfg.items("cored.py"):
|
for name, val in cfg.items("cored.py"):
|
||||||
if name == 'pidfile' or name == 'logfile':
|
if name == "pidfile" or name == "logfile":
|
||||||
bn = os.path.basename(val).replace('coredpy', 'core-daemon')
|
bn = os.path.basename(val).replace("coredpy", "core-daemon")
|
||||||
val = os.path.join(os.path.dirname(val), bn)
|
val = os.path.join(os.path.dirname(val), bn)
|
||||||
cfg.set(section, name, val)
|
cfg.set(section, name, val)
|
||||||
if cfg.has_section("cored"):
|
if cfg.has_section("cored"):
|
||||||
for name, val in cfg.items("cored"):
|
for name, val in cfg.items("cored"):
|
||||||
if name == 'pidfile' or name == 'logfile':
|
if name == "pidfile" or name == "logfile":
|
||||||
bn = os.path.basename(val).replace('cored', 'core-daemon')
|
bn = os.path.basename(val).replace("cored", "core-daemon")
|
||||||
val = os.path.join(os.path.dirname(val), bn)
|
val = os.path.join(os.path.dirname(val), bn)
|
||||||
cfg.set(section, name, val)
|
cfg.set(section, name, val)
|
||||||
|
|
||||||
|
|
@ -271,49 +294,74 @@ def getMergedConfig(filename):
|
||||||
|
|
||||||
return dict(cfg.items(section)), args
|
return dict(cfg.items(section)), args
|
||||||
|
|
||||||
|
|
||||||
def exec_file(cfg):
|
def exec_file(cfg):
|
||||||
''' Send a Register Message to execute a new session based on XML or Python
|
"""
|
||||||
script file.
|
Send a Register Message to execute a new session based on XML or Python script file.
|
||||||
'''
|
|
||||||
filename = cfg['execfile']
|
:param dict cfg: configuration settings
|
||||||
sys.stdout.write("Telling daemon to execute file: '%s'...\n" % filename)
|
:return: 0
|
||||||
sys.stdout.flush()
|
"""
|
||||||
tlvdata = coreapi.CoreRegTlv.pack(coreapi.CORE_TLV_REG_EXECSRV, filename)
|
filename = cfg["execfile"]
|
||||||
msg = coreapi.CoreRegMessage.pack(coreapi.CORE_API_ADD_FLAG, tlvdata)
|
logger.info("Telling daemon to execute file: %s...", filename)
|
||||||
|
tlvdata = coreapi.CoreRegisterTlv.pack(RegisterTlvs.EXECUTE_SERVER.value, filename)
|
||||||
|
msg = coreapi.CoreRegMessage.pack(MessageFlags.ADD.value, tlvdata)
|
||||||
sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
|
sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
|
||||||
sock.connect(("localhost", int(cfg['port']))) # TODO: connect address option
|
sock.connect(("localhost", int(cfg["port"]))) # TODO: connect address option
|
||||||
sock.sendall(msg)
|
sock.sendall(msg)
|
||||||
return 0
|
return 0
|
||||||
|
|
||||||
def main():
|
|
||||||
''' Main program startup.
|
|
||||||
'''
|
|
||||||
# get a configuration merged from config file and command-line arguments
|
|
||||||
cfg, args = getMergedConfig("%s/core.conf" % CORE_CONF_DIR)
|
|
||||||
for a in args:
|
|
||||||
sys.stderr.write("ignoring command line argument: '%s'\n" % a)
|
|
||||||
|
|
||||||
if cfg['daemonize'] == 'True':
|
def main():
|
||||||
|
"""
|
||||||
|
Main program startup.
|
||||||
|
|
||||||
|
:return: nothing
|
||||||
|
"""
|
||||||
|
# get a configuration merged from config file and command-line arguments
|
||||||
|
cfg, args = get_merged_config("%s/core.conf" % constants.CORE_CONF_DIR)
|
||||||
|
for a in args:
|
||||||
|
logger.error("ignoring command line argument: %s", a)
|
||||||
|
|
||||||
|
if cfg["daemonize"] == "True":
|
||||||
daemonize(rootdir=None, umask=0, close_fds=False,
|
daemonize(rootdir=None, umask=0, close_fds=False,
|
||||||
stdin=os.devnull,
|
stdin=os.devnull,
|
||||||
stdout = cfg['logfile'], stderr = cfg['logfile'],
|
stdout=cfg["logfile"], stderr=cfg["logfile"],
|
||||||
pidfilename = cfg['pidfile'],
|
pidfilename=cfg["pidfile"],
|
||||||
defaultmaxfd=DEFAULT_MAXFD)
|
defaultmaxfd=DEFAULT_MAXFD)
|
||||||
signal.signal(signal.SIGUSR1, lambda signum, stackframe:
|
signal.signal(signal.SIGUSR1, lambda signum, stackframe:
|
||||||
logrotate(stdout = cfg['logfile'],
|
logrotate(stdout=cfg["logfile"], stderr=cfg["logfile"]))
|
||||||
stderr = cfg['logfile']))
|
|
||||||
|
|
||||||
banner()
|
banner()
|
||||||
if cfg['execfile']:
|
if cfg["execfile"]:
|
||||||
cfg['execfile'] = os.path.abspath(cfg['execfile'])
|
cfg["execfile"] = os.path.abspath(cfg["execfile"])
|
||||||
sys.exit(exec_file(cfg))
|
sys.exit(exec_file(cfg))
|
||||||
try:
|
try:
|
||||||
cored(cfg)
|
cored(cfg)
|
||||||
except KeyboardInterrupt:
|
except KeyboardInterrupt:
|
||||||
pass
|
logger.info("keyboard interrupt, stopping core daemon")
|
||||||
|
|
||||||
sys.exit(0)
|
sys.exit(0)
|
||||||
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
|
log.setup(level=logging.INFO)
|
||||||
|
|
||||||
|
# configure nodes to use
|
||||||
|
node_map = nodemaps.CLASSIC_NODES
|
||||||
|
if len(sys.argv) == 2 and sys.argv[1] == "ovs":
|
||||||
|
node_map = nodemaps.OVS_NODES
|
||||||
|
nodeutils.set_node_map(node_map)
|
||||||
|
|
||||||
|
# load default services
|
||||||
|
quagga.load_services()
|
||||||
|
nrl.load_services()
|
||||||
|
xorp.load_services()
|
||||||
|
bird.load_services()
|
||||||
|
utility.load_services()
|
||||||
|
security.load_services()
|
||||||
|
ucarp.load_services()
|
||||||
|
dockersvc.load_services()
|
||||||
|
startup.load_services()
|
||||||
|
|
||||||
main()
|
main()
|
||||||
|
|
|
||||||
|
|
@ -6,29 +6,31 @@
|
||||||
#
|
#
|
||||||
# author: Jeff Ahrenholz <jeffrey.m.ahrenholz@boeing.com>
|
# author: Jeff Ahrenholz <jeffrey.m.ahrenholz@boeing.com>
|
||||||
#
|
#
|
||||||
'''
|
"""
|
||||||
core-manage: Helper tool to add, remove, or check for services, models, and
|
core-manage: Helper tool to add, remove, or check for services, models, and
|
||||||
node types in a CORE installation.
|
node types in a CORE installation.
|
||||||
'''
|
"""
|
||||||
|
|
||||||
import os
|
|
||||||
import sys
|
|
||||||
import ast
|
import ast
|
||||||
import optparse
|
import optparse
|
||||||
|
import os
|
||||||
import re
|
import re
|
||||||
|
import sys
|
||||||
|
|
||||||
from core import pycore
|
from core import services
|
||||||
from core.constants import CORE_CONF_DIR
|
from core.constants import CORE_CONF_DIR
|
||||||
|
|
||||||
|
|
||||||
class FileUpdater(object):
|
class FileUpdater(object):
|
||||||
''' Helper class for changing configuration files.
|
"""
|
||||||
'''
|
Helper class for changing configuration files.
|
||||||
|
"""
|
||||||
actions = ("add", "remove", "check")
|
actions = ("add", "remove", "check")
|
||||||
targets = ("service", "model", "nodetype")
|
targets = ("service", "model", "nodetype")
|
||||||
|
|
||||||
def __init__(self, action, target, data, options):
|
def __init__(self, action, target, data, options):
|
||||||
'''
|
"""
|
||||||
'''
|
"""
|
||||||
self.action = action
|
self.action = action
|
||||||
self.target = target
|
self.target = target
|
||||||
self.data = data
|
self.data = data
|
||||||
|
|
@ -37,13 +39,13 @@ class FileUpdater(object):
|
||||||
self.search, self.filename = self.get_filename(target)
|
self.search, self.filename = self.get_filename(target)
|
||||||
|
|
||||||
def process(self):
|
def process(self):
|
||||||
''' Invoke update_file() using a helper method depending on target.
|
""" Invoke update_file() using a helper method depending on target.
|
||||||
'''
|
"""
|
||||||
if self.verbose:
|
if self.verbose:
|
||||||
txt = "Updating"
|
txt = "Updating"
|
||||||
if self.action == "check":
|
if self.action == "check":
|
||||||
txt = "Checking"
|
txt = "Checking"
|
||||||
sys.stdout.write("%s file: '%s'\n" % (txt, self.filename))
|
sys.stdout.write("%s file: %s\n" % (txt, self.filename))
|
||||||
|
|
||||||
if self.target == "service":
|
if self.target == "service":
|
||||||
r = self.update_file(fn=self.update_services)
|
r = self.update_file(fn=self.update_services)
|
||||||
|
|
@ -64,41 +66,40 @@ class FileUpdater(object):
|
||||||
return r
|
return r
|
||||||
|
|
||||||
def update_services(self, line):
|
def update_services(self, line):
|
||||||
''' Modify the __init__.py file having this format:
|
""" Modify the __init__.py file having this format:
|
||||||
__all__ = ["quagga", "nrl", "xorp", "bird", ]
|
__all__ = ["quagga", "nrl", "xorp", "bird", ]
|
||||||
Returns True or False when "check" is the action, a modified line
|
Returns True or False when "check" is the action, a modified line
|
||||||
otherwise.
|
otherwise.
|
||||||
'''
|
"""
|
||||||
line = line.strip('\n')
|
line = line.strip("\n")
|
||||||
key, valstr = line.split('= ')
|
key, valstr = line.split("= ")
|
||||||
vals = ast.literal_eval(valstr)
|
vals = ast.literal_eval(valstr)
|
||||||
r = self.update_keyvals(key, vals)
|
r = self.update_keyvals(key, vals)
|
||||||
if self.action == "check":
|
if self.action == "check":
|
||||||
return r
|
return r
|
||||||
valstr = '%s' % r
|
valstr = "%s" % r
|
||||||
return '= '.join([key, valstr]) + '\n'
|
return "= ".join([key, valstr]) + "\n"
|
||||||
|
|
||||||
|
|
||||||
def update_emane_models(self, line):
|
def update_emane_models(self, line):
|
||||||
''' Modify the core.conf file having this format:
|
""" Modify the core.conf file having this format:
|
||||||
emane_models = RfPipe, Ieee80211abg, CommEffect, Bypass
|
emane_models = RfPipe, Ieee80211abg, CommEffect, Bypass
|
||||||
Returns True or False when "check" is the action, a modified line
|
Returns True or False when "check" is the action, a modified line
|
||||||
otherwise.
|
otherwise.
|
||||||
'''
|
"""
|
||||||
line = line.strip('\n')
|
line = line.strip("\n")
|
||||||
key, valstr = line.split('= ')
|
key, valstr = line.split("= ")
|
||||||
vals = valstr.split(', ')
|
vals = valstr.split(", ")
|
||||||
r = self.update_keyvals(key, vals)
|
r = self.update_keyvals(key, vals)
|
||||||
if self.action == "check":
|
if self.action == "check":
|
||||||
return r
|
return r
|
||||||
valstr = ', '.join(r)
|
valstr = ", ".join(r)
|
||||||
return '= '.join([key, valstr]) + '\n'
|
return "= ".join([key, valstr]) + "\n"
|
||||||
|
|
||||||
def update_keyvals(self, key, vals):
|
def update_keyvals(self, key, vals):
|
||||||
''' Perform self.action on (key, vals).
|
""" Perform self.action on (key, vals).
|
||||||
Returns True or False when "check" is the action, a modified line
|
Returns True or False when "check" is the action, a modified line
|
||||||
otherwise.
|
otherwise.
|
||||||
'''
|
"""
|
||||||
if self.action == "check":
|
if self.action == "check":
|
||||||
if self.data in vals:
|
if self.data in vals:
|
||||||
return True
|
return True
|
||||||
|
|
@ -115,11 +116,10 @@ class FileUpdater(object):
|
||||||
return vals
|
return vals
|
||||||
|
|
||||||
def get_filename(self, target):
|
def get_filename(self, target):
|
||||||
''' Return search string and filename based on target.
|
""" Return search string and filename based on target.
|
||||||
'''
|
"""
|
||||||
if target == "service":
|
if target == "service":
|
||||||
pypath = os.path.dirname(pycore.__file__)
|
filename = os.path.abspath(services.__file__)
|
||||||
filename = os.path.join(pypath, "services", "__init__.py")
|
|
||||||
search = "__all__ ="
|
search = "__all__ ="
|
||||||
elif target == "model":
|
elif target == "model":
|
||||||
filename = os.path.join(CORE_CONF_DIR, "core.conf")
|
filename = os.path.join(CORE_CONF_DIR, "core.conf")
|
||||||
|
|
@ -132,15 +132,15 @@ class FileUpdater(object):
|
||||||
else:
|
else:
|
||||||
raise ValueError, "unknown target"
|
raise ValueError, "unknown target"
|
||||||
if not os.path.exists(filename):
|
if not os.path.exists(filename):
|
||||||
raise ValueError, "file '%s' does not exist" % filename
|
raise ValueError, "file %s does not exist" % filename
|
||||||
return search, filename
|
return search, filename
|
||||||
|
|
||||||
def update_file(self, fn=None):
|
def update_file(self, fn=None):
|
||||||
''' Open a file and search for self.search, invoking the supplied
|
""" Open a file and search for self.search, invoking the supplied
|
||||||
function on the matching line. Write file changes if necessary.
|
function on the matching line. Write file changes if necessary.
|
||||||
Returns True if the file has changed (or action is "check" and the
|
Returns True if the file has changed (or action is "check" and the
|
||||||
search string is found), False otherwise.
|
search string is found), False otherwise.
|
||||||
'''
|
"""
|
||||||
changed = False
|
changed = False
|
||||||
output = "" # this accumulates output, assumes input is small
|
output = "" # this accumulates output, assumes input is small
|
||||||
with open(self.filename, "r") as f:
|
with open(self.filename, "r") as f:
|
||||||
|
|
@ -161,11 +161,11 @@ class FileUpdater(object):
|
||||||
return changed
|
return changed
|
||||||
|
|
||||||
def update_nodes_conf(self):
|
def update_nodes_conf(self):
|
||||||
''' Add/remove/check entries from nodes.conf. This file
|
""" Add/remove/check entries from nodes.conf. This file
|
||||||
contains a Tcl-formatted array of node types. The array index must be
|
contains a Tcl-formatted array of node types. The array index must be
|
||||||
properly set for new entries. Uses self.{action, filename, search,
|
properly set for new entries. Uses self.{action, filename, search,
|
||||||
data} variables as input and returns the same value as update_file().
|
data} variables as input and returns the same value as update_file().
|
||||||
'''
|
"""
|
||||||
changed = False
|
changed = False
|
||||||
output = "" # this accumulates output, assumes input is small
|
output = "" # this accumulates output, assumes input is small
|
||||||
with open(self.filename, "r") as f:
|
with open(self.filename, "r") as f:
|
||||||
|
|
@ -181,9 +181,10 @@ class FileUpdater(object):
|
||||||
continue
|
continue
|
||||||
else:
|
else:
|
||||||
output += line
|
output += line
|
||||||
|
|
||||||
if self.action == "add":
|
if self.action == "add":
|
||||||
index = int(re.match('^\d+', line).group(0))
|
index = int(re.match("^\d+", line).group(0))
|
||||||
output += str(index + 1) + ' ' + self.data + '\n'
|
output += str(index + 1) + " " + self.data + "\n"
|
||||||
changed = True
|
changed = True
|
||||||
if changed:
|
if changed:
|
||||||
with open(self.filename, "w") as f:
|
with open(self.filename, "w") as f:
|
||||||
|
|
@ -200,11 +201,11 @@ def main():
|
||||||
usagestr += "\n %prog -v check model RfPipe"
|
usagestr += "\n %prog -v check model RfPipe"
|
||||||
usagestr += "\n %prog --userpath=\"$HOME/.core\" add nodetype \"{ftp ftp.gif ftp.gif {DefaultRoute FTP} netns {FTP server} }\" \n"
|
usagestr += "\n %prog --userpath=\"$HOME/.core\" add nodetype \"{ftp ftp.gif ftp.gif {DefaultRoute FTP} netns {FTP server} }\" \n"
|
||||||
usagestr += "\nArguments:\n <action> should be one of: %s" % \
|
usagestr += "\nArguments:\n <action> should be one of: %s" % \
|
||||||
', '.join(FileUpdater.actions)
|
", ".join(FileUpdater.actions)
|
||||||
usagestr += "\n <target> should be one of: %s" % \
|
usagestr += "\n <target> should be one of: %s" % \
|
||||||
', '.join(FileUpdater.targets)
|
", ".join(FileUpdater.targets)
|
||||||
usagestr += "\n <string> is the text to %s" % \
|
usagestr += "\n <string> is the text to %s" % \
|
||||||
', '.join(FileUpdater.actions)
|
", ".join(FileUpdater.actions)
|
||||||
parser = optparse.OptionParser(usage=usagestr)
|
parser = optparse.OptionParser(usage=usagestr)
|
||||||
parser.set_defaults(userpath=None, verbose=False, )
|
parser.set_defaults(userpath=None, verbose=False, )
|
||||||
|
|
||||||
|
|
@ -228,11 +229,11 @@ def main():
|
||||||
|
|
||||||
action = args[0]
|
action = args[0]
|
||||||
if action not in FileUpdater.actions:
|
if action not in FileUpdater.actions:
|
||||||
usage("invalid action '%s'" % action, 1)
|
usage("invalid action %s" % action, 1)
|
||||||
|
|
||||||
target = args[1]
|
target = args[1]
|
||||||
if target not in FileUpdater.targets:
|
if target not in FileUpdater.targets:
|
||||||
usage("invalid target '%s'" % target, 1)
|
usage("invalid target %s" % target, 1)
|
||||||
|
|
||||||
if target == "nodetype" and not options.userpath:
|
if target == "nodetype" and not options.userpath:
|
||||||
usage("user path option required for this target (%s)" % target)
|
usage("user path option required for this target (%s)" % target)
|
||||||
|
|
@ -249,5 +250,6 @@ def main():
|
||||||
sys.exit(1)
|
sys.exit(1)
|
||||||
sys.exit(0)
|
sys.exit(0)
|
||||||
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
main()
|
main()
|
||||||
|
|
|
||||||
|
|
@ -1,8 +1,12 @@
|
||||||
# Copyright (c)2010-2012 the Boeing Company.
|
# Copyright (c)2010-2012 the Boeing Company.
|
||||||
# See the LICENSE file included in this distribution.
|
# See the LICENSE file included in this distribution.
|
||||||
|
|
||||||
import os, glob
|
"""
|
||||||
from distutils.core import setup
|
Defines how CORE will be built for installation.
|
||||||
|
"""
|
||||||
|
|
||||||
|
from setuptools import setup
|
||||||
|
|
||||||
from core.constants import COREDPY_VERSION
|
from core.constants import COREDPY_VERSION
|
||||||
|
|
||||||
setup(name="core-python",
|
setup(name="core-python",
|
||||||
|
|
@ -11,18 +15,23 @@ setup(name = "core-python",
|
||||||
"core",
|
"core",
|
||||||
"core.addons",
|
"core.addons",
|
||||||
"core.api",
|
"core.api",
|
||||||
|
"core.bsd",
|
||||||
"core.emane",
|
"core.emane",
|
||||||
"core.misc",
|
"core.misc",
|
||||||
"core.bsd",
|
|
||||||
"core.netns",
|
"core.netns",
|
||||||
"core.phys",
|
"core.phys",
|
||||||
"core.xen",
|
|
||||||
"core.services",
|
"core.services",
|
||||||
|
"core.xen",
|
||||||
|
"core.xml",
|
||||||
],
|
],
|
||||||
|
install_requires=[
|
||||||
|
"enum34"
|
||||||
|
],
|
||||||
|
setup_requires=["pytest-runner"],
|
||||||
|
tests_require=["pytest"],
|
||||||
description="Python components of CORE",
|
description="Python components of CORE",
|
||||||
url="http://www.nrl.navy.mil/itd/ncs/products/core",
|
url="http://www.nrl.navy.mil/itd/ncs/products/core",
|
||||||
author="Boeing Research & Technology",
|
author="Boeing Research & Technology",
|
||||||
author_email="core-dev@pf.itd.nrl.navy.mil",
|
author_email="core-dev@pf.itd.nrl.navy.mil",
|
||||||
license="BSD",
|
license="BSD",
|
||||||
long_description="Python scripts and modules for building virtual " \
|
long_description="Python scripts and modules for building virtual emulated networks.")
|
||||||
"emulated networks.")
|
|
||||||
|
|
|
||||||
|
|
@ -1,12 +1,20 @@
|
||||||
# Copyright (c)2010-2012 the Boeing Company.
|
# Copyright (c)2010-2012 the Boeing Company.
|
||||||
# See the LICENSE file included in this distribution.
|
# See the LICENSE file included in this distribution.
|
||||||
|
|
||||||
import os, glob
|
from setuptools import setup, Extension
|
||||||
from distutils.core import setup, Extension
|
|
||||||
|
|
||||||
netns = Extension("netns", sources = ["netnsmodule.c", "netns.c"])
|
netns = Extension(
|
||||||
vcmd = Extension("vcmd",
|
"netns",
|
||||||
sources = ["vcmdmodule.c",
|
sources=[
|
||||||
|
"netnsmodule.c",
|
||||||
|
"netns.c"
|
||||||
|
]
|
||||||
|
)
|
||||||
|
|
||||||
|
vcmd = Extension(
|
||||||
|
"vcmd",
|
||||||
|
sources=[
|
||||||
|
"vcmdmodule.c",
|
||||||
"vnode_client.c",
|
"vnode_client.c",
|
||||||
"vnode_chnl.c",
|
"vnode_chnl.c",
|
||||||
"vnode_io.c",
|
"vnode_io.c",
|
||||||
|
|
@ -14,16 +22,22 @@ vcmd = Extension("vcmd",
|
||||||
"vnode_cmd.c",
|
"vnode_cmd.c",
|
||||||
],
|
],
|
||||||
library_dirs=["build/lib"],
|
library_dirs=["build/lib"],
|
||||||
libraries = ["ev"])
|
libraries=["ev"]
|
||||||
|
)
|
||||||
|
|
||||||
setup(name = "core-python-netns",
|
setup(
|
||||||
|
name="core-python-netns",
|
||||||
version="1.0",
|
version="1.0",
|
||||||
description = "Extension modules to support virtual nodes using " \
|
description="Extension modules to support virtual nodes using "
|
||||||
"Linux network namespaces",
|
"Linux network namespaces",
|
||||||
ext_modules = [netns, vcmd],
|
ext_modules=[
|
||||||
|
netns,
|
||||||
|
vcmd
|
||||||
|
],
|
||||||
url="http://www.nrl.navy.mil/itd/ncs/products/core",
|
url="http://www.nrl.navy.mil/itd/ncs/products/core",
|
||||||
author="Boeing Research & Technology",
|
author="Boeing Research & Technology",
|
||||||
author_email="core-dev@pf.itd.nrl.navy.mil",
|
author_email="core-dev@pf.itd.nrl.navy.mil",
|
||||||
license="BSD",
|
license="BSD",
|
||||||
long_description="Extension modules and utilities to support virtual " \
|
long_description="Extension modules and utilities to support virtual "
|
||||||
"nodes using Linux network namespaces")
|
"nodes using Linux network namespaces"
|
||||||
|
)
|
||||||
|
|
|
||||||
|
|
@ -1,26 +1,26 @@
|
||||||
#!/usr/bin/env python
|
#!/usr/bin/env python
|
||||||
|
|
||||||
import sys
|
|
||||||
import os.path
|
import os.path
|
||||||
import site
|
import site
|
||||||
|
import sys
|
||||||
|
|
||||||
|
|
||||||
def main():
|
def main():
|
||||||
'''\
|
"""
|
||||||
Check if the given prefix is included in sys.path for the given
|
Check if the given prefix is included in sys.path for the given
|
||||||
python version; if not find an alternate valid prefix. Print the
|
python version; if not find an alternate valid prefix. Print the
|
||||||
result to standard out.
|
result to standard out.
|
||||||
'''
|
"""
|
||||||
if len(sys.argv) != 3:
|
if len(sys.argv) != 3:
|
||||||
msg = 'usage: %s <prefix> <python version>\n' % \
|
msg = "usage: %s <prefix> <python version>\n" % os.path.basename(sys.argv[0])
|
||||||
os.path.basename(sys.argv[0])
|
|
||||||
sys.stderr.write(msg)
|
sys.stderr.write(msg)
|
||||||
return 1
|
return 1
|
||||||
python_prefix = sys.argv[1]
|
python_prefix = sys.argv[1]
|
||||||
python_version = sys.argv[2]
|
python_version = sys.argv[2]
|
||||||
path = '%s/lib/python%s' % (python_prefix, python_version)
|
path = "%s/lib/python%s" % (python_prefix, python_version)
|
||||||
path = os.path.normpath(path)
|
path = os.path.normpath(path)
|
||||||
if path[-1] != '/':
|
if path[-1] != "/":
|
||||||
path = path + '/'
|
path += "/"
|
||||||
prefix = None
|
prefix = None
|
||||||
for p in sys.path:
|
for p in sys.path:
|
||||||
if p.startswith(path):
|
if p.startswith(path):
|
||||||
|
|
@ -28,8 +28,9 @@ def main():
|
||||||
break
|
break
|
||||||
if not prefix:
|
if not prefix:
|
||||||
prefix = site.PREFIXES[-1]
|
prefix = site.PREFIXES[-1]
|
||||||
sys.stdout.write('%s\n' % prefix)
|
sys.stdout.write("%s\n" % prefix)
|
||||||
return 0
|
return 0
|
||||||
|
|
||||||
if __name__ == '__main__':
|
|
||||||
|
if __name__ == "__main__":
|
||||||
sys.exit(main())
|
sys.exit(main())
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue