netdev-dpdk: fix mbuf leaks
[cascardo/ovs.git] / xenserver / opt_xensource_libexec_InterfaceReconfigureVswitch.py
index fd66d37..1379fb4 100644 (file)
@@ -1,5 +1,5 @@
-# Copyright (c) 2008,2009 Citrix Systems, Inc.
-# Copyright (c) 2009,2010 Nicira Networks.
+# Copyright (c) 2008,2009,2011 Citrix Systems, Inc.
+# Copyright (c) 2009,2010,2011,2012,2013 Nicira, Inc.
 #
 # This program is free software; you can redistribute it and/or modify
 # it under the terms of the GNU Lesser General Public License as published
@@ -12,6 +12,9 @@
 # GNU Lesser General Public License for more details.
 #
 from InterfaceReconfigure import *
+import os
+import re
+import subprocess
 
 #
 # Bare Network Devices -- network devices without IP configuration
@@ -36,6 +39,49 @@ def netdev_up(netdev, mtu=None):
 
     run_command(["/sbin/ifconfig", netdev, 'up'] + mtu)
 
+# This is a list of drivers that do support VLAN tx or rx acceleration, but
+# to which the VLAN bug workaround should not be applied.  This could be
+# because these are known-good drivers (that is, they do not have any of
+# the bugs that the workaround avoids) or because the VLAN bug workaround
+# will not work for them and may cause other problems.
+#
+# This is a very short list because few drivers have been tested.
+NO_VLAN_WORKAROUND_DRIVERS = (
+    "bonding",
+)
+def netdev_get_driver_name(netdev):
+    """Returns the name of the driver for network device 'netdev'"""
+    symlink = '%s/sys/class/net/%s/device/driver' % (root_prefix(), netdev)
+    try:
+        target = os.readlink(symlink)
+    except OSError, e:
+        log("%s: could not read netdev's driver name (%s)" % (netdev, e))
+        return None
+
+    slash = target.rfind('/')
+    if slash < 0:
+        log("target %s of symbolic link %s does not contain slash"
+            % (target, symlink))
+        return None
+
+    return target[slash + 1:]
+
+def netdev_get_features(netdev):
+    """Returns the features bitmap for the driver for 'netdev'.
+    The features bitmap is a set of NETIF_F_ flags supported by its driver."""
+    try:
+        features = open("%s/sys/class/net/%s/features" % (root_prefix(), netdev)).read().strip()
+        return int(features, 0)
+    except:
+        return 0 # interface prolly doesn't exist
+
+def netdev_has_vlan_accel(netdev):
+    """Returns True if 'netdev' supports VLAN acceleration, False otherwise."""
+    NETIF_F_HW_VLAN_TX = 128
+    NETIF_F_HW_VLAN_RX = 256
+    NETIF_F_VLAN = NETIF_F_HW_VLAN_TX | NETIF_F_HW_VLAN_RX
+    return (netdev_get_features(netdev) & NETIF_F_VLAN) != 0
+
 #
 # PIF miscellanea
 #
