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

@ -5,9 +5,9 @@
from math import pi, sin, cos, tan, sqrt
#LatLong- UTM conversion..h
#definitions for lat/long to UTM and UTM to lat/lng conversions
#include <string.h>
# LatLong- UTM conversion..h
# definitions for lat/long to UTM and UTM to lat/lng conversions
# include <string.h>
_deg2rad = pi / 180.0
_rad2deg = 180.0 / pi
@ -16,48 +16,49 @@ _EquatorialRadius = 2
_eccentricitySquared = 3
_ellipsoid = [
# id, Ellipsoid name, Equatorial Radius, square of eccentricity
# first once is a placeholder only, To allow array indices to match id numbers
[ -1, "Placeholder", 0, 0],
[ 1, "Airy", 6377563, 0.00667054],
[ 2, "Australian National", 6378160, 0.006694542],
[ 3, "Bessel 1841", 6377397, 0.006674372],
[ 4, "Bessel 1841 (Nambia] ", 6377484, 0.006674372],
[ 5, "Clarke 1866", 6378206, 0.006768658],
[ 6, "Clarke 1880", 6378249, 0.006803511],
[ 7, "Everest", 6377276, 0.006637847],
[ 8, "Fischer 1960 (Mercury] ", 6378166, 0.006693422],
[ 9, "Fischer 1968", 6378150, 0.006693422],
[ 10, "GRS 1967", 6378160, 0.006694605],
[ 11, "GRS 1980", 6378137, 0.00669438],
[ 12, "Helmert 1906", 6378200, 0.006693422],
[ 13, "Hough", 6378270, 0.00672267],
[ 14, "International", 6378388, 0.00672267],
[ 15, "Krassovsky", 6378245, 0.006693422],
[ 16, "Modified Airy", 6377340, 0.00667054],
[ 17, "Modified Everest", 6377304, 0.006637847],
[ 18, "Modified Fischer 1960", 6378155, 0.006693422],
[ 19, "South American 1969", 6378160, 0.006694542],
[ 20, "WGS 60", 6378165, 0.006693422],
[ 21, "WGS 66", 6378145, 0.006694542],
[ 22, "WGS-72", 6378135, 0.006694318],
[ 23, "WGS-84", 6378137, 0.00669438]
# id, Ellipsoid name, Equatorial Radius, square of eccentricity
# first once is a placeholder only, To allow array indices to match id numbers
[-1, "Placeholder", 0, 0],
[1, "Airy", 6377563, 0.00667054],
[2, "Australian National", 6378160, 0.006694542],
[3, "Bessel 1841", 6377397, 0.006674372],
[4, "Bessel 1841 (Nambia] ", 6377484, 0.006674372],
[5, "Clarke 1866", 6378206, 0.006768658],
[6, "Clarke 1880", 6378249, 0.006803511],
[7, "Everest", 6377276, 0.006637847],
[8, "Fischer 1960 (Mercury] ", 6378166, 0.006693422],
[9, "Fischer 1968", 6378150, 0.006693422],
[10, "GRS 1967", 6378160, 0.006694605],
[11, "GRS 1980", 6378137, 0.00669438],
[12, "Helmert 1906", 6378200, 0.006693422],
[13, "Hough", 6378270, 0.00672267],
[14, "International", 6378388, 0.00672267],
[15, "Krassovsky", 6378245, 0.006693422],
[16, "Modified Airy", 6377340, 0.00667054],
[17, "Modified Everest", 6377304, 0.006637847],
[18, "Modified Fischer 1960", 6378155, 0.006693422],
[19, "South American 1969", 6378160, 0.006694542],
[20, "WGS 60", 6378165, 0.006693422],
[21, "WGS 66", 6378145, 0.006694542],
[22, "WGS-72", 6378135, 0.006694318],
[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
#Internet: pdana@mail.utexas.edu
#3/22/95
#Source
#Defense Mapping Agency. 1987b. DMA Technical Report: Supplement to Department of Defense World Geodetic System
#1984 Technical Report. Part I and II. Washington, DC: Defense Mapping Agency
# 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
# Internet: pdana@mail.utexas.edu
# 3/22/95
#def LLtoUTM(int ReferenceEllipsoid, const double Lat, const double Long,
# Source
# Defense Mapping Agency. 1987b. DMA Technical Report: Supplement to Department of Defense World Geodetic System
# 1984 Technical Report. Part I and II. Washington, DC: Defense Mapping Agency
# def LLtoUTM(int ReferenceEllipsoid, const double Lat, const double Long,
# double &UTMNorthing, double &UTMEasting, char* UTMZone)
def LLtoUTM(ReferenceEllipsoid, Lat, Long, zone = None):
def LLtoUTM(ReferenceEllipsoid, Lat, Long, zone=None):
"""converts lat/long to UTM coords. Equations from USGS Bulletin 1532
East Longitudes are positive, West longitudes are negative.
North latitudes are positive, South latitudes are negative
@ -68,14 +69,14 @@ def LLtoUTM(ReferenceEllipsoid, Lat, Long, zone = None):
eccSquared = _ellipsoid[ReferenceEllipsoid][_eccentricitySquared]
k0 = 0.9996
#Make sure the longitude is between -180.00 .. 179.9
LongTemp = (Long+180)-int((Long+180)/360)*360-180 # -180.00 .. 179.9
# Make sure the longitude is between -180.00 .. 179.9
LongTemp = (Long + 180) - int((Long + 180) / 360) * 360 - 180 # -180.00 .. 179.9
LatRad = Lat*_deg2rad
LongRad = LongTemp*_deg2rad
LatRad = Lat * _deg2rad
LongRad = LongTemp * _deg2rad
if zone is None:
ZoneNumber = int((LongTemp + 180)/6) + 1
ZoneNumber = int((LongTemp + 180) / 6) + 1
else:
ZoneNumber = zone
@ -84,46 +85,50 @@ 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
LongOrigin = (ZoneNumber - 1) * 6 - 180 + 3 # +3 puts origin in middle of zone
LongOriginRad = LongOrigin * _deg2rad
#compute the UTM Zone from the latitude and longitude
# compute the UTM Zone from the latitude and longitude
UTMZone = "%d%c" % (ZoneNumber, _UTMLetterDesignator(Lat))
eccPrimeSquared = (eccSquared)/(1-eccSquared)
N = a/sqrt(1-eccSquared*sin(LatRad)*sin(LatRad))
T = tan(LatRad)*tan(LatRad)
C = eccPrimeSquared*cos(LatRad)*cos(LatRad)
A = cos(LatRad)*(LongRad-LongOriginRad)
eccPrimeSquared = (eccSquared) / (1 - eccSquared)
N = a / sqrt(1 - eccSquared * sin(LatRad) * sin(LatRad))
T = tan(LatRad) * tan(LatRad)
C = eccPrimeSquared * cos(LatRad) * cos(LatRad)
A = cos(LatRad) * (LongRad - LongOriginRad)
M = a*((1
- eccSquared/4
- 3*eccSquared*eccSquared/64
- 5*eccSquared*eccSquared*eccSquared/256)*LatRad
- (3*eccSquared/8
+ 3*eccSquared*eccSquared/32
+ 45*eccSquared*eccSquared*eccSquared/1024)*sin(2*LatRad)
+ (15*eccSquared*eccSquared/256 + 45*eccSquared*eccSquared*eccSquared/1024)*sin(4*LatRad)
- (35*eccSquared*eccSquared*eccSquared/3072)*sin(6*LatRad))
M = a * ((1
- eccSquared / 4
- 3 * eccSquared * eccSquared / 64
- 5 * eccSquared * eccSquared * eccSquared / 256) * LatRad
- (3 * eccSquared / 8
+ 3 * eccSquared * eccSquared / 32
+ 45 * eccSquared * eccSquared * eccSquared / 1024) * sin(2 * LatRad)
+ (15 * eccSquared * eccSquared / 256 + 45 * eccSquared * eccSquared * eccSquared / 1024) * sin(4 * LatRad)
- (35 * eccSquared * eccSquared * eccSquared / 3072) * sin(6 * LatRad))
UTMEasting = (k0*N*(A+(1-T+C)*A*A*A/6
+ (5-18*T+T*T+72*C-58*eccPrimeSquared)*A*A*A*A*A/120)
UTMEasting = (k0 * N * (A + (1 - T + C) * A * A * A / 6
+ (5 - 18 * T + T * T + 72 * C - 58 * eccPrimeSquared) * A * A * A * A * A / 120)
+ 500000.0)
UTMNorthing = (k0*(M+N*tan(LatRad)*(A*A/2+(5-T+9*C+4*C*C)*A*A*A*A/24
+ (61
-58*T
+T*T
+600*C
-330*eccPrimeSquared)*A*A*A*A*A*A/720)))
UTMNorthing = (k0 * (M + N * tan(LatRad) * (A * A / 2 + (5 - T + 9 * C + 4 * C * C) * A * A * A * A / 24
+ (61
- 58 * T
+ T * T
+ 600 * C
- 330 * eccPrimeSquared) * A * A * A * A * A * A / 720)))
if Lat < 0:
UTMNorthing = UTMNorthing + 10000000.0; #10000000 meter offset for southern hemisphere
UTMNorthing = UTMNorthing + 10000000.0; # 10000000 meter offset for southern hemisphere
return (UTMZone, UTMEasting, UTMNorthing)
@ -132,29 +137,51 @@ 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,
# void UTMtoLL(int ReferenceEllipsoid, const double UTMNorthing, const double UTMEasting, const char* UTMZone,
# double& Lat, double& Long )
def UTMtoLL(ReferenceEllipsoid, northing, easting, zone):
@ -168,10 +195,10 @@ Converted to Python by Russ Nelson <nelson@crynwr.com>"""
k0 = 0.9996
a = _ellipsoid[ReferenceEllipsoid][_EquatorialRadius]
eccSquared = _ellipsoid[ReferenceEllipsoid][_eccentricitySquared]
e1 = (1-sqrt(1-eccSquared))/(1+sqrt(1-eccSquared))
#NorthernHemisphere; //1 for northern hemispher, 0 for southern
e1 = (1 - sqrt(1 - eccSquared)) / (1 + sqrt(1 - eccSquared))
# NorthernHemisphere; //1 for northern hemispher, 0 for southern
x = easting - 500000.0 #remove 500,000 meter offset for longitude
x = easting - 500000.0 # remove 500,000 meter offset for longitude
y = northing
ZoneLetter = zone[-1]
@ -180,37 +207,40 @@ Converted to Python by Russ Nelson <nelson@crynwr.com>"""
NorthernHemisphere = 1 # point is in northern hemisphere
else:
NorthernHemisphere = 0 # point is in southern hemisphere
y -= 10000000.0 # remove 10,000,000 meter offset used for southern hemisphere
y -= 10000000.0 # remove 10,000,000 meter offset used for southern hemisphere
LongOrigin = (ZoneNumber - 1)*6 - 180 + 3 # +3 puts origin in middle of zone
LongOrigin = (ZoneNumber - 1) * 6 - 180 + 3 # +3 puts origin in middle of zone
eccPrimeSquared = (eccSquared)/(1-eccSquared)
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)
+(151*e1*e1*e1/96)*sin(6*mu))
phi1 = phi1Rad*_rad2deg;
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)
+ (151 * e1 * e1 * e1 / 96) * sin(6 * mu))
phi1 = phi1Rad * _rad2deg;
N1 = a/sqrt(1-eccSquared*sin(phi1Rad)*sin(phi1Rad))
T1 = tan(phi1Rad)*tan(phi1Rad)
C1 = eccPrimeSquared*cos(phi1Rad)*cos(phi1Rad)
R1 = a*(1-eccSquared)/pow(1-eccSquared*sin(phi1Rad)*sin(phi1Rad), 1.5)
D = x/(N1*k0)
N1 = a / sqrt(1 - eccSquared * sin(phi1Rad) * sin(phi1Rad))
T1 = tan(phi1Rad) * tan(phi1Rad)
C1 = eccPrimeSquared * cos(phi1Rad) * cos(phi1Rad)
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
+(61+90*T1+298*C1+45*T1*T1-252*eccPrimeSquared-3*C1*C1)*D*D*D*D*D*D/720)
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)
*D*D*D*D*D/120)/cos(phi1Rad)
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,75 +1,76 @@
#
# 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 threading
import time
from core.misc import log
logger = log.get_logger(__name__)
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(Timer, self).__init__()
self.interval = interval
self.function = function
self.args = args
self.kwargs = kwargs
self.finished = threading.Event()
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()
self._running.release()
return locked
def run(self):
self.finished.wait(self.interval)
with self._running:
if not self.finished.is_set():
self.function(*self.args, **self.kwargs)
self.finished.set()
class Event(object):
def __init__(self, eventnum, event_time, func, *args, **kwds):
self.eventnum = eventnum
self.time = event_time
self.func = func
self.args = args
self.kwds = kwds
self.canceled = False
def __cmp__(self, other):
tmp = cmp(self.time, other.time)
if tmp == 0:
tmp = cmp(self.eventnum, other.eventnum)
return tmp
def run(self):
if self.canceled:
return
self.func(*self.args, **self.kwds)
def cancel(self):
# XXX not thread-safe
self.canceled = True
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__()
self.interval = interval
self.function = function
self.args = args
self.kwargs = kwargs
self.finished = threading.Event()
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()
self._running.release()
return locked
def run(self):
self.finished.wait(self.interval)
with self._running:
if not self.finished.is_set():
self.function(*self.args, **self.kwargs)
self.finished.set()
class Event(object):
def __init__(self, eventnum, time, func, *args, **kwds):
self.eventnum = eventnum
self.time = time
self.func = func
self.args = args
self.kwds = kwds
self.canceled = False
def __cmp__(self, other):
tmp = cmp(self.time, other.time)
if tmp == 0:
tmp = cmp(self.eventnum, other.eventnum)
return tmp
def run(self):
if self.canceled:
return
self.func(*self.args, **self.kwds)
def cancel(self):
self.canceled = True # XXX not thread-safe
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))
def tolinklocal(self):
''' Convert the MAC address to a IPv6 link-local address, using EUI 48
return ":".join(map(lambda x: "%02x" % ord(x), self.addr))
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,32 +37,33 @@ 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)
@classmethod
def random(cls):
tmp = random.randint(0, 0xFFFFFF)
tmp |= 0x00163E << 24 # use the Xen OID 00:16:3E
tmp |= 0x00163E << 24 # use the Xen OID 00:16:3E
tmpbytes = struct.pack("!Q", tmp)
return cls(tmpbytes[2:])
class IPAddr(object):
def __init__(self, af, addr):
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
'''
bin = socket.inet_pton(AF_INET, s)
return(struct.unpack('!I', bin)[0])
return cls(af, socket.inet_pton(af, s))
class IPPrefix(object):
@staticmethod
def to_int(s):
"""
convert IPv4 string to 32-bit integer
"""
bin = socket.inet_pton(AF_INET, s)
return struct.unpack('!I', bin)[0]
class IpPrefix(object):
def __init__(self, af, prefixstr):
"prefixstr format: address/prefixlen"
tmp = prefixstr.split("/")
@ -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__()
netmaskbytes = struct.pack("!L", netmask)
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 = []):
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
@ -50,13 +53,14 @@ interface $interface
ipv6 ospf6 lsafullness mincostlsa
""")
# ip address $ipaddr/32
# ipv6 ospf6 simhelloLLtoULRecv :$simhelloport
# !$ipaddr:$simhelloport
# ip address $ipaddr/32
# ipv6 ospf6 simhelloLLtoULRecv :$simhelloport
# !$ipaddr:$simhelloport
def __init__(self, netif, instanceid = AF_IPV4_ID,
network = "manet-designated-router", **kwds):
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,18 +68,19 @@ interface $interface
return "ipv6 address %s" % x
else:
raise Value, "invalid address: %s", x
addr = "\n ".join(map(addrstr, netif.addrlist))
self.instanceid = instanceid
self.network = network
Conf.__init__(self, interface = netif.name, addr = addr,
instanceid = instanceid, network = network, **kwds)
Conf.__init__(self, interface=netif.name, addr=addr,
instanceid=instanceid, network=network, **kwds)
def name(self):
return self.netif.name
class QuaggaOSPF6(Conf):
class QuaggaOSPF6(Conf):
template = Template("""\
$interfaces
!
@ -86,13 +91,13 @@ router ospf6
""")
def __init__(self, ospf6ifs, area, routerid,
redistribute = "! no redistribute"):
redistribute="! no redistribute"):
ospf6ifs = maketuple(ospf6ifs)
interfaces = "\n!\n".join(map(str, ospf6ifs))
ospfifs = "\n ".join(map(lambda x: "interface %s area %s" % \
(x.name(), area), ospf6ifs))
Conf.__init__(self, interfaces = interfaces, routerid = routerid,
ospfifs = ospfifs, redistribute = redistribute)
(x.name(), area), ospf6ifs))
Conf.__init__(self, interfaces=interfaces, routerid=routerid,
ospfifs=ospfifs, redistribute=redistribute)
class QuaggaConf(Conf):
@ -105,12 +110,12 @@ $routers
$forwarding
""")
def __init__(self, routers, logfile, debugs = ()):
def __init__(self, routers, logfile, debugs=()):
routers = "\n!\n".join(map(str, maketuple(routers)))
if debugs:
debugs = "\n".join(maketuple(debugs))
else:
debugs = "! no debugs"
forwarding = "ip forwarding\nipv6 forwarding"
Conf.__init__(self, logfile = logfile, debugs = debugs,
routers = routers, forwarding = forwarding)
Conf.__init__(self, logfile=logfile, debugs=debugs,
routers=routers, forwarding=forwarding)

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,82 +48,91 @@ 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('\\', '\\\\')
return ast.literal_eval(s)
#return tuple(type(i) for i in s[1:-1].split(','))
#r = ()
#for i in s.strip("()").split(','):
# return tuple(type(i) for i in s[1:-1].split(','))
# r = ()
# for i in s.strip("()").split(','):
# r += (i.strip("' "), )
# chop empty last element from "('a',)" strings
#if r[-1] == '':
# if r[-1] == '':
# r = r[:-1]
#return r
# 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
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
status = cmdid.wait()
return (status, result)
def hexdump(s, bytes_per_word = 2, words_per_line = 8):
def cmdresult(args):
"""
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)
# err will always be None
result, err = cmdid.communicate()
status = cmdid.wait()
return status, result
def hexdump(s, bytes_per_word=2, words_per_line=8):
dump = ""
count = 0
bytes = bytes_per_word * words_per_line
@ -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()
@ -161,10 +181,12 @@ def filedemunge(pathname, header):
lines = lines[:start] + lines[end:]
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))
@ -174,20 +196,24 @@ def expandcorepath(pathname, session=None, node=None):
pathname = pathname.replace('%NODE%', str(node.objid))
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.
'''
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.
"""
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.
Skip comments; strip newline characters and spacing.
'''
"""
Read key=value pairs from a file, into a dict.
Skip comments; strip newline characters and spacing.
"""
with open(filename, 'r') as f:
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
@ -139,7 +140,7 @@ def to_latlon(easting, northing, zone_number, zone_letter):
n = R / ep_sin_sqrt
r = (1 - E) / ep_sin
c = _E * p_cos**2
c = _E * p_cos ** 2
c2 = c * c
d = x / (n * K0)
@ -152,7 +153,7 @@ def to_latlon(easting, northing, zone_number, zone_letter):
latitude = (p_rad - (p_tan / r) *
(d2 / 2 -
d4 / 24 * (5 + 3 * p_tan2 + 10 * c - 4 * c2 - 9 * E_P2)) +
d6 / 720 * (61 + 90 * p_tan2 + 298 * c + 45 * p_tan4 - 252 * E_P2 - 3 * c2))
d6 / 720 * (61 + 90 * p_tan2 + 298 * c + 45 * p_tan4 - 252 * E_P2 - 3 * c2))
longitude = (d -
d3 / 6 * (1 + 2 * p_tan2 + c) +
@ -184,8 +185,8 @@ def from_latlon(latitude, longitude):
zone_letter = latitude_to_zone_letter(latitude)
n = R / math.sqrt(1 - E * lat_sin**2)
c = E_P2 * lat_cos**2
n = R / math.sqrt(1 - E * lat_sin ** 2)
c = E_P2 * lat_cos ** 2
a = lat_cos * (lon_rad - central_lon_rad)
a2 = a * a
@ -204,7 +205,7 @@ def from_latlon(latitude, longitude):
a5 / 120 * (5 - 18 * lat_tan2 + lat_tan4 + 72 * c - 58 * E_P2)) + 500000
northing = K0 * (m + n * lat_tan * (a2 / 2 +
a4 / 24 * (5 - lat_tan2 + 9 * c + 4 * c**2) +
a4 / 24 * (5 - lat_tan2 + 9 * c + 4 * c ** 2) +
a6 / 720 * (61 - 58 * lat_tan2 + lat_tan4 + 600 * c - 330 * E_P2)))
if latitude < 0:
@ -244,16 +245,15 @@ def zone_number_to_central_longitude(zone_number):
def haversine(lon1, lat1, lon2, lat2):
"""
Calculate the great circle distance between two points
Calculate the great circle distance between two points
on the earth (specified in decimal degrees)
"""
# convert decimal degrees to radians
# convert decimal degrees to radians
lon1, lat1, lon2, lat2 = map(math.radians, [lon1, lat1, lon2, lat2])
# haversine formula
dlon = lon2 - lon1
dlat = lat2 - lat1
a = math.sin(dlat/2)**2 + math.cos(lat1) * math.cos(lat2) * math.sin(dlon/2)**2
c = 2 * math.asin(math.sqrt(a))
# haversine formula
dlon = lon2 - lon1
dlat = lat2 - lat1
a = math.sin(dlat / 2) ** 2 + math.cos(lat1) * math.cos(lat2) * math.sin(dlon / 2) ** 2
c = 2 * math.asin(math.sqrt(a))
m = 6367000 * c
return m
return m

View file

@ -1,205 +0,0 @@
import socket
import subprocess
import os
import xmlutils
from core.netns import nodes
from core.misc import ipaddr
from core import constants
class CoreDeploymentWriter(object):
def __init__(self, dom, root, session):
self.dom = dom
self.root = root
self.session = session
self.hostname = socket.gethostname()
if self.session.emane.version < self.session.emane.EMANE092:
self.transport = None
self.platform = None
@staticmethod
def get_ipv4_addresses(hostname):
if hostname == 'localhost':
addr_list = []
cmd = (constants.IP_BIN, '-o', '-f', 'inet', 'addr', 'show')
output = subprocess.check_output(cmd)
for line in output.split(os.linesep):
split = line.split()
if not split:
continue
addr = split[3]
if not addr.startswith('127.'):
addr_list.append(addr)
return addr_list
else:
# TODO: handle other hosts
raise NotImplementedError
@staticmethod
def get_interface_names(hostname):
'''Uses same methodology of get_ipv4_addresses() to get
parallel list of interface names to go with ...'''
if hostname == 'localhost':
iface_list = []
cmd = (constants.IP_BIN, '-o', '-f', 'inet', 'addr', 'show')
output = subprocess.check_output(cmd)
for line in output.split(os.linesep):
split = line.split()
if not split:
continue
ifaceName = split[1]
addr = split[3]
if not addr.startswith('127.'):
iface_list.append(ifaceName)
return iface_list
else:
# TODO: handle other hosts
raise NotImplementedError
@staticmethod
def find_device(scenario, name):
tagName = ('device', 'host', 'router')
for d in xmlutils.iterDescendantsWithAttribute(scenario, tagName,
'name', name):
return d
return None
@staticmethod
def find_interface(device, name):
for i in xmlutils.iterDescendantsWithAttribute(device, 'interface',
'name', name):
return i
return None
def add_deployment(self):
testbed = self.dom.createElement('container')
testbed.setAttribute('name', 'TestBed')
testbed.setAttribute('id', 'TestBed')
self.root.baseEle.appendChild(testbed)
nodelist = []
for obj in self.session.objs():
if isinstance(obj, nodes.PyCoreNode):
nodelist.append(obj)
name = self.hostname
ipv4_addresses = self.get_ipv4_addresses('localhost')
iface_names = self.get_interface_names('localhost')
testhost = self.add_physical_host(testbed, name, ipv4_addresses, iface_names)
for n in nodelist:
self.add_virtual_host(testhost, n)
# TODO: handle other servers
# servers = self.session.broker.getservernames()
# servers.remove('localhost')
def add_child_element(self, parent, tagName):
el = self.dom.createElement(tagName)
parent.appendChild(el)
return el
def add_child_element_with_nameattr(self, parent, tagName,
name, setid = True):
el = self.add_child_element(parent, tagName)
el.setAttribute('name', name)
if setid:
el.setAttribute('id', '%s/%s' % (parent.getAttribute('id'), name))
return el
def add_address(self, parent, address_type, address_str, address_iface=None):
el = self.add_child_element(parent, 'address')
el.setAttribute('type', address_type)
if address_iface is not None:
el.setAttribute('iface', address_iface)
el.appendChild(self.dom.createTextNode(address_str))
return el
def add_type(self, parent, type_str):
el = self.add_child_element(parent, 'type')
el.appendChild(self.dom.createTextNode(type_str))
return el
def add_platform(self, parent, name):
el = self.add_child_element_with_nameattr(parent,
'emanePlatform', name)
return el
def add_transport(self, parent, name):
el = self.add_child_element_with_nameattr(parent, 'transport', name)
return el
def add_nem(self, parent, name):
el = self.add_child_element_with_nameattr(parent, 'nem', name)
return el
def add_parameter(self, parent, name, val):
el = self.add_child_element_with_nameattr(parent, 'parameter',
name, False)
el.appendChild(self.dom.createTextNode(val))
return el
def add_mapping(self, parent, maptype, mapref):
el = self.add_child_element(parent, 'mapping')
el.setAttribute('type', maptype)
el.setAttribute('ref', mapref)
return el
def add_host(self, parent, name):
el = self.add_child_element_with_nameattr(parent, 'testHost', name)
return el
def add_physical_host(self, parent, name, ipv4_addresses, iface_names):
el = self.add_host(parent, name)
self.add_type(el, 'physical')
for i in range(0, len(ipv4_addresses)):
addr = ipv4_addresses[i]
if iface_names:
ifaceName = iface_names[i]
else:
ifaceName = None
self.add_address(el, 'IPv4', addr, ifaceName)
return el
def add_virtual_host(self, parent, obj):
assert isinstance(obj, nodes.PyCoreNode)
el = self.add_host(parent, obj.name)
device = self.find_device(self.root.baseEle, obj.name)
if device is None:
self.session.warn('corresponding XML device not found for %s' %
(obj.name))
return
self.add_mapping(device, 'testHost', el.getAttribute('id'))
self.add_type(el, 'virtual')
for netif in obj.netifs():
for address in netif.addrlist:
addr, slash, prefixlen= address.partition('/')
if ipaddr.isIPv4Address(addr):
addr_type = 'IPv4'
elif ipaddr.isIPv6Address(addr):
addr_type = 'IPv6'
else:
raise NotImplementedError
self.add_address(el, addr_type, address, netif.name)
if isinstance(netif.net, nodes.EmaneNode):
nem = self.add_emane_interface(parent, el, netif)
interface = self.find_interface(device, netif.name)
self.add_mapping(interface, 'nem', nem.getAttribute('id'))
return el
def add_emane_interface(self, physical_host, virtual_host, netif,
platform_name = 'p1', transport_name = 't1'):
nemid = netif.net.nemidmap[netif]
if self.session.emane.version < self.session.emane.EMANE092:
if self.platform is None:
self.platform = \
self.add_platform(physical_host, name = platform_name)
platform = self.platform
if self.transport is None:
self.transport = \
self.add_transport(physical_host, name = transport_name)
transport = self.transport
else:
platform = self.add_platform(virtual_host, name = platform_name)
transport = self.add_transport(virtual_host, name = transport_name)
nem_name = 'nem%s' % nemid
nem = self.add_nem(platform, nem_name)
self.add_parameter(nem, 'nemid', str(nemid))
self.add_mapping(transport, 'nem', nem.getAttribute('id'))
return nem

View file

@ -1,46 +0,0 @@
# CORE
# Copyright (c) 2014 The Boeing Company.
# See the LICENSE file included in this distribution.
from xml.dom.minidom import parse
from xmlutils import getFirstChildByTagName
from xmlparser0 import CoreDocumentParser0
from xmlparser1 import CoreDocumentParser1
class CoreVersionParser(object):
DEFAULT_SCENARIO_VERSION = '1.0'
'''\
Helper class to check the version of Network Plan document. This
simply looks for a "Scenario" element; when present, this
indicates a 0.0 version document. The dom member is set in order
to prevent parsing a file twice (it can be passed to the
appropriate CoreDocumentParser class.)
'''
def __init__(self, filename, options={}):
if 'dom' in options:
self.dom = options['dom']
else:
self.dom = parse(filename)
scenario = getFirstChildByTagName(self.dom, 'scenario')
if scenario:
version = scenario.getAttribute('version')
if not version:
version = self.DEFAULT_SCENARIO_VERSION
self.version = version
elif getFirstChildByTagName(self.dom, 'Scenario'):
self.version = '0.0'
else:
self.version = 'unknown'
def core_document_parser(session, filename, options):
vp = CoreVersionParser(filename, options)
if 'dom' not in options:
options['dom'] = vp.dom
if vp.version == '0.0':
doc = CoreDocumentParser0(session, filename, options)
elif vp.version == '1.0':
doc = CoreDocumentParser1(session, filename, options)
else:
raise ValueError, 'unsupported document version: %s' % vp.version
return doc

View file

