diff --git a/daemon/core/api/tlv/corehandlers.py b/daemon/core/api/tlv/corehandlers.py index e520ce95..a531efe2 100644 --- a/daemon/core/api/tlv/corehandlers.py +++ b/daemon/core/api/tlv/corehandlers.py @@ -37,7 +37,7 @@ from core.emulator.enumerations import ( RegisterTlvs, SessionTlvs, ) -from core.errors import CoreError +from core.errors import CoreCommandError, CoreError from core.location.mobility import BasicRangeModel from core.nodes.network import WlanNode from core.services.coreservices import ServiceManager, ServiceShim @@ -882,16 +882,21 @@ class CoreHandler(socketserver.BaseRequestHandler): return (reply,) else: logging.info("execute message with cmd=%s", command) + command = utils.split_args(command) # execute command and send a response if ( message.flags & MessageFlags.STRING.value or message.flags & MessageFlags.TEXT.value ): - # shlex.split() handles quotes within the string if message.flags & MessageFlags.LOCAL.value: status, res = utils.cmd_output(command) else: - status, res = node.cmd_output(command) + try: + res = node.node_net_cmd(command) + status = 0 + except CoreCommandError as e: + res = e.stderr + status = e.returncode logging.info( "done exec cmd=%s with status=%d res=(%d bytes)", command, @@ -913,7 +918,7 @@ class CoreHandler(socketserver.BaseRequestHandler): if message.flags & MessageFlags.LOCAL.value: utils.mute_detach(command) else: - node.cmd(command, wait=False) + node.node_net_cmd(command, wait=False) except CoreError: logging.exception("error getting object: %s", node_num) # XXX wait and queue this message to try again later diff --git a/daemon/core/emane/emanemanager.py b/daemon/core/emane/emanemanager.py index 2902c47c..49cf4f24 100644 --- a/daemon/core/emane/emanemanager.py +++ b/daemon/core/emane/emanemanager.py @@ -772,7 +772,8 @@ class EmaneManager(ModelManager): Kill the appropriate EMANE daemons. """ # TODO: we may want to improve this if we had the PIDs from the specific EMANE daemons that we"ve started - args = ["killall", "-q", "emane"] + kill_emaned = ["killall", "-q", "emane"] + kill_transortd = ["killall", "-q", "emanetransportd"] stop_emane_on_host = False for node in self.getnodes(): if hasattr(node, "transport_type") and node.transport_type == "raw": @@ -780,13 +781,19 @@ class EmaneManager(ModelManager): continue if node.up: - node.cmd(args, wait=False) + node.node_net_cmd(kill_emaned, wait=False) # TODO: RJ45 node if stop_emane_on_host: try: - utils.check_cmd(args) - utils.check_cmd(["killall", "-q", "emanetransportd"]) + utils.check_cmd(kill_emaned) + utils.check_cmd(kill_transortd) + kill_emaned = " ".join(kill_emaned) + kill_transortd = " ".join(kill_transortd) + for server in self.session.servers: + conn = self.session[server] + distributed.remote_cmd(conn, kill_emaned) + distributed.remote_cmd(conn, kill_transortd) except CoreCommandError: logging.exception("error shutting down emane daemons") @@ -976,8 +983,13 @@ class EmaneManager(ModelManager): Return True if an EMANE process associated with the given node is running, False otherwise. """ args = ["pkill", "-0", "-x", "emane"] - status = node.cmd(args) - return status == 0 + try: + node.node_net_cmd(args) + result = True + except CoreCommandError: + result = False + + return result class EmaneGlobalModel(EmaneModel): diff --git a/daemon/core/emulator/distributed.py b/daemon/core/emulator/distributed.py index 35cbf208..abec0a57 100644 --- a/daemon/core/emulator/distributed.py +++ b/daemon/core/emulator/distributed.py @@ -10,7 +10,7 @@ from core.errors import CoreCommandError LOCK = threading.Lock() -def remote_cmd(server, cmd, env=None, cwd=None): +def remote_cmd(server, cmd, env=None, cwd=None, wait=True): """ Run command remotely using server connection. @@ -20,12 +20,18 @@ def remote_cmd(server, cmd, env=None, cwd=None): :param dict env: environment for remote command, default is None :param str cwd: directory to run command in, defaults to None, which is the user's home directory + :param bool wait: True to wait for status, False to background process :return: stdout when success :rtype: str :raises CoreCommandError: when a non-zero exit status occurs """ - logging.info("remote cmd server(%s): %s", server, cmd) + replace_env = env is not None + if not wait: + cmd += " &" + logging.info( + "remote cmd server(%s) cwd(%s) wait(%s): %s", server.host, cwd, wait, cmd + ) try: with LOCK: if cwd is None: diff --git a/daemon/core/emulator/session.py b/daemon/core/emulator/session.py index 31e75de2..9bc2ab80 100644 --- a/daemon/core/emulator/session.py +++ b/daemon/core/emulator/session.py @@ -2044,4 +2044,5 @@ class Session(object): utils.mute_detach(data) else: node = self.get_node(node_id) - node.cmd(data, wait=False) + data = utils.split_args(data) + node.node_net_cmd(data, wait=False) diff --git a/daemon/core/errors.py b/daemon/core/errors.py index bb124434..5b76abb3 100644 --- a/daemon/core/errors.py +++ b/daemon/core/errors.py @@ -10,7 +10,12 @@ class CoreCommandError(subprocess.CalledProcessError): """ def __str__(self): - return "Command(%s), Status(%s):\n%s" % (self.cmd, self.returncode, self.output) + return "Command(%s), Status(%s):\nstdout: %s\nstderr: %s" % ( + self.cmd, + self.returncode, + self.output, + self.stderr, + ) class CoreError(Exception): diff --git a/daemon/core/nodes/base.py b/daemon/core/nodes/base.py index d8cac8c5..147989d7 100644 --- a/daemon/core/nodes/base.py +++ b/daemon/core/nodes/base.py @@ -630,29 +630,31 @@ class CoreNode(CoreNodeBase): """ return self.client.cmd_output(args) - def node_net_cmd(self, args): + def node_net_cmd(self, args, wait=True): """ Runs a command that is used to configure and setup the network within a node. - :param list[str]|str args: command to run + :param list[str] args: command to run + :param bool wait: True to wait for status, False otherwise :return: combined stdout and stderr :rtype: str :raises CoreCommandError: when a non-zero exit status occurs """ if self.server is None: logging.info("node(%s) cmd: %s", self.name, args) - return self.check_cmd(args) + return self.client.check_cmd(args, wait=wait) else: args = self.client._cmd_args() + args args = " ".join(args) - return distributed.remote_cmd(self.server, args) + return distributed.remote_cmd(self.server, args, wait=wait) def check_cmd(self, args): """ Runs shell command on node. :param list[str]|str args: command to run + :param bool wait: True to wait for status, False otherwise :return: combined stdout and stderr :rtype: str :raises CoreCommandError: when a non-zero exit status occurs diff --git a/daemon/core/nodes/client.py b/daemon/core/nodes/client.py index 6b7fa44c..6c72547b 100644 --- a/daemon/core/nodes/client.py +++ b/daemon/core/nodes/client.py @@ -97,17 +97,18 @@ class VnodeClient(object): status = p.wait() return status, output.decode("utf-8").strip() - def check_cmd(self, args): + def check_cmd(self, args, wait=True): """ Run command and return exit status and combined stdout and stderr. :param list[str]|str args: command to run + :param bool wait: True to wait for command status, False otherwise :return: combined stdout and stderr :rtype: str :raises core.CoreCommandError: when there is a non-zero exit status """ status, output = self.cmd_output(args) - if status != 0: + if wait and status != 0: raise CoreCommandError(status, args, output) return output.strip() diff --git a/daemon/core/nodes/physical.py b/daemon/core/nodes/physical.py index ba8cdc25..48bd5a5a 100644 --- a/daemon/core/nodes/physical.py +++ b/daemon/core/nodes/physical.py @@ -94,7 +94,7 @@ class PhysicalNode(CoreNodeBase): return output.strip() def shcmd(self, cmdstr, sh="/bin/sh"): - return self.cmd([sh, "-c", cmdstr]) + return self.node_net_cmd([sh, "-c", cmdstr]) def sethwaddr(self, ifindex, addr): """ diff --git a/daemon/core/services/coreservices.py b/daemon/core/services/coreservices.py index b34daa73..d6eeb1b5 100644 --- a/daemon/core/services/coreservices.py +++ b/daemon/core/services/coreservices.py @@ -9,7 +9,6 @@ services. import enum import logging -import shlex import time from multiprocessing.pool import ThreadPool @@ -598,7 +597,7 @@ class CoreServices(object): status = 0 for cmd in cmds: logging.debug("validating service(%s) using: %s", service.name, cmd) - cmd = shlex.split(cmd) + cmd = utils.split_args(cmd) try: node.node_net_cmd(cmd) except CoreCommandError as e: @@ -730,11 +729,9 @@ class CoreServices(object): status = 0 for cmd in cmds: - cmd = shlex.split(cmd) + cmd = utils.split_args(cmd) try: - if wait: - cmd.append("&") - node.node_net_cmd(cmd) + node.node_net_cmd(cmd, wait) except CoreCommandError: logging.exception("error starting command") status = -1 diff --git a/daemon/examples/python/switch.py b/daemon/examples/python/switch.py index d669478d..3c6ec383 100644 --- a/daemon/examples/python/switch.py +++ b/daemon/examples/python/switch.py @@ -43,11 +43,14 @@ def example(options): last_node = session.get_node(options.nodes + 1) print("starting iperf server on node: %s" % first_node.name) - first_node.cmd(["iperf", "-s", "-D"]) + first_node.node_net_cmd(["iperf", "-s", "-D"]) first_node_address = prefixes.ip4_address(first_node) print("node %s connecting to %s" % (last_node.name, first_node_address)) - last_node.client.icmd(["iperf", "-t", str(options.time), "-c", first_node_address]) - first_node.cmd(["killall", "-9", "iperf"]) + output = last_node.node_net_cmd( + ["iperf", "-t", str(options.time), "-c", first_node_address] + ) + print(output) + first_node.node_net_cmd(["killall", "-9", "iperf"]) # shutdown session coreemu.shutdown() diff --git a/daemon/examples/python/wlan.py b/daemon/examples/python/wlan.py index 376f34d0..3d5171c2 100644 --- a/daemon/examples/python/wlan.py +++ b/daemon/examples/python/wlan.py @@ -47,11 +47,11 @@ def example(options): last_node = session.get_node(options.nodes + 1) print("starting iperf server on node: %s" % first_node.name) - first_node.cmd(["iperf", "-s", "-D"]) + first_node.node_net_cmd(["iperf", "-s", "-D"]) address = prefixes.ip4_address(first_node) print("node %s connecting to %s" % (last_node.name, address)) - last_node.client.icmd(["iperf", "-t", str(options.time), "-c", address]) - first_node.cmd(["killall", "-9", "iperf"]) + last_node.node_net_cmd(["iperf", "-t", str(options.time), "-c", address]) + first_node.node_net_cmd(["killall", "-9", "iperf"]) # shutdown session coreemu.shutdown() diff --git a/daemon/tests/emane/test_emane.py b/daemon/tests/emane/test_emane.py index 85836605..d9001065 100644 --- a/daemon/tests/emane/test_emane.py +++ b/daemon/tests/emane/test_emane.py @@ -26,7 +26,7 @@ _DIR = os.path.dirname(os.path.abspath(__file__)) def ping(from_node, to_node, ip_prefixes, count=3): address = ip_prefixes.ip4_address(to_node) - return from_node.cmd(["ping", "-c", str(count), address]) + return from_node.node_net_cmd(["ping", "-c", str(count), address]) class TestEmane: diff --git a/daemon/tests/test_core.py b/daemon/tests/test_core.py index e120dcc8..7d64ae69 100644 --- a/daemon/tests/test_core.py +++ b/daemon/tests/test_core.py @@ -38,7 +38,7 @@ def createclients(sessiondir, clientcls=VnodeClient, cmdchnlfilterfunc=None): def ping(from_node, to_node, ip_prefixes): address = ip_prefixes.ip4_address(to_node) - return from_node.cmd(["ping", "-c", "3", address]) + return from_node.node_net_cmd(["ping", "-c", "3", address]) class TestCore: @@ -102,7 +102,6 @@ class TestCore: # check various command using vcmd module command = ["ls"] - assert not client.cmd(command) status, output = client.cmd_output(command) assert not status p, stdin, stdout, stderr = client.popen(command) @@ -110,7 +109,6 @@ class TestCore: assert not client.icmd(command) # check various command using command line - assert not client.cmd(command) status, output = client.cmd_output(command) assert not status p, stdin, stdout, stderr = client.popen(command)