updates to lxd/docker to work with net_cmd/node_net_cmd

This commit is contained in:
Blake Harnden 2019-10-14 14:28:18 -07:00
parent 82bdbd776b
commit 5f282bb695
5 changed files with 122 additions and 72 deletions

View file

@ -452,14 +452,24 @@ class CoreNode(CoreNodeBase):
self._mounts = [] self._mounts = []
self.bootsh = bootsh self.bootsh = bootsh
if session.options.get_config("ovs") == "True": use_ovs = session.options.get_config("ovs") == "True"
self.node_net_client = OvsNetClient(self.node_net_cmd) self.node_net_client = self.create_node_net_client(use_ovs)
else:
self.node_net_client = LinuxNetClient(self.node_net_cmd)
if start: if start:
self.startup() self.startup()
def create_node_net_client(self, use_ovs):
"""
Create a client for running network orchestration commands.
:param bool use_ovs: True to use OVS bridges, False for Linux bridge
:return: network client
"""
if use_ovs:
return OvsNetClient(self.node_net_cmd)
else:
return LinuxNetClient(self.node_net_cmd)
def alive(self): def alive(self):
""" """
Check if the node is alive. Check if the node is alive.
@ -584,7 +594,7 @@ class CoreNode(CoreNodeBase):
:param str sh: shell to execute command in :param str sh: shell to execute command in
:return: str :return: str
""" """
return self.client.termcmdstring(sh) return self.client.create_cmd(sh)
def privatedir(self, path): def privatedir(self, path):
""" """

View file

@ -4,7 +4,7 @@ over a control channel to the vnoded process running in a network namespace.
The control channel can be accessed via calls using the vcmd shell. The control channel can be accessed via calls using the vcmd shell.
""" """
from core import constants, utils from core import utils
from core.constants import VCMD_BIN from core.constants import VCMD_BIN
@ -66,12 +66,3 @@ class VnodeClient(object):
self._verify_connection() self._verify_connection()
args = self.create_cmd(args) args = self.create_cmd(args)
return utils.check_cmd(args, wait=wait) return utils.check_cmd(args, wait=wait)
def termcmdstring(self, sh="/bin/sh"):
"""
Create a terminal command string.
:param str sh: shell to execute command in
:return: str
"""
return "%s -c %s -- %s" % (constants.VCMD_BIN, self.ctrlchnlname, sh)

View file

