Commit b64aa4ec authored by Salvatore Orlando's avatar Salvatore Orlando

NetNS: Allow full namespace path in constructor

If the netns argument to the NetNS constructor is the path of an
existing file, that is interpreted as the full namespace path, and
not appended to netns.NETNS_RUN_DIR.

This patch add a setns_from_path function to the netns module.
This function simply opens the namespace, perform the setns syscall,
and returns the file descriptor. In order to avoid code duplication
the setns function now calls setns_from_path

A test has been added to validate creating a NetNS object using the
path of an existing namespace.

Creating or deleting namespaces using a full path has not been
implemented as a part of this patch.
parent a0fa0a75
......@@ -164,6 +164,15 @@ def remove(netns, libc=None):
os.unlink(netnspath)
def setns_from_path(netnspath, libc=None):
nsfd = os.open(netnspath, os.O_RDONLY)
libc = libc or ctypes.CDLL('libc.so.6', use_errno=True)
ret = libc.syscall(__NR_setns, nsfd, CLONE_NEWNET)
if ret != 0:
raise OSError(ctypes.get_errno(), 'failed to open netns', netnspath)
return nsfd
def setns(netns, flags=os.O_CREAT, libc=None):
'''
Set netns for the current process.
......@@ -174,7 +183,6 @@ def setns(netns, flags=os.O_CREAT, libc=None):
- O_CREAT -- create netns, if doesn't exist
- O_CREAT | O_EXCL -- create only if doesn't exist
'''
libc = libc or ctypes.CDLL('libc.so.6', use_errno=True)
netnspath = '%s/%s' % (NETNS_RUN_DIR, netns)
netnspath = netnspath.encode('ascii')
......@@ -185,8 +193,4 @@ def setns(netns, flags=os.O_CREAT, libc=None):
if flags & os.O_CREAT:
create(netns, libc=libc)
nsfd = os.open(netnspath, os.O_RDONLY)
ret = libc.syscall(__NR_setns, nsfd, CLONE_NEWNET)
if ret != 0:
raise OSError(ctypes.get_errno(), 'failed to open netns', netns)
return nsfd
return setns_from_path(netnspath, libc)
......@@ -65,6 +65,7 @@ run `remove()`.
'''
import os
import os.path
import errno
import fcntl
import atexit
......@@ -76,6 +77,7 @@ from pyroute2.config import MpProcess
from pyroute2.netlink.rtnl.iprsocket import MarshalRtnl
from pyroute2.iproute import IPRouteMixin
from pyroute2.netns import setns
from pyroute2.netns import setns_from_path
from pyroute2.netns import remove
from pyroute2.remote import Server
from pyroute2.remote import RemoteSocket
......@@ -128,7 +130,13 @@ def NetNServer(netns, cmdch, brdch, flags=os.O_CREAT):
'''
signal.signal(signal.SIGINT, signal.SIG_IGN)
try:
nsfd = setns(netns, flags)
# Check if we wrepassed a namespace name or the namespace full path
# In the latter case flags will be ignored as it is assumed the
# namespace already exists
if os.path.isfile(netns):
nsfd = setns_from_path(netns)
else:
nsfd = setns(netns, flags)
except OSError as e:
cmdch.send({'stage': 'init',
'error': e})
......
......@@ -3,6 +3,7 @@ import time
import fcntl
import platform
import subprocess
import tempfile
from pyroute2 import IPDB
from pyroute2 import IPRoute
from pyroute2 import NetNS
......@@ -188,12 +189,11 @@ class TestNetNS(object):
netnsmod.remove(foo)
netnsmod.remove(bar)
def test_create(self):
def _test_create(self, ns_name, ns_fd=None):
require_user('root')
nsid = str(uuid4())
ipdb_main = IPDB()
ipdb_test = IPDB(nl=NetNS(nsid))
ipdb_test = IPDB(nl=NetNS(ns_name))
if1 = uifname()
if2 = uifname()
......@@ -202,7 +202,7 @@ class TestNetNS(object):
# move the peer to netns
with ipdb_main.interfaces[if2] as veth:
veth.net_ns_fd = nsid
veth.net_ns_fd = ns_fd or ns_name
# assign addresses
with ipdb_main.interfaces[if1] as veth:
......@@ -231,11 +231,31 @@ class TestNetNS(object):
ipdb_main.interfaces[if1].remove().commit()
ipdb_main.release()
ipdb_test.release()
netnsmod.remove(nsid)
assert ret_ping
assert ret_arp
assert nsid not in netnsmod.listnetns()
def test_create(self):
ns_name = str(uuid4())
self._test_create(ns_name)
netnsmod.remove(ns_name)
assert ns_name not in netnsmod.listnetns()
def test_create_from_path(self):
ns_dir = tempfile.mkdtemp()
netns_run_dir = netnsmod.NETNS_RUN_DIR
netnsmod.NETNS_RUN_DIR = ns_dir
# Create namespace
ns_name = str(uuid4())
temp_ns = NetNS(ns_name)
temp_ns.close()
nspath = '%s/%s' % (ns_dir, ns_name)
fd = open(nspath)
self._test_create(nspath, fd.fileno())
fd.close()
netnsmod.remove(ns_name)
assert ns_name not in netnsmod.listnetns()
netnsmod.NETNS_RUN_DIR = netns_run_dir
def test_rename_plus_ipv6(self):
require_user('root')
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment