Merge pull request #267 from coreemu/feature/new-nodes

Adds Docker/LXD support
This commit is contained in:
bharnden 2019-07-05 16:36:16 -07:00 committed by GitHub
commit 802379d763
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
18 changed files with 936 additions and 26 deletions

View file

@ -669,12 +669,12 @@ class EmaneManager(ModelManager):
# multicast route is needed for OTA data
args = [constants.IP_BIN, "route", "add", otagroup, "dev", otadev]
node.check_cmd(args)
node.network_cmd(args)
# multicast route is also needed for event data if on control network
if eventservicenetidx >= 0 and eventgroup != otagroup:
args = [constants.IP_BIN, "route", "add", eventgroup, "dev", eventdev]
node.check_cmd(args)
node.network_cmd(args)
# start emane
args = emanecmd + ["-f", os.path.join(path, "emane%d.log" % n), os.path.join(path, "platform%d.xml" % n)]

View file

@ -79,12 +79,13 @@ class NodeOptions(object):
Options for creating and updating nodes within core.
"""
def __init__(self, name=None, model="PC"):
def __init__(self, name=None, model="PC", image=None):
"""
Create a NodeOptions object.
:param str name: name of node, defaults to node class name postfix with its id
:param str model: defines services for default and physical nodes, defaults to "router"
:param str image: image to use for docker nodes
"""
self.name = name
self.model = model
@ -99,6 +100,7 @@ class NodeOptions(object):
self.alt = None
self.emulation_id = None
self.emulation_server = None
self.image = image
def set_position(self, x, y):
"""

View file

@ -81,6 +81,8 @@ class NodeTypes(Enum):
PEER_TO_PEER = 12
CONTROL_NET = 13
EMANE_NET = 14
DOCKER = 15
LXC = 16
class Rj45Models(Enum):

View file

@ -500,7 +500,10 @@ class Session(object):
# create node
logging.info("creating node(%s) id(%s) name(%s) start(%s)", node_class.__name__, _id, name, start)
node = self.create_node(cls=node_class, _id=_id, name=name, start=start)
if _type in [NodeTypes.DOCKER, NodeTypes.LXC]:
node = self.create_node(cls=node_class, _id=_id, name=name, start=start, image=node_options.image)
else:
node = self.create_node(cls=node_class, _id=_id, name=name, start=start)
# set node attributes
node.icon = node_options.icon
@ -511,7 +514,7 @@ class Session(object):
self.set_node_position(node, node_options)
# add services to default and physical nodes only
if _type in [NodeTypes.DEFAULT, NodeTypes.PHYSICAL]:
if _type in [NodeTypes.DEFAULT, NodeTypes.PHYSICAL, NodeTypes.DOCKER, NodeTypes.LXC]:
node.type = node_options.model
logging.debug("set node type: %s", node.type)
self.services.add_services(node, node.type, node_options.services)
@ -1364,7 +1367,7 @@ class Session(object):
# TODO: PyCoreNode is not the type to check
if isinstance(node, CoreNodeBase) and not nodeutils.is_node(node, NodeTypes.RJ45):
# add a control interface if configured
logging.info("booting node: %s", node.name)
logging.info("booting node(%s): %s", node.name, node.services)
self.add_remove_control_interface(node=node, remove=False)
result = pool.apply_async(self.services.boot_services, (node,))
results.append(result)

View file

@ -493,11 +493,11 @@ class CoreNode(CoreNodeBase):
# bring up the loopback interface
logging.debug("bringing up loopback interface")
self.check_cmd([constants.IP_BIN, "link", "set", "lo", "up"])
self.network_cmd([constants.IP_BIN, "link", "set", "lo", "up"])
# set hostname for node
logging.debug("setting hostname: %s", self.name)
self.check_cmd(["hostname", self.name])
self.network_cmd(["hostname", self.name])
# mark node as up
self.up = True
@ -572,6 +572,17 @@ class CoreNode(CoreNodeBase):
"""
return self.client.cmd_output(args)
def network_cmd(self, args):
"""
Runs a command for a node that is used to configure and setup network interfaces.
:param list[str]|str args: command to run
:return: combined stdout and stderr
:rtype: str
:raises CoreCommandError: when a non-zero exit status occurs
"""
return self.check_cmd(args)
def check_cmd(self, args):
"""
Runs shell command on node.
@ -667,15 +678,15 @@ class CoreNode(CoreNodeBase):
if self.up:
utils.check_cmd([constants.IP_BIN, "link", "set", veth.name, "netns", str(self.pid)])
self.check_cmd([constants.IP_BIN, "link", "set", veth.name, "name", ifname])
self.check_cmd([constants.ETHTOOL_BIN, "-K", ifname, "rx", "off", "tx", "off"])
self.network_cmd([constants.IP_BIN, "link", "set", veth.name, "name", ifname])
self.network_cmd([constants.ETHTOOL_BIN, "-K", ifname, "rx", "off", "tx", "off"])
veth.name = ifname
if self.up:
# TODO: potentially find better way to query interface ID
# retrieve interface information
output = self.check_cmd(["ip", "link", "show", veth.name])
output = self.network_cmd([constants.IP_BIN, "link", "show", veth.name])
logging.debug("interface command output: %s", output)
output = output.split("\n")
veth.flow_id = int(output[0].strip().split(":")[0]) + 1
@ -736,7 +747,7 @@ class CoreNode(CoreNodeBase):
self._netif[ifindex].sethwaddr(addr)
if self.up:
args = [constants.IP_BIN, "link", "set", "dev", self.ifname(ifindex), "address", str(addr)]
self.check_cmd(args)
self.network_cmd(args)
def addaddr(self, ifindex, addr):
"""
@ -750,10 +761,10 @@ class CoreNode(CoreNodeBase):
# check if addr is ipv6
if ":" in str(addr):
args = [constants.IP_BIN, "addr", "add", str(addr), "dev", self.ifname(ifindex)]
self.check_cmd(args)
self.network_cmd(args)
else:
args = [constants.IP_BIN, "addr", "add", str(addr), "broadcast", "+", "dev", self.ifname(ifindex)]
self.check_cmd(args)
self.network_cmd(args)
self._netif[ifindex].addaddr(addr)
@ -772,7 +783,7 @@ class CoreNode(CoreNodeBase):
logging.exception("trying to delete unknown address: %s" % addr)
if self.up:
self.check_cmd([constants.IP_BIN, "addr", "del", str(addr), "dev", self.ifname(ifindex)])
self.network_cmd([constants.IP_BIN, "addr", "del", str(addr), "dev", self.ifname(ifindex)])
def delalladdr(self, ifindex, address_types=None):
"""
@ -806,7 +817,7 @@ class CoreNode(CoreNodeBase):
:return: nothing
"""
if self.up:
self.check_cmd([constants.IP_BIN, "link", "set", self.ifname(ifindex), "up"])
self.network_cmd([constants.IP_BIN, "link", "set", self.ifname(ifindex), "up"])
def newnetif(self, net=None, addrlist=None, hwaddr=None, ifindex=None, ifname=None):
"""
@ -867,12 +878,12 @@ class CoreNode(CoreNodeBase):
utils.check_cmd([constants.IP_BIN, "link", "add", "name", tmp1, "type", "veth", "peer", "name", tmp2])
utils.check_cmd([constants.IP_BIN, "link", "set", tmp1, "netns", str(self.pid)])
self.check_cmd([constants.IP_BIN, "link", "set", tmp1, "name", ifname])
self.network_cmd([constants.IP_BIN, "link", "set", tmp1, "name", ifname])
interface = CoreInterface(node=self, name=ifname, mtu=_DEFAULT_MTU)
self.addnetif(interface, self.newifindex())
utils.check_cmd([constants.IP_BIN, "link", "set", tmp2, "netns", str(othernode.pid)])
othernode.check_cmd([constants.IP_BIN, "link", "set", tmp2, "name", otherifname])
othernode.network_cmd([constants.IP_BIN, "link", "set", tmp2, "name", otherifname])
other_interface = CoreInterface(node=othernode, name=otherifname, mtu=_DEFAULT_MTU)
othernode.addnetif(other_interface, othernode.newifindex())

297
daemon/core/nodes/docker.py Normal file
View file