@ -1,420 +0,0 @@
#
# CORE
# Copyright (c)2011-2014 the Boeing Company.
# See the LICENSE file included in this distribution.
#
# author: Jeff Ahrenholz <jeffrey.m.ahrenholz@boeing.com>
#
from core.netns import nodes
from xml.dom.minidom import parse
from xmlutils import *
class CoreDocumentParser0(object):
def __init__(self, session, filename, options):
self.session = session
self.verbose = self.session.getcfgitembool('verbose', False)
self.filename = filename
if 'dom' in options:
# this prevents parsing twice when detecting file versions
self.dom = options['dom']
else:
self.dom = parse(filename)
self.start = options['start']
self.nodecls = options['nodecls']
self.np = getoneelement(self.dom, "NetworkPlan")
if self.np is None:
raise ValueError, "missing NetworkPlan!"
self.mp = getoneelement(self.dom, "MotionPlan")
self.sp = getoneelement(self.dom, "ServicePlan")
self.meta = getoneelement(self.dom, "CoreMetaData")
self.coords = self.getmotiondict(self.mp)
# link parameters parsed in parsenets(), applied in parsenodes()
self.linkparams = {}
self.parsedefaultservices()
self.parseorigin()
self.parsenets()
self.parsenodes()
self.parseservices()
self.parsemeta()
def warn(self, msg):
if self.session:
warnstr = "XML parsing '%s':" % (self.filename)
self.session.warn("%s %s" % (warnstr, msg))
def getmotiondict(self, mp):
''' Parse a MotionPlan into a dict with node names for keys and coordinates
for values.
'''
if mp is None:
return {}
coords = {}
for node in mp.getElementsByTagName("Node"):
nodename = str(node.getAttribute("name"))
if nodename == '':
continue
for m in node.getElementsByTagName("motion"):
if m.getAttribute("type") != "stationary":
continue
point = m.getElementsByTagName("point")
if len(point) == 0:
continue
txt = point[0].firstChild
if txt is None:
continue
xyz = map(int, txt.nodeValue.split(','))
z = None
x, y = xyz[0:2]
if (len(xyz) == 3):
z = xyz[2]
coords[nodename] = (x, y, z)
return coords
@staticmethod
def getcommonattributes(obj):
''' Helper to return tuple of attributes common to nodes and nets.
'''
id = int(obj.getAttribute("id"))
name = str(obj.getAttribute("name"))
type = str(obj.getAttribute("type"))
return(id, name, type)
def parsenets(self):
linkednets = []
for net in self.np.getElementsByTagName("NetworkDefinition"):
id, name, type = self.getcommonattributes(net)
nodecls = xmltypetonodeclass(self.session, type)
if not nodecls:
self.warn("skipping unknown network node '%s' type '%s'" % \
(name, type))
continue
n = self.session.addobj(cls = nodecls, objid = id, name = name,
start = self.start)
if name in self.coords:
x, y, z = self.coords[name]
n.setposition(x, y, z)
getparamssetattrs(net, ("icon", "canvas", "opaque"), n)
if hasattr(n, "canvas") and n.canvas is not None:
n.canvas = int(n.canvas)
# links between two nets (e.g. switch-switch)
for ifc in net.getElementsByTagName("interface"):
netid = str(ifc.getAttribute("net"))
ifcname = str(ifc.getAttribute("name"))
linkednets.append((n, netid, ifcname))
self.parsemodels(net, n)
# link networks together now that they all have been parsed
for (n, netid, ifcname) in linkednets:
try:
n2 = n.session.objbyname(netid)
except KeyError:
n.warn("skipping net %s interface: unknown net %s" % \
(n.name, netid))
continue
upstream = False
netif = n.getlinknetif(n2)
if netif is None:
netif = n2.linknet(n)
else:
netif.swapparams('_params_up')
upstream = True
key = (n2.name, ifcname)
if key in self.linkparams:
for (k, v) in self.linkparams[key]:
netif.setparam(k, v)
if upstream:
netif.swapparams('_params_up')
def parsenodes(self):
for node in self.np.getElementsByTagName("Node"):
id, name, type = self.getcommonattributes(node)
if type == "rj45":
nodecls = nodes.RJ45Node
else:
nodecls = self.nodecls
n = self.session.addobj(cls = nodecls, objid = id, name = name,
start = self.start)
if name in self.coords:
x, y, z = self.coords[name]
n.setposition(x, y, z)
n.type = type
getparamssetattrs(node, ("icon", "canvas", "opaque"), n)
if hasattr(n, "canvas") and n.canvas is not None:
n.canvas = int(n.canvas)
for ifc in node.getElementsByTagName("interface"):
self.parseinterface(n, ifc)
def parseinterface(self, n, ifc):
''' Parse a interface block such as:
<interface name="eth0" net="37278">
<address type="mac">00:00:00:aa:00:01</address>
<address>10.0.0.2/24</address>
<address>2001::2/64</address>
</interface>
'''
name = str(ifc.getAttribute("name"))
netid = str(ifc.getAttribute("net"))
hwaddr = None
addrlist = []
try:
net = n.session.objbyname(netid)
except KeyError:
n.warn("skipping node %s interface %s: unknown net %s" % \
(n.name, name, netid))
return
for addr in ifc.getElementsByTagName("address"):
addrstr = gettextchild(addr)
if addrstr is None:
continue
if addr.getAttribute("type") == "mac":
hwaddr = addrstr
else:
addrlist.append(addrstr)
i = n.newnetif(net, addrlist = addrlist, hwaddr = hwaddr,
ifindex = None, ifname = name)
for model in ifc.getElementsByTagName("model"):
self.parsemodel(model, n, n.objid)
key = (n.name, name)
if key in self.linkparams:
netif = n.netif(i)
for (k, v) in self.linkparams[key]:
netif.setparam(k, v)
def parsemodels(self, dom, obj):
''' Mobility/wireless model config is stored in a ConfigurableManager's
config dict.
'''
nodenum = int(dom.getAttribute("id"))
for model in dom.getElementsByTagName("model"):
self.parsemodel(model, obj, nodenum)
def parsemodel(self, model, obj, nodenum):
''' Mobility/wireless model config is stored in a ConfigurableManager's
config dict.
'''
name = model.getAttribute("name")
if name == '':
return
type = model.getAttribute("type")
# convert child text nodes into key=value pairs
kvs = gettextelementstolist(model)
mgr = self.session.mobility
# TODO: the session.confobj() mechanism could be more generic;
# it only allows registering Conf Message callbacks, but here
# we want access to the ConfigurableManager, not the callback
if name[:5] == "emane":
mgr = self.session.emane
elif name[:5] == "netem":
mgr = None
self.parsenetem(model, obj, kvs)
elif name[:3] == "xen":
mgr = self.session.xen
# TODO: assign other config managers here
if mgr:
mgr.setconfig_keyvalues(nodenum, name, kvs)
def parsenetem(self, model, obj, kvs):
''' Determine interface and invoke setparam() using the parsed
(key, value) pairs.
'''
ifname = model.getAttribute("netif")
peer = model.getAttribute("peer")
key = (peer, ifname)
# nodes and interfaces do not exist yet, at this point of the parsing,
# save (key, value) pairs for later
try:
#kvs = map(lambda(k, v): (int(v)), kvs)
kvs = map(self.numericvalue, kvs)
except ValueError:
self.warn("error parsing link parameters for '%s' on '%s'" % \
(ifname, peer))
self.linkparams[key] = kvs
@staticmethod
def numericvalue(keyvalue):
(key, value) = keyvalue
if '.' in str(value):
value = float(value)
else:
value = int(value)
return (key, value)
def parseorigin(self):
''' Parse any origin tag from the Mobility Plan and set the CoreLocation
reference point appropriately.
'''
origin = getoneelement(self.mp, "origin")
if not origin:
return
location = self.session.location
geo = []
attrs = ("lat","lon","alt")
for i in xrange(3):
a = origin.getAttribute(attrs[i])
if a is not None:
a = float(a)
geo.append(a)
location.setrefgeo(geo[0], geo[1], geo[2])
scale = origin.getAttribute("scale100")
if scale is not None:
location.refscale = float(scale)
point = getoneelement(origin, "point")
if point is not None and point.firstChild is not None:
xyz = point.firstChild.nodeValue.split(',')
if len(xyz) == 2:
xyz.append('0.0')
if len(xyz) == 3:
xyz = map(lambda(x): float(x), xyz)
location.refxyz = (xyz[0], xyz[1], xyz[2])
def parsedefaultservices(self):
''' Prior to parsing nodes, use session.services manager to store
default services for node types
'''
for node in self.sp.getElementsByTagName("Node"):
type = node.getAttribute("type")
if type == '':
continue # node-specific service config
services = []
for service in node.getElementsByTagName("Service"):
services.append(str(service.getAttribute("name")))
self.session.services.defaultservices[type] = services
self.session.info("default services for type %s set to %s" % \
(type, services))
def parseservices(self):
''' After node objects exist, parse service customizations and add them
to the nodes.
'''
svclists = {}
# parse services and store configs into session.services.configs
for node in self.sp.getElementsByTagName("Node"):
name = node.getAttribute("name")
if name == '':
continue # node type without name
n = self.session.objbyname(name)
if n is None:
self.warn("skipping service config for unknown node '%s'" % \
name)
continue
for service in node.getElementsByTagName("Service"):
svcname = service.getAttribute("name")
if self.parseservice(service, n):
if n.objid in svclists:
svclists[n.objid] += "|" + svcname
else:
svclists[n.objid] = svcname
# nodes in NetworkPlan but not in ServicePlan use the
# default services for their type
for node in self.np.getElementsByTagName("Node"):
id, name, type = self.getcommonattributes(node)
if id in svclists:
continue # custom config exists
else:
svclists[int(id)] = None # use defaults
# associate nodes with services
for objid in sorted(svclists.keys()):
n = self.session.obj(objid)
self.session.services.addservicestonode(node=n, nodetype=n.type,
services_str=svclists[objid],
verbose=self.verbose)
def parseservice(self, service, n):
''' Use session.services manager to store service customizations before
they are added to a node.
'''
name = service.getAttribute("name")
svc = self.session.services.getservicebyname(name)
if svc is None:
return False
values = []
startup_idx = service.getAttribute("startup_idx")
if startup_idx is not None:
values.append("startidx=%s" % startup_idx)
startup_time = service.getAttribute("start_time")
if startup_time is not None:
values.append("starttime=%s" % startup_time)
dirs = []
for dir in service.getElementsByTagName("Directory"):
dirname = dir.getAttribute("name")
dirs.append(dirname)
if len(dirs):
values.append("dirs=%s" % dirs)
startup = []
shutdown = []
validate = []
for cmd in service.getElementsByTagName("Command"):
type = cmd.getAttribute("type")
cmdstr = gettextchild(cmd)
if cmdstr is None:
continue
if type == "start":
startup.append(cmdstr)
elif type == "stop":
shutdown.append(cmdstr)
elif type == "validate":
validate.append(cmdstr)
if len(startup):
values.append("cmdup=%s" % startup)
if len(shutdown):
values.append("cmddown=%s" % shutdown)
if len(validate):
values.append("cmdval=%s" % validate)
files = []
for file in service.getElementsByTagName("File"):
filename = file.getAttribute("name")
files.append(filename)
data = gettextchild(file)
typestr = "service:%s:%s" % (name, filename)
self.session.services.setservicefile(nodenum=n.objid, type=typestr,
filename=filename,
srcname=None, data=data)
if len(files):
values.append("files=%s" % files)
if not bool(service.getAttribute("custom")):
return True
self.session.services.setcustomservice(n.objid, svc, values)
return True
def parsehooks(self, hooks):
''' Parse hook scripts from XML into session._hooks.
'''
for hook in hooks.getElementsByTagName("Hook"):
filename = hook.getAttribute("name")
state = hook.getAttribute("state")
data = gettextchild(hook)
if data is None:
data = "" # allow for empty file
type = "hook:%s" % state
self.session.sethook(type, filename=filename,
srcname=None, data=data)
def parsemeta(self):
opt = getoneelement(self.meta, "SessionOptions")
if opt:
for param in opt.getElementsByTagName("param"):
k = str(param.getAttribute("name"))
v = str(param.getAttribute("value"))
if v == '':
v = gettextchild(param) # allow attribute/text for newlines
setattr(self.session.options, k, v)
hooks = getoneelement(self.meta, "Hooks")
if hooks:
self.parsehooks(hooks)
meta = getoneelement(self.meta, "MetaData")
if meta:
for param in meta.getElementsByTagName("param"):
k = str(param.getAttribute("name"))
v = str(param.getAttribute("value"))
if v == '':
v = gettextchild(param)
self.session.metadata.additem(k, v)

View file

