class NetNS(IPRouteMixin, RemoteSocket): ''' NetNS is the IPRoute API with network namespace support. **Why not IPRoute?** The task to run netlink commands in some network namespace, being in another network namespace, requires the architecture, that differs too much from a simple Netlink socket. NetNS starts a proxy process in a network namespace and uses `multiprocessing` communication channels between the main and the proxy processes to route all `recv()` and `sendto()` requests/responses. **Any specific API calls?** Nope. `NetNS` supports all the same, that `IPRoute` does, in the same way. It provides full `socket`-compatible API and can be used in poll/select as well. The only difference is the `close()` call. In the case of `NetNS` it is **mandatory** to close the socket before exit. **NetNS and IPDB** It is possible to run IPDB with NetNS:: from pyroute2 import NetNS from pyroute2 import IPDB ip = IPDB(nl=NetNS('somenetns')) ... ip.release() Do not forget to call `release()` when the work is done. It will shut down `NetNS` instance as well. ''' def __init__(self, netns, flags=os.O_CREAT): self.netns = netns self.flags = flags self.cmdch, cmdch = MpPipe() self.brdch, brdch = MpPipe() self.server = MpProcess(target=NetNServer, args=(self.netns, cmdch, brdch, self.flags)) self.server.start() super(NetNS, self).__init__() self.marshal = MarshalRtnl() def close(self): super(NetNS, self).close() self.server.join() def post_init(self): pass def remove(self): ''' Try to remove this network namespace from the system. ''' remove(self.netns)
class NetNS(IPRouteMixin, RemoteSocket): """ NetNS is the IPRoute API with network namespace support. **Why not IPRoute?** The task to run netlink commands in some network namespace, being in another network namespace, requires the architecture, that differs too much from a simple Netlink socket. NetNS starts a proxy process in a network namespace and uses `multiprocessing` communication channels between the main and the proxy processes to route all `recv()` and `sendto()` requests/responses. **Any specific API calls?** Nope. `NetNS` supports all the same, that `IPRoute` does, in the same way. It provides full `socket`-compatible API and can be used in poll/select as well. The only difference is the `close()` call. In the case of `NetNS` it is **mandatory** to close the socket before exit. **NetNS and IPDB** It is possible to run IPDB with NetNS:: from pyroute2 import NetNS from pyroute2 import IPDB ip = IPDB(nl=NetNS('somenetns')) ... ip.release() Do not forget to call `release()` when the work is done. It will shut down `NetNS` instance as well. """ def __init__(self, netns, flags=os.O_CREAT): self.netns = netns self.flags = flags self.cmdch, cmdch = MpPipe() self.brdch, brdch = MpPipe() self.server = MpProcess(target=NetNServer, args=(self.netns, cmdch, brdch, self.flags)) self.server.start() super(NetNS, self).__init__() self.marshal = MarshalRtnl() def close(self): super(NetNS, self).close() self.server.join() def post_init(self): pass def remove(self): """ Try to remove this network namespace from the system. """ remove(self.netns)
def __init__(self, netns, flags=os.O_CREAT): self.netns = netns self.flags = flags self.cmdch, cmdch = MpPipe() self.brdch, brdch = MpPipe() self.server = MpProcess(target=NetNServer, args=(self.netns, cmdch, brdch, self.flags)) self.server.start() super(NetNS, self).__init__() self.marshal = MarshalRtnl()
def __init__(self, *argv, **kwarg): self.cmdlock = threading.Lock() self.rcvch, rcvch = MpPipe() self.cmdch, cmdch = MpPipe() self.server = MpProcess(target=NetNServer, args=(self.netns, rcvch, cmdch, self.flags)) self.server.start() error = self.cmdch.recv() if error is not None: self.server.join() raise error else: atexit.register(self.close)
def __init__(self, nsname, *argv, **kwarg): ''' The only differences from the `subprocess.Popen` init are: * `nsname` -- network namespace name * `flags` keyword argument All other arguments are passed directly to `subprocess.Popen`. Flags usage samples. Create a network namespace, if it doesn't exist yet:: import os nsp = NSPopen('nsname', ['command'], flags=os.O_CREAT) Create a network namespace only if it doesn't exist, otherwise fail and raise an exception:: import os nsp = NSPopen('nsname', ['command'], flags=os.O_CREAT | os.O_EXCL) ''' # create a child self.nsname = nsname if 'flags' in kwarg: self.flags = kwarg.pop('flags') else: self.flags = 0 self.channel_out = MpQueue() self.channel_in = MpQueue() self.lock = threading.Lock() self.released = False self.server = MpProcess(target=NSPopenServer, args=(self.nsname, self.flags, self.channel_out, self.channel_in, argv, kwarg)) # start the child and check the status self.server.start() response = self.channel_in.get() if isinstance(response, Exception): self.server.join() raise response else: atexit.register(self.release)
class NetNSProxy(object): netns = 'default' flags = os.O_CREAT def __init__(self, *argv, **kwarg): self.cmdlock = threading.Lock() self.rcvch, rcvch = MpPipe() self.cmdch, cmdch = MpPipe() self.server = MpProcess(target=NetNServer, args=(self.netns, rcvch, cmdch, self.flags)) self.server.start() error = self.cmdch.recv() if error is not None: self.server.join() raise error else: atexit.register(self.close) def recv(self, bufsize, flags=0): return self.rcvch.recv() def close(self): self.cmdch.send(None) self.server.join() def proxy(self, cmd, *argv, **kwarg): with self.cmdlock: self.cmdch.send((cmd, argv, kwarg)) response = self.cmdch.recv() if isinstance(response, Exception): raise response return response def fileno(self): return self.rcvch.fileno() def bind(self, *argv, **kwarg): if 'async' in kwarg: kwarg['async'] = False return self.proxy('bind', *argv, **kwarg) def send(self, *argv, **kwarg): return self.proxy('send', *argv, **kwarg) def sendto(self, *argv, **kwarg): return self.proxy('sendto', *argv, **kwarg) def getsockopt(self, *argv, **kwarg): return self.proxy('getsockopt', *argv, **kwarg) def setsockopt(self, *argv, **kwarg): return self.proxy('setsockopt', *argv, **kwarg)
class NSPopen(NSPopenBase, ObjNS): ''' A proxy class to run `Popen()` object in some network namespace. Sample to run `ip ad` command in `nsname` network namespace:: nsp = NSPopen('nsname', ['ip', 'ad'], stdout=subprocess.PIPE) print(nsp.communicate()) nsp.wait() nsp.release() The `NSPopen` class was intended to be a drop-in replacement for the `Popen` class, but there are still some important differences. The `NSPopen` object implicitly spawns a child python process to be run in the background in a network namespace. The target process specified as the argument of the `NSPopen` will be started in its turn from this child. Thus all the fd numbers of the running `NSPopen` object are meaningless in the context of the main process. Trying to operate on them, one will get 'Bad file descriptor' in the best case or a system call working on a wrong file descriptor in the worst case. A possible solution would be to transfer file descriptors between the `NSPopen` object and the main process, but it is not implemented yet. The process' diagram for `NSPopen('test', ['ip', 'ad'])`:: +---------------------+ +--------------+ +------------+ | main python process |<--->| child python |<--->| netns test | | NSPopen() | | Popen() | | $ ip ad | +---------------------+ +--------------+ +------------+ As a workaround for the issue with file descriptors, some additional methods are available on file objects `stdin`, `stdout` and `stderr`. E.g., one can run fcntl calls:: from fcntl import F_GETFL from pyroute2 import NSPopen from subprocess import PIPE proc = NSPopen('test', ['my_program'], stdout=PIPE) flags = proc.stdout.fcntl(F_GETFL) In that way one can use `fcntl()`, `ioctl()`, `flock()` and `lockf()` calls. Another additional method is `release()`, which can be used to explicitly stop the proxy process and release all the resources. ''' def __init__(self, nsname, *argv, **kwarg): ''' The only differences from the `subprocess.Popen` init are: * `nsname` -- network namespace name * `flags` keyword argument All other arguments are passed directly to `subprocess.Popen`. Flags usage samples. Create a network namespace, if it doesn't exist yet:: import os nsp = NSPopen('nsname', ['command'], flags=os.O_CREAT) Create a network namespace only if it doesn't exist, otherwise fail and raise an exception:: import os nsp = NSPopen('nsname', ['command'], flags=os.O_CREAT | os.O_EXCL) ''' # create a child self.nsname = nsname if 'flags' in kwarg: self.flags = kwarg.pop('flags') else: self.flags = 0 self.channel_out = MpQueue() self.channel_in = MpQueue() self.lock = threading.Lock() self.released = False self.server = MpProcess(target=NSPopenServer, args=(self.nsname, self.flags, self.channel_out, self.channel_in, argv, kwarg)) # start the child and check the status self.server.start() response = self.channel_in.get() if isinstance(response, Exception): self.server.join() raise response else: atexit.register(self.release) def release(self): ''' Explicitly stop the proxy process and release all the resources. The `NSPopen` object can not be used after the `release()` call. ''' with self.lock: if self.released: return self.released = True self.channel_out.put({'name': 'release'}) self.channel_out.close() self.channel_in.close() self.server.join() def __dir__(self): return list(self.api.keys()) + ['release']
class NSPopen(NSPopenBase, ObjNS): ''' A proxy class to run `Popen()` object in some network namespace. Sample to run `ip ad` command in `nsname` network namespace:: nsp = NSPopen('nsname', ['ip', 'ad'], stdout=subprocess.PIPE) print(nsp.communicate()) nsp.wait() nsp.release() The only difference in the `release()` call. It explicitly ends the proxy process and releases all the resources. ''' def __init__(self, nsname, *argv, **kwarg): ''' The only differences from the `subprocess.Popen` init are: * `nsname` -- network namespace name * `flags` keyword argument All other arguments are passed directly to `subprocess.Popen`. Flags usage samples. Create a network namespace, if it doesn't exist yet:: import os nsp = NSPopen('nsname', ['command'], flags=os.O_CREAT) Create a network namespace only if it doesn't exist, otherwise fail and raise an exception:: import os nsp = NSPopen('nsname', ['command'], flags=os.O_CREAT | os.O_EXCL) ''' # create a child self.nsname = nsname if 'flags' in kwarg: self.flags = kwarg.pop('flags') else: self.flags = 0 self.channel_out = MpQueue() self.channel_in = MpQueue() self.lock = threading.Lock() self.released = False self.server = MpProcess(target=NSPopenServer, args=(self.nsname, self.flags, self.channel_out, self.channel_in, argv, kwarg)) # start the child and check the status self.server.start() response = self.channel_in.get() if isinstance(response, Exception): self.server.join() raise response else: atexit.register(self.release) def release(self): ''' Explicitly stop the proxy process and release all the resources. The `NSPopen` object can not be used after the `release()` call. ''' with self.lock: if self.released: return self.released = True self.channel_out.put({'name': 'release'}) self.channel_out.close() self.channel_in.close() self.server.join() def __dir__(self): return list(self.api.keys()) + ['release']
class NetNS(IPRouteMixin, RemoteSocket): ''' NetNS is the IPRoute API with network namespace support. **Why not IPRoute?** The task to run netlink commands in some network namespace, being in another network namespace, requires the architecture, that differs too much from a simple Netlink socket. NetNS starts a proxy process in a network namespace and uses `multiprocessing` communication channels between the main and the proxy processes to route all `recv()` and `sendto()` requests/responses. **Any specific API calls?** Nope. `NetNS` supports all the same, that `IPRoute` does, in the same way. It provides full `socket`-compatible API and can be used in poll/select as well. The only difference is the `close()` call. In the case of `NetNS` it is **mandatory** to close the socket before exit. **NetNS and IPDB** It is possible to run IPDB with NetNS:: from pyroute2 import NetNS from pyroute2 import IPDB ip = IPDB(nl=NetNS('somenetns')) ... ip.release() Do not forget to call `release()` when the work is done. It will shut down `NetNS` instance as well. ''' def __init__(self, netns, flags=os.O_CREAT): self.netns = netns self.flags = flags self.cmdch, self._cmdch = MpPipe() self.brdch, self._brdch = MpPipe() atexit.register(self.close) self.server = MpProcess(target=NetNServer, args=(self.netns, self._cmdch, self._brdch, self.flags)) self.server.start() super(NetNS, self).__init__() self.marshal = MarshalRtnl() def clone(self): return type(self)(self.netns, self.flags) def close(self): try: super(NetNS, self).close() except: # something went wrong, force server shutdown self.cmdch.send({'stage': 'shutdown'}) log.error('forced shutdown procedure, clean up netns manually') # force cleanup command channels self.cmdch.close() self.brdch.close() self._cmdch.close() self._brdch.close() # join the server self.server.join() # Workaround for http://bugs.python.org/issue27151 if sys.version_info > (3, 2) and sys.version_info < (3, 6): try: fcntl.fcntl(self.server.sentinel, fcntl.F_GETFD) except: pass else: os.close(self.server.sentinel) def post_init(self): pass def remove(self): ''' Try to remove this network namespace from the system. ''' remove(self.netns)