@ -1,21 +1,25 @@
import json import json
import logging import logging
import os import os
from tempfile import NamedTemporaryFile
from core import utils from core import utils
from core.emulator import distributed
from core.emulator.enumerations import NodeTypes from core.emulator.enumerations import NodeTypes
from core.errors import CoreCommandError from core.errors import CoreCommandError
from core.nodes.base import CoreNode from core.nodes.base import CoreNode
from core.nodes.netclient import LinuxNetClient, OvsNetClient
class DockerClient(object): class DockerClient(object):
def __init__(self, name, image): def __init__(self, name, image, run):
self.name = name self.name = name
self.image = image self.image = image
self.run = run
self.pid = None self.pid = None
def create_container(self): def create_container(self):
utils.check_cmd( self.run(
"docker run -td --init --net=none --hostname {name} --name {name} " "docker run -td --init --net=none --hostname {name} --name {name} "
"--sysctl net.ipv6.conf.all.disable_ipv6=0 " "--sysctl net.ipv6.conf.all.disable_ipv6=0 "
"{image} /bin/bash".format( "{image} /bin/bash".format(
@ -27,7 +31,7 @@ class DockerClient(object):
def get_info(self): def get_info(self):
args = "docker inspect {name}".format(name=self.name) args = "docker inspect {name}".format(name=self.name)
output = utils.check_cmd(args) output = self.run(args)
data = json.loads(output) data = json.loads(output)
if not data: if not data:
raise CoreCommandError( raise CoreCommandError(
@ -43,22 +47,24 @@ class DockerClient(object):
return False return False
def stop_container(self): def stop_container(self):
utils.check_cmd("docker rm -f {name}".format( self.run("docker rm -f {name}".format(
name=self.name name=self.name
)) ))
def check_cmd(self, cmd): def check_cmd(self, cmd):
if isinstance(cmd, list):
cmd = " ".join(cmd)
logging.info("docker cmd output: %s", cmd) logging.info("docker cmd output: %s", cmd)
return utils.check_cmd("docker exec {name} {cmd}".format( return utils.check_cmd("docker exec {name} {cmd}".format(
name=self.name, name=self.name,
cmd=cmd cmd=cmd
)) ))
def create_ns_cmd(self, cmd):
return "nsenter -t {pid} -u -i -p -n {cmd}".format(
pid=self.pid,
cmd=cmd
)
def ns_cmd(self, cmd, wait): def ns_cmd(self, cmd, wait):
if isinstance(cmd, list):
cmd = " ".join(cmd)
args = "nsenter -t {pid} -u -i -p -n {cmd}".format( args = "nsenter -t {pid} -u -i -p -n {cmd}".format(
pid=self.pid, pid=self.pid,
cmd=cmd cmd=cmd
@ -67,7 +73,7 @@ class DockerClient(object):
def get_pid(self): def get_pid(self):
args = "docker inspect -f '{{{{.State.Pid}}}}' {name}".format(name=self.name) args = "docker inspect -f '{{{{.State.Pid}}}}' {name}".format(name=self.name)
output = utils.check_cmd(args) output = self.run(args)
self.pid = output self.pid = output
logging.debug("node(%s) pid: %s", self.name, self.pid) logging.debug("node(%s) pid: %s", self.name, self.pid)
return output return output
@ -78,7 +84,7 @@ class DockerClient(object):
name=self.name, name=self.name,
destination=destination destination=destination
) )
return utils.check_cmd(args) return self.run(args)
class DockerNode(CoreNode): class DockerNode(CoreNode):
@ -101,6 +107,12 @@ class DockerNode(CoreNode):
self.image = image self.image = image
super(DockerNode, self).__init__(session, _id, name, nodedir, bootsh, start) super(DockerNode, self).__init__(session, _id, name, nodedir, bootsh, start)
def create_node_net_client(self, use_ovs):
if use_ovs:
return OvsNetClient(self.nsenter_cmd)
else:
return LinuxNetClient(self.nsenter_cmd)
def alive(self): def alive(self):
""" """
Check if the node is alive. Check if the node is alive.
@ -122,7 +134,7 @@ class DockerNode(CoreNode):
if self.up: if self.up:
raise ValueError("starting a node that is already up") raise ValueError("starting a node that is already up")
self.makenodedir() self.makenodedir()
self.client = DockerClient(self.name, self.image) self.client = DockerClient(self.name, self.image, self.net_cmd)
self.pid = self.client.create_container() self.pid = self.client.create_container()
self.up = True self.up = True
@ -141,12 +153,13 @@ class DockerNode(CoreNode):
self.client.stop_container() self.client.stop_container()
self.up = False self.up = False
def node_net_cmd(self, args, wait=True): def nsenter_cmd(self, args, wait=True):
if not self.up: if self.server is None:
logging.debug("node down, not running network command: %s", args) args = self.client.create_ns_cmd(args)
return "" return utils.check_cmd(args, wait=wait)
else:
return self.client.ns_cmd(args, wait) args = self.client.create_ns_cmd(args)
return distributed.remote_cmd(self.server, args, wait=wait)
def termcmdstring(self, sh="/bin/sh"): def termcmdstring(self, sh="/bin/sh"):
""" """
@ -166,7 +179,7 @@ class DockerNode(CoreNode):
""" """
logging.debug("creating node dir: %s", path) logging.debug("creating node dir: %s", path)
args = "mkdir -p {path}".format(path=path) args = "mkdir -p {path}".format(path=path)
self.client.check_cmd(args) self.node_net_cmd(args)
def mount(self, source, target): def mount(self, source, target):
""" """
@ -189,13 +202,24 @@ class DockerNode(CoreNode):
:param int mode: mode for file :param int mode: mode for file
:return: nothing :return: nothing
""" """
logging.debug("node dir(%s) ctrlchannel(%s)", self.nodedir, self.ctrlchnlname)
logging.debug("nodefile filename(%s) mode(%s)", filename, mode) logging.debug("nodefile filename(%s) mode(%s)", filename, mode)
file_path = os.path.join(self.nodedir, filename) directory = os.path.dirname(filename)
with open(file_path, "w") as f: temp = NamedTemporaryFile(delete=False)
os.chmod(f.name, mode) temp.write(contents.encode("utf-8"))
f.write(contents) temp.close()
self.client.copy_file(file_path, filename)
if directory:
self.node_net_cmd("mkdir -m %o -p %s" % (0o755, directory))
if self.server is not None:
distributed.remote_put(self.server, temp.name, temp.name)
self.client.copy_file(temp.name, filename)
self.node_net_cmd("chmod %o %s" % (mode, filename))
if self.server is not None:
self.net_cmd("rm -f %s" % temp.name)
os.unlink(temp.name)
logging.debug(
"node(%s) added file: %s; mode: 0%o", self.name, filename, mode
)
def nodefilecopy(self, filename, srcfilename, mode=None): def nodefilecopy(self, filename, srcfilename, mode=None):
""" """
@ -207,5 +231,18 @@ class DockerNode(CoreNode):
:param int mode: mode to copy to :param int mode: mode to copy to
:return: nothing :return: nothing
""" """
logging.info("node file copy file(%s) source(%s) mode(%s)", filename, srcfilename, mode) logging.info(
raise Exception("not supported") "node file copy file(%s) source(%s) mode(%s)", filename, srcfilename, mode
)
directory = os.path.dirname(filename)
self.node_net_cmd("mkdir -p %s" % directory)
if self.server is None:
source = srcfilename
else:
temp = NamedTemporaryFile(delete=False)
source = temp.name
distributed.remote_put(self.server, source, temp.name)
self.client.copy_file(source, filename)
self.node_net_cmd("chmod %o %s" % (mode, filename))

View file

@ -2,30 +2,31 @@ import json
import logging import logging
import os import os
import time import time
from tempfile import NamedTemporaryFile
from core import utils from core import utils
from core.emulator import distributed
from core.emulator.enumerations import NodeTypes from core.emulator.enumerations import NodeTypes
from core.errors import CoreCommandError from core.errors import CoreCommandError
from core.nodes.base import CoreNode from core.nodes.base import CoreNode
class LxdClient(object): class LxdClient(object):
def __init__(self, name, image): def __init__(self, name, image, run):
self.name = name self.name = name
self.image = image self.image = image
self.run = run
self.pid = None self.pid = None
def create_container(self): def create_container(self):
utils.check_cmd( self.run("lxc launch {image} {name}".format(name=self.name, image=self.image))
"lxc launch {image} {name}".format(name=self.name, image=self.image)
)
data = self.get_info() data = self.get_info()
self.pid = data["state"]["pid"] self.pid = data["state"]["pid"]
return self.pid return self.pid
def get_info(self): def get_info(self):
args = "lxc list {name} --format json".format(name=self.name) args = "lxc list {name} --format json".format(name=self.name)
output = utils.check_cmd(args) output = self.run(args)
data = json.loads(output) data = json.loads(output)
if not data: if not data:
raise CoreCommandError( raise CoreCommandError(
@ -41,20 +42,16 @@ class LxdClient(object):
return False return False
def stop_container(self): def stop_container(self):
utils.check_cmd("lxc delete --force {name}".format(name=self.name)) self.run("lxc delete --force {name}".format(name=self.name))
def create_cmd(self, cmd): def create_cmd(self, cmd):
return "lxc exec -nT {name} -- {cmd}".format(name=self.name, cmd=cmd) return "lxc exec -nT {name} -- {cmd}".format(name=self.name, cmd=cmd)
def check_cmd(self, cmd, wait=True):
args = self.create_cmd(cmd)
return utils.check_cmd(args, wait=wait)
def create_ns_cmd(self, cmd): def create_ns_cmd(self, cmd):
return "nsenter -t {pid} -m -u -i -p -n {cmd}".format(pid=self.pid, cmd=cmd) return "nsenter -t {pid} -m -u -i -p -n {cmd}".format(pid=self.pid, cmd=cmd)
def ns_check_cmd(self, cmd, wait=True): def check_cmd(self, cmd, wait=True):
args = self.create_ns_cmd(cmd) args = self.create_cmd(cmd)
return utils.check_cmd(args, wait=wait) return utils.check_cmd(args, wait=wait)
def copy_file(self, source, destination): def copy_file(self, source, destination):
@ -64,7 +61,7 @@ class LxdClient(object):
args = "lxc file push {source} {name}/{destination}".format( args = "lxc file push {source} {name}/{destination}".format(
source=source, name=self.name, destination=destination source=source, name=self.name, destination=destination
) )
utils.check_cmd(args) self.run(args)
class LxcNode(CoreNode): class LxcNode(CoreNode):
@ -115,7 +112,7 @@ class LxcNode(CoreNode):
if self.up: if self.up:
raise ValueError("starting a node that is already up") raise ValueError("starting a node that is already up")
self.makenodedir() self.makenodedir()
self.client = LxdClient(self.name, self.image) self.client = LxdClient(self.name, self.image, self.net_cmd)
self.pid = self.client.create_container() self.pid = self.client.create_container()
self.up = True self.up = True
@ -134,12 +131,6 @@ class LxcNode(CoreNode):
self.client.stop_container() self.client.stop_container()
self.up = False self.up = False
def node_net_cmd(self, args, wait=True):
if not self.up:
logging.debug("node down, not running network command: %s", args)
return ""
return self.client.check_cmd(args, wait)
def termcmdstring(self, sh="/bin/sh"): def termcmdstring(self, sh="/bin/sh"):
""" """
Create a terminal command string. Create a terminal command string.
@ -147,7 +138,7 @@ class LxcNode(CoreNode):
:param str sh: shell to execute command in :param str sh: shell to execute command in
:return: str :return: str
""" """
return "lxc exec {name} -- bash".format(name=self.name) return "lxc exec {name} -- {sh}".format(name=self.name, sh=sh)
def privatedir(self, path): def privatedir(self, path):
""" """
@ -158,7 +149,7 @@ class LxcNode(CoreNode):
""" """
logging.info("creating node dir: %s", path) logging.info("creating node dir: %s", path)
args = "mkdir -p {path}".format(path=path) args = "mkdir -p {path}".format(path=path)
return self.client.check_cmd(args) return self.node_net_cmd(args)
def mount(self, source, target): def mount(self, source, target):
""" """
@ -181,13 +172,23 @@ class LxcNode(CoreNode):
:param int mode: mode for file :param int mode: mode for file
:return: nothing :return: nothing
""" """
logging.debug("node dir(%s) ctrlchannel(%s)", self.nodedir, self.ctrlchnlname)
logging.debug("nodefile filename(%s) mode(%s)", filename, mode) logging.debug("nodefile filename(%s) mode(%s)", filename, mode)
file_path = os.path.join(self.nodedir, filename)
with open(file_path, "w") as f: directory = os.path.dirname(filename)
os.chmod(f.name, mode) temp = NamedTemporaryFile(delete=False)
f.write(contents) temp.write(contents.encode("utf-8"))
self.client.copy_file(file_path, filename) temp.close()
if directory:
self.node_net_cmd("mkdir -m %o -p %s" % (0o755, directory))
if self.server is not None:
distributed.remote_put(self.server, temp.name, temp.name)
self.client.copy_file(temp.name, filename)
self.node_net_cmd("chmod %o %s" % (mode, filename))
if self.server is not None:
self.net_cmd("rm -f %s" % temp.name)
os.unlink(temp.name)
logging.debug("node(%s) added file: %s; mode: 0%o", self.name, filename, mode)
def nodefilecopy(self, filename, srcfilename, mode=None): def nodefilecopy(self, filename, srcfilename, mode=None):
""" """
@ -202,7 +203,18 @@ class LxcNode(CoreNode):
logging.info( logging.info(
"node file copy file(%s) source(%s) mode(%s)", filename, srcfilename, mode "node file copy file(%s) source(%s) mode(%s)", filename, srcfilename, mode
) )
raise Exception("not supported") directory = os.path.dirname(filename)
self.node_net_cmd("mkdir -p %s" % directory)
if self.server is None:
source = srcfilename
else:
temp = NamedTemporaryFile(delete=False)
source = temp.name
distributed.remote_put(self.server, source, temp.name)
self.client.copy_file(source, filename)
self.node_net_cmd("chmod %o %s" % (mode, filename))
def addnetif(self, netif, ifindex): def addnetif(self, netif, ifindex):
super(LxcNode, self).addnetif(netif, ifindex) super(LxcNode, self).addnetif(netif, ifindex)

View file

@ -5,7 +5,7 @@ from core.emulator.emudata import IpPrefixes, NodeOptions
from core.emulator.enumerations import EventTypes, NodeTypes from core.emulator.enumerations import EventTypes, NodeTypes
if __name__ == "__main__": if __name__ == "__main__":
logging.basicConfig(level=logging.DEBUG) logging.basicConfig(level=logging.INFO)
coreemu = CoreEmu() coreemu = CoreEmu()
session = coreemu.create_session() session = coreemu.create_session()
@ -14,7 +14,7 @@ if __name__ == "__main__":
# create nodes and interfaces # create nodes and interfaces
try: try:
prefixes = IpPrefixes(ip4_prefix="10.83.0.0/16") prefixes = IpPrefixes(ip4_prefix="10.83.0.0/16")
options = NodeOptions(image="ubuntu") options = NodeOptions(image="ubuntu:18.04")
# create node one # create node one
node_one = session.add_node(_type=NodeTypes.LXC, node_options=options) node_one = session.add_node(_type=NodeTypes.LXC, node_options=options)