From 6b8ee13f5ded762c08816f58fbb81599038fb0dd Mon Sep 17 00:00:00 2001 From: "Blake J. Harnden" Date: Tue, 27 Feb 2018 10:48:01 -0800 Subject: [PATCH] initial changes to try and cleanup shell commands used within core --- daemon/core/bsd/vnode.py | 1 - daemon/core/corehandlers.py | 8 +- daemon/core/coreobj.py | 2 +- daemon/core/emane/emanemanager.py | 11 +- daemon/core/netns/vif.py | 12 +- daemon/core/netns/vnode.py | 137 +++--------- daemon/core/netns/vnodeclient.py | 195 +++++++----------- daemon/core/service.py | 10 +- daemon/core/session.py | 3 +- daemon/examples/netns/basicrange.py | 2 +- daemon/examples/netns/distributed.py | 2 +- daemon/examples/netns/emane80211.py | 2 +- daemon/examples/netns/howmanynodes.py | 2 +- .../examples/netns/iperf-performance-chain.py | 8 +- daemon/examples/netns/ospfmanetmdrtest.py | 7 +- daemon/examples/netns/switch.py | 2 +- daemon/examples/netns/switchtest.py | 6 +- daemon/examples/netns/wlanemanetests.py | 18 +- daemon/examples/netns/wlantest.py | 6 +- daemon/tests/conftest.py | 12 +- daemon/tests/test_core.py | 28 ++- doc/scripting.rst | 2 +- 22 files changed, 185 insertions(+), 291 deletions(-) diff --git a/daemon/core/bsd/vnode.py b/daemon/core/bsd/vnode.py index 57967dbf..5e28eff8 100644 --- a/daemon/core/bsd/vnode.py +++ b/daemon/core/bsd/vnode.py @@ -282,7 +282,6 @@ class SimpleJailNode(PyCoreNode): def getaddr(self, ifname, rescan=False): return None - # return self.vnodeclient.getaddr(ifname = ifname, rescan = rescan) def addsymlink(self, path, file): """ diff --git a/daemon/core/corehandlers.py b/daemon/core/corehandlers.py index dc6fe0af..b770e38e 100644 --- a/daemon/core/corehandlers.py +++ b/daemon/core/corehandlers.py @@ -988,7 +988,7 @@ class CoreRequestHandler(SocketServer.BaseRequestHandler): upstream = True netif = net2.getlinknetif(net) if netif is None: - raise ValueError, "modify unknown link between nets" + raise ValueError("modify unknown link between nets") if upstream: netif.swapparams("_params_up") net.linkconfig(netif, bw=bw, delay=delay, @@ -1087,7 +1087,7 @@ class CoreRequestHandler(SocketServer.BaseRequestHandler): # echo back exec message with cmd for spawning interactive terminal if command == "bash": command = "/bin/bash" - res = node.termcmdstring(command) + res = node.client.termcmdstring(command) tlv_data += coreapi.CoreExecuteTlv.pack(ExecuteTlvs.RESULT.value, res) reply = coreapi.CoreExecMessage.pack(MessageFlags.TTY.value, tlv_data) return reply, @@ -1099,7 +1099,7 @@ class CoreRequestHandler(SocketServer.BaseRequestHandler): if message.flags & MessageFlags.LOCAL.value: status, res = utils.cmdresult(shlex.split(command)) else: - status, res = node.cmdresult(shlex.split(command)) + status, res = node.client.cmdresult(shlex.split(command)) logger.info("done exec cmd=%s with status=%d res=(%d bytes)", command, status, len(res)) if message.flags & MessageFlags.TEXT.value: tlv_data += coreapi.CoreExecuteTlv.pack(ExecuteTlvs.RESULT.value, res) @@ -1112,7 +1112,7 @@ class CoreRequestHandler(SocketServer.BaseRequestHandler): if message.flags & MessageFlags.LOCAL.value: utils.mutedetach(shlex.split(command)) else: - node.cmd(shlex.split(command), wait=False) + node.client.cmd(shlex.split(command), wait=False) except KeyError: logger.exception("error getting object: %s", node_num) # XXX wait and queue this message to try again later diff --git a/daemon/core/coreobj.py b/daemon/core/coreobj.py index 76fb59e0..962c4a2a 100644 --- a/daemon/core/coreobj.py +++ b/daemon/core/coreobj.py @@ -556,7 +556,7 @@ class PyCoreNetIf(object): """ Creates a PyCoreNetIf instance. - :param node: node for interface + :param core.netns.vnode.SimpleLxcNode node: node for interface :param str name: interface name :param mtu: mtu value """ diff --git a/daemon/core/emane/emanemanager.py b/daemon/core/emane/emanemanager.py index 918f2c56..49780661 100644 --- a/daemon/core/emane/emanemanager.py +++ b/daemon/core/emane/emanemanager.py @@ -912,17 +912,16 @@ class EmaneManager(ConfigurableManager): # multicast route is needed for OTA data cmd = [constants.IP_BIN, "route", "add", otagroup, "dev", otadev] - # rc = node.cmd(cmd, wait=True) - node.cmd(cmd, wait=True) + node.client.cmd(cmd, wait=True) # multicast route is also needed for event data if on control network if eventservicenetidx >= 0 and eventgroup != otagroup: cmd = [constants.IP_BIN, "route", "add", eventgroup, "dev", eventdev] - node.cmd(cmd, wait=True) + node.client.cmd(cmd, wait=True) try: cmd = emanecmd + ["-f", os.path.join(path, "emane%d.log" % n), os.path.join(path, "platform%d.xml" % n)] logger.info("Emane.startdaemons2() running %s" % str(cmd)) - status, output = node.cmdresult(cmd) + status, output = node.client.cmdresult(cmd) logger.info("Emane.startdaemons2() return code %d" % status) logger.info("Emane.startdaemons2() output: %s" % output) except subprocess.CalledProcessError: @@ -955,7 +954,7 @@ class EmaneManager(ConfigurableManager): stop_emane_on_host = True continue if node.up: - node.cmd(cmd, wait=False) + node.client.cmd(cmd, wait=False) # TODO: RJ45 node else: stop_emane_on_host = True @@ -1161,7 +1160,7 @@ class EmaneManager(ConfigurableManager): if emane.VERSION < emane.EMANE092: status = subprocess.call(cmd) else: - status = node.cmd(cmd, wait=True) + status = node.client.cmd(cmd, wait=True) except IOError: logger.exception("error checking if emane is running") diff --git a/daemon/core/netns/vif.py b/daemon/core/netns/vif.py index 7f254858..33d656ac 100644 --- a/daemon/core/netns/vif.py +++ b/daemon/core/netns/vif.py @@ -60,7 +60,7 @@ class VEth(PyCoreNetIf): if not self.up: return if self.node: - self.node.cmd([constants.IP_BIN, "-6", "addr", "flush", "dev", self.name]) + self.node.client.cmd([constants.IP_BIN, "-6", "addr", "flush", "dev", self.name]) if self.localname: utils.mutedetach([constants.IP_BIN, "link", "delete", self.localname]) self.up = False @@ -112,7 +112,7 @@ class TunTap(PyCoreNetIf): """ if not self.up: return - self.node.cmd([constants.IP_BIN, "-6", "addr", "flush", "dev", self.name]) + self.node.client.cmd([constants.IP_BIN, "-6", "addr", "flush", "dev", self.name]) # if self.name: # mutedetach(["tunctl", "-d", self.localname]) self.up = False @@ -169,7 +169,7 @@ class TunTap(PyCoreNetIf): def nodedevexists(): cmd = (constants.IP_BIN, "link", "show", self.name) - return self.node.cmd(cmd) + return self.node.client.cmd(cmd) count = 0 while True: @@ -206,8 +206,8 @@ class TunTap(PyCoreNetIf): logger.exception(msg) return - self.node.cmd([constants.IP_BIN, "link", "set", self.localname, "name", self.name]) - self.node.cmd([constants.IP_BIN, "link", "set", self.name, "up"]) + self.node.client.cmd([constants.IP_BIN, "link", "set", self.localname, "name", self.name]) + self.node.client.cmd([constants.IP_BIN, "link", "set", self.name, "up"]) def setaddrs(self): """ @@ -217,7 +217,7 @@ class TunTap(PyCoreNetIf): """ self.waitfordevicenode() for addr in self.addrlist: - self.node.cmd([constants.IP_BIN, "addr", "add", str(addr), "dev", self.name]) + self.node.client.cmd([constants.IP_BIN, "addr", "add", str(addr), "dev", self.name]) class GreTap(PyCoreNetIf): diff --git a/daemon/core/netns/vnode.py b/daemon/core/netns/vnode.py index 400362b6..7dd70e0a 100644 --- a/daemon/core/netns/vnode.py +++ b/daemon/core/netns/vnode.py @@ -27,6 +27,14 @@ utils.check_executables([constants.IP_BIN]) class SimpleLxcNode(PyCoreNode): """ Provides simple lxc functionality for core nodes. + + :type nodedir: str + :type ctrlchnlname: str + :type client: core.netns.vnodeclient.VnodeClient + :type pid: int + :type up: bool + :type lock: threading.RLock + :type _mounts: list[tuple[str, str]] """ valid_deladdrtype = ("inet", "inet6", "inet6link") @@ -43,7 +51,7 @@ class SimpleLxcNode(PyCoreNode): PyCoreNode.__init__(self, session, objid, name, start=start) self.nodedir = nodedir self.ctrlchnlname = os.path.abspath(os.path.join(self.session.session_dir, self.name)) - self.vnodeclient = None + self.client = None self.pid = None self.up = False self.lock = threading.RLock() @@ -100,11 +108,11 @@ class SimpleLxcNode(PyCoreNode): if tmp.wait(): raise Exception("command failed: %s" % vnoded) - self.vnodeclient = vnodeclient.VnodeClient(self.name, self.ctrlchnlname) + self.client = vnodeclient.VnodeClient(self.name, self.ctrlchnlname) logger.info("bringing up loopback interface") - self.cmd([constants.IP_BIN, "link", "set", "lo", "up"]) + self.client.cmd([constants.IP_BIN, "link", "set", "lo", "up"]) logger.info("setting hostname: %s" % self.name) - self.cmd(["hostname", self.name]) + self.client.cmd(["hostname", self.name]) self.up = True def shutdown(self): @@ -142,88 +150,9 @@ class SimpleLxcNode(PyCoreNode): # clear interface data, close client, and mark self and not up self._netif.clear() - self.vnodeclient.close() + self.client.close() self.up = False - # TODO: potentially remove all these wrapper methods, just make use of object itself. - def cmd(self, args, wait=True): - """ - Wrapper around vnodeclient cmd. - - :param args: arguments for ocmmand - :param wait: wait or not - :return: - """ - return self.vnodeclient.cmd(args, wait) - - def cmdresult(self, args): - """ - Wrapper around vnodeclient cmdresult. - - :param args: arguments for ocmmand - :return: - """ - return self.vnodeclient.cmdresult(args) - - def popen(self, args): - """ - Wrapper around vnodeclient popen. - - :param args: arguments for ocmmand - :return: - """ - return self.vnodeclient.popen(args) - - def icmd(self, args): - """ - Wrapper around vnodeclient icmd. - - :param args: arguments for ocmmand - :return: - """ - return self.vnodeclient.icmd(args) - - def redircmd(self, infd, outfd, errfd, args, wait=True): - """ - Wrapper around vnodeclient redircmd. - - :param infd: input file descriptor - :param outfd: output file descriptor - :param errfd: err file descriptor - :param args: command arguments - :param wait: wait or not - :return: - """ - return self.vnodeclient.redircmd(infd, outfd, errfd, args, wait) - - def term(self, sh="/bin/sh"): - """ - Wrapper around vnodeclient term. - - :param sh: shell to create terminal for - :return: - """ - return self.vnodeclient.term(sh=sh) - - def termcmdstring(self, sh="/bin/sh"): - """ - Wrapper around vnodeclient termcmdstring. - - :param sh: shell to run command in - :return: - """ - return self.vnodeclient.termcmdstring(sh=sh) - - def shcmd(self, cmdstr, sh="/bin/sh"): - """ - Wrapper around vnodeclient shcmd. - - :param str cmdstr: command string - :param sh: shell to run command in - :return: - """ - return self.vnodeclient.shcmd(cmdstr, sh=sh) - def boot(self): """ Boot logic. @@ -243,9 +172,8 @@ class SimpleLxcNode(PyCoreNode): source = os.path.abspath(source) logger.info("mounting %s at %s" % (source, target)) try: - shcmd = 'mkdir -p "%s" && %s -n --bind "%s" "%s"' % ( - target, constants.MOUNT_BIN, source, target) - self.shcmd(shcmd) + shcmd = 'mkdir -p "%s" && %s -n --bind "%s" "%s"' % (target, constants.MOUNT_BIN, source, target) + self.client.shcmd(shcmd) self._mounts.append((source, target)) except IOError: logger.exception("mounting failed for %s at %s", source, target) @@ -259,7 +187,7 @@ class SimpleLxcNode(PyCoreNode): """ logger.info("unmounting: %s", target) try: - self.cmd([constants.UMOUNT_BIN, "-n", "-l", target]) + self.client.cmd([constants.UMOUNT_BIN, "-n", "-l", target]) except IOError: logger.exception("unmounting failed for %s" % target) @@ -307,14 +235,14 @@ class SimpleLxcNode(PyCoreNode): if self.up: subprocess.check_call([constants.IP_BIN, "link", "set", veth.name, "netns", str(self.pid)]) - self.cmd([constants.IP_BIN, "link", "set", veth.name, "name", ifname]) + self.client.cmd([constants.IP_BIN, "link", "set", veth.name, "name", ifname]) veth.name = ifname if self.up: # TODO: potentially find better way to query interface ID # retrieve interface information - result, output = self.cmdresult(["ip", "link", "show", veth.name]) + result, output = self.client.cmdresult(["ip", "link", "show", veth.name]) logger.info("interface command output: %s", output) output = output.split("\n") veth.flow_id = int(output[0].strip().split(":")[0]) + 1 @@ -375,8 +303,8 @@ class SimpleLxcNode(PyCoreNode): """ self._netif[ifindex].sethwaddr(addr) if self.up: - (status, result) = self.cmdresult([constants.IP_BIN, "link", "set", "dev", - self.ifname(ifindex), "address", str(addr)]) + (status, result) = self.client.cmdresult([constants.IP_BIN, "link", "set", "dev", + self.ifname(ifindex), "address", str(addr)]) if status: logger.error("error setting MAC address %s", str(addr)) @@ -390,11 +318,11 @@ class SimpleLxcNode(PyCoreNode): """ if self.up: if ":" in str(addr): # check if addr is ipv6 - self.cmd([constants.IP_BIN, "addr", "add", str(addr), - "dev", self.ifname(ifindex)]) + self.client.cmd([constants.IP_BIN, "addr", "add", str(addr), + "dev", self.ifname(ifindex)]) else: - self.cmd([constants.IP_BIN, "addr", "add", str(addr), "broadcast", "+", - "dev", self.ifname(ifindex)]) + self.client.cmd([constants.IP_BIN, "addr", "add", str(addr), "broadcast", "+", + "dev", self.ifname(ifindex)]) self._netif[ifindex].addaddr(addr) def deladdr(self, ifindex, addr): @@ -411,7 +339,7 @@ class SimpleLxcNode(PyCoreNode): logger.exception("trying to delete unknown address: %s" % addr) if self.up: - self.cmd([constants.IP_BIN, "addr", "del", str(addr), "dev", self.ifname(ifindex)]) + self.client.cmd([constants.IP_BIN, "addr", "del", str(addr), "dev", self.ifname(ifindex)]) def delalladdr(self, ifindex, addrtypes=valid_deladdrtype): """ @@ -438,7 +366,7 @@ class SimpleLxcNode(PyCoreNode): :return: nothing """ if self.up: - self.cmd([constants.IP_BIN, "link", "set", self.ifname(ifindex), "up"]) + self.client.cmd([constants.IP_BIN, "link", "set", self.ifname(ifindex), "up"]) def newnetif(self, net=None, addrlist=None, hwaddr=None, ifindex=None, ifname=None): """ @@ -503,13 +431,12 @@ class SimpleLxcNode(PyCoreNode): "type", "veth", "peer", "name", tmp2]) subprocess.call([constants.IP_BIN, "link", "set", tmp1, "netns", str(self.pid)]) - self.cmd([constants.IP_BIN, "link", "set", tmp1, "name", ifname]) + self.client.cmd([constants.IP_BIN, "link", "set", tmp1, "name", ifname]) self.addnetif(PyCoreNetIf(self, ifname), self.newifindex()) subprocess.check_call([constants.IP_BIN, "link", "set", tmp2, "netns", str(othernode.pid)]) - othernode.cmd([constants.IP_BIN, "link", "set", tmp2, "name", otherifname]) - othernode.addnetif(PyCoreNetIf(othernode, otherifname), - othernode.newifindex()) + othernode.client.cmd([constants.IP_BIN, "link", "set", tmp2, "name", otherifname]) + othernode.addnetif(PyCoreNetIf(othernode, otherifname), othernode.newifindex()) def addfile(self, srcname, filename): """ @@ -520,7 +447,7 @@ class SimpleLxcNode(PyCoreNode): :return: nothing """ shcmd = 'mkdir -p $(dirname "%s") && mv "%s" "%s" && sync' % (filename, srcname, filename) - self.shcmd(shcmd) + self.client.shcmd(shcmd) def getaddr(self, ifname, rescan=False): """ @@ -530,7 +457,7 @@ class SimpleLxcNode(PyCoreNode): :param bool rescan: rescan flag :return: """ - return self.vnodeclient.getaddr(ifname=ifname, rescan=rescan) + return self.client.getaddr(ifname=ifname, rescan=rescan) def netifstats(self, ifname=None): """ @@ -539,7 +466,7 @@ class SimpleLxcNode(PyCoreNode): :param str ifname: interface name to get state for :return: """ - return self.vnodeclient.netifstats(ifname=ifname) + return self.client.netifstats(ifname=ifname) class LxcNode(SimpleLxcNode): diff --git a/daemon/core/netns/vnodeclient.py b/daemon/core/netns/vnodeclient.py index 1d5fb05f..f7b36f4b 100644 --- a/daemon/core/netns/vnodeclient.py +++ b/daemon/core/netns/vnodeclient.py @@ -6,17 +6,12 @@ by invoking the vcmd shell command. """ import os -import stat -import subprocess + +import vcmd from core import constants from core import logger -USE_VCMD_MODULE = True - -if USE_VCMD_MODULE: - import vcmd - VCMD = os.path.join(constants.CORE_BIN_DIR, "vcmd") @@ -34,12 +29,19 @@ class VnodeClient(object): """ self.name = name self.ctrlchnlname = ctrlchnlname - if USE_VCMD_MODULE: - self.cmdchnl = vcmd.VCmd(self.ctrlchnlname) - else: - self.cmdchnl = None + self.cmdchnl = vcmd.VCmd(self.ctrlchnlname) self._addr = {} + def _verify_connection(self): + """ + Checks that the vcmd client is properly connected. + + :return: nothing + :raises ValueError: when not connected + """ + if not self.connected(): + raise ValueError("vcmd not connected") + def connected(self): """ Check if node is connected or not. @@ -47,10 +49,7 @@ class VnodeClient(object): :return: True if connected, False otherwise :rtype: bool """ - if USE_VCMD_MODULE: - return self.cmdchnl.connected() - else: - return True + return self.cmdchnl.connected() def close(self): """ @@ -58,8 +57,7 @@ class VnodeClient(object): :return: nothing """ - if USE_VCMD_MODULE: - self.cmdchnl.close() + self.cmdchnl.close() def cmd(self, args, wait=True): """ @@ -70,24 +68,16 @@ class VnodeClient(object): :return: command status :rtype: int """ - if USE_VCMD_MODULE: - if not self.cmdchnl.connected(): - raise ValueError("self.cmdchnl not connected") - tmp = self.cmdchnl.qcmd(args) - if not wait: - return tmp - tmp = tmp.wait() - else: - if wait: - mode = os.P_WAIT - else: - mode = os.P_NOWAIT - tmp = os.spawnlp(mode, VCMD, VCMD, "-c", self.ctrlchnlname, "-q", "--", *args) - if not wait: - return tmp + self._verify_connection() + + # TODO: clean this up after checking return value for qcmd + tmp = self.cmdchnl.qcmd(args) + if not wait: + return tmp + tmp = tmp.wait() if tmp: - logger.warn("cmd exited with status %s: %s" % (tmp, str(args))) + logger.warn("cmd exited with status %s: %s", tmp, args) return tmp @@ -101,14 +91,16 @@ class VnodeClient(object): :return: command status and combined stdout and stderr output :rtype: tuple[int, str] """ - cmdid, cmdin, cmdout, cmderr = self.popen(args) - result = cmdout.read() - result += cmderr.read() - cmdin.close() - cmdout.close() - cmderr.close() - status = cmdid.wait() - return status, result + self._verify_connection() + + p, stdin, stdout, stderr = self.popen(args) + output = stdout.read() + stderr.read() + stdin.close() + stdout.close() + stderr.close() + status = p.wait() + + return status, output def popen(self, args): """ @@ -118,15 +110,8 @@ class VnodeClient(object): :return: popen object, stdin, stdout, and stderr :rtype: tuple """ - if USE_VCMD_MODULE: - if not self.cmdchnl.connected(): - raise ValueError("self.cmdchnl not connected") - return self.cmdchnl.popen(args) - else: - cmd = [VCMD, "-c", self.ctrlchnlname, "--"] - cmd.extend(args) - tmp = subprocess.Popen(cmd, stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.PIPE) - return tmp, tmp.stdin, tmp.stdout, tmp.stderr + self._verify_connection() + return self.cmdchnl.popen(args) def icmd(self, args): """ @@ -151,18 +136,20 @@ class VnodeClient(object): :return: command status :rtype: int """ - if not USE_VCMD_MODULE: - raise NotImplementedError - if not self.cmdchnl.connected(): - raise ValueError("self.cmdchnl not connected") + self._verify_connection() + + # TODO: clean this up after verifying redircmd return values tmp = self.cmdchnl.redircmd(infd, outfd, errfd, args) if not wait: return tmp + tmp = tmp.wait() if tmp: - logger.warn("cmd exited with status %s: %s" % (tmp, str(args))) + logger.warn("cmd exited with status %s: %s", tmp, args) + return tmp + # TODO: validate if this is ever used def term(self, sh="/bin/sh"): """ Open a terminal on a node. @@ -171,8 +158,7 @@ class VnodeClient(object): :return: terminal command result :rtype: int """ - cmd = ("xterm", "-ut", "-title", self.name, "-e", - VCMD, "-c", self.ctrlchnlname, "--", sh) + cmd = ("xterm", "-ut", "-title", self.name, "-e", VCMD, "-c", self.ctrlchnlname, "--", sh) if "SUDO_USER" in os.environ: cmd = ("su", "-s", "/bin/sh", "-c", "exec " + " ".join(map(lambda x: "'%s'" % x, cmd)), @@ -210,35 +196,36 @@ class VnodeClient(object): """ if ifname in self._addr and not rescan: return self._addr[ifname] - tmp = {"ether": [], "inet": [], "inet6": [], "inet6link": []} - cmd = [constants.IP_BIN, "addr", "show", "dev", ifname] - cmdid, cmdin, cmdout, cmderr = self.popen(cmd) - cmdin.close() - for line in cmdout: + interface = {"ether": [], "inet": [], "inet6": [], "inet6link": []} + cmd = [constants.IP_BIN, "addr", "show", "dev", ifname] + p, stdin, stdout, stderr = self.popen(cmd) + stdin.close() + + for line in stdout: line = line.strip().split() if line[0] == "link/ether": - tmp["ether"].append(line[1]) + interface["ether"].append(line[1]) elif line[0] == "inet": - tmp["inet"].append(line[1]) + interface["inet"].append(line[1]) elif line[0] == "inet6": if line[3] == "global": - tmp["inet6"].append(line[1]) + interface["inet6"].append(line[1]) elif line[3] == "link": - tmp["inet6link"].append(line[1]) + interface["inet6link"].append(line[1]) else: logger.warn("unknown scope: %s" % line[3]) - err = cmderr.read() - cmdout.close() - cmderr.close() - status = cmdid.wait() + err = stderr.read() + stdout.close() + stderr.close() + status = p.wait() if status: - logger.warn("nonzero exist status (%s) for cmd: %s" % (status, cmd)) + logger.warn("nonzero exist status (%s) for cmd: %s", status, cmd) if err: - logger.warn("error output: %s" % err) - self._addr[ifname] = tmp - return tmp + logger.warn("error output: %s", err) + self._addr[ifname] = interface + return interface def netifstats(self, ifname=None): """ @@ -250,15 +237,15 @@ class VnodeClient(object): """ stats = {} cmd = ["cat", "/proc/net/dev"] - cmdid, cmdin, cmdout, cmderr = self.popen(cmd) - cmdin.close() + p, stdin, stdout, stderr = self.popen(cmd) + stdin.close() # ignore first line - cmdout.readline() + stdout.readline() # second line has count names - tmp = cmdout.readline().strip().split("|") + tmp = stdout.readline().strip().split("|") rxkeys = tmp[1].split() txkeys = tmp[2].split() - for line in cmdout: + for line in stdout: line = line.strip().split() devname, tmp = line[0].split(":") if tmp: @@ -271,53 +258,15 @@ class VnodeClient(object): for count in txkeys: stats[devname]["tx"][count] = int(line[field]) field += 1 - err = cmderr.read() - cmdout.close() - cmderr.close() - status = cmdid.wait() + err = stderr.read() + stdout.close() + stderr.close() + status = p.wait() if status: - logger.warn("nonzero exist status (%s) for cmd: %s" % (status, cmd)) + logger.warn("nonzero exist status (%s) for cmd: %s", status, cmd) if err: - logger.warn("error output: %s" % err) + logger.warn("error output: %s", err) if ifname is not None: return stats[ifname] else: return stats - - -def createclients(sessiondir, clientcls=VnodeClient, cmdchnlfilterfunc=None): - """ - Create clients - - :param str sessiondir: session directory to create clients - :param class clientcls: class to create clients from - :param func cmdchnlfilterfunc: command channel filter function - :return: list of created clients - :rtype: list - """ - direntries = map(lambda x: os.path.join(sessiondir, x), os.listdir(sessiondir)) - cmdchnls = filter(lambda x: stat.S_ISSOCK(os.stat(x).st_mode), direntries) - if cmdchnlfilterfunc: - cmdchnls = filter(cmdchnlfilterfunc, cmdchnls) - cmdchnls.sort() - return map(lambda x: clientcls(os.path.basename(x), x), cmdchnls) - - -def createremoteclients(sessiondir, clientcls=VnodeClient, filterfunc=None): - """ - Creates remote VnodeClients, for nodes emulated on other machines. The - session.Broker writes a n1.conf/server file having the server's info. - - :param str sessiondir: session directory to create clients - :param class clientcls: class to create clients from - :param func filterfunc: filter function - :return: list of remove clients - :rtype: list - """ - direntries = map(lambda x: os.path.join(sessiondir, x), os.listdir(sessiondir)) - nodedirs = filter(lambda x: stat.S_ISDIR(os.stat(x).st_mode), direntries) - nodedirs = filter(lambda x: os.path.exists(os.path.join(x, "server")), nodedirs) - if filterfunc: - nodedirs = filter(filterfunc, nodedirs) - nodedirs.sort() - return map(lambda x: clientcls(x), nodedirs) diff --git a/daemon/core/service.py b/daemon/core/service.py index 16b7834d..b212d3e9 100644 --- a/daemon/core/service.py +++ b/daemon/core/service.py @@ -312,7 +312,7 @@ class CoreServices(ConfigurableManager): for cmd in service.getstartup(node, services): try: # NOTE: this wait=False can be problematic! - node.cmd(shlex.split(cmd), wait=False) + node.client.cmd(shlex.split(cmd), wait=False) except: logger.exception("error starting command %s", cmd) @@ -358,7 +358,7 @@ class CoreServices(ConfigurableManager): for cmd in service._startup: try: # NOTE: this wait=False can be problematic! - node.cmd(shlex.split(cmd), wait=False) + node.client.cmd(shlex.split(cmd), wait=False) except: logger.exception("error starting command %s", cmd) @@ -417,7 +417,7 @@ class CoreServices(ConfigurableManager): for cmd in validate_cmds: logger.info("validating service %s using: %s", service._name, cmd) try: - status, result = node.cmdresult(shlex.split(cmd)) + status, result = node.client.cmdresult(shlex.split(cmd)) if status != 0: raise ValueError("non-zero exit status") except: @@ -453,7 +453,7 @@ class CoreServices(ConfigurableManager): else: for cmd in service._shutdown: try: - tmp = node.cmd(shlex.split(cmd), wait=True) + tmp = node.client.cmd(shlex.split(cmd), wait=True) status += "%s" % tmp except: logger.exception("error running stop command %s", cmd) @@ -766,7 +766,7 @@ class CoreServices(ConfigurableManager): for cmd in cmds: try: # node.cmd(shlex.split(cmd), wait = False) - status = node.cmd(shlex.split(cmd), wait=True) + status = node.client.cmd(shlex.split(cmd), wait=True) if status != 0: fail += "Start %s(%s)," % (s._name, cmd) except: diff --git a/daemon/core/session.py b/daemon/core/session.py index aba53711..f4d7e01d 100644 --- a/daemon/core/session.py +++ b/daemon/core/session.py @@ -610,6 +610,7 @@ class Session(object): :param int object_id: object id to retrieve :return: object for the given id + :rtype: core.netns.vnode.SimpleLxcNode """ if object_id not in self.objects: raise KeyError("unknown object id %s" % object_id) @@ -1257,7 +1258,7 @@ class Session(object): utils.mutedetach(commands) else: node = self.get_object(node_id) - node.cmd(commands, wait=False) + node.client.cmd(commands, wait=False) def send_objects(self): """ diff --git a/daemon/examples/netns/basicrange.py b/daemon/examples/netns/basicrange.py index b1fdd2f8..a6a29348 100755 --- a/daemon/examples/netns/basicrange.py +++ b/daemon/examples/netns/basicrange.py @@ -59,7 +59,7 @@ def test(options): # launches terminal for the first node # n[0].term("bash") - n[0].icmd(["ping", "-c", "5", "127.0.0.1"]) + n[0].client.icmd(["ping", "-c", "5", "127.0.0.1"]) # wait for rate seconds to allow ebtables commands to commit time.sleep(EbtablesQueue.rate) diff --git a/daemon/examples/netns/distributed.py b/daemon/examples/netns/distributed.py index 81cc7e3f..1674b4df 100755 --- a/daemon/examples/netns/distributed.py +++ b/daemon/examples/netns/distributed.py @@ -82,7 +82,7 @@ def main(): for i in xrange(1, num_local + 1): node = session.add_object(cls=nodes.CoreNode, name="n%d" % i, objid=i) node.newnetif(switch, ["%s/%s" % (prefix.addr(i), prefix.prefixlen)]) - node.cmd([constants.SYSCTL_BIN, "net.ipv4.icmp_echo_ignore_broadcasts=0"]) + node.client.cmd([constants.SYSCTL_BIN, "net.ipv4.icmp_echo_ignore_broadcasts=0"]) node.setposition(x=150 * i, y=150) n.append(node) diff --git a/daemon/examples/netns/emane80211.py b/daemon/examples/netns/emane80211.py index e1874158..3ca5ae45 100755 --- a/daemon/examples/netns/emane80211.py +++ b/daemon/examples/netns/emane80211.py @@ -84,7 +84,7 @@ def main(): tmp = session.add_object(cls=nodes.CoreNode, name="n%d" % i, objid=i) tmp.newnetif(wlan, ["%s/%s" % (prefix.addr(i), prefix.prefixlen)]) - tmp.cmd([constants.SYSCTL_BIN, "net.ipv4.icmp_echo_ignore_broadcasts=0"]) + tmp.client.cmd([constants.SYSCTL_BIN, "net.ipv4.icmp_echo_ignore_broadcasts=0"]) tmp.setposition(x=150 * i, y=150) session.services.addservicestonode(tmp, "", services_str) n.append(tmp) diff --git a/daemon/examples/netns/howmanynodes.py b/daemon/examples/netns/howmanynodes.py index 8803d872..bff69478 100755 --- a/daemon/examples/netns/howmanynodes.py +++ b/daemon/examples/netns/howmanynodes.py @@ -159,7 +159,7 @@ def main(): try: n = session.add_object(cls=nodes.LxcNode, name="n%d" % i) n.newnetif(switch, ["%s/%s" % (prefix.addr(i), prefix.prefixlen)]) - n.cmd([constants.SYSCTL_BIN, "net.ipv4.icmp_echo_ignore_broadcasts=0"]) + n.client.cmd([constants.SYSCTL_BIN, "net.ipv4.icmp_echo_ignore_broadcasts=0"]) if options.services is not None: session.services.addservicestonode(n, "", options.services) n.boot() diff --git a/daemon/examples/netns/iperf-performance-chain.py b/daemon/examples/netns/iperf-performance-chain.py index e946f727..ddc08568 100755 --- a/daemon/examples/netns/iperf-performance-chain.py +++ b/daemon/examples/netns/iperf-performance-chain.py @@ -19,7 +19,7 @@ import optparse import sys from core import constants -from core.misc import ipaddress +from core.misc import ipaddress from core.netns import nodes # node list (count from 1) @@ -72,9 +72,9 @@ def main(): prefix = ipaddress.Ipv4Prefix("10.83.%d.0/24" % i) right = session.add_object(cls=nodes.PtpNet) tmp.newnetif(right, ["%s/%s" % (prefix.addr(1), prefix.prefixlen)]) - tmp.cmd([constants.SYSCTL_BIN, "net.ipv4.icmp_echo_ignore_broadcasts=0"]) - tmp.cmd([constants.SYSCTL_BIN, "net.ipv4.conf.all.forwarding=1"]) - tmp.cmd([constants.SYSCTL_BIN, "net.ipv4.conf.default.rp_filter=0"]) + tmp.client.cmd([constants.SYSCTL_BIN, "net.ipv4.icmp_echo_ignore_broadcasts=0"]) + tmp.client.cmd([constants.SYSCTL_BIN, "net.ipv4.conf.all.forwarding=1"]) + tmp.client.cmd([constants.SYSCTL_BIN, "net.ipv4.conf.default.rp_filter=0"]) tmp.setposition(x=100 * i, y=150) n.append(tmp) left = right diff --git a/daemon/examples/netns/ospfmanetmdrtest.py b/daemon/examples/netns/ospfmanetmdrtest.py index f51d1e91..63634c7f 100755 --- a/daemon/examples/netns/ospfmanetmdrtest.py +++ b/daemon/examples/netns/ospfmanetmdrtest.py @@ -16,6 +16,7 @@ import time from string import Template from core.constants import QUAGGA_STATE_DIR + from core.misc import ipaddress from core.misc.utils import mutecall from core.netns import nodes @@ -267,7 +268,7 @@ class ManetExperiment(object): self.nodes[i].boot() # run the boot.sh script on all nodes to start Quagga for i in xrange(numnodes): - self.nodes[i].cmd(["./%s" % self.nodes[i].bootsh]) + self.nodes[i].client.cmd(["./%s" % self.nodes[i].bootsh]) def compareroutes(self, node, kr, zr): """ Compare two lists of Route objects. @@ -386,7 +387,7 @@ class Cmd: def open(self): """ Exceute call to node.popen(). """ self.id, self.stdin, self.out, self.err = \ - self.node.popen(self.args) + self.node.client.popen(self.args) def parse(self): """ This method is overloaded by child classes and should return some @@ -409,7 +410,7 @@ class VtyshCmd(Cmd): def open(self): args = ("vtysh", "-c", self.args) - self.id, self.stdin, self.out, self.err = self.node.popen(args) + self.id, self.stdin, self.out, self.err = self.node.client.popen(args) class Ospf6NeighState(VtyshCmd): diff --git a/daemon/examples/netns/switch.py b/daemon/examples/netns/switch.py index 64137f36..66018da1 100755 --- a/daemon/examples/netns/switch.py +++ b/daemon/examples/netns/switch.py @@ -57,7 +57,7 @@ def main(): for i in xrange(1, options.numnodes + 1): tmp = session.add_object(cls=nodes.CoreNode, name="n%d" % i, objid=i) tmp.newnetif(switch, ["%s/%s" % (prefix.addr(i), prefix.prefixlen)]) - tmp.cmd([constants.SYSCTL_BIN, "net.ipv4.icmp_echo_ignore_broadcasts=0"]) + tmp.client.cmd([constants.SYSCTL_BIN, "net.ipv4.icmp_echo_ignore_broadcasts=0"]) tmp.setposition(x=150 * i, y=150) n.append(tmp) diff --git a/daemon/examples/netns/switchtest.py b/daemon/examples/netns/switchtest.py index 524ae2b0..bb050c9d 100755 --- a/daemon/examples/netns/switchtest.py +++ b/daemon/examples/netns/switchtest.py @@ -36,9 +36,9 @@ def test(numnodes, testsec): tmp = session.add_object(cls=nodes.LxcNode, name="n%d" % i) tmp.newnetif(net, ["%s/%s" % (prefix.addr(i), prefix.prefixlen)]) n.append(tmp) - n[0].cmd(["iperf", "-s", "-D"]) - n[-1].icmd(["iperf", "-t", str(int(testsec)), "-c", str(prefix.addr(1))]) - n[0].cmd(["killall", "-9", "iperf"]) + n[0].client.cmd(["iperf", "-s", "-D"]) + n[-1].client.icmd(["iperf", "-t", str(int(testsec)), "-c", str(prefix.addr(1))]) + n[0].client.cmd(["killall", "-9", "iperf"]) raw_input("press enter to exit") session.shutdown() diff --git a/daemon/examples/netns/wlanemanetests.py b/daemon/examples/netns/wlanemanetests.py index 60ad31d2..bfc06ad5 100755 --- a/daemon/examples/netns/wlanemanetests.py +++ b/daemon/examples/netns/wlanemanetests.py @@ -131,7 +131,7 @@ class Cmd(object): def open(self): """ Exceute call to node.popen(). """ - self.id, self.stdin, self.out, self.err = self.node.popen(self.args) + self.id, self.stdin, self.out, self.err = self.node.client.popen(self.args) def parse(self): """ This method is overloaded by child classes and should return some @@ -166,7 +166,7 @@ class ClientServerCmd(Cmd): self.client_open() # client status = self.client_id.wait() # stop the server - self.node.cmdresult(["killall", self.args[0]]) + self.node.client.cmdresult(["killall", self.args[0]]) r = self.parse() self.cleanup() return r @@ -174,7 +174,7 @@ class ClientServerCmd(Cmd): def client_open(self): """ Exceute call to client_node.popen(). """ self.client_id, self.client_stdin, self.client_out, self.client_err = \ - self.client_node.popen(self.client_args) + self.client_node.client.popen(self.client_args) def parse(self): """ This method is overloaded by child classes and should return some @@ -207,7 +207,7 @@ class PingCmd(Cmd): def run(self): if self.verbose: self.info("%s initial test ping (max 1 second)..." % self.node.name) - (status, result) = self.node.cmdresult(["ping", "-q", "-c", "1", "-w", "1", self.addr]) + (status, result) = self.node.client.cmdresult(["ping", "-q", "-c", "1", "-w", "1", self.addr]) if status != 0: self.warn("initial ping from %s to %s failed! result:\n%s" % (self.node.name, self.addr, result)) @@ -487,13 +487,13 @@ class Experiment(object): if i > 1: neigh_left = "%s" % prefix.addr(i - 1) cmd = routecmd + [neigh_left, "dev", node.netif(0).name] - (status, result) = node.cmdresult(cmd) + (status, result) = node.client.cmdresult(cmd) if status != 0: self.warn("failed to add interface route: %s" % cmd) if i < numnodes: neigh_right = "%s" % prefix.addr(i + 1) cmd = routecmd + [neigh_right, "dev", node.netif(0).name] - (status, result) = node.cmdresult(cmd) + (status, result) = node.client.cmdresult(cmd) if status != 0: self.warn("failed to add interface route: %s" % cmd) @@ -507,7 +507,7 @@ class Experiment(object): else: gw = neigh_right cmd = routecmd + [addr, "via", gw] - (status, result) = node.cmdresult(cmd) + (status, result) = node.client.cmdresult(cmd) if status != 0: self.warn("failed to add route: %s" % cmd) @@ -635,8 +635,8 @@ class Experiment(object): if self.verbose: self.info("%s initial test ping (max 1 second)..." % \ self.firstnode.name) - (status, result) = self.firstnode.cmdresult(["ping", "-q", "-c", "1", - "-w", "1", self.lastaddr]) + (status, result) = self.firstnode.client.cmdresult(["ping", "-q", "-c", "1", + "-w", "1", self.lastaddr]) if status != 0: self.warn("initial ping from %s to %s failed! result:\n%s" % \ (self.firstnode.name, self.lastaddr, result)) diff --git a/daemon/examples/netns/wlantest.py b/daemon/examples/netns/wlantest.py index 70892d1b..ce14377f 100755 --- a/daemon/examples/netns/wlantest.py +++ b/daemon/examples/netns/wlantest.py @@ -37,9 +37,9 @@ def test(numnodes, testsec): tmp.newnetif(net, ["%s/%s" % (prefix.addr(i), prefix.prefixlen)]) n.append(tmp) net.link(n[0].netif(0), n[-1].netif(0)) - n[0].cmd(["iperf", "-s", "-D"]) - n[-1].icmd(["iperf", "-t", str(int(testsec)), "-c", str(prefix.addr(1))]) - n[0].cmd(["killall", "-9", "iperf"]) + n[0].client.cmd(["iperf", "-s", "-D"]) + n[-1].client.icmd(["iperf", "-t", str(int(testsec)), "-c", str(prefix.addr(1))]) + n[0].client.cmd(["killall", "-9", "iperf"]) session.shutdown() diff --git a/daemon/tests/conftest.py b/daemon/tests/conftest.py index 96f740e8..3890fe32 100644 --- a/daemon/tests/conftest.py +++ b/daemon/tests/conftest.py @@ -186,18 +186,18 @@ class Core(object): def ping(self, from_name, to_name): from_node = self.nodes[from_name] to_ip = str(self.get_ip(to_name)) - return from_node.cmd(["ping", "-c", "3", to_ip]) + return from_node.client.cmd(["ping", "-c", "3", to_ip]) def ping_output(self, from_name, to_name): from_node = self.nodes[from_name] to_ip = str(self.get_ip(to_name)) - vcmd, stdin, stdout, stderr = from_node.popen(["ping", "-i", "0.05", "-c", "3", to_ip]) + vcmd, stdin, stdout, stderr = from_node.client.popen(["ping", "-i", "0.05", "-c", "3", to_ip]) return stdout.read().strip() def iping(self, from_name, to_name): from_node = self.nodes[from_name] to_ip = str(self.get_ip(to_name)) - from_node.icmd(["ping", "-i", "0.01", "-c", "10", to_ip]) + from_node.client.icmd(["ping", "-i", "0.01", "-c", "10", to_ip]) def iperf(self, from_name, to_name): from_node = self.nodes[from_name] @@ -205,9 +205,9 @@ class Core(object): to_ip = str(self.get_ip(to_name)) # run iperf server, run client, kill iperf server - vcmd, stdin, stdout, stderr = to_node.popen(["iperf", "-s", "-u", "-y", "C"]) - from_node.cmd(["iperf", "-u", "-t", "5", "-c", to_ip]) - to_node.cmd(["killall", "-9", "iperf"]) + vcmd, stdin, stdout, stderr = to_node.client.popen(["iperf", "-s", "-u", "-y", "C"]) + from_node.client.cmd(["iperf", "-u", "-t", "5", "-c", to_ip]) + to_node.client.cmd(["killall", "-9", "iperf"]) return stdout.read().strip() diff --git a/daemon/tests/test_core.py b/daemon/tests/test_core.py index f47827a0..8367c084 100644 --- a/daemon/tests/test_core.py +++ b/daemon/tests/test_core.py @@ -3,13 +3,12 @@ Unit tests for testing basic CORE networks. """ import os +import stat import threading import time - -import pytest - from xml.etree import ElementTree +import pytest from mock import MagicMock from conftest import EMANE_SERVICES @@ -18,6 +17,7 @@ from core.enumerations import MessageFlags from core.mobility import BasicRangeModel from core.netns import nodes from core.netns import vnodeclient +from core.netns.vnodeclient import VnodeClient from core.phys.pnodes import PhysicalNode from core.service import ServiceManager from core.xml import xmlsession @@ -29,6 +29,24 @@ _XML_VERSIONS = ["0.0", "1.0"] _NODE_CLASSES = [nodes.PtpNet, nodes.HubNode, nodes.SwitchNode] +def createclients(sessiondir, clientcls=VnodeClient, cmdchnlfilterfunc=None): + """ + Create clients + + :param str sessiondir: session directory to create clients + :param class clientcls: class to create clients from + :param func cmdchnlfilterfunc: command channel filter function + :return: list of created clients + :rtype: list + """ + direntries = map(lambda x: os.path.join(sessiondir, x), os.listdir(sessiondir)) + cmdchnls = filter(lambda x: stat.S_ISSOCK(os.stat(x).st_mode), direntries) + if cmdchnlfilterfunc: + cmdchnls = filter(cmdchnlfilterfunc, cmdchnls) + cmdchnls.sort() + return map(lambda x: clientcls(os.path.basename(x), x), cmdchnls) + + class TestCore: def test_import_service(self, core): """ @@ -145,7 +163,7 @@ class TestCore: # get node client for testing n1 = core.get_node("n1") - client = n1.vnodeclient + client = n1.client # instantiate session core.session.instantiate() @@ -178,7 +196,7 @@ class TestCore: assert not client.shcmd(command[0]) # check module methods - assert vnodeclient.createclients(core.session.session_dir) + assert createclients(core.session.session_dir) # check convenience methods for interface information assert client.getaddr("eth0") diff --git a/doc/scripting.rst b/doc/scripting.rst index 8db7b165..573522d0 100644 --- a/doc/scripting.rst +++ b/doc/scripting.rst @@ -47,7 +47,7 @@ Here are the basic elements of a CORE Python script: node1.newnetif(hub1, ["10.0.0.1/24"]) node2.newnetif(hub1, ["10.0.0.2/24"]) - node1.icmd(["ping", "-c", "5", "10.0.0.2"]) + node1.vnodeclient.icmd(["ping", "-c", "5", "10.0.0.2"]) session.shutdown()