updates to consolidate commands that need to be defined by a new node type

This commit is contained in:
Blake J. Harnden 2018-03-01 13:21:25 -08:00
parent 0b8ee7760d
commit d3bd61ddcf
25 changed files with 314 additions and 175 deletions

View file

@ -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.client.termcmdstring(command)
res = node.termcmdstring(command)
tlv_data += coreapi.CoreExecuteTlv.pack(ExecuteTlvs.RESULT.value, res)
reply = coreapi.CoreExecMessage.pack(MessageFlags.TTY.value, tlv_data)
return reply,
@ -1097,9 +1097,9 @@ class CoreRequestHandler(SocketServer.BaseRequestHandler):
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.cmdresult(shlex.split(command))
status, res = utils.cmd_output(command)
else:
status, res = node.client.cmdresult(shlex.split(command))
status, res = node.cmd_output(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)
@ -1110,9 +1110,10 @@ class CoreRequestHandler(SocketServer.BaseRequestHandler):
# execute the command with no response
else:
if message.flags & MessageFlags.LOCAL.value:
# TODO: get this consolidated into utils
utils.mutedetach(shlex.split(command))
else:
node.client.cmd(shlex.split(command), wait=False)
node.cmd(command, wait=False)
except KeyError:
logger.exception("error getting object: %s", node_num)
# XXX wait and queue this message to try again later

View file

@ -412,6 +412,47 @@ class PyCoreNode(PyCoreObj):
return common
def check_cmd(self, cmd):
"""
Runs shell command on node.
:param list[str]/str cmd: command to run
:return: exist status and combined stdout and stderr
:rtype: tuple[int, str]
:raises subprocess.CalledProcessError: when a non-zero exit status occurs
"""
raise NotImplementedError
def cmd(self, cmd, wait=True):
"""
Runs shell command on node, with option to not wait for a result.
:param list[str]/str cmd: command to run
:param bool wait: wait for command to exit, defaults to True
:return: exit status for command
:rtype: int
"""
raise NotImplementedError
def cmd_output(self, cmd):
"""
Runs shell command on node and get exit status and output.
:param list[str]/str cmd: command to run
:return: exit status and combined stdout and stderr
:rtype: tuple[int, str]
"""
raise NotImplementedError
def termcmdstring(self, sh):
"""
Create a terminal command string.
:param str sh: shell to execute command in
:return: str
"""
raise NotImplementedError
class PyCoreNet(PyCoreObj):
"""

View file

@ -24,7 +24,7 @@ def emane_version():
cmd = ("emane", "--version")
try:
status, result = utils.cmdresult(cmd)
status, result = utils.cmd_output(cmd)
except (OSError, subprocess.CalledProcessError):
logger.exception("error checking emane version")
status = -1

View file

@ -103,7 +103,6 @@ class EmaneManager(ConfigurableManager):
emane_models = utils.load_classes(custom_models_path, EmaneModel)
self.load_models(emane_models)
def logversion(self):
"""
Log the installed EMANE version.
@ -912,16 +911,23 @@ class EmaneManager(ConfigurableManager):
# multicast route is needed for OTA data
cmd = [constants.IP_BIN, "route", "add", otagroup, "dev", otadev]
node.client.cmd(cmd, wait=True)
try:
node.check_cmd(cmd)
except subprocess.CalledProcessError:
logger.exception("error adding route for OTA data")
# 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.client.cmd(cmd, wait=True)
try:
node.check_cmd(cmd)
except subprocess.CalledProcessError:
logger.exception("error adding route for event data")
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.client.cmdresult(cmd)
status, output = node.check_cmd(cmd)
logger.info("Emane.startdaemons2() return code %d" % status)
logger.info("Emane.startdaemons2() output: %s" % output)
except subprocess.CalledProcessError:
@ -934,8 +940,8 @@ class EmaneManager(ConfigurableManager):
try:
emanecmd += ["-f", os.path.join(path, "emane.log")]
cmd = emanecmd + [os.path.join(path, "platform.xml")]
logger.info("Emane.startdaemons2() running %s" % str(cmd))
subprocess.check_call(cmd, cwd=path)
logger.info("Emane.startdaemons2() running %s" % cmd)
utils.check_cmd(cmd, cwd=path)
except subprocess.CalledProcessError:
logger.exception("error starting emane")
@ -954,7 +960,7 @@ class EmaneManager(ConfigurableManager):
stop_emane_on_host = True
continue
if node.up:
node.client.cmd(cmd, wait=False)
node.cmd(cmd, wait=False)
# TODO: RJ45 node
else:
stop_emane_on_host = True
@ -1158,10 +1164,10 @@ class EmaneManager(ConfigurableManager):
try:
if emane.VERSION < emane.EMANE092:
status = subprocess.call(cmd)
status = subprocess.check_call(cmd)
else:
status = node.client.cmd(cmd, wait=True)
except IOError:
status, _ = node.check_cmd(cmd)
except subprocess.CalledProcessError:
logger.exception("error checking if emane is running")
return status == 0

