initial commit after bringing over cleaned up code and testing some examples

This commit is contained in:
Blake J. Harnden 2017-04-25 08:45:34 -07:00
parent c4858e6e0d
commit 00f4ebf5a9
93 changed files with 15189 additions and 13083 deletions

View file

@ -1,7 +1,8 @@
# Copyright (c)2010-2012 the Boeing Company.
# See the LICENSE file included in this distribution.
"""core
"""
core
Top-level Python package containing CORE components.

File diff suppressed because it is too large Load diff

View file

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

View file

@ -0,0 +1,44 @@
"""
Converts CORE data objects into legacy API messages.
"""
from core.api import coreapi
from core.enumerations import NodeTlvs
from core.misc import log
from core.misc import structutils
logger = log.get_logger(__name__)
def convert_node(node_data):
"""
Callback to handle an node broadcast out from a session.
:param core.data.NodeData node_data: node data to handle
:return: nothing
"""
logger.debug("converting node data to message: %s", node_data)
tlv_data = structutils.pack_values(coreapi.CoreNodeTlv, [
(NodeTlvs.NUMBER, node_data.id),
(NodeTlvs.TYPE, node_data.node_type),
(NodeTlvs.NAME, node_data.name),
(NodeTlvs.IP_ADDRESS, node_data.ip_address),
(NodeTlvs.MAC_ADDRESS, node_data.mac_address),
(NodeTlvs.IP6_ADDRESS, node_data.ip6_address),
(NodeTlvs.MODEL, node_data.model),
(NodeTlvs.EMULATION_ID, node_data.emulation_id),
(NodeTlvs.EMULATION_SERVER, node_data.emulation_server),
(NodeTlvs.SESSION, node_data.session),
(NodeTlvs.X_POSITION, node_data.x_position),
(NodeTlvs.Y_POSITION, node_data.y_position),
(NodeTlvs.CANVAS, node_data.canvas),
(NodeTlvs.NETWORK_ID, node_data.network_id),
(NodeTlvs.SERVICES, node_data.services),
(NodeTlvs.LATITUDE, node_data.latitude),
(NodeTlvs.LONGITUDE, node_data.longitude),
(NodeTlvs.ALTITUDE, node_data.altitude),
(NodeTlvs.ICON, node_data.icon),
(NodeTlvs.OPAQUE, node_data.opaque)
])
return coreapi.CoreNodeMessage.pack(node_data.message_type, tlv_data)

File diff suppressed because it is too large Load diff

View file

@ -5,61 +5,69 @@
#
# authors: core-dev@pf.itd.nrl.navy.mil
#
'''
"""
netgraph.py: Netgraph helper functions; for now these are wrappers around
ngctl commands.
'''
"""
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):
''' 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
other commands may be inserted after the two names.
Return the name and netgraph ID of the new node.
'''
"""
hook1 = hookstr.split()[0]
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,
stdout=subprocess.PIPE,
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()
if status > 0:
raise Exception, "error creating Netgraph node %s (%s): %s" % \
(type, ngcmd, result)
raise Exception("error creating Netgraph node %s (%s): %s" % (type, ngcmd, result))
results = result.split()
ngname = results[1]
ngid = results[5]
if name:
check_call([NGCTL_BIN, "name", "[0x%s]:" % ngid, name])
return (ngname, ngid)
utils.check_call([constants.NGCTL_BIN, "name", "[0x%s]:" % ngid, name])
return ngname, ngid
def destroyngnode(name):
''' Shutdown a Netgraph node having the given name.
'''
check_call([NGCTL_BIN, "shutdown", "%s:" % name])
""" Shutdown a Netgraph node having the given name.
"""
utils.check_call([constants.NGCTL_BIN, "shutdown", "%s:" % name])
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
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):
''' Send a Netgraph message to the node named name.
'''
cmd = [NGCTL_BIN, "msg", "%s:" % name] + msg
check_call(cmd)
""" Send a Netgraph message to the node named name.
"""
cmd = [constants.NGCTL_BIN, "msg", "%s:" % name] + msg
utils.check_call(cmd)
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.
'''
mutecall(["kldload", name])
"""
utils.mutecall(["kldload", name])

View file

@ -6,128 +6,129 @@
# author: core-dev@pf.itd.nrl.navy.mil
#
'''
"""
nodes.py: definition of CoreNode classes and other node classes that inherit
from the CoreNode, implementing specific node types.
'''
"""
from vnode import *
from vnet import *
from core.constants import *
from core.misc.ipaddr import *
import socket
from core import constants
from core.api import coreapi
from core.bsd.netgraph import connectngnodes
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):
apitype = coreapi.CORE_NODE_DEF
apitype = NodeTypes.DEFAULT.value
class PtpNet(NetgraphPipeNet):
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.
'''
"""
pass
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.
'''
"""
tlvdata = ""
if len(self._netif) != 2:
return tlvdata
(if1, if2) = self._netif.items()
if1 = if1[1]
if2 = if2[1]
tlvdata += coreapi.CoreLinkTlv.pack(coreapi.CORE_TLV_LINK_N1NUMBER,
if1.node.objid)
tlvdata += coreapi.CoreLinkTlv.pack(coreapi.CORE_TLV_LINK_N2NUMBER,
if2.node.objid)
delay = if1.getparam('delay')
bw = if1.getparam('bw')
loss = if1.getparam('loss')
duplicate = if1.getparam('duplicate')
jitter = if1.getparam('jitter')
tlvdata += coreapi.CoreLinkTlv.pack(LinkTlvs.N1_NUMBER.value, if1.node.objid)
tlvdata += coreapi.CoreLinkTlv.pack(LinkTlvs.N2_NUMBER.value, if2.node.objid)
delay = if1.getparam("delay")
bw = if1.getparam("bw")
loss = if1.getparam("loss")
duplicate = if1.getparam("duplicate")
jitter = if1.getparam("jitter")
if delay is not None:
tlvdata += coreapi.CoreLinkTlv.pack(coreapi.CORE_TLV_LINK_DELAY,
delay)
tlvdata += coreapi.CoreLinkTlv.pack(LinkTlvs.DELAY.value, delay)
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:
tlvdata += coreapi.CoreLinkTlv.pack(coreapi.CORE_TLV_LINK_PER,
str(loss))
tlvdata += coreapi.CoreLinkTlv.pack(LinkTlvs.PER.value, str(loss))
if duplicate is not None:
tlvdata += coreapi.CoreLinkTlv.pack(coreapi.CORE_TLV_LINK_DUP,
str(duplicate))
tlvdata += coreapi.CoreLinkTlv.pack(LinkTlvs.DUP.value, str(duplicate))
if jitter is not None:
tlvdata += coreapi.CoreLinkTlv.pack(coreapi.CORE_TLV_LINK_JITTER,
jitter)
tlvdata += coreapi.CoreLinkTlv.pack(coreapi.CORE_TLV_LINK_TYPE,
self.linktype)
tlvdata += coreapi.CoreLinkTlv.pack(LinkTlvs.JITTER.value, jitter)
tlvdata += coreapi.CoreLinkTlv.pack(LinkTlvs.TYPE.value, self.linktype)
tlvdata += coreapi.CoreLinkTlv.pack(coreapi.CORE_TLV_LINK_IF1NUM, \
if1.node.getifindex(if1))
tlvdata += coreapi.CoreLinkTlv.pack(LinkTlvs.INTERFACE1_NUMBER.value, if1.node.getifindex(if1))
if if1.hwaddr:
tlvdata += coreapi.CoreLinkTlv.pack(coreapi.CORE_TLV_LINK_IF1MAC,
if1.hwaddr)
tlvdata += coreapi.CoreLinkTlv.pack(LinkTlvs.INTERFACE1_MAC.value, if1.hwaddr)
for addr in if1.addrlist:
(ip, sep, mask) = addr.partition('/')
(ip, sep, mask) = addr.partition("/")
mask = int(mask)
if isIPv4Address(ip):
family = AF_INET
tlvtypeip = coreapi.CORE_TLV_LINK_IF1IP4
tlvtypemask = coreapi.CORE_TLV_LINK_IF1IP4MASK
if ipaddress.is_ipv4_address(ip):
family = socket.AF_INET
tlvtypeip = LinkTlvs.INTERFACE1_IP4.value
tlvtypemask = LinkTlvs.INTERFACE1_IP4_MASK
else:
family = AF_INET6
tlvtypeip = coreapi.CORE_TLV_LINK_IF1IP6
tlvtypemask = coreapi.CORE_TLV_LINK_IF1IP6MASK
family = socket.AF_INET6
tlvtypeip = LinkTlvs.INTERFACE1_IP6.value
tlvtypemask = LinkTlvs.INTERFACE1_IP6_MASK.value
ipl = socket.inet_pton(family, ip)
tlvdata += coreapi.CoreLinkTlv.pack(tlvtypeip,
IPAddr(af=family, addr=ipl))
tlvdata += coreapi.CoreLinkTlv.pack(tlvtypeip, ipaddress.IpAddress(af=family, address=ipl))
tlvdata += coreapi.CoreLinkTlv.pack(tlvtypemask, mask)
tlvdata += coreapi.CoreLinkTlv.pack(coreapi.CORE_TLV_LINK_IF2NUM, \
if2.node.getifindex(if2))
tlvdata += coreapi.CoreLinkTlv.pack(LinkTlvs.INTERFACE2_NUMBER.value, if2.node.getifindex(if2))
if if2.hwaddr:
tlvdata += coreapi.CoreLinkTlv.pack(coreapi.CORE_TLV_LINK_IF2MAC,
if2.hwaddr)
tlvdata += coreapi.CoreLinkTlv.pack(LinkTlvs.INTERFACE2_MAC.value, if2.hwaddr)
for addr in if2.addrlist:
(ip, sep, mask) = addr.partition('/')
(ip, sep, mask) = addr.partition("/")
mask = int(mask)
if isIPv4Address(ip):
family = AF_INET
tlvtypeip = coreapi.CORE_TLV_LINK_IF2IP4
tlvtypemask = coreapi.CORE_TLV_LINK_IF2IP4MASK
if ipaddress.is_ipv4_address(ip):
family = socket.AF_INET
tlvtypeip = LinkTlvs.INTERFACE2_IP4.value
tlvtypemask = LinkTlvs.INTERFACE2_IP4_MASK
else:
family = AF_INET6
tlvtypeip = coreapi.CORE_TLV_LINK_IF2IP6
tlvtypemask = coreapi.CORE_TLV_LINK_IF2IP6MASK
family = socket.AF_INET6
tlvtypeip = LinkTlvs.INTERFACE2_IP6.value
tlvtypemask = LinkTlvs.INTERFACE2_IP6_MASK.value
ipl = socket.inet_pton(family, ip)
tlvdata += coreapi.CoreLinkTlv.pack(tlvtypeip,
IPAddr(af=family, addr=ipl))
tlvdata += coreapi.CoreLinkTlv.pack(tlvtypeip, ipaddress.IpAddress(af=family, address=ipl))
tlvdata += coreapi.CoreLinkTlv.pack(tlvtypemask, mask)
msg = coreapi.CoreLinkMessage.pack(flags, tlvdata)
return [msg, ]
class SwitchNode(NetgraphNet):
ngtype = "bridge"
nghooks = "link0 link0\nmsg .link0 setpersistent"
apitype = coreapi.CORE_NODE_SWITCH
apitype = NodeTypes.SWITCH.value
policy = "ACCEPT"
class HubNode(NetgraphNet):
ngtype = "hub"
nghooks = "link0 link0\nmsg .link0 setpersistent"
apitype = coreapi.CORE_NODE_HUB
apitype = NodeTypes.HUB.value
policy = "ACCEPT"
class WlanNode(NetgraphNet):
ngtype = "wlan"
nghooks = "anchor anchor"
apitype = coreapi.CORE_NODE_WLAN
linktype = coreapi.CORE_LINK_WIRELESS
apitype = NodeTypes.WIRELESS_LAN.value
linktype = LinkTypes.WIRELESS.value
policy = "DROP"
def __init__(self, session, objid=None, name=None, verbose=False,
@ -144,15 +145,15 @@ class WlanNode(NetgraphNet):
netif.poshook = self.model._positioncallback
if netif.node is None:
return
(x,y,z) = netif.node.position.get()
x, y, z = netif.node.position.get()
netif.poshook(netif, x, y, z)
def setmodel(self, model, config):
''' Mobility and wireless model.
'''
if (self.verbose):
""" Mobility and wireless model.
"""
if self.verbose:
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,
verbose=self.verbose, values=config)
if self.model._positioncallback:
@ -162,13 +163,13 @@ class WlanNode(NetgraphNet):
(x, y, z) = netif.node.position.get()
netif.poshook(netif, x, y, z)
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,
verbose=self.verbose, values=config)
class RJ45Node(NetgraphPipeNet):
apitype = coreapi.CORE_NODE_RJ45
apitype = NodeTypes.RJ45.value
policy = "ACCEPT"
def __init__(self, session, objid, name, verbose, start=True):
@ -186,7 +187,7 @@ class RJ45Node(NetgraphPipeNet):
p = "promisc"
if not promisc:
p = "-" + p
check_call([IFCONFIG_BIN, self.name, "up", p])
utils.check_call([constants.IFCONFIG_BIN, self.name, "up", p])
def attach(self, netif):
if len(self._netif) > 0:
@ -195,9 +196,9 @@ class RJ45Node(NetgraphPipeNet):
NetgraphPipeNet.attach(self, netif)
connectngnodes(self.ngname, self.name, self.gethook(), "lower")
class TunnelNode(NetgraphNet):
ngtype = "pipe"
nghooks = "upper lower"
apitype = coreapi.CORE_NODE_TUNNEL
apitype = NodeTypes.TUNNEL.value
policy = "ACCEPT"

View file

@ -5,18 +5,18 @@
#
# authors: core-dev@pf.itd.nrl.navy.mil
#
'''
"""
vnet.py: NetgraphNet and NetgraphPipeNet classes that implement virtual networks
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):
ngtype = None
@ -40,8 +40,7 @@ class NetgraphNet(PyCoreNet):
self.startup()
def startup(self):
tmp, self.ngid = createngnode(type=self.ngtype, hookstr=self.nghooks,
name=self.ngname)
tmp, self.ngid = createngnode(type=self.ngtype, hookstr=self.nghooks, name=self.ngname)
self.up = True
def shutdown(self):
@ -62,13 +61,12 @@ class NetgraphNet(PyCoreNet):
destroyngnode(self.ngname)
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.
(Note that the PtpNet subclass overrides this method.)
'''
"""
if self.up:
pipe = self.session.addobj(cls = NetgraphPipeNet,
verbose = self.verbose, start = True)
pipe = self.session.addobj(cls=NetgraphPipeNet, verbose=self.verbose, start=True)
pipe.attach(netif)
hook = "link%d" % len(self._netif)
pipe.attachnet(self, hook)
@ -82,14 +80,16 @@ class NetgraphNet(PyCoreNet):
def linked(self, netif1, netif2):
# check if the network interfaces are attached to this network
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:
raise ValueError, "inconsistency for netif %s" % netif2.name
raise ValueError("inconsistency for netif %s" % netif2.name)
try:
linked = self._linked[netif1][netif2]
except KeyError:
linked = False
self._linked[netif1][netif2] = linked
return linked
def unlink(self, netif1, netif2):
@ -109,21 +109,20 @@ class NetgraphNet(PyCoreNet):
self._linked[netif1][netif2] = True
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.
'''
"""
raise NotImplementedError
def linkconfig(self, netif, bw=None, delay=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:
self.warn("linkconfig for %s but interface %s has no pipe" % \
(self.name, netif.name))
self.warn("linkconfig for %s but interface %s has no pipe" % (self.name, netif.name))
return
return netif.pipe.linkconfig(netif, bw, delay, loss, duplicate, jitter,
netif2)
return netif.pipe.linkconfig(netif, bw, delay, loss, duplicate, jitter, netif2)
class NetgraphPipeNet(NetgraphNet):
ngtype = "pipe"
@ -137,10 +136,10 @@ class NetgraphPipeNet(NetgraphNet):
ngmessage(self.ngname, ["setcfg", "{", "header_offset=14", "}"])
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
connected to the "lower" hook.
'''
"""
if len(self._netif) > 1:
raise ValueError, \
"Netgraph pipes support at most 2 network interfaces"
@ -156,15 +155,15 @@ class NetgraphPipeNet(NetgraphNet):
self._linked[netif] = {}
def attachnet(self, net, hook):
''' Attach another NetgraphNet to this pipe node.
'''
""" Attach another NetgraphNet to this pipe node.
"""
localhook = self.gethook()
connectngnodes(self.ngname, net.ngname, localhook, hook)
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.
'''
"""
hooks = self.nghooks.split()
if len(self._netif) == 0:
return hooks[0]
@ -173,13 +172,13 @@ class NetgraphPipeNet(NetgraphNet):
def linkconfig(self, netif, bw=None, delay=None,
loss=None, duplicate=None, jitter=None, netif2=None):
''' Set link effects by sending a Netgraph setcfg message to the pipe.
'''
netif.setparam('bw', bw)
netif.setparam('delay', delay)
netif.setparam('loss', loss)
netif.setparam('duplicate', duplicate)
netif.setparam('jitter', jitter)
""" Set link effects by sending a Netgraph setcfg message to the pipe.
"""
netif.setparam("bw", bw)
netif.setparam("delay", delay)
netif.setparam("loss", loss)
netif.setparam("duplicate", duplicate)
netif.setparam("jitter", jitter)
if not self.up:
return
params = []
@ -213,4 +212,3 @@ class NetgraphPipeNet(NetgraphNet):
setcfg += ["downstream={", ] + downstream + ["}", ]
setcfg += ["}", ]
ngmessage(self.ngname, setcfg)

View file

@ -5,20 +5,25 @@
#
# authors: core-dev@pf.itd.nrl.navy.mil
#
'''
"""
vnode.py: SimpleJailNode and JailNode classes that implement the FreeBSD
jail-based virtual node.
'''
"""
import os, signal, sys, subprocess, threading, string
import random, time
from core.misc.utils import *
from core.constants import *
from core.coreobj import PyCoreObj, PyCoreNode, PyCoreNetIf, Position
from core.emane.nodes import EmaneNode
from core.bsd.netgraph import *
import os
import subprocess
import threading
from core import constants
from core.bsd.netgraph import createngnode
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):
def __init__(self, node, name, localname, mtu=1500, net=None,
@ -45,7 +50,7 @@ class VEth(PyCoreNetIf):
name=self.localname)
self.name = ngname
self.ngid = ngid
check_call([IFCONFIG_BIN, ngname, "up"])
utils.check_call([constants.IFCONFIG_BIN, ngname, "up"])
self.up = True
def shutdown(self):
@ -74,12 +79,15 @@ class VEth(PyCoreNetIf):
def sethwaddr(self, addr):
self.hwaddr = addr
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,
start=True):
raise NotImplementedError
class SimpleJailNode(PyCoreNode):
def __init__(self, session, objid=None, name=None, nodedir=None,
verbose=False):
@ -94,17 +102,17 @@ class SimpleJailNode(PyCoreNode):
def startup(self):
if self.up:
raise Exception, "already up"
vimg = [VIMAGE_BIN, "-c", self.name]
vimg = [constants.VIMAGE_BIN, "-c", self.name]
try:
os.spawnlp(os.P_WAIT, VIMAGE_BIN, *vimg)
os.spawnlp(os.P_WAIT, constants.VIMAGE_BIN, *vimg)
except OSError:
raise Exception, ("vimage command not found while running: %s" % \
vimg)
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.cmd(["hostname", self.name])
self.cmd([SYSCTL_BIN, "vfs.morphing_symlinks=1"])
self.cmd([constants.SYSCTL_BIN, "vfs.morphing_symlinks=1"])
self.up = True
def shutdown(self):
@ -114,12 +122,11 @@ class SimpleJailNode(PyCoreNode):
netif.shutdown()
self._netif.clear()
del self.session
vimg = [VIMAGE_BIN, "-d", self.name]
vimg = [constants.VIMAGE_BIN, "-d", self.name]
try:
os.spawnlp(os.P_WAIT, VIMAGE_BIN, *vimg)
os.spawnlp(os.P_WAIT, constants.VIMAGE_BIN, *vimg)
except OSError:
raise Exception, ("vimage command not found while running: %s" % \
vimg)
raise Exception("vimage command not found while running: %s" % vimg)
self.up = False
def cmd(self, args, wait=True):
@ -127,7 +134,7 @@ class SimpleJailNode(PyCoreNode):
mode = os.P_WAIT
else:
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:
tmp = None
if tmp:
@ -145,10 +152,10 @@ class SimpleJailNode(PyCoreNode):
status = cmdid.wait()
else:
status = 0
return (status, result)
return status, result
def popen(self, args):
cmd = [VIMAGE_BIN, self.name]
cmd = [constants.VIMAGE_BIN, self.name]
cmd.extend(args)
tmp = subprocess.Popen(cmd, stdin=subprocess.PIPE,
stdout=subprocess.PIPE,
@ -156,17 +163,17 @@ class SimpleJailNode(PyCoreNode):
return tmp, tmp.stdin, tmp.stdout, tmp.stderr
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"):
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"):
''' 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.
'''
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"):
return self.cmd([sh, "-c", cmdstr])
@ -180,7 +187,7 @@ class SimpleJailNode(PyCoreNode):
self.addsymlink(path=target, file=None)
def umount(self, target):
self.info("unmounting '%s'" % target)
self.info("unmounting %s" % target)
def newveth(self, ifindex=None, ifname=None, net=None):
self.lock.acquire()
@ -197,9 +204,11 @@ class SimpleJailNode(PyCoreNode):
mtu=1500, net=net, start=self.up)
if self.up:
# 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"
self.cmd([IFCONFIG_BIN, veth.name, "name", ifname])
self.cmd([constants.IFCONFIG_BIN, veth.name, "name", ifname])
veth.name = ifname
try:
self.addnetif(veth, ifindex)
@ -214,16 +223,16 @@ class SimpleJailNode(PyCoreNode):
def sethwaddr(self, ifindex, addr):
self._netif[ifindex].sethwaddr(addr)
if self.up:
self.cmd([IFCONFIG_BIN, self.ifname(ifindex), "link",
self.cmd([constants.IFCONFIG_BIN, self.ifname(ifindex), "link",
str(addr)])
def addaddr(self, ifindex, addr):
if self.up:
if ':' in addr:
if ":" in addr:
family = "inet6"
else:
family = "inet"
self.cmd([IFCONFIG_BIN, self.ifname(ifindex), family, "alias",
self.cmd([constants.IFCONFIG_BIN, self.ifname(ifindex), family, "alias",
str(addr)])
self._netif[ifindex].addaddr(addr)
@ -233,14 +242,15 @@ class SimpleJailNode(PyCoreNode):
except ValueError:
self.warn("trying to delete unknown address: %s" % addr)
if self.up:
if ':' in addr:
if ":" in addr:
family = "inet6"
else:
family = "inet"
self.cmd([IFCONFIG_BIN, self.ifname(ifindex), family, "-alias",
self.cmd([constants.IFCONFIG_BIN, self.ifname(ifindex), family, "-alias",
str(addr)])
valid_deladdrtype = ("inet", "inet6", "inet6link")
def delalladdr(self, ifindex, addrtypes=valid_deladdrtype):
addr = self.getaddr(self.ifname(ifindex), rescan=True)
for t in addrtypes:
@ -254,19 +264,18 @@ class SimpleJailNode(PyCoreNode):
def ifup(self, ifindex):
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,
ifindex=None, ifname=None):
self.lock.acquire()
try:
ifindex = self.newveth(ifindex = ifindex, ifname = ifname,
net = net)
ifindex = self.newveth(ifindex=ifindex, ifname=ifname, net=net)
if net is not None:
self.attachnet(ifindex, net)
if hwaddr:
self.sethwaddr(ifindex, hwaddr)
for addr in maketuple(addrlist):
for addr in utils.maketuple(addrlist):
self.addaddr(ifindex, addr)
self.ifup(ifindex)
return ifindex
@ -280,8 +289,7 @@ class SimpleJailNode(PyCoreNode):
self._netif[ifindex].detachnet()
def addfile(self, srcname, filename):
shcmd = "mkdir -p $(dirname '%s') && mv '%s' '%s' && sync" % \
(filename, srcname, filename)
shcmd = 'mkdir -p $(dirname "%s") && mv "%s" "%s" && sync' % (filename, srcname, filename)
self.shcmd(shcmd)
def getaddr(self, ifname, rescan=False):
@ -289,9 +297,9 @@ class SimpleJailNode(PyCoreNode):
# return self.vnodeclient.getaddr(ifname = ifname, rescan = rescan)
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
'''
"""
dirname = path
if dirname and dirname[0] == "/":
dirname = dirname[1:]
@ -316,8 +324,8 @@ class SimpleJailNode(PyCoreNode):
self.info("creating symlink %s -> %s" % (pathname, sym))
os.symlink(sym, pathname)
class JailNode(SimpleJailNode):
class JailNode(SimpleJailNode):
def __init__(self, session, objid=None, name=None,
nodedir=None, bootsh="boot.sh", verbose=False,
start=True):
@ -362,7 +370,7 @@ class JailNode(SimpleJailNode):
if path[0] != "/":
raise ValueError, "path not fully qualified: " + path
hostpath = os.path.join(self.nodedir,
os.path.normpath(path).strip('/').replace('/', '.'))
os.path.normpath(path).strip("/").replace("/", "."))
try:
os.mkdir(hostpath)
except OSError:
@ -390,5 +398,4 @@ class JailNode(SimpleJailNode):
f.write(contents)
os.chmod(f.name, mode)
f.close()
self.info("created nodefile: '%s'; mode: 0%o" % (f.name, mode))
self.info("created nodefile: %s; mode: 0%o" % (f.name, mode))

View file

@ -1,35 +1,42 @@
#
# CORE
# 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
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):
''' 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
for itself or for the Configurables that it manages.
'''
"""
# name corresponds to configuration object field
_name = ""
# type corresponds with register message types
_type = None
name = ""
def __init__(self, session=None):
self.session = session
self.session.addconfobj(self._name, self._type, self.configure)
# Configurable key=values, indexed by node number
# type corresponds with register message types
config_type = None
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 = {}
def configure(self, session, msg):
''' Handle configure messages. The configuration message sent to a
def configure(self, session, config_data):
"""
Handle configure messages. The configuration message sent to a
ConfigurableManager usually is used to:
1. Request a list of Configurables (request flag)
2. Reset manager and clear configs (reset flag)
@ -37,45 +44,65 @@ class ConfigurableManager(object):
Configurables
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):
''' 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
def configure_reset(self, msg):
''' By default, resets this manager to clear configs.
'''
def configure_reset(self, config_data):
"""
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()
def configure_values(self, msg, values):
''' Values have been sent to this manager.
'''
def configure_values(self, config_data):
"""
Values have been sent to this manager.
:param ConfigData config_data: configuration data for carrying out a configuration
:return: nothing
"""
return None
def configure_values_keyvalues(self, msg, values, target, keys):
''' Helper that can be used for configure_values for parsing in
def configure_values_keyvalues(self, config_data, target, keys):
"""
Helper that can be used for configure_values for parsing in
'key=value' strings from a values field. The key name must be
in the keys list, and target.key=value is set.
'''
:param core.conf.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: None
"""
values = config_data.data_values
if values is None:
return None
kvs = values.split('|')
for kv in kvs:
try:
# key=value
(key, value) = kv.split('=', 1)
key, value = kv.split('=', 1)
if value is not None and not value.strip():
value = None
except ValueError:
@ -83,25 +110,28 @@ class ConfigurableManager(object):
key = keys[kvs.index(kv)]
value = kv
if key not in keys:
raise ValueError, "invalid key: %s" % key
raise ValueError("invalid key: %s" % key)
if value is not None:
setattr(target, key, value)
return None
def reset(self):
return None
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
node for which no object exists yet
'''
"""
logger.info("setting config for node(%s): %s - %s", nodenum, conftype, values)
conflist = []
if nodenum in self.configs:
oldlist = self.configs[nodenum]
found = False
for (t, v) in oldlist:
if (t == conftype):
for t, v in oldlist:
if t == conftype:
# replace existing config
found = True
conflist.append((conftype, values))
@ -114,34 +144,39 @@ class ConfigurableManager(object):
self.configs[nodenum] = conflist
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
'''
"""
logger.info("getting config for node(%s): %s - default(%s)",
nodenum, conftype, defaultvalues)
if nodenum in self.configs:
# return configured values
conflist = self.configs[nodenum]
for (t, v) in conflist:
if (conftype is None) or (t == conftype):
return (t, v)
for t, v in conflist:
if conftype is None or t == conftype:
return t, v
# return default values provided (may be None)
return (conftype, defaultvalues)
return conftype, defaultvalues
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.
'''
"""
r = []
for nodenum in self.configs:
for (t, v) in self.configs[nodenum]:
for t, v in self.configs[nodenum]:
if use_clsmap:
t = self._modelclsmap[t]
r.append((nodenum, t, v))
return r
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
'''
"""
if nodenum is None:
self.configs = {}
return
@ -149,10 +184,11 @@ class ConfigurableManager(object):
self.configs.pop(nodenum)
def setconfig_keyvalues(self, nodenum, conftype, keyvalues):
''' keyvalues list of tuples
'''
"""
keyvalues list of tuples
"""
if conftype not in self._modelclsmap:
self.warn("Unknown model type '%s'" % (conftype))
logger.warn("Unknown model type '%s'" % conftype)
return
model = self._modelclsmap[conftype]
keys = model.getnames()
@ -160,7 +196,7 @@ class ConfigurableManager(object):
values = list(model.getdefaultvalues())
for key, value in keyvalues:
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))
continue
i = keys.index(key)
@ -168,11 +204,12 @@ class ConfigurableManager(object):
self.setconfig(nodenum, conftype, values)
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.
This assumes self.configs contains an iterable of (model-names, values)
and a self._modelclsmapdict exists.
'''
"""
r = []
if n.objid in self.configs:
v = self.configs[n.objid]
@ -183,28 +220,29 @@ class ConfigurableManager(object):
return r
def info(self, msg):
self.session.info(msg)
def warn(self, msg):
self.session.warn(msg)
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
to build dynamic dialogs depending on what is being configured.
'''
_name = ""
"""
name = ""
# Configuration items:
# ('name', 'type', 'default', 'possible-value-list', 'caption')
_confmatrix = []
_confgroups = None
_bitmap = None
config_matrix = []
config_groups = 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:
:return:
"""
self.session = session
self.objid = objid
self.object_id = object_id
def reset(self):
pass
@ -214,61 +252,59 @@ class Configurable(object):
@classmethod
def getdefaultvalues(cls):
return tuple( map(lambda x: x[2], cls._confmatrix) )
return tuple(map(lambda x: x[2], cls.config_matrix))
@classmethod
def getnames(cls):
return tuple( map( lambda x: x[0], cls._confmatrix) )
return tuple(map(lambda x: x[0], cls.config_matrix))
@classmethod
def configure(cls, mgr, msg):
''' Handle configuration messages for this object.
'''
def configure(cls, manager, config_data):
"""
Handle configuration messages for this object.
"""
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)
node_id = config_data.node
object_name = config_data.object
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 ifacenum is not None:
nodenum = nodenum*1000 + ifacenum
if interface_id is not None:
node_id = node_id * 1000 + interface_id
if mgr.verbose:
mgr.info("received configure message for %s nodenum:%s" % (cls._name, str(nodenum)))
if conftype == coreapi.CONF_TYPE_FLAGS_REQUEST:
if mgr.verbose:
mgr.info("replying to configure request for %s model" %
cls._name)
logger.info("received configure message for %s nodenum:%s", cls.name, str(node_id))
if config_type == ConfigFlags.REQUEST.value:
logger.info("replying to configure request for %s model", cls.name)
# 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
# reply with the defaults for this model
if objname == "all":
if object_name == "all":
defaults = None
typeflags = coreapi.CONF_TYPE_FLAGS_UPDATE
typeflags = ConfigFlags.UPDATE.value
else:
defaults = cls.getdefaultvalues()
typeflags = coreapi.CONF_TYPE_FLAGS_NONE
values = mgr.getconfig(nodenum, cls._name, defaults)[1]
typeflags = ConfigFlags.coreapi.CONF_TYPE_FLAGS_NONE
values = manager.getconfig(node_id, cls.name, defaults)[1]
if values is None:
# node has no active config for this model (don't send defaults)
return None
# reply with config options
reply = cls.toconfmsg(0, nodenum, typeflags, values)
elif conftype == coreapi.CONF_TYPE_FLAGS_RESET:
if objname == "all":
mgr.clearconfig(nodenum)
reply = cls.config_data(0, node_id, typeflags, values)
elif config_type == ConfigFlags.RESET.value:
if object_name == "all":
manager.clearconfig(node_id)
# elif conftype == coreapi.CONF_TYPE_FLAGS_UPDATE:
else:
# store the configuration values for later use, when the node
# object has been created
if objname is None:
mgr.info("no configuration object for node %s" % nodenum)
if object_name is None:
logger.info("no configuration object for node %s", node_id)
return None
values_str = msg.gettlv(coreapi.CORE_TLV_CONF_VALUES)
defaults = cls.getdefaultvalues()
if values_str is None:
# use default or preconfigured values
values = mgr.getconfig(nodenum, cls._name, defaults)[1]
values = manager.getconfig(node_id, cls.name, defaults)[1]
else:
# use new values supplied from the conf message
values = values_str.split('|')
@ -282,54 +318,44 @@ class Configurable(object):
try:
new_values[keys.index(key)] = value
except ValueError:
mgr.info("warning: ignoring invalid key '%s'" % key)
logger.info("warning: ignoring invalid key '%s'" % key)
values = new_values
mgr.setconfig(nodenum, objname, values)
manager.setconfig(node_id, object_name, values)
return reply
@classmethod
def toconfmsg(cls, flags, nodenum, typeflags, values):
''' Convert this class to a Config API message. Some TLVs are defined
def config_data(cls, flags, node_id, type_flags, values):
"""
Convert this class to a Config API message. Some TLVs are defined
by the class, but node number, conf type flags, and values must
be passed in.
'''
"""
keys = cls.getnames()
keyvalues = map(lambda a, b: "%s=%s" % (a, b), keys, values)
values_str = string.join(keyvalues, '|')
tlvdata = ""
if nodenum is not None:
tlvdata += coreapi.CoreConfTlv.pack(coreapi.CORE_TLV_CONF_NODE,
nodenum)
tlvdata += coreapi.CoreConfTlv.pack(coreapi.CORE_TLV_CONF_OBJ,
cls._name)
tlvdata += coreapi.CoreConfTlv.pack(coreapi.CORE_TLV_CONF_TYPE,
typeflags)
datatypes = tuple( map(lambda x: x[1], cls._confmatrix) )
tlvdata += coreapi.CoreConfTlv.pack(coreapi.CORE_TLV_CONF_DATA_TYPES,
datatypes)
tlvdata += coreapi.CoreConfTlv.pack(coreapi.CORE_TLV_CONF_VALUES,
values_str)
captions = reduce( lambda a,b: a + '|' + b, \
map(lambda x: x[4], cls._confmatrix))
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
datatypes = tuple(map(lambda x: x[1], cls.config_matrix))
captions = reduce(lambda a, b: a + '|' + b, map(lambda x: x[4], cls.config_matrix))
possible_valuess = reduce(lambda a, b: a + '|' + b, map(lambda x: x[3], cls.config_matrix))
return ConfigData(
message_type=flags,
node=node_id,
object=cls.name,
type=type_flags,
data_types=datatypes,
data_values=values_str,
captions=captions,
possible_values=possible_valuess,
bitmap=cls.bitmap,
groups=cls.config_groups
)
@staticmethod
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.
"""
if value == "1" or value == "true" or value == "on":
return "on"
else:
@ -346,20 +372,22 @@ class Configurable(object):
@classmethod
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
"""
i = cls.getnames().index(name)
if cls._confmatrix[i][1] == coreapi.CONF_DATA_TYPE_BOOL and \
values[i] != "":
if cls.config_matrix[i][1] == ConfigDataTypes.BOOL.value and values[i] != "":
return cls.booltooffon(values[i])
else:
return values[i]
@staticmethod
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".
'''
"""
if len(values) == 0:
return False
for v in values:
@ -368,13 +396,12 @@ class Configurable(object):
return True
def getkeyvaluelist(self):
''' Helper to return a list of (key, value) tuples. Keys come from
"""
Helper to return a list of (key, value) tuples. Keys come from
self._confmatrix and values are instance attributes.
'''
"""
r = []
for k in self.getnames():
if hasattr(self, k):
r.append((k, getattr(self, k)))
return r

1692
daemon/core/corehandlers.py Normal file

File diff suppressed because it is too large Load diff

View file

@ -1,23 +1,27 @@
#
# CORE
# Copyright (c)2010-2013 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>
#
'''
"""
coreobj.py: defines the basic objects for emulation: the PyCoreObj base class,
along with PyCoreNode, PyCoreNet, and PyCoreNetIf
'''
import sys, threading, os, shutil
"""
import os
import shutil
import socket
import threading
from socket import AF_INET
from socket import AF_INET6
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):
''' Helper class for Cartesian coordinate position
'''
"""
Helper class for Cartesian coordinate position
"""
def __init__(self, x=None, y=None, z=None):
self.x = None
self.y = None
@ -25,8 +29,9 @@ class Position(object):
self.set(x, y, z)
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.
"""
if self.x == x and self.y == y and self.z == z:
return False
self.x = x
@ -35,20 +40,22 @@ class Position(object):
return True
def get(self):
''' Fetch the (x,y,z) position tuple.
'''
return (self.x, self.y, self.z)
"""
Fetch the (x,y,z) position tuple.
"""
return self.x, self.y, self.z
class PyCoreObj(object):
''' Base class for pycore objects (nodes and nets)
'''
"""
Base class for pycore objects (nodes and nets)
"""
apitype = None
def __init__(self, session, objid = None, name = None, verbose = False,
start = True):
def __init__(self, session, objid=None, name=None, start=True):
self.session = session
if objid is None:
objid = session.getobjid()
objid = session.get_object_id()
self.objid = objid
if name is None:
name = "o%s" % self.objid
@ -59,43 +66,48 @@ class PyCoreObj(object):
self.canvas = None
self.icon = None
self.opaque = None
self.verbose = verbose
self.position = Position()
def startup(self):
''' Each object implements its own startup method.
'''
"""
Each object implements its own startup method.
"""
raise NotImplementedError
def shutdown(self):
''' Each object implements its own shutdown method.
'''
"""
Each object implements its own shutdown method.
"""
raise NotImplementedError
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.
"""
return self.position.set(x=x, y=y, z=z)
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 self.position.get()
def ifname(self, ifindex):
return self.netif(ifindex).name
def netifs(self, sort=False):
''' Iterate over attached network interfaces.
'''
"""
Iterate over attached network interfaces.
"""
if sort:
return map(lambda k: self._netif[k], sorted(self._netif.keys()))
else:
return self._netif.itervalues()
def numnetif(self):
''' Return the attached interface count.
'''
"""
Return the attached interface count.
"""
return len(self._netif)
def getifindex(self, netif):
@ -111,95 +123,71 @@ class PyCoreObj(object):
self.ifindex += 1
return ifindex
def tonodemsg(self, flags):
''' Build a CORE API Node Message for this object. Both nodes and
networks can be represented by a Node Message.
'''
def data(self, message_type):
"""
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:
return None
tlvdata = ""
(x, y, z) = self.getposition()
tlvdata += coreapi.CoreNodeTlv.pack(coreapi.CORE_TLV_NODE_NUMBER,
self.objid)
tlvdata += coreapi.CoreNodeTlv.pack(coreapi.CORE_TLV_NODE_TYPE,
self.apitype)
tlvdata += coreapi.CoreNodeTlv.pack(coreapi.CORE_TLV_NODE_NAME,
self.name)
if hasattr(self, "type") and self.type is not None:
tlvdata += coreapi.CoreNodeTlv.pack(coreapi.CORE_TLV_NODE_MODEL,
self.type)
if hasattr(self, "server") and self.server is not None:
tlvdata += coreapi.CoreNodeTlv.pack(coreapi.CORE_TLV_NODE_EMUSRV,
self.server)
x, y, z = self.getposition()
model = None
if hasattr(self, "type"):
model = self.type
emulation_server = None
if hasattr(self, "server"):
emulation_server = self.server
services = None
if hasattr(self, "services") and len(self.services) != 0:
nodeservices = []
for s in self.services:
nodeservices.append(s._name)
tlvdata += coreapi.CoreNodeTlv.pack(coreapi.CORE_TLV_NODE_SERVICES,
"|".join(nodeservices))
services = "|".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:
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
return node_data
def tolinkmsgs(self, flags):
''' Build CORE API Link Messages for this object. There is no default
def all_link_data(self, flags):
"""
Build CORE API Link Messages for this object. There is no default
method for PyCoreObjs as PyCoreNodes do not implement this but
PyCoreNets do.
'''
"""
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):
''' Base class for nodes
'''
def __init__(self, session, objid = None, name = None, verbose = False,
start = True):
''' Initialization for node objects.
'''
PyCoreObj.__init__(self, session, objid, name, verbose=verbose,
start=start)
"""
Base class for nodes
"""
def __init__(self, session, objid=None, name=None, start=True):
"""
Initialization for node objects.
"""
PyCoreObj.__init__(self, session, objid, name, start=start)
self.services = []
if not hasattr(self, "type"):
self.type = None
@ -215,7 +203,7 @@ class PyCoreNode(PyCoreObj):
def makenodedir(self):
if self.nodedir is None:
self.nodedir = \
os.path.join(self.session.sessiondir, self.name + ".conf")
os.path.join(self.session.session_dir, self.name + ".conf")
os.makedirs(self.nodedir)
self.tmpnodedir = True
else:
@ -266,10 +254,11 @@ class PyCoreNode(PyCoreObj):
netif.setposition(x, y, z)
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
consisting of (network, interface1, interface2).
'''
"""
r = []
for netif1 in self.netifs():
if not want_ctrl and hasattr(netif1, 'control'):
@ -280,17 +269,17 @@ class PyCoreNode(PyCoreObj):
return r
class PyCoreNet(PyCoreObj):
''' Base class for networks
'''
linktype = coreapi.CORE_LINK_WIRED
"""
Base class for networks
"""
linktype = LinkTypes.WIRED.value
def __init__(self, session, objid, name, verbose = False, start = True):
''' Initialization for network objects.
'''
PyCoreObj.__init__(self, session, objid, name, verbose=verbose,
start=start)
def __init__(self, session, objid, name, start=True):
"""
Initialization for network objects.
"""
PyCoreObj.__init__(self, session, objid, name, start=start)
self._linked = {}
self._linked_lock = threading.Lock()
@ -308,37 +297,37 @@ class PyCoreNet(PyCoreObj):
del self._linked[netif]
def netifparamstolink(self, netif):
''' Helper for tolinkmsgs() to build TLVs having link parameters
"""
Helper for tolinkmsgs() to build TLVs having link parameters
from interface parameters.
'''
tlvdata = ""
"""
delay = netif.getparam('delay')
bw = netif.getparam('bw')
loss = netif.getparam('loss')
duplicate = netif.getparam('duplicate')
jitter = netif.getparam('jitter')
tlvdata = ""
if delay is not None:
tlvdata += coreapi.CoreLinkTlv.pack(coreapi.CORE_TLV_LINK_DELAY,
delay)
tlvdata += coreapi.CoreLinkTlv.pack(LinkTlvs.DELAY.value, delay)
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:
tlvdata += coreapi.CoreLinkTlv.pack(coreapi.CORE_TLV_LINK_PER,
str(loss))
tlvdata += coreapi.CoreLinkTlv.pack(LinkTlvs.PER.value, str(loss))
if duplicate is not None:
tlvdata += coreapi.CoreLinkTlv.pack(coreapi.CORE_TLV_LINK_DUP,
str(duplicate))
tlvdata += coreapi.CoreLinkTlv.pack(LinkTlvs.DUP.value, str(duplicate))
if jitter is not None:
tlvdata += coreapi.CoreLinkTlv.pack(coreapi.CORE_TLV_LINK_JITTER,
jitter)
tlvdata += coreapi.CoreLinkTlv.pack(LinkTlvs.JITTER.value, jitter)
return tlvdata
def tolinkmsgs(self, flags):
''' Build CORE API Link Messages for this network. Each link message
def all_link_data(self, flags):
"""
Build CORE API Link Messages for this network. Each link message
describes a link between this network and a node.
'''
msgs = []
"""
all_links = []
# build a link message from this network node to each node having a
# connected interface
for netif in self.netifs(sort=True):
@ -359,60 +348,76 @@ class PyCoreNet(PyCoreObj):
if netif.getparams() != upstream_params:
uni = True
tlvdata = ""
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)
unidirectional = 0
if uni:
tlvdata += coreapi.CoreLinkTlv.pack(coreapi.CORE_TLV_LINK_UNI,
1)
tlvdata += coreapi.CoreLinkTlv.pack(coreapi.CORE_TLV_LINK_IF2NUM,
otherobj.getifindex(netif))
if netif.hwaddr:
tlvdata += \
coreapi.CoreLinkTlv.pack(coreapi.CORE_TLV_LINK_IF2MAC,
netif.hwaddr)
for addr in netif.addrlist:
(ip, sep, mask) = addr.partition('/')
unidirectional = 1
interface2_ip4 = None
interface2_ip4_mask = None
interface2_ip6 = None
interface2_ip6_mask = None
for address in netif.addrlist:
ip, sep, mask = address.partition('/')
mask = int(mask)
if isIPv4Address(ip):
if ipaddress.is_ipv4_address(ip):
family = AF_INET
tlvtypeip = coreapi.CORE_TLV_LINK_IF2IP4
tlvtypemask = coreapi.CORE_TLV_LINK_IF2IP4MASK
ipl = socket.inet_pton(family, ip)
interface2_ip4 = ipaddress.IpAddress(af=family, address=ipl)
interface2_ip4_mask = mask
else:
family = AF_INET6
tlvtypeip = coreapi.CORE_TLV_LINK_IF2IP6
tlvtypemask = coreapi.CORE_TLV_LINK_IF2IP6MASK
ipl = socket.inet_pton(family, ip)
tlvdata += coreapi.CoreLinkTlv.pack(tlvtypeip, \
IPAddr(af=family, addr=ipl))
tlvdata += coreapi.CoreLinkTlv.pack(tlvtypemask, mask)
interface2_ip6 = ipaddress.IpAddress(af=family, address=ipl)
interface2_ip6_mask = 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:
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')
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')
tlvdata += coreapi.CoreLinkTlv.pack(coreapi.CORE_TLV_LINK_UNI, 1)
msg = coreapi.CoreLinkMessage.pack(0, tlvdata)
msgs.append(msg)
return msgs
all_links.append(link_data)
return all_links
class PyCoreNetIf(object):
''' Base class for interfaces.
'''
"""
Base class for interfaces.
"""
def __init__(self, node, name, mtu):
self.node = node
self.name = name
@ -428,6 +433,8 @@ class PyCoreNetIf(object):
self.transport_type = None
# interface index on the network
self.netindex = None
# index used to find flow data
self.flow_id = None
def startup(self):
pass
@ -456,25 +463,28 @@ class PyCoreNetIf(object):
self.hwaddr = addr
def getparam(self, key):
''' Retrieve a parameter from the _params dict,
"""
Retrieve a parameter from the _params dict,
or None if the parameter does not exist.
'''
"""
if key not in self._params:
return None
return self._params[key]
def getparams(self):
''' Return (key, value) pairs from the _params dict.
'''
"""
Return (key, value) pairs from the _params dict.
"""
r = []
for k in sorted(self._params.keys()):
r.append((k, self._params[k]))
return r
def setparam(self, key, value):
''' Set a parameter in the _params dict.
"""
Set a parameter in the _params dict.
Returns True if the parameter has changed.
'''
"""
if key in self._params:
if self._params[key] == value:
return False
@ -485,10 +495,11 @@ class PyCoreNetIf(object):
return True
def swapparams(self, name):
''' Swap out the _params dict for name. If name does not exist,
"""
Swap out the _params dict for name. If name does not exist,
intialize it. This is for supporting separate upstream/downstream
parameters when two layer-2 nodes are linked together.
'''
"""
tmp = self._params
if not hasattr(self, name):
setattr(self, name, {})
@ -496,8 +507,8 @@ class PyCoreNetIf(object):
setattr(self, name, tmp)
def setposition(self, x, y, z):
''' Dispatch to any position hook (self.poshook) handler.
'''
"""
Dispatch to any position hook (self.poshook) handler.
"""
if self.poshook is not None:
self.poshook(self, x, y, z)

File diff suppressed because it is too large Load diff

120
daemon/core/data.py Normal file
View file

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

View file

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

View file

@ -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
'''
"""
import sys
import string
from core.api import coreapi
from core.emane.emanemodel import EmaneModel
from core.enumerations import ConfigDataTypes
from core.constants import *
from emane import EmaneModel
class EmaneBypassModel(EmaneModel):
def __init__(self, session, objid = None, verbose = False):
EmaneModel.__init__(self, session, objid, verbose)
def __init__(self, session, object_id=None):
EmaneModel.__init__(self, session, object_id)
_name = "emane_bypass"
_confmatrix = [
("none",coreapi.CONF_DATA_TYPE_BOOL, '0',
name = "emane_bypass"
config_matrix = [
("none", ConfigDataTypes.BOOL.value, '0',
'True,False', 'There are no parameters for the bypass model.'),
]
# value groupings
_confgroups = "Bypass Parameters:1-1"
config_groups = "Bypass Parameters:1-1"
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
that file also. Otherwise the WLAN-wide nXXemane_bypassnem.xml,
nXXemane_bypassmac.xml, nXXemane_bypassphy.xml are used.
'''
values = e.getifcconfig(self.objid, self._name,
self.getdefaultvalues(), ifc)
"""
values = e.getifcconfig(self.object_id, self.name, self.getdefaultvalues(), ifc)
if values is None:
return
nemdoc = e.xmldoc("nem")
nem = nemdoc.getElementsByTagName("nem").pop()
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.setAttribute("definition", self.macxmlname(ifc))
nem.appendChild(mactag)
@ -62,5 +52,3 @@ class EmaneBypassModel(EmaneModel):
phy.setAttribute("name", "BYPASS PHY")
phy.setAttribute("library", "bypassphylayer")
e.xmlwrite(phydoc, self.phyxmlname(ifc))

View file

@ -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
'''
"""
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:
from emanesh.events import EventService
except:
pass
from core.api import coreapi
from core.constants import *
from emane import Emane, EmaneModel
except ImportError:
logger.error("error importing emanesh")
try:
import emaneeventservice
import emaneeventcommeffect
except Exception, e:
pass
except ImportError:
logger.error("error importing emaneeventservice and emaneeventcommeffect")
class EmaneCommEffectModel(EmaneModel):
def __init__(self, session, objid = None, verbose = False):
EmaneModel.__init__(self, session, objid, verbose)
def __init__(self, session, object_id=None):
EmaneModel.__init__(self, session, object_id)
# model name
_name = "emane_commeffect"
name = "emane_commeffect"
# CommEffect parameters
_confmatrix_shim_base = [
("filterfile", coreapi.CONF_DATA_TYPE_STRING, '',
("filterfile", ConfigDataTypes.STRING.value, '',
'', 'filter file'),
("groupid", coreapi.CONF_DATA_TYPE_UINT32, '0',
("groupid", ConfigDataTypes.UINT32.value, '0',
'', 'NEM Group ID'),
("enablepromiscuousmode", coreapi.CONF_DATA_TYPE_BOOL, '0',
("enablepromiscuousmode", ConfigDataTypes.BOOL.value, '0',
'On,Off', 'enable promiscuous mode'),
("receivebufferperiod", coreapi.CONF_DATA_TYPE_FLOAT, '1.0',
("receivebufferperiod", ConfigDataTypes.FLOAT.value, '1.0',
'', 'receivebufferperiod'),
]
_confmatrix_shim_081 = [
("defaultconnectivity", coreapi.CONF_DATA_TYPE_BOOL, '0',
("defaultconnectivity", ConfigDataTypes.BOOL.value, '0',
'On,Off', 'defaultconnectivity'),
("enabletighttimingmode", coreapi.CONF_DATA_TYPE_BOOL, '0',
("enabletighttimingmode", ConfigDataTypes.BOOL.value, '0',
'On,Off', 'enable tight timing mode'),
]
_confmatrix_shim_091 = [
("defaultconnectivitymode", coreapi.CONF_DATA_TYPE_BOOL, '0',
("defaultconnectivitymode", ConfigDataTypes.BOOL.value, '0',
'On,Off', 'defaultconnectivity'),
]
if Emane.version >= Emane.EMANE091:
if emane.VERSION >= emane.EMANE091:
_confmatrix_shim = _confmatrix_shim_base + _confmatrix_shim_091
else:
_confmatrix_shim = _confmatrix_shim_base + _confmatrix_shim_081
_confmatrix = _confmatrix_shim
config_matrix = _confmatrix_shim
# value groupings
_confgroups = "CommEffect SHIM Parameters:1-%d" \
% len(_confmatrix_shim)
config_groups = "CommEffect SHIM Parameters:1-%d" % len(_confmatrix_shim)
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
that file also. Otherwise the WLAN-wide
nXXemane_commeffectnem.xml, nXXemane_commeffectshim.xml are used.
'''
values = e.getifcconfig(self.objid, self._name,
self.getdefaultvalues(), ifc)
"""
values = e.getifcconfig(self.object_id, self.name, self.getdefaultvalues(), ifc)
if values is None:
return
shimdoc = e.xmldoc("shim")
@ -83,8 +77,7 @@ class EmaneCommEffectModel(EmaneModel):
shimnames.remove("filterfile")
# append all shim options (except filterfile) to shimdoc
map( lambda n: shim.appendChild(e.xmlparam(shimdoc, n, \
self.valueof(n, values))), shimnames)
map(lambda n: shim.appendChild(e.xmlparam(shimdoc, n, self.valueof(n, values))), shimnames)
# empty filterfile is not allowed
ff = self.valueof("filterfile", values)
if ff.strip() != '':
@ -95,20 +88,24 @@ class EmaneCommEffectModel(EmaneModel):
nem = nemdoc.getElementsByTagName("nem").pop()
nem.setAttribute("name", "commeffect NEM")
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)))
e.xmlwrite(nemdoc, self.nemxmlname(ifc))
def linkconfig(self, netif, bw=None, delay=None,
loss=None, duplicate=None, jitter=None, netif2=None):
''' Generate CommEffect events when a Link Message is received having
"""
Generate CommEffect events when a Link Message is received having
link parameters.
'''
if self.session.emane.version >= self.session.emane.EMANE091:
"""
if emane.VERSION >= emane.EMANE091:
raise NotImplementedError, \
"CommEffect linkconfig() not implemented for EMANE 0.9.1+"
def z(x):
''' Helper to use 0 for None values. '''
"""
Helper to use 0 for None values.
"""
if type(x) is str:
x = float(x)
if x is None:
@ -118,17 +115,16 @@ class EmaneCommEffectModel(EmaneModel):
service = self.session.emane.service
if service is None:
self.session.warn("%s: EMANE event service unavailable" % \
self._name)
logger.warn("%s: EMANE event service unavailable" % self.name)
return
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
# TODO: batch these into multiple events per transmission
# TODO: may want to split out seconds portion of delay and jitter
event = emaneeventcommeffect.EventCommEffect(1)
index = 0
e = self.session.obj(self.objid)
e = self.session.get_object(self.object_id)
nemid = e.getnemid(netif)
nemid2 = e.getnemid(netif2)
mbw = bw
@ -139,6 +135,3 @@ class EmaneCommEffectModel(EmaneModel):
emaneeventservice.PLATFORMID_ANY,
nemid2, emaneeventservice.COMPONENTID_ANY,
event.export())

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,203 @@
"""
Defines Emane Models used within CORE.
"""
from core import emane
from core.misc import log
from core.misc import utils
from core.mobility import WirelessModel
from core.xml import xmlutils
logger = log.get_logger(__name__)
class EmaneModel(WirelessModel):
"""
EMANE models inherit from this parent class, which takes care of
handling configuration messages based on the _confmatrix list of
configurable parameters. Helper functions also live here.
"""
_prefix = {'y': 1e-24, # yocto
'z': 1e-21, # zepto
'a': 1e-18, # atto
'f': 1e-15, # femto
'p': 1e-12, # pico
'n': 1e-9, # nano
'u': 1e-6, # micro
'm': 1e-3, # mili
'c': 1e-2, # centi
'd': 1e-1, # deci
'k': 1e3, # kilo
'M': 1e6, # mega
'G': 1e9, # giga
'T': 1e12, # tera
'P': 1e15, # peta
'E': 1e18, # exa
'Z': 1e21, # zetta
'Y': 1e24, # yotta
}
@classmethod
def configure_emane(cls, session, config_data):
"""
Handle configuration messages for setting up a model.
Pass the Emane object as the manager object.
:param core.session.Session session: session to configure emane
:param core.conf.ConfigData config_data: configuration data for carrying out a configuration
"""
return cls.configure(session.emane, config_data)
@classmethod
def emane074_fixup(cls, value, div=1.0):
"""
Helper for converting 0.8.1 and newer values to EMANE 0.7.4
compatible values.
NOTE: This should be removed when support for 0.7.4 has been
deprecated.
"""
if div == 0:
return "0"
if type(value) is not str:
return str(value / div)
if value.endswith(tuple(cls._prefix.keys())):
suffix = value[-1]
value = float(value[:-1]) * cls._prefix[suffix]
return str(int(value / div))
def buildnemxmlfiles(self, e, ifc):
"""
Build the necessary nem, mac, and phy XMLs in the given path.
"""
raise NotImplementedError
def buildplatformxmlnementry(self, doc, n, ifc):
"""
Build the NEM definition that goes into the platform.xml file.
This returns an XML element that will be added to the <platform/> element.
This default method supports per-interface config
(e.g. <nem definition="n2_0_63emane_rfpipe.xml" id="1"> or per-EmaneNode
config (e.g. <nem definition="n1emane_rfpipe.xml" id="1">.
This can be overriden by a model for NEM flexibility; n is the EmaneNode.
"""
nem = doc.createElement("nem")
nem.setAttribute("name", ifc.localname)
# if this netif contains a non-standard (per-interface) config,
# then we need to use a more specific xml file here
nem.setAttribute("definition", self.nemxmlname(ifc))
return nem
def buildplatformxmltransportentry(self, doc, n, ifc):
"""
Build the transport definition that goes into the platform.xml file.
This returns an XML element that will added to the nem definition.
This default method supports raw and virtual transport types, but may be
overriden by a model to support the e.g. pluggable virtual transport.
n is the EmaneNode.
"""
ttype = ifc.transport_type
if not ttype:
logger.info("warning: %s interface type unsupported!" % ifc.name)
ttype = "raw"
trans = doc.createElement("transport")
trans.setAttribute("definition", n.transportxmlname(ttype))
if emane.VERSION < emane.EMANE092:
trans.setAttribute("group", "1")
param = doc.createElement("param")
param.setAttribute("name", "device")
if ttype == "raw":
# raw RJ45 name e.g. 'eth0'
param.setAttribute("value", ifc.name)
else:
# virtual TAP name e.g. 'n3.0.17'
param.setAttribute("value", ifc.localname)
if emane.VERSION > emane.EMANE091:
param.setAttribute("value", ifc.name)
trans.appendChild(param)
return trans
def basename(self, interface=None):
"""
Return the string that other names are based on.
If a specific config is stored for a node's interface, a unique
filename is needed; otherwise the name of the EmaneNode is used.
"""
emane = self.session.emane
name = "n%s" % self.object_id
if interface is not None:
nodenum = interface.node.objid
# Adamson change - use getifcconfig() to get proper result
# if emane.getconfig(nodenum, self._name, None)[1] is not None:
if emane.getifcconfig(nodenum, self.name, None, interface) is not None:
name = interface.localname.replace('.', '_')
return "%s%s" % (name, self.name)
def nemxmlname(self, interface=None):
"""
Return the string name for the NEM XML file, e.g. 'n3rfpipenem.xml'
"""
append = ""
if emane.VERSION > emane.EMANE091:
if interface and interface.transport_type == "raw":
append = "_raw"
return "%snem%s.xml" % (self.basename(interface), append)
def shimxmlname(self, ifc=None):
"""
Return the string name for the SHIM XML file, e.g. 'commeffectshim.xml'
"""
return "%sshim.xml" % self.basename(ifc)
def macxmlname(self, ifc=None):
"""
Return the string name for the MAC XML file, e.g. 'n3rfpipemac.xml'
"""
return "%smac.xml" % self.basename(ifc)
def phyxmlname(self, ifc=None):
"""
Return the string name for the PHY XML file, e.g. 'n3rfpipephy.xml'
"""
return "%sphy.xml" % self.basename(ifc)
def update(self, moved, moved_netifs):
"""
invoked from MobilityModel when nodes are moved; this causes
EMANE location events to be generated for the nodes in the moved
list, making EmaneModels compatible with Ns2ScriptedMobility
"""
try:
wlan = self.session.get_object(self.object_id)
wlan.setnempositions(moved_netifs)
except KeyError:
logger.exception("error during update")
def linkconfig(self, netif, bw=None, delay=None, loss=None, duplicate=None, jitter=None, netif2=None):
"""
Invoked when a Link Message is received. Default is unimplemented.
"""
warntxt = "EMANE model %s does not support link " % self.name
warntxt += "configuration, dropping Link Message"
logger.warn(warntxt)
@staticmethod
def valuestrtoparamlist(dom, name, value):
"""
Helper to convert a parameter to a paramlist.
Returns a an XML paramlist, or None if the value does not expand to
multiple values.
"""
try:
values = utils.maketuplefromstr(value, str)
except SyntaxError:
logger.exception("error in value string to param list")
return None
if not hasattr(values, '__iter__'):
return None
if len(values) < 2:
return None
return xmlutils.add_param_list_to_parent(dom, parent=None, name=name, values=values)

View file

@ -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
'''
"""
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:
from emanesh.events import EventService
except:
pass
from core.api import coreapi
from core.constants import *
from emane import Emane, EmaneModel
from universal import EmaneUniversalModel
logger.error("error importing emanesh")
class EmaneIeee80211abgModel(EmaneModel):
def __init__(self, session, objid = None, verbose = False):
EmaneModel.__init__(self, session, objid, verbose)
def __init__(self, session, object_id=None):
EmaneModel.__init__(self, session, object_id)
# 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,' + \
'6 9 Mbps,7 12 Mbps,8 18 Mbps,9 24 Mbps,10 36 Mbps,11 48 Mbps,' + \
'12 54 Mbps'
if Emane.version >= Emane.EMANE091:
if emane.VERSION >= emane.EMANE091:
xml_path = '/usr/share/emane/xml/models/mac/ieee80211abg'
else:
xml_path = "/usr/share/emane/models/ieee80211abg/xml"
# MAC parameters
_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),' + \
'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'),
("distance", coreapi.CONF_DATA_TYPE_UINT32, '1000',
("distance", ConfigDataTypes.UINT32.value, '1000',
'', 'max distance (m)'),
("unicastrate", coreapi.CONF_DATA_TYPE_UINT8, '4', _80211rates,
("unicastrate", ConfigDataTypes.UINT8.value, '4', _80211rates,
'unicast rate (Mbps)'),
("multicastrate", coreapi.CONF_DATA_TYPE_UINT8, '1', _80211rates,
("multicastrate", ConfigDataTypes.UINT8.value, '1', _80211rates,
'multicast rate (Mbps)'),
("rtsthreshold", coreapi.CONF_DATA_TYPE_UINT16, '0',
("rtsthreshold", ConfigDataTypes.UINT16.value, '0',
'', 'RTS threshold (bytes)'),
("pcrcurveuri", coreapi.CONF_DATA_TYPE_STRING,
("pcrcurveuri", ConfigDataTypes.STRING.value,
'%s/ieee80211pcr.xml' % xml_path,
'', 'SINR/PCR curve file'),
("flowcontrolenable", coreapi.CONF_DATA_TYPE_BOOL, '0',
("flowcontrolenable", ConfigDataTypes.BOOL.value, '0',
'On,Off', 'enable traffic flow control'),
("flowcontroltokens", coreapi.CONF_DATA_TYPE_UINT16, '10',
("flowcontroltokens", ConfigDataTypes.UINT16.value, '10',
'', 'number of flow control tokens'),
]
# 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
# CORE GUI. A conversion function (get9xmacparamequivalent) has been defined to support this.
_confmatrix_mac_extended = [
("wmmenable", coreapi.CONF_DATA_TYPE_BOOL, '0',
("wmmenable", ConfigDataTypes.BOOL.value, '0',
'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)'),
("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)'),
("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)'),
("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)'),
("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)'),
("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)'),
]
_confmatrix_mac_091 = [
('radiometricenable', coreapi.CONF_DATA_TYPE_BOOL, '0',
('radiometricenable', ConfigDataTypes.BOOL.value, '0',
'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)'),
('neighbormetricdeletetime', coreapi.CONF_DATA_TYPE_FLOAT, '60.0',
('neighbormetricdeletetime', ConfigDataTypes.FLOAT.value, '60.0',
'', 'R2RI neighbor table inactivity time (sec)'),
]
_confmatrix_mac = _confmatrix_mac_base + _confmatrix_mac_extended
if Emane.version >= Emane.EMANE091:
if emane.VERSION >= emane.EMANE091:
_confmatrix_mac += _confmatrix_mac_091
# 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
_confgroups = "802.11 MAC Parameters:1-%d|Universal PHY Parameters:%d-%d" \
% (len(_confmatrix_mac), len(_confmatrix_mac) + 1, len(_confmatrix))
config_groups = "802.11 MAC Parameters:1-%d|Universal PHY Parameters:%d-%d" % (
len(_confmatrix_mac), len(_confmatrix_mac) + 1, len(config_matrix))
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
that file also. Otherwise the WLAN-wide
nXXemane_ieee80211abgnem.xml, nXXemane_ieee80211abgemac.xml,
nXXemane_ieee80211abgphy.xml are used.
'''
values = e.getifcconfig(self.objid, self._name,
self.getdefaultvalues(), ifc)
"""
values = e.getifcconfig(self.object_id, self.name, self.getdefaultvalues(), ifc)
if values is None:
return
nemdoc = e.xmldoc("nem")
nem = nemdoc.getElementsByTagName("nem").pop()
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.setAttribute("definition", self.macxmlname(ifc))
nem.appendChild(mactag)
@ -130,14 +126,13 @@ class EmaneIeee80211abgModel(EmaneModel):
phynames = names[len(self._confmatrix_mac):]
# append all MAC options to macdoc
if Emane.version >= Emane.EMANE091:
if emane.VERSION >= emane.EMANE091:
for macname in macnames:
mac9xnvpairlist = self.get9xmacparamequivalent(macname, values)
for nvpair in mac9xnvpairlist:
mac.appendChild(e.xmlparam(macdoc, nvpair[0], nvpair[1]))
else:
map( lambda n: mac.appendChild(e.xmlparam(macdoc, n, \
self.valueof(n, values))), macnames)
map(lambda n: mac.appendChild(e.xmlparam(macdoc, n, self.valueof(n, values))), macnames)
e.xmlwrite(macdoc, self.macxmlname(ifc))
@ -150,11 +145,12 @@ class EmaneIeee80211abgModel(EmaneModel):
# and work with EMANE 9.x onwards.
#
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
equivalent 9.x parameter and value pairs. Otherwise, the list returned will only
contain a single name and value pair.
'''
"""
nvpairlist = []
macparmval = self.valueof(macname, values)
if macname in ["queuesize", "aifs", "cwmin", "cwmax", "txop", "retrylimit"]:
@ -164,10 +160,9 @@ class EmaneIeee80211abgModel(EmaneModel):
val = idx_and_val[1]
# aifs and tx are in microseconds. Convert to seconds.
if macname in ["aifs", "txop"]:
val = "%f" % (float(val)*(1e-6))
val = "%f" % (float(val) * 1e-6)
name9x = "%s%d" % (macname, idx)
nvpairlist.append([name9x, val])
else:
nvpairlist.append([macname, macparmval])
return nvpairlist

View file

@ -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
control of an EMANE emulation. An EmaneNode has several attached NEMs that
share the same MAC+PHY model.
'''
"""
import sys
import os.path
from os import path
from core.api import coreapi
from core import emane
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:
from emanesh.events import EventService
from emanesh.events import LocationEvent
except Exception, e:
pass
except ImportError:
logger.error("error loading emanesh")
try:
import emaneeventservice
import emaneeventlocation
except Exception, e:
''' Don't require all CORE users to have EMANE libeventservice and its
except ImportError:
"""
Don't require all CORE users to have EMANE libeventservice and its
Python bindings installed.
'''
pass
"""
logger.error("error loading emaneeventservice and emaneeventlocation")
class EmaneNet(PyCoreNet):
''' EMANE network base class.
'''
apitype = coreapi.CORE_NODE_EMANE
linktype = coreapi.CORE_LINK_WIRELESS
type = "wlan" # icon used
"""
EMANE network base class.
"""
apitype = NodeTypes.EMANE.value
linktype = LinkTypes.WIRELESS.value
# icon used
type = "wlan"
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
Emane controller object that exists in a session.
'''
def __init__(self, session, objid = None, name = None, verbose = False,
start = True):
PyCoreNet.__init__(self, session, objid, name, verbose, start)
self.verbose = verbose
"""
def __init__(self, session, objid=None, name=None, start=True):
PyCoreNet.__init__(self, session, objid, name, start)
self.conf = ""
self.up = False
self.nemidmap = {}
self.model = None
self.mobility = None
def linkconfig(self, netif, bw = None, delay = None,
loss = None, duplicate = None, jitter = None, netif2 = None):
''' The CommEffect model supports link configuration.
'''
def linkconfig(self, netif, bw=None, delay=None, loss=None, duplicate=None, jitter=None, netif2=None):
"""
The CommEffect model supports link configuration.
"""
if not self.model:
return
return self.model.linkconfig(netif=netif, bw=bw, delay=delay, loss=loss,
duplicate=duplicate, jitter=jitter, netif2=netif2)
def config(self, conf):
#print "emane", self.name, "got config:", conf
self.conf = conf
def shutdown(self):
@ -76,55 +79,57 @@ class EmaneNode(EmaneNet):
pass
def setmodel(self, model, config):
''' set the EmaneModel associated with this node
'''
if (self.verbose):
self.info("adding model %s" % model._name)
if model._type == coreapi.CORE_TLV_REG_WIRELESS:
"""
set the EmaneModel associated with this node
"""
logger.info("adding model: %s", model.name)
if model.config_type == RegisterTlvs.WIRELESS.value:
# EmaneModel really uses values from ConfigurableManager
# when buildnemxml() is called, not during init()
self.model = model(session=self.session, objid=self.objid,
verbose=self.verbose)
elif model._type == coreapi.CORE_TLV_REG_MOBILITY:
self.mobility = model(session=self.session, objid=self.objid,
verbose=self.verbose, values=config)
self.model = model(session=self.session, object_id=self.objid)
elif model.config_type == RegisterTlvs.MOBILITY.value:
self.mobility = model(session=self.session, object_id=self.objid, values=config)
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.
'''
"""
self.nemidmap[netif] = nemid
def getnemid(self, netif):
''' Given an interface, return its numerical ID.
'''
"""
Given an interface, return its numerical ID.
"""
if netif not in self.nemidmap:
return None
else:
return self.nemidmap[netif]
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.
'''
"""
for netif in self.nemidmap:
if self.nemidmap[netif] == nemid:
return netif
return None
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)
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.
'''
"""
ret = {}
if self.model is None:
self.info("warning: EmaneNode %s has no associated model" % \
self.name)
logger.info("warning: EmaneNode %s has no associated model" % self.name)
return ret
for netif in self.netifs():
# <nem name="NODE-001" definition="rfpipenem.xml">
@ -139,9 +144,9 @@ class EmaneNode(EmaneNet):
return ret
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:
return
# build XML for overall network (EmaneNode) configs
@ -166,8 +171,9 @@ class EmaneNode(EmaneNet):
self.buildtransportxml(emane, rtype)
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")
trans = transdoc.getElementsByTagName("transport").pop()
trans.setAttribute("name", "%s Transport" % type.capitalize())
@ -176,7 +182,7 @@ class EmaneNode(EmaneNet):
flowcontrol = False
names = self.model.getnames()
values = emane.getconfig(self.objid, self.model._name,
values = emane.getconfig(self.objid, self.model.name,
self.model.getdefaultvalues())[1]
if "flowcontrolenable" in names and values:
i = names.index("flowcontrolenable")
@ -184,35 +190,30 @@ class EmaneNode(EmaneNet):
flowcontrol = True
if "virtual" in type.lower():
if os.path.exists("/dev/net/tun_flowctl"):
trans.appendChild(emane.xmlparam(transdoc, "devicepath",
"/dev/net/tun_flowctl"))
if path.exists("/dev/net/tun_flowctl"):
trans.appendChild(emane.xmlparam(transdoc, "devicepath", "/dev/net/tun_flowctl"))
else:
trans.appendChild(emane.xmlparam(transdoc, "devicepath",
"/dev/net/tun"))
trans.appendChild(emane.xmlparam(transdoc, "devicepath", "/dev/net/tun"))
if flowcontrol:
trans.appendChild(emane.xmlparam(transdoc, "flowcontrolenable",
"on"))
trans.appendChild(emane.xmlparam(transdoc, "flowcontrolenable", "on"))
emane.xmlwrite(transdoc, self.transportxmlname(type.lower()))
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)
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
to bind to the TAPs.
'''
if self.session.emane.genlocationevents() and \
self.session.emane.service is None:
"""
if self.session.emane.genlocationevents() and self.session.emane.service is None:
warntxt = "unable to publish EMANE events because the eventservice "
warntxt += "Python bindings failed to load"
self.session.exception(coreapi.CORE_EXCP_LEVEL_ERROR, self.name,
self.objid, warntxt)
logger.error(warntxt)
for netif in self.netifs():
if do_netns and "virtual" in netif.transport_type.lower():
@ -228,63 +229,65 @@ class EmaneNode(EmaneNet):
self.setnemposition(netif, x, y, z)
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
emanetransportd terminates.
'''
"""
for netif in self.netifs():
if "virtual" in netif.transport_type.lower():
netif.shutdown()
netif.poshook = None
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.verbose:
self.info("position service not available")
logger.info("position service not available")
return
nemid = self.getnemid(netif)
ifname = netif.localname
if nemid is None:
self.info("nemid for %s is unknown" % ifname)
logger.info("nemid for %s is unknown" % ifname)
return
(lat, long, alt) = self.session.location.getgeo(x, y, z)
if self.verbose:
self.info("setnemposition %s (%s) x,y,z=(%d,%d,%s)"
logger.info("setnemposition %s (%s) x,y,z=(%d,%d,%s)"
"(%.6f,%.6f,%.6f)" % \
(ifname, nemid, x, y, z, lat, long, alt))
if self.session.emane.version >= self.session.emane.EMANE091:
if emane.VERSION >= emane.EMANE091:
event = LocationEvent()
else:
event = emaneeventlocation.EventLocation(1)
# altitude must be an integer or warning is printed
# unused: yaw, pitch, roll, azimuth, elevation, velocity
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)
self.session.emane.service.publish(0, event)
else:
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.NEMID_ANY,
emaneeventservice.COMPONENTID_ANY,
event.export())
event.export()
)
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
entries for each netif that has moved.
'''
"""
if len(moved_netifs) == 0:
return
if self.session.emane.service is None:
if self.verbose:
self.info("position service not available")
logger.info("position service not available")
return
if self.session.emane.version >= self.session.emane.EMANE091:
if emane.VERSION >= emane.EMANE091:
event = LocationEvent()
else:
event = emaneeventlocation.EventLocation(len(moved_netifs))
@ -293,29 +296,28 @@ class EmaneNode(EmaneNet):
nemid = self.getnemid(netif)
ifname = netif.localname
if nemid is None:
self.info("nemid for %s is unknown" % ifname)
logger.info("nemid for %s is unknown" % ifname)
continue
(x, y, z) = netif.node.getposition()
(lat, long, alt) = self.session.location.getgeo(x, y, z)
if self.verbose:
self.info("setnempositions %d %s (%s) x,y,z=(%d,%d,%s)"
"(%.6f,%.6f,%.6f)" % \
logger.info("setnempositions %d %s (%s) x,y,z=(%d,%d,%s)"
"(%.6f,%.6f,%.6f)" %
(i, ifname, nemid, x, y, z, lat, long, alt))
# altitude must be an integer or warning is printed
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)
else:
event.set(i, nemid, lat, long, alt)
i += 1
if self.session.emane.version >= self.session.emane.EMANE091:
if emane.VERSION >= emane.EMANE091:
self.session.emane.service.publish(0, event)
else:
self.session.emane.service.publish(emaneeventlocation.EVENT_ID,
self.session.emane.service.publish(
emaneeventlocation.EVENT_ID,
emaneeventservice.PLATFORMID_ANY,
emaneeventservice.NEMID_ANY,
emaneeventservice.COMPONENTID_ANY,
event.export())
event.export()
)

View file

@ -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
'''
"""
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:
from emanesh.events import EventService
except:
pass
from core.api import coreapi
from core.constants import *
from emane import Emane, EmaneModel
from universal import EmaneUniversalModel
except ImportError:
logger.error("error importing emanesh")
class EmaneRfPipeModel(EmaneModel):
def __init__(self, session, objid = None, verbose = False):
EmaneModel.__init__(self, session, objid, verbose)
def __init__(self, session, object_id=None):
EmaneModel.__init__(self, session, object_id)
# model name
_name = "emane_rfpipe"
if Emane.version >= Emane.EMANE091:
name = "emane_rfpipe"
if emane.VERSION >= emane.EMANE091:
xml_path = '/usr/share/emane/xml/models/mac/rfpipe'
else:
xml_path = "/usr/share/emane/models/rfpipe/xml"
@ -36,68 +31,69 @@ class EmaneRfPipeModel(EmaneModel):
# ( 'name', 'type', 'default', 'possible-value-list', 'caption')
# MAC parameters
_confmatrix_mac_base = [
("enablepromiscuousmode", coreapi.CONF_DATA_TYPE_BOOL, '0',
("enablepromiscuousmode", ConfigDataTypes.BOOL.value, '0',
'True,False', 'enable promiscuous mode'),
("datarate", coreapi.CONF_DATA_TYPE_UINT32, '1M',
("datarate", ConfigDataTypes.UINT32.value, '1M',
'', 'data rate (bps)'),
("flowcontrolenable", coreapi.CONF_DATA_TYPE_BOOL, '0',
("flowcontrolenable", ConfigDataTypes.BOOL.value, '0',
'On,Off', 'enable traffic flow control'),
("flowcontroltokens", coreapi.CONF_DATA_TYPE_UINT16, '10',
("flowcontroltokens", ConfigDataTypes.UINT16.value, '10',
'', 'number of flow control tokens'),
("pcrcurveuri", coreapi.CONF_DATA_TYPE_STRING,
("pcrcurveuri", ConfigDataTypes.STRING.value,
'%s/rfpipepcr.xml' % xml_path,
'', 'SINR/PCR curve file'),
]
_confmatrix_mac_081 = [
("jitter", coreapi.CONF_DATA_TYPE_FLOAT, '0.0',
("jitter", ConfigDataTypes.FLOAT.value, '0.0',
'', 'transmission jitter (usec)'),
("delay", coreapi.CONF_DATA_TYPE_FLOAT, '0.0',
("delay", ConfigDataTypes.FLOAT.value, '0.0',
'', 'transmission delay (usec)'),
("transmissioncontrolmap", coreapi.CONF_DATA_TYPE_STRING, '',
("transmissioncontrolmap", ConfigDataTypes.STRING.value, '',
'', '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'),
]
_confmatrix_mac_091 = [
("jitter", coreapi.CONF_DATA_TYPE_FLOAT, '0.0',
("jitter", ConfigDataTypes.FLOAT.value, '0.0',
'', 'transmission jitter (sec)'),
("delay", coreapi.CONF_DATA_TYPE_FLOAT, '0.0',
("delay", ConfigDataTypes.FLOAT.value, '0.0',
'', 'transmission delay (sec)'),
('radiometricenable', coreapi.CONF_DATA_TYPE_BOOL, '0',
('radiometricenable', ConfigDataTypes.BOOL.value, '0',
'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)'),
('neighbormetricdeletetime', coreapi.CONF_DATA_TYPE_FLOAT, '60.0',
('neighbormetricdeletetime', ConfigDataTypes.FLOAT.value, '60.0',
'', 'R2RI neighbor table inactivity time (sec)'),
]
if Emane.version >= Emane.EMANE091:
if emane.VERSION >= emane.EMANE091:
_confmatrix_mac = _confmatrix_mac_base + _confmatrix_mac_091
else:
_confmatrix_mac = _confmatrix_mac_base + _confmatrix_mac_081
# 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
_confgroups = "RF-PIPE MAC Parameters:1-%d|Universal PHY Parameters:%d-%d" \
% ( len(_confmatrix_mac), len(_confmatrix_mac) + 1, len(_confmatrix))
config_groups = "RF-PIPE MAC Parameters:1-%d|Universal PHY Parameters:%d-%d" % (
len(_confmatrix_mac), len(_confmatrix_mac) + 1, len(config_matrix))
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
that file also. Otherwise the WLAN-wide nXXemane_rfpipenem.xml,
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)
if values is None:
return
nemdoc = e.xmldoc("nem")
nem = nemdoc.getElementsByTagName("nem").pop()
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.setAttribute("definition", self.macxmlname(ifc))
nem.appendChild(mactag)
@ -124,10 +120,8 @@ class EmaneRfPipeModel(EmaneModel):
values = list(values)
values[i] = self.emane074_fixup(values[i], 1000)
# append MAC options to macdoc
map(lambda n: mac.appendChild(e.xmlparam(macdoc, n, \
self.valueof(n, values))), macnames)
map(lambda n: mac.appendChild(e.xmlparam(macdoc, n, self.valueof(n, values))), macnames)
e.xmlwrite(macdoc, self.macxmlname(ifc))
phydoc = EmaneUniversalModel.getphydoc(e, self, values, phynames)
e.xmlwrite(phydoc, self.phyxmlname(ifc))

View file

@ -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
'''
"""
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:
from emanesh.events import EventService
except:
pass
from core.api import coreapi
from core.constants import *
from emane import Emane, EmaneModel
from universal import EmaneUniversalModel
logger.error("error importing emanesh")
class EmaneTdmaModel(EmaneModel):
def __init__(self, session, objid = None, verbose = False):
EmaneModel.__init__(self, session, objid, verbose)
def __init__(self, session, object_id=None):
EmaneModel.__init__(self, session, object_id)
# model name
_name = "emane_tdma"
if Emane.version >= Emane.EMANE101:
name = "emane_tdma"
if emane.VERSION >= emane.EMANE101:
xml_path = '/usr/share/emane/xml/models/mac/tdmaeventscheduler'
else:
raise Exception("EMANE TDMA requires EMANE 1.0.1 or greater")
# MAC parameters
_confmatrix_mac = [
("enablepromiscuousmode", coreapi.CONF_DATA_TYPE_BOOL, '0',
("enablepromiscuousmode", ConfigDataTypes.BOOL.value, '0',
'True,False', 'enable promiscuous mode'),
("flowcontrolenable", coreapi.CONF_DATA_TYPE_BOOL, '0',
("flowcontrolenable", ConfigDataTypes.BOOL.value, '0',
'On,Off', 'enable traffic flow control'),
("flowcontroltokens", coreapi.CONF_DATA_TYPE_UINT16, '10',
("flowcontroltokens", ConfigDataTypes.UINT16.value, '10',
'', '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'),
("fragmenttimeoutthreshold", coreapi.CONF_DATA_TYPE_UINT16, '5',
("fragmenttimeoutthreshold", ConfigDataTypes.UINT16.value, '5',
'', '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)'),
('neighbormetricupdateinterval', coreapi.CONF_DATA_TYPE_FLOAT, '1.0',
('neighbormetricupdateinterval', ConfigDataTypes.FLOAT.value, '1.0',
'', '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'),
("queue.aggregationenable", coreapi.CONF_DATA_TYPE_BOOL, '1',
("queue.aggregationenable", ConfigDataTypes.BOOL.value, '1',
'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'),
("queue.depth", coreapi.CONF_DATA_TYPE_UINT16, '256',
("queue.depth", ConfigDataTypes.UINT16.value, '256',
'', '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)'),
("queue.strictdequeueenable", coreapi.CONF_DATA_TYPE_BOOL, '0',
("queue.strictdequeueenable", ConfigDataTypes.BOOL.value, '0',
'On,Off', 'enable strict dequeueing to specified queues only'),
]
# 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
_confgroups = "TDMA MAC Parameters:1-%d|Universal PHY Parameters:%d-%d" % \
(len(_confmatrix_mac), len(_confmatrix_mac) + 1, len(_confmatrix))
config_groups = "TDMA MAC Parameters:1-%d|Universal PHY Parameters:%d-%d" % (
len(_confmatrix_mac), len(_confmatrix_mac) + 1, len(config_matrix))
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
that file also. Otherwise the WLAN-wide nXXemane_tdmanem.xml,
nXXemane_tdmamac.xml, nXXemane_tdmaphy.xml are used.
'''
values = e.getifcconfig(self.objid, self._name,
self.getdefaultvalues(), ifc)
"""
values = e.getifcconfig(self.object_id, self.name, self.getdefaultvalues(), ifc)
if values is None:
return
nemdoc = e.xmldoc("nem")
nem = nemdoc.getElementsByTagName("nem").pop()
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.setAttribute("definition", self.macxmlname(ifc))
nem.appendChild(mactag)
@ -105,10 +99,8 @@ class EmaneTdmaModel(EmaneModel):
mac.setAttribute("name", "TDMA MAC")
mac.setAttribute("library", "tdmaeventschedulerradiomodel")
# append MAC options to macdoc
map(lambda n: mac.appendChild(e.xmlparam(macdoc, n, \
self.valueof(n, values))), macnames)
map(lambda n: mac.appendChild(e.xmlparam(macdoc, n, self.valueof(n, values))), macnames)
e.xmlwrite(macdoc, self.macxmlname(ifc))
phydoc = EmaneUniversalModel.getphydoc(e, self, values, phynames)
e.xmlwrite(phydoc, self.phyxmlname(ifc))

View file

@ -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
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:
from emanesh.events import EventService
except:
pass
from core.api import coreapi
from core.constants import *
from emane import Emane, EmaneModel
except ImportError:
logger.error("error importing emanesh")
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.
'''
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"
_xmllibrary = "universalphylayer"
# universal PHY parameters
_confmatrix_base = [
("bandwidth", coreapi.CONF_DATA_TYPE_UINT64, '1M',
("bandwidth", ConfigDataTypes.UINT64.value, '1M',
'', 'rf bandwidth (hz)'),
("frequency", coreapi.CONF_DATA_TYPE_UINT64, '2.347G',
("frequency", ConfigDataTypes.UINT64.value, '2.347G',
'', 'frequency (Hz)'),
("frequencyofinterest", coreapi.CONF_DATA_TYPE_UINT64, '2.347G',
("frequencyofinterest", ConfigDataTypes.UINT64.value, '2.347G',
'', 'frequency of interest (Hz)'),
("subid", coreapi.CONF_DATA_TYPE_UINT16, '1',
("subid", ConfigDataTypes.UINT16.value, '1',
'', 'subid'),
("systemnoisefigure", coreapi.CONF_DATA_TYPE_FLOAT, '4.0',
("systemnoisefigure", ConfigDataTypes.FLOAT.value, '4.0',
'', 'system noise figure (dB)'),
("txpower", coreapi.CONF_DATA_TYPE_FLOAT, '0.0',
("txpower", ConfigDataTypes.FLOAT.value, '0.0',
'', 'transmit power (dBm)'),
]
_confmatrix_081 = [
("antennagain", coreapi.CONF_DATA_TYPE_FLOAT, '0.0',
("antennagain", ConfigDataTypes.FLOAT.value, '0.0',
'', 'antenna gain (dBi)'),
("antennaazimuth", coreapi.CONF_DATA_TYPE_FLOAT, '0.0',
("antennaazimuth", ConfigDataTypes.FLOAT.value, '0.0',
'', 'antenna azimuth (deg)'),
("antennaelevation", coreapi.CONF_DATA_TYPE_FLOAT, '0.0',
("antennaelevation", ConfigDataTypes.FLOAT.value, '0.0',
'', 'antenna elevation (deg)'),
("antennaprofileid", coreapi.CONF_DATA_TYPE_STRING, '1',
("antennaprofileid", ConfigDataTypes.STRING.value, '1',
'', 'antenna profile ID'),
("antennaprofilemanifesturi", coreapi.CONF_DATA_TYPE_STRING, '',
("antennaprofilemanifesturi", ConfigDataTypes.STRING.value, '',
'', 'antenna profile manifest URI'),
("antennaprofileenable", coreapi.CONF_DATA_TYPE_BOOL, '0',
("antennaprofileenable", ConfigDataTypes.BOOL.value, '0',
'On,Off', 'antenna profile mode'),
("defaultconnectivitymode", coreapi.CONF_DATA_TYPE_BOOL, '1',
("defaultconnectivitymode", ConfigDataTypes.BOOL.value, '1',
'On,Off', 'default connectivity'),
("frequencyofinterestfilterenable", coreapi.CONF_DATA_TYPE_BOOL, '1',
("frequencyofinterestfilterenable", ConfigDataTypes.BOOL.value, '1',
'On,Off', 'frequency of interest filter enable'),
("noiseprocessingmode", coreapi.CONF_DATA_TYPE_BOOL, '0',
("noiseprocessingmode", ConfigDataTypes.BOOL.value, '0',
'On,Off', 'enable noise processing'),
("pathlossmode", coreapi.CONF_DATA_TYPE_STRING, '2ray',
("pathlossmode", ConfigDataTypes.STRING.value, '2ray',
'pathloss,2ray,freespace', 'path loss mode'),
]
_confmatrix_091 = [
("fixedantennagain", coreapi.CONF_DATA_TYPE_FLOAT, '0.0',
("fixedantennagain", ConfigDataTypes.FLOAT.value, '0.0',
'', 'antenna gain (dBi)'),
("fixedantennagainenable", coreapi.CONF_DATA_TYPE_BOOL, '1',
("fixedantennagainenable", ConfigDataTypes.BOOL.value, '1',
'On,Off', 'enable fixed antenna gain'),
("noisemode", coreapi.CONF_DATA_TYPE_STRING, 'none',
("noisemode", ConfigDataTypes.STRING.value, 'none',
'none,all,outofband', 'noise processing mode'),
("noisebinsize", coreapi.CONF_DATA_TYPE_UINT64, '20',
("noisebinsize", ConfigDataTypes.UINT64.value, '20',
'', 'noise bin size in microseconds'),
("propagationmodel", coreapi.CONF_DATA_TYPE_STRING, '2ray',
("propagationmodel", ConfigDataTypes.STRING.value, '2ray',
'precomputed,2ray,freespace', 'path loss mode'),
]
if Emane.version >= Emane.EMANE091:
_confmatrix = _confmatrix_base + _confmatrix_091
if emane.VERSION >= emane.EMANE091:
config_matrix = _confmatrix_base + _confmatrix_091
else:
_confmatrix = _confmatrix_base + _confmatrix_081
config_matrix = _confmatrix_base + _confmatrix_081
# old parameters
_confmatrix_ver074 = [
("antennaazimuthbeamwidth", coreapi.CONF_DATA_TYPE_FLOAT, '360.0',
("antennaazimuthbeamwidth", ConfigDataTypes.FLOAT.value, '360.0',
'', 'azimith beam width (deg)'),
("antennaelevationbeamwidth", coreapi.CONF_DATA_TYPE_FLOAT, '180.0',
("antennaelevationbeamwidth", ConfigDataTypes.FLOAT.value, '180.0',
'', 'elevation beam width (deg)'),
("antennatype", coreapi.CONF_DATA_TYPE_STRING, 'omnidirectional',
("antennatype", ConfigDataTypes.STRING.value, 'omnidirectional',
'omnidirectional,unidirectional', 'antenna type'),
]
@ -102,16 +100,15 @@ class EmaneUniversalModel(EmaneModel):
"antennaprofilemanifesturi",
"frequencyofinterestfilterenable")
@classmethod
def getphydoc(cls, e, mac, values, phynames):
phydoc = e.xmldoc("phy")
phy = phydoc.getElementsByTagName("phy").pop()
phy.setAttribute("name", cls._xmlname)
if e.version < e.EMANE091:
if emane.VERSION < emane.EMANE091:
phy.setAttribute("library", cls._xmllibrary)
# 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()
values = list(values)
phynames = list(phynames)
@ -128,7 +125,7 @@ class EmaneUniversalModel(EmaneModel):
phy.appendChild(e.xmlparam(phydoc, old[0], old[2]))
frequencies = None
if e.version >= e.EMANE091:
if emane.VERSION >= emane.EMANE091:
name = "frequencyofinterest"
value = mac.valueof(name, values)
frequencies = cls.valuestrtoparamlist(phydoc, name, value)
@ -137,10 +134,7 @@ class EmaneUniversalModel(EmaneModel):
phynames.remove("frequencyofinterest")
# append all PHY options to phydoc
map( lambda n: phy.appendChild(e.xmlparam(phydoc, n, \
mac.valueof(n, values))), phynames)
map(lambda n: phy.appendChild(e.xmlparam(phydoc, n, mac.valueof(n, values))), phynames)
if frequencies:
phy.appendChild(frequencies)
return phydoc

315
daemon/core/enumerations.py Normal file
View file

@ -0,0 +1,315 @@
"""
Contains all legacy enumerations for interacting with legacy CORE code.
"""
from enum import Enum
CORE_API_VERSION = "1.23"
CORE_API_PORT = 4038
class MessageTypes(Enum):
"""
CORE message types.
"""
NODE = 0x01
LINK = 0x02
EXECUTE = 0x03
REGISTER = 0x04
CONFIG = 0x05
FILE = 0x06
INTERFACE = 0x07
EVENT = 0x08
SESSION = 0x09
EXCEPTION = 0x0A
class MessageFlags(Enum):
"""
CORE message flags.
"""
ADD = 0x01
DELETE = 0x02
CRI = 0x04
LOCAL = 0x08
STRING = 0x10
TEXT = 0x20
TTY = 0x40
class NodeTlvs(Enum):
"""
Node type, length, value enumerations.
"""
NUMBER = 0x01
TYPE = 0x02
NAME = 0x03
IP_ADDRESS = 0x04
MAC_ADDRESS = 0x05
IP6_ADDRESS = 0x06
MODEL = 0x07
EMULATION_SERVER = 0x08
SESSION = 0x0A
X_POSITION = 0x20
Y_POSITION = 0x21
CANVAS = 0x22
EMULATION_ID = 0x23
NETWORK_ID = 0x24
SERVICES = 0x25
LATITUDE = 0x30
LONGITUDE = 0x31
ALTITUDE = 0x32
ICON = 0x42
OPAQUE = 0x50
class NodeTypes(Enum):
"""
Node types.
"""
DEFAULT = 0
PHYSICAL = 1
XEN = 2
TBD = 3
SWITCH = 4
HUB = 5
WIRELESS_LAN = 6
RJ45 = 7
TUNNEL = 8
KTUNNEL = 9
EMANE = 10
TAP_BRIDGE = 11
PEER_TO_PEER = 12
CONTROL_NET = 13
EMANE_NET = 14
class Rj45Models(Enum):
"""
RJ45 model types.
"""
LINKED = 0
WIRELESS = 1
INSTALLED = 2
# Link Message TLV Types
class LinkTlvs(Enum):
"""
Link type, length, value enumerations.
"""
N1_NUMBER = 0x01
N2_NUMBER = 0x02
DELAY = 0x03
BANDWIDTH = 0x04
PER = 0x05
DUP = 0x06
JITTER = 0x07
MER = 0x08
BURST = 0x09
SESSION = 0x0A
MBURST = 0x10
TYPE = 0x20
GUI_ATTRIBUTES = 0x21
UNIDIRECTIONAL = 0x22
EMULATION_ID = 0x23
NETWORK_ID = 0x24
KEY = 0x25
INTERFACE1_NUMBER = 0x30
INTERFACE1_IP4 = 0x31
INTERFACE1_IP4_MASK = 0x32
INTERFACE1_MAC = 0x33
INTERFACE1_IP6 = 0x34
INTERFACE1_IP6_MASK = 0x35
INTERFACE2_NUMBER = 0x36
INTERFACE2_IP4 = 0x37
INTERFACE2_IP4_MASK = 0x38
INTERFACE2_MAC = 0x39
INTERFACE2_IP6 = 0x40
INTERFACE2_IP6_MASK = 0x41
INTERFACE1_NAME = 0x42
INTERFACE2_NAME = 0x43
OPAQUE = 0x50
class LinkTypes(Enum):
"""
Link types.
"""
WIRELESS = 0
WIRED = 1
class ExecuteTlvs(Enum):
"""
Execute type, length, value enumerations.
"""
NODE = 0x01
NUMBER = 0x02
TIME = 0x03
COMMAND = 0x04
RESULT = 0x05
STATUS = 0x06
SESSION = 0x0A
class RegisterTlvs(Enum):
"""
Register type, length, value enumerations.
"""
WIRELESS = 0x01
MOBILITY = 0x02
UTILITY = 0x03
EXECUTE_SERVER = 0x04
GUI = 0x05
EMULATION_SERVER = 0x06
SESSION = 0x0A
class ConfigTlvs(Enum):
"""
Configuration type, length, value enumerations.
"""
NODE = 0x01
OBJECT = 0x02
TYPE = 0x03
DATA_TYPES = 0x04
VALUES = 0x05
CAPTIONS = 0x06
BITMAP = 0x07
POSSIBLE_VALUES = 0x08
GROUPS = 0x09
SESSION = 0x0A
INTERFACE_NUMBER = 0x0B
NETWORK_ID = 0x24
OPAQUE = 0x50
class ConfigFlags(Enum):
"""
Configuration flags.
"""
NONE = 0x00
REQUEST = 0x01
UPDATE = 0x02
RESET = 0x03
class ConfigDataTypes(Enum):
"""
Configuration data types.
"""
UINT8 = 0x01
UINT16 = 0x02
UINT32 = 0x03
UINT64 = 0x04
INT8 = 0x05
INT16 = 0x06
INT32 = 0x07
INT64 = 0x08
FLOAT = 0x09
STRING = 0x0A
BOOL = 0x0B
class FileTlvs(Enum):
"""
File type, length, value enumerations.
"""
NODE = 0x01
NAME = 0x02
MODE = 0x03
NUMBER = 0x04
TYPE = 0x05
SOURCE_NAME = 0x06
SESSION = 0x0A
DATA = 0x10
COMPRESSED_DATA = 0x11
class InterfaceTlvs(Enum):
"""
Interface type, length, value enumerations.
"""
NODE = 0x01
NUMBER = 0x02
NAME = 0x03
IP_ADDRESS = 0x04
MASK = 0x05
MAC_ADDRESS = 0x06
IP6_ADDRESS = 0x07
IP6_MASK = 0x08
TYPE = 0x09
SESSION = 0x0A
STATE = 0x0B
EMULATION_ID = 0x23
NETWORK_ID = 0x24
class EventTlvs(Enum):
"""
Event type, length, value enumerations.
"""
NODE = 0x01
TYPE = 0x02
NAME = 0x03
DATA = 0x04
TIME = 0x05
SESSION = 0x0A
class EventTypes(Enum):
"""
Event types.
"""
NONE = 0
DEFINITION_STATE = 1
CONFIGURATION_STATE = 2
INSTANTIATION_STATE = 3
RUNTIME_STATE = 4
DATACOLLECT_STATE = 5
SHUTDOWN_STATE = 6
START = 7
STOP = 8
PAUSE = 9
RESTART = 10
FILE_OPEN = 11
FILE_SAVE = 12
SCHEDULED = 13
RECONFIGURE = 14
# Session Message TLV Types
class SessionTlvs(Enum):
NUMBER = 0x01
NAME = 0x02
FILE = 0x03
NODE_COUNT = 0x04
DATE = 0x05
THUMB = 0x06
USER = 0x07
OPAQUE = 0x0A
class ExceptionTlvs(Enum):
"""
Exception type, length, value enumerations.
"""
NODE = 0x01
SESSION = 0x02
LEVEL = 0x03
SOURCE = 0x04
DATE = 0x05
TEXT = 0x06
OPAQUE = 0x0A
class ExceptionLevels(Enum):
"""
Exception levels.
"""
NONE = 0
FATAL = 1
ERROR = 2
WARNING = 3
NOTICE = 4

View file

@ -1,42 +1,47 @@
#
# 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
Session object. Provides conversions between Cartesian and geographic coordinate
systems. Depends on utm contributed module, from
https://pypi.python.org/pypi/utm (version 0.3.0).
'''
"""
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
logger = log.get_logger(__name__)
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
order to convert between X,Y and geo coordinates.
TODO: this could be updated to use more generic
Configurable/ConfigurableManager code like other Session objects
'''
_name = "location"
_type = coreapi.CORE_TLV_REG_UTILITY
"""
name = "location"
config_type = RegisterTlvs.UTILITY.value
def __init__(self, session):
ConfigurableManager.__init__(self, session)
def __init__(self):
"""
Creates a MobilityManager instance.
:param core.session.Session session: session this manager is tied to
:return: nothing
"""
ConfigurableManager.__init__(self)
self.reset()
self.zonemap = {}
for n, l in utm.ZONE_LETTERS:
self.zonemap[l] = n
def reset(self):
''' Reset to initial state.
'''
"""
Reset to initial state.
"""
# (x, y, z) coordinates of the point given by self.refgeo
self.refxyz = (0.0, 0.0, 0.0)
# decimal latitude, longitude, and altitude at the point (x, y, z)
@ -46,66 +51,76 @@ class CoreLocation(ConfigurableManager):
# cached distance to refpt in other zones
self.zoneshifts = {}
def configure_values(self, msg, values):
''' Receive configuration message for setting the reference point
def configure_values(self, config_data):
"""
Receive configuration message for setting the reference point
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:
self.session.info("location data missing")
logger.info("location data missing")
return None
values = values.split('|')
# Cartesian coordinate reference point
refx, refy = map(lambda x: float(x), values[0:2])
refz = 0.0
self.refxyz = (refx, refy, refz)
# Geographic reference point
lat,long,alt = map(lambda x: float(x), values[2:5])
self.setrefgeo(lat, long, alt)
lat, lon, alt = map(lambda x: float(x), values[2:5])
self.setrefgeo(lat, lon, alt)
self.refscale = float(values[5])
self.session.info("location configured: (%.2f,%.2f,%.2f) = "
"(%.5f,%.5f,%.5f) scale=%.2f" %
logger.info("location configured: (%.2f,%.2f,%.2f) = (%.5f,%.5f,%.5f) scale=%.2f" %
(self.refxyz[0], self.refxyz[1], self.refxyz[2], self.refgeo[0],
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]))
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
100 pixels = s meters.
'''
"""
return (val / 100.0) * self.refscale
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
100 pixels = s meters.
'''
"""
if self.refscale == 0.0:
return 0.0
return 100.0 * (val / self.refscale)
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.
'''
"""
self.refgeo = (lat, lon, alt)
# easting, northing, zone
(e, n, zonen, zonel) = utm.from_latlon(lat, lon)
self.refutm = ((zonen, zonel), e, n, alt)
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
and scale.
'''
"""
# shift (x,y,z) over to reference point (x,y,z)
x = x - self.refxyz[0]
y = -(y - self.refxyz[1])
if z is None:
z = self.refxyz[2]
else:
z = z - self.refxyz[2]
z -= self.refxyz[2]
# use UTM coordinates since unit is meters
zone = self.refutm[0]
if zone == "":
@ -117,23 +132,23 @@ class CoreLocation(ConfigurableManager):
try:
lat, lon = utm.to_latlon(e, n, zone[0], zone[1])
except utm.OutOfRangeError:
self.info("UTM out of range error for e=%s n=%s zone=%s" \
"xyz=(%s,%s,%s)" % (e, n, zone, x, y, z))
logger.exception("UTM out of range error for n=%s zone=%s xyz=(%s,%s,%s)", n, zone, x, y, z)
(lat, lon) = self.refgeo[:2]
# self.info("getgeo(%s,%s,%s) e=%s n=%s zone=%s lat,lon,alt=" \
# "%.3f,%.3f,%.3f" % (x, y, z, e, n, zone, lat, lon, alt))
return (lat, lon, alt)
return 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
reference point and scale. Lat/lon is converted to UTM meter
coordinates, UTM zones are accounted for, and the scale turns
meters to pixels.
'''
"""
# convert lat/lon to UTM coordinates in meters
(e, n, zonen, zonel) = utm.from_latlon(lat, lon)
(rlat, rlon, ralt) = self.refgeo
e, n, zonen, zonel = utm.from_latlon(lat, lon)
rlat, rlon, ralt = self.refgeo
xshift = self.geteastingshift(zonen, zonel)
if xshift is None:
xm = e - self.refutm[1]
@ -150,16 +165,17 @@ class CoreLocation(ConfigurableManager):
x = self.m2px(xm) + self.refxyz[0]
y = -(self.m2px(ym) + self.refxyz[1])
z = self.m2px(zm) + self.refxyz[2]
return (x, y, z)
return x, y, z
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
may need to be shifted.
This picks a reference point in the same longitudinal band
(UTM zone number) as the provided zone, to calculate the shift in
meters for the x coordinate.
'''
"""
rzonen = int(self.refutm[0][0])
if zonen == rzonen:
return None # same zone number, no x shift required
@ -167,9 +183,9 @@ class CoreLocation(ConfigurableManager):
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
rlat, rlon, ralt = self.refgeo
lon2 = rlon + 6 * (zonen - rzonen) # ea. zone is 6deg band
(e2, n2, zonen2, zonel2) = utm.from_latlon(rlat, lon2) # ignore northing
e2, n2, zonen2, zonel2 = utm.from_latlon(rlat, lon2) # ignore northing
# NOTE: great circle distance used here, not reference ellipsoid!
xshift = utm.haversine(rlon, rlat, lon2, rlat) - e2
# cache the return value
@ -180,13 +196,14 @@ class CoreLocation(ConfigurableManager):
return xshift
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
may need to be shifted.
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
y coordinate.
'''
"""
rzonel = self.refutm[0][1]
if zonel == rzonel:
return None # same zone letter, no y shift required
@ -194,11 +211,11 @@ class CoreLocation(ConfigurableManager):
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
rlat, rlon, ralt = self.refgeo
# zonemap is used to calculate degrees difference between zone letters
latshift = self.zonemap[zonel] - self.zonemap[rzonel]
lat2 = rlat + latshift # ea. latitude band is 8deg high
(e2, n2, zonen2, zonel2) = utm.from_latlon(lat2, rlon)
e2, n2, zonen2, zonel2 = utm.from_latlon(lat2, rlon)
# NOTE: great circle distance used here, not reference ellipsoid
yshift = -(utm.haversine(rlon, rlat, rlon, lat2) + n2)
# cache the return value
@ -209,18 +226,19 @@ class CoreLocation(ConfigurableManager):
return yshift
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
different zone and the new zone if they do. Zone lettering is only
changed when the reference point is in the opposite hemisphere.
'''
"""
zone = self.refutm[0]
(rlat, rlon, ralt) = self.refgeo
rlat, rlon, ralt = self.refgeo
if e > 834000 or e < 166000:
num_zones = (int(e) - 166000) / (utm.R / 10)
# estimate number of zones to shift, E (positive) or W (negative)
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)
# after >3 zones away from refpt, the above estimate won't work
# (the above estimate could be improved)
@ -228,7 +246,7 @@ class CoreLocation(ConfigurableManager):
# move one more zone away
num_zones = (abs(num_zones) + 1) * (abs(num_zones) / num_zones)
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)
e = e - xshift
zone = (zonen2, zonel2)
@ -240,7 +258,4 @@ class CoreLocation(ConfigurableManager):
# refpt in southern hemisphere and we crossed north of equator
n -= 10000000
zone = (zone[0], 'N')
return (e, n, zone)
return e, n, zone

View file

@ -44,6 +44,7 @@ _ellipsoid = [
[23, "WGS-84", 6378137, 0.00669438]
]
# Reference ellipsoids derived from Peter H. Dana's website-
# http://www.utexas.edu/depts/grg/gcraft/notes/datum/elist.html
# Department of Geography, University of Texas at Austin
@ -84,10 +85,14 @@ def LLtoUTM(ReferenceEllipsoid, Lat, Long, zone = None):
# Special zones for Svalbard
if Lat >= 72.0 and Lat < 84.0:
if LongTemp >= 0.0 and LongTemp < 9.0:ZoneNumber = 31
elif LongTemp >= 9.0 and LongTemp < 21.0: ZoneNumber = 33
elif LongTemp >= 21.0 and LongTemp < 33.0: ZoneNumber = 35
elif LongTemp >= 33.0 and LongTemp < 42.0: ZoneNumber = 37
if LongTemp >= 0.0 and LongTemp < 9.0:
ZoneNumber = 31
elif LongTemp >= 9.0 and LongTemp < 21.0:
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
LongOriginRad = LongOrigin * _deg2rad
@ -132,27 +137,49 @@ def _UTMLetterDesignator(Lat):
latitude returns 'Z' if latitude is outside the UTM limits of 84N to 80S
Written by Chuck Gantz- chuck.gantz@globalstar.com"""
if 84 >= Lat >= 72: return 'X'
elif 72 > Lat >= 64: return 'W'
elif 64 > Lat >= 56: return 'V'
elif 56 > Lat >= 48: return 'U'
elif 48 > Lat >= 40: return 'T'
elif 40 > Lat >= 32: return 'S'
elif 32 > Lat >= 24: return 'R'
elif 24 > Lat >= 16: return 'Q'
elif 16 > Lat >= 8: return 'P'
elif 8 > Lat >= 0: return 'N'
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
if 84 >= Lat >= 72:
return 'X'
elif 72 > Lat >= 64:
return 'W'
elif 64 > Lat >= 56:
return 'V'
elif 56 > Lat >= 48:
return 'U'
elif 48 > Lat >= 40:
return 'T'
elif 40 > Lat >= 32:
return 'S'
elif 32 > Lat >= 24:
return 'R'
elif 24 > Lat >= 16:
return 'Q'
elif 16 > Lat >= 8:
return 'P'
elif 8 > Lat >= 0:
return 'N'
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,
# double& Lat, double& Long )
@ -187,7 +214,8 @@ Converted to Python by Russ Nelson <nelson@crynwr.com>"""
eccPrimeSquared = (eccSquared) / (1 - eccSquared)
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)
+ (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)
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)
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)
Long = LongOrigin + Long * _rad2deg
return (Lat, Long)
if __name__ == '__main__':
(z, e, n) = LLtoUTM(23, 45.00, -75.00)
print z, e, n
print UTMtoLL(23, n, e, z)

View file

@ -1,27 +1,24 @@
#
# 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.
'''
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):
'''\
"""
Based on threading.Timer but cancel() returns if the timer was
already running.
'''
"""
def __init__(self, interval, function, args=[], kwargs={}):
super(EventLoop.Timer, self).__init__()
super(Timer, self).__init__()
self.interval = interval
self.function = function
self.args = args
@ -30,10 +27,10 @@ class EventLoop(object):
self._running = threading.Lock()
def cancel(self):
'''\
"""
Stop the timer if it hasn't finished yet. Return False if
the timer was already running.
'''
"""
locked = self._running.acquire(False)
if locked:
self.finished.set()
@ -47,10 +44,11 @@ class EventLoop(object):
self.function(*self.args, **self.kwargs)
self.finished.set()
class Event(object):
def __init__(self, eventnum, time, func, *args, **kwds):
def __init__(self, eventnum, event_time, func, *args, **kwds):
self.eventnum = eventnum
self.time = time
self.time = event_time
self.func = func
self.args = args
self.kwds = kwds
@ -68,8 +66,11 @@ class EventLoop(object):
self.func(*self.args, **self.kwds)
def cancel(self):
self.canceled = True # XXX not thread-safe
# XXX not thread-safe
self.canceled = True
class EventLoop(object):
def __init__(self):
self.lock = threading.RLock()
self.queue = []
@ -103,7 +104,7 @@ class EventLoop(object):
return
delay = self.queue[0].time - time.time()
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.start()
@ -136,7 +137,7 @@ class EventLoop(object):
evtime = float(delaysec)
if self.running:
evtime += time.time()
event = self.Event(eventnum, evtime, func, *args, **kwds)
event = Event(eventnum, evtime, func, *args, **kwds)
if self.queue:
prevhead = self.queue[0]
@ -152,12 +153,13 @@ class EventLoop(object):
self.__schedule_event()
return event
def example():
loop = EventLoop()
def msg(arg):
delta = time.time() - loop.start
print delta, 'arg:', arg
logger.debug("%s arg: %s", delta, arg)
def repeat(interval, count):
count -= 1

View file

@ -1,34 +1,35 @@
#
# 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.
'''
"""
Helper objects for dealing with IPv4/v6 addresses.
"""
import random
import socket
import struct
import random
from socket import AF_INET
from socket import AF_INET6
AF_INET = socket.AF_INET
AF_INET6 = socket.AF_INET6
from core.misc import log
class MacAddr(object):
def __init__(self, addr):
self.addr = addr
logger = log.get_logger(__name__)
class MacAddress(object):
def __init__(self, address):
self.addr = address
def __str__(self):
return ":".join(map(lambda x: ("%02x" % ord(x)), self.addr))
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
def to_link_local(self):
"""
Convert the MAC address to a IPv6 link-local address, using EUI 48
to EUI 64 conversion process per RFC 5342.
'''
:return: ip address object
:rtype: IpAddress
"""
if not self.addr:
return IPAddr.fromstring("::")
return IpAddress.from_string("::")
tmp = struct.unpack("!Q", '\x00\x00' + self.addr)[0]
nic = long(tmp) & 0x000000FFFFFFL
oui = long(tmp) & 0xFFFFFF000000L
@ -36,10 +37,10 @@ class MacAddr(object):
oui ^= 0x020000000000L
# append EUI-48 octets
oui = (oui << 16) | 0xFFFE000000L
return IPAddr(AF_INET6, struct.pack("!QQ", 0xfe80 << 48, oui | nic))
return IpAddress(AF_INET6, struct.pack("!QQ", 0xfe80 << 48, oui | nic))
@classmethod
def fromstring(cls, s):
def from_string(cls, s):
addr = "".join(map(lambda x: chr(int(x, 16)), s.split(":")))
return cls(addr)
@ -50,18 +51,19 @@ class MacAddr(object):
tmpbytes = struct.pack("!Q", tmp)
return cls(tmpbytes[2:])
class IPAddr(object):
def __init__(self, af, addr):
class IpAddress(object):
def __init__(self, af, address):
# check if (af, addr) is valid
if not socket.inet_ntop(af, addr):
if not socket.inet_ntop(af, address):
raise ValueError, "invalid af/addr"
self.af = af
self.addr = addr
self.addr = address
def isIPv4(self):
def is_ipv4(self):
return self.af == AF_INET
def isIPv6(self):
def is_ipv6(self):
return self.af == AF_INET6
def __str__(self):
@ -70,14 +72,17 @@ class IPAddr(object):
def __eq__(self, other):
try:
return other.af == self.af and other.addr == self.addr
except:
except AttributeError:
logger.exception("error during equals compare")
return False
def __add__(self, other):
try:
carry = int(other)
except:
except ValueError:
logger.exception("error during addition")
return NotImplemented
tmp = map(lambda x: ord(x), self.addr)
for i in xrange(len(tmp) - 1, -1, -1):
x = tmp[i] + carry
@ -91,27 +96,26 @@ class IPAddr(object):
def __sub__(self, other):
try:
tmp = -int(other)
except:
except ValueError:
logger.exception("error during subtraction")
return NotImplemented
return self.__add__(tmp)
@classmethod
def fromstring(cls, s):
def from_string(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
'''
def to_int(s):
"""
convert IPv4 string to 32-bit integer
"""
bin = socket.inet_pton(AF_INET, s)
return(struct.unpack('!I', bin)[0])
return struct.unpack('!I', bin)[0]
class IPPrefix(object):
class IpPrefix(object):
def __init__(self, af, prefixstr):
"prefixstr format: address/prefixlen"
tmp = prefixstr.split("/")
@ -139,26 +143,21 @@ class IPPrefix(object):
self.prefix = self.prefix[:i] + prefix
def __str__(self):
return "%s/%s" % (socket.inet_ntop(self.af, self.prefix),
self.prefixlen)
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
return other.af == self.af and other.prefixlen == self.prefixlen and other.prefix == self.prefix
def __add__(self, other):
try:
tmp = int(other)
except:
except ValueError:
logger.exception("error during addition")
return NotImplemented
a = IPAddr(self.af, self.prefix) + \
(tmp << (self.addrlen - self.prefixlen))
a = IpAddress(self.af, self.prefix) + (tmp << (self.addrlen - self.prefixlen))
prefixstr = "%s/%s" % (a, self.prefixlen)
if self.__class__ == IPPrefix:
if self.__class__ == IpPrefix:
return self.__class__(self.af, prefixstr)
else:
return self.__class__(prefixstr)
@ -166,65 +165,74 @@ class IPPrefix(object):
def __sub__(self, other):
try:
tmp = -int(other)
except:
except ValueError:
logger.exception("error during subtraction")
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 \
if tmp in [-1, 0, 1] and self.addrlen == self.prefixlen:
return IpAddress(self.af, self.prefix)
if tmp == 0 or tmp > (1 << (self.addrlen - self.prefixlen)) - 1 or \
(self.af == AF_INET and tmp == (1 << (self.addrlen - self.prefixlen)) - 1):
raise ValueError, "invalid hostid for prefix %s: %s" % (self, hostid)
raise ValueError("invalid hostid for prefix %s: %s" % (self, hostid))
addr = ""
prefix_endpoint = -1
for i in xrange(-1, -(self.addrlen >> 3) - 1, -1):
prefix_endpoint = i
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)
addr = self.prefix[:prefix_endpoint] + addr
return IpAddress(self.af, addr)
def minaddr(self):
def min_addr(self):
return self.addr(1)
def maxaddr(self):
def max_addr(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):
def num_addr(self):
return max(0, (1 << (self.addrlen - self.prefixlen)) - 2)
def prefixstr(self):
def prefix_str(self):
return "%s" % socket.inet_ntop(self.af, self.prefix)
def netmaskstr(self):
def netmask_str(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__()
return IpAddress(af=AF_INET, address=netmaskbytes).__str__()
class IPv4Prefix(IPPrefix):
class Ipv4Prefix(IpPrefix):
def __init__(self, prefixstr):
IPPrefix.__init__(self, AF_INET, prefixstr)
IpPrefix.__init__(self, AF_INET, prefixstr)
class IPv6Prefix(IPPrefix):
class Ipv6Prefix(IpPrefix):
def __init__(self, prefixstr):
IPPrefix.__init__(self, AF_INET6, prefixstr)
IpPrefix.__init__(self, AF_INET6, prefixstr)
def isIPAddress(af, addrstr):
def is_ip_address(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)
def is_ipv4_address(addrstr):
return is_ip_address(AF_INET, addrstr)
def is_ipv6_address(addrstr):
return is_ip_address(AF_INET6, addrstr)

35
daemon/core/misc/log.py Normal file
View file

@ -0,0 +1,35 @@
"""
Convenience methods to setup logging.
"""
import logging
_LOG_LEVEL = logging.INFO
_LOG_FORMAT = '%(levelname)-7s %(asctime)s %(name)-15s %(funcName)-15s %(lineno)-4d: %(message)s'
_INITIAL = True
def setup(level=_LOG_LEVEL, log_format=_LOG_FORMAT):
"""
Configure a logging with a basic configuration, output to console.
:param logging.LEVEL level: level for logger, defaults to module defined format
:param int log_format: format for logger, default to DEBUG
:return: nothing
"""
logging.basicConfig(level=level, format=log_format)
def get_logger(name):
"""
Retrieve a logger for logging.
:param str name: name for logger to retrieve
:return: logging.logger
"""
global _INITIAL
if _INITIAL:
setup()
_INITIAL = False
return logging.getLogger(name)

View file

@ -0,0 +1,44 @@
from core.emane.nodes import EmaneNet
from core.emane.nodes import EmaneNode
from core.enumerations import NodeTypes
from core.netns import nodes
from core.netns import openvswitch
from core.netns.vnet import GreTapBridge
from core.phys import pnodes
from core.xen import xen
CLASSIC_NODES = {
NodeTypes.DEFAULT: nodes.CoreNode,
NodeTypes.PHYSICAL: pnodes.PhysicalNode,
NodeTypes.XEN: xen.XenNode,
NodeTypes.TBD: None,
NodeTypes.SWITCH: nodes.SwitchNode,
NodeTypes.HUB: nodes.HubNode,
NodeTypes.WIRELESS_LAN: nodes.WlanNode,
NodeTypes.RJ45: nodes.RJ45Node,
NodeTypes.TUNNEL: nodes.TunnelNode,
NodeTypes.KTUNNEL: None,
NodeTypes.EMANE: EmaneNode,
NodeTypes.EMANE_NET: EmaneNet,
NodeTypes.TAP_BRIDGE: GreTapBridge,
NodeTypes.PEER_TO_PEER: nodes.PtpNet,
NodeTypes.CONTROL_NET: nodes.CtrlNet
}
OVS_NODES = {
NodeTypes.DEFAULT: nodes.CoreNode,
NodeTypes.PHYSICAL: pnodes.PhysicalNode,
NodeTypes.XEN: xen.XenNode,
NodeTypes.TBD: None,
NodeTypes.SWITCH: openvswitch.OvsSwitchNode,
NodeTypes.HUB: openvswitch.OvsHubNode,
NodeTypes.WIRELESS_LAN: openvswitch.OvsWlanNode,
NodeTypes.RJ45: nodes.RJ45Node,
NodeTypes.TUNNEL: openvswitch.OvsTunnelNode,
NodeTypes.KTUNNEL: None,
NodeTypes.EMANE: EmaneNode,
NodeTypes.EMANE_NET: EmaneNet,
NodeTypes.TAP_BRIDGE: openvswitch.OvsGreTapBridge,
NodeTypes.PEER_TO_PEER: openvswitch.OvsPtpNet,
NodeTypes.CONTROL_NET: openvswitch.OvsCtrlNet
}

View file

@ -0,0 +1,41 @@
"""
Serves as a global point for storing and retrieving node types needed during simulation.
"""
import pprint
from core.misc import log
logger = log.get_logger(__name__)
_NODE_MAP = None
def _convert_map(x, y):
x[y[0].name] = y[1]
return x
def set_node_map(node_map):
global _NODE_MAP
print_map = reduce(lambda x, y: _convert_map(x, y), node_map.items(), {})
logger.info("setting node class map: \n%s", pprint.pformat(print_map, indent=4))
_NODE_MAP = node_map
def get_node_class(node_type):
global _NODE_MAP
return _NODE_MAP[node_type]
def is_node(obj, node_types):
type_classes = []
if isinstance(node_types, (tuple, list)):
for node_type in node_types:
type_class = get_node_class(node_type)
type_classes.append(type_class)
else:
type_class = get_node_class(node_types)
type_classes.append(type_class)
return isinstance(obj, tuple(type_classes))

View file

@ -9,20 +9,22 @@
quagga.py: helper class for generating Quagga configuration.
'''
import os.path
from string import Template
def maketuple(obj):
if hasattr(obj, "__iter__"):
return tuple(obj)
else:
return (obj,)
class NetIf(object):
def __init__(self, name, addrlist=[]):
self.name = name
self.addrlist = addrlist
class Conf(object):
def __init__(self, **kwds):
self.kwds = kwds
@ -33,6 +35,7 @@ class Conf(object):
tmp = tmp[:-1]
return tmp
class QuaggaOSPF6Interface(Conf):
AF_IPV6_ID = 0
AF_IPV4_ID = 65
@ -57,6 +60,7 @@ interface $interface
def __init__(self, netif, instanceid=AF_IPV4_ID,
network="manet-designated-router", **kwds):
self.netif = netif
def addrstr(x):
if x.find(".") >= 0:
return "ip address %s" % x
@ -64,6 +68,7 @@ interface $interface
return "ipv6 address %s" % x
else:
raise Value, "invalid address: %s", x
addr = "\n ".join(map(addrstr, netif.addrlist))
self.instanceid = instanceid
@ -74,8 +79,8 @@ interface $interface
def name(self):
return self.netif.name
class QuaggaOSPF6(Conf):
class QuaggaOSPF6(Conf):
template = Template("""\
$interfaces
!

View file

@ -0,0 +1,44 @@
from core.misc import log
logger = log.get_logger(__name__)
def pack_values(clazz, packers):
"""
Pack values for a given legacy class.
:param class clazz: class that will provide a pack method
:param list packers: a list of tuples that are used to pack values and transform them
:return: packed data string of all values
"""
# iterate through tuples of values to pack
data = ""
for packer in packers:
# check if a transformer was provided for valid values
transformer = None
if len(packer) == 2:
tlv_type, value = packer
elif len(packer) == 3:
tlv_type, value, transformer = packer
else:
raise RuntimeError("packer had more than 3 arguments")
# convert unicode to normal str for packing
if isinstance(value, unicode):
value = str(value)
# only pack actual values and avoid packing empty strings
# protobuf defaults to empty strings and does no imply a value to set
if value is None or (isinstance(value, str) and not value):
continue
# transform values as needed
if transformer:
value = transformer(value)
# pack and add to existing data
logger.info("packing: %s - %s", tlv_type, value)
data += clazz.pack(tlv_type.value, value)
return data

View file

@ -1,31 +1,37 @@
#
# CORE
# 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>
#
'''
utils.py: miscellaneous utility functions, wrappers around some subprocess
procedures.
'''
"""
Miscellaneous utility functions, wrappers around some subprocess procedures.
"""
import ast
import os
import subprocess
import subprocess, os, ast
import fcntl
from core.misc import log
logger = log.get_logger(__name__)
def closeonexec(fd):
fdflags = fcntl.fcntl(fd, fcntl.F_GETFD)
fcntl.fcntl(fd, fcntl.F_SETFD, fdflags | fcntl.FD_CLOEXEC)
def checkexec(execlist):
for bin in execlist:
if which(bin) is None:
raise EnvironmentError, "executable not found: %s" % bin
def check_executables(executables):
for executable in executables:
if not (os.path.isfile(executable) and os.access(executable, os.X_OK)):
raise EnvironmentError("executable not found: %s" % executable)
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
:param str program: program to check for
:return: path if it exists, none otherwise
"""
def is_exe(fpath):
return os.path.isfile(fpath) and os.access(fpath, os.X_OK)
@ -42,17 +48,20 @@ def which(program):
return None
def ensurepath(pathlist):
searchpath = os.environ["PATH"].split(":")
for p in set(pathlist):
if p not in searchpath:
os.environ["PATH"] += ":" + p
def maketuple(obj):
if hasattr(obj, "__iter__"):
return tuple(obj)
else:
return (obj,)
return obj,
def maketuplefromstr(s, type):
s.replace('\\', '\\\\')
@ -66,56 +75,62 @@ def maketuplefromstr(s, type):
# r = r[:-1]
# return r
def call(*args, **kwds):
return subprocess.call(*args, **kwds)
def mutecall(*args, **kwds):
kwds["stdout"] = open(os.devnull, "w")
kwds["stderr"] = subprocess.STDOUT
return call(*args, **kwds)
return subprocess.call(*args, **kwds)
def check_call(*args, **kwds):
return subprocess.check_call(*args, **kwds)
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):
return subprocess.Popen(*args, **kwds).pid
def mutespawn(*args, **kwds):
kwds["stdout"] = open(os.devnull, "w")
kwds["stderr"] = subprocess.STDOUT
return subprocess.Popen(*args, **kwds).pid
def detachinit():
if os.fork():
os._exit(0) # parent exits
# parent exits
os._exit(0)
os.setsid()
def detach(*args, **kwds):
kwds["preexec_fn"] = detachinit
return subprocess.Popen(*args, **kwds).pid
def mutedetach(*args, **kwds):
kwds["preexec_fn"] = detachinit
kwds["stdout"] = open(os.devnull, "w")
kwds["stderr"] = subprocess.STDOUT
return subprocess.Popen(*args, **kwds).pid
def cmdresult(args):
''' Execute a command on the host and return a tuple containing the
"""
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.
'''
"""
cmdid = subprocess.Popen(args, stdin=open(os.devnull, 'r'),
stdout=subprocess.PIPE,
stderr=subprocess.STDOUT)
result, err = cmdid.communicate() # err will always be None
# err will always be None
result, err = cmdid.communicate()
status = cmdid.wait()
return (status, result)
return status, result
def hexdump(s, bytes_per_word=2, words_per_line=8):
dump = ""
@ -132,19 +147,24 @@ def hexdump(s, bytes_per_word = 2, words_per_line = 8):
count += len(line)
return dump[:-1]
def filemunge(pathname, header, text):
''' Insert text at the end of a file, surrounded by header comments.
'''
filedemunge(pathname, header) # prevent duplicates
"""
Insert text at the end of a file, surrounded by header comments.
"""
# prevent duplicates
filedemunge(pathname, header)
f = open(pathname, 'a')
f.write("# BEGIN %s\n" % header)
f.write(text)
f.write("# END %s\n" % header)
f.close()
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')
lines = f.readlines()
f.close()
@ -162,9 +182,11 @@ def filedemunge(pathname, header):
f.write("".join(lines))
f.close()
def expandcorepath(pathname, session=None, node=None):
''' Expand a file path given session information.
'''
"""
Expand a file path given session information.
"""
if session is not None:
pathname = pathname.replace('~', "/home/%s" % session.user)
pathname = pathname.replace('%SESSION%', str(session.sessionid))
@ -175,19 +197,23 @@ def expandcorepath(pathname, session=None, node=None):
pathname = pathname.replace('%NODENAME%', node.name)
return pathname
def sysctldevname(devname):
''' Translate a device name to the name used with sysctl.
'''
"""
Translate a device name to the name used with sysctl.
"""
if devname is None:
return None
return devname.replace(".", "/")
def daemonize(rootdir="/", umask=0, close_fds=False, dontclose=(),
stdin=os.devnull, stdout=os.devnull, stderr=os.devnull,
stdoutmode=0644, stderrmode=0644, pidfilename=None,
defaultmaxfd=1024):
''' Run the background process as a daemon.
'''
"""
Run the background process as a daemon.
"""
if not hasattr(dontclose, "__contains__"):
if not isinstance(dontclose, int):
raise TypeError, "dontclose must be an integer"
@ -206,7 +232,7 @@ def daemonize(rootdir = "/", umask = 0, close_fds = False, dontclose = (),
fd = os.open(stdout, os.O_WRONLY | os.O_CREAT | os.O_APPEND,
stdoutmode)
os.dup2(fd, 1)
if (stdout == stderr):
if stdout == stderr:
os.dup2(1, 2)
os.close(fd)
# redirect stderr
@ -216,7 +242,8 @@ def daemonize(rootdir = "/", umask = 0, close_fds = False, dontclose = (),
os.dup2(fd, 2)
os.close(fd)
if os.fork():
os._exit(0) # parent exits
# parent exits
os._exit(0)
os.setsid()
pid = os.fork()
if pid:
@ -225,9 +252,10 @@ def daemonize(rootdir = "/", umask = 0, close_fds = False, dontclose = (),
f = open(pidfilename, "w")
f.write("%s\n" % pid)
f.close()
except:
pass
os._exit(0) # parent exits
except IOError:
logger.exception("error writing to file: %s", pidfilename)
# parent exits
os._exit(0)
if rootdir:
os.chdir(rootdir)
os.umask(umask)
@ -244,12 +272,14 @@ def daemonize(rootdir = "/", umask = 0, close_fds = False, dontclose = (),
try:
os.close(fd)
except:
pass
logger.exception("error closing file descriptor")
def readfileintodict(filename, d):
''' Read key=value pairs from a file, into a dict.
"""
Read key=value pairs from a file, into a dict.
Skip comments; strip newline characters and spacing.
'''
"""
with open(filename, 'r') as f:
lines = f.readlines()
for l in lines:
@ -259,15 +289,16 @@ def readfileintodict(filename, d):
key, value = l.split('=', 1)
d[key] = value.strip()
except ValueError:
pass
logger.exception("error reading file to dict: %s", filename)
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,
memory size (bytes), number of loaded instances, dependencies, state,
and kernel memory offset.
'''
"""
with open('/proc/modules', 'r') as f:
for line in f:
if line.startswith(name + ' '):

View file

@ -66,6 +66,7 @@ import math
__all__ = ['to_latlon', 'from_latlon']
class OutOfRangeError(ValueError):
pass
@ -256,4 +257,3 @@ def haversine(lon1, lat1, lon2, lat2):
c = 2 * math.asin(math.sqrt(a))
m = 6367000 * c
return m

View file

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

View file

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

View file

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

View file

@ -1,41 +1,52 @@
#
# CORE
# Copyright (c)2010-2013 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>
#
'''
"""
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 *
from vnet import *
from core.misc.ipaddr import *
from core.api import coreapi
import socket
import subprocess
import threading
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 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):
policy = "ACCEPT"
CTRLIF_IDX_BASE = 99 # 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",
# base control interface index
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.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,
verbose = False, prefix = None,
def __init__(self, session, objid="ctrlnet", name=None, prefix=None,
hostid=None, start=True, assign_address=True,
updown_script=None, serverintf=None):
self.prefix = IPv4Prefix(prefix)
self.prefix = ipaddress.Ipv4Prefix(prefix)
self.hostid = hostid
self.assign_address = assign_address
self.updown_script = updown_script
self.serverintf = serverintf
LxBrNet.__init__(self, session, objid = objid, name = name,
verbose = verbose, start = start)
LxBrNet.__init__(self, session, objid=objid, name=name, start=start)
def startup(self):
if self.detectoldbridge():
@ -45,36 +56,34 @@ class CtrlNet(LxBrNet):
if self.hostid:
addr = self.prefix.addr(self.hostid)
else:
addr = self.prefix.maxaddr()
addr = self.prefix.max_addr()
msg = "Added control network bridge: %s %s" % \
(self.brname, self.prefix)
addrlist = ["%s/%s" % (addr, self.prefix.prefixlen)]
if self.assign_address:
self.addrconfig(addrlist=addrlist)
msg += " address %s" % addr
self.session.info(msg)
logger.info(msg)
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))
check_call([self.updown_script, self.brname, "startup"])
subprocess.check_call([self.updown_script, self.brname, "startup"])
if self.serverintf is not None:
try:
check_call([BRCTL_BIN, "addif", self.brname, self.serverintf])
check_call([IP_BIN, "link", "set", self.serverintf, "up"])
except Exception, e:
self.exception(coreapi.CORE_EXCP_LEVEL_FATAL, self.brname,
"Error joining server interface %s to controlnet bridge %s: %s" % \
(self.serverintf, self.brname, e))
subprocess.check_call([constants.BRCTL_BIN, "addif", self.brname, self.serverintf])
subprocess.check_call([constants.IP_BIN, "link", "set", self.serverintf, "up"])
except subprocess.CalledProcessError:
logger.exception("Error joining server interface %s to controlnet bridge %s",
self.serverintf, self.brname)
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
'''
retstat, retstr = cmdresult([BRCTL_BIN,'show'])
"""
retstat, retstr = utils.cmdresult([constants.BRCTL_BIN, 'show'])
if retstat != 0:
self.exception(coreapi.CORE_EXCP_LEVEL_FATAL, None,
"Unable to retrieve list of installed bridges")
logger.error("Unable to retrieve list of installed bridges")
lines = retstr.split('\n')
for line in lines[1:]:
cols = line.split('\t')
@ -82,46 +91,47 @@ class CtrlNet(LxBrNet):
flds = cols[0].split('.')
if len(flds) == 3:
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. " \
"An older session might still be running. " \
"Stop all sessions and, if needed, delete %s to continue." % \
(oldbr, oldbr))
(oldbr, oldbr)
)
return True
'''
"""
# 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:
check_call([BRCTL_BIN, 'delbr', oldbr])
except Exception, e:
self.exception(coreapi.CORE_EXCP_LEVEL_ERROR, oldbr,
"Error deleting old bridge %s" % oldbr)
self.info("Deleted %s" % oldbr)
'''
except subprocess.CalledProcessError as e:
logger.exception("Error deleting old bridge %s", oldbr, e)
logger.info("Deleted %s", oldbr)
"""
return False
def shutdown(self):
if self.serverintf is not None:
try:
check_call([BRCTL_BIN, "delif", self.brname, self.serverintf])
except Exception, e:
self.exception(coreapi.CORE_EXCP_LEVEL_ERROR, self.brname,
"Error deleting server interface %s to controlnet bridge %s: %s" % \
(self.serverintf, self.brname, e))
subprocess.check_call([constants.BRCTL_BIN, "delif", self.brname, self.serverintf])
except subprocess.CalledProcessError:
logger.exception("Error deleting server interface %s to controlnet bridge %s",
self.serverintf, self.brname)
if self.updown_script is not None:
self.info("interface %s updown script '%s shutdown' called" % \
(self.brname, self.updown_script))
check_call([self.updown_script, self.brname, "shutdown"])
logger.info("interface %s updown script '%s shutdown' called" % (self.brname, self.updown_script))
subprocess.check_call([self.updown_script, self.brname, "shutdown"])
LxBrNet.shutdown(self)
def tolinkmsgs(self, flags):
''' Do not include CtrlNet in link messages describing this session.
'''
def all_link_data(self, flags):
"""
Do not include CtrlNet in link messages describing this session.
"""
return []
class CoreNode(LxcNode):
apitype = coreapi.CORE_NODE_DEF
apitype = NodeTypes.DEFAULT.value
class PtpNet(LxBrNet):
policy = "ACCEPT"
@ -132,124 +142,145 @@ class PtpNet(LxBrNet):
"Point-to-point links support at most 2 network interfaces"
LxBrNet.attach(self, netif)
def tonodemsg(self, flags):
''' Do not generate a Node Message for point-to-point links. They are
def data(self, message_type):
"""
Do not generate a Node Message for point-to-point links. They are
built using a link message instead.
'''
"""
pass
def tolinkmsgs(self, flags):
''' Build CORE API TLVs for a point-to-point link. One Link message
def all_link_data(self, flags):
"""
Build CORE API TLVs for a point-to-point link. One Link message
describes this network.
'''
tlvdata = ""
"""
all_links = []
if len(self._netif) != 2:
return tlvdata
(if1, if2) = self._netif.items()
return all_links
if1, if2 = self._netif.items()
if1 = if1[1]
if2 = if2[1]
tlvdata += coreapi.CoreLinkTlv.pack(coreapi.CORE_TLV_LINK_N1NUMBER,
if1.node.objid)
tlvdata += coreapi.CoreLinkTlv.pack(coreapi.CORE_TLV_LINK_N2NUMBER,
if2.node.objid)
uni = False
unidirectional = 0
if if1.getparams() != if2.getparams():
uni = True
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)
unidirectional = 1
tlvdata += coreapi.CoreLinkTlv.pack(coreapi.CORE_TLV_LINK_IF1NUM, \
if1.node.getifindex(if1))
if if1.hwaddr:
tlvdata += coreapi.CoreLinkTlv.pack(coreapi.CORE_TLV_LINK_IF1MAC,
if1.hwaddr)
for addr in if1.addrlist:
(ip, sep, mask) = addr.partition('/')
interface1_ip4 = None
interface1_ip4_mask = None
interface1_ip6 = None
interface1_ip6_mask = None
for address in if1.addrlist:
ip, sep, mask = address.partition('/')
mask = int(mask)
if isIPv4Address(ip):
if ipaddress.is_ipv4_address(ip):
family = AF_INET
tlvtypeip = coreapi.CORE_TLV_LINK_IF1IP4
tlvtypemask = coreapi.CORE_TLV_LINK_IF1IP4MASK
ipl = socket.inet_pton(family, ip)
interface1_ip4 = ipaddress.IpAddress(af=family, address=ipl)
interface1_ip4_mask = mask
else:
family = AF_INET6
tlvtypeip = coreapi.CORE_TLV_LINK_IF1IP6
tlvtypemask = coreapi.CORE_TLV_LINK_IF1IP6MASK
ipl = socket.inet_pton(family, ip)
tlvdata += coreapi.CoreLinkTlv.pack(tlvtypeip,
IPAddr(af=family, addr=ipl))
tlvdata += coreapi.CoreLinkTlv.pack(tlvtypemask, mask)
interface1_ip6 = ipaddress.IpAddress(af=family, address=ipl)
interface1_ip6_mask = mask
tlvdata += coreapi.CoreLinkTlv.pack(coreapi.CORE_TLV_LINK_IF2NUM, \
if2.node.getifindex(if2))
if if2.hwaddr:
tlvdata += coreapi.CoreLinkTlv.pack(coreapi.CORE_TLV_LINK_IF2MAC,
if2.hwaddr)
for addr in if2.addrlist:
(ip, sep, mask) = addr.partition('/')
interface2_ip4 = None
interface2_ip4_mask = None
interface2_ip6 = None
interface2_ip6_mask = None
for address in if2.addrlist:
ip, sep, mask = address.partition('/')
mask = int(mask)
if isIPv4Address(ip):
if ipaddress.is_ipv4_address(ip):
family = AF_INET
tlvtypeip = coreapi.CORE_TLV_LINK_IF2IP4
tlvtypemask = coreapi.CORE_TLV_LINK_IF2IP4MASK
ipl = socket.inet_pton(family, ip)
interface2_ip4 = ipaddress.IpAddress(af=family, address=ipl)
interface2_ip4_mask = mask
else:
family = AF_INET6
tlvtypeip = coreapi.CORE_TLV_LINK_IF2IP6
tlvtypemask = coreapi.CORE_TLV_LINK_IF2IP6MASK
ipl = socket.inet_pton(family, ip)
tlvdata += coreapi.CoreLinkTlv.pack(tlvtypeip,
IPAddr(af=family, addr=ipl))
tlvdata += coreapi.CoreLinkTlv.pack(tlvtypemask, mask)
msg = coreapi.CoreLinkMessage.pack(flags, tlvdata)
if not uni:
return [msg,]
interface2_ip6 = ipaddress.IpAddress(af=family, address=ipl)
interface2_ip6_mask = mask
# TODO: not currently used
# loss=netif.getparam('loss')
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
# (swap if1 and if2)
tlvdata = ""
tlvdata += coreapi.CoreLinkTlv.pack(coreapi.CORE_TLV_LINK_N1NUMBER,
if2.node.objid)
tlvdata += coreapi.CoreLinkTlv.pack(coreapi.CORE_TLV_LINK_N2NUMBER,
if1.node.objid)
tlvdata += self.netifparamstolink(if2)
tlvdata += coreapi.CoreLinkTlv.pack(coreapi.CORE_TLV_LINK_UNI, 1)
tlvdata += coreapi.CoreLinkTlv.pack(coreapi.CORE_TLV_LINK_IF1NUM, \
if2.node.getifindex(if2))
tlvdata += coreapi.CoreLinkTlv.pack(coreapi.CORE_TLV_LINK_IF2NUM, \
if1.node.getifindex(if1))
msg2 = coreapi.CoreLinkMessage.pack(0, tlvdata)
return [msg, msg2]
if unidirectional:
link_data = LinkData(
message_type=0,
node1_id=if2.node.objid,
node2_id=if1.node.objid,
delay=if1.getparam("delay"),
bandwidth=if1.getparam("bw"),
dup=if1.getparam("duplicate"),
jitter=if1.getparam("jitter"),
unidirectional=1,
interface1_id=if2.node.getifindex(if2),
interface2_id=if1.node.getifindex(if1)
)
all_links.append(link_data)
return all_links
class SwitchNode(LxBrNet):
apitype = coreapi.CORE_NODE_SWITCH
apitype = NodeTypes.SWITCH.value
policy = "ACCEPT"
type = "lanswitch"
class HubNode(LxBrNet):
apitype = coreapi.CORE_NODE_HUB
apitype = NodeTypes.HUB.value
policy = "ACCEPT"
type = "hub"
def __init__(self, session, objid = None, name = None, verbose = False,
start = True):
''' the Hub node forwards packets to all bridge ports by turning off
def __init__(self, session, objid=None, name=None, start=True):
"""
the Hub node forwards packets to all bridge ports by turning off
the MAC address learning
'''
LxBrNet.__init__(self, session, objid, name, verbose, start)
"""
LxBrNet.__init__(self, session, objid, name, start)
if start:
check_call([BRCTL_BIN, "setageing", self.brname, "0"])
subprocess.check_call([constants.BRCTL_BIN, "setageing", self.brname, "0"])
class WlanNode(LxBrNet):
apitype = coreapi.CORE_NODE_WLAN
linktype = coreapi.CORE_LINK_WIRELESS
apitype = NodeTypes.WIRELESS_LAN.value
linktype = LinkTypes.WIRELESS.value
policy = "DROP"
type = "wlan"
def __init__(self, session, objid = None, name = None, verbose = False,
start = True, policy = None):
LxBrNet.__init__(self, session, objid, name, verbose, start, policy)
def __init__(self, session, objid=None, name=None, start=True, policy=None):
LxBrNet.__init__(self, session, objid, name, start, policy)
# wireless model such as basic range
self.model = None
# mobility model such as scripted
@ -258,7 +289,7 @@ class WlanNode(LxBrNet):
def attach(self, netif):
LxBrNet.attach(self, netif)
if self.model:
netif.poshook = self.model._positioncallback
netif.poshook = self.model.position_callback
if netif.node is None:
return
(x, y, z) = netif.node.position.get()
@ -267,61 +298,64 @@ class WlanNode(LxBrNet):
# self.model.setlinkparams()
def setmodel(self, model, config):
''' Mobility and wireless model.
'''
if (self.verbose):
self.info("adding model %s" % model._name)
if model._type == coreapi.CORE_TLV_REG_WIRELESS:
self.model = model(session=self.session, objid=self.objid,
verbose=self.verbose, values=config)
if self.model._positioncallback:
"""
Sets the mobility and wireless model.
:param core.mobility.WirelessModel.cls model: wireless model to set to
:param config:
:return:
"""
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():
netif.poshook = self.model._positioncallback
netif.poshook = self.model.position_callback
if netif.node is not None:
(x, y, z) = netif.node.position.get()
netif.poshook(netif, x, y, z)
self.model.setlinkparams()
elif model._type == coreapi.CORE_TLV_REG_MOBILITY:
self.mobility = model(session=self.session, objid=self.objid,
verbose=self.verbose, values=config)
elif model.config_type == RegisterTlvs.MOBILITY.value:
self.mobility = model(session=self.session, object_id=self.objid, values=config)
def updatemodel(self, model_name, values):
''' Allow for model updates during runtime (similar to setmodel().)
'''
if (self.verbose):
self.info("updating model %s" % model_name)
if self.model is None or self.model._name != model_name:
"""
Allow for model updates during runtime (similar to setmodel().)
"""
logger.info("updating model %s" % model_name)
if self.model is None or self.model.name != model_name:
return
model = self.model
if model._type == coreapi.CORE_TLV_REG_WIRELESS:
if model.config_type == RegisterTlvs.WIRELESS.value:
if not model.updateconfig(values):
return
if self.model._positioncallback:
if self.model.position_callback:
for netif in self.netifs():
netif.poshook = self.model._positioncallback
netif.poshook = self.model.position_callback
if netif.node is not None:
(x, y, z) = netif.node.position.get()
netif.poshook(netif, x, y, z)
self.model.setlinkparams()
def tolinkmsgs(self, flags):
msgs = LxBrNet.tolinkmsgs(self, flags)
def all_link_data(self, flags):
all_links = LxBrNet.all_link_data(self, flags)
if self.model:
msgs += self.model.tolinkmsgs(flags)
return msgs
all_links.extend(self.model.all_link_data(flags))
return all_links
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.
'''
apitype = coreapi.CORE_NODE_RJ45
"""
apitype = NodeTypes.RJ45.value
type = "rj45"
def __init__(self, session, objid = None, name = None, mtu = 1500,
verbose = False, start = True):
PyCoreNode.__init__(self, session, objid, name, verbose=verbose,
start=start)
def __init__(self, session, objid=None, name=None, mtu=1500, start=True):
PyCoreNode.__init__(self, session, objid, name, start=start)
# this initializes net, params, poshook
PyCoreNetIf.__init__(self, node=self, name=name, mtu=mtu)
self.up = False
@ -334,27 +368,28 @@ class RJ45Node(PyCoreNode, PyCoreNetIf):
self.startup()
def startup(self):
''' Set the interface in the up state.
'''
"""
Set the interface in the up state.
"""
# interface will also be marked up during net.attach()
self.savestate()
try:
check_call([IP_BIN, "link", "set", self.localname, "up"])
except:
self.warn("Failed to run command: %s link set %s up" % \
(IP_BIN, self.localname))
subprocess.check_call([constants.IP_BIN, "link", "set", self.localname, "up"])
except subprocess.CalledProcessError:
logger.exception("failed to run command: %s link set %s up", constants.IP_BIN, self.localname)
return
self.up = True
def shutdown(self):
''' Bring the interface down. Remove any addresses and queuing
"""
Bring the interface down. Remove any addresses and queuing
disciplines.
'''
"""
if not self.up:
return
check_call([IP_BIN, "link", "set", self.localname, "down"])
check_call([IP_BIN, "addr", "flush", "dev", self.localname])
mutecall([TC_BIN, "qdisc", "del", "dev", self.localname, "root"])
subprocess.check_call([constants.IP_BIN, "link", "set", self.localname, "down"])
subprocess.check_call([constants.IP_BIN, "addr", "flush", "dev", self.localname])
utils.mutecall([constants.TC_BIN, "qdisc", "del", "dev", self.localname, "root"])
self.up = False
self.restorestate()
@ -366,10 +401,11 @@ class RJ45Node(PyCoreNode, PyCoreNetIf):
def newnetif(self, net=None, addrlist=[], 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,
but attach ourselves to the given network.
'''
"""
self.lock.acquire()
try:
if ifindex is None:
@ -378,11 +414,12 @@ class RJ45Node(PyCoreNode, PyCoreNetIf):
raise ValueError, \
"RJ45 nodes support at most 1 network interface"
self._netif[ifindex] = self
self.node = self # PyCoreNetIf.node is self
# PyCoreNetIf.node is self
self.node = self
self.ifindex = ifindex
if net is not None:
self.attachnet(net)
for addr in maketuple(addrlist):
for addr in utils.maketuple(addrlist):
self.addaddr(addr)
return ifindex
finally:
@ -400,10 +437,11 @@ class RJ45Node(PyCoreNode, PyCoreNetIf):
raise ValueError, "ifindex %s does not exist" % ifindex
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
real nodes.
'''
"""
if net is not None and net == self.net:
return self
if ifindex is None:
@ -419,27 +457,28 @@ class RJ45Node(PyCoreNode, PyCoreNetIf):
def addaddr(self, addr):
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)
def deladdr(self, addr):
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)
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
'''
"""
self.old_up = False
self.old_addrs = []
cmd = [IP_BIN, "addr", "show", "dev", self.localname]
cmd = [constants.IP_BIN, "addr", "show", "dev", self.localname]
try:
tmp = subprocess.Popen(cmd, stdout=subprocess.PIPE)
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():
self.warn("Command failed: %s" % cmd)
logger.warn("Command failed: %s", cmd)
return
lines = tmp.stdout.read()
tmp.stdout.close()
@ -459,31 +498,27 @@ class RJ45Node(PyCoreNode, PyCoreNetIf):
self.old_addrs.append((items[1], None))
def restorestate(self):
''' Restore the addresses and other interface state after using it.
'''
"""
Restore the addresses and other interface state after using it.
"""
for addr in self.old_addrs:
if addr[1] is None:
check_call([IP_BIN, "addr", "add", addr[0], "dev",
self.localname])
subprocess.check_call([constants.IP_BIN, "addr", "add", addr[0], "dev", self.localname])
else:
check_call([IP_BIN, "addr", "add", addr[0], "brd", addr[1],
"dev", self.localname])
subprocess.check_call([constants.IP_BIN, "addr", "add", addr[0], "brd", addr[1], "dev", self.localname])
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):
''' Use setposition() from both parent classes.
'''
"""
Use setposition() from both parent classes.
"""
PyCoreObj.setposition(self, x, y, z)
# invoke any poshook
PyCoreNetIf.setposition(self, x, y, z)
class TunnelNode(GreTapBridge):
apitype = coreapi.CORE_NODE_TUNNEL
apitype = NodeTypes.TUNNEL.value
policy = "ACCEPT"
type = "tunnel"

View file

@ -0,0 +1,741 @@
"""
TODO: probably goes away, or implement the usage of "unshare", or docker formal.
"""
import socket
import subprocess
import threading
from socket import AF_INET
from socket import AF_INET6
from core import constants
from core.coreobj import PyCoreNet
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.vif import GreTap
from core.netns.vif import VEth
from core.netns.vnet import EbtablesQueue
from core.netns.vnet import GreTapBridge
logger = log.get_logger(__name__)
# a global object because all WLANs share the same queue
# cannot have multiple threads invoking the ebtables commnd
ebtables_queue = EbtablesQueue()
ebtables_lock = threading.Lock()
utils.check_executables([
constants.IP_BIN,
constants.EBTABLES_BIN,
constants.TC_BIN
])
def ebtables_commands(call, commands):
ebtables_lock.acquire()
try:
for command in commands:
call(command)
finally:
ebtables_lock.release()
class OvsNet(PyCoreNet):
"""
Used to be LxBrNet.
Base class for providing Openvswitch functionality to objects that create bridges.
"""
policy = "DROP"
def __init__(self, session, objid=None, name=None, start=True, policy=None):
"""
Creates an OvsNet instance.
:param core.session.Session session: session this object is a part of
:param objid:
:param name:
:param start:
:param policy:
:return:
"""
PyCoreNet.__init__(self, session, objid, name, start)
if policy:
self.policy = policy
else:
self.policy = self.__class__.policy
session_id = self.session.short_session_id()
self.bridge_name = "b.%s.%s" % (str(self.objid), session_id)
self.up = False
if start:
self.startup()
ebtables_queue.startupdateloop(self)
def startup(self):
try:
subprocess.check_call([constants.OVS_BIN, "add-br", self.bridge_name])
except subprocess.CalledProcessError:
logger.exception("error adding bridge")
try:
# turn off spanning tree protocol and forwarding delay
# TODO: appears stp and rstp are off by default, make sure this always holds true
# TODO: apears ovs only supports rstp forward delay and again it's off by default
subprocess.check_call([constants.IP_BIN, "link", "set", self.bridge_name, "up"])
# create a new ebtables chain for this bridge
ebtables_commands(subprocess.check_call, [
[constants.EBTABLES_BIN, "-N", self.bridge_name, "-P", self.policy],
[constants.EBTABLES_BIN, "-A", "FORWARD", "--logical-in", self.bridge_name, "-j", self.bridge_name]
])
except subprocess.CalledProcessError:
logger.exception("Error setting bridge parameters")
self.up = True
def shutdown(self):
if not self.up:
logger.info("exiting shutdown, object is not up")
return
ebtables_queue.stopupdateloop(self)
utils.mutecall([constants.IP_BIN, "link", "set", self.bridge_name, "down"])
utils.mutecall([constants.OVS_BIN, "del-br", self.bridge_name])
ebtables_commands(utils.mutecall, [
[constants.EBTABLES_BIN, "-D", "FORWARD", "--logical-in", self.bridge_name, "-j", self.bridge_name],
[constants.EBTABLES_BIN, "-X", self.bridge_name]
])
for interface in self.netifs():
# removes veth pairs used for bridge-to-bridge connections
interface.shutdown()
self._netif.clear()
self._linked.clear()
del self.session
self.up = False
def attach(self, interface):
if self.up:
try:
subprocess.check_call([constants.OVS_BIN, "add-port", self.bridge_name, interface.localname])
subprocess.check_call([constants.IP_BIN, "link", "set", interface.localname, "up"])
except subprocess.CalledProcessError:
logger.exception("error joining interface %s to bridge %s", interface.localname, self.bridge_name)
return
PyCoreNet.attach(self, interface)
def detach(self, interface):
if self.up:
try:
subprocess.check_call([constants.OVS_BIN, "del-port", self.bridge_name, interface.localname])
except subprocess.CalledProcessError:
logger.exception("error removing interface %s from bridge %s", interface.localname, self.bridge_name)
return
PyCoreNet.detach(self, interface)
def linked(self, interface_one, interface_two):
# check if the network interfaces are attached to this network
if self._netif[interface_one.netifi] != interface_one:
raise ValueError("inconsistency for interface %s" % interface_one.name)
if self._netif[interface_two.netifi] != interface_two:
raise ValueError("inconsistency for interface %s" % interface_two.name)
try:
linked = self._linked[interface_one][interface_two]
except KeyError:
if self.policy == "ACCEPT":
linked = True
elif self.policy == "DROP":
linked = False
else:
raise ValueError("unknown policy: %s" % self.policy)
self._linked[interface_one][interface_two] = linked
return linked
def unlink(self, interface_one, interface_two):
"""
Unlink two PyCoreNetIfs, resulting in adding or removing ebtables
filtering rules.
"""
with self._linked_lock:
if not self.linked(interface_one, interface_two):
return
self._linked[interface_one][interface_two] = False
ebtables_queue.ebchange(self)
def link(self, interface_one, interface_two):
"""
Link two PyCoreNetIfs together, resulting in adding or removing
ebtables filtering rules.
"""
with self._linked_lock:
if self.linked(interface_one, interface_two):
return
self._linked[interface_one][interface_two] = True
ebtables_queue.ebchange(self)
def linkconfig(self, interface, bw=None, delay=None, loss=None, duplicate=None,
jitter=None, netif2=None, devname=None):
"""
Configure link parameters by applying tc queuing disciplines on the
interface.
"""
if not devname:
devname = interface.localname
tc = [constants.TC_BIN, "qdisc", "replace", "dev", devname]
parent = ["root"]
# attempt to set bandwidth and update as needed if value changed
bandwidth_changed = interface.setparam("bw", bw)
if bandwidth_changed:
# from tc-tbf(8): minimum value for burst is rate / kernel_hz
if bw > 0:
if self.up:
burst = max(2 * interface.mtu, bw / 1000)
limit = 0xffff # max IP payload
tbf = ["tbf", "rate", str(bw), "burst", str(burst), "limit", str(limit)]
logger.info("linkconfig: %s" % [tc + parent + ["handle", "1:"] + tbf])
subprocess.check_call(tc + parent + ["handle", "1:"] + tbf)
interface.setparam("has_tbf", True)
elif interface.getparam("has_tbf") and bw <= 0:
tcd = [] + tc
tcd[2] = "delete"
if self.up:
subprocess.check_call(tcd + parent)
interface.setparam("has_tbf", False)
# removing the parent removes the child
interface.setparam("has_netem", False)
if interface.getparam("has_tbf"):
parent = ["parent", "1:1"]
netem = ["netem"]
delay_changed = interface.setparam("delay", delay)
if loss is not None:
loss = float(loss)
loss_changed = interface.setparam("loss", loss)
if duplicate is not None:
duplicate = float(duplicate)
duplicate_changed = interface.setparam("duplicate", duplicate)
jitter_changed = interface.setparam("jitter", jitter)
# if nothing changed return
if not any([bandwidth_changed, delay_changed, loss_changed, duplicate_changed, jitter_changed]):
return
# jitter and delay use the same delay statement
if delay is not None:
netem += ["delay", "%sus" % delay]
else:
netem += ["delay", "0us"]
if jitter is not None:
netem += ["%sus" % jitter, "25%"]
if loss is not None:
netem += ["loss", "%s%%" % min(loss, 100)]
if duplicate is not None:
netem += ["duplicate", "%s%%" % min(duplicate, 100)]
if delay <= 0 and jitter <= 0 and loss <= 0 and duplicate <= 0:
# possibly remove netem if it exists and parent queue wasn"t removed
if not interface.getparam("has_netem"):
return
tc[2] = "delete"
if self.up:
logger.info("linkconfig: %s" % ([tc + parent + ["handle", "10:"]],))
subprocess.check_call(tc + parent + ["handle", "10:"])
interface.setparam("has_netem", False)
elif len(netem) > 1:
if self.up:
logger.info("linkconfig: %s" % ([tc + parent + ["handle", "10:"] + netem],))
subprocess.check_call(tc + parent + ["handle", "10:"] + netem)
interface.setparam("has_netem", True)
def linknet(self, network):
"""
Link this bridge with another by creating a veth pair and installing
each device into each bridge.
"""
session_id = self.session.short_session_id()
try:
self_objid = "%x" % self.objid
except TypeError:
self_objid = "%s" % self.objid
try:
net_objid = "%x" % network.objid
except TypeError:
net_objid = "%s" % network.objid
localname = "veth%s.%s.%s" % (self_objid, net_objid, session_id)
if len(localname) >= 16:
raise ValueError("interface local name %s too long" % localname)
name = "veth%s.%s.%s" % (net_objid, self_objid, session_id)
if len(name) >= 16:
raise ValueError("interface name %s too long" % name)
interface = VEth(node=None, name=name, localname=localname, mtu=1500, net=self, start=self.up)
self.attach(interface)
if network.up:
# this is similar to net.attach() but uses netif.name instead
# of localname
subprocess.check_call([constants.OVS_BIN, "add-port", network.brname, interface.name])
subprocess.check_call([constants.IP_BIN, "link", "set", interface.name, "up"])
# TODO: is there a native method for this? see if this causes issues
# i = network.newifindex()
# network._netif[i] = interface
# with network._linked_lock:
# network._linked[interface] = {}
# this method call is equal to the above, with a interface.netifi = call
network.attach(interface)
interface.net = self
interface.othernet = network
return interface
def getlinknetif(self, network):
"""
Return the interface of that links this net with another net
(that were linked using linknet()).
"""
for interface in self.netifs():
if hasattr(interface, "othernet") and interface.othernet == network:
return interface
return None
def addrconfig(self, addresses):
"""
Set addresses on the bridge.
"""
if not self.up:
return
for address in addresses:
try:
subprocess.check_call([constants.IP_BIN, "addr", "add", str(address), "dev", self.bridge_name])
except subprocess.CalledProcessError:
logger.exception("error adding IP address")
class OvsCtrlNet(OvsNet):
policy = "ACCEPT"
CTRLIF_IDX_BASE = 99 # 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",
"172.17.0.0/24 172.17.1.0/24 172.17.2.0/24 172.17.3.0/24 172.17.4.0/24",
"172.18.0.0/24 172.18.1.0/24 172.18.2.0/24 172.18.3.0/24 172.18.4.0/24",
"172.19.0.0/24 172.19.1.0/24 172.19.2.0/24 172.19.3.0/24 172.19.4.0/24"
]
def __init__(self, session, objid="ctrlnet", name=None, prefix=None, hostid=None,
start=True, assign_address=True, updown_script=None, serverintf=None):
OvsNet.__init__(self, session, objid=objid, name=name, start=start)
self.prefix = ipaddress.Ipv4Prefix(prefix)
self.hostid = hostid
self.assign_address = assign_address
self.updown_script = updown_script
self.serverintf = serverintf
def startup(self):
if self.detectoldbridge():
return
OvsNet.startup(self)
if self.hostid:
addr = self.prefix.addr(self.hostid)
else:
addr = self.prefix.max_addr()
message = "Added control network bridge: %s %s" % (self.bridge_name, self.prefix)
addresses = ["%s/%s" % (addr, self.prefix.prefixlen)]
if self.assign_address:
self.addrconfig(addresses=addresses)
message += " address %s" % addr
logger.info(message)
if self.updown_script:
logger.info("interface %s updown script %s startup called" % (self.bridge_name, self.updown_script))
subprocess.check_call([self.updown_script, self.bridge_name, "startup"])
if self.serverintf:
try:
subprocess.check_call([constants.OVS_BIN, "add-port", self.bridge_name, self.serverintf])
subprocess.check_call([constants.IP_BIN, "link", "set", self.serverintf, "up"])
except subprocess.CalledProcessError:
logger.exception("error joining server interface %s to controlnet bridge %s",
self.serverintf, self.bridge_name)
def detectoldbridge(self):
"""
Occassionally, control net bridges from previously closed sessions are not cleaned up.
Check if there are old control net bridges and delete them
"""
status, output = utils.cmdresult([constants.OVS_BIN, "list-br"])
output = output.strip()
if output:
for line in output.split("\n"):
bride_name = line.split(".")
if bride_name[0] == "b" and bride_name[1] == self.objid:
logger.error("older session may still be running with conflicting id for bridge: %s", line)
return True
return False
def shutdown(self):
if self.serverintf:
try:
subprocess.check_call([constants.OVS_BIN, "del-port", self.bridge_name, self.serverintf])
except subprocess.CalledProcessError:
logger.exception("Error deleting server interface %s to controlnet bridge %s",
self.serverintf, self.bridge_name)
if self.updown_script:
logger.info("interface %s updown script '%s shutdown' called", self.bridge_name, self.updown_script)
subprocess.check_call([self.updown_script, self.bridge_name, "shutdown"])
OvsNet.shutdown(self)
def all_link_data(self, flags):
"""
Do not include CtrlNet in link messages describing this session.
"""
return []
class OvsPtpNet(OvsNet):
policy = "ACCEPT"
def attach(self, interface):
if len(self._netif) >= 2:
raise ValueError("point-to-point links support at most 2 network interfaces")
OvsNet.attach(self, interface)
def data(self, message_type):
"""
Do not generate a Node Message for point-to-point links. They are
built using a link message instead.
"""
pass
def all_link_data(self, flags):
"""
Build CORE API TLVs for a point-to-point link. One Link message describes this network.
"""
all_links = []
if len(self._netif) != 2:
return all_links
if1, if2 = self._netif.items()
if1 = if1[1]
if2 = if2[1]
unidirectional = 0
if if1.getparams() != if2.getparams():
unidirectional = 1
interface1_ip4 = None
interface1_ip4_mask = None
interface1_ip6 = None
interface1_ip6_mask = None
for address in if1.addrlist:
ip, sep, mask = address.partition('/')
mask = int(mask)
if ipaddress.is_ipv4_address(ip):
family = AF_INET
ipl = socket.inet_pton(family, ip)
interface1_ip4 = ipaddress.IpAddress(af=family, address=ipl)
interface1_ip4_mask = mask
else:
family = AF_INET6
ipl = socket.inet_pton(family, ip)
interface1_ip6 = ipaddress.IpAddress(af=family, address=ipl)
interface1_ip6_mask = mask
interface2_ip4 = None
interface2_ip4_mask = None
interface2_ip6 = None
interface2_ip6_mask = None
for address in if2.addrlist:
ip, sep, mask = address.partition('/')
mask = int(mask)
if ipaddress.is_ipv4_address(ip):
family = AF_INET
ipl = socket.inet_pton(family, ip)
interface2_ip4 = ipaddress.IpAddress(af=family, address=ipl)
interface2_ip4_mask = mask
else:
family = AF_INET6
ipl = socket.inet_pton(family, ip)
interface2_ip6 = ipaddress.IpAddress(af=family, address=ipl)
interface2_ip6_mask = mask
# TODO: not currently used
# loss=netif.getparam('loss')
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
# (swap if1 and if2)
if unidirectional:
link_data = LinkData(
message_type=0,
node1_id=if2.node.objid,
node2_id=if1.node.objid,
delay=if1.getparam("delay"),
bandwidth=if1.getparam("bw"),
dup=if1.getparam("duplicate"),
jitter=if1.getparam("jitter"),
unidirectional=1,
interface1_id=if2.node.getifindex(if2),
interface2_id=if1.node.getifindex(if1)
)
all_links.append(link_data)
return all_links
class OvsSwitchNode(OvsNet):
apitype = NodeTypes.SWITCH.value
policy = "ACCEPT"
type = "lanswitch"
class OvsHubNode(OvsNet):
apitype = NodeTypes.HUB.value
policy = "ACCEPT"
type = "hub"
def __init__(self, session, objid=None, name=None, start=True):
"""
the Hub node forwards packets to all bridge ports by turning off
the MAC address learning
"""
OvsNet.__init__(self, session, objid, name, start)
if start:
# TODO: verify that the below flow accomplishes what is desired for a "HUB"
# TODO: replace "brctl setageing 0"
subprocess.check_call([constants.OVS_FLOW_BIN, "add-flow", self.bridge_name, "action=flood"])
class OvsWlanNode(OvsNet):
apitype = NodeTypes.WIRELESS_LAN.value
linktype = LinkTypes.WIRELESS.value
policy = "DROP"
type = "wlan"
def __init__(self, session, objid=None, name=None, start=True, policy=None):
OvsNet.__init__(self, session, objid, name, start, policy)
# wireless model such as basic range
self.model = None
# mobility model such as scripted
self.mobility = None
def attach(self, interface):
OvsNet.attach(self, interface)
if self.model:
interface.poshook = self.model.position_callback
if interface.node is None:
return
x, y, z = interface.node.position.get()
# invokes any netif.poshook
interface.setposition(x, y, z)
# self.model.setlinkparams()
def setmodel(self, model, config):
"""
Mobility and wireless model.
"""
logger.info("adding model %s", model.name)
if model.type == RegisterTlvs.WIRELESS.value:
self.model = model(session=self.session, object_id=self.objid, values=config)
if self.model.position_callback:
for interface in self.netifs():
interface.poshook = self.model.position_callback
if interface.node is not None:
x, y, z = interface.node.position.get()
interface.poshook(interface, x, y, z)
self.model.setlinkparams()
elif model.type == RegisterTlvs.MOBILITY.value:
self.mobility = model(session=self.session, object_id=self.objid, values=config)
def updatemodel(self, model_name, values):
"""
Allow for model updates during runtime (similar to setmodel().)
"""
logger.info("updating model %s", model_name)
if self.model is None or self.model.name != model_name:
logger.info(
"failure to update model, model doesn't exist or invalid name: model(%s) - name(%s)",
self.model, model_name
)
return
model = self.model
if model.type == RegisterTlvs.WIRELESS.value:
if not model.updateconfig(values):
return
if self.model.position_callback:
for interface in self.netifs():
interface.poshook = self.model.position_callback
if interface.node is not None:
x, y, z = interface.node.position.get()
interface.poshook(interface, x, y, z)
self.model.setlinkparams()
def all_link_data(self, flags):
all_links = OvsNet.all_link_data(self, flags)
if self.model:
all_links.extend(self.model.all_link_data(flags))
return all_links
class OvsTunnelNode(GreTapBridge):
apitype = NodeTypes.TUNNEL.value
policy = "ACCEPT"
type = "tunnel"
class OvsGreTapBridge(OvsNet):
"""
A network consisting of a bridge with a gretap device for tunneling to
another system.
"""
def __init__(self, session, remoteip=None, objid=None, name=None, policy="ACCEPT",
localip=None, ttl=255, key=None, start=True):
OvsNet.__init__(self, session=session, objid=objid, name=name, policy=policy, start=False)
self.grekey = key
if self.grekey is None:
self.grekey = self.session.session_id ^ self.objid
self.localnum = None
self.remotenum = None
self.remoteip = remoteip
self.localip = localip
self.ttl = ttl
if remoteip is None:
self.gretap = None
else:
self.gretap = GreTap(node=self, name=None, session=session, remoteip=remoteip,
objid=None, localip=localip, ttl=ttl, key=self.grekey)
if start:
self.startup()
def startup(self):
"""
Creates a bridge and adds the gretap device to it.
"""
OvsNet.startup(self)
if self.gretap:
self.attach(self.gretap)
def shutdown(self):
"""
Detach the gretap device and remove the bridge.
"""
if self.gretap:
self.detach(self.gretap)
self.gretap.shutdown()
self.gretap = None
OvsNet.shutdown(self)
def addrconfig(self, addresses):
"""
Set the remote tunnel endpoint. This is a one-time method for
creating the GreTap device, which requires the remoteip at startup.
The 1st address in the provided list is remoteip, 2nd optionally
specifies localip.
"""
if self.gretap:
raise ValueError("gretap already exists for %s" % self.name)
remoteip = addresses[0].split('/')[0]
localip = None
if len(addresses) > 1:
localip = addresses[1].split('/')[0]
self.gretap = GreTap(session=self.session, remoteip=remoteip, objid=None, name=None,
localip=localip, ttl=self.ttl, key=self.grekey)
self.attach(self.gretap)
def setkey(self, key):
"""
Set the GRE key used for the GreTap device. This needs to be set
prior to instantiating the GreTap device (before addrconfig).
"""
self.grekey = key

View file

@ -1,29 +1,23 @@
#
# CORE
# 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
"""
PyCoreNetIf classes that implement the interfaces available
under Linux.
'''
"""
import os, signal, shutil, sys, subprocess, vnodeclient, threading, string
import random, time
from core.api import coreapi
from core.misc.utils import *
from core.constants import *
from core.coreobj import PyCoreObj, PyCoreNode, PyCoreNetIf, Position
from core.emane.nodes import EmaneNode
import subprocess
import time
from core import constants
from core.coreobj import PyCoreNetIf
from core.misc import log
from core.misc import utils
logger = log.get_logger(__name__)
utils.check_executables([constants.IP_BIN])
checkexec([IP_BIN])
class VEth(PyCoreNetIf):
def __init__(self, node, name, localname, mtu = 1500, net = None,
start = True):
def __init__(self, node, name, localname, mtu=1500, net=None, start=True):
# note that net arg is ignored
PyCoreNetIf.__init__(self, node=node, name=name, mtu=mtu)
self.localname = localname
@ -32,26 +26,27 @@ class VEth(PyCoreNetIf):
self.startup()
def startup(self):
check_call([IP_BIN, "link", "add", "name", self.localname,
subprocess.check_call([constants.IP_BIN, "link", "add", "name", self.localname,
"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
def shutdown(self):
if not self.up:
return
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:
mutedetach([IP_BIN, "link", "delete", self.localname])
utils.mutedetach([constants.IP_BIN, "link", "delete", self.localname])
self.up = False
class TunTap(PyCoreNetIf):
''' TUN/TAP virtual device in TAP mode
'''
def __init__(self, node, name, localname, mtu = 1500, net = None,
start = True):
"""
TUN/TAP virtual device in TAP mode
"""
def __init__(self, node, name, localname, mtu=1500, net=None, start=True):
PyCoreNetIf.__init__(self, node=node, name=name, mtu=mtu)
self.localname = localname
self.up = False
@ -71,15 +66,15 @@ class TunTap(PyCoreNetIf):
def shutdown(self):
if not self.up:
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:
# mutedetach(["tunctl", "-d", self.localname])
self.up = False
def waitfor(self, func, attempts=10, maxretrydelay=0.25):
'''\
"""
Wait for func() to return zero with exponential backoff
'''
"""
delay = 0.01
for i in xrange(1, attempts + 1):
r = func()
@ -88,81 +83,76 @@ class TunTap(PyCoreNetIf):
msg = 'attempt %s failed with nonzero exit status %s' % (i, r)
if i < attempts + 1:
msg += ', retrying...'
self.node.info(msg)
logger.info(msg)
time.sleep(delay)
delay = delay + delay
if delay > maxretrydelay:
delay = maxretrydelay
else:
msg += ', giving up'
self.node.info(msg)
raise RuntimeError, 'command failed after %s attempts' % attempts
logger.info(msg)
raise RuntimeError('command failed after %s attempts' % attempts)
def waitfordevicelocal(self):
'''\
"""
Check for presence of a local device - tap device may not
appear right away waits
'''
"""
def localdevexists():
cmd = (IP_BIN, 'link', 'show', self.localname)
return mutecall(cmd)
cmd = (constants.IP_BIN, 'link', 'show', self.localname)
return utils.mutecall(cmd)
self.waitfor(localdevexists)
def waitfordevicenode(self):
'''\
"""
Check for presence of a node device - tap device may not
appear right away waits
'''
"""
def nodedevexists():
cmd = (IP_BIN, 'link', 'show', self.name)
cmd = (constants.IP_BIN, 'link', 'show', self.name)
return self.node.cmd(cmd)
count = 0
while True:
try:
self.waitfor(nodedevexists)
break
except RuntimeError:
# check if this is an EMANE interface; if so, continue
# waiting if EMANE is still running
if count < 5 and isinstance(self.net, EmaneNode) and \
self.node.session.emane.emanerunning(self.node):
count += 1
else:
raise
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
program (running on the host) has had a chance to open the socket
end of the TAP.
'''
"""
self.waitfordevicelocal()
netns = str(self.node.pid)
try:
check_call([IP_BIN, "link", "set", self.localname, "netns", netns])
except Exception, e:
subprocess.check_call([constants.IP_BIN, "link", "set", self.localname, "netns", netns])
except subprocess.CalledProcessError:
msg = "error installing TAP interface %s, command:" % self.localname
msg += "ip link set %s netns %s" % (self.localname, netns)
self.node.exception(coreapi.CORE_EXCP_LEVEL_ERROR, self.localname, msg)
self.node.warn(msg)
logger.exception(msg)
return
self.node.cmd([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.localname, "name", self.name])
self.node.cmd([constants.IP_BIN, "link", "set", self.name, "up"])
def setaddrs(self):
''' Set interface addresses based on self.addrlist.
'''
"""
Set interface addresses based on self.addrlist.
"""
self.waitfordevicenode()
for addr in self.addrlist:
self.node.cmd([IP_BIN, "addr", "add", str(addr),
"dev", self.name])
self.node.cmd([constants.IP_BIN, "addr", "add", str(addr), "dev", self.name])
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
having a MAC address. The MAC address is required for bridging.
'''
"""
def __init__(self, node=None, name=None, session=None, mtu=1458,
remoteip=None, objid=None, localip=None, ttl=255,
key=None, start=True):
@ -170,9 +160,9 @@ class GreTap(PyCoreNetIf):
self.session = session
if objid is None:
# from PyCoreObj
objid = (((id(self) >> 16) ^ (id(self) & 0xffff)) & 0xffff)
objid = ((id(self) >> 16) ^ (id(self) & 0xffff)) & 0xffff
self.objid = objid
sessionid = self.session.shortsessionid()
sessionid = self.session.short_session_id()
# interface name on the local host machine
self.localname = "gt.%s.%s" % (self.objid, sessionid)
self.transport_type = "raw"
@ -190,21 +180,21 @@ class GreTap(PyCoreNetIf):
cmd += ("ttl", str(ttl))
if key:
cmd += ("key", str(key))
check_call(cmd)
subprocess.check_call(cmd)
cmd = ("ip", "link", "set", self.localname, "up")
check_call(cmd)
subprocess.check_call(cmd)
self.up = True
def shutdown(self):
if self.localname:
cmd = ("ip", "link", "set", self.localname, "down")
check_call(cmd)
subprocess.check_call(cmd)
cmd = ("ip", "link", "del", self.localname)
check_call(cmd)
subprocess.check_call(cmd)
self.localname = None
def tonodemsg(self, flags):
def data(self, message_type):
return None
def tolinkmsgs(self, flags):
def all_link_data(self, flags):
return []

View file

@ -1,42 +1,48 @@
#
# CORE
# 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
"""
PyCoreNet and LxBrNet classes that implement virtual networks using
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.misc.utils import *
from core.constants import *
from core.coreobj import PyCoreNet, PyCoreObj
from core.netns.vif import VEth, GreTap
from core import constants
from core.coreobj import PyCoreNet
from core.misc import log
from core.misc import utils
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()
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
many WLAN link updates.
'''
"""
# update rate is every 300ms
rate = 0.3
# ebtables
atomic_file = "/tmp/pycore.ebtables.atomic"
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.
'''
"""
self.doupdateloop = False
self.updatethread = None
# this lock protects cmds and updates lists
@ -50,8 +56,9 @@ class EbtablesQueue(object):
self.last_update_time = {}
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.
"""
self.updatelock.acquire()
self.last_update_time[wlan] = time.time()
self.updatelock.release()
@ -63,13 +70,15 @@ class EbtablesQueue(object):
self.updatethread.start()
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.
"""
self.updatelock.acquire()
try:
del self.last_update_time[wlan]
except KeyError:
pass
logger.exception("error deleting last update time for wlan: %s", wlan)
self.updatelock.release()
if len(self.last_update_time) > 0:
return
@ -79,16 +88,18 @@ class EbtablesQueue(object):
self.updatethread = None
def ebatomiccmd(self, cmd):
''' Helper for building ebtables atomic file command list.
'''
r = [EBTABLES_BIN, "--atomic-file", self.atomic_file]
"""
Helper for building ebtables atomic file command list.
"""
r = [constants.EBTABLES_BIN, "--atomic-file", self.atomic_file]
if cmd:
r.extend(cmd)
return r
def lastupdate(self, wlan):
''' Return the time elapsed since this WLAN was last updated.
'''
"""
Return the time elapsed since this WLAN was last updated.
"""
try:
elapsed = time.time() - self.last_update_time[wlan]
except KeyError:
@ -97,24 +108,26 @@ class EbtablesQueue(object):
return elapsed
def updated(self, wlan):
''' Keep track of when this WLAN was last updated.
'''
"""
Keep track of when this WLAN was last updated.
"""
self.last_update_time[wlan] = time.time()
self.updates.remove(wlan)
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
should use ebtables at any given time, or results can be unpredictable.
'''
"""
while self.doupdateloop:
self.updatelock.acquire()
for wlan in self.updates:
'''
"""
Check if wlan is from a previously closed session. Because of the
rate limiting scheme employed here, this may happen if a new session
is started soon after closing a previous session.
'''
"""
try:
wlan.session
except:
@ -130,53 +143,57 @@ class EbtablesQueue(object):
time.sleep(self.rate)
def ebcommit(self, wlan):
''' Perform ebtables atomic commit using commands built in the
"""
Perform ebtables atomic commit using commands built in the
self.cmds list.
'''
"""
# save kernel ebtables snapshot to a file
cmd = self.ebatomiccmd(["--atomic-save", ])
try:
check_call(cmd)
except Exception, e:
self.eberror(wlan, "atomic-save (%s)" % cmd, e)
subprocess.check_call(cmd)
except subprocess.CalledProcessError:
logger.exception("atomic-save (%s)", cmd)
# no atomic file, exit
return
# modify the table file using queued ebtables commands
for c in self.cmds:
cmd = self.ebatomiccmd(c)
try:
check_call(cmd)
except Exception, e:
self.eberror(wlan, "cmd=%s" % cmd, e)
pass
subprocess.check_call(cmd)
except subprocess.CalledProcessError:
logger.exception("cmd=%s", cmd)
self.cmds = []
# commit the table file to the kernel
cmd = self.ebatomiccmd(["--atomic-commit", ])
try:
check_call(cmd)
subprocess.check_call(cmd)
os.unlink(self.atomic_file)
except Exception, e:
self.eberror(wlan, "atomic-commit (%s)" % cmd, e)
except OSError:
logger.exception("atomic-commit (%s)", cmd)
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.
'''
"""
self.updatelock.acquire()
if wlan not in self.updates:
self.updates.append(wlan)
self.updatelock.release()
def buildcmds(self, wlan):
''' Inspect a _linked dict from a wlan, and rebuild the ebtables chain
"""
Inspect a _linked dict from a wlan, and rebuild the ebtables chain
for that WLAN.
'''
"""
wlan._linked_lock.acquire()
# flush the chain
self.cmds.extend([["-F", wlan.brname], ])
# rebuild the chain
for (netif1, v) in wlan._linked.items():
for (netif2, linked) in v.items():
for netif1, v in wlan._linked.items():
for netif2, linked in v.items():
if wlan.policy == "DROP" and linked:
self.cmds.extend([["-A", wlan.brname, "-i", netif1.localname,
"-o", netif2.localname, "-j", "ACCEPT"],
@ -189,19 +206,12 @@ class EbtablesQueue(object):
"-i", netif2.localname, "-j", "DROP"]])
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
# cannot have multiple threads invoking the ebtables commnd
ebq = EbtablesQueue()
def ebtablescmds(call, cmds):
ebtables_lock.acquire()
try:
@ -210,19 +220,18 @@ def ebtablescmds(call, cmds):
finally:
ebtables_lock.release()
class LxBrNet(PyCoreNet):
class LxBrNet(PyCoreNet):
policy = "DROP"
def __init__(self, session, objid = None, name = None, verbose = False,
start = True, policy = None):
PyCoreNet.__init__(self, session, objid, name, verbose, start)
def __init__(self, session, objid=None, name=None, start=True, policy=None):
PyCoreNet.__init__(self, session, objid, name, start)
if name is None:
name = str(self.objid)
if policy is not None:
self.policy = policy
self.name = name
sessionid = self.session.shortsessionid()
sessionid = self.session.short_session_id()
self.brname = "b.%s.%s" % (str(self.objid), sessionid)
self.up = False
if start:
@ -231,28 +240,26 @@ class LxBrNet(PyCoreNet):
def startup(self):
try:
check_call([BRCTL_BIN, "addbr", self.brname])
except Exception, e:
self.exception(coreapi.CORE_EXCP_LEVEL_FATAL, self.brname,
"Error adding bridge: %s" % e)
subprocess.check_call([constants.BRCTL_BIN, "addbr", self.brname])
except subprocess.CalledProcessError:
logger.exception("Error adding bridge")
try:
# turn off spanning tree protocol and forwarding delay
check_call([BRCTL_BIN, "stp", self.brname, "off"])
check_call([BRCTL_BIN, "setfd", self.brname, "0"])
check_call([IP_BIN, "link", "set", self.brname, "up"])
subprocess.check_call([constants.BRCTL_BIN, "stp", self.brname, "off"])
subprocess.check_call([constants.BRCTL_BIN, "setfd", self.brname, "0"])
subprocess.check_call([constants.IP_BIN, "link", "set", self.brname, "up"])
# create a new ebtables chain for this bridge
ebtablescmds(check_call, [
[EBTABLES_BIN, "-N", self.brname, "-P", self.policy],
[EBTABLES_BIN, "-A", "FORWARD",
"--logical-in", self.brname, "-j", self.brname]])
ebtablescmds(subprocess.check_call, [
[constants.EBTABLES_BIN, "-N", self.brname, "-P", self.policy],
[constants.EBTABLES_BIN, "-A", "FORWARD", "--logical-in", self.brname, "-j", self.brname]
])
# turn off multicast snooping so mcast forwarding occurs w/o IGMP joins
snoop = "/sys/devices/virtual/net/%s/bridge/multicast_snooping" % \
self.brname
snoop = "/sys/devices/virtual/net/%s/bridge/multicast_snooping" % self.brname
if os.path.exists(snoop):
open(snoop, "w").write('0')
except Exception, e:
self.exception(coreapi.CORE_EXCP_LEVEL_WARNING, self.brname,
"Error setting bridge parameters: %s" % e)
except subprocess.CalledProcessError:
logger.exception("Error setting bridge parameters")
self.up = True
@ -260,12 +267,12 @@ class LxBrNet(PyCoreNet):
if not self.up:
return
ebq.stopupdateloop(self)
mutecall([IP_BIN, "link", "set", self.brname, "down"])
mutecall([BRCTL_BIN, "delbr", self.brname])
ebtablescmds(mutecall, [
[EBTABLES_BIN, "-D", "FORWARD",
utils.mutecall([constants.IP_BIN, "link", "set", self.brname, "down"])
utils.mutecall([constants.BRCTL_BIN, "delbr", self.brname])
ebtablescmds(utils.mutecall, [
[constants.EBTABLES_BIN, "-D", "FORWARD",
"--logical-in", self.brname, "-j", self.brname],
[EBTABLES_BIN, "-X", self.brname]])
[constants.EBTABLES_BIN, "-X", self.brname]])
for netif in self.netifs():
# removes veth pairs used for bridge-to-bridge connections
netif.shutdown()
@ -277,23 +284,19 @@ class LxBrNet(PyCoreNet):
def attach(self, netif):
if self.up:
try:
check_call([BRCTL_BIN, "addif", self.brname, netif.localname])
check_call([IP_BIN, "link", "set", netif.localname, "up"])
except Exception, e:
self.exception(coreapi.CORE_EXCP_LEVEL_ERROR, self.brname,
"Error joining interface %s to bridge %s: %s" % \
(netif.localname, self.brname, e))
subprocess.check_call([constants.BRCTL_BIN, "addif", self.brname, netif.localname])
subprocess.check_call([constants.IP_BIN, "link", "set", netif.localname, "up"])
except subprocess.CalledProcessError:
logger.exception("Error joining interface %s to bridge %s", netif.localname, self.brname)
return
PyCoreNet.attach(self, netif)
def detach(self, netif):
if self.up:
try:
check_call([BRCTL_BIN, "delif", self.brname, netif.localname])
except Exception, e:
self.exception(coreapi.CORE_EXCP_LEVEL_ERROR, self.brname,
"Error removing interface %s from bridge %s: %s" % \
(netif.localname, self.brname, e))
subprocess.check_call([constants.BRCTL_BIN, "delif", self.brname, netif.localname])
except subprocess.CalledProcessError:
logger.exception("Error removing interface %s from bridge %s", netif.localname, self.brname)
return
PyCoreNet.detach(self, netif)
@ -316,9 +319,10 @@ class LxBrNet(PyCoreNet):
return linked
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.
'''
"""
self._linked_lock.acquire()
if not self.linked(netif1, netif2):
self._linked_lock.release()
@ -328,9 +332,10 @@ class LxBrNet(PyCoreNet):
ebq.ebchange(self)
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.
'''
"""
self._linked_lock.acquire()
if self.linked(netif1, netif2):
self._linked_lock.release()
@ -339,15 +344,15 @@ class LxBrNet(PyCoreNet):
self._linked_lock.release()
ebq.ebchange(self)
def linkconfig(self, netif, bw = None, delay = None,
loss = None, duplicate = None, jitter = None, netif2 = None,
devname = None):
''' Configure link parameters by applying tc queuing disciplines on the
def linkconfig(self, netif, bw=None, delay=None, loss=None, duplicate=None,
jitter=None, netif2=None, devname=None):
"""
Configure link parameters by applying tc queuing disciplines on the
interface.
'''
"""
if devname is None:
devname = netif.localname
tc = [TC_BIN, "qdisc", "replace", "dev", devname]
tc = [constants.TC_BIN, "qdisc", "replace", "dev", devname]
parent = ["root"]
changed = False
if netif.setparam('bw', bw):
@ -359,17 +364,15 @@ class LxBrNet(PyCoreNet):
"burst", str(burst), "limit", str(limit)]
if bw > 0:
if self.up:
if (self.verbose):
self.info("linkconfig: %s" % \
([tc + parent + ["handle", "1:"] + tbf],))
check_call(tc + parent + ["handle", "1:"] + tbf)
logger.info("linkconfig: %s" % ([tc + parent + ["handle", "1:"] + tbf],))
subprocess.check_call(tc + parent + ["handle", "1:"] + tbf)
netif.setparam('has_tbf', True)
changed = True
elif netif.getparam('has_tbf') and bw <= 0:
tcd = [] + tc
tcd[2] = "delete"
if self.up:
check_call(tcd + parent)
subprocess.check_call(tcd + parent)
netif.setparam('has_tbf', False)
# removing the parent removes the child
netif.setparam('has_netem', False)
@ -406,24 +409,21 @@ class LxBrNet(PyCoreNet):
return
tc[2] = "delete"
if self.up:
if self.verbose:
self.info("linkconfig: %s" % \
([tc + parent + ["handle", "10:"]],))
check_call(tc + parent + ["handle", "10:"])
logger.info("linkconfig: %s" % ([tc + parent + ["handle", "10:"]],))
subprocess.check_call(tc + parent + ["handle", "10:"])
netif.setparam('has_netem', False)
elif len(netem) > 1:
if self.up:
if self.verbose:
self.info("linkconfig: %s" % \
([tc + parent + ["handle", "10:"] + netem],))
check_call(tc + parent + ["handle", "10:"] + netem)
logger.info("linkconfig: %s" % ([tc + parent + ["handle", "10:"] + netem],))
subprocess.check_call(tc + parent + ["handle", "10:"] + netem)
netif.setparam('has_netem', True)
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.
'''
sessionid = self.session.shortsessionid()
"""
sessionid = self.session.short_session_id()
try:
self_objid = '%x' % self.objid
except TypeError:
@ -434,19 +434,18 @@ class LxBrNet(PyCoreNet):
net_objid = '%s' % net.objid
localname = 'veth%s.%s.%s' % (self_objid, net_objid, sessionid)
if len(localname) >= 16:
raise ValueError, "interface local name '%s' too long" % \
localname
raise ValueError("interface local name '%s' too long" % localname)
name = 'veth%s.%s.%s' % (net_objid, self_objid, sessionid)
if len(name) >= 16:
raise ValueError, "interface name '%s' too long" % name
raise ValueError("interface name '%s' too long" % name)
netif = VEth(node=None, name=name, localname=localname,
mtu=1500, net=self, start=self.up)
self.attach(netif)
if net.up:
# this is similar to net.attach() but uses netif.name instead
# of localname
check_call([BRCTL_BIN, "addif", net.brname, netif.name])
check_call([IP_BIN, "link", "set", netif.name, "up"])
subprocess.check_call([constants.BRCTL_BIN, "addif", net.brname, netif.name])
subprocess.check_call([constants.IP_BIN, "link", "set", netif.name, "up"])
i = net.newifindex()
net._netif[i] = netif
with net._linked_lock:
@ -456,36 +455,37 @@ class LxBrNet(PyCoreNet):
return netif
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()).
'''
"""
for netif in self.netifs():
if hasattr(netif, 'othernet') and netif.othernet == net:
return netif
return None
def addrconfig(self, addrlist):
''' Set addresses on the bridge.
'''
"""
Set addresses on the bridge.
"""
if not self.up:
return
for addr in addrlist:
try:
check_call([IP_BIN, "addr", "add", str(addr), "dev", self.brname])
except Exception, e:
self.exception(coreapi.CORE_EXCP_LEVEL_ERROR, self.brname,
"Error adding IP address: %s" % e)
subprocess.check_call([constants.IP_BIN, "addr", "add", str(addr), "dev", self.brname])
except subprocess.CalledProcessError:
logger.exception("Error adding IP address")
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.
'''
"""
def __init__(self, session, remoteip=None, objid=None, name=None,
policy = "ACCEPT", localip = None, ttl = 255, key = None,
verbose = False, start = True):
LxBrNet.__init__(self, session = session, objid = objid,
name = name, verbose = verbose, policy = policy,
start = False)
policy="ACCEPT", localip=None, ttl=255, key=None, start=True):
LxBrNet.__init__(self, session=session, objid=objid, name=name, policy=policy, start=False)
self.grekey = key
if self.grekey is None:
self.grekey = self.session.sessionid ^ self.objid
@ -504,15 +504,17 @@ class GreTapBridge(LxBrNet):
self.startup()
def startup(self):
''' Creates a bridge and adds the gretap device to it.
'''
"""
Creates a bridge and adds the gretap device to it.
"""
LxBrNet.startup(self)
if self.gretap:
self.attach(self.gretap)
def shutdown(self):
''' Detach the gretap device and remove the bridge.
'''
"""
Detach the gretap device and remove the bridge.
"""
if self.gretap:
self.detach(self.gretap)
self.gretap.shutdown()
@ -520,11 +522,12 @@ class GreTapBridge(LxBrNet):
LxBrNet.shutdown(self)
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.
The 1st address in the provided list is remoteip, 2nd optionally
specifies localip.
'''
"""
if self.gretap:
raise ValueError, "gretap already exists for %s" % self.name
remoteip = addrlist[0].split('/')[0]
@ -537,7 +540,8 @@ class GreTapBridge(LxBrNet):
self.attach(self.gretap)
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).
'''
"""
self.grekey = key

View file

@ -1,35 +1,37 @@
#
# CORE
# 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 random, time
from core.api import coreapi
from core.misc.utils import *
from core.constants import *
from core.coreobj import PyCoreObj, PyCoreNode, PyCoreNetIf, Position
from core.netns.vif import VEth, TunTap
from core.emane.nodes import EmaneNode
import os
import random
import shutil
import signal
import string
import subprocess
import threading
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):
def __init__(self, session, objid = None, name = None, nodedir = None,
verbose = False, start = True):
PyCoreNode.__init__(self, session, objid, name, verbose=verbose,
start=start)
def __init__(self, session, objid=None, name=None, nodedir=None, start=True):
PyCoreNode.__init__(self, session, objid, name, start=start)
self.nodedir = nodedir
self.ctrlchnlname = \
os.path.abspath(os.path.join(self.session.sessiondir, self.name))
self.ctrlchnlname = os.path.abspath(os.path.join(self.session.session_dir, self.name))
self.vnodeclient = None
self.pid = None
self.up = False
@ -44,63 +46,69 @@ class SimpleLxcNode(PyCoreNode):
return True
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
the hostname.
'''
"""
if self.up:
raise Exception, "already up"
vnoded = ["%s/vnoded" % CORE_SBIN_DIR, "-v", "-c", self.ctrlchnlname,
raise Exception("already up")
vnoded = ["%s/vnoded" % constants.CORE_SBIN_DIR, "-v", "-c", self.ctrlchnlname,
"-l", self.ctrlchnlname + ".log",
"-p", self.ctrlchnlname + ".pid"]
if 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_NAME'] = str(self.name)
try:
tmp = subprocess.Popen(vnoded, stdout=subprocess.PIPE, env=env)
except OSError, e:
msg = "error running vnoded command: %s (%s)" % (vnoded, e)
self.exception(coreapi.CORE_EXCP_LEVEL_FATAL,
"SimpleLxcNode.startup()", msg)
raise Exception, msg
except OSError:
msg = "error running vnoded command: %s" % vnoded
logger.exception("SimpleLxcNode.startup(): %s", msg)
raise Exception(msg)
try:
self.pid = int(tmp.stdout.read())
tmp.stdout.close()
except Exception:
except ValueError:
msg = "vnoded failed to create a namespace; "
msg += "check kernel support and user priveleges"
self.exception(coreapi.CORE_EXCP_LEVEL_FATAL,
"SimpleLxcNode.startup()", msg)
logger.exception("SimpleLxcNode.startup(): %s", msg)
if tmp.wait():
raise Exception, ("command failed: %s" % vnoded)
self.vnodeclient = vnodeclient.VnodeClient(self.name,
self.ctrlchnlname)
self.info("bringing up loopback interface")
self.cmd([IP_BIN, "link", "set", "lo", "up"])
self.info("setting hostname: %s" % self.name)
raise Exception("command failed: %s" % vnoded)
self.vnodeclient = vnodeclient.VnodeClient(self.name, self.ctrlchnlname)
logger.info("bringing up loopback interface")
self.cmd([constants.IP_BIN, "link", "set", "lo", "up"])
logger.info("setting hostname: %s" % self.name)
self.cmd(["hostname", self.name])
self.up = True
def shutdown(self):
if not self.up:
return
while self._mounts:
source, target = self._mounts.pop(-1)
self.umount(target)
for netif in self.netifs():
netif.shutdown()
try:
os.kill(self.pid, signal.SIGTERM)
os.waitpid(self.pid, 0)
except OSError:
pass
logger.exception("error killing process")
try:
os.unlink(self.ctrlchnlname)
except OSError:
pass
logger.exception("error removing file")
self._netif.clear()
self.vnodeclient.close()
self.up = False
@ -134,21 +142,21 @@ class SimpleLxcNode(PyCoreNode):
def mount(self, source, target):
source = os.path.abspath(source)
self.info("mounting %s at %s" % (source, target))
logger.info("mounting %s at %s" % (source, target))
try:
shcmd = "mkdir -p '%s' && %s -n --bind '%s' '%s'" % \
(target, MOUNT_BIN, source, target)
(target, constants.MOUNT_BIN, source, target)
self.shcmd(shcmd)
self._mounts.append((source, target))
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):
self.info("unmounting '%s'" % target)
logger.info("unmounting '%s'" % target)
try:
self.cmd([UMOUNT_BIN, "-n", "-l", target])
self.cmd([constants.UMOUNT_BIN, "-n", "-l", target])
except:
self.warn("unmounting failed for %s" % target)
logger.exception("unmounting failed for %s" % target)
def newifindex(self):
with self.lock:
@ -159,13 +167,17 @@ class SimpleLxcNode(PyCoreNode):
try:
if ifindex is None:
ifindex = self.newifindex()
if ifname is None:
ifname = "eth%d" % ifindex
sessionid = self.session.shortsessionid()
sessionid = self.session.short_session_id()
try:
suffix = '%x.%s.%s' % (self.objid, ifindex, sessionid)
except TypeError:
suffix = '%s.%s.%s' % (self.objid, ifindex, sessionid)
localname = 'veth' + suffix
if len(localname) >= 16:
raise ValueError, "interface local name '%s' too long" % \
@ -173,20 +185,30 @@ class SimpleLxcNode(PyCoreNode):
name = localname + 'p'
if len(name) >= 16:
raise ValueError, "interface name '%s' too long" % name
ifclass = VEth
veth = ifclass(node = self, name = name, localname = localname,
mtu = 1500, net = net, start = self.up)
veth = VEth(node=self, name=name, localname=localname, mtu=1500, net=net, start=self.up)
if self.up:
check_call([IP_BIN, "link", "set", veth.name,
"netns", str(self.pid)])
self.cmd([IP_BIN, "link", "set", veth.name, "name", ifname])
subprocess.check_call([constants.IP_BIN, "link", "set", veth.name, "netns", str(self.pid)])
self.cmd([constants.IP_BIN, "link", "set", veth.name, "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:
self.addnetif(veth, ifindex)
except:
veth.shutdown()
del veth
raise
return ifindex
finally:
self.lock.release()
@ -198,7 +220,7 @@ class SimpleLxcNode(PyCoreNode):
ifindex = self.newifindex()
if ifname is None:
ifname = "eth%d" % ifindex
sessionid = self.session.shortsessionid()
sessionid = self.session.short_session_id()
localname = "tap%s.%s.%s" % (self.objid, ifindex, sessionid)
name = ifname
ifclass = TunTap
@ -217,15 +239,14 @@ class SimpleLxcNode(PyCoreNode):
def sethwaddr(self, ifindex, addr):
self._netif[ifindex].sethwaddr(addr)
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)])
if status:
self.exception(coreapi.CORE_EXCP_LEVEL_ERROR,
"SimpleLxcNode.sethwaddr()",
"error setting MAC address %s" % str(addr))
logger.error("error setting MAC address %s", str(addr))
def addaddr(self, ifindex, addr):
if self.up:
self.cmd([IP_BIN, "addr", "add", str(addr),
self.cmd([constants.IP_BIN, "addr", "add", str(addr),
"dev", self.ifname(ifindex)])
self._netif[ifindex].addaddr(addr)
@ -233,18 +254,18 @@ class SimpleLxcNode(PyCoreNode):
try:
self._netif[ifindex].deladdr(addr)
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),
"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):
addr = self.getaddr(self.ifname(ifindex), rescan=True)
for t in addrtypes:
if t not in self.valid_deladdrtype:
raise ValueError, "addr type must be in: " + \
" ".join(self.valid_deladdrtype)
raise ValueError("addr type must be in: " + " ".join(self.valid_deladdrtype))
for a in addr[t]:
self.deladdr(ifindex, a)
# update cached information
@ -252,13 +273,13 @@ class SimpleLxcNode(PyCoreNode):
def ifup(self, ifindex):
if self.up:
self.cmd([IP_BIN, "link", "set", self.ifname(ifindex), "up"])
self.cmd([constants.IP_BIN, "link", "set", self.ifname(ifindex), "up"])
def newnetif(self, net=None, addrlist=[], hwaddr=None,
ifindex=None, ifname=None):
self.lock.acquire()
try:
if isinstance(net, EmaneNode):
if nodeutils.is_node(net, NodeTypes.EMANE):
ifindex = self.newtuntap(ifindex=ifindex, ifname=ifname,
net=net)
# TUN/TAP is not ready for addressing yet; the device may
@ -268,18 +289,21 @@ class SimpleLxcNode(PyCoreNode):
self.attachnet(ifindex, net)
netif = self.netif(ifindex)
netif.sethwaddr(hwaddr)
for addr in maketuple(addrlist):
for addr in utils.maketuple(addrlist):
netif.addaddr(addr)
return ifindex
else:
ifindex = self.newveth(ifindex = ifindex, ifname = ifname,
net = net)
ifindex = self.newveth(ifindex=ifindex, ifname=ifname, net=net)
if net is not None:
self.attachnet(ifindex, net)
if hwaddr:
self.sethwaddr(ifindex, hwaddr)
for addr in maketuple(addrlist):
for addr in utils.maketuple(addrlist):
self.addaddr(ifindex, addr)
self.ifup(ifindex)
return ifindex
finally:
@ -291,15 +315,15 @@ class SimpleLxcNode(PyCoreNode):
for x in xrange(tmplen)])
tmp2 = "tmp." + "".join([random.choice(string.ascii_lowercase)
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])
check_call([IP_BIN, "link", "set", tmp1, "netns", str(self.pid)])
self.cmd([IP_BIN, "link", "set", tmp1, "name", ifname])
subprocess.call([constants.IP_BIN, "link", "set", tmp1, "netns", str(self.pid)])
self.cmd([constants.IP_BIN, "link", "set", tmp1, "name", ifname])
self.addnetif(PyCoreNetIf(self, ifname), self.newifindex())
check_call([IP_BIN, "link", "set", tmp2, "netns", str(othernode.pid)])
othernode.cmd([IP_BIN, "link", "set", tmp2, "name", otherifname])
subprocess.check_call([constants.IP_BIN, "link", "set", tmp2, "netns", str(othernode.pid)])
othernode.cmd([constants.IP_BIN, "link", "set", tmp2, "name", otherifname])
othernode.addnetif(PyCoreNetIf(othernode, otherifname),
othernode.newifindex())
@ -317,11 +341,9 @@ class SimpleLxcNode(PyCoreNode):
class LxcNode(SimpleLxcNode):
def __init__(self, session, objid=None, name=None,
nodedir = None, bootsh = "boot.sh", verbose = False,
start = True):
nodedir=None, bootsh="boot.sh", start=True):
super(LxcNode, self).__init__(session=session, objid=objid,
name = name, nodedir = nodedir,
verbose = verbose, start = start)
name=name, nodedir=nodedir, start=start)
self.bootsh = bootsh
if start:
self.startup()
@ -339,10 +361,8 @@ class LxcNode(SimpleLxcNode):
super(LxcNode, self).startup()
self.privatedir("/var/run")
self.privatedir("/var/log")
except OSError, e:
self.warn("Error with LxcNode.startup(): %s" % e)
self.exception(coreapi.CORE_EXCP_LEVEL_ERROR,
"LxcNode.startup()", "%s" % e)
except OSError:
logger.exception("error during LxcNode.startup()")
finally:
self.lock.release()
@ -360,20 +380,20 @@ class LxcNode(SimpleLxcNode):
def privatedir(self, path):
if path[0] != "/":
raise ValueError, "path not fully qualified: " + path
hostpath = os.path.join(self.nodedir,
os.path.normpath(path).strip('/').replace('/', '.'))
raise ValueError("path not fully qualified: %s" % path)
hostpath = os.path.join(self.nodedir, os.path.normpath(path).strip('/').replace('/', '.'))
try:
os.mkdir(hostpath)
except OSError:
pass
except Exception, e:
raise Exception, e
logger.exception("error creating directory: %s", hostpath)
self.mount(hostpath, path)
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.
"""
dirname, basename = os.path.split(filename)
if not basename:
raise ValueError, "no basename for filename: " + filename
@ -395,16 +415,15 @@ class LxcNode(SimpleLxcNode):
f.write(contents)
os.chmod(f.name, mode)
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):
''' 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.
'''
"""
hostfilename = self.hostfilename(filename)
shutil.copy2(srcfilename, hostfilename)
if mode is not None:
os.chmod(hostfilename, mode)
self.info("copied nodefile: '%s'; mode: %s" % (hostfilename, mode))
logger.info("copied nodefile: '%s'; mode: %s" % (hostfilename, mode))

View file

@ -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
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
by invoking the vcmd shell command.
'''
"""
import os, stat, sys
from core.constants import *
import os
import stat
from core import constants
from core.misc import log
logger = log.get_logger(__name__)
USE_VCMD_MODULE = True
@ -22,7 +20,8 @@ if USE_VCMD_MODULE:
else:
import subprocess
VCMD = os.path.join(CORE_SBIN_DIR, "vcmd")
VCMD = os.path.join(constants.CORE_SBIN_DIR, "vcmd")
class VnodeClient(object):
def __init__(self, name, ctrlchnlname):
@ -34,9 +33,6 @@ class VnodeClient(object):
self.cmdchnl = None
self._addr = {}
def warn(self, msg):
print >> sys.stderr, "%s: %s" % (self.name, msg)
def connected(self):
if USE_VCMD_MODULE:
return self.cmdchnl.connected()
@ -48,11 +44,12 @@ class VnodeClient(object):
self.cmdchnl.close()
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).
"""
if USE_VCMD_MODULE:
if not self.cmdchnl.connected():
raise ValueError, "self.cmdchnl not connected"
raise ValueError("self.cmdchnl not connected")
tmp = self.cmdchnl.qcmd(args)
if not wait:
return tmp
@ -62,19 +59,19 @@ class VnodeClient(object):
mode = os.P_WAIT
else:
mode = os.P_NOWAIT
tmp = os.spawnlp(mode, VCMD, VCMD, "-c",
self.ctrlchnlname, "-q", "--", *args)
tmp = os.spawnlp(mode, VCMD, VCMD, "-c", self.ctrlchnlname, "-q", "--", *args)
if not wait:
return 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
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
is folded into the stdout result string.
'''
"""
cmdid, cmdin, cmdout, cmderr = self.popen(args)
result = cmdout.read()
result += cmderr.read()
@ -82,40 +79,37 @@ class VnodeClient(object):
cmdout.close()
cmderr.close()
status = cmdid.wait()
return (status, result)
return status, result
def popen(self, args):
if USE_VCMD_MODULE:
if not self.cmdchnl.connected():
raise ValueError, "self.cmdchnl not connected"
raise ValueError("self.cmdchnl not connected")
return self.cmdchnl.popen(args)
else:
cmd = [VCMD, "-c", self.ctrlchnlname, "--"]
cmd.extend(args)
tmp = subprocess.Popen(cmd, stdin = subprocess.PIPE,
stdout = subprocess.PIPE,
stderr = subprocess.PIPE)
tmp = subprocess.Popen(cmd, stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
return tmp, tmp.stdin, tmp.stdout, tmp.stderr
def icmd(self, args):
return os.spawnlp(os.P_WAIT, VCMD, VCMD, "-c", self.ctrlchnlname,
"--", *args)
return os.spawnlp(os.P_WAIT, VCMD, VCMD, "-c", self.ctrlchnlname, "--", *args)
def redircmd(self, infd, outfd, errfd, args, wait=True):
'''
"""
Execute a command on a node with standard input, output, and
error redirected according to the given file descriptors.
'''
"""
if not USE_VCMD_MODULE:
raise NotImplementedError
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)
if not wait:
return tmp
tmp = tmp.wait()
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
def term(self, sh="/bin/sh"):
@ -137,9 +131,10 @@ class VnodeClient(object):
if ifname in self._addr and not rescan:
return self._addr[ifname]
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)
cmdin.close()
for line in cmdout:
line = line.strip().split()
if line[0] == "link/ether":
@ -152,17 +147,16 @@ class VnodeClient(object):
elif line[3] == "link":
tmp["inet6link"].append(line[1])
else:
self.warn("unknown scope: %s" % line[3])
else:
pass
logger.warn("unknown scope: %s" % line[3])
err = cmderr.read()
cmdout.close()
cmderr.close()
status = cmdid.wait()
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:
self.warn("error output: %s" % err)
logger.warn("error output: %s" % err)
self._addr[ifname] = tmp
return tmp
@ -195,34 +189,32 @@ class VnodeClient(object):
cmderr.close()
status = cmdid.wait()
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:
self.warn("error output: %s" % err)
logger.warn("error output: %s" % err)
if ifname is not None:
return stats[ifname]
else:
return stats
def createclients(sessiondir, clientcls = VnodeClient,
cmdchnlfilterfunc = None):
direntries = map(lambda x: os.path.join(sessiondir, x),
os.listdir(sessiondir))
def createclients(sessiondir, clientcls=VnodeClient, cmdchnlfilterfunc=None):
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)
if cmdchnlfilterfunc:
cmdchnls = filter(cmdchnlfilterfunc, cmdchnls)
cmdchnls.sort()
return map(lambda x: clientcls(os.path.basename(x), x), cmdchnls)
def createremoteclients(sessiondir, clientcls = VnodeClient,
filterfunc = None):
''' Creates remote VnodeClients, for nodes emulated on other machines. The
def createremoteclients(sessiondir, clientcls=VnodeClient, filterfunc=None):
"""
Creates remote VnodeClients, for nodes emulated on other machines. The
session.Broker writes a n1.conf/server file having the server's info.
'''
direntries = map(lambda x: os.path.join(sessiondir, x),
os.listdir(sessiondir))
"""
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: os.path.exists(os.path.join(x, "server")),
nodedirs)
nodedirs = filter(lambda x: os.path.exists(os.path.join(x, "server")), nodedirs)
if filterfunc:
nodedirs = filter(filterfunc, nodedirs)
nodedirs.sort()

View file

@ -1,32 +1,24 @@
#
# CORE
# 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
"""
PhysicalNode class for including real systems in the emulated network.
"""
from core.misc.ipaddr import *
from core.misc.utils import *
from core.constants import *
from core.api import coreapi
from core.coreobj import PyCoreNode, PyCoreNetIf
from core.emane.nodes import EmaneNode
if os.uname()[0] == "Linux":
import os
import subprocess
import threading
from core import constants
from core.coreobj import PyCoreNode
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.vif import GreTap
elif os.uname()[0] == "FreeBSD":
from core.bsd.vnet import NetgraphNet
logger = log.get_logger(__name__)
class PhysicalNode(PyCoreNode):
def __init__(self, session, objid = None, name = None,
nodedir = None, verbose = False, start = True):
PyCoreNode.__init__(self, session, objid, name, verbose=verbose,
start=start)
def __init__(self, session, objid=None, name=None, nodedir=None, start=True):
PyCoreNode.__init__(self, session, objid, name, start=start)
self.nodedir = nodedir
self.up = start
self.lock = threading.RLock()
@ -46,9 +38,8 @@ class PhysicalNode(PyCoreNode):
self.makenodedir()
# self.privatedir("/var/run")
# self.privatedir("/var/log")
except OSError, e:
self.exception(coreapi.CORE_EXCP_LEVEL_ERROR,
"PhysicalNode.startup()", e)
except OSError:
logger.exception("PhysicalNode.startup()")
finally:
self.lock.release()
@ -64,16 +55,17 @@ class PhysicalNode(PyCoreNode):
self.rmnodedir()
self.lock.release()
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.
'''
"""
return sh
def cmd(self, args, wait=True):
''' run a command on the physical node
'''
"""
run a command on the physical node
"""
os.chdir(self.nodedir)
try:
if wait:
@ -82,87 +74,88 @@ class PhysicalNode(PyCoreNode):
else:
# os.spawnlp(os.P_NOWAIT, args)
subprocess.Popen(args)
except CalledProcessError, e:
self.warn("cmd exited with status %s: %s" % (e, str(args)))
except subprocess.CalledProcessError:
logger.exception("cmd exited with status: %s", str(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)
# in Python 2.7 we can use subprocess.check_output() here
tmp = subprocess.Popen(args, stdin=open(os.devnull, 'r'),
stdout=subprocess.PIPE,
stderr=subprocess.STDOUT)
result, err = tmp.communicate() # err will always be None
# err will always be None
result, err = tmp.communicate()
status = tmp.wait()
return (status, result)
return status, result
def shcmd(self, cmdstr, sh="/bin/sh"):
return self.cmd([sh, "-c", cmdstr])
def sethwaddr(self, ifindex, addr):
''' same as SimpleLxcNode.sethwaddr()
'''
"""
same as SimpleLxcNode.sethwaddr()
"""
self._netif[ifindex].sethwaddr(addr)
ifname = self.ifname(ifindex)
if self.up:
(status, result) = self.cmdresult([IP_BIN, "link", "set", "dev",
ifname, "address", str(addr)])
(status, result) = self.cmdresult(
[constants.IP_BIN, "link", "set", "dev", ifname, "address", str(addr)])
if status:
self.exception(coreapi.CORE_EXCP_LEVEL_ERROR,
"PhysicalNode.sethwaddr()",
"error setting MAC address %s" % str(addr))
logger.error("error setting MAC address %s", str(addr))
def addaddr(self, ifindex, addr):
''' same as SimpleLxcNode.addaddr()
'''
"""
same as SimpleLxcNode.addaddr()
"""
if self.up:
self.cmd([IP_BIN, "addr", "add", str(addr),
"dev", self.ifname(ifindex)])
self.cmd([constants.IP_BIN, "addr", "add", str(addr), "dev", self.ifname(ifindex)])
self._netif[ifindex].addaddr(addr)
def deladdr(self, ifindex, addr):
''' same as SimpleLxcNode.deladdr()
'''
"""
same as SimpleLxcNode.deladdr()
"""
try:
self._netif[ifindex].deladdr(addr)
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),
"dev", self.ifname(ifindex)])
self.cmd([constants.IP_BIN, "addr", "del", str(addr), "dev", self.ifname(ifindex)])
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
the emulation, no new interface is created; instead, adopt the
GreTap netif as the node interface.
'''
"""
netif.name = "gt%d" % ifindex
netif.node = self
self.addnetif(netif, ifindex)
# use a more reasonable name, e.g. "gt0" instead of "gt.56286.150"
if self.up:
self.cmd([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", "dev", netif.localname, "down"])
self.cmd([constants.IP_BIN, "link", "set", netif.localname, "name", netif.name])
netif.localname = netif.name
if hwaddr:
self.sethwaddr(ifindex, hwaddr)
for addr in maketuple(addrlist):
for addr in utils.maketuple(addrlist):
self.addaddr(ifindex, addr)
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,
loss=None, duplicate=None, jitter=None, netif2=None):
''' Apply tc queing disciplines using LxBrNet.linkconfig()
'''
if os.uname()[0] == "Linux":
"""
Apply tc queing disciplines using LxBrNet.linkconfig()
"""
netcls = LxBrNet
elif os.uname()[0] == "FreeBSD":
netcls = NetgraphNet
else:
raise NotImplementedError, "unsupported platform"
# borrow the tc qdisc commands from LxBrNet.linkconfig()
tmp = netcls(session=self.session, start=False)
tmp.up = True
@ -193,8 +186,7 @@ class PhysicalNode(PyCoreNode):
# tunnel to net not built yet, so build it now and adopt it
gt = self.session.broker.addnettunnel(net.objid)
if gt is None or len(gt) != 1:
self.session.warn("Error building tunnel from PhysicalNode."
"newnetif()")
logger.warn("Error building tunnel from PhysicalNode.newnetif()")
gt = gt[0]
net.detach(gt)
self.adoptnetif(gt, ifindex, hwaddr, addrlist)
@ -208,7 +200,6 @@ class PhysicalNode(PyCoreNode):
self.adoptnetif(netif, ifindex, hwaddr, addrlist)
return ifindex
def privatedir(self, path):
if path[0] != "/":
raise ValueError, "path not fully qualified: " + path
@ -217,30 +208,29 @@ class PhysicalNode(PyCoreNode):
try:
os.mkdir(hostpath)
except OSError:
pass
except Exception, e:
raise Exception, e
logger.exception("error creating directory: %s", hostpath)
self.mount(hostpath, path)
def mount(self, source, target):
source = os.path.abspath(source)
self.info("mounting %s at %s" % (source, target))
logger.info("mounting %s at %s" % (source, target))
try:
os.makedirs(target)
except OSError:
pass
try:
self.cmd([MOUNT_BIN, "--bind", source, target])
self.cmd([constants.MOUNT_BIN, "--bind", source, target])
self._mounts.append((source, target))
except OSError:
logger.exception("error making directories")
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):
self.info("unmounting '%s'" % target)
logger.info("unmounting '%s'" % target)
try:
self.cmd([UMOUNT_BIN, "-l", target])
self.cmd([constants.UMOUNT_BIN, "-l", target])
except:
self.warn("unmounting failed for %s" % target)
logger.exception("unmounting failed for %s", target)
def opennodefile(self, filename, mode="w"):
dirname, basename = os.path.split(filename)
@ -260,6 +250,4 @@ class PhysicalNode(PyCoreNode):
f.write(contents)
os.chmod(f.name, mode)
f.close()
self.info("created nodefile: '%s'; mode: 0%o" % (f.name, mode))
logger.info("created nodefile: '%s'; mode: 0%o" % (f.name, mode))

View file

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

View file

@ -1,32 +1,49 @@
#
# 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
'''
"""
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
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__)
class Bunch:
"""
Helper class for recording a collection of attributes.
"""
def __init__(self, **kwds):
self.__dict__.update(kwds)
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
when a node position or link has changed.
'''
"""
DEFAULT_SDT_URL = "tcp://127.0.0.1:50000/"
# default altitude (in meters) for flyto view
DEFAULT_ALT = 2500
# TODO: read in user's nodes.conf here; below are default node types
# from the GUI
DEFAULT_SPRITES = [('router', 'router.gif'), ('host', 'host.gif'),
DEFAULT_SPRITES = [
('router', 'router.gif'), ('host', 'host.gif'),
('PC', 'pc.gif'), ('mdr', 'mdr.gif'),
('prouter', 'router_green.gif'), ('xen', 'xen.gif'),
('hub', 'hub.gif'), ('lanswitch', 'lanswitch.gif'),
@ -34,28 +51,28 @@ class Sdt(object):
('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):
"""
Creates a Sdt instance.
:param core.session.Session session: session this manager is tied to
:return: nothing
"""
self.session = session
self.sock = None
self.connected = False
self.showerror = True
self.url = self.DEFAULT_SDT_URL
self.verbose = self.session.getcfgitembool('verbose', False)
# node information for remote nodes not in session._objs
# local nodes also appear here since their obj may not exist yet
self.remotes = {}
session.broker.handlers.add(self.handledistributed)
session.broker.handlers += (self.handledistributed,)
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.
'''
"""
if not hasattr(self.session.options, 'enablesdt'):
return False
enabled = self.session.options.enablesdt
@ -64,9 +81,10 @@ class Sdt(object):
return False
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
'''
"""
url = None
if hasattr(self.session.options, 'sdturl'):
if self.session.options.sdturl != "":
@ -78,132 +96,135 @@ class Sdt(object):
self.protocol = self.url.scheme
def connect(self, flags=0):
''' Connect to the SDT address/port if enabled.
'''
"""
Connect to the SDT address/port if enabled.
"""
if not self.is_enabled():
return False
if self.connected:
return True
if self.session.getstate() == coreapi.CORE_EVENT_SHUTDOWN_STATE:
if self.session.state == EventTypes.SHUTDOWN_STATE.value:
return False
self.seturl()
if self.showerror:
self.session.info("connecting to SDT at %s://%s" \
% (self.protocol, self.address))
logger.info("connecting to SDT at %s://%s" % (self.protocol, self.address))
if self.sock is None:
try:
if (self.protocol.lower() == 'udp'):
if self.protocol.lower() == 'udp':
self.sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
self.sock.connect(self.address)
else:
# Default to tcp
self.sock = socket.create_connection(self.address, 5)
except Exception, e:
if self.showerror:
self.session.warn("SDT socket connect error: %s" % e)
self.showerror = False
except IOError:
logger.exception("SDT socket connect error")
return False
if not self.initialize():
return False
self.connected = True
# refresh all objects in SDT3D when connecting after session start
if not flags & coreapi.CORE_API_ADD_FLAG:
if not self.sendobjs():
if not flags & MessageFlags.ADD.value and not self.sendobjs():
return False
return True
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.
'''
if not self.cmd('path "%s/icons/normal"' % CORE_DATA_DIR):
"""
if not self.cmd('path "%s/icons/normal"' % constants.CORE_DATA_DIR):
return False
# 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)):
return False
(lat, long) = self.session.location.refgeo[:2]
return self.cmd('flyto %.6f,%.6f,%d' % (long, lat, self.DEFAULT_ALT))
def disconnect(self):
if self.sock:
try:
self.sock.close()
except:
pass
except IOError:
logger.error("error closing socket")
finally:
self.sock = None
self.connected = False
def shutdown(self):
''' Invoked from Session.shutdown() and Session.checkshutdown().
'''
"""
Invoked from Session.shutdown() and Session.checkshutdown().
"""
self.cmd('clear all')
self.disconnect()
self.showerror = True
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
there is no socket listener.
'''
"""
if self.sock is None:
return False
try:
if self.verbose:
self.session.info("sdt: %s" % cmdstr)
logger.info("sdt: %s" % cmdstr)
self.sock.sendall("%s\n" % cmdstr)
return True
except Exception, e:
if self.showerror:
self.session.warn("SDT connection error: %s" % e)
self.showerror = False
except IOError:
logger.exception("SDT connection error")
self.sock = None
self.connected = False
return False
def updatenode(self, nodenum, flags, x, y, z,
name=None, type=None, icon=None):
''' Node is updated from a Node Message or mobility script.
'''
def updatenode(self, nodenum, flags, x, y, z, name=None, type=None, icon=None):
"""
Node is updated from a Node Message or mobility script.
"""
if not self.connect():
return
if flags & coreapi.CORE_API_DEL_FLAG:
if flags & MessageFlags.DELETE.value:
self.cmd('delete node,%d' % nodenum)
return
if x is None or y is None:
return
(lat, long, alt) = self.session.location.getgeo(x, y, z)
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:
type = name
icon = icon.replace("$CORE_DATA_DIR", CORE_DATA_DIR)
icon = icon.replace("$CORE_CONF_DIR", CORE_CONF_DIR)
icon = icon.replace("$CORE_DATA_DIR", constants.CORE_DATA_DIR)
icon = icon.replace("$CORE_CONF_DIR", constants.CORE_CONF_DIR)
self.cmd('sprite %s image %s' % (type, icon))
self.cmd('node %d type %s label on,"%s" %s' % \
(nodenum, type, name, pos))
self.cmd('node %d type %s label on,"%s" %s' % (nodenum, type, name, pos))
else:
self.cmd('node %d %s' % (nodenum, pos))
def updatenodegeo(self, nodenum, lat, long, alt):
''' Node is updated upon receiving an EMANE Location Event.
"""
Node is updated upon receiving an EMANE Location Event.
TODO: received Node Message with lat/long/alt.
'''
"""
if not self.connect():
return
pos = "pos %.6f,%.6f,%.6f" % (long, lat, alt)
self.cmd('node %d %s' % (nodenum, pos))
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.
"""
if node1num is None or node2num is None:
return
if not self.connect():
return
if flags & coreapi.CORE_API_DEL_FLAG:
if flags & MessageFlags.DELETE.value:
self.cmd('delete link,%s,%s' % (node1num, node2num))
elif flags & coreapi.CORE_API_ADD_FLAG:
elif flags & MessageFlags.ADD.value:
attr = ""
if wireless:
attr = " line green,2"
@ -212,13 +233,14 @@ class Sdt(object):
self.cmd('link %s,%s%s' % (node1num, node2num, attr))
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
links will only be drawn when they have been updated (e.g. moved).
'''
"""
nets = []
with self.session._objslock:
for obj in self.session.objs():
with self.session._objects_lock:
for obj in self.session.objects.itervalues():
if isinstance(obj, PyCoreNet):
nets.append(obj)
if not isinstance(obj, PyCoreObj):
@ -226,88 +248,88 @@ class Sdt(object):
(x, y, z) = obj.getposition()
if x is None or y is None:
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)
for nodenum in sorted(self.remotes.keys()):
r = self.remotes[nodenum]
(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)
for net in nets:
# use tolinkmsgs() to handle various types of links
msgs = net.tolinkmsgs(flags = coreapi.CORE_API_ADD_FLAG)
for msg in msgs:
msghdr = msg[:coreapi.CoreMessage.hdrsiz]
flags = coreapi.CoreMessage.unpackhdr(msghdr)[1]
m = coreapi.CoreLinkMessage(flags, msghdr,
msg[coreapi.CoreMessage.hdrsiz:])
n1num = m.gettlv(coreapi.CORE_TLV_LINK_N1NUMBER)
n2num = m.gettlv(coreapi.CORE_TLV_LINK_N2NUMBER)
link_msg_type = m.gettlv(coreapi.CORE_TLV_LINK_TYPE)
if isinstance(net, nodes.WlanNode) or \
isinstance(net, nodes.EmaneNode):
if (n1num == net.objid):
messages = net.all_link_data(flags=MessageFlags.ADD.value)
for message in messages:
msghdr = message[:coreapi.CoreMessage.header_len]
flags = coreapi.CoreMessage.unpack_header(msghdr)[1]
m = coreapi.CoreLinkMessage(flags, msghdr, message[coreapi.CoreMessage.header_len:])
n1num = m.get_tlv(LinkTlvs.N1_NUMBER.value)
n2num = m.get_tlv(LinkTlvs.N2_NUMBER.value)
link_msg_type = m.get_tlv(LinkTlvs.TYPE.value)
if nodeutils.is_node(net, (NodeTypes.WIRELESS_LAN, NodeTypes.EMANE)):
if n1num == net.objid:
continue
wl = (link_msg_type == coreapi.CORE_LINK_WIRELESS)
self.updatelink(n1num, n2num, coreapi.CORE_API_ADD_FLAG, wl)
wl = link_msg_type == LinkTypes.WIRELESS.value
self.updatelink(n1num, n2num, MessageFlags.ADD.value, wl)
for n1num in sorted(self.remotes.keys()):
r = self.remotes[n1num]
for (n2num, wl) in r.links:
self.updatelink(n1num, n2num, coreapi.CORE_API_ADD_FLAG, wl)
for n2num, wl in r.links:
self.updatelink(n1num, n2num, MessageFlags.ADD.value, wl)
def handledistributed(self, msg):
''' 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
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)
"""
if message.message_type == MessageTypes.LINK.value:
return self.handlelinkmsg(message)
elif message.message_type == MessageTypes.NODE.value:
return self.handlenodemsg(message)
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
self.remotes for remote nodes (or those not yet instantiated).
'''
"""
# for distributed sessions to work properly, the SDT option should be
# enabled prior to starting the session
if not self.is_enabled():
return False
# 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:
return
x = msg.gettlv(coreapi.CORE_TLV_NODE_XPOS)
y = msg.gettlv(coreapi.CORE_TLV_NODE_YPOS)
x = msg.get_tlv(NodeTlvs.X_POSITION.value)
y = msg.get_tlv(NodeTlvs.Y_POSITION.value)
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)
model = msg.gettlv(coreapi.CORE_TLV_NODE_MODEL)
icon = msg.gettlv(coreapi.CORE_TLV_NODE_ICON)
nodetype = msg.get_tlv(NodeTlvs.TYPE.value)
model = msg.get_tlv(NodeTlvs.MODEL.value)
icon = msg.get_tlv(NodeTlvs.ICON.value)
net = False
if nodetype == coreapi.CORE_NODE_DEF or \
nodetype == coreapi.CORE_NODE_PHYS or \
nodetype == coreapi.CORE_NODE_XEN:
if nodetype == NodeTypes.DEFAULT.value or \
nodetype == NodeTypes.PHYSICAL.value or \
nodetype == NodeTypes.XEN.value:
if model is None:
model = "router"
type = model
elif nodetype != None:
type = coreapi.node_class(nodetype).type
elif nodetype is not None:
type = nodeutils.get_node_class(NodeTypes(nodetype)).type
net = True
else:
type = None
try:
node = self.session.obj(nodenum)
node = self.session.get_object(nodenum)
except KeyError:
node = None
if node:
self.updatenode(node.objid, msg.flags, x, y, z,
node.name, node.type, node.icon)
self.updatenode(node.objid, msg.flags, x, y, z, node.name, node.type, node.icon)
else:
if nodenum in self.remotes:
remote = self.remotes[nodenum]
@ -318,29 +340,29 @@ class Sdt(object):
if icon is None:
icon = remote.icon
else:
remote = self.Bunch(objid=nodenum, type=type, icon=icon,
name=name, net=net, links=set())
remote = Bunch(objid=nodenum, type=type, icon=icon, name=name, net=net, links=set())
self.remotes[nodenum] = remote
remote.pos = (x, y, z)
self.updatenode(nodenum, msg.flags, x, y, z, name, type, icon)
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
the SDT display at a later time.
'''
"""
if not self.is_enabled():
return False
nodenum1 = msg.gettlv(coreapi.CORE_TLV_LINK_N1NUMBER)
nodenum2 = msg.gettlv(coreapi.CORE_TLV_LINK_N2NUMBER)
link_msg_type = msg.gettlv(coreapi.CORE_TLV_LINK_TYPE)
nodenum1 = msg.get_tlv(LinkTlvs.N1_NUMBER.value)
nodenum2 = msg.get_tlv(LinkTlvs.N2_NUMBER.value)
link_msg_type = msg.get_tlv(LinkTlvs.TYPE.value)
# this filters out links to WLAN and EMANE nodes which are not drawn
if self.wlancheck(nodenum1):
return
wl = (link_msg_type == coreapi.CORE_LINK_WIRELESS)
wl = link_msg_type == LinkTypes.WIRELESS.value
if nodenum1 in self.remotes:
r = self.remotes[nodenum1]
if msg.flags & coreapi.CORE_API_DEL_FLAG:
if msg.flags & MessageFlags.DELETE.value:
if (nodenum2, wl) in r.links:
r.links.remove((nodenum2, wl))
else:
@ -348,18 +370,19 @@ class Sdt(object):
self.updatelink(nodenum1, nodenum2, msg.flags, wireless=wl)
def wlancheck(self, nodenum):
''' Helper returns True if a node number corresponds to a WlanNode
"""
Helper returns True if a node number corresponds to a WlanNode
or EmaneNode.
'''
"""
if nodenum in self.remotes:
type = self.remotes[nodenum].type
if type in ("wlan", "emane"):
return True
else:
try:
n = self.session.obj(nodenum)
n = self.session.get_object(nodenum)
except KeyError:
return False
if isinstance(n, (nodes.WlanNode, nodes.EmaneNode)):
if nodeutils.is_node(n, (NodeTypes.WIRELESS_LAN, NodeTypes.EMANE)):
return True
return False

File diff suppressed because it is too large Load diff

View file

@ -1,6 +1,6 @@
"""Services
"""
Services
Services available to nodes can be put in this directory. Everything listed in
__all__ is automatically loaded by the main core module.
"""
__all__ = ["quagga", "nrl", "xorp", "bird", "utility", "security", "ucarp", "dockersvc", 'startup']

View file

@ -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.
'''
"""
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):
''' Bird router support
'''
"""
Bird router support
"""
_name = "bird"
_group = "BIRD"
_depends = ()
@ -31,8 +22,9 @@ class Bird(CoreService):
@classmethod
def generateconfig(cls, node, filename, services):
''' Return the bird.conf file contents.
'''
"""
Return the bird.conf file contents.
"""
if filename == cls._configs[0]:
return cls.generateBirdConf(node, services)
else:
@ -40,8 +32,9 @@ class Bird(CoreService):
@staticmethod
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():
if hasattr(ifc, 'control') and ifc.control == True:
continue
@ -53,10 +46,11 @@ class Bird(CoreService):
@classmethod
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()
hooks that are invoked here.
'''
"""
cfg = """\
/* Main configuration file for BIRD. This is ony a template,
* you will *need* to customize it according to your needs
@ -90,10 +84,12 @@ protocol device {
return cfg
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.
'''
"""
_name = "BirdDaemon"
_group = "BIRD"
@ -118,14 +114,17 @@ class BirdService(CoreService):
cfg = ""
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
return cfg
class BirdBgp(BirdService):
'''BGP BIRD Service (configuration generation)'''
"""
BGP BIRD Service (configuration generation)
"""
_name = "BIRD_BGP"
_custom_needed = True
@ -152,8 +151,11 @@ protocol bgp {
"""
class BirdOspf(BirdService):
'''OSPF BIRD Service (configuration generation)'''
"""
OSPF BIRD Service (configuration generation)
"""
_name = "BIRD_OSPFv2"
@ -176,7 +178,9 @@ class BirdOspf(BirdService):
class BirdRadv(BirdService):
'''RADV BIRD Service (configuration generation)'''
"""
RADV BIRD Service (configuration generation)
"""
_name = "BIRD_RADV"
@ -202,7 +206,9 @@ class BirdRadv(BirdService):
class BirdRip(BirdService):
'''RIP BIRD Service (configuration generation)'''
"""
RIP BIRD Service (configuration generation)
"""
_name = "BIRD_RIP"
@ -222,7 +228,9 @@ class BirdRip(BirdService):
class BirdStatic(BirdService):
'''Static Bird Service (configuration generation)'''
"""
Static Bird Service (configuration generation)
"""
_name = "BIRD_static"
_custom_needed = True
@ -240,10 +248,11 @@ class BirdStatic(BirdService):
return cfg
def load_services():
# Register all protocols
addservice(Bird)
addservice(BirdOspf)
addservice(BirdBgp)
#addservice(BirdRadv) # untested
addservice(BirdRip)
addservice(BirdStatic)
ServiceManager.add(Bird)
ServiceManager.add(BirdOspf)
ServiceManager.add(BirdBgp)
# ServiceManager.add(BirdRadv) # untested
ServiceManager.add(BirdRip)
ServiceManager.add(BirdStatic)

View file

@ -1,12 +1,5 @@
#
# CORE
# 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.
"""
Docker service allows running docker containers within CORE nodes.
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
@ -102,23 +95,25 @@ Limitations:
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
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:
from docker import Client
except Exception:
pass
except ImportError:
logger.error("failure to import docker")
from core.service import CoreService, addservice
from core.misc.ipaddr import IPv4Prefix, IPv6Prefix
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.
'''
"""
_name = "Docker"
_group = "Docker"
_depends = ()
@ -132,9 +127,10 @@ class DockerService(CoreService):
@classmethod
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.
'''
"""
cfg = "#!/bin/sh\n"
cfg += "# auto-generated by Docker (docker.py)\n"
# Docker likes to think it has DNS set up or it complains.
@ -159,10 +155,14 @@ done
""" % (cls._image,)
return cfg
addservice(DockerService)
def load_services():
ServiceManager.add(DockerService)
# This auto-loads Docker images having a :core tag, adding them to the list
# 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():
client = Client(version='1.10')
images = client.images()
@ -176,7 +176,6 @@ for image in images:
if u':core' not in repo:
continue
dockerid = repo.encode('ascii', 'ignore').split(':')[0]
SubClass = type('SubClass', (DockerService,),
{'_name': dockerid, '_image': dockerid})
addservice(SubClass)
sub_class = type('SubClass', (DockerService,), {'_name': dockerid, '_image': dockerid})
ServiceManager.add(sub_class)
del images

View file

@ -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:
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):
''' Parent class for NRL services. Defines properties and methods
"""
Parent class for NRL services. Defines properties and methods
common to NRL's routing daemons.
'''
"""""
_name = "Protean"
_group = "ProtoSvc"
_depends = ()
@ -34,21 +29,23 @@ class NrlService(CoreService):
@staticmethod
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
interface's prefix length, so e.g. '/32' can turn into '/24'.
'''
"""
for ifc in node.netifs():
if hasattr(ifc, 'control') and ifc.control == True:
continue
for a in ifc.addrlist:
if a.find(".") >= 0:
addr = a.split('/')[0]
pre = IPv4Prefix("%s/%s" % (addr, prefixlen))
pre = Ipv4Prefix("%s/%s" % (addr, prefixlen))
return str(pre)
# raise ValueError, "no IPv4 address found"
return "0.0.0.0/%s" % prefixlen
class MgenSinkService(NrlService):
_name = "MGEN_Sink"
_configs = ("sink.mgen",)
@ -61,7 +58,7 @@ class MgenSinkService(NrlService):
def generateconfig(cls, node, filename, services):
cfg = "0.0 LISTEN UDP 5000\n"
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
return cfg
@ -69,13 +66,13 @@ class MgenSinkService(NrlService):
def getstartup(cls, node, services):
cmd = cls._startup[0]
cmd += " output /tmp/mgen_%s.log" % node.name
return (cmd, )
return cmd,
addservice(MgenSinkService)
class NrlNhdp(NrlService):
''' NeighborHood Discovery Protocol for MANET networks.
'''
"""
NeighborHood Discovery Protocol for MANET networks.
"""
_name = "NHDP"
_startup = ("nrlnhdp",)
_shutdown = ("killall nrlnhdp",)
@ -83,8 +80,9 @@ class NrlNhdp(NrlService):
@classmethod
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 += " -l /var/log/nrlnhdp.log"
cmd += " -rpipe %s_nhdp" % node.name
@ -101,13 +99,13 @@ class NrlNhdp(NrlService):
cmd += " -i "
cmd += " -i ".join(interfacenames)
return (cmd, )
return cmd,
addservice(NrlNhdp)
class NrlSmf(NrlService):
''' Simplified Multicast Forwarding for MANET networks.
'''
"""
Simplified Multicast Forwarding for MANET networks.
"""
_name = "SMF"
_startup = ("sh startsmf.sh",)
_shutdown = ("killall nrlsmf",)
@ -116,18 +114,18 @@ class NrlSmf(NrlService):
@classmethod
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
directly using vcmd.
'''
"""
cfg = "#!/bin/sh\n"
cfg += "# auto-generated by nrl.py:NrlSmf.generateconfig()\n"
comments = ""
cmd = "nrlsmf instance %s_smf" % (node.name)
cmd = "nrlsmf instance %s_smf" % node.name
servicenames = map(lambda x: x._name, services)
netifs = filter(lambda x: not getattr(x, 'control', False), \
node.netifs())
netifs = filter(lambda x: not getattr(x, 'control', False), node.netifs())
if len(netifs) == 0:
return ()
@ -154,11 +152,11 @@ class NrlSmf(NrlService):
cfg += comments + cmd + " < /dev/null > /dev/null 2>&1 &\n\n"
return cfg
addservice(NrlSmf)
class NrlOlsr(NrlService):
''' Optimized Link State Routing protocol for MANET networks.
'''
"""
Optimized Link State Routing protocol for MANET networks.
"""
_name = "OLSR"
_startup = ("nrlolsrd",)
_shutdown = ("killall nrlolsrd",)
@ -166,8 +164,9 @@ class NrlOlsr(NrlService):
@classmethod
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]
# are multiple interfaces supported? No.
netifs = list(node.netifs())
@ -184,13 +183,13 @@ class NrlOlsr(NrlService):
if "zebra" in servicenames:
cmd += " -z"
return (cmd, )
return cmd,
addservice(NrlOlsr)
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"
_startup = ("nrlolsrv2",)
_shutdown = ("killall nrlolsrv2",)
@ -198,8 +197,9 @@ class NrlOlsrv2(NrlService):
@classmethod
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 += " -l /var/log/nrlolsrv2.log"
cmd += " -rpipe %s_olsrv2" % node.name
@ -211,20 +211,19 @@ class NrlOlsrv2(NrlService):
cmd += " -p olsr"
netifs = filter(lambda x: not getattr(x, 'control', False), \
node.netifs())
netifs = filter(lambda x: not getattr(x, 'control', False), node.netifs())
if len(netifs) > 0:
interfacenames = map(lambda x: x.name, netifs)
cmd += " -i "
cmd += " -i ".join(interfacenames)
return (cmd, )
return cmd,
addservice(NrlOlsrv2)
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"
_configs = ("/etc/olsrd/olsrd.conf",)
_dirs = ("/etc/olsrd",)
@ -234,21 +233,23 @@ class OlsrOrg(NrlService):
@classmethod
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]
netifs = filter(lambda x: not getattr(x, 'control', False), \
node.netifs())
netifs = filter(lambda x: not getattr(x, 'control', False), node.netifs())
if len(netifs) > 0:
interfacenames = map(lambda x: x.name, netifs)
cmd += " -i "
cmd += " -i ".join(interfacenames)
return (cmd, )
return cmd,
@classmethod
def 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 = """\
#
# OLSR.org routing daemon config file
@ -566,11 +567,11 @@ InterfaceDefaults {
"""
return cfg
addservice(OlsrOrg)
class MgenActor(NrlService):
''' ZpcMgenActor.
'''
"""
ZpcMgenActor.
"""
# a unique name is required, without spaces
_name = "MgenActor"
@ -594,30 +595,29 @@ class MgenActor(NrlService):
@classmethod
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
directly using vcmd.
'''
"""
cfg = "#!/bin/sh\n"
cfg += "# auto-generated by nrl.py:MgenActor.generateconfig()\n"
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)
netifs = filter(lambda x: not getattr(x, 'control', False), \
node.netifs())
netifs = filter(lambda x: not getattr(x, 'control', False), node.netifs())
if len(netifs) == 0:
return ()
cfg += comments + cmd + " < /dev/null > /dev/null 2>&1 &\n\n"
return cfg
# this line is required to add the above class to the list of available services
addservice(MgenActor)
class Arouted(NrlService):
''' Adaptive Routing
'''
"""
Adaptive Routing
"""
_name = "arouted"
_configs = ("startarouted.sh",)
_startindex = NrlService._startindex + 10
@ -627,8 +627,9 @@ class Arouted(NrlService):
@classmethod
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 = """
#!/bin/sh
for f in "/tmp/%s_smf"; do
@ -643,12 +644,23 @@ for f in "/tmp/%s_smf"; do
done
done
""" % (node.name)
""" % node.name
cfg += "ip route add %s dev lo\n" % cls.firstipv4prefix(node, 24)
cfg += "arouted instance %s_smf tap %s_tap" % (node.name, node.name)
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"
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
#addservice(Arouted)
# ServiceManager.add(Arouted)

View file

@ -1,33 +1,23 @@
#
# 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.
'''
"""
import os
if os.uname()[0] == "Linux":
from core.netns import nodes
elif os.uname()[0] == "FreeBSD":
from core.bsd import nodes
from core.service import CoreService, addservice
from core.misc.ipaddr import IPv4Prefix, isIPv4Address, isIPv6Address
from core.api import coreapi
from core.constants import *
from core import constants
from core.enumerations import LinkTypes, NodeTypes
from core.misc import ipaddress
from core.misc import nodeutils
from core.service import CoreService
from core.service import ServiceManager
QUAGGA_USER = "root"
QUAGGA_GROUP = "root"
if os.uname()[0] == "FreeBSD":
QUAGGA_GROUP = "wheel"
class Zebra(CoreService):
'''
'''
_name = "zebra"
_group = "Quagga"
_depends = ("vtysh",)
@ -41,8 +31,9 @@ class Zebra(CoreService):
@classmethod
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]:
return cls.generateQuaggaConf(node, services)
elif filename == cls._configs[1]:
@ -54,16 +45,18 @@ class Zebra(CoreService):
@classmethod
def generateVtyshConf(cls, node, services):
''' Returns configuration file text.
'''
"""
Returns configuration file text.
"""
return "service integrated-vtysh-config\n"
@classmethod
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()
hooks that are invoked here.
'''
"""
# we could verify here that filename == Quagga.conf
cfg = ""
for ifc in node.netifs():
@ -91,15 +84,13 @@ class Zebra(CoreService):
cfgv4 += ifccfg
if want_ipv4:
ipv4list = filter(lambda x: isIPv4Address(x.split('/')[0]),
ifc.addrlist)
ipv4list = filter(lambda x: ipaddress.is_ipv4_address(x.split('/')[0]), ifc.addrlist)
cfg += " "
cfg += "\n ".join(map(cls.addrstr, ipv4list))
cfg += "\n"
cfg += cfgv4
if want_ipv6:
ipv6list = filter(lambda x: isIPv6Address(x.split('/')[0]),
ifc.addrlist)
ipv6list = filter(lambda x: ipaddress.is_ipv6_address(x.split('/')[0]), ifc.addrlist)
cfg += " "
cfg += "\n ".join(map(cls.addrstr, ipv6list))
cfg += "\n"
@ -114,22 +105,24 @@ class Zebra(CoreService):
@staticmethod
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:
return "ip address %s" % x
elif x.find(":") >= 0:
return "ipv6 address %s" % x
else:
raise Value, "invalid address: %s", x
raise ValueError("invalid address: %s", x)
@classmethod
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:
quagga_bin_search = node.session.cfg['quagga_bin_search']
quagga_sbin_search = node.session.cfg['quagga_sbin_search']
quagga_bin_search = node.session.config['quagga_bin_search']
quagga_sbin_search = node.session.config['quagga_sbin_search']
except KeyError:
quagga_bin_search = '"/usr/local/bin /usr/bin /usr/lib/quagga"'
quagga_sbin_search = '"/usr/local/sbin /usr/sbin /usr/lib/quagga"'
@ -243,15 +236,14 @@ elif [ "$1" = "vtysh" ]; then
else
bootdaemon $1
fi
""" % (cls._configs[0], quagga_sbin_search, quagga_bin_search, \
QUAGGA_STATE_DIR, QUAGGA_USER, QUAGGA_GROUP)
""" % (cls._configs[0], quagga_sbin_search, quagga_bin_search, constants.QUAGGA_STATE_DIR, QUAGGA_USER, QUAGGA_GROUP)
addservice(Zebra)
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.
'''
"""
_name = "QuaggaDaemon"
_group = "Quagga"
_depends = ("zebra",)
@ -267,8 +259,9 @@ class QuaggaService(CoreService):
@staticmethod
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():
if hasattr(ifc, 'control') and ifc.control == True:
continue
@ -280,14 +273,15 @@ class QuaggaService(CoreService):
@staticmethod
def rj45check(ifc):
''' Helper to detect whether interface is connected an external RJ45
"""
Helper to detect whether interface is connected an external RJ45
link.
'''
"""
if ifc.net:
for peerifc in ifc.net.netifs():
if peerifc == ifc:
continue
if isinstance(peerifc, nodes.RJ45Node):
if nodeutils.is_node(peerifc, NodeTypes.RJ45):
return True
return False
@ -304,12 +298,12 @@ class QuaggaService(CoreService):
return ""
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
unified Quagga.conf file.
'''
"""
_name = "OSPFv2"
_startup = ("sh quaggaboot.sh ospfd",)
_shutdown = ("killall ospfd",)
@ -318,10 +312,11 @@ class Ospfv2(QuaggaService):
@staticmethod
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
GreTap device.
'''
"""
if ifc.mtu != 1500:
# a workaround for PhysicalNode GreTap, which has no knowledge of
# the other nodes/nets
@ -335,10 +330,11 @@ class Ospfv2(QuaggaService):
@staticmethod
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.
'''
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 ""
@ -349,12 +345,12 @@ class Ospfv2(QuaggaService):
cfg += " router-id %s\n" % rtrid
# network 10.0.0.0/24 area 0
for ifc in node.netifs():
if hasattr(ifc, 'control') and ifc.control == True:
if hasattr(ifc, 'control') and ifc.control is True:
continue
for a in ifc.addrlist:
if a.find(".") < 0:
continue
net = IPv4Prefix(a)
net = ipaddress.Ipv4Prefix(a)
cfg += " network %s area 0\n" % net
cfg += "!\n"
return cfg
@ -369,18 +365,20 @@ class Ospfv2(QuaggaService):
# cfg += cls.ptpcheck(ifc)
# return cfg + """\
# ip ospf hello-interval 2
# ip ospf dead-interval 6
# ip ospf retransmit-interval 5
# """
addservice(Ospfv2)
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
unified Quagga.conf file.
'''
"""
_name = "OSPFv3"
_startup = ("sh quaggaboot.sh ospf6d",)
_shutdown = ("killall ospf6d",)
@ -390,9 +388,10 @@ class Ospfv3(QuaggaService):
@staticmethod
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.
'''
"""
mtu = ifc.mtu
if not ifc.net:
return mtu
@ -403,10 +402,11 @@ class Ospfv3(QuaggaService):
@classmethod
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
GreTap device.
'''
"""
minmtu = cls.minmtu(ifc)
if minmtu < ifc.mtu:
return " ipv6 ospf6 ifmtu %d\n" % minmtu
@ -415,10 +415,11 @@ class Ospfv3(QuaggaService):
@staticmethod
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.
'''
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 ""
@ -428,7 +429,7 @@ class Ospfv3(QuaggaService):
rtrid = cls.routerid(node)
cfg += " router-id %s\n" % rtrid
for ifc in node.netifs():
if hasattr(ifc, 'control') and ifc.control == True:
if hasattr(ifc, 'control') and ifc.control is True:
continue
cfg += " interface %s area 0.0.0.0\n" % ifc.name
cfg += "!\n"
@ -444,19 +445,21 @@ class Ospfv3(QuaggaService):
# cfg += cls.ptpcheck(ifc)
# return cfg + """\
# ipv6 ospf6 hello-interval 2
# ipv6 ospf6 dead-interval 6
# ipv6 ospf6 retransmit-interval 5
# """
addservice(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
configuration file but has hooks for adding to the
unified Quagga.conf file.
'''
"""
_name = "OSPFv3MDR"
_ipv4_routing = True
@ -464,8 +467,7 @@ class Ospfv3mdr(Ospfv3):
def generatequaggaifcconfig(cls, node, ifc):
cfg = cls.mtucheck(ifc)
cfg += " ipv6 ospf6 instance-id 65\n"
if ifc.net is not None and \
isinstance(ifc.net, (nodes.WlanNode, nodes.EmaneNode)):
if ifc.net is not None and nodeutils.is_node(ifc.net, (NodeTypes.WIRELESS_LAN, NodeTypes.EMANE)):
return cfg + """\
ipv6 ospf6 hello-interval 2
ipv6 ospf6 dead-interval 6
@ -478,13 +480,13 @@ class Ospfv3mdr(Ospfv3):
else:
return cfg
addservice(Ospfv3mdr)
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
having the same AS number.
'''
"""
_name = "BGP"
_startup = ("sh quaggaboot.sh bgpd",)
_shutdown = ("killall bgpd",)
@ -505,11 +507,11 @@ class Bgp(QuaggaService):
cfg += "! neighbor 1.2.3.4 remote-as 555\n!\n"
return cfg
addservice(Bgp)
class Rip(QuaggaService):
''' The RIP service provides IPv4 routing for wired networks.
'''
"""
The RIP service provides IPv4 routing for wired networks.
"""
_name = "RIP"
_startup = ("sh quaggaboot.sh ripd",)
_shutdown = ("killall ripd",)
@ -528,11 +530,11 @@ router rip
"""
return cfg
addservice(Rip)
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"
_startup = ("sh quaggaboot.sh ripngd",)
_shutdown = ("killall ripngd",)
@ -551,12 +553,12 @@ router ripng
"""
return cfg
addservice(Ripng)
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.
'''
"""
_name = "Babel"
_startup = ("sh quaggaboot.sh babeld",)
_shutdown = ("killall babeld",)
@ -567,7 +569,7 @@ class Babel(QuaggaService):
def generatequaggaconfig(cls, node):
cfg = "router babel\n"
for ifc in node.netifs():
if hasattr(ifc, 'control') and ifc.control == True:
if hasattr(ifc, 'control') and ifc.control is True:
continue
cfg += " network %s\n" % ifc.name
cfg += " redistribute static\n redistribute connected\n"
@ -576,17 +578,16 @@ class Babel(QuaggaService):
@classmethod
def generatequaggaifcconfig(cls, node, ifc):
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"
else:
return " babel wired\n babel split-horizon\n"
addservice(Babel)
class Xpimd(QuaggaService):
'''\
"""
PIM multicast routing based on XORP.
'''
"""
_name = 'Xpimd'
_startup = ('sh quaggaboot.sh xpimd',)
_shutdown = ('killall xpimd',)
@ -613,12 +614,12 @@ class Xpimd(QuaggaService):
def generatequaggaifcconfig(cls, node, ifc):
return ' ip mfea\n ip igmp\n ip pim\n'
addservice(Xpimd)
class Vtysh(CoreService):
''' Simple service to run vtysh -b (boot) after all Quagga daemons have
"""
Simple service to run vtysh -b (boot) after all Quagga daemons have
started.
'''
"""
_name = "vtysh"
_group = "Quagga"
_startindex = 45
@ -629,6 +630,15 @@ class Vtysh(CoreService):
def generateconfig(cls, node, filename, services):
return ""
addservice(Vtysh)
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)
ServiceManager.add(Vtysh)

View file

@ -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
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):
'''
'''
_name = "VPNClient"
_group = "Security"
_configs = ('vpnclient.sh',)
@ -28,24 +23,22 @@ class VPNClient(CoreService):
@classmethod
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 += "# 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:
cfg += open(fname, "rb").read()
except e:
print "Error opening VPN client configuration template (%s): %s" % \
(fname, e)
except IOError:
logger.exception("Error opening VPN client configuration template (%s)", fname)
return cfg
# this line is required to add the above class to the list of available services
addservice(VPNClient)
class VPNServer(CoreService):
'''
'''
_name = "VPNServer"
_group = "Security"
_configs = ('vpnserver.sh',)
@ -57,24 +50,23 @@ class VPNServer(CoreService):
@classmethod
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.
'''
"""
cfg = "#!/bin/sh\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:
cfg += open(fname, "rb").read()
except e:
print "Error opening VPN server configuration template (%s): %s" % \
(fname, e)
except IOError:
logger.exception("Error opening VPN server configuration template (%s)", fname)
return cfg
addservice(VPNServer)
class IPsec(CoreService):
'''
'''
_name = "IPsec"
_group = "Security"
_configs = ('ipsec.sh',)
@ -85,25 +77,24 @@ class IPsec(CoreService):
@classmethod
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.
'''
"""
cfg = "#!/bin/sh\n"
cfg += "# set up static tunnel mode security assocation for service "
cfg += "(security.py)\n"
fname = "%s/examples/services/sampleIPsec" % CORE_DATA_DIR
fname = "%s/examples/services/sampleIPsec" % constants.CORE_DATA_DIR
try:
cfg += open(fname, "rb").read()
except e:
print "Error opening IPsec configuration template (%s): %s" % \
(fname, e)
except IOError:
logger.exception("Error opening IPsec configuration template (%s)", fname)
return cfg
addservice(IPsec)
class Firewall(CoreService):
'''
'''
_name = "Firewall"
_group = "Security"
_configs = ('firewall.sh',)
@ -113,17 +104,24 @@ class Firewall(CoreService):
@classmethod
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 += "# 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:
cfg += open(fname, "rb").read()
except e:
print "Error opening Firewall configuration template (%s): %s" % \
(fname, e)
except IOError:
logger.exception("Error opening Firewall configuration template (%s)", fname)
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)

View file

@ -1,9 +1,14 @@
from core.service import CoreService, addservice
from sys import maxint
from inspect import isclass
from sys import maxint
from core.service import CoreService
from core.service import ServiceManager
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'
_group = 'Utility'
_depends = ()
@ -15,9 +20,8 @@ class Startup(CoreService):
_validate = ()
@staticmethod
def isStartupService(s):
return isinstance(s, Startup) or \
(isclass(s) and issubclass(s, Startup))
def is_startup_service(s):
return isinstance(s, Startup) or (isclass(s) and issubclass(s, Startup))
@classmethod
def generateconfig(cls, node, filename, services):
@ -27,11 +31,13 @@ class Startup(CoreService):
'# auto-generated by Startup (startup.py)\n\n' \
'exec > startup.log 2>&1\n\n'
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
start = '\n'.join(s.getstartup(node, services))
if start:
script += start + '\n'
return script
addservice(Startup)
def load_services():
ServiceManager.add(Startup)

View file

@ -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
'''
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"
class Ucarp(CoreService):
'''
'''
_name = "ucarp"
_group = "Utility"
_depends = ( )
_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
_startup = ("sh ucarpboot.sh",)
_shutdown = ("killall ucarp",)
@ -34,8 +22,9 @@ class Ucarp(CoreService):
@classmethod
def generateconfig(cls, node, filename, services):
''' Return the default file contents
'''
"""
Return the default file contents
"""
if filename == cls._configs[0]:
return cls.generateUcarpConf(node, services)
elif filename == cls._configs[1]:
@ -49,12 +38,14 @@ class Ucarp(CoreService):
@classmethod
def generateUcarpConf(cls, node, services):
''' Returns configuration file text.
'''
"""
Returns configuration file text.
"""
try:
ucarp_bin = node.session.cfg['ucarp_bin']
except KeyError:
ucarp_bin = "/usr/sbin/ucarp"
return """\
#!/bin/sh
# Location of UCARP executable
@ -116,8 +107,10 @@ ${UCARP_EXEC} -B ${UCARP_OPTS}
@classmethod
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:
ucarp_bin = node.session.cfg['ucarp_bin']
except KeyError:
@ -132,16 +125,18 @@ chmod a+x ${UCARP_CFGDIR}/*.sh
# Start the default ucarp daemon configuration
${UCARP_CFGDIR}/default.sh
""" % (UCARP_ETC)
""" % UCARP_ETC
@classmethod
def generateVipUp(cls, node, services):
''' Generate a shell script used to start the virtual ip
'''
"""
Generate a shell script used to start the virtual ip
"""
try:
ucarp_bin = node.session.cfg['ucarp_bin']
except KeyError:
ucarp_bin = "/usr/sbin/ucarp"
return """\
#!/bin/bash
@ -161,8 +156,9 @@ fi
@classmethod
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:
ucarp_bin = node.session.cfg['ucarp_bin']
except KeyError:
@ -185,5 +181,5 @@ fi
"""
addservice(Ucarp)
def load_services():
ServiceManager.add(Ucarp)

View file

@ -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.
'''
"""
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):
''' Parent class for utility services.
'''
"""
Parent class for utility services.
"""
_name = "UtilityProcess"
_group = "Utility"
_depends = ()
@ -32,6 +30,7 @@ class UtilService(CoreService):
def generateconfig(cls, node, filename, services):
return ""
class IPForwardService(UtilService):
_name = "IPForward"
_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.all.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():
name = sysctldevname(ifc.name)
cfg += "%s -w net.ipv4.conf.%s.forwarding=1\n" % (SYSCTL_BIN, name)
name = utils.sysctldevname(ifc.name)
cfg += "%s -w net.ipv4.conf.%s.forwarding=1\n" % (constants.SYSCTL_BIN, name)
cfg += "%s -w net.ipv4.conf.%s.send_redirects=0\n" % \
(SYSCTL_BIN, name)
cfg += "%s -w net.ipv4.conf.%s.rp_filter=0\n" % (SYSCTL_BIN, name)
(constants.SYSCTL_BIN, name)
cfg += "%s -w net.ipv4.conf.%s.rp_filter=0\n" % (constants.SYSCTL_BIN, name)
return cfg
@classmethod
@ -78,9 +77,8 @@ class IPForwardService(UtilService):
%s -w net.inet6.ip6.forwarding=1
%s -w net.inet.icmp.bmcastecho=1
%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):
_name = "DefaultRoute"
@ -92,7 +90,7 @@ class DefaultRouteService(UtilService):
cfg = "#!/bin/sh\n"
cfg += "# auto-generated by DefaultRoute service (utility.py)\n"
for ifc in node.netifs():
if hasattr(ifc, 'control') and ifc.control == True:
if hasattr(ifc, 'control') and ifc.control is True:
continue
cfg += "\n".join(map(cls.addrstr, ifc.addrlist))
cfg += "\n"
@ -101,12 +99,12 @@ class DefaultRouteService(UtilService):
@staticmethod
def addrstr(x):
if x.find(":") >= 0:
net = IPv6Prefix(x)
net = Ipv6Prefix(x)
fam = "inet6 ::"
else:
net = IPv4Prefix(x)
net = Ipv4Prefix(x)
fam = "inet 0.0.0.0"
if net.maxaddr() == net.minaddr():
if net.max_addr() == net.min_addr():
return ""
else:
if os.uname()[0] == "Linux":
@ -115,9 +113,8 @@ class DefaultRouteService(UtilService):
rtcmd = "route add -%s" % fam
else:
raise Exception, "unknown platform"
return "%s %s" % (rtcmd, net.minaddr())
return "%s %s" % (rtcmd, net.min_addr())
addservice(DefaultRouteService)
class DefaultMulticastRouteService(UtilService):
_name = "DefaultMulticastRoute"
@ -145,7 +142,6 @@ class DefaultMulticastRouteService(UtilService):
break
return cfg
addservice(DefaultMulticastRouteService)
class StaticRouteService(UtilService):
_name = "StaticRoute"
@ -160,7 +156,7 @@ class StaticRouteService(UtilService):
cfg += "# NOTE: this service must be customized to be of any use\n"
cfg += "# Below are samples that you can uncomment and edit.\n#\n"
for ifc in node.netifs():
if hasattr(ifc, 'control') and ifc.control == True:
if hasattr(ifc, 'control') and ifc.control is True:
continue
cfg += "\n".join(map(cls.routestr, ifc.addrlist))
cfg += "\n"
@ -169,14 +165,14 @@ class StaticRouteService(UtilService):
@staticmethod
def routestr(x):
if x.find(":") >= 0:
net = IPv6Prefix(x)
net = Ipv6Prefix(x)
fam = "inet6"
dst = "3ffe:4::/64"
else:
net = IPv4Prefix(x)
net = Ipv4Prefix(x)
fam = "inet"
dst = "10.9.8.0/24"
if net.maxaddr() == net.minaddr():
if net.max_addr() == net.min_addr():
return ""
else:
if os.uname()[0] == "Linux":
@ -185,9 +181,8 @@ class StaticRouteService(UtilService):
rtcmd = "#/sbin/route add -%s %s" % (fam, dst)
else:
raise Exception, "unknown platform"
return "%s %s" % (rtcmd, net.minaddr())
return "%s %s" % (rtcmd, net.min_addr())
addservice(StaticRouteService)
class SshService(UtilService):
_name = "SSH"
@ -203,9 +198,10 @@ class SshService(UtilService):
@classmethod
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.
'''
"""
if os.uname()[0] == "FreeBSD":
sshcfgdir = node.nodedir
sshstatedir = node.nodedir
@ -264,7 +260,6 @@ UsePAM yes
UseDNS no
""" % (sshcfgdir, sshstatedir, sshlibdir)
addservice(SshService)
class DhcpService(UtilService):
_name = "DHCP"
@ -276,9 +271,10 @@ class DhcpService(UtilService):
@classmethod
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.
'''
"""
cfg = """\
# auto-generated by DHCP service (utility.py)
# NOTE: move these option lines into the desired pool { } block(s) below
@ -294,7 +290,7 @@ max-lease-time 7200;
ddns-update-style none;
"""
for ifc in node.netifs():
if hasattr(ifc, 'control') and ifc.control == True:
if hasattr(ifc, 'control') and ifc.control is True:
continue
cfg += "\n".join(map(cls.subnetentry, ifc.addrlist))
cfg += "\n"
@ -302,17 +298,18 @@ ddns-update-style none;
@staticmethod
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.
'''
"""
if x.find(":") >= 0:
return ""
else:
addr = x.split("/")[0]
net = IPv4Prefix(x)
net = Ipv4Prefix(x)
# divide the address space in half
rangelow = net.addr(net.numaddr() / 2)
rangehigh = net.maxaddr()
rangelow = net.addr(net.num_addr() / 2)
rangehigh = net.max_addr()
return """
subnet %s netmask %s {
pool {
@ -321,13 +318,13 @@ subnet %s netmask %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):
''' Use a DHCP client for all interfaces for addressing.
'''
"""
Use a DHCP client for all interfaces for addressing.
"""
_name = "DHCPClient"
_configs = ("startdhcpclient.sh",)
_startup = ("sh startdhcpclient.sh",)
@ -336,8 +333,9 @@ class DhcpClientService(UtilService):
@classmethod
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 += "# auto-generated by DHCPClient service (utility.py)\n"
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)
return cfg
addservice(DhcpClientService)
class FtpService(UtilService):
''' Start a vsftpd server.
'''
"""
Start a vsftpd server.
"""
_name = "FTP"
_configs = ("vsftpd.conf",)
_dirs = ("/var/run/vsftpd/empty", "/var/ftp",)
@ -367,8 +365,9 @@ class FtpService(UtilService):
@classmethod
def generateconfig(cls, node, filename, services):
''' Generate a vsftpd.conf configuration file.
'''
"""
Generate a vsftpd.conf configuration file.
"""
return """\
# vsftpd.conf auto-generated by FTP service (utility.py)
listen=YES
@ -384,11 +383,11 @@ secure_chroot_dir=/var/run/vsftpd/empty
anon_root=/var/ftp
"""
addservice(FtpService)
class HttpService(UtilService):
''' Start an apache server.
'''
"""
Start an apache server.
"""
_name = "HTTP"
_configs = ("/etc/apache2/apache2.conf", "/etc/apache2/envvars",
"/var/www/index.html",)
@ -402,8 +401,9 @@ class HttpService(UtilService):
@classmethod
def generateconfig(cls, node, filename, services):
''' Generate an apache2.conf configuration file.
'''
"""
Generate an apache2.conf configuration file.
"""
if filename == cls._configs[0]:
return cls.generateapache2conf(node, filename, services)
elif filename == cls._configs[1]:
@ -415,16 +415,18 @@ class HttpService(UtilService):
@classmethod
def detectversionfromcmd(cls):
''' Detect the apache2 version using the 'a2query' command.
'''
"""
Detect the apache2 version using the 'a2query' command.
"""
try:
status, result = cmdresult(['a2query', '-v'])
except Exception:
status, result = utils.cmdresult(['a2query', '-v'])
except subprocess.CalledProcessError:
status = -1
if status == 0 and result[:3] == '2.4':
return cls.APACHEVER24
return cls.APACHEVER22
return cls.APACHEVER22
@classmethod
def generateapache2conf(cls, node, filename, services):
@ -593,11 +595,11 @@ export LANG
body += "<li>%s - %s</li>\n" % (ifc.name, ifc.addrlist)
return "<html><body>%s</body></html>" % body
addservice(HttpService)
class PcapService(UtilService):
''' Pcap service for logging packets.
'''
"""
Pcap service for logging packets.
"""
_name = "pcap"
_configs = ("pcap.sh",)
_dirs = ()
@ -609,8 +611,9 @@ class PcapService(UtilService):
@classmethod
def generateconfig(cls, node, filename, services):
''' Generate a startpcap.sh traffic logging script.
'''
"""
Generate a startpcap.sh traffic logging script.
"""
cfg = """
#!/bin/sh
# set tcpdump options here (see 'man tcpdump' for help)
@ -635,7 +638,6 @@ fi;
"""
return cfg
addservice(PcapService)
class RadvdService(UtilService):
_name = "radvd"
@ -647,9 +649,10 @@ class RadvdService(UtilService):
@classmethod
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.
'''
"""
cfg = "# auto-generated by RADVD service (utility.py)\n"
for ifc in node.netifs():
if hasattr(ifc, 'control') and ifc.control == True:
@ -682,20 +685,21 @@ interface %s
@staticmethod
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.
'''
"""
if x.find(":") >= 0:
net = IPv6Prefix(x)
net = Ipv6Prefix(x)
return str(net)
else:
return ""
addservice(RadvdService)
class AtdService(UtilService):
''' Atd service for scheduling at jobs
'''
"""
Atd service for scheduling at jobs
"""
_name = "atd"
_configs = ("startatd.sh",)
_dirs = ("/var/spool/cron/atjobs", "/var/spool/cron/atspool")
@ -712,13 +716,27 @@ chmod -R 700 /var/spool/cron/*
atd
"""
addservice(AtdService)
class UserDefinedService(UtilService):
''' Dummy service allowing customization of anything.
'''
"""
Dummy service allowing customization of anything.
"""
_name = "UserDefined"
_startindex = 50
_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)

View file

@ -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.
'''
"""
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):
''' 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.
'''
"""
_name = "xorp_rtrmgr"
_group = "XORP"
_depends = ()
@ -31,10 +26,11 @@ class XorpRtrmgr(CoreService):
@classmethod
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
invoked here. Filename currently ignored.
'''
"""
cfg = "interfaces {\n"
for ifc in node.netifs():
cfg += " interface %s {\n" % ifc.name
@ -50,17 +46,16 @@ class XorpRtrmgr(CoreService):
s._depends.index(cls._name)
cfg += s.generatexorpconfig(node)
except ValueError:
pass
logger.exception("error getting value from service: %s", cls._name)
return cfg
@staticmethod
def addrstr(x):
''' helper for mapping IP addresses to XORP config statements
'''
try:
(addr, plen) = x.split("/")
except Exception:
raise ValueError, "invalid address"
"""
helper for mapping IP addresses to XORP config statements
"""
addr, plen = x.split("/")
cfg = "\t address %s {\n" % addr
cfg += "\t\tprefix-length: %s\n" % plen
cfg += "\t }\n"
@ -68,19 +63,20 @@ class XorpRtrmgr(CoreService):
@staticmethod
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\tprefix-length: 64\n"
cfg += "\t }\n"
return cfg
addservice(XorpRtrmgr)
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.
'''
"""
_name = "XorpDaemon"
_group = "XORP"
_depends = ("xorp_rtrmgr",)
@ -93,8 +89,9 @@ class XorpService(CoreService):
@staticmethod
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 += " %s {\n" % forwarding
cfg += "\tdisable:false\n"
@ -104,11 +101,12 @@ class XorpService(CoreService):
@staticmethod
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 = []
for ifc in ifcs:
if hasattr(ifc, 'control') and ifc.control == True:
if hasattr(ifc, 'control') and ifc.control is True:
continue
names.append(ifc.name)
names.append("register_vif")
@ -125,11 +123,11 @@ class XorpService(CoreService):
cfg += "}\n"
return cfg
@staticmethod
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-statement export-connected {\n"
cfg += "\tterm 100 {\n"
@ -143,10 +141,11 @@ class XorpService(CoreService):
@staticmethod
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():
if hasattr(ifc, 'control') and ifc.control == True:
if hasattr(ifc, 'control') and ifc.control is True:
continue
for a in ifc.addrlist:
if a.find(".") >= 0:
@ -162,11 +161,13 @@ class XorpService(CoreService):
def generatexorpconfig(cls, node):
return ""
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
unified XORP configuration file.
'''
"""
_name = "XORP_OSPFv2"
@classmethod
@ -178,7 +179,7 @@ class XorpOspfv2(XorpService):
cfg += "\trouter-id: %s\n" % rtrid
cfg += "\tarea 0.0.0.0 {\n"
for ifc in node.netifs():
if hasattr(ifc, 'control') and ifc.control == True:
if hasattr(ifc, 'control') and ifc.control is True:
continue
cfg += "\t interface %s {\n" % ifc.name
cfg += "\t\tvif %s {\n" % ifc.name
@ -195,13 +196,13 @@ class XorpOspfv2(XorpService):
cfg += "}\n"
return cfg
addservice(XorpOspfv2)
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
unified XORP configuration file.
'''
"""
_name = "XORP_OSPFv3"
@classmethod
@ -213,7 +214,7 @@ class XorpOspfv3(XorpService):
cfg += "\trouter-id: %s\n" % rtrid
cfg += "\tarea 0.0.0.0 {\n"
for ifc in node.netifs():
if hasattr(ifc, 'control') and ifc.control == True:
if hasattr(ifc, 'control') and ifc.control is True:
continue
cfg += "\t interface %s {\n" % ifc.name
cfg += "\t\tvif %s {\n" % ifc.name
@ -224,11 +225,11 @@ class XorpOspfv3(XorpService):
cfg += "}\n"
return cfg
addservice(XorpOspfv3)
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"
_custom_needed = True
@ -253,11 +254,12 @@ class XorpBgp(XorpService):
cfg += "}\n"
return cfg
addservice(XorpBgp)
class XorpRip(XorpService):
''' RIP IPv4 unicast routing.
'''
"""
RIP IPv4 unicast routing.
"""
_name = "XORP_RIP"
@classmethod
@ -268,7 +270,7 @@ class XorpRip(XorpService):
cfg += " rip {\n"
cfg += "\texport: \"export-connected\"\n"
for ifc in node.netifs():
if hasattr(ifc, 'control') and ifc.control == True:
if hasattr(ifc, 'control') and ifc.control is True:
continue
cfg += "\tinterface %s {\n" % ifc.name
cfg += "\t vif %s {\n" % ifc.name
@ -285,11 +287,11 @@ class XorpRip(XorpService):
cfg += "}\n"
return cfg
addservice(XorpRip)
class XorpRipng(XorpService):
''' RIP NG IPv6 unicast routing.
'''
"""
RIP NG IPv6 unicast routing.
"""
_name = "XORP_RIPNG"
@classmethod
@ -300,7 +302,7 @@ class XorpRipng(XorpService):
cfg += " ripng {\n"
cfg += "\texport: \"export-connected\"\n"
for ifc in node.netifs():
if hasattr(ifc, 'control') and ifc.control == True:
if hasattr(ifc, 'control') and ifc.control is True:
continue
cfg += "\tinterface %s {\n" % ifc.name
cfg += "\t vif %s {\n" % ifc.name
@ -320,11 +322,11 @@ class XorpRipng(XorpService):
cfg += "}\n"
return cfg
addservice(XorpRipng)
class XorpPimSm4(XorpService):
''' PIM Sparse Mode IPv4 multicast routing.
'''
"""
PIM Sparse Mode IPv4 multicast routing.
"""
_name = "XORP_PIMSM4"
@classmethod
@ -335,7 +337,7 @@ class XorpPimSm4(XorpService):
cfg += " igmp {\n"
names = []
for ifc in node.netifs():
if hasattr(ifc, 'control') and ifc.control == True:
if hasattr(ifc, 'control') and ifc.control is True:
continue
names.append(ifc.name)
cfg += "\tinterface %s {\n" % ifc.name
@ -379,11 +381,11 @@ class XorpPimSm4(XorpService):
cfg += "}\n"
return cfg
addservice(XorpPimSm4)
class XorpPimSm6(XorpService):
''' PIM Sparse Mode IPv6 multicast routing.
'''
"""
PIM Sparse Mode IPv6 multicast routing.
"""
_name = "XORP_PIMSM6"
@classmethod
@ -394,7 +396,7 @@ class XorpPimSm6(XorpService):
cfg += " mld {\n"
names = []
for ifc in node.netifs():
if hasattr(ifc, 'control') and ifc.control == True:
if hasattr(ifc, 'control') and ifc.control is True:
continue
names.append(ifc.name)
cfg += "\tinterface %s {\n" % ifc.name
@ -438,11 +440,11 @@ class XorpPimSm6(XorpService):
cfg += "}\n"
return cfg
addservice(XorpPimSm6)
class XorpOlsr(XorpService):
''' OLSR IPv4 unicast MANET routing.
'''
"""
OLSR IPv4 unicast MANET routing.
"""
_name = "XORP_OLSR"
@classmethod
@ -453,7 +455,7 @@ class XorpOlsr(XorpService):
cfg += " olsr4 {\n"
cfg += "\tmain-address: %s\n" % rtrid
for ifc in node.netifs():
if hasattr(ifc, 'control') and ifc.control == True:
if hasattr(ifc, 'control') and ifc.control is True:
continue
cfg += "\tinterface %s {\n" % ifc.name
cfg += "\t vif %s {\n" % ifc.name
@ -469,4 +471,14 @@ class XorpOlsr(XorpService):
cfg += "}\n"
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

View file

@ -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
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.coreobj import PyCoreObj, PyCoreNode, PyCoreNetIf
from core.misc.ipaddr import *
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
logger = log.get_logger(__name__)
try:
import parted
except ImportError, e:
#print "Failed to load parted Python module required by Xen support."
#print "Error was:", e
raise ImportError
except ImportError:
logger.error("failed to import parted for xen nodes")
import base64
import crypt
import subprocess
try:
import fsimage
except ImportError, e:
except ImportError:
# fix for fsimage under Ubuntu
sys.path.append("/usr/lib/xen-default/lib/python")
try:
import fsimage
except ImportError, e:
#print "Failed to load fsimage Python module required by Xen support."
#print "Error was:", e
raise ImportError
import os
import time
import shutil
import string
except ImportError:
logger.error("failed to import fsimage for xen nodes")
# XXX move these out to config file
AWK_PATH = "/bin/awk"
@ -60,6 +52,7 @@ SED_PATH = "/bin/sed"
XM_PATH = "/usr/sbin/xm"
UDEVADM_PATH = "/sbin/udevadm"
class XenVEth(PyCoreNetIf):
def __init__(self, node, name, localname, mtu=1500, net=None,
start=True, hwaddr=None):
@ -76,8 +69,8 @@ class XenVEth(PyCoreNetIf):
'vifname=%s' % self.localname, 'script=vif-core']
if self.hwaddr is not None:
cmd.append('mac=%s' % self.hwaddr)
check_call(cmd)
check_call([IP_BIN, "link", "set", self.localname, "up"])
subprocess.check_call(cmd)
subprocess.check_call([constants.IP_BIN, "link", "set", self.localname, "up"])
self.up = True
def shutdown(self):
@ -93,7 +86,7 @@ class XenVEth(PyCoreNetIf):
class XenNode(PyCoreNode):
apitype = coreapi.CORE_NODE_XEN
apitype = NodeTypes.XEN.value
FilesToIgnore = frozenset([
# 'ipforward.sh',
@ -117,24 +110,22 @@ class XenNode(PyCoreNode):
])
def RedirCmd_ipforward(self):
sysctlFile = open(os.path.join(self.mountdir, self.etcdir,
'sysctl.conf'), 'a')
p1 = subprocess.Popen([AWK_PATH,
'/^\/sbin\/sysctl -w/ {print $NF}',
os.path.join(self.nodedir,
'core-tmp/ipforward.sh') ],
stdout=sysctlFile)
sysctlFile = open(os.path.join(self.mountdir, self.etcdir, 'sysctl.conf'), 'a')
p1 = subprocess.Popen([AWK_PATH, '/^\/sbin\/sysctl -w/ {print $NF}',
os.path.join(self.nodedir, 'core-tmp/ipforward.sh')], stdout=sysctlFile)
p1.wait()
sysctlFile.close()
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')])
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')])
def RedirCmd_ospf6d(self):
check_call([SED_PATH, '-i', '-e',
subprocess.check_call([SED_PATH, '-i', '-e',
's/^ospf6d=no/ospf6d=yes/',
os.path.join(self.mountdir, self.etcdir, 'quagga/daemons')])
@ -147,13 +138,11 @@ class XenNode(PyCoreNode):
# CoreNode: no __init__, take from LxcNode & SimpleLxcNode
def __init__(self, session, objid=None, name=None,
nodedir = None, bootsh = "boot.sh", verbose = False,
start = True, model = None,
nodedir=None, bootsh="boot.sh", start=True, model=None,
vgname=None, ramsize=None, disksize=None,
isofile=None):
# SimpleLxcNode initialization
PyCoreNode.__init__(self, session = session, objid = objid, name = name,
verbose = verbose)
PyCoreNode.__init__(self, session=session, objid=objid, name=name)
self.nodedir = nodedir
self.model = model
# indicates startup() has been invoked and disk has been initialized
@ -186,31 +175,30 @@ class XenNode(PyCoreNode):
# LxcNode initialization
# self.makenodedir()
if self.nodedir is None:
self.nodedir = \
os.path.join(session.sessiondir, self.name + ".conf")
self.nodedir = os.path.join(session.sessiondir, self.name + ".conf")
self.mountdir = self.nodedir + self.getconfigitem('mount_path')
if not os.path.isdir(self.mountdir):
os.makedirs(self.mountdir)
self.tmpnodedir = True
else:
raise Exception, "Xen PVM node requires a temporary nodedir"
raise Exception("Xen PVM node requires a temporary nodedir")
self.tmpnodedir = False
self.bootsh = bootsh
if start:
self.startup()
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
object. self.model is used to identify particular profiles
associated with a node type in the GUI.
'''
return self.session.xen.getconfigitem(name=name, model=self.model,
node=self, value=default)
"""
return self.session.xen.getconfigitem(name=name, model=self.model, node=self, value=default)
# from class LxcNode (also SimpleLxcNode)
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()
try:
if self.up:
@ -218,7 +206,7 @@ class XenNode(PyCoreNode):
self.createlogicalvolume()
self.createpartitions()
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'),
iso=True)
self.setrootpassword(pw=self.getconfigitem('root_password'))
@ -231,11 +219,11 @@ class XenNode(PyCoreNode):
# from class LxcNode (also SimpleLxcNode)
def boot(self):
self.warn("XEN PVM boot() called")
logger.warn("XEN PVM boot() called")
self.lock.acquire()
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:
self.lock.release()
@ -247,18 +235,17 @@ class XenNode(PyCoreNode):
self.untarpersistent(tarname=tarname, iso=False)
try:
check_call([UMOUNT_BIN, self.mountdir])
subprocess.check_call([constants.UMOUNT_BIN, self.mountdir])
self.unmount_all(self.mountdir)
check_call([UDEVADM_PATH, 'settle'])
check_call([KPARTX_PATH, '-d', self.lvpath])
subprocess.check_call([UDEVADM_PATH, 'settle'])
subprocess.check_call([KPARTX_PATH, '-d', self.lvpath])
# time.sleep(5)
# time.sleep(1)
# unpause VM
if self.verbose:
self.warn("XEN PVM boot() unpause domU %s" % self.vmname)
mutecheck_call([XM_PATH, 'unpause', self.vmname])
logger.warn("XEN PVM boot() unpause domU %s" % self.vmname)
utils.mutecheck_call([XM_PATH, 'unpause', self.vmname])
self.booted = True
finally:
@ -269,7 +256,7 @@ class XenNode(PyCoreNode):
# from class LxcNode (also SimpleLxcNode)
def shutdown(self):
self.warn("XEN PVM shutdown() called")
logger.warn("XEN PVM shutdown() called")
if not self.up:
return
self.lock.acquire()
@ -282,27 +269,25 @@ class XenNode(PyCoreNode):
try:
# RJE XXX what to do here
if self.booted:
mutecheck_call([XM_PATH, 'destroy', self.vmname])
utils.mutecheck_call([XM_PATH, 'destroy', self.vmname])
self.booted = False
except OSError:
pass
except subprocess.CalledProcessError:
except (OSError, subprocess.CalledProcessError):
# ignore this error too, the VM may have exited already
pass
logger.exception("error during shutdown")
# discard LVM volume
lvmRemoveCount = 0
while os.path.exists(self.lvpath):
try:
check_call([UDEVADM_PATH, 'settle'])
mutecall([LVCHANGE_PATH, '-an', self.lvpath])
subprocess.check_call([UDEVADM_PATH, 'settle'])
utils.mutecall([LVCHANGE_PATH, '-an', self.lvpath])
lvmRemoveCount += 1
mutecall([LVREMOVE_PATH, '-f', self.lvpath])
utils.mutecall([LVREMOVE_PATH, '-f', self.lvpath])
except OSError:
pass
if (lvmRemoveCount > 1):
self.warn("XEN PVM shutdown() required %d lvremove " \
"executions." % lvmRemoveCount)
logger.exception("error during shutdown")
if lvmRemoveCount > 1:
logger.warn("XEN PVM shutdown() required %d lvremove executions." % lvmRemoveCount)
self._netif.clear()
del self.session
@ -314,78 +299,82 @@ class XenNode(PyCoreNode):
self.lock.release()
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):
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])
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.
'''
"""
dev = parted.Device(path=self.lvpath)
dev.removeFromCache()
disk = parted.freshDisk(dev, 'msdos')
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,
end=(persist_size - 1) , type="ext4")
end=persist_size - 1, type="ext4")
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()
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.
'''
"""
geo = parted.Geometry(device=device, start=start, end=end)
fs = parted.FileSystem(type=type, geometry=geo)
part = parted.Partition(disk=disk, fs=fs, type=parted.PARTITION_NORMAL,
geometry=geo)
part = parted.Partition(disk=disk, fs=fs, type=parted.PARTITION_NORMAL, geometry=geo)
constraint = parted.Constraint(exactGeom=geo)
disk.addPartition(partition=part, constraint=constraint)
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.
'''
"""
output = subprocess.Popen([KPARTX_PATH, '-l', self.lvpath],
stdout=subprocess.PIPE).communicate()[0]
lines = output.splitlines()
persistdev = '/dev/mapper/' + lines[0].strip().split(' ')[0].strip()
swapdev = '/dev/mapper/' + lines[1].strip().split(' ')[0].strip()
check_call([KPARTX_PATH, '-a', self.lvpath])
mutecheck_call([MKFSEXT4_PATH, '-L', 'persist', persistdev])
mutecheck_call([MKSWAP_PATH, '-f', '-L', 'swap', swapdev])
subprocess.check_call([KPARTX_PATH, '-a', self.lvpath])
utils.mutecheck_call([MKFSEXT4_PATH, '-L', 'persist', persistdev])
utils.mutecheck_call([MKSWAP_PATH, '-f', '-L', 'swap', swapdev])
return persistdev
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.
'''
"""
tarname = tarname.replace('%h', self.name) # filename may use hostname
if iso:
try:
fs = fsimage.open(self.isofile, 0)
except IOError, e:
self.warn("Failed to open ISO file: %s (%s)" % (self.isofile,e))
except IOError:
logger.exception("Failed to open ISO file: %s", self.isofile)
return
try:
tardata = fs.open_file(tarname).read();
except IOError, e:
self.warn("Failed to open tar file: %s (%s)" % (tarname, e))
tardata = fs.open_file(tarname).read()
except IOError:
logger.exception("Failed to open tar file: %s", tarname)
return
finally:
del fs;
del fs
else:
try:
f = open(tarname)
tardata = f.read()
f.close()
except IOError, e:
self.warn("Failed to open tar file: %s (%s)" % (tarname, e))
except IOError:
logger.exception("Failed to open tar file: %s", tarname)
return
p = subprocess.Popen([TAR_PATH, '-C', self.mountdir, '--numeric-owner',
'-xf', '-'], stdin=subprocess.PIPE)
@ -393,38 +382,41 @@ class XenNode(PyCoreNode):
p.wait()
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.
'''
"""
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 + ':_',
os.path.join(self.mountdir, self.etcdir, 'shadow')])
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.
'''
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')])
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')])
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.
'''
"""
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])
sshdir = os.path.join(self.getconfigitem('mount_path'), self.etcdir,
'ssh')
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/' + \
'AuthorizedKeysFile ' + sshdir + '\/authorized_keys/',
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)
dst = os.path.join(self.mountdir, self.etcdir, 'ssh', f)
shutil.copy(src, dst)
@ -432,10 +424,11 @@ class XenNode(PyCoreNode):
os.chmod(dst, 0600)
def createvm(self):
''' Instantiate a *paused* domU VM
"""
Instantiate a *paused* domU VM
Instantiate it now, so we can add network interfaces,
pause it so we can have the filesystem open for configuration.
'''
"""
args = [XM_PATH, 'create', os.devnull, '--paused']
args.extend(['name=' + self.vmname, 'memory=' + str(self.ramsize)])
args.append('disk=tap:aio:' + self.isofile + ',hda,r')
@ -446,7 +439,7 @@ class XenNode(PyCoreNode):
for action in ('poweroff', 'reboot', 'suspend', 'crash', 'halt'):
args.append('on_%s=destroy' % action)
args.append('extra=' + self.getconfigitem('xm_create_extra'))
mutecheck_call(args)
utils.mutecheck_call(args)
# from class LxcNode
def privatedir(self, path):
@ -456,8 +449,8 @@ class XenNode(PyCoreNode):
# from class LxcNode
def opennodefile(self, filename, mode="w"):
self.warn("XEN PVM opennodefile() called")
raise Exception, "Can't open VM file with opennodefile()"
logger.warn("XEN PVM opennodefile() called")
raise Exception("Can't open VM file with opennodefile()")
# from class LxcNode
# open a file on a paused Xen node
@ -482,27 +475,25 @@ class XenNode(PyCoreNode):
if filename in self.FilesRedirection:
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
self.warn("XEN PVM nodefile(filename=%s) called" % [filename])
logger.warn("XEN PVM nodefile(filename=%s) called" % [filename])
self.lock.acquire()
if not self.up:
self.lock.release()
raise Exception, "Can't access VM file as VM disk isn't ready"
return
raise Exception("Can't access VM file as VM disk isn't ready")
if self.booted:
self.lock.release()
raise Exception, "Can't access VM file as VM is already running"
return
raise Exception("Can't access VM file as VM is already running")
try:
f = self.openpausednodefile(filename, "w")
f.write(contents)
os.chmod(f.name, mode)
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:
self.lock.release()
@ -520,36 +511,37 @@ class XenNode(PyCoreNode):
self.CmdsRedirection[cmdAsString](self)
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
def cmdresult(self, args):
cmdAsString = string.join(args, ' ')
if cmdAsString in self.CmdsToIgnore:
# self.warn("XEN PVM cmd(args=[%s]) called and ignored" % cmdAsString)
return (0, "")
self.warn("XEN PVM cmdresult(args=[%s]) called, but not yet implemented" % cmdAsString)
return (0, "")
return 0, ""
logger.warn("XEN PVM cmdresult(args=[%s]) called, but not yet implemented" % cmdAsString)
return 0, ""
def popen(self, 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
def icmd(self, 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
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
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
use Xen console with a keymapping for easy login.
'''
"""
controlifc = None
for ifc in self.netifs():
if hasattr(ifc, 'control') and ifc.control == True:
@ -569,25 +561,14 @@ class XenNode(PyCoreNode):
return cmd
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
# 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):
self.warn("XEN PVM Nodes can't bind-mount filesystems")
logger.warn("XEN PVM Nodes can't bind-mount filesystems")
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):
self.lock.acquire()
@ -607,15 +588,15 @@ class XenNode(PyCoreNode):
return -1
def addnetif(self, netif, ifindex):
self.warn("XEN PVM addnetif() called")
logger.warn("XEN PVM addnetif() called")
PyCoreNode.addnetif(self, netif, ifindex)
def delnetif(self, ifindex):
self.warn("XEN PVM delnetif() called")
logger.warn("XEN PVM delnetif() called")
PyCoreNode.delnetif(self, ifindex)
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))
self.lock.acquire()
@ -624,7 +605,7 @@ class XenNode(PyCoreNode):
ifindex = self.newifindex()
if ifname is None:
ifname = "eth%d" % ifindex
sessionid = self.session.shortsessionid()
sessionid = self.session.short_session_id()
name = "n%s.%s.%s" % (self.objid, ifindex, sessionid)
localname = "n%s.%s.%s" % (self.objid, ifname, sessionid)
ifclass = XenVEth
@ -643,7 +624,7 @@ class XenNode(PyCoreNode):
self.lock.release()
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):
self._netif[ifindex].sethwaddr(addr)
@ -663,13 +644,15 @@ class XenNode(PyCoreNode):
try:
self._netif[ifindex].deladdr(addr)
except ValueError:
self.warn("trying to delete unknown address: %s" % addr)
logger.exception("trying to delete unknown address: %s", addr)
if self.up:
pass
# self.cmd([IP_BIN, "addr", "del", str(addr),
# "dev", self.ifname(ifindex)])
valid_deladdrtype = ("inet", "inet6", "inet6link")
def delalladdr(self, ifindex, addrtypes=valid_deladdrtype):
addr = self.getaddr(self.ifname(ifindex), rescan=True)
for t in addrtypes:
@ -688,24 +671,22 @@ class XenNode(PyCoreNode):
def newnetif(self, net=None, addrlist=[], hwaddr=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))
self.lock.acquire()
if not self.up:
self.lock.release()
raise Exception, "Can't access add veth as VM disk isn't ready"
return
raise Exception("Can't access add veth as VM disk isn't ready")
if self.booted:
self.lock.release()
raise Exception, "Can't access add veth as VM is already running"
return
raise Exception("Can't access add veth as VM is already running")
try:
if isinstance(net, EmaneNode):
raise Exception, "Xen PVM doesn't yet support Emane nets"
if nodeutils.is_node(net, NodeTypes.EMANE):
raise Exception("Xen PVM doesn't yet support Emane nets")
# ifindex = self.newtuntap(ifindex = ifindex, ifname = ifname,
# net = net)
@ -729,16 +710,19 @@ class XenNode(PyCoreNode):
self.etcdir,
'udev/rules.d/70-persistent-net.rules')
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"
# 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.close()
if hwaddr:
self.sethwaddr(ifindex, hwaddr)
for addr in maketuple(addrlist):
for addr in utils.maketuple(addrlist):
self.addaddr(ifindex, addr)
# self.ifup(ifindex)
return ifindex
@ -746,7 +730,7 @@ class XenNode(PyCoreNode):
self.lock.release()
def connectnode(self, ifname, othernode, otherifname):
self.warn("XEN PVM connectnode() called")
logger.warn("XEN PVM connectnode() called")
# tmplen = 8
# tmp1 = "tmp." + "".join([random.choice(string.ascii_lowercase)
@ -769,13 +753,11 @@ class XenNode(PyCoreNode):
self.lock.acquire()
if not self.up:
self.lock.release()
raise Exception, "Can't access VM file as VM disk isn't ready"
return
raise Exception("Can't access VM file as VM disk isn't ready")
if self.booted:
self.lock.release()
raise Exception, "Can't access VM file as VM is already running"
return
raise Exception("Can't access VM file as VM is already running")
if filename in self.FilesToIgnore:
# self.warn("XEN PVM addfile(filename=%s) ignored" % [filename])
@ -783,7 +765,7 @@ class XenNode(PyCoreNode):
if filename in self.FilesRedirection:
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
try:
@ -795,24 +777,24 @@ class XenNode(PyCoreNode):
fout.write(contents)
os.chmod(fout.name, mode)
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:
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" % \
# (filename, srcname, filename)
# self.shcmd(shcmd)
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
kpartx command will succeed.
'''
"""
# 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):
continue
o.umount(path)

View file

@ -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
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.
Node-specific config: XenConfigManager.configs[nodenumber] = (type, values)
The node having this specific number has this config.
'''
"""
import sys, os, threading, subprocess, time, string
import ConfigParser
from xml.dom.minidom import parseString, Document
from core.constants import *
import os
import string
from core import constants
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):
''' 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.
'''
_name = "xen"
_type = coreapi.CORE_TLV_REG_EMULSRV
"""
name = "xen"
config_type = RegisterTlvs.EMULATION_SERVER.value
def __init__(self, session):
ConfigurableManager.__init__(self, session)
self.verbose = self.session.getcfgitembool('verbose', False)
self.default_config = XenDefaultConfig(session, objid=None)
"""
Creates a XenConfigManager instance.
: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()
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
node for which no object exists yet
'''
"""
if nodenum is None:
nodenum = 0 # used for storing the global default config
return ConfigurableManager.setconfig(self, nodenum, conftype, values)
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
is None then we return a match on any conftype.
'''
"""
if nodenum is None:
nodenum = 0 # used for storing the global default config
return ConfigurableManager.getconfig(self, nodenum, conftype,
defaultvalues)
return ConfigurableManager.getconfig(self, nodenum, conftype, defaultvalues)
def clearconfig(self, nodenum):
''' remove configuration values for a node
'''
"""
remove configuration values for a node
"""
ConfigurableManager.clearconfig(self, nodenum)
if 0 in self.configs:
self.configs.pop(0)
def configure(self, session, msg):
''' Handle configuration messages for global Xen config.
'''
return self.default_config.configure(self, msg)
def configure(self, session, config_data):
"""
Handle configuration messages for global Xen config.
: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):
''' 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:
filename = os.path.join(CORE_CONF_DIR, 'xen.conf')
filename = os.path.join(constants.CORE_CONF_DIR, 'xen.conf')
cfg = ConfigParser.SafeConfigParser()
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
section = "xen"
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
self.configfile = dict(cfg.items(section))
# populate default config items from config file entries
@ -92,13 +106,14 @@ class XenConfigManager(ConfigurableManager):
if names[i] in self.configfile:
vals[i] = self.configfile[names[i]]
# 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):
''' 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.
If a value is supplied, it will override any stored config.
'''
"""
if value is not None:
return value
n = None
@ -112,7 +127,7 @@ class XenConfigManager(ConfigurableManager):
if v is None:
# get item from default config for the machine type
(t, v) = self.getconfig(nodenum=None,
conftype=self.default_config._name,
conftype=self.default_config.name,
defaultvalues=None)
confignames = self.default_config.getnames()
@ -124,142 +139,136 @@ class XenConfigManager(ConfigurableManager):
if name in self.configfile:
return self.configfile[name]
else:
#self.warn("missing config item '%s'" % name)
# logger.warn("missing config item '%s'" % name)
return None
class XenConfig(Configurable):
''' Manage Xen configuration profiles.
'''
"""
Manage Xen configuration profiles.
"""
@classmethod
def configure(cls, xen, msg):
''' Handle configuration messages for setting up a model.
def configure(cls, xen, config_data):
"""
Handle configuration messages for setting up a model.
Similar to Configurable.configure(), but considers opaque data
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:
opaque_items = opaque.split(':')
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
nodetype = opaque_items[1]
if xen.verbose:
xen.info("received configure message for %s" % nodetype)
if conftype == coreapi.CONF_TYPE_FLAGS_REQUEST:
if xen.verbose:
xen.info("replying to configure request for %s " % nodetype)
logger.info("received configure message for %s", nodetype)
if config_type == ConfigFlags.REQUEST.value:
logger.info("replying to configure request for %s " % nodetype)
# 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
# reply with the defaults for this model
if objname == "all":
typeflags = coreapi.CONF_TYPE_FLAGS_UPDATE
if object_name == "all":
typeflags = ConfigFlags.UPDATE.value
else:
typeflags = coreapi.CONF_TYPE_FLAGS_NONE
values = xen.getconfig(nodenum, nodetype, defaultvalues=None)[1]
typeflags = ConfigFlags.NONE.value
values = xen.getconfig(node_id, nodetype, defaultvalues=None)[1]
if values is None:
# get defaults from default "xen" config which includes
# settings from both cls._confdefaultvalues and xen.conf
defaults = cls.getdefaultvalues()
values = xen.getconfig(nodenum, cls._name, defaults)[1]
values = xen.getconfig(node_id, cls.name, defaults)[1]
if values is None:
return None
# reply with config options
if nodenum is None:
nodenum = 0
reply = cls.toconfmsg(0, nodenum, typeflags, nodetype, values)
elif conftype == coreapi.CONF_TYPE_FLAGS_RESET:
if objname == "all":
xen.clearconfig(nodenum)
if node_id is None:
node_id = 0
reply = cls.config_data(0, node_id, typeflags, nodetype, values)
elif config_type == ConfigFlags.RESET.value:
if object_name == "all":
xen.clearconfig(node_id)
# elif conftype == coreapi.CONF_TYPE_FLAGS_UPDATE:
else:
# store the configuration values for later use, when the XenNode
# object has been created
if objname is None:
xen.info("no configuration object for node %s" % nodenum)
if object_name is None:
logger.info("no configuration object for node %s" % node_id)
return None
values_str = msg.gettlv(coreapi.CORE_TLV_CONF_VALUES)
if values_str is None:
# use default or preconfigured values
defaults = cls.getdefaultvalues()
values = xen.getconfig(nodenum, cls._name, defaults)[1]
values = xen.getconfig(node_id, cls.name, defaults)[1]
else:
# use new values supplied from the conf message
values = values_str.split('|')
xen.setconfig(nodenum, nodetype, values)
xen.setconfig(node_id, nodetype, values)
return reply
@classmethod
def toconfmsg(cls, flags, nodenum, typeflags, nodetype, values):
''' Convert this class to a Config API message. Some TLVs are defined
def config_data(cls, flags, node_id, type_flags, nodetype, values):
"""
Convert this class to a Config API message. Some TLVs are defined
by the class, but node number, conf type flags, and values must
be passed in.
'''
"""
values_str = string.join(values, '|')
tlvdata = ""
tlvdata += coreapi.CoreConfTlv.pack(coreapi.CORE_TLV_CONF_NODE, nodenum)
tlvdata += coreapi.CoreConfTlv.pack(coreapi.CORE_TLV_CONF_OBJ,
cls._name)
tlvdata += coreapi.CoreConfTlv.pack(coreapi.CORE_TLV_CONF_TYPE,
typeflags)
datatypes = tuple( map(lambda x: x[1], cls._confmatrix) )
tlvdata += coreapi.CoreConfTlv.pack(coreapi.CORE_TLV_CONF_DATA_TYPES,
datatypes)
tlvdata += coreapi.CoreConfTlv.pack(coreapi.CORE_TLV_CONF_VALUES,
values_str)
captions = reduce( lambda a,b: a + '|' + b, \
map(lambda x: x[4], cls._confmatrix))
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)
opaque = "%s:%s" % (cls._name, nodetype)
tlvdata += coreapi.CoreConfTlv.pack(coreapi.CORE_TLV_CONF_OPAQUE,
opaque)
tlvdata += coreapi.CoreConfigTlv.pack(ConfigTlvs.NODE.value, node_id)
tlvdata += coreapi.CoreConfigTlv.pack(ConfigTlvs.OBJECT.value, cls.name)
tlvdata += coreapi.CoreConfigTlv.pack(ConfigTlvs.TYPE.value, type_flags)
datatypes = tuple(map(lambda x: x[1], cls.config_matrix))
tlvdata += coreapi.CoreConfigTlv.pack(ConfigTlvs.DATA_TYPES.value, datatypes)
tlvdata += coreapi.CoreConfigTlv.pack(ConfigTlvs.VALUES.value, values_str)
captions = reduce(lambda a, b: a + '|' + b, map(lambda x: x[4], cls.config_matrix))
tlvdata += coreapi.CoreConfigTlv.pack(ConfigTlvs.CAPTIONS, captions)
possiblevals = reduce(lambda a, b: a + '|' + b, map(lambda x: x[3], cls.config_matrix))
tlvdata += coreapi.CoreConfigTlv.pack(ConfigTlvs.POSSIBLE_VALUES.value, possiblevals)
if cls.bitmap is not None:
tlvdata += coreapi.CoreConfigTlv.pack(ConfigTlvs.BITMAP.value, cls.bitmap)
if cls.config_groups is not None:
tlvdata += coreapi.CoreConfigTlv.pack(ConfigTlvs.GROUPS.value, cls.config_groups)
opaque = "%s:%s" % (cls.name, nodetype)
tlvdata += coreapi.CoreConfigTlv.pack(ConfigTlvs.OPAQUE.value, opaque)
msg = coreapi.CoreConfMessage.pack(flags, tlvdata)
return msg
class XenDefaultConfig(XenConfig):
''' Global default Xen configuration options.
'''
_name = "xen"
"""
Global default Xen configuration options.
"""
name = "xen"
# Configuration items:
# ('name', 'type', 'default', 'possible-value-list', 'caption')
_confmatrix = [
('ram_size', coreapi.CONF_DATA_TYPE_STRING, '256', '',
config_matrix = [
('ram_size', ConfigDataTypes.STRING.value, '256', '',
'ram size (MB)'),
('disk_size', coreapi.CONF_DATA_TYPE_STRING, '256M', '',
('disk_size', ConfigDataTypes.STRING.value, '256M', '',
'disk size (use K/M/G suffix)'),
('iso_file', coreapi.CONF_DATA_TYPE_STRING, '', '',
('iso_file', ConfigDataTypes.STRING.value, '', '',
'iso file'),
('mount_path', coreapi.CONF_DATA_TYPE_STRING, '', '',
('mount_path', ConfigDataTypes.STRING.value, '', '',
'mount path'),
('etc_path', coreapi.CONF_DATA_TYPE_STRING, '', '',
('etc_path', ConfigDataTypes.STRING.value, '', '',
'etc path'),
('persist_tar_iso', coreapi.CONF_DATA_TYPE_STRING, '', '',
('persist_tar_iso', ConfigDataTypes.STRING.value, '', '',
'iso persist tar file'),
('persist_tar', coreapi.CONF_DATA_TYPE_STRING, '', '',
('persist_tar', ConfigDataTypes.STRING.value, '', '',
'persist tar file'),
('root_password', coreapi.CONF_DATA_TYPE_STRING, 'password', '',
('root_password', ConfigDataTypes.STRING.value, 'password', '',
'root password'),
]
_confgroups = "domU properties:1-%d" % len(_confmatrix)
config_groups = "domU properties:1-%d" % len(config_matrix)

View file

View file

@ -1,11 +1,18 @@
import os
import socket
import subprocess
import os
import xmlutils
from core.netns import nodes
from core.misc import ipaddr
from core import constants
from core import emane
from core.enumerations import NodeTypes
from core.misc import ipaddress
from core.misc import log
from core.misc import nodeutils
from core.netns import nodes
from core.xml import xmlutils
logger = log.get_logger(__name__)
class CoreDeploymentWriter(object):
def __init__(self, dom, root, session):
@ -13,7 +20,7 @@ class CoreDeploymentWriter(object):
self.root = root
self.session = session
self.hostname = socket.gethostname()
if self.session.emane.version < self.session.emane.EMANE092:
if emane.VERSION < emane.EMANE092:
self.transport = None
self.platform = None
@ -37,8 +44,10 @@ class CoreDeploymentWriter(object):
@staticmethod
def get_interface_names(hostname):
'''Uses same methodology of get_ipv4_addresses() to get
parallel list of interface names to go with ...'''
"""
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')
@ -47,10 +56,10 @@ class CoreDeploymentWriter(object):
split = line.split()
if not split:
continue
ifaceName = split[1]
interface_name = split[1]
addr = split[3]
if not addr.startswith('127.'):
iface_list.append(ifaceName)
iface_list.append(interface_name)
return iface_list
else:
# TODO: handle other hosts
@ -58,16 +67,14 @@ class CoreDeploymentWriter(object):
@staticmethod
def find_device(scenario, name):
tagName = ('device', 'host', 'router')
for d in xmlutils.iterDescendantsWithAttribute(scenario, tagName,
'name', name):
tag_name = ('device', 'host', 'router')
for d in xmlutils.iter_descendants_with_attribute(scenario, tag_name, 'name', name):
return d
return None
@staticmethod
def find_interface(device, name):
for i in xmlutils.iterDescendantsWithAttribute(device, 'interface',
'name', name):
for i in xmlutils.iter_descendants_with_attribute(device, 'interface', 'name', name):
return i
return None
@ -75,9 +82,9 @@ class CoreDeploymentWriter(object):
testbed = self.dom.createElement('container')
testbed.setAttribute('name', 'TestBed')
testbed.setAttribute('id', 'TestBed')
self.root.baseEle.appendChild(testbed)
self.root.base_element.appendChild(testbed)
nodelist = []
for obj in self.session.objs():
for obj in self.session.objects.itervalues():
if isinstance(obj, nodes.PyCoreNode):
nodelist.append(obj)
name = self.hostname
@ -87,17 +94,16 @@ class CoreDeploymentWriter(object):
for n in nodelist:
self.add_virtual_host(testhost, n)
# TODO: handle other servers
# servers = self.session.broker.getservernames()
# servers = self.session.broker.getserverlist()
# servers.remove('localhost')
def add_child_element(self, parent, tagName):
el = self.dom.createElement(tagName)
def add_child_element(self, parent, tag_name):
el = self.dom.createElement(tag_name)
parent.appendChild(el)
return el
def add_child_element_with_nameattr(self, parent, tagName,
name, setid = True):
el = self.add_child_element(parent, tagName)
def add_child_element_with_nameattr(self, parent, tag_name, name, setid=True):
el = self.add_child_element(parent, tag_name)
el.setAttribute('name', name)
if setid:
el.setAttribute('id', '%s/%s' % (parent.getAttribute('id'), name))
@ -117,8 +123,7 @@ class CoreDeploymentWriter(object):
return el
def add_platform(self, parent, name):
el = self.add_child_element_with_nameattr(parent,
'emanePlatform', name)
el = self.add_child_element_with_nameattr(parent, 'emanePlatform', name)
return el
def add_transport(self, parent, name):
@ -130,8 +135,7 @@ class CoreDeploymentWriter(object):
return el
def add_parameter(self, parent, name, val):
el = self.add_child_element_with_nameattr(parent, 'parameter',
name, False)
el = self.add_child_element_with_nameattr(parent, 'parameter', name, False)
el.appendChild(self.dom.createTextNode(val))
return el
@ -151,42 +155,40 @@ class CoreDeploymentWriter(object):
for i in range(0, len(ipv4_addresses)):
addr = ipv4_addresses[i]
if iface_names:
ifaceName = iface_names[i]
interface_name = iface_names[i]
else:
ifaceName = None
self.add_address(el, 'IPv4', addr, ifaceName)
interface_name = None
self.add_address(el, 'IPv4', addr, interface_name)
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)
device = self.find_device(self.root.base_element, obj.name)
if device is None:
self.session.warn('corresponding XML device not found for %s' %
(obj.name))
logger.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):
if ipaddress.is_ipv4_address(addr):
addr_type = 'IPv4'
elif ipaddr.isIPv6Address(addr):
elif ipaddress.is_ipv6_address(addr):
addr_type = 'IPv6'
else:
raise NotImplementedError
self.add_address(el, addr_type, address, netif.name)
if isinstance(netif.net, nodes.EmaneNode):
if nodeutils.is_node(netif.net, NodeTypes.EMANE):
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'):
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 emane.VERSION < emane.EMANE092:
if self.platform is None:
self.platform = \
self.add_platform(physical_host, name=platform_name)

View file

@ -1,38 +1,38 @@
# 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
from core.xml.xmlparser0 import CoreDocumentParser0
from core.xml.xmlparser1 import CoreDocumentParser1
from core.xml.xmlutils import get_first_child_by_tag_name
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={}):
"""
DEFAULT_SCENARIO_VERSION = '1.0'
def __init__(self, filename, options):
if 'dom' in options:
self.dom = options['dom']
else:
self.dom = parse(filename)
scenario = getFirstChildByTagName(self.dom, 'scenario')
scenario = get_first_child_by_tag_name(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'):
elif get_first_child_by_tag_name(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:

View file

@ -1,19 +1,17 @@
#
# 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 *
from core.enumerations import NodeTypes
from core.misc import log
from core.misc import nodeutils
from core.service import ServiceManager
from core.xml import xmlutils
logger = log.get_logger(__name__)
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
@ -23,12 +21,12 @@ class CoreDocumentParser0(object):
self.start = options['start']
self.nodecls = options['nodecls']
self.np = getoneelement(self.dom, "NetworkPlan")
self.np = xmlutils.get_one_element(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.mp = xmlutils.get_one_element(self.dom, "MotionPlan")
self.sp = xmlutils.get_one_element(self.dom, "ServicePlan")
self.meta = xmlutils.get_one_element(self.dom, "CoreMetaData")
self.coords = self.getmotiondict(self.mp)
# link parameters parsed in parsenets(), applied in parsenodes()
@ -41,16 +39,11 @@ class CoreDocumentParser0(object):
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
"""
Parse a MotionPlan into a dict with node names for keys and coordinates
for values.
'''
"""
if mp is None:
return {}
coords = {}
@ -70,35 +63,34 @@ class CoreDocumentParser0(object):
xyz = map(int, txt.nodeValue.split(','))
z = None
x, y = xyz[0:2]
if (len(xyz) == 3):
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.
'''
"""
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)
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)
nodecls = xmlutils.xml_type_to_node_class(self.session, type)
if not nodecls:
self.warn("skipping unknown network node '%s' type '%s'" % \
(name, type))
logger.warn("skipping unknown network node '%s' type '%s'", name, type)
continue
n = self.session.addobj(cls = nodecls, objid = id, name = name,
start = self.start)
n = self.session.add_object(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)
xmlutils.get_params_set_attrs(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)
@ -108,12 +100,11 @@ class CoreDocumentParser0(object):
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:
for n, netid, ifcname in linkednets:
try:
n2 = n.session.objbyname(netid)
n2 = n.session.get_object_by_name(netid)
except KeyError:
n.warn("skipping net %s interface: unknown net %s" % \
(n.name, netid))
logger.warn("skipping net %s interface: unknown net %s", n.name, netid)
continue
upstream = False
netif = n.getlinknetif(n2)
@ -124,7 +115,7 @@ class CoreDocumentParser0(object):
upstream = True
key = (n2.name, ifcname)
if key in self.linkparams:
for (k, v) in self.linkparams[key]:
for k, v in self.linkparams[key]:
netif.setparam(k, v)
if upstream:
netif.swapparams('_params_up')
@ -133,75 +124,75 @@ class CoreDocumentParser0(object):
for node in self.np.getElementsByTagName("Node"):
id, name, type = self.getcommonattributes(node)
if type == "rj45":
nodecls = nodes.RJ45Node
nodecls = nodeutils.get_node_class(NodeTypes.RJ45)
else:
nodecls = self.nodecls
n = self.session.addobj(cls = nodecls, objid = id, name = name,
start = self.start)
n = self.session.add_object(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)
xmlutils.get_params_set_attrs(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:
"""
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)
net = n.session.get_object_by_name(netid)
except KeyError:
n.warn("skipping node %s interface %s: unknown net %s" % \
(n.name, name, netid))
logger.warn("skipping node %s interface %s: unknown net %s", n.name, name, netid)
return
for addr in ifc.getElementsByTagName("address"):
addrstr = gettextchild(addr)
addrstr = xmlutils.get_text_child(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)
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]:
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
"""
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
"""
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)
kvs = xmlutils.get_text_elements_to_list(model)
mgr = self.session.mobility
# TODO: the session.confobj() mechanism could be more generic;
@ -220,9 +211,10 @@ class CoreDocumentParser0(object):
mgr.setconfig_keyvalues(nodenum, name, kvs)
def parsenetem(self, model, obj, kvs):
''' Determine interface and invoke setparam() using the parsed
"""
Determine interface and invoke setparam() using the parsed
(key, value) pairs.
'''
"""
ifname = model.getAttribute("netif")
peer = model.getAttribute("peer")
key = (peer, ifname)
@ -232,8 +224,7 @@ class CoreDocumentParser0(object):
# 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))
logger.warn("error parsing link parameters for '%s' on '%s'", ifname, peer)
self.linkparams[key] = kvs
@staticmethod
@ -243,13 +234,14 @@ class CoreDocumentParser0(object):
value = float(value)
else:
value = int(value)
return (key, value)
return key, value
def parseorigin(self):
''' Parse any origin tag from the Mobility Plan and set the CoreLocation
"""
Parse any origin tag from the Mobility Plan and set the CoreLocation
reference point appropriately.
'''
origin = getoneelement(self.mp, "origin")
"""
origin = xmlutils.get_one_element(self.mp, "origin")
if not origin:
return
location = self.session.location
@ -264,7 +256,7 @@ class CoreDocumentParser0(object):
scale = origin.getAttribute("scale100")
if scale is not None:
location.refscale = float(scale)
point = getoneelement(origin, "point")
point = xmlutils.get_one_element(origin, "point")
if point is not None and point.firstChild is not None:
xyz = point.firstChild.nodeValue.split(',')
if len(xyz) == 2:
@ -274,9 +266,10 @@ class CoreDocumentParser0(object):
location.refxyz = (xyz[0], xyz[1], xyz[2])
def parsedefaultservices(self):
''' Prior to parsing nodes, use session.services manager to store
"""
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 == '':
@ -285,23 +278,22 @@ class CoreDocumentParser0(object):
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))
logger.info("default services for type %s set to %s" % (type, services))
def parseservices(self):
''' After node objects exist, parse service customizations and add them
"""
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)
n = self.session.get_object_by_name(name)
if n is None:
self.warn("skipping service config for unknown node '%s'" % \
name)
logger.warn("skipping service config for unknown node '%s'" % name)
continue
for service in node.getElementsByTagName("Service"):
svcname = service.getAttribute("name")
@ -321,17 +313,16 @@ class CoreDocumentParser0(object):
# 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)
n = self.session.get_object(objid)
self.session.services.addservicestonode(node=n, nodetype=n.type, services_str=svclists[objid])
def parseservice(self, service, n):
''' Use session.services manager to store service customizations before
"""
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)
svc = ServiceManager.get(name)
if svc is None:
return False
values = []
@ -353,7 +344,7 @@ class CoreDocumentParser0(object):
validate = []
for cmd in service.getElementsByTagName("Command"):
type = cmd.getAttribute("type")
cmdstr = gettextchild(cmd)
cmdstr = xmlutils.get_text_child(cmd)
if cmdstr is None:
continue
if type == "start":
@ -373,7 +364,7 @@ class CoreDocumentParser0(object):
for file in service.getElementsByTagName("File"):
filename = file.getAttribute("name")
files.append(filename)
data = gettextchild(file)
data = xmlutils.get_text_child(file)
typestr = "service:%s:%s" % (name, filename)
self.session.services.setservicefile(nodenum=n.objid, type=typestr,
filename=filename,
@ -391,30 +382,29 @@ class CoreDocumentParser0(object):
for hook in hooks.getElementsByTagName("Hook"):
filename = hook.getAttribute("name")
state = hook.getAttribute("state")
data = gettextchild(hook)
data = xmlutils.get_text_child(hook)
if data is None:
data = "" # allow for empty file
type = "hook:%s" % state
self.session.sethook(type, filename=filename,
srcname=None, data=data)
self.session.set_hook(type, file_name=filename, source_name=None, data=data)
def parsemeta(self):
opt = getoneelement(self.meta, "SessionOptions")
opt = xmlutils.get_one_element(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
v = xmlutils.get_text_child(param) # allow attribute/text for newlines
setattr(self.session.options, k, v)
hooks = getoneelement(self.meta, "Hooks")
hooks = xmlutils.get_one_element(self.meta, "Hooks")
if hooks:
self.parsehooks(hooks)
meta = getoneelement(self.meta, "MetaData")
meta = xmlutils.get_one_element(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)
v = xmlutils.get_text_child(param)
self.session.metadata.add_item(k, v)

View file

@ -1,19 +1,19 @@
#
# 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 Node
from xml.dom.minidom import parse
from xmlutils import *
from core import constants
from core.enumerations import NodeTypes
from core.misc import log
from core.misc import nodeutils
from core.misc.ipaddress import MacAddress
from core.service import ServiceManager
from core.xml import xmlutils
logger = log.get_logger(__name__)
class CoreDocumentParser1(object):
layer2_device_types = 'hub', 'switch'
layer3_device_types = 'host', 'router'
device_types = layer2_device_types + layer3_device_types
@ -23,8 +23,14 @@ class CoreDocumentParser1(object):
# TunnelNode
def __init__(self, session, filename, options):
"""
:param core.session.Session session:
:param filename:
:param options:
:return:
"""
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
@ -46,23 +52,9 @@ class CoreDocumentParser1(object):
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')
scenario = xmlutils.get_first_child_by_tag_name(dom, 'scenario')
if not scenario:
raise ValueError, 'no scenario element found'
version = scenario.getAttribute('version')
@ -90,10 +82,10 @@ class CoreDocumentParser1(object):
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)
@ -109,19 +101,21 @@ class CoreDocumentParser1(object):
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'))
@ -129,9 +123,8 @@ class CoreDocumentParser1(object):
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)
for interface in xmlutils.iter_children_with_attribute(element, 'member', 'type', 'interface'):
if_id = xmlutils.get_child_text_trim(interface)
assert if_id # XXX for testing
if not if_id:
continue
@ -141,38 +134,32 @@ class CoreDocumentParser1(object):
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
"""
if network_type in ['ethernet', 'satcom']:
return nodeutils.get_node_class(NodeTypes.PEER_TO_PEER)
elif network_type == 'wireless':
channel = getFirstChildByTagName(network, 'channel')
channel = xmlutils.get_first_child_by_tag_name(network, 'channel')
if channel:
# use an explicit CORE type if it exists
coretype = getFirstChildTextTrimWithAttribute(channel, 'type',
'domain', 'CORE')
coretype = xmlutils.get_first_child_text_trim_with_attribute(channel, 'type', 'domain', 'CORE')
if coretype:
if coretype == 'basic_range':
return nodes.WlanNode
return nodeutils.get_node_class(NodeTypes.WIRELESS_LAN)
elif coretype.startswith('emane'):
return nodes.EmaneNode
return nodeutils.get_node_class(NodeTypes.EMANE)
else:
self.warn('unknown network type: \'%s\'' % coretype)
return xmltypetonodeclass(self.session, coretype)
return nodes.WlanNode
self.warn('unknown network type: \'%s\'' % network_type)
logger.warn('unknown network type: \'%s\'', coretype)
return xmlutils.xml_type_to_node_class(self.session, coretype)
return nodeutils.get_node_class(NodeTypes.WIRELESS_LAN)
logger.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))
obj = self.session.add_object(cls=objcls, objid=objid, name=objname, start=self.start)
logger.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
@ -180,7 +167,7 @@ class CoreDocumentParser1(object):
def get_core_object(self, idstr):
if idstr and idstr in self.objidmap:
objid = self.objidmap[idstr]
return self.session.obj(objid)
return self.session.get_object(objid)
return None
def parse_network_plan(self):
@ -192,8 +179,7 @@ class CoreDocumentParser1(object):
self.parse_networks()
self.parse_layer3_devices()
def set_ethernet_link_parameters(self, channel, link_params,
mobility_model_name, mobility_params):
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):
@ -204,18 +190,15 @@ class CoreDocumentParser1(object):
if mobility_model_name or mobility_params:
raise NotImplementedError
def set_wireless_link_parameters(self, channel, link_params,
mobility_model_name, mobility_params):
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'))
logger.warn('unknown network: %s', network.toxml('utf-8'))
assert False # XXX for testing
return
model_name = getFirstChildTextTrimWithAttribute(channel, 'type',
'domain', 'CORE')
model_name = xmlutils.get_first_child_text_trim_with_attribute(channel, 'type', 'domain', 'CORE')
if not model_name:
model_name = 'basic_range'
if model_name == 'basic_range':
@ -229,13 +212,12 @@ class CoreDocumentParser1(object):
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())
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')
@ -243,7 +225,7 @@ class CoreDocumentParser1(object):
assert dev1 and dev2 # XXX for testing
if dev1 and dev2:
# TODO: review this
if isinstance(dev2, nodes.RJ45Node):
if nodeutils.is_node(dev2, NodeTypes.RJ45):
# RJ45 nodes have different linknet()
netif = dev2.linknet(dev1)
else:
@ -266,7 +248,7 @@ class CoreDocumentParser1(object):
@classmethod
def parse_parameter_children(cls, parent):
params = {}
for parameter in iterChildrenWithName(parent, 'parameter'):
for parameter in xmlutils.iter_children_with_name(parent, 'parameter'):
param_name = parameter.getAttribute('name')
assert param_name # XXX for testing
if not param_name:
@ -274,36 +256,30 @@ class CoreDocumentParser1(object):
# TODO: consider supporting unicode; for now convert
# to an ascii string
param_name = str(param_name)
param_val = cls.parse_xml_value(getChildTextTrim(parameter))
param_val = cls.parse_xml_value(xmlutils.get_child_text_trim(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)
element = self.search_for_element(channel, 'type', lambda x: not x.hasAttributes())
channel_type = xmlutils.get_child_text_trim(element)
link_params = self.parse_parameter_children(channel)
mobility = getFirstChildByTagName(channel, 'CORE:mobility')
mobility = xmlutils.get_first_child_by_tag_name(channel, 'CORE:mobility')
if mobility:
mobility_model_name = \
getFirstChildTextTrimByTagName(mobility, 'type')
mobility_model_name = xmlutils.get_first_child_text_trim_by_tag_name(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)
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)
self.set_ethernet_link_parameters(channel, link_params, mobility_model_name, mobility_params)
else:
raise NotImplementedError
layer2_device = []
@ -316,14 +292,14 @@ class CoreDocumentParser1(object):
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):
@ -336,31 +312,28 @@ class CoreDocumentParser1(object):
layer3_members += 1
else:
raise NotImplementedError
if len(layer2_members) == 0:
net_type = getFirstChildTextTrimByTagName(network, 'type')
net_type = xmlutils.get_first_child_text_trim_by_tag_name(network, 'type')
if not net_type:
msg = 'no network type found for network: \'%s\'' % \
network.toxml('utf-8')
self.warn(msg)
logger.warn('no network type found for network: \'%s\'', network.toxml('utf-8'))
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:
logger.info('parsing network: name=%s id=%s' % (net_name, objid))
if objid in self.session.objects:
return
n = self.create_core_object(net_cls, objid, net_name,
network, None)
n = self.create_core_object(net_cls, objid, net_name, network, None)
# handle channel parameters
for channel in iterChildrenWithName(network, 'channel'):
for channel in xmlutils.iter_children_with_name(network, 'channel'):
self.parse_network_channel(channel)
def parse_networks(self):
'''\
"""
Parse all 'network' elements.
'''
for network in iterDescendantsWithName(self.scenario, 'network'):
"""
for network in xmlutils.iter_descendants_with_name(self.scenario, 'network'):
self.parse_network(network)
def parse_addresses(self, interface):
@ -368,21 +341,19 @@ class CoreDocumentParser1(object):
ipv4 = []
ipv6 = []
hostname = []
for address in iterChildrenWithName(interface, 'address'):
for address in xmlutils.iter_children_with_name(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)
logger.warn(msg)
assert False # XXX for testing
continue
addr_text = getChildTextTrim(address)
addr_text = xmlutils.get_child_text_trim(address)
if not addr_text:
msg = 'no text found for address ' \
'in interface: \'%s\'' % interface.toxml('utf-8')
self.warn(msg)
logger.warn(msg)
assert False # XXX for testing
continue
if addr_type == 'mac':
mac.append(addr_text)
elif addr_type == 'IPv4':
@ -394,44 +365,37 @@ class CoreDocumentParser1(object):
else:
msg = 'skipping unknown address type \'%s\' in ' \
'interface: \'%s\'' % (addr_type, interface.toxml('utf-8'))
self.warn(msg)
logger.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)
logger.warn(msg)
assert False # XXX for testing
return
mac, ipv4, ipv6, hostname = self.parse_addresses(interface)
if mac:
hwaddr = MacAddr.fromstring(mac[0])
hwaddr = MacAddress.from_string(mac[0])
else:
hwaddr = None
ifindex = node.newnetif(network, addrlist = ipv4 + ipv6,
hwaddr = hwaddr, ifindex = None,
ifname = if_name)
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)
logger.info(msg)
# set link parameters for wired links
if isinstance(network,
(nodes.HubNode, nodes.PtpNet, nodes.SwitchNode)):
if nodeutils.is_node(network, (NodeTypes.HUB, NodeTypes.PEER_TO_PEER, NodeTypes.SWITCH)):
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):
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)
@ -443,22 +407,20 @@ class CoreDocumentParser1(object):
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)
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):
'''\
def search_for_element(node, tag_name, 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)):
for child in xmlutils.iter_children(node, Node.ELEMENT_NODE):
if child.tagName == tag_name and (match is None or match(child)):
return child
node = node.parentNode
if not node:
@ -470,9 +432,10 @@ class CoreDocumentParser1(object):
def match(x):
domain = x.getAttribute('domain')
return domain == 'COREID'
alias = cls.search_for_element(node, 'alias', match)
if alias:
return getChildTextTrim(alias)
return xmlutils.get_child_text_trim(alias)
return None
@classmethod
@ -487,8 +450,7 @@ class CoreDocumentParser1(object):
return None
def find_interface_network_object(self, interface):
network_id = getFirstChildTextTrimWithAttribute(interface, 'member',
'type', 'network')
network_id = xmlutils.get_first_child_text_trim_with_attribute(interface, 'member', 'type', 'network')
if not network_id:
# support legacy notation: <interface net="netid" ...
network_id = interface.getAttribute('net')
@ -498,16 +460,13 @@ class CoreDocumentParser1(object):
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')
channel_id = xmlutils.get_first_child_text_trim_with_attribute(interface, 'member', 'type', 'channel')
if not network_id or not channel_id:
return None
network = getFirstChildWithAttribute(self.scenario, 'network',
'id', network_id)
network = xmlutils.get_first_child_with_attribute(self.scenario, 'network', 'id', network_id)
if not network:
return None
channel = getFirstChildWithAttribute(network, 'channel',
'id', channel_id)
channel = xmlutils.get_first_child_with_attribute(network, 'channel', 'id', channel_id)
if not channel:
return None
device = None
@ -532,12 +491,10 @@ class CoreDocumentParser1(object):
# 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))
logger.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))
logger.warn('limiting negative y position of \'%s\' to zero: %s' % (obj.name, y))
y = 0.0
obj.setposition(x, y, z)
@ -558,12 +515,10 @@ class CoreDocumentParser1(object):
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))
logger.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))
logger.warn('limiting negative y position of \'%s\' to zero: %s' % (obj.name, y))
y = 0.0
obj.setposition(x, y, z)
@ -586,20 +541,18 @@ class CoreDocumentParser1(object):
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))
logger.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))
logger.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
@ -607,9 +560,8 @@ class CoreDocumentParser1(object):
if not point_type:
msg = 'no type attribute found for point: \'%s\'' % \
point.toxml('utf-8')
self.warn(msg)
logger.warn(msg)
assert False # XXX for testing
return False
elif point_type == 'pixel':
self.set_object_position_pixel(obj, point)
elif point_type == 'gps':
@ -617,21 +569,17 @@ class CoreDocumentParser1(object):
elif point_type == 'cart':
self.set_object_position_cartesian(obj, point)
else:
self.warn("skipping unknown point type: '%s'" % point_type)
logger.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)
logger.info('set position of %s from point element: \'%s\'', obj.name, point.toxml('utf-8'))
return True
def parse_device_service(self, service, node):
name = service.getAttribute('name')
session_service = self.session.services.getservicebyname(name)
session_service = ServiceManager.get(name)
if not session_service:
assert False # XXX for testing
return None
values = []
startup_idx = service.getAttribute('startup_idx')
if startup_idx:
@ -640,7 +588,7 @@ class CoreDocumentParser1(object):
if startup_time:
values.append('starttime=%s' % startup_time)
dirs = []
for directory in iterChildrenWithName(service, 'directory'):
for directory in xmlutils.iter_children_with_name(service, 'directory'):
dirname = directory.getAttribute('name')
dirs.append(str(dirname))
if dirs:
@ -648,9 +596,9 @@ class CoreDocumentParser1(object):
startup = []
shutdown = []
validate = []
for command in iterChildrenWithName(service, 'command'):
for command in xmlutils.iter_children_with_name(service, 'command'):
command_type = command.getAttribute('type')
command_text = getChildTextTrim(command)
command_text = xmlutils.get_child_text_trim(command)
if not command_text:
continue
if command_type == 'start':
@ -667,12 +615,12 @@ class CoreDocumentParser1(object):
values.append('cmdval=%s' % validate)
filenames = []
files = []
for f in iterChildrenWithName(service, 'file'):
for f in xmlutils.iter_children_with_name(service, 'file'):
filename = f.getAttribute('name')
if not filename:
continue;
continue
filenames.append(filename)
data = getChildTextTrim(f)
data = xmlutils.get_child_text_trim(f)
if data:
data = str(data)
else:
@ -683,48 +631,48 @@ class CoreDocumentParser1(object):
values.append('files=%s' % filenames)
custom = service.getAttribute('custom')
if custom and custom.lower() == 'true':
self.session.services.setcustomservice(node.objid,
session_service, values)
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,
self.session.services.setservicefile(
nodenum=node.objid,
type=typestr,
filename=filename,
srcname=None,
data = data)
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'):
for service in xmlutils.iter_children_with_name(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')
"""
services = xmlutils.get_first_child_by_tag_name(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))
logger.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,
self.session.services.addservicestonode(
node=node,
nodetype=node_type,
services_str = services_str,
verbose = self.verbose)
services_str=services_str
)
def set_object_presentation(self, obj, element, node_type):
# defaults from the CORE GUI
@ -737,7 +685,7 @@ class CoreDocumentParser1(object):
# 'xen': 'xen.gif'
}
icon_set = False
for child in iterChildrenWithName(element, 'CORE:presentation'):
for child in xmlutils.iter_children_with_name(element, 'CORE:presentation'):
canvas = child.getAttribute('canvas')
if canvas:
obj.canvas = int(canvas)
@ -757,18 +705,15 @@ class CoreDocumentParser1(object):
def core_node_type(self, device):
# use an explicit CORE type if it exists
coretype = getFirstChildTextTrimWithAttribute(device, 'type',
'domain', 'CORE')
coretype = xmlutils.get_first_child_text_trim_with_attribute(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)
for device in xmlutils.iter_descendants_with_name(self.scenario, self.device_types):
interface = xmlutils.get_first_child_with_attribute(device, 'interface', 'id', interface_id)
if interface:
if_name = interface.getAttribute('name')
return device, if_name
@ -776,72 +721,70 @@ class CoreDocumentParser1(object):
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))
logger.info('parsing layer-2 device: name=%s id=%s' % (device_name, objid))
try:
return self.session.obj(objid)
return self.session.get_object(objid)
except KeyError:
pass
logger.exception("error geting object: %s", objid)
device_type = self.device_type(device)
if device_type == 'hub':
device_class = nodes.HubNode
device_class = nodeutils.get_node_class(NodeTypes.HUB)
elif device_type == 'switch':
device_class = nodes.SwitchNode
device_class = nodeutils.get_node_class(NodeTypes.SWITCH)
else:
self.warn('unknown layer-2 device type: \'%s\'' % device_type)
logger.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)
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))
logger.info('parsing layer-3 device: name=%s id=%s', device_name, objid)
try:
return self.session.obj(objid)
return self.session.get_object(objid)
except KeyError:
pass
logger.exception("error getting session object: %s", objid)
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 = 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'):
for interface in xmlutils.iter_children_with_name(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):
for device in xmlutils.iter_descendants_with_name(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):
for device in xmlutils.iter_descendants_with_name(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')
origin = xmlutils.get_first_child_by_tag_name(session_config, 'origin')
if not origin:
return
lat = origin.getAttribute('lat')
@ -853,18 +796,17 @@ class CoreDocumentParser1(object):
scale100 = origin.getAttribute("scale100")
if scale100:
self.session.location.refscale = float(scale100)
point = getFirstChildTextTrimByTagName(origin, 'point')
point = xmlutils.get_first_child_text_trim_by_tag_name(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.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')
options = xmlutils.get_first_child_by_tag_name(session_config, 'options')
if not options:
return
params = self.parse_parameter_children(options)
@ -873,34 +815,32 @@ class CoreDocumentParser1(object):
setattr(self.session.options, str(name), str(value))
def parse_session_hooks(self, session_config):
'''\
"""
Parse hook scripts.
'''
hooks = getFirstChildByTagName(session_config, 'hooks')
"""
hooks = xmlutils.get_first_child_by_tag_name(session_config, 'hooks')
if not hooks:
return
for hook in iterChildrenWithName(hooks, 'hook'):
for hook in xmlutils.iter_children_with_name(hooks, 'hook'):
filename = hook.getAttribute('name')
state = hook.getAttribute('state')
data = getChildTextTrim(hook)
data = xmlutils.get_child_text_trim(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))
self.session.set_hook(hook_type, file_name=str(filename), source_name=None, data=str(data))
def parse_session_metadata(self, session_config):
metadata = getFirstChildByTagName(session_config, 'metadata')
metadata = xmlutils.get_first_child_by_tag_name(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))
self.session.metadata.add_item(str(name), str(value))
def parse_session_config(self):
session_config = \
getFirstChildByTagName(self.scenario, 'CORE:sessionconfig')
session_config = xmlutils.get_first_child_by_tag_name(self.scenario, 'CORE:sessionconfig')
if not session_config:
return
self.parse_session_origin(session_config)
@ -918,18 +858,16 @@ class CoreDocumentParser1(object):
# 'prouter': ['zebra', 'OSPFv2', 'OSPFv3', 'vtysh', 'IPForward'],
# 'xen': ['zebra', 'OSPFv2', 'OSPFv3', 'vtysh', 'IPForward'],
}
default_services = \
getFirstChildByTagName(self.scenario, 'CORE:defaultservices')
default_services = xmlutils.get_first_child_by_tag_name(self.scenario, 'CORE:defaultservices')
if not default_services:
return
for device in iterChildrenWithName(default_services, 'device'):
for device in xmlutils.iter_children_with_name(default_services, 'device'):
device_type = device.getAttribute('type')
if not device_type:
self.warn('parse_default_services: no type attribute ' \
'found for device')
logger.warn('parse_default_services: no type attribute found for device')
continue
services = []
for service in iterChildrenWithName(device, 'service'):
for service in xmlutils.iter_children_with_name(device, 'service'):
name = service.getAttribute('name')
if name:
services.append(str(name))
@ -937,6 +875,4 @@ class CoreDocumentParser1(object):
# 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))
logger.info('default services for node type \'%s\' set to: %s' % (t, s))

View file

@ -0,0 +1,36 @@
"""
Helpers for loading and saving XML files. savesessionxml(session, filename) is
the main public interface here.
"""
import os.path
from core.enumerations import NodeTypes
from core.misc import nodeutils
from core.xml.xmlparser import core_document_parser
from core.xml.xmlwriter import core_document_writer
def open_session_xml(session, filename, start=False, nodecls=None):
"""
Import a session from the EmulationScript XML format.
"""
# set default node class when one is not provided
if not nodecls:
nodecls = nodeutils.get_node_class(NodeTypes.DEFAULT)
options = {'start': start, 'nodecls': nodecls}
doc = core_document_parser(session, filename, options)
if start:
session.name = os.path.basename(filename)
session.filename = filename
session.instantiate()
def save_session_xml(session, filename, version):
"""
Export a session to the EmulationScript XML format.
"""
doc = core_document_writer(session, version)
doc.writexml(filename)

View file

@ -1,16 +1,14 @@
#
# 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
from core.misc import log
from core.netns import nodes
logger = log.get_logger(__name__)
def add_elements_from_list(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>
@ -18,14 +16,16 @@ def addelementsfromlist(dom, parent, iterable, name, attr_name):
<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
def add_text_elements_from_list(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",
@ -35,7 +35,7 @@ def addtextelementsfromlist(dom, parent, iterable, name, attrs):
<letter show="True">b</letter>
<letter show="True">c</letter>
</parent>
'''
"""
for item in iterable:
element = dom.createElement(name)
for k, v in attrs:
@ -44,8 +44,10 @@ def addtextelementsfromlist(dom, parent, iterable, name, attrs):
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
def add_text_elements_from_tuples(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.
@ -57,7 +59,7 @@ def addtextelementsfromtuples(dom, parent, iterable, attrs=()):
<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:
@ -66,10 +68,12 @@ def addtextelementsfromtuples(dom, parent, iterable, attrs=()):
txt = dom.createTextNode(value)
element.appendChild(txt)
def gettextelementstolist(parent):
''' XML helper to parse child text nodes from the given parent and return
def get_text_elements_to_list(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:
@ -84,10 +88,12 @@ def gettextelementstolist(parent):
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
def add_param_to_parent(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")
@ -96,10 +102,12 @@ def addparamtoparent(dom, parent, name, value):
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
def add_text_param_to_parent(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")
@ -109,14 +117,16 @@ def addtextparamtoparent(dom, parent, name, 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
def add_param_list_to_parent(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")
@ -129,18 +139,20 @@ def addparamlisttoparent(dom, parent, name, values):
p.appendChild(item)
return p
def getoneelement(dom, name):
def get_one_element(dom, name):
e = dom.getElementsByTagName(name)
if len(e) == 0:
return None
return e[0]
def iterDescendants(dom, max_depth = 0):
'''\
def iter_descendants(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
@ -160,71 +172,82 @@ def iterDescendants(dom, max_depth = 0):
current_depth_nodes = next_depth_nodes
next_depth_nodes = 0
def iterMatchingDescendants(dom, matchFunction, max_depth = 0):
'''\
def iter_matching_descendants(dom, match_function, 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):
"""
for d in iter_descendants(dom, max_depth):
if match_function(d):
yield d
def iterDescendantsWithName(dom, tagName, max_depth = 0):
'''\
def iter_descendants_with_name(dom, tag_name, 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)
"""
if isinstance(tag_name, basestring):
tag_name = (tag_name,)
def iterDescendantsWithAttribute(dom, tagName, attrName, attrValue,
max_depth = 0):
'''\
def match(d):
return d.tagName in tag_name
return iter_matching_descendants(dom, match, max_depth)
def iter_descendants_with_attribute(dom, tag_name, attr_name, attr_value, 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)
"""
if isinstance(tag_name, basestring):
tag_name = (tag_name,)
def iterChildren(dom, nodeType):
'''\
def match(d):
return d.tagName in tag_name and \
d.getAttribute(attr_name) == attr_value
return iter_matching_descendants(dom, match, max_depth)
def iter_children(dom, node_type):
"""
Iterate over all child elements of the given type.
'''
"""
for child in dom.childNodes:
if child.nodeType == nodeType:
if child.nodeType == node_type:
yield child
def gettextchild(dom):
'''\
def get_text_child(dom):
"""
Return the text node of the given element.
'''
for child in iterChildren(dom, Node.TEXT_NODE):
"""
for child in iter_children(dom, Node.TEXT_NODE):
return str(child.nodeValue)
return None
def getChildTextTrim(dom):
text = gettextchild(dom)
def get_child_text_trim(dom):
text = get_text_child(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
def get_params_set_attrs(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")
@ -234,70 +257,82 @@ def getparamssetattrs(dom, param_names, target):
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.*.
'''
def xml_type_to_node_class(session, type):
"""
Helper to convert from a type string to a class name in nodes.*.
"""
if hasattr(nodes, type):
# TODO: remove and use a mapping to known nodes
logger.error("using eval to retrieve node type: %s", 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 iter_children_with_name(dom, tag_name):
return iter_descendants_with_name(dom, tag_name, 1)
def getFirstChildByTagName(dom, tagName):
'''\
def iter_children_with_attribute(dom, tag_name, attr_name, attr_value):
return iter_descendants_with_attribute(dom, tag_name, attr_name, attr_value, 1)
def get_first_child_by_tag_name(dom, tag_name):
"""
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):
"""
for child in iter_children_with_name(dom, tag_name):
return child
return None
def getFirstChildTextByTagName(dom, tagName):
'''\
def get_first_child_text_by_tag_name(dom, tag_name):
"""
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)
"""
child = get_first_child_by_tag_name(dom, tag_name)
if child:
return gettextchild(child)
return get_text_child(child)
return None
def getFirstChildTextTrimByTagName(dom, tagName):
text = getFirstChildTextByTagName(dom, tagName)
def get_first_child_text_trim_by_tag_name(dom, tag_name):
text = get_first_child_text_by_tag_name(dom, tag_name)
if text:
text = text.strip()
return text
def getFirstChildWithAttribute(dom, tagName, attrName, attrValue):
'''\
def get_first_child_with_attribute(dom, tag_name, attr_name, attr_value):
"""
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):
iter_children_with_attribute(dom, tag_name, attr_name, attr_value):
return child
return None
def getFirstChildTextWithAttribute(dom, tagName, attrName, attrValue):
'''\
def get_first_child_text_with_attribute(dom, tag_name, attr_name, attr_value):
"""
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)
"""
child = get_first_child_with_attribute(dom, tag_name, attr_name, attr_value)
if child:
return gettextchild(child)
return get_text_child(child)
return None
def getFirstChildTextTrimWithAttribute(dom, tagName, attrName, attrValue):
text = getFirstChildTextWithAttribute(dom, tagName, attrName, attrValue)
def get_first_child_text_trim_with_attribute(dom, tag_name, attr_name, attr_value):
text = get_first_child_text_with_attribute(dom, tag_name, attr_name, attr_value)
if text:
text = text.strip()
return text

View file

@ -0,0 +1,12 @@
from core.xml.xmlwriter0 import CoreDocumentWriter0
from core.xml.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

View file

@ -1,26 +1,28 @@
#
# 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 *
import pwd
from core.coreobj import PyCoreNet
from core.coreobj import PyCoreNode
from core.enumerations import RegisterTlvs
from core.misc import log
from core.xml import xmlutils
logger = log.get_logger(__name__)
class CoreDocumentWriter0(Document):
''' Utility class for writing a CoreSession to XML. The init method builds
"""
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
"""
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")
@ -46,10 +48,9 @@ class CoreDocumentWriter0(Document):
self.addmetadata()
def writexml(self, filename):
self.session.info("saving session XML file %s" % filename)
logger.info("saving session XML file %s", filename)
f = open(filename, "w")
Document.writexml(self, writer=f, indent="", addindent=" ", newl="\n", \
encoding="UTF-8")
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
@ -57,17 +58,19 @@ class CoreDocumentWriter0(Document):
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):
"""
Add PyCoreNet objects as NetworkDefinition XML elements.
"""
with self.session._objects_lock:
for net in self.session.objects.itervalues():
if not isinstance(net, PyCoreNet):
continue
self.addnet(net)
def addnet(self, net):
''' Add one PyCoreNet object as a NetworkDefinition XML element.
'''
"""
Add one PyCoreNet object as a NetworkDefinition XML element.
"""
n = self.createElement("NetworkDefinition")
self.np.appendChild(n)
n.setAttribute("name", net.name)
@ -88,10 +91,11 @@ class CoreDocumentWriter0(Document):
self.addposition(net)
def addnetem(self, n, netif):
''' Similar to addmodels(); used for writing netem link effects
"""
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
@ -128,14 +132,15 @@ class CoreDocumentWriter0(Document):
n.appendChild(model)
def addmodels(self, n, configs):
''' Add models from a list of model-class, config values tuples.
'''
for (m, conf) in 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:
if m._type == RegisterTlvs.MOBILITY.value:
type = "mobility"
model.setAttribute("type", type)
for i, k in enumerate(m.getnames()):
@ -147,17 +152,19 @@ class CoreDocumentWriter0(Document):
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):
"""
Add PyCoreNode objects as node XML elements.
"""
with self.session._objects_lock:
for node in self.session.objects.itervalues():
if not isinstance(node, PyCoreNode):
continue
self.addnode(node)
def addnode(self, node):
''' Add a PyCoreNode object as node XML elements.
'''
"""
Add a PyCoreNode object as node XML elements.
"""
n = self.createElement("Node")
self.np.appendChild(n)
n.setAttribute("name", node.name)
@ -166,13 +173,14 @@ class CoreDocumentWriter0(Document):
n.setAttribute("type", node.type)
self.addinterfaces(n, node)
self.addposition(node)
addparamtoparent(self, n, "icon", node.icon)
addparamtoparent(self, n, "canvas", node.canvas)
xmlutils.add_param_to_parent(self, n, "icon", node.icon)
xmlutils.add_param_to_parent(self, n, "canvas", node.canvas)
self.addservices(node)
def addinterfaces(self, n, node):
''' Add PyCoreNetIfs to node XML elements.
'''
"""
Add PyCoreNetIfs to node XML elements.
"""
for ifc in node.netifs(sort=True):
i = self.createElement("interface")
n.appendChild(i)
@ -193,12 +201,12 @@ class CoreDocumentWriter0(Document):
if cfg:
self.addmodels(i, ((netmodel, cfg),))
def addnetinterfaces(self, n, net):
''' Similar to addinterfaces(), but only adds interface elements to the
"""
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
@ -212,8 +220,9 @@ class CoreDocumentWriter0(Document):
i.setAttribute("net", ifc.net.name)
def addposition(self, node):
''' Add object coordinates as location XML element.
'''
"""
Add object coordinates as location XML element.
"""
(x, y, z) = node.position.get()
if x is None or y is None:
return
@ -237,9 +246,10 @@ class CoreDocumentWriter0(Document):
pt.appendChild(coords)
def addorigin(self):
''' Add origin to Motion Plan using canvas reference point.
"""
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")
@ -265,8 +275,9 @@ class CoreDocumentWriter0(Document):
self.mp.appendChild(origin)
def adddefaultservices(self):
''' Add default services and node types to the ServicePlan.
'''
"""
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")
@ -278,8 +289,9 @@ class CoreDocumentWriter0(Document):
s.setAttribute("name", str(svc._name))
def addservices(self, node):
''' Add services and their customizations to the ServicePlan.
'''
"""
Add services and their customizations to the ServicePlan.
"""
if len(node.services) == 0:
return
defaults = self.session.services.getdefaultservices(node.type)
@ -300,7 +312,7 @@ class CoreDocumentWriter0(Document):
if not svc._custom:
continue
s.setAttribute("custom", str(svc._custom))
addelementsfromlist(self, s, svc._dirs, "Directory", "name")
xmlutils.add_elements_from_list(self, s, svc._dirs, "Directory", "name")
for fn in svc._configs:
if len(fn) == 0:
@ -317,16 +329,14 @@ class CoreDocumentWriter0(Document):
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"),))
xmlutils.add_text_elements_from_list(self, s, svc._startup, "Command", (("type", "start"),))
xmlutils.add_text_elements_from_list(self, s, svc._shutdown, "Command", (("type", "stop"),))
xmlutils.add_text_elements_from_list(self, s, svc._validate, "Command", (("type", "validate"),))
def addaddresses(self, i, netif):
''' Add MAC and IP addresses to interface XML elements.
'''
"""
Add MAC and IP addresses to interface XML elements.
"""
if netif.hwaddr:
h = self.createElement("address")
i.appendChild(h)
@ -341,11 +351,12 @@ class CoreDocumentWriter0(Document):
a.appendChild(atxt)
def addhooks(self):
''' Add hook script XML elements to the metadata tag.
'''
"""
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]:
for filename, data in self.session._hooks[state]:
hook = self.createElement("Hook")
hook.setAttribute("name", filename)
hook.setAttribute("state", str(state))
@ -356,14 +367,15 @@ class CoreDocumentWriter0(Document):
self.meta.appendChild(hooks)
def addmetadata(self):
''' Add CORE-specific session meta-data XML elements.
'''
"""
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)
xmlutils.add_text_param_to_parent(self, options, k, v)
# addparamtoparent(self, options, k, v)
if options.hasChildNodes():
self.meta.appendChild(options)
@ -372,6 +384,6 @@ class CoreDocumentWriter0(Document):
# meta
meta = self.createElement("MetaData")
self.meta.appendChild(meta)
for (k, v) in self.session.metadata.items():
addtextparamtoparent(self, meta, k, v)
for k, v in self.session.metadata.items():
xmlutils.add_text_param_to_parent(self, meta, k, v)
# addparamtoparent(self, meta, k, v)

File diff suppressed because it is too large Load diff

View file

@ -29,12 +29,12 @@ quagga_sbin_search = "/usr/local/sbin /usr/sbin /usr/lib/quagga"
#
#
# 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.
#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
#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
#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
#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 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 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
# for use in connecting with other servers in a distributed environments.
@ -62,4 +62,4 @@ emane_models = RfPipe, Ieee80211abg, CommEffect, Bypass
#emane_log_level = 2
emane_realtime = True
aux_request_handler = core.addons.api2handler.CoreApi2RequestHandler:12222
#aux_request_handler = core.addons.api2handler.CoreApi2RequestHandler:12222

View file

@ -8,63 +8,80 @@
#
import optparse, sys, os, datetime, time
import datetime
import optparse
import sys
import time
from core import pycore
from core.misc import ipaddr
from core.misc.utils import mutecall
from core.misc import ipaddress, nodeutils
from core.misc import nodemaps
from core.mobility import BasicRangeModel
from core.netns.nodes import WlanNode
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'
# e.g. >>> n[0].session.shutdown()
n = []
def test(options):
prefix = ipaddr.IPv4Prefix("10.83.0.0/16")
session = pycore.Session(persistent = True)
prefix = ipaddress.Ipv4Prefix("10.83.0.0/16")
session = Session(1, persistent=True)
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.options.enablesdt = True
session.options.sdturl = options.sdturl
wlanid = options.numnodes + 1
net = session.addobj(cls = pycore.nodes.WlanNode, name = "wlan%d" % wlanid,
objid = wlanid, verbose = True)
net = session.add_object(
cls=WlanNode,
name="wlan%d" % wlanid,
objid=wlanid
)
values = list(BasicRangeModel.getdefaultvalues())
# values[0] = 5000000 # 5000km range
net.setmodel(BasicRangeModel, values)
for i in xrange(1, options.numnodes + 1):
tmp = session.addobj(cls = pycore.nodes.LxcNode, name = "n%d" % i,
objid = i)
tmp.newnetif(net, ["%s/%s" % (prefix.addr(i), prefix.prefixlen)])
node = session.add_object(cls=LxcNode, name="n%d" % i, objid=i)
address = "%s/%s" % (prefix.addr(i), prefix.prefixlen)
print "setting node address: %s - %s" % (node.objid, address)
node.newnetif(net, [address])
# set increasing Z coordinates
tmp.setposition(10, 10, 100*i)
n.append(tmp)
node.setposition(10, 10, 100)
n.append(node)
# example setting node n2 to a high altitude
# n[1].setposition(10, 10, 2000000) # 2000km
# session.sdt.updatenode(n[1].objid, 0, 10, 10, 2000000)
n[0].term("bash")
# launches terminal for the first node
# n[0].term("bash")
# wait for rate seconds to allow ebtables commands to commit
time.sleep(EbtablesQueue.rate)
#session.shutdown()
raw_input("press enter to exit")
session.shutdown()
def main():
usagestr = "usage: %prog [-h] [options] [args]"
parser = optparse.OptionParser(usage=usagestr)
parser.set_defaults(numnodes = 2, enablesdt = False,
sdturl = "tcp://127.0.0.1:50000/")
parser.add_option("-n", "--numnodes", dest = "numnodes", type = int,
help = "number of nodes to test; default = %s" %
parser.defaults["numnodes"])
parser.add_option("-s", "--sdt", dest = "enablesdt", action = "store_true",
help = "enable SDT output")
parser.add_option("-u", "--sdturl", dest = "sdturl", type = "string",
help = "URL for SDT connection, default = %s" % \
parser.defaults["sdturl"])
parser.set_defaults(numnodes=2, enablesdt=False, sdturl="tcp://127.0.0.1:50000/")
parser.add_option(
"-n", "--numnodes", dest="numnodes", type=int,
help="number of nodes to test; default = %s" % parser.defaults["numnodes"]
)
parser.add_option("-s", "--sdt", dest="enablesdt", action="store_true", help="enable SDT output")
parser.add_option(
"-u", "--sdturl", dest="sdturl", type="string",
help="URL for SDT connection, default = %s" % parser.defaults["sdturl"]
)
def usage(msg=None, err=0):
sys.stdout.write("\n")
@ -86,8 +103,12 @@ def main():
test(options)
print >> sys.stderr, \
"elapsed time: %s" % (datetime.datetime.now() - start)
print >> sys.stderr, "elapsed time: %s" % (datetime.datetime.now() - start)
if __name__ == "__main__":
# configure nodes to use
node_map = nodemaps.CLASSIC_NODES
nodeutils.set_node_map(node_map)
main()

View file

@ -5,64 +5,79 @@
# 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
# 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.
# This script creates no nodes locally and therefore can be run as an
# 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 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
import select
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)
from core.session import Session
# node list (count from 1)
n = [None]
exec_num = 1
def cmd(node, exec_cmd):
'''
"""
:param node: The node the command should be issued too
:param exec_cmd: A string with the command to be run
:return: Returns the result of the command
'''
"""
global exec_num
# Set up the command api message
tlvdata = coreapi.CoreExecTlv.pack(coreapi.CORE_TLV_EXEC_NODE, node.objid)
tlvdata += coreapi.CoreExecTlv.pack(coreapi.CORE_TLV_EXEC_NUM, exec_num)
tlvdata += coreapi.CoreExecTlv.pack(coreapi.CORE_TLV_EXEC_CMD, exec_cmd)
msg = coreapi.CoreExecMessage.pack(coreapi.CORE_API_STR_FLAG | coreapi.CORE_API_TXT_FLAG, tlvdata)
tlvdata = CoreExecuteTlv.pack(ExecuteTlvs.NODE.value, node.objid)
tlvdata += CoreExecuteTlv.pack(ExecuteTlvs.NUMBER.value, exec_num)
tlvdata += CoreExecuteTlv.pack(ExecuteTlvs.COMMAND.value, exec_cmd)
msg = coreapi.CoreExecMessage.pack(MessageFlags.STRING.value | MessageFlags.TEXT.value, tlvdata)
node.session.broker.handlerawmsg(msg)
exec_num += 1
# Now wait for the response
(h, p, sock) = node.session.broker.servers['localhost']
h, p, sock = node.session.broker.servers["localhost"]
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
result = None
while True:
msghdr = sock.recv(coreapi.CoreMessage.header_len)
msgtype, msgflags, msglen = coreapi.CoreMessage.unpack_header(msghdr)
msgdata = sock.recv(msglen)
# 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)
return msg.gettlv(coreapi.CORE_TLV_EXEC_RESULT)
else:
return None
result = msg.get_tlv(ExecuteTlvs.RESULT.value)
break
return result
def main():
usagestr = "usage: %prog [-n] number of nodes [-d] daemon address"
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,
help="number of nodes")
@ -85,94 +100,95 @@ def main():
usage("daemon server IP address (-d) is a required argument")
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()
prefix = ipaddr.IPv4Prefix("10.83.0.0/16")
session = pycore.Session(persistent=True)
if 'server' in globals():
prefix = ipaddress.Ipv4Prefix("10.83.0.0/16")
session = Session(1, persistent=True)
if "server" in globals():
server.addsession(session)
# distributed setup - connect to daemon server
daemonport = options.daemon.split(':')
daemonport = options.daemon.split(":")
daemonip = daemonport[0]
# 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
daemon = 'localhost'
daemon = "localhost"
if len(daemonport) > 1:
port = int(daemonport[1])
else:
port = coreapi.CORE_API_PORT
port = CORE_API_PORT
print "connecting to daemon at %s:%d" % (daemon, port)
session.broker.addserver(daemon, daemonip, port)
# Set the local session id to match the port.
# 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)
# We do not want the recvloop running as we will deal ourselves
session.broker.dorecvloop = False
# Change to configuration state on both machines
session.setstate(coreapi.CORE_EVENT_CONFIGURATION_STATE)
tlvdata = coreapi.CoreEventTlv.pack(coreapi.CORE_TLV_EVENT_TYPE,
coreapi.CORE_EVENT_CONFIGURATION_STATE)
session.set_state(EventTypes.CONFIGURATION_STATE.value)
tlvdata = coreapi.CoreEventTlv.pack(EventTlvs.TYPE.value, EventTypes.CONFIGURATION_STATE.value)
session.broker.handlerawmsg(coreapi.CoreEventMessage.pack(0, tlvdata))
flags = coreapi.CORE_API_ADD_FLAG
switch = pycore.nodes.SwitchNode(session = session, name='switch', start=False)
flags = MessageFlags.ADD.value
switch = nodes.SwitchNode(session=session, name="switch", start=False)
switch.setposition(x=80, y=50)
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" % \
(options.numnodes, prefix)
print "creating %d remote nodes with addresses from %s" % (options.numnodes, prefix)
# create remote nodes via API
for i in xrange(1, numberOfNodes + 1):
tmp = pycore.nodes.CoreNode(session = session, objid = i,
name = "n%d" % i, start=False)
tmp.setposition(x=150*i,y=150)
tmp.server = daemon
session.broker.handlerawmsg(tmp.tonodemsg(flags=flags))
n.append(tmp)
for i in xrange(1, number_of_nodes + 1):
node = nodes.CoreNode(session=session, objid=i, name="n%d" % i, start=False)
node.setposition(x=150 * i, y=150)
node.server = daemon
node_data = node.data(flags)
node_message = dataconversion.convert_node(node_data)
session.broker.handlerawmsg(node_message)
n.append(node)
# create remote links via API
for i in xrange(1, numberOfNodes + 1):
tlvdata = coreapi.CoreLinkTlv.pack(coreapi.CORE_TLV_LINK_N1NUMBER,
switch.objid)
tlvdata += coreapi.CoreLinkTlv.pack(coreapi.CORE_TLV_LINK_N2NUMBER, i)
tlvdata += coreapi.CoreLinkTlv.pack(coreapi.CORE_TLV_LINK_TYPE,
coreapi.CORE_LINK_WIRED)
tlvdata += coreapi.CoreLinkTlv.pack(coreapi.CORE_TLV_LINK_IF2NUM, 0)
tlvdata += coreapi.CoreLinkTlv.pack(coreapi.CORE_TLV_LINK_IF2IP4,
prefix.addr(i))
tlvdata += coreapi.CoreLinkTlv.pack(coreapi.CORE_TLV_LINK_IF2IP4MASK,
prefix.prefixlen)
for i in xrange(1, number_of_nodes + 1):
tlvdata = coreapi.CoreLinkTlv.pack(LinkTlvs.N1_NUMBER.value, switch.objid)
tlvdata += coreapi.CoreLinkTlv.pack(LinkTlvs.N2_NUMBER.value, i)
tlvdata += coreapi.CoreLinkTlv.pack(LinkTlvs.TYPE.value, LinkTypes.WIRED.value)
tlvdata += coreapi.CoreLinkTlv.pack(LinkTlvs.INTERFACE2_NUMBER.value, 0)
tlvdata += coreapi.CoreLinkTlv.pack(LinkTlvs.INTERFACE2_IP4.value, prefix.addr(i))
tlvdata += coreapi.CoreLinkTlv.pack(LinkTlvs.INTERFACE2_IP4_MASK.value, prefix.prefixlen)
msg = coreapi.CoreLinkMessage.pack(flags, tlvdata)
session.broker.handlerawmsg(msg)
# We change the daemon to Instantiation state
# 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,
coreapi.CORE_EVENT_INSTANTIATION_STATE)
tlvdata = coreapi.CoreEventTlv.pack(EventTlvs.TYPE.value, EventTypes.INSTANTIATION_STATE.value)
msg = coreapi.CoreEventMessage.pack(0, tlvdata)
session.broker.handlerawmsg(msg)
# Get the ip or last node and ping it from the first
print 'Pinging from the first to the last node'
pingip = cmd(n[-1], 'ip -4 -o addr show dev eth0').split()[3].split('/')[0]
print cmd(n[1], 'ping -c 5 ' + pingip)
print "Pinging from the first to the last node"
pingip = cmd(n[-1], "ip -4 -o addr show dev eth0").split()[3].split("/")[0]
print cmd(n[1], "ping -c 5 " + pingip)
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__":
main()
# configure nodes to use
node_map = nodemaps.CLASSIC_NODES
nodeutils.set_node_map(node_map)
main()

View file

@ -9,22 +9,21 @@
# 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.misc import ipaddr
from core.constants import *
from core.api import coreapi
# declare classes for use with Broker
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)
from core import constants
from core.api import coreapi, dataconversion
from core.enumerations import CORE_API_PORT, EventTypes, EventTlvs, LinkTlvs, LinkTypes, MessageFlags
from core.misc import ipaddress, nodeutils, nodemaps
from core.netns import nodes
from core.session import Session
# node list (count from 1)
n = [None]
def main():
usagestr = "usage: %prog [-h] [options] [args]"
parser = optparse.OptionParser(usage=usagestr)
@ -55,8 +54,8 @@ def main():
start = datetime.datetime.now()
prefix = ipaddr.IPv4Prefix("10.83.0.0/16")
session = pycore.Session(persistent=True)
prefix = ipaddress.Ipv4Prefix("10.83.0.0/16")
session = Session(1, persistent=True)
if 'server' in globals():
server.addsession(session)
@ -66,59 +65,53 @@ def main():
if len(slaveport) > 1:
port = int(slaveport[1])
else:
port = coreapi.CORE_API_PORT
port = CORE_API_PORT
print "connecting to slave at %s:%d" % (slave, port)
session.broker.addserver(slave, slave, port)
session.broker.setupserver(slave)
session.setstate(coreapi.CORE_EVENT_CONFIGURATION_STATE)
tlvdata = coreapi.CoreEventTlv.pack(coreapi.CORE_TLV_EVENT_TYPE,
coreapi.CORE_EVENT_CONFIGURATION_STATE)
session.set_state(EventTypes.CONFIGURATION_STATE.value)
tlvdata = coreapi.CoreEventTlv.pack(EventTlvs.TYPE.value, EventTypes.CONFIGURATION_STATE.value)
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)
num_local = options.numnodes / 2
num_remote = options.numnodes / 2 + options.numnodes % 2
print "creating %d (%d local / %d remote) nodes with addresses from %s" % \
(options.numnodes, num_local, num_remote, prefix)
for i in xrange(1, num_local + 1):
tmp = session.addobj(cls = pycore.nodes.CoreNode, name = "n%d" % i,
objid=i)
tmp.newnetif(switch, ["%s/%s" % (prefix.addr(i), prefix.prefixlen)])
tmp.cmd([SYSCTL_BIN, "net.ipv4.icmp_echo_ignore_broadcasts=0"])
tmp.setposition(x=150*i,y=150)
n.append(tmp)
node = session.add_object(cls=nodes.CoreNode, name="n%d" % i, objid=i)
node.newnetif(switch, ["%s/%s" % (prefix.addr(i), prefix.prefixlen)])
node.cmd([constants.SYSCTL_BIN, "net.ipv4.icmp_echo_ignore_broadcasts=0"])
node.setposition(x=150 * i, y=150)
n.append(node)
flags = coreapi.CORE_API_ADD_FLAG
flags = MessageFlags.ADD.value
session.broker.handlerawmsg(switch.tonodemsg(flags=flags))
# create remote nodes via API
for i in xrange(num_local + 1, options.numnodes + 1):
tmp = pycore.nodes.CoreNode(session = session, objid = i,
name = "n%d" % i, start=False)
tmp.setposition(x=150*i,y=150)
tmp.server = slave
n.append(tmp)
session.broker.handlerawmsg(tmp.tonodemsg(flags=flags))
node = nodes.CoreNode(session=session, objid=i, name="n%d" % i, start=False)
node.setposition(x=150 * i, y=150)
node.server = slave
n.append(node)
node_data = node.data(flags)
node_message = dataconversion.convert_node(node_data)
session.broker.handlerawmsg(node_message)
# create remote links via API
for i in xrange(num_local + 1, options.numnodes + 1):
tlvdata = coreapi.CoreLinkTlv.pack(coreapi.CORE_TLV_LINK_N1NUMBER,
switch.objid)
tlvdata += coreapi.CoreLinkTlv.pack(coreapi.CORE_TLV_LINK_N2NUMBER, i)
tlvdata += coreapi.CoreLinkTlv.pack(coreapi.CORE_TLV_LINK_TYPE,
coreapi.CORE_LINK_WIRED)
tlvdata += coreapi.CoreLinkTlv.pack(coreapi.CORE_TLV_LINK_IF2NUM, 0)
tlvdata += coreapi.CoreLinkTlv.pack(coreapi.CORE_TLV_LINK_IF2IP4,
prefix.addr(i))
tlvdata += coreapi.CoreLinkTlv.pack(coreapi.CORE_TLV_LINK_IF2IP4MASK,
prefix.prefixlen)
tlvdata = coreapi.CoreLinkTlv.pack(LinkTlvs.N1_NUMBER.value, switch.objid)
tlvdata += coreapi.CoreLinkTlv.pack(LinkTlvs.N2_NUMBER.value, i)
tlvdata += coreapi.CoreLinkTlv.pack(LinkTlvs.TYPE.value, LinkTypes.WIRED.value)
tlvdata += coreapi.CoreLinkTlv.pack(LinkTlvs.INTERFACE2_NUMBER.value, 0)
tlvdata += coreapi.CoreLinkTlv.pack(LinkTlvs.INTERFACE2_IP4.value, prefix.addr(i))
tlvdata += coreapi.CoreLinkTlv.pack(LinkTlvs.INTERFACE2_IP4_MASK.value, prefix.prefixlen)
msg = coreapi.CoreLinkMessage.pack(flags, tlvdata)
session.broker.handlerawmsg(msg)
session.instantiate()
tlvdata = coreapi.CoreEventTlv.pack(coreapi.CORE_TLV_EVENT_TYPE,
coreapi.CORE_EVENT_INSTANTIATION_STATE)
tlvdata = coreapi.CoreEventTlv.pack(EventTlvs.TYPE.value, EventTypes.INSTANTIATION_STATE.value)
msg = coreapi.CoreEventMessage.pack(0, tlvdata)
session.broker.handlerawmsg(msg)
@ -132,6 +125,10 @@ def main():
print "To stop this session, use the 'core-cleanup' script on this 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()

View file

@ -6,16 +6,22 @@
# Example CORE Python script that attaches N nodes to an EMANE 802.11abg
# 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.misc import ipaddr
from core.constants import *
from core import constants
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)
from core.session import Session
n = [None]
def main():
usagestr = "usage: %prog [-h] [options] [args]"
parser = optparse.OptionParser(usage=usagestr)
@ -43,21 +49,21 @@ def main():
start = datetime.datetime.now()
# IP subnet
prefix = ipaddr.IPv4Prefix("10.83.0.0/16")
prefix = ipaddress.Ipv4Prefix("10.83.0.0/16")
# session with some EMANE initialization
cfg = {'verbose': 'false'}
session = pycore.Session(cfg = cfg, persistent = True)
session = Session(1, config=cfg, persistent=True)
session.master = True
session.location.setrefgeo(47.57917, -122.13232, 2.00000)
session.location.refscale = 150.0
session.cfg['emane_models'] = "RfPipe, Ieee80211abg, Bypass"
session.config['emane_models'] = "RfPipe, Ieee80211abg, Bypass"
session.emane.loadmodels()
if 'server' in globals():
server.addsession(session)
# EMANE WLAN
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)
names = EmaneIeee80211abgModel.getnames()
values = list(EmaneIeee80211abgModel.getdefaultvalues())
@ -69,18 +75,18 @@ def main():
except ValueError:
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|vtysh|IPForward"
print "creating %d nodes with addresses from %s" % \
(options.numnodes, prefix)
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)
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)
session.services.addservicestonode(tmp, "", services_str, verbose=False)
session.services.addservicestonode(tmp, "", services_str)
n.append(tmp)
# this starts EMANE, etc.
@ -92,6 +98,10 @@ def main():
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()

View file

@ -5,49 +5,48 @@
#
# author: Jeff Ahrenholz <jeffrey.m.ahrenholz@boeing.com>
#
'''
"""
howmanynodes.py - This is a CORE script that creates network namespace nodes
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
specified.
'''
"""
import optparse, sys, os, datetime, time, shutil
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.constants import *
import datetime
import optparse
import shutil
import sys
import time
from core import constants
from core.misc import ipaddress, nodeutils, nodemaps
from core.netns import nodes
from core.session import Session
GBD = 1024.0 * 1024.0
def linuxversion():
''' Return a string having the Linux kernel version.
'''
f = open('/proc/version', 'r')
""" Return a string having the Linux kernel version.
"""
f = open("/proc/version", "r")
v = f.readline().split()
version_str = ' '.join(v[:3])
version_str = " ".join(v[:3])
f.close()
return version_str
MEMKEYS = ('total', 'free', 'buff', 'cached', 'stotal', 'sfree')
MEMKEYS = ("total", "free", "buff", "cached", "stotal", "sfree")
def memfree():
''' Returns kilobytes memory [total, free, buff, cached, stotal, sfree].
""" Returns kilobytes memory [total, free, buff, cached, stotal, sfree].
useful stats are:
free memory = free + buff + cached
swap used = stotal - sfree
'''
f = open('/proc/meminfo', 'r')
"""
f = open("/proc/meminfo", "r")
lines = f.readlines()
f.close()
kbs = {}
@ -55,20 +54,21 @@ def memfree():
kbs[k] = 0
for l in lines:
if l[:9] == "MemTotal:":
kbs['total'] = int(l.split()[1])
kbs["total"] = int(l.split()[1])
elif l[:8] == "MemFree:":
kbs['free'] = int(l.split()[1])
kbs["free"] = int(l.split()[1])
elif l[:8] == "Buffers:":
kbs['buff'] = int(l.split()[1])
kbs["buff"] = int(l.split()[1])
elif l[:8] == "Cached:":
kbs['cache'] = int(l.split()[1])
kbs["cache"] = int(l.split()[1])
elif l[:10] == "SwapTotal:":
kbs['stotal'] = int(l.split()[1])
kbs["stotal"] = int(l.split()[1])
elif l[:9] == "SwapFree:":
kbs['sfree'] = int(l.split()[1])
kbs["sfree"] = int(l.split()[1])
break
return kbs
# node list (count from 1)
nodelist = [None]
switchlist = []
@ -95,9 +95,9 @@ def main():
help="log memory usage to this file (default = %s)" % \
parser.defaults["logfile"])
parser.add_option("-s", "--services", dest="services", type=str,
help = "pipe-delimited list of services added to each " \
"node (default = %s)\n(Example: 'zebra|OSPFv2|OSPFv3|" \
"vtysh|IPForward')" % parser.defaults["services"])
help="pipe-delimited list of services added to each "
"node (default = %s)\n(Example: zebra|OSPFv2|OSPFv3|"
"vtysh|IPForward)" % parser.defaults["services"])
def usage(msg=None, err=0):
sys.stdout.write("\n")
@ -109,16 +109,16 @@ def main():
(options, args) = parser.parse_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()
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 " - %s" % linuxversion()
mem = memfree()
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 wait time of %s" % options.waittime
print " - using %d nodes per bridge" % options.bridges
@ -132,11 +132,11 @@ def main():
lfp = open(options.logfile, "a")
lfp.write("# log from howmanynodes.py %s\n" % time.ctime())
lfp.write("# options = %s\n#\n" % options)
lfp.write("# numnodes,%s\n" % ','.join(MEMKEYS))
lfp.write("# numnodes,%s\n" % ",".join(MEMKEYS))
lfp.flush()
session = pycore.Session(persistent=True)
switch = session.addobj(cls = pycore.nodes.SwitchNode)
session = Session(1, persistent=True)
switch = session.add_object(cls=nodes.SwitchNode)
switchlist.append(switch)
print "Added bridge %s (%d)." % (switch.brname, len(switchlist))
@ -147,7 +147,7 @@ def main():
# optionally add a bridge (options.bridges nodes per bridge)
try:
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)
print "\nAdded bridge %s (%d) for node %d." % \
(switch.brname, len(switchlist), i)
@ -157,23 +157,22 @@ def main():
break
# create a node
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.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:
session.services.addservicestonode(n, "", options.services,
verbose=False)
session.services.addservicestonode(n, "", options.services)
n.boot()
nodelist.append(n)
if i % 25 == 0:
print "\n%s nodes created " % i,
mem = memfree()
free = mem['free'] + mem['buff'] + mem['cached']
swap = mem['stotal'] - mem['sfree']
free = mem["free"] + mem["buff"] + mem["cached"]
swap = mem["stotal"] - mem["sfree"]
print "(%.02f/%.02f GB free/swap)" % (free / GBD, swap / GBD),
if lfp:
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()
else:
sys.stdout.write(".")
@ -205,5 +204,10 @@ def main():
print "elapsed time: %s" % (datetime.datetime.now() - start)
print "Use the core-cleanup script to remove nodes and bridges."
if __name__ == "__main__":
# configure nodes to use
node_map = nodemaps.CLASSIC_NODES
nodeutils.set_node_map(node_map)
main()

View file

@ -14,15 +14,20 @@
# 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.misc import ipaddr
from core.constants import *
from core import constants
from core.misc import ipaddress, nodeutils, nodemaps
from core.netns import nodes
# node list (count from 1)
from core.session import Session
n = [None]
def main():
usagestr = "usage: %prog [-h] [options] [args]"
parser = optparse.OptionParser(usage=usagestr)
@ -52,29 +57,29 @@ def main():
start = datetime.datetime.now()
session = pycore.Session(persistent=True)
session = Session(1, persistent=True)
if 'server' in globals():
server.addsession(session)
print "creating %d nodes" % options.numnodes
left = None
prefix = None
for i in xrange(1, options.numnodes + 1):
tmp = session.addobj(cls = pycore.nodes.CoreNode, name = "n%d" % i,
objid=i)
tmp = session.add_object(cls=nodes.CoreNode, name="n%d" % i, objid=i)
if left:
tmp.newnetif(left, ["%s/%s" % (prefix.addr(2), prefix.prefixlen)])
prefix = ipaddr.IPv4Prefix("10.83.%d.0/24" % i) # limit: i < 255
right = session.addobj(cls = pycore.nodes.PtpNet)
# limit: i < 255
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.cmd([SYSCTL_BIN, "net.ipv4.icmp_echo_ignore_broadcasts=0"])
tmp.cmd([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.icmp_echo_ignore_broadcasts=0"])
tmp.cmd([constants.SYSCTL_BIN, "net.ipv4.conf.all.forwarding=1"])
tmp.cmd([constants.SYSCTL_BIN, "net.ipv4.conf.default.rp_filter=0"])
tmp.setposition(x=100 * i, y=150)
n.append(tmp)
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))
# set up static routing in the chain
@ -89,11 +94,14 @@ def main():
else:
continue
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)
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()

View file

@ -7,26 +7,22 @@
# that all neighbor states are either full or two-way, and check the routes
# 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
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.misc import ipaddress, nodeutils, nodemaps
from core.misc.utils import mutecall
from core.netns import nodes
# 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_path = "zebra"
@ -42,7 +38,8 @@ except OSError:
sys.stderr.write("ERROR: running zebra failed\n")
sys.exit(1)
class ManetNode(pycore.nodes.LxcNode):
class ManetNode(nodes.LxcNode):
""" An Lxc namespace node configured for Quagga OSPFv3 MANET MDR
"""
conftemp = Template("""\
@ -72,7 +69,7 @@ ip forwarding
routerid = ipaddr.split("/")[0]
self.ipaddr = ipaddr
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(QUAGGA_STATE_DIR)
@ -125,11 +122,13 @@ waitfile $STATEDIR/ospf6d.vty
vtysh -b
""" % (QUAGGA_STATE_DIR, quagga_path, quagga_path)
class Route(object):
""" Helper class for organzing routing table entries. """
def __init__(self, prefix=None, gw=None, metric=None):
try:
self.prefix = ipaddr.IPv4Prefix(prefix)
self.prefix = ipaddress.Ipv4Prefix(prefix)
except Exception, e:
raise ValueError, "Invalid prefix given to Route object: %s\n%s" % \
(prefix, e)
@ -156,6 +155,7 @@ class Route(object):
class ManetExperiment(object):
""" A class for building an MDR network and checking and logging its state.
"""
def __init__(self, options, start):
""" Initialize with options and start time. """
self.session = None
@ -240,13 +240,13 @@ class ManetExperiment(object):
the session, WLAN, and node list objects.
"""
# IP subnet
prefix = ipaddr.IPv4Prefix("10.14.0.0/16")
self.session = pycore.Session()
prefix = ipaddress.Ipv4Prefix("10.14.0.0/16")
self.session = Session(1)
# 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):
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])
self.nodes.append(tmp)
# connect nodes with probability linkprob
@ -353,9 +353,11 @@ class ManetExperiment(object):
self.comparelsdbs(lsdbs)
self.logdata(nbrs, mdrs, lsdbs, krs, zrs)
class Cmd:
""" Helper class for running a command on a node and parsing the result. """
args = ""
def __init__(self, node, verbose=False):
""" Initialize with a CoreNode (LxcNode) """
self.id = None
@ -384,7 +386,7 @@ class Cmd:
def open(self):
""" Exceute call to node.popen(). """
self.id, self.stdin, self.out, self.err = \
self.node.popen((self.args))
self.node.popen(self.args)
def parse(self):
""" This method is overloaded by child classes and should return some
@ -401,18 +403,22 @@ class Cmd:
if tmp:
self.warn("nonzero exit status:", tmp)
class VtyshCmd(Cmd):
""" Runs a vtysh command. """
def open(self):
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):
""" Check a node for OSPFv3 neighbors in the full/two-way states. """
args = "show ipv6 ospf6 neighbor"
def parse(self):
self.out.readline() # skip first line
# skip first line
self.out.readline()
nbrlist = []
for line in self.out:
field = line.split()
@ -428,6 +434,7 @@ class Ospf6NeighState(VtyshCmd):
self.info(" %s has %d neighbors" % (self.node.routerid, len(nbrlist)))
return nbrlist
class Ospf6MdrLevel(VtyshCmd):
""" Retrieve the OSPFv3 MDR level for a node. """
args = "show ipv6 ospf6 mdrlevel"
@ -443,6 +450,7 @@ class Ospf6MdrLevel(VtyshCmd):
self.info(" %s is %s" % (self.node.routerid, mdrlevel))
return mdrlevel
class Ospf6Database(VtyshCmd):
""" Retrieve the OSPFv3 LSDB summary for a node. """
args = "show ipv6 ospf6 database"
@ -458,6 +466,7 @@ class Ospf6Database(VtyshCmd):
db += " ".join(filtered) + "\n"
return db
class ZebraRoutes(VtyshCmd):
""" Return a list of Route objects for a node based on its zebra
routing table.
@ -466,7 +475,8 @@ class ZebraRoutes(VtyshCmd):
def parse(self):
for i in xrange(0, 3):
self.out.readline() # skip first three lines
# skip first three lines
self.out.readline()
r = []
prefix = None
for line in self.out:
@ -497,6 +507,7 @@ class ZebraRoutes(VtyshCmd):
self.info(" %s has %d zebra routes" % (self.node.routerid, len(r)))
return r
class KernelRoutes(Cmd):
""" Return a list of Route objects for a node based on its kernel
routing table.
@ -521,7 +532,8 @@ class KernelRoutes(Cmd):
if field[1] == "proto":
# nexthop entry is on the next line
continue
gw = field[2] # nexthop IP or interface
# nexthop IP or interface
gw = field[2]
r.append(Route(prefix, gw, metric))
prefix = None
@ -531,6 +543,7 @@ class KernelRoutes(Cmd):
self.info(" %s has %d kernel routes" % (self.node.routerid, len(r)))
return r
def main():
usagestr = "usage: %prog [-h] [options] [args]"
parser = optparse.OptionParser(usage=usagestr)
@ -587,5 +600,10 @@ def main():
return me
if __name__ == "__main__":
# configure nodes to use
node_map = nodemaps.CLASSIC_NODES
nodeutils.set_node_map(node_map)
me = main()

View file

@ -5,15 +5,20 @@
# connect n nodes to a virtual switch/hub
import sys, datetime, optparse
import datetime
import optparse
import sys
from core import pycore
from core.misc import ipaddr
from core.constants import *
from core import constants
from core.misc import ipaddress, nodeutils, nodemaps
from core.netns import nodes
# node list (count from 1)
from core.session import Session
n = [None]
def main():
usagestr = "usage: %prog [-h] [options] [args]"
parser = optparse.OptionParser(usage=usagestr)
@ -41,30 +46,35 @@ def main():
start = datetime.datetime.now()
# IP subnet
prefix = ipaddr.IPv4Prefix("10.83.0.0/16")
session = pycore.Session(persistent=True)
prefix = ipaddress.Ipv4Prefix("10.83.0.0/16")
session = Session(1, persistent=True)
if 'server' in globals():
server.addsession(session)
# 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)
print "creating %d nodes with addresses from %s" % \
(options.numnodes, prefix)
print "creating %d nodes with addresses from %s" % (options.numnodes, prefix)
for i in xrange(1, options.numnodes + 1):
tmp = session.addobj(cls = pycore.nodes.CoreNode, name = "n%d" % i,
objid=i)
tmp = session.add_object(cls=nodes.CoreNode, name="n%d" % i, objid=i)
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)
n.append(tmp)
session.node_count = str(options.numnodes + 1)
session.instantiate()
print "elapsed time: %s" % (datetime.datetime.now() - start)
# start a shell on node 1
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__":
main()
# configure nodes to use
node_map = nodemaps.CLASSIC_NODES
nodeutils.set_node_map(node_map)
main()

View file

@ -8,11 +8,14 @@
# and repeat for minnodes <= n <= maxnodes with a step size of
# nodestep
import optparse, sys, os, datetime
import datetime
import optparse
import sys
from core import pycore
from core.misc import ipaddr
from core.misc import ipaddress, nodeutils, nodemaps
from core.misc.utils import mutecall
from core.netns import nodes
from core.session import Session
try:
mutecall(["iperf", "-v"])
@ -20,23 +23,27 @@ except OSError:
sys.stderr.write("ERROR: running iperf failed\n")
sys.exit(1)
def test(numnodes, testsec):
# node list
n = []
# IP subnet
prefix = ipaddr.IPv4Prefix("10.83.0.0/16")
session = pycore.Session()
prefix = ipaddress.Ipv4Prefix("10.83.0.0/16")
session = Session(1)
# emulated network
net = session.addobj(cls = pycore.nodes.SwitchNode)
net = session.add_object(cls=nodes.SwitchNode)
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)])
n.append(tmp)
n[0].cmd(["iperf", "-s", "-D"])
n[-1].icmd(["iperf", "-t", str(int(testsec)), "-c", str(prefix.addr(1))])
n[0].cmd(["killall", "-9", "iperf"])
raw_input("press enter to exit")
session.shutdown()
def main():
usagestr = "usage: %prog [-h] [options] [args]"
parser = optparse.OptionParser(usage=usagestr)
@ -90,8 +97,12 @@ def main():
test(i, options.testsec)
print >> sys.stderr, ""
print >> sys.stderr, \
"elapsed time: %s" % (datetime.datetime.now() - start)
print >> sys.stderr, "elapsed time: %s" % (datetime.datetime.now() - start)
if __name__ == "__main__":
# configure nodes to use
node_map = nodemaps.CLASSIC_NODES
nodeutils.set_node_map(node_map)
main()

View file

@ -5,7 +5,7 @@
#
# author: Jeff Ahrenholz <jeffrey.m.ahrenholz@boeing.com>
#
'''
"""
wlanemanetests.py - This script tests the performance of the WLAN device in
CORE by measuring various metrics:
- delay experienced when pinging end-to-end
@ -28,29 +28,22 @@ Various underlying network types are tested:
Results are printed/logged in CSV format.
'''
"""
import os, sys, time, optparse, datetime, math
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.emane.emane import Emane
import datetime
import math
import optparse
import os
import sys
import time
from core import emane
from core.emane.bypass import EmaneBypassModel
from core.emane.nodes import EmaneNode
from core.emane.rfpipe import EmaneRfPipeModel
from core.misc import ipaddress, nodemaps, nodeutils
from core.netns import nodes
from core.session import Session
try:
import emaneeventservice
@ -60,12 +53,12 @@ except Exception, e:
from emanesh.events import EventService
from emanesh.events import PathlossEvent
except Exception, e2:
raise ImportError, "failed to import EMANE Python bindings:\n%s\n%s" % \
(e, e2)
raise ImportError("failed to import EMANE Python bindings:\n%s\n%s" % (e, e2))
# global Experiment object (for interaction with 'python -i')
# global Experiment object (for interaction with "python -i")
exp = None
# move these to core.misc.utils
def readstat():
f = open("/proc/stat", "r")
@ -73,6 +66,7 @@ def readstat():
f.close()
return lines
def numcpus():
lines = readstat()
n = 0
@ -82,6 +76,7 @@ def numcpus():
n += 1
return n
def getcputimes(line):
# return (user, nice, sys, idle) from a /proc/stat cpu line
# assume columns are:
@ -90,6 +85,7 @@ def getcputimes(line):
(user, nice, sys, idle) = map(lambda (x): int(x), items[1:5])
return [user, nice, sys, idle]
def calculatecpu(timesa, timesb):
for i in range(len(timesa)):
timesb[i] -= timesa[i]
@ -100,13 +96,15 @@ def calculatecpu(timesa, timesb):
# subtract % time spent in idle time
return 100 - ((100.0 * timesb[-1]) / total)
# end move these to core.misc.utils
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 = ""
def __init__(self, node, verbose=False):
''' Initialize with a CoreNode (LxcNode) '''
""" Initialize with a CoreNode (LxcNode) """
self.id = None
self.stdin = None
self.out = None
@ -114,17 +112,17 @@ class Cmd(object):
self.verbose = verbose
def info(self, msg):
''' Utility method for writing output to stdout.'''
""" Utility method for writing output to stdout."""
print msg
sys.stdout.flush()
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
sys.stderr.flush()
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()
status = self.id.wait()
r = self.parse()
@ -132,18 +130,17 @@ class Cmd(object):
return r
def open(self):
''' Exceute call to node.popen(). '''
self.id, self.stdin, self.out, self.err = \
self.node.popen((self.args))
""" Exceute call to node.popen(). """
self.id, self.stdin, self.out, self.err = self.node.popen(self.args)
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.
'''
"""
return None
def cleanup(self):
''' Close the Popen channels.'''
""" Close the Popen channels."""
self.stdin.close()
self.out.close()
self.err.close()
@ -153,69 +150,70 @@ class Cmd(object):
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 = ""
client_args = ""
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)
self.client_node = client_node
def run(self):
''' Run the server command, then the client command, then
kill the server '''
""" Run the server command, then the client command, then
kill the server """
self.open() # server
self.client_open() # client
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()
self.cleanup()
return r
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_node.popen((self.client_args))
self.client_node.popen(self.client_args)
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.
'''
"""
return None
def cleanup(self):
''' Close the Popen channels.'''
""" Close the Popen channels."""
self.stdin.close()
self.out.close()
self.err.close()
tmp = self.id.wait()
if 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):
''' Test latency using ping.
'''
""" Test latency using ping.
"""
def __init__(self, node, verbose=False, addr=None, count=50, interval=0.1, ):
Cmd.__init__(self, node, verbose)
self.addr = addr
self.count = count
self.interval = interval
self.args = ['ping', '-q', '-c', '%s' % count, '-i', '%s' % interval,
addr]
self.args = ["ping", "-q", "-c", "%s" % count, "-i", "%s" % interval, addr]
def run(self):
if self.verbose:
self.info("%s initial test ping (max 1 second)..." % self.node.name)
(status, result) = self.node.cmdresult(["ping", "-q", "-c", "1", "-w",
"1", self.addr])
(status, result) = self.node.cmdresult(["ping", "-q", "-c", "1", "-w", "1", self.addr])
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))
return (0.0, 0.0)
return 0.0, 0.0
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))
return Cmd.run(self)
@ -224,17 +222,19 @@ class PingCmd(Cmd):
avg_latency = 0
mdev = 0
try:
stats_str = lines[-1].split('=')[1]
stats = stats_str.split('/')
stats_str = lines[-1].split("=")[1]
stats = stats_str.split("/")
avg_latency = float(stats[1])
mdev = float(stats[3].split(' ')[0])
mdev = float(stats[3].split(" ")[0])
except Exception, e:
self.warn("ping parsing exception: %s" % e)
return (avg_latency, mdev)
return avg_latency, mdev
class IperfCmd(ClientServerCmd):
''' Test throughput using iperf.
'''
""" Test throughput using iperf.
"""
def __init__(self, node, client_node, verbose=False, addr=None, time=10):
# node is the server
ClientServerCmd.__init__(self, node, client_node, verbose)
@ -254,35 +254,37 @@ class IperfCmd(ClientServerCmd):
def parse(self):
lines = self.out.readlines()
try:
bps = int(lines[-1].split(',')[-1].strip('\n'))
bps = int(lines[-1].split(",")[-1].strip("\n"))
except Exception, e:
self.warn("iperf parsing exception: %s" % e)
bps = 0
return bps
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,
rate=512):
ClientServerCmd.__init__(self, node, client_node, verbose)
self.addr = addr
self.time = time
self.args = ['mgen', 'event', 'listen udp 5000', 'output',
'/var/log/mgen.log']
self.args = ["mgen", "event", "listen udp 5000", "output",
"/var/log/mgen.log"]
self.rate = rate
sendevent = "ON 1 UDP DST %s/5000 PERIODIC [%s]" % \
(addr, self.mgenrate(self.rate))
stopevent = "%s OFF 1" % time
self.client_args = ['mgen', 'event', sendevent, 'event', stopevent,
'output', '/var/log/mgen.log']
self.client_args = ["mgen", "event", sendevent, "event", stopevent,
"output", "/var/log/mgen.log"]
@staticmethod
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
1472 bytes for data.
'''
"""
bps = (kbps / 8) * 1000.0
maxdata = 1472
pps = math.ceil(bps / maxdata)
@ -296,26 +298,27 @@ class MgenCmd(ClientServerCmd):
return ClientServerCmd.run(self)
def cleanup(self):
''' Close the Popen channels.'''
""" Close the Popen channels."""
self.stdin.close()
self.out.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):
''' 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.
'''
logfile = os.path.join(self.node.nodedir, 'var.log/mgen.log')
f = open(logfile, 'r')
"""
logfile = os.path.join(self.node.nodedir, "var.log/mgen.log")
f = open(logfile, "r")
numlost = 0
lastseq = 0
for line in f.readlines():
fields = line.split()
if fields[1] != 'RECV':
if fields[1] != "RECV":
continue
try:
seq = int(fields[4].split('>')[1])
seq = int(fields[4].split(">")[1])
except:
self.info("Unexpected MGEN line:\n%s" % fields)
if seq > (lastseq + 1):
@ -333,10 +336,11 @@ class MgenCmd(ClientServerCmd):
class Experiment(object):
''' Experiment object to organize tests.
'''
""" Experiment object to organize tests.
"""
def __init__(self, opt, start):
''' Initialize with opt and start time. '''
""" Initialize with opt and start time. """
self.session = None
# node list
self.nodes = []
@ -352,19 +356,19 @@ class Experiment(object):
self.logbegin()
def info(self, msg):
''' Utility method for writing output to stdout. '''
""" Utility method for writing output to stdout. """
print msg
sys.stdout.flush()
self.log(msg)
def warn(self, msg):
''' Utility method for writing output to stderr. '''
""" Utility method for writing output to stderr. """
print >> sys.stderr, msg
sys.stderr.flush()
self.log(msg)
def logbegin(self):
''' Start logging. '''
""" Start logging. """
self.logfp = None
if not self.opt.logfile:
return
@ -375,7 +379,7 @@ class Experiment(object):
self.log("%s %s %s %s on %s" % (sysname, rel, ver, machine, nodename))
def logend(self):
''' End logging. '''
""" End logging. """
if not self.logfp:
return
end = datetime.datetime.now()
@ -386,14 +390,14 @@ class Experiment(object):
self.logfp = None
def log(self, msg):
''' Write to the log file, if any. '''
""" Write to the log file, if any. """
if not self.logfp:
return
print >> self.logfp, msg
def reset(self):
''' Prepare for another experiment run.
'''
""" Prepare for another experiment run.
"""
if self.session:
self.session.shutdown()
del self.session
@ -402,24 +406,21 @@ class Experiment(object):
self.net = None
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.
'''
"""
# IP subnet
prefix = ipaddr.IPv4Prefix("10.0.0.0/16")
self.session = pycore.Session()
prefix = ipaddress.Ipv4Prefix("10.0.0.0/16")
self.session = Session(1)
# emulated network
self.net = self.session.addobj(cls = pycore.nodes.WlanNode,
name = "wlan1")
self.net = self.session.add_object(cls=nodes.WlanNode, name="wlan1")
prev = None
for i in xrange(1, numnodes + 1):
addr = "%s/%s" % (prefix.addr(i), 32)
tmp = self.session.addobj(cls = pycore.nodes.CoreNode, objid = i,
name = "n%d" % i)
tmp = self.session.add_object(cls=nodes.CoreNode, objid=i, name="n%d" % i)
tmp.newnetif(self.net, [addr])
self.nodes.append(tmp)
self.session.services.addservicestonode(tmp, "router",
"IPForward", self.verbose)
self.session.services.addservicestonode(tmp, "router", "IPForward")
self.session.services.bootnodeservices(tmp)
self.staticroutes(i, prefix, numnodes)
@ -430,35 +431,33 @@ class Experiment(object):
def createemanesession(self, numnodes, verbose=False, cls=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.
'''
prefix = ipaddr.IPv4Prefix("10.0.0.0/16")
self.session = pycore.Session()
"""
prefix = ipaddress.Ipv4Prefix("10.0.0.0/16")
self.session = Session(2)
self.session.node_count = str(numnodes + 1)
self.session.master = True
self.session.location.setrefgeo(47.57917, -122.13232, 2.00000)
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.net = self.session.addobj(cls = pycore.nodes.EmaneNode,
objid = numnodes + 1, name = "wlan1")
self.net = self.session.add_object(cls=EmaneNode, objid=numnodes + 1, name="wlan1")
self.net.verbose = verbose
# self.session.emane.addobj(self.net)
for i in xrange(1, numnodes + 1):
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)
# tmp.setposition(i * 20, 50, None)
tmp.setposition(50, 50, None)
tmp.newnetif(self.net, [addr])
self.nodes.append(tmp)
self.session.services.addservicestonode(tmp, "router",
"IPForward", self.verbose)
self.session.services.addservicestonode(tmp, "router", "IPForward")
if values is None:
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.info("waiting %s sec (TAP bring-up)" % 2)
@ -469,19 +468,17 @@ class Experiment(object):
self.session.services.bootnodeservices(tmp)
self.staticroutes(i, prefix, numnodes)
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.
'''
"""
self.firstnode = self.nodes[0]
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):
''' 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"]
node = self.nodes[i - 1]
neigh_left = ""
@ -515,17 +512,17 @@ class Experiment(object):
self.warn("failed to add route: %s" % cmd)
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:
service = emaneeventservice.EventService()
e = emaneeventpathloss.EventPathloss(1)
old = True
else:
if self.session.emane.version == self.session.emane.EMANE091:
dev = 'lo'
dev = "lo"
else:
dev = self.session.obj('ctrlnet').brname
dev = self.session.obj("ctrlnet").brname
service = EventService(eventchannel=("224.1.2.8", 45703, dev),
otachannel=None)
old = False
@ -559,8 +556,8 @@ class Experiment(object):
service.publish(rxnem, e)
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:
self.warn("failed to set effects: no network node")
return
@ -568,10 +565,10 @@ class Experiment(object):
self.net.linkconfig(netif, bw=bw, delay=delay)
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
those runs.
'''
"""
duration = self.opt.duration
rate = self.opt.rate
if len(title) > 0:
@ -591,7 +588,8 @@ class Experiment(object):
throughput = self.iperftest(time=duration)
if self.numiperf > 1:
throughputs += throughput
time.sleep(1) # iperf is very CPU intensive
# iperf is very CPU intensive
time.sleep(1)
if self.numiperf > 1:
throughput = sum(throughputs) / len(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("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):
''' 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,
addr=self.lastaddr, count=count, interval=0.1).run()
(latency, mdev) = p
@ -622,18 +620,18 @@ class Experiment(object):
return p
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.
'''
"""
bps = IperfCmd(node=self.lastnode, client_node=self.firstnode,
verbose=False, addr=self.lastaddr, time=time).run()
self.info("throughput (bps): %s" % bps)
return bps
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.
'''
"""
if self.verbose:
self.info("%s initial test ping (max 1 second)..." % \
self.firstnode.name)
@ -642,7 +640,7 @@ class Experiment(object):
if status != 0:
self.warn("initial ping from %s to %s failed! result:\n%s" % \
(self.firstnode.name, self.lastaddr, result))
return (0.0, 0.0)
return 0.0, 0.0
lines = readstat()
cpustart = getcputimes(lines[0])
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))
return percent, loss
def main():
''' Main routine when running from command-line.
'''
""" Main routine when running from command-line.
"""
usagestr = "usage: %prog [-h] [options] [args]"
parser = optparse.OptionParser(usage=usagestr)
parser.set_defaults(numnodes=10, delay=3, duration=10, rate=512,
@ -700,7 +699,7 @@ def main():
usage("invalid rate: %s" % opt.rate)
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 = {}
starttime = datetime.datetime.now()
@ -708,51 +707,49 @@ def main():
exp.info("Starting wlanemanetests.py tests %s" % starttime.ctime())
# system sanity checks here
emanever, emaneverstr = Emane.detectversionfromcmd()
emanever, emaneverstr = emane.VERSION, emane.VERSIONSTR
if opt.verbose:
exp.info("Detected EMANE version %s" % (emaneverstr,))
# bridged
exp.info("setting up bridged tests 1/2 no link effects")
exp.info("creating topology: numnodes = %s" % \
(opt.numnodes, ))
exp.info("creating topology: numnodes = %s" % (opt.numnodes,))
exp.createbridgedsession(numnodes=opt.numnodes, verbose=opt.verbose)
exp.setnodes()
exp.info("waiting %s sec (node/route bring-up)" % 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))
# bridged with netem
exp.info("setting up bridged tests 2/2 with netem")
exp.setneteffects(bw=54000000, delay=0)
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")
# bridged with netem (1 Mbps,200ms)
exp.info("setting up bridged tests 3/2 with netem")
exp.setneteffects(bw=1000000, delay=20000)
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")
# bridged with netem (54 kbps,500ms)
exp.info("setting up bridged tests 3/2 with netem")
exp.setneteffects(bw=54000, delay=100000)
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.reset()
# EMANE bypass model
exp.info("setting up EMANE tests 1/2 with bypass model")
exp.createemanesession(numnodes=opt.numnodes, verbose=opt.verbose,
cls=EmaneBypassModel, values=None)
exp.createemanesession(numnodes=opt.numnodes, verbose=opt.verbose, cls=EmaneBypassModel, values=None)
exp.setnodes()
exp.info("waiting %s sec (node/route bring-up)" % 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.reset()
@ -763,18 +760,18 @@ def main():
exp.info("setting up EMANE tests 2/4 with RF-PIPE model")
rfpipevals = list(EmaneRfPipeModel.getdefaultvalues())
rfpnames = EmaneRfPipeModel.getnames()
rfpipevals[ rfpnames.index('datarate') ] = '4294967295' # max value
if emanever < Emane.EMANE091:
rfpipevals[ rfpnames.index('pathlossmode') ] = '2ray'
rfpipevals[ rfpnames.index('defaultconnectivitymode') ] = '1'
# max value
rfpipevals[rfpnames.index("datarate")] = "4294967295"
if emanever < emane.EMANE091:
rfpipevals[rfpnames.index("pathlossmode")] = "2ray"
rfpipevals[rfpnames.index("defaultconnectivitymode")] = "1"
else:
rfpipevals[ rfpnames.index('propagationmodel') ] = '2ray'
exp.createemanesession(numnodes=opt.numnodes, verbose=opt.verbose,
cls=EmaneRfPipeModel, values=rfpipevals)
rfpipevals[rfpnames.index("propagationmodel")] = "2ray"
exp.createemanesession(numnodes=opt.numnodes, verbose=opt.verbose, cls=EmaneRfPipeModel, values=rfpipevals)
exp.setnodes()
exp.info("waiting %s sec (node/route bring-up)" % 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.reset()
@ -782,20 +779,20 @@ def main():
exp.info("setting up EMANE tests 3/4 with RF-PIPE model 54M")
rfpipevals = list(EmaneRfPipeModel.getdefaultvalues())
rfpnames = EmaneRfPipeModel.getnames()
rfpipevals[ rfpnames.index('datarate') ] = '54000000'
rfpipevals[rfpnames.index("datarate")] = "54000000"
# TX delay != propagation delay
#rfpipevals[ rfpnames.index('delay') ] = '5000'
if emanever < Emane.EMANE091:
rfpipevals[ rfpnames.index('pathlossmode') ] = '2ray'
rfpipevals[ rfpnames.index('defaultconnectivitymode') ] = '1'
# rfpipevals[ rfpnames.index("delay") ] = "5000"
if emanever < emane.EMANE091:
rfpipevals[rfpnames.index("pathlossmode")] = "2ray"
rfpipevals[rfpnames.index("defaultconnectivitymode")] = "1"
else:
rfpipevals[ rfpnames.index('propagationmodel') ] = '2ray'
rfpipevals[rfpnames.index("propagationmodel")] = "2ray"
exp.createemanesession(numnodes=opt.numnodes, verbose=opt.verbose,
cls=EmaneRfPipeModel, values=rfpipevals)
exp.setnodes()
exp.info("waiting %s sec (node/route bring-up)" % 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.reset()
@ -803,12 +800,12 @@ def main():
exp.info("setting up EMANE tests 4/4 with RF-PIPE model pathloss")
rfpipevals = list(EmaneRfPipeModel.getdefaultvalues())
rfpnames = EmaneRfPipeModel.getnames()
rfpipevals[ rfpnames.index('datarate') ] = '54000'
if emanever < Emane.EMANE091:
rfpipevals[ rfpnames.index('pathlossmode') ] = 'pathloss'
rfpipevals[ rfpnames.index('defaultconnectivitymode') ] = '0'
rfpipevals[rfpnames.index("datarate")] = "54000"
if emanever < emane.EMANE091:
rfpipevals[rfpnames.index("pathlossmode")] = "pathloss"
rfpipevals[rfpnames.index("defaultconnectivitymode")] = "0"
else:
rfpipevals[ rfpnames.index('propagationmodel') ] = 'precomputed'
rfpipevals[rfpnames.index("propagationmodel")] = "precomputed"
exp.createemanesession(numnodes=opt.numnodes, verbose=opt.verbose,
cls=EmaneRfPipeModel, values=rfpipevals)
exp.setnodes()
@ -816,7 +813,7 @@ def main():
time.sleep(opt.delay)
exp.info("sending pathloss events to govern connectivity")
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.reset()
@ -824,10 +821,10 @@ def main():
exp.info("setting up EMANE tests 4/4 with RF-PIPE model pathloss")
rfpipevals = list(EmaneRfPipeModel.getdefaultvalues())
rfpnames = EmaneRfPipeModel.getnames()
rfpipevals[ rfpnames.index('datarate') ] = '512000'
rfpipevals[ rfpnames.index('delay') ] = '200'
rfpipevals[ rfpnames.index('pathlossmode') ] = 'pathloss'
rfpipevals[ rfpnames.index('defaultconnectivitymode') ] = '0'
rfpipevals[rfpnames.index("datarate")] = "512000"
rfpipevals[rfpnames.index("delay")] = "200"
rfpipevals[rfpnames.index("pathlossmode")] = "pathloss"
rfpipevals[rfpnames.index("defaultconnectivitymode")] = "0"
exp.createemanesession(numnodes=opt.numnodes, verbose=opt.verbose,
cls=EmaneRfPipeModel, values=rfpipevals)
exp.setnodes()
@ -835,7 +832,7 @@ def main():
time.sleep(opt.delay)
exp.info("sending pathloss events to govern connectivity")
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.reset()
@ -852,5 +849,10 @@ def main():
exp.logend()
return exp
if __name__ == "__main__":
exp = main()
# configure nodes to use
node_map = nodemaps.CLASSIC_NODES
nodeutils.set_node_map(node_map)
main()

View file

@ -8,11 +8,14 @@
# and repeat for minnodes <= n <= maxnodes with a step size of
# nodestep
import optparse, sys, os, datetime
import datetime
import optparse
import sys
from core import pycore
from core.misc import ipaddr
from core.misc import ipaddress, nodeutils, nodemaps
from core.misc.utils import mutecall
from core.netns import nodes
from core.session import Session
try:
mutecall(["iperf", "-v"])
@ -20,16 +23,17 @@ except OSError:
sys.stderr.write("ERROR: running iperf failed\n")
sys.exit(1)
def test(numnodes, testsec):
# node list
n = []
# IP subnet
prefix = ipaddr.IPv4Prefix("10.83.0.0/16")
session = pycore.Session()
prefix = ipaddress.Ipv4Prefix("10.83.0.0/16")
session = Session(1)
# emulated network
net = session.addobj(cls = pycore.nodes.WlanNode)
net = session.add_object(cls=nodes.WlanNode)
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)])
n.append(tmp)
net.link(n[0].netif(0), n[-1].netif(0))
@ -38,14 +42,14 @@ def test(numnodes, testsec):
n[0].cmd(["killall", "-9", "iperf"])
session.shutdown()
def main():
usagestr = "usage: %prog [-h] [options] [args]"
parser = optparse.OptionParser(usage=usagestr)
parser.set_defaults(minnodes=2)
parser.add_option("-m", "--minnodes", dest="minnodes", type=int,
help = "min number of nodes to test; default = %s" %
parser.defaults["minnodes"])
help="min number of nodes to test; default = %s" % parser.defaults["minnodes"])
parser.set_defaults(maxnodes=2)
parser.add_option("-n", "--maxnodes", dest="maxnodes", type=int,
@ -91,8 +95,12 @@ def main():
test(i, options.testsec)
print >> sys.stderr, ""
print >> sys.stderr, \
"elapsed time: %s" % (datetime.datetime.now() - start)
print >> sys.stderr, "elapsed time: %s" % (datetime.datetime.now() - start)
if __name__ == "__main__":
# configure nodes to use
node_map = nodemaps.CLASSIC_NODES
nodeutils.set_node_map(node_map)
main()

View file

@ -5,9 +5,12 @@
# List and stop CORE sessions from the command line.
#
import socket, optparse
from core.constants import *
import optparse
import socket
from core.api import coreapi
from core.enumerations import MessageFlags, SessionTlvs, CORE_API_PORT
def main():
parser = optparse.OptionParser(usage="usage: %prog [-l] <sessionid>")
@ -17,29 +20,30 @@ def main():
if options.list is True:
num = '0'
flags = coreapi.CORE_API_STR_FLAG
flags = MessageFlags.STRING.value
else:
num = args[0]
flags = coreapi.CORE_API_DEL_FLAG
tlvdata = coreapi.CoreSessionTlv.pack(coreapi.CORE_TLV_SESS_NUMBER, num)
msg = coreapi.CoreSessionMessage.pack(flags, tlvdata)
flags = MessageFlags.DELETE.value
tlvdata = coreapi.CoreSessionTlv.pack(SessionTlvs.NUMBER.value, num)
message = coreapi.CoreSessionMessage.pack(flags, tlvdata)
sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
sock.connect(('localhost', coreapi.CORE_API_PORT))
sock.send(msg)
sock.connect(('localhost', CORE_API_PORT))
sock.send(message)
# receive and print a session list
if options.list is True:
hdr = sock.recv(coreapi.CoreMessage.hdrsiz)
msgtype, msgflags, msglen = coreapi.CoreMessage.unpackhdr(hdr)
hdr = sock.recv(coreapi.CoreMessage.header_len)
msgtype, msgflags, msglen = coreapi.CoreMessage.unpack_header(hdr)
data = ""
if msglen:
data = sock.recv(msglen)
msg = coreapi.CoreMessage(msgflags, hdr, data)
sessions = msg.gettlv(coreapi.CORE_TLV_SESS_NUMBER)
message = coreapi.CoreMessage(msgflags, hdr, data)
sessions = message.get_tlv(coreapi.SessionTlvs.NUMBER.value)
print "sessions:", sessions
sock.close()
if __name__ == "__main__":
main()

6
daemon/requirements.txt Normal file
View file

@ -0,0 +1,6 @@
enum34==1.1.6
grpcio==1.0.0
grpcio-tools==1.0.0
pycco==0.5.1
sphinx==1.4.8
sphinx_rtd_theme==0.1.9

View file

@ -7,75 +7,61 @@
# authors: Tom Goff <thomas.goff@boeing.com>
# Jeff Ahrenholz <jeffrey.m.ahrenholz@boeing.com>
#
'''
"""
core-daemon: the CORE daemon is a server process that receives CORE API
messages and instantiates emulated nodes and networks within the kernel. Various
message handlers are defined and some support for sending messages.
'''
"""
import os, optparse, ConfigParser, gc, shlex, socket, shutil
import ConfigParser
import atexit
import signal
import importlib
import logging
import optparse
import os
import signal
import socket
import sys
import threading
import time
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.coreserver import *
from core.constants import *
from core import constants
from core import corehandlers
from core import coreserver
from core import enumerations
from core.api import coreapi
from core.misc.utils import daemonize, closeonexec
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
DEFAULT_MAXFD = 1024
# garbage collection debugging
# gc.set_debug(gc.DEBUG_STATS | gc.DEBUG_LEAK)
coreapi.add_node_class("CORE_NODE_DEF",
coreapi.CORE_NODE_DEF, pycore.nodes.CoreNode)
coreapi.add_node_class("CORE_NODE_PHYS",
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
""" Start a thread running a UDP server on the same host,port for
connectionless requests.
'''
mainserver.udpserver = CoreUdpServer(server_address,
CoreDatagramRequestHandler, mainserver)
"""
mainserver.udpserver = coreserver.CoreUdpServer(
server_address,
corehandlers.CoreDatagramRequestHandler,
mainserver)
mainserver.udpthread = threading.Thread(target=mainserver.udpserver.start)
mainserver.udpthread.daemon = True
mainserver.udpthread.start()
@ -86,17 +72,15 @@ def startudp(mainserver, server_address):
# Auxiliary server startup
#
def startaux(mainserver, aux_address, aux_handler):
''' Start a thread running an auxiliary TCP server on the given address.
""" Start a thread running an auxiliary TCP server on the given address.
This server will communicate with client requests using a handler
using the aux_handler class. The aux_handler can provide an alternative
API to CORE.
'''
handlermodname,dot,handlerclassname = aux_handler.rpartition('.')
"""
handlermodname, dot, handlerclassname = aux_handler.rpartition(".")
handlermod = importlib.import_module(handlermodname)
handlerclass = getattr(handlermod, handlerclassname)
mainserver.auxserver = CoreAuxServer(aux_address,
handlerclass,
mainserver)
mainserver.auxserver = coreserver.CoreAuxServer(aux_address, handlerclass, mainserver)
mainserver.auxthread = threading.Thread(target=mainserver.auxserver.start)
mainserver.auxthread.daemon = True
mainserver.auxthread.start()
@ -104,25 +88,23 @@ def startaux(mainserver, aux_address, aux_handler):
def banner():
''' 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()))
""" Output the program banner printed to the terminal or log file.
"""
sys.stdout.write("CORE daemon v.%s started %s\n" % (constants.COREDPY_VERSION, time.ctime()))
sys.stdout.flush()
def cored(cfg=None):
''' Start the CoreServer object and enter the server loop.
'''
host = cfg['listenaddr']
port = int(cfg['port'])
if host == '' or host is None:
""" Start the CoreServer object and enter the server loop.
"""
host = cfg["listenaddr"]
port = int(cfg["port"])
if host == "" or host is None:
host = "localhost"
try:
server = CoreServer((host, port), CoreRequestHandler, cfg)
server = coreserver.CoreServer((host, port), corehandlers.CoreRequestHandler, cfg)
except Exception, e:
sys.stderr.write("error starting main server on: %s:%s\n\t%s\n" % \
(host, port, e))
sys.stderr.write("error starting main server on: %s:%s\n\t%s\n" % (host, port, e))
sys.stderr.flush()
sys.exit(1)
closeonexec(server.fileno())
@ -132,10 +114,10 @@ def cored(cfg = None):
udpserver = startudp(server, (host, port))
closeonexec(udpserver.fileno())
auxreqhandler = cfg['aux_request_handler']
auxreqhandler = cfg["aux_request_handler"]
if auxreqhandler:
try:
handler, auxport = auxreqhandler.rsplit(':')
handler, auxport = auxreqhandler.rsplit(":")
auxserver = startaux(server, (host, int(auxport)), handler)
closeonexec(auxserver.fileno())
except Exception as e:
@ -143,17 +125,21 @@ def cored(cfg = None):
server.serve_forever()
def cleanup():
while CoreServer.servers:
server = CoreServer.servers.pop()
while coreserver.CoreServer.servers:
server = coreserver.CoreServer.servers.pop()
server.shutdown()
atexit.register(cleanup)
def sighandler(signum, stackframe):
print >> sys.stderr, "terminated by signal:", signum
sys.exit(signum)
signal.signal(signal.SIGHUP, sighandler)
signal.signal(signal.SIGINT, sighandler)
signal.signal(signal.SIGPIPE, sighandler)
@ -161,6 +147,7 @@ signal.signal(signal.SIGTERM, sighandler)
signal.signal(signal.SIGUSR1, sighandler)
signal.signal(signal.SIGUSR2, sighandler)
def logrotate(stdout, stderr, stdoutmode=0644, stderrmode=0644):
def reopen(fileno, filename, mode):
err = 0
@ -175,6 +162,7 @@ def logrotate(stdout, stderr, stdoutmode = 0644, stderrmode = 0644):
if fd >= 0:
os.close(fd)
return err
if stdout:
err = reopen(1, stdout, stdoutmode)
if stderr:
@ -186,27 +174,28 @@ def logrotate(stdout, stderr, stdoutmode = 0644, stderrmode = 0644):
else:
reopen(2, stderr, stderrmode)
def getMergedConfig(filename):
''' Return a configuration after merging config file and command-line
def get_merged_config(filename):
""" Return a configuration after merging config file and command-line
arguments.
'''
"""
# these are the defaults used in the config file
defaults = { 'port' : '%d' % coreapi.CORE_API_PORT,
'listenaddr' : 'localhost',
'pidfile' : '%s/run/core-daemon.pid' % CORE_STATE_DIR,
'logfile' : '%s/log/core-daemon.log' % CORE_STATE_DIR,
'xmlfilever' : '1.0',
'numthreads' : '1',
'verbose' : 'False',
'daemonize' : 'False',
'debug' : 'False',
'execfile' : None,
'aux_request_handler' : None,
defaults = {"port": "%d" % enumerations.CORE_API_PORT,
"listenaddr": "localhost",
"pidfile": "%s/run/core-daemon.pid" % constants.CORE_STATE_DIR,
"logfile": "%s/log/core-daemon.log" % constants.CORE_STATE_DIR,
"xmlfilever": "1.0",
"numthreads": "1",
"verbose": "False",
"daemonize": "False",
"debug": "False",
"execfile": None,
"aux_request_handler": None,
}
usagestr = "usage: %prog [-h] [options] [args]\n\n" + \
"CORE daemon v.%s instantiates Linux network namespace " \
"nodes." % COREDPY_VERSION
"nodes." % constants.COREDPY_VERSION
parser = optparse.OptionParser(usage=usagestr)
parser.add_option("-f", "--configfile", dest="configfile",
type="string",
@ -253,14 +242,14 @@ def getMergedConfig(filename):
# gracefully support legacy configs (cored.py/cored now core-daemon)
if cfg.has_section("cored.py"):
for name, val in cfg.items("cored.py"):
if name == 'pidfile' or name == 'logfile':
bn = os.path.basename(val).replace('coredpy', 'core-daemon')
if name == "pidfile" or name == "logfile":
bn = os.path.basename(val).replace("coredpy", "core-daemon")
val = os.path.join(os.path.dirname(val), bn)
cfg.set(section, name, val)
if cfg.has_section("cored"):
for name, val in cfg.items("cored"):
if name == 'pidfile' or name == 'logfile':
bn = os.path.basename(val).replace('cored', 'core-daemon')
if name == "pidfile" or name == "logfile":
bn = os.path.basename(val).replace("cored", "core-daemon")
val = os.path.join(os.path.dirname(val), bn)
cfg.set(section, name, val)
@ -272,41 +261,43 @@ def getMergedConfig(filename):
return dict(cfg.items(section)), args
def exec_file(cfg):
''' Send a Register Message to execute a new session based on XML or Python
""" Send a Register Message to execute a new session based on XML or Python
script file.
'''
filename = cfg['execfile']
sys.stdout.write("Telling daemon to execute file: '%s'...\n" % filename)
"""
filename = cfg["execfile"]
sys.stdout.write("Telling daemon to execute file: %s...\n" % filename)
sys.stdout.flush()
tlvdata = coreapi.CoreRegTlv.pack(coreapi.CORE_TLV_REG_EXECSRV, filename)
msg = coreapi.CoreRegMessage.pack(coreapi.CORE_API_ADD_FLAG, tlvdata)
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.connect(("localhost", int(cfg['port']))) # TODO: connect address option
sock.connect(("localhost", int(cfg["port"]))) # TODO: connect address option
sock.sendall(msg)
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.
"""
# 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:
sys.stderr.write("ignoring command line argument: %s\n" % a)
if cfg["daemonize"] == "True":
daemonize(rootdir=None, umask=0, close_fds=False,
stdin=os.devnull,
stdout = cfg['logfile'], stderr = cfg['logfile'],
pidfilename = cfg['pidfile'],
stdout=cfg["logfile"], stderr=cfg["logfile"],
pidfilename=cfg["pidfile"],
defaultmaxfd=DEFAULT_MAXFD)
signal.signal(signal.SIGUSR1, lambda signum, stackframe:
logrotate(stdout = cfg['logfile'],
stderr = cfg['logfile']))
logrotate(stdout=cfg["logfile"],
stderr=cfg["logfile"]))
banner()
if cfg['execfile']:
cfg['execfile'] = os.path.abspath(cfg['execfile'])
if cfg["execfile"]:
cfg["execfile"] = os.path.abspath(cfg["execfile"])
sys.exit(exec_file(cfg))
try:
cored(cfg)
@ -317,4 +308,23 @@ def 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()

View file

@ -6,29 +6,31 @@
#
# author: Jeff Ahrenholz <jeffrey.m.ahrenholz@boeing.com>
#
'''
"""
core-manage: Helper tool to add, remove, or check for services, models, and
node types in a CORE installation.
'''
"""
import os
import sys
import ast
import optparse
import os
import re
import sys
from core import pycore
from core import services
from core.constants import CORE_CONF_DIR
class FileUpdater(object):
''' Helper class for changing configuration files.
'''
"""
Helper class for changing configuration files.
"""
actions = ("add", "remove", "check")
targets = ("service", "model", "nodetype")
def __init__(self, action, target, data, options):
'''
'''
"""
"""
self.action = action
self.target = target
self.data = data
@ -37,13 +39,13 @@ class FileUpdater(object):
self.search, self.filename = self.get_filename(target)
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:
txt = "Updating"
if self.action == "check":
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":
r = self.update_file(fn=self.update_services)
@ -64,41 +66,40 @@ class FileUpdater(object):
return r
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", ]
Returns True or False when "check" is the action, a modified line
otherwise.
'''
line = line.strip('\n')
key, valstr = line.split('= ')
"""
line = line.strip("\n")
key, valstr = line.split("= ")
vals = ast.literal_eval(valstr)
r = self.update_keyvals(key, vals)
if self.action == "check":
return r
valstr = '%s' % r
return '= '.join([key, valstr]) + '\n'
valstr = "%s" % r
return "= ".join([key, valstr]) + "\n"
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
Returns True or False when "check" is the action, a modified line
otherwise.
'''
line = line.strip('\n')
key, valstr = line.split('= ')
vals = valstr.split(', ')
"""
line = line.strip("\n")
key, valstr = line.split("= ")
vals = valstr.split(", ")
r = self.update_keyvals(key, vals)
if self.action == "check":
return r
valstr = ', '.join(r)
return '= '.join([key, valstr]) + '\n'
valstr = ", ".join(r)
return "= ".join([key, valstr]) + "\n"
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
otherwise.
'''
"""
if self.action == "check":
if self.data in vals:
return True
@ -115,11 +116,10 @@ class FileUpdater(object):
return vals
def get_filename(self, target):
''' Return search string and filename based on target.
'''
""" Return search string and filename based on target.
"""
if target == "service":
pypath = os.path.dirname(pycore.__file__)
filename = os.path.join(pypath, "services", "__init__.py")
filename = os.path.abspath(services.__file__)
search = "__all__ ="
elif target == "model":
filename = os.path.join(CORE_CONF_DIR, "core.conf")
@ -132,15 +132,15 @@ class FileUpdater(object):
else:
raise ValueError, "unknown target"
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
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.
Returns True if the file has changed (or action is "check" and the
search string is found), False otherwise.
'''
"""
changed = False
output = "" # this accumulates output, assumes input is small
with open(self.filename, "r") as f:
@ -161,11 +161,11 @@ class FileUpdater(object):
return changed
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
properly set for new entries. Uses self.{action, filename, search,
data} variables as input and returns the same value as update_file().
'''
"""
changed = False
output = "" # this accumulates output, assumes input is small
with open(self.filename, "r") as f:
@ -181,9 +181,10 @@ class FileUpdater(object):
continue
else:
output += line
if self.action == "add":
index = int(re.match('^\d+', line).group(0))
output += str(index + 1) + ' ' + self.data + '\n'
index = int(re.match("^\d+", line).group(0))
output += str(index + 1) + " " + self.data + "\n"
changed = True
if changed:
with open(self.filename, "w") as f:
@ -200,11 +201,11 @@ def main():
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 += "\nArguments:\n <action> should be one of: %s" % \
', '.join(FileUpdater.actions)
", ".join(FileUpdater.actions)
usagestr += "\n <target> should be one of: %s" % \
', '.join(FileUpdater.targets)
", ".join(FileUpdater.targets)
usagestr += "\n <string> is the text to %s" % \
', '.join(FileUpdater.actions)
", ".join(FileUpdater.actions)
parser = optparse.OptionParser(usage=usagestr)
parser.set_defaults(userpath=None, verbose=False, )
@ -228,11 +229,11 @@ def main():
action = args[0]
if action not in FileUpdater.actions:
usage("invalid action '%s'" % action, 1)
usage("invalid action %s" % action, 1)
target = args[1]
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:
usage("user path option required for this target (%s)" % target)
@ -249,5 +250,6 @@ def main():
sys.exit(1)
sys.exit(0)
if __name__ == "__main__":
main()

View file

@ -1,8 +1,12 @@
# Copyright (c)2010-2012 the Boeing Company.
# 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
setup(name="core-python",
@ -11,18 +15,21 @@ setup(name = "core-python",
"core",
"core.addons",
"core.api",
"core.bsd",
"core.emane",
"core.misc",
"core.bsd",
"core.netns",
"core.phys",
"core.xen",
"core.services",
"core.xen",
"core.xml",
],
install_requires=[
"enum34"
],
description="Python components of CORE",
url="http://www.nrl.navy.mil/itd/ncs/products/core",
author="Boeing Research & Technology",
author_email="core-dev@pf.itd.nrl.navy.mil",
license="BSD",
long_description="Python scripts and modules for building virtual " \
"emulated networks.")
long_description="Python scripts and modules for building virtual emulated networks.")

View file

@ -1,12 +1,20 @@
# Copyright (c)2010-2012 the Boeing Company.
# See the LICENSE file included in this distribution.
import os, glob
from distutils.core import setup, Extension
from setuptools import setup, Extension
netns = Extension("netns", sources = ["netnsmodule.c", "netns.c"])
vcmd = Extension("vcmd",
sources = ["vcmdmodule.c",
netns = Extension(
"netns",
sources=[
"netnsmodule.c",
"netns.c"
]
)
vcmd = Extension(
"vcmd",
sources=[
"vcmdmodule.c",
"vnode_client.c",
"vnode_chnl.c",
"vnode_io.c",
@ -14,16 +22,22 @@ vcmd = Extension("vcmd",
"vnode_cmd.c",
],
library_dirs=["build/lib"],
libraries = ["ev"])
libraries=["ev"]
)
setup(name = "core-python-netns",
setup(
name="core-python-netns",
version="1.0",
description = "Extension modules to support virtual nodes using " \
description="Extension modules to support virtual nodes using "
"Linux network namespaces",
ext_modules = [netns, vcmd],
ext_modules=[
netns,
vcmd
],
url="http://www.nrl.navy.mil/itd/ncs/products/core",
author="Boeing Research & Technology",
author_email="core-dev@pf.itd.nrl.navy.mil",
license="BSD",
long_description="Extension modules and utilities to support virtual " \
"nodes using Linux network namespaces")
long_description="Extension modules and utilities to support virtual "
"nodes using Linux network namespaces"
)

View file

@ -1,26 +1,26 @@
#!/usr/bin/env python
import sys
import os.path
import site
import sys
def main():
'''\
"""
Check if the given prefix is included in sys.path for the given
python version; if not find an alternate valid prefix. Print the
result to standard out.
'''
"""
if len(sys.argv) != 3:
msg = 'usage: %s <prefix> <python version>\n' % \
os.path.basename(sys.argv[0])
msg = "usage: %s <prefix> <python version>\n" % os.path.basename(sys.argv[0])
sys.stderr.write(msg)
return 1
python_prefix = sys.argv[1]
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)
if path[-1] != '/':
path = path + '/'
if path[-1] != "/":
path += "/"
prefix = None
for p in sys.path:
if p.startswith(path):
@ -28,8 +28,9 @@ def main():
break
if not prefix:
prefix = site.PREFIXES[-1]
sys.stdout.write('%s\n' % prefix)
sys.stdout.write("%s\n" % prefix)
return 0
if __name__ == '__main__':
if __name__ == "__main__":
sys.exit(main())