@ -1,942 +0,0 @@
#
# CORE
# Copyright (c) 2015 the Boeing Company.
# See the LICENSE file included in this distribution.
#
import sys
import random
from core.netns import nodes
from core import constants
from core.misc.ipaddr import MacAddr
from xml.dom.minidom import parse
from xmlutils import *
class CoreDocumentParser1(object):
layer2_device_types = 'hub', 'switch'
layer3_device_types = 'host', 'router'
device_types = layer2_device_types + layer3_device_types
# TODO: support CORE interface classes:
# RJ45Node
# TunnelNode
def __init__(self, session, filename, options):
self.session = session
self.verbose = self.session.getcfgitembool('verbose', False)
self.filename = filename
if 'dom' in options:
# this prevents parsing twice when detecting file versions
self.dom = options['dom']
else:
self.dom = parse(filename)
self.start = options['start']
self.nodecls = options['nodecls']
self.scenario = self.get_scenario(self.dom)
self.location_refgeo_set = False
self.location_refxyz_set = False
# saved link parameters saved when parsing networks and applied later
self.link_params = {}
# map from id-string to objid, for files having node names but
# not node numbers
self.objidmap = {}
self.objids = set()
self.default_services = {}
if self.scenario:
self.parse_scenario()
def info(self, msg):
s = 'XML parsing \'%s\': %s' % (self.filename, msg)
if self.session:
self.session.info(s)
else:
sys.stdout.write(s + '\n')
def warn(self, msg):
s = 'WARNING XML parsing \'%s\': %s' % (self.filename, msg)
if self.session:
self.session.warn(s)
else:
sys.stderr.write(s + '\n')
@staticmethod
def get_scenario(dom):
scenario = getFirstChildByTagName(dom, 'scenario')
if not scenario:
raise ValueError, 'no scenario element found'
version = scenario.getAttribute('version')
if version and version != '1.0':
raise ValueError, \
'unsupported scenario version found: \'%s\'' % version
return scenario
def parse_scenario(self):
self.parse_default_services()
self.parse_session_config()
self.parse_network_plan()
def assign_id(self, idstr, idval):
if idstr in self.objidmap:
assert self.objidmap[idstr] == idval and idval in self.objids
return
self.objidmap[idstr] = idval
self.objids.add(idval)
def rand_id(self):
while True:
x = random.randint(0, 0xffff)
if x not in self.objids:
return x
def get_id(self, idstr):
'''\
Get a, possibly new, object id (node number) corresponding to
the given XML string id.
'''
if not idstr:
idn = self.rand_id()
self.objids.add(idn)
return idn
elif idstr in self.objidmap:
return self.objidmap[idstr]
else:
try:
idn = int(idstr)
except ValueError:
idn = self.rand_id()
self.assign_id(idstr, idn)
return idn
def get_common_attributes(self, node):
'''\
Return id, name attributes for the given XML element. These
attributes are common to nodes and networks.
'''
idstr = node.getAttribute('id')
# use an explicit set COREID if it exists
coreid = self.find_core_id(node)
if coreid:
idn = int(coreid)
if idstr:
self.assign_id(idstr, idn)
else:
idn = self.get_id(idstr)
# TODO: consider supporting unicode; for now convert to an
# ascii string
namestr = str(node.getAttribute('name'))
return idn, namestr
def iter_network_member_devices(self, element):
# element can be a network or a channel
for interface in iterChildrenWithAttribute(element, 'member',
'type', 'interface'):
if_id = getChildTextTrim(interface)
assert if_id # XXX for testing
if not if_id:
continue
device, if_name = self.find_device_with_interface(if_id)
assert device, 'no device for if_id: %s' % if_id # XXX for testing
if device:
yield device, if_name
def network_class(self, network, network_type):
'''\
Return the corresponding CORE network class for the given
network/network_type.
'''
if network_type == 'ethernet':
return nodes.PtpNet
elif network_type == 'satcom':
return nodes.PtpNet
elif network_type == 'wireless':
channel = getFirstChildByTagName(network, 'channel')
if channel:
# use an explicit CORE type if it exists
coretype = getFirstChildTextTrimWithAttribute(channel, 'type',
'domain', 'CORE')
if coretype:
if coretype == 'basic_range':
return nodes.WlanNode
elif coretype.startswith('emane'):
return nodes.EmaneNode
else:
self.warn('unknown network type: \'%s\'' % coretype)
return xmltypetonodeclass(self.session, coretype)
return nodes.WlanNode
self.warn('unknown network type: \'%s\'' % network_type)
return None
def create_core_object(self, objcls, objid, objname, element, node_type):
obj = self.session.addobj(cls = objcls, objid = objid,
name = objname, start = self.start)
if self.verbose:
self.info('added object objid=%s name=%s cls=%s' % \
(objid, objname, objcls))
self.set_object_position(obj, element)
self.set_object_presentation(obj, element, node_type)
return obj
def get_core_object(self, idstr):
if idstr and idstr in self.objidmap:
objid = self.objidmap[idstr]
return self.session.obj(objid)
return None
def parse_network_plan(self):
# parse the scenario in the following order:
# 1. layer-2 devices
# 2. other networks (ptp/wlan)
# 3. layer-3 devices
self.parse_layer2_devices()
self.parse_networks()
self.parse_layer3_devices()
def set_ethernet_link_parameters(self, channel, link_params,
mobility_model_name, mobility_params):
# save link parameters for later use, indexed by the tuple
# (device_id, interface_name)
for dev, if_name in self.iter_network_member_devices(channel):
if self.device_type(dev) in self.device_types:
dev_id = dev.getAttribute('id')
key = (dev_id, if_name)
self.link_params[key] = link_params
if mobility_model_name or mobility_params:
raise NotImplementedError
def set_wireless_link_parameters(self, channel, link_params,
mobility_model_name, mobility_params):
network = self.find_channel_network(channel)
network_id = network.getAttribute('id')
if network_id in self.objidmap:
nodenum = self.objidmap[network_id]
else:
self.warn('unknown network: %s' % network.toxml('utf-8'))
assert False # XXX for testing
return
model_name = getFirstChildTextTrimWithAttribute(channel, 'type',
'domain', 'CORE')
if not model_name:
model_name = 'basic_range'
if model_name == 'basic_range':
mgr = self.session.mobility
elif model_name.startswith('emane'):
mgr = self.session.emane
elif model_name.startswith('xen'):
mgr = self.session.xen
else:
# TODO: any other config managers?
raise NotImplementedError
mgr.setconfig_keyvalues(nodenum, model_name, link_params.items())
if mobility_model_name and mobility_params:
mgr.setconfig_keyvalues(nodenum, mobility_model_name,
mobility_params.items())
def link_layer2_devices(self, device1, ifname1, device2, ifname2):
'''\
Link two layer-2 devices together.
'''
devid1 = device1.getAttribute('id')
dev1 = self.get_core_object(devid1)
devid2 = device2.getAttribute('id')
dev2 = self.get_core_object(devid2)
assert dev1 and dev2 # XXX for testing
if dev1 and dev2:
# TODO: review this
if isinstance(dev2, nodes.RJ45Node):
# RJ45 nodes have different linknet()
netif = dev2.linknet(dev1)
else:
netif = dev1.linknet(dev2)
self.set_wired_link_parameters(dev1, netif, devid1, ifname1)
@classmethod
def parse_xml_value(cls, valtext):
if not valtext:
return None
try:
if not valtext.translate(None, '0123456789'):
val = int(valtext)
else:
val = float(valtext)
except ValueError:
val = str(valtext)
return val
@classmethod
def parse_parameter_children(cls, parent):
params = {}
for parameter in iterChildrenWithName(parent, 'parameter'):
param_name = parameter.getAttribute('name')
assert param_name # XXX for testing
if not param_name:
continue
# TODO: consider supporting unicode; for now convert
# to an ascii string
param_name = str(param_name)
param_val = cls.parse_xml_value(getChildTextTrim(parameter))
# TODO: check if the name already exists?
if param_name and param_val:
params[param_name] = param_val
return params
def parse_network_channel(self, channel):
element = self.search_for_element(channel, 'type',
lambda x: not x.hasAttributes())
channel_type = getChildTextTrim(element)
link_params = self.parse_parameter_children(channel)
mobility = getFirstChildByTagName(channel, 'CORE:mobility')
if mobility:
mobility_model_name = \
getFirstChildTextTrimByTagName(mobility, 'type')
mobility_params = self.parse_parameter_children(mobility)
else:
mobility_model_name = None
mobility_params = None
if channel_type == 'wireless':
self.set_wireless_link_parameters(channel, link_params,
mobility_model_name,
mobility_params)
elif channel_type == 'ethernet':
# TODO: maybe this can be done in the loop below to avoid
# iterating through channel members multiple times
self.set_ethernet_link_parameters(channel, link_params,
mobility_model_name,
mobility_params)
else:
raise NotImplementedError
layer2_device = []
for dev, if_name in self.iter_network_member_devices(channel):
if self.device_type(dev) in self.layer2_device_types:
layer2_device.append((dev, if_name))
assert len(layer2_device) <= 2
if len(layer2_device) == 2:
self.link_layer2_devices(layer2_device[0][0], layer2_device[0][1],
layer2_device[1][0], layer2_device[1][1])
def parse_network(self, network):
'''\
Each network element should have an 'id' and 'name' attribute
and include the following child elements:
type (one)
member (zero or more with type="interface" or type="channel")
channel (zero or more)
'''
layer2_members = set()
layer3_members = 0
for dev, if_name in self.iter_network_member_devices(network):
if not dev:
continue
devtype = self.device_type(dev)
if devtype in self.layer2_device_types:
layer2_members.add(dev)
elif devtype in self.layer3_device_types:
layer3_members += 1
else:
raise NotImplementedError
if len(layer2_members) == 0:
net_type = getFirstChildTextTrimByTagName(network, 'type')
if not net_type:
msg = 'no network type found for network: \'%s\'' % \
network.toxml('utf-8')
self.warn(msg)
assert False # XXX for testing
return
net_cls = self.network_class(network, net_type)
objid, net_name = self.get_common_attributes(network)
if self.verbose:
self.info('parsing network: %s %s' % (net_name, objid))
if objid in self.session._objs:
return
n = self.create_core_object(net_cls, objid, net_name,
network, None)
# handle channel parameters
for channel in iterChildrenWithName(network, 'channel'):
self.parse_network_channel(channel)
def parse_networks(self):
'''\
Parse all 'network' elements.
'''
for network in iterDescendantsWithName(self.scenario, 'network'):
self.parse_network(network)
def parse_addresses(self, interface):
mac = []
ipv4 = []
ipv6= []
hostname = []
for address in iterChildrenWithName(interface, 'address'):
addr_type = address.getAttribute('type')
if not addr_type:
msg = 'no type attribute found for address ' \
'in interface: \'%s\'' % interface.toxml('utf-8')
self.warn(msg)
assert False # XXX for testing
continue
addr_text = getChildTextTrim(address)
if not addr_text:
msg = 'no text found for address ' \
'in interface: \'%s\'' % interface.toxml('utf-8')
self.warn(msg)
assert False # XXX for testing
continue
if addr_type == 'mac':
mac.append(addr_text)
elif addr_type == 'IPv4':
ipv4.append(addr_text)
elif addr_type == 'IPv6':
ipv6.append(addr_text)
elif addr_type == 'hostname':
hostname.append(addr_text)
else:
msg = 'skipping unknown address type \'%s\' in ' \
'interface: \'%s\'' % (addr_type, interface.toxml('utf-8'))
self.warn(msg)
assert False # XXX for testing
continue
return mac, ipv4, ipv6, hostname
def parse_interface(self, node, device_id, interface):
'''\
Each interface can have multiple 'address' elements.
'''
if_name = interface.getAttribute('name')
network = self.find_interface_network_object(interface)
if not network:
msg = 'skipping node \'%s\' interface \'%s\': ' \
'unknown network' % (node.name, if_name)
self.warn(msg)
assert False # XXX for testing
return
mac, ipv4, ipv6, hostname = self.parse_addresses(interface)
if mac:
hwaddr = MacAddr.fromstring(mac[0])
else:
hwaddr = None
ifindex = node.newnetif(network, addrlist = ipv4 + ipv6,
hwaddr = hwaddr, ifindex = None,
ifname = if_name)
# TODO: 'hostname' addresses are unused
if self.verbose:
msg = 'node \'%s\' interface \'%s\' connected ' \
'to network \'%s\'' % (node.name, if_name, network.name)
self.info(msg)
# set link parameters for wired links
if isinstance(network,
(nodes.HubNode, nodes.PtpNet, nodes.SwitchNode)):
netif = node.netif(ifindex)
self.set_wired_link_parameters(network, netif, device_id)
def set_wired_link_parameters(self, network, netif,
device_id, netif_name = None):
if netif_name is None:
netif_name = netif.name
key = (device_id, netif_name)
if key in self.link_params:
link_params = self.link_params[key]
if self.start:
bw = link_params.get('bw')
delay = link_params.get('delay')
loss = link_params.get('loss')
duplicate = link_params.get('duplicate')
jitter = link_params.get('jitter')
network.linkconfig(netif, bw = bw, delay = delay, loss = loss,
duplicate = duplicate, jitter = jitter)
else:
for k, v in link_params.iteritems():
netif.setparam(k, v)
@staticmethod
def search_for_element(node, tagName, match = None):
'''\
Search the given node and all ancestors for an element named
tagName that satisfies the given matching function.
'''
while True:
for child in iterChildren(node, Node.ELEMENT_NODE):
if child.tagName == tagName and \
(match is None or match(child)):
return child
node = node.parentNode
if not node:
break
return None
@classmethod
def find_core_id(cls, node):
def match(x):
domain = x.getAttribute('domain')
return domain == 'COREID'
alias = cls.search_for_element(node, 'alias', match)
if alias:
return getChildTextTrim(alias)
return None
@classmethod
def find_point(cls, node):
return cls.search_for_element(node, 'point')
@staticmethod
def find_channel_network(channel):
p = channel.parentNode
if p and p.tagName == 'network':
return p
return None
def find_interface_network_object(self, interface):
network_id = getFirstChildTextTrimWithAttribute(interface, 'member',
'type', 'network')
if not network_id:
# support legacy notation: <interface net="netid" ...
network_id = interface.getAttribute('net')
obj = self.get_core_object(network_id)
if obj:
# the network_id should exist for ptp or wlan/emane networks
return obj
# the network should correspond to a layer-2 device if the
# network_id does not exist
channel_id = getFirstChildTextTrimWithAttribute(interface, 'member',
'type', 'channel')
if not network_id or not channel_id:
return None
network = getFirstChildWithAttribute(self.scenario, 'network',
'id', network_id)
if not network:
return None
channel = getFirstChildWithAttribute(network, 'channel',
'id', channel_id)
if not channel:
return None
device = None
for dev, if_name in self.iter_network_member_devices(channel):
if self.device_type(dev) in self.layer2_device_types:
assert not device # XXX
device = dev
if device:
obj = self.get_core_object(device.getAttribute('id'))
if obj:
return obj
return None
def set_object_position_pixel(self, obj, point):
x = float(point.getAttribute('x'))
y = float(point.getAttribute('y'))
z = point.getAttribute('z')
if z:
z = float(z)
else:
z = 0.0
# TODO: zMode is unused
# z_mode = point.getAttribute('zMode'))
if x < 0.0:
self.warn('limiting negative x position of \'%s\' to zero: %s' %
(obj.name, x))
x = 0.0
if y < 0.0:
self.warn('limiting negative y position of \'%s\' to zero: %s' %
(obj.name, y))
y = 0.0
obj.setposition(x, y, z)
def set_object_position_gps(self, obj, point):
lat = float(point.getAttribute('lat'))
lon = float(point.getAttribute('lon'))
zalt = point.getAttribute('z')
if zalt:
zalt = float(zalt)
else:
zalt = 0.0
# TODO: zMode is unused
# z_mode = point.getAttribute('zMode'))
if not self.location_refgeo_set:
# for x,y,z conversion, we need a reasonable refpt; this
# picks the first coordinates as the origin
self.session.location.setrefgeo(lat, lon, zalt)
self.location_refgeo_set = True
x, y, z = self.session.location.getxyz(lat, lon, zalt)
if x < 0.0:
self.warn('limiting negative x position of \'%s\' to zero: %s' %
(obj.name, x))
x = 0.0
if y < 0.0:
self.warn('limiting negative y position of \'%s\' to zero: %s' %
(obj.name, y))
y = 0.0
obj.setposition(x, y, z)
def set_object_position_cartesian(self, obj, point):
# TODO: review this
xm = float(point.getAttribute('x'))
ym = float(point.getAttribute('y'))
zm = point.getAttribute('z')
if zm:
zm = float(zm)
else:
zm = 0.0
# TODO: zMode is unused
# z_mode = point.getAttribute('zMode'))
if not self.location_refxyz_set:
self.session.location.refxyz = xm, ym, zm
self.location_refxyz_set = True
# need to convert meters to pixels
x = self.session.location.m2px(xm) + self.session.location.refxyz[0]
y = self.session.location.m2px(ym) + self.session.location.refxyz[1]
z = self.session.location.m2px(zm) + self.session.location.refxyz[2]
if x < 0.0:
self.warn('limiting negative x position of \'%s\' to zero: %s' %
(obj.name, x))
x = 0.0
if y < 0.0:
self.warn('limiting negative y position of \'%s\' to zero: %s' %
(obj.name, y))
y = 0.0
obj.setposition(x, y, z)
def set_object_position(self, obj, element):
'''\
Set the x,y,x position of obj from the point associated with
the given element.
'''
point = self.find_point(element)
if not point:
return False
point_type = point.getAttribute('type')
if not point_type:
msg = 'no type attribute found for point: \'%s\'' % \
point.toxml('utf-8')
self.warn(msg)
assert False # XXX for testing
return False
elif point_type == 'pixel':
self.set_object_position_pixel(obj, point)
elif point_type == 'gps':
self.set_object_position_gps(obj, point)
elif point_type == 'cart':
self.set_object_position_cartesian(obj, point)
else:
self.warn("skipping unknown point type: '%s'" % point_type)
assert False # XXX for testing
return False
if self.verbose:
msg = 'set position of %s from point element: \'%s\'' % \
(obj.name, point.toxml('utf-8'))
self.info(msg)
return True
def parse_device_service(self, service, node):
name = service.getAttribute('name')
session_service = self.session.services.getservicebyname(name)
if not session_service:
assert False # XXX for testing
return None
values = []
startup_idx = service.getAttribute('startup_idx')
if startup_idx:
values.append('startidx=%s' % startup_idx)
startup_time = service.getAttribute('start_time')
if startup_time:
values.append('starttime=%s' % startup_time)
dirs = []
for directory in iterChildrenWithName(service, 'directory'):
dirname = directory.getAttribute('name')
dirs.append(str(dirname))
if dirs:
values.append("dirs=%s" % dirs)
startup = []
shutdown = []
validate = []
for command in iterChildrenWithName(service, 'command'):
command_type = command.getAttribute('type')
command_text = getChildTextTrim(command)
if not command_text:
continue
if command_type == 'start':
startup.append(str(command_text))
elif command_type == 'stop':
shutdown.append(str(command_text))
elif command_type == 'validate':
validate.append(str(command_text))
if startup:
values.append('cmdup=%s' % startup)
if shutdown:
values.append('cmddown=%s' % shutdown)
if validate:
values.append('cmdval=%s' % validate)
filenames = []
files = []
for f in iterChildrenWithName(service, 'file'):
filename = f.getAttribute('name')
if not filename:
continue;
filenames.append(filename)
data = getChildTextTrim(f)
if data:
data = str(data)
else:
data = None
typestr = 'service:%s:%s' % (name, filename)
files.append((typestr, filename, data))
if filenames:
values.append('files=%s' % filenames)
custom = service.getAttribute('custom')
if custom and custom.lower() == 'true':
self.session.services.setcustomservice(node.objid,
session_service, values)
# NOTE: if a custom service is used, setservicefile() must be
# called after the custom service exists
for typestr, filename, data in files:
self.session.services.setservicefile(nodenum = node.objid,
type = typestr,
filename = filename,
srcname = None,
data = data)
return str(name)
def parse_device_services(self, services, node):
'''\
Use session.services manager to store service customizations
before they are added to a node.
'''
service_names = []
for service in iterChildrenWithName(services, 'service'):
name = self.parse_device_service(service, node)
if name:
service_names.append(name)
return '|'.join(service_names)
def add_device_services(self, node, device, node_type):
'''\
Add services to the given node.
'''
services = getFirstChildByTagName(device, 'CORE:services')
if services:
services_str = self.parse_device_services(services, node)
if self.verbose:
self.info('services for node \'%s\': %s' % \
(node.name, services_str))
elif node_type in self.default_services:
services_str = None # default services will be added
else:
return
self.session.services.addservicestonode(node = node,
nodetype = node_type,
services_str = services_str,
verbose = self.verbose)
def set_object_presentation(self, obj, element, node_type):
# defaults from the CORE GUI
default_icons = {
'router': 'router.gif',
'host': 'host.gif',
'PC': 'pc.gif',
'mdr': 'mdr.gif',
# 'prouter': 'router_green.gif',
# 'xen': 'xen.gif'
}
icon_set = False
for child in iterChildrenWithName(element, 'CORE:presentation'):
canvas = child.getAttribute('canvas')
if canvas:
obj.canvas = int(canvas)
icon = child.getAttribute('icon')
if icon:
icon = str(icon).replace("$CORE_DATA_DIR",
constants.CORE_DATA_DIR)
obj.icon = icon
icon_set = True
if not icon_set and node_type in default_icons:
obj.icon = default_icons[node_type]
def device_type(self, device):
if device.tagName in self.device_types:
return device.tagName
return None
def core_node_type(self, device):
# use an explicit CORE type if it exists
coretype = getFirstChildTextTrimWithAttribute(device, 'type',
'domain', 'CORE')
if coretype:
return coretype
return self.device_type(device)
def find_device_with_interface(self, interface_id):
# TODO: suport generic 'device' elements
for device in iterDescendantsWithName(self.scenario,
self.device_types):
interface = getFirstChildWithAttribute(device, 'interface',
'id', interface_id)
if interface:
if_name = interface.getAttribute('name')
return device, if_name
return None, None
def parse_layer2_device(self, device):
objid, device_name = self.get_common_attributes(device)
if self.verbose:
self.info('parsing layer-2 device: %s %s' % (device_name, objid))
try:
return self.session.obj(objid)
except KeyError:
pass
device_type = self.device_type(device)
if device_type == 'hub':
device_class = nodes.HubNode
elif device_type == 'switch':
device_class = nodes.SwitchNode
else:
self.warn('unknown layer-2 device type: \'%s\'' % device_type)
assert False # XXX for testing
return None
n = self.create_core_object(device_class, objid, device_name,
device, None)
return n
def parse_layer3_device(self, device):
objid, device_name = self.get_common_attributes(device)
if self.verbose:
self.info('parsing layer-3 device: %s %s' % (device_name, objid))
try:
return self.session.obj(objid)
except KeyError:
pass
device_cls = self.nodecls
core_node_type = self.core_node_type(device)
n = self.create_core_object(device_cls, objid, device_name,
device, core_node_type)
n.type = core_node_type
self.add_device_services(n, device, core_node_type)
for interface in iterChildrenWithName(device, 'interface'):
self.parse_interface(n, device.getAttribute('id'), interface)
return n
def parse_layer2_devices(self):
'''\
Parse all layer-2 device elements. A device can be: 'switch',
'hub'.
'''
# TODO: suport generic 'device' elements
for device in iterDescendantsWithName(self.scenario,
self.layer2_device_types):
self.parse_layer2_device(device)
def parse_layer3_devices(self):
'''\
Parse all layer-3 device elements. A device can be: 'host',
'router'.
'''
# TODO: suport generic 'device' elements
for device in iterDescendantsWithName(self.scenario,
self.layer3_device_types):
self.parse_layer3_device(device)
def parse_session_origin(self, session_config):
'''\
Parse the first origin tag and set the CoreLocation reference
point appropriately.
'''
# defaults from the CORE GUI
self.session.location.setrefgeo(47.5791667, -122.132322, 2.0)
self.session.location.refscale = 150.0
origin = getFirstChildByTagName(session_config, 'origin')
if not origin:
return
lat = origin.getAttribute('lat')
lon = origin.getAttribute('lon')
alt = origin.getAttribute('alt')
if lat and lon and alt:
self.session.location.setrefgeo(float(lat), float(lon), float(alt))
self.location_refgeo_set = True
scale100 = origin.getAttribute("scale100")
if scale100:
self.session.location.refscale = float(scale100)
point = getFirstChildTextTrimByTagName(origin, 'point')
if point:
xyz = point.split(',')
if len(xyz) == 2:
xyz.append('0.0')
if len(xyz) == 3:
self.session.location.refxyz = \
(float(xyz[0]), float(xyz[1]), float(xyz[2]))
self.location_refxyz_set = True
def parse_session_options(self, session_config):
options = getFirstChildByTagName(session_config, 'options')
if not options:
return
params = self.parse_parameter_children(options)
for name, value in params.iteritems():
if name and value:
setattr(self.session.options, str(name), str(value))
def parse_session_hooks(self, session_config):
'''\
Parse hook scripts.
'''
hooks = getFirstChildByTagName(session_config, 'hooks')
if not hooks:
return
for hook in iterChildrenWithName(hooks, 'hook'):
filename = hook.getAttribute('name')
state = hook.getAttribute('state')
data = getChildTextTrim(hook)
if data is None:
data = '' # allow for empty file
hook_type = "hook:%s" % state
self.session.sethook(hook_type, filename = str(filename),
srcname = None, data = str(data))
def parse_session_metadata(self, session_config):
metadata = getFirstChildByTagName(session_config, 'metadata')
if not metadata:
return
params = self.parse_parameter_children(metadata)
for name, value in params.iteritems():
if name and value:
self.session.metadata.additem(str(name), str(value))
def parse_session_config(self):
session_config = \
getFirstChildByTagName(self.scenario, 'CORE:sessionconfig')
if not session_config:
return
self.parse_session_origin(session_config)
self.parse_session_options(session_config)
self.parse_session_hooks(session_config)
self.parse_session_metadata(session_config)
def parse_default_services(self):
# defaults from the CORE GUI
self.default_services = {
'router': ['zebra', 'OSPFv2', 'OSPFv3', 'vtysh', 'IPForward'],
'host': ['DefaultRoute', 'SSH'],
'PC': ['DefaultRoute',],
'mdr': ['zebra', 'OSPFv3MDR', 'vtysh', 'IPForward'],
# 'prouter': ['zebra', 'OSPFv2', 'OSPFv3', 'vtysh', 'IPForward'],
# 'xen': ['zebra', 'OSPFv2', 'OSPFv3', 'vtysh', 'IPForward'],
}
default_services = \
getFirstChildByTagName(self.scenario, 'CORE:defaultservices')
if not default_services:
return
for device in iterChildrenWithName(default_services, 'device'):
device_type = device.getAttribute('type')
if not device_type:
self.warn('parse_default_services: no type attribute ' \
'found for device')
continue
services = []
for service in iterChildrenWithName(device, 'service'):
name = service.getAttribute('name')
if name:
services.append(str(name))
self.default_services[device_type] = services
# store default services for the session
for t, s in self.default_services.iteritems():
self.session.services.defaultservices[t] = s
if self.verbose:
self.info('default services for node type \'%s\' ' \
'set to: %s' % (t, s))

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,303 +0,0 @@
#
# CORE
# Copyright (c)2011-2013 the Boeing Company.
# See the LICENSE file included in this distribution.
#
# author: Jeff Ahrenholz <jeffrey.m.ahrenholz@boeing.com>
#
from core.netns import nodes
from xml.dom.minidom import Node
def addelementsfromlist(dom, parent, iterable, name, attr_name):
''' XML helper to iterate through a list and add items to parent using tags
of the given name and the item value as an attribute named attr_name.
Example: addelementsfromlist(dom, parent, ('a','b','c'), "letter", "value")
<parent>
<letter value="a"/>
<letter value="b"/>
<letter value="c"/>
</parent>
'''
for item in iterable:
element = dom.createElement(name)
element.setAttribute(attr_name, item)
parent.appendChild(element)
def addtextelementsfromlist(dom, parent, iterable, name, attrs):
''' XML helper to iterate through a list and add items to parent using tags
of the given name, attributes specified in the attrs tuple, and having the
text of the item within the tags.
Example: addtextelementsfromlist(dom, parent, ('a','b','c'), "letter",
(('show','True'),))
<parent>
<letter show="True">a</letter>
<letter show="True">b</letter>
<letter show="True">c</letter>
</parent>
'''
for item in iterable:
element = dom.createElement(name)
for k,v in attrs:
element.setAttribute(k, v)
parent.appendChild(element)
txt = dom.createTextNode(item)
element.appendChild(txt)
def addtextelementsfromtuples(dom, parent, iterable, attrs=()):
''' XML helper to iterate through a list of tuples and add items to
parent using tags named for the first tuple element,
attributes specified in the attrs tuple, and having the
text of second tuple element.
Example: addtextelementsfromtuples(dom, parent,
(('first','a'),('second','b'),('third','c')),
(('show','True'),))
<parent>
<first show="True">a</first>
<second show="True">b</second>
<third show="True">c</third>
</parent>
'''
for name, value in iterable:
element = dom.createElement(name)
for k,v in attrs:
element.setAttribute(k, v)
parent.appendChild(element)
txt = dom.createTextNode(value)
element.appendChild(txt)
def gettextelementstolist(parent):
''' XML helper to parse child text nodes from the given parent and return
a list of (key, value) tuples.
'''
r = []
for n in parent.childNodes:
if n.nodeType != Node.ELEMENT_NODE:
continue
k = str(n.nodeName)
v = '' # sometimes want None here?
for c in n.childNodes:
if c.nodeType != Node.TEXT_NODE:
continue
v = str(c.nodeValue)
break
r.append((k,v))
return r
def addparamtoparent(dom, parent, name, value):
''' XML helper to add a <param name="name" value="value"/> tag to the parent
element, when value is not None.
'''
if value is None:
return None
p = dom.createElement("param")
parent.appendChild(p)
p.setAttribute("name", name)
p.setAttribute("value", "%s" % value)
return p
def addtextparamtoparent(dom, parent, name, value):
''' XML helper to add a <param name="name">value</param> tag to the parent
element, when value is not None.
'''
if value is None:
return None
p = dom.createElement("param")
parent.appendChild(p)
p.setAttribute("name", name)
txt = dom.createTextNode(value)
p.appendChild(txt)
return p
def addparamlisttoparent(dom, parent, name, values):
''' XML helper to return a parameter list and optionally add it to the
parent element:
<paramlist name="name">
<item value="123">
<item value="456">
</paramlist>
'''
if values is None:
return None
p = dom.createElement("paramlist")
if parent:
parent.appendChild(p)
p.setAttribute("name", name)
for v in values:
item = dom.createElement("item")
item.setAttribute("value", str(v))
p.appendChild(item)
return p
def getoneelement(dom, name):
e = dom.getElementsByTagName(name)
if len(e) == 0:
return None
return e[0]
def iterDescendants(dom, max_depth = 0):
'''\
Iterate over all descendant element nodes in breadth first order.
Only consider nodes up to max_depth deep when max_depth is greater
than zero.
'''
nodes = [dom]
depth = 0
current_depth_nodes = 1
next_depth_nodes = 0
while nodes:
n = nodes.pop(0)
for child in n.childNodes:
if child.nodeType == Node.ELEMENT_NODE:
yield child
nodes.append(child)
next_depth_nodes += 1
current_depth_nodes -= 1
if current_depth_nodes == 0:
depth += 1
if max_depth > 0 and depth == max_depth:
return
current_depth_nodes = next_depth_nodes
next_depth_nodes = 0
def iterMatchingDescendants(dom, matchFunction, max_depth = 0):
'''\
Iterate over descendant elements where matchFunction(descendant)
returns true. Only consider nodes up to max_depth deep when
max_depth is greater than zero.
'''
for d in iterDescendants(dom, max_depth):
if matchFunction(d):
yield d
def iterDescendantsWithName(dom, tagName, max_depth = 0):
'''\
Iterate over descendant elements whose name is contained in
tagName (or is named tagName if tagName is a string). Only
consider nodes up to max_depth deep when max_depth is greater than
zero.
'''
if isinstance(tagName, basestring):
tagName = (tagName,)
def match(d):
return d.tagName in tagName
return iterMatchingDescendants(dom, match, max_depth)
def iterDescendantsWithAttribute(dom, tagName, attrName, attrValue,
max_depth = 0):
'''\
Iterate over descendant elements whose name is contained in
tagName (or is named tagName if tagName is a string) and have an
attribute named attrName with value attrValue. Only consider
nodes up to max_depth deep when max_depth is greater than zero.
'''
if isinstance(tagName, basestring):
tagName = (tagName,)
def match(d):
return d.tagName in tagName and \
d.getAttribute(attrName) == attrValue
return iterMatchingDescendants(dom, match, max_depth)
def iterChildren(dom, nodeType):
'''\
Iterate over all child elements of the given type.
'''
for child in dom.childNodes:
if child.nodeType == nodeType:
yield child
def gettextchild(dom):
'''\
Return the text node of the given element.
'''
for child in iterChildren(dom, Node.TEXT_NODE):
return str(child.nodeValue)
return None
def getChildTextTrim(dom):
text = gettextchild(dom)
if text:
text = text.strip()
return text
def getparamssetattrs(dom, param_names, target):
''' XML helper to get <param name="name" value="value"/> tags and set
the attribute in the target object. String type is used. Target object
attribute is unchanged if the XML attribute is not present.
'''
params = dom.getElementsByTagName("param")
for param in params:
param_name = param.getAttribute("name")
value = param.getAttribute("value")
if value is None:
continue # never reached?
if param_name in param_names:
setattr(target, param_name, str(value))
def xmltypetonodeclass(session, type):
''' Helper to convert from a type string to a class name in nodes.*.
'''
if hasattr(nodes, type):
return eval("nodes.%s" % type)
else:
return None
def iterChildrenWithName(dom, tagName):
return iterDescendantsWithName(dom, tagName, 1)
def iterChildrenWithAttribute(dom, tagName, attrName, attrValue):
return iterDescendantsWithAttribute(dom, tagName, attrName, attrValue, 1)
def getFirstChildByTagName(dom, tagName):
'''\
Return the first child element whose name is contained in tagName
(or is named tagName if tagName is a string).
'''
for child in iterChildrenWithName(dom, tagName):
return child
return None
def getFirstChildTextByTagName(dom, tagName):
'''\
Return the corresponding text of the first child element whose
name is contained in tagName (or is named tagName if tagName is a
string).
'''
child = getFirstChildByTagName(dom, tagName)
if child:
return gettextchild(child)
return None
def getFirstChildTextTrimByTagName(dom, tagName):
text = getFirstChildTextByTagName(dom, tagName)
if text:
text = text.strip()
return text
def getFirstChildWithAttribute(dom, tagName, attrName, attrValue):
'''\
Return the first child element whose name is contained in tagName
(or is named tagName if tagName is a string) that has an attribute
named attrName with value attrValue.
'''
for child in \
iterChildrenWithAttribute(dom, tagName, attrName, attrValue):
return child
return None
def getFirstChildTextWithAttribute(dom, tagName, attrName, attrValue):
'''\
Return the corresponding text of the first child element whose
name is contained in tagName (or is named tagName if tagName is a
string) that has an attribute named attrName with value attrValue.
'''
child = getFirstChildWithAttribute(dom, tagName, attrName, attrValue)
if child:
return gettextchild(child)
return None
def getFirstChildTextTrimWithAttribute(dom, tagName, attrName, attrValue):
text = getFirstChildTextWithAttribute(dom, tagName, attrName, attrValue)
if text:
text = text.strip()
return text

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,377 +0,0 @@
#
# CORE
# Copyright (c)2011-2013 the Boeing Company.
# See the LICENSE file included in this distribution.
#
# author: Jeff Ahrenholz <jeffrey.m.ahrenholz@boeing.com>
#
import os
import pwd
from core.netns import nodes
from core.api import coreapi
from xml.dom.minidom import Document
from xmlutils import *
class CoreDocumentWriter0(Document):
''' Utility class for writing a CoreSession to XML. The init method builds
an xml.dom.minidom.Document, and the writexml() method saves the XML file.
'''
def __init__(self, session):
''' Create an empty Scenario XML Document, then populate it with
objects from the given session.
'''
Document.__init__(self)
self.session = session
self.scenario = self.createElement("Scenario")
self.np = self.createElement("NetworkPlan")
self.mp = self.createElement("MotionPlan")
self.sp = self.createElement("ServicePlan")
self.meta = self.createElement("CoreMetaData")
self.appendChild(self.scenario)
self.scenario.appendChild(self.np)
self.scenario.appendChild(self.mp)
self.scenario.appendChild(self.sp)
self.scenario.appendChild(self.meta)
self.populatefromsession()
def populatefromsession(self):
self.session.emane.setup() # not during runtime?
self.addorigin()
self.adddefaultservices()
self.addnets()
self.addnodes()
self.addmetadata()
def writexml(self, filename):
self.session.info("saving session XML file %s" % filename)
f = open(filename, "w")
Document.writexml(self, writer=f, indent="", addindent=" ", newl="\n", \
encoding="UTF-8")
f.close()
if self.session.user is not None:
uid = pwd.getpwnam(self.session.user).pw_uid
gid = os.stat(self.session.sessiondir).st_gid
os.chown(filename, uid, gid)
def addnets(self):
''' Add PyCoreNet objects as NetworkDefinition XML elements.
'''
with self.session._objslock:
for net in self.session.objs():
if not isinstance(net, nodes.PyCoreNet):
continue
self.addnet(net)
def addnet(self, net):
''' Add one PyCoreNet object as a NetworkDefinition XML element.
'''
n = self.createElement("NetworkDefinition")
self.np.appendChild(n)
n.setAttribute("name", net.name)
# could use net.brname
n.setAttribute("id", "%s" % net.objid)
n.setAttribute("type", "%s" % net.__class__.__name__)
self.addnetinterfaces(n, net)
# key used with tunnel node
if hasattr(net, 'grekey') and net.grekey is not None:
n.setAttribute("key", "%s" % net.grekey)
# link parameters
for netif in net.netifs(sort=True):
self.addnetem(n, netif)
# wireless/mobility models
modelconfigs = net.session.mobility.getmodels(net)
modelconfigs += net.session.emane.getmodels(net)
self.addmodels(n, modelconfigs)
self.addposition(net)
def addnetem(self, n, netif):
''' Similar to addmodels(); used for writing netem link effects
parameters. TODO: Interface parameters should be moved to the model
construct, then this separate method shouldn't be required.
'''
params = netif.getparams()
if len(params) == 0:
return
model = self.createElement("model")
model.setAttribute("name", "netem")
model.setAttribute("netif", netif.name)
if hasattr(netif, "node") and netif.node is not None:
model.setAttribute("peer", netif.node.name)
# link between switches uses one veth interface
elif hasattr(netif, "othernet") and netif.othernet is not None:
if netif.othernet.name == n.getAttribute("name"):
model.setAttribute("peer", netif.net.name)
else:
model.setAttribute("peer", netif.othernet.name)
model.setAttribute("netif", netif.localname)
# hack used for upstream parameters for link between switches
# (see LxBrNet.linknet())
if netif.othernet.objid == int(n.getAttribute("id")):
netif.swapparams('_params_up')
params = netif.getparams()
netif.swapparams('_params_up')
has_params = False
for k, v in params:
# default netem parameters are 0 or None
if v is None or v == 0:
continue
if k == "has_netem" or k == "has_tbf":
continue
key = self.createElement(k)
key.appendChild(self.createTextNode("%s" % v))
model.appendChild(key)
has_params = True
if has_params:
n.appendChild(model)
def addmodels(self, n, configs):
''' Add models from a list of model-class, config values tuples.
'''
for (m, conf) in configs:
model = self.createElement("model")
n.appendChild(model)
model.setAttribute("name", m._name)
type = "wireless"
if m._type == coreapi.CORE_TLV_REG_MOBILITY:
type = "mobility"
model.setAttribute("type", type)
for i, k in enumerate(m.getnames()):
key = self.createElement(k)
value = conf[i]
if value is None:
value = ""
key.appendChild(self.createTextNode("%s" % value))
model.appendChild(key)
def addnodes(self):
''' Add PyCoreNode objects as node XML elements.
'''
with self.session._objslock:
for node in self.session.objs():
if not isinstance(node, nodes.PyCoreNode):
continue
self.addnode(node)
def addnode(self, node):
''' Add a PyCoreNode object as node XML elements.
'''
n = self.createElement("Node")
self.np.appendChild(n)
n.setAttribute("name", node.name)
n.setAttribute("id", "%s" % node.nodeid())
if node.type:
n.setAttribute("type", node.type)
self.addinterfaces(n, node)
self.addposition(node)
addparamtoparent(self, n, "icon", node.icon)
addparamtoparent(self, n, "canvas", node.canvas)
self.addservices(node)
def addinterfaces(self, n, node):
''' Add PyCoreNetIfs to node XML elements.
'''
for ifc in node.netifs(sort=True):
i = self.createElement("interface")
n.appendChild(i)
i.setAttribute("name", ifc.name)
netmodel = None
if ifc.net:
i.setAttribute("net", ifc.net.name)
if hasattr(ifc.net, "model"):
netmodel = ifc.net.model
if ifc.mtu and ifc.mtu != 1500:
i.setAttribute("mtu", "%s" % ifc.mtu)
# could use ifc.params, transport_type
self.addaddresses(i, ifc)
# per-interface models
if netmodel and netmodel._name[:6] == "emane_":
cfg = self.session.emane.getifcconfig(node.objid, netmodel._name,
None, ifc)
if cfg:
self.addmodels(i, ((netmodel, cfg),) )
def addnetinterfaces(self, n, net):
''' Similar to addinterfaces(), but only adds interface elements to the
supplied XML node that would not otherwise appear in the Node elements.
These are any interfaces that link two switches/hubs together.
'''
for ifc in net.netifs(sort=True):
if not hasattr(ifc, "othernet") or not ifc.othernet:
continue
i = self.createElement("interface")
n.appendChild(i)
if net.objid == ifc.net.objid:
i.setAttribute("name", ifc.localname)
i.setAttribute("net", ifc.othernet.name)
else:
i.setAttribute("name", ifc.name)
i.setAttribute("net", ifc.net.name)
def addposition(self, node):
''' Add object coordinates as location XML element.
'''
(x,y,z) = node.position.get()
if x is None or y is None:
return
# <Node name="n1">
mpn = self.createElement("Node")
mpn.setAttribute("name", node.name)
self.mp.appendChild(mpn)
# <motion type="stationary">
motion = self.createElement("motion")
motion.setAttribute("type", "stationary")
mpn.appendChild(motion)
# <point>$X$,$Y$,$Z$</point>
pt = self.createElement("point")
motion.appendChild(pt)
coordstxt = "%s,%s" % (x,y)
if z:
coordstxt += ",%s" % z
coords = self.createTextNode(coordstxt)
pt.appendChild(coords)
def addorigin(self):
''' Add origin to Motion Plan using canvas reference point.
The CoreLocation class maintains this reference point.
'''
refgeo = self.session.location.refgeo
origin = self.createElement("origin")
attrs = ("lat","lon","alt")
have_origin = False
for i in xrange(3):
if refgeo[i] is not None:
origin.setAttribute(attrs[i], str(refgeo[i]))
have_origin = True
if not have_origin:
return
if self.session.location.refscale != 1.0: # 100 pixels = refscale m
origin.setAttribute("scale100", str(self.session.location.refscale))
if self.session.location.refxyz != (0.0, 0.0, 0.0):
pt = self.createElement("point")
origin.appendChild(pt)
x,y,z = self.session.location.refxyz
coordstxt = "%s,%s" % (x,y)
if z:
coordstxt += ",%s" % z
coords = self.createTextNode(coordstxt)
pt.appendChild(coords)
self.mp.appendChild(origin)
def adddefaultservices(self):
''' Add default services and node types to the ServicePlan.
'''
for type in self.session.services.defaultservices:
defaults = self.session.services.getdefaultservices(type)
spn = self.createElement("Node")
spn.setAttribute("type", type)
self.sp.appendChild(spn)
for svc in defaults:
s = self.createElement("Service")
spn.appendChild(s)
s.setAttribute("name", str(svc._name))
def addservices(self, node):
''' Add services and their customizations to the ServicePlan.
'''
if len(node.services) == 0:
return
defaults = self.session.services.getdefaultservices(node.type)
if node.services == defaults:
return
spn = self.createElement("Node")
spn.setAttribute("name", node.name)
self.sp.appendChild(spn)
for svc in node.services:
s = self.createElement("Service")
spn.appendChild(s)
s.setAttribute("name", str(svc._name))
s.setAttribute("startup_idx", str(svc._startindex))
if svc._starttime != "":
s.setAttribute("start_time", str(svc._starttime))
# only record service names if not a customized service
if not svc._custom:
continue
s.setAttribute("custom", str(svc._custom))
addelementsfromlist(self, s, svc._dirs, "Directory", "name")
for fn in svc._configs:
if len(fn) == 0:
continue
f = self.createElement("File")
f.setAttribute("name", fn)
# all file names are added to determine when a file has been deleted
s.appendChild(f)
data = self.session.services.getservicefiledata(svc, fn)
if data is None:
# this includes only customized file contents and skips
# the auto-generated files
continue
txt = self.createTextNode(data)
f.appendChild(txt)
addtextelementsfromlist(self, s, svc._startup, "Command",
(("type","start"),))
addtextelementsfromlist(self, s, svc._shutdown, "Command",
(("type","stop"),))
addtextelementsfromlist(self, s, svc._validate, "Command",
(("type","validate"),))
def addaddresses(self, i, netif):
''' Add MAC and IP addresses to interface XML elements.
'''
if netif.hwaddr:
h = self.createElement("address")
i.appendChild(h)
h.setAttribute("type", "mac")
htxt = self.createTextNode("%s" % netif.hwaddr)
h.appendChild(htxt)
for addr in netif.addrlist:
a = self.createElement("address")
i.appendChild(a)
# a.setAttribute("type", )
atxt = self.createTextNode("%s" % addr)
a.appendChild(atxt)
def addhooks(self):
''' Add hook script XML elements to the metadata tag.
'''
hooks = self.createElement("Hooks")
for state in sorted(self.session._hooks.keys()):
for (filename, data) in self.session._hooks[state]:
hook = self.createElement("Hook")
hook.setAttribute("name", filename)
hook.setAttribute("state", str(state))
txt = self.createTextNode(data)
hook.appendChild(txt)
hooks.appendChild(hook)
if hooks.hasChildNodes():
self.meta.appendChild(hooks)
def addmetadata(self):
''' Add CORE-specific session meta-data XML elements.
'''
# options
options = self.createElement("SessionOptions")
defaults = self.session.options.getdefaultvalues()
for i, (k, v) in enumerate(self.session.options.getkeyvaluelist()):
if str(v) != str(defaults[i]):
addtextparamtoparent(self, options, k, v)
#addparamtoparent(self, options, k, v)
if options.hasChildNodes():
self.meta.appendChild(options)
# hook scripts
self.addhooks()
# meta
meta = self.createElement("MetaData")
self.meta.appendChild(meta)
for (k, v) in self.session.metadata.items():
addtextparamtoparent(self, meta, k, v)
#addparamtoparent(self, meta, k, v)

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