This commit is contained in:
Blake J. Harnden 2018-06-01 08:51:07 -07:00
commit c1b6747a26
29 changed files with 510 additions and 42 deletions

View file

@ -1,17 +1,34 @@
2018-XX-XX CORE 5.1
2018-05-22 CORE 5.1
* DAEMON:
- removed and cleared out code that is either legacy or no longer supported (Xen, BSD, Kernel patching, RPM/DEB specific files)
- default nodes are now set in the node map
- moved ns3 and netns directories to the top of the repo
- changes to make use of fpm as the tool for building packages
- removed usage of logzero to avoid dependency issues for built packages
- removed daemon addons directory
- added CoreEmu to core.emulator.coreemu to help begin serving as the basis for a more formal API for scripting and creating new external APIs out of
- cleaned up logging, moved more logging to DEBUG from INFO, tried to mold INFO message to be more simple and informative
- EMANE 1.0.1-1.21 supported
- updates to leverage EMANE python bindings for dynamically parsing phy/mac manifest files
- example custom EMANE model lives under /usr/share/core/examples/myemane/examplemodel.py
- EMANE TDMA model now supports an option to start a TDMA schedule when running
- fixed issues with coresendmsg script due to code refactoring
- added make target for generating documentation "make doc"
- Python 2.7+ is now required
- ns3 is no longer bundled by default, but will be produced as a separate package for installation
* GUI
- updated broken help links in GUI Help->About
* Packaging
- fixed PYTHON_PATH to PYTHONPATH in sysv script
- added make command to leverage FPM as the tool for creating deb/rpm packages going forward, there is documentation within README.md to try it out
* TEST:
- fixed some broken tests
- new test cases based on CoreEmu usage
* BUGFIXES:
- #142 - duplication of custom services
- #136 - sphinx-apidoc command not found
- #137 - make command fails when using distclean
2017-09-01 CORE 5.0
* DEVELOPMENT:
- support for editorconfig to help standardize development across IDEs, from the defined configuration file

View file