View file

@ -5,6 +5,7 @@ Miscellaneous utility functions, wrappers around some subprocess procedures.
import importlib
import inspect
import os
import shlex
import subprocess
import sys
@ -111,6 +112,20 @@ def maketuplefromstr(s, value_type):
return tuple(value_type(i) for i in values)
def split_cmd(cmd):
"""
Convenience method for splitting potential string commands into a shell-like syntax list.
:param list/str cmd: command list or string
:return: shell-like syntax list
:rtype: list
"""
# split shell string to shell array for convenience
if type(cmd) == str:
cmd = shlex.split(cmd)
return cmd
def mutecall(*args, **kwargs):
"""
Run a muted call command.
@ -129,11 +144,12 @@ def check_alloutput(cmd, **kwargs):
"""
Convenience wrapper to run subprocess.check_output and include stderr as well.
:param list[str] cmd: command arguments to run
:param list[str]/str cmd: command arguments to run
:param dict kwargs: option for running subprocess.check_output, beyond setting stderr to stdout
:return: combined stdout and stderr
:raises subprocess.CalledProcessError: when a non-zero exit status is encountered
"""
cmd = split_cmd(cmd)
kwargs["stderr"] = subprocess.STDOUT
return subprocess.check_output(cmd, **kwargs)
@ -218,20 +234,43 @@ def mutedetach(*args, **kwargs):
return subprocess.Popen(*args, **kwargs).pid
def cmdresult(args):
def cmd_output(cmd):
"""
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
:param list[str]/str cmd: 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)
# err will always be None
result, err = cmdid.communicate()
status = cmdid.wait()
return status, result
# split shell string to shell array for convenience
cmd = split_cmd(cmd)
p = subprocess.Popen(cmd, stdin=open(os.devnull, "r"), stdout=subprocess.PIPE, stderr=subprocess.STDOUT)
stdout, _ = p.communicate()
status = p.wait()
return status, stdout
def check_cmd(cmd, **kwargs):
"""
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[str]/str cmd: command arguments
:param dict kwargs: keyword arguments to pass to subprocess.Popen
:return: command status and stdout
:rtype: tuple[int, str]
:raises subprocess.CalledProcessError: when there is a non-zero exit status
"""
kwargs["stdout"] = subprocess.PIPE
kwargs["stderr"] = subprocess.STDOUT
cmd = split_cmd(cmd)
p = subprocess.Popen(cmd, **kwargs)
stdout, _ = p.communicate()
status = p.wait()
if status:
raise subprocess.CalledProcessError(status, cmd, stdout)
return status, stdout
def hexdump(s, bytes_per_word=2, words_per_line=8):

View file

@ -103,7 +103,7 @@ class CtrlNet(LxBrNet):
:return: True if an old bridge was detected, False otherwise
:rtype: bool
"""
retstat, retstr = utils.cmdresult([constants.BRCTL_BIN, "show"])
retstat, retstr = utils.cmd_output([constants.BRCTL_BIN, "show"])
if retstat != 0:
logger.error("Unable to retrieve list of installed bridges")
lines = retstr.split("\n")

View file

@ -406,7 +406,7 @@ class OvsCtrlNet(OvsNet):
Check if there are old control net bridges and delete them
"""
status, output = utils.cmdresult([constants.OVS_BIN, "list-br"])
status, output = utils.cmd_output([constants.OVS_BIN, "list-br"])
output = output.strip()
if output:
for line in output.split("\n"):

View file

