core-extra/daemon/core/coreobj.py

503 lines
18 KiB
Python

#
# 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
from core.api import coreapi
from core.misc.ipaddr import *
class Position(object):
''' Helper class for Cartesian coordinate position
'''
def __init__(self, x = None, y = None, z = None):
self.x = None
self.y = None
self.z = None
self.set(x, y, z)
def set(self, x = None, y = None, z = None):
''' 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
self.y = y
self.z = z
return True
def get(self):
''' 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)
'''
apitype = None
def __init__(self, session, objid = None, name = None, verbose = False,
start = True):
self.session = session
if objid is None:
objid = session.getobjid()
self.objid = objid
if name is None:
name = "o%s" % self.objid
self.name = name
# ifindex is key, PyCoreNetIf instance is value
self._netif = {}
self.ifindex = 0
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.
'''
raise NotImplementedError
def shutdown(self):
''' 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.
'''
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 self.position.get()
def ifname(self, ifindex):
return self.netif(ifindex).name
def netifs(self, sort=False):
''' 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 len(self._netif)
def getifindex(self, netif):
for ifindex in self._netif:
if self._netif[ifindex] is netif:
return ifindex
return -1
def newifindex(self):
while self.ifindex in self._netif:
self.ifindex += 1
ifindex = self.ifindex
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.
'''
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)
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))
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
def tolinkmsgs(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)
self.services = []
if not hasattr(self, "type"):
self.type = None
self.nodedir = None
def nodeid(self):
return self.objid
def addservice(self, service):
if service is not None:
self.services.append(service)
def makenodedir(self):
if self.nodedir is None:
self.nodedir = \
os.path.join(self.session.sessiondir, self.name + ".conf")
os.makedirs(self.nodedir)
self.tmpnodedir = True
else:
self.tmpnodedir = False
def rmnodedir(self):
if hasattr(self.session.options, 'preservedir'):
if self.session.options.preservedir == '1':
return
if self.tmpnodedir:
shutil.rmtree(self.nodedir, ignore_errors = True)
def addnetif(self, netif, ifindex):
if ifindex in self._netif:
raise ValueError, "ifindex %s already exists" % ifindex
self._netif[ifindex] = netif
netif.netindex = ifindex
def delnetif(self, ifindex):
if ifindex not in self._netif:
raise ValueError, "ifindex %s does not exist" % ifindex
netif = self._netif.pop(ifindex)
netif.shutdown()
del netif
def netif(self, ifindex, net = None):
if ifindex in self._netif:
return self._netif[ifindex]
else:
return None
def attachnet(self, ifindex, net):
if ifindex not in self._netif:
raise ValueError, "ifindex %s does not exist" % ifindex
self._netif[ifindex].attachnet(net)
def detachnet(self, ifindex):
if ifindex not in self._netif:
raise ValueError, "ifindex %s does not exist" % ifindex
self._netif[ifindex].detachnet()
def setposition(self, x = None, y = None, z = None):
changed = PyCoreObj.setposition(self, x = x, y = y, z = z)
if not changed:
# save extra interface range calculations
return
for netif in self.netifs(sort=True):
netif.setposition(x, y, z)
def commonnets(self, obj, want_ctrl=False):
''' 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'):
continue
for netif2 in obj.netifs():
if netif1.net == netif2.net:
r += (netif1.net, netif1, netif2),
return r
class PyCoreNet(PyCoreObj):
''' Base class for networks
'''
linktype = coreapi.CORE_LINK_WIRED
def __init__(self, session, objid, name, verbose = False, start = True):
''' Initialization for network objects.
'''
PyCoreObj.__init__(self, session, objid, name, verbose=verbose,
start=start)
self._linked = {}
self._linked_lock = threading.Lock()
def attach(self, netif):
i = self.newifindex()
self._netif[i] = netif
netif.netifi = i
with self._linked_lock:
self._linked[netif] = {}
def detach(self, netif):
del self._netif[netif.netifi]
netif.netifi = None
with self._linked_lock:
del self._linked[netif]
def netifparamstolink(self, netif):
''' 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')
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)
return tlvdata
def tolinkmsgs(self, flags):
''' Build CORE API Link Messages for this network. Each link message
describes a link between this network and a node.
'''
msgs = []
# build a link message from this network node to each node having a
# connected interface
for netif in self.netifs(sort=True):
if not hasattr(netif, "node"):
continue
otherobj = netif.node
uni = False
if otherobj is None:
# two layer-2 switches/hubs linked together via linknet()
if not hasattr(netif, "othernet"):
continue
otherobj = netif.othernet
if otherobj.objid == self.objid:
continue
netif.swapparams('_params_up')
upstream_params = netif.getparams()
netif.swapparams('_params_up')
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)
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('/')
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)
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)
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
class PyCoreNetIf(object):
''' Base class for interfaces.
'''
def __init__(self, node, name, mtu):
self.node = node
self.name = name
if not isinstance(mtu, (int, long)):
raise ValueError
self.mtu = mtu
self.net = None
self._params = {}
self.addrlist = []
self.hwaddr = None
self.poshook = None
# used with EMANE
self.transport_type = None
# interface index on the network
self.netindex = None
def startup(self):
pass
def shutdown(self):
pass
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
def getparam(self, key):
''' 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.
'''
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.
Returns True if the parameter has changed.
'''
if key in self._params:
if self._params[key] == value:
return False
elif self._params[key] <= 0 and value <= 0:
# treat None and 0 as unchanged values
return False
self._params[key] = value
return True
def swapparams(self, name):
''' 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, {})
self._params = getattr(self, name)
setattr(self, name, tmp)
def setposition(self, x, y, z):
''' Dispatch to any position hook (self.poshook) handler.
'''
if self.poshook is not None:
self.poshook(self, x, y, z)