initial import (Boeing r1752, NRL r878)
This commit is contained in:
commit
f8f46d28be
394 changed files with 99738 additions and 0 deletions
216
daemon/core/misc/LatLongUTMconversion.py
Executable file
216
daemon/core/misc/LatLongUTMconversion.py
Executable file
|
@ -0,0 +1,216 @@
|
|||
#!/usr/bin/env python
|
||||
# this file is from http://pygps.org/
|
||||
|
||||
# Lat Long - UTM, UTM - Lat Long conversions
|
||||
|
||||
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>
|
||||
|
||||
_deg2rad = pi / 180.0
|
||||
_rad2deg = 180.0 / pi
|
||||
|
||||
_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]
|
||||
]
|
||||
|
||||
#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
|
||||
|
||||
#def LLtoUTM(int ReferenceEllipsoid, const double Lat, const double Long,
|
||||
# double &UTMNorthing, double &UTMEasting, char* UTMZone)
|
||||
|
||||
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
|
||||
Lat and Long are in decimal degrees
|
||||
Written by Chuck Gantz- chuck.gantz@globalstar.com"""
|
||||
|
||||
a = _ellipsoid[ReferenceEllipsoid][_EquatorialRadius]
|
||||
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
|
||||
|
||||
LatRad = Lat*_deg2rad
|
||||
LongRad = LongTemp*_deg2rad
|
||||
|
||||
if zone is None:
|
||||
ZoneNumber = int((LongTemp + 180)/6) + 1
|
||||
else:
|
||||
ZoneNumber = zone
|
||||
|
||||
if Lat >= 56.0 and Lat < 64.0 and LongTemp >= 3.0 and LongTemp < 12.0:
|
||||
ZoneNumber = 32
|
||||
|
||||
# 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
|
||||
|
||||
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
|
||||
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)
|
||||
|
||||
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)
|
||||
+ 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)))
|
||||
|
||||
if Lat < 0:
|
||||
UTMNorthing = UTMNorthing + 10000000.0; #10000000 meter offset for southern hemisphere
|
||||
return (UTMZone, UTMEasting, UTMNorthing)
|
||||
|
||||
|
||||
def _UTMLetterDesignator(Lat):
|
||||
"""This routine determines the correct UTM letter designator for the given
|
||||
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
|
||||
|
||||
#void UTMtoLL(int ReferenceEllipsoid, const double UTMNorthing, const double UTMEasting, const char* UTMZone,
|
||||
# double& Lat, double& Long )
|
||||
|
||||
def UTMtoLL(ReferenceEllipsoid, northing, easting, zone):
|
||||
"""converts UTM coords to lat/long. Equations from USGS Bulletin 1532
|
||||
East Longitudes are positive, West longitudes are negative.
|
||||
North latitudes are positive, South latitudes are negative
|
||||
Lat and Long are in decimal degrees.
|
||||
Written by Chuck Gantz- chuck.gantz@globalstar.com
|
||||
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
|
||||
|
||||
x = easting - 500000.0 #remove 500,000 meter offset for longitude
|
||||
y = northing
|
||||
|
||||
ZoneLetter = zone[-1]
|
||||
ZoneNumber = int(zone[:-1])
|
||||
if ZoneLetter >= 'N':
|
||||
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
|
||||
|
||||
LongOrigin = (ZoneNumber - 1)*6 - 180 + 3 # +3 puts origin in middle of zone
|
||||
|
||||
eccPrimeSquared = (eccSquared)/(1-eccSquared)
|
||||
|
||||
M = y / k0
|
||||
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;
|
||||
|
||||
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 = 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 = 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)
|
||||
|
0
daemon/core/misc/__init__.py
Normal file
0
daemon/core/misc/__init__.py
Normal file
160
daemon/core/misc/event.py
Normal file
160
daemon/core/misc/event.py
Normal file
|
@ -0,0 +1,160 @@
|
|||
#
|
||||
# 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
|
||||
|
||||
class EventLoop(object):
|
||||
|
||||
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 = []
|
||||
self.eventnum = 0
|
||||
self.timer = None
|
||||
self.running = False
|
||||
self.start = None
|
||||
|
||||
def __del__(self):
|
||||
self.stop()
|
||||
|
||||
def __run_events(self):
|
||||
schedule = False
|
||||
while True:
|
||||
with self.lock:
|
||||
if not self.running or not self.queue:
|
||||
break
|
||||
now = time.time()
|
||||
if self.queue[0].time > now:
|
||||
schedule = True
|
||||
break
|
||||
event = heapq.heappop(self.queue)
|
||||
assert event.time <= now
|
||||
event.run()
|
||||
with self.lock:
|
||||
self.timer = None
|
||||
if schedule:
|
||||
self.__schedule_event()
|
||||
|
||||
def __schedule_event(self):
|
||||
with self.lock:
|
||||
assert self.running
|
||||
if not self.queue:
|
||||
return
|
||||
delay = self.queue[0].time - time.time()
|
||||
assert self.timer is None
|
||||
self.timer = threading.Timer(delay, self.__run_events)
|
||||
self.timer.daemon = True
|
||||
self.timer.start()
|
||||
|
||||
def run(self):
|
||||
with self.lock:
|
||||
if self.running:
|
||||
return
|
||||
self.running = True
|
||||
self.start = time.time()
|
||||
for event in self.queue:
|
||||
event.time += self.start
|
||||
self.__schedule_event()
|
||||
|
||||
def stop(self):
|
||||
with self.lock:
|
||||
if not self.running:
|
||||
return
|
||||
self.queue = []
|
||||
self.eventnum = 0
|
||||
if self.timer is not None:
|
||||
self.timer.cancel()
|
||||
self.timer = None
|
||||
self.running = False
|
||||
self.start = None
|
||||
|
||||
def add_event(self, delaysec, func, *args, **kwds):
|
||||
with self.lock:
|
||||
eventnum = self.eventnum
|
||||
self.eventnum += 1
|
||||
evtime = float(delaysec)
|
||||
if self.running:
|
||||
evtime += time.time()
|
||||
event = self.Event(eventnum, evtime, func, *args, **kwds)
|
||||
|
||||
if self.queue:
|
||||
prevhead = self.queue[0]
|
||||
else:
|
||||
prevhead = None
|
||||
|
||||
heapq.heappush(self.queue, event)
|
||||
head = self.queue[0]
|
||||
if prevhead is not None and prevhead != head:
|
||||
if self.timer is not None and not self.timer.is_alive():
|
||||
self.timer.cancel()
|
||||
self.timer = None
|
||||
|
||||
if self.running and self.timer is None:
|
||||
self.__schedule_event()
|
||||
return event
|
||||
|
||||
def example():
|
||||
loop = EventLoop()
|
||||
|
||||
def msg(arg):
|
||||
delta = time.time() - loop.start
|
||||
print delta, 'arg:', arg
|
||||
|
||||
def repeat(interval, count):
|
||||
count -= 1
|
||||
msg('repeat: interval: %s; remaining: %s' % (interval, count))
|
||||
if count > 0:
|
||||
loop.add_event(interval, repeat, interval, count)
|
||||
|
||||
def sleep(delay):
|
||||
msg('sleep %s' % delay)
|
||||
time.sleep(delay)
|
||||
msg('sleep done')
|
||||
|
||||
def stop(arg):
|
||||
msg(arg)
|
||||
loop.stop()
|
||||
|
||||
loop.add_event(0, msg, 'start')
|
||||
loop.add_event(0, msg, 'time zero')
|
||||
|
||||
for delay in 5, 4, 10, -1, 0, 9, 3, 7, 3.14:
|
||||
loop.add_event(delay, msg, 'time %s' % delay)
|
||||
|
||||
loop.run()
|
||||
|
||||
loop.add_event(0, repeat, 1, 5)
|
||||
loop.add_event(12, sleep, 10)
|
||||
|
||||
loop.add_event(15.75, stop, 'stop time: 15.75')
|
230
daemon/core/misc/ipaddr.py
Normal file
230
daemon/core/misc/ipaddr.py
Normal file
|
@ -0,0 +1,230 @@
|
|||
#
|
||||
# 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.
|
||||
'''
|
||||
|
||||
import socket
|
||||
import struct
|
||||
import random
|
||||
|
||||
AF_INET = socket.AF_INET
|
||||
AF_INET6 = socket.AF_INET6
|
||||
|
||||
class MacAddr(object):
|
||||
def __init__(self, addr):
|
||||
self.addr = addr
|
||||
|
||||
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
|
||||
to EUI 64 conversion process per RFC 5342.
|
||||
'''
|
||||
if not self.addr:
|
||||
return IPAddr.fromstring("::")
|
||||
tmp = struct.unpack("!Q", '\x00\x00' + self.addr)[0]
|
||||
nic = long(tmp) & 0x000000FFFFFFL
|
||||
oui = long(tmp) & 0xFFFFFF000000L
|
||||
# toggle U/L bit
|
||||
oui ^= 0x020000000000L
|
||||
# append EUI-48 octets
|
||||
oui = (oui << 16) | 0xFFFE000000L
|
||||
return IPAddr(AF_INET6, struct.pack("!QQ", 0xfe80 << 48, oui | nic))
|
||||
|
||||
@classmethod
|
||||
def fromstring(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
|
||||
tmpbytes = struct.pack("!Q", tmp)
|
||||
return cls(tmpbytes[2:])
|
||||
|
||||
class IPAddr(object):
|
||||
def __init__(self, af, addr):
|
||||
# check if (af, addr) is valid
|
||||
if not socket.inet_ntop(af, addr):
|
||||
raise ValueError, "invalid af/addr"
|
||||
self.af = af
|
||||
self.addr = addr
|
||||
|
||||
def isIPv4(self):
|
||||
return self.af == AF_INET
|
||||
|
||||
def isIPv6(self):
|
||||
return self.af == AF_INET6
|
||||
|
||||
def __str__(self):
|
||||
return socket.inet_ntop(self.af, self.addr)
|
||||
|
||||
def __eq__(self, other):
|
||||
try:
|
||||
return other.af == self.af and other.addr == self.addr
|
||||
except:
|
||||
return False
|
||||
|
||||
def __add__(self, other):
|
||||
try:
|
||||
carry = int(other)
|
||||
except:
|
||||
return NotImplemented
|
||||
tmp = map(lambda x: ord(x), self.addr)
|
||||
for i in xrange(len(tmp) - 1, -1, -1):
|
||||
x = tmp[i] + carry
|
||||
tmp[i] = x & 0xff
|
||||
carry = x >> 8
|
||||
if carry == 0:
|
||||
break
|
||||
addr = "".join(map(lambda x: chr(x), tmp))
|
||||
return self.__class__(self.af, addr)
|
||||
|
||||
def __sub__(self, other):
|
||||
try:
|
||||
tmp = -int(other)
|
||||
except:
|
||||
return NotImplemented
|
||||
return self.__add__(tmp)
|
||||
|
||||
@classmethod
|
||||
def fromstring(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])
|
||||
|
||||
class IPPrefix(object):
|
||||
def __init__(self, af, prefixstr):
|
||||
"prefixstr format: address/prefixlen"
|
||||
tmp = prefixstr.split("/")
|
||||
if len(tmp) > 2:
|
||||
raise ValueError, "invalid prefix: '%s'" % prefixstr
|
||||
self.af = af
|
||||
if self.af == AF_INET:
|
||||
self.addrlen = 32
|
||||
elif self.af == AF_INET6:
|
||||
self.addrlen = 128
|
||||
else:
|
||||
raise ValueError, "invalid address family: '%s'" % self.af
|
||||
if len(tmp) == 2:
|
||||
self.prefixlen = int(tmp[1])
|
||||
else:
|
||||
self.prefixlen = self.addrlen
|
||||
self.prefix = socket.inet_pton(self.af, tmp[0])
|
||||
if self.addrlen > self.prefixlen:
|
||||
addrbits = self.addrlen - self.prefixlen
|
||||
netmask = ((1L << self.prefixlen) - 1) << addrbits
|
||||
prefix = ""
|
||||
for i in xrange(-1, -(addrbits >> 3) - 2, -1):
|
||||
prefix = chr(ord(self.prefix[i]) & (netmask & 0xff)) + prefix
|
||||
netmask >>= 8
|
||||
self.prefix = self.prefix[:i] + prefix
|
||||
|
||||
def __str__(self):
|
||||
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
|
||||
|
||||
def __add__(self, other):
|
||||
try:
|
||||
tmp = int(other)
|
||||
except:
|
||||
return NotImplemented
|
||||
a = IPAddr(self.af, self.prefix) + \
|
||||
(tmp << (self.addrlen - self.prefixlen))
|
||||
prefixstr = "%s/%s" % (a, self.prefixlen)
|
||||
if self.__class__ == IPPrefix:
|
||||
return self.__class__(self.af, prefixstr)
|
||||
else:
|
||||
return self.__class__(prefixstr)
|
||||
|
||||
def __sub__(self, other):
|
||||
try:
|
||||
tmp = -int(other)
|
||||
except:
|
||||
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 \
|
||||
(self.af == AF_INET and tmp == (1 << (self.addrlen - self.prefixlen)) - 1):
|
||||
raise ValueError, "invalid hostid for prefix %s: %s" % (self, hostid)
|
||||
addr = ""
|
||||
for i in xrange(-1, -(self.addrlen >> 3) - 1, -1):
|
||||
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)
|
||||
|
||||
def minaddr(self):
|
||||
return self.addr(1)
|
||||
|
||||
def maxaddr(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):
|
||||
return max(0, (1 << (self.addrlen - self.prefixlen)) - 2)
|
||||
|
||||
def prefixstr(self):
|
||||
return "%s" % socket.inet_ntop(self.af, self.prefix)
|
||||
|
||||
def netmaskstr(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__()
|
||||
|
||||
class IPv4Prefix(IPPrefix):
|
||||
def __init__(self, prefixstr):
|
||||
IPPrefix.__init__(self, AF_INET, prefixstr)
|
||||
|
||||
class IPv6Prefix(IPPrefix):
|
||||
def __init__(self, prefixstr):
|
||||
IPPrefix.__init__(self, AF_INET6, prefixstr)
|
||||
|
||||
def isIPAddress(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)
|
116
daemon/core/misc/quagga.py
Normal file
116
daemon/core/misc/quagga.py
Normal file
|
@ -0,0 +1,116 @@
|
|||
#
|
||||
# CORE
|
||||
# Copyright (c)2010-2012 the Boeing Company.
|
||||
# See the LICENSE file included in this distribution.
|
||||
#
|
||||
# author: Tom Goff <thomas.goff@boeing.com>
|
||||
#
|
||||
'''
|
||||
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 = []):
|
||||
self.name = name
|
||||
self.addrlist = addrlist
|
||||
|
||||
class Conf(object):
|
||||
def __init__(self, **kwds):
|
||||
self.kwds = kwds
|
||||
|
||||
def __str__(self):
|
||||
tmp = self.template.substitute(**self.kwds)
|
||||
if tmp[-1] == '\n':
|
||||
tmp = tmp[:-1]
|
||||
return tmp
|
||||
|
||||
class QuaggaOSPF6Interface(Conf):
|
||||
AF_IPV6_ID = 0
|
||||
AF_IPV4_ID = 65
|
||||
|
||||
template = Template("""\
|
||||
interface $interface
|
||||
$addr
|
||||
ipv6 ospf6 instance-id $instanceid
|
||||
ipv6 ospf6 hello-interval 2
|
||||
ipv6 ospf6 dead-interval 11
|
||||
ipv6 ospf6 retransmit-interval 5
|
||||
ipv6 ospf6 network $network
|
||||
ipv6 ospf6 diffhellos
|
||||
ipv6 ospf6 adjacencyconnectivity uniconnected
|
||||
ipv6 ospf6 lsafullness mincostlsa
|
||||
""")
|
||||
|
||||
# ip address $ipaddr/32
|
||||
# ipv6 ospf6 simhelloLLtoULRecv :$simhelloport
|
||||
# !$ipaddr:$simhelloport
|
||||
|
||||
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
|
||||
elif x.find(":") >= 0:
|
||||
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)
|
||||
|
||||
def name(self):
|
||||
return self.netif.name
|
||||
|
||||
class QuaggaOSPF6(Conf):
|
||||
|
||||
template = Template("""\
|
||||
$interfaces
|
||||
!
|
||||
router ospf6
|
||||
router-id $routerid
|
||||
$ospfifs
|
||||
$redistribute
|
||||
""")
|
||||
|
||||
def __init__(self, ospf6ifs, area, routerid,
|
||||
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)
|
||||
|
||||
|
||||
class QuaggaConf(Conf):
|
||||
template = Template("""\
|
||||
log file $logfile
|
||||
$debugs
|
||||
!
|
||||
$routers
|
||||
!
|
||||
$forwarding
|
||||
""")
|
||||
|
||||
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)
|
228
daemon/core/misc/utils.py
Normal file
228
daemon/core/misc/utils.py
Normal file
|
@ -0,0 +1,228 @@
|
|||
#
|
||||
# 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.
|
||||
'''
|
||||
|
||||
import subprocess, os, ast
|
||||
|
||||
def checkexec(execlist):
|
||||
for bin in execlist:
|
||||
# note that os.access() uses real uid/gid; that should be okay
|
||||
# here
|
||||
if not os.access(bin, os.X_OK):
|
||||
raise EnvironmentError, "executable not found: %s" % bin
|
||||
|
||||
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,)
|
||||
|
||||
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(','):
|
||||
# r += (i.strip("' "), )
|
||||
# chop empty last element from "('a',)" strings
|
||||
#if r[-1] == '':
|
||||
# r = r[:-1]
|
||||
#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)
|
||||
|
||||
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
|
||||
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 hexdump(s, bytes_per_word = 2, words_per_line = 8):
|
||||
dump = ""
|
||||
count = 0
|
||||
bytes = bytes_per_word * words_per_line
|
||||
while s:
|
||||
line = s[:bytes]
|
||||
s = s[bytes:]
|
||||
tmp = map(lambda x: ("%02x" * bytes_per_word) % x,
|
||||
zip(*[iter(map(ord, line))] * bytes_per_word))
|
||||
if len(line) % 2:
|
||||
tmp.append("%x" % ord(line[-1]))
|
||||
dump += "0x%08x: %s\n" % (count, " ".join(tmp))
|
||||
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
|
||||
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.
|
||||
'''
|
||||
f = open(pathname, 'r')
|
||||
lines = f.readlines()
|
||||
f.close()
|
||||
start = None
|
||||
end = None
|
||||
for i in range(len(lines)):
|
||||
if lines[i] == "# BEGIN %s\n" % header:
|
||||
start = i
|
||||
elif lines[i] == "# END %s\n" % header:
|
||||
end = i + 1
|
||||
if start is None or end is None:
|
||||
return
|
||||
f = open(pathname, 'w')
|
||||
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.
|
||||
'''
|
||||
if session is not None:
|
||||
pathname = pathname.replace('~', "/home/%s" % session.user)
|
||||
pathname = pathname.replace('%SESSION%', str(session.sessionid))
|
||||
pathname = pathname.replace('%SESSION_DIR%', session.sessiondir)
|
||||
pathname = pathname.replace('%SESSION_USER%', session.user)
|
||||
if node is not 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.
|
||||
'''
|
||||
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.
|
||||
'''
|
||||
if not hasattr(dontclose, "__contains__"):
|
||||
if not isinstance(dontclose, int):
|
||||
raise TypeError, "dontclose must be an integer"
|
||||
dontclose = (int(dontclose),)
|
||||
else:
|
||||
for fd in dontclose:
|
||||
if not isinstance(fd, int):
|
||||
raise TypeError, "dontclose must contain only integers"
|
||||
# redirect stdin
|
||||
if stdin:
|
||||
fd = os.open(stdin, os.O_RDONLY)
|
||||
os.dup2(fd, 0)
|
||||
os.close(fd)
|
||||
# redirect stdout
|
||||
if stdout:
|
||||
fd = os.open(stdout, os.O_WRONLY | os.O_CREAT | os.O_APPEND,
|
||||
stdoutmode)
|
||||
os.dup2(fd, 1)
|
||||
if (stdout == stderr):
|
||||
os.dup2(1, 2)
|
||||
os.close(fd)
|
||||
# redirect stderr
|
||||
if stderr and (stderr != stdout):
|
||||
fd = os.open(stderr, os.O_WRONLY | os.O_CREAT | os.O_APPEND,
|
||||
stderrmode)
|
||||
os.dup2(fd, 2)
|
||||
os.close(fd)
|
||||
if os.fork():
|
||||
os._exit(0) # parent exits
|
||||
os.setsid()
|
||||
pid = os.fork()
|
||||
if pid:
|
||||
if pidfilename:
|
||||
try:
|
||||
f = open(pidfilename, "w")
|
||||
f.write("%s\n" % pid)
|
||||
f.close()
|
||||
except:
|
||||
pass
|
||||
os._exit(0) # parent exits
|
||||
if rootdir:
|
||||
os.chdir(rootdir)
|
||||
os.umask(umask)
|
||||
if close_fds:
|
||||
try:
|
||||
maxfd = resource.getrlimit(resource.RLIMIT_NOFILE)[1]
|
||||
if maxfd == resource.RLIM_INFINITY:
|
||||
raise ValueError
|
||||
except:
|
||||
maxfd = defaultmaxfd
|
||||
for fd in xrange(3, maxfd):
|
||||
if fd in dontclose:
|
||||
continue
|
||||
try:
|
||||
os.close(fd)
|
||||
except:
|
||||
pass
|
||||
|
||||
def readfileintodict(filename, d):
|
||||
''' 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:
|
||||
if l[:1] == '#':
|
||||
continue
|
||||
try:
|
||||
key, value = l.split('=', 1)
|
||||
d[key] = value.strip()
|
||||
except ValueError:
|
||||
pass
|
259
daemon/core/misc/utm.py
Normal file
259
daemon/core/misc/utm.py
Normal file
|
@ -0,0 +1,259 @@
|
|||
"""
|
||||
utm
|
||||
===
|
||||
|
||||
.. image:: https://travis-ci.org/Turbo87/utm.png
|
||||
|
||||
Bidirectional UTM-WGS84 converter for python
|
||||
|
||||
Usage
|
||||
-----
|
||||
|
||||
::
|
||||
|
||||
import utm
|
||||
|
||||
Convert a (latitude, longitude) tuple into an UTM coordinate::
|
||||
|
||||
utm.from_latlon(51.2, 7.5)
|
||||
>>> (395201.3103811303, 5673135.241182375, 32, 'U')
|
||||
|
||||
Convert an UTM coordinate into a (latitude, longitude) tuple::
|
||||
|
||||
utm.to_latlon(340000, 5710000, 32, 'U')
|
||||
>>> (51.51852098408468, 6.693872395145327)
|
||||
|
||||
Speed
|
||||
-----
|
||||
|
||||
The library has been compared to the more generic pyproj library by running the
|
||||
unit test suite through pyproj instead of utm. These are the results:
|
||||
|
||||
* with pyproj (without projection cache): 4.0 - 4.5 sec
|
||||
* with pyproj (with projection cache): 0.9 - 1.0 sec
|
||||
* with utm: 0.4 - 0.5 sec
|
||||
|
||||
Authors
|
||||
-------
|
||||
|
||||
* Tobias Bieniek <Tobias.Bieniek@gmx.de>
|
||||
|
||||
License
|
||||
-------
|
||||
|
||||
Copyright (C) 2012 Tobias Bieniek <Tobias.Bieniek@gmx.de>
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy of
|
||||
this software and associated documentation files (the "Software"), to deal in
|
||||
the Software without restriction, including without limitation the rights to
|
||||
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
|
||||
of the Software, and to permit persons to whom the Software is furnished to do
|
||||
so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
"""
|
||||
|
||||
import math
|
||||
|
||||
__all__ = ['to_latlon', 'from_latlon']
|
||||
|
||||
class OutOfRangeError(ValueError):
|
||||
pass
|
||||
|
||||
|
||||
K0 = 0.9996
|
||||
|
||||
E = 0.00669438
|
||||
E2 = E * E
|
||||
E3 = E2 * E
|
||||
E_P2 = E / (1.0 - E)
|
||||
|
||||
SQRT_E = math.sqrt(1 - E)
|
||||
_E = (1 - SQRT_E) / (1 + SQRT_E)
|
||||
_E3 = _E * _E * _E
|
||||
_E4 = _E3 * _E
|
||||
|
||||
M1 = (1 - E / 4 - 3 * E2 / 64 - 5 * E3 / 256)
|
||||
M2 = (3 * E / 8 + 3 * E2 / 32 + 45 * E3 / 1024)
|
||||
M3 = (15 * E2 / 256 + 45 * E3 / 1024)
|
||||
M4 = (35 * E3 / 3072)
|
||||
|
||||
P2 = (3 * _E / 2 - 27 * _E3 / 32)
|
||||
P3 = (21 * _E3 / 16 - 55 * _E4 / 32)
|
||||
P4 = (151 * _E3 / 96)
|
||||
|
||||
R = 6378137
|
||||
|
||||
ZONE_LETTERS = [
|
||||
(84, None), (72, 'X'), (64, 'W'), (56, 'V'), (48, 'U'), (40, 'T'),
|
||||
(32, 'S'), (24, 'R'), (16, 'Q'), (8, 'P'), (0, 'N'), (-8, 'M'), (-16, 'L'),
|
||||
(-24, 'K'), (-32, 'J'), (-40, 'H'), (-48, 'G'), (-56, 'F'), (-64, 'E'),
|
||||
(-72, 'D'), (-80, 'C')
|
||||
]
|
||||
|
||||
|
||||
def to_latlon(easting, northing, zone_number, zone_letter):
|
||||
zone_letter = zone_letter.upper()
|
||||
|
||||
if not 100000 <= easting < 1000000:
|
||||
raise OutOfRangeError('easting out of range (must be between 100.000 m and 999.999 m)')
|
||||
if not 0 <= northing <= 10000000:
|
||||
raise OutOfRangeError('northing out of range (must be between 0 m and 10.000.000 m)')
|
||||
if not 1 <= zone_number <= 60:
|
||||
raise OutOfRangeError('zone number out of range (must be between 1 and 60)')
|
||||
if not 'C' <= zone_letter <= 'X' or zone_letter in ['I', 'O']:
|
||||
raise OutOfRangeError('zone letter out of range (must be between C and X)')
|
||||
|
||||
x = easting - 500000
|
||||
y = northing
|
||||
|
||||
if zone_letter < 'N':
|
||||
y -= 10000000
|
||||
|
||||
m = y / K0
|
||||
mu = m / (R * M1)
|
||||
|
||||
p_rad = (mu + P2 * math.sin(2 * mu) + P3 * math.sin(4 * mu) + P4 * math.sin(6 * mu))
|
||||
|
||||
p_sin = math.sin(p_rad)
|
||||
p_sin2 = p_sin * p_sin
|
||||
|
||||
p_cos = math.cos(p_rad)
|
||||
|
||||
p_tan = p_sin / p_cos
|
||||
p_tan2 = p_tan * p_tan
|
||||
p_tan4 = p_tan2 * p_tan2
|
||||
|
||||
ep_sin = 1 - E * p_sin2
|
||||
ep_sin_sqrt = math.sqrt(1 - E * p_sin2)
|
||||
|
||||
n = R / ep_sin_sqrt
|
||||
r = (1 - E) / ep_sin
|
||||
|
||||
c = _E * p_cos**2
|
||||
c2 = c * c
|
||||
|
||||
d = x / (n * K0)
|
||||
d2 = d * d
|
||||
d3 = d2 * d
|
||||
d4 = d3 * d
|
||||
d5 = d4 * d
|
||||
d6 = d5 * d
|
||||
|
||||
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))
|
||||
|
||||
longitude = (d -
|
||||
d3 / 6 * (1 + 2 * p_tan2 + c) +
|
||||
d5 / 120 * (5 - 2 * c + 28 * p_tan2 - 3 * c2 + 8 * E_P2 + 24 * p_tan4)) / p_cos
|
||||
|
||||
return (math.degrees(latitude),
|
||||
math.degrees(longitude) + zone_number_to_central_longitude(zone_number))
|
||||
|
||||
|
||||
def from_latlon(latitude, longitude):
|
||||
if not -80.0 <= latitude <= 84.0:
|
||||
raise OutOfRangeError('latitude out of range (must be between 80 deg S and 84 deg N)')
|
||||
if not -180.0 <= longitude <= 180.0:
|
||||
raise OutOfRangeError('northing out of range (must be between 180 deg W and 180 deg E)')
|
||||
|
||||
lat_rad = math.radians(latitude)
|
||||
lat_sin = math.sin(lat_rad)
|
||||
lat_cos = math.cos(lat_rad)
|
||||
|
||||
lat_tan = lat_sin / lat_cos
|
||||
lat_tan2 = lat_tan * lat_tan
|
||||
lat_tan4 = lat_tan2 * lat_tan2
|
||||
|
||||
lon_rad = math.radians(longitude)
|
||||
|
||||
zone_number = latlon_to_zone_number(latitude, longitude)
|
||||
central_lon = zone_number_to_central_longitude(zone_number)
|
||||
central_lon_rad = math.radians(central_lon)
|
||||
|
||||
zone_letter = latitude_to_zone_letter(latitude)
|
||||
|
||||
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
|
||||
a3 = a2 * a
|
||||
a4 = a3 * a
|
||||
a5 = a4 * a
|
||||
a6 = a5 * a
|
||||
|
||||
m = R * (M1 * lat_rad -
|
||||
M2 * math.sin(2 * lat_rad) +
|
||||
M3 * math.sin(4 * lat_rad) -
|
||||
M4 * math.sin(6 * lat_rad))
|
||||
|
||||
easting = K0 * n * (a +
|
||||
a3 / 6 * (1 - lat_tan2 + c) +
|
||||
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) +
|
||||
a6 / 720 * (61 - 58 * lat_tan2 + lat_tan4 + 600 * c - 330 * E_P2)))
|
||||
|
||||
if latitude < 0:
|
||||
northing += 10000000
|
||||
|
||||
return easting, northing, zone_number, zone_letter
|
||||
|
||||
|
||||
def latitude_to_zone_letter(latitude):
|
||||
for lat_min, zone_letter in ZONE_LETTERS:
|
||||
if latitude >= lat_min:
|
||||
return zone_letter
|
||||
|
||||
return None
|
||||
|
||||
|
||||
def latlon_to_zone_number(latitude, longitude):
|
||||
if 56 <= latitude <= 64 and 3 <= longitude <= 12:
|
||||
return 32
|
||||
|
||||
if 72 <= latitude <= 84 and longitude >= 0:
|
||||
if longitude <= 9:
|
||||
return 31
|
||||
elif longitude <= 21:
|
||||
return 33
|
||||
elif longitude <= 33:
|
||||
return 35
|
||||
elif longitude <= 42:
|
||||
return 37
|
||||
|
||||
return int((longitude + 180) / 6) + 1
|
||||
|
||||
|
||||
def zone_number_to_central_longitude(zone_number):
|
||||
return (zone_number - 1) * 6 - 180 + 3
|
||||
|
||||
|
||||
def haversine(lon1, lat1, lon2, lat2):
|
||||
"""
|
||||
Calculate the great circle distance between two points
|
||||
on the earth (specified in decimal degrees)
|
||||
"""
|
||||
# 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))
|
||||
m = 6367000 * c
|
||||
return m
|
||||
|
776
daemon/core/misc/xmlutils.py
Normal file
776
daemon/core/misc/xmlutils.py
Normal file
|
@ -0,0 +1,776 @@
|
|||
#
|
||||
# CORE
|
||||
# Copyright (c)2011-2013 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, pwd
|
||||
from xml.dom.minidom import parse, Document, Node
|
||||
from core import pycore
|
||||
from core.api import coreapi
|
||||
|
||||
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.
|
||||
'''
|
||||
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 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 getoneelement(dom, name):
|
||||
e = dom.getElementsByTagName(name)
|
||||
if len(e) == 0:
|
||||
return None
|
||||
return e[0]
|
||||
|
||||
def gettextchild(dom):
|
||||
# this could be improved to skip XML comments
|
||||
child = dom.firstChild
|
||||
if child is not None and child.nodeType == Node.TEXT_NODE:
|
||||
return str(child.nodeValue)
|
||||
return None
|
||||
|
||||
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 pycore.nodes.*.
|
||||
'''
|
||||
if hasattr(pycore.nodes, type):
|
||||
return eval("pycore.nodes.%s" % type)
|
||||
else:
|
||||
return None
|
||||
|
||||
class CoreDocumentParser(object):
|
||||
def __init__(self, session, filename):
|
||||
self.session = session
|
||||
self.verbose = self.session.getcfgitembool('verbose', False)
|
||||
self.filename = filename
|
||||
self.dom = parse(filename)
|
||||
|
||||
#self.scenario = getoneelement(self.dom, "Scenario")
|
||||
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.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 = False)
|
||||
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"))
|
||||
linkednets.append((n, netid))
|
||||
self.parsemodels(net, n)
|
||||
# link networks together now that they all have been parsed
|
||||
for (n, netid) in linkednets:
|
||||
try:
|
||||
n2 = n.session.objbyname(netid)
|
||||
except KeyError:
|
||||
n.warn("skipping net %s interface: unknown net %s" % \
|
||||
(n.name, netid))
|
||||
continue
|
||||
n.linknet(n2)
|
||||
|
||||
def parsenodes(self):
|
||||
for node in self.np.getElementsByTagName("Node"):
|
||||
id, name, type = self.getcommonattributes(node)
|
||||
if type == "rj45":
|
||||
nodecls = pycore.nodes.RJ45Node
|
||||
else:
|
||||
nodecls = pycore.nodes.CoreNode
|
||||
n = self.session.addobj(cls = nodecls, objid = id, name = name,
|
||||
start = False)
|
||||
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 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")
|
||||
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
|
||||
# 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)
|
||||
|
||||
|
||||
class CoreDocumentWriter(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.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, pycore.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.
|
||||
'''
|
||||
if not hasattr(netif, "node") or netif.node is None:
|
||||
return
|
||||
params = netif.getparams()
|
||||
if len(params) == 0:
|
||||
return
|
||||
model = self.createElement("model")
|
||||
model.setAttribute("name", "netem")
|
||||
model.setAttribute("netif", netif.name)
|
||||
model.setAttribute("peer", netif.node.name)
|
||||
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, pycore.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
|
||||
if net.objid == ifc.net.objid:
|
||||
continue
|
||||
i = self.createElement("interface")
|
||||
n.appendChild(i)
|
||||
i.setAttribute("name", ifc.name)
|
||||
if ifc.net:
|
||||
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 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)
|
||||
|
||||
def opensessionxml(session, filename):
|
||||
''' Import a session from the EmulationScript XML format.
|
||||
'''
|
||||
doc = CoreDocumentParser(session, filename)
|
||||
|
||||
def savesessionxml(session, filename):
|
||||
''' Export a session to the EmulationScript XML format.
|
||||
'''
|
||||
doc = CoreDocumentWriter(session)
|
||||
doc.writexml(filename)
|
||||
|
Loading…
Add table
Add a link
Reference in a new issue