Merge pull request #476 from coreemu/cleanup/executables

Cleanup/executables
This commit is contained in:
bharnden 2020-06-23 09:46:48 -07:00 committed by GitHub
commit 29a69e8b40
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
14 changed files with 117 additions and 99 deletions

View file

@ -18,7 +18,7 @@ jobs:
cd daemon
cp setup.py.in setup.py
cp core/constants.py.in core/constants.py
sed -i 's/True/False/g' core/constants.py
sed -i 's/required=True/required=False/g' core/emulator/coreemu.py
pipenv sync --dev
- name: isort
run: |

View file

@ -52,10 +52,8 @@ class ConfigServiceManager:
for executable in service.executables:
try:
utils.which(executable, required=True)
except ValueError:
raise CoreError(
f"service({service.name}) missing executable {executable}"
)
except CoreError as e:
raise CoreError(f"config service({service.name}): {e}")
# make service available
self.services[name] = service

View file

@ -1,17 +1,3 @@
from core.utils import which
COREDPY_VERSION = "@PACKAGE_VERSION@"
CORE_CONF_DIR = "@CORE_CONF_DIR@"
CORE_DATA_DIR = "@CORE_DATA_DIR@"
VNODED_BIN = which("vnoded", required=True)
VCMD_BIN = which("vcmd", required=True)
SYSCTL_BIN = which("sysctl", required=True)
IP_BIN = which("ip", required=True)
ETHTOOL_BIN = which("ethtool", required=True)
TC_BIN = which("tc", required=True)
EBTABLES_BIN = which("ebtables", required=True)
MOUNT_BIN = which("mount", required=True)
UMOUNT_BIN = which("umount", required=True)
OVS_BIN = which("ovs-vsctl", required=False)
OVS_FLOW_BIN = which("ovs-ofctl", required=False)

View file

@ -6,9 +6,10 @@ import sys
from typing import Dict, List, Type
import core.services
from core import configservices
from core import configservices, utils
from core.configservice.manager import ConfigServiceManager
from core.emulator.session import Session
from core.executables import COMMON_REQUIREMENTS, OVS_REQUIREMENTS, VCMD_REQUIREMENTS
from core.services.coreservices import ServiceManager
@ -65,10 +66,34 @@ class CoreEmu:
if custom_dir:
self.service_manager.load(custom_dir)
# check executables exist on path
self._validate_env()
# catch exit event
atexit.register(self.shutdown)
def _validate_env(self) -> None:
"""
Validates executables CORE depends on exist on path.
:return: nothing
:raises core.errors.CoreError: when an executable does not exist on path
"""
requirements = COMMON_REQUIREMENTS
use_ovs = self.config.get("ovs") == "True"
if use_ovs:
requirements += OVS_REQUIREMENTS
else:
requirements += VCMD_REQUIREMENTS
for requirement in requirements:
utils.which(requirement, required=True)
def load_services(self) -> None:
"""
Loads default and custom services for use within CORE.
:return: nothing
"""
# load default services
self.service_errors = core.services.load()

View file

@ -0,0 +1,16 @@
from typing import List
VNODED: str = "vnoded"
VCMD: str = "vcmd"
SYSCTL: str = "sysctl"
IP: str = "ip"
ETHTOOL: str = "ethtool"
TC: str = "tc"
EBTABLES: str = "ebtables"
MOUNT: str = "mount"
UMOUNT: str = "umount"
OVS_VSCTL: str = "ovs-vsctl"
COMMON_REQUIREMENTS: List[str] = [SYSCTL, IP, ETHTOOL, TC, EBTABLES, MOUNT, UMOUNT]
VCMD_REQUIREMENTS: List[str] = [VNODED, VCMD]
OVS_REQUIREMENTS: List[str] = [OVS_VSCTL]

View file