@ -0,0 +1,297 @@
import json
import logging
import os
from core import utils, CoreCommandError
from core.emulator.enumerations import NodeTypes
from core.nodes.base import CoreNode
class DockerClient(object):
def __init__(self, name, image):
self.name = name
self.image = image
self.pid = None
self._addr = {}
def create_container(self):
utils.check_cmd(
"docker run -td --init --net=none --hostname {name} --name {name} "
"--sysctl net.ipv6.conf.all.disable_ipv6=0 "
"{image} /bin/bash".format(
name=self.name,
image=self.image
))
self.pid = self.get_pid()
return self.pid
def get_info(self):
args = "docker inspect {name}".format(name=self.name)
status, output = utils.cmd_output(args)
if status:
raise CoreCommandError(status, args, output)
data = json.loads(output)
if not data:
raise CoreCommandError(status, args, "docker({name}) not present".format(name=self.name))
return data[0]
def is_alive(self):
try:
data = self.get_info()
return data["State"]["Running"]
except CoreCommandError:
return False
def stop_container(self):
utils.check_cmd("docker rm -f {name}".format(
name=self.name
))
def cmd(self, cmd, wait=True):
if isinstance(cmd, list):
cmd = " ".join(cmd)
logging.info("docker cmd wait(%s): %s", wait, cmd)
return utils.cmd("docker exec {name} {cmd}".format(
name=self.name,
cmd=cmd
), wait)
def cmd_output(self, cmd):
if isinstance(cmd, list):
cmd = " ".join(cmd)
logging.info("docker cmd output: %s", cmd)
return utils.cmd_output("docker exec {name} {cmd}".format(
name=self.name,
cmd=cmd
))
def ns_cmd(self, cmd):
if isinstance(cmd, list):
cmd = " ".join(cmd)
args = "nsenter -t {pid} -u -i -p -n {cmd}".format(
pid=self.pid,
cmd=cmd
)
logging.info("ns cmd: %s", args)
return utils.cmd_output(args)
def get_pid(self):
args = "docker inspect -f '{{{{.State.Pid}}}}' {name}".format(name=self.name)
status, output = utils.cmd_output(args)
if status:
raise CoreCommandError(status, args, output)
self.pid = output
logging.debug("node(%s) pid: %s", self.name, self.pid)
return output
def copy_file(self, source, destination):
args = "docker cp {source} {name}:{destination}".format(
source=source,
name=self.name,
destination=destination
)
status, output = utils.cmd_output(args)
if status:
raise CoreCommandError(status, args, output)
def getaddr(self, ifname, rescan=False):
"""
Get address for interface on node.
:param str ifname: interface name to get address for
:param bool rescan: rescan flag
:return: interface information
:rtype: dict
"""
if ifname in self._addr and not rescan:
return self._addr[ifname]
interface = {"ether": [], "inet": [], "inet6": [], "inet6link": []}
args = ["ip", "addr", "show", "dev", ifname]
status, output = self.ns_cmd(args)
for line in output:
line = line.strip().split()
if line[0] == "link/ether":
interface["ether"].append(line[1])
elif line[0] == "inet":
interface["inet"].append(line[1])
elif line[0] == "inet6":
if line[3] == "global":
interface["inet6"].append(line[1])
elif line[3] == "link":
interface["inet6link"].append(line[1])
else:
logging.warning("unknown scope: %s" % line[3])
if status:
logging.warning("nonzero exist status (%s) for cmd: %s", status, args)
self._addr[ifname] = interface
return interface
class DockerNode(CoreNode):
apitype = NodeTypes.DOCKER.value
def __init__(self, session, _id=None, name=None, nodedir=None, bootsh="boot.sh", start=True, image=None):
"""
Create a DockerNode instance.
:param core.emulator.session.Session session: core session instance
:param int _id: object id
:param str name: object name
:param str nodedir: node directory
:param str bootsh: boot shell to use
:param bool start: start flag
:param str image: image to start container with
"""
if image is None:
image = "ubuntu"
self.image = image
super(DockerNode, self).__init__(session, _id, name, nodedir, bootsh, start)
def alive(self):
"""
Check if the node is alive.
:return: True if node is alive, False otherwise
:rtype: bool
"""
return self.client.is_alive()
def startup(self):
"""
Start a new namespace node by invoking the vnoded process that
allocates a new namespace. Bring up the loopback device and set
the hostname.
:return: nothing
"""
with self.lock:
if self.up:
raise ValueError("starting a node that is already up")
self.makenodedir()
self.client = DockerClient(self.name, self.image)
self.pid = self.client.create_container()
self.up = True
def shutdown(self):
"""
Shutdown logic.
:return: nothing
"""
# nothing to do if node is not up
if not self.up:
return
with self.lock:
self._netif.clear()
self.client.stop_container()
self.up = False
def cmd(self, args, wait=True):
"""
Runs shell command on node, with option to not wait for a result.
:param list[str]|str args: command to run
:param bool wait: wait for command to exit, defaults to True
:return: exit status for command
:rtype: int
"""
return self.client.cmd(args, wait)
def cmd_output(self, args):
"""
Runs shell command on node and get exit status and output.
:param list[str]|str args: command to run
:return: exit status and combined stdout and stderr
:rtype: tuple[int, str]
"""
return self.client.cmd_output(args)
def check_cmd(self, args):
"""
Runs shell command on node.
:param list[str]|str args: command to run
:return: combined stdout and stderr
:rtype: str
:raises CoreCommandError: when a non-zero exit status occurs
"""
status, output = self.client.cmd_output(args)
if status:
raise CoreCommandError(status, args, output)
return output
def network_cmd(self, args):
if not self.up:
logging.debug("node down, not running network command: %s", args)
return 0
status, output = self.client.ns_cmd(args)
if status:
raise CoreCommandError(status, args, output)
return output
def termcmdstring(self, sh="/bin/sh"):
"""
Create a terminal command string.
:param str sh: shell to execute command in
:return: str
"""
return "docker exec -it {name} bash".format(name=self.name)
def privatedir(self, path):
"""
Create a private directory.
:param str path: path to create
:return: nothing
"""
logging.info("creating node dir: %s", path)
args = "mkdir -p {path}".format(path=path)
self.check_cmd(args)
def mount(self, source, target):
"""
Create and mount a directory.
:param str source: source directory to mount
:param str target: target directory to create
:return: nothing
:raises CoreCommandError: when a non-zero exit status occurs
"""
logging.info("mounting source(%s) target(%s)", source, target)
raise Exception("not supported")
def nodefile(self, filename, contents, mode=0o644):
"""
Create a node file with a given mode.
:param str filename: name of file to create
:param contents: contents of file
:param int mode: mode for file
:return: nothing
"""
logging.info("node dir(%s) ctrlchannel(%s)", self.nodedir, self.ctrlchnlname)
logging.info("nodefile filename(%s) mode(%s)", filename, mode)
file_path = os.path.join(self.nodedir, filename)
with open(file_path, "w") as f:
os.chmod(f.name, mode)
f.write(contents)
self.client.copy_file(file_path, filename)
def nodefilecopy(self, filename, srcfilename, mode=None):
"""
Copy a file to a node, following symlinks and preserving metadata.
Change file mode if specified.
:param str filename: file name to copy file to
:param str srcfilename: file to copy
:param int mode: mode to copy to
:return: nothing
"""
logging.info("node file copy file(%s) source(%s) mode(%s)", filename, srcfilename, mode)
raise Exception("not supported")

View file

@ -237,7 +237,7 @@ class Veth(CoreInterface):
if self.node:
try:
self.node.check_cmd([constants.IP_BIN, "-6", "addr", "flush", "dev", self.name])
self.node.network_cmd([constants.IP_BIN, "-6", "addr", "flush", "dev", self.name])
except CoreCommandError:
logging.exception("error shutting down interface")
@ -245,7 +245,7 @@ class Veth(CoreInterface):
try:
utils.check_cmd([constants.IP_BIN, "link", "delete", self.localname])
except CoreCommandError:
logging.exception("error deleting link")
logging.info("link already removed: %s", self.localname)
self.up = False
@ -298,7 +298,7 @@ class TunTap(CoreInterface):
return
try:
self.node.check_cmd([constants.IP_BIN, "-6", "addr", "flush", "dev", self.name])
self.node.network_cmd([constants.IP_BIN, "-6", "addr", "flush", "dev", self.name])
except CoreCommandError:
logging.exception("error shutting down tunnel tap")
@ -361,7 +361,11 @@ class TunTap(CoreInterface):
def nodedevexists():
args = [constants.IP_BIN, "link", "show", self.name]
return self.node.cmd(args)
try:
self.node.network_cmd(args)
return 0
except CoreCommandError:
return 1
count = 0
while True:
@ -393,8 +397,8 @@ class TunTap(CoreInterface):
self.waitfordevicelocal()
netns = str(self.node.pid)
utils.check_cmd([constants.IP_BIN, "link", "set", self.localname, "netns", netns])
self.node.check_cmd([constants.IP_BIN, "link", "set", self.localname, "name", self.name])
self.node.check_cmd([constants.IP_BIN, "link", "set", self.name, "up"])
self.node.network_cmd([constants.IP_BIN, "link", "set", self.localname, "name", self.name])
self.node.network_cmd([constants.IP_BIN, "link", "set", self.name, "up"])
def setaddrs(self):
"""
@ -404,7 +408,7 @@ class TunTap(CoreInterface):
"""
self.waitfordevicenode()
for addr in self.addrlist:
self.node.check_cmd([constants.IP_BIN, "addr", "add", str(addr), "dev", self.name])
self.node.network_cmd([constants.IP_BIN, "addr", "add", str(addr), "dev", self.name])
class GreTap(CoreInterface):

301
daemon/core/nodes/lxd.py Normal file
View file

@ -0,0 +1,301 @@
import json
import logging
import os
import time
from core import utils, CoreCommandError
from core.emulator.enumerations import NodeTypes
from core.nodes.base import CoreNode
class LxdClient(object):
def __init__(self, name, image):
self.name = name
self.image = image
self.pid = None
self._addr = {}
def create_container(self):
utils.check_cmd("lxc launch {image} {name}".format(
name=self.name,
image=self.image
))
data = self.get_info()
self.pid = data["state"]["pid"]
return self.pid
def get_info(self):
args = "lxc list {name} --format json".format(name=self.name)
status, output = utils.cmd_output(args)
if status:
raise CoreCommandError(status, args, output)
data = json.loads(output)
if not data:
raise CoreCommandError(status, args, "LXC({name}) not present".format(name=self.name))
return data[0]
def is_alive(self):
try:
data = self.get_info()
return data["state"]["status"] == "Running"
except CoreCommandError:
return False
def stop_container(self):
utils.check_cmd("lxc delete --force {name}".format(
name=self.name
))
def _cmd_args(self, cmd):
return "lxc exec -nT {name} -- {cmd}".format(
name=self.name,
cmd=cmd
)
def cmd_output(self, cmd):
if isinstance(cmd, list):
cmd = " ".join(cmd)
args = self._cmd_args(cmd)
logging.info("lxc cmd output: %s", args)
return utils.cmd_output(args)
def cmd(self, cmd, wait=True):
if isinstance(cmd, list):
cmd = " ".join(cmd)
args = self._cmd_args(cmd)
logging.info("lxc cmd: %s", args)
return utils.cmd(args, wait)
def _ns_args(self, cmd):
return "nsenter -t {pid} -m -u -i -p -n {cmd}".format(
pid=self.pid,
cmd=cmd
)
def ns_cmd_output(self, cmd):
if isinstance(cmd, list):
cmd = " ".join(cmd)
args = self._ns_args(cmd)
logging.info("ns cmd: %s", args)
return utils.cmd_output(args)
def ns_cmd(self, cmd, wait=True):
if isinstance(cmd, list):
cmd = " ".join(cmd)
args = self._ns_args(cmd)
logging.info("ns cmd: %s", args)
return utils.cmd(args, wait)
def copy_file(self, source, destination):
if destination[0] != "/":
destination = os.path.join("/root/", destination)
args = "lxc file push {source} {name}/{destination}".format(
source=source,
name=self.name,
destination=destination
)
status, output = utils.cmd_output(args)
if status:
raise CoreCommandError(status, args, output)
def getaddr(self, ifname, rescan=False):
"""
Get address for interface on node.
:param str ifname: interface name to get address for
:param bool rescan: rescan flag
:return: interface information
:rtype: dict
"""
if ifname in self._addr and not rescan:
return self._addr[ifname]
interface = {"ether": [], "inet": [], "inet6": [], "inet6link": []}
args = ["ip", "addr", "show", "dev", ifname]
status, output = self.ns_cmd_output(args)
for line in output:
line = line.strip().split()
if line[0] == "link/ether":
interface["ether"].append(line[1])
elif line[0] == "inet":
interface["inet"].append(line[1])
elif line[0] == "inet6":
if line[3] == "global":
interface["inet6"].append(line[1])
elif line[3] == "link":
interface["inet6link"].append(line[1])
else:
logging.warning("unknown scope: %s" % line[3])
if status:
logging.warning("nonzero exist status (%s) for cmd: %s", status, args)
self._addr[ifname] = interface
return interface
class LxcNode(CoreNode):
apitype = NodeTypes.LXC.value
def __init__(self, session, _id=None, name=None, nodedir=None, bootsh="boot.sh", start=True, image=None):
"""
Create a LxcNode instance.
:param core.emulator.session.Session session: core session instance
:param int _id: object id
:param str name: object name
:param str nodedir: node directory
:param str bootsh: boot shell to use
:param bool start: start flag
:param str image: image to start container with
"""
if image is None:
image = "ubuntu"
self.image = image
super(LxcNode, self).__init__(session, _id, name, nodedir, bootsh, start)
def alive(self):
"""
Check if the node is alive.
:return: True if node is alive, False otherwise
:rtype: bool
"""
return self.client.is_alive()
def startup(self):
"""
Startup logic.
:return: nothing
"""
with self.lock:
if self.up:
raise ValueError("starting a node that is already up")
self.makenodedir()
self.client = LxdClient(self.name, self.image)
self.pid = self.client.create_container()
self.up = True
def shutdown(self):
"""
Shutdown logic.
:return: nothing
"""
# nothing to do if node is not up
if not self.up:
return
with self.lock:
self._netif.clear()
self.client.stop_container()
self.up = False
def cmd(self, args, wait=True):
"""
Runs shell command on node, with option to not wait for a result.
:param list[str]|str args: command to run
:param bool wait: wait for command to exit, defaults to True
:return: exit status for command
:rtype: int
"""
return self.client.cmd(args, wait)
def cmd_output(self, args):
"""
Runs shell command on node and get exit status and output.
:param list[str]|str args: command to run
:return: exit status and combined stdout and stderr
:rtype: tuple[int, str]
"""
return self.client.cmd_output(args)
def check_cmd(self, args):
"""
Runs shell command on node.
:param list[str]|str args: command to run
:return: combined stdout and stderr
:rtype: str
:raises CoreCommandError: when a non-zero exit status occurs
"""
status, output = self.client.cmd_output(args)
if status:
raise CoreCommandError(status, args, output)
return output
def network_cmd(self, args):
if not self.up:
logging.debug("node down, not running network command: %s", args)
return 0
return self.check_cmd(args)
def termcmdstring(self, sh="/bin/sh"):
"""
Create a terminal command string.
:param str sh: shell to execute command in
:return: str
"""
return "lxc exec {name} -- bash".format(name=self.name)
def privatedir(self, path):
"""
Create a private directory.
:param str path: path to create
:return: nothing
"""
logging.info("creating node dir: %s", path)
args = "mkdir -p {path}".format(path=path)
self.check_cmd(args)
def mount(self, source, target):
"""
Create and mount a directory.
:param str source: source directory to mount
:param str target: target directory to create
:return: nothing
:raises CoreCommandError: when a non-zero exit status occurs
"""
logging.info("mounting source(%s) target(%s)", source, target)
raise Exception("not supported")
def nodefile(self, filename, contents, mode=0o644):
"""
Create a node file with a given mode.
:param str filename: name of file to create
:param contents: contents of file
:param int mode: mode for file
:return: nothing
"""
logging.info("node dir(%s) ctrlchannel(%s)", self.nodedir, self.ctrlchnlname)
logging.info("nodefile filename(%s) mode(%s)", filename, mode)
file_path = os.path.join(self.nodedir, filename)
with open(file_path, "w") as f:
os.chmod(f.name, mode)
f.write(contents)
self.client.copy_file(file_path, filename)
def nodefilecopy(self, filename, srcfilename, mode=None):
"""
Copy a file to a node, following symlinks and preserving metadata.
Change file mode if specified.
:param str filename: file name to copy file to
:param str srcfilename: file to copy
:param int mode: mode to copy to
:return: nothing
"""
logging.info("node file copy file(%s) source(%s) mode(%s)", filename, srcfilename, mode)
raise Exception("not supported")
def addnetif(self, netif, ifindex):
super(LxcNode, self).addnetif(netif, ifindex)
# adding small delay to allow time for adding addresses to work correctly
time.sleep(0.5)

View file

@ -2,6 +2,8 @@
Provides default node maps that can be used to run core with.
"""
import core.nodes.base
import core.nodes.docker
import core.nodes.lxd
import core.nodes.network
import core.nodes.physical
from core.emane.nodes import EmaneNet
@ -25,5 +27,7 @@ NODES = {
NodeTypes.EMANE_NET: EmaneNet,
NodeTypes.TAP_BRIDGE: GreTapBridge,
NodeTypes.PEER_TO_PEER: core.nodes.network.PtpNet,
NodeTypes.CONTROL_NET: core.nodes.network.CtrlNet
NodeTypes.CONTROL_NET: core.nodes.network.CtrlNet,
NodeTypes.DOCKER: core.nodes.docker.DockerNode,
NodeTypes.LXC: core.nodes.lxd.LxcNode
}

View file

@ -0,0 +1,30 @@
# Docker Support
Information on how Docker can be leveraged and included to create
nodes based on Docker containers and images to interface with
existing CORE nodes, when needed.
# Installation
```shell
sudo apt install docker.io
```
# Configuration
Custom configuration required to avoid iptable rules being added and removing
the need for the default docker network, since core will be orchestrating
connections between nodes.
Place the file below in **/etc/docker/**
* daemon.json
# Tools and Versions Tested With
* Docker version 18.09.5, build e8ff056
* nsenter from util-linux 2.31.1
# Examples
This directory provides a few small examples creating Docker nodes
and linking them to themselves or with standard CORE nodes.

View file

@ -0,0 +1,5 @@
{
"bridge": "none",
"iptables": false
}

View file

@ -0,0 +1,33 @@
import logging
from core.emulator.coreemu import CoreEmu
from core.emulator.emudata import IpPrefixes, NodeOptions
from core.emulator.enumerations import NodeTypes, EventTypes
if __name__ == "__main__":
logging.basicConfig(level=logging.DEBUG)
coreemu = CoreEmu()
session = coreemu.create_session()
session.set_state(EventTypes.CONFIGURATION_STATE)
try:
prefixes = IpPrefixes(ip4_prefix="10.83.0.0/16")
options = NodeOptions(model=None, image="ubuntu")
# create node one
node_one = session.add_node(_type=NodeTypes.DOCKER, node_options=options)
interface_one = prefixes.create_interface(node_one)
# create node two
node_two = session.add_node()
interface_two = prefixes.create_interface(node_two)
# add link
session.add_link(node_one.id, node_two.id, interface_one, interface_two)
# instantiate
session.instantiate()
finally:
input("continue to shutdown")
coreemu.shutdown()

View file

@ -0,0 +1,35 @@
import logging
from core.emulator.coreemu import CoreEmu
from core.emulator.emudata import IpPrefixes, NodeOptions
from core.emulator.enumerations import NodeTypes, EventTypes
if __name__ == "__main__":
logging.basicConfig(level=logging.DEBUG)
coreemu = CoreEmu()
session = coreemu.create_session()
session.set_state(EventTypes.CONFIGURATION_STATE)
# create nodes and interfaces
try:
prefixes = IpPrefixes(ip4_prefix="10.83.0.0/16")
options = NodeOptions(model=None, image="ubuntu")
# create node one
node_one = session.add_node(_type=NodeTypes.DOCKER, node_options=options)
interface_one = prefixes.create_interface(node_one)
# create node two
node_two = session.add_node(_type=NodeTypes.DOCKER, node_options=options)
interface_two = prefixes.create_interface(node_two)
# add link
session.add_link(node_one.id, node_two.id, interface_one, interface_two)
# instantiate
session.instantiate()
finally:
input("continue to shutdown")
coreemu.shutdown()

View file

@ -0,0 +1,43 @@
import logging
from core.emulator.coreemu import CoreEmu
from core.emulator.emudata import IpPrefixes, NodeOptions
from core.emulator.enumerations import NodeTypes, EventTypes
if __name__ == "__main__":
logging.basicConfig(level=logging.DEBUG)
coreemu = CoreEmu()
session = coreemu.create_session()
session.set_state(EventTypes.CONFIGURATION_STATE)
try:
prefixes = IpPrefixes(ip4_prefix="10.83.0.0/16")
options = NodeOptions(model=None, image="ubuntu")
# create switch
switch = session.add_node(_type=NodeTypes.SWITCH)
# node one
node_one = session.add_node(_type=NodeTypes.DOCKER, node_options=options)
interface_one = prefixes.create_interface(node_one)
# node two
node_two = session.add_node(_type=NodeTypes.DOCKER, node_options=options)
interface_two = prefixes.create_interface(node_two)
# node three
node_three = session.add_node()
interface_three = prefixes.create_interface(node_three)
# add links
session.add_link(node_one.id, switch.id, interface_one)
session.add_link(node_two.id, switch.id, interface_two)
session.add_link(node_three.id, switch.id, interface_three)
# instantiate
session.instantiate()
finally:
input("continue to shutdown")
coreemu.shutdown()

View file

@ -0,0 +1,29 @@
# LXD Support
Information on how LXD can be leveraged and included to create
nodes based on LXC containers and images to interface with
existing CORE nodes, when needed.
# Installation
```shell
sudo snap install lxd
```
# Configuration
Initialize LXD and say no to adding a default bridge.
```shell
sudo lxd init
```
# Tools and Versions Tested With
* LXD 3.14
* nsenter from util-linux 2.31.1
# Examples
This directory provides a few small examples creating LXC nodes
using LXD and linking them to themselves or with standard CORE nodes.

View file

@ -0,0 +1,33 @@
import logging
from core.emulator.coreemu import CoreEmu
from core.emulator.emudata import IpPrefixes, NodeOptions
from core.emulator.enumerations import NodeTypes, EventTypes
if __name__ == "__main__":
logging.basicConfig(level=logging.DEBUG)
coreemu = CoreEmu()
session = coreemu.create_session()
session.set_state(EventTypes.CONFIGURATION_STATE)
try:
prefixes = IpPrefixes(ip4_prefix="10.83.0.0/16")
options = NodeOptions(image="ubuntu")
# create node one
node_one = session.add_node(_type=NodeTypes.LXC, node_options=options)
interface_one = prefixes.create_interface(node_one)
# create node two
node_two = session.add_node()
interface_two = prefixes.create_interface(node_two)
# add link
session.add_link(node_one.id, node_two.id, interface_one, interface_two)
# instantiate
session.instantiate()
finally:
input("continue to shutdown")
coreemu.shutdown()

View file

@ -0,0 +1,35 @@
import logging
from core.emulator.coreemu import CoreEmu
from core.emulator.emudata import IpPrefixes, NodeOptions
from core.emulator.enumerations import NodeTypes, EventTypes
if __name__ == "__main__":
logging.basicConfig(level=logging.DEBUG)
coreemu = CoreEmu()
session = coreemu.create_session()
session.set_state(EventTypes.CONFIGURATION_STATE)
# create nodes and interfaces
try:
prefixes = IpPrefixes(ip4_prefix="10.83.0.0/16")
options = NodeOptions(image="ubuntu")
# create node one
node_one = session.add_node(_type=NodeTypes.LXC, node_options=options)
interface_one = prefixes.create_interface(node_one)
# create node two
node_two = session.add_node(_type=NodeTypes.LXC, node_options=options)
interface_two = prefixes.create_interface(node_two)
# add link
session.add_link(node_one.id, node_two.id, interface_one, interface_two)
# instantiate
session.instantiate()
finally:
input("continue to shutdown")
coreemu.shutdown()

View file

@ -0,0 +1,43 @@
import logging
from core.emulator.coreemu import CoreEmu
from core.emulator.emudata import IpPrefixes, NodeOptions
from core.emulator.enumerations import NodeTypes, EventTypes
if __name__ == "__main__":
logging.basicConfig(level=logging.DEBUG)
coreemu = CoreEmu()
session = coreemu.create_session()
session.set_state(EventTypes.CONFIGURATION_STATE)
try:
prefixes = IpPrefixes(ip4_prefix="10.83.0.0/16")
options = NodeOptions(image="ubuntu")
# create switch
switch = session.add_node(_type=NodeTypes.SWITCH)
# node one
node_one = session.add_node(_type=NodeTypes.LXC, node_options=options)
interface_one = prefixes.create_interface(node_one)
# node two
node_two = session.add_node(_type=NodeTypes.LXC, node_options=options)
interface_two = prefixes.create_interface(node_two)
# node three
node_three = session.add_node()
interface_three = prefixes.create_interface(node_three)
# add links
session.add_link(node_one.id, switch.id, interface_one)
session.add_link(node_two.id, switch.id, interface_two)
session.add_link(node_three.id, switch.id, interface_three)
# instantiate
session.instantiate()
finally:
input("continue to shutdown")
coreemu.shutdown()