@ -59,10 +59,16 @@ class VEth(PyCoreNetIf):
"""
if not self.up:
return
if self.node:
self.node.client.cmd([constants.IP_BIN, "-6", "addr", "flush", "dev", self.name])
try:
self.node.check_cmd([constants.IP_BIN, "-6", "addr", "flush", "dev", self.name])
except subprocess.CalledProcessError:
logger.exception("error shutting down interface")
if self.localname:
utils.mutedetach([constants.IP_BIN, "link", "delete", self.localname])
self.up = False
@ -98,10 +104,10 @@ class TunTap(PyCoreNetIf):
"""
# TODO: more sophisticated TAP creation here
# Debian does not support -p (tap) option, RedHat does.
# For now, this is disabled to allow the TAP to be created by another
# system (e.g. EMANE"s emanetransportd)
# check_call(["tunctl", "-t", self.name])
# self.install()
# For now, this is disabled to allow the TAP to be created by another
# system (e.g. EMANE"s emanetransportd)
# check_call(["tunctl", "-t", self.name])
# self.install()
self.up = True
def shutdown(self):
@ -112,9 +118,12 @@ class TunTap(PyCoreNetIf):
"""
if not self.up:
return
self.node.client.cmd([constants.IP_BIN, "-6", "addr", "flush", "dev", self.name])
# if self.name:
# mutedetach(["tunctl", "-d", self.localname])
try:
self.node.check_cmd([constants.IP_BIN, "-6", "addr", "flush", "dev", self.name])
except subprocess.CalledProcessError:
logger.exception("error shutting down tunnel tap")
self.up = False
def waitfor(self, func, attempts=10, maxretrydelay=0.25):
@ -169,7 +178,7 @@ class TunTap(PyCoreNetIf):
def nodedevexists():
cmd = [constants.IP_BIN, "link", "show", self.name]
return self.node.client.cmd(cmd)
return self.node.cmd(cmd)
count = 0
while True:
@ -199,15 +208,11 @@ class TunTap(PyCoreNetIf):
netns = str(self.node.pid)
try:
subprocess.check_call([constants.IP_BIN, "link", "set", self.localname, "netns", netns])
utils.check_cmd([constants.IP_BIN, "link", "set", self.localname, "netns", netns])
self.node.check_cmd([constants.IP_BIN, "link", "set", self.localname, "name", self.name])
self.node.check_cmd([constants.IP_BIN, "link", "set", self.name, "up"])
except subprocess.CalledProcessError:
msg = "error installing TAP interface %s, command:" % self.localname
msg += "ip link set %s netns %s" % (self.localname, netns)
logger.exception(msg)
return
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"])
logger.exception("error installing TAP interface")
def setaddrs(self):
"""
@ -217,7 +222,10 @@ class TunTap(PyCoreNetIf):
"""
self.waitfordevicenode()
for addr in self.addrlist:
self.node.client.cmd([constants.IP_BIN, "addr", "add", str(addr), "dev", self.name])
try:
self.node.check_cmd([constants.IP_BIN, "addr", "add", str(addr), "dev", self.name])
except subprocess.CalledProcessError:
logger.exception("failure setting interface address")
class GreTap(PyCoreNetIf):

View file

@ -114,11 +114,11 @@ class SimpleLxcNode(PyCoreNode):
try:
# bring up the loopback interface
logger.info("bringing up loopback interface")
self.client.check_alloutput([constants.IP_BIN, "link", "set", "lo", "up"])
self.check_cmd([constants.IP_BIN, "link", "set", "lo", "up"])
# set hostname for node
logger.info("setting hostname: %s" % self.name)
self.client.check_alloutput(["hostname", self.name])
self.check_cmd(["hostname", self.name])
except subprocess.CalledProcessError:
logger.exception("error setting up loopback and hostname: %s")
@ -171,6 +171,47 @@ class SimpleLxcNode(PyCoreNode):
"""
pass
def cmd(self, cmd, wait=True):
"""
Runs shell command on node, with option to not wait for a result.
:param list[str]/str cmd: command to run
:param bool wait: wait for command to exit, defaults to True
:return: exit status for command
:rtype: int
"""
return self.client.cmd(cmd, wait)
def cmd_output(self, cmd):
"""
Runs shell command on node and get exit status and output.
:param list[str]/str cmd: command to run
:return: exit status and combined stdout and stderr
:rtype: tuple[int, str]
"""
return self.client.cmd_output(cmd)
def check_cmd(self, cmd):
"""
Runs shell command on node.
:param list[str]/str cmd: command to run
:return: exist status and combined stdout and stderr
:rtype: tuple[int, str]
:raises subprocess.CalledProcessError: when a non-zero exit status occurs
"""
return self.client.check_cmd(cmd)
def termcmdstring(self, sh="/bin/sh"):
"""
Create a terminal command string.
:param str sh: shell to execute command in
:return: str
"""
return self.client.termcmdstring(sh)
# TODO: should change how this exception is just swallowed up
def mount(self, source, target):
"""
@ -200,7 +241,7 @@ class SimpleLxcNode(PyCoreNode):
"""
logger.info("unmounting: %s", target)
try:
self.client.check_alloutput([constants.UMOUNT_BIN, "-n", "-l", target])
self.check_cmd([constants.UMOUNT_BIN, "-n", "-l", target])
except subprocess.CalledProcessError:
logger.exception("error during unmount")
@ -247,8 +288,8 @@ class SimpleLxcNode(PyCoreNode):
if self.up:
try:
utils.check_alloutput([constants.IP_BIN, "link", "set", veth.name, "netns", str(self.pid)])
self.client.check_alloutput([constants.IP_BIN, "link", "set", veth.name, "name", ifname])
utils.check_cmd([constants.IP_BIN, "link", "set", veth.name, "netns", str(self.pid)])
self.check_cmd([constants.IP_BIN, "link", "set", veth.name, "name", ifname])
except subprocess.CalledProcessError:
logger.exception("failure setting eth name")
@ -258,7 +299,7 @@ class SimpleLxcNode(PyCoreNode):
# TODO: potentially find better way to query interface ID
# retrieve interface information
try:
output = self.client.check_alloutput(["ip", "link", "show", veth.name])
_, output = self.check_cmd(["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
@ -320,7 +361,7 @@ class SimpleLxcNode(PyCoreNode):
if self.up:
cmd = [constants.IP_BIN, "link", "set", "dev", self.ifname(ifindex), "address", str(addr)]
try:
self.client.check_alloutput(cmd)
self.check_cmd(cmd)
except subprocess.CalledProcessError:
logger.exception("error setting MAC address %s: %s", addr)
@ -337,10 +378,10 @@ class SimpleLxcNode(PyCoreNode):
# check if addr is ipv6
if ":" in str(addr):
cmd = [constants.IP_BIN, "addr", "add", str(addr), "dev", self.ifname(ifindex)]
self.client.check_alloutput(cmd)
self.check_cmd(cmd)
else:
cmd = [constants.IP_BIN, "addr", "add", str(addr), "broadcast", "+", "dev", self.ifname(ifindex)]
self.client.check_alloutput(cmd)
self.check_cmd(cmd)
except subprocess.CalledProcessError:
logger.exception("failure adding interface address")
@ -361,7 +402,7 @@ class SimpleLxcNode(PyCoreNode):
if self.up:
try:
self.client.check_alloutput([constants.IP_BIN, "addr", "del", str(addr), "dev", self.ifname(ifindex)])
self.check_cmd([constants.IP_BIN, "addr", "del", str(addr), "dev", self.ifname(ifindex)])
except subprocess.CalledProcessError:
logger.exception("failure deleting address")
@ -394,7 +435,7 @@ class SimpleLxcNode(PyCoreNode):
"""
if self.up:
try:
self.client.check_alloutput([constants.IP_BIN, "link", "set", self.ifname(ifindex), "up"])
self.check_cmd([constants.IP_BIN, "link", "set", self.ifname(ifindex), "up"])
except subprocess.CalledProcessError:
logger.exception("failure bringing interface up")
@ -454,15 +495,15 @@ class SimpleLxcNode(PyCoreNode):
tmplen = 8
tmp1 = "tmp." + "".join([random.choice(string.ascii_lowercase) for _ in xrange(tmplen)])
tmp2 = "tmp." + "".join([random.choice(string.ascii_lowercase) for _ in xrange(tmplen)])
utils.check_alloutput([constants.IP_BIN, "link", "add", "name", tmp1, "type", "veth", "peer", "name", tmp2])
utils.check_cmd([constants.IP_BIN, "link", "add", "name", tmp1, "type", "veth", "peer", "name", tmp2])
utils.check_alloutput([constants.IP_BIN, "link", "set", tmp1, "netns", str(self.pid)])
self.client.check_alloutput([constants.IP_BIN, "link", "set", tmp1, "name", ifname])
utils.check_cmd([constants.IP_BIN, "link", "set", tmp1, "netns", str(self.pid)])
self.check_cmd([constants.IP_BIN, "link", "set", tmp1, "name", ifname])
interface = PyCoreNetIf(node=self, name=ifname, mtu=_DEFAULT_MTU)
self.addnetif(interface, self.newifindex())
utils.check_alloutput([constants.IP_BIN, "link", "set", tmp2, "netns", str(othernode.pid)])
othernode.client.check_alloutput([constants.IP_BIN, "link", "set", tmp2, "name", otherifname])
utils.check_cmd([constants.IP_BIN, "link", "set", tmp2, "netns", str(othernode.pid)])
othernode.check_cmd([constants.IP_BIN, "link", "set", tmp2, "name", otherifname])
other_interface = PyCoreNetIf(node=othernode, name=otherifname, mtu=_DEFAULT_MTU)
othernode.addnetif(other_interface, othernode.newifindex())

View file

@ -6,13 +6,13 @@ by invoking the vcmd shell command.
"""
import os
import shlex
import subprocess
import vcmd
from core import constants
from core import logger
from core.misc import utils
VCMD = os.path.join(constants.CORE_BIN_DIR, "vcmd")
@ -61,89 +61,86 @@ class VnodeClient(object):
"""
self.cmdchnl.close()
def cmd(self, args, wait=True):
def cmd(self, cmd, wait=True):
"""
Execute a command on a node and return the status (return code).
:param list args: command arguments
:param list[str]/str cmd: command arguments
:param bool wait: wait for command to end or not
:return: command status
:rtype: int
"""
self._verify_connection()
cmd = utils.split_cmd(cmd)
# run command, return process when not waiting
p = self.cmdchnl.qcmd(args)
p = self.cmdchnl.qcmd(cmd)
if not wait:
return p
return 0
# wait for and return exit status
status = p.wait()
if status:
logger.warn("cmd exited with status %s: %s", status, args)
logger.warn("cmd exited with status %s: %s", status, cmd)
return status
def cmdresult(self, cmd):
def cmd_output(self, cmd):
"""
Execute a command on a node and return a tuple containing the
exit status and result string. stderr output
is folded into the stdout result string.
:param list cmd: command arguments
:param list[str]/str cmd: command to run
:return: command status and combined stdout and stderr output
:rtype: tuple[int, str]
"""
self._verify_connection()
# split shell string to shell array for convenience
if type(cmd) == str:
cmd = shlex.split(cmd)
p, stdin, stdout, stderr = self.popen(cmd)
stdin.close()
output = stdout.read() + stderr.read()
stdout.close()
stderr.close()
status = p.wait()
return status, output
def check_alloutput(self, cmd):
def check_cmd(self, cmd):
"""
Run command and return output, raises exception when non-zero exit status is encountered.
Run command and return exit status and combined stdout and stderr.
:param cmd:
:return: combined stdout and stderr combined
:rtype: str
:param list[str]/str cmd: command to run
:return: exit status and combined stdout and stderr
:rtype: tuple[int, str]
:raises subprocess.CalledProcessError: when there is a non-zero exit status
"""
status, output = self.cmdresult(cmd)
status, output = self.cmd_output(cmd)
if status:
raise subprocess.CalledProcessError(status, cmd, output)
return output
return status, output
def popen(self, args):
def popen(self, cmd):
"""
Execute a popen command against the node.
:param list args: command arguments
:param list[str]/str cmd: command arguments
:return: popen object, stdin, stdout, and stderr
:rtype: tuple
"""
self._verify_connection()
return self.cmdchnl.popen(args)
cmd = utils.split_cmd(cmd)
return self.cmdchnl.popen(cmd)
def icmd(self, args):
def icmd(self, cmd):
"""
Execute an icmd against a node.
:param list args: command arguments
:param list[str]/str cmd: command arguments
:return: command result
:rtype: int
"""
return os.spawnlp(os.P_WAIT, VCMD, VCMD, "-c", self.ctrlchnlname, "--", *args)
cmd = utils.split_cmd(cmd)
return os.spawnlp(os.P_WAIT, VCMD, VCMD, "-c", self.ctrlchnlname, "--", *cmd)
def redircmd(self, infd, outfd, errfd, args, wait=True):
def redircmd(self, infd, outfd, errfd, cmd, wait=True):
"""
Execute a command on a node with standard input, output, and
error redirected according to the given file descriptors.
@ -151,7 +148,7 @@ class VnodeClient(object):
:param infd: stdin file descriptor
:param outfd: stdout file descriptor
:param errfd: stderr file descriptor
:param list args: command arguments
:param list[str]/str cmd: command arguments
:param bool wait: wait flag
:return: command status
:rtype: int
@ -159,14 +156,15 @@ class VnodeClient(object):
self._verify_connection()
# run command, return process when not waiting
p = self.cmdchnl.redircmd(infd, outfd, errfd, args)
cmd = utils.split_cmd(cmd)
p = self.cmdchnl.redircmd(infd, outfd, errfd, cmd)
if not wait:
return p
# wait for and return exit status
status = p.wait()
if status:
logger.warn("cmd exited with status %s: %s", status, args)
logger.warn("cmd exited with status %s: %s", status, cmd)
return status
def term(self, sh="/bin/sh"):
@ -193,16 +191,16 @@ class VnodeClient(object):
"""
return "%s -c %s -- %s" % (VCMD, self.ctrlchnlname, sh)
def shcmd(self, cmdstr, sh="/bin/sh"):
def shcmd(self, cmd, sh="/bin/sh"):
"""
Execute a shell command.
:param str cmdstr: command string
:param str cmd: command string
:param str sh: shell to run command in
:return: command result
:rtype: int
"""
return self.cmd([sh, "-c", cmdstr])
return self.cmd([sh, "-c", cmd])
def shcmd_result(self, cmd, sh="/bin/sh"):
"""
@ -213,7 +211,7 @@ class VnodeClient(object):
:return: exist status and combined output
:rtype: tuple[int, str]
"""
return self.cmdresult([sh, "-c", cmd])
return self.cmd_output([sh, "-c", cmd])
def getaddr(self, ifname, rescan=False):
"""

View file

@ -65,29 +65,46 @@ class PhysicalNode(PyCoreNode):
run a command on the physical node
"""
os.chdir(self.nodedir)
status = -1
try:
if wait:
# os.spawnlp(os.P_WAIT, args)
subprocess.call(args)
status = subprocess.call(args)
else:
# os.spawnlp(os.P_NOWAIT, args)
subprocess.Popen(args)
status = 0
except subprocess.CalledProcessError:
logger.exception("cmd exited with status: %s", str(args))
logger.exception("cmd exited with status: %s", args)
def cmdresult(self, args):
return status
def cmd_output(self, args):
"""
run a command on the physical node and get the result
"""
os.chdir(self.nodedir)
# in Python 2.7 we can use subprocess.check_output() here
tmp = subprocess.Popen(args, stdin=open(os.devnull, 'r'),
stdout=subprocess.PIPE,
stderr=subprocess.STDOUT)
p = subprocess.Popen(args, stdout=subprocess.PIPE, stderr=subprocess.STDOUT)
# err will always be None
result, err = tmp.communicate()
status = tmp.wait()
return status, result
stdout, err = p.communicate()
status = p.wait()
return status, stdout
def check_cmd(self, cmd):
"""
Runs shell command on node.
:param list[str]/str cmd: command to run
:return: exist status and combined stdout and stderr
:rtype: tuple[int, str]
:raises subprocess.CalledProcessError: when a non-zero exit status occurs
"""
status, output = self.cmd_output(cmd)
if status:
raise subprocess.CalledProcessError(status, cmd, output)
return status, output
def shcmd(self, cmdstr, sh="/bin/sh"):
return self.cmd([sh, "-c", cmdstr])
@ -99,10 +116,10 @@ class PhysicalNode(PyCoreNode):
self._netif[ifindex].sethwaddr(addr)
ifname = self.ifname(ifindex)
if self.up:
(status, result) = self.cmdresult(
[constants.IP_BIN, "link", "set", "dev", ifname, "address", str(addr)])
if status:
logger.error("error setting MAC address %s", str(addr))
try:
self.check_cmd([constants.IP_BIN, "link", "set", "dev", ifname, "address", str(addr)])
except subprocess.CalledProcessError:
logger.exception("error setting MAC address %s", addr)
def addaddr(self, ifindex, addr):
"""

View file

@ -6,8 +6,7 @@ The CoreServices class handles configuration messages for sending
a list of available services to the GUI and for configuring individual
services.
"""
import shlex
import subprocess
import time
from itertools import repeat
@ -307,8 +306,8 @@ class CoreServices(ConfigurableManager):
for cmd in service.getstartup(node, services):
try:
# NOTE: this wait=False can be problematic!
node.client.cmd(shlex.split(cmd), wait=False)
# TODO: this wait=False can be problematic!
node.cmd(cmd, wait=False)
except:
logger.exception("error starting command %s", cmd)
@ -353,8 +352,8 @@ class CoreServices(ConfigurableManager):
for cmd in service._startup:
try:
# NOTE: this wait=False can be problematic!
node.client.cmd(shlex.split(cmd), wait=False)
# TODO: this wait=False can be problematic!
node.cmd(cmd, wait=False)
except:
logger.exception("error starting command %s", cmd)
@ -413,11 +412,9 @@ class CoreServices(ConfigurableManager):
for cmd in validate_cmds:
logger.info("validating service %s using: %s", service._name, cmd)
try:
status, result = node.client.cmdresult(shlex.split(cmd))
if status != 0:
raise ValueError("non-zero exit status")
except:
logger.exception("validate command failed: %s", cmd)
status, _ = node.check_cmd(cmd)
except subprocess.CalledProcessError:
logger.exception("validate command failed")
status = -1
return status
@ -449,11 +446,12 @@ class CoreServices(ConfigurableManager):
else:
for cmd in service._shutdown:
try:
tmp = node.client.cmd(shlex.split(cmd), wait=True)
status += "%s" % tmp
except:
status, _ = node.check_cmd(cmd)
status = str(status)
except subprocess.CalledProcessError:
logger.exception("error running stop command %s", cmd)
status += "-1"
# TODO: determine if its ok to just return the bad exit status
status = "-1"
return status
def configure_request(self, config_data):
@ -761,13 +759,10 @@ class CoreServices(ConfigurableManager):
if len(cmds) > 0:
for cmd in cmds:
try:
# node.cmd(shlex.split(cmd), wait = False)
status = node.client.cmd(shlex.split(cmd), wait=True)
if status != 0:
fail += "Start %s(%s)," % (s._name, cmd)
except:
node.check_cmd(cmd)
except subprocess.CalledProcessError:
logger.exception("error starting command %s", cmd)
fail += "Start %s," % s._name
fail += "Start %s(%s)," % (s._name, cmd)
if event_type == EventTypes.PAUSE.value:
status = self.validatenodeservice(node, s, services)
if status != 0:

View file

@ -418,7 +418,7 @@ class HttpService(UtilService):
Detect the apache2 version using the 'a2query' command.
"""
try:
status, result = utils.cmdresult(['a2query', '-v'])
status, result = utils.cmd_output(['a2query', '-v'])
except subprocess.CalledProcessError:
status = -1

View file

@ -530,7 +530,7 @@ class Session(object):
try:
utils.readfileintodict(environment_user_file, env)
except IOError:
logger.exception("error reading user core environment settings file: %s", environment_user_file)
logger.warn("error reading user core environment settings file: %s", environment_user_file)
return env
@ -609,7 +609,7 @@ class Session(object):
:param int object_id: object id to retrieve
:return: object for the given id
:rtype: core.netns.vnode.LxcNode
:rtype: core.coreobj.PyCoreNode
"""
if object_id not in self.objects:
raise KeyError("unknown object id %s" % object_id)
@ -1238,13 +1238,14 @@ class Session(object):
name = ""
logger.info("scheduled event %s at time %s data=%s", name, event_time + current_time, data)
# TODO: if data is None, this blows up, but this ties into how event functions are ran, need to clean that up
def run_event(self, node_id=None, name=None, data=None):
"""
Run a scheduled event, executing commands in the data string.
:param int node_id: node id to run event
:param str name: event name
:param data: event data
:param str data: event data
:return: nothing
"""
now = self.runtime()
@ -1252,12 +1253,13 @@ class Session(object):
name = ""
logger.info("running event %s at time %s cmd=%s" % (name, now, data))
commands = shlex.split(data)
if not node_id:
# TODO: look to consolidate shlex to utils
commands = shlex.split(data)
utils.mutedetach(commands)
else:
node = self.get_object(node_id)
node.client.cmd(commands, wait=False)
node.cmd(data, wait=False)
def send_objects(self):
"""