@ -13,10 +13,10 @@ import netaddr
from core import utils
from core.configservice.dependencies import ConfigServiceDependencies
from core.constants import MOUNT_BIN, VNODED_BIN
from core.emulator.data import InterfaceData, LinkData, LinkOptions
from core.emulator.enumerations import LinkTypes, MessageFlags, NodeTypes
from core.errors import CoreCommandError, CoreError
from core.executables import MOUNT, VNODED
from core.nodes.client import VnodeClient
from core.nodes.interface import CoreInterface, TunTap, Veth
from core.nodes.netclient import LinuxNetClient, get_net_client
@ -511,7 +511,7 @@ class CoreNode(CoreNodeBase):
# create a new namespace for this node using vnoded
vnoded = (
f"{VNODED_BIN} -v -c {self.ctrlchnlname} -l {self.ctrlchnlname}.log "
f"{VNODED} -v -c {self.ctrlchnlname} -l {self.ctrlchnlname}.log "
f"-p {self.ctrlchnlname}.pid"
)
if self.nodedir:
@ -640,7 +640,7 @@ class CoreNode(CoreNodeBase):
source = os.path.abspath(source)
logging.debug("node(%s) mounting: %s at %s", self.name, source, target)
self.cmd(f"mkdir -p {target}")
self.cmd(f"{MOUNT_BIN} -n --bind {source} {target}")
self.cmd(f"{MOUNT} -n --bind {source} {target}")
self._mounts.append((source, target))
def next_iface_id(self) -> int:
@ -753,7 +753,7 @@ class CoreNode(CoreNodeBase):
iface = self.get_iface(iface_id)
iface.set_mac(mac)
if self.up:
self.node_net_client.device_mac(iface.name, mac)
self.node_net_client.device_mac(iface.name, str(iface.mac))
def add_ip(self, iface_id: int, ip: str) -> None:
"""

View file

@ -5,7 +5,7 @@ The control channel can be accessed via calls using the vcmd shell.
"""
from core import utils
from core.constants import VCMD_BIN
from core.executables import VCMD
class VnodeClient:
@ -50,7 +50,7 @@ class VnodeClient:
pass
def create_cmd(self, args: str) -> str:
return f"{VCMD_BIN} -c {self.ctrlchnlname} -- {args}"
return f"{VCMD} -c {self.ctrlchnlname} -- {args}"
def check_cmd(self, args: str, wait: bool = True, shell: bool = False) -> str:
"""

View file

@ -5,7 +5,7 @@ from typing import Callable
import netaddr
from core.constants import ETHTOOL_BIN, IP_BIN, OVS_BIN, SYSCTL_BIN, TC_BIN
from core.executables import ETHTOOL, IP, OVS_VSCTL, SYSCTL, TC
class LinuxNetClient:
@ -38,7 +38,7 @@ class LinuxNetClient:
:param device: device to add route to
:return: nothing
"""
self.run(f"{IP_BIN} route add {route} dev {device}")
self.run(f"{IP} route add {route} dev {device}")
def device_up(self, device: str) -> None:
"""
@ -47,7 +47,7 @@ class LinuxNetClient:
:param device: device to bring up
:return: nothing
"""
self.run(f"{IP_BIN} link set {device} up")
self.run(f"{IP} link set {device} up")
def device_down(self, device: str) -> None:
"""
@ -56,7 +56,7 @@ class LinuxNetClient:
:param device: device to bring down
:return: nothing
"""
self.run(f"{IP_BIN} link set {device} down")
self.run(f"{IP} link set {device} down")
def device_name(self, device: str, name: str) -> None:
"""
@ -66,7 +66,7 @@ class LinuxNetClient:
:param name: name to set
:return: nothing
"""
self.run(f"{IP_BIN} link set {device} name {name}")
self.run(f"{IP} link set {device} name {name}")
def device_show(self, device: str) -> str:
"""
@ -75,7 +75,7 @@ class LinuxNetClient:
:param device: device to get information for
:return: device information
"""
return self.run(f"{IP_BIN} link show {device}")
return self.run(f"{IP} link show {device}")
def address_show(self, device: str) -> str:
"""
@ -84,7 +84,7 @@ class LinuxNetClient:
:param device: device name
:return: address information
"""
return self.run(f"{IP_BIN} address show {device}")
return self.run(f"{IP} address show {device}")
def get_mac(self, device: str) -> str:
"""
@ -112,7 +112,7 @@ class LinuxNetClient:
:param namespace: namespace to set device to
:return: nothing
"""
self.run(f"{IP_BIN} link set {device} netns {namespace}")
self.run(f"{IP} link set {device} netns {namespace}")
def device_flush(self, device: str) -> None:
"""
@ -123,7 +123,7 @@ class LinuxNetClient:
"""
self.run(
f"[ -e /sys/class/net/{device} ] && "
f"{IP_BIN} address flush dev {device} || true",
f"{IP} address flush dev {device} || true",
shell=True,
)
@ -135,7 +135,7 @@ class LinuxNetClient:
:param mac: mac to set
:return: nothing
"""
self.run(f"{IP_BIN} link set dev {device} address {mac}")
self.run(f"{IP} link set dev {device} address {mac}")
def delete_device(self, device: str) -> None:
"""
@ -144,7 +144,7 @@ class LinuxNetClient:
:param device: device to delete
:return: nothing
"""
self.run(f"{IP_BIN} link delete {device}")
self.run(f"{IP} link delete {device}")
def delete_tc(self, device: str) -> None:
"""
@ -153,7 +153,7 @@ class LinuxNetClient:
:param device: device to remove tc
:return: nothing
"""
self.run(f"{TC_BIN} qdisc delete dev {device} root")
self.run(f"{TC} qdisc delete dev {device} root")
def checksums_off(self, iface_name: str) -> None:
"""
@ -162,7 +162,7 @@ class LinuxNetClient:
:param iface_name: interface to update
:return: nothing
"""
self.run(f"{ETHTOOL_BIN} -K {iface_name} rx off tx off")
self.run(f"{ETHTOOL} -K {iface_name} rx off tx off")
def create_address(self, device: str, address: str, broadcast: str = None) -> None:
"""
@ -174,15 +174,13 @@ class LinuxNetClient:
:return: nothing
"""
if broadcast is not None:
self.run(
f"{IP_BIN} address add {address} broadcast {broadcast} dev {device}"
)
self.run(f"{IP} address add {address} broadcast {broadcast} dev {device}")
else:
self.run(f"{IP_BIN} address add {address} dev {device}")
self.run(f"{IP} address add {address} dev {device}")
if netaddr.valid_ipv6(address.split("/")[0]):
# IPv6 addresses are removed by default on interface down.
# Make sure that the IPv6 address we add is not removed
self.run(f"{SYSCTL_BIN} -w net.ipv6.conf.{device}.keep_addr_on_down=1")
self.run(f"{SYSCTL} -w net.ipv6.conf.{device}.keep_addr_on_down=1")
def delete_address(self, device: str, address: str) -> None:
"""
@ -192,7 +190,7 @@ class LinuxNetClient:
:param address: address to remove
:return: nothing
"""
self.run(f"{IP_BIN} address delete {address} dev {device}")
self.run(f"{IP} address delete {address} dev {device}")
def create_veth(self, name: str, peer: str) -> None:
"""
@ -202,7 +200,7 @@ class LinuxNetClient:
:param peer: peer name
:return: nothing
"""
self.run(f"{IP_BIN} link add name {name} type veth peer name {peer}")
self.run(f"{IP} link add name {name} type veth peer name {peer}")
def create_gretap(
self, device: str, address: str, local: str, ttl: int, key: int
@ -217,7 +215,7 @@ class LinuxNetClient:
:param key: key for tap
:return: nothing
"""
cmd = f"{IP_BIN} link add {device} type gretap remote {address}"
cmd = f"{IP} link add {device} type gretap remote {address}"
if local is not None:
cmd += f" local {local}"
if ttl is not None:
@ -233,11 +231,11 @@ class LinuxNetClient:
:param name: bridge name
:return: nothing
"""
self.run(f"{IP_BIN} link add name {name} type bridge")
self.run(f"{IP_BIN} link set {name} type bridge stp_state 0")
self.run(f"{IP_BIN} link set {name} type bridge forward_delay 0")
self.run(f"{IP_BIN} link set {name} type bridge mcast_snooping 0")
self.run(f"{IP_BIN} link set {name} type bridge group_fwd_mask 65528")
self.run(f"{IP} link add name {name} type bridge")
self.run(f"{IP} link set {name} type bridge stp_state 0")
self.run(f"{IP} link set {name} type bridge forward_delay 0")
self.run(f"{IP} link set {name} type bridge mcast_snooping 0")
self.run(f"{IP} link set {name} type bridge group_fwd_mask 65528")
self.device_up(name)
def delete_bridge(self, name: str) -> None:
@ -248,7 +246,7 @@ class LinuxNetClient:
:return: nothing
"""
self.device_down(name)
self.run(f"{IP_BIN} link delete {name} type bridge")
self.run(f"{IP} link delete {name} type bridge")
def set_iface_master(self, bridge_name: str, iface_name: str) -> None:
"""
@ -258,7 +256,7 @@ class LinuxNetClient:
:param iface_name: interface name
:return: nothing
"""
self.run(f"{IP_BIN} link set dev {iface_name} master {bridge_name}")
self.run(f"{IP} link set dev {iface_name} master {bridge_name}")
self.device_up(iface_name)
def delete_iface(self, bridge_name: str, iface_name: str) -> None:
@ -269,7 +267,7 @@ class LinuxNetClient:
:param iface_name: interface name
:return: nothing
"""
self.run(f"{IP_BIN} link set dev {iface_name} nomaster")
self.run(f"{IP} link set dev {iface_name} nomaster")
def existing_bridges(self, _id: int) -> bool:
"""
@ -278,7 +276,7 @@ class LinuxNetClient:
:param _id: node id to check bridges for
:return: True if there are existing bridges, False otherwise
"""
output = self.run(f"{IP_BIN} -o link show type bridge")
output = self.run(f"{IP} -o link show type bridge")
lines = output.split("\n")
for line in lines:
values = line.split(":")
@ -299,7 +297,7 @@ class LinuxNetClient:
:param name: bridge name
:return: nothing
"""
self.run(f"{IP_BIN} link set {name} type bridge ageing_time 0")
self.run(f"{IP} link set {name} type bridge ageing_time 0")
class OvsNetClient(LinuxNetClient):
@ -314,10 +312,10 @@ class OvsNetClient(LinuxNetClient):
:param name: bridge name
:return: nothing
"""
self.run(f"{OVS_BIN} add-br {name}")
self.run(f"{OVS_BIN} set bridge {name} stp_enable=false")
self.run(f"{OVS_BIN} set bridge {name} other_config:stp-max-age=6")
self.run(f"{OVS_BIN} set bridge {name} other_config:stp-forward-delay=4")
self.run(f"{OVS_VSCTL} add-br {name}")
self.run(f"{OVS_VSCTL} set bridge {name} stp_enable=false")
self.run(f"{OVS_VSCTL} set bridge {name} other_config:stp-max-age=6")
self.run(f"{OVS_VSCTL} set bridge {name} other_config:stp-forward-delay=4")
self.device_up(name)
def delete_bridge(self, name: str) -> None:
@ -328,7 +326,7 @@ class OvsNetClient(LinuxNetClient):
:return: nothing
"""
self.device_down(name)
self.run(f"{OVS_BIN} del-br {name}")
self.run(f"{OVS_VSCTL} del-br {name}")
def set_iface_master(self, bridge_name: str, iface_name: str) -> None:
"""
@ -338,7 +336,7 @@ class OvsNetClient(LinuxNetClient):
:param iface_name: interface name
:return: nothing
"""
self.run(f"{OVS_BIN} add-port {bridge_name} {iface_name}")
self.run(f"{OVS_VSCTL} add-port {bridge_name} {iface_name}")
self.device_up(iface_name)
def delete_iface(self, bridge_name: str, iface_name: str) -> None:
@ -349,7 +347,7 @@ class OvsNetClient(LinuxNetClient):
:param iface_name: interface name
:return: nothing
"""
self.run(f"{OVS_BIN} del-port {bridge_name} {iface_name}")
self.run(f"{OVS_VSCTL} del-port {bridge_name} {iface_name}")
def existing_bridges(self, _id: int) -> bool:
"""
@ -358,7 +356,7 @@ class OvsNetClient(LinuxNetClient):
:param _id: node id to check bridges for
:return: True if there are existing bridges, False otherwise
"""
output = self.run(f"{OVS_BIN} list-br")
output = self.run(f"{OVS_VSCTL} list-br")
if output:
for line in output.split("\n"):
fields = line.split(".")
@ -373,7 +371,7 @@ class OvsNetClient(LinuxNetClient):
:param name: bridge name
:return: nothing
"""
self.run(f"{OVS_BIN} set bridge {name} other_config:mac-aging-time=0")
self.run(f"{OVS_VSCTL} set bridge {name} other_config:mac-aging-time=0")
def get_net_client(use_ovs: bool, run: Callable[..., str]) -> LinuxNetClient:

View file

@ -10,7 +10,6 @@ from typing import TYPE_CHECKING, Callable, Dict, List, Optional, Type
import netaddr
from core import utils
from core.constants import EBTABLES_BIN, TC_BIN
from core.emulator.data import InterfaceData, LinkData, LinkOptions
from core.emulator.enumerations import (
LinkTypes,
@ -20,6 +19,7 @@ from core.emulator.enumerations import (
RegisterTlvs,
)
from core.errors import CoreCommandError, CoreError
from core.executables import EBTABLES, TC
from core.nodes.base import CoreNetworkBase
from core.nodes.interface import CoreInterface, GreTap, Veth
from core.nodes.netclient import get_net_client
@ -104,7 +104,7 @@ class EbtablesQueue:
:param cmd: ebtable command
:return: ebtable atomic command
"""
return f"{EBTABLES_BIN} --atomic-file {self.atomic_file} {cmd}"
return f"{EBTABLES} --atomic-file {self.atomic_file} {cmd}"
def lastupdate(self, wlan: "CoreNetwork") -> float:
"""
@ -338,8 +338,8 @@ class CoreNetwork(CoreNetworkBase):
self.net_client.delete_bridge(self.brname)
if self.has_ebtables_chain:
cmds = [
f"{EBTABLES_BIN} -D FORWARD --logical-in {self.brname} -j {self.brname}",
f"{EBTABLES_BIN} -X {self.brname}",
f"{EBTABLES} -D FORWARD --logical-in {self.brname} -j {self.brname}",
f"{EBTABLES} -X {self.brname}",
]
ebtablescmds(self.host_cmd, cmds)
except CoreCommandError:
@ -448,7 +448,7 @@ class CoreNetwork(CoreNetworkBase):
:return: nothing
"""
devname = iface.localname
tc = f"{TC_BIN} qdisc replace dev {devname}"
tc = f"{TC} qdisc replace dev {devname}"
parent = "root"
changed = False
bw = options.bandwidth
@ -466,7 +466,7 @@ class CoreNetwork(CoreNetworkBase):
changed = True
elif iface.getparam("has_tbf") and bw <= 0:
if self.up:
cmd = f"{TC_BIN} qdisc delete dev {devname} {parent}"
cmd = f"{TC} qdisc delete dev {devname} {parent}"
iface.host_cmd(cmd)
iface.setparam("has_tbf", False)
# removing the parent removes the child
@ -512,14 +512,12 @@ class CoreNetwork(CoreNetworkBase):
if not iface.getparam("has_netem"):
return
if self.up:
cmd = f"{TC_BIN} qdisc delete dev {devname} {parent} handle 10:"
cmd = f"{TC} qdisc delete dev {devname} {parent} handle 10:"
iface.host_cmd(cmd)
iface.setparam("has_netem", False)
elif len(netem) > 1:
if self.up:
cmd = (
f"{TC_BIN} qdisc replace dev {devname} {parent} handle 10: {netem}"
)
cmd = f"{TC} qdisc replace dev {devname} {parent} handle 10: {netem}"
iface.host_cmd(cmd)
iface.setparam("has_netem", True)

View file

@ -7,11 +7,11 @@ import os
import threading
from typing import IO, TYPE_CHECKING, List, Optional, Tuple
from core.constants import MOUNT_BIN, UMOUNT_BIN
from core.emulator.data import InterfaceData, LinkOptions
from core.emulator.distributed import DistributedServer
from core.emulator.enumerations import NodeTypes, TransportType
from core.errors import CoreCommandError, CoreError
from core.executables import MOUNT, UMOUNT
from core.nodes.base import CoreNetworkBase, CoreNodeBase
from core.nodes.interface import CoreInterface
from core.nodes.network import CoreNetwork, GreTap
@ -76,7 +76,7 @@ class PhysicalNode(CoreNodeBase):
iface = self.get_iface(iface_id)
iface.set_mac(mac)
if self.up:
self.net_client.device_mac(iface.name, mac)
self.net_client.device_mac(iface.name, str(iface.mac))
def add_ip(self, iface_id: int, ip: str) -> None:
"""
@ -186,13 +186,13 @@ class PhysicalNode(CoreNodeBase):
source = os.path.abspath(source)
logging.info("mounting %s at %s", source, target)
os.makedirs(target)
self.host_cmd(f"{MOUNT_BIN} --bind {source} {target}", cwd=self.nodedir)
self.host_cmd(f"{MOUNT} --bind {source} {target}", cwd=self.nodedir)
self._mounts.append((source, target))
def umount(self, target: str) -> None:
logging.info("unmounting '%s'", target)
try:
self.host_cmd(f"{UMOUNT_BIN} -l {target}", cwd=self.nodedir)
self.host_cmd(f"{UMOUNT} -l {target}", cwd=self.nodedir)
except CoreCommandError:
logging.exception("unmounting failed for %s", target)

View file

@ -13,10 +13,9 @@ import time
from typing import TYPE_CHECKING, Dict, Iterable, List, Optional, Set, Tuple, Type
from core import utils
from core.constants import which
from core.emulator.data import FileData
from core.emulator.enumerations import ExceptionLevels, MessageFlags, RegisterTlvs
from core.errors import CoreCommandError
from core.errors import CoreCommandError, CoreError
from core.nodes.base import CoreNode
if TYPE_CHECKING:
@ -262,7 +261,10 @@ class ServiceManager:
# validate dependent executables are present
for executable in service.executables:
which(executable, required=True)
try:
utils.which(executable, required=True)
except CoreError as e:
raise CoreError(f"service({name}): {e}")
# validate service on load succeeds
try:
@ -300,7 +302,7 @@ class ServiceManager:
try:
cls.add(service)
except ValueError as e:
except (CoreError, ValueError) as e:
service_errors.append(service.name)
logging.debug("not loading service(%s): %s", service.name, e)
return service_errors

View file

@ -5,8 +5,9 @@ from typing import Optional, Tuple
import netaddr
from core import constants, utils
from core import utils
from core.errors import CoreCommandError
from core.executables import SYSCTL
from core.nodes.base import CoreNode
from core.services.coreservices import CoreService, ServiceMode
@ -47,19 +48,13 @@ class IPForwardService(UtilService):
%(sysctl)s -w net.ipv4.conf.all.rp_filter=0
%(sysctl)s -w net.ipv4.conf.default.rp_filter=0
""" % {
"sysctl": constants.SYSCTL_BIN
"sysctl": SYSCTL
}
for iface in node.get_ifaces():
name = utils.sysctl_devname(iface.name)
cfg += "%s -w net.ipv4.conf.%s.forwarding=1\n" % (
constants.SYSCTL_BIN,
name,
)
cfg += "%s -w net.ipv4.conf.%s.send_redirects=0\n" % (
constants.SYSCTL_BIN,
name,
)
cfg += "%s -w net.ipv4.conf.%s.rp_filter=0\n" % (constants.SYSCTL_BIN, name)
cfg += "%s -w net.ipv4.conf.%s.forwarding=1\n" % (SYSCTL, name)
cfg += "%s -w net.ipv4.conf.%s.send_redirects=0\n" % (SYSCTL, name)
cfg += "%s -w net.ipv4.conf.%s.rp_filter=0\n" % (SYSCTL, name)
return cfg

View file

@ -33,7 +33,7 @@ from typing import (
import netaddr
from core.errors import CoreCommandError
from core.errors import CoreCommandError, CoreError
if TYPE_CHECKING:
from core.emulator.session import Session
@ -154,7 +154,7 @@ def which(command: str, required: bool) -> str:
"""
found_path = shutil.which(command)
if found_path is None and required:
raise ValueError(f"failed to find required executable({command}) in path")
raise CoreError(f"failed to find required executable({command}) in path")
return found_path

View file

@ -6,8 +6,8 @@ import netaddr
from lxml import etree
from core import utils
from core.constants import IP_BIN
from core.emane.nodes import EmaneNet
from core.executables import IP
from core.nodes.base import CoreNodeBase, NodeBase
from core.nodes.interface import CoreInterface
@ -83,7 +83,7 @@ def get_address_type(address: str) -> str:
def get_ipv4_addresses(hostname: str) -> List[Tuple[str, str]]:
if hostname == "localhost":
addresses = []
args = f"{IP_BIN} -o -f inet address show"
args = f"{IP} -o -f inet address show"
output = utils.cmd(args)
for line in output.split(os.linesep):
split = line.split()