diff options
Diffstat (limited to 'python')
| -rw-r--r-- | python/vyos/ifconfig/bond.py | 4 | ||||
| -rw-r--r-- | python/vyos/ifconfig/bridge.py | 4 | ||||
| -rw-r--r-- | python/vyos/ifconfig/dummy.py | 5 | ||||
| -rw-r--r-- | python/vyos/ifconfig/ethernet.py | 6 | ||||
| -rw-r--r-- | python/vyos/ifconfig/geneve.py | 3 | ||||
| -rw-r--r-- | python/vyos/ifconfig/input.py | 5 | ||||
| -rw-r--r-- | python/vyos/ifconfig/interface.py | 23 | ||||
| -rw-r--r-- | python/vyos/ifconfig/l2tpv3.py | 1 | ||||
| -rw-r--r-- | python/vyos/ifconfig/loopback.py | 6 | ||||
| -rw-r--r-- | python/vyos/ifconfig/macsec.py | 3 | ||||
| -rw-r--r-- | python/vyos/ifconfig/macvlan.py | 5 | ||||
| -rw-r--r-- | python/vyos/ifconfig/pppoe.py | 1 | ||||
| -rw-r--r-- | python/vyos/ifconfig/sstpc.py | 1 | ||||
| -rw-r--r-- | python/vyos/ifconfig/tunnel.py | 9 | ||||
| -rw-r--r-- | python/vyos/ifconfig/veth.py | 3 | ||||
| -rw-r--r-- | python/vyos/ifconfig/vrrp.py | 3 | ||||
| -rw-r--r-- | python/vyos/ifconfig/vti.py | 1 | ||||
| -rw-r--r-- | python/vyos/ifconfig/vtun.py | 1 | ||||
| -rw-r--r-- | python/vyos/ifconfig/vxlan.py | 4 | ||||
| -rw-r--r-- | python/vyos/ifconfig/wireguard.py | 9 | ||||
| -rw-r--r-- | python/vyos/ifconfig/wireless.py | 1 | ||||
| -rw-r--r-- | python/vyos/ifconfig/wwan.py | 1 | ||||
| -rw-r--r-- | python/vyos/utils/process.py | 52 | 
23 files changed, 84 insertions, 67 deletions
| diff --git a/python/vyos/ifconfig/bond.py b/python/vyos/ifconfig/bond.py index 8ba481728..a659b9bd2 100644 --- a/python/vyos/ifconfig/bond.py +++ b/python/vyos/ifconfig/bond.py @@ -31,7 +31,6 @@ class BondIf(Interface):      monitoring may be performed.      """ -    iftype = 'bond'      definition = {          **Interface.definition,          ** { @@ -109,6 +108,9 @@ class BondIf(Interface):          ]          return options +    def _create(self): +        super()._create('bond') +      def remove(self):          """          Remove interface from operating system. Removing the interface diff --git a/python/vyos/ifconfig/bridge.py b/python/vyos/ifconfig/bridge.py index 917f962b7..d534dade7 100644 --- a/python/vyos/ifconfig/bridge.py +++ b/python/vyos/ifconfig/bridge.py @@ -32,7 +32,6 @@ class BridgeIf(Interface):      The Linux bridge code implements a subset of the ANSI/IEEE 802.1d standard.      """ -    iftype = 'bridge'      definition = {          **Interface.definition,          **{ @@ -107,6 +106,9 @@ class BridgeIf(Interface):          },      }} +    def _create(self): +        super()._create('bridge') +      def get_vlan_filter(self):          """          Get the status of the bridge VLAN filter diff --git a/python/vyos/ifconfig/dummy.py b/python/vyos/ifconfig/dummy.py index d45769931..29a1965a3 100644 --- a/python/vyos/ifconfig/dummy.py +++ b/python/vyos/ifconfig/dummy.py @@ -22,8 +22,6 @@ class DummyIf(Interface):      interface. The purpose of a dummy interface is to provide a device to route      packets through without actually transmitting them.      """ - -    iftype = 'dummy'      definition = {          **Interface.definition,          **{ @@ -31,3 +29,6 @@ class DummyIf(Interface):              'prefixes': ['dum', ],          },      } + +    def _create(self): +        super()._create('dummy') diff --git a/python/vyos/ifconfig/ethernet.py b/python/vyos/ifconfig/ethernet.py index d0c03dbe0..93727bdf6 100644 --- a/python/vyos/ifconfig/ethernet.py +++ b/python/vyos/ifconfig/ethernet.py @@ -33,7 +33,6 @@ class EthernetIf(Interface):      Abstraction of a Linux Ethernet Interface      """ -    iftype = 'ethernet'      definition = {          **Interface.definition,          **{ @@ -119,6 +118,9 @@ class EthernetIf(Interface):          super().__init__(ifname, **kargs)          self.ethtool = Ethtool(ifname) +    def _create(self): +        pass +      def remove(self):          """          Remove interface from config. Removing the interface deconfigures all @@ -137,7 +139,7 @@ class EthernetIf(Interface):          # Remove all VLAN subinterfaces - filter with the VLAN dot          for vlan in [              x -            for x in Section.interfaces(self.iftype) +            for x in Section.interfaces('ethernet')              if x.startswith(f'{self.ifname}.')          ]:              Interface(vlan).remove() diff --git a/python/vyos/ifconfig/geneve.py b/python/vyos/ifconfig/geneve.py index fbb261a35..f7fddb812 100644 --- a/python/vyos/ifconfig/geneve.py +++ b/python/vyos/ifconfig/geneve.py @@ -27,7 +27,6 @@ class GeneveIf(Interface):      https://developers.redhat.com/blog/2019/05/17/an-introduction-to-linux-virtual-interfaces-tunnels/#geneve      https://lwn.net/Articles/644938/      """ -    iftype = 'geneve'      definition = {          **Interface.definition,          **{ @@ -49,7 +48,7 @@ class GeneveIf(Interface):              'parameters.ipv6.flowlabel'  : 'flowlabel',          } -        cmd = 'ip link add name {ifname} type {type} id {vni} remote {remote}' +        cmd = 'ip link add name {ifname} type geneve id {vni} remote {remote}'          for vyos_key, iproute2_key in mapping.items():              # dict_search will return an empty dict "{}" for valueless nodes like              # "parameters.nolearning" - thus we need to test the nodes existence diff --git a/python/vyos/ifconfig/input.py b/python/vyos/ifconfig/input.py index 3e5f5790d..201d3cacb 100644 --- a/python/vyos/ifconfig/input.py +++ b/python/vyos/ifconfig/input.py @@ -25,8 +25,6 @@ class InputIf(Interface):      a single stack of qdiscs, classes and filters can be shared between      multiple interfaces.      """ - -    iftype = 'ifb'      definition = {          **Interface.definition,          **{ @@ -34,3 +32,6 @@ class InputIf(Interface):              'prefixes': ['ifb', ],          },      } + +    def _create(self): +        super()._create('ifb') diff --git a/python/vyos/ifconfig/interface.py b/python/vyos/ifconfig/interface.py index a58ee0bbd..cb73e2597 100644 --- a/python/vyos/ifconfig/interface.py +++ b/python/vyos/ifconfig/interface.py @@ -29,7 +29,6 @@ from netifaces import AF_INET6  from netaddr import EUI  from netaddr import mac_unix_expanded -from vyos.base import ConfigError  from vyos.configdict import list_diff  from vyos.configdict import dict_merge  from vyos.configdict import get_vlan_ids @@ -74,7 +73,6 @@ class Interface(Control):      OperationalClass = Operational      options = ['debug', 'create'] -    required = []      default = {          'debug': True,          'create': True, @@ -336,22 +334,10 @@ class Interface(Control):          super().__init__(**kargs)          if not self.exists(ifname): -            # Any instance of Interface, such as Interface('eth0') can be used -            # safely to access the generic function in this class as 'type' is -            # unset, the class can not be created -            if not hasattr(self, 'iftype'): -                raise ConfigError(f'Interface "{ifname}" has no "iftype" attribute defined!') -            self.config['type'] = self.iftype -              # Should an Instance of a child class (EthernetIf, DummyIf, ..)              # be required, then create should be set to False to not accidentally create it.              # In case a subclass does not define it, we use get to set the default to True -            if self.config.get('create',True): -                for k in self.required: -                    if k not in kargs: -                        name = self.default['type'] -                        raise ConfigError(f'missing required option {k} for {name} {ifname} creation') - +            if self.config.get('create', True):                  self._create()              # If we can not connect to the interface then let the caller know              # as the class could not be correctly initialised @@ -364,13 +350,14 @@ class Interface(Control):          self.operational = self.OperationalClass(ifname)          self.vrrp = VRRP(ifname) -    def _create(self): +    def _create(self, type: str=''):          # Do not create interface that already exist or exists in netns          netns = self.config.get('netns', None)          if self.exists(f'{self.ifname}', netns=netns):              return -        cmd = 'ip link add dev {ifname} type {type}'.format(**self.config) +        cmd = f'ip link add dev {self.ifname}' +        if type: cmd += f' type {type}'          if 'netns' in self.config: cmd = f'ip netns exec {netns} {cmd}'          self._cmd(cmd) @@ -1952,8 +1939,6 @@ class Interface(Control):  class VLANIf(Interface):      """ Specific class which abstracts 802.1q and 802.1ad (Q-in-Q) VLAN interfaces """ -    iftype = 'vlan' -      def _create(self):          # bail out early if interface already exists          if self.exists(f'{self.ifname}'): diff --git a/python/vyos/ifconfig/l2tpv3.py b/python/vyos/ifconfig/l2tpv3.py index c1f2803ee..dfaa006aa 100644 --- a/python/vyos/ifconfig/l2tpv3.py +++ b/python/vyos/ifconfig/l2tpv3.py @@ -45,7 +45,6 @@ class L2TPv3If(Interface):      either hot standby or load balancing services. Additionally, link integrity      monitoring may be performed.      """ -    iftype = 'l2tp'      definition = {          **Interface.definition,          **{ diff --git a/python/vyos/ifconfig/loopback.py b/python/vyos/ifconfig/loopback.py index e1d041839..13e8a2c50 100644 --- a/python/vyos/ifconfig/loopback.py +++ b/python/vyos/ifconfig/loopback.py @@ -22,16 +22,20 @@ class LoopbackIf(Interface):      uses to communicate with itself.      """      _persistent_addresses = ['127.0.0.1/8', '::1/128'] -    iftype = 'loopback'      definition = {          **Interface.definition,          **{              'section': 'loopback',              'prefixes': ['lo', ],              'bridgeable': True, +            'eternal': 'lo$',          }      } +    def _create(self): +        # we can not create this interface as it is managed by the Kernel +        pass +      def remove(self):          """          Loopback interface can not be deleted from operating system. We can diff --git a/python/vyos/ifconfig/macsec.py b/python/vyos/ifconfig/macsec.py index 383905814..3b4dc223f 100644 --- a/python/vyos/ifconfig/macsec.py +++ b/python/vyos/ifconfig/macsec.py @@ -27,7 +27,6 @@ class MACsecIf(Interface):      other security solutions such as IPsec (layer 3) or TLS (layer 4), as all      those solutions are used for their own specific use cases.      """ -    iftype = 'macsec'      definition = {          **Interface.definition,          **{ @@ -43,7 +42,7 @@ class MACsecIf(Interface):          """          # create tunnel interface -        cmd  = 'ip link add link {source_interface} {ifname} type {type}'.format(**self.config) +        cmd  = 'ip link add link {source_interface} {ifname} type macsec'.format(**self.config)          cmd += f' cipher {self.config["security"]["cipher"]}'          if 'encrypt' in self.config["security"]: diff --git a/python/vyos/ifconfig/macvlan.py b/python/vyos/ifconfig/macvlan.py index fb7f1d298..fe948b920 100644 --- a/python/vyos/ifconfig/macvlan.py +++ b/python/vyos/ifconfig/macvlan.py @@ -20,7 +20,6 @@ class MACVLANIf(Interface):      """      Abstraction of a Linux MACvlan interface      """ -    iftype = 'macvlan'      definition = {          **Interface.definition,          **{ @@ -35,12 +34,12 @@ class MACVLANIf(Interface):          down by default.          """          # please do not change the order when assembling the command -        cmd = 'ip link add {ifname} link {source_interface} type {type} mode {mode}' +        cmd = 'ip link add {ifname} link {source_interface} type macvlan mode {mode}'          self._cmd(cmd.format(**self.config))          # interface is always A/D down. It needs to be enabled explicitly          self.set_admin_state('down')      def set_mode(self, mode): -        cmd = f'ip link set dev {self.ifname} type {self.iftype} mode {mode}' +        cmd = f'ip link set dev {self.ifname} type macvlan mode {mode}'          return self._cmd(cmd) diff --git a/python/vyos/ifconfig/pppoe.py b/python/vyos/ifconfig/pppoe.py index f80a68d4f..85ca3877e 100644 --- a/python/vyos/ifconfig/pppoe.py +++ b/python/vyos/ifconfig/pppoe.py @@ -19,7 +19,6 @@ from vyos.utils.network import get_interface_config  @Interface.register  class PPPoEIf(Interface): -    iftype = 'pppoe'      definition = {          **Interface.definition,          **{ diff --git a/python/vyos/ifconfig/sstpc.py b/python/vyos/ifconfig/sstpc.py index 50fc6ee6b..d92ef23dc 100644 --- a/python/vyos/ifconfig/sstpc.py +++ b/python/vyos/ifconfig/sstpc.py @@ -17,7 +17,6 @@ from vyos.ifconfig.interface import Interface  @Interface.register  class SSTPCIf(Interface): -    iftype = 'sstpc'      definition = {          **Interface.definition,          **{ diff --git a/python/vyos/ifconfig/tunnel.py b/python/vyos/ifconfig/tunnel.py index 9ba7b31a6..df904f7d5 100644 --- a/python/vyos/ifconfig/tunnel.py +++ b/python/vyos/ifconfig/tunnel.py @@ -90,9 +90,8 @@ class TunnelIf(Interface):          # T3357: we do not have the 'encapsulation' in kargs when calling this          # class from op-mode like "show interfaces tunnel"          if 'encapsulation' in kargs: -            self.iftype = kargs['encapsulation']              # The gretap interface has the possibility to act as L2 bridge -            if self.iftype in ['gretap', 'ip6gretap']: +            if kargs['encapsulation'] in ['gretap', 'ip6gretap']:                  # no multicast, ttl or tos for gretap                  self.definition = {                      **TunnelIf.definition, @@ -110,10 +109,10 @@ class TunnelIf(Interface):              mapping = { **self.mapping, **self.mapping_ipv4 }          cmd = 'ip tunnel add {ifname} mode {encapsulation}' -        if self.iftype in ['gretap', 'ip6gretap', 'erspan', 'ip6erspan']: +        if self.config['encapsulation'] in ['gretap', 'ip6gretap', 'erspan', 'ip6erspan']:              cmd = 'ip link add name {ifname} type {encapsulation}'              # ERSPAN requires the serialisation of packets -            if self.iftype in ['erspan', 'ip6erspan']: +            if self.config['encapsulation'] in ['erspan', 'ip6erspan']:                  cmd += ' seq'          for vyos_key, iproute2_key in mapping.items(): @@ -132,7 +131,7 @@ class TunnelIf(Interface):      def _change_options(self):          # gretap interfaces do not support changing any parameter -        if self.iftype in ['gretap', 'ip6gretap', 'erspan', 'ip6erspan']: +        if self.config['encapsulation'] in ['gretap', 'ip6gretap', 'erspan', 'ip6erspan']:              return          if self.config['encapsulation'] in ['ipip6', 'ip6ip6', 'ip6gre']: diff --git a/python/vyos/ifconfig/veth.py b/python/vyos/ifconfig/veth.py index aafbf226a..2c8709d20 100644 --- a/python/vyos/ifconfig/veth.py +++ b/python/vyos/ifconfig/veth.py @@ -21,7 +21,6 @@ class VethIf(Interface):      """      Abstraction of a Linux veth interface      """ -    iftype = 'veth'      definition = {          **Interface.definition,          **{ @@ -46,7 +45,7 @@ class VethIf(Interface):              return          # create virtual-ethernet interface -        cmd = 'ip link add {ifname} type {type}'.format(**self.config) +        cmd = f'ip link add {self.ifname} type veth'          cmd += f' peer name {self.config["peer_name"]}'          self._cmd(cmd) diff --git a/python/vyos/ifconfig/vrrp.py b/python/vyos/ifconfig/vrrp.py index a3657370f..3ee22706c 100644 --- a/python/vyos/ifconfig/vrrp.py +++ b/python/vyos/ifconfig/vrrp.py @@ -26,15 +26,12 @@ from vyos.utils.file import read_file  from vyos.utils.file import wait_for_file_write_complete  from vyos.utils.process import process_running -  class VRRPError(Exception):      pass -  class VRRPNoData(VRRPError):      pass -  class VRRP(object):      _vrrp_prefix = '00:00:5E:00:01:'      location = { diff --git a/python/vyos/ifconfig/vti.py b/python/vyos/ifconfig/vti.py index 251cbeb36..78f5895f8 100644 --- a/python/vyos/ifconfig/vti.py +++ b/python/vyos/ifconfig/vti.py @@ -19,7 +19,6 @@ from vyos.utils.vti_updown_db import vti_updown_db_exists, open_vti_updown_db_re  @Interface.register  class VTIIf(Interface): -    iftype = 'vti'      definition = {          **Interface.definition,          **{ diff --git a/python/vyos/ifconfig/vtun.py b/python/vyos/ifconfig/vtun.py index 6fb414e56..ee790f275 100644 --- a/python/vyos/ifconfig/vtun.py +++ b/python/vyos/ifconfig/vtun.py @@ -17,7 +17,6 @@ from vyos.ifconfig.interface import Interface  @Interface.register  class VTunIf(Interface): -    iftype = 'vtun'      definition = {          **Interface.definition,          **{ diff --git a/python/vyos/ifconfig/vxlan.py b/python/vyos/ifconfig/vxlan.py index 1023c58d1..58844885b 100644 --- a/python/vyos/ifconfig/vxlan.py +++ b/python/vyos/ifconfig/vxlan.py @@ -42,8 +42,6 @@ class VXLANIf(Interface):      For more information please refer to:      https://www.kernel.org/doc/Documentation/networking/vxlan.txt      """ - -    iftype = 'vxlan'      definition = {          **Interface.definition,          **{ @@ -94,7 +92,7 @@ class VXLANIf(Interface):              remote_list = self.config['remote'][1:]              self.config['remote'] = self.config['remote'][0] -        cmd = 'ip link add {ifname} type {type} dstport {port}' +        cmd = 'ip link add {ifname} type vxlan dstport {port}'          for vyos_key, iproute2_key in mapping.items():              # dict_search will return an empty dict "{}" for valueless nodes like              # "parameters.nolearning" - thus we need to test the nodes existence diff --git a/python/vyos/ifconfig/wireguard.py b/python/vyos/ifconfig/wireguard.py index cccac361d..519012625 100644 --- a/python/vyos/ifconfig/wireguard.py +++ b/python/vyos/ifconfig/wireguard.py @@ -26,7 +26,6 @@ from vyos.ifconfig import Interface  from vyos.ifconfig import Operational  from vyos.template import is_ipv6 -  class WireGuardOperational(Operational):      def _dump(self):          """Dump wireguard data in a python friendly way.""" @@ -160,18 +159,18 @@ class WireGuardOperational(Operational):  @Interface.register  class WireGuardIf(Interface):      OperationalClass = WireGuardOperational -    iftype = 'wireguard'      definition = {          **Interface.definition,          **{              'section': 'wireguard', -            'prefixes': [ -                'wg', -            ], +            'prefixes': ['wg', ],              'bridgeable': False,          },      } +    def _create(self): +        super()._create('wireguard') +      def get_mac(self):          """Get a synthetic MAC address."""          return self.get_mac_synthetic() diff --git a/python/vyos/ifconfig/wireless.py b/python/vyos/ifconfig/wireless.py index 88eaa772b..121f56bd5 100644 --- a/python/vyos/ifconfig/wireless.py +++ b/python/vyos/ifconfig/wireless.py @@ -20,7 +20,6 @@ class WiFiIf(Interface):      """      Handle WIFI/WLAN interfaces.      """ -    iftype = 'wifi'      definition = {          **Interface.definition,          **{ diff --git a/python/vyos/ifconfig/wwan.py b/python/vyos/ifconfig/wwan.py index 845c9bef9..004a64b39 100644 --- a/python/vyos/ifconfig/wwan.py +++ b/python/vyos/ifconfig/wwan.py @@ -17,7 +17,6 @@ from vyos.ifconfig.interface import Interface  @Interface.register  class WWANIf(Interface): -    iftype = 'wwan'      definition = {          **Interface.definition,          **{ diff --git a/python/vyos/utils/process.py b/python/vyos/utils/process.py index d8aabb822..054088325 100644 --- a/python/vyos/utils/process.py +++ b/python/vyos/utils/process.py @@ -20,10 +20,23 @@ from subprocess import PIPE  from subprocess import STDOUT  from subprocess import DEVNULL + +def get_wrapper(vrf, netns, auth): +    wrapper = '' +    if vrf: +        wrapper = f'ip vrf exec {vrf} ' +    elif netns: +        wrapper = f'ip netns exec {netns} ' +    if auth: +        wrapper = f'{auth} {wrapper}' +    return wrapper + +  def popen(command, flag='', shell=None, input=None, timeout=None, env=None, -          stdout=PIPE, stderr=PIPE, decode='utf-8'): +          stdout=PIPE, stderr=PIPE, decode='utf-8', auth='', vrf=None, +          netns=None):      """ -    popen is a wrapper helper aound subprocess.Popen +    popen is a wrapper helper around subprocess.Popen      with it default setting it will return a tuple (out, err)      out: the output of the program run      err: the error code returned by the program @@ -45,6 +58,8 @@ def popen(command, flag='', shell=None, input=None, timeout=None, env=None,                - DEVNULL, discard the output      decode:  specify the expected text encoding (utf-8, ascii, ...)               the default is explicitely utf-8 which is python's own default +    vrf:     run command in a VRF context +    netns:   run command in the named network namespace      usage:      get both stdout and stderr: popen('command', stdout=PIPE, stderr=STDOUT) @@ -60,6 +75,16 @@ def popen(command, flag='', shell=None, input=None, timeout=None, env=None,      if not debug.enabled(flag):          flag = 'command' +    # Must be run as root to execute command in VRF or network namespace +    if vrf or netns: +        if os.getuid() != 0: +            raise OSError( +                'Permission denied: cannot execute commands in VRF and netns contexts as an unprivileged user' +            ) + +    wrapper = get_wrapper(vrf, netns, auth) +    command = f'{wrapper} {command}' +      cmd_msg = f"cmd '{command}'"      debug.message(cmd_msg, flag) @@ -111,7 +136,7 @@ def popen(command, flag='', shell=None, input=None, timeout=None, env=None,  def run(command, flag='', shell=None, input=None, timeout=None, env=None, -        stdout=DEVNULL, stderr=PIPE, decode='utf-8'): +        stdout=DEVNULL, stderr=PIPE, decode='utf-8', vrf=None, netns=None):      """      A wrapper around popen, which discard the stdout and      will return the error code of a command @@ -122,13 +147,15 @@ def run(command, flag='', shell=None, input=None, timeout=None, env=None,          input=input, timeout=timeout,          env=env, shell=shell,          decode=decode, +        vrf=vrf, +        netns=netns,      )      return code  def cmd(command, flag='', shell=None, input=None, timeout=None, env=None,          stdout=PIPE, stderr=PIPE, decode='utf-8', raising=None, message='', -        expect=[0], auth=''): +        expect=[0], auth='', vrf=None, netns=None):      """      A wrapper around popen, which returns the stdout and      will raise the error code of a command @@ -139,13 +166,18 @@ def cmd(command, flag='', shell=None, input=None, timeout=None, env=None,      expect:  a list of error codes to consider as normal      """      decoded, code = popen( -        f'{auth} {command}'.strip(), flag, +        command, flag,          stdout=stdout, stderr=stderr,          input=input, timeout=timeout,          env=env, shell=shell,          decode=decode, +        auth=auth, +        vrf=vrf, +        netns=netns,      )      if code not in expect: +        wrapper = get_wrapper(vrf, netns, auth='') +        command = f'{wrapper} {command}'          feedback = message + '\n' if message else ''          feedback += f'failed to run command: {command}\n'          feedback += f'returned: {decoded}\n' @@ -159,7 +191,7 @@ def cmd(command, flag='', shell=None, input=None, timeout=None, env=None,  def rc_cmd(command, flag='', shell=None, input=None, timeout=None, env=None, -           stdout=PIPE, stderr=STDOUT, decode='utf-8'): +           stdout=PIPE, stderr=STDOUT, decode='utf-8', vrf=None, netns=None):      """      A wrapper around popen, which returns the return code      of a command and stdout @@ -175,11 +207,14 @@ def rc_cmd(command, flag='', shell=None, input=None, timeout=None, env=None,          input=input, timeout=timeout,          env=env, shell=shell,          decode=decode, +        vrf=vrf, +        netns=netns,      )      return code, out +  def call(command, flag='', shell=None, input=None, timeout=None, env=None, -         stdout=None, stderr=None, decode='utf-8'): +         stdout=None, stderr=None, decode='utf-8', vrf=None, netns=None):      """      A wrapper around popen, which print the stdout and      will return the error code of a command @@ -190,11 +225,14 @@ def call(command, flag='', shell=None, input=None, timeout=None, env=None,          input=input, timeout=timeout,          env=env, shell=shell,          decode=decode, +        vrf=vrf, +        netns=netns,      )      if out:          print(out)      return code +  def process_running(pid_file):      """ Checks if a process with PID in pid_file is running """      from psutil import pid_exists | 
