initial commit after bringing over cleaned up code and testing some examples
This commit is contained in:
parent
c4858e6e0d
commit
00f4ebf5a9
93 changed files with 15189 additions and 13083 deletions
|
@ -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)
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
35
daemon/core/misc/log.py
Normal 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)
|
44
daemon/core/misc/nodemaps.py
Normal file
44
daemon/core/misc/nodemaps.py
Normal 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
|
||||
}
|
41
daemon/core/misc/nodeutils.py
Normal file
41
daemon/core/misc/nodeutils.py
Normal 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))
|
|
@ -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)
|
||||
|
|
44
daemon/core/misc/structutils.py
Normal file
44
daemon/core/misc/structutils.py
Normal 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
|
|
@ -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 + ' '):
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
|
@ -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
|
|
@ -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)
|
|
@ -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))
|
|
@ -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)
|
|
@ -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
|
|
@ -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
|
|
@ -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)
|
|
@ -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
|
Loading…
Add table
Add a link
Reference in a new issue