@@ -89,7 +135,9 @@ For a non-VLAN, non-bond master PIF, the PIF is its own physical device PIF.
 
 A VLAN PIF cannot be a datapath PIF.
 """
-    if pif_is_vlan(pif):
+    if pif_is_tunnel(pif):
+        return []
+    elif pif_is_vlan(pif):
         # Seems like overkill...
         raise Error("get-physical-pifs should not get passed a VLAN")
     elif pif_is_bond(pif):
@@ -100,6 +148,33 @@ A VLAN PIF cannot be a datapath PIF.
 def datapath_deconfigure_physical(netdev):
     return ['--', '--with-iface', '--if-exists', 'del-port', netdev]
 
+def vsctl_escape(s):
+    if s.isalnum():
+        return s
+
+    def escape(match):
+        c = match.group(0)
+        if c == '\0':
+            raise Error("strings may not contain null bytes")
+        elif c == '\\':
+            return r'\\'
+        elif c == '\n':
+            return r'\n'
+        elif c == '\r':
+            return r'\r'
+        elif c == '\t':
+            return r'\t'
+        elif c == '\b':
+            return r'\b'
+        elif c == '\a':
+            return r'\a'
+        else:
+            return r'\x%02x' % ord(c)
+    return '"' + re.sub(r'["\\\000-\037]', escape, s) + '"'
+
+def datapath_configure_tunnel(pif):
+    pass
+
 def datapath_configure_bond(pif,slaves):
     bridge = pif_bridge_name(pif)
     pifrec = db().get_pif_record(pif)
@@ -109,10 +184,6 @@ def datapath_configure_bond(pif,slaves):
     for slave in slaves:
         argv += [pif_netdev_name(slave)]
 
-    # XXX need ovs-vsctl support
-    #if pifrec['MAC'] != "":
-    #    argv += ['--add=port.%s.mac=%s' % (interface, pifrec['MAC'])]
-
     # Bonding options.
     bond_options = {
         "mode":   "balance-slb",
@@ -120,6 +191,7 @@ def datapath_configure_bond(pif,slaves):
         "downdelay": "200",
         "updelay": "31000",
         "use_carrier": "1",
+        "hashing-algorithm": "src_mac",
         }
     # override defaults with values from other-config whose keys
     # being with "bond-"
@@ -128,10 +200,65 @@ def datapath_configure_bond(pif,slaves):
                            key.startswith("bond-"), oc.items())
     overrides = map(lambda (key,val): (key[5:], val), overrides)
     bond_options.update(overrides)
+    mode = None
+    halgo = None
+
+    argv += ['--', 'set', 'Port', interface]
+    if pifrec['MAC'] != "":
+        argv += ['MAC=%s' % vsctl_escape(pifrec['MAC'])]
     for (name,val) in bond_options.items():
-        # XXX need ovs-vsctl support for bond options
-        #argv += ["--add=bonding.%s.%s=%s" % (interface, name, val)]
-        pass
+        if name in ['updelay', 'downdelay']:
+            # updelay and downdelay have dedicated schema columns.
+            # The value must be a nonnegative integer.
+            try:
+                value = int(val)
+                if value < 0:
+                    raise ValueError
+
+                argv += ['bond_%s=%d' % (name, value)]
+            except ValueError:
+                log("bridge %s has invalid %s '%s'" % (bridge, name, value))
+        elif name in ['miimon', 'use_carrier']:
+            try:
+                value = int(val)
+                if value < 0:
+                    raise ValueError
+
+                if name == 'use_carrier':
+                    if value:
+                        value = "carrier"
+                    else:
+                        value = "miimon"
+                    argv += ["other-config:bond-detect-mode=%s" % value]
+                else:
+                    argv += ["other-config:bond-miimon-interval=%d" % value]
+            except ValueError:
+                log("bridge %s has invalid %s '%s'" % (bridge, name, value))
+        elif name == "mode":
+            mode = val
+        elif name == "hashing-algorithm":
+            halgo = val
+        else:
+            # Pass other bond options into other_config.
+            argv += ["other-config:%s=%s" % (vsctl_escape("bond-%s" % name),
+                                             vsctl_escape(val))]
+
+    if mode == 'lacp':
+        argv += ['lacp=active']
+
+        if halgo == 'src_mac':
+            argv += ['bond_mode=balance-slb']
+        elif halgo == "tcpudp_ports":
+            argv += ['bond_mode=balance-tcp']
+        else:
+            log("bridge %s has invalid bond-hashing-algorithm '%s'" % (bridge, halgo))
+            argv += ['bond_mode=balance-slb']
+    elif mode in ['balance-slb', 'active-backup']:
+        argv += ['lacp=off', 'bond_mode=%s' % mode]
+    else:
+        log("bridge %s has invalid bond-mode '%s'" % (bridge, mode))
+        argv += ['lacp=off', 'bond_mode=balance-slb']
+
     return argv
 
 def datapath_deconfigure_bond(netdev):
@@ -166,15 +293,21 @@ def configure_datapath(pif):
     - A list containing the necessary vsctl command line arguments
     - A list of additional devices which should be brought up after
       the configuration is applied.
+    - A list containing flows to apply to the pif bridge, note that
+      port numbers may need to be substituted once ofport is known
     """
 
     vsctl_argv = []
     extra_up_ports = []
+    bridge_flows = []
 
+    assert not pif_is_vlan(pif)
     bridge = pif_bridge_name(pif)
 
     physical_devices = datapath_get_physical_pifs(pif)
 
+    vsctl_argv += ['## configuring datapath %s' % bridge]
+
     # Determine additional devices to deconfigure.
     #
     # Given all physical devices which are part of this PIF we need to
@@ -252,35 +385,107 @@ def configure_datapath(pif):
         vsctl_argv += ['# configure bond %s' % pif_netdev_name(pif)]
         vsctl_argv += datapath_configure_bond(pif, physical_devices)
         extra_up_ports += [pif_netdev_name(pif)]
-    else:
+    elif len(physical_devices) == 1:
         iface = pif_netdev_name(physical_devices[0])
         vsctl_argv += ['# add physical device %s' % iface]
         vsctl_argv += ['--', '--may-exist', 'add-port', bridge, iface]
+    elif pif_is_tunnel(pif):
+        datapath_configure_tunnel(pif)
+
+    vsctl_argv += ['# configure Bridge MAC']
+    vsctl_argv += ['--', 'set', 'Bridge', bridge,
+                   'other-config:hwaddr=%s' % vsctl_escape(db().get_pif_record(pif)['MAC'])]
+
+    pool = db().get_pool_record()
+    network = db().get_network_by_bridge(bridge)
+    network_rec = None
+    fail_mode = None
+    valid_fail_modes = ['standalone', 'secure']
+
+    if network:
+        network_rec = db().get_network_record(network)
+        fail_mode = network_rec['other_config'].get('vswitch-controller-fail-mode')
+
+    if (fail_mode not in valid_fail_modes) and pool:
+        fail_mode = pool['other_config'].get('vswitch-controller-fail-mode')
+        # Add default flows to allow management traffic if fail-mode
+        # transitions to secure based on pool fail-mode setting
+        if fail_mode == 'secure' and db().get_pif_record(pif).get('management', False):
+            prev_fail_mode = vswitchCfgQuery(['get-fail-mode', bridge])
+            if prev_fail_mode != 'secure':
+                tp = 'idle_timeout=0,priority=0'
+                host_mgmt_mac = db().get_pif_record(pif)['MAC']
+                # account for bond as management interface
+                if len(physical_devices) > 1:
+                    bridge_flows += ['%s,in_port=local,arp,dl_src=%s,actions=NORMAL' % (tp, host_mgmt_mac)]
+                    bridge_flows += ['%s,in_port=local,dl_src=%s,actions=NORMAL' % (tp, host_mgmt_mac)]
+                    # we don't know slave ofports yet, substitute later
+                    bridge_flows += ['%s,in_port=%%s,arp,nw_proto=1,actions=local' % (tp)]
+                    bridge_flows += ['%s,in_port=%%s,dl_dst=%s,actions=local' % (tp, host_mgmt_mac)]
+                else:
+                    bridge_flows += ['%s,in_port=%%s,arp,nw_proto=1,actions=local' % (tp)]
+                    bridge_flows += ['%s,in_port=local,arp,dl_src=%s,actions=%%s' % (tp, host_mgmt_mac)]
+                    bridge_flows += ['%s,in_port=%%s,dl_dst=%s,actions=local' % (tp, host_mgmt_mac)]
+                    bridge_flows += ['%s,in_port=local,dl_src=%s,actions=%%s' % (tp, host_mgmt_mac)]
+
+    if fail_mode not in valid_fail_modes:
+        fail_mode = 'standalone'
+
+    vsctl_argv += ['--', 'set', 'Bridge', bridge, 'fail_mode=%s' % fail_mode]
+
+    if network_rec:
+        dib = network_rec['other_config'].get('vswitch-disable-in-band')
+        if not dib:
+            vsctl_argv += ['--', 'remove', 'Bridge', bridge, 'other_config', 'disable-in-band']
+        elif dib in ['true', 'false']:
+            vsctl_argv += ['--', 'set', 'Bridge', bridge, 'other_config:disable-in-band=' + dib]
+        else:
+            log('"' + dib + '"' "isn't a valid setting for other_config:disable-in-band on " + bridge)
+
+    vsctl_argv += set_br_external_ids(pif)
+    vsctl_argv += ['## done configuring datapath %s' % bridge]
 
-    return vsctl_argv,extra_up_ports
+    return vsctl_argv,extra_up_ports,bridge_flows
 
-def deconfigure_datapath(pif):
+def deconfigure_bridge(pif):
     vsctl_argv = []
 
     bridge = pif_bridge_name(pif)
 
-    physical_devices = datapath_get_physical_pifs(pif)
+    log("deconfigure_bridge: bridge           - %s" % bridge)
 
-    log("deconfigure_datapath: bridge           - %s" % bridge)
-    log("deconfigure_datapath: physical devices - %s" % [pif_netdev_name(p) for p in physical_devices])
+    vsctl_argv += ['# deconfigure bridge %s' % bridge]
+    vsctl_argv += ['--', '--if-exists', 'del-br', bridge]
 
-    for p in physical_devices:
-        dev = pif_netdev_name(p)
-        vsctl_argv += ['# deconfigure physical port %s' % dev]
-        vsctl_argv += datapath_deconfigure_physical(dev)
-        netdev_down(dev)
+    return vsctl_argv
 
-    if len(physical_devices) > 1:
-        vsctl_argv += ['# deconfigure bond %s' % pif_netdev_name(pif)]
-        vsctl_argv += datapath_deconfigure_bond(pif_netdev_name(pif))
+def set_br_external_ids(pif):
+    pifrec = db().get_pif_record(pif)
+    dp = pif_datapath(pif)
+    dprec = db().get_pif_record(dp)
+
+    xs_network_uuids = []
+    for nwpif in db().get_pifs_by_device(pifrec['device']):
+        rec = db().get_pif_record(nwpif)
+
+        # When state is read from dbcache PIF.currently_attached
+        # is always assumed to be false... Err on the side of
+        # listing even detached networks for the time being.
+        #if nwpif != pif and not rec['currently_attached']:
+        #    log("Network PIF %s not currently attached (%s)" % (rec['uuid'],pifrec['uuid']))
+        #    continue
+        nwrec = db().get_network_record(rec['network'])
+
+        uuid = nwrec['uuid']
+        if pif_is_vlan(nwpif):
+            xs_network_uuids.append(uuid)
+        else:
+            xs_network_uuids.insert(0, uuid)
 
-    vsctl_argv += ['# deconfigure bridge %s' % bridge]
-    vsctl_argv += ['--', '--if-exists', 'del-br', bridge]
+    vsctl_argv = []
+    vsctl_argv += ['# configure xs-network-uuids']
+    vsctl_argv += ['--', 'br-set-external-id', pif_bridge_name(pif),
+            'xs-network-uuids', ';'.join(xs_network_uuids)]
 
     return vsctl_argv
 
@@ -293,53 +498,62 @@ class DatapathVswitch(Datapath):
         Datapath.__init__(self, pif)
         self._dp = pif_datapath(pif)
         self._ipdev = pif_ipdev_name(pif)
+        self._bridge_flows = []
 
         if pif_is_vlan(pif) and not self._dp:
             raise Error("Unbridged VLAN devices not implemented yet")
         
         log("Configured for Vswitch datapath")
 
+    @classmethod
+    def rewrite(cls):
+        if not os.path.exists("/var/run/openvswitch/db.sock"):
+            # ovsdb-server is not running, so we can't update the database.
+            # Probably we are being called as part of system shutdown.  Just
+            # skip the update, since the external-ids will be updated on the
+            # next boot anyhow.
+            return
+
+        vsctl_argv = []
+        for pif in db().get_all_pifs():
+            pifrec = db().get_pif_record(pif)
+            if not pif_is_vlan(pif) and pifrec['currently_attached']:
+                vsctl_argv += set_br_external_ids(pif)
+
+        if vsctl_argv != []:
+            datapath_modify_config(vsctl_argv)
+
     def configure_ipdev(self, cfg):
         cfg.write("TYPE=Ethernet\n")
 
     def preconfigure(self, parent):
         vsctl_argv = []
         extra_ports = []
+        bridge_flows = []
 
         pifrec = db().get_pif_record(self._pif)
         dprec = db().get_pif_record(self._dp)
 
         ipdev = self._ipdev
-        c,e = configure_datapath(self._dp)
+        c,e,f = configure_datapath(self._dp)
         bridge = pif_bridge_name(self._pif)
         vsctl_argv += c
         extra_ports += e
+        bridge_flows += f
 
+        dpname = pif_bridge_name(self._dp)
+        
         if pif_is_vlan(self._pif):
-            # XXX this is only needed on XS5.5, because XAPI misguidedly
-            # creates the fake bridge (via bridge ioctl) before it calls us.
+            # In some cases XAPI may misguidedly leave an instance of
+            # 'bridge' which should be deleted.
             vsctl_argv += ['--', '--if-exists', 'del-br', bridge]
 
             # configure_datapath() set up the underlying datapath bridge.
             # Stack a VLAN bridge on top of it.
             vsctl_argv += ['--', '--may-exist', 'add-br',
-                           bridge, pif_bridge_name(self._dp), pifrec['VLAN']]
-        xs_network_uuids = []
-        for nwpif in db().get_pifs_by_device(db().get_pif_record(self._pif)['device']):
-            rec = db().get_pif_record(nwpif)
-
-            # When state is read from dbcache PIF.currently_attached
-            # is always assumed to be false... Err on the side of
-            # listing even detached networks for the time being.
-            #if nwpif != pif and not rec['currently_attached']:
-            #    log("Network PIF %s not currently attached (%s)" % (rec['uuid'],pifrec['uuid']))
-            #    continue
-            nwrec = db().get_network_record(rec['network'])
-            xs_network_uuids += [nwrec['uuid']]
-
-        vsctl_argv += ['# configure xs-network-uuids']
-        vsctl_argv += ['--', 'br-set-external-id', bridge,
-                'xs-network-uuids', ';'.join(xs_network_uuids)]
+                           bridge, dpname, pifrec['VLAN']]
+
+            vsctl_argv += set_br_external_ids(self._pif)
 
         if ipdev != bridge:
             vsctl_argv += ["# deconfigure ipdev %s" % ipdev]
@@ -347,17 +561,36 @@ class DatapathVswitch(Datapath):
             vsctl_argv += ["# reconfigure ipdev %s" % ipdev]
             vsctl_argv += ['--', 'add-port', bridge, ipdev]
 
-        # XXX Needs support in ovs-vsctl
-        #if bridge == ipdev:
-        #    vsctl_argv += ['--add=bridge.%s.mac=%s' % (bridge, dprec['MAC'])]
-        #else:
-        #    vsctl_argv += ['--add=iface.%s.mac=%s' % (ipdev, dprec['MAC'])]
+        if ipdev != dpname:
+            vsctl_argv += ['# configure Interface MAC']
+            vsctl_argv += ['--', 'set', 'Interface', pif_ipdev_name(self._pif),
+                           'MAC=%s' % vsctl_escape(dprec['MAC'])]
 
         self._vsctl_argv = vsctl_argv
         self._extra_ports = extra_ports
+        self._bridge_flows = bridge_flows
 
     def bring_down_existing(self):
-        pass
+        # interface-reconfigure is never explicitly called to down a
+        # bond master.  However, when we are called to up a slave it
+        # is implicit that we are destroying the master.  Conversely,
+        # when we are called to up a bond is is implicit that we are
+        # taking down the slaves.
+        #
+        # This is (only) important in the case where the device being
+        # implicitly taken down uses DHCP.  We need to kill the
+        # dhclient process, otherwise performing the inverse operation
+        # later later will fail because ifup will refuse to start a
+        # duplicate dhclient.
+        bond_masters = pif_get_bond_masters(self._pif)
+        for master in bond_masters:
+            log("action_up: bring down bond master %s" % (pif_netdev_name(master)))
+            run_command(["/sbin/ifdown", pif_bridge_name(master)])
+
+        bond_slaves = pif_get_bond_slaves(self._pif)
+        for slave in bond_slaves:
+            log("action_up: bring down bond slave %s" % (pif_netdev_name(slave)))
+            run_command(["/sbin/ifdown", pif_bridge_name(slave)])
 
     def configure(self):
         # Bring up physical devices. ovs-vswitchd initially enables or
@@ -365,23 +598,67 @@ class DatapathVswitch(Datapath):
         # when they are added, and a network device that is down
         # always reports "no carrier".
         physical_devices = datapath_get_physical_pifs(self._dp)
+
+        if pif_is_bond(self._dp):
+            brec = db().get_pif_record(self._dp)
+            bond_mtu = mtu_setting(brec['network'], "PIF", brec['other_config'])
+        else:
+            bond_mtu = None
         
         for p in physical_devices:
-            oc = db().get_pif_record(p)['other_config']
+            prec = db().get_pif_record(p)
+            oc = prec['other_config']
 
             dev = pif_netdev_name(p)
 
-            mtu = mtu_setting(oc)
+            if bond_mtu:
+                mtu = bond_mtu
+            else:
+                mtu = mtu_setting(prec['network'], "PIF", oc)
 
             netdev_up(dev, mtu)
 
-            settings, offload = ethtool_settings(oc)
+            settings, offload = ethtool_settings(oc, PIF_OTHERCONFIG_DEFAULTS)
             if len(settings):
                 run_command(['/sbin/ethtool', '-s', dev] + settings)
             if len(offload):
                 run_command(['/sbin/ethtool', '-K', dev] + offload)
 
+            driver = netdev_get_driver_name(dev)
+            if 'vlan-bug-workaround' in oc:
+                vlan_bug_workaround = oc['vlan-bug-workaround'] == 'true'
+            elif driver in NO_VLAN_WORKAROUND_DRIVERS:
+                vlan_bug_workaround = False
+            else:
+                vlan_bug_workaround = netdev_has_vlan_accel(dev)
+
+            if vlan_bug_workaround:
+                setting = 'on'
+            else:
+                setting = 'off'
+            run_command(['/usr/sbin/ovs-vlan-bug-workaround', dev, setting])
+
         datapath_modify_config(self._vsctl_argv)
+        if self._bridge_flows:
+            ofports = []
+            physical_devices = datapath_get_physical_pifs(self._dp)
+            if len(physical_devices) > 1:
+                for slave in physical_devices:
+                    name = pif_netdev_name(slave)
+                    ofport = vswitchCfgQuery(['get', 'interface', name, 'ofport'])
+                    ofports.append(ofport)
+            else:
+                name = pif_netdev_name(self._dp)
+                ofport = vswitchCfgQuery(['get', 'interface', name, 'ofport'])
+                ofports.append(ofport)
+            dpname = pif_bridge_name(self._dp)
+            for flow in self._bridge_flows:
+                if flow.find('in_port=%s') != -1 or flow.find('actions=%s') != -1:
+                    for port in ofports:
+                        f = flow % (port)
+                        run_command(['/usr/bin/ovs-ofctl', 'add-flow', dpname, f])
+                else:
+                    run_command(['/usr/bin/ovs-ofctl', 'add-flow', dpname, flow])
 
     def post(self):
         for p in self._extra_ports:
@@ -396,16 +673,14 @@ class DatapathVswitch(Datapath):
         
         bridge = pif_bridge_name(dp)
 
-        #nw = db().get_pif_record(self._pif)['network']
-        #nwrec = db().get_network_record(nw)
-        #vsctl_argv += ['# deconfigure xs-network-uuids']
-        #vsctl_argv += ['--del-entry=bridge.%s.xs-network-uuids=%s' % (bridge,nwrec['uuid'])]
-
         log("deconfigure ipdev %s on %s" % (ipdev,bridge))
         vsctl_argv += ["# deconfigure ipdev %s" % ipdev]
         vsctl_argv += datapath_deconfigure_ipdev(ipdev)
 
         if pif_is_vlan(self._pif):
+            # Delete the VLAN bridge.
+            vsctl_argv += deconfigure_bridge(self._pif)
+
             # If the VLAN's slave is attached, leave datapath setup.
             slave = pif_get_vlan_slave(self._pif)
             if db().get_pif_record(slave)['currently_attached']:
@@ -429,5 +704,27 @@ class DatapathVswitch(Datapath):
                 dp = None
 
         if dp:
-            vsctl_argv += deconfigure_datapath(dp)
-            datapath_modify_config(vsctl_argv)
+            vsctl_argv += deconfigure_bridge(dp)
+
+            physical_devices = [pif_netdev_name(p) for p in datapath_get_physical_pifs(dp)]
+
+            log("action_down: bring down physical devices - %s" % physical_devices)
+        
+            for p in physical_devices:
+                netdev_down(p)
+
+        datapath_modify_config(vsctl_argv)
+
+#
+# utility methods
+#
+
+def vswitchCfgQuery(action_args):
+    cmd = ['%s/usr/bin/ovs-vsctl' % root_prefix(),
+           '-vconsole:off'] + action_args
+    output = subprocess.Popen(cmd, stdout=subprocess.PIPE).communicate()
+    if len(output) == 0 or output[0] == None:
+        output = ""
+    else:
+        output = output[0].strip()
+    return output