core-extra/daemon/core/bsd/vnode.py
2013-08-29 14:21:13 +00:00

393 lines
13 KiB
Python

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