@ -36,7 +36,7 @@ AC_ARG_WITH([guiconfdir],
[AS_HELP_STRING([--with-guiconfdir=dir],
[specify GUI configuration directory])],
[CORE_GUI_CONF_DIR="$with_guiconfdir"],
[CORE_GUI_CONF_DIR="\${HOME}/.core"])
[CORE_GUI_CONF_DIR="\$\${HOME}/.core"])
AC_SUBST(CORE_GUI_CONF_DIR)
AC_ARG_ENABLE([gui],
[AS_HELP_STRING([--enable-gui[=ARG]],

View file

@ -958,7 +958,7 @@ class CoreHandler(SocketServer.BaseRequestHandler):
:return: reply messages
"""
if message.flags & MessageFlags.ADD.value:
node_num = message.get_tlv(FileTlvs.NUMBER.value)
node_num = message.get_tlv(FileTlvs.NODE.value)
file_name = message.get_tlv(FileTlvs.NAME.value)
file_type = message.get_tlv(FileTlvs.TYPE.value)
source_name = message.get_tlv(FileTlvs.SOURCE_NAME.value)

View file

@ -378,7 +378,7 @@ class EmuSession(Session):
if node_two:
node_two.lock.release()
def update_link(self, node_one_id, node_two_id, link_options, interface_one_id=None, interface_two_id=None):
def update_link(self, node_one_id, node_two_id, interface_one_id=None, interface_two_id=None, link_options=LinkOptions()):
"""
Update link information between nodes.
@ -849,6 +849,9 @@ class CoreEmu(object):
:param dict config: configuration options
"""
# set umask 0
os.umask(0)
# configuration
self.config = config

View file

@ -248,10 +248,10 @@ class OvsNet(PyCoreNet):
if jitter is not None:
netem += ["%sus" % jitter, "25%"]
if loss is not None:
if loss is not None and loss > 0:
netem += ["loss", "%s%%" % min(loss, 100)]
if duplicate is not None:
if duplicate is not None and duplicate > 0:
netem += ["duplicate", "%s%%" % min(duplicate, 100)]
if delay <= 0 and jitter <= 0 and loss <= 0 and duplicate <= 0:

View file

@ -475,9 +475,9 @@ class LxBrNet(PyCoreNet):
else:
netem += ["%sus" % jitter, "25%"]
if loss is not None:
if loss is not None and loss > 0:
netem += ["loss", "%s%%" % min(loss, 100)]
if duplicate is not None:
if duplicate is not None and duplicate > 0:
netem += ["duplicate", "%s%%" % min(duplicate, 100)]
if delay <= 0 and jitter <= 0 and loss <= 0 and duplicate <= 0:
# possibly remove netem if it exists and parent queue wasn't removed

View file

@ -127,10 +127,9 @@ class SimpleLxcNode(PyCoreNode):
if not self.up:
return
# unmount all targets
while self._mounts:
source, target = self._mounts.pop(-1)
self.umount(target)
# unmount all targets (NOTE: non-persistent mount namespaces are
# removed by the kernel when last referencing process is killed)
self._mounts = []
# shutdown all interfaces
for netif in self.netifs():
@ -223,18 +222,6 @@ class SimpleLxcNode(PyCoreNode):
raise CoreCommandError(status, cmd, output)
self._mounts.append((source, target))
def umount(self, target):
"""
Unmount a target directory.
:param str target: target directory to unmount
:return: nothing
"""
logger.info("node(%s) unmounting: %s", self.name, target)
try:
self.check_cmd([constants.UMOUNT_BIN, "-n", "-l", target])
except CoreCommandError:
logger.exception("error during unmount")
def newifindex(self):
"""

View file

@ -23,12 +23,12 @@ quagga_sbin_search = "/usr/local/sbin /usr/sbin /usr/lib/quagga"
#
#
# uncomment and edit to establish a distributed control backchannel
#controlnet = core1:172.16.1.0/24 core:172.16.2.0/24 core3:172.16.3.0/24 core4 :172.16.4.0/24 core5:172.16.5.0/24
#controlnet = core1:172.16.1.0/24 core2:172.16.2.0/24 core3:172.16.3.0/24 core4:172.16.4.0/24 core5:172.16.5.0/24
# uncomment and edit to establish distributed auxiliary control channels.
#controlnet1 = core1:172.17.1.0/24 core:172.17.2.0/24 core3:172.17.3.0/24 core4 :172.17.4.0/24 core5:172.17.5.0/24
#controlnet2 = core1:172.18.1.0/24 core:172.18.2.0/24 core3:172.18.3.0/24 core4 :172.18.4.0/24 core5:172.18.5.0/24
#controlnet3 = core1:172.19.1.0/24 core:172.19.2.0/24 core3:172.19.3.0/24 core4 :172.19.4.0/24 core5:172.19.5.0/24
#controlnet1 = core1:172.17.1.0/24 core2:172.17.2.0/24 core3:172.17.3.0/24 core4:172.17.4.0/24 core5:172.17.5.0/24
#controlnet2 = core1:172.18.1.0/24 core2:172.18.2.0/24 core3:172.18.3.0/24 core4:172.18.4.0/24 core5:172.18.5.0/24
#controlnet3 = core1:172.19.1.0/24 core2:172.19.2.0/24 core3:172.19.3.0/24 core4:172.19.4.0/24 core5:172.19.5.0/24
# uncomment and edit to assign host interfaces to auxilary control channels
# for use in connecting with other servers in a distributed environments.

357
daemon/sbin/core-daemon Executable file
View file

@ -0,0 +1,357 @@
#!/usr/bin/env python
#
# CORE
# Copyright (c)2010-2013 the Boeing Company.
# See the LICENSE file included in this distribution.
#
# authors: Tom Goff <thomas.goff@boeing.com>
# Jeff Ahrenholz <jeffrey.m.ahrenholz@boeing.com>
#
"""
core-daemon: the CORE daemon is a server process that receives CORE API
messages and instantiates emulated nodes and networks within the kernel. Various
message handlers are defined and some support for sending messages.
"""
import ConfigParser
import atexit
import importlib
import optparse
import os
import signal
import socket
import sys
import threading
import time
from pkg_resources import require
require("core_python", "corens3_python", "core_python_netns")
from core import constants
from core import corehandlers
from core import coreserver
from core import enumerations
from core import logger
from core import services
from core.api import coreapi
from core.corehandlers import CoreDatagramRequestHandler
from core.enumerations import MessageFlags
from core.enumerations import RegisterTlvs
from core.misc import nodemaps
from core.misc import nodeutils
from core.misc.utils import closeonexec
from core.misc.utils import daemonize
DEFAULT_MAXFD = 1024
def startudp(core_server, server_address):
"""
Start a thread running a UDP server on the same host,port for connectionless requests.
:param core.coreserver.CoreServer core_server: core server instance
:param tuple[str, int] server_address: server address
:return: created core udp server
:rtype: core.coreserver.CoreUdpServer
"""
core_server.udpserver = coreserver.CoreUdpServer(server_address, CoreDatagramRequestHandler, core_server)
core_server.udpthread = threading.Thread(target=core_server.udpserver.start)
core_server.udpthread.daemon = True
core_server.udpthread.start()
return core_server.udpserver
def startaux(core_server, aux_address, aux_handler):
"""
Start a thread running an auxiliary TCP server on the given address.
This server will communicate with client requests using a handler
using the aux_handler class. The aux_handler can provide an alternative
API to CORE.
:param core.coreserver.CoreServer core_server: core server instance
:param tuple[str, int] aux_address: auxiliary server address
:param str aux_handler: auxiliary handler string to import
:return: auxiliary server
"""
handlermodname, dot, handlerclassname = aux_handler.rpartition(".")
handlermod = importlib.import_module(handlermodname)
handlerclass = getattr(handlermod, handlerclassname)
core_server.auxserver = coreserver.CoreAuxServer(aux_address, handlerclass, core_server)
core_server.auxthread = threading.Thread(target=core_server.auxserver.start)
core_server.auxthread.daemon = True
core_server.auxthread.start()
return core_server.auxserver
def banner():
"""
Output the program banner printed to the terminal or log file.
:return: nothing
"""
logger.info("CORE daemon v.%s started %s\n" % (constants.COREDPY_VERSION, time.ctime()))
def cored(cfg=None):
"""
Start the CoreServer object and enter the server loop.
:param dict cfg: core configuration
:return: nothing
"""
host = cfg["listenaddr"]
port = int(cfg["port"])
if host == "" or host is None:
host = "localhost"
try:
server = coreserver.CoreServer((host, port), corehandlers.CoreRequestHandler, cfg)
except:
logger.exception("error starting main server on: %s:%s", host, port)
sys.exit(1)
closeonexec(server.fileno())
logger.info("main server started, listening on: %s:%s\n" % (host, port))
udpserver = startudp(server, (host, port))
closeonexec(udpserver.fileno())
auxreqhandler = cfg["aux_request_handler"]
if auxreqhandler:
handler, auxport = auxreqhandler.rsplit(":")
auxserver = startaux(server, (host, int(auxport)), handler)
closeonexec(auxserver.fileno())
server.serve_forever()
# TODO: should sessions and the main core daemon both catch at exist to shutdown independently?
def cleanup():
"""
Runs server shutdown and cleanup when catching an exit signal.
:return: nothing
"""
while coreserver.CoreServer.servers:
server = coreserver.CoreServer.servers.pop()
server.shutdown()
atexit.register(cleanup)
def sighandler(signum, stackframe):
"""
Signal handler when different signals are sent.
:param int signum: singal number sent
:param stackframe: stack frame sent
:return: nothing
"""
logger.error("terminated by signal: %s", signum)
sys.exit(signum)
signal.signal(signal.SIGHUP, sighandler)
signal.signal(signal.SIGINT, sighandler)
signal.signal(signal.SIGTERM, sighandler)
signal.signal(signal.SIGUSR1, sighandler)
signal.signal(signal.SIGUSR2, sighandler)
def logrotate(stdout, stderr, stdoutmode=0644, stderrmode=0644):
"""
Log rotation method.
:param stdout: stdout
:param stderr: stderr
:param int stdoutmode: stdout mode
:param int stderrmode: stderr mode
:return:
"""
def reopen(fileno, filename, mode):
err = 0
fd = -1
try:
fd = os.open(filename,
os.O_WRONLY | os.O_CREAT | os.O_APPEND, mode)
os.dup2(fd, fileno)
except OSError as e:
err = e.errno
finally:
if fd >= 0:
os.close(fd)
return err
if stdout:
err = reopen(1, stdout, stdoutmode)
if stderr:
if stderr == stdout and not err:
try:
os.dup2(1, 2)
except OSError as e:
pass
else:
reopen(2, stderr, stderrmode)
def get_merged_config(filename):
"""
Return a configuration after merging config file and command-line arguments.
:param str filename: file name to merge configuration settings with
:return: merged configuration
:rtype: dict
"""
# these are the defaults used in the config file
defaults = {"port": "%d" % enumerations.CORE_API_PORT,
"listenaddr": "localhost",
"pidfile": "%s/run/core-daemon.pid" % constants.CORE_STATE_DIR,
"logfile": "%s/log/core-daemon.log" % constants.CORE_STATE_DIR,
"xmlfilever": "1.0",
"numthreads": "1",
"verbose": "False",
"daemonize": "False",
"debug": "False",
"execfile": None,
"aux_request_handler": None,
}
usagestr = "usage: %prog [-h] [options] [args]\n\n" + \
"CORE daemon v.%s instantiates Linux network namespace " \
"nodes." % constants.COREDPY_VERSION
parser = optparse.OptionParser(usage=usagestr)
parser.add_option("-f", "--configfile", dest="configfile",
type="string",
help="read config from specified file; default = %s" %
filename)
parser.add_option("-d", "--daemonize", dest="daemonize",
action="store_true",
help="run in background as daemon; default=%s" % \
defaults["daemonize"])
parser.add_option("-e", "--execute", dest="execfile", type="string",
help="execute a Python/XML-based session")
parser.add_option("-l", "--logfile", dest="logfile", type="string",
help="log output to specified file; default = %s" %
defaults["logfile"])
parser.add_option("-p", "--port", dest="port", type=int,
help="port number to listen on; default = %s" % \
defaults["port"])
parser.add_option("-i", "--pidfile", dest="pidfile",
help="filename to write pid to; default = %s" % \
defaults["pidfile"])
parser.add_option("-t", "--numthreads", dest="numthreads", type=int,
help="number of server threads; default = %s" % \
defaults["numthreads"])
parser.add_option("-v", "--verbose", dest="verbose", action="store_true",
help="enable verbose logging; default = %s" % \
defaults["verbose"])
parser.add_option("-g", "--debug", dest="debug", action="store_true",
help="enable debug logging; default = %s" % \
defaults["debug"])
# parse command line options
options, args = parser.parse_args()
# read the config file
if options.configfile is not None:
filename = options.configfile
del options.configfile
cfg = ConfigParser.SafeConfigParser(defaults)
cfg.read(filename)
section = "core-daemon"
if not cfg.has_section(section):
cfg.add_section(section)
# gracefully support legacy configs (cored.py/cored now core-daemon)
if cfg.has_section("cored.py"):
for name, val in cfg.items("cored.py"):
if name == "pidfile" or name == "logfile":
bn = os.path.basename(val).replace("coredpy", "core-daemon")
val = os.path.join(os.path.dirname(val), bn)
cfg.set(section, name, val)
if cfg.has_section("cored"):
for name, val in cfg.items("cored"):
if name == "pidfile" or name == "logfile":
bn = os.path.basename(val).replace("cored", "core-daemon")
val = os.path.join(os.path.dirname(val), bn)
cfg.set(section, name, val)
# merge command line with config file
for opt in options.__dict__:
val = options.__dict__[opt]
if val is not None:
cfg.set(section, opt, val.__str__())
return dict(cfg.items(section)), args
def exec_file(cfg):
"""
Send a Register Message to execute a new session based on XML or Python script file.
:param dict cfg: configuration settings
:return: 0
"""
filename = cfg["execfile"]
logger.info("Telling daemon to execute file: %s...", filename)
tlvdata = coreapi.CoreRegisterTlv.pack(RegisterTlvs.EXECUTE_SERVER.value, filename)
msg = coreapi.CoreRegMessage.pack(MessageFlags.ADD.value, tlvdata)
sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
# TODO: connect address option
sock.connect(("localhost", int(cfg["port"])))
sock.sendall(msg)
return 0
def main():
"""
Main program startup.
:return: nothing
"""
# get a configuration merged from config file and command-line arguments
cfg, args = get_merged_config("%s/core.conf" % constants.CORE_CONF_DIR)
for a in args:
logger.error("ignoring command line argument: %s", a)
if cfg["daemonize"] == "True":
daemonize(rootdir=None, umask=0, close_fds=False,
stdin=os.devnull,
stdout=cfg["logfile"], stderr=cfg["logfile"],
pidfilename=cfg["pidfile"],
defaultmaxfd=DEFAULT_MAXFD)
signal.signal(signal.SIGUSR1, lambda signum, stackframe:
logrotate(stdout=cfg["logfile"], stderr=cfg["logfile"]))
banner()
if cfg["execfile"]:
cfg["execfile"] = os.path.abspath(cfg["execfile"])
sys.exit(exec_file(cfg))
try:
cored(cfg)
except KeyboardInterrupt:
logger.info("keyboard interrupt, stopping core daemon")
sys.exit(0)
if __name__ == "__main__":
# configure nodes to use
node_map = nodemaps.NODES
if len(sys.argv) == 2 and sys.argv[1] == "ovs":
from core.netns.openvswitch import OVS_NODES
node_map.update(OVS_NODES)
# update with BSD based nodes
if os.uname()[0] == "FreeBSD":
from core.bsd.nodes import BSD_NODES
node_map.update(BSD_NODES)
nodeutils.set_node_map(node_map)
# load default services
services.load()
main()

0
daemon/scripts/core-cleanup Executable file → Normal file
View file

0
daemon/scripts/core-manage Executable file → Normal file
View file

0
daemon/scripts/coresendmsg Executable file → Normal file
View file

73
daemon/src/Makefile.am Normal file
View file

@ -0,0 +1,73 @@
# CORE
# (c)2010-2013 the Boeing Company.
# See the LICENSE file included in this distribution.
#
# author: Jeff Ahrenholz <jeffrey.m.ahrenholz@boeing.com>
#
# Makefile for building netns.
#
AM_CFLAGS = -Wall -fno-strict-aliasing -O3 -g @libev_CFLAGS@
SETUPPY = setup.py
SETUPPYFLAGS = -v
# -DDEBUG
SRC_COMMON = vnode_msg.c vnode_cmd.c vnode_chnl.c vnode_io.c \
vnode_msg.h vnode_cmd.h vnode_chnl.h vnode_io.h \
vnode_tlv.h myerr.h netns.h
SRC_VNODED = vnoded_main.c vnode_server.c netns.c \
vnode_server.h
SRC_VCMD = vcmd_main.c vnode_client.c \
vnode_client.h
SRC_NETNS = netns_main.c netns.c netns.h
sbin_PROGRAMS = vnoded vcmd netns
vnoded_LDADD = @libev_LIBS@
vnoded_SOURCES = ${SRC_COMMON} ${SRC_VNODED}
vcmd_LDADD = @libev_LIBS@
vcmd_SOURCES = ${SRC_COMMON} ${SRC_VCMD}
netns_SOURCES = ${SRC_NETNS}
# this triggers automake to run setup.py for building the Python libraries
# actual library names are netns.so and vcmd.so
# SOURCES line prevents 'make dist' from looking for a 'libnetns.c' file
noinst_LIBRARIES = libnetns.a
libnetns_a_SOURCES = netnsmodule.c vcmdmodule.c
libnetns.a:
SBINDIR=@SBINDIR@ LDFLAGS="$(LDFLAGS) @libev_LIBS@" CFLAGS="$(CFLAGS) @libev_CFLAGS@" $(PYTHON) setup.py build
# Python libraries install
install-exec-local:
$(MKDIR_P) ${DESTDIR}/${pythondir}
$(MKDIR_P) ${DESTDIR}/${pyexecdir}
SBINDIR=${DESTDIR}/@SBINDIR@ PYTHONPATH=${DESTDIR}/${pythondir}:${DESTDIR}/${pyexecdir} $(PYTHON) $(SETUPPY) $(SETUPPYFLAGS) install \
--prefix=${DESTDIR}/${pyprefix} \
--install-purelib=${DESTDIR}/${pythondir} \
--install-platlib=${DESTDIR}/${pyexecdir} \
--no-compile
# Python libraries uninstall
uninstall-hook:
rm -f ${pyexecdir}/core_python_netns-1.0-py${PYTHON_VERSION}.egg-info
rm -f ${pyexecdir}/netns.so
rm -f ${pyexecdir}/vcmd.so
# Python libraries cleanup
clean-local: clean-local-check
.PHONY: clean-local-check
clean-local-check:
-rm -rf build
rpmbuild.sh:
echo SBINDIR=@SBINDIR@ LDFLAGS="$(LDFLAGS)" CFLAGS="$(CFLAGS) @libev_CFLAGS@" $(PYTHON) setup.py build > rpmbuild.sh
chmod a+x rpmbuild.sh
rpm: rpmbuild.sh
$(PYTHON) setup.py bdist_rpm --build-script=rpmbuild.sh --requires="libev" --build-requires="libev-devel"
# extra cruft to remove
DISTCLEANFILES = Makefile.in rpmbuild.sh MANIFEST
# include source files for Python libraries with distribution tarball
EXTRA_DIST = setup.py MANIFEST.in

View file

@ -24,6 +24,6 @@ remove the core-daemon.log file
.BR vnoded(1)
.SH BUGS
Report bugs to
.BI core-dev@pf.itd.nrl.navy.mil.
.BI core-dev@nrl.navy.mil.

View file

@ -48,5 +48,5 @@ enable debug logging; default = False
.BR vnoded(1)
.SH BUGS
Report bugs to
.BI core-dev@pf.itd.nrl.navy.mil.
.BI core-dev@nrl.navy.mil.

View file

@ -40,5 +40,5 @@ With no parameters, starts the GUI in edit mode with a blank canvas.
.BR vnoded(1)
.SH BUGS
Report bugs to
.BI core-dev@pf.itd.nrl.navy.mil.
.BI core-dev@nrl.navy.mil.

View file

@ -0,0 +1,28 @@
.\" DO NOT MODIFY THIS FILE! It was generated by help2man 1.40.4.
.TH CORE-XEN-CLEANUP "1" "2014-08-06" "CORE-XEN-CLEANUP" "User Commands"
.SH NAME
core-xen-cleanup \- clean-up script for CORE Xen domUs
.SH DESCRIPTION
usage: core\-xen\-cleanup [\-d]
.IP
Clean up all CORE Xen domUs, bridges, interfaces, and session
directories. Options:
.TP
\fB\-h\fR
show this help message and exit
.TP
\fB\-d\fR
also kill the Python daemon
.SH "SEE ALSO"
.BR core-gui(1),
.BR core-daemon(1),
.BR coresendmsg(1),
.BR core-cleanup(1),
.BR vcmd(1),
.BR vnoded(1)
.SH BUGS
Warning! This script will remove logical volumes that match the name "/dev/vg*/c*-n*-" on all volume groups. Use with care.
Report bugs to
.BI core-dev@nrl.navy.mil.

View file

@ -82,4 +82,4 @@ coresendmsg \-H
.BR vnoded(1)
.SH BUGS
Report bugs to
.BI core-dev@pf.itd.nrl.navy.mil.
.BI core-dev@nrl.navy.mil.

View file

@ -26,5 +26,5 @@ wait for command to complete (useful for interactive commands)
.BR vnoded(1)
.SH BUGS
Report bugs to
.BI core-dev@pf.itd.nrl.navy.mil.
.BI core-dev@nrl.navy.mil.

View file

@ -38,5 +38,5 @@ control channel name (e.g. '/tmp/pycore.45647/n3')
.BR vnoded(1),
.SH BUGS
Report bugs to
.BI core-dev@pf.itd.nrl.navy.mil.
.BI core-dev@nrl.navy.mil.

View file

@ -40,5 +40,5 @@ establish the specified <control channel> for receiving control commands
.BR vcmd(1),
.SH BUGS
Report bugs to
.BI core-dev@pf.itd.nrl.navy.mil.
.BI core-dev@nrl.navy.mil.

View file

@ -772,14 +772,17 @@ proc manageCPUwindow {xpos ypos start} {
}
proc getMyIP { } {
if { [catch {set theServer [socket -server none -myaddr \
[info hostname] 0]} ] } {
return "127.0.0.1"
variable myIP
if { ![info exists myIP] } {
if { [catch {set theServer [socket -server none \
-myaddr [info hostname] 0]} ] } {
set myIP "127.0.0.1"
} else {
set myIP [lindex [fconfigure $theServer -sockname] 0]
close $theServer
}
}
set myIP [lindex [fconfigure $theServer -sockname] 0]
close $theServer
return $myIP
}
# display all values stored in cpu usage history for each server

0
gui/icons/normal/OVS.gif Normal file → Executable file
View file

Before

Width:  |  Height:  |  Size: 744 B

After

Width:  |  Height:  |  Size: 744 B

0
gui/icons/svg/OVS.svg Normal file → Executable file
View file

Before

Width:  |  Height:  |  Size: 6.5 KiB

After

Width:  |  Height:  |  Size: 6.5 KiB

0
gui/icons/tiny/OVS.gif Normal file → Executable file
View file

Before

Width:  |  Height:  |  Size: 744 B

After

Width:  |  Height:  |  Size: 744 B

0
ns3/examples/ns3lte.py Executable file → Normal file
View file

0
ns3/examples/ns3wifi.py Executable file → Normal file
View file

0
ns3/examples/ns3wifirandomwalk.py Executable file → Normal file
View file

0
ns3/examples/ns3wimax.py Executable file → Normal file
View file