Commit 239bd691 authored by Tom Niget's avatar Tom Niget

python3: clean up system resources (pipes, processes)

Python3 added automatic detection of leaked resources, which causes lots of warnings in Re6st, mostly because of unclosed subprocess streams.
parent 56e68106
...@@ -74,7 +74,8 @@ if args.duration: ...@@ -74,7 +74,8 @@ if args.duration:
signal.signal(signal.SIGALRM, handler) signal.signal(signal.SIGALRM, handler)
signal.alarm(args.duration) signal.alarm(args.duration)
exec(compile(open("fixnemu.py", "rb").read(), "fixnemu.py", 'exec')) with open('fixnemu.py') as f:
exec(compile(f.read(), 'fixnemu.py', 'exec'))
class Re6stNode(nemu.Node): class Re6stNode(nemu.Node):
name: str name: str
......
...@@ -76,9 +76,10 @@ def main(): ...@@ -76,9 +76,10 @@ def main():
print("WARNING: it is strongly recommended to use --fingerprint option.") print("WARNING: it is strongly recommended to use --fingerprint option.")
network = x509.networkFromCa(ca) network = x509.networkFromCa(ca)
if config.is_needed: if config.is_needed:
route, err = subprocess.Popen(('ip', '-6', '-o', 'route', 'get', with subprocess.Popen(('ip', '-6', '-o', 'route', 'get',
utils.ipFromBin(network)), utils.ipFromBin(network)),
stdout=subprocess.PIPE).communicate() stdout=subprocess.PIPE) as proc:
route, err = proc.communicate()
sys.exit(err or route and sys.exit(err or route and
utils.binFromIp(route.split()[8]).startswith(network)) utils.binFromIp(route.split()[8]).startswith(network))
...@@ -89,7 +90,7 @@ def main(): ...@@ -89,7 +90,7 @@ def main():
reserved = 'CN', 'serial' reserved = 'CN', 'serial'
req = crypto.X509Req() req = crypto.X509Req()
try: try:
with open(cert_path) as f: with open(cert_path, "rb") as f:
cert = loadCert(f.read()) cert = loadCert(f.read())
components = {k.decode(): v for k, v in cert.get_subject().get_components()} components = {k.decode(): v for k, v in cert.get_subject().get_components()}
for k in reserved: for k in reserved:
......
...@@ -266,14 +266,8 @@ def main(): ...@@ -266,14 +266,8 @@ def main():
def call(cmd): def call(cmd):
logging.debug('%r', cmd) logging.debug('%r', cmd)
p = subprocess.Popen(cmd, stdout=subprocess.PIPE, return subprocess.run(cmd, capture_output=True, check=True).stdout
stderr=subprocess.PIPE) def ip4(object: str, *args):
stdout, stderr = p.communicate()
if p.returncode:
raise EnvironmentError("%r failed with error %u\n%s"
% (' '.join(cmd), p.returncode, stderr))
return stdout
def ip4(object, *args):
args = ['ip', '-4', object, 'add'] + list(args) args = ['ip', '-4', object, 'add'] + list(args)
call(args) call(args)
args[3] = 'del' args[3] = 'del'
......
...@@ -155,6 +155,11 @@ class RegistryServer: ...@@ -155,6 +155,11 @@ class RegistryServer:
else: else:
self.newHMAC(0) self.newHMAC(0)
def close(self):
self.sock.close()
self.db.close()
self.ctl.close()
def getConfig(self, name, *default): def getConfig(self, name, *default):
r, = next(self.db.execute( r, = next(self.db.execute(
"SELECT value FROM config WHERE name=?", (name,)), default) "SELECT value FROM config WHERE name=?", (name,)), default)
......
...@@ -41,7 +41,8 @@ class TestRegistryClientInteract(unittest.TestCase): ...@@ -41,7 +41,8 @@ class TestRegistryClientInteract(unittest.TestCase):
self.max_clients = 10 self.max_clients = 10
def tearDown(self): def tearDown(self):
self.server.proc.terminate() with self.server.proc as p:
p.terminate()
def test_1_main(self): def test_1_main(self):
""" a client interact a server, no re6stnet node test basic function""" """ a client interact a server, no re6stnet node test basic function"""
......
...@@ -3,7 +3,7 @@ import logging ...@@ -3,7 +3,7 @@ import logging
import nemu import nemu
import time import time
import weakref import weakref
from subprocess import PIPE from subprocess import DEVNULL, PIPE
from pathlib import Path from pathlib import Path
from re6st.tests import DEMO_PATH from re6st.tests import DEMO_PATH
...@@ -62,8 +62,8 @@ class NetManager: ...@@ -62,8 +62,8 @@ class NetManager:
""" """
for reg, nodes in self.registries.items(): for reg, nodes in self.registries.items():
for node in nodes: for node in nodes:
app0 = node.Popen(["ping", "-c", "1", reg.ip], stdout=PIPE) with node.Popen(["ping", "-c", "1", reg.ip], stdout=DEVNULL) as app0:
ret = app0.wait() ret = app0.wait()
if ret: if ret:
raise ConnectableError( raise ConnectableError(
"network construct failed {} to {}".format(node.ip, reg.ip)) "network construct failed {} to {}".format(node.ip, reg.ip))
......
...@@ -134,13 +134,17 @@ class Re6stRegistry: ...@@ -134,13 +134,17 @@ class Re6stRegistry:
if e.errno != errno.ENOENT: if e.errno != errno.ENOENT:
raise raise
def __del__(self): def terminate(self):
try: try:
logging.debug("teminate process %s", self.proc.pid) logging.debug("teminate process %s", self.proc.pid)
self.proc.destroy() with self.proc as p:
p.destroy()
except: except:
pass pass
def __del__(self):
self.terminate()
class Re6stNode: class Re6stNode:
"""class run a re6stnet service on a namespace""" """class run a re6stnet service on a namespace"""
...@@ -263,7 +267,8 @@ class Re6stNode: ...@@ -263,7 +267,8 @@ class Re6stNode:
def stop(self): def stop(self):
"""stop running re6stnet process""" """stop running re6stnet process"""
logging.debug("%s teminate process %s", self.name, self.proc.pid) logging.debug("%s teminate process %s", self.name, self.proc.pid)
self.proc.destroy() with self.proc as p:
p.destroy()
def __del__(self): def __del__(self):
"""teminate process and rm temp dir""" """teminate process and rm temp dir"""
......
...@@ -12,25 +12,6 @@ from . import network_build, re6st_wrap ...@@ -12,25 +12,6 @@ from . import network_build, re6st_wrap
PING_PATH = str(Path(__file__).parent.resolve() / "ping.py") PING_PATH = str(Path(__file__).parent.resolve() / "ping.py")
def deploy_re6st(nm, recreate=False):
net = nm.registries
nodes = []
registries = []
re6st_wrap.Re6stRegistry.registry_seq = 0
re6st_wrap.Re6stNode.node_seq = 0
for registry in net:
reg = re6st_wrap.Re6stRegistry(registry, "2001:db8:42::", len(net[registry]),
recreate=recreate)
reg_node = re6st_wrap.Re6stNode(registry, reg, name=reg.name)
registries.append(reg)
reg_node.run("--gateway", "--disable-proto", "none", "--ip", registry.ip)
nodes.append(reg_node)
for m in net[registry]:
node = re6st_wrap.Re6stNode(m, reg)
node.run("-i" + m.iface.name)
nodes.append(node)
return nodes, registries
def wait_stable(nodes, timeout=240): def wait_stable(nodes, timeout=240):
"""try use ping6 from each node to the other until ping success to all the """try use ping6 from each node to the other until ping success to all the
other nodes other nodes
...@@ -77,6 +58,36 @@ class TestNet(unittest.TestCase): ...@@ -77,6 +58,36 @@ class TestNet(unittest.TestCase):
logging.basicConfig(level=logging.INFO) logging.basicConfig(level=logging.INFO)
re6st_wrap.initial() re6st_wrap.initial()
def deploy_re6st(self, nm, recreate=False):
net = nm.registries
nodes = []
registries = []
re6st_wrap.Re6stRegistry.registry_seq = 0
re6st_wrap.Re6stNode.node_seq = 0
for registry in net:
reg = re6st_wrap.Re6stRegistry(registry, "2001:db8:42::", len(net[registry]),
recreate=recreate)
reg_node = re6st_wrap.Re6stNode(registry, reg, name=reg.name)
registries.append(reg)
reg_node.run("--gateway", "--disable-proto", "none", "--ip", registry.ip)
nodes.append(reg_node)
for m in net[registry]:
node = re6st_wrap.Re6stNode(m, reg)
node.run("-i" + m.iface.name)
nodes.append(node)
def clean_re6st():
for node in nodes:
node.node.destroy()
node.stop()
for reg in registries:
reg.terminate()
self.addCleanup(clean_re6st)
return nodes, registries
@classmethod @classmethod
def tearDownClass(cls): def tearDownClass(cls):
"""watch any process leaked after tests""" """watch any process leaked after tests"""
...@@ -94,7 +105,7 @@ class TestNet(unittest.TestCase): ...@@ -94,7 +105,7 @@ class TestNet(unittest.TestCase):
"""create a network in a net segment, test the connectivity by ping """create a network in a net segment, test the connectivity by ping
""" """
nm = network_build.net_route() nm = network_build.net_route()
nodes, _ = deploy_re6st(nm) nodes, registries = self.deploy_re6st(nm)
wait_stable(nodes, 40) wait_stable(nodes, 40)
time.sleep(10) time.sleep(10)
...@@ -107,7 +118,7 @@ class TestNet(unittest.TestCase): ...@@ -107,7 +118,7 @@ class TestNet(unittest.TestCase):
then test if network recover, this test seems always failed then test if network recover, this test seems always failed
""" """
nm = network_build.net_demo() nm = network_build.net_demo()
nodes, _ = deploy_re6st(nm) nodes, registries = self.deploy_re6st(nm)
wait_stable(nodes, 100) wait_stable(nodes, 100)
...@@ -126,7 +137,7 @@ class TestNet(unittest.TestCase): ...@@ -126,7 +137,7 @@ class TestNet(unittest.TestCase):
then test if network recover, then test if network recover,
""" """
nm = network_build.net_route() nm = network_build.net_route()
nodes, _ = deploy_re6st(nm) nodes, registries = self.deploy_re6st(nm)
wait_stable(nodes, 40) wait_stable(nodes, 40)
......
...@@ -70,6 +70,7 @@ class TestRegistryServer(unittest.TestCase): ...@@ -70,6 +70,7 @@ class TestRegistryServer(unittest.TestCase):
@classmethod @classmethod
def tearDownClass(cls): def tearDownClass(cls):
cls.server.close()
# remove database # remove database
for file in [cls.config.db, cls.config.ca, cls.config.key]: for file in [cls.config.db, cls.config.ca, cls.config.key]:
try: try:
......
...@@ -103,7 +103,7 @@ def decrypt(pkey, incontent): ...@@ -103,7 +103,7 @@ def decrypt(pkey, incontent):
with open("node.key", 'w') as f: with open("node.key", 'w') as f:
f.write(pkey.decode()) f.write(pkey.decode())
args = "openssl rsautl -decrypt -inkey node.key".split() args = "openssl rsautl -decrypt -inkey node.key".split()
p = subprocess.Popen( with subprocess.Popen(
args, stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.PIPE) args, stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.PIPE) as p:
outcontent, err = p.communicate(incontent) outcontent, err = p.communicate(incontent)
return outcontent return outcontent
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment