initial import (Boeing r1752, NRL r878)
This commit is contained in:
commit
f8f46d28be
394 changed files with 99738 additions and 0 deletions
0
daemon/core/bsd/__init__.py
Normal file
0
daemon/core/bsd/__init__.py
Normal file
70
daemon/core/bsd/netgraph.py
Normal file
70
daemon/core/bsd/netgraph.py
Normal file
|
@ -0,0 +1,70 @@
|
|||
#
|
||||
# CORE
|
||||
# Copyright (c)2010-2012 the Boeing Company.
|
||||
# See the LICENSE file included in this distribution.
|
||||
#
|
||||
# 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])
|
||||
|
||||
def createngnode(type, hookstr, name=None):
|
||||
''' 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", "-"]
|
||||
cmdid = subprocess.Popen(cmd, stdin = subprocess.PIPE,
|
||||
stdout = subprocess.PIPE,
|
||||
stderr = subprocess.PIPE)
|
||||
cmdid.stdin.write(ngcmd)
|
||||
cmdid.stdin.close()
|
||||
result = cmdid.stdout.read()
|
||||
result += cmdid.stderr.read()
|
||||
cmdid.stdout.close()
|
||||
cmdid.stderr.close()
|
||||
status = cmdid.wait()
|
||||
if status > 0:
|
||||
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)
|
||||
|
||||
def destroyngnode(name):
|
||||
''' Shutdown a Netgraph node having the given name.
|
||||
'''
|
||||
check_call([NGCTL_BIN, "shutdown", "%s:" % name])
|
||||
|
||||
def connectngnodes(name1, name2, hook1, hook2):
|
||||
''' 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])
|
||||
|
||||
def ngmessage(name, msg):
|
||||
''' Send a Netgraph message to the node named name.
|
||||
'''
|
||||
cmd = [NGCTL_BIN, "msg", "%s:" % name] + msg
|
||||
check_call(cmd)
|
||||
|
||||
def ngloadkernelmodule(name):
|
||||
''' 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])
|
197
daemon/core/bsd/nodes.py
Normal file
197
daemon/core/bsd/nodes.py
Normal file
|
@ -0,0 +1,197 @@
|
|||
#
|
||||
# CORE
|
||||
# Copyright (c)2010-2013 the Boeing Company.
|
||||
# See the LICENSE file included in this distribution.
|
||||
#
|
||||
# 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 *
|
||||
from core.api import coreapi
|
||||
from core.bsd.netgraph import ngloadkernelmodule
|
||||
|
||||
checkexec([IFCONFIG_BIN])
|
||||
|
||||
class CoreNode(JailNode):
|
||||
apitype = coreapi.CORE_NODE_DEF
|
||||
|
||||
class PtpNet(NetgraphPipeNet):
|
||||
def tonodemsg(self, flags):
|
||||
''' 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
|
||||
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')
|
||||
if delay is not None:
|
||||
tlvdata += coreapi.CoreLinkTlv.pack(coreapi.CORE_TLV_LINK_DELAY,
|
||||
delay)
|
||||
if bw is not None:
|
||||
tlvdata += coreapi.CoreLinkTlv.pack(coreapi.CORE_TLV_LINK_BW, bw)
|
||||
if loss is not None:
|
||||
tlvdata += coreapi.CoreLinkTlv.pack(coreapi.CORE_TLV_LINK_PER,
|
||||
str(loss))
|
||||
if duplicate is not None:
|
||||
tlvdata += coreapi.CoreLinkTlv.pack(coreapi.CORE_TLV_LINK_DUP,
|
||||
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(coreapi.CORE_TLV_LINK_IF1NUM, \
|
||||
if1.node.getifindex(if1))
|
||||
for addr in if1.addrlist:
|
||||
(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
|
||||
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)
|
||||
|
||||
tlvdata += coreapi.CoreLinkTlv.pack(coreapi.CORE_TLV_LINK_IF2NUM, \
|
||||
if2.node.getifindex(if2))
|
||||
for addr in if2.addrlist:
|
||||
(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
|
||||
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)
|
||||
return [msg,]
|
||||
|
||||
class SwitchNode(NetgraphNet):
|
||||
ngtype = "bridge"
|
||||
nghooks = "link0 link0\nmsg .link0 setpersistent"
|
||||
apitype = coreapi.CORE_NODE_SWITCH
|
||||
policy = "ACCEPT"
|
||||
|
||||
class HubNode(NetgraphNet):
|
||||
ngtype = "hub"
|
||||
nghooks = "link0 link0\nmsg .link0 setpersistent"
|
||||
apitype = coreapi.CORE_NODE_HUB
|
||||
policy = "ACCEPT"
|
||||
|
||||
class WlanNode(NetgraphNet):
|
||||
ngtype = "wlan"
|
||||
nghooks = "anchor anchor"
|
||||
apitype = coreapi.CORE_NODE_WLAN
|
||||
linktype = coreapi.CORE_LINK_WIRELESS
|
||||
policy = "DROP"
|
||||
|
||||
def __init__(self, session, objid = None, name = None, verbose = False,
|
||||
start = True, policy = None):
|
||||
NetgraphNet.__init__(self, session, objid, name, verbose, start, policy)
|
||||
# wireless model such as basic range
|
||||
self.model = None
|
||||
# mobility model such as scripted
|
||||
self.mobility = None
|
||||
|
||||
def attach(self, netif):
|
||||
NetgraphNet.attach(self, netif)
|
||||
if self.model:
|
||||
netif.poshook = self.model._positioncallback
|
||||
if netif.node is None:
|
||||
return
|
||||
(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):
|
||||
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:
|
||||
for netif in self.netifs():
|
||||
netif.poshook = self.model._positioncallback
|
||||
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)
|
||||
|
||||
|
||||
class RJ45Node(NetgraphPipeNet):
|
||||
apitype = coreapi.CORE_NODE_RJ45
|
||||
policy = "ACCEPT"
|
||||
|
||||
def __init__(self, session, objid, name, verbose, start = True):
|
||||
if start:
|
||||
ngloadkernelmodule("ng_ether")
|
||||
NetgraphPipeNet.__init__(self, session, objid, name, verbose, start)
|
||||
if start:
|
||||
self.setpromisc(True)
|
||||
|
||||
def shutdown(self):
|
||||
self.setpromisc(False)
|
||||
NetgraphPipeNet.shutdown(self)
|
||||
|
||||
def setpromisc(self, promisc):
|
||||
p = "promisc"
|
||||
if not promisc:
|
||||
p = "-" + p
|
||||
check_call([IFCONFIG_BIN, self.name, "up", p])
|
||||
|
||||
def attach(self, netif):
|
||||
if len(self._netif) > 0:
|
||||
raise ValueError, \
|
||||
"RJ45 networks support at most 1 network interface"
|
||||
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
|
||||
policy = "ACCEPT"
|
||||
|
216
daemon/core/bsd/vnet.py
Normal file
216
daemon/core/bsd/vnet.py
Normal file
|
@ -0,0 +1,216 @@
|
|||
#
|
||||
# CORE
|
||||
# Copyright (c)2010-2012 the Boeing Company.
|
||||
# See the LICENSE file included in this distribution.
|
||||
#
|
||||
# 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.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
|
||||
nghooks = ()
|
||||
|
||||
def __init__(self, session, objid = None, name = None, verbose = False,
|
||||
start = True, policy = None):
|
||||
PyCoreNet.__init__(self, session, objid, name)
|
||||
if name is None:
|
||||
name = str(self.objid)
|
||||
if policy is not None:
|
||||
self.policy = policy
|
||||
self.name = name
|
||||
self.ngname = "n_%s_%s" % (str(self.objid), self.session.sessionid)
|
||||
self.ngid = None
|
||||
self.verbose = verbose
|
||||
self._netif = {}
|
||||
self._linked = {}
|
||||
self.up = False
|
||||
if start:
|
||||
self.startup()
|
||||
|
||||
def startup(self):
|
||||
tmp, self.ngid = createngnode(type=self.ngtype, hookstr=self.nghooks,
|
||||
name=self.ngname)
|
||||
self.up = True
|
||||
|
||||
def shutdown(self):
|
||||
if not self.up:
|
||||
return
|
||||
self.up = False
|
||||
while self._netif:
|
||||
k, netif = self._netif.popitem()
|
||||
if netif.pipe:
|
||||
pipe = netif.pipe
|
||||
netif.pipe = None
|
||||
pipe.shutdown()
|
||||
else:
|
||||
netif.shutdown()
|
||||
self._netif.clear()
|
||||
self._linked.clear()
|
||||
del self.session
|
||||
destroyngnode(self.ngname)
|
||||
|
||||
def attach(self, netif):
|
||||
''' 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.attach(netif)
|
||||
hook = "link%d" % len(self._netif)
|
||||
pipe.attachnet(self, hook)
|
||||
PyCoreNet.attach(self, netif)
|
||||
|
||||
def detach(self, netif):
|
||||
if self.up:
|
||||
pass
|
||||
PyCoreNet.detach(self, netif)
|
||||
|
||||
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
|
||||
if self._netif[netif2] != netif2:
|
||||
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):
|
||||
if not self.linked(netif1, netif2):
|
||||
return
|
||||
msg = ["unlink", "{", "node1=0x%s" % netif1.pipe.ngid]
|
||||
msg += ["node2=0x%s" % netif2.pipe.ngid, "}"]
|
||||
ngmessage(self.ngname, msg)
|
||||
self._linked[netif1][netif2] = False
|
||||
|
||||
def link(self, netif1, netif2):
|
||||
if self.linked(netif1, netif2):
|
||||
return
|
||||
msg = ["link", "{", "node1=0x%s" % netif1.pipe.ngid]
|
||||
msg += ["node2=0x%s" % netif2.pipe.ngid, "}"]
|
||||
ngmessage(self.ngname, msg)
|
||||
self._linked[netif1][netif2] = True
|
||||
|
||||
def linknet(self, net):
|
||||
''' 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.
|
||||
'''
|
||||
if not netif.pipe:
|
||||
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)
|
||||
|
||||
class NetgraphPipeNet(NetgraphNet):
|
||||
ngtype = "pipe"
|
||||
nghooks = "upper lower"
|
||||
|
||||
def __init__(self, session, objid = None, name = None, verbose = False,
|
||||
start = True, policy = None):
|
||||
NetgraphNet.__init__(self, session, objid, name, verbose, start, policy)
|
||||
if start:
|
||||
# account for Ethernet header
|
||||
ngmessage(self.ngname, ["setcfg", "{", "header_offset=14", "}"])
|
||||
|
||||
def attach(self, netif):
|
||||
''' 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"
|
||||
if self.up:
|
||||
hook = self.gethook()
|
||||
connectngnodes(self.ngname, netif.localname, hook, netif.hook)
|
||||
if netif.pipe:
|
||||
raise ValueError, \
|
||||
"Interface %s already attached to pipe %s" % \
|
||||
(netif.name, netif.pipe.name)
|
||||
netif.pipe = self
|
||||
self._netif[netif] = netif
|
||||
self._linked[netif] = {}
|
||||
|
||||
def attachnet(self, net, hook):
|
||||
''' 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
|
||||
(e.g. "lower") based on the number of connections.
|
||||
'''
|
||||
hooks = self.nghooks.split()
|
||||
if len(self._netif) == 0:
|
||||
return hooks[0]
|
||||
else:
|
||||
return hooks[1]
|
||||
|
||||
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)
|
||||
if not self.up:
|
||||
return
|
||||
params = []
|
||||
upstream = []
|
||||
downstream = []
|
||||
if bw is not None:
|
||||
if str(bw)=="0":
|
||||
bw="-1"
|
||||
params += ["bandwidth=%s" % bw,]
|
||||
if delay is not None:
|
||||
if str(delay)=="0":
|
||||
delay="-1"
|
||||
params += ["delay=%s" % delay,]
|
||||
if loss is not None:
|
||||
if str(loss)=="0":
|
||||
loss="-1"
|
||||
upstream += ["BER=%s" % loss,]
|
||||
downstream += ["BER=%s" % loss,]
|
||||
if duplicate is not None:
|
||||
if str(duplicate)=="0":
|
||||
duplicate="-1"
|
||||
upstream += ["duplicate=%s" % duplicate,]
|
||||
downstream += ["duplicate=%s" % duplicate,]
|
||||
if jitter:
|
||||
self.warn("jitter parameter ignored for link %s" % self.name)
|
||||
if len(params) > 0 or len(upstream) > 0 or len(downstream) > 0:
|
||||
setcfg = ["setcfg", "{",] + params
|
||||
if len(upstream) > 0:
|
||||
setcfg += ["upstream={",] + upstream + ["}",]
|
||||
if len(downstream) > 0:
|
||||
setcfg += ["downstream={",] + downstream + ["}",]
|
||||
setcfg += ["}",]
|
||||
ngmessage(self.ngname, setcfg)
|
||||
|
393
daemon/core/bsd/vnode.py
Normal file
393
daemon/core/bsd/vnode.py
Normal file
|
@ -0,0 +1,393 @@
|
|||
#
|
||||
# CORE
|
||||
# Copyright (c)2010-2012 the Boeing Company.
|
||||
# See the LICENSE file included in this distribution.
|
||||
#
|
||||
# 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 *
|
||||
|
||||
checkexec([IFCONFIG_BIN, VIMAGE_BIN])
|
||||
|
||||
class VEth(PyCoreNetIf):
|
||||
def __init__(self, node, name, localname, mtu = 1500, net = None,
|
||||
start = True):
|
||||
PyCoreNetIf.__init__(self, node = node, name = name, mtu = mtu)
|
||||
# name is the device name (e.g. ngeth0, ngeth1, etc.) before it is
|
||||
# installed in a node; the Netgraph name is renamed to localname
|
||||
# e.g. before install: name = ngeth0 localname = n0_0_123
|
||||
# after install: name = eth0 localname = n0_0_123
|
||||
self.localname = localname
|
||||
self.ngid = None
|
||||
self.net = None
|
||||
self.pipe = None
|
||||
self.addrlist = []
|
||||
self.hwaddr = None
|
||||
self.up = False
|
||||
self.hook = "ether"
|
||||
if start:
|
||||
self.startup()
|
||||
|
||||
def startup(self):
|
||||
hookstr = "%s %s" % (self.hook, self.hook)
|
||||
ngname, ngid = createngnode(type="eiface", hookstr=hookstr,
|
||||
name=self.localname)
|
||||
self.name = ngname
|
||||
self.ngid = ngid
|
||||
check_call([IFCONFIG_BIN, ngname, "up"])
|
||||
self.up = True
|
||||
|
||||
def shutdown(self):
|
||||
if not self.up:
|
||||
return
|
||||
destroyngnode(self.localname)
|
||||
self.up = False
|
||||
|
||||
def attachnet(self, net):
|
||||
if self.net:
|
||||
self.detachnet()
|
||||
self.net = None
|
||||
net.attach(self)
|
||||
self.net = net
|
||||
|
||||
def detachnet(self):
|
||||
if self.net is not None:
|
||||
self.net.detach(self)
|
||||
|
||||
def addaddr(self, addr):
|
||||
self.addrlist.append(addr)
|
||||
|
||||
def deladdr(self, addr):
|
||||
self.addrlist.remove(addr)
|
||||
|
||||
def sethwaddr(self, addr):
|
||||
self.hwaddr = addr
|
||||
|
||||
class TunTap(PyCoreNetIf):
|
||||
'''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):
|
||||
PyCoreNode.__init__(self, session, objid, name)
|
||||
self.nodedir = nodedir
|
||||
self.verbose = verbose
|
||||
self.pid = None
|
||||
self.up = False
|
||||
self.lock = threading.RLock()
|
||||
self._mounts = []
|
||||
|
||||
def startup(self):
|
||||
if self.up:
|
||||
raise Exception, "already up"
|
||||
vimg = [VIMAGE_BIN, "-c", self.name]
|
||||
try:
|
||||
os.spawnlp(os.P_WAIT, 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.info("setting hostname: %s" % self.name)
|
||||
self.cmd(["hostname", self.name])
|
||||
self.cmd([SYSCTL_BIN, "vfs.morphing_symlinks=1"])
|
||||
self.up = True
|
||||
|
||||
def shutdown(self):
|
||||
if not self.up:
|
||||
return
|
||||
for netif in self.netifs():
|
||||
netif.shutdown()
|
||||
self._netif.clear()
|
||||
del self.session
|
||||
vimg = [VIMAGE_BIN, "-d", self.name]
|
||||
try:
|
||||
os.spawnlp(os.P_WAIT, VIMAGE_BIN, *vimg)
|
||||
except OSError:
|
||||
raise Exception, ("vimage command not found while running: %s" % \
|
||||
vimg)
|
||||
self.up = False
|
||||
|
||||
def cmd(self, args, wait = True):
|
||||
if wait:
|
||||
mode = os.P_WAIT
|
||||
else:
|
||||
mode = os.P_NOWAIT
|
||||
tmp = call([VIMAGE_BIN, self.name] + args, cwd=self.nodedir)
|
||||
if not wait:
|
||||
tmp = None
|
||||
if tmp:
|
||||
self.warn("cmd exited with status %s: %s" % (tmp, str(args)))
|
||||
return tmp
|
||||
|
||||
def cmdresult(self, args, wait = True):
|
||||
cmdid, cmdin, cmdout, cmderr = self.popen(args)
|
||||
result = cmdout.read()
|
||||
result += cmderr.read()
|
||||
cmdin.close()
|
||||
cmdout.close()
|
||||
cmderr.close()
|
||||
if wait:
|
||||
status = cmdid.wait()
|
||||
else:
|
||||
status = 0
|
||||
return (status, result)
|
||||
|
||||
def popen(self, args):
|
||||
cmd = [VIMAGE_BIN, self.name]
|
||||
cmd.extend(args)
|
||||
tmp = subprocess.Popen(cmd, stdin = subprocess.PIPE,
|
||||
stdout = subprocess.PIPE,
|
||||
stderr = subprocess.PIPE, cwd=self.nodedir)
|
||||
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)
|
||||
|
||||
def term(self, sh = "/bin/sh"):
|
||||
return os.spawnlp(os.P_WAIT, "xterm", "xterm", "-ut",
|
||||
"-title", self.name, "-e", VIMAGE_BIN, self.name, sh)
|
||||
|
||||
def termcmdstring(self, sh = "/bin/sh"):
|
||||
''' 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)
|
||||
|
||||
def shcmd(self, cmdstr, sh = "/bin/sh"):
|
||||
return self.cmd([sh, "-c", cmdstr])
|
||||
|
||||
def boot(self):
|
||||
pass
|
||||
|
||||
def mount(self, source, target):
|
||||
source = os.path.abspath(source)
|
||||
self.info("mounting %s at %s" % (source, target))
|
||||
self.addsymlink(path=target, file=None)
|
||||
|
||||
def umount(self, target):
|
||||
self.info("unmounting '%s'" % target)
|
||||
|
||||
def newveth(self, ifindex = None, ifname = None, net = None):
|
||||
self.lock.acquire()
|
||||
try:
|
||||
if ifindex is None:
|
||||
ifindex = self.newifindex()
|
||||
if ifname is None:
|
||||
ifname = "eth%d" % ifindex
|
||||
sessionid = self.session.shortsessionid()
|
||||
name = "n%s_%s_%s" % (self.objid, ifindex, sessionid)
|
||||
localname = name
|
||||
ifclass = VEth
|
||||
veth = ifclass(node = self, name = name, localname = localname,
|
||||
mtu = 1500, net = net, start = self.up)
|
||||
if self.up:
|
||||
# install into jail
|
||||
check_call([IFCONFIG_BIN, veth.name, "vnet", self.name])
|
||||
# rename from "ngeth0" to "eth0"
|
||||
self.cmd([IFCONFIG_BIN, veth.name, "name", ifname])
|
||||
veth.name = ifname
|
||||
try:
|
||||
self.addnetif(veth, ifindex)
|
||||
except:
|
||||
veth.shutdown()
|
||||
del veth
|
||||
raise
|
||||
return ifindex
|
||||
finally:
|
||||
self.lock.release()
|
||||
|
||||
def sethwaddr(self, ifindex, addr):
|
||||
self._netif[ifindex].sethwaddr(addr)
|
||||
if self.up:
|
||||
self.cmd([IFCONFIG_BIN, self.ifname(ifindex), "link",
|
||||
str(addr)])
|
||||
|
||||
def addaddr(self, ifindex, addr):
|
||||
if self.up:
|
||||
if ':' in addr:
|
||||
family = "inet6"
|
||||
else:
|
||||
family = "inet"
|
||||
self.cmd([IFCONFIG_BIN, self.ifname(ifindex), family, "alias",
|
||||
str(addr)])
|
||||
self._netif[ifindex].addaddr(addr)
|
||||
|
||||
def deladdr(self, ifindex, addr):
|
||||
try:
|
||||
self._netif[ifindex].deladdr(addr)
|
||||
except ValueError:
|
||||
self.warn("trying to delete unknown address: %s" % addr)
|
||||
if self.up:
|
||||
if ':' in addr:
|
||||
family = "inet6"
|
||||
else:
|
||||
family = "inet"
|
||||
self.cmd([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:
|
||||
if t not in 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
|
||||
self.getaddr(self.ifname(ifindex), rescan = True)
|
||||
|
||||
def ifup(self, ifindex):
|
||||
if self.up:
|
||||
self.cmd([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)
|
||||
if net is not None:
|
||||
self.attachnet(ifindex, net)
|
||||
if hwaddr:
|
||||
self.sethwaddr(ifindex, hwaddr)
|
||||
for addr in maketuple(addrlist):
|
||||
self.addaddr(ifindex, addr)
|
||||
self.ifup(ifindex)
|
||||
return ifindex
|
||||
finally:
|
||||
self.lock.release()
|
||||
|
||||
def attachnet(self, ifindex, net):
|
||||
self._netif[ifindex].attachnet(net)
|
||||
|
||||
def detachnet(self, ifindex):
|
||||
self._netif[ifindex].detachnet()
|
||||
|
||||
def addfile(self, srcname, filename):
|
||||
shcmd = "mkdir -p $(dirname '%s') && mv '%s' '%s' && sync" % \
|
||||
(filename, srcname, filename)
|
||||
self.shcmd(shcmd)
|
||||
|
||||
def getaddr(self, ifname, rescan = False):
|
||||
return None
|
||||
#return self.vnodeclient.getaddr(ifname = ifname, rescan = rescan)
|
||||
|
||||
def addsymlink(self, path, 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:]
|
||||
dirname = dirname.replace("/", ".")
|
||||
if file:
|
||||
pathname = os.path.join(path, file)
|
||||
sym = os.path.join(self.session.sessiondir, "@.conf", dirname, file)
|
||||
else:
|
||||
pathname = path
|
||||
sym = os.path.join(self.session.sessiondir, "@.conf", dirname)
|
||||
|
||||
if os.path.islink(pathname):
|
||||
if os.readlink(pathname) == sym:
|
||||
# this link already exists - silently return
|
||||
return
|
||||
os.unlink(pathname)
|
||||
else:
|
||||
if os.path.exists(pathname):
|
||||
self.warn("did not create symlink for %s since path " \
|
||||
"exists on host" % pathname)
|
||||
return
|
||||
self.info("creating symlink %s -> %s" % (pathname, sym))
|
||||
os.symlink(sym, pathname)
|
||||
|
||||
class JailNode(SimpleJailNode):
|
||||
|
||||
def __init__(self, session, objid = None, name = None,
|
||||
nodedir = None, bootsh = "boot.sh", verbose = False,
|
||||
start = True):
|
||||
super(JailNode, self).__init__(session = session, objid = objid,
|
||||
name = name, nodedir = nodedir,
|
||||
verbose = verbose)
|
||||
self.bootsh = bootsh
|
||||
if not start:
|
||||
return
|
||||
# below here is considered node startup/instantiation code
|
||||
self.makenodedir()
|
||||
self.startup()
|
||||
|
||||
def boot(self):
|
||||
self.session.services.bootnodeservices(self)
|
||||
|
||||
def validate(self):
|
||||
self.session.services.validatenodeservices(self)
|
||||
|
||||
def startup(self):
|
||||
self.lock.acquire()
|
||||
try:
|
||||
super(JailNode, self).startup()
|
||||
#self.privatedir("/var/run")
|
||||
#self.privatedir("/var/log")
|
||||
finally:
|
||||
self.lock.release()
|
||||
|
||||
def shutdown(self):
|
||||
if not self.up:
|
||||
return
|
||||
self.lock.acquire()
|
||||
# services are instead stopped when session enters datacollect state
|
||||
#self.session.services.stopnodeservices(self)
|
||||
try:
|
||||
super(JailNode, self).shutdown()
|
||||
finally:
|
||||
self.rmnodedir()
|
||||
self.lock.release()
|
||||
|
||||
def privatedir(self, path):
|
||||
if path[0] != "/":
|
||||
raise ValueError, "path not fully qualified: " + path
|
||||
hostpath = os.path.join(self.nodedir, path[1:].replace("/", "."))
|
||||
try:
|
||||
os.mkdir(hostpath)
|
||||
except OSError:
|
||||
pass
|
||||
except Exception, e:
|
||||
raise Exception, e
|
||||
self.mount(hostpath, path)
|
||||
|
||||
def opennodefile(self, filename, mode = "w"):
|
||||
dirname, basename = os.path.split(filename)
|
||||
#self.addsymlink(path=dirname, file=basename)
|
||||
if not basename:
|
||||
raise ValueError, "no basename for filename: " + filename
|
||||
if dirname and dirname[0] == "/":
|
||||
dirname = dirname[1:]
|
||||
dirname = dirname.replace("/", ".")
|
||||
dirname = os.path.join(self.nodedir, dirname)
|
||||
if not os.path.isdir(dirname):
|
||||
os.makedirs(dirname, mode = 0755)
|
||||
hostfilename = os.path.join(dirname, basename)
|
||||
return open(hostfilename, mode)
|
||||
|
||||
def nodefile(self, filename, contents, mode = 0644):
|
||||
f = self.opennodefile(filename, "w")
|
||||
f.write(contents)
|
||||
os.chmod(f.name, mode)
|
||||
f.close()
|
||||
self.info("created nodefile: '%s'; mode: 0%o" % (f.name, mode))
|
||||
|
Loading…
Add table
Add a link
Reference in a new issue