import paramiko import subprocess from charmhelpers.core.hookenv import config class NetNS(object): def __init__(self, name): pass @classmethod def create(cls, name): # @TODO: Need to check if namespace exists already try: ip('netns', 'add', name) except Exception as e: raise Exception('could not create net namespace: %s' % e) return cls(name) def up(self, iface, cidr): self.do('ip', 'link', 'set', 'dev', iface, 'up') self.do('ip', 'address', 'add', cidr, 'dev', iface) def add_iface(self, iface): ip('link', 'set', 'dev', iface, 'netns', self.name) def do(self, *cmd): ip(*['netns', 'exec', self.name] + cmd) def ip(*args): return _run(['ip'] + list(args)) def _run(cmd, env=None): if isinstance(cmd, str): cmd = cmd.split() if ' ' in cmd else [cmd] cfg = config() if all(k in cfg for k in ['pass', 'vpe-router', 'user']): router = cfg['vpe-router'] user = cfg['user'] passwd = cfg['pass'] if router and user and passwd: return ssh(cmd, router, user, passwd) p = subprocess.Popen(cmd, env=env, stdout=subprocess.PIPE, stderr=subprocess.PIPE) stdout, stderr = p.communicate() retcode = p.poll() if retcode > 0: raise subprocess.CalledProcessError(returncode=retcode, cmd=cmd, output=stderr.decode("utf-8").strip()) return (''.join(stdout), ''.join(stderr)) def ssh(cmd, host, user, password=None): ''' Suddenly this project needs to SSH to something. So we replicate what _run was doing with subprocess using the Paramiko library. This is temporary until this charm /is/ the VPE Router ''' cmds = ' '.join(cmd) client = paramiko.SSHClient() client.set_missing_host_key_policy(paramiko.AutoAddPolicy()) client.connect(host, port=22, username=user, password=password) stdin, stdout, stderr = client.exec_command(cmds) retcode = stdout.channel.recv_exit_status() client.close() # @TODO re-use connections if retcode > 0: output = stderr.read().strip() raise subprocess.CalledProcessError(returncode=retcode, cmd=cmd, output=output) return (''.join(stdout), ''.join(stderr))