feat: add option to work with and keep an existing namespace

pull/4/head
Marnik De Bont 2 years ago
parent 42f67672f0
commit 0e25354d50

@ -3,6 +3,7 @@ from argparse import ArgumentParser, RawDescriptionHelpFormatter
from pathlib import Path from pathlib import Path
import json import json
import os import os
import re
import subprocess import subprocess
import sys import sys
@ -33,6 +34,7 @@ def main(args):
parser = subparsers.add_parser('down', help='teardown namespace and associated interfaces') parser = subparsers.add_parser('down', help='teardown namespace and associated interfaces')
parser.add_argument('-f', '--force', action='store_true', help='ignore errors') parser.add_argument('-f', '--force', action='store_true', help='ignore errors')
parser.add_argument('-n', '--keep-namespace', action='store_true', help='keep the namespace')
parser.add_argument('profile', type=lambda x: Path(x).expanduser(), help='path to profile') parser.add_argument('profile', type=lambda x: Path(x).expanduser(), help='path to profile')
opts = entrypoint.parse_args(args) opts = entrypoint.parse_args(args)
@ -47,25 +49,26 @@ def main(args):
if opts.action == 'up': if opts.action == 'up':
setup_action(opts.profile) setup_action(opts.profile)
elif opts.action == 'down': elif opts.action == 'down':
teardown_action(opts.profile, check=not opts.force) teardown_action(opts.profile, check=not opts.force, keep_namespace=opts.keep_namespace)
else: else:
raise RuntimeError('congratulations, you reached unreachable code') raise RuntimeError('congratulations, you reached unreachable code')
def setup_action(path): def setup_action(path):
namespace = profile_read(path) namespace = profile_read(path)
namespace_exist = namespace_exists(namespace)
try: try:
namespace_setup(namespace) namespace_setup(namespace)
except KeyboardInterrupt: except KeyboardInterrupt:
namespace_teardown(namespace, check=False) namespace_teardown(namespace, check=False, keep_namespace=namespace_exist)
except Exception as e: except Exception as e:
namespace_teardown(namespace, check=False) namespace_teardown(namespace, check=False, keep_namespace=namespace_exist)
raise raise
def teardown_action(path, check=True): def teardown_action(path, check=True, keep_namespace=False):
namespace = profile_read(path) namespace = profile_read(path)
namespace_teardown(namespace, check=check) namespace_teardown(namespace, check=check, keep_namespace=keep_namespace)
def profile_read(path): def profile_read(path):
@ -76,7 +79,8 @@ def profile_read(path):
def namespace_setup(namespace): def namespace_setup(namespace):
if namespace.get('pre-up'): if namespace.get('pre-up'):
ip_netns_shell(namespace['pre-up'], netns=namespace) ip_netns_shell(namespace['pre-up'], netns=namespace)
namespace_create(namespace) if not namespace_exists(namespace):
namespace_create(namespace)
namespace_resolvconf_write(namespace) namespace_resolvconf_write(namespace)
for interface in namespace['interfaces']: for interface in namespace['interfaces']:
interface_setup(interface, namespace) interface_setup(interface, namespace)
@ -84,6 +88,22 @@ def namespace_setup(namespace):
ip_netns_shell(namespace['post-up'], netns=namespace) ip_netns_shell(namespace['post-up'], netns=namespace)
def namespace_get_list_of_existing():
ip_list = ip('netns', 'list', capture=True).splitlines()
rg = re.compile('(?P<name>[^ ]*)(?: \(id: (?P<id>\d+)\))?')
existing_namespaces = dict()
for line in ip_list:
match = rg.fullmatch(line)
if match:
existing_namespaces[match.group("name")] = match.group("id")
return existing_namespaces
def namespace_exists(namespace):
existing_namespaces = namespace_get_list_of_existing()
return namespace['name'] in existing_namespaces
def namespace_create(namespace): def namespace_create(namespace):
ip('netns', 'add', namespace['name']) ip('netns', 'add', namespace['name'])
ip('-n', namespace['name'], 'link', 'set', 'dev', 'lo', 'up') ip('-n', namespace['name'], 'link', 'set', 'dev', 'lo', 'up')
@ -96,12 +116,13 @@ def namespace_resolvconf_write(namespace):
NETNS_CONFIG_DIR.joinpath(namespace['name']).joinpath('resolv.conf').write_text(content) NETNS_CONFIG_DIR.joinpath(namespace['name']).joinpath('resolv.conf').write_text(content)
def namespace_teardown(namespace, check=True): def namespace_teardown(namespace, check=True, keep_namespace=False):
if namespace.get('pre-down'): if namespace.get('pre-down'):
ip_netns_shell(namespace['pre-down'], netns=namespace) ip_netns_shell(namespace['pre-down'], netns=namespace)
for interface in namespace['interfaces']: for interface in namespace['interfaces']:
interface_teardown(interface, namespace) interface_teardown(interface, namespace)
namespace_delete(namespace) if not keep_namespace:
namespace_delete(namespace)
namespace_resolvconf_delete(namespace) namespace_resolvconf_delete(namespace)
if namespace.get('post-down'): if namespace.get('post-down'):
ip_netns_shell(namespace['post-down'], netns=namespace) ip_netns_shell(namespace['post-down'], netns=namespace)

Loading…
Cancel
Save