pass to help flesh out documentation for core.misc
This commit is contained in:
parent
8ade6f4f02
commit
3f82c980de
8 changed files with 662 additions and 116 deletions
|
@ -17,19 +17,41 @@ class Timer(threading.Thread):
|
|||
already running.
|
||||
"""
|
||||
|
||||
def __init__(self, interval, function, args=[], kwargs={}):
|
||||
def __init__(self, interval, function, args=None, kwargs=None):
|
||||
"""
|
||||
Create a Timer instance.
|
||||
|
||||
:param interval: time interval
|
||||
:param function: function to call when timer finishes
|
||||
:param args: function arguments
|
||||
:param kwargs: function keyword arguments
|
||||
"""
|
||||
super(Timer, self).__init__()
|
||||
self.interval = interval
|
||||
self.function = function
|
||||
self.args = args
|
||||
self.kwargs = kwargs
|
||||
|
||||
self.finished = threading.Event()
|
||||
self._running = threading.Lock()
|
||||
|
||||
# validate arguments were provided
|
||||
if args:
|
||||
self.args = args
|
||||
else:
|
||||
self.args = []
|
||||
|
||||
# validate keyword arguments were provided
|
||||
if kwargs:
|
||||
self.kwargs = kwargs
|
||||
else:
|
||||
self.kwargs = {}
|
||||
|
||||
def cancel(self):
|
||||
"""
|
||||
Stop the timer if it hasn't finished yet. Return False if
|
||||
the timer was already running.
|
||||
|
||||
:return: True if canceled, False otherwise
|
||||
:rtype: bool
|
||||
"""
|
||||
locked = self._running.acquire(False)
|
||||
if locked:
|
||||
|
@ -38,6 +60,11 @@ class Timer(threading.Thread):
|
|||
return locked
|
||||
|
||||
def run(self):
|
||||
"""
|
||||
Run the timer.
|
||||
|
||||
:return: nothing
|
||||
"""
|
||||
self.finished.wait(self.interval)
|
||||
with self._running:
|
||||
if not self.finished.is_set():
|
||||
|
@ -46,7 +73,20 @@ class Timer(threading.Thread):
|
|||
|
||||
|
||||
class Event(object):
|
||||
"""
|
||||
Provides event objects that can be used within the EventLoop class.
|
||||
"""
|
||||
|
||||
def __init__(self, eventnum, event_time, func, *args, **kwds):
|
||||
"""
|
||||
Create an Event instance.
|
||||
|
||||
:param eventnum: event number
|
||||
:param event_time: event time
|
||||
:param func: event function
|
||||
:param args: function arguments
|
||||
:param kwds: function keyword arguments
|
||||
"""
|
||||
self.eventnum = eventnum
|
||||
self.time = event_time
|
||||
self.func = func
|
||||
|
@ -55,23 +95,47 @@ class Event(object):
|
|||
self.canceled = False
|
||||
|
||||
def __cmp__(self, other):
|
||||
"""
|
||||
Comparison function.
|
||||
|
||||
:param Event other: event to compare with
|
||||
:return: comparison result
|
||||
:rtype: int
|
||||
"""
|
||||
tmp = cmp(self.time, other.time)
|
||||
if tmp == 0:
|
||||
tmp = cmp(self.eventnum, other.eventnum)
|
||||
return tmp
|
||||
|
||||
def run(self):
|
||||
"""
|
||||
Run an event.
|
||||
|
||||
:return: nothing
|
||||
"""
|
||||
if self.canceled:
|
||||
return
|
||||
self.func(*self.args, **self.kwds)
|
||||
|
||||
def cancel(self):
|
||||
"""
|
||||
Cancel event.
|
||||
|
||||
:return: nothing
|
||||
"""
|
||||
# XXX not thread-safe
|
||||
self.canceled = True
|
||||
|
||||
|
||||
class EventLoop(object):
|
||||
"""
|
||||
Provides an event loop for running events.
|
||||
"""
|
||||
|
||||
def __init__(self):
|
||||
"""
|
||||
Creates a EventLoop instance.
|
||||
"""
|
||||
self.lock = threading.RLock()
|
||||
self.queue = []
|
||||
self.eventnum = 0
|
||||
|
@ -80,6 +144,11 @@ class EventLoop(object):
|
|||
self.start = None
|
||||
|
||||
def __run_events(self):
|
||||
"""
|
||||
Run events.
|
||||
|
||||
:return: nothing
|
||||
"""
|
||||
schedule = False
|
||||
while True:
|
||||
with self.lock:
|
||||
|
@ -92,12 +161,18 @@ class EventLoop(object):
|
|||
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):
|
||||
"""
|
||||
Schedule event.
|
||||
|
||||
:return: nothing
|
||||
"""
|
||||
with self.lock:
|
||||
assert self.running
|
||||
if not self.queue:
|
||||
|
@ -109,6 +184,11 @@ class EventLoop(object):
|
|||
self.timer.start()
|
||||
|
||||
def run(self):
|
||||
"""
|
||||
Start event loop.
|
||||
|
||||
:return: nothing
|
||||
"""
|
||||
with self.lock:
|
||||
if self.running:
|
||||
return
|
||||
|
@ -119,6 +199,11 @@ class EventLoop(object):
|
|||
self.__schedule_event()
|
||||
|
||||
def stop(self):
|
||||
"""
|
||||
Stop event loop.
|
||||
|
||||
:return: nothing
|
||||
"""
|
||||
with self.lock:
|
||||
if not self.running:
|
||||
return
|
||||
|
@ -131,6 +216,16 @@ class EventLoop(object):
|
|||
self.start = None
|
||||
|
||||
def add_event(self, delaysec, func, *args, **kwds):
|
||||
"""
|
||||
Add an event to the event loop.
|
||||
|
||||
:param int delaysec: delay in seconds for event
|
||||
:param func: event function
|
||||
:param args: event arguments
|
||||
:param kwds: event keyword arguments
|
||||
:return: created event
|
||||
:rtype: Event
|
||||
"""
|
||||
with self.lock:
|
||||
eventnum = self.eventnum
|
||||
self.eventnum += 1
|
||||
|
@ -154,6 +249,7 @@ class EventLoop(object):
|
|||
return event
|
||||
|
||||
|
||||
# TODO: move example to documentation
|
||||
def example():
|
||||
loop = EventLoop()
|
||||
|
||||
|
|
|
@ -14,10 +14,25 @@ logger = log.get_logger(__name__)
|
|||
|
||||
|
||||
class MacAddress(object):
|
||||
"""
|
||||
Provides mac address utilities for use within core.
|
||||
"""
|
||||
|
||||
def __init__(self, address):
|
||||
"""
|
||||
Creates a MacAddress instance.
|
||||
|
||||
:param str address: mac address
|
||||
"""
|
||||
self.addr = address
|
||||
|
||||
def __str__(self):
|
||||
"""
|
||||
Create a string representation of a MacAddress.
|
||||
|
||||
:return: string representation
|
||||
:rtype: str
|
||||
"""
|
||||
return ":".join(map(lambda x: "%02x" % ord(x), self.addr))
|
||||
|
||||
def to_link_local(self):
|
||||
|
@ -41,42 +56,100 @@ class MacAddress(object):
|
|||
|
||||
@classmethod
|
||||
def from_string(cls, s):
|
||||
"""
|
||||
Create a mac address object from a string.
|
||||
|
||||
:param s: string representation of a mac address
|
||||
:return: mac address class
|
||||
:rtype: MacAddress
|
||||
"""
|
||||
addr = "".join(map(lambda x: chr(int(x, 16)), s.split(":")))
|
||||
return cls(addr)
|
||||
|
||||
@classmethod
|
||||
def random(cls):
|
||||
"""
|
||||
Create a random mac address.
|
||||
|
||||
:return: random mac address
|
||||
:rtype: MacAddress
|
||||
"""
|
||||
tmp = random.randint(0, 0xFFFFFF)
|
||||
tmp |= 0x00163E << 24 # use the Xen OID 00:16:3E
|
||||
# use the Xen OID 00:16:3E
|
||||
tmp |= 0x00163E << 24
|
||||
tmpbytes = struct.pack("!Q", tmp)
|
||||
return cls(tmpbytes[2:])
|
||||
|
||||
|
||||
class IpAddress(object):
|
||||
"""
|
||||
Provides ip utilities and functionality for use within core.
|
||||
"""
|
||||
|
||||
def __init__(self, af, address):
|
||||
"""
|
||||
Create a IpAddress instance.
|
||||
|
||||
:param int af: address family
|
||||
:param str address: ip address
|
||||
:return:
|
||||
"""
|
||||
# check if (af, addr) is valid
|
||||
if not socket.inet_ntop(af, address):
|
||||
raise ValueError, "invalid af/addr"
|
||||
raise ValueError("invalid af/addr")
|
||||
self.af = af
|
||||
self.addr = address
|
||||
|
||||
def is_ipv4(self):
|
||||
"""
|
||||
Checks if this is an ipv4 address.
|
||||
|
||||
:return: True if ipv4 address, False otherwise
|
||||
:rtype: bool
|
||||
"""
|
||||
return self.af == AF_INET
|
||||
|
||||
def is_ipv6(self):
|
||||
"""
|
||||
Checks if this is an ipv6 address.
|
||||
|
||||
:return: True if ipv6 address, False otherwise
|
||||
:rtype: bool
|
||||
"""
|
||||
return self.af == AF_INET6
|
||||
|
||||
def __str__(self):
|
||||
"""
|
||||
Create a string representation of this address.
|
||||
|
||||
:return: string representation of address
|
||||
:rtype: str
|
||||
"""
|
||||
return socket.inet_ntop(self.af, self.addr)
|
||||
|
||||
def __eq__(self, other):
|
||||
try:
|
||||
return other.af == self.af and other.addr == self.addr
|
||||
except AttributeError:
|
||||
logger.exception("error during equals compare")
|
||||
"""
|
||||
Checks for equality with another ip address.
|
||||
|
||||
:param IpAddress other: other ip address to check equality with
|
||||
:return: True is the other IpAddress is equal, False otherwise
|
||||
:rtype: bool
|
||||
"""
|
||||
if not isinstance(other, IpAddress):
|
||||
return False
|
||||
elif self is other:
|
||||
return True
|
||||
else:
|
||||
return other.af == self.af and other.addr == self.addr
|
||||
|
||||
def __add__(self, other):
|
||||
"""
|
||||
Add value to ip addresses.
|
||||
|
||||
:param int other: value to add to ip address
|
||||
:return: added together ip address instance
|
||||
:rtype: IpAddress
|
||||
"""
|
||||
try:
|
||||
carry = int(other)
|
||||
except ValueError:
|
||||
|
@ -94,40 +167,68 @@ class IpAddress(object):
|
|||
return self.__class__(self.af, addr)
|
||||
|
||||
def __sub__(self, other):
|
||||
"""
|
||||
Subtract value from ip address.
|
||||
|
||||
:param int other: value to subtract from ip address
|
||||
:return:
|
||||
"""
|
||||
try:
|
||||
tmp = -int(other)
|
||||
except ValueError:
|
||||
logger.exception("error during subtraction")
|
||||
return NotImplemented
|
||||
|
||||
return self.__add__(tmp)
|
||||
|
||||
@classmethod
|
||||
def from_string(cls, s):
|
||||
"""
|
||||
Create a ip address from a string representation.
|
||||
|
||||
:param s: string representation to create ip address from
|
||||
:return: ip address instance
|
||||
:rtype: IpAddress
|
||||
"""
|
||||
for af in AF_INET, AF_INET6:
|
||||
return cls(af, socket.inet_pton(af, s))
|
||||
|
||||
@staticmethod
|
||||
def to_int(s):
|
||||
"""
|
||||
convert IPv4 string to 32-bit integer
|
||||
Convert IPv4 string to integer
|
||||
|
||||
:param s: string to convert to 32-bit integer
|
||||
:return: integer value
|
||||
:rtype: int
|
||||
"""
|
||||
bin = socket.inet_pton(AF_INET, s)
|
||||
return struct.unpack('!I', bin)[0]
|
||||
|
||||
|
||||
class IpPrefix(object):
|
||||
"""
|
||||
Provides ip address generation and prefix utilities.
|
||||
"""
|
||||
|
||||
def __init__(self, af, prefixstr):
|
||||
"prefixstr format: address/prefixlen"
|
||||
"""
|
||||
Create a IpPrefix instance.
|
||||
|
||||
:param int af: address family for ip prefix
|
||||
:param prefixstr: ip prefix string
|
||||
"""
|
||||
# prefixstr format: address/prefixlen
|
||||
tmp = prefixstr.split("/")
|
||||
if len(tmp) > 2:
|
||||
raise ValueError, "invalid prefix: '%s'" % prefixstr
|
||||
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
|
||||
raise ValueError("invalid address family: '%s'" % self.af)
|
||||
if len(tmp) == 2:
|
||||
self.prefixlen = int(tmp[1])
|
||||
else:
|
||||
|
@ -143,12 +244,37 @@ class IpPrefix(object):
|
|||
self.prefix = self.prefix[:i] + prefix
|
||||
|
||||
def __str__(self):
|
||||
"""
|
||||
String representation of an ip prefix.
|
||||
|
||||
:return: string representation
|
||||
:rtype: str
|
||||
"""
|
||||
return "%s/%s" % (socket.inet_ntop(self.af, self.prefix), self.prefixlen)
|
||||
|
||||
def __eq__(self, other):
|
||||
return other.af == self.af and other.prefixlen == self.prefixlen and other.prefix == self.prefix
|
||||
"""
|
||||
Compare equality with another ip prefix.
|
||||
|
||||
:param IpPrefix other: other ip prefix to compare with
|
||||
:return: True is equal, False otherwise
|
||||
:rtype: bool
|
||||
"""
|
||||
if not isinstance(other, IpPrefix):
|
||||
return False
|
||||
elif self is other:
|
||||
return True
|
||||
else:
|
||||
return other.af == self.af and other.prefixlen == self.prefixlen and other.prefix == self.prefix
|
||||
|
||||
def __add__(self, other):
|
||||
"""
|
||||
Add a value to this ip prefix.
|
||||
|
||||
:param int other: value to add
|
||||
:return: added ip prefix instance
|
||||
:rtype: IpPrefix
|
||||
"""
|
||||
try:
|
||||
tmp = int(other)
|
||||
except ValueError:
|
||||
|
@ -163,6 +289,13 @@ class IpPrefix(object):
|
|||
return self.__class__(prefixstr)
|
||||
|
||||
def __sub__(self, other):
|
||||
"""
|
||||
Subtract value from this ip prefix.
|
||||
|
||||
:param int other: value to subtract
|
||||
:return: subtracted ip prefix instance
|
||||
:rtype: IpPrefix
|
||||
"""
|
||||
try:
|
||||
tmp = -int(other)
|
||||
except ValueError:
|
||||
|
@ -172,11 +305,19 @@ class IpPrefix(object):
|
|||
return self.__add__(tmp)
|
||||
|
||||
def addr(self, hostid):
|
||||
"""
|
||||
Create an ip address for a given host id.
|
||||
|
||||
:param hostid: host id for an ip address
|
||||
:return: ip address
|
||||
:rtype: IpAddress
|
||||
"""
|
||||
tmp = int(hostid)
|
||||
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):
|
||||
|
||||
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 = ""
|
||||
|
@ -191,21 +332,51 @@ class IpPrefix(object):
|
|||
return IpAddress(self.af, addr)
|
||||
|
||||
def min_addr(self):
|
||||
"""
|
||||
Return the minimum ip address for this prefix.
|
||||
|
||||
:return: minimum ip address
|
||||
:rtype: IpAddress
|
||||
"""
|
||||
return self.addr(1)
|
||||
|
||||
def max_addr(self):
|
||||
"""
|
||||
Return the maximum ip address for this prefix.
|
||||
|
||||
:return: maximum ip address
|
||||
:rtype: IpAddress
|
||||
"""
|
||||
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 num_addr(self):
|
||||
"""
|
||||
Retrieve the number of ip addresses for this prefix.
|
||||
|
||||
:return: maximum number of ip addresses
|
||||
:rtype: int
|
||||
"""
|
||||
return max(0, (1 << (self.addrlen - self.prefixlen)) - 2)
|
||||
|
||||
def prefix_str(self):
|
||||
"""
|
||||
Retrieve the prefix string for this ip address.
|
||||
|
||||
:return: prefix string
|
||||
:rtype: str
|
||||
"""
|
||||
return "%s" % socket.inet_ntop(self.af, self.prefix)
|
||||
|
||||
def netmask_str(self):
|
||||
"""
|
||||
Retrieve the netmask string for this ip address.
|
||||
|
||||
:return: netmask string
|
||||
:rtype: str
|
||||
"""
|
||||
addrbits = self.addrlen - self.prefixlen
|
||||
netmask = ((1L << self.prefixlen) - 1) << addrbits
|
||||
netmaskbytes = struct.pack("!L", netmask)
|
||||
|
@ -213,26 +384,66 @@ class IpPrefix(object):
|
|||
|
||||
|
||||
class Ipv4Prefix(IpPrefix):
|
||||
"""
|
||||
Provides an ipv4 specific class for ip prefixes.
|
||||
"""
|
||||
|
||||
def __init__(self, prefixstr):
|
||||
"""
|
||||
Create a Ipv4Prefix instance.
|
||||
|
||||
:param str prefixstr: ip prefix
|
||||
"""
|
||||
IpPrefix.__init__(self, AF_INET, prefixstr)
|
||||
|
||||
|
||||
class Ipv6Prefix(IpPrefix):
|
||||
"""
|
||||
Provides an ipv6 specific class for ip prefixes.
|
||||
"""
|
||||
|
||||
def __init__(self, prefixstr):
|
||||
"""
|
||||
Create a Ipv6Prefix instance.
|
||||
|
||||
:param str prefixstr: ip prefix
|
||||
"""
|
||||
IpPrefix.__init__(self, AF_INET6, prefixstr)
|
||||
|
||||
|
||||
def is_ip_address(af, addrstr):
|
||||
"""
|
||||
Check if ip address string is a valid ip address.
|
||||
|
||||
:param int af: address family
|
||||
:param str addrstr: ip address string
|
||||
:return: True if a valid ip address, False otherwise
|
||||
:rtype: bool
|
||||
"""
|
||||
try:
|
||||
tmp = socket.inet_pton(af, addrstr)
|
||||
socket.inet_pton(af, addrstr)
|
||||
return True
|
||||
except:
|
||||
except IOError:
|
||||
return False
|
||||
|
||||
|
||||
def is_ipv4_address(addrstr):
|
||||
"""
|
||||
Check if ipv4 address string is a valid ipv4 address.
|
||||
|
||||
:param str addrstr: ipv4 address string
|
||||
:return: True if a valid ipv4 address, False otherwise
|
||||
:rtype: bool
|
||||
"""
|
||||
return is_ip_address(AF_INET, addrstr)
|
||||
|
||||
|
||||
def is_ipv6_address(addrstr):
|
||||
"""
|
||||
Check if ipv6 address string is a valid ipv6 address.
|
||||
|
||||
:param str addrstr: ipv6 address string
|
||||
:return: True if a valid ipv6 address, False otherwise
|
||||
:rtype: bool
|
||||
"""
|
||||
return is_ip_address(AF_INET6, addrstr)
|
||||
|
|
|
@ -5,7 +5,7 @@ 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'
|
||||
_LOG_FORMAT = "%(levelname)-7s %(asctime)s %(name)-15s %(funcName)-15s %(lineno)-4d: %(message)s"
|
||||
_INITIAL = True
|
||||
|
||||
|
||||
|
|
|
@ -1,3 +1,7 @@
|
|||
"""
|
||||
Provides default node maps that can be used to run core with.
|
||||
"""
|
||||
|
||||
from core.emane.nodes import EmaneNet
|
||||
from core.emane.nodes import EmaneNode
|
||||
from core.enumerations import NodeTypes
|
||||
|
@ -7,6 +11,7 @@ from core.netns.vnet import GreTapBridge
|
|||
from core.phys import pnodes
|
||||
from core.xen import xen
|
||||
|
||||
# legacy core nodes, that leverage linux bridges
|
||||
CLASSIC_NODES = {
|
||||
NodeTypes.DEFAULT: nodes.CoreNode,
|
||||
NodeTypes.PHYSICAL: pnodes.PhysicalNode,
|
||||
|
@ -25,6 +30,7 @@ CLASSIC_NODES = {
|
|||
NodeTypes.CONTROL_NET: nodes.CtrlNet
|
||||
}
|
||||
|
||||
# ovs nodes, that depend on ovs to leverage ovs based bridges
|
||||
OVS_NODES = {
|
||||
NodeTypes.DEFAULT: nodes.CoreNode,
|
||||
NodeTypes.PHYSICAL: pnodes.PhysicalNode,
|
||||
|
|
|
@ -12,11 +12,24 @@ _NODE_MAP = None
|
|||
|
||||
|
||||
def _convert_map(x, y):
|
||||
"""
|
||||
Convenience method to create a human readable version of the node map to log.
|
||||
|
||||
:param dict x: dictionary to reduce node items into
|
||||
:param tuple y: current node item
|
||||
:return:
|
||||
"""
|
||||
x[y[0].name] = y[1]
|
||||
return x
|
||||
|
||||
|
||||
def set_node_map(node_map):
|
||||
"""
|
||||
Set the global node map that proides a consistent way to retrieve differently configured nodes.
|
||||
|
||||
:param dict node_map: node map to set to
|
||||
:return: nothing
|
||||
"""
|
||||
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))
|
||||
|
@ -24,11 +37,25 @@ def set_node_map(node_map):
|
|||
|
||||
|
||||
def get_node_class(node_type):
|
||||
"""
|
||||
Retrieve the node class for a given node type.
|
||||
|
||||
:param int node_type: node type to retrieve class for
|
||||
:return: node class
|
||||
"""
|
||||
global _NODE_MAP
|
||||
return _NODE_MAP[node_type]
|
||||
|
||||
|
||||
def is_node(obj, node_types):
|
||||
"""
|
||||
Validates if an object is one of the provided node types.
|
||||
|
||||
:param obj: object to check type for
|
||||
:param int|tuple|list node_types: node type(s) to check against
|
||||
:return: True if the object is one of the node types, False otherwise
|
||||
:rtype: bool
|
||||
"""
|
||||
type_classes = []
|
||||
if isinstance(node_types, (tuple, list)):
|
||||
for node_type in node_types:
|
||||
|
|
|
@ -5,38 +5,76 @@
|
|||
#
|
||||
# author: Tom Goff <thomas.goff@boeing.com>
|
||||
#
|
||||
'''
|
||||
|
||||
"""
|
||||
quagga.py: helper class for generating Quagga configuration.
|
||||
'''
|
||||
"""
|
||||
|
||||
from string import Template
|
||||
|
||||
from core.misc import utils
|
||||
|
||||
def maketuple(obj):
|
||||
if hasattr(obj, "__iter__"):
|
||||
return tuple(obj)
|
||||
|
||||
def addrstr(x):
|
||||
if x.find(".") >= 0:
|
||||
return "ip address %s" % x
|
||||
elif x.find(":") >= 0:
|
||||
return "ipv6 address %s" % x
|
||||
else:
|
||||
return (obj,)
|
||||
raise ValueError("invalid address: %s" % x)
|
||||
|
||||
|
||||
class NetIf(object):
|
||||
def __init__(self, name, addrlist=[]):
|
||||
"""
|
||||
Represents a network interface.
|
||||
"""
|
||||
|
||||
def __init__(self, name, addrlist=None):
|
||||
"""
|
||||
Create a NetIf instance.
|
||||
|
||||
:param str name: interface name
|
||||
:param addrlist: address list for the interface
|
||||
"""
|
||||
self.name = name
|
||||
self.addrlist = addrlist
|
||||
|
||||
if addrlist:
|
||||
self.addrlist = addrlist
|
||||
else:
|
||||
self.addrlist = []
|
||||
|
||||
|
||||
class Conf(object):
|
||||
def __init__(self, **kwds):
|
||||
self.kwds = kwds
|
||||
"""
|
||||
Provides a configuration object.
|
||||
"""
|
||||
|
||||
def __init__(self, **kwargs):
|
||||
"""
|
||||
Create a Conf instance.
|
||||
|
||||
:param dict kwargs: configuration keyword arguments
|
||||
"""
|
||||
self.kwargs = kwargs
|
||||
|
||||
def __str__(self):
|
||||
tmp = self.template.substitute(**self.kwds)
|
||||
if tmp[-1] == '\n':
|
||||
"""
|
||||
Provides a string representation of a configuration object.
|
||||
|
||||
:return: string representation
|
||||
:rtype: str
|
||||
"""
|
||||
# TODO: seems like an error here
|
||||
tmp = self.template.substitute(**self.kwargs)
|
||||
if tmp[-1] == "\n":
|
||||
tmp = tmp[:-1]
|
||||
return tmp
|
||||
|
||||
|
||||
class QuaggaOSPF6Interface(Conf):
|
||||
"""
|
||||
Provides quagga ospf6 interface functionality.
|
||||
"""
|
||||
AF_IPV6_ID = 0
|
||||
AF_IPV4_ID = 65
|
||||
|
||||
|
@ -57,30 +95,36 @@ interface $interface
|
|||
# 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", **kwargs):
|
||||
"""
|
||||
Create a QuaggaOSPF6Interface instance.
|
||||
|
||||
:param netif: network interface
|
||||
:param int instanceid: instance id
|
||||
:param network: network
|
||||
:param dict kwargs: keyword arguments
|
||||
"""
|
||||
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)
|
||||
instanceid=instanceid, network=network, **kwargs)
|
||||
|
||||
def name(self):
|
||||
"""
|
||||
Retrieve network interface name.
|
||||
|
||||
:return: network interface name
|
||||
:rtype: str
|
||||
"""
|
||||
return self.netif.name
|
||||
|
||||
|
||||
class QuaggaOSPF6(Conf):
|
||||
"""
|
||||
Provides quagga ospf6 functionality.
|
||||
"""
|
||||
template = Template("""\
|
||||
$interfaces
|
||||
!
|
||||
|
@ -90,17 +134,25 @@ router ospf6
|
|||
$redistribute
|
||||
""")
|
||||
|
||||
def __init__(self, ospf6ifs, area, routerid,
|
||||
redistribute="! no redistribute"):
|
||||
ospf6ifs = maketuple(ospf6ifs)
|
||||
def __init__(self, ospf6ifs, area, routerid, redistribute="! no redistribute"):
|
||||
"""
|
||||
Create a QuaggaOSPF6 instance.
|
||||
|
||||
:param list ospf6ifs: ospf6 interfaces
|
||||
:param area: area
|
||||
:param routerid: router id
|
||||
:param str redistribute: redistribute value
|
||||
"""
|
||||
ospf6ifs = utils.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)
|
||||
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):
|
||||
"""
|
||||
Provides quagga configuration functionality.
|
||||
"""
|
||||
template = Template("""\
|
||||
log file $logfile
|
||||
$debugs
|
||||
|
@ -111,11 +163,17 @@ $forwarding
|
|||
""")
|
||||
|
||||
def __init__(self, routers, logfile, debugs=()):
|
||||
routers = "\n!\n".join(map(str, maketuple(routers)))
|
||||
"""
|
||||
Create a QuaggaConf instance.
|
||||
|
||||
:param list routers: routers
|
||||
:param str logfile: log file name
|
||||
:param debugs: debug options
|
||||
"""
|
||||
routers = "\n!\n".join(map(str, utils.maketuple(routers)))
|
||||
if debugs:
|
||||
debugs = "\n".join(maketuple(debugs))
|
||||
debugs = "\n".join(utils.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)
|
||||
|
|
|
@ -1,3 +1,7 @@
|
|||
"""
|
||||
Utilities for working with python struct data.
|
||||
"""
|
||||
|
||||
from core.misc import log
|
||||
|
||||
logger = log.get_logger(__name__)
|
||||
|
|
|
@ -7,6 +7,7 @@ import os
|
|||
import subprocess
|
||||
|
||||
import fcntl
|
||||
import resource
|
||||
|
||||
from core.misc import log
|
||||
|
||||
|
@ -14,16 +15,40 @@ logger = log.get_logger(__name__)
|
|||
|
||||
|
||||
def closeonexec(fd):
|
||||
"""
|
||||
Close on execution of a shell process.
|
||||
|
||||
:param fd: file descriptor to close
|
||||
:return: nothing
|
||||
"""
|
||||
fdflags = fcntl.fcntl(fd, fcntl.F_GETFD)
|
||||
fcntl.fcntl(fd, fcntl.F_SETFD, fdflags | fcntl.FD_CLOEXEC)
|
||||
|
||||
|
||||
def check_executables(executables):
|
||||
"""
|
||||
Check executables, verify they exist and are executable.
|
||||
|
||||
:param list[str] executables: executable to check
|
||||
:return: nothing
|
||||
:raises EnvironmentError: when an executable doesn't exist or is not executable
|
||||
"""
|
||||
for executable in executables:
|
||||
if not (os.path.isfile(executable) and os.access(executable, os.X_OK)):
|
||||
if not is_exe(executable):
|
||||
raise EnvironmentError("executable not found: %s" % executable)
|
||||
|
||||
|
||||
def is_exe(file_path):
|
||||
"""
|
||||
Check if a given file path exists and is an executable file.
|
||||
|
||||
:param str file_path: file path to check
|
||||
:return: True if the file is considered and executable file, False otherwise
|
||||
:rtype: bool
|
||||
"""
|
||||
return os.path.isfile(file_path) and os.access(file_path, os.X_OK)
|
||||
|
||||
|
||||
def which(program):
|
||||
"""
|
||||
From: http://stackoverflow.com/questions/377017/test-if-executable-exists-in-python
|
||||
|
@ -31,17 +56,13 @@ def which(program):
|
|||
: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)
|
||||
|
||||
fpath, fname = os.path.split(program)
|
||||
if fpath:
|
||||
if is_exe(program):
|
||||
return program
|
||||
else:
|
||||
for path in os.environ["PATH"].split(os.pathsep):
|
||||
path = path.strip('"')
|
||||
path = path.strip("\"")
|
||||
exe_file = os.path.join(path, program)
|
||||
if is_exe(exe_file):
|
||||
return exe_file
|
||||
|
@ -50,6 +71,12 @@ def which(program):
|
|||
|
||||
|
||||
def ensurepath(pathlist):
|
||||
"""
|
||||
Checks a list of paths are contained within the environment path, if not add it to the path.
|
||||
|
||||
:param list[str] pathlist: list of paths to check
|
||||
:return: nothing
|
||||
"""
|
||||
searchpath = os.environ["PATH"].split(":")
|
||||
for p in set(pathlist):
|
||||
if p not in searchpath:
|
||||
|
@ -57,75 +84,137 @@ def ensurepath(pathlist):
|
|||
|
||||
|
||||
def maketuple(obj):
|
||||
"""
|
||||
Create a tuple from an object, or return the object itself.
|
||||
|
||||
:param obj: object to convert to a tuple
|
||||
:return: converted tuple or the object itself
|
||||
:rtype: tuple
|
||||
"""
|
||||
if hasattr(obj, "__iter__"):
|
||||
return tuple(obj)
|
||||
else:
|
||||
return obj,
|
||||
|
||||
|
||||
# TODO: remove unused parameter type
|
||||
def maketuplefromstr(s, type):
|
||||
s.replace('\\', '\\\\')
|
||||
"""
|
||||
Create a tuple from a string.
|
||||
|
||||
:param str s: string to convert to a tuple
|
||||
:param type: type of tuple to convert to
|
||||
:return: tuple from string
|
||||
:rtype: tuple
|
||||
"""
|
||||
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 mutecall(*args, **kwds):
|
||||
kwds["stdout"] = open(os.devnull, "w")
|
||||
kwds["stderr"] = subprocess.STDOUT
|
||||
return subprocess.call(*args, **kwds)
|
||||
def mutecall(*args, **kwargs):
|
||||
"""
|
||||
Run a muted call command.
|
||||
|
||||
:param list args: arguments for the command
|
||||
:param dict kwargs: keyword arguments for the command
|
||||
:return: command result
|
||||
:rtype: int
|
||||
"""
|
||||
kwargs["stdout"] = open(os.devnull, "w")
|
||||
kwargs["stderr"] = subprocess.STDOUT
|
||||
return subprocess.call(*args, **kwargs)
|
||||
|
||||
|
||||
def mutecheck_call(*args, **kwds):
|
||||
kwds["stdout"] = open(os.devnull, "w")
|
||||
kwds["stderr"] = subprocess.STDOUT
|
||||
return subprocess.check_call(*args, **kwds)
|
||||
def mutecheck_call(*args, **kwargs):
|
||||
"""
|
||||
Run a muted check call command.
|
||||
|
||||
:param list args: arguments for the command
|
||||
:param dict kwargs: keyword arguments for the command
|
||||
:return: command result
|
||||
:rtype: int
|
||||
"""
|
||||
kwargs["stdout"] = open(os.devnull, "w")
|
||||
kwargs["stderr"] = subprocess.STDOUT
|
||||
return subprocess.check_call(*args, **kwargs)
|
||||
|
||||
|
||||
def spawn(*args, **kwds):
|
||||
return subprocess.Popen(*args, **kwds).pid
|
||||
def spawn(*args, **kwargs):
|
||||
"""
|
||||
Wrapper for running a spawn command and returning the process id.
|
||||
|
||||
:param list args: arguments for the command
|
||||
:param dict kwargs: keyword arguments for the command
|
||||
:return: process id of the command
|
||||
:rtype: int
|
||||
"""
|
||||
return subprocess.Popen(*args, **kwargs).pid
|
||||
|
||||
|
||||
def mutespawn(*args, **kwds):
|
||||
kwds["stdout"] = open(os.devnull, "w")
|
||||
kwds["stderr"] = subprocess.STDOUT
|
||||
return subprocess.Popen(*args, **kwds).pid
|
||||
def mutespawn(*args, **kwargs):
|
||||
"""
|
||||
Wrapper for running a muted spawned command.
|
||||
|
||||
:param list args: arguments for the command
|
||||
:param dict kwargs: keyword arguments for the command
|
||||
:return: process id of the command
|
||||
:rtype: int
|
||||
"""
|
||||
kwargs["stdout"] = open(os.devnull, "w")
|
||||
kwargs["stderr"] = subprocess.STDOUT
|
||||
return subprocess.Popen(*args, **kwargs).pid
|
||||
|
||||
|
||||
def detachinit():
|
||||
"""
|
||||
Fork a child process and exit.
|
||||
|
||||
:return: nothing
|
||||
"""
|
||||
if os.fork():
|
||||
# parent exits
|
||||
os._exit(0)
|
||||
os.setsid()
|
||||
|
||||
|
||||
def detach(*args, **kwds):
|
||||
kwds["preexec_fn"] = detachinit
|
||||
return subprocess.Popen(*args, **kwds).pid
|
||||
def detach(*args, **kwargs):
|
||||
"""
|
||||
Run a detached process by forking it.
|
||||
|
||||
:param list args: arguments for the command
|
||||
:param dict kwargs: keyword arguments for the command
|
||||
:return: process id of the command
|
||||
:rtype: int
|
||||
"""
|
||||
kwargs["preexec_fn"] = detachinit
|
||||
return subprocess.Popen(*args, **kwargs).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 mutedetach(*args, **kwargs):
|
||||
"""
|
||||
Run a muted detached process by forking it.
|
||||
|
||||
:param list args: arguments for the command
|
||||
:param dict kwargs: keyword arguments for the command
|
||||
:return: process id of the command
|
||||
:rtype: int
|
||||
"""
|
||||
kwargs["preexec_fn"] = detachinit
|
||||
kwargs["stdout"] = open(os.devnull, "w")
|
||||
kwargs["stderr"] = subprocess.STDOUT
|
||||
return subprocess.Popen(*args, **kwargs).pid
|
||||
|
||||
|
||||
def cmdresult(args):
|
||||
"""
|
||||
Execute a command on the host and return a tuple containing the
|
||||
exit status and result string. stderr output
|
||||
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.
|
||||
|
||||
:param list args: command arguments
|
||||
:return: command status and stdout
|
||||
:rtype: tuple[int, str]
|
||||
"""
|
||||
cmdid = subprocess.Popen(args, stdin=open(os.devnull, 'r'),
|
||||
stdout=subprocess.PIPE,
|
||||
stderr=subprocess.STDOUT)
|
||||
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()
|
||||
|
@ -133,6 +222,14 @@ def cmdresult(args):
|
|||
|
||||
|
||||
def hexdump(s, bytes_per_word=2, words_per_line=8):
|
||||
"""
|
||||
Hex dump of a string.
|
||||
|
||||
:param str s: string to hex dump
|
||||
:param bytes_per_word: number of bytes per word
|
||||
:param words_per_line: number of words per line
|
||||
:return: hex dump of string
|
||||
"""
|
||||
dump = ""
|
||||
count = 0
|
||||
bytes = bytes_per_word * words_per_line
|
||||
|
@ -151,10 +248,15 @@ def hexdump(s, bytes_per_word=2, words_per_line=8):
|
|||
def filemunge(pathname, header, text):
|
||||
"""
|
||||
Insert text at the end of a file, surrounded by header comments.
|
||||
|
||||
:param str pathname: file path to add text to
|
||||
:param str header: header text comments
|
||||
:param str text: text to append to file
|
||||
:return: nothing
|
||||
"""
|
||||
# prevent duplicates
|
||||
filedemunge(pathname, header)
|
||||
f = open(pathname, 'a')
|
||||
f = open(pathname, "a")
|
||||
f.write("# BEGIN %s\n" % header)
|
||||
f.write(text)
|
||||
f.write("# END %s\n" % header)
|
||||
|
@ -164,8 +266,12 @@ def filemunge(pathname, header, text):
|
|||
def filedemunge(pathname, header):
|
||||
"""
|
||||
Remove text that was inserted in a file surrounded by header comments.
|
||||
|
||||
:param str pathname: file path to open for removing a header
|
||||
:param str header: header text to target for removal
|
||||
:return: nothing
|
||||
"""
|
||||
f = open(pathname, 'r')
|
||||
f = open(pathname, "r")
|
||||
lines = f.readlines()
|
||||
f.close()
|
||||
start = None
|
||||
|
@ -177,7 +283,7 @@ def filedemunge(pathname, header):
|
|||
end = i + 1
|
||||
if start is None or end is None:
|
||||
return
|
||||
f = open(pathname, 'w')
|
||||
f = open(pathname, "w")
|
||||
lines = lines[:start] + lines[end:]
|
||||
f.write("".join(lines))
|
||||
f.close()
|
||||
|
@ -186,21 +292,31 @@ def filedemunge(pathname, header):
|
|||
def expandcorepath(pathname, session=None, node=None):
|
||||
"""
|
||||
Expand a file path given session information.
|
||||
|
||||
:param str pathname: file path to expand
|
||||
:param core.session.Session session: core session object to expand path with
|
||||
:param core.netns.LxcNode node: node to expand path with
|
||||
:return: expanded path
|
||||
:rtype: str
|
||||
"""
|
||||
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)
|
||||
pathname = pathname.replace("~", "/home/%s" % session.user)
|
||||
pathname = pathname.replace("%SESSION%", str(session.session_id))
|
||||
pathname = pathname.replace("%SESSION_DIR%", session.session_dir)
|
||||
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)
|
||||
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.
|
||||
|
||||
:param str devname: device name to translate
|
||||
:return: translated device name
|
||||
:rtype: str
|
||||
"""
|
||||
if devname is None:
|
||||
return None
|
||||
|
@ -213,20 +329,35 @@ def daemonize(rootdir="/", umask=0, close_fds=False, dontclose=(),
|
|||
defaultmaxfd=1024):
|
||||
"""
|
||||
Run the background process as a daemon.
|
||||
|
||||
:param str rootdir: root directory for daemon
|
||||
:param int umask: umask for daemon
|
||||
:param bool close_fds: flag to close file descriptors
|
||||
:param dontclose: dont close options
|
||||
:param stdin: stdin for daemon
|
||||
:param stdout: stdout for daemon
|
||||
:param stderr: stderr for daemon
|
||||
:param int stdoutmode: stdout mode
|
||||
:param int stderrmode: stderr mode
|
||||
:param str pidfilename: pid file name
|
||||
:param int defaultmaxfd: default max file descriptors
|
||||
:return: nothing
|
||||
"""
|
||||
if not hasattr(dontclose, "__contains__"):
|
||||
if not isinstance(dontclose, int):
|
||||
raise TypeError, "dontclose must be an integer"
|
||||
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"
|
||||
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,
|
||||
|
@ -235,15 +366,18 @@ def daemonize(rootdir="/", umask=0, close_fds=False, dontclose=(),
|
|||
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():
|
||||
# parent exits
|
||||
os._exit(0)
|
||||
|
||||
os.setsid()
|
||||
pid = os.fork()
|
||||
if pid:
|
||||
|
@ -256,8 +390,10 @@ def daemonize(rootdir="/", umask=0, close_fds=False, dontclose=(),
|
|||
logger.exception("error writing to file: %s", pidfilename)
|
||||
# parent exits
|
||||
os._exit(0)
|
||||
|
||||
if rootdir:
|
||||
os.chdir(rootdir)
|
||||
|
||||
os.umask(umask)
|
||||
if close_fds:
|
||||
try:
|
||||
|
@ -266,27 +402,31 @@ def daemonize(rootdir="/", umask=0, close_fds=False, dontclose=(),
|
|||
raise ValueError
|
||||
except:
|
||||
maxfd = defaultmaxfd
|
||||
|
||||
for fd in xrange(3, maxfd):
|
||||
if fd in dontclose:
|
||||
continue
|
||||
try:
|
||||
os.close(fd)
|
||||
except:
|
||||
except IOError:
|
||||
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.
|
||||
|
||||
:param str filename: file to read into a dictionary
|
||||
:param dict d: dictionary to read file into
|
||||
:return: nothing
|
||||
"""
|
||||
with open(filename, 'r') as f:
|
||||
with open(filename, "r") as f:
|
||||
lines = f.readlines()
|
||||
for l in lines:
|
||||
if l[:1] == '#':
|
||||
if l[:1] == "#":
|
||||
continue
|
||||
try:
|
||||
key, value = l.split('=', 1)
|
||||
key, value = l.split("=", 1)
|
||||
d[key] = value.strip()
|
||||
except ValueError:
|
||||
logger.exception("error reading file to dict: %s", filename)
|
||||
|
@ -298,9 +438,13 @@ def checkforkernelmodule(name):
|
|||
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.
|
||||
|
||||
:param str name: name of kernel module to check for
|
||||
:return: kernel module line, None otherwise
|
||||
:rtype: str
|
||||
"""
|
||||
with open('/proc/modules', 'r') as f:
|
||||
with open("/proc/modules", "r") as f:
|
||||
for line in f:
|
||||
if line.startswith(name + ' '):
|
||||
if line.startswith(name + " "):
|
||||
return line.rstrip()
|
||||
return None
|
||||
|
|
Loading…
Reference in a new issue