Merge branch 'develop' into documentation
This commit is contained in:
commit
ec9fc37f8c
106 changed files with 3167 additions and 3502 deletions
371
CHANGELOG.md
Normal file
371
CHANGELOG.md
Normal file
|
@ -0,0 +1,371 @@
|
|||
## 2019-06-10 CORE 5.3.0
|
||||
* Enhancements
|
||||
* python 2 / 3 support
|
||||
* added new API using [gRPC](https://grpc.io/)
|
||||
* --grpc --grpc-port --grpc-address flags added to core-daemon
|
||||
* core.api.grpc.client.CoreGrpcClient, provides a convenience wrapper for leveraging the API
|
||||
* Docs
|
||||
* Updates to installation instructions for latest changes
|
||||
* Services
|
||||
* Added FRR service
|
||||
* EMANE
|
||||
* Added EMANE prefix configuration when looking for emane model manifest files
|
||||
* requires configuring **emane_prefix** in /etc/core/core.conf
|
||||
* Cleanup
|
||||
* Refactoring of the core python package structure, trying to help provide better organization and
|
||||
logical groupings
|
||||
* Issues
|
||||
* \#246 - Fixed network to network link handling when reading xml files
|
||||
* \#236 - Fixed storing/reading of link configuration values within xml files
|
||||
* \#170 - FRR Service
|
||||
* \#155 - EMANE path configuration
|
||||
* \#233 - Python 3 support
|
||||
* \#245 - Fixed bidirectional link configurations when reading from xml files
|
||||
* \#208 - gRPC API
|
||||
* Fixed link configuration dup handling when loaded from xml files
|
||||
|
||||
## 2019-06-07 CORE 5.2.2
|
||||
* Enhancements:
|
||||
* adds back in core-daemon udp support for coresendmsg, people may have depended on previously for certain scenarios
|
||||
* Bug Fixes:
|
||||
* fixes issue in GUI that would prevent moving nodes during mobility scenarios
|
||||
|
||||
## 2019-03-25 CORE 5.2.1
|
||||
* Packaging:
|
||||
* documentation no longer builds by default, must use configure flag
|
||||
* added configure flag to allow only building vcmd
|
||||
* sphinx will no long be required when not building documentation
|
||||
* Services:
|
||||
* Added source NAT service
|
||||
* Fixed DHCP service for Ubuntu 18.04
|
||||
* BUGFIXES:
|
||||
* \#188 - properly remove session on delete TLV API call
|
||||
* \#192 - updated default gnome terminal command for nodes to be Ubuntu 18.04 compatible
|
||||
* \#193 - updates to service validation, will retry on failure and better exception logging
|
||||
* \#195 - TLV link message data fix
|
||||
* \#196 - fix to avoid clearing out default services
|
||||
* \#197 - removed wireless_link_all API from EmuSession
|
||||
* \#216 - updated default WLAN bandwidth to 54Mbps
|
||||
* \#223 - fix to saving RJ45 to session XML files
|
||||
|
||||
## 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
|
||||
* support for sonarqube analysis, from the defined configuration file
|
||||
* DAEMON:
|
||||
* code cleanup and improvements to adhere to coding standards (SonarQube)
|
||||
* leverage "logzero" module to make easy usage of the standard logging module
|
||||
* improvements to documentation across the code base
|
||||
* initial work to separate the dependence on TCP API messaging from the core library (easier core scripting)
|
||||
* beta support for running core in Open vSwitch mode, leveraging Open vSwitch bridges, instead of Linux bridges
|
||||
* SERVICES:
|
||||
* added Ryu SDN controller service
|
||||
* added Open vSwitch service
|
||||
* TEST:
|
||||
* added unit/integration tests to support validating changes going forward
|
||||
* BUGFIXES:
|
||||
* merged pull requests for: #115, #110, #109, #107, #106, #105, #103, #102, #101, #96
|
||||
|
||||
## 2015-06-05 CORE 4.8
|
||||
* EMANE:
|
||||
* support for EMANE 0.9.2
|
||||
* run emane in each container when using EMANE 0.9.2
|
||||
* support using separate control networks for EMANE OTA and event traffic
|
||||
* GUI:
|
||||
* fixed an issue where the adjacency widget lines pointed to old node positions
|
||||
* fixed an issue where not all EMANE 0.9.x IEEE 802.11 MAC parameter were configurable
|
||||
* fixed an issue related to running python scripts from the GUI when using tcl/tk version 8.6
|
||||
* improved batch mode execution to display the check emulation light status
|
||||
* improved managing multiple sessions
|
||||
* improved support for using multiple canvases
|
||||
* added a reload option to the file menu to revert back to a saved scenario
|
||||
* DAEMON:
|
||||
* support exporting scenarios in NRL Network Modeling Framework 1.0 XML format
|
||||
* support importing scenarios in NRL Network Modeling Framework 1.0 XML format
|
||||
* support exporting the deployed scenario state in NRL NMF XML 1.0 format
|
||||
* improved EMANE post-startup processing to better synchronize distributed emulations
|
||||
* improved how addresses are assigned to tun/tap devices
|
||||
* added support for python state-change callbacks
|
||||
* SERVICES:
|
||||
* added mgen sink and mgen actor services
|
||||
* added oslrv2 and olsr.org services
|
||||
* added a docker service
|
||||
* BUILD:
|
||||
* improved the install/uninstall process
|
||||
* improved debian and rpm packaging
|
||||
* BUGFIXES:
|
||||
* updated the http service for ubuntu 14.04
|
||||
* improved included examples
|
||||
* shortened the length of network interface names
|
||||
* improved how the core system service manages running the core daemon
|
||||
* fixed an issues related to applying session configuration setting
|
||||
* improved detecting when a distributed emulation is already running
|
||||
* improved documentation
|
||||
|
||||
## 2014-08-06 CORE 4.7
|
||||
* EMANE:
|
||||
* support for EMANE 0.9.1
|
||||
* fix error when using Comm Effect model with loss/duplicate string values
|
||||
* enable flow control in virtual transport if enabled in the MAC model
|
||||
* fix bug #150 where EMANE event service/address port were not used
|
||||
* GUI:
|
||||
* support Tcl/Tk 8.6 when available
|
||||
* added --(a)ddress and --(p)ort arguments to core-gui command-line
|
||||
* added File > Execute XML or Python script... option
|
||||
* added File > Execute Python script with options... menu item
|
||||
* when executing Python script from GUI, run in background thread, wait for
|
||||
RUNTIME state
|
||||
* enter RUNTIME state when start button pressed with empty canvas
|
||||
* added support for asymmetric link effects
|
||||
* support link delays up to 274 seconds (netem maximum)
|
||||
* allow runtime changes of WLAN link effects
|
||||
* DAEMON:
|
||||
* set NODE_NAME, NODE_NUMBER, SESSION_SHORT in default vnoded environment
|
||||
* changed host device naming to use veth, tap prefixes; b.n.SS for bridges
|
||||
* allow parsing XML files into live running session
|
||||
* enable link effects between hub/switch and hub/switch connections
|
||||
* update MDR service to use broadcast interfaces for non-WLAN links
|
||||
* allow node class to be specified when initializing XML parser
|
||||
* save and parse canvas origin (reference point) and scale in MP XML
|
||||
* up/down control script session option
|
||||
* fix hash calculation used to determine GRE tunnel keys
|
||||
* use shell script to detach SMF on startup
|
||||
* added NRL services for mgen sink and nrlolsrv2
|
||||
* use SDT URL session option
|
||||
* added core-manage tool for addons to add/remove/check services, models,
|
||||
and custom node types
|
||||
* API:
|
||||
* implement local flag in Execute Message for running host commands
|
||||
* jitter changed to 64-bit value to align with delay in Link Message
|
||||
* added unidirectional link flag TLV to Link Message
|
||||
* added reconfigure event type for re-generating service config files
|
||||
* return errors in API with failed services
|
||||
* BUGFIXES:
|
||||
* fix HTTP service running under Ubuntu
|
||||
* fixed the following bugs: #150, 169, 188, 220, 225, 230, 231, 242, 244,
|
||||
247, 248, 250, 251
|
||||
|
||||
## 2013-09-25 CORE 4.6
|
||||
* NOTE: cored is now core-daemon, and core is now core-gui (for Debian acceptance)
|
||||
* NOTE: /etc/init.d/core is now /etc/init.d/core-daemon (for insserv compatibility)
|
||||
* EMANE:
|
||||
* don't start EMANE locally if no local NEMs
|
||||
* EMANE poststartup() to re-transmit location events during initialization
|
||||
* added debug port to EMANE options
|
||||
* added a basic EMANE 802.11 CORE Python script example
|
||||
* expose transport XML block generation to EmaneModels
|
||||
* expose NEM entry to the EmaneModel so it can be overridden by a model
|
||||
* add the control interface bridge prior to starting EMANE, as some models may
|
||||
* depend on the controlnet functionality
|
||||
* added EMANE model to CORE converter
|
||||
* parse lat/long/alt from node messages, for moving nodes using command-line
|
||||
* fix bug #196 incorrect distance when traversing UTM zones
|
||||
* GUI:
|
||||
* added Cut, Copy, and Paste options to the Edit menu
|
||||
* paste will copy selected services and take care of node and interface
|
||||
* renumbering
|
||||
* implement Edit > Find dialog for searching nodes and links
|
||||
* when copying existing file for a service, perform string replacement of:
|
||||
* "~", "%SESSION%", "%SESSION_DIR%", "%SESSION_USER%", "%NODE%", "%NODENAME%"
|
||||
* use CORE_DATA_DIR insteadof LIBDIR
|
||||
* fix Adjacency Widget to work with OSPFv2 only networks
|
||||
* BUILD:
|
||||
* build/packaging improvements for inclusion on Debian
|
||||
* fix error when running scenario with a mobility script in batch mode
|
||||
* include Linux kernel patches for 3.8
|
||||
* renamed core-cleanup.sh to core-cleanup for Debian conformance
|
||||
* don't always generate man pages from Makefile; new manpages for
|
||||
coresendmsg and core-daemon
|
||||
* BUGFIXES:
|
||||
* don't auto-assign IPv4/IPv6 addresses when none received in Link Messages (session reconnect)
|
||||
* fixed lock view
|
||||
* fix GUI spinbox errors for Tk 8.5.8 (RHEL/CentOS 6.2)
|
||||
* fix broker node count for distributed session entering the RUNTIME state when
|
||||
* (non-EMANE) WLANs or GreTapBridges are involved;
|
||||
* fix "file exists" error message when distributed session number is re-used
|
||||
* and servers file is written
|
||||
* fix bug #194 configuration dialog too long, make dialog scrollable/resizable
|
||||
* allow float values for loss and duplicates percent
|
||||
* fix the following bugs: 166, 172, 177, 178, 192, 194, 196, 201, 202,
|
||||
205, 206, 210, 212, 213, 214, 221
|
||||
|
||||
## 2013-04-13 CORE 4.5
|
||||
* GUI:
|
||||
* improved behavior when starting GUI without daemon, or using File New after connection with daemon is lost
|
||||
* fix various GUI issues when reconnecting to a session
|
||||
* support 3D GUI via output to SDT3D
|
||||
* added "Execute Python script..." entry to the File Menu
|
||||
* support user-defined terminal program instead of hard-coded xterm
|
||||
* added session options for "enable RJ45s", "preserve session dir"
|
||||
* added buttons to the IP Addresses dialog for removing all/selected IPv4/IPv6
|
||||
* allow sessions with multiple canvases to enter RUNTIME state
|
||||
* added "--addons" startup mode to pass control to code included from addons dir
|
||||
* added "Locked" entry to View menu to prevent moving items
|
||||
* use currently selected node type when invoking a topology generator
|
||||
* updated throughput plots with resizing, color picker, plot labels, locked scales, and save/load plot
|
||||
configuration with imn file
|
||||
* improved session dialog
|
||||
* EMANE:
|
||||
* EMANE 0.8.1 support with backwards-compatibility for 0.7.4
|
||||
* extend CommEffect model to generate CommEffect events upon receipt of Link Messages having link effects
|
||||
* Services:
|
||||
* updated FTP service with root directory for anonymous users
|
||||
* added HTTP, PCAP, BIRD, RADVD, and Babel services
|
||||
* support copying existing files instead of always generating them
|
||||
* added "Services..." entry to node right-click menu
|
||||
* added "View" button for side-by-side comparison when copying customized config files
|
||||
* updated Quagga daemons to wait for zebra.vty VTY file before starting
|
||||
* General:
|
||||
* XML import and export
|
||||
* renamed "cored.py" to "cored", "coresendmsg.py" to "coresendmsg"
|
||||
* code reorganization and clean-up
|
||||
* updated XML export to write NetworkPlan, MotionPlan, and ServicePlan within a Scenario tag, added new
|
||||
"Save As XML..." File menu entry
|
||||
* added script_start/pause/stop options to Ns2ScriptedMobility
|
||||
* "python" source sub-directory renamed to "daemon"
|
||||
* added "cored -e" option to execute a Python script, adding its session to the active sessions list, allowing for
|
||||
GUI connection
|
||||
* support comma-separated list for custom_services_dir in core.conf file
|
||||
* updated kernel patches for Linux kernel 3.5
|
||||
* support RFC 6164-style IPv6 /127 addressing
|
||||
* ns-3:
|
||||
* integrate ns-3 node location between CORE and ns-3 simulation
|
||||
* added ns-3 random walk mobility example
|
||||
* updated ns-3 Wifi example to allow GUI connection and moving of nodes
|
||||
* fixed the following bugs: 54, 103, 111, 136, 145, 153, 157, 160, 161, 162, 164, 165, 168, 170, 171, 173, 174, 176,
|
||||
184, 190, 193
|
||||
|
||||
## 2012-09-25 CORE 4.4
|
||||
* GUI:
|
||||
* real-time bandwidth plotting tool
|
||||
* added Wireshark and tshark right-click menu items
|
||||
* X,Y coordinates shown in the status bar
|
||||
* updated GUI attribute option to link messages for changing color/width/dash
|
||||
* added sample IPsec and VPN scenarios, how many nodes script
|
||||
* added jitter parameter to WLANs
|
||||
* renamed Experiment menu to Session menu, added session options
|
||||
* use 'key=value' configuration for services, EMANE models, WLAN models, etc.
|
||||
* save only service values that have been customized
|
||||
* copy service parameters from one customized service to another
|
||||
* right-click menu to start/stop/restart each service
|
||||
* EMANE:
|
||||
* EMANE 0.7.4 support
|
||||
* added support for EMANE CommEffect model and Comm Effect controller GUI
|
||||
* added support for EMANE Raw Transport when using RJ45 devices
|
||||
* Services:
|
||||
* improved service customization; allow a service to define custom Tcl tab
|
||||
* added vtysh.conf for Quagga service to support 'write mem'
|
||||
* support scheduled events and services that start N seconds after runtime
|
||||
* added UCARP service
|
||||
* Documentation:
|
||||
* converted the CORE manual to reStructuredText using Sphinx; added Python docs
|
||||
* General:
|
||||
* Python code reorganization
|
||||
* improved cored.py thread locking
|
||||
* merged xen branch into trunk
|
||||
* added an event queue to a session with notion of time zero
|
||||
* added UDP support to cored.py
|
||||
* use UDP by default in coresendmsg.py; added '-H' option to print examples
|
||||
* enter a bash shell by default when running vcmd with no arguments
|
||||
* fixes to distributed emulation entering runtime state
|
||||
* write 'nodes' file upon session startup
|
||||
* make session number and other attributes available in environment
|
||||
* support /etc/core/environment and ~/.core/environment files
|
||||
* added Ns2ScriptedMobility model to Python, removed from the GUI
|
||||
* namespace nodes mount a private /sys
|
||||
* fixed the following bugs: 80, 81, 84, 99, 104, 109, 110, 122, 124, 131, 133, 134, 135, 137, 140, 143, 144, 146,
|
||||
147, 151, 154, 155
|
||||
|
||||
## 2012-03-07 CORE 4.3
|
||||
* EMANE 0.7.2 and 0.7.3 support
|
||||
* hook scripts: customize actions at any of six different session states
|
||||
* Check Emulation Light (CEL) exception feedback system
|
||||
* added FTP and XORP services, and service validate commands
|
||||
* services can flag when customization is required
|
||||
* Python classes to support ns-3 simulation experiments
|
||||
* write state, node X,Y position, and servers to pycore session dir
|
||||
* removed over 9,000 lines of unused GUI code
|
||||
* performance monitoring script
|
||||
* batch mode improvements and --closebatch option
|
||||
* export session to EmulationScript XML files
|
||||
* basic range model moved from GUI to Python, supports 3D coordinates
|
||||
* improved WLAN dialog with tabs
|
||||
* added PhysicalNode class for joining real nodes with emulated networks
|
||||
* fixed the following bugs: 50, 75, 76, 79, 82, 83, 85, 86, 89, 90, 92, 94, 96, 98, 100, 112, 113, 116, 119, 120
|
||||
|
||||
## 2011-08-19 CORE 4.2
|
||||
* EMANE 0.7.1 support
|
||||
* support for Bypass model, Universal PHY, logging, realtime
|
||||
* configurable MAC addresses
|
||||
* control interfaces (backchannel between node and host)
|
||||
* service customization dialog improved (tabbed)
|
||||
* new testing scripts for MDR and EMANE performance testing
|
||||
* improved upgrading of old imn files
|
||||
* new coresendmsg.py utility (deprecates libcoreapi and coreapisend)
|
||||
* new security services, custom service becomes UserDefined
|
||||
* new services and Python scripting chapters in manual
|
||||
* fixes to distributed emulation, linking tunnels/RJ45s with WLANs/hubs/switches
|
||||
* fixed the following bugs: 18, 32, 34, 38, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 52, 53, 55, 57, 58, 60, 62, 64,
|
||||
65, 66, 68, 71, 72, 74
|
||||
|
||||
## 2011-01-05 CORE 4.1
|
||||
* new icons for toolbars and nodes
|
||||
* node services introduced, node models deprecated
|
||||
* customizable node types
|
||||
* traffic flow editor with MGEN support
|
||||
* user configs moved from /etc/core/`*` to ~/.core/
|
||||
* allocate addresses from custom IPv4/IPv6 prefixes
|
||||
* distributed emulation using GRE tunnels
|
||||
* FreeBSD 8.1 now uses cored.py
|
||||
* EMANE 0.6.4 support
|
||||
* numerous bugfixes
|
||||
|
||||
## 2010-08-17 CORE 4.0
|
||||
* Python framework with Linux network namespace (netns) support (Linux netns is now the primary supported platform)
|
||||
* ability to close the GUI and later reconnect to a running session (netns only)
|
||||
* EMANE integration (netns only)
|
||||
* new topology generators, host file generator
|
||||
* user-editable Observer Widgets
|
||||
* use of /etc/core instead of /usr/local/etc/core
|
||||
* various bugfixes
|
||||
|
||||
## 2009-09-15 CORE 3.5
|
||||
|
||||
## 2009-06-23 CORE 3.4
|
||||
|
||||
## 2009-03-11 CORE 3.3
|
342
Changelog
342
Changelog
|
@ -1,342 +0,0 @@
|
|||
2019-03-25 CORE 5.2.1
|
||||
* Packaging:
|
||||
- documentation no longer builds by default, must use configure flag
|
||||
- added configure flag to allow only building vcmd
|
||||
- sphinx will no long be required when not building documentation
|
||||
* Services:
|
||||
- Added source NAT service
|
||||
- Fixed DHCP service for Ubuntu 18.04
|
||||
* BUGFIXES:
|
||||
- #188 - properly remove session on delete TLV API call
|
||||
- #192 - updated default gnome terminal command for nodes to be Ubuntu 18.04 compatible
|
||||
- #193 - updates to service validation, will retry on failure and better exception logging
|
||||
- #195 - TLV link message data fix
|
||||
- #196 - fix to avoid clearing out default services
|
||||
- #197 - removed wireless_link_all API from EmuSession
|
||||
- #216 - updated default WLAN bandwidth to 54Mbps
|
||||
- #223 - fix to saving RJ45 to session XML files
|
||||
|
||||
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
|
||||
- support for sonarqube analysis, from the defined configuration file
|
||||
* DAEMON:
|
||||
- code cleanup and improvements to adhere to coding standards (SonarQube)
|
||||
- leverage "logzero" module to make easy usage of the standard logging module
|
||||
- improvements to documentation across the code base
|
||||
- initial work to separate the dependence on TCP API messaging from the core library (easier core scripting)
|
||||
- beta support for running core in Open vSwitch mode, leveraging Open vSwitch bridges, instead of Linux bridges
|
||||
* SERVICES:
|
||||
- added Ryu SDN controller service
|
||||
- added Open vSwitch service
|
||||
* TEST:
|
||||
- added unit/integration tests to support validating changes going forward
|
||||
* BUGFIXES:
|
||||
- merged pull requests for: #115, #110, #109, #107, #106, #105, #103, #102, #101, #96
|
||||
|
||||
2015-06-05 CORE 4.8
|
||||
* EMANE:
|
||||
- support for EMANE 0.9.2
|
||||
- run emane in each container when using EMANE 0.9.2
|
||||
- support using separate control networks for EMANE OTA and event traffic
|
||||
* GUI:
|
||||
- fixed an issue where the adjacency widget lines pointed to old node positions
|
||||
- fixed an issue where not all EMANE 0.9.x IEEE 802.11 MAC parameter were configurable
|
||||
- fixed an issue related to running python scripts from the GUI when using tcl/tk version 8.6
|
||||
- improved batch mode execution to display the check emulation light status
|
||||
- improved managing multiple sessions
|
||||
- improved support for using multiple canvases
|
||||
- added a reload option to the file menu to revert back to a saved scenario
|
||||
* DAEMON:
|
||||
- support exporting scenarios in NRL Network Modeling Framework 1.0 XML format
|
||||
- support importing scenarios in NRL Network Modeling Framework 1.0 XML format
|
||||
- support exporting the deployed scenario state in NRL NMF XML 1.0 format
|
||||
- improved EMANE post-startup processing to better synchronize distributed emulations
|
||||
- improved how addresses are assigned to tun/tap devices
|
||||
- added support for python state-change callbacks
|
||||
* SERVICES:
|
||||
- added mgen sink and mgen actor services
|
||||
- added oslrv2 and olsr.org services
|
||||
- added a docker service
|
||||
* BUILD:
|
||||
- improved the install/uninstall process
|
||||
- improved debian and rpm packaging
|
||||
* BUGFIXES:
|
||||
- updated the http service for ubuntu 14.04
|
||||
- improved included examples
|
||||
- shortened the length of network interface names
|
||||
- improved how the core system service manages running the core daemon
|
||||
- fixed an issues related to applying session configuration setting
|
||||
- improved detecting when a distributed emulation is already running
|
||||
- improved documentation
|
||||
|
||||
2014-08-06 CORE 4.7
|
||||
|
||||
* EMANE:
|
||||
- support for EMANE 0.9.1
|
||||
- fix error when using Comm Effect model with loss/duplicate string values
|
||||
- enable flow control in virtual transport if enabled in the MAC model
|
||||
- fix bug #150 where EMANE event service/address port were not used
|
||||
* GUI:
|
||||
- support Tcl/Tk 8.6 when available
|
||||
- added --(a)ddress and --(p)ort arguments to core-gui command-line
|
||||
- added File > Execute XML or Python script... option
|
||||
- added File > Execute Python script with options... menu item
|
||||
- when executing Python script from GUI, run in background thread, wait for
|
||||
RUNTIME state
|
||||
- enter RUNTIME state when start button pressed with empty canvas
|
||||
- added support for asymmetric link effects
|
||||
- support link delays up to 274 seconds (netem maximum)
|
||||
- allow runtime changes of WLAN link effects
|
||||
* DAEMON:
|
||||
- set NODE_NAME, NODE_NUMBER, SESSION_SHORT in default vnoded environment
|
||||
- changed host device naming to use veth, tap prefixes; b.n.SS for bridges
|
||||
- allow parsing XML files into live running session
|
||||
- enable link effects between hub/switch and hub/switch connections
|
||||
- update MDR service to use broadcast interfaces for non-WLAN links
|
||||
- allow node class to be specified when initializing XML parser
|
||||
- save and parse canvas origin (reference point) and scale in MP XML
|
||||
- up/down control script session option
|
||||
- fix hash calculation used to determine GRE tunnel keys
|
||||
- use shell script to detach SMF on startup
|
||||
- added NRL services for mgen sink and nrlolsrv2
|
||||
- use SDT URL session option
|
||||
- added core-manage tool for addons to add/remove/check services, models,
|
||||
and custom node types
|
||||
* API:
|
||||
- implement local flag in Execute Message for running host commands
|
||||
- jitter changed to 64-bit value to align with delay in Link Message
|
||||
- added unidirectional link flag TLV to Link Message
|
||||
- added reconfigure event type for re-generating service config files
|
||||
- return errors in API with failed services
|
||||
* BUGFIXES:
|
||||
- fix HTTP service running under Ubuntu
|
||||
- fixed the following bugs: #150, 169, 188, 220, 225, 230, 231, 242, 244,
|
||||
247, 248, 250, 251
|
||||
|
||||
2013-09-25 CORE 4.6
|
||||
|
||||
* NOTE: cored is now core-daemon, and core is now core-gui (for Debian
|
||||
acceptance)
|
||||
* NOTE: /etc/init.d/core is now /etc/init.d/core-daemon (for insserv
|
||||
compatibility)
|
||||
* EMANE:
|
||||
- don't start EMANE locally if no local NEMs
|
||||
- EMANE poststartup() to re-transmit location events during initialization
|
||||
- added debug port to EMANE options
|
||||
- added a basic EMANE 802.11 CORE Python script example
|
||||
- expose transport XML block generation to EmaneModels
|
||||
- expose NEM entry to the EmaneModel so it can be overridden by a model
|
||||
- add the control interface bridge prior to starting EMANE, as some models may
|
||||
- depend on the controlnet functionality
|
||||
- added EMANE model to CORE converter
|
||||
- parse lat/long/alt from node messages, for moving nodes using command-line
|
||||
- fix bug #196 incorrect distance when traversing UTM zones
|
||||
|
||||
* GUI:
|
||||
- added Cut, Copy, and Paste options to the Edit menu
|
||||
- paste will copy selected services and take care of node and interface
|
||||
- renumbering
|
||||
- implement Edit > Find dialog for searching nodes and links
|
||||
- when copying existing file for a service, perform string replacement of:
|
||||
- "~", "%SESSION%", "%SESSION_DIR%", "%SESSION_USER%", "%NODE%", "%NODENAME%"
|
||||
- use CORE_DATA_DIR insteadof LIBDIR
|
||||
- fix Adjacency Widget to work with OSPFv2 only networks
|
||||
|
||||
* BUILD:
|
||||
- build/packaging improvements for inclusion on Debian
|
||||
- fix error when running scenario with a mobility script in batch mode
|
||||
- include Linux kernel patches for 3.8
|
||||
- renamed core-cleanup.sh to core-cleanup for Debian conformance
|
||||
- don't always generate man pages from Makefile; new manpages for
|
||||
coresendmsg and core-daemon
|
||||
|
||||
* BUGFIXES:
|
||||
- don't auto-assign IPv4/IPv6 addresses when none received in Link Messages (session reconnect)
|
||||
- fixed lock view
|
||||
- fix GUI spinbox errors for Tk 8.5.8 (RHEL/CentOS 6.2)
|
||||
- fix broker node count for distributed session entering the RUNTIME state when
|
||||
- (non-EMANE) WLANs or GreTapBridges are involved;
|
||||
- fix "file exists" error message when distributed session number is re-used
|
||||
- and servers file is written
|
||||
- fix bug #194 configuration dialog too long, make dialog scrollable/resizable
|
||||
- allow float values for loss and duplicates percent
|
||||
- fix the following bugs: 166, 172, 177, 178, 192, 194, 196, 201, 202,
|
||||
205, 206, 210, 212, 213, 214, 221
|
||||
|
||||
2013-04-13 CORE 4.5
|
||||
|
||||
* GUI:
|
||||
- improved behavior when starting GUI without daemon, or using File New after connection with daemon is lost
|
||||
- fix various GUI issues when reconnecting to a session
|
||||
- support 3D GUI via output to SDT3D
|
||||
- added "Execute Python script..." entry to the File Menu
|
||||
- support user-defined terminal program instead of hard-coded xterm
|
||||
- added session options for "enable RJ45s", "preserve session dir"
|
||||
- added buttons to the IP Addresses dialog for removing all/selected IPv4/IPv6
|
||||
- allow sessions with multiple canvases to enter RUNTIME state
|
||||
- added "--addons" startup mode to pass control to code included from addons dir
|
||||
- added "Locked" entry to View menu to prevent moving items
|
||||
- use currently selected node type when invoking a topology generator
|
||||
- updated throughput plots with resizing, color picker, plot labels, locked scales, and save/load plot configuration with imn file
|
||||
- improved session dialog
|
||||
* EMANE:
|
||||
- EMANE 0.8.1 support with backwards-compatibility for 0.7.4
|
||||
- extend CommEffect model to generate CommEffect events upon receipt of Link Messages having link effects
|
||||
* Services:
|
||||
- updated FTP service with root directory for anonymous users
|
||||
- added HTTP, PCAP, BIRD, RADVD, and Babel services
|
||||
- support copying existing files instead of always generating them
|
||||
- added "Services..." entry to node right-click menu
|
||||
- added "View" button for side-by-side comparison when copying customized config files
|
||||
- updated Quagga daemons to wait for zebra.vty VTY file before starting
|
||||
* General:
|
||||
- XML import and export
|
||||
- renamed "cored.py" to "cored", "coresendmsg.py" to "coresendmsg"
|
||||
- code reorganization and clean-up
|
||||
- updated XML export to write NetworkPlan, MotionPlan, and ServicePlan within a Scenario tag, added new "Save As XML..." File menu entry
|
||||
- added script_start/pause/stop options to Ns2ScriptedMobility
|
||||
- "python" source sub-directory renamed to "daemon"
|
||||
- added "cored -e" option to execute a Python script, adding its session to the active sessions list, allowing for GUI connection
|
||||
- support comma-separated list for custom_services_dir in core.conf file
|
||||
- updated kernel patches for Linux kernel 3.5
|
||||
- support RFC 6164-style IPv6 /127 addressing
|
||||
* ns-3:
|
||||
- integrate ns-3 node location between CORE and ns-3 simulation
|
||||
- added ns-3 random walk mobility example
|
||||
- updated ns-3 Wifi example to allow GUI connection and moving of nodes
|
||||
* fixed the following bugs: 54, 103, 111, 136, 145, 153, 157, 160, 161, 162, 164, 165, 168, 170, 171, 173, 174, 176, 184, 190, 193
|
||||
|
||||
2012-09-25 CORE 4.4
|
||||
|
||||
* GUI:
|
||||
- real-time bandwidth plotting tool
|
||||
- added Wireshark and tshark right-click menu items
|
||||
- X,Y coordinates shown in the status bar
|
||||
- updated GUI attribute option to link messages for changing color/width/dash
|
||||
- added sample IPsec and VPN scenarios, how many nodes script
|
||||
- added jitter parameter to WLANs
|
||||
- renamed Experiment menu to Session menu, added session options
|
||||
- use 'key=value' configuration for services, EMANE models, WLAN models, etc.
|
||||
- save only service values that have been customized
|
||||
- copy service parameters from one customized service to another
|
||||
- right-click menu to start/stop/restart each service
|
||||
* EMANE:
|
||||
- EMANE 0.7.4 support
|
||||
- added support for EMANE CommEffect model and Comm Effect controller GUI
|
||||
- added support for EMANE Raw Transport when using RJ45 devices
|
||||
* Services:
|
||||
- improved service customization; allow a service to define custom Tcl tab
|
||||
- added vtysh.conf for Quagga service to support 'write mem'
|
||||
- support scheduled events and services that start N seconds after runtime
|
||||
- added UCARP service
|
||||
* Documentation:
|
||||
- converted the CORE manual to reStructuredText using Sphinx; added Python docs
|
||||
* General:
|
||||
- Python code reorganization
|
||||
- improved cored.py thread locking
|
||||
- merged xen branch into trunk
|
||||
- added an event queue to a session with notion of time zero
|
||||
- added UDP support to cored.py
|
||||
- use UDP by default in coresendmsg.py; added '-H' option to print examples
|
||||
- enter a bash shell by default when running vcmd with no arguments
|
||||
- fixes to distributed emulation entering runtime state
|
||||
- write 'nodes' file upon session startup
|
||||
- make session number and other attributes available in environment
|
||||
- support /etc/core/environment and ~/.core/environment files
|
||||
- added Ns2ScriptedMobility model to Python, removed from the GUI
|
||||
- namespace nodes mount a private /sys
|
||||
|
||||
- fixed the following bugs: 80, 81, 84, 99, 104, 109, 110, 122, 124, 131, 133, 134, 135, 137, 140, 143, 144, 146, 147, 151, 154, 155
|
||||
|
||||
2012-03-07 CORE 4.3
|
||||
|
||||
* EMANE 0.7.2 and 0.7.3 support
|
||||
* hook scripts: customize actions at any of six different session states
|
||||
* Check Emulation Light (CEL) exception feedback system
|
||||
* added FTP and XORP services, and service validate commands
|
||||
* services can flag when customization is required
|
||||
* Python classes to support ns-3 simulation experiments
|
||||
* write state, node X,Y position, and servers to pycore session dir
|
||||
* removed over 9,000 lines of unused GUI code
|
||||
* performance monitoring script
|
||||
* batch mode improvements and --closebatch option
|
||||
* export session to EmulationScript XML files
|
||||
* basic range model moved from GUI to Python, supports 3D coordinates
|
||||
* improved WLAN dialog with tabs
|
||||
* added PhysicalNode class for joining real nodes with emulated networks
|
||||
* fixed the following bugs: 50, 75, 76, 79, 82, 83, 85, 86, 89, 90, 92, 94, 96, 98, 100, 112, 113, 116, 119, 120
|
||||
|
||||
2011-08-19 CORE 4.2
|
||||
|
||||
* EMANE 0.7.1 support
|
||||
- support for Bypass model, Universal PHY, logging, realtime
|
||||
* configurable MAC addresses
|
||||
* control interfaces (backchannel between node and host)
|
||||
* service customization dialog improved (tabbed)
|
||||
* new testing scripts for MDR and EMANE performance testing
|
||||
* improved upgrading of old imn files
|
||||
* new coresendmsg.py utility (deprecates libcoreapi and coreapisend)
|
||||
* new security services, custom service becomes UserDefined
|
||||
* new services and Python scripting chapters in manual
|
||||
* fixes to distributed emulation, linking tunnels/RJ45s with WLANs/hubs/switches
|
||||
* fixed the following bugs: 18, 32, 34, 38, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 52, 53, 55, 57, 58, 60, 62, 64, 65, 66, 68, 71, 72, 74
|
||||
|
||||
2011-01-05 CORE 4.1
|
||||
* new icons for toolbars and nodes
|
||||
* node services introduced, node models deprecated
|
||||
* customizable node types
|
||||
* traffic flow editor with MGEN support
|
||||
* user configs moved from /etc/core/`*` to ~/.core/
|
||||
* allocate addresses from custom IPv4/IPv6 prefixes
|
||||
* distributed emulation using GRE tunnels
|
||||
* FreeBSD 8.1 now uses cored.py
|
||||
* EMANE 0.6.4 support
|
||||
* numerous bugfixes
|
||||
|
||||
2010-08-17 CORE 4.0
|
||||
* Python framework with Linux network namespace (netns) support (Linux netns is now the primary supported platform)
|
||||
* ability to close the GUI and later reconnect to a running session (netns only)
|
||||
* EMANE integration (netns only)
|
||||
* new topology generators, host file generator
|
||||
* user-editable Observer Widgets
|
||||
* use of /etc/core instead of /usr/local/etc/core
|
||||
* various bugfixes
|
||||
|
||||
2009-09-15 CORE 3.5
|
||||
|
||||
2009-06-23 CORE 3.4
|
||||
|
||||
2009-03-11 CORE 3.3
|
||||
|
82
Makefile.am
82
Makefile.am
|
@ -28,7 +28,7 @@ EXTRA_DIST = bootstrap.sh \
|
|||
LICENSE \
|
||||
README.md \
|
||||
ASSIGNMENT_OF_COPYRIGHT.pdf \
|
||||
Changelog \
|
||||
CHANGELOG.md \
|
||||
.version \
|
||||
.version.date
|
||||
|
||||
|
@ -44,38 +44,29 @@ DISTCLEANFILES = aclocal.m4 \
|
|||
MAINTAINERCLEANFILES = .version \
|
||||
.version.date
|
||||
|
||||
define fpm-python =
|
||||
fpm -s python -t $1 \
|
||||
-m "$(PACKAGE_MAINTAINERS)" \
|
||||
--vendor "$(PACKAGE_VENDOR)" \
|
||||
$2
|
||||
endef
|
||||
|
||||
define fpm-gui =
|
||||
fpm -s dir -t $1 -n core-gui \
|
||||
if PYTHON3
|
||||
PYTHON_DEB_DEP = python3 >= 3.0
|
||||
PYTHON_RPM_DEP = python3 >= 3.0
|
||||
else
|
||||
PYTHON_DEB_DEP = python (>= 2.7), python (<< 3.0)
|
||||
PYTHON_RPM_DEP = python >= 2.7, python < 3.0
|
||||
endif
|
||||
|
||||
define fpm-rpm =
|
||||
fpm -s dir -t rpm -n core \
|
||||
-m "$(PACKAGE_MAINTAINERS)" \
|
||||
--license "BSD" \
|
||||
--description "Common Open Research Emulator GUI front-end" \
|
||||
--description "Common Open Research Emulator" \
|
||||
--url https://github.com/coreemu/core \
|
||||
--vendor "$(PACKAGE_VENDOR)" \
|
||||
-p core-gui_VERSION_ARCH.$1 \
|
||||
-p core_$(PYTHON)_VERSION_ARCH.rpm \
|
||||
-v $(PACKAGE_VERSION) \
|
||||
-d "bash" \
|
||||
--rpm-init scripts/core-daemon \
|
||||
--config-files "/etc/core" \
|
||||
-d "ethtool" \
|
||||
-d "tcl" \
|
||||
-d "tk" \
|
||||
$2 \
|
||||
-C $(DESTDIR)
|
||||
endef
|
||||
|
||||
define fpm-daemon-rpm =
|
||||
fpm -s python -t rpm \
|
||||
-p NAME_sysv_VERSION_ARCH.rpm \
|
||||
--rpm-init scripts/core-daemon \
|
||||
--python-install-bin $(bindir) \
|
||||
--python-install-data $(prefix) \
|
||||
--python-install-lib $(pythondir) \
|
||||
-m "$(PACKAGE_MAINTAINERS)" \
|
||||
--vendor "$(PACKAGE_VENDOR)" \
|
||||
-d "procps-ng" \
|
||||
-d "bash >= 3.0" \
|
||||
-d "bridge-utils" \
|
||||
|
@ -83,19 +74,26 @@ fpm -s python -t rpm \
|
|||
-d "iproute" \
|
||||
-d "libev" \
|
||||
-d "net-tools" \
|
||||
-d "python >= 2.7, python < 3.0" \
|
||||
netns/setup.py daemon/setup.py
|
||||
-d "$(PYTHON_RPM_DEP)" \
|
||||
-C $(DESTDIR)
|
||||
endef
|
||||
|
||||
define fpm-daemon-deb =
|
||||
fpm -s python -t deb \
|
||||
-p NAME_$1_VERSION_ARCH.deb \
|
||||
--python-install-bin $(bindir) \
|
||||
--python-install-data $(prefix) \
|
||||
--python-install-lib $(pythondir) \
|
||||
$2 $3 \
|
||||
define fpm-deb =
|
||||
fpm -s dir -t deb -n core \
|
||||
-m "$(PACKAGE_MAINTAINERS)" \
|
||||
--license "BSD" \
|
||||
--description "Common Open Research Emulator" \
|
||||
--url https://github.com/coreemu/core \
|
||||
--vendor "$(PACKAGE_VENDOR)" \
|
||||
-p core_$(PYTHON)_VERSION_ARCH.deb \
|
||||
-v $(PACKAGE_VERSION) \
|
||||
--deb-systemd scripts/core-daemon.service \
|
||||
--deb-no-default-config-files \
|
||||
--config-files "/etc/core" \
|
||||
-d "ethtool" \
|
||||
-d "tcl" \
|
||||
-d "tk" \
|
||||
-d "libtk-img" \
|
||||
-d "procps" \
|
||||
-d "libc6 >= 2.14" \
|
||||
-d "bash >= 3.0" \
|
||||
|
@ -103,21 +101,15 @@ fpm -s python -t deb \
|
|||
-d "ebtables" \
|
||||
-d "iproute2" \
|
||||
-d "libev4" \
|
||||
-d "python (>= 2.7), python (<< 3.0)" \
|
||||
--deb-recommends quagga \
|
||||
netns/setup.py daemon/setup.py
|
||||
-d "$(PYTHON_DEB_DEP)" \
|
||||
-C $(DESTDIR)
|
||||
endef
|
||||
|
||||
.PHONY: fpm
|
||||
fpm: clean-local-fpm
|
||||
$(MAKE) -C gui install DESTDIR=$(DESTDIR)
|
||||
$(call fpm-gui,rpm)
|
||||
$(call fpm-gui,deb,-d "libtk-img")
|
||||
$(call fpm-python,rpm,ns3/setup.py)
|
||||
$(call fpm-python,deb,ns3/setup.py)
|
||||
$(call fpm-daemon-rpm)
|
||||
$(call fpm-daemon-deb,sysv,--deb-init,scripts/core-daemon)
|
||||
$(call fpm-daemon-deb,systemd,--deb-systemd,scripts/core-daemon.service)
|
||||
$(MAKE) install DESTDIR=$(DESTDIR)
|
||||
$(call fpm-deb)
|
||||
$(call fpm-rpm)
|
||||
|
||||
.PHONY: clean-local-fpm
|
||||
clean-local-fpm:
|
||||
|
|
|
@ -17,13 +17,13 @@ scripting network emulation.
|
|||
## Documentation and Examples
|
||||
|
||||
* Documentation hosted on GitHub
|
||||
* <http://coreemu.github.io/core/>
|
||||
* <http://coreemu.github.io/core/>
|
||||
* Basic Script Examples
|
||||
* [Examples](daemon/examples/api)
|
||||
* [Examples](daemon/examples/api)
|
||||
* Custom Service Example
|
||||
* [sample.py](daemon/examples/myservices/sample.py)
|
||||
* [sample.py](daemon/examples/myservices/sample.py)
|
||||
* Custom Emane Model Example
|
||||
* [examplemodel.py](daemon/examples/myemane/examplemodel.py)
|
||||
* [examplemodel.py](daemon/examples/myemane/examplemodel.py)
|
||||
|
||||
## Support
|
||||
|
||||
|
|
10
configure.ac
10
configure.ac
|
@ -2,7 +2,7 @@
|
|||
# Process this file with autoconf to produce a configure script.
|
||||
|
||||
# this defines the CORE version number, must be static for AC_INIT
|
||||
AC_INIT(core, 5.2.1)
|
||||
AC_INIT(core, 5.3.0)
|
||||
|
||||
# autoconf and automake initialization
|
||||
AC_CONFIG_SRCDIR([netns/version.h.in])
|
||||
|
@ -14,7 +14,7 @@ AM_INIT_AUTOMAKE([tar-ustar])
|
|||
# define variables used for packaging and date display
|
||||
PACKAGE_DATE=m4_esyscmd_s([date +%Y%m%d])
|
||||
PACKAGE_VENDOR="CORE Developers"
|
||||
PACKAGE_MAINTAINERS="$PACKAGE_VENDOR <$PACKAGE_BUGREPORT>"
|
||||
PACKAGE_MAINTAINERS="$PACKAGE_VENDOR"
|
||||
|
||||
# core specific variables
|
||||
CORE_LIB_DIR="\${prefix}/lib/core"
|
||||
|
@ -110,8 +110,8 @@ if test "x$enable_daemon" = "xyes"; then
|
|||
AC_CHECK_FUNCS([atexit dup2 gettimeofday memset socket strerror uname])
|
||||
|
||||
AM_PATH_PYTHON(2.7)
|
||||
pythondir=`echo ${pythondir} | sed s,site-packages,dist-packages,`
|
||||
AC_SUBST(pythondir,$pythondir)
|
||||
AM_CONDITIONAL([PYTHON3], [test "x$PYTHON" == "xpython3"])
|
||||
AS_IF([$PYTHON -m grpc_tools.protoc -h &> /dev/null], [], [AC_MSG_ERROR([please install python grpcio-tools])])
|
||||
|
||||
AC_CHECK_PROG(brctl_path, brctl, $as_dir, no, $SEARCHPATH)
|
||||
if test "x$brctl_path" = "xno" ; then
|
||||
|
@ -266,7 +266,7 @@ GUI:
|
|||
Daemon:
|
||||
Daemon path: ${bindir}
|
||||
Daemon config: ${CORE_CONF_DIR}
|
||||
Python modules: ${pythondir}
|
||||
Python: ${PYTHON}
|
||||
Logs: ${CORE_STATE_DIR}/log
|
||||
|
||||
Startup: ${with_startup}
|
||||
|
|
|
@ -13,8 +13,9 @@
|
|||
<maven.compiler.source>1.8</maven.compiler.source>
|
||||
<maven.compiler.target>1.8</maven.compiler.target>
|
||||
<jung.version>2.1.1</jung.version>
|
||||
<jackson.version>2.9.6</jackson.version>
|
||||
<jackson.version>2.9.9</jackson.version>
|
||||
<grpc.version>1.20.0</grpc.version>
|
||||
<log4j.version>2.9.0</log4j.version>
|
||||
</properties>
|
||||
|
||||
<dependencies>
|
||||
|
@ -58,25 +59,15 @@
|
|||
<artifactId>jackson-annotations</artifactId>
|
||||
<version>${jackson.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.squareup.okhttp3</groupId>
|
||||
<artifactId>okhttp</artifactId>
|
||||
<version>3.11.0</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.apache.logging.log4j</groupId>
|
||||
<artifactId>log4j-api</artifactId>
|
||||
<version>2.9.0</version>
|
||||
<version>${log4j.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.apache.logging.log4j</groupId>
|
||||
<artifactId>log4j-core</artifactId>
|
||||
<version>2.9.0</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>io.socket</groupId>
|
||||
<artifactId>socket.io-client</artifactId>
|
||||
<version>0.8.3</version>
|
||||
<version>${log4j.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.projectlombok</groupId>
|
||||
|
@ -84,11 +75,6 @@
|
|||
<version>1.18.0</version>
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>commons-net</groupId>
|
||||
<artifactId>commons-net</artifactId>
|
||||
<version>3.6</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.jfoenix</groupId>
|
||||
<artifactId>jfoenix</artifactId>
|
||||
|
@ -114,6 +100,11 @@
|
|||
<artifactId>guava</artifactId>
|
||||
<version>20.0</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.github.seancfoley</groupId>
|
||||
<artifactId>ipaddress</artifactId>
|
||||
<version>5.0.2</version>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
|
||||
<build>
|
||||
|
@ -153,4 +144,4 @@
|
|||
</plugin>
|
||||
</plugins>
|
||||
</build>
|
||||
</project>
|
||||
</project>
|
||||
|
|
|
@ -1,8 +1,8 @@
|
|||
package com.core.client;
|
||||
|
||||
import com.core.Controller;
|
||||
import com.core.client.rest.ServiceFile;
|
||||
import com.core.client.rest.WlanConfig;
|
||||
import com.core.data.ServiceFile;
|
||||
import com.core.data.WlanConfig;
|
||||
import com.core.data.*;
|
||||
|
||||
import java.io.File;
|
||||
|
|
|
@ -2,11 +2,10 @@ package com.core.client.grpc;
|
|||
|
||||
import com.core.Controller;
|
||||
import com.core.client.ICoreClient;
|
||||
import com.core.client.rest.ServiceFile;
|
||||
import com.core.client.rest.WlanConfig;
|
||||
import com.core.data.*;
|
||||
import com.core.ui.dialogs.MobilityPlayerDialog;
|
||||
import com.google.protobuf.ByteString;
|
||||
import inet.ipaddr.IPAddress;
|
||||
import inet.ipaddr.IPAddressString;
|
||||
import io.grpc.Context;
|
||||
import io.grpc.ManagedChannel;
|
||||
import io.grpc.ManagedChannelBuilder;
|
||||
|
@ -15,7 +14,6 @@ import org.apache.logging.log4j.LogManager;
|
|||
import org.apache.logging.log4j.Logger;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.FileInputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.PrintWriter;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
|
@ -38,8 +36,8 @@ public class CoreGrpcClient implements ICoreClient {
|
|||
|
||||
private CoreProto.Node nodeToProto(CoreNode node) {
|
||||
CoreProto.Position position = CoreProto.Position.newBuilder()
|
||||
.setX(node.getPosition().getX().floatValue())
|
||||
.setY(node.getPosition().getY().floatValue())
|
||||
.setX(node.getPosition().getX().intValue())
|
||||
.setY(node.getPosition().getY().intValue())
|
||||
.build();
|
||||
CoreProto.Node.Builder builder = CoreProto.Node.newBuilder()
|
||||
.addAllServices(node.getServices())
|
||||
|
@ -90,31 +88,31 @@ public class CoreGrpcClient implements ICoreClient {
|
|||
unidirectional = true;
|
||||
}
|
||||
if (options.getBandwidth() != null) {
|
||||
builder.setBandwidth(options.getBandwidth().floatValue());
|
||||
builder.setBandwidth(options.getBandwidth().intValue());
|
||||
}
|
||||
if (options.getBurst() != null) {
|
||||
builder.setBurst(options.getBurst().floatValue());
|
||||
builder.setBurst(options.getBurst().intValue());
|
||||
}
|
||||
if (options.getDelay() != null) {
|
||||
builder.setDelay(options.getDelay().floatValue());
|
||||
builder.setDelay(options.getDelay().intValue());
|
||||
}
|
||||
if (options.getDup() != null) {
|
||||
builder.setDup(options.getDup().floatValue());
|
||||
builder.setDup(options.getDup().intValue());
|
||||
}
|
||||
if (options.getJitter() != null) {
|
||||
builder.setJitter(options.getJitter().floatValue());
|
||||
builder.setJitter(options.getJitter().intValue());
|
||||
}
|
||||
if (options.getMburst() != null) {
|
||||
builder.setMburst(options.getMburst().floatValue());
|
||||
builder.setMburst(options.getMburst().intValue());
|
||||
}
|
||||
if (options.getMer() != null) {
|
||||
builder.setMer(options.getMer().floatValue());
|
||||
builder.setMer(options.getMer().intValue());
|
||||
}
|
||||
if (options.getPer() != null) {
|
||||
builder.setPer(options.getPer().floatValue());
|
||||
builder.setPer(options.getPer().intValue());
|
||||
}
|
||||
if (options.getKey() != null) {
|
||||
builder.setKey(options.getKey().toString());
|
||||
builder.setKey(options.getKey());
|
||||
}
|
||||
if (options.getOpaque() != null) {
|
||||
builder.setOpaque(options.getOpaque());
|
||||
|
@ -125,6 +123,9 @@ public class CoreGrpcClient implements ICoreClient {
|
|||
|
||||
private CoreProto.Interface interfaceToProto(CoreInterface coreInterface) {
|
||||
CoreProto.Interface.Builder builder = CoreProto.Interface.newBuilder();
|
||||
if (coreInterface.getId() != null) {
|
||||
builder.setId(coreInterface.getId());
|
||||
}
|
||||
if (coreInterface.getName() != null) {
|
||||
builder.setName(coreInterface.getName());
|
||||
}
|
||||
|
@ -132,16 +133,16 @@ public class CoreGrpcClient implements ICoreClient {
|
|||
builder.setMac(coreInterface.getMac());
|
||||
}
|
||||
if (coreInterface.getIp4() != null) {
|
||||
builder.setIp4(coreInterface.getIp4());
|
||||
builder.setIp4(coreInterface.getIp4().toAddressString().getHostAddress().toString());
|
||||
}
|
||||
if (coreInterface.getIp4Mask() != null) {
|
||||
builder.setIp4Mask(coreInterface.getIp4Mask());
|
||||
if (coreInterface.getIp4() != null) {
|
||||
builder.setIp4Mask(coreInterface.getIp4().getPrefixLength());
|
||||
}
|
||||
if (coreInterface.getIp6() != null) {
|
||||
builder.setIp6(coreInterface.getIp6());
|
||||
builder.setIp6(coreInterface.getIp6().toAddressString().getHostAddress().toString());
|
||||
}
|
||||
if (coreInterface.getIp6Mask() != null) {
|
||||
builder.setIp6Mask(Integer.parseInt(coreInterface.getIp6Mask()));
|
||||
if (coreInterface.getIp6() != null) {
|
||||
builder.setIp6Mask(coreInterface.getIp6().getPrefixLength());
|
||||
}
|
||||
return builder.build();
|
||||
}
|
||||
|
@ -172,10 +173,12 @@ public class CoreGrpcClient implements ICoreClient {
|
|||
coreInterface.setId(protoInterface.getId());
|
||||
coreInterface.setName(protoInterface.getName());
|
||||
coreInterface.setMac(protoInterface.getMac());
|
||||
coreInterface.setIp4(protoInterface.getIp4());
|
||||
coreInterface.setIp4Mask(protoInterface.getIp4Mask());
|
||||
coreInterface.setIp6(protoInterface.getIp6());
|
||||
coreInterface.setIp6Mask(Integer.toString(protoInterface.getIp6Mask()));
|
||||
String ip4String = String.format("%s/%s", protoInterface.getIp4(), protoInterface.getIp4Mask());
|
||||
IPAddress ip4 = new IPAddressString(ip4String).getAddress();
|
||||
coreInterface.setIp4(ip4);
|
||||
String ip6String = String.format("%s/%s", protoInterface.getIp6(), protoInterface.getIp6Mask());
|
||||
IPAddress ip6 = new IPAddressString(ip6String).getAddress();
|
||||
coreInterface.setIp6(ip6);
|
||||
return coreInterface;
|
||||
}
|
||||
|
||||
|
@ -196,8 +199,8 @@ public class CoreGrpcClient implements ICoreClient {
|
|||
options.setJitter((double) protoOptions.getJitter());
|
||||
options.setPer((double) protoOptions.getPer());
|
||||
options.setBurst((double) protoOptions.getBurst());
|
||||
if (!protoOptions.getKey().isEmpty()) {
|
||||
options.setKey(Integer.parseInt(protoOptions.getKey()));
|
||||
if (protoOptions.hasField(CoreProto.LinkOptions.getDescriptor().findFieldByName("key"))) {
|
||||
options.setKey(protoOptions.getKey());
|
||||
}
|
||||
options.setMburst((double) protoOptions.getMburst());
|
||||
options.setMer((double) protoOptions.getMer());
|
||||
|
@ -784,8 +787,8 @@ public class CoreGrpcClient implements ICoreClient {
|
|||
@Override
|
||||
public boolean editNode(CoreNode node) throws IOException {
|
||||
CoreProto.Position position = CoreProto.Position.newBuilder()
|
||||
.setX(node.getPosition().getX().floatValue())
|
||||
.setY(node.getPosition().getY().floatValue())
|
||||
.setX(node.getPosition().getX().intValue())
|
||||
.setY(node.getPosition().getY().intValue())
|
||||
.build();
|
||||
CoreProto.EditNodeRequest request = CoreProto.EditNodeRequest.newBuilder()
|
||||
.setSessionId(sessionId)
|
||||
|
@ -1104,13 +1107,13 @@ public class CoreGrpcClient implements ICoreClient {
|
|||
}
|
||||
CoreProto.Position.Builder positionBuilder = CoreProto.Position.newBuilder();
|
||||
if (config.getPosition().getX() != null) {
|
||||
positionBuilder.setX(config.getPosition().getX().floatValue());
|
||||
positionBuilder.setX(config.getPosition().getX().intValue());
|
||||
}
|
||||
if (config.getPosition().getY() != null) {
|
||||
positionBuilder.setY(config.getPosition().getY().floatValue());
|
||||
positionBuilder.setY(config.getPosition().getY().intValue());
|
||||
}
|
||||
if (config.getPosition().getZ() != null) {
|
||||
positionBuilder.setZ(config.getPosition().getZ().floatValue());
|
||||
positionBuilder.setZ(config.getPosition().getZ().intValue());
|
||||
}
|
||||
if (config.getLocation().getLongitude() != null) {
|
||||
positionBuilder.setLon(config.getLocation().getLongitude().floatValue());
|
||||
|
|
|
@ -1,430 +0,0 @@
|
|||
package com.core.client.rest;
|
||||
|
||||
import com.core.Controller;
|
||||
import com.core.client.ICoreClient;
|
||||
import com.core.data.*;
|
||||
import com.core.utils.WebUtils;
|
||||
import com.core.websocket.CoreWebSocket;
|
||||
import lombok.Data;
|
||||
import org.apache.logging.log4j.LogManager;
|
||||
import org.apache.logging.log4j.Logger;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.net.URISyntaxException;
|
||||
import java.util.*;
|
||||
|
||||
@Data
|
||||
public class CoreRestClient implements ICoreClient {
|
||||
private static final Logger logger = LogManager.getLogger();
|
||||
private String address;
|
||||
private int port;
|
||||
private Integer sessionId;
|
||||
private SessionState sessionState;
|
||||
private CoreWebSocket coreWebSocket;
|
||||
|
||||
@Override
|
||||
public void setConnection(String address, int port) {
|
||||
this.address = address;
|
||||
this.port = port;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isLocalConnection() {
|
||||
return address.equals("127.0.0.1") || address.equals("localhost");
|
||||
}
|
||||
|
||||
@Override
|
||||
public Integer currentSession() {
|
||||
return sessionId;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void updateState(SessionState state) {
|
||||
sessionState = state;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void updateSession(Integer sessionId) {
|
||||
this.sessionId = sessionId;
|
||||
}
|
||||
|
||||
private String getUrl(String path) {
|
||||
return String.format("http://%s:%s/%s", address, port, path);
|
||||
}
|
||||
|
||||
@Override
|
||||
public SessionOverview createSession() throws IOException {
|
||||
String url = getUrl("sessions");
|
||||
return WebUtils.post(url, SessionOverview.class);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean deleteSession(Integer sessionId) throws IOException {
|
||||
String path = String.format("sessions/%s", sessionId);
|
||||
String url = getUrl(path);
|
||||
return WebUtils.delete(url);
|
||||
}
|
||||
|
||||
public Map<String, List<String>> getServices() throws IOException {
|
||||
String url = getUrl("services");
|
||||
GetServices getServices = WebUtils.getJson(url, GetServices.class);
|
||||
return getServices.getGroups();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Session getSession(Integer sessionId) throws IOException {
|
||||
String path = String.format("sessions/%s", sessionId);
|
||||
String url = getUrl(path);
|
||||
return WebUtils.getJson(url, Session.class);
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<SessionOverview> getSessions() throws IOException {
|
||||
String url = getUrl("sessions");
|
||||
GetSessions getSessions = WebUtils.getJson(url, GetSessions.class);
|
||||
return getSessions.getSessions();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean start(Collection<CoreNode> nodes, Collection<CoreLink> links, List<Hook> hooks) throws IOException {
|
||||
boolean result = setState(SessionState.DEFINITION);
|
||||
if (!result) {
|
||||
return false;
|
||||
}
|
||||
|
||||
result = setState(SessionState.CONFIGURATION);
|
||||
if (!result) {
|
||||
return false;
|
||||
}
|
||||
|
||||
for (Hook hook : hooks) {
|
||||
if (!createHook(hook)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
for (CoreNode node : nodes) {
|
||||
// must pre-configure wlan nodes, if not already
|
||||
if (node.getNodeType().getValue() == NodeType.WLAN) {
|
||||
WlanConfig config = getWlanConfig(node);
|
||||
setWlanConfig(node, config);
|
||||
}
|
||||
|
||||
if (!createNode(node)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
for (CoreLink link : links) {
|
||||
if (!createLink(link)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return setState(SessionState.INSTANTIATION);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean stop() throws IOException {
|
||||
return setState(SessionState.SHUTDOWN);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean setState(SessionState state) throws IOException {
|
||||
String url = getUrl(String.format("sessions/%s/state", sessionId));
|
||||
Map<String, Integer> data = new HashMap<>();
|
||||
data.put("state", state.getValue());
|
||||
boolean result = WebUtils.putJson(url, data);
|
||||
|
||||
if (result) {
|
||||
sessionState = state;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
private boolean uploadFile(File file) throws IOException {
|
||||
String url = getUrl("upload");
|
||||
return WebUtils.postFile(url, file);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean startThroughput(Controller controller) throws IOException {
|
||||
String url = getUrl("throughput/start");
|
||||
return WebUtils.putJson(url);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean stopThroughput() throws IOException {
|
||||
String url = getUrl("throughput/stop");
|
||||
return WebUtils.putJson(url);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Map<String, List<String>> getDefaultServices() throws IOException {
|
||||
String url = getUrl(String.format("sessions/%s/services/default", sessionId));
|
||||
GetDefaultServices getDefaultServices = WebUtils.getJson(url, GetDefaultServices.class);
|
||||
return getDefaultServices.getDefaults();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean setDefaultServices(Map<String, Set<String>> defaults) throws IOException {
|
||||
String url = getUrl(String.format("sessions/%s/services/default", sessionId));
|
||||
return WebUtils.postJson(url, defaults);
|
||||
}
|
||||
|
||||
@Override
|
||||
public CoreService getService(CoreNode node, String serviceName) throws IOException {
|
||||
String url = getUrl(String.format("sessions/%s/nodes/%s/services/%s", sessionId, node.getId(), serviceName));
|
||||
return WebUtils.getJson(url, CoreService.class);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean setService(CoreNode node, String serviceName, CoreService service) throws IOException {
|
||||
String url = getUrl(String.format("sessions/%s/nodes/%s/services/%s", sessionId, node.getId(), serviceName));
|
||||
return WebUtils.putJson(url, service);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getServiceFile(CoreNode node, String serviceName, String fileName) throws IOException {
|
||||
String url = getUrl(String.format("sessions/%s/nodes/%s/services/%s/file", sessionId, node.getId(),
|
||||
serviceName));
|
||||
Map<String, String> args = new HashMap<>();
|
||||
args.put("file", fileName);
|
||||
return WebUtils.getJson(url, String.class, args);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean startService(CoreNode node, String serviceName) throws IOException {
|
||||
String url = getUrl(String.format("sessions/%s/nodes/%s/services/%s/start", sessionId, node.getId(),
|
||||
serviceName));
|
||||
return WebUtils.putJson(url);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean stopService(CoreNode node, String serviceName) throws IOException {
|
||||
String url = getUrl(String.format("sessions/%s/nodes/%s/services/%s/stop", sessionId, node.getId(),
|
||||
serviceName));
|
||||
return WebUtils.putJson(url);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean restartService(CoreNode node, String serviceName) throws IOException {
|
||||
String url = getUrl(String.format("sessions/%s/nodes/%s/services/%s/restart", sessionId, node.getId(),
|
||||
serviceName));
|
||||
return WebUtils.putJson(url);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean validateService(CoreNode node, String serviceName) throws IOException {
|
||||
String url = getUrl(String.format("sessions/%s/nodes/%s/services/%s/validate", sessionId, node.getId(),
|
||||
serviceName));
|
||||
return WebUtils.putJson(url);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean setServiceFile(CoreNode node, String serviceName, ServiceFile serviceFile) throws IOException {
|
||||
String url = getUrl(String.format("sessions/%s/nodes/%s/services/%s/file", sessionId, node.getId(),
|
||||
serviceName));
|
||||
return WebUtils.putJson(url, serviceFile);
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<String> getEmaneModels() throws IOException {
|
||||
String url = getUrl(String.format("sessions/%s/emane/models", sessionId));
|
||||
GetEmaneModels getEmaneModels = WebUtils.getJson(url, GetEmaneModels.class);
|
||||
return getEmaneModels.getModels();
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<ConfigGroup> getEmaneModelConfig(Integer id, String model) throws IOException {
|
||||
String url = getUrl(String.format("sessions/%s/emane/model/config", sessionId));
|
||||
Map<String, String> args = new HashMap<>();
|
||||
args.put("node", id.toString());
|
||||
args.put("name", model);
|
||||
GetConfig getConfig = WebUtils.getJson(url, GetConfig.class, args);
|
||||
return getConfig.getGroups();
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<ConfigGroup> getEmaneConfig(CoreNode node) throws IOException {
|
||||
String url = getUrl(String.format("sessions/%s/emane/config", sessionId));
|
||||
Map<String, String> args = new HashMap<>();
|
||||
args.put("node", node.getId().toString());
|
||||
GetConfig getConfig = WebUtils.getJson(url, GetConfig.class, args);
|
||||
return getConfig.getGroups();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean setEmaneConfig(CoreNode node, List<ConfigOption> options) throws IOException {
|
||||
String url = getUrl(String.format("sessions/%s/emane/config", sessionId));
|
||||
SetEmaneConfig setEmaneConfig = new SetEmaneConfig();
|
||||
setEmaneConfig.setNode(node.getId());
|
||||
setEmaneConfig.setValues(options);
|
||||
return WebUtils.putJson(url, setEmaneConfig);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean setEmaneModelConfig(Integer id, String model, List<ConfigOption> options) throws IOException {
|
||||
String url = getUrl(String.format("sessions/%s/emane/model/config", sessionId));
|
||||
SetEmaneModelConfig setEmaneModelConfig = new SetEmaneModelConfig();
|
||||
setEmaneModelConfig.setNode(id);
|
||||
setEmaneModelConfig.setName(model);
|
||||
setEmaneModelConfig.setValues(options);
|
||||
return WebUtils.putJson(url, setEmaneModelConfig);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isRunning() {
|
||||
return sessionState == SessionState.RUNTIME;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void saveSession(File file) throws IOException {
|
||||
String path = String.format("sessions/%s/xml", sessionId);
|
||||
String url = getUrl(path);
|
||||
WebUtils.getFile(url, file);
|
||||
}
|
||||
|
||||
@Override
|
||||
public SessionOverview openSession(File file) throws IOException {
|
||||
String url = getUrl("sessions/xml");
|
||||
return WebUtils.postFile(url, file, SessionOverview.class);
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<ConfigGroup> getSessionConfig() throws IOException {
|
||||
String url = getUrl(String.format("sessions/%s/options", sessionId));
|
||||
GetConfig getConfig = WebUtils.getJson(url, GetConfig.class);
|
||||
return getConfig.getGroups();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean setSessionConfig(List<ConfigOption> configOptions) throws IOException {
|
||||
String url = getUrl(String.format("sessions/%s/options", sessionId));
|
||||
SetConfig setConfig = new SetConfig(configOptions);
|
||||
return WebUtils.putJson(url, setConfig);
|
||||
}
|
||||
|
||||
@Override
|
||||
public LocationConfig getLocationConfig() throws IOException {
|
||||
String url = getUrl(String.format("sessions/%s/location", sessionId));
|
||||
return WebUtils.getJson(url, LocationConfig.class);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean setLocationConfig(LocationConfig config) throws IOException {
|
||||
String url = getUrl(String.format("sessions/%s/location", sessionId));
|
||||
return WebUtils.putJson(url, config);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String nodeCommand(CoreNode node, String command) throws IOException {
|
||||
String url = getUrl(String.format("sessions/%s/nodes/%s/command", sessionId, node.getId()));
|
||||
return WebUtils.putJson(url, command, String.class);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean createNode(CoreNode node) throws IOException {
|
||||
String url = getUrl(String.format("sessions/%s/nodes", sessionId));
|
||||
return WebUtils.postJson(url, node);
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public boolean editNode(CoreNode node) throws IOException {
|
||||
String url = getUrl(String.format("sessions/%s/nodes/%s", sessionId, node.getId()));
|
||||
return WebUtils.putJson(url, node);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean deleteNode(CoreNode node) throws IOException {
|
||||
String url = getUrl(String.format("/sessions/%s/nodes/%s", sessionId, node.getId()));
|
||||
return WebUtils.delete(url);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean createLink(CoreLink link) throws IOException {
|
||||
String url = getUrl(String.format("sessions/%s/links", sessionId));
|
||||
return WebUtils.postJson(url, link);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean editLink(CoreLink link) throws IOException {
|
||||
String url = getUrl(String.format("sessions/%s/links", sessionId));
|
||||
return WebUtils.putJson(url, link);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean createHook(Hook hook) throws IOException {
|
||||
String url = getUrl(String.format("sessions/%s/hooks", sessionId));
|
||||
return WebUtils.postJson(url, hook);
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<Hook> getHooks() throws IOException {
|
||||
String url = getUrl(String.format("sessions/%s/hooks", sessionId));
|
||||
GetHooks getHooks = WebUtils.getJson(url, GetHooks.class);
|
||||
return getHooks.getHooks();
|
||||
}
|
||||
|
||||
@Override
|
||||
public WlanConfig getWlanConfig(CoreNode node) throws IOException {
|
||||
String url = getUrl(String.format("sessions/%s/nodes/%s/wlan", sessionId, node.getId()));
|
||||
return WebUtils.getJson(url, WlanConfig.class);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean setWlanConfig(CoreNode node, WlanConfig config) throws IOException {
|
||||
String url = getUrl(String.format("sessions/%s/nodes/%s/wlan", sessionId, node.getId()));
|
||||
return WebUtils.putJson(url, config);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getTerminalCommand(CoreNode node) throws IOException {
|
||||
String url = getUrl(String.format("sessions/%s/nodes/%s/terminal", sessionId, node.getId()));
|
||||
return WebUtils.getJson(url, String.class);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean setMobilityConfig(CoreNode node, MobilityConfig config) throws IOException {
|
||||
boolean uploaded = uploadFile(config.getScriptFile());
|
||||
if (!uploaded) {
|
||||
throw new IOException("failed to upload mobility script");
|
||||
}
|
||||
|
||||
String url = getUrl(String.format("sessions/%s/nodes/%s/mobility", sessionId, node.getId()));
|
||||
config.setFile(config.getScriptFile().getName());
|
||||
return WebUtils.postJson(url, config);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Map<Integer, MobilityConfig> getMobilityConfigs() throws IOException {
|
||||
String url = getUrl(String.format("sessions/%s/mobility/configs", sessionId));
|
||||
GetMobilityConfigs getMobilityConfigs = WebUtils.getJson(url, GetMobilityConfigs.class);
|
||||
return getMobilityConfigs.getConfigurations();
|
||||
}
|
||||
|
||||
@Override
|
||||
public MobilityConfig getMobilityConfig(CoreNode node) throws IOException {
|
||||
String url = getUrl(String.format("sessions/%s/nodes/%s/mobility", sessionId, node.getId()));
|
||||
return WebUtils.getJson(url, MobilityConfig.class);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean mobilityAction(CoreNode node, String action) throws IOException {
|
||||
String url = getUrl(String.format("sessions/%s/nodes/%s/mobility/%s", sessionId, node.getId(), action));
|
||||
return WebUtils.putJson(url);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setupEventHandlers(Controller controller) throws IOException {
|
||||
coreWebSocket.stop();
|
||||
coreWebSocket = new CoreWebSocket(controller);
|
||||
try {
|
||||
coreWebSocket.start(address, port);
|
||||
} catch (URISyntaxException ex) {
|
||||
throw new IOException("error starting web socket", ex);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,12 +0,0 @@
|
|||
package com.core.client.rest;
|
||||
|
||||
import com.core.data.ConfigGroup;
|
||||
import lombok.Data;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
@Data
|
||||
public class GetConfig {
|
||||
private List<ConfigGroup> groups = new ArrayList<>();
|
||||
}
|
|
@ -1,16 +0,0 @@
|
|||
package com.core.client.rest;
|
||||
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Data;
|
||||
import lombok.NoArgsConstructor;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
@Data
|
||||
@AllArgsConstructor
|
||||
@NoArgsConstructor
|
||||
public class GetDefaultServices {
|
||||
private Map<String, List<String>> defaults = new HashMap<>();
|
||||
}
|
|
@ -1,11 +0,0 @@
|
|||
package com.core.client.rest;
|
||||
|
||||
import lombok.Data;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
@Data
|
||||
public class GetEmaneModels {
|
||||
private List<String> models = new ArrayList<>();
|
||||
}
|
|
@ -1,12 +0,0 @@
|
|||
package com.core.client.rest;
|
||||
|
||||
import com.core.data.Hook;
|
||||
import lombok.Data;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
@Data
|
||||
public class GetHooks {
|
||||
private List<Hook> hooks = new ArrayList<>();
|
||||
}
|
|
@ -1,12 +0,0 @@
|
|||
package com.core.client.rest;
|
||||
|
||||
import com.core.data.MobilityConfig;
|
||||
import lombok.Data;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
@Data
|
||||
public class GetMobilityConfigs {
|
||||
private Map<Integer, MobilityConfig> configurations = new HashMap<>();
|
||||
}
|
|
@ -1,13 +0,0 @@
|
|||
package com.core.client.rest;
|
||||
|
||||
import lombok.Data;
|
||||
import lombok.NoArgsConstructor;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
@Data
|
||||
@NoArgsConstructor
|
||||
public class GetServices {
|
||||
private Map<String, List<String>> groups;
|
||||
}
|
|
@ -1,14 +0,0 @@
|
|||
package com.core.client.rest;
|
||||
|
||||
import com.core.data.SessionOverview;
|
||||
import lombok.Data;
|
||||
import lombok.NoArgsConstructor;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
@Data
|
||||
@NoArgsConstructor
|
||||
public class GetSessions {
|
||||
private List<SessionOverview> sessions = new ArrayList<>();
|
||||
}
|
|
@ -1,16 +0,0 @@
|
|||
package com.core.client.rest;
|
||||
|
||||
import com.core.data.ConfigOption;
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Data;
|
||||
import lombok.NoArgsConstructor;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
@Data
|
||||
@AllArgsConstructor
|
||||
@NoArgsConstructor
|
||||
public class SetConfig {
|
||||
private List<ConfigOption> values = new ArrayList<>();
|
||||
}
|
|
@ -1,13 +0,0 @@
|
|||
package com.core.client.rest;
|
||||
|
||||
import com.core.data.ConfigOption;
|
||||
import lombok.Data;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
@Data
|
||||
public class SetEmaneConfig {
|
||||
private Integer node;
|
||||
private List<ConfigOption> values = new ArrayList<>();
|
||||
}
|
|
@ -1,14 +0,0 @@
|
|||
package com.core.client.rest;
|
||||
|
||||
import com.core.data.ConfigOption;
|
||||
import lombok.Data;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
@Data
|
||||
public class SetEmaneModelConfig {
|
||||
private Integer node;
|
||||
private String name;
|
||||
private List<ConfigOption> values = new ArrayList<>();
|
||||
}
|
|
@ -1,6 +1,5 @@
|
|||
package com.core.data;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonSetter;
|
||||
import lombok.Data;
|
||||
|
||||
@Data
|
||||
|
@ -11,9 +10,4 @@ public class CoreEvent {
|
|||
private Double time;
|
||||
private EventType eventType;
|
||||
private String data;
|
||||
|
||||
@JsonSetter("event_type")
|
||||
public void setEventType(int value) {
|
||||
eventType = EventType.get(value);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
package com.core.data;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonProperty;
|
||||
import inet.ipaddr.IPAddress;
|
||||
import lombok.Data;
|
||||
import lombok.NoArgsConstructor;
|
||||
|
||||
|
@ -10,10 +10,6 @@ public class CoreInterface {
|
|||
private Integer id;
|
||||
private String name;
|
||||
private String mac;
|
||||
private String ip4;
|
||||
@JsonProperty("ip4mask")
|
||||
private Integer ip4Mask;
|
||||
private String ip6;
|
||||
@JsonProperty("ip6mask")
|
||||
private String ip6Mask;
|
||||
private IPAddress ip4;
|
||||
private IPAddress ip6;
|
||||
}
|
||||
|
|
|
@ -1,7 +1,5 @@
|
|||
package com.core.data;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonIgnore;
|
||||
import com.fasterxml.jackson.annotation.JsonProperty;
|
||||
import lombok.Data;
|
||||
import lombok.EqualsAndHashCode;
|
||||
import lombok.NoArgsConstructor;
|
||||
|
@ -12,36 +10,16 @@ import lombok.NoArgsConstructor;
|
|||
public class CoreLink {
|
||||
@EqualsAndHashCode.Include
|
||||
private Integer id;
|
||||
|
||||
@JsonIgnore
|
||||
private Float weight = 1.0f;
|
||||
|
||||
@JsonIgnore
|
||||
private boolean loaded = true;
|
||||
|
||||
@JsonIgnore
|
||||
private double throughput;
|
||||
|
||||
@JsonIgnore
|
||||
private boolean visible = true;
|
||||
|
||||
@JsonProperty("message_type")
|
||||
private Integer messageType;
|
||||
|
||||
private Integer type = 1;
|
||||
|
||||
@JsonProperty("node_one")
|
||||
private Integer nodeOne;
|
||||
|
||||
@JsonProperty("node_two")
|
||||
private Integer nodeTwo;
|
||||
|
||||
@JsonProperty("interface_one")
|
||||
private CoreInterface interfaceOne;
|
||||
|
||||
@JsonProperty("interface_two")
|
||||
private CoreInterface interfaceTwo;
|
||||
|
||||
private CoreLinkOptions options = new CoreLinkOptions();
|
||||
|
||||
public CoreLink(Integer id) {
|
||||
|
|
|
@ -2,7 +2,6 @@ package com.core.data;
|
|||
|
||||
import com.core.graph.RadioIcon;
|
||||
import com.core.utils.IconUtils;
|
||||
import com.fasterxml.jackson.annotation.JsonIgnore;
|
||||
import edu.uci.ics.jung.visualization.LayeredIcon;
|
||||
import lombok.Data;
|
||||
import lombok.EqualsAndHashCode;
|
||||
|
@ -27,15 +26,10 @@ public class CoreNode {
|
|||
private Set<String> services = new HashSet<>();
|
||||
private String emane;
|
||||
private String url;
|
||||
@JsonIgnore
|
||||
private NodeType nodeType;
|
||||
@JsonIgnore
|
||||
private String icon;
|
||||
@JsonIgnore
|
||||
private boolean loaded = true;
|
||||
@JsonIgnore
|
||||
private LayeredIcon graphIcon;
|
||||
@JsonIgnore
|
||||
private RadioIcon radioIcon = new RadioIcon();
|
||||
|
||||
public CoreNode(Integer id) {
|
||||
|
|
|
@ -1,6 +1,5 @@
|
|||
package com.core.data;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonProperty;
|
||||
import lombok.Data;
|
||||
|
||||
import java.util.ArrayList;
|
||||
|
@ -14,9 +13,7 @@ public class CoreService {
|
|||
private List<String> configs = new ArrayList<>();
|
||||
private List<String> startup = new ArrayList<>();
|
||||
private List<String> validate = new ArrayList<>();
|
||||
@JsonProperty("validation_mode")
|
||||
private String validationMode;
|
||||
@JsonProperty("validation_timer")
|
||||
private String validationTimer;
|
||||
private List<String> shutdown = new ArrayList<>();
|
||||
private String meta;
|
||||
|
|
|
@ -1,13 +1,11 @@
|
|||
package com.core.data;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonIgnore;
|
||||
import lombok.Data;
|
||||
|
||||
@Data
|
||||
public class Hook {
|
||||
private String file;
|
||||
private Integer state;
|
||||
@JsonIgnore
|
||||
private String stateDisplay;
|
||||
private String data;
|
||||
}
|
||||
|
|
|
@ -1,12 +1,10 @@
|
|||
package com.core.data;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonProperty;
|
||||
import lombok.Data;
|
||||
|
||||
@Data
|
||||
public class InterfaceThroughput {
|
||||
private int node;
|
||||
@JsonProperty("interface")
|
||||
private int nodeInterface;
|
||||
private double throughput;
|
||||
}
|
||||
|
|
|
@ -1,7 +1,5 @@
|
|||
package com.core.data;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonIgnore;
|
||||
import com.fasterxml.jackson.annotation.JsonProperty;
|
||||
import lombok.Data;
|
||||
|
||||
import java.io.File;
|
||||
|
@ -9,17 +7,12 @@ import java.io.File;
|
|||
@Data
|
||||
public class MobilityConfig {
|
||||
private String file;
|
||||
@JsonIgnore
|
||||
private File scriptFile;
|
||||
@JsonProperty("refresh_ms")
|
||||
private Integer refresh;
|
||||
private String loop;
|
||||
private String autostart;
|
||||
private String map;
|
||||
@JsonProperty("script_start")
|
||||
private String startScript;
|
||||
@JsonProperty("script_pause")
|
||||
private String pauseScript;
|
||||
@JsonProperty("script_stop")
|
||||
private String stopScript;
|
||||
}
|
||||
|
|
|
@ -73,11 +73,9 @@ public class NodeType {
|
|||
return ID_LOOKUP.values().stream()
|
||||
.filter(nodeType -> {
|
||||
boolean sameType = nodeType.getValue() == type;
|
||||
boolean sameModel;
|
||||
if (model != null) {
|
||||
boolean sameModel = true;
|
||||
if (!model.isEmpty()) {
|
||||
sameModel = model.equals(nodeType.getModel());
|
||||
} else {
|
||||
sameModel = nodeType.getModel() == null;
|
||||
}
|
||||
return sameType && sameModel;
|
||||
})
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
package com.core.client.rest;
|
||||
package com.core.data;
|
||||
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Data;
|
|
@ -1,4 +1,4 @@
|
|||
package com.core.client.rest;
|
||||
package com.core.data;
|
||||
|
||||
import lombok.Data;
|
||||
|
|
@ -1,41 +1,79 @@
|
|||
package com.core.graph;
|
||||
|
||||
import com.core.data.CoreInterface;
|
||||
import inet.ipaddr.IPAddress;
|
||||
import inet.ipaddr.IPAddressString;
|
||||
import org.apache.logging.log4j.LogManager;
|
||||
import org.apache.logging.log4j.Logger;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.beans.IndexedPropertyDescriptor;
|
||||
import java.util.Comparator;
|
||||
import java.util.HashSet;
|
||||
import java.util.Queue;
|
||||
import java.util.Set;
|
||||
import java.util.concurrent.ArrayBlockingQueue;
|
||||
import java.util.concurrent.LinkedBlockingQueue;
|
||||
import java.util.concurrent.atomic.AtomicBoolean;
|
||||
|
||||
public class CoreAddresses {
|
||||
private static final Logger logger = LogManager.getLogger();
|
||||
public static final int IP4_MASK = 24;
|
||||
public static final int IP4_INDEX = (IP4_MASK / 8) - 1;
|
||||
private IPAddress currentSubnet = new IPAddressString("10.0.0.0/24").getAddress().toPrefixBlock();
|
||||
private Queue<IPAddress> deleted = new LinkedBlockingQueue<>();
|
||||
private Set<IPAddress> usedSubnets = new HashSet<>();
|
||||
|
||||
private String ip4Base;
|
||||
|
||||
public CoreAddresses(String ip4Base) {
|
||||
this.ip4Base = ip4Base;
|
||||
public void usedAddress(IPAddress address) {
|
||||
logger.info("adding used address: {} - {}", address, address.toPrefixBlock());
|
||||
usedSubnets.add(address.toPrefixBlock());
|
||||
logger.info("used subnets: {}", usedSubnets);
|
||||
}
|
||||
|
||||
public int getSubnet(Collection<CoreInterface> nodeOneInterfaces, Collection<CoreInterface> nodeTwoInterfaces) {
|
||||
int subOne = getMaxSubnet(nodeOneInterfaces);
|
||||
int subTwo = getMaxSubnet(nodeTwoInterfaces);
|
||||
logger.info("next subnet: {} - {}", subOne, subTwo);
|
||||
return Math.max(subOne, subTwo) + 1;
|
||||
public void reuseSubnet(IPAddress subnet) {
|
||||
deleted.add(subnet);
|
||||
}
|
||||
|
||||
private int getMaxSubnet(Collection<CoreInterface> coreInterfaces) {
|
||||
int sub = 0;
|
||||
for (CoreInterface coreInterface : coreInterfaces) {
|
||||
String[] values = coreInterface.getIp4().split("\\.");
|
||||
int currentSub = Integer.parseInt(values[IP4_INDEX]);
|
||||
logger.info("checking {} value {}", coreInterface.getIp4(), currentSub);
|
||||
sub = Math.max(currentSub, sub);
|
||||
public IPAddress nextSubnet() {
|
||||
logger.info("getting next subnet: {}", currentSubnet);
|
||||
// skip existing subnets, when loaded from file
|
||||
while (usedSubnets.contains(currentSubnet)) {
|
||||
currentSubnet = currentSubnet.incrementBoundary(1).toPrefixBlock();
|
||||
}
|
||||
return sub;
|
||||
|
||||
// re-use any deleted subnets
|
||||
IPAddress next = deleted.poll();
|
||||
if (next == null) {
|
||||
next = currentSubnet;
|
||||
currentSubnet = currentSubnet.incrementBoundary(1).toPrefixBlock();
|
||||
}
|
||||
return next;
|
||||
}
|
||||
|
||||
public String getIp4Address(int sub, int id) {
|
||||
return String.format("%s.%s.%s", ip4Base, sub, id);
|
||||
public IPAddress findSubnet(Set<CoreInterface> interfaces) {
|
||||
IPAddress subnet;
|
||||
logger.info("finding subnet from interfaces: {}", interfaces);
|
||||
if (interfaces.isEmpty()) {
|
||||
subnet = nextSubnet();
|
||||
} else {
|
||||
IPAddress maxAddress = getMaxAddress(interfaces);
|
||||
subnet = maxAddress.toPrefixBlock();
|
||||
}
|
||||
return subnet;
|
||||
}
|
||||
|
||||
private IPAddress getMaxAddress(Set<CoreInterface> interfaces) {
|
||||
return interfaces.stream()
|
||||
.map(CoreInterface::getIp4)
|
||||
.max(Comparator.comparingInt(x -> x.toIPv4().intValue()))
|
||||
.orElseGet(() -> currentSubnet);
|
||||
}
|
||||
|
||||
public void reset() {
|
||||
deleted.clear();
|
||||
usedSubnets.clear();
|
||||
currentSubnet = new IPAddressString("10.0.0.0/24").getAddress().toPrefixBlock();
|
||||
}
|
||||
|
||||
public static void main(String... args) {
|
||||
IPAddress addresses = new IPAddressString("10.0.0.0/16").getAddress();
|
||||
System.out.println(String.format("address: %s", addresses.increment(257)));
|
||||
}
|
||||
}
|
||||
|
|
|
@ -20,9 +20,9 @@ import edu.uci.ics.jung.visualization.control.GraphMouseListener;
|
|||
import edu.uci.ics.jung.visualization.control.ModalGraphMouse;
|
||||
import edu.uci.ics.jung.visualization.decorators.EdgeShape;
|
||||
import edu.uci.ics.jung.visualization.renderers.Renderer;
|
||||
import inet.ipaddr.IPAddress;
|
||||
import javafx.application.Platform;
|
||||
import lombok.Data;
|
||||
import org.apache.commons.net.util.SubnetUtils;
|
||||
import org.apache.logging.log4j.LogManager;
|
||||
import org.apache.logging.log4j.Logger;
|
||||
|
||||
|
@ -32,7 +32,6 @@ import java.awt.event.MouseEvent;
|
|||
import java.awt.geom.Ellipse2D;
|
||||
import java.io.IOException;
|
||||
import java.util.*;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.stream.Collectors;
|
||||
|
@ -50,8 +49,7 @@ public class NetworkGraph {
|
|||
private EditingModalGraphMouse<CoreNode, CoreLink> graphMouse;
|
||||
private AnnotationControls<CoreNode, CoreLink> annotationControls;
|
||||
|
||||
private SubnetUtils subnetUtils = new SubnetUtils("10.0.0.0/24");
|
||||
private CoreAddresses coreAddresses = new CoreAddresses("10.0");
|
||||
private CoreAddresses coreAddresses = new CoreAddresses();
|
||||
private NodeType nodeType;
|
||||
private Map<Integer, CoreNode> nodeMap = new ConcurrentHashMap<>();
|
||||
private int vertexId = 1;
|
||||
|
@ -77,9 +75,8 @@ public class NetworkGraph {
|
|||
graphViewer.setBackground(Color.WHITE);
|
||||
graphViewer.getRenderer().getVertexLabelRenderer().setPosition(Renderer.VertexLabel.Position.S);
|
||||
|
||||
RenderContext<CoreNode, CoreLink> renderContext = graphViewer.getRenderContext();
|
||||
|
||||
// node render properties
|
||||
RenderContext<CoreNode, CoreLink> renderContext = graphViewer.getRenderContext();
|
||||
renderContext.setVertexLabelTransformer(CoreNode::getName);
|
||||
renderContext.setVertexLabelRenderer(nodeLabelRenderer);
|
||||
renderContext.setVertexShapeTransformer(node -> {
|
||||
|
@ -251,6 +248,7 @@ public class NetworkGraph {
|
|||
}
|
||||
nodeMap.clear();
|
||||
graphViewer.repaint();
|
||||
coreAddresses.reset();
|
||||
}
|
||||
|
||||
public void updatePositions() {
|
||||
|
@ -287,40 +285,93 @@ public class NetworkGraph {
|
|||
|
||||
private void handleEdgeAdded(GraphEvent.Edge<CoreNode, CoreLink> edgeEvent) {
|
||||
CoreLink link = edgeEvent.getEdge();
|
||||
if (!link.isLoaded()) {
|
||||
Pair<CoreNode> endpoints = graph.getEndpoints(link);
|
||||
|
||||
CoreNode nodeOne = endpoints.getFirst();
|
||||
CoreNode nodeTwo = endpoints.getSecond();
|
||||
|
||||
// create interfaces for nodes
|
||||
int sub = coreAddresses.getSubnet(
|
||||
getInterfaces(nodeOne),
|
||||
getInterfaces(nodeTwo)
|
||||
);
|
||||
|
||||
link.setNodeOne(nodeOne.getId());
|
||||
if (isNode(nodeOne)) {
|
||||
int interfaceOneId = nextInterfaceId(nodeOne);
|
||||
CoreInterface interfaceOne = createInterface(nodeOne, sub, interfaceOneId);
|
||||
link.setInterfaceOne(interfaceOne);
|
||||
if (link.isLoaded()) {
|
||||
// load addresses to avoid duplication
|
||||
if (link.getInterfaceOne().getIp4() != null) {
|
||||
coreAddresses.usedAddress(link.getInterfaceOne().getIp4());
|
||||
}
|
||||
|
||||
link.setNodeTwo(nodeTwo.getId());
|
||||
if (isNode(nodeTwo)) {
|
||||
int interfaceTwoId = nextInterfaceId(nodeTwo);
|
||||
CoreInterface interfaceTwo = createInterface(nodeTwo, sub, interfaceTwoId);
|
||||
link.setInterfaceTwo(interfaceTwo);
|
||||
if (link.getInterfaceTwo().getIp4() != null) {
|
||||
coreAddresses.usedAddress(link.getInterfaceTwo().getIp4());
|
||||
}
|
||||
|
||||
boolean isVisible = !checkForWirelessNode(nodeOne, nodeTwo);
|
||||
link.setVisible(isVisible);
|
||||
|
||||
logger.info("adding user created edge: {}", link);
|
||||
return;
|
||||
}
|
||||
Pair<CoreNode> endpoints = graph.getEndpoints(link);
|
||||
CoreNode nodeOne = endpoints.getFirst();
|
||||
CoreNode nodeTwo = endpoints.getSecond();
|
||||
boolean nodeOneIsDefault = isNode(nodeOne);
|
||||
boolean nodeTwoIsDefault = isNode(nodeTwo);
|
||||
|
||||
// check what we are linking together
|
||||
IPAddress subnet = null;
|
||||
Set<CoreInterface> interfaces;
|
||||
if (nodeOneIsDefault && nodeTwoIsDefault) {
|
||||
subnet = coreAddresses.nextSubnet();
|
||||
logger.info("linking node to node using subnet: {}", subnet);
|
||||
} else if (nodeOneIsDefault) {
|
||||
interfaces = getNetworkInterfaces(nodeTwo, new HashSet<>());
|
||||
subnet = coreAddresses.findSubnet(interfaces);
|
||||
logger.info("linking node one to network using subnet: {}", subnet);
|
||||
} else if (nodeTwoIsDefault) {
|
||||
interfaces = getNetworkInterfaces(nodeOne, new HashSet<>());
|
||||
subnet = coreAddresses.findSubnet(interfaces);
|
||||
logger.info("linking node two to network using subnet: {}", subnet);
|
||||
} else {
|
||||
logger.info("subnet not needed for linking networks together");
|
||||
}
|
||||
|
||||
link.setNodeOne(nodeOne.getId());
|
||||
if (nodeOneIsDefault) {
|
||||
int interfaceOneId = nextInterfaceId(nodeOne);
|
||||
CoreInterface interfaceOne = createInterface(nodeOne, interfaceOneId, subnet);
|
||||
link.setInterfaceOne(interfaceOne);
|
||||
}
|
||||
|
||||
link.setNodeTwo(nodeTwo.getId());
|
||||
if (nodeTwoIsDefault) {
|
||||
int interfaceTwoId = nextInterfaceId(nodeTwo);
|
||||
CoreInterface interfaceTwo = createInterface(nodeTwo, interfaceTwoId, subnet);
|
||||
link.setInterfaceTwo(interfaceTwo);
|
||||
}
|
||||
|
||||
boolean isVisible = !checkForWirelessNode(nodeOne, nodeTwo);
|
||||
link.setVisible(isVisible);
|
||||
logger.info("adding user created edge: {}", link);
|
||||
}
|
||||
|
||||
public List<CoreInterface> getInterfaces(CoreNode node) {
|
||||
public Set<CoreInterface> getNetworkInterfaces(CoreNode node, Set<CoreNode> visited) {
|
||||
Set<CoreInterface> interfaces = new HashSet<>();
|
||||
if (visited.contains(node)) {
|
||||
return interfaces;
|
||||
}
|
||||
visited.add(node);
|
||||
|
||||
logger.info("checking network node links: {}", node);
|
||||
for (CoreLink link : graph.getIncidentEdges(node)) {
|
||||
logger.info("checking link: {}", link);
|
||||
if (link.getNodeOne() == null && link.getNodeTwo() == null) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// ignore oneself
|
||||
CoreNode currentNode = getVertex(link.getNodeOne());
|
||||
CoreInterface currentInterface = link.getInterfaceOne();
|
||||
if (node.getId().equals(link.getNodeOne())) {
|
||||
currentNode = getVertex(link.getNodeTwo());
|
||||
currentInterface = link.getInterfaceTwo();
|
||||
}
|
||||
|
||||
if (isNode(currentNode)) {
|
||||
interfaces.add(currentInterface);
|
||||
} else {
|
||||
Set<CoreInterface> nextInterfaces = getNetworkInterfaces(currentNode, visited);
|
||||
interfaces.addAll(nextInterfaces);
|
||||
}
|
||||
}
|
||||
|
||||
return interfaces;
|
||||
}
|
||||
|
||||
public Set<CoreInterface> getNodeInterfaces(CoreNode node) {
|
||||
return graph.getIncidentEdges(node).stream()
|
||||
.map(link -> {
|
||||
if (node.getId().equals(link.getNodeOne())) {
|
||||
|
@ -330,7 +381,7 @@ public class NetworkGraph {
|
|||
}
|
||||
})
|
||||
.filter(Objects::nonNull)
|
||||
.collect(Collectors.toList());
|
||||
.collect(Collectors.toSet());
|
||||
}
|
||||
|
||||
private int nextInterfaceId(CoreNode node) {
|
||||
|
@ -360,19 +411,52 @@ public class NetworkGraph {
|
|||
return node.getType() == NodeType.DEFAULT;
|
||||
}
|
||||
|
||||
private CoreInterface createInterface(CoreNode node, int sub, int interfaceId) {
|
||||
private CoreInterface createInterface(CoreNode node, int interfaceId, IPAddress subnet) {
|
||||
CoreInterface coreInterface = new CoreInterface();
|
||||
coreInterface.setId(interfaceId);
|
||||
coreInterface.setName(String.format("eth%s", interfaceId));
|
||||
String nodeOneIp4 = coreAddresses.getIp4Address(sub, node.getId());
|
||||
coreInterface.setIp4(nodeOneIp4);
|
||||
coreInterface.setIp4Mask(CoreAddresses.IP4_MASK);
|
||||
IPAddress address = subnet.increment(node.getId());
|
||||
logger.info("creating interface for node({}): {}", node.getId(), address);
|
||||
coreInterface.setIp4(address);
|
||||
coreInterface.setIp6(address.toIPv6());
|
||||
return coreInterface;
|
||||
}
|
||||
|
||||
private void handleEdgeRemoved(GraphEvent.Edge<CoreNode, CoreLink> edgeEvent) {
|
||||
CoreLink link = edgeEvent.getEdge();
|
||||
logger.info("removed edge: {}", link);
|
||||
CoreNode nodeOne = getVertex(link.getNodeOne());
|
||||
CoreInterface interfaceOne = link.getInterfaceOne();
|
||||
CoreNode nodeTwo = getVertex(link.getNodeTwo());
|
||||
CoreInterface interfaceTwo = link.getInterfaceTwo();
|
||||
boolean nodeOneIsDefault = isNode(nodeOne);
|
||||
boolean nodeTwoIsDefault = isNode(nodeTwo);
|
||||
|
||||
// check what we are unlinking
|
||||
Set<CoreInterface> interfaces;
|
||||
IPAddress subnet = null;
|
||||
if (nodeOneIsDefault && nodeTwoIsDefault) {
|
||||
subnet = interfaceOne.getIp4().toPrefixBlock();
|
||||
logger.info("unlinking node to node reuse subnet: {}", subnet);
|
||||
} else if (nodeOneIsDefault) {
|
||||
interfaces = getNetworkInterfaces(nodeTwo, new HashSet<>());
|
||||
if (interfaces.isEmpty()) {
|
||||
subnet = interfaceOne.getIp4().toPrefixBlock();
|
||||
logger.info("unlinking node one from network reuse subnet: {}", subnet);
|
||||
}
|
||||
} else if (nodeTwoIsDefault) {
|
||||
interfaces = getNetworkInterfaces(nodeOne, new HashSet<>());
|
||||
if (interfaces.isEmpty()) {
|
||||
subnet = interfaceTwo.getIp4().toPrefixBlock();
|
||||
logger.info("unlinking node two from network reuse subnet: {}", subnet);
|
||||
}
|
||||
} else {
|
||||
logger.info("nothing to do when unlinking networks");
|
||||
}
|
||||
|
||||
if (subnet != null) {
|
||||
coreAddresses.reuseSubnet(subnet);
|
||||
}
|
||||
}
|
||||
|
||||
private void handleVertexAdded(GraphEvent.Vertex<CoreNode, CoreLink> vertexEvent) {
|
||||
|
@ -431,7 +515,7 @@ public class NetworkGraph {
|
|||
}
|
||||
|
||||
private boolean isWirelessNode(CoreNode node) {
|
||||
return node.getType() == NodeType.EMANE || node.getType() == NodeType.WLAN;
|
||||
return node != null && (node.getType() == NodeType.EMANE || node.getType() == NodeType.WLAN);
|
||||
}
|
||||
|
||||
private boolean checkForWirelessNode(CoreNode nodeOne, CoreNode nodeTwo) {
|
||||
|
|
|
@ -11,6 +11,7 @@ import com.core.ui.textfields.DoubleFilter;
|
|||
import com.core.utils.FxmlUtils;
|
||||
import com.jfoenix.controls.JFXButton;
|
||||
import com.jfoenix.controls.JFXTextField;
|
||||
import inet.ipaddr.IPAddress;
|
||||
import javafx.event.ActionEvent;
|
||||
import javafx.event.EventHandler;
|
||||
import javafx.fxml.FXML;
|
||||
|
@ -129,8 +130,8 @@ public class LinkDetails extends ScrollPane {
|
|||
if (coreInterface.getMac() != null) {
|
||||
addRow("MAC", coreInterface.getMac(), true);
|
||||
}
|
||||
addIp4Address(coreInterface.getIp4(), coreInterface.getIp4Mask());
|
||||
addIp6Address(coreInterface.getIp6(), coreInterface.getIp6Mask());
|
||||
addIp4Address(coreInterface.getIp4());
|
||||
addIp6Address(coreInterface.getIp6());
|
||||
}
|
||||
|
||||
private void addRow(String labelText, String value, boolean disabled) {
|
||||
|
@ -155,18 +156,18 @@ public class LinkDetails extends ScrollPane {
|
|||
return textField;
|
||||
}
|
||||
|
||||
private void addIp4Address(String ip, Integer mask) {
|
||||
private void addIp4Address(IPAddress ip) {
|
||||
if (ip == null) {
|
||||
return;
|
||||
}
|
||||
addRow("IP4", String.format("%s/%s", ip, mask), true);
|
||||
addRow("IP4", ip.toString(), true);
|
||||
}
|
||||
|
||||
private void addIp6Address(String ip, String mask) {
|
||||
private void addIp6Address(IPAddress ip) {
|
||||
if (ip == null) {
|
||||
return;
|
||||
}
|
||||
addRow("IP6", String.format("%s/%s", ip, mask), true);
|
||||
addRow("IP6", ip.toString(), true);
|
||||
}
|
||||
|
||||
private void clear() {
|
||||
|
|
|
@ -10,6 +10,7 @@ import com.jfoenix.controls.JFXButton;
|
|||
import com.jfoenix.controls.JFXListView;
|
||||
import com.jfoenix.controls.JFXScrollPane;
|
||||
import com.jfoenix.controls.JFXTextField;
|
||||
import inet.ipaddr.IPAddress;
|
||||
import javafx.event.ActionEvent;
|
||||
import javafx.event.EventHandler;
|
||||
import javafx.fxml.FXML;
|
||||
|
@ -147,8 +148,8 @@ public class NodeDetails extends ScrollPane {
|
|||
if (coreInterface.getMac() != null) {
|
||||
addRow("MAC", coreInterface.getMac(), true);
|
||||
}
|
||||
addIp4Address(coreInterface.getIp4(), coreInterface.getIp4Mask());
|
||||
addIp6Address(coreInterface.getIp6(), coreInterface.getIp6Mask());
|
||||
addIp4Address(coreInterface.getIp4());
|
||||
addIp6Address(coreInterface.getIp6());
|
||||
}
|
||||
|
||||
private void addRow(String labelText, String value, boolean disabled) {
|
||||
|
@ -158,18 +159,18 @@ public class NodeDetails extends ScrollPane {
|
|||
gridPane.addRow(index++, label, textField);
|
||||
}
|
||||
|
||||
private void addIp4Address(String ip, Integer mask) {
|
||||
private void addIp4Address(IPAddress ip) {
|
||||
if (ip == null) {
|
||||
return;
|
||||
}
|
||||
addRow("IP4", String.format("%s/%s", ip, mask), true);
|
||||
addRow("IP4", ip.toString(), true);
|
||||
}
|
||||
|
||||
private void addIp6Address(String ip, String mask) {
|
||||
private void addIp6Address(IPAddress ip) {
|
||||
if (ip == null) {
|
||||
return;
|
||||
}
|
||||
addRow("IP6", String.format("%s/%s", ip, mask), true);
|
||||
addRow("IP6", ip.toString(), true);
|
||||
}
|
||||
|
||||
private void clear() {
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
package com.core.ui.dialogs;
|
||||
|
||||
import com.core.Controller;
|
||||
import com.core.client.rest.WlanConfig;
|
||||
import com.core.data.WlanConfig;
|
||||
import com.core.data.CoreNode;
|
||||
import com.core.ui.Toast;
|
||||
import com.jfoenix.controls.JFXButton;
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
package com.core.ui.dialogs;
|
||||
|
||||
import com.core.Controller;
|
||||
import com.core.client.rest.ServiceFile;
|
||||
import com.core.data.ServiceFile;
|
||||
import com.core.data.CoreNode;
|
||||
import com.core.data.CoreService;
|
||||
import com.jfoenix.controls.JFXButton;
|
||||
|
|
|
@ -1,174 +0,0 @@
|
|||
package com.core.utils;
|
||||
|
||||
import com.fasterxml.jackson.core.type.TypeReference;
|
||||
import okhttp3.*;
|
||||
import org.apache.logging.log4j.LogManager;
|
||||
import org.apache.logging.log4j.Logger;
|
||||
|
||||
import java.io.*;
|
||||
import java.util.Collections;
|
||||
import java.util.Map;
|
||||
|
||||
public final class WebUtils {
|
||||
private static final Logger logger = LogManager.getLogger();
|
||||
private static final OkHttpClient client = new OkHttpClient();
|
||||
private static final MediaType JSON = MediaType.parse("application/json; charset=utf-8");
|
||||
|
||||
private WebUtils() {
|
||||
}
|
||||
|
||||
public static <T> T getJson(String url, Class<T> clazz) throws IOException {
|
||||
return getJson(url, clazz, Collections.emptyMap());
|
||||
}
|
||||
|
||||
public static <T> T getJson(String url, Class<T> clazz, Map<String, String> args) throws IOException {
|
||||
logger.debug("get json: {}", url);
|
||||
HttpUrl.Builder urlBuilder = HttpUrl.parse(url).newBuilder();
|
||||
args.forEach(urlBuilder::addQueryParameter);
|
||||
HttpUrl httpUrl = urlBuilder.build();
|
||||
|
||||
Request request = new Request.Builder()
|
||||
.url(httpUrl)
|
||||
.build();
|
||||
String response = readResponse(request);
|
||||
return JsonUtils.read(response, clazz);
|
||||
}
|
||||
|
||||
public static void getFile(String url, File file) throws IOException {
|
||||
logger.debug("get file: {}", url);
|
||||
Request request = new Request.Builder()
|
||||
.url(url)
|
||||
.build();
|
||||
try (Response response = client.newCall(request).execute()) {
|
||||
InputStream input = response.body().byteStream();
|
||||
try (OutputStream output = new FileOutputStream(file)) {
|
||||
int count;
|
||||
byte[] data = new byte[1024];
|
||||
while ((count = input.read(data)) != -1) {
|
||||
output.write(data, 0, count);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static boolean postFile(String url, File file) throws IOException {
|
||||
MediaType mediaType = MediaType.parse("File/*");
|
||||
RequestBody requestBody = new MultipartBody.Builder()
|
||||
.setType(MultipartBody.FORM)
|
||||
.addFormDataPart("file", file.getName(), RequestBody.create(mediaType, file))
|
||||
.build();
|
||||
|
||||
Request request = new Request.Builder()
|
||||
.url(url)
|
||||
.post(requestBody)
|
||||
.build();
|
||||
|
||||
try (Response response = client.newCall(request).execute()) {
|
||||
return response.isSuccessful();
|
||||
}
|
||||
}
|
||||
|
||||
public static <T> T postFile(String url, File file, Class<T> clazz) throws IOException {
|
||||
MediaType mediaType = MediaType.parse("File/*");
|
||||
RequestBody requestBody = new MultipartBody.Builder()
|
||||
.setType(MultipartBody.FORM)
|
||||
.addFormDataPart("file", file.getName(), RequestBody.create(mediaType, file))
|
||||
.build();
|
||||
|
||||
Request request = new Request.Builder()
|
||||
.url(url)
|
||||
.post(requestBody)
|
||||
.build();
|
||||
|
||||
String response = readResponse(request);
|
||||
return JsonUtils.read(response, clazz);
|
||||
}
|
||||
|
||||
public static <T> T getJson(String url, TypeReference<T> reference) throws IOException {
|
||||
Request request = new Request.Builder()
|
||||
.url(url)
|
||||
.build();
|
||||
String response = readResponse(request);
|
||||
return JsonUtils.getMapper().readValue(response, reference);
|
||||
}
|
||||
|
||||
private static String readResponse(Request request) throws IOException {
|
||||
try (Response response = client.newCall(request).execute()) {
|
||||
ResponseBody body = response.body();
|
||||
if (body == null) {
|
||||
throw new IOException("failed to received body");
|
||||
} else {
|
||||
return body.string();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static boolean postJson(String url, Object json) throws IOException {
|
||||
logger.debug("post json: {} - {}", url, json);
|
||||
RequestBody body = RequestBody.create(JSON, JsonUtils.toString(json));
|
||||
Request request = new Request.Builder()
|
||||
.url(url)
|
||||
.post(body)
|
||||
.build();
|
||||
try (Response response = client.newCall(request).execute()) {
|
||||
return response.isSuccessful();
|
||||
}
|
||||
}
|
||||
|
||||
public static boolean putJson(String url) throws IOException {
|
||||
logger.debug("put json: {}", url);
|
||||
RequestBody body = new FormBody.Builder().build();
|
||||
Request request = new Request.Builder()
|
||||
.url(url)
|
||||
.put(body)
|
||||
.build();
|
||||
try (Response response = client.newCall(request).execute()) {
|
||||
return response.isSuccessful();
|
||||
}
|
||||
}
|
||||
|
||||
public static boolean putJson(String url, Object json) throws IOException {
|
||||
logger.debug("put json: {} - {}", url, json);
|
||||
RequestBody body = RequestBody.create(JSON, JsonUtils.toString(json));
|
||||
Request request = new Request.Builder()
|
||||
.url(url)
|
||||
.put(body)
|
||||
.build();
|
||||
try (Response response = client.newCall(request).execute()) {
|
||||
return response.isSuccessful();
|
||||
}
|
||||
}
|
||||
|
||||
public static <T> T putJson(String url, Object json, Class<T> clazz) throws IOException {
|
||||
logger.debug("put json: {} - {}", url, json);
|
||||
RequestBody body = RequestBody.create(JSON, JsonUtils.toString(json));
|
||||
Request request = new Request.Builder()
|
||||
.url(url)
|
||||
.put(body)
|
||||
.build();
|
||||
String response = readResponse(request);
|
||||
return JsonUtils.read(response, clazz);
|
||||
}
|
||||
|
||||
public static <T> T post(String url, Class<T> clazz) throws IOException {
|
||||
logger.debug("post: {}", url);
|
||||
RequestBody body = new FormBody.Builder().build();
|
||||
Request request = new Request.Builder()
|
||||
.url(url)
|
||||
.post(body)
|
||||
.build();
|
||||
String response = readResponse(request);
|
||||
return JsonUtils.read(response, clazz);
|
||||
}
|
||||
|
||||
public static boolean delete(String url) throws IOException {
|
||||
logger.debug("delete: {}", url);
|
||||
Request request = new Request.Builder()
|
||||
.url(url)
|
||||
.delete()
|
||||
.build();
|
||||
try (Response response = client.newCall(request).execute()) {
|
||||
return response.isSuccessful();
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,130 +0,0 @@
|
|||
package com.core.websocket;
|
||||
|
||||
import com.core.Controller;
|
||||
import com.core.data.*;
|
||||
import com.core.ui.dialogs.MobilityPlayerDialog;
|
||||
import com.core.utils.JsonUtils;
|
||||
import io.socket.client.IO;
|
||||
import io.socket.client.Socket;
|
||||
import org.apache.logging.log4j.LogManager;
|
||||
import org.apache.logging.log4j.Logger;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.net.URISyntaxException;
|
||||
|
||||
public class CoreWebSocket {
|
||||
private static final Logger logger = LogManager.getLogger();
|
||||
private final Controller controller;
|
||||
private Thread socketThread;
|
||||
private Socket socket;
|
||||
|
||||
public CoreWebSocket(Controller controller) {
|
||||
this.controller = controller;
|
||||
}
|
||||
|
||||
public void start(String address, int port) throws URISyntaxException {
|
||||
socket = IO.socket(String.format("http://%s:%s", address, port));
|
||||
socket.on(Socket.EVENT_CONNECT, args -> logger.info("connected to web socket"));
|
||||
socket.on("node", this::handleNodes);
|
||||
socket.on("event", this::handleEvents);
|
||||
socket.on("config", this::handleConfigs);
|
||||
socket.on("link", this::handleLinks);
|
||||
socket.on("throughput", this::handleThroughputs);
|
||||
socket.on(Socket.EVENT_DISCONNECT, args -> logger.info("disconnected from web socket"));
|
||||
|
||||
logger.info("attempting to connect to web socket!");
|
||||
socketThread = new Thread(socket::connect);
|
||||
socketThread.setDaemon(true);
|
||||
socketThread.start();
|
||||
}
|
||||
|
||||
public void stop() {
|
||||
if (socketThread != null) {
|
||||
socket.close();
|
||||
socketThread.interrupt();
|
||||
}
|
||||
}
|
||||
|
||||
private void handleThroughputs(Object... args) {
|
||||
for (Object arg : args) {
|
||||
logger.info("throughput update: {}", arg);
|
||||
try {
|
||||
Throughputs throughputs = JsonUtils.read(arg.toString(), Throughputs.class);
|
||||
controller.handleThroughputs(throughputs);
|
||||
} catch (IOException ex) {
|
||||
logger.error("error getting throughputs", ex);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void handleNodes(Object... args) {
|
||||
for (Object arg : args) {
|
||||
try {
|
||||
CoreNode node = JsonUtils.read(arg.toString(), CoreNode.class);
|
||||
logger.info("core node update: {}", node);
|
||||
controller.getNetworkGraph().setNodeLocation(node);
|
||||
} catch (IOException ex) {
|
||||
logger.error("error getting core node", ex);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void handleEvents(Object... args) {
|
||||
for (Object arg : args) {
|
||||
try {
|
||||
CoreEvent event = JsonUtils.read(arg.toString(), CoreEvent.class);
|
||||
logger.info("handling broadcast event: {}", event);
|
||||
SessionState state = SessionState.get(event.getEventType().getValue());
|
||||
if (state == null) {
|
||||
logger.warn("unknown event type: {}", event.getEventType().getValue());
|
||||
return;
|
||||
}
|
||||
|
||||
// session state event
|
||||
if (state.getValue() <= 6) {
|
||||
logger.info("event updating session state: {}", state);
|
||||
controller.getCoreClient().updateState(state);
|
||||
// mobility script event
|
||||
} else if (state.getValue() <= 9) {
|
||||
Integer nodeId = event.getNode();
|
||||
String[] values = event.getData().split("\\s+");
|
||||
Integer start = Integer.parseInt(values[0].split("=")[1]);
|
||||
Integer end = Integer.parseInt(values[1].split("=")[1]);
|
||||
logger.info(String.format("node(%s) mobility event (%s) - start(%s) stop(%s)",
|
||||
nodeId, state, start, end));
|
||||
logger.info("all dialogs: {}", controller.getMobilityPlayerDialogs().keySet());
|
||||
MobilityPlayerDialog mobilityPlayerDialog = controller.getMobilityPlayerDialogs().get(nodeId);
|
||||
mobilityPlayerDialog.event(state, start, end);
|
||||
}
|
||||
} catch (IOException ex) {
|
||||
logger.error("error getting core event", ex);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void handleLinks(Object... args) {
|
||||
for (Object arg : args) {
|
||||
try {
|
||||
CoreLink link = JsonUtils.read(arg.toString(), CoreLink.class);
|
||||
logger.info("handling broadcast link: {}", link);
|
||||
MessageFlags flag = MessageFlags.get(link.getMessageType());
|
||||
if (MessageFlags.DELETE == flag) {
|
||||
logger.info("delete");
|
||||
controller.getNetworkGraph().removeWirelessLink(link);
|
||||
} else if (MessageFlags.ADD == flag) {
|
||||
link.setLoaded(true);
|
||||
controller.getNetworkGraph().addLink(link);
|
||||
}
|
||||
controller.getNetworkGraph().getGraphViewer().repaint();
|
||||
} catch (IOException ex) {
|
||||
logger.error("error handling broadcast link", ex);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void handleConfigs(Object... args) {
|
||||
for (Object arg : args) {
|
||||
logger.info("handling broadcast config: {}", arg);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1 +1 @@
|
|||
../../../../daemon/proto/core.proto
|
||||
../../../../daemon/proto/core/api/grpc/core.proto
|
|
@ -11,7 +11,13 @@ SETUPPY = setup.py
|
|||
SETUPPYFLAGS = -v
|
||||
|
||||
if WANT_DOCS
|
||||
DOCS = doc
|
||||
DOCS = doc
|
||||
endif
|
||||
|
||||
if PYTHON3
|
||||
PYTHONLIBDIR=$(libdir)/python3/dist-packages
|
||||
else
|
||||
PYTHONLIBDIR=$(pythondir)
|
||||
endif
|
||||
|
||||
SUBDIRS = proto $(DOCS)
|
||||
|
@ -29,7 +35,7 @@ install-exec-hook:
|
|||
$(PYTHON) $(SETUPPY) $(SETUPPYFLAGS) install \
|
||||
--root=/$(DESTDIR) \
|
||||
--prefix=$(prefix) \
|
||||
--install-lib=$(pythondir) \
|
||||
--install-lib=$(PYTHONLIBDIR) \
|
||||
--single-version-externally-managed
|
||||
|
||||
# Python package uninstall
|
||||
|
@ -38,8 +44,8 @@ uninstall-hook:
|
|||
rm -rf $(DESTDIR)/$(datadir)/core
|
||||
rm -f $(addprefix $(DESTDIR)/$(datarootdir)/man/man1/, $(MAN_FILES))
|
||||
rm -f $(addprefix $(DESTDIR)/$(bindir)/,$(SCRIPT_FILES))
|
||||
rm -rf $(DESTDIR)/$(pythondir)/core-$(PACKAGE_VERSION)-py$(PYTHON_VERSION).egg-info
|
||||
rm -rf $(DESTDIR)/$(pythondir)/core
|
||||
rm -rf $(DESTDIR)/$(PYTHONLIBDIR)/core-$(PACKAGE_VERSION)-py$(PYTHON_VERSION).egg-info
|
||||
rm -rf $(DESTDIR)/$(PYTHONLIBDIR)/core
|
||||
|
||||
# Python package cleanup
|
||||
clean-local:
|
||||
|
|
|
@ -246,7 +246,7 @@ class CoreGrpcClient(object):
|
|||
:rtype: core_pb2.SetSessionLocationResponse
|
||||
:raises grpc.RpcError: when session doesn't exist
|
||||
"""
|
||||
position = core_pb2.Position(x=x, y=y, z=z, lat=lat, lon=lon, alt=alt)
|
||||
position = core_pb2.SessionPosition(x=x, y=y, z=z, lat=lat, lon=lon, alt=alt)
|
||||
request = core_pb2.SetSessionLocationRequest(session_id=session_id, position=position, scale=scale)
|
||||
return self.stub.SetSessionLocation(request)
|
||||
|
||||
|
|
|
@ -17,6 +17,7 @@ from core.emulator.emudata import NodeOptions, InterfaceData, LinkOptions
|
|||
from core.emulator.enumerations import NodeTypes, EventTypes, LinkTypes
|
||||
from core.location.mobility import BasicRangeModel, Ns2ScriptedMobility
|
||||
from core.nodes import nodeutils
|
||||
from core.nodes.base import CoreNetworkBase
|
||||
from core.nodes.ipaddress import MacAddress
|
||||
from core.services.coreservices import ServiceManager
|
||||
|
||||
|
@ -73,18 +74,24 @@ def convert_link(session, link_data):
|
|||
interface_one = None
|
||||
if link_data.interface1_id is not None:
|
||||
node = session.get_node(link_data.node1_id)
|
||||
interface = node.netif(link_data.interface1_id)
|
||||
interface_name = None
|
||||
if not isinstance(node, CoreNetworkBase):
|
||||
interface = node.netif(link_data.interface1_id)
|
||||
interface_name = interface.name
|
||||
interface_one = core_pb2.Interface(
|
||||
id=link_data.interface1_id, name=interface.name, mac=convert_value(link_data.interface1_mac),
|
||||
id=link_data.interface1_id, name=interface_name, mac=convert_value(link_data.interface1_mac),
|
||||
ip4=convert_value(link_data.interface1_ip4), ip4mask=link_data.interface1_ip4_mask,
|
||||
ip6=convert_value(link_data.interface1_ip6), ip6mask=link_data.interface1_ip6_mask)
|
||||
|
||||
interface_two = None
|
||||
if link_data.interface2_id is not None:
|
||||
node = session.get_node(link_data.node2_id)
|
||||
interface = node.netif(link_data.interface2_id)
|
||||
interface_name = None
|
||||
if not isinstance(node, CoreNetworkBase):
|
||||
interface = node.netif(link_data.interface2_id)
|
||||
interface_name = interface.name
|
||||
interface_two = core_pb2.Interface(
|
||||
id=link_data.interface2_id, name=interface.name, mac=convert_value(link_data.interface2_mac),
|
||||
id=link_data.interface2_id, name=interface_name, mac=convert_value(link_data.interface2_mac),
|
||||
ip4=convert_value(link_data.interface2_ip4), ip4mask=link_data.interface2_ip4_mask,
|
||||
ip6=convert_value(link_data.interface2_ip6), ip6mask=link_data.interface2_ip6_mask)
|
||||
|
||||
|
@ -195,7 +202,7 @@ class CoreGrpcServer(core_pb2_grpc.CoreApiServicer):
|
|||
session = self.get_session(request.session_id, context)
|
||||
x, y, z = session.location.refxyz
|
||||
lat, lon, alt = session.location.refgeo
|
||||
position = core_pb2.Position(x=x, y=y, z=z, lat=lat, lon=lon, alt=alt)
|
||||
position = core_pb2.SessionPosition(x=x, y=y, z=z, lat=lat, lon=lon, alt=alt)
|
||||
return core_pb2.GetSessionLocationResponse(position=position, scale=session.location.refscale)
|
||||
|
||||
def SetSessionLocation(self, request, context):
|
||||
|
@ -855,6 +862,9 @@ class CoreGrpcServer(core_pb2_grpc.CoreApiServicer):
|
|||
logging.debug("set wlan config: %s", request)
|
||||
session = self.get_session(request.session_id, context)
|
||||
session.mobility.set_model_config(request.node_id, BasicRangeModel.name, request.config)
|
||||
if session.state == EventTypes.RUNTIME_STATE.value:
|
||||
node = self.get_node(session, request.node_id, context)
|
||||
node.updatemodel(request.config)
|
||||
return core_pb2.SetWlanConfigResponse(result=True)
|
||||
|
||||
def GetEmaneConfig(self, request, context):
|
||||
|
|
|
@ -10,6 +10,7 @@ import select
|
|||
import socket
|
||||
import threading
|
||||
|
||||
from core import utils
|
||||
from core.api.tlv import coreapi
|
||||
from core.nodes.base import CoreNodeBase, CoreNetworkBase
|
||||
from core.emulator.enumerations import ConfigDataTypes
|
||||
|
@ -96,7 +97,7 @@ class CoreBroker(object):
|
|||
"""
|
||||
Creates a CoreBroker instance.
|
||||
|
||||
:param core.session.Session session: session this manager is tied to
|
||||
:param core.emulator.session.Session session: session this manager is tied to
|
||||
:return: nothing
|
||||
"""
|
||||
|
||||
|
@ -121,7 +122,6 @@ class CoreBroker(object):
|
|||
self.physical_nodes = set()
|
||||
# allows for other message handlers to process API messages (e.g. EMANE)
|
||||
self.handlers = set()
|
||||
self.handlers.add(self.handle_distributed)
|
||||
# dict with tunnel key to tunnel device mapping
|
||||
self.tunnels = {}
|
||||
self.dorecvloop = False
|
||||
|
@ -388,12 +388,13 @@ class CoreBroker(object):
|
|||
:return: tunnel key for the node pair
|
||||
:rtype: int
|
||||
"""
|
||||
logging.debug("creating tunnel key for: %s, %s", n1num, n2num)
|
||||
sid = self.session_id_master
|
||||
if sid is None:
|
||||
# this is the master session
|
||||
sid = self.session.id
|
||||
|
||||
key = (sid << 16) ^ hash(n1num) ^ (hash(n2num) << 8)
|
||||
key = (sid << 16) ^ utils.hashkey(n1num) ^ (utils.hashkey(n2num) << 8)
|
||||
return key & 0xFFFFFFFF
|
||||
|
||||
def addtunnel(self, remoteip, n1num, n2num, localnum):
|
||||
|
@ -901,7 +902,7 @@ class CoreBroker(object):
|
|||
opaque data in the link message, otherwise use the IP of the message
|
||||
sender (the master server).
|
||||
|
||||
:param coreapi.CoreLinkMessage msg:
|
||||
:param core.api.tlv.coreapi.CoreLinkMessage msg: link message
|
||||
:param bool first_is_local: is first local
|
||||
:return: host address
|
||||
:rtype: str
|
||||
|
@ -1049,62 +1050,3 @@ class CoreBroker(object):
|
|||
if not server.instantiation_complete:
|
||||
return False
|
||||
return True
|
||||
|
||||
def handle_distributed(self, message):
|
||||
"""
|
||||
Handle the session options config message as it has reached the
|
||||
broker. Options requiring modification for distributed operation should
|
||||
be handled here.
|
||||
|
||||
:param message: message to handle
|
||||
:return: nothing
|
||||
"""
|
||||
if not self.session.master:
|
||||
return
|
||||
|
||||
if message.message_type != MessageTypes.CONFIG.value or message.get_tlv(ConfigTlvs.OBJECT.value) != "session":
|
||||
return
|
||||
|
||||
values_str = message.get_tlv(ConfigTlvs.VALUES.value)
|
||||
if values_str is None:
|
||||
return
|
||||
|
||||
value_strings = values_str.split("|")
|
||||
for value_string in value_strings:
|
||||
key, _value = value_string.split("=", 1)
|
||||
if key == "controlnet":
|
||||
self.handle_distributed_control_net(message, value_strings, value_strings.index(value_string))
|
||||
|
||||
def handle_distributed_control_net(self, message, values, index):
|
||||
"""
|
||||
Modify Config Message if multiple control network prefixes are
|
||||
defined. Map server names to prefixes and repack the message before
|
||||
it is forwarded to slave servers.
|
||||
|
||||
:param message: message to handle
|
||||
:param list values: values to handle
|
||||
:param int index: index ti get key value from
|
||||
:return: nothing
|
||||
"""
|
||||
key_value = values[index]
|
||||
_key, value = key_value.split("=", 1)
|
||||
control_nets = value.split()
|
||||
|
||||
if len(control_nets) < 2:
|
||||
logging.warning("multiple controlnet prefixes do not exist")
|
||||
return
|
||||
|
||||
servers = self.session.broker.getservernames()
|
||||
if len(servers) < 2:
|
||||
logging.warning("not distributed")
|
||||
return
|
||||
|
||||
servers.remove("localhost")
|
||||
# master always gets first prefix
|
||||
servers.insert(0, "localhost")
|
||||
# create list of "server1:ctrlnet1 server2:ctrlnet2 ..."
|
||||
control_nets = map(lambda x: "%s:%s" % (x[0], x[1]), zip(servers, control_nets))
|
||||
values[index] = "controlnet=%s" % (" ".join(control_nets))
|
||||
values_str = "|".join(values)
|
||||
message.tlv_data[ConfigTlvs.VALUES.value] = values_str
|
||||
message.repack()
|
||||
|
|
|
@ -243,7 +243,7 @@ class CoreTlvDataUint16List(CoreTlvData):
|
|||
Retrieves a unint 16 list from a string
|
||||
|
||||
:param str value: string representation of a uint 16 list
|
||||
:return: unint 16 list
|
||||
:return: uint 16 list
|
||||
:rtype: list
|
||||
"""
|
||||
return tuple(int(x) for x in value.split())
|
||||
|
@ -274,7 +274,7 @@ class CoreTlvDataIpv4Addr(CoreTlvDataObj):
|
|||
|
||||
:param str value: value to get Ipv4 address from
|
||||
:return: Ipv4 address
|
||||
:rtype: core.misc.ipaddress.IpAddress
|
||||
:rtype: core.nodes.ipaddress.IpAddress
|
||||
"""
|
||||
return IpAddress(af=socket.AF_INET, address=value)
|
||||
|
||||
|
@ -292,7 +292,7 @@ class CoreTlvDataIPv6Addr(CoreTlvDataObj):
|
|||
"""
|
||||
Retrieve Ipv6 address value from object.
|
||||
|
||||
:param core.misc.ipaddress.IpAddress obj: ip address to get value from
|
||||
:param core.nodes.ipaddress.IpAddress obj: ip address to get value from
|
||||
:return:
|
||||
"""
|
||||
return obj.addr
|
||||
|
@ -304,7 +304,7 @@ class CoreTlvDataIPv6Addr(CoreTlvDataObj):
|
|||
|
||||
:param str value: value to get Ipv4 address from
|
||||
:return: Ipv4 address
|
||||
:rtype: core.misc.ipaddress.IpAddress
|
||||
:rtype: core.nodes.ipaddress.IpAddress
|
||||
"""
|
||||
return IpAddress(af=socket.AF_INET6, address=value)
|
||||
|
||||
|
@ -322,7 +322,7 @@ class CoreTlvDataMacAddr(CoreTlvDataObj):
|
|||
"""
|
||||
Retrieve Ipv6 address value from object.
|
||||
|
||||
:param core.misc.ipaddress.MacAddress obj: mac address to get value from
|
||||
:param core.nodes.ipaddress.MacAddress obj: mac address to get value from
|
||||
:return:
|
||||
"""
|
||||
# extend to 64 bits
|
||||
|
@ -335,7 +335,7 @@ class CoreTlvDataMacAddr(CoreTlvDataObj):
|
|||
|
||||
:param str value: value to get Ipv4 address from
|
||||
:return: Ipv4 address
|
||||
:rtype: core.misc.ipaddress.MacAddress
|
||||
:rtype: core.nodes.ipaddress.MacAddress
|
||||
"""
|
||||
# only use 48 bits
|
||||
return MacAddress(address=value[2:])
|
||||
|
|
|
@ -4,16 +4,17 @@ socket server request handlers leveraged by core servers.
|
|||
|
||||
import logging
|
||||
import os
|
||||
from queue import Queue, Empty
|
||||
import shlex
|
||||
import shutil
|
||||
import socketserver
|
||||
import sys
|
||||
import threading
|
||||
import time
|
||||
from builtins import range
|
||||
from itertools import repeat
|
||||
|
||||
import socketserver
|
||||
from builtins import range
|
||||
from queue import Queue, Empty
|
||||
|
||||
from core import utils
|
||||
from core.api.tlv import coreapi, dataconversion, structutils
|
||||
from core.config import ConfigShim
|
||||
|
@ -39,6 +40,7 @@ from core.emulator.enumerations import NodeTlvs
|
|||
from core.emulator.enumerations import NodeTypes
|
||||
from core.emulator.enumerations import RegisterTlvs
|
||||
from core.emulator.enumerations import SessionTlvs
|
||||
from core.location.mobility import BasicRangeModel
|
||||
from core.nodes import nodeutils
|
||||
from core.services.coreservices import ServiceManager
|
||||
from core.services.coreservices import ServiceShim
|
||||
|
@ -213,7 +215,7 @@ class CoreHandler(socketserver.BaseRequestHandler):
|
|||
"""
|
||||
Callback to handle an event broadcast out from a session.
|
||||
|
||||
:param core.data.EventData event_data: event data to handle
|
||||
:param core.emulator.data.EventData event_data: event data to handle
|
||||
:return: nothing
|
||||
"""
|
||||
logging.debug("handling broadcast event: %s", event_data)
|
||||
|
@ -237,7 +239,7 @@ class CoreHandler(socketserver.BaseRequestHandler):
|
|||
"""
|
||||
Callback to handle a file broadcast out from a session.
|
||||
|
||||
:param core.data.FileData file_data: file data to handle
|
||||
:param core.emulator.data.FileData file_data: file data to handle
|
||||
:return: nothing
|
||||
"""
|
||||
logging.debug("handling broadcast file: %s", file_data)
|
||||
|
@ -264,7 +266,7 @@ class CoreHandler(socketserver.BaseRequestHandler):
|
|||
"""
|
||||
Callback to handle a config broadcast out from a session.
|
||||
|
||||
:param core.data.ConfigData config_data: config data to handle
|
||||
:param core.emulator.data.ConfigData config_data: config data to handle
|
||||
:return: nothing
|
||||
"""
|
||||
logging.debug("handling broadcast config: %s", config_data)
|
||||
|
@ -278,7 +280,7 @@ class CoreHandler(socketserver.BaseRequestHandler):
|
|||
"""
|
||||
Callback to handle an exception broadcast out from a session.
|
||||
|
||||
:param core.data.ExceptionData exception_data: exception data to handle
|
||||
:param core.emulator.data.ExceptionData exception_data: exception data to handle
|
||||
:return: nothing
|
||||
"""
|
||||
logging.debug("handling broadcast exception: %s", exception_data)
|
||||
|
@ -301,11 +303,12 @@ class CoreHandler(socketserver.BaseRequestHandler):
|
|||
"""
|
||||
Callback to handle an node broadcast out from a session.
|
||||
|
||||
:param core.data.NodeData node_data: node data to handle
|
||||
:param core.emulator.data.NodeData node_data: node data to handle
|
||||
:return: nothing
|
||||
"""
|
||||
logging.debug("handling broadcast node: %s", node_data)
|
||||
message = dataconversion.convert_node(node_data)
|
||||
|
||||
try:
|
||||
self.sendall(message)
|
||||
except IOError:
|
||||
|
@ -315,13 +318,16 @@ class CoreHandler(socketserver.BaseRequestHandler):
|
|||
"""
|
||||
Callback to handle an link broadcast out from a session.
|
||||
|
||||
:param core.data.LinkData link_data: link data to handle
|
||||
:param core.emulator.data.LinkData link_data: link data to handle
|
||||
:return: nothing
|
||||
"""
|
||||
logging.debug("handling broadcast link: %s", link_data)
|
||||
per = ""
|
||||
if link_data.per is not None:
|
||||
per = str(link_data.per)
|
||||
dup = ""
|
||||
if link_data.dup is not None:
|
||||
dup = str(link_data.dup)
|
||||
|
||||
tlv_data = structutils.pack_values(coreapi.CoreLinkTlv, [
|
||||
(LinkTlvs.N1_NUMBER, link_data.node1_id),
|
||||
|
@ -329,7 +335,7 @@ class CoreHandler(socketserver.BaseRequestHandler):
|
|||
(LinkTlvs.DELAY, link_data.delay),
|
||||
(LinkTlvs.BANDWIDTH, link_data.bandwidth),
|
||||
(LinkTlvs.PER, per),
|
||||
(LinkTlvs.DUP, link_data.dup),
|
||||
(LinkTlvs.DUP, dup),
|
||||
(LinkTlvs.JITTER, link_data.jitter),
|
||||
(LinkTlvs.MER, link_data.mer),
|
||||
(LinkTlvs.BURST, link_data.burst),
|
||||
|
@ -407,7 +413,7 @@ class CoreHandler(socketserver.BaseRequestHandler):
|
|||
Receive data and return a CORE API message object.
|
||||
|
||||
:return: received message
|
||||
:rtype: coreapi.CoreMessage
|
||||
:rtype: core.api.tlv.coreapi.CoreMessage
|
||||
"""
|
||||
try:
|
||||
header = self.request.recv(coreapi.CoreMessage.header_len)
|
||||
|
@ -504,7 +510,6 @@ class CoreHandler(socketserver.BaseRequestHandler):
|
|||
:param message: message for replies
|
||||
:return: nothing
|
||||
"""
|
||||
logging.debug("dispatching replies: %s", replies)
|
||||
for reply in replies:
|
||||
message_type, message_flags, message_length = coreapi.CoreMessage.unpack_header(reply)
|
||||
try:
|
||||
|
@ -518,7 +523,7 @@ class CoreHandler(socketserver.BaseRequestHandler):
|
|||
reply_message = "CoreMessage (type %d flags %d length %d)" % (
|
||||
message_type, message_flags, message_length)
|
||||
|
||||
logging.debug("dispatch reply:\n%s", reply_message)
|
||||
logging.debug("sending reply:\n%s", reply_message)
|
||||
|
||||
try:
|
||||
self.sendall(reply)
|
||||
|
@ -538,7 +543,6 @@ class CoreHandler(socketserver.BaseRequestHandler):
|
|||
|
||||
# TODO: add shutdown handler for session
|
||||
self.session = self.coreemu.create_session(port, master=False)
|
||||
# self.session.shutdown_handlers.append(self.session_shutdown)
|
||||
logging.debug("created new session for client: %s", self.session.id)
|
||||
|
||||
# TODO: hack to associate this handler with this sessions broker for broadcasting
|
||||
|
@ -586,7 +590,7 @@ class CoreHandler(socketserver.BaseRequestHandler):
|
|||
"""
|
||||
Sends an exception for display within the GUI.
|
||||
|
||||
:param core.enumerations.ExceptionLevel level: level for exception
|
||||
:param core.emulator.enumerations.ExceptionLevel level: level for exception
|
||||
:param str source: source where exception came from
|
||||
:param str text: details about exception
|
||||
:param int node: node id, if related to a specific node
|
||||
|
@ -624,7 +628,7 @@ class CoreHandler(socketserver.BaseRequestHandler):
|
|||
"""
|
||||
Node Message handler
|
||||
|
||||
:param coreapi.CoreNodeMessage message: node message
|
||||
:param core.api.tlv.coreapi.CoreNodeMessage message: node message
|
||||
:return: replies to node message
|
||||
"""
|
||||
replies = []
|
||||
|
@ -696,7 +700,7 @@ class CoreHandler(socketserver.BaseRequestHandler):
|
|||
"""
|
||||
Link Message handler
|
||||
|
||||
:param coreapi.CoreLinkMessage message: link message to handle
|
||||
:param core.api.tlv.coreapi.CoreLinkMessage message: link message to handle
|
||||
:return: link message replies
|
||||
"""
|
||||
node_one_id = message.get_tlv(LinkTlvs.N1_NUMBER.value)
|
||||
|
@ -756,7 +760,7 @@ class CoreHandler(socketserver.BaseRequestHandler):
|
|||
"""
|
||||
Execute Message handler
|
||||
|
||||
:param coreapi.CoreExecMessage message: execute message to handle
|
||||
:param core.api.tlv.coreapi.CoreExecMessage message: execute message to handle
|
||||
:return: reply messages
|
||||
"""
|
||||
node_num = message.get_tlv(ExecuteTlvs.NODE.value)
|
||||
|
@ -831,7 +835,7 @@ class CoreHandler(socketserver.BaseRequestHandler):
|
|||
"""
|
||||
Register Message Handler
|
||||
|
||||
:param coreapi.CoreRegMessage message: register message to handle
|
||||
:param core.api.tlv.coreapi.CoreRegMessage message: register message to handle
|
||||
:return: reply messages
|
||||
"""
|
||||
replies = []
|
||||
|
@ -855,7 +859,7 @@ class CoreHandler(socketserver.BaseRequestHandler):
|
|||
raise
|
||||
else:
|
||||
thread = threading.Thread(
|
||||
target=execfile,
|
||||
target=utils.execute_file,
|
||||
args=(file_name, {"__file__": file_name, "coreemu": self.coreemu})
|
||||
)
|
||||
thread.daemon = True
|
||||
|
@ -923,7 +927,7 @@ class CoreHandler(socketserver.BaseRequestHandler):
|
|||
"""
|
||||
Configuration Message handler
|
||||
|
||||
:param coreapi.CoreConfMessage message: configuration message to handle
|
||||
:param core.api.tlv.coreapi.CoreConfMessage message: configuration message to handle
|
||||
:return: reply messages
|
||||
"""
|
||||
# convert config message to standard config data object
|
||||
|
@ -1023,7 +1027,7 @@ class CoreHandler(socketserver.BaseRequestHandler):
|
|||
self.session.location.setrefgeo(lat, lon, alt)
|
||||
self.session.location.refscale = values[5]
|
||||
logging.info("location configured: %s = %s scale=%s", self.session.location.refxyz,
|
||||
self.session.location.refgeo, self.session.location.refscale)
|
||||
self.session.location.refgeo, self.session.location.refscale)
|
||||
logging.info("location configured: UTM%s", self.session.location.refutm)
|
||||
|
||||
def handle_config_metadata(self, message_type, config_data):
|
||||
|
@ -1031,8 +1035,10 @@ class CoreHandler(socketserver.BaseRequestHandler):
|
|||
if message_type == ConfigFlags.REQUEST:
|
||||
node_id = config_data.node
|
||||
metadata_configs = self.session.metadata.get_configs()
|
||||
if metadata_configs is None:
|
||||
metadata_configs = {}
|
||||
data_values = "|".join(["%s=%s" % (x, metadata_configs[x]) for x in metadata_configs])
|
||||
data_types = tuple(ConfigDataTypes.STRING.value for _ in self.session.metadata.get_configs())
|
||||
data_types = tuple(ConfigDataTypes.STRING.value for _ in metadata_configs)
|
||||
config_response = ConfigData(
|
||||
message_type=0,
|
||||
node=node_id,
|
||||
|
@ -1264,6 +1270,13 @@ class CoreHandler(socketserver.BaseRequestHandler):
|
|||
parsed_config = ConfigShim.str_to_dict(values_str)
|
||||
|
||||
self.session.mobility.set_model_config(node_id, object_name, parsed_config)
|
||||
if self.session.state == EventTypes.RUNTIME_STATE.value:
|
||||
try:
|
||||
node = self.session.get_node(node_id)
|
||||
if object_name == BasicRangeModel.name:
|
||||
node.updatemodel(parsed_config)
|
||||
except KeyError:
|
||||
logging.error("skipping mobility configuration for unknown node: %s", node_id)
|
||||
|
||||
return replies
|
||||
|
||||
|
@ -1341,7 +1354,7 @@ class CoreHandler(socketserver.BaseRequestHandler):
|
|||
"""
|
||||
File Message handler
|
||||
|
||||
:param coreapi.CoreFileMessage message: file message to handle
|
||||
:param core.api.tlv.coreapi.CoreFileMessage message: file message to handle
|
||||
:return: reply messages
|
||||
"""
|
||||
if message.flags & MessageFlags.ADD.value:
|
||||
|
@ -1385,7 +1398,7 @@ class CoreHandler(socketserver.BaseRequestHandler):
|
|||
open_file.write(data)
|
||||
return ()
|
||||
|
||||
self.session.node_add_file(node_num, source_name, file_name, data)
|
||||
self.session.add_node_file(node_num, source_name, file_name, data)
|
||||
else:
|
||||
raise NotImplementedError
|
||||
|
||||
|
@ -1405,7 +1418,7 @@ class CoreHandler(socketserver.BaseRequestHandler):
|
|||
"""
|
||||
Event Message handler
|
||||
|
||||
:param coreapi.CoreEventMessage message: event message to handle
|
||||
:param core.api.tlv.coreapi.CoreEventMessage message: event message to handle
|
||||
:return: reply messages
|
||||
"""
|
||||
event_data = EventData(
|
||||
|
@ -1508,7 +1521,7 @@ class CoreHandler(socketserver.BaseRequestHandler):
|
|||
Handle an Event Message used to start, stop, restart, or validate
|
||||
a service on a given node.
|
||||
|
||||
:param EventData event_data: event data to handle
|
||||
:param core.emulator.enumerations.EventData event_data: event data to handle
|
||||
:return: nothing
|
||||
"""
|
||||
event_type = event_data.event_type
|
||||
|
@ -1573,7 +1586,7 @@ class CoreHandler(socketserver.BaseRequestHandler):
|
|||
"""
|
||||
Session Message handler
|
||||
|
||||
:param coreapi.CoreSessionMessage message: session message to handle
|
||||
:param core.api.tlv.coreapi.CoreSessionMessage message: session message to handle
|
||||
:return: reply messages
|
||||
"""
|
||||
session_id_str = message.get_tlv(SessionTlvs.NUMBER.value)
|
||||
|
@ -1628,10 +1641,10 @@ class CoreHandler(socketserver.BaseRequestHandler):
|
|||
logging.info("request to connect to session %s", session_id)
|
||||
|
||||
# remove client from session broker and shutdown if needed
|
||||
self.remove_session_handlers()
|
||||
self.session.broker.session_clients.remove(self)
|
||||
if not self.session.broker.session_clients and not self.session.is_active():
|
||||
self.coreemu.delete_session(self.session.id)
|
||||
self.remove_session_handlers()
|
||||
|
||||
# set session to join
|
||||
self.session = session
|
||||
|
@ -1787,3 +1800,116 @@ class CoreHandler(socketserver.BaseRequestHandler):
|
|||
self.session.broadcast_config(config_data)
|
||||
|
||||
logging.info("informed GUI about %d nodes and %d links", len(nodes_data), len(links_data))
|
||||
|
||||
|
||||
class CoreUdpHandler(CoreHandler):
|
||||
def __init__(self, request, client_address, server):
|
||||
self.message_handlers = {
|
||||
MessageTypes.NODE.value: self.handle_node_message,
|
||||
MessageTypes.LINK.value: self.handle_link_message,
|
||||
MessageTypes.EXECUTE.value: self.handle_execute_message,
|
||||
MessageTypes.REGISTER.value: self.handle_register_message,
|
||||
MessageTypes.CONFIG.value: self.handle_config_message,
|
||||
MessageTypes.FILE.value: self.handle_file_message,
|
||||
MessageTypes.INTERFACE.value: self.handle_interface_message,
|
||||
MessageTypes.EVENT.value: self.handle_event_message,
|
||||
MessageTypes.SESSION.value: self.handle_session_message,
|
||||
}
|
||||
self.master = False
|
||||
self.session = None
|
||||
socketserver.BaseRequestHandler.__init__(self, request, client_address, server)
|
||||
|
||||
def setup(self):
|
||||
"""
|
||||
Client has connected, set up a new connection.
|
||||
:return: nothing
|
||||
"""
|
||||
pass
|
||||
|
||||
def receive_message(self):
|
||||
data = self.request[0]
|
||||
header = data[:coreapi.CoreMessage.header_len]
|
||||
if len(header) < coreapi.CoreMessage.header_len:
|
||||
raise IOError("error receiving header (received %d bytes)" % len(header))
|
||||
|
||||
message_type, message_flags, message_len = coreapi.CoreMessage.unpack_header(header)
|
||||
if message_len == 0:
|
||||
logging.warning("received message with no data")
|
||||
return
|
||||
|
||||
if len(data) != coreapi.CoreMessage.header_len + message_len:
|
||||
logging.error("received message length does not match received data (%s != %s)",
|
||||
len(data), coreapi.CoreMessage.header_len + message_len)
|
||||
raise IOError
|
||||
|
||||
try:
|
||||
message_class = coreapi.CLASS_MAP[message_type]
|
||||
message = message_class(message_flags, header, data[coreapi.CoreMessage.header_len:])
|
||||
return message
|
||||
except KeyError:
|
||||
message = coreapi.CoreMessage(message_flags, header, data[coreapi.CoreMessage.header_len:])
|
||||
message.msgtype = message_type
|
||||
logging.exception("unimplemented core message type: %s", message.type_str())
|
||||
|
||||
def handle(self):
|
||||
message = self.receive_message()
|
||||
sessions = message.session_numbers()
|
||||
message.queuedtimes = 0
|
||||
if sessions:
|
||||
for session_id in sessions:
|
||||
session = self.server.mainserver.coreemu.sessions.get(session_id)
|
||||
if session:
|
||||
logging.debug("session handling message: %s", session.session_id)
|
||||
self.session = session
|
||||
self.handle_message(message)
|
||||
self.broadcast(message)
|
||||
else:
|
||||
logging.error("session %d in %s message not found.", session_id, message.type_str())
|
||||
else:
|
||||
# no session specified, find an existing one
|
||||
session = None
|
||||
node_count = 0
|
||||
for session_id in self.server.mainserver.coreemu.sessions:
|
||||
current_session = self.server.mainserver.coreemu.sessions[session_id]
|
||||
current_node_count = current_session.get_node_count()
|
||||
if current_session.state == EventTypes.RUNTIME_STATE.value and current_node_count > node_count:
|
||||
node_count = current_node_count
|
||||
session = current_session
|
||||
|
||||
if session or message.message_type == MessageTypes.REGISTER.value:
|
||||
self.session = session
|
||||
self.handle_message(message)
|
||||
self.broadcast(message)
|
||||
else:
|
||||
logging.error("no active session, dropping %s message.", message.type_str())
|
||||
|
||||
def broadcast(self, message):
|
||||
if not isinstance(message, (coreapi.CoreNodeMessage, coreapi.CoreLinkMessage)):
|
||||
return
|
||||
|
||||
for client in self.session.broker.session_clients:
|
||||
try:
|
||||
client.sendall(message.raw_message)
|
||||
except IOError:
|
||||
logging.error("error broadcasting")
|
||||
|
||||
def finish(self):
|
||||
return socketserver.BaseRequestHandler.finish(self)
|
||||
|
||||
def queuemsg(self, msg):
|
||||
"""
|
||||
UDP handlers are short-lived and do not have message queues.
|
||||
|
||||
:param bytes msg: message to queue
|
||||
:return:
|
||||
"""
|
||||
raise Exception("Unable to queue %s message for later processing using UDP!" % msg)
|
||||
|
||||
def sendall(self, data):
|
||||
"""
|
||||
Use sendto() on the connectionless UDP socket.
|
||||
|
||||
:param data:
|
||||
:return:
|
||||
"""
|
||||
self.request[1].sendto(data, self.client_address)
|
||||
|
|
|
@ -23,8 +23,36 @@ class CoreServer(socketserver.ThreadingMixIn, socketserver.TCPServer):
|
|||
:param tuple[str, int] server_address: server host and port to use
|
||||
:param class handler_class: request handler
|
||||
:param dict config: configuration setting
|
||||
:return:
|
||||
"""
|
||||
self.coreemu = CoreEmu(config)
|
||||
self.config = config
|
||||
socketserver.TCPServer.__init__(self, server_address, handler_class)
|
||||
|
||||
|
||||
class CoreUdpServer(socketserver.ThreadingMixIn, socketserver.UDPServer):
|
||||
"""
|
||||
UDP server class, manages sessions and spawns request handlers for
|
||||
incoming connections.
|
||||
"""
|
||||
daemon_threads = True
|
||||
allow_reuse_address = True
|
||||
|
||||
def __init__(self, server_address, handler_class, mainserver):
|
||||
"""
|
||||
Server class initialization takes configuration data and calls
|
||||
the SocketServer constructor
|
||||
|
||||
:param server_address:
|
||||
:param class handler_class: request handler
|
||||
:param mainserver:
|
||||
"""
|
||||
self.mainserver = mainserver
|
||||
socketserver.UDPServer.__init__(self, server_address, handler_class)
|
||||
|
||||
def start(self):
|
||||
"""
|
||||
Thread target to run concurrently with the TCP server.
|
||||
|
||||
:return: nothing
|
||||
"""
|
||||
self.serve_forever()
|
||||
|
|
|
@ -11,7 +11,7 @@ def convert_node(node_data):
|
|||
"""
|
||||
Convenience method for converting NodeData to a packed TLV message.
|
||||
|
||||
:param core.data.NodeData node_data: node data to convert
|
||||
:param core.emulator.data.NodeData node_data: node data to convert
|
||||
:return: packed node message
|
||||
"""
|
||||
tlv_data = structutils.pack_values(coreapi.CoreNodeTlv, [
|
||||
|
@ -43,7 +43,7 @@ def convert_config(config_data):
|
|||
"""
|
||||
Convenience method for converting ConfigData to a packed TLV message.
|
||||
|
||||
:param core.data.ConfigData config_data: config data to convert
|
||||
:param core.emulator.data.ConfigData config_data: config data to convert
|
||||
:return: packed message
|
||||
"""
|
||||
tlv_data = structutils.pack_values(coreapi.CoreConfigTlv, [
|
||||
|
|
|
@ -20,6 +20,7 @@ VCMD_BIN = which("vcmd")
|
|||
BRCTL_BIN = which("brctl")
|
||||
SYSCTL_BIN = which("sysctl")
|
||||
IP_BIN = which("ip")
|
||||
ETHTOOL_BIN = which("ethtool")
|
||||
TC_BIN = which("tc")
|
||||
EBTABLES_BIN = which("ebtables")
|
||||
MOUNT_BIN = which("mount")
|
||||
|
|
|
@ -2,6 +2,7 @@
|
|||
emane.py: definition of an Emane class for implementing configuration control of an EMANE emulation.
|
||||
"""
|
||||
|
||||
import copy
|
||||
import logging
|
||||
import os
|
||||
import threading
|
||||
|
@ -443,10 +444,13 @@ class EmaneManager(ModelManager):
|
|||
continue
|
||||
|
||||
platformid += 1
|
||||
|
||||
# create temporary config for updating distributed nodes
|
||||
typeflags = ConfigFlags.UPDATE.value
|
||||
self.set_config("platform_id_start", str(platformid))
|
||||
self.set_config("nem_id_start", str(nemid))
|
||||
config_data = ConfigShim.config_data(0, None, typeflags, self.emane_config, self.get_configs())
|
||||
config = copy.deepcopy(self.get_configs())
|
||||
config["platform_id_start"] = str(platformid)
|
||||
config["nem_id_start"] = str(nemid)
|
||||
config_data = ConfigShim.config_data(0, None, typeflags, self.emane_config, config)
|
||||
message = dataconversion.convert_config(config_data)
|
||||
server.sock.send(message)
|
||||
# increment nemid for next server by number of interfaces
|
||||
|
@ -477,26 +481,30 @@ class EmaneManager(ModelManager):
|
|||
be configured. This generates configuration for slave control nets
|
||||
using the default list of prefixes.
|
||||
"""
|
||||
session = self.session
|
||||
# slave server
|
||||
session = self.session
|
||||
if not session.master:
|
||||
return
|
||||
|
||||
servers = session.broker.getservernames()
|
||||
# not distributed
|
||||
servers = session.broker.getservernames()
|
||||
if len(servers) < 2:
|
||||
return
|
||||
|
||||
prefix = session.options.get_config("controlnet")
|
||||
prefixes = prefix.split()
|
||||
# normal Config messaging will distribute controlnets
|
||||
if len(prefixes) >= len(servers):
|
||||
return
|
||||
prefix = session.options.get_config("controlnet", default="")
|
||||
prefixes = prefix.split()
|
||||
if len(prefixes) < len(servers):
|
||||
logging.info("setting up default controlnet prefixes for distributed (%d configured)", len(prefixes))
|
||||
prefix = ctrlnet.DEFAULT_PREFIX_LIST[0]
|
||||
prefixes = prefix.split()
|
||||
servers.remove("localhost")
|
||||
servers.insert(0, "localhost")
|
||||
prefix = " ".join("%s:%s" % (s, prefixes[i]) for i, s in enumerate(servers))
|
||||
|
||||
# this generates a config message having controlnet prefix assignments
|
||||
logging.info("Setting up default controlnet prefixes for distributed (%d configured)" % len(prefixes))
|
||||
prefixes = ctrlnet.DEFAULT_PREFIX_LIST[0]
|
||||
vals = 'controlnet="%s"' % prefixes
|
||||
logging.info("setting up controlnet prefixes for distributed: %s", prefix)
|
||||
vals = "controlnet=%s" % prefix
|
||||
tlvdata = b""
|
||||
tlvdata += coreapi.CoreConfigTlv.pack(ConfigTlvs.OBJECT.value, "session")
|
||||
tlvdata += coreapi.CoreConfigTlv.pack(ConfigTlvs.TYPE.value, 0)
|
||||
|
@ -504,6 +512,7 @@ class EmaneManager(ModelManager):
|
|||
rawmsg = coreapi.CoreConfMessage.pack(0, tlvdata)
|
||||
msghdr = rawmsg[:coreapi.CoreMessage.header_len]
|
||||
msg = coreapi.CoreConfMessage(flags=0, hdr=msghdr, data=rawmsg[coreapi.CoreMessage.header_len:])
|
||||
logging.debug("sending controlnet message:\n%s", msg)
|
||||
self.session.broker.handle_message(msg)
|
||||
|
||||
def check_node_models(self):
|
||||
|
|
|
@ -147,7 +147,7 @@ class EmaneModel(WirelessModel):
|
|||
"""
|
||||
Invoked when a Link Message is received. Default is unimplemented.
|
||||
|
||||
:param core.netns.vif.Veth netif: interface one
|
||||
:param core.nodes.interface.Veth netif: interface one
|
||||
:param bw: bandwidth to set to
|
||||
:param delay: packet delay to set to
|
||||
:param loss: packet loss to set to
|
||||
|
|
|
@ -165,16 +165,16 @@ class EmaneNode(EmaneNet):
|
|||
nemid = self.getnemid(netif)
|
||||
ifname = netif.localname
|
||||
if nemid is None:
|
||||
logging.info("nemid for %s is unknown" % ifname)
|
||||
logging.info("nemid for %s is unknown", ifname)
|
||||
return
|
||||
lat, long, alt = self.session.location.getgeo(x, y, z)
|
||||
logging.info("setnemposition %s (%s) x,y,z=(%d,%d,%s)(%.6f,%.6f,%.6f)", ifname, nemid, x, y, z, lat, long, alt)
|
||||
lat, lon, alt = self.session.location.getgeo(x, y, z)
|
||||
logging.info("setnemposition %s (%s) x,y,z=(%d,%d,%s)(%.6f,%.6f,%.6f)", ifname, nemid, x, y, z, lat, lon, alt)
|
||||
event = LocationEvent()
|
||||
|
||||
# altitude must be an integer or warning is printed
|
||||
# unused: yaw, pitch, roll, azimuth, elevation, velocity
|
||||
alt = int(round(alt))
|
||||
event.append(nemid, latitude=lat, longitude=long, altitude=alt)
|
||||
event.append(nemid, latitude=lat, longitude=lon, altitude=alt)
|
||||
self.session.emane.service.publish(0, event)
|
||||
|
||||
def setnempositions(self, moved_netifs):
|
||||
|
@ -199,12 +199,12 @@ class EmaneNode(EmaneNet):
|
|||
logging.info("nemid for %s is unknown" % ifname)
|
||||
continue
|
||||
x, y, z = netif.node.getposition()
|
||||
lat, long, alt = self.session.location.getgeo(x, y, z)
|
||||
lat, lon, alt = self.session.location.getgeo(x, y, z)
|
||||
logging.info("setnempositions %d %s (%s) x,y,z=(%d,%d,%s)(%.6f,%.6f,%.6f)",
|
||||
i, ifname, nemid, x, y, z, lat, long, alt)
|
||||
i, ifname, nemid, x, y, z, lat, lon, alt)
|
||||
# altitude must be an integer or warning is printed
|
||||
alt = int(round(alt))
|
||||
event.append(nemid, latitude=lat, longitude=long, altitude=alt)
|
||||
event.append(nemid, latitude=lat, longitude=lon, altitude=alt)
|
||||
i += 1
|
||||
|
||||
self.session.emane.service.publish(0, event)
|
||||
|
|
|
@ -32,7 +32,7 @@ def create_interface(node, network, interface_data):
|
|||
Create an interface for a node on a network using provided interface data.
|
||||
|
||||
:param node: node to create interface for
|
||||
:param network: network to associate interface with
|
||||
:param core.nodes.base.CoreNetworkBase network: network to associate interface with
|
||||
:param core.emulator.emudata.InterfaceData interface_data: interface data
|
||||
:return: created interface
|
||||
"""
|
||||
|
@ -134,7 +134,7 @@ class LinkOptions(object):
|
|||
"""
|
||||
Create a LinkOptions object.
|
||||
|
||||
:param core.enumerations.LinkTypes _type: type of link, defaults to wired
|
||||
:param core.emulator.enumerations.LinkTypes _type: type of link, defaults to wired
|
||||
"""
|
||||
self.type = _type
|
||||
self.session = None
|
||||
|
@ -206,7 +206,7 @@ class IpPrefixes(object):
|
|||
Creates interface data for linking nodes, using the nodes unique id for generation, along with a random
|
||||
mac address, unless provided.
|
||||
|
||||
:param core.coreobj.PyCoreNode node: node to create interface for
|
||||
:param core.nodes.base.CoreNode node: node to create interface for
|
||||
:param str name: name to set for interface, default is eth{id}
|
||||
:param str mac: mac address to use for this interface, default is random generation
|
||||
:return: new interface data for the provided node
|
||||
|
@ -255,7 +255,7 @@ class InterfaceData(object):
|
|||
|
||||
:param int _id: interface id
|
||||
:param str name: name for interface
|
||||
:param core.misc.ipaddress.MacAddress mac: mac address
|
||||
:param core.nodes.ipaddress.MacAddress mac: mac address
|
||||
:param str ip4: ipv4 address
|
||||
:param int ip4_mask: ipv4 bit mask
|
||||
:param str ip6: ipv6 address
|
||||
|
@ -270,24 +270,50 @@ class InterfaceData(object):
|
|||
self.ip6_mask = ip6_mask
|
||||
|
||||
def has_ip4(self):
|
||||
"""
|
||||
Determines if interface has an ip4 address.
|
||||
|
||||
:return: True if has ip4, False otherwise
|
||||
"""
|
||||
return all([self.ip4, self.ip4_mask])
|
||||
|
||||
def has_ip6(self):
|
||||
"""
|
||||
Determines if interface has an ip6 address.
|
||||
|
||||
:return: True if has ip6, False otherwise
|
||||
"""
|
||||
return all([self.ip6, self.ip6_mask])
|
||||
|
||||
def ip4_address(self):
|
||||
"""
|
||||
Retrieve a string representation of the ip4 address and netmask.
|
||||
|
||||
:return: ip4 string or None
|
||||
"""
|
||||
if self.has_ip4():
|
||||
return "%s/%s" % (self.ip4, self.ip4_mask)
|
||||
else:
|
||||
return None
|
||||
|
||||
def ip6_address(self):
|
||||
"""
|
||||
Retrieve a string representation of the ip6 address and netmask.
|
||||
|
||||
:return: ip4 string or None
|
||||
"""
|
||||
if self.has_ip6():
|
||||
return "%s/%s" % (self.ip6, self.ip6_mask)
|
||||
else:
|
||||
return None
|
||||
|
||||
def get_addresses(self):
|
||||
"""
|
||||
Returns a list of ip4 and ip6 address when present.
|
||||
|
||||
:return: list of addresses
|
||||
:rtype: list
|
||||
"""
|
||||
ip4 = self.ip4_address()
|
||||
ip6 = self.ip6_address()
|
||||
return [i for i in [ip4, ip6] if i]
|
||||
|
|
|
@ -22,10 +22,10 @@ from core.api.tlv.broker import CoreBroker
|
|||
from core.emane.emanemanager import EmaneManager
|
||||
from core.emulator.data import EventData, NodeData
|
||||
from core.emulator.data import ExceptionData
|
||||
from core.emulator.emudata import LinkOptions, NodeOptions
|
||||
from core.emulator.emudata import IdGen
|
||||
from core.emulator.emudata import is_net_node
|
||||
from core.emulator.emudata import LinkOptions, NodeOptions
|
||||
from core.emulator.emudata import create_interface
|
||||
from core.emulator.emudata import is_net_node
|
||||
from core.emulator.emudata import link_config
|
||||
from core.emulator.enumerations import EventTypes, LinkTypes
|
||||
from core.emulator.enumerations import ExceptionLevels
|
||||
|
@ -314,7 +314,7 @@ class Session(object):
|
|||
:param int node_two_id: node two id
|
||||
:param int interface_one_id: interface id for node one
|
||||
:param int interface_two_id: interface id for node two
|
||||
:param core.enumerations.LinkTypes link_type: link type to delete
|
||||
:param core.emulator.enumerations.LinkTypes link_type: link type to delete
|
||||
:return: nothing
|
||||
"""
|
||||
# get node objects identified by link data
|
||||
|
@ -361,14 +361,25 @@ class Session(object):
|
|||
self.delete_node(net_one.id)
|
||||
node_one.delnetif(interface_one.netindex)
|
||||
node_two.delnetif(interface_two.netindex)
|
||||
elif node_one and net_one:
|
||||
interface = node_one.netif(interface_one_id)
|
||||
logging.info("deleting link node(%s):interface(%s) node(%s)",
|
||||
node_one.name, interface.name, net_one.name)
|
||||
interface.detachnet()
|
||||
node_one.delnetif(interface.netindex)
|
||||
elif node_two and net_one:
|
||||
interface = node_two.netif(interface_two_id)
|
||||
logging.info("deleting link node(%s):interface(%s) node(%s)",
|
||||
node_two.name, interface.name, net_one.name)
|
||||
interface.detachnet()
|
||||
node_two.delnetif(interface.netindex)
|
||||
finally:
|
||||
if node_one:
|
||||
node_one.lock.release()
|
||||
if node_two:
|
||||
node_two.lock.release()
|
||||
|
||||
def update_link(self, node_one_id, node_two_id, interface_one_id=None, interface_two_id=None,
|
||||
link_options=LinkOptions()):
|
||||
def update_link(self, node_one_id, node_two_id, interface_one_id=None, interface_two_id=None, link_options=None):
|
||||
"""
|
||||
Update link information between nodes.
|
||||
|
||||
|
@ -379,6 +390,9 @@ class Session(object):
|
|||
:param core.emulator.emudata.LinkOptions link_options: data to update link with
|
||||
:return: nothing
|
||||
"""
|
||||
if not link_options:
|
||||
link_options = LinkOptions()
|
||||
|
||||
# get node objects identified by link data
|
||||
node_one, node_two, net_one, net_two, _tunnel = self._link_nodes(node_one_id, node_two_id)
|
||||
|
||||
|
@ -441,7 +455,6 @@ class Session(object):
|
|||
link_config(net_one, interface_one, link_options, interface_two=interface_two)
|
||||
if not link_options.unidirectional:
|
||||
link_config(net_one, interface_two, link_options, interface_two=interface_one)
|
||||
|
||||
finally:
|
||||
if node_one:
|
||||
node_one.lock.release()
|
||||
|
@ -452,7 +465,7 @@ class Session(object):
|
|||
"""
|
||||
Add a node to the session, based on the provided node data.
|
||||
|
||||
:param core.enumerations.NodeTypes _type: type of node to create
|
||||
:param core.emulator.enumerations.NodeTypes _type: type of node to create
|
||||
:param int _id: id for node, defaults to None for generated id
|
||||
:param core.emulator.emudata.NodeOptions node_options: data to create node with
|
||||
:return: created node
|
||||
|
@ -574,7 +587,7 @@ class Session(object):
|
|||
"""
|
||||
Broadcast node location to all listeners.
|
||||
|
||||
:param core.netns.nodes.PyCoreObj node: node to broadcast location for
|
||||
:param core.nodes.base.NodeBase node: node to broadcast location for
|
||||
:return: nothing
|
||||
"""
|
||||
node_data = NodeData(
|
||||
|
@ -615,6 +628,9 @@ class Session(object):
|
|||
# clear out existing session
|
||||
self.clear()
|
||||
|
||||
if start:
|
||||
self.set_state(EventTypes.CONFIGURATION_STATE)
|
||||
|
||||
# write out xml file
|
||||
CoreXmlReader(self).read(file_name)
|
||||
|
||||
|
@ -688,7 +704,7 @@ class Session(object):
|
|||
"""
|
||||
Handle a mobility event.
|
||||
|
||||
:param core.data.EventData event_data: event data to handle
|
||||
:param core.emulator.data.EventData event_data: event data to handle
|
||||
:return: nothing
|
||||
"""
|
||||
self.mobility.handleevent(event_data)
|
||||
|
@ -700,7 +716,7 @@ class Session(object):
|
|||
:param int _id: int for node, defaults to None and will be generated
|
||||
:param core.emulator.emudata.NodeOptions node_options: options for emane node, model will always be "mdr"
|
||||
:return: new emane node
|
||||
:rtype: core.netns.nodes.CoreNode
|
||||
:rtype: core.nodes.network.WlanNode
|
||||
"""
|
||||
if not node_options:
|
||||
node_options = NodeOptions()
|
||||
|
@ -768,7 +784,7 @@ class Session(object):
|
|||
"""
|
||||
Handle exception data that should be provided to exception handlers.
|
||||
|
||||
:param core.data.ExceptionData exception_data: exception data to send out
|
||||
:param core.emulator.data.ExceptionData exception_data: exception data to send out
|
||||
:return: nothing
|
||||
"""
|
||||
|
||||
|
@ -779,7 +795,7 @@ class Session(object):
|
|||
"""
|
||||
Handle node data that should be provided to node handlers.
|
||||
|
||||
:param core.data.ExceptionData node_data: node data to send out
|
||||
:param core.emulator.data.ExceptionData node_data: node data to send out
|
||||
:return: nothing
|
||||
"""
|
||||
|
||||
|
@ -801,7 +817,7 @@ class Session(object):
|
|||
"""
|
||||
Handle config data that should be provided to config handlers.
|
||||
|
||||
:param core.data.ConfigData config_data: config data to send out
|
||||
:param core.emulator.data.ConfigData config_data: config data to send out
|
||||
:return: nothing
|
||||
"""
|
||||
|
||||
|
@ -812,7 +828,7 @@ class Session(object):
|
|||
"""
|
||||
Handle link data that should be provided to link handlers.
|
||||
|
||||
:param core.data.ExceptionData link_data: link data to send out
|
||||
:param core.emulator.data.ExceptionData link_data: link data to send out
|
||||
:return: nothing
|
||||
"""
|
||||
|
||||
|
@ -889,7 +905,7 @@ class Session(object):
|
|||
:param str hook_type: hook type
|
||||
:param str file_name: file name for hook
|
||||
:param str source_name: source name
|
||||
:param data: hook data
|
||||
:param str data: hook data
|
||||
:return: nothing
|
||||
"""
|
||||
logging.info("setting state hook: %s - %s from %s", hook_type, file_name, source_name)
|
||||
|
@ -1018,7 +1034,8 @@ class Session(object):
|
|||
variables.
|
||||
|
||||
:param bool state: flag to determine if session state should be included
|
||||
:return:
|
||||
:return: environment variables
|
||||
:rtype: dict
|
||||
"""
|
||||
env = os.environ.copy()
|
||||
env["SESSION"] = "%s" % self.id
|
||||
|
@ -1166,7 +1183,7 @@ class Session(object):
|
|||
with self._nodes_lock:
|
||||
file_path = os.path.join(self.session_dir, "nodes")
|
||||
with open(file_path, "w") as f:
|
||||
for _id in sorted(self.nodes.keys()):
|
||||
for _id in self.nodes.keys():
|
||||
node = self.nodes[_id]
|
||||
f.write("%s %s %s %s\n" % (_id, node.name, node.apitype, type(node)))
|
||||
except IOError:
|
||||
|
@ -1212,19 +1229,19 @@ class Session(object):
|
|||
# write current nodes out to session directory file
|
||||
self.write_nodes()
|
||||
|
||||
# controlnet may be needed by some EMANE models
|
||||
# create control net interfaces and broker network tunnels
|
||||
# which need to exist for emane to sync on location events
|
||||
# in distributed scenarios
|
||||
self.add_remove_control_interface(node=None, remove=False)
|
||||
self.broker.startup()
|
||||
|
||||
# instantiate will be invoked again upon Emane configure
|
||||
if self.emane.startup() == self.emane.NOT_READY:
|
||||
return
|
||||
|
||||
# start feature helpers
|
||||
self.broker.startup()
|
||||
self.mobility.startup()
|
||||
|
||||
# boot the services on each node
|
||||
# boot node services and then start mobility
|
||||
self.boot_nodes()
|
||||
self.mobility.startup()
|
||||
|
||||
# set broker local instantiation to complete
|
||||
self.broker.local_instantiation_complete()
|
||||
|
@ -1496,7 +1513,7 @@ class Session(object):
|
|||
break
|
||||
|
||||
if not prefix:
|
||||
logging.error("Control network prefix not found for server '%s'" % servers[0])
|
||||
logging.error("control network prefix not found for server: %s", servers[0])
|
||||
assign_address = False
|
||||
try:
|
||||
prefix = prefixes[0].split(':', 1)[1]
|
||||
|
@ -1532,7 +1549,7 @@ class Session(object):
|
|||
If conf_reqd is False, the control network may be built even
|
||||
when the user has not configured one (e.g. for EMANE.)
|
||||
|
||||
:param core.netns.vnode.CoreNode node: node to add or remove control interface
|
||||
:param core.nodes.base.CoreNode node: node to add or remove control interface
|
||||
:param int net_index: network index
|
||||
:param bool remove: flag to check if it should be removed
|
||||
:param bool conf_required: flag to check if conf is required
|
||||
|
@ -1615,7 +1632,7 @@ class Session(object):
|
|||
start of the runtime state.
|
||||
|
||||
:param event_time: event time
|
||||
:param core.netns.vnode.CoreNode node: node to add event for
|
||||
:param core.nodes.base.CoreNode node: node to add event for
|
||||
:param str name: name of event
|
||||
:param data: data for event
|
||||
:return: nothing
|
||||
|
|
|
@ -9,7 +9,7 @@ import os
|
|||
import threading
|
||||
import time
|
||||
from builtins import int
|
||||
from past.builtins import cmp
|
||||
from functools import total_ordering
|
||||
|
||||
from core import utils
|
||||
from core.config import ConfigGroup
|
||||
|
@ -41,7 +41,7 @@ class MobilityManager(ModelManager):
|
|||
"""
|
||||
Creates a MobilityManager instance.
|
||||
|
||||
:param core.session.Session session: session this manager is tied to
|
||||
:param core.emulator.session.Session session: session this manager is tied to
|
||||
"""
|
||||
super(MobilityManager, self).__init__()
|
||||
self.session = session
|
||||
|
@ -53,6 +53,14 @@ class MobilityManager(ModelManager):
|
|||
self.physnets = {}
|
||||
self.session.broker.handlers.add(self.physnodehandlelink)
|
||||
|
||||
def reset(self):
|
||||
"""
|
||||
Clear out all current configurations.
|
||||
|
||||
:return: nothing
|
||||
"""
|
||||
self.config_reset()
|
||||
|
||||
def startup(self, node_ids=None):
|
||||
"""
|
||||
Session is transitioning from instantiation to runtime state.
|
||||
|
@ -318,11 +326,11 @@ class BasicRangeModel(WirelessModel):
|
|||
name = "basic_range"
|
||||
options = [
|
||||
Configuration(_id="range", _type=ConfigDataTypes.UINT32, default="275", label="wireless range (pixels)"),
|
||||
Configuration(_id="bandwidth", _type=ConfigDataTypes.UINT32, default="54000000", label="bandwidth (bps)"),
|
||||
Configuration(_id="jitter", _type=ConfigDataTypes.FLOAT, default="0.0", label="transmission jitter (usec)"),
|
||||
Configuration(_id="delay", _type=ConfigDataTypes.FLOAT, default="5000.0",
|
||||
Configuration(_id="bandwidth", _type=ConfigDataTypes.UINT64, default="54000000", label="bandwidth (bps)"),
|
||||
Configuration(_id="jitter", _type=ConfigDataTypes.UINT64, default="0", label="transmission jitter (usec)"),
|
||||
Configuration(_id="delay", _type=ConfigDataTypes.UINT64, default="5000",
|
||||
label="transmission delay (usec)"),
|
||||
Configuration(_id="error", _type=ConfigDataTypes.FLOAT, default="0.0", label="error rate (%)")
|
||||
Configuration(_id="error", _type=ConfigDataTypes.STRING, default="0", label="error rate (%)")
|
||||
]
|
||||
|
||||
@classmethod
|
||||
|
@ -358,19 +366,19 @@ class BasicRangeModel(WirelessModel):
|
|||
:param dict config: values to convert
|
||||
:return: nothing
|
||||
"""
|
||||
self.range = float(config["range"])
|
||||
self.range = int(float(config["range"]))
|
||||
logging.info("basic range model configured for WLAN %d using range %d", self.wlan.id, self.range)
|
||||
self.bw = int(config["bandwidth"])
|
||||
if self.bw == 0.0:
|
||||
if self.bw == 0:
|
||||
self.bw = None
|
||||
self.delay = float(config["delay"])
|
||||
if self.delay == 0.0:
|
||||
self.delay = int(config["delay"])
|
||||
if self.delay == 0:
|
||||
self.delay = None
|
||||
self.loss = float(config["error"])
|
||||
if self.loss == 0.0:
|
||||
self.loss = int(config["error"])
|
||||
if self.loss == 0:
|
||||
self.loss = None
|
||||
self.jitter = float(config["jitter"])
|
||||
if self.jitter == 0.0:
|
||||
self.jitter = int(config["jitter"])
|
||||
if self.jitter == 0:
|
||||
self.jitter = None
|
||||
|
||||
def setlinkparams(self):
|
||||
|
@ -468,8 +476,6 @@ class BasicRangeModel(WirelessModel):
|
|||
with self.wlan._linked_lock:
|
||||
linked = self.wlan.linked(a, b)
|
||||
|
||||
logging.debug("checking range netif1(%s) netif2(%s): linked(%s) actual(%s) > config(%s)",
|
||||
a.name, b.name, linked, d, self.range)
|
||||
if d > self.range:
|
||||
if linked:
|
||||
logging.debug("was linked, unlinking")
|
||||
|
@ -533,8 +539,8 @@ class BasicRangeModel(WirelessModel):
|
|||
"""
|
||||
Send a wireless link/unlink API message to the GUI.
|
||||
|
||||
:param core.coreobj.PyCoreNetIf netif: interface one
|
||||
:param core.coreobj.PyCoreNetIf netif2: interface two
|
||||
:param core.nodes.interface.CoreInterface netif: interface one
|
||||
:param core.nodes.interface.CoreInterface netif2: interface two
|
||||
:param bool unlink: unlink or not
|
||||
:return: nothing
|
||||
"""
|
||||
|
@ -563,6 +569,7 @@ class BasicRangeModel(WirelessModel):
|
|||
return all_links
|
||||
|
||||
|
||||
@total_ordering
|
||||
class WayPoint(object):
|
||||
"""
|
||||
Maintains information regarding waypoints.
|
||||
|
@ -582,18 +589,17 @@ class WayPoint(object):
|
|||
self.coords = coords
|
||||
self.speed = speed
|
||||
|
||||
def __cmp__(self, other):
|
||||
"""
|
||||
Custom comparison method for waypoints.
|
||||
def __eq__(self, other):
|
||||
return (self.time, self.nodenum) == (other.time, other.nodedum)
|
||||
|
||||
:param WayPoint other: waypoint to compare to
|
||||
:return: the comparison result against the other waypoint
|
||||
:rtype: int
|
||||
"""
|
||||
tmp = cmp(self.time, other.time)
|
||||
if tmp == 0:
|
||||
tmp = cmp(self.nodenum, other.nodenum)
|
||||
return tmp
|
||||
def __ne__(self, other):
|
||||
return not self == other
|
||||
|
||||
def __lt__(self, other):
|
||||
result = self.time < other.time
|
||||
if result:
|
||||
result = self.nodenum < other.nodenum
|
||||
return result
|
||||
|
||||
|
||||
class WayPointMobility(WirelessModel):
|
||||
|
@ -611,7 +617,7 @@ class WayPointMobility(WirelessModel):
|
|||
"""
|
||||
Create a WayPointMobility instance.
|
||||
|
||||
:param core.session.Session session: CORE session instance
|
||||
:param core.emulator.session.Session session: CORE session instance
|
||||
:param int _id: object id
|
||||
:return:
|
||||
"""
|
||||
|
@ -702,7 +708,7 @@ class WayPointMobility(WirelessModel):
|
|||
Calculate next node location and update its coordinates.
|
||||
Returns True if the node's position has changed.
|
||||
|
||||
:param core.netns.vnode.CoreNode node: node to move
|
||||
:param core.nodes.base.CoreNode node: node to move
|
||||
:param dt: move factor
|
||||
:return: True if node was moved, False otherwise
|
||||
:rtype: bool
|
||||
|
@ -838,7 +844,12 @@ class WayPointMobility(WirelessModel):
|
|||
:param z: z position
|
||||
:return: nothing
|
||||
"""
|
||||
# this would cause PyCoreNetIf.poshook() callback (range calculation)
|
||||
if x is not None:
|
||||
x = int(x)
|
||||
if y is not None:
|
||||
y = int(y)
|
||||
if z is not None:
|
||||
z = int(z)
|
||||
node.position.set(x, y, z)
|
||||
node_data = node.data(message_type=0)
|
||||
self.session.broadcast_node(node_data)
|
||||
|
@ -929,9 +940,8 @@ class Ns2ScriptedMobility(WayPointMobility):
|
|||
"""
|
||||
Creates a Ns2ScriptedMobility instance.
|
||||
|
||||
:param core.session.Session session: CORE session instance
|
||||
:param core.emulator.session.Session session: CORE session instance
|
||||
:param int _id: object id
|
||||
:param config: values
|
||||
"""
|
||||
super(Ns2ScriptedMobility, self).__init__(session=session, _id=_id)
|
||||
self._netifs = {}
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
"""
|
||||
PyCoreNode and LxcNode classes that implement the network namespac virtual node.
|
||||
Defines the base logic for nodes used within core.
|
||||
"""
|
||||
|
||||
import errno
|
||||
|
@ -11,9 +11,10 @@ import signal
|
|||
import socket
|
||||
import string
|
||||
import threading
|
||||
from builtins import range
|
||||
from socket import AF_INET, AF_INET6
|
||||
|
||||
from builtins import range
|
||||
|
||||
from core import CoreCommandError, utils
|
||||
from core import constants
|
||||
from core.emulator.data import NodeData, LinkData
|
||||
|
@ -21,7 +22,6 @@ from core.emulator.enumerations import NodeTypes, LinkTypes
|
|||
from core.nodes import client, nodeutils, ipaddress
|
||||
from core.nodes.interface import TunTap, CoreInterface
|
||||
from core.nodes.interface import Veth
|
||||
from core.nodes.ipaddress import MacAddress
|
||||
|
||||
_DEFAULT_MTU = 1500
|
||||
|
||||
|
@ -39,7 +39,7 @@ class NodeBase(object):
|
|||
"""
|
||||
Creates a PyCoreObj instance.
|
||||
|
||||
:param core.session.Session session: CORE session object
|
||||
:param core.emulator.session.Session session: CORE session object
|
||||
:param int _id: id
|
||||
:param str name: object name
|
||||
:param bool start: start value
|
||||
|
@ -226,7 +226,7 @@ class CoreNodeBase(NodeBase):
|
|||
"""
|
||||
Create a CoreNodeBase instance.
|
||||
|
||||
:param core.session.Session session: CORE session object
|
||||
:param core.emulator.session.Session session: CORE session object
|
||||
:param int _id: object id
|
||||
:param str name: object name
|
||||
:param bool start: boolean for starting
|
||||
|
@ -417,14 +417,6 @@ class CoreNodeBase(NodeBase):
|
|||
class CoreNode(CoreNodeBase):
|
||||
"""
|
||||
Provides standard core node logic.
|
||||
|
||||
:var nodedir: str
|
||||
:var ctrlchnlname: str
|
||||
:var client: core.netns.vnodeclient.VnodeClient
|
||||
:var pid: int
|
||||
:var up: bool
|
||||
:var lock: threading.RLock
|
||||
:var _mounts: list[tuple[str, str]]
|
||||
"""
|
||||
apitype = NodeTypes.DEFAULT.value
|
||||
valid_address_types = {"inet", "inet6", "inet6link"}
|
||||
|
@ -433,7 +425,7 @@ class CoreNode(CoreNodeBase):
|
|||
"""
|
||||
Create a CoreNode instance.
|
||||
|
||||
:param core.session.Session session: core session 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
|
||||
|
@ -646,7 +638,7 @@ class CoreNode(CoreNodeBase):
|
|||
|
||||
:param int ifindex: index for the new interface
|
||||
:param str ifname: name for the new interface
|
||||
:param net: network to associate interface with
|
||||
:param core.nodes.base.CoreNetworkBase net: network to associate interface with
|
||||
:return: nothing
|
||||
"""
|
||||
with self.lock:
|
||||
|
@ -676,6 +668,7 @@ 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"])
|
||||
|
||||
veth.name = ifname
|
||||
|
||||
|
@ -736,7 +729,7 @@ class CoreNode(CoreNodeBase):
|
|||
Set hardware addres for an interface.
|
||||
|
||||
:param int ifindex: index of interface to set hardware address for
|
||||
:param core.misc.ipaddress.MacAddress addr: hardware address to set
|
||||
:param core.nodes.ipaddress.MacAddress addr: hardware address to set
|
||||
:return: nothing
|
||||
:raises CoreCommandError: when a non-zero exit status occurs
|
||||
"""
|
||||
|
@ -819,9 +812,9 @@ class CoreNode(CoreNodeBase):
|
|||
"""
|
||||
Create a new network interface.
|
||||
|
||||
:param net: network to associate with
|
||||
:param core.nodes.base.CoreNetworkBase net: network to associate with
|
||||
:param list addrlist: addresses to add on the interface
|
||||
:param core.misc.ipaddress.MacAddress hwaddr: hardware address to set for interface
|
||||
:param core.nodes.ipaddress.MacAddress hwaddr: hardware address to set for interface
|
||||
:param int ifindex: index of interface to create
|
||||
:param str ifname: name for interface
|
||||
:return: interface index
|
||||
|
@ -864,7 +857,7 @@ class CoreNode(CoreNodeBase):
|
|||
Connect a node.
|
||||
|
||||
:param str ifname: name of interface to connect
|
||||
:param core.netns.nodes.LxcNode othernode: node to connect to
|
||||
:param core.nodes.CoreNodeBase othernode: node to connect to
|
||||
:param str otherifname: interface name to connect to
|
||||
:return: nothing
|
||||
"""
|
||||
|
@ -970,9 +963,9 @@ class CoreNetworkBase(NodeBase):
|
|||
|
||||
def __init__(self, session, _id, name, start=True):
|
||||
"""
|
||||
Create a PyCoreNet instance.
|
||||
Create a CoreNetworkBase instance.
|
||||
|
||||
:param core.session.Session session: CORE session object
|
||||
:param core.emulator.session.Session session: CORE session object
|
||||
:param int _id: object id
|
||||
:param str name: object name
|
||||
:param bool start: should object start
|
||||
|
|
|
@ -1,8 +1,7 @@
|
|||
"""
|
||||
client.py: implementation of the VnodeClient class for issuing commands
|
||||
over a control channel to the vnoded process running in a network namespace.
|
||||
The control channel can be accessed via calls to the vcmd Python module or
|
||||
by invoking the vcmd shell command.
|
||||
The control channel can be accessed via calls using the vcmd shell.
|
||||
"""
|
||||
|
||||
import logging
|
||||
|
@ -74,7 +73,7 @@ class VnodeClient(object):
|
|||
|
||||
# run command, return process when not waiting
|
||||
cmd = self._cmd_args() + args
|
||||
logging.info("cmd wait(%s): %s", wait, cmd)
|
||||
logging.debug("cmd wait(%s): %s", wait, cmd)
|
||||
p = Popen(cmd, stdout=PIPE, stderr=PIPE)
|
||||
if not wait:
|
||||
return 0
|
||||
|
@ -124,10 +123,8 @@ class VnodeClient(object):
|
|||
"""
|
||||
self._verify_connection()
|
||||
args = utils.split_args(args)
|
||||
# if isinstance(args, list):
|
||||
# args = " ".join(args)
|
||||
cmd = self._cmd_args() + args
|
||||
logging.info("popen: %s", cmd)
|
||||
logging.debug("popen: %s", cmd)
|
||||
p = Popen(cmd, stdout=PIPE, stderr=PIPE, stdin=PIPE)
|
||||
return p, p.stdin, p.stdout, p.stderr
|
||||
|
||||
|
@ -160,7 +157,7 @@ class VnodeClient(object):
|
|||
# run command, return process when not waiting
|
||||
args = utils.split_args(args)
|
||||
cmd = self._cmd_args() + args
|
||||
logging.info("redircmd: %s", cmd)
|
||||
logging.debug("redircmd: %s", cmd)
|
||||
p = Popen(cmd, stdin=infd, stdout=outfd, stderr=errfd)
|
||||
|
||||
if not wait:
|
||||
|
|
|
@ -24,7 +24,7 @@ class CoreInterface(object):
|
|||
"""
|
||||
Creates a PyCoreNetIf instance.
|
||||
|
||||
:param core.coreobj.PyCoreNode node: node for interface
|
||||
:param core.nodes.base.CoreNode node: node for interface
|
||||
:param str name: interface name
|
||||
:param mtu: mtu value
|
||||
"""
|
||||
|
@ -67,7 +67,7 @@ class CoreInterface(object):
|
|||
"""
|
||||
Attach network.
|
||||
|
||||
:param core.coreobj.PyCoreNet net: network to attach
|
||||
:param core.nodes.base.CoreNetworkBase net: network to attach
|
||||
:return: nothing
|
||||
"""
|
||||
if self.net:
|
||||
|
@ -109,7 +109,7 @@ class CoreInterface(object):
|
|||
"""
|
||||
Set hardware address.
|
||||
|
||||
:param core.misc.ipaddress.MacAddress addr: hardware address to set to.
|
||||
:param core.nodes.ipaddress.MacAddress addr: hardware address to set to.
|
||||
:return: nothing
|
||||
"""
|
||||
self.hwaddr = addr
|
||||
|
@ -199,7 +199,7 @@ class Veth(CoreInterface):
|
|||
"""
|
||||
Creates a VEth instance.
|
||||
|
||||
:param core.netns.vnode.SimpleLxcNode node: related core node
|
||||
:param core.nodes.base.CoreNode node: related core node
|
||||
:param str name: interface name
|
||||
:param str localname: interface local name
|
||||
:param mtu: interface mtu
|
||||
|
@ -260,11 +260,11 @@ class TunTap(CoreInterface):
|
|||
"""
|
||||
Create a TunTap instance.
|
||||
|
||||
:param core.netns.vnode.SimpleLxcNode node: related core node
|
||||
:param core.nodes.base.CoreNode node: related core node
|
||||
:param str name: interface name
|
||||
:param str localname: local interface name
|
||||
:param mtu: interface mtu
|
||||
:param net: related network
|
||||
:param core.nodes.base.CoreNetworkBase net: related network
|
||||
:param bool start: start flag
|
||||
"""
|
||||
CoreInterface.__init__(self, node=node, name=name, mtu=mtu)
|
||||
|
@ -420,9 +420,9 @@ class GreTap(CoreInterface):
|
|||
"""
|
||||
Creates a GreTap instance.
|
||||
|
||||
:param core.netns.vnode.SimpleLxcNode node: related core node
|
||||
:param core.nodes.base.CoreNode node: related core node
|
||||
:param str name: interface name
|
||||
:param core.session.Session session: core session instance
|
||||
:param core.emulator.session.Session session: core session instance
|
||||
:param mtu: interface mtu
|
||||
:param str remoteip: remote address
|
||||
:param int _id: object id
|
||||
|
@ -493,6 +493,6 @@ class GreTap(CoreInterface):
|
|||
|
||||
:param flags: link flags
|
||||
:return: link data
|
||||
:rtype: list[core.data.LinkData]
|
||||
:rtype: list[core.emulator.data.LinkData]
|
||||
"""
|
||||
return []
|
||||
|
|
|
@ -215,7 +215,7 @@ class IpPrefix(object):
|
|||
Create a IpPrefix instance.
|
||||
|
||||
:param int af: address family for ip prefix
|
||||
:param prefixstr: ip prefix string
|
||||
:param str prefixstr: ip prefix string
|
||||
"""
|
||||
# prefixstr format: address/prefixlen
|
||||
tmp = prefixstr.split("/")
|
||||
|
|
|
@ -1,6 +1,5 @@
|
|||
"""
|
||||
PyCoreNet and LxBrNet classes that implement virtual networks using
|
||||
Linux Ethernet bridging and ebtables rules.
|
||||
Defines network nodes used within core.
|
||||
"""
|
||||
|
||||
import logging
|
||||
|
@ -343,7 +342,7 @@ class CoreNetwork(CoreNetworkBase):
|
|||
"""
|
||||
Detach a network interface.
|
||||
|
||||
:param core.netns.vif.Veth netif: network interface to detach
|
||||
:param core.nodes.interface.Veth netif: network interface to detach
|
||||
:return: nothing
|
||||
"""
|
||||
if self.up:
|
||||
|
@ -355,8 +354,8 @@ class CoreNetwork(CoreNetworkBase):
|
|||
"""
|
||||
Determine if the provided network interfaces are linked.
|
||||
|
||||
:param core.netns.vif.Veth netif1: interface one
|
||||
:param core.netns.vif.Veth netif2: interface two
|
||||
:param core.nodes.interface.CoreInterface netif1: interface one
|
||||
:param core.nodes.interface.CoreInterface netif2: interface two
|
||||
:return: True if interfaces are linked, False otherwise
|
||||
:rtype: bool
|
||||
"""
|
||||
|
@ -385,8 +384,8 @@ class CoreNetwork(CoreNetworkBase):
|
|||
Unlink two PyCoreNetIfs, resulting in adding or removing ebtables
|
||||
filtering rules.
|
||||
|
||||
:param core.netns.vif.Veth netif1: interface one
|
||||
:param core.netns.vif.Veth netif2: interface two
|
||||
:param core.nodes.interface.CoreInterface netif1: interface one
|
||||
:param core.nodes.interface.CoreInterface netif2: interface two
|
||||
:return: nothing
|
||||
"""
|
||||
with self._linked_lock:
|
||||
|
@ -401,8 +400,8 @@ class CoreNetwork(CoreNetworkBase):
|
|||
Link two PyCoreNetIfs together, resulting in adding or removing
|
||||
ebtables filtering rules.
|
||||
|
||||
:param core.netns.vif.Veth netif1: interface one
|
||||
:param core.netns.vif.Veth netif2: interface two
|
||||
:param core.nodes.interface.CoreInterface netif1: interface one
|
||||
:param core.nodes.interface.CoreInterface netif2: interface two
|
||||
:return: nothing
|
||||
"""
|
||||
with self._linked_lock:
|
||||
|
@ -417,7 +416,7 @@ class CoreNetwork(CoreNetworkBase):
|
|||
"""
|
||||
Configure link parameters by applying tc queuing disciplines on the interface.
|
||||
|
||||
:param core.netns.vif.Veth netif: interface one
|
||||
:param core.nodes.interface.Veth netif: interface one
|
||||
:param bw: bandwidth to set to
|
||||
:param delay: packet delay to set to
|
||||
:param loss: packet loss to set to
|
||||
|
@ -460,10 +459,10 @@ class CoreNetwork(CoreNetworkBase):
|
|||
netem = ["netem"]
|
||||
changed = max(changed, netif.setparam("delay", delay))
|
||||
if loss is not None:
|
||||
loss = float(loss)
|
||||
loss = int(loss)
|
||||
changed = max(changed, netif.setparam("loss", loss))
|
||||
if duplicate is not None:
|
||||
duplicate = float(duplicate)
|
||||
duplicate = int(duplicate)
|
||||
changed = max(changed, netif.setparam("duplicate", duplicate))
|
||||
changed = max(changed, netif.setparam("jitter", jitter))
|
||||
if not changed:
|
||||
|
@ -549,9 +548,9 @@ class CoreNetwork(CoreNetworkBase):
|
|||
Return the interface of that links this net with another net
|
||||
(that were linked using linknet()).
|
||||
|
||||
:param core.netns.vnet.LxBrNet net: interface to get link for
|
||||
:param core.nodes.base.CoreNetworkBase net: interface to get link for
|
||||
:return: interface the provided network is linked to
|
||||
:rtype: core.netns.vnet.LxBrNet
|
||||
:rtype: core.nodes.interface.CoreInterface
|
||||
"""
|
||||
for netif in self.netifs():
|
||||
if hasattr(netif, "othernet") and netif.othernet == net:
|
||||
|
@ -584,7 +583,7 @@ class GreTapBridge(CoreNetwork):
|
|||
"""
|
||||
Create a GreTapBridge instance.
|
||||
|
||||
:param core.session.Session session: core session instance
|
||||
:param core.emulator.session.Session session: core session instance
|
||||
:param str remoteip: remote address
|
||||
:param int _id: object id
|
||||
:param str name: object name
|
||||
|
@ -593,7 +592,6 @@ class GreTapBridge(CoreNetwork):
|
|||
:param ttl: ttl value
|
||||
:param key: gre tap key
|
||||
:param bool start: start flag
|
||||
:return:
|
||||
"""
|
||||
CoreNetwork.__init__(self, session=session, _id=_id, name=name, policy=policy, start=False)
|
||||
self.grekey = key
|
||||
|
@ -685,7 +683,7 @@ class CtrlNet(CoreNetwork):
|
|||
"""
|
||||
Creates a CtrlNet instance.
|
||||
|
||||
:param core.session.Session session: core session instance
|
||||
:param core.emulator.session.Session session: core session instance
|
||||
:param int _id: node id
|
||||
:param str name: node namee
|
||||
:param prefix: control network ipv4 prefix
|
||||
|
@ -740,7 +738,7 @@ class CtrlNet(CoreNetwork):
|
|||
|
||||
def detectoldbridge(self):
|
||||
"""
|
||||
Occassionally, control net bridges from previously closed sessions are not cleaned up.
|
||||
Occasionally, control net bridges from previously closed sessions are not cleaned up.
|
||||
Check if there are old control net bridges and delete them
|
||||
|
||||
:return: True if an old bridge was detected, False otherwise
|
||||
|
@ -825,7 +823,7 @@ class PtpNet(CoreNetwork):
|
|||
:param float lon: longitude
|
||||
:param float alt: altitude
|
||||
:return: node data object
|
||||
:rtype: core.data.NodeData
|
||||
:rtype: core.emulator.data.NodeData
|
||||
"""
|
||||
return None
|
||||
|
||||
|
@ -836,7 +834,7 @@ class PtpNet(CoreNetwork):
|
|||
|
||||
:param flags: message flags
|
||||
:return: list of link data
|
||||
:rtype: list[core.data.LinkData]
|
||||
:rtype: list[core.emulator.data.LinkData]
|
||||
"""
|
||||
|
||||
all_links = []
|
||||
|
@ -845,7 +843,6 @@ class PtpNet(CoreNetwork):
|
|||
return all_links
|
||||
|
||||
if1, if2 = self._netif.values()
|
||||
|
||||
unidirectional = 0
|
||||
if if1.getparams() != if2.getparams():
|
||||
unidirectional = 1
|
||||
|
@ -920,10 +917,11 @@ class PtpNet(CoreNetwork):
|
|||
message_type=0,
|
||||
node1_id=if2.node.id,
|
||||
node2_id=if1.node.id,
|
||||
delay=if1.getparam("delay"),
|
||||
bandwidth=if1.getparam("bw"),
|
||||
dup=if1.getparam("duplicate"),
|
||||
jitter=if1.getparam("jitter"),
|
||||
delay=if2.getparam("delay"),
|
||||
bandwidth=if2.getparam("bw"),
|
||||
per=if2.getparam("loss"),
|
||||
dup=if2.getparam("duplicate"),
|
||||
jitter=if2.getparam("jitter"),
|
||||
unidirectional=1,
|
||||
interface1_id=if2.node.getifindex(if2),
|
||||
interface2_id=if1.node.getifindex(if1)
|
||||
|
@ -997,7 +995,7 @@ class WlanNode(CoreNetwork):
|
|||
"""
|
||||
Attach a network interface.
|
||||
|
||||
:param core.netns.vif.VEth netif: network interface
|
||||
:param core.nodes.interface.CoreInterface netif: network interface
|
||||
:return: nothing
|
||||
"""
|
||||
CoreNetwork.attach(self, netif)
|
||||
|
@ -1013,21 +1011,14 @@ class WlanNode(CoreNetwork):
|
|||
"""
|
||||
Sets the mobility and wireless model.
|
||||
|
||||
:param core.mobility.WirelessModel.cls model: wireless model to set to
|
||||
:param core.location.mobility.WirelessModel.cls model: wireless model to set to
|
||||
:param dict config: configuration for model being set
|
||||
:return: nothing
|
||||
"""
|
||||
logging.info("adding model: %s", model.name)
|
||||
if model.config_type == RegisterTlvs.WIRELESS.value:
|
||||
self.model = model(session=self.session, _id=self.id)
|
||||
self.model.update_config(config)
|
||||
if self.model.position_callback:
|
||||
for netif in self.netifs():
|
||||
netif.poshook = self.model.position_callback
|
||||
if netif.node is not None:
|
||||
x, y, z = netif.node.position.get()
|
||||
netif.poshook(netif, x, y, z)
|
||||
self.model.setlinkparams()
|
||||
self.updatemodel(config)
|
||||
elif model.config_type == RegisterTlvs.MOBILITY.value:
|
||||
self.mobility = model(session=self.session, _id=self.id)
|
||||
self.mobility.update_config(config)
|
||||
|
@ -1035,20 +1026,19 @@ class WlanNode(CoreNetwork):
|
|||
def update_mobility(self, config):
|
||||
if not self.mobility:
|
||||
raise ValueError("no mobility set to update for node(%s)", self.id)
|
||||
self.mobility.set_configs(config, node_id=self.id)
|
||||
self.mobility.update_config(config)
|
||||
|
||||
def updatemodel(self, config):
|
||||
if not self.model:
|
||||
raise ValueError("no model set to update for node(%s)", self.id)
|
||||
logging.info("node(%s) updating model(%s): %s", self.id, self.model.name, config)
|
||||
self.model.set_configs(config, node_id=self.id)
|
||||
self.model.update_config(config)
|
||||
if self.model.position_callback:
|
||||
for netif in self.netifs():
|
||||
netif.poshook = self.model.position_callback
|
||||
if netif.node is not None:
|
||||
x, y, z = netif.node.position.get()
|
||||
netif.poshook(netif, x, y, z)
|
||||
self.model.updateconfig()
|
||||
|
||||
def all_link_data(self, flags):
|
||||
"""
|
||||
|
@ -1056,7 +1046,7 @@ class WlanNode(CoreNetwork):
|
|||
|
||||
:param flags: message flags
|
||||
:return: list of link data
|
||||
:rtype: list[core.data.LinkData]
|
||||
:rtype: list[core.emulator.data.LinkData]
|
||||
"""
|
||||
all_links = CoreNetwork.all_link_data(self, flags)
|
||||
|
||||
|
|
|
@ -23,7 +23,7 @@ def _convert_map(x, y):
|
|||
|
||||
:param dict x: dictionary to reduce node items into
|
||||
:param tuple y: current node item
|
||||
:return:
|
||||
:return: human readable name mapping of the node map
|
||||
"""
|
||||
x[y[0].name] = y[1]
|
||||
return x
|
||||
|
@ -33,7 +33,6 @@ def update_node_map(node_map):
|
|||
"""
|
||||
Update the current node map with the provided node map values.
|
||||
|
||||
|
||||
:param dict node_map: node map to update with
|
||||
"""
|
||||
global _NODE_MAP
|
||||
|
@ -70,7 +69,7 @@ def get_node_type(node_class):
|
|||
|
||||
:param class node_class: node class to get type for
|
||||
:return: node type
|
||||
:rtype: core.enumerations.NodeTypes
|
||||
:rtype: core.emulator.enumerations.NodeTypes
|
||||
"""
|
||||
global _NODE_MAP
|
||||
node_type_map = {_NODE_MAP[x]: x for x in _NODE_MAP}
|
||||
|
|
|
@ -53,12 +53,11 @@ class OvsNet(CoreNetworkBase):
|
|||
"""
|
||||
Creates an OvsNet instance.
|
||||
|
||||
:param core.session.Session session: session this object is a part of
|
||||
:param _id:
|
||||
:param name:
|
||||
:param start:
|
||||
:param policy:
|
||||
:return:
|
||||
:param core.emulator.session.Session session: session this object is a part of
|
||||
:param int _id: object id
|
||||
:param str name: object name
|
||||
:param bool start: start flag
|
||||
:param policy: network policy
|
||||
"""
|
||||
|
||||
CoreNetworkBase.__init__(self, session, _id, name, start)
|
||||
|
@ -173,7 +172,7 @@ class OvsNet(CoreNetworkBase):
|
|||
|
||||
def link(self, interface_one, interface_two):
|
||||
"""
|
||||
Link two PyCoreNetIfs together, resulting in adding or removing
|
||||
Link two interfaces together, resulting in adding or removing
|
||||
ebtables filtering rules.
|
||||
"""
|
||||
with self._linked_lock:
|
||||
|
@ -226,11 +225,11 @@ class OvsNet(CoreNetworkBase):
|
|||
delay_changed = netif.setparam("delay", delay)
|
||||
|
||||
if loss is not None:
|
||||
loss = float(loss)
|
||||
loss = int(loss)
|
||||
loss_changed = netif.setparam("loss", loss)
|
||||
|
||||
if duplicate is not None:
|
||||
duplicate = float(duplicate)
|
||||
duplicate = int(duplicate)
|
||||
duplicate_changed = netif.setparam("duplicate", duplicate)
|
||||
jitter_changed = netif.setparam("jitter", jitter)
|
||||
|
||||
|
@ -299,19 +298,11 @@ class OvsNet(CoreNetworkBase):
|
|||
interface = Veth(node=None, name=name, localname=localname, mtu=1500, net=self, start=self.up)
|
||||
self.attach(interface)
|
||||
if network.up:
|
||||
# this is similar to net.attach() but uses netif.name instead
|
||||
# of localname
|
||||
# this is similar to net.attach() but uses netif.name instead of localname
|
||||
utils.check_cmd([constants.OVS_BIN, "add-port", network.bridge_name, interface.name])
|
||||
utils.check_cmd([constants.IP_BIN, "link", "set", interface.name, "up"])
|
||||
|
||||
# TODO: is there a native method for this? see if this causes issues
|
||||
# i = network.newifindex()
|
||||
# network._netif[i] = interface
|
||||
# with network._linked_lock:
|
||||
# network._linked[interface] = {}
|
||||
# this method call is equal to the above, with a interface.netifi = call
|
||||
network.attach(interface)
|
||||
|
||||
interface.net = self
|
||||
interface.othernet = network
|
||||
return interface
|
||||
|
|
|
@ -99,7 +99,7 @@ class PhysicalNode(CoreNodeBase):
|
|||
|
||||
def sethwaddr(self, ifindex, addr):
|
||||
"""
|
||||
same as SimpleLxcNode.sethwaddr()
|
||||
Set hardware address for an interface.
|
||||
"""
|
||||
self._netif[ifindex].sethwaddr(addr)
|
||||
ifname = self.ifname(ifindex)
|
||||
|
@ -108,7 +108,7 @@ class PhysicalNode(CoreNodeBase):
|
|||
|
||||
def addaddr(self, ifindex, addr):
|
||||
"""
|
||||
same as SimpleLxcNode.addaddr()
|
||||
Add an address to an interface.
|
||||
"""
|
||||
if self.up:
|
||||
self.check_cmd([constants.IP_BIN, "addr", "add", str(addr), "dev", self.ifname(ifindex)])
|
||||
|
@ -117,7 +117,7 @@ class PhysicalNode(CoreNodeBase):
|
|||
|
||||
def deladdr(self, ifindex, addr):
|
||||
"""
|
||||
same as SimpleLxcNode.deladdr()
|
||||
Delete an address from an interface.
|
||||
"""
|
||||
try:
|
||||
self._netif[ifindex].deladdr(addr)
|
||||
|
@ -258,7 +258,7 @@ class Rj45Node(CoreNodeBase, CoreInterface):
|
|||
"""
|
||||
Create an RJ45Node instance.
|
||||
|
||||
:param core.session.Session session: core session instance
|
||||
:param core.emulator.session.Session session: core session instance
|
||||
:param int _id: node id
|
||||
:param str name: node name
|
||||
:param mtu: rj45 mtu
|
||||
|
@ -336,7 +336,7 @@ class Rj45Node(CoreNodeBase, CoreInterface):
|
|||
represents an interface, we do not create another object here,
|
||||
but attach ourselves to the given network.
|
||||
|
||||
:param core.coreobj.PyCoreNet net: new network instance
|
||||
:param core.nodes.base.CoreNetworkBase net: new network instance
|
||||
:param list[str] addrlist: address list
|
||||
:param str hwaddr: hardware address
|
||||
:param int ifindex: interface index
|
||||
|
@ -392,7 +392,7 @@ class Rj45Node(CoreNodeBase, CoreInterface):
|
|||
:param int ifindex: interface index to retrieve
|
||||
:param net: network to retrieve
|
||||
:return: a network interface
|
||||
:rtype: core.coreobj.PyCoreNetIf
|
||||
:rtype: core.nodes.interface,CoreInterface
|
||||
"""
|
||||
if net is not None and net == self.net:
|
||||
return self
|
||||
|
@ -409,7 +409,7 @@ class Rj45Node(CoreNodeBase, CoreInterface):
|
|||
"""
|
||||
Retrieve network interface index.
|
||||
|
||||
:param core.coreobj.PyCoreNetIf netif: network interface to retrieve index for
|
||||
:param core.nodes.interface.CoreInterface netif: network interface to retrieve index for
|
||||
:return: interface index, None otherwise
|
||||
:rtype: int
|
||||
"""
|
||||
|
|
|
@ -61,7 +61,7 @@ class Sdt(object):
|
|||
"""
|
||||
Creates a Sdt instance.
|
||||
|
||||
:param core.session.Session session: session this manager is tied to
|
||||
:param core.emulator.session.Session session: session this manager is tied to
|
||||
"""
|
||||
self.session = session
|
||||
self.sock = None
|
||||
|
@ -83,7 +83,7 @@ class Sdt(object):
|
|||
"""
|
||||
Handler for node updates, specifically for updating their location.
|
||||
|
||||
:param core.data.NodeData node_data: node data being updated
|
||||
:param core.emulator.data.NodeData node_data: node data being updated
|
||||
:return: nothing
|
||||
"""
|
||||
x = node_data.x_position
|
||||
|
@ -103,7 +103,7 @@ class Sdt(object):
|
|||
"""
|
||||
Handler for link updates, checking for wireless link/unlink messages.
|
||||
|
||||
:param core.data.LinkData link_data: link data being updated
|
||||
:param core.emulator.data.LinkData link_data: link data being updated
|
||||
:return: nothing
|
||||
"""
|
||||
if link_data.link_type == LinkTypes.WIRELESS.value:
|
||||
|
|
|
@ -125,7 +125,7 @@ class ServiceShim(object):
|
|||
Convert service properties into a string list of key=value pairs,
|
||||
separated by "|".
|
||||
|
||||
:param core.netns.vnode.CoreNode node: node to get value list for
|
||||
:param core.nodes.base.CoreNode node: node to get value list for
|
||||
:param CoreService service: service to get value list for
|
||||
:return: value list string
|
||||
:rtype: str
|
||||
|
@ -441,7 +441,7 @@ class CoreServices(object):
|
|||
"""
|
||||
Start all service boot paths found, based on dependencies.
|
||||
|
||||
:param core.netns.vnode.LxcNode node: node to start services on
|
||||
:param core.nodes.base.CoreNode node: node to start services on
|
||||
:param list[CoreService] boot_path: service to start in dependent order
|
||||
:return: nothing
|
||||
"""
|
||||
|
@ -458,7 +458,7 @@ class CoreServices(object):
|
|||
Start a service on a node. Create private dirs, generate config
|
||||
files, and execute startup commands.
|
||||
|
||||
:param core.netns.vnode.LxcNode node: node to boot services on
|
||||
:param core.nodes.base.CoreNode node: node to boot services on
|
||||
:param CoreService service: service to start
|
||||
:return: nothing
|
||||
"""
|
||||
|
@ -511,7 +511,7 @@ class CoreServices(object):
|
|||
config references an existing file that should be copied.
|
||||
Returns True for local files, False for generated.
|
||||
|
||||
:param core.netns.vnode.LxcNode node: node to copy service for
|
||||
:param core.nodes.base.CoreNode node: node to copy service for
|
||||
:param str filename: file name for a configured service
|
||||
:param str cfg: configuration string
|
||||
:return: True if successful, False otherwise
|
||||
|
@ -530,7 +530,7 @@ class CoreServices(object):
|
|||
"""
|
||||
Run the validation command(s) for a service.
|
||||
|
||||
:param core.netns.vnode.LxcNode node: node to validate service for
|
||||
:param core.nodes.base.CoreNode node: node to validate service for
|
||||
:param CoreService service: service to validate
|
||||
:return: service validation status
|
||||
:rtype: int
|
||||
|
@ -567,7 +567,7 @@ class CoreServices(object):
|
|||
"""
|
||||
Stop a service on a node.
|
||||
|
||||
:param core.netns.vnode.LxcNode node: node to stop a service on
|
||||
:param core.nodes.base.CoreNode node: node to stop a service on
|
||||
:param CoreService service: service to stop
|
||||
:return: status for stopping the services
|
||||
:rtype: str
|
||||
|
@ -586,7 +586,7 @@ class CoreServices(object):
|
|||
Send a File Message when the GUI has requested a service file.
|
||||
The file data is either auto-generated or comes from an existing config.
|
||||
|
||||
:param core.netns.vnode.LxcNode node: node to get service file from
|
||||
:param core.nodes.base.CoreNode node: node to get service file from
|
||||
:param str service_name: service to get file from
|
||||
:param str filename: file name to retrieve
|
||||
:return: file message for node
|
||||
|
@ -655,7 +655,7 @@ class CoreServices(object):
|
|||
"""
|
||||
Startup a node service.
|
||||
|
||||
:param PyCoreNode node: node to reconfigure service for
|
||||
:param core.nodes.base.CoreNode node: node to reconfigure service for
|
||||
:param CoreService service: service to reconfigure
|
||||
:param bool wait: determines if we should wait to validate startup
|
||||
:return: status of startup
|
||||
|
@ -682,7 +682,7 @@ class CoreServices(object):
|
|||
"""
|
||||
Creates node service files.
|
||||
|
||||
:param PyCoreNode node: node to reconfigure service for
|
||||
:param core.nodes.base.CoreNode node: node to reconfigure service for
|
||||
:param CoreService service: service to reconfigure
|
||||
:return: nothing
|
||||
"""
|
||||
|
@ -715,7 +715,7 @@ class CoreServices(object):
|
|||
"""
|
||||
Reconfigure a node service.
|
||||
|
||||
:param PyCoreNode node: node to reconfigure service for
|
||||
:param core.nodes.base.CoreNode node: node to reconfigure service for
|
||||
:param CoreService service: service to reconfigure
|
||||
:return: nothing
|
||||
"""
|
||||
|
@ -805,7 +805,7 @@ class CoreService(object):
|
|||
returns the cls._configs tuple, but this method may be overriden to
|
||||
provide node-specific filenames that may be based on other services.
|
||||
|
||||
:param core.netns.vnode.LxcNode node: node to generate config for
|
||||
:param core.nodes.base.CoreNode node: node to generate config for
|
||||
:return: configuration files
|
||||
:rtype: tuple
|
||||
"""
|
||||
|
@ -819,7 +819,7 @@ class CoreService(object):
|
|||
Return the configuration string to be written to a file or sent
|
||||
to the GUI for customization.
|
||||
|
||||
:param core.netns.vnode.LxcNode node: node to generate config for
|
||||
:param core.nodes.base.CoreNode node: node to generate config for
|
||||
:param str filename: file name to generate config for
|
||||
:return: nothing
|
||||
"""
|
||||
|
@ -833,7 +833,7 @@ class CoreService(object):
|
|||
overridden to provide node-specific commands that may be
|
||||
based on other services.
|
||||
|
||||
:param core.netns.vnode.LxcNode node: node to get startup for
|
||||
:param core.nodes.base.CoreNode node: node to get startup for
|
||||
:return: startup commands
|
||||
:rtype: tuple
|
||||
"""
|
||||
|
@ -847,7 +847,7 @@ class CoreService(object):
|
|||
overridden to provide node-specific commands that may be
|
||||
based on other services.
|
||||
|
||||
:param core.netns.vnode.LxcNode node: node to validate
|
||||
:param core.nodes.base.CoreNode node: node to validate
|
||||
:return: validation commands
|
||||
:rtype: tuple
|
||||
"""
|
||||
|
|
|
@ -3,6 +3,7 @@ Miscellaneous utility functions, wrappers around some subprocess procedures.
|
|||
"""
|
||||
|
||||
import fcntl
|
||||
import hashlib
|
||||
import importlib
|
||||
import inspect
|
||||
import logging
|
||||
|
@ -17,6 +18,43 @@ from core import CoreCommandError
|
|||
DEVNULL = open(os.devnull, "wb")
|
||||
|
||||
|
||||
def execute_file(path, exec_globals=None, exec_locals=None):
|
||||
"""
|
||||
Provides an alternative way to run execfile to be compatible for
|
||||
both python2/3.
|
||||
|
||||
:param str path: path of file to execute
|
||||
:param dict exec_globals: globals values to pass to execution
|
||||
:param dict exec_locals: local values to pass to execution
|
||||
:return: nothing
|
||||
"""
|
||||
if exec_globals is None:
|
||||
exec_globals = {}
|
||||
exec_globals.update({
|
||||
"__file__": path,
|
||||
"__name__": "__main__"
|
||||
})
|
||||
with open(path, "rb") as f:
|
||||
data = compile(f.read(), path, "exec")
|
||||
exec(data, exec_globals, exec_locals)
|
||||
|
||||
|
||||
def hashkey(value):
|
||||
"""
|
||||
Provide a consistent hash that can be used in place
|
||||
of the builtin hash, that no longer behaves consistently
|
||||
in python3.
|
||||
|
||||
:param str/int value: value to hash
|
||||
:return: hash value
|
||||
:rtype: int
|
||||
"""
|
||||
if isinstance(value, int):
|
||||
value = str(value)
|
||||
value = value.encode("utf-8")
|
||||
return int(hashlib.sha256(value).hexdigest(), 16)
|
||||
|
||||
|
||||
def _detach_init():
|
||||
"""
|
||||
Fork a child process and exit.
|
||||
|
@ -148,9 +186,7 @@ def split_args(args):
|
|||
:return: shell-like syntax list
|
||||
:rtype: list
|
||||
"""
|
||||
logging.info("split args: %s - %s", args, type(args))
|
||||
if isinstance(args, basestring):
|
||||
logging.info("splitting args")
|
||||
args = shlex.split(args)
|
||||
return args
|
||||
|
||||
|
@ -313,8 +349,8 @@ def expand_corepath(pathname, session=None, node=None):
|
|||
Expand a file path given session information.
|
||||
|
||||
:param str pathname: file path to expand
|
||||
:param core.session.Session session: core session object to expand path with
|
||||
:param core.netns.LxcNode node: node to expand path with
|
||||
:param core.emulator.session.Session session: core session object to expand path with
|
||||
:param core.nodes.base.CoreNode node: node to expand path with
|
||||
:return: expanded path
|
||||
:rtype: str
|
||||
"""
|
||||
|
|
|
@ -755,9 +755,11 @@ class CoreXmlReader(object):
|
|||
if link_elements is None:
|
||||
return
|
||||
|
||||
node_sets = set()
|
||||
for link_element in link_elements.iterchildren():
|
||||
node_one = get_int(link_element, "node_one")
|
||||
node_two = get_int(link_element, "node_two")
|
||||
node_set = frozenset((node_one, node_two))
|
||||
|
||||
interface_one_element = link_element.find("interface_one")
|
||||
interface_one = None
|
||||
|
@ -772,15 +774,15 @@ class CoreXmlReader(object):
|
|||
options_element = link_element.find("options")
|
||||
link_options = LinkOptions()
|
||||
if options_element is not None:
|
||||
link_options.bandwidth = get_float(options_element, "bandwidth")
|
||||
link_options.burst = get_float(options_element, "burst")
|
||||
link_options.delay = get_float(options_element, "delay")
|
||||
link_options.dup = get_float(options_element, "dup")
|
||||
link_options.mer = get_float(options_element, "mer")
|
||||
link_options.mburst = get_float(options_element, "mburst")
|
||||
link_options.jitter = get_float(options_element, "jitter")
|
||||
link_options.key = get_float(options_element, "key")
|
||||
link_options.per = get_float(options_element, "per")
|
||||
link_options.bandwidth = get_int(options_element, "bandwidth")
|
||||
link_options.burst = get_int(options_element, "burst")
|
||||
link_options.delay = get_int(options_element, "delay")
|
||||
link_options.dup = get_int(options_element, "dup")
|
||||
link_options.mer = get_int(options_element, "mer")
|
||||
link_options.mburst = get_int(options_element, "mburst")
|
||||
link_options.jitter = get_int(options_element, "jitter")
|
||||
link_options.key = get_int(options_element, "key")
|
||||
link_options.per = get_int(options_element, "per")
|
||||
link_options.unidirectional = get_int(options_element, "unidirectional")
|
||||
link_options.session = options_element.get("session")
|
||||
link_options.emulation_id = get_int(options_element, "emulation_id")
|
||||
|
@ -788,5 +790,11 @@ class CoreXmlReader(object):
|
|||
link_options.opaque = options_element.get("opaque")
|
||||
link_options.gui_attributes = options_element.get("gui_attributes")
|
||||
|
||||
logging.info("reading link node_one(%s) node_two(%s)", node_one, node_two)
|
||||
self.session.add_link(node_one, node_two, interface_one, interface_two, link_options)
|
||||
if link_options.unidirectional == 1 and node_set in node_sets:
|
||||
logging.info("updating link node_one(%s) node_two(%s): %s", node_one, node_two, link_options)
|
||||
self.session.update_link(node_one, node_two, interface_one.id, interface_two.id, link_options)
|
||||
else:
|
||||
logging.info("adding link node_one(%s) node_two(%s): %s", node_one, node_two, link_options)
|
||||
self.session.add_link(node_one, node_two, interface_one, interface_two, link_options)
|
||||
|
||||
node_sets.add(node_set)
|
||||
|
|
|
@ -108,7 +108,7 @@ def build_node_platform_xml(emane_manager, control_net, node, nem_id, platform_x
|
|||
:return: the next nem id that can be used for creating platform xml files
|
||||
:rtype: int
|
||||
"""
|
||||
logging.debug("building emane platform xml for node(%s): %s", node, node.name)
|
||||
logging.debug("building emane platform xml for node(%s) nem_id(%s): %s", node, nem_id, node.name)
|
||||
nem_entries = {}
|
||||
|
||||
if node.model is None:
|
||||
|
@ -116,6 +116,7 @@ def build_node_platform_xml(emane_manager, control_net, node, nem_id, platform_x
|
|||
return nem_entries
|
||||
|
||||
for netif in node.netifs():
|
||||
logging.debug("building platform xml for interface(%s) nem_id(%s)", netif.name, nem_id)
|
||||
# build nem xml
|
||||
nem_definition = nem_file_name(node.model, netif)
|
||||
nem_element = etree.Element("nem", id=str(nem_id), name=netif.localname, definition=nem_definition)
|
||||
|
|
|
@ -14,7 +14,7 @@
|
|||
}
|
||||
},
|
||||
"root": {
|
||||
"level": "DEBUG",
|
||||
"level": "INFO",
|
||||
"handlers": ["console"]
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,48 +1,54 @@
|
|||
"""
|
||||
Example custom emane model.
|
||||
"""
|
||||
|
||||
from core.emane import emanemanifest
|
||||
from core.emane import emanemodel
|
||||
|
||||
|
||||
## Custom EMANE Model
|
||||
class ExampleModel(emanemodel.EmaneModel):
|
||||
### MAC Definition
|
||||
"""
|
||||
Custom emane model.
|
||||
|
||||
:var str name: defines the emane model name that will show up in the GUI
|
||||
|
||||
Mac Definition:
|
||||
:var str mac_library: defines that mac library that the model will reference
|
||||
:var str mac_xml: defines the mac manifest file that will be parsed to obtain configuration options,
|
||||
that will be displayed within the GUI
|
||||
:var dict mac_mac_defaults: allows you to override options that are maintained within the manifest file above
|
||||
:var list mac_mac_config: parses the manifest file and converts configurations into core supported formats
|
||||
|
||||
Phy Definition:
|
||||
NOTE: phy configuration will default to the universal model as seen below and the below section does not
|
||||
have to be included
|
||||
:var str phy_library: defines that phy library that the model will reference, used if you need to
|
||||
provide a custom phy
|
||||
:var str phy_xml: defines the phy manifest file that will be parsed to obtain configuration options,
|
||||
that will be displayed within the GUI
|
||||
:var dict phy_defaults: allows you to override options that are maintained within the manifest file above
|
||||
or for the default universal model
|
||||
:var list phy_config: parses the manifest file and converts configurations into core supported formats
|
||||
|
||||
Custom Override Options:
|
||||
NOTE: these options default to what's seen below and do not have to be included
|
||||
:var set config_ignore: allows you to ignore options within phy/mac, used typically if you needed to add
|
||||
a custom option for display within the gui
|
||||
"""
|
||||
|
||||
# Defines the emane model name that will show up in the GUI.
|
||||
name = "emane_example"
|
||||
|
||||
# Defines that mac library that the model will reference.
|
||||
mac_library = "rfpipemaclayer"
|
||||
# Defines the mac manifest file that will be parsed to obtain configuration options, that will be displayed
|
||||
# within the GUI.
|
||||
mac_xml = "/usr/share/emane/manifest/rfpipemaclayer.xml"
|
||||
# Allows you to override options that are maintained within the manifest file above.
|
||||
mac_defaults = {
|
||||
"pcrcurveuri": "/usr/share/emane/xml/models/mac/rfpipe/rfpipepcr.xml",
|
||||
}
|
||||
# Parses the manifest file and converts configurations into core supported formats.
|
||||
mac_config = emanemanifest.parse(mac_xml, mac_defaults)
|
||||
|
||||
### PHY Definition
|
||||
# **NOTE: phy configuration will default to the universal model as seen below and the below section does not
|
||||
# have to be included.**
|
||||
|
||||
# Defines that phy library that the model will reference, used if you need to provide a custom phy.
|
||||
phy_library = None
|
||||
# Defines the phy manifest file that will be parsed to obtain configuration options, that will be displayed
|
||||
# within the GUI.
|
||||
phy_xml = "/usr/share/emane/manifest/emanephy.xml"
|
||||
# Allows you to override options that are maintained within the manifest file above or for the default universal
|
||||
# model.
|
||||
phy_defaults = {
|
||||
"subid": "1",
|
||||
"propagationmodel": "2ray",
|
||||
"noisemode": "none"
|
||||
}
|
||||
# Parses the manifest file and converts configurations into core supported formats.
|
||||
phy_config = emanemanifest.parse(phy_xml, phy_defaults)
|
||||
|
||||
### Custom override options
|
||||
# **NOTE: these options default to what's seen below and do not have to be included.**
|
||||
|
||||
# Allows you to ignore options within phy/mac, used typically if you needed to add a custom option for display
|
||||
# within the gui.
|
||||
config_ignore = set()
|
||||
|
|
|
@ -1,26 +0,0 @@
|
|||
This directory contains a sample custom service that you can use as a template
|
||||
for creating your own services.
|
||||
|
||||
Follow these steps to add your own services:
|
||||
|
||||
1. Modify the sample service MyService to do what you want. It could generate
|
||||
config/script files, mount per-node directories, start processes/scripts,
|
||||
etc. sample.py is a Python file that defines one or more classes to be
|
||||
imported. You can create multiple Python files that will be imported.
|
||||
Add any new filenames to the __init__.py file.
|
||||
|
||||
2. Put these files in a directory such as /home/username/.core/myservices
|
||||
Note that the last component of this directory name 'myservices' should not
|
||||
be named something like 'services' which conflicts with an existing Python
|
||||
name (the syntax 'from myservices import *' is used).
|
||||
|
||||
3. Add a 'custom_services_dir = /home/username/.core/myservices' entry to the
|
||||
/etc/core/core.conf file.
|
||||
|
||||
4. Restart the CORE daemon (core-daemon). Any import errors (Python syntax)
|
||||
should be displayed in the /var/log/core-daemon.log log file (or on screen).
|
||||
|
||||
5. Start using your custom service on your nodes. You can create a new node
|
||||
type that uses your service, or change the default services for an existing
|
||||
node type, or change individual nodes.
|
||||
|
|
@ -1,64 +1,81 @@
|
|||
"""
|
||||
Sample user-defined service.
|
||||
Simple example for a user-defined service.
|
||||
"""
|
||||
|
||||
from core.services.coreservices import CoreService
|
||||
from core.services.coreservices import ServiceMode
|
||||
|
||||
|
||||
## Custom CORE Service
|
||||
class MyService(CoreService):
|
||||
### Service Attributes
|
||||
"""
|
||||
Custom CORE Service
|
||||
|
||||
# Name used as a unique ID for this service and is required, no spaces.
|
||||
:var str name: name used as a unique ID for this service and is required, no spaces
|
||||
:var str group: allows you to group services within the GUI under a common name
|
||||
:var tuple executables: executables this service depends on to function, if executable is
|
||||
not on the path, service will not be loaded
|
||||
:var tuple dependencies: services that this service depends on for startup, tuple of service names
|
||||
:var tuple dirs: directories that this service will create within a node
|
||||
:var tuple configs: files that this service will generate, without a full path this file goes in
|
||||
the node's directory e.g. /tmp/pycore.12345/n1.conf/myfile
|
||||
:var tuple startup: commands used to start this service, any non-zero exit code will cause a failure
|
||||
:var tuple validate: commands used to validate that a service was started, any non-zero exit code
|
||||
will cause a failure
|
||||
:var ServiceMode validation_mode: validation mode, used to determine startup success.
|
||||
NON_BLOCKING - runs startup commands, and validates success with validation commands
|
||||
BLOCKING - runs startup commands, and validates success with the startup commands themselves
|
||||
TIMER - runs startup commands, and validates success by waiting for "validation_timer" alone
|
||||
:var int validation_timer: time in seconds for a service to wait for validation, before determining
|
||||
success in TIMER/NON_BLOCKING modes.
|
||||
:var float validation_validation_period: period in seconds to wait before retrying validation,
|
||||
only used in NON_BLOCKING mode
|
||||
:var tuple shutdown: shutdown commands to stop this service
|
||||
"""
|
||||
name = "MyService"
|
||||
# Allows you to group services within the GUI under a common name.
|
||||
group = "Utility"
|
||||
# Executables this service depends on to function, if executable is not on the path, service will not be loaded.
|
||||
executables = ()
|
||||
# Services that this service depends on for startup, tuple of service names.
|
||||
dependencies = ()
|
||||
# Directories that this service will create within a node.
|
||||
dirs = ()
|
||||
# Files that this service will generate, without a full path this file goes in the node's directory.
|
||||
# e.g. /tmp/pycore.12345/n1.conf/myfile
|
||||
configs = ("myservice1.sh", "myservice2.sh")
|
||||
# Commands used to start this service, any non-zero exit code will cause a failure.
|
||||
startup = ("sh %s" % configs[0], "sh %s" % configs[1])
|
||||
# Commands used to validate that a service was started, any non-zero exit code will cause a failure.
|
||||
validate = ()
|
||||
# Validation mode, used to determine startup success.
|
||||
#
|
||||
# * NON_BLOCKING - runs startup commands, and validates success with validation commands
|
||||
# * BLOCKING - runs startup commands, and validates success with the startup commands themselves
|
||||
# * TIMER - runs startup commands, and validates success by waiting for "validation_timer" alone
|
||||
validation_mode = ServiceMode.NON_BLOCKING
|
||||
# Time in seconds for a service to wait for validation, before determining success in TIMER/NON_BLOCKING modes.
|
||||
validation_timer = 5
|
||||
# Period in seconds to wait before retrying validation, only used in NON_BLOCKING mode.
|
||||
validation_period = 0.5
|
||||
# Shutdown commands to stop this service.
|
||||
shutdown = ()
|
||||
|
||||
### On Load
|
||||
@classmethod
|
||||
def on_load(cls):
|
||||
# Provides a way to run some arbitrary logic when the service is loaded, possibly to help facilitate
|
||||
# dynamic settings for the environment.
|
||||
"""
|
||||
Provides a way to run some arbitrary logic when the service is loaded, possibly to help facilitate
|
||||
dynamic settings for the environment.
|
||||
|
||||
:return: nothing
|
||||
"""
|
||||
pass
|
||||
|
||||
### Get Configs
|
||||
@classmethod
|
||||
def get_configs(cls, node):
|
||||
# Provides a way to dynamically generate the config files from the node a service will run.
|
||||
# Defaults to the class definition and can be left out entirely if not needed.
|
||||
"""
|
||||
Provides a way to dynamically generate the config files from the node a service will run.
|
||||
Defaults to the class definition and can be left out entirely if not needed.
|
||||
|
||||
:param node: core node that the service is being ran on
|
||||
:return: tuple of config files to create
|
||||
"""
|
||||
return cls.configs
|
||||
|
||||
### Generate Config
|
||||
@classmethod
|
||||
def generate_config(cls, node, filename):
|
||||
# Returns a string representation for a file, given the node the service is starting on the config filename
|
||||
# that this information will be used for. This must be defined, if "configs" are defined.
|
||||
"""
|
||||
Returns a string representation for a file, given the node the service is starting on the config filename
|
||||
that this information will be used for. This must be defined, if "configs" are defined.
|
||||
|
||||
:param node: core node that the service is being ran on
|
||||
:param str filename: configuration file to generate
|
||||
:return: configuration file content
|
||||
:rtype: str
|
||||
"""
|
||||
cfg = "#!/bin/sh\n"
|
||||
|
||||
if filename == cls.configs[0]:
|
||||
|
@ -70,16 +87,24 @@ class MyService(CoreService):
|
|||
|
||||
return cfg
|
||||
|
||||
### Get Startup
|
||||
@classmethod
|
||||
def get_startup(cls, node):
|
||||
# Provides a way to dynamically generate the startup commands from the node a service will run.
|
||||
# Defaults to the class definition and can be left out entirely if not needed.
|
||||
"""
|
||||
Provides a way to dynamically generate the startup commands from the node a service will run.
|
||||
Defaults to the class definition and can be left out entirely if not needed.
|
||||
|
||||
:param node: core node that the service is being ran on
|
||||
:return: tuple of startup commands to run
|
||||
"""
|
||||
return cls.startup
|
||||
|
||||
### Get Validate
|
||||
@classmethod
|
||||
def get_validate(cls, node):
|
||||
# Provides a way to dynamically generate the validate commands from the node a service will run.
|
||||
# Defaults to the class definition and can be left out entirely if not needed.
|
||||
"""
|
||||
Provides a way to dynamically generate the validate commands from the node a service will run.
|
||||
Defaults to the class definition and can be left out entirely if not needed.
|
||||
|
||||
:param node: core node that the service is being ran on
|
||||
:return: tuple of commands to validate service startup with
|
||||
"""
|
||||
return cls.validate
|
||||
|
|
|
@ -172,13 +172,13 @@ message GetSessionLocationRequest {
|
|||
}
|
||||
|
||||
message GetSessionLocationResponse {
|
||||
Position position = 1;
|
||||
SessionPosition position = 1;
|
||||
float scale = 2;
|
||||
}
|
||||
|
||||
message SetSessionLocationRequest {
|
||||
int32 session_id = 1;
|
||||
Position position = 2;
|
||||
SessionPosition position = 2;
|
||||
float scale = 3;
|
||||
}
|
||||
|
||||
|
@ -787,15 +787,15 @@ message Link {
|
|||
|
||||
message LinkOptions {
|
||||
string opaque = 1;
|
||||
float jitter = 2;
|
||||
string key = 3;
|
||||
float mburst = 4;
|
||||
float mer = 5;
|
||||
float per = 6;
|
||||
float bandwidth = 7;
|
||||
float burst = 8;
|
||||
float delay = 9;
|
||||
float dup = 10;
|
||||
int64 jitter = 2;
|
||||
int32 key = 3;
|
||||
int32 mburst = 4;
|
||||
int32 mer = 5;
|
||||
int32 per = 6;
|
||||
int64 bandwidth = 7;
|
||||
int32 burst = 8;
|
||||
int64 delay = 9;
|
||||
int32 dup = 10;
|
||||
bool unidirectional = 11;
|
||||
}
|
||||
|
||||
|
@ -812,7 +812,7 @@ message Interface {
|
|||
int32 mtu = 10;
|
||||
}
|
||||
|
||||
message Position {
|
||||
message SessionPosition {
|
||||
float x = 1;
|
||||
float y = 2;
|
||||
float z = 3;
|
||||
|
@ -820,3 +820,12 @@ message Position {
|
|||
float lon = 5;
|
||||
float alt = 6;
|
||||
}
|
||||
|
||||
message Position {
|
||||
int32 x = 1;
|
||||
int32 y = 2;
|
||||
int32 z = 3;
|
||||
float lat = 4;
|
||||
float lon = 5;
|
||||
float alt = 6;
|
||||
}
|
||||
|
|
0
daemon/scripts/core-cleanup
Normal file → Executable file
0
daemon/scripts/core-cleanup
Normal file → Executable file
|
@ -6,18 +6,21 @@ message handlers are defined and some support for sending messages.
|
|||
"""
|
||||
|
||||
import argparse
|
||||
from configparser import ConfigParser
|
||||
import logging
|
||||
import sys
|
||||
import threading
|
||||
import time
|
||||
|
||||
from core import load_logging_config
|
||||
from configparser import ConfigParser
|
||||
|
||||
from core import constants
|
||||
from core.emulator import enumerations
|
||||
from core.api.tlv.corehandlers import CoreHandler
|
||||
from core.api.tlv.coreserver import CoreServer
|
||||
from core import load_logging_config
|
||||
from core.api.grpc.server import CoreGrpcServer
|
||||
from core.api.tlv.corehandlers import CoreHandler
|
||||
from core.api.tlv.corehandlers import CoreUdpHandler
|
||||
from core.api.tlv.coreserver import CoreServer
|
||||
from core.api.tlv.coreserver import CoreUdpServer
|
||||
from core.emulator import enumerations
|
||||
from core.utils import close_onexec
|
||||
|
||||
load_logging_config()
|
||||
|
@ -32,7 +35,22 @@ def banner():
|
|||
logging.info("CORE daemon v.%s started %s", constants.COREDPY_VERSION, time.ctime())
|
||||
|
||||
|
||||
def cored(cfg):
|
||||
def start_udp(mainserver, server_address):
|
||||
"""
|
||||
Start a thread running a UDP server on the same host,port for
|
||||
connectionless requests.
|
||||
|
||||
:param CoreServer mainserver: main core tcp server to piggy back off of
|
||||
:param server_address:
|
||||
:return: CoreUdpServer
|
||||
"""
|
||||
mainserver.udpserver = CoreUdpServer(server_address, CoreUdpHandler, mainserver)
|
||||
mainserver.udpthread = threading.Thread(target=mainserver.udpserver.start)
|
||||
mainserver.udpthread.daemon = True
|
||||
mainserver.udpthread.start()
|
||||
|
||||
|
||||
def cored(cfg, use_ovs):
|
||||
"""
|
||||
Start the CoreServer object and enter the server loop.
|
||||
|
||||
|
@ -46,8 +64,9 @@ def cored(cfg):
|
|||
host = "localhost"
|
||||
|
||||
try:
|
||||
server = CoreServer((host, port), CoreHandler, cfg)
|
||||
if cfg["ovs"] == "True":
|
||||
address = (host, port)
|
||||
server = CoreServer(address, CoreHandler, cfg)
|
||||
if use_ovs:
|
||||
from core.nodes.openvswitch import OVS_NODES
|
||||
server.coreemu.update_nodes(OVS_NODES)
|
||||
except:
|
||||
|
@ -62,8 +81,13 @@ def cored(cfg):
|
|||
grpc_thread.daemon = True
|
||||
grpc_thread.start()
|
||||
|
||||
# start udp server
|
||||
start_udp(server, address)
|
||||
|
||||
# close handlers
|
||||
close_onexec(server.fileno())
|
||||
logging.info("server started, listening on: %s:%s", host, port)
|
||||
|
||||
logging.info("tcp/udp servers started, listening on: %s:%s", host, port)
|
||||
server.serve_forever()
|
||||
|
||||
|
||||
|
@ -133,8 +157,11 @@ def main():
|
|||
cfg = get_merged_config("%s/core.conf" % constants.CORE_CONF_DIR)
|
||||
banner()
|
||||
|
||||
# check if ovs flag was provided
|
||||
use_ovs = len(sys.argv) == 2 and sys.argv[1] == "ovs"
|
||||
|
||||
try:
|
||||
cored(cfg)
|
||||
cored(cfg, use_ovs)
|
||||
except KeyboardInterrupt:
|
||||
logging.info("keyboard interrupt, stopping core daemon")
|
||||
|
||||
|
|
0
daemon/scripts/core-manage
Normal file → Executable file
0
daemon/scripts/core-manage
Normal file → Executable file
11
daemon/scripts/coresendmsg
Normal file → Executable file
11
daemon/scripts/coresendmsg
Normal file → Executable file
|
@ -186,6 +186,8 @@ def main():
|
|||
help="Listen for a response message and print it.")
|
||||
parser.add_option("-t", "--list-tlvs", dest="tlvs", action="store_true",
|
||||
help="List TLVs for the specified message type.")
|
||||
parser.add_option("--tcp", dest="tcp", action="store_true",
|
||||
help="Use TCP instead of UDP and connect to a session default: %s" % parser.defaults["tcp"])
|
||||
|
||||
def usage(msg=None, err=0):
|
||||
sys.stdout.write("\n")
|
||||
|
@ -249,7 +251,12 @@ def main():
|
|||
|
||||
msg = msg_cls.pack(flags, tlvdata)
|
||||
|
||||
sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
|
||||
if opt.tcp:
|
||||
protocol = socket.SOCK_STREAM
|
||||
else:
|
||||
protocol = socket.SOCK_DGRAM
|
||||
|
||||
sock = socket.socket(socket.AF_INET, protocol)
|
||||
sock.setblocking(True)
|
||||
|
||||
try:
|
||||
|
@ -258,7 +265,7 @@ def main():
|
|||
print("Error connecting to %s:%s:\n\t%s" % (opt.address, opt.port, e))
|
||||
sys.exit(1)
|
||||
|
||||
if not connect_to_session(sock, opt.session):
|
||||
if opt.tcp and not connect_to_session(sock, opt.session):
|
||||
print("warning: continuing without joining a session!")
|
||||
|
||||
sock.sendall(msg)
|
||||
|
|
|
@ -9,134 +9,22 @@ import time
|
|||
import pytest
|
||||
from mock.mock import MagicMock
|
||||
|
||||
from core.api.tlv.coreapi import CoreConfMessage
|
||||
from core.api.tlv.coreapi import CoreEventMessage
|
||||
from core.api.tlv.coreapi import CoreExecMessage
|
||||
from core.api.tlv.coreapi import CoreLinkMessage
|
||||
from core.api.tlv.coreapi import CoreNodeMessage
|
||||
from core.api.grpc.client import InterfaceHelper
|
||||
from core.api.grpc.server import CoreGrpcServer
|
||||
from core.api.tlv.coreapi import CoreConfMessage, CoreEventMessage
|
||||
from core.api.tlv.corehandlers import CoreHandler
|
||||
from core.api.tlv.coreserver import CoreServer
|
||||
from core.emulator.coreemu import CoreEmu
|
||||
from core.emulator.emudata import IpPrefixes
|
||||
from core.emulator.enumerations import CORE_API_PORT
|
||||
from core.emulator.enumerations import CORE_API_PORT, EventTlvs
|
||||
from core.emulator.enumerations import ConfigTlvs
|
||||
from core.emulator.enumerations import EventTlvs
|
||||
from core.emulator.enumerations import EventTypes
|
||||
from core.emulator.enumerations import ExecuteTlvs
|
||||
from core.emulator.enumerations import LinkTlvs
|
||||
from core.emulator.enumerations import LinkTypes
|
||||
from core.emulator.enumerations import MessageFlags
|
||||
from core.emulator.enumerations import NodeTlvs
|
||||
from core.emulator.enumerations import NodeTypes
|
||||
from core.api.grpc.client import InterfaceHelper
|
||||
from core.api.grpc.server import CoreGrpcServer
|
||||
from core.nodes import ipaddress
|
||||
from core.nodes.ipaddress import MacAddress
|
||||
from core.services.coreservices import ServiceManager
|
||||
|
||||
EMANE_SERVICES = "zebra|OSPFv3MDR|IPForward"
|
||||
|
||||
|
||||
def node_message(_id, name, emulation_server=None, node_type=NodeTypes.DEFAULT, model=None):
|
||||
"""
|
||||
Convenience method for creating a node TLV messages.
|
||||
|
||||
:param int _id: node id
|
||||
:param str name: node name
|
||||
:param str emulation_server: distributed server name, if desired
|
||||
:param core.enumerations.NodeTypes node_type: node type
|
||||
:param str model: model for node
|
||||
:return: tlv message
|
||||
:rtype: core.api.coreapi.CoreNodeMessage
|
||||
"""
|
||||
values = [
|
||||
(NodeTlvs.NUMBER, _id),
|
||||
(NodeTlvs.TYPE, node_type.value),
|
||||
(NodeTlvs.NAME, name),
|
||||
(NodeTlvs.EMULATION_SERVER, emulation_server),
|
||||
]
|
||||
|
||||
if model:
|
||||
values.append((NodeTlvs.MODEL, model))
|
||||
|
||||
return CoreNodeMessage.create(MessageFlags.ADD.value, values)
|
||||
|
||||
|
||||
def link_message(n1, n2, intf_one=None, address_one=None, intf_two=None, address_two=None, key=None):
|
||||
"""
|
||||
Convenience method for creating link TLV messages.
|
||||
|
||||
:param int n1: node one id
|
||||
:param int n2: node two id
|
||||
:param int intf_one: node one interface id
|
||||
:param core.misc.ipaddress.IpAddress address_one: node one ip4 address
|
||||
:param int intf_two: node two interface id
|
||||
:param core.misc.ipaddress.IpAddress address_two: node two ip4 address
|
||||
:param int key: tunnel key for link if needed
|
||||
:return: tlv mesage
|
||||
:rtype: core.api.coreapi.CoreLinkMessage
|
||||
"""
|
||||
mac_one, mac_two = None, None
|
||||
if address_one:
|
||||
mac_one = MacAddress.random()
|
||||
if address_two:
|
||||
mac_two = MacAddress.random()
|
||||
|
||||
values = [
|
||||
(LinkTlvs.N1_NUMBER, n1),
|
||||
(LinkTlvs.N2_NUMBER, n2),
|
||||
(LinkTlvs.DELAY, 0),
|
||||
(LinkTlvs.BANDWIDTH, 0),
|
||||
(LinkTlvs.PER, "0"),
|
||||
(LinkTlvs.DUP, "0"),
|
||||
(LinkTlvs.JITTER, 0),
|
||||
(LinkTlvs.TYPE, LinkTypes.WIRED.value),
|
||||
(LinkTlvs.INTERFACE1_NUMBER, intf_one),
|
||||
(LinkTlvs.INTERFACE1_IP4, address_one),
|
||||
(LinkTlvs.INTERFACE1_IP4_MASK, 24),
|
||||
(LinkTlvs.INTERFACE1_MAC, mac_one),
|
||||
(LinkTlvs.INTERFACE2_NUMBER, intf_two),
|
||||
(LinkTlvs.INTERFACE2_IP4, address_two),
|
||||
(LinkTlvs.INTERFACE2_IP4_MASK, 24),
|
||||
(LinkTlvs.INTERFACE2_MAC, mac_two),
|
||||
]
|
||||
|
||||
if key:
|
||||
values.append((LinkTlvs.KEY, key))
|
||||
|
||||
return CoreLinkMessage.create(MessageFlags.ADD.value, values)
|
||||
|
||||
|
||||
def command_message(node, command):
|
||||
"""
|
||||
Create an execute command TLV message.
|
||||
|
||||
:param node: node to execute command for
|
||||
:param command: command to execute
|
||||
:return: tlv message
|
||||
:rtype: core.api.coreapi.CoreExecMessage
|
||||
"""
|
||||
flags = MessageFlags.STRING.value | MessageFlags.TEXT.value
|
||||
return CoreExecMessage.create(flags, [
|
||||
(ExecuteTlvs.NODE, node.id),
|
||||
(ExecuteTlvs.NUMBER, 1),
|
||||
(ExecuteTlvs.COMMAND, command)
|
||||
])
|
||||
|
||||
|
||||
def state_message(state):
|
||||
"""
|
||||
Create a event TLV message for a new state.
|
||||
|
||||
:param core.enumerations.EventTypes state: state to create message for
|
||||
:return: tlv message
|
||||
:rtype: core.api.coreapi.CoreEventMessage
|
||||
"""
|
||||
return CoreEventMessage.create(0, [
|
||||
(EventTlvs.TYPE, state.value)
|
||||
])
|
||||
|
||||
|
||||
class CoreServerTest(object):
|
||||
def __init__(self, port=CORE_API_PORT):
|
||||
self.host = "localhost"
|
||||
|
@ -152,13 +40,20 @@ class CoreServerTest(object):
|
|||
self.session = None
|
||||
self.request_handler = None
|
||||
|
||||
def setup(self, distributed_address, port):
|
||||
def setup_handler(self):
|
||||
self.session = self.server.coreemu.create_session(1)
|
||||
request_mock = MagicMock()
|
||||
request_mock.fileno = MagicMock(return_value=1)
|
||||
self.request_handler = CoreHandler(request_mock, "", self.server)
|
||||
self.request_handler.session = self.session
|
||||
self.request_handler.add_session_handlers()
|
||||
|
||||
def setup(self, distributed_address):
|
||||
# validate address
|
||||
assert distributed_address, "distributed server address was not provided"
|
||||
|
||||
# create session
|
||||
self.session = self.server.coreemu.create_session(1)
|
||||
self.session.master = True
|
||||
|
||||
# create request handler
|
||||
request_mock = MagicMock()
|
||||
|
@ -170,11 +65,11 @@ class CoreServerTest(object):
|
|||
|
||||
# have broker handle a configuration state change
|
||||
self.session.set_state(EventTypes.DEFINITION_STATE)
|
||||
message = state_message(EventTypes.CONFIGURATION_STATE)
|
||||
message = CoreEventMessage.create(0, [(EventTlvs.TYPE, EventTypes.CONFIGURATION_STATE.value)])
|
||||
self.request_handler.handle_message(message)
|
||||
|
||||
# add broker server for distributed core
|
||||
distributed = "%s:%s:%s" % (self.distributed_server, distributed_address, port)
|
||||
distributed = "%s:%s:%s" % (self.distributed_server, distributed_address, self.port)
|
||||
message = CoreConfMessage.create(0, [
|
||||
(ConfigTlvs.OBJECT, "broker"),
|
||||
(ConfigTlvs.TYPE, 0),
|
||||
|
@ -204,7 +99,6 @@ class CoreServerTest(object):
|
|||
|
||||
def shutdown(self):
|
||||
self.server.coreemu.shutdown()
|
||||
self.server.shutdown()
|
||||
self.server.server_close()
|
||||
|
||||
|
||||
|
@ -268,6 +162,20 @@ def cored():
|
|||
ServiceManager.services.clear()
|
||||
|
||||
|
||||
@pytest.fixture()
|
||||
def coreserver():
|
||||
# create and return server
|
||||
server = CoreServerTest()
|
||||
server.setup_handler()
|
||||
yield server
|
||||
|
||||
# cleanup
|
||||
server.shutdown()
|
||||
|
||||
# cleanup services
|
||||
ServiceManager.services.clear()
|
||||
|
||||
|
||||
def ping(from_node, to_node, ip_prefixes, count=3):
|
||||
address = ip_prefixes.ip4_address(to_node)
|
||||
return from_node.cmd(["ping", "-c", str(count), address])
|
||||
|
|
|
@ -1,15 +1,125 @@
|
|||
"""
|
||||
Unit tests for testing CORE with distributed networks.
|
||||
"""
|
||||
from core.emane.ieee80211abg import EmaneIeee80211abgModel
|
||||
|
||||
import conftest
|
||||
|
||||
from core.api.tlv.coreapi import CoreExecMessage
|
||||
from core.emulator.enumerations import EventTypes
|
||||
from core.api.tlv.coreapi import CoreExecMessage, CoreNodeMessage, CoreLinkMessage, CoreEventMessage, CoreConfMessage
|
||||
from core.emulator.enumerations import EventTypes, NodeTlvs, LinkTlvs, LinkTypes, EventTlvs, ConfigTlvs, ConfigFlags
|
||||
from core.emulator.enumerations import ExecuteTlvs
|
||||
from core.emulator.enumerations import MessageFlags
|
||||
from core.emulator.enumerations import NodeTypes
|
||||
from core.nodes.ipaddress import IpAddress
|
||||
from core.nodes.ipaddress import IpAddress, MacAddress, Ipv4Prefix
|
||||
|
||||
|
||||
def set_emane_model(node_id, model):
|
||||
return CoreConfMessage.create(0, [
|
||||
(ConfigTlvs.NODE, node_id),
|
||||
(ConfigTlvs.OBJECT, model),
|
||||
(ConfigTlvs.TYPE, ConfigFlags.UPDATE.value),
|
||||
])
|
||||
|
||||
|
||||
def node_message(_id, name, emulation_server=None, node_type=NodeTypes.DEFAULT, model=None):
|
||||
"""
|
||||
Convenience method for creating a node TLV messages.
|
||||
|
||||
:param int _id: node id
|
||||
:param str name: node name
|
||||
:param str emulation_server: distributed server name, if desired
|
||||
:param core.emulator.enumerations.NodeTypes node_type: node type
|
||||
:param str model: model for node
|
||||
:return: tlv message
|
||||
:rtype: core.api.tlv.coreapi.CoreNodeMessage
|
||||
"""
|
||||
values = [
|
||||
(NodeTlvs.NUMBER, _id),
|
||||
(NodeTlvs.TYPE, node_type.value),
|
||||
(NodeTlvs.NAME, name),
|
||||
(NodeTlvs.EMULATION_SERVER, emulation_server),
|
||||
(NodeTlvs.X_POSITION, 0),
|
||||
(NodeTlvs.Y_POSITION, 0),
|
||||
]
|
||||
|
||||
if model:
|
||||
values.append((NodeTlvs.MODEL, model))
|
||||
|
||||
return CoreNodeMessage.create(MessageFlags.ADD.value, values)
|
||||
|
||||
|
||||
def link_message(n1, n2, intf_one=None, address_one=None, intf_two=None, address_two=None, key=None, mask=24):
|
||||
"""
|
||||
Convenience method for creating link TLV messages.
|
||||
|
||||
:param int n1: node one id
|
||||
:param int n2: node two id
|
||||
:param int intf_one: node one interface id
|
||||
:param core.nodes.ipaddress.IpAddress address_one: node one ip4 address
|
||||
:param int intf_two: node two interface id
|
||||
:param core.nodes.ipaddress.IpAddress address_two: node two ip4 address
|
||||
:param int key: tunnel key for link if needed
|
||||
:param int mask: ip4 mask to use for link
|
||||
:return: tlv mesage
|
||||
:rtype: core.api.tlv.coreapi.CoreLinkMessage
|
||||
"""
|
||||
mac_one, mac_two = None, None
|
||||
if address_one:
|
||||
mac_one = MacAddress.random()
|
||||
if address_two:
|
||||
mac_two = MacAddress.random()
|
||||
|
||||
values = [
|
||||
(LinkTlvs.N1_NUMBER, n1),
|
||||
(LinkTlvs.N2_NUMBER, n2),
|
||||
(LinkTlvs.DELAY, 0),
|
||||
(LinkTlvs.BANDWIDTH, 0),
|
||||
(LinkTlvs.PER, "0"),
|
||||
(LinkTlvs.DUP, "0"),
|
||||
(LinkTlvs.JITTER, 0),
|
||||
(LinkTlvs.TYPE, LinkTypes.WIRED.value),
|
||||
(LinkTlvs.INTERFACE1_NUMBER, intf_one),
|
||||
(LinkTlvs.INTERFACE1_IP4, address_one),
|
||||
(LinkTlvs.INTERFACE1_IP4_MASK, mask),
|
||||
(LinkTlvs.INTERFACE1_MAC, mac_one),
|
||||
(LinkTlvs.INTERFACE2_NUMBER, intf_two),
|
||||
(LinkTlvs.INTERFACE2_IP4, address_two),
|
||||
(LinkTlvs.INTERFACE2_IP4_MASK, mask),
|
||||
(LinkTlvs.INTERFACE2_MAC, mac_two),
|
||||
]
|
||||
|
||||
if key:
|
||||
values.append((LinkTlvs.KEY, key))
|
||||
|
||||
return CoreLinkMessage.create(MessageFlags.ADD.value, values)
|
||||
|
||||
|
||||
def command_message(node, command):
|
||||
"""
|
||||
Create an execute command TLV message.
|
||||
|
||||
:param node: node to execute command for
|
||||
:param command: command to execute
|
||||
:return: tlv message
|
||||
:rtype: core.api.tlv.coreapi.CoreExecMessage
|
||||
"""
|
||||
flags = MessageFlags.STRING.value | MessageFlags.TEXT.value
|
||||
return CoreExecMessage.create(flags, [
|
||||
(ExecuteTlvs.NODE, node.id),
|
||||
(ExecuteTlvs.NUMBER, 1),
|
||||
(ExecuteTlvs.COMMAND, command)
|
||||
])
|
||||
|
||||
|
||||
def state_message(state):
|
||||
"""
|
||||
Create a event TLV message for a new state.
|
||||
|
||||
:param core.enumerations.EventTypes state: state to create message for
|
||||
:return: tlv message
|
||||
:rtype: core.api.tlv.coreapi.CoreEventMessage
|
||||
"""
|
||||
return CoreEventMessage.create(0, [
|
||||
(EventTlvs.TYPE, state.value)
|
||||
])
|
||||
|
||||
|
||||
def validate_response(replies, _):
|
||||
|
@ -28,18 +138,18 @@ def validate_response(replies, _):
|
|||
|
||||
|
||||
class TestDistributed:
|
||||
def test_distributed(self, cored, distributed_address):
|
||||
def test_switch(self, cored, distributed_address):
|
||||
"""
|
||||
Test creating a distributed network.
|
||||
Test creating a distributed switch network.
|
||||
|
||||
:param core.coreserver.CoreServer conftest.Core cored: core daemon server to test with
|
||||
:param core.api.tlv.coreserver.CoreServer conftest.Core cored: core daemon server to test with
|
||||
:param str distributed_address: distributed server to test against
|
||||
"""
|
||||
# initialize server for testing
|
||||
cored.setup(distributed_address)
|
||||
|
||||
# create local node
|
||||
message = conftest.node_message(
|
||||
message = node_message(
|
||||
_id=1,
|
||||
name="n1",
|
||||
model="host"
|
||||
|
@ -47,7 +157,7 @@ class TestDistributed:
|
|||
cored.request_handler.handle_message(message)
|
||||
|
||||
# create distributed node and assign to distributed server
|
||||
message = conftest.node_message(
|
||||
message = node_message(
|
||||
_id=2,
|
||||
name="n2",
|
||||
emulation_server=cored.distributed_server,
|
||||
|
@ -56,17 +166,16 @@ class TestDistributed:
|
|||
cored.request_handler.handle_message(message)
|
||||
|
||||
# create distributed switch and assign to distributed server
|
||||
message = conftest.node_message(
|
||||
message = node_message(
|
||||
_id=3,
|
||||
name="n3",
|
||||
emulation_server=cored.distributed_server,
|
||||
node_type=NodeTypes.SWITCH
|
||||
)
|
||||
cored.request_handler.handle_message(message)
|
||||
|
||||
# link message one
|
||||
ip4_address = cored.prefix.addr(1)
|
||||
message = conftest.link_message(
|
||||
message = link_message(
|
||||
n1=1,
|
||||
n2=3,
|
||||
intf_one=0,
|
||||
|
@ -76,7 +185,7 @@ class TestDistributed:
|
|||
|
||||
# link message two
|
||||
ip4_address = cored.prefix.addr(2)
|
||||
message = conftest.link_message(
|
||||
message = link_message(
|
||||
n1=3,
|
||||
n2=2,
|
||||
intf_two=0,
|
||||
|
@ -85,12 +194,86 @@ class TestDistributed:
|
|||
cored.request_handler.handle_message(message)
|
||||
|
||||
# change session to instantiation state
|
||||
message = conftest.state_message(EventTypes.INSTANTIATION_STATE)
|
||||
message = state_message(EventTypes.INSTANTIATION_STATE)
|
||||
cored.request_handler.handle_message(message)
|
||||
|
||||
# test a ping command
|
||||
node_one = cored.session.get_node(1)
|
||||
message = conftest.command_message(node_one, "ping -c 5 %s" % ip4_address)
|
||||
message = command_message(node_one, "ping -c 5 %s" % ip4_address)
|
||||
cored.request_handler.dispatch_replies = validate_response
|
||||
cored.request_handler.handle_message(message)
|
||||
|
||||
def test_emane(self, cored, distributed_address):
|
||||
"""
|
||||
Test creating a distributed emane network.
|
||||
|
||||
:param core.api.tlv.coreserver.CoreServer conftest.Core cored: core daemon server to test with
|
||||
:param str distributed_address: distributed server to test against
|
||||
"""
|
||||
# initialize server for testing
|
||||
cored.setup(distributed_address)
|
||||
|
||||
# configure required controlnet
|
||||
cored.session.options.set_config("controlnet", "core1:172.16.1.0/24 core2:172.16.2.0/24")
|
||||
|
||||
# create local node
|
||||
message = node_message(
|
||||
_id=1,
|
||||
name="n1",
|
||||
model="mdr"
|
||||
)
|
||||
cored.request_handler.handle_message(message)
|
||||
|
||||
# create distributed node and assign to distributed server
|
||||
message = node_message(
|
||||
_id=2,
|
||||
name="n2",
|
||||
emulation_server=cored.distributed_server,
|
||||
model="mdr"
|
||||
)
|
||||
cored.request_handler.handle_message(message)
|
||||
|
||||
# create distributed switch and assign to distributed server
|
||||
message = node_message(
|
||||
_id=3,
|
||||
name="n3",
|
||||
node_type=NodeTypes.EMANE
|
||||
)
|
||||
cored.request_handler.handle_message(message)
|
||||
|
||||
# set emane model
|
||||
message = set_emane_model(3, EmaneIeee80211abgModel.name)
|
||||
cored.request_handler.handle_message(message)
|
||||
|
||||
# link message one
|
||||
ip4_address = cored.prefix.addr(1)
|
||||
message = link_message(
|
||||
n1=1,
|
||||
n2=3,
|
||||
intf_one=0,
|
||||
address_one=ip4_address,
|
||||
mask=32
|
||||
)
|
||||
cored.request_handler.handle_message(message)
|
||||
|
||||
# link message two
|
||||
ip4_address = cored.prefix.addr(2)
|
||||
message = link_message(
|
||||
n1=2,
|
||||
n2=3,
|
||||
intf_one=0,
|
||||
address_one=ip4_address,
|
||||
mask=32
|
||||
)
|
||||
cored.request_handler.handle_message(message)
|
||||
|
||||
# change session to instantiation state
|
||||
message = state_message(EventTypes.INSTANTIATION_STATE)
|
||||
cored.request_handler.handle_message(message)
|
||||
|
||||
# test a ping command
|
||||
node_one = cored.session.get_node(1)
|
||||
message = command_message(node_one, "ping -c 5 %s" % ip4_address)
|
||||
cored.request_handler.dispatch_replies = validate_response
|
||||
cored.request_handler.handle_message(message)
|
||||
|
||||
|
@ -98,14 +281,14 @@ class TestDistributed:
|
|||
"""
|
||||
Test creating a distributed prouter node.
|
||||
|
||||
:param core.coreserver.CoreServer conftest.Core cored: core daemon server to test with
|
||||
:param core.coreserver.CoreServer Core cored: core daemon server to test with
|
||||
:param str distributed_address: distributed server to test against
|
||||
"""
|
||||
# initialize server for testing
|
||||
cored.setup(distributed_address)
|
||||
|
||||
# create local node
|
||||
message = conftest.node_message(
|
||||
message = node_message(
|
||||
_id=1,
|
||||
name="n1",
|
||||
model="host"
|
||||
|
@ -113,7 +296,7 @@ class TestDistributed:
|
|||
cored.request_handler.handle_message(message)
|
||||
|
||||
# create distributed node and assign to distributed server
|
||||
message = conftest.node_message(
|
||||
message = node_message(
|
||||
_id=2,
|
||||
name="n2",
|
||||
emulation_server=cored.distributed_server,
|
||||
|
@ -123,7 +306,7 @@ class TestDistributed:
|
|||
cored.request_handler.handle_message(message)
|
||||
|
||||
# create distributed switch and assign to distributed server
|
||||
message = conftest.node_message(
|
||||
message = node_message(
|
||||
_id=3,
|
||||
name="n3",
|
||||
node_type=NodeTypes.SWITCH
|
||||
|
@ -132,7 +315,7 @@ class TestDistributed:
|
|||
|
||||
# link message one
|
||||
ip4_address = cored.prefix.addr(1)
|
||||
message = conftest.link_message(
|
||||
message = link_message(
|
||||
n1=1,
|
||||
n2=3,
|
||||
intf_one=0,
|
||||
|
@ -142,7 +325,7 @@ class TestDistributed:
|
|||
|
||||
# link message two
|
||||
ip4_address = cored.prefix.addr(2)
|
||||
message = conftest.link_message(
|
||||
message = link_message(
|
||||
n1=3,
|
||||
n2=2,
|
||||
intf_two=0,
|
||||
|
@ -151,12 +334,12 @@ class TestDistributed:
|
|||
cored.request_handler.handle_message(message)
|
||||
|
||||
# change session to instantiation state
|
||||
message = conftest.state_message(EventTypes.INSTANTIATION_STATE)
|
||||
message = state_message(EventTypes.INSTANTIATION_STATE)
|
||||
cored.request_handler.handle_message(message)
|
||||
|
||||
# test a ping command
|
||||
node_one = cored.session.get_node(1)
|
||||
message = conftest.command_message(node_one, "ping -c 5 %s" % ip4_address)
|
||||
message = command_message(node_one, "ping -c 5 %s" % ip4_address)
|
||||
cored.request_handler.dispatch_replies = validate_response
|
||||
cored.request_handler.handle_message(message)
|
||||
cored.request_handler.handle_message(message)
|
||||
|
@ -165,14 +348,14 @@ class TestDistributed:
|
|||
"""
|
||||
Test session broker creation.
|
||||
|
||||
:param core.coreserver.CoreServer conftest.Core cored: core daemon server to test with
|
||||
:param core.coreserver.CoreServer Core cored: core daemon server to test with
|
||||
:param str distributed_address: distributed server to test against
|
||||
"""
|
||||
# initialize server for testing
|
||||
cored.setup(distributed_address)
|
||||
|
||||
# create local node
|
||||
message = conftest.node_message(
|
||||
message = node_message(
|
||||
_id=1,
|
||||
name="n1",
|
||||
model="host"
|
||||
|
@ -180,7 +363,7 @@ class TestDistributed:
|
|||
cored.request_handler.handle_message(message)
|
||||
|
||||
# create distributed node and assign to distributed server
|
||||
message = conftest.node_message(
|
||||
message = node_message(
|
||||
_id=2,
|
||||
name=distributed_address,
|
||||
emulation_server=cored.distributed_server,
|
||||
|
@ -191,7 +374,7 @@ class TestDistributed:
|
|||
# link message one
|
||||
ip4_address = cored.prefix.addr(1)
|
||||
address_two = IpAddress.from_string(distributed_address)
|
||||
message = conftest.link_message(
|
||||
message = link_message(
|
||||
n1=1,
|
||||
n2=2,
|
||||
intf_one=0,
|
||||
|
@ -203,5 +386,5 @@ class TestDistributed:
|
|||
cored.request_handler.handle_message(message)
|
||||
|
||||
# change session to instantiation state
|
||||
message = conftest.state_message(EventTypes.INSTANTIATION_STATE)
|
||||
message = state_message(EventTypes.INSTANTIATION_STATE)
|
||||
cored.request_handler.handle_message(message)
|
||||
|
|
|
@ -458,18 +458,29 @@ class TestGrpc:
|
|||
# given
|
||||
client = CoreGrpcClient()
|
||||
session = grpc_server.coreemu.create_session()
|
||||
session.set_state(EventTypes.CONFIGURATION_STATE)
|
||||
wlan = session.add_node(_type=NodeTypes.WIRELESS_LAN)
|
||||
wlan.setmodel(BasicRangeModel, BasicRangeModel.default_values())
|
||||
session.instantiate()
|
||||
range_key = "range"
|
||||
range_value = "300"
|
||||
range_value = "50"
|
||||
|
||||
# then
|
||||
with client.context_connect():
|
||||
response = client.set_wlan_config(session.id, wlan.id, {range_key: range_value})
|
||||
response = client.set_wlan_config(session.id, wlan.id, {
|
||||
range_key: range_value,
|
||||
"delay": "0",
|
||||
"loss": "0",
|
||||
"bandwidth": "50000",
|
||||
"error": "0",
|
||||
"jitter": "0"
|
||||
})
|
||||
|
||||
# then
|
||||
assert response.result is True
|
||||
config = session.mobility.get_model_config(wlan.id, BasicRangeModel.name)
|
||||
assert config[range_key] == range_value
|
||||
assert wlan.model.range == int(range_value)
|
||||
|
||||
def test_get_emane_config(self, grpc_server):
|
||||
# given
|
||||
|
|
|
@ -1,174 +1,851 @@
|
|||
"""
|
||||
Unit tests for testing with a CORE switch.
|
||||
Tests for testing tlv message handling.
|
||||
"""
|
||||
import os
|
||||
import time
|
||||
|
||||
import threading
|
||||
import mock
|
||||
import pytest
|
||||
|
||||
from core.api.tlv import coreapi, dataconversion
|
||||
from core.api.tlv.coreapi import CoreExecuteTlv
|
||||
from core.emulator.enumerations import CORE_API_PORT, NodeTypes
|
||||
from core.emulator.enumerations import EventTlvs
|
||||
from core.emulator.enumerations import EventTypes
|
||||
from core.api.tlv import coreapi
|
||||
from core.emane.ieee80211abg import EmaneIeee80211abgModel
|
||||
from core.emulator.enumerations import EventTlvs, SessionTlvs, EventTypes, FileTlvs, RegisterTlvs, ConfigTlvs, \
|
||||
ConfigFlags
|
||||
from core.emulator.enumerations import ExecuteTlvs
|
||||
from core.emulator.enumerations import LinkTlvs
|
||||
from core.emulator.enumerations import LinkTypes
|
||||
from core.emulator.enumerations import MessageFlags
|
||||
from core.emulator.enumerations import MessageTypes
|
||||
from core.nodes import ipaddress
|
||||
from core.emulator.enumerations import NodeTypes, NodeTlvs
|
||||
from core.location.mobility import BasicRangeModel
|
||||
from core.nodes.ipaddress import Ipv4Prefix
|
||||
|
||||
|
||||
def command_message(node, command):
|
||||
"""
|
||||
Create an execute command TLV message.
|
||||
|
||||
:param node: node to execute command for
|
||||
:param command: command to execute
|
||||
:return: packed execute message
|
||||
"""
|
||||
tlv_data = CoreExecuteTlv.pack(ExecuteTlvs.NODE.value, node.id)
|
||||
tlv_data += CoreExecuteTlv.pack(ExecuteTlvs.NUMBER.value, 1)
|
||||
tlv_data += CoreExecuteTlv.pack(ExecuteTlvs.COMMAND.value, command)
|
||||
return coreapi.CoreExecMessage.pack(MessageFlags.STRING.value | MessageFlags.TEXT.value, tlv_data)
|
||||
|
||||
|
||||
def state_message(state):
|
||||
"""
|
||||
Create a event TLV message for a new state.
|
||||
|
||||
:param core.enumerations.EventTypes state: state to create message for
|
||||
:return: packed event message
|
||||
"""
|
||||
tlv_data = coreapi.CoreEventTlv.pack(EventTlvs.TYPE.value, state.value)
|
||||
return coreapi.CoreEventMessage.pack(0, tlv_data)
|
||||
|
||||
|
||||
def switch_link_message(switch, node, address, prefix_len):
|
||||
"""
|
||||
Create a link TLV message for node to a switch, with the provided address and prefix length.
|
||||
|
||||
:param switch: switch for link
|
||||
:param node: node for link
|
||||
:param address: address node on link
|
||||
:param prefix_len: prefix length of address
|
||||
:return: packed link message
|
||||
"""
|
||||
tlv_data = coreapi.CoreLinkTlv.pack(LinkTlvs.N1_NUMBER.value, switch.id)
|
||||
tlv_data += coreapi.CoreLinkTlv.pack(LinkTlvs.N2_NUMBER.value, node.id)
|
||||
tlv_data += coreapi.CoreLinkTlv.pack(LinkTlvs.TYPE.value, LinkTypes.WIRED.value)
|
||||
tlv_data += coreapi.CoreLinkTlv.pack(LinkTlvs.INTERFACE2_NUMBER.value, 0)
|
||||
tlv_data += coreapi.CoreLinkTlv.pack(LinkTlvs.INTERFACE2_IP4.value, address)
|
||||
tlv_data += coreapi.CoreLinkTlv.pack(LinkTlvs.INTERFACE2_IP4_MASK.value, prefix_len)
|
||||
return coreapi.CoreLinkMessage.pack(MessageFlags.ADD.value, tlv_data)
|
||||
|
||||
|
||||
def run_cmd(node, exec_cmd):
|
||||
"""
|
||||
Convenience method for sending commands to a node using the legacy API.
|
||||
|
||||
:param node: The node the command should be issued too
|
||||
:param exec_cmd: A string with the command to be run
|
||||
:return: Returns the result of the command
|
||||
"""
|
||||
# Set up the command api message
|
||||
# tlv_data = CoreExecuteTlv.pack(ExecuteTlvs.NODE.value, node.id)
|
||||
# tlv_data += CoreExecuteTlv.pack(ExecuteTlvs.NUMBER.value, 1)
|
||||
# tlv_data += CoreExecuteTlv.pack(ExecuteTlvs.COMMAND.value, exec_cmd)
|
||||
# message = coreapi.CoreExecMessage.pack(MessageFlags.STRING.value | MessageFlags.TEXT.value, tlv_data)
|
||||
message = command_message(node, exec_cmd)
|
||||
node.session.broker.handlerawmsg(message)
|
||||
|
||||
# Now wait for the response
|
||||
server = node.session.broker.servers["localhost"]
|
||||
server.sock.settimeout(50.0)
|
||||
|
||||
# receive messages until we get our execute response
|
||||
result = None
|
||||
status = False
|
||||
while True:
|
||||
message_header = server.sock.recv(coreapi.CoreMessage.header_len)
|
||||
message_type, message_flags, message_length = coreapi.CoreMessage.unpack_header(message_header)
|
||||
message_data = server.sock.recv(message_length)
|
||||
|
||||
# If we get the right response return the results
|
||||
print("received response message: %s" % message_type)
|
||||
if message_type == MessageTypes.EXECUTE.value:
|
||||
message = coreapi.CoreExecMessage(message_flags, message_header, message_data)
|
||||
result = message.get_tlv(ExecuteTlvs.RESULT.value)
|
||||
status = message.get_tlv(ExecuteTlvs.STATUS.value)
|
||||
break
|
||||
|
||||
return result, status
|
||||
def dict_to_str(values):
|
||||
return "|".join("%s=%s" % (x, values[x]) for x in values)
|
||||
|
||||
|
||||
class TestGui:
|
||||
def test_broker(self, cored):
|
||||
"""
|
||||
Test session broker creation.
|
||||
@pytest.mark.parametrize("node_type, model", [
|
||||
(NodeTypes.DEFAULT, "PC"),
|
||||
(NodeTypes.EMANE, None),
|
||||
(NodeTypes.HUB, None),
|
||||
(NodeTypes.SWITCH, None),
|
||||
(NodeTypes.WIRELESS_LAN, None),
|
||||
(NodeTypes.TUNNEL, None),
|
||||
(NodeTypes.RJ45, None),
|
||||
])
|
||||
def test_node_add(self, coreserver, node_type, model):
|
||||
node_id = 1
|
||||
message = coreapi.CoreNodeMessage.create(MessageFlags.ADD.value, [
|
||||
(NodeTlvs.NUMBER, node_id),
|
||||
(NodeTlvs.TYPE, node_type.value),
|
||||
(NodeTlvs.NAME, "n1"),
|
||||
(NodeTlvs.X_POSITION, 0),
|
||||
(NodeTlvs.Y_POSITION, 0),
|
||||
(NodeTlvs.MODEL, model),
|
||||
])
|
||||
|
||||
:param core.emulator.coreemu.EmuSession session: session for test
|
||||
:param cored: cored daemon server to test with
|
||||
"""
|
||||
coreserver.request_handler.handle_message(message)
|
||||
|
||||
# set core daemon to run in the background
|
||||
thread = threading.Thread(target=cored.server.serve_forever)
|
||||
thread.daemon = True
|
||||
thread.start()
|
||||
assert coreserver.session.get_node(node_id) is not None
|
||||
|
||||
# ip prefix for nodes
|
||||
prefix = ipaddress.Ipv4Prefix("10.83.0.0/16")
|
||||
daemon = "localhost"
|
||||
def test_node_update(self, coreserver):
|
||||
node_id = 1
|
||||
coreserver.session.add_node(_id=node_id)
|
||||
x = 50
|
||||
y = 100
|
||||
message = coreapi.CoreNodeMessage.create(0, [
|
||||
(NodeTlvs.NUMBER, node_id),
|
||||
(NodeTlvs.X_POSITION, x),
|
||||
(NodeTlvs.Y_POSITION, y),
|
||||
])
|
||||
|
||||
# add server
|
||||
session = cored.server.coreemu.create_session()
|
||||
session.broker.addserver(daemon, "127.0.0.1", CORE_API_PORT)
|
||||
coreserver.request_handler.handle_message(message)
|
||||
|
||||
# setup server
|
||||
session.broker.setupserver(daemon)
|
||||
node = coreserver.session.get_node(node_id)
|
||||
assert node is not None
|
||||
assert node.position.x == x
|
||||
assert node.position.y == y
|
||||
|
||||
# do not want the recvloop running as we will deal ourselves
|
||||
session.broker.dorecvloop = False
|
||||
def test_node_delete(self, coreserver):
|
||||
node_id = 1
|
||||
coreserver.session.add_node(_id=node_id)
|
||||
message = coreapi.CoreNodeMessage.create(MessageFlags.DELETE.value, [
|
||||
(NodeTlvs.NUMBER, node_id),
|
||||
])
|
||||
|
||||
# have broker handle a configuration state change
|
||||
session.set_state(EventTypes.CONFIGURATION_STATE)
|
||||
event_message = state_message(EventTypes.CONFIGURATION_STATE)
|
||||
session.broker.handlerawmsg(event_message)
|
||||
coreserver.request_handler.handle_message(message)
|
||||
|
||||
# create a switch node
|
||||
switch = session.add_node(_type=NodeTypes.SWITCH)
|
||||
switch.setposition(x=80, y=50)
|
||||
switch.server = daemon
|
||||
with pytest.raises(KeyError):
|
||||
coreserver.session.get_node(node_id)
|
||||
|
||||
# retrieve switch data representation, create a switch message for broker to handle
|
||||
switch_data = switch.data(MessageFlags.ADD.value)
|
||||
switch_message = dataconversion.convert_node(switch_data)
|
||||
session.broker.handlerawmsg(switch_message)
|
||||
def test_link_add_node_to_net(self, coreserver):
|
||||
node_one = 1
|
||||
coreserver.session.add_node(_id=node_one)
|
||||
switch = 2
|
||||
coreserver.session.add_node(_id=switch, _type=NodeTypes.SWITCH)
|
||||
ip_prefix = Ipv4Prefix("10.0.0.0/24")
|
||||
interface_one = ip_prefix.addr(node_one)
|
||||
message = coreapi.CoreLinkMessage.create(MessageFlags.ADD.value, [
|
||||
(LinkTlvs.N1_NUMBER, node_one),
|
||||
(LinkTlvs.N2_NUMBER, switch),
|
||||
(LinkTlvs.INTERFACE1_NUMBER, 0),
|
||||
(LinkTlvs.INTERFACE1_IP4, interface_one),
|
||||
(LinkTlvs.INTERFACE1_IP4_MASK, 24),
|
||||
])
|
||||
|
||||
# create node one
|
||||
node_one = session.add_node()
|
||||
node_one.server = daemon
|
||||
coreserver.request_handler.handle_message(message)
|
||||
|
||||
# create node two
|
||||
node_two = session.add_node()
|
||||
node_two.server = daemon
|
||||
switch_node = coreserver.session.get_node(switch)
|
||||
all_links = switch_node.all_link_data(0)
|
||||
assert len(all_links) == 1
|
||||
|
||||
# create node messages for the broker to handle
|
||||
for node in [node_one, node_two]:
|
||||
node_data = node.data(MessageFlags.ADD.value)
|
||||
node_message = dataconversion.convert_node(node_data)
|
||||
session.broker.handlerawmsg(node_message)
|
||||
def test_link_add_net_to_node(self, coreserver):
|
||||
node_one = 1
|
||||
coreserver.session.add_node(_id=node_one)
|
||||
switch = 2
|
||||
coreserver.session.add_node(_id=switch, _type=NodeTypes.SWITCH)
|
||||
ip_prefix = Ipv4Prefix("10.0.0.0/24")
|
||||
interface_one = ip_prefix.addr(node_one)
|
||||
message = coreapi.CoreLinkMessage.create(MessageFlags.ADD.value, [
|
||||
(LinkTlvs.N1_NUMBER, switch),
|
||||
(LinkTlvs.N2_NUMBER, node_one),
|
||||
(LinkTlvs.INTERFACE2_NUMBER, 0),
|
||||
(LinkTlvs.INTERFACE2_IP4, interface_one),
|
||||
(LinkTlvs.INTERFACE2_IP4_MASK, 24),
|
||||
])
|
||||
|
||||
# create links to switch from nodes for broker to handle
|
||||
for index, node in enumerate([node_one, node_two], start=1):
|
||||
ip4_address = prefix.addr(index)
|
||||
link_message = switch_link_message(switch, node, ip4_address, prefix.prefixlen)
|
||||
session.broker.handlerawmsg(link_message)
|
||||
coreserver.request_handler.handle_message(message)
|
||||
|
||||
# change session to instantiation state
|
||||
event_message = state_message(EventTypes.INSTANTIATION_STATE)
|
||||
session.broker.handlerawmsg(event_message)
|
||||
switch_node = coreserver.session.get_node(switch)
|
||||
all_links = switch_node.all_link_data(0)
|
||||
assert len(all_links) == 1
|
||||
|
||||
# Get the ip or last node and ping it from the first
|
||||
output, status = run_cmd(node_one, "ip -4 -o addr show dev eth0")
|
||||
pingip = output.split()[3].split("/")[0]
|
||||
output, status = run_cmd(node_two, "ping -c 5 " + pingip)
|
||||
assert not status
|
||||
def test_link_add_node_to_node(self, coreserver):
|
||||
node_one = 1
|
||||
coreserver.session.add_node(_id=node_one)
|
||||
node_two = 2
|
||||
coreserver.session.add_node(_id=node_two)
|
||||
ip_prefix = Ipv4Prefix("10.0.0.0/24")
|
||||
interface_one = ip_prefix.addr(node_one)
|
||||
interface_two = ip_prefix.addr(node_two)
|
||||
message = coreapi.CoreLinkMessage.create(MessageFlags.ADD.value, [
|
||||
(LinkTlvs.N1_NUMBER, node_one),
|
||||
(LinkTlvs.N2_NUMBER, node_two),
|
||||
(LinkTlvs.INTERFACE1_NUMBER, 0),
|
||||
(LinkTlvs.INTERFACE1_IP4, interface_one),
|
||||
(LinkTlvs.INTERFACE1_IP4_MASK, 24),
|
||||
(LinkTlvs.INTERFACE2_NUMBER, 0),
|
||||
(LinkTlvs.INTERFACE2_IP4, interface_two),
|
||||
(LinkTlvs.INTERFACE2_IP4_MASK, 24),
|
||||
])
|
||||
|
||||
coreserver.request_handler.handle_message(message)
|
||||
|
||||
all_links = []
|
||||
for node_id in coreserver.session.nodes:
|
||||
node = coreserver.session.nodes[node_id]
|
||||
all_links += node.all_link_data(0)
|
||||
assert len(all_links) == 1
|
||||
|
||||
def test_link_update(self, coreserver):
|
||||
node_one = 1
|
||||
coreserver.session.add_node(_id=node_one)
|
||||
switch = 2
|
||||
coreserver.session.add_node(_id=switch, _type=NodeTypes.SWITCH)
|
||||
ip_prefix = Ipv4Prefix("10.0.0.0/24")
|
||||
interface_one = ip_prefix.addr(node_one)
|
||||
message = coreapi.CoreLinkMessage.create(MessageFlags.ADD.value, [
|
||||
(LinkTlvs.N1_NUMBER, node_one),
|
||||
(LinkTlvs.N2_NUMBER, switch),
|
||||
(LinkTlvs.INTERFACE1_NUMBER, 0),
|
||||
(LinkTlvs.INTERFACE1_IP4, interface_one),
|
||||
(LinkTlvs.INTERFACE1_IP4_MASK, 24),
|
||||
])
|
||||
coreserver.request_handler.handle_message(message)
|
||||
switch_node = coreserver.session.get_node(switch)
|
||||
all_links = switch_node.all_link_data(0)
|
||||
assert len(all_links) == 1
|
||||
link = all_links[0]
|
||||
assert link.bandwidth is None
|
||||
|
||||
bandwidth = 50000
|
||||
message = coreapi.CoreLinkMessage.create(0, [
|
||||
(LinkTlvs.N1_NUMBER, node_one),
|
||||
(LinkTlvs.N2_NUMBER, switch),
|
||||
(LinkTlvs.INTERFACE1_NUMBER, 0),
|
||||
(LinkTlvs.BANDWIDTH, bandwidth),
|
||||
])
|
||||
coreserver.request_handler.handle_message(message)
|
||||
|
||||
switch_node = coreserver.session.get_node(switch)
|
||||
all_links = switch_node.all_link_data(0)
|
||||
assert len(all_links) == 1
|
||||
link = all_links[0]
|
||||
assert link.bandwidth == bandwidth
|
||||
|
||||
def test_link_delete_node_to_node(self, coreserver):
|
||||
node_one = 1
|
||||
coreserver.session.add_node(_id=node_one)
|
||||
node_two = 2
|
||||
coreserver.session.add_node(_id=node_two)
|
||||
ip_prefix = Ipv4Prefix("10.0.0.0/24")
|
||||
interface_one = ip_prefix.addr(node_one)
|
||||
interface_two = ip_prefix.addr(node_two)
|
||||
message = coreapi.CoreLinkMessage.create(MessageFlags.ADD.value, [
|
||||
(LinkTlvs.N1_NUMBER, node_one),
|
||||
(LinkTlvs.N2_NUMBER, node_two),
|
||||
(LinkTlvs.INTERFACE1_NUMBER, 0),
|
||||
(LinkTlvs.INTERFACE1_IP4, interface_one),
|
||||
(LinkTlvs.INTERFACE1_IP4_MASK, 24),
|
||||
(LinkTlvs.INTERFACE2_IP4, interface_two),
|
||||
(LinkTlvs.INTERFACE2_IP4_MASK, 24),
|
||||
])
|
||||
coreserver.request_handler.handle_message(message)
|
||||
all_links = []
|
||||
for node_id in coreserver.session.nodes:
|
||||
node = coreserver.session.nodes[node_id]
|
||||
all_links += node.all_link_data(0)
|
||||
assert len(all_links) == 1
|
||||
|
||||
message = coreapi.CoreLinkMessage.create(MessageFlags.DELETE.value, [
|
||||
(LinkTlvs.N1_NUMBER, node_one),
|
||||
(LinkTlvs.N2_NUMBER, node_two),
|
||||
(LinkTlvs.INTERFACE1_NUMBER, 0),
|
||||
(LinkTlvs.INTERFACE2_NUMBER, 0),
|
||||
])
|
||||
coreserver.request_handler.handle_message(message)
|
||||
|
||||
all_links = []
|
||||
for node_id in coreserver.session.nodes:
|
||||
node = coreserver.session.nodes[node_id]
|
||||
all_links += node.all_link_data(0)
|
||||
assert len(all_links) == 0
|
||||
|
||||
def test_link_delete_node_to_net(self, coreserver):
|
||||
node_one = 1
|
||||
coreserver.session.add_node(_id=node_one)
|
||||
switch = 2
|
||||
coreserver.session.add_node(_id=switch, _type=NodeTypes.SWITCH)
|
||||
ip_prefix = Ipv4Prefix("10.0.0.0/24")
|
||||
interface_one = ip_prefix.addr(node_one)
|
||||
message = coreapi.CoreLinkMessage.create(MessageFlags.ADD.value, [
|
||||
(LinkTlvs.N1_NUMBER, node_one),
|
||||
(LinkTlvs.N2_NUMBER, switch),
|
||||
(LinkTlvs.INTERFACE1_NUMBER, 0),
|
||||
(LinkTlvs.INTERFACE1_IP4, interface_one),
|
||||
(LinkTlvs.INTERFACE1_IP4_MASK, 24),
|
||||
])
|
||||
coreserver.request_handler.handle_message(message)
|
||||
switch_node = coreserver.session.get_node(switch)
|
||||
all_links = switch_node.all_link_data(0)
|
||||
assert len(all_links) == 1
|
||||
|
||||
message = coreapi.CoreLinkMessage.create(MessageFlags.DELETE.value, [
|
||||
(LinkTlvs.N1_NUMBER, node_one),
|
||||
(LinkTlvs.N2_NUMBER, switch),
|
||||
(LinkTlvs.INTERFACE1_NUMBER, 0),
|
||||
])
|
||||
coreserver.request_handler.handle_message(message)
|
||||
|
||||
switch_node = coreserver.session.get_node(switch)
|
||||
all_links = switch_node.all_link_data(0)
|
||||
assert len(all_links) == 0
|
||||
|
||||
def test_link_delete_net_to_node(self, coreserver):
|
||||
node_one = 1
|
||||
coreserver.session.add_node(_id=node_one)
|
||||
switch = 2
|
||||
coreserver.session.add_node(_id=switch, _type=NodeTypes.SWITCH)
|
||||
ip_prefix = Ipv4Prefix("10.0.0.0/24")
|
||||
interface_one = ip_prefix.addr(node_one)
|
||||
message = coreapi.CoreLinkMessage.create(MessageFlags.ADD.value, [
|
||||
(LinkTlvs.N1_NUMBER, node_one),
|
||||
(LinkTlvs.N2_NUMBER, switch),
|
||||
(LinkTlvs.INTERFACE1_NUMBER, 0),
|
||||
(LinkTlvs.INTERFACE1_IP4, interface_one),
|
||||
(LinkTlvs.INTERFACE1_IP4_MASK, 24),
|
||||
])
|
||||
coreserver.request_handler.handle_message(message)
|
||||
switch_node = coreserver.session.get_node(switch)
|
||||
all_links = switch_node.all_link_data(0)
|
||||
assert len(all_links) == 1
|
||||
|
||||
message = coreapi.CoreLinkMessage.create(MessageFlags.DELETE.value, [
|
||||
(LinkTlvs.N1_NUMBER, switch),
|
||||
(LinkTlvs.N2_NUMBER, node_one),
|
||||
(LinkTlvs.INTERFACE2_NUMBER, 0),
|
||||
])
|
||||
coreserver.request_handler.handle_message(message)
|
||||
|
||||
switch_node = coreserver.session.get_node(switch)
|
||||
all_links = switch_node.all_link_data(0)
|
||||
assert len(all_links) == 0
|
||||
|
||||
def test_session_update(self, coreserver):
|
||||
session_id = coreserver.session.id
|
||||
name = "test"
|
||||
message = coreapi.CoreSessionMessage.create(0, [
|
||||
(SessionTlvs.NUMBER, str(session_id)),
|
||||
(SessionTlvs.NAME, name),
|
||||
])
|
||||
|
||||
coreserver.request_handler.handle_message(message)
|
||||
|
||||
assert coreserver.session.name == name
|
||||
|
||||
def test_session_query(self, coreserver):
|
||||
coreserver.request_handler.dispatch_replies = mock.MagicMock()
|
||||
message = coreapi.CoreSessionMessage.create(MessageFlags.STRING.value, [])
|
||||
|
||||
coreserver.request_handler.handle_message(message)
|
||||
|
||||
args, _ = coreserver.request_handler.dispatch_replies.call_args
|
||||
replies = args[0]
|
||||
assert len(replies) == 1
|
||||
|
||||
def test_session_join(self, coreserver):
|
||||
coreserver.request_handler.dispatch_replies = mock.MagicMock()
|
||||
session_id = coreserver.session.id
|
||||
message = coreapi.CoreSessionMessage.create(MessageFlags.ADD.value, [
|
||||
(SessionTlvs.NUMBER, str(session_id)),
|
||||
])
|
||||
|
||||
coreserver.request_handler.handle_message(message)
|
||||
|
||||
assert coreserver.request_handler.session.id == session_id
|
||||
|
||||
def test_session_delete(self, coreserver):
|
||||
assert len(coreserver.server.coreemu.sessions) == 1
|
||||
session_id = coreserver.session.id
|
||||
message = coreapi.CoreSessionMessage.create(MessageFlags.DELETE.value, [
|
||||
(SessionTlvs.NUMBER, str(session_id)),
|
||||
])
|
||||
|
||||
coreserver.request_handler.handle_message(message)
|
||||
|
||||
assert len(coreserver.server.coreemu.sessions) == 0
|
||||
|
||||
def test_file_hook_add(self, coreserver):
|
||||
state = EventTypes.DATACOLLECT_STATE.value
|
||||
assert coreserver.session._hooks.get(state) is None
|
||||
file_name = "test.sh"
|
||||
file_data = "echo hello"
|
||||
message = coreapi.CoreFileMessage.create(MessageFlags.ADD.value, [
|
||||
(FileTlvs.TYPE, "hook:%s" % state),
|
||||
(FileTlvs.NAME, file_name),
|
||||
(FileTlvs.DATA, file_data),
|
||||
])
|
||||
|
||||
coreserver.request_handler.handle_message(message)
|
||||
|
||||
hooks = coreserver.session._hooks.get(state)
|
||||
assert len(hooks) == 1
|
||||
name, data = hooks[0]
|
||||
assert file_name == name
|
||||
assert file_data == data
|
||||
|
||||
def test_file_service_file_set(self, coreserver):
|
||||
node = coreserver.session.add_node()
|
||||
service = "DefaultRoute"
|
||||
file_name = "defaultroute.sh"
|
||||
file_data = "echo hello"
|
||||
message = coreapi.CoreFileMessage.create(MessageFlags.ADD.value, [
|
||||
(FileTlvs.NODE, node.id),
|
||||
(FileTlvs.TYPE, "service:%s" % service),
|
||||
(FileTlvs.NAME, file_name),
|
||||
(FileTlvs.DATA, file_data),
|
||||
])
|
||||
|
||||
coreserver.request_handler.handle_message(message)
|
||||
|
||||
service_file = coreserver.session.services.get_service_file(node, service, file_name)
|
||||
assert file_data == service_file.data
|
||||
|
||||
def test_file_node_file_copy(self, coreserver):
|
||||
file_name = "/var/log/test/node.log"
|
||||
node = coreserver.session.add_node()
|
||||
node.makenodedir()
|
||||
file_data = "echo hello"
|
||||
message = coreapi.CoreFileMessage.create(MessageFlags.ADD.value, [
|
||||
(FileTlvs.NODE, node.id),
|
||||
(FileTlvs.NAME, file_name),
|
||||
(FileTlvs.DATA, file_data),
|
||||
])
|
||||
|
||||
coreserver.request_handler.handle_message(message)
|
||||
|
||||
directory, basename = os.path.split(file_name)
|
||||
created_directory = directory[1:].replace("/", ".")
|
||||
create_path = os.path.join(node.nodedir, created_directory, basename)
|
||||
assert os.path.exists(create_path)
|
||||
|
||||
def test_exec_node_tty(self, coreserver):
|
||||
coreserver.request_handler.dispatch_replies = mock.MagicMock()
|
||||
node = coreserver.session.add_node()
|
||||
node.startup()
|
||||
message = coreapi.CoreExecMessage.create(MessageFlags.TTY.value, [
|
||||
(ExecuteTlvs.NODE, node.id),
|
||||
(ExecuteTlvs.NUMBER, 1),
|
||||
(ExecuteTlvs.COMMAND, "bash")
|
||||
])
|
||||
|
||||
coreserver.request_handler.handle_message(message)
|
||||
|
||||
args, _ = coreserver.request_handler.dispatch_replies.call_args
|
||||
replies = args[0]
|
||||
assert len(replies) == 1
|
||||
|
||||
def test_exec_local_command(self, coreserver):
|
||||
coreserver.request_handler.dispatch_replies = mock.MagicMock()
|
||||
node = coreserver.session.add_node()
|
||||
node.startup()
|
||||
message = coreapi.CoreExecMessage.create(
|
||||
MessageFlags.TEXT.value | MessageFlags.LOCAL.value, [
|
||||
(ExecuteTlvs.NODE, node.id),
|
||||
(ExecuteTlvs.NUMBER, 1),
|
||||
(ExecuteTlvs.COMMAND, "echo hello")
|
||||
])
|
||||
|
||||
coreserver.request_handler.handle_message(message)
|
||||
|
||||
args, _ = coreserver.request_handler.dispatch_replies.call_args
|
||||
replies = args[0]
|
||||
assert len(replies) == 1
|
||||
|
||||
def test_exec_node_command(self, coreserver):
|
||||
coreserver.request_handler.dispatch_replies = mock.MagicMock()
|
||||
node = coreserver.session.add_node()
|
||||
node.startup()
|
||||
message = coreapi.CoreExecMessage.create(
|
||||
MessageFlags.TEXT.value, [
|
||||
(ExecuteTlvs.NODE, node.id),
|
||||
(ExecuteTlvs.NUMBER, 1),
|
||||
(ExecuteTlvs.COMMAND, "echo hello")
|
||||
])
|
||||
|
||||
coreserver.request_handler.handle_message(message)
|
||||
|
||||
args, _ = coreserver.request_handler.dispatch_replies.call_args
|
||||
replies = args[0]
|
||||
assert len(replies) == 1
|
||||
|
||||
@pytest.mark.parametrize("state", [
|
||||
EventTypes.SHUTDOWN_STATE,
|
||||
EventTypes.RUNTIME_STATE,
|
||||
EventTypes.DATACOLLECT_STATE,
|
||||
EventTypes.CONFIGURATION_STATE,
|
||||
EventTypes.DEFINITION_STATE
|
||||
])
|
||||
def test_event_state(self, coreserver, state):
|
||||
message = coreapi.CoreEventMessage.create(0, [
|
||||
(EventTlvs.TYPE, state.value),
|
||||
])
|
||||
|
||||
coreserver.request_handler.handle_message(message)
|
||||
|
||||
assert coreserver.session.state == state.value
|
||||
|
||||
def test_event_schedule(self, coreserver):
|
||||
coreserver.session.add_event = mock.MagicMock()
|
||||
node = coreserver.session.add_node()
|
||||
message = coreapi.CoreEventMessage.create(MessageFlags.ADD.value, [
|
||||
(EventTlvs.TYPE, EventTypes.SCHEDULED.value),
|
||||
(EventTlvs.TIME, str(time.time() + 100)),
|
||||
(EventTlvs.NODE, node.id),
|
||||
(EventTlvs.NAME, "event"),
|
||||
(EventTlvs.DATA, "data"),
|
||||
])
|
||||
|
||||
coreserver.request_handler.handle_message(message)
|
||||
|
||||
coreserver.session.add_event.assert_called_once()
|
||||
|
||||
def test_event_save_xml(self, coreserver, tmpdir):
|
||||
xml_file = tmpdir.join("session.xml")
|
||||
file_path = xml_file.strpath
|
||||
coreserver.session.add_node()
|
||||
message = coreapi.CoreEventMessage.create(0, [
|
||||
(EventTlvs.TYPE, EventTypes.FILE_SAVE.value),
|
||||
(EventTlvs.NAME, file_path),
|
||||
])
|
||||
|
||||
coreserver.request_handler.handle_message(message)
|
||||
|
||||
assert os.path.exists(file_path)
|
||||
|
||||
def test_event_open_xml(self, coreserver, tmpdir):
|
||||
xml_file = tmpdir.join("session.xml")
|
||||
file_path = xml_file.strpath
|
||||
node = coreserver.session.add_node()
|
||||
coreserver.session.save_xml(file_path)
|
||||
coreserver.session.delete_node(node.id)
|
||||
message = coreapi.CoreEventMessage.create(0, [
|
||||
(EventTlvs.TYPE, EventTypes.FILE_OPEN.value),
|
||||
(EventTlvs.NAME, file_path),
|
||||
])
|
||||
|
||||
coreserver.request_handler.handle_message(message)
|
||||
|
||||
assert coreserver.session.get_node(node.id)
|
||||
|
||||
@pytest.mark.parametrize("state", [
|
||||
EventTypes.START,
|
||||
EventTypes.STOP,
|
||||
EventTypes.RESTART,
|
||||
EventTypes.PAUSE,
|
||||
EventTypes.RECONFIGURE
|
||||
])
|
||||
def test_event_service(self, coreserver, state):
|
||||
coreserver.session.broadcast_event = mock.MagicMock()
|
||||
node = coreserver.session.add_node()
|
||||
node.startup()
|
||||
message = coreapi.CoreEventMessage.create(0, [
|
||||
(EventTlvs.TYPE, state.value),
|
||||
(EventTlvs.NODE, node.id),
|
||||
(EventTlvs.NAME, "service:DefaultRoute"),
|
||||
])
|
||||
|
||||
coreserver.request_handler.handle_message(message)
|
||||
|
||||
coreserver.session.broadcast_event.assert_called_once()
|
||||
|
||||
@pytest.mark.parametrize("state", [
|
||||
EventTypes.START,
|
||||
EventTypes.STOP,
|
||||
EventTypes.RESTART,
|
||||
EventTypes.PAUSE,
|
||||
EventTypes.RECONFIGURE
|
||||
])
|
||||
def test_event_mobility(self, coreserver, state):
|
||||
message = coreapi.CoreEventMessage.create(0, [
|
||||
(EventTlvs.TYPE, state.value),
|
||||
(EventTlvs.NAME, "mobility:ns2script"),
|
||||
])
|
||||
|
||||
coreserver.request_handler.handle_message(message)
|
||||
|
||||
def test_register_gui(self, coreserver):
|
||||
coreserver.request_handler.master = False
|
||||
message = coreapi.CoreRegMessage.create(0, [
|
||||
(RegisterTlvs.GUI, "gui"),
|
||||
])
|
||||
|
||||
coreserver.request_handler.handle_message(message)
|
||||
|
||||
assert coreserver.request_handler.master is True
|
||||
|
||||
def test_register_xml(self, coreserver, tmpdir):
|
||||
xml_file = tmpdir.join("session.xml")
|
||||
file_path = xml_file.strpath
|
||||
node = coreserver.session.add_node()
|
||||
coreserver.session.save_xml(file_path)
|
||||
coreserver.session.delete_node(node.id)
|
||||
message = coreapi.CoreRegMessage.create(0, [
|
||||
(RegisterTlvs.EXECUTE_SERVER, file_path),
|
||||
])
|
||||
coreserver.session.instantiate()
|
||||
|
||||
coreserver.request_handler.handle_message(message)
|
||||
|
||||
assert coreserver.server.coreemu.sessions[2].get_node(node.id)
|
||||
|
||||
def test_register_python(self, coreserver, tmpdir):
|
||||
xml_file = tmpdir.join("test.py")
|
||||
file_path = xml_file.strpath
|
||||
with open(file_path, "w") as f:
|
||||
f.write("coreemu = globals()['coreemu']\n")
|
||||
f.write("session = coreemu.sessions[1]\n")
|
||||
f.write("session.add_node()\n")
|
||||
message = coreapi.CoreRegMessage.create(0, [
|
||||
(RegisterTlvs.EXECUTE_SERVER, file_path),
|
||||
])
|
||||
coreserver.session.instantiate()
|
||||
|
||||
coreserver.request_handler.handle_message(message)
|
||||
|
||||
assert len(coreserver.session.nodes) == 1
|
||||
|
||||
def test_config_all(self, coreserver):
|
||||
node = coreserver.session.add_node()
|
||||
message = coreapi.CoreConfMessage.create(MessageFlags.ADD.value, [
|
||||
(ConfigTlvs.OBJECT, "all"),
|
||||
(ConfigTlvs.NODE, node.id),
|
||||
(ConfigTlvs.TYPE, ConfigFlags.RESET.value),
|
||||
])
|
||||
coreserver.session.location.reset = mock.MagicMock()
|
||||
|
||||
coreserver.request_handler.handle_message(message)
|
||||
|
||||
coreserver.session.location.reset.assert_called_once()
|
||||
|
||||
def test_config_options_request(self, coreserver):
|
||||
message = coreapi.CoreConfMessage.create(0, [
|
||||
(ConfigTlvs.OBJECT, "session"),
|
||||
(ConfigTlvs.TYPE, ConfigFlags.REQUEST.value),
|
||||
])
|
||||
coreserver.request_handler.handle_broadcast_config = mock.MagicMock()
|
||||
|
||||
coreserver.request_handler.handle_message(message)
|
||||
|
||||
coreserver.request_handler.handle_broadcast_config.assert_called_once()
|
||||
|
||||
def test_config_options_update(self, coreserver):
|
||||
test_key = "test"
|
||||
test_value = "test"
|
||||
values = {
|
||||
test_key: test_value
|
||||
}
|
||||
message = coreapi.CoreConfMessage.create(0, [
|
||||
(ConfigTlvs.OBJECT, "session"),
|
||||
(ConfigTlvs.TYPE, ConfigFlags.UPDATE.value),
|
||||
(ConfigTlvs.VALUES, dict_to_str(values)),
|
||||
])
|
||||
|
||||
coreserver.request_handler.handle_message(message)
|
||||
|
||||
assert coreserver.session.options.get_config(test_key) == test_value
|
||||
|
||||
def test_config_location_reset(self, coreserver):
|
||||
message = coreapi.CoreConfMessage.create(0, [
|
||||
(ConfigTlvs.OBJECT, "location"),
|
||||
(ConfigTlvs.TYPE, ConfigFlags.RESET.value),
|
||||
])
|
||||
coreserver.session.location.refxyz = (10, 10, 10)
|
||||
|
||||
coreserver.request_handler.handle_message(message)
|
||||
|
||||
assert coreserver.session.location.refxyz == (0, 0, 0)
|
||||
|
||||
def test_config_location_update(self, coreserver):
|
||||
message = coreapi.CoreConfMessage.create(0, [
|
||||
(ConfigTlvs.OBJECT, "location"),
|
||||
(ConfigTlvs.TYPE, ConfigFlags.UPDATE.value),
|
||||
(ConfigTlvs.VALUES, "10|10|70|50|0|0.5"),
|
||||
])
|
||||
|
||||
coreserver.request_handler.handle_message(message)
|
||||
|
||||
assert coreserver.session.location.refxyz == (10, 10, 0.0)
|
||||
assert coreserver.session.location.refgeo == (70, 50, 0)
|
||||
assert coreserver.session.location.refscale == 0.5
|
||||
|
||||
def test_config_metadata_request(self, coreserver):
|
||||
message = coreapi.CoreConfMessage.create(0, [
|
||||
(ConfigTlvs.OBJECT, "metadata"),
|
||||
(ConfigTlvs.TYPE, ConfigFlags.REQUEST.value),
|
||||
])
|
||||
coreserver.request_handler.handle_broadcast_config = mock.MagicMock()
|
||||
|
||||
coreserver.request_handler.handle_message(message)
|
||||
|
||||
coreserver.request_handler.handle_broadcast_config.assert_called_once()
|
||||
|
||||
def test_config_metadata_update(self, coreserver):
|
||||
test_key = "test"
|
||||
test_value = "test"
|
||||
values = {
|
||||
test_key: test_value
|
||||
}
|
||||
message = coreapi.CoreConfMessage.create(0, [
|
||||
(ConfigTlvs.OBJECT, "metadata"),
|
||||
(ConfigTlvs.TYPE, ConfigFlags.UPDATE.value),
|
||||
(ConfigTlvs.VALUES, dict_to_str(values)),
|
||||
])
|
||||
|
||||
coreserver.request_handler.handle_message(message)
|
||||
|
||||
assert coreserver.session.metadata.get_config(test_key) == test_value
|
||||
|
||||
def test_config_broker_request(self, coreserver):
|
||||
server = "test"
|
||||
host = "10.0.0.1"
|
||||
port = 50000
|
||||
message = coreapi.CoreConfMessage.create(0, [
|
||||
(ConfigTlvs.OBJECT, "broker"),
|
||||
(ConfigTlvs.TYPE, ConfigFlags.UPDATE.value),
|
||||
(ConfigTlvs.VALUES, "%s:%s:%s" % (server, host, port)),
|
||||
])
|
||||
coreserver.session.broker.addserver = mock.MagicMock()
|
||||
coreserver.session.broker.setupserver = mock.MagicMock()
|
||||
|
||||
coreserver.request_handler.handle_message(message)
|
||||
|
||||
coreserver.session.broker.addserver.assert_called_once_with(server, host, port)
|
||||
coreserver.session.broker.setupserver.assert_called_once_with(server)
|
||||
|
||||
def test_config_services_request_all(self, coreserver):
|
||||
message = coreapi.CoreConfMessage.create(0, [
|
||||
(ConfigTlvs.OBJECT, "services"),
|
||||
(ConfigTlvs.TYPE, ConfigFlags.REQUEST.value),
|
||||
])
|
||||
coreserver.request_handler.handle_broadcast_config = mock.MagicMock()
|
||||
|
||||
coreserver.request_handler.handle_message(message)
|
||||
|
||||
coreserver.request_handler.handle_broadcast_config.assert_called_once()
|
||||
|
||||
def test_config_services_request_specific(self, coreserver):
|
||||
node = coreserver.session.add_node()
|
||||
message = coreapi.CoreConfMessage.create(0, [
|
||||
(ConfigTlvs.NODE, node.id),
|
||||
(ConfigTlvs.OBJECT, "services"),
|
||||
(ConfigTlvs.TYPE, ConfigFlags.REQUEST.value),
|
||||
(ConfigTlvs.OPAQUE, "service:DefaultRoute"),
|
||||
])
|
||||
coreserver.request_handler.handle_broadcast_config = mock.MagicMock()
|
||||
|
||||
coreserver.request_handler.handle_message(message)
|
||||
|
||||
coreserver.request_handler.handle_broadcast_config.assert_called_once()
|
||||
|
||||
def test_config_services_request_specific_file(self, coreserver):
|
||||
node = coreserver.session.add_node()
|
||||
message = coreapi.CoreConfMessage.create(0, [
|
||||
(ConfigTlvs.NODE, node.id),
|
||||
(ConfigTlvs.OBJECT, "services"),
|
||||
(ConfigTlvs.TYPE, ConfigFlags.REQUEST.value),
|
||||
(ConfigTlvs.OPAQUE, "service:DefaultRoute:defaultroute.sh"),
|
||||
])
|
||||
coreserver.session.broadcast_file = mock.MagicMock()
|
||||
|
||||
coreserver.request_handler.handle_message(message)
|
||||
|
||||
coreserver.session.broadcast_file.assert_called_once()
|
||||
|
||||
def test_config_services_reset(self, coreserver):
|
||||
node = coreserver.session.add_node()
|
||||
service = "DefaultRoute"
|
||||
coreserver.session.services.set_service(node.id, service)
|
||||
message = coreapi.CoreConfMessage.create(0, [
|
||||
(ConfigTlvs.OBJECT, "services"),
|
||||
(ConfigTlvs.TYPE, ConfigFlags.RESET.value),
|
||||
])
|
||||
assert coreserver.session.services.get_service(node.id, service) is not None
|
||||
|
||||
coreserver.request_handler.handle_message(message)
|
||||
|
||||
assert coreserver.session.services.get_service(node.id, service) is None
|
||||
|
||||
def test_config_services_set(self, coreserver):
|
||||
node = coreserver.session.add_node()
|
||||
service = "DefaultRoute"
|
||||
values = {
|
||||
"meta": "metadata"
|
||||
}
|
||||
message = coreapi.CoreConfMessage.create(0, [
|
||||
(ConfigTlvs.NODE, node.id),
|
||||
(ConfigTlvs.OBJECT, "services"),
|
||||
(ConfigTlvs.TYPE, ConfigFlags.UPDATE.value),
|
||||
(ConfigTlvs.OPAQUE, "service:%s" % service),
|
||||
(ConfigTlvs.VALUES, dict_to_str(values)),
|
||||
])
|
||||
assert coreserver.session.services.get_service(node.id, service) is None
|
||||
|
||||
coreserver.request_handler.handle_message(message)
|
||||
|
||||
assert coreserver.session.services.get_service(node.id, service) is not None
|
||||
|
||||
def test_config_mobility_reset(self, coreserver):
|
||||
wlan = coreserver.session.add_node(_type=NodeTypes.WIRELESS_LAN)
|
||||
message = coreapi.CoreConfMessage.create(0, [
|
||||
(ConfigTlvs.OBJECT, "MobilityManager"),
|
||||
(ConfigTlvs.TYPE, ConfigFlags.RESET.value),
|
||||
])
|
||||
coreserver.session.mobility.set_model_config(wlan.id, BasicRangeModel.name, {})
|
||||
assert len(coreserver.session.mobility.node_configurations) == 1
|
||||
|
||||
coreserver.request_handler.handle_message(message)
|
||||
|
||||
assert len(coreserver.session.mobility.node_configurations) == 0
|
||||
|
||||
def test_config_mobility_model_request(self, coreserver):
|
||||
wlan = coreserver.session.add_node(_type=NodeTypes.WIRELESS_LAN)
|
||||
message = coreapi.CoreConfMessage.create(0, [
|
||||
(ConfigTlvs.NODE, wlan.id),
|
||||
(ConfigTlvs.OBJECT, BasicRangeModel.name),
|
||||
(ConfigTlvs.TYPE, ConfigFlags.REQUEST.value),
|
||||
])
|
||||
coreserver.request_handler.handle_broadcast_config = mock.MagicMock()
|
||||
|
||||
coreserver.request_handler.handle_message(message)
|
||||
|
||||
coreserver.request_handler.handle_broadcast_config.assert_called_once()
|
||||
|
||||
def test_config_mobility_model_update(self, coreserver):
|
||||
wlan = coreserver.session.add_node(_type=NodeTypes.WIRELESS_LAN)
|
||||
config_key = "range"
|
||||
config_value = "1000"
|
||||
values = {
|
||||
config_key: config_value
|
||||
}
|
||||
message = coreapi.CoreConfMessage.create(0, [
|
||||
(ConfigTlvs.NODE, wlan.id),
|
||||
(ConfigTlvs.OBJECT, BasicRangeModel.name),
|
||||
(ConfigTlvs.TYPE, ConfigFlags.UPDATE.value),
|
||||
(ConfigTlvs.VALUES, dict_to_str(values)),
|
||||
])
|
||||
|
||||
coreserver.request_handler.handle_message(message)
|
||||
|
||||
config = coreserver.session.mobility.get_model_config(wlan.id, BasicRangeModel.name)
|
||||
assert config[config_key] == config_value
|
||||
|
||||
def test_config_emane_model_request(self, coreserver):
|
||||
wlan = coreserver.session.add_node(_type=NodeTypes.WIRELESS_LAN)
|
||||
message = coreapi.CoreConfMessage.create(0, [
|
||||
(ConfigTlvs.NODE, wlan.id),
|
||||
(ConfigTlvs.OBJECT, EmaneIeee80211abgModel.name),
|
||||
(ConfigTlvs.TYPE, ConfigFlags.REQUEST.value),
|
||||
])
|
||||
coreserver.request_handler.handle_broadcast_config = mock.MagicMock()
|
||||
|
||||
coreserver.request_handler.handle_message(message)
|
||||
|
||||
coreserver.request_handler.handle_broadcast_config.assert_called_once()
|
||||
|
||||
def test_config_emane_model_update(self, coreserver):
|
||||
wlan = coreserver.session.add_node(_type=NodeTypes.WIRELESS_LAN)
|
||||
config_key = "distance"
|
||||
config_value = "50051"
|
||||
values = {
|
||||
config_key: config_value
|
||||
}
|
||||
message = coreapi.CoreConfMessage.create(0, [
|
||||
(ConfigTlvs.NODE, wlan.id),
|
||||
(ConfigTlvs.OBJECT, EmaneIeee80211abgModel.name),
|
||||
(ConfigTlvs.TYPE, ConfigFlags.UPDATE.value),
|
||||
(ConfigTlvs.VALUES, dict_to_str(values)),
|
||||
])
|
||||
|
||||
coreserver.request_handler.handle_message(message)
|
||||
|
||||
config = coreserver.session.emane.get_model_config(wlan.id, EmaneIeee80211abgModel.name)
|
||||
assert config[config_key] == config_value
|
||||
|
||||
def test_config_emane_request(self, coreserver):
|
||||
message = coreapi.CoreConfMessage.create(0, [
|
||||
(ConfigTlvs.OBJECT, "emane"),
|
||||
(ConfigTlvs.TYPE, ConfigFlags.REQUEST.value),
|
||||
])
|
||||
coreserver.request_handler.handle_broadcast_config = mock.MagicMock()
|
||||
|
||||
coreserver.request_handler.handle_message(message)
|
||||
|
||||
coreserver.request_handler.handle_broadcast_config.assert_called_once()
|
||||
|
||||
def test_config_emane_update(self, coreserver):
|
||||
config_key = "eventservicedevice"
|
||||
config_value = "eth4"
|
||||
values = {
|
||||
config_key: config_value
|
||||
}
|
||||
message = coreapi.CoreConfMessage.create(0, [
|
||||
(ConfigTlvs.OBJECT, "emane"),
|
||||
(ConfigTlvs.TYPE, ConfigFlags.UPDATE.value),
|
||||
(ConfigTlvs.VALUES, dict_to_str(values)),
|
||||
])
|
||||
|
||||
coreserver.request_handler.handle_message(message)
|
||||
|
||||
config = coreserver.session.emane.get_configs()
|
||||
assert config[config_key] == config_value
|
||||
|
|
|
@ -3,7 +3,7 @@ from xml.etree import ElementTree
|
|||
import pytest
|
||||
|
||||
from core.emane.ieee80211abg import EmaneIeee80211abgModel
|
||||
from core.emulator.emudata import NodeOptions
|
||||
from core.emulator.emudata import NodeOptions, LinkOptions
|
||||
from core.emulator.enumerations import NodeTypes
|
||||
from core.location.mobility import BasicRangeModel
|
||||
from core.services.utility import SshService
|
||||
|
@ -16,7 +16,6 @@ class TestXml:
|
|||
|
||||
:param session: session for test
|
||||
:param tmpdir: tmpdir to create data in
|
||||
:param str version: xml version to write and parse
|
||||
"""
|
||||
# create hook
|
||||
file_name = "runtime_hook.sh"
|
||||
|
@ -51,7 +50,6 @@ class TestXml:
|
|||
|
||||
:param session: session for test
|
||||
:param tmpdir: tmpdir to create data in
|
||||
:param str version: xml version to write and parse
|
||||
:param ip_prefixes: generates ip addresses for nodes
|
||||
"""
|
||||
# create ptp
|
||||
|
@ -104,7 +102,6 @@ class TestXml:
|
|||
|
||||
:param session: session for test
|
||||
:param tmpdir: tmpdir to create data in
|
||||
:param str version: xml version to write and parse
|
||||
:param ip_prefixes: generates ip addresses for nodes
|
||||
"""
|
||||
# create ptp
|
||||
|
@ -168,7 +165,6 @@ class TestXml:
|
|||
|
||||
:param session: session for test
|
||||
:param tmpdir: tmpdir to create data in
|
||||
:param str version: xml version to write and parse
|
||||
:param ip_prefixes: generates ip addresses for nodes
|
||||
"""
|
||||
# create wlan
|
||||
|
@ -230,7 +226,6 @@ class TestXml:
|
|||
|
||||
:param session: session for test
|
||||
:param tmpdir: tmpdir to create data in
|
||||
:param str version: xml version to write and parse
|
||||
:param ip_prefixes: generates ip addresses for nodes
|
||||
"""
|
||||
# create emane node for networking the core nodes
|
||||
|
@ -290,3 +285,262 @@ class TestXml:
|
|||
assert session.get_node(n2_id)
|
||||
assert session.get_node(emane_id)
|
||||
assert value == "1"
|
||||
|
||||
def test_network_to_network(self, session, tmpdir):
|
||||
"""
|
||||
Test xml generation when dealing with network to network nodes.
|
||||
|
||||
:param session: session for test
|
||||
:param tmpdir: tmpdir to create data in
|
||||
"""
|
||||
# create nodes
|
||||
switch_one = session.add_node(_type=NodeTypes.SWITCH)
|
||||
switch_two = session.add_node(_type=NodeTypes.SWITCH)
|
||||
|
||||
# link nodes
|
||||
session.add_link(switch_one.id, switch_two.id)
|
||||
|
||||
# instantiate session
|
||||
session.instantiate()
|
||||
|
||||
# get ids for nodes
|
||||
n1_id = switch_one.id
|
||||
n2_id = switch_two.id
|
||||
|
||||
# save xml
|
||||
xml_file = tmpdir.join("session.xml")
|
||||
file_path = xml_file.strpath
|
||||
session.save_xml(file_path)
|
||||
|
||||
# verify xml file was created and can be parsed
|
||||
assert xml_file.isfile()
|
||||
assert ElementTree.parse(file_path)
|
||||
|
||||
# stop current session, clearing data
|
||||
session.shutdown()
|
||||
|
||||
# verify nodes have been removed from session
|
||||
with pytest.raises(KeyError):
|
||||
assert not session.get_node(n1_id)
|
||||
with pytest.raises(KeyError):
|
||||
assert not session.get_node(n2_id)
|
||||
|
||||
# load saved xml
|
||||
session.open_xml(file_path, start=True)
|
||||
|
||||
# verify nodes have been recreated
|
||||
switch_one = session.get_node(n1_id)
|
||||
switch_two = session.get_node(n2_id)
|
||||
assert switch_one
|
||||
assert switch_two
|
||||
assert len(switch_one.all_link_data(0) + switch_two.all_link_data(0)) == 1
|
||||
|
||||
def test_link_options(self, session, tmpdir, ip_prefixes):
|
||||
"""
|
||||
Test xml client methods for a ptp network.
|
||||
|
||||
:param session: session for test
|
||||
:param tmpdir: tmpdir to create data in
|
||||
:param ip_prefixes: generates ip addresses for nodes
|
||||
"""
|
||||
# create nodes
|
||||
node_one = session.add_node()
|
||||
interface_one = ip_prefixes.create_interface(node_one)
|
||||
switch = session.add_node(_type=NodeTypes.SWITCH)
|
||||
|
||||
# create link
|
||||
link_options = LinkOptions()
|
||||
link_options.per = 20
|
||||
link_options.bandwidth = 50000
|
||||
link_options.jitter = 10
|
||||
link_options.delay = 30
|
||||
link_options.dup = 5
|
||||
session.add_link(node_one.id, switch.id, interface_one, link_options=link_options)
|
||||
|
||||
# instantiate session
|
||||
session.instantiate()
|
||||
|
||||
# get ids for nodes
|
||||
n1_id = node_one.id
|
||||
n2_id = switch.id
|
||||
|
||||
# save xml
|
||||
xml_file = tmpdir.join("session.xml")
|
||||
file_path = xml_file.strpath
|
||||
session.save_xml(file_path)
|
||||
|
||||
# verify xml file was created and can be parsed
|
||||
assert xml_file.isfile()
|
||||
assert ElementTree.parse(file_path)
|
||||
|
||||
# stop current session, clearing data
|
||||
session.shutdown()
|
||||
|
||||
# verify nodes have been removed from session
|
||||
with pytest.raises(KeyError):
|
||||
assert not session.get_node(n1_id)
|
||||
with pytest.raises(KeyError):
|
||||
assert not session.get_node(n2_id)
|
||||
|
||||
# load saved xml
|
||||
session.open_xml(file_path, start=True)
|
||||
|
||||
# verify nodes have been recreated
|
||||
assert session.get_node(n1_id)
|
||||
assert session.get_node(n2_id)
|
||||
links = []
|
||||
for node_id in session.nodes:
|
||||
node = session.nodes[node_id]
|
||||
links += node.all_link_data(0)
|
||||
link = links[0]
|
||||
assert link_options.per == link.per
|
||||
assert link_options.bandwidth == link.bandwidth
|
||||
assert link_options.jitter == link.jitter
|
||||
assert link_options.delay == link.delay
|
||||
assert link_options.dup == link.dup
|
||||
|
||||
def test_link_options_ptp(self, session, tmpdir, ip_prefixes):
|
||||
"""
|
||||
Test xml client methods for a ptp network.
|
||||
|
||||
:param session: session for test
|
||||
:param tmpdir: tmpdir to create data in
|
||||
:param ip_prefixes: generates ip addresses for nodes
|
||||
"""
|
||||
# create nodes
|
||||
node_one = session.add_node()
|
||||
interface_one = ip_prefixes.create_interface(node_one)
|
||||
node_two = session.add_node()
|
||||
interface_two = ip_prefixes.create_interface(node_two)
|
||||
|
||||
# create link
|
||||
link_options = LinkOptions()
|
||||
link_options.per = 20
|
||||
link_options.bandwidth = 50000
|
||||
link_options.jitter = 10
|
||||
link_options.delay = 30
|
||||
link_options.dup = 5
|
||||
session.add_link(node_one.id, node_two.id, interface_one, interface_two, link_options)
|
||||
|
||||
# instantiate session
|
||||
session.instantiate()
|
||||
|
||||
# get ids for nodes
|
||||
n1_id = node_one.id
|
||||
n2_id = node_two.id
|
||||
|
||||
# save xml
|
||||
xml_file = tmpdir.join("session.xml")
|
||||
file_path = xml_file.strpath
|
||||
session.save_xml(file_path)
|
||||
|
||||
# verify xml file was created and can be parsed
|
||||
assert xml_file.isfile()
|
||||
assert ElementTree.parse(file_path)
|
||||
|
||||
# stop current session, clearing data
|
||||
session.shutdown()
|
||||
|
||||
# verify nodes have been removed from session
|
||||
with pytest.raises(KeyError):
|
||||
assert not session.get_node(n1_id)
|
||||
with pytest.raises(KeyError):
|
||||
assert not session.get_node(n2_id)
|
||||
|
||||
# load saved xml
|
||||
session.open_xml(file_path, start=True)
|
||||
|
||||
# verify nodes have been recreated
|
||||
assert session.get_node(n1_id)
|
||||
assert session.get_node(n2_id)
|
||||
links = []
|
||||
for node_id in session.nodes:
|
||||
node = session.nodes[node_id]
|
||||
links += node.all_link_data(0)
|
||||
link = links[0]
|
||||
assert link_options.per == link.per
|
||||
assert link_options.bandwidth == link.bandwidth
|
||||
assert link_options.jitter == link.jitter
|
||||
assert link_options.delay == link.delay
|
||||
assert link_options.dup == link.dup
|
||||
|
||||
def test_link_options_bidirectional(self, session, tmpdir, ip_prefixes):
|
||||
"""
|
||||
Test xml client methods for a ptp network.
|
||||
|
||||
:param session: session for test
|
||||
:param tmpdir: tmpdir to create data in
|
||||
:param ip_prefixes: generates ip addresses for nodes
|
||||
"""
|
||||
# create nodes
|
||||
node_one = session.add_node()
|
||||
interface_one = ip_prefixes.create_interface(node_one)
|
||||
node_two = session.add_node()
|
||||
interface_two = ip_prefixes.create_interface(node_two)
|
||||
|
||||
# create link
|
||||
link_options_one = LinkOptions()
|
||||
link_options_one.unidirectional = 1
|
||||
link_options_one.bandwidth = 5000
|
||||
link_options_one.delay = 10
|
||||
link_options_one.per = 5
|
||||
link_options_one.dup = 5
|
||||
link_options_one.jitter = 5
|
||||
session.add_link(node_one.id, node_two.id, interface_one, interface_two, link_options_one)
|
||||
link_options_two = LinkOptions()
|
||||
link_options_two.unidirectional = 1
|
||||
link_options_two.bandwidth = 10000
|
||||
link_options_two.delay = 20
|
||||
link_options_two.per = 10
|
||||
link_options_two.dup = 10
|
||||
link_options_two.jitter = 10
|
||||
session.update_link(node_two.id, node_one.id, interface_two.id, interface_one.id, link_options_two)
|
||||
|
||||
# instantiate session
|
||||
session.instantiate()
|
||||
|
||||
# get ids for nodes
|
||||
n1_id = node_one.id
|
||||
n2_id = node_two.id
|
||||
|
||||
# save xml
|
||||
xml_file = tmpdir.join("session.xml")
|
||||
file_path = xml_file.strpath
|
||||
session.save_xml(file_path)
|
||||
|
||||
# verify xml file was created and can be parsed
|
||||
assert xml_file.isfile()
|
||||
assert ElementTree.parse(file_path)
|
||||
|
||||
# stop current session, clearing data
|
||||
session.shutdown()
|
||||
|
||||
# verify nodes have been removed from session
|
||||
with pytest.raises(KeyError):
|
||||
assert not session.get_node(n1_id)
|
||||
with pytest.raises(KeyError):
|
||||
assert not session.get_node(n2_id)
|
||||
|
||||
# load saved xml
|
||||
session.open_xml(file_path, start=True)
|
||||
|
||||
# verify nodes have been recreated
|
||||
assert session.get_node(n1_id)
|
||||
assert session.get_node(n2_id)
|
||||
links = []
|
||||
for node_id in session.nodes:
|
||||
node = session.nodes[node_id]
|
||||
links += node.all_link_data(0)
|
||||
assert len(links) == 2
|
||||
link_one = links[0]
|
||||
link_two = links[1]
|
||||
assert link_options_one.bandwidth == link_one.bandwidth
|
||||
assert link_options_one.delay == link_one.delay
|
||||
assert link_options_one.per == link_one.per
|
||||
assert link_options_one.dup == link_one.dup
|
||||
assert link_options_one.jitter == link_one.jitter
|
||||
assert link_options_two.bandwidth == link_two.bandwidth
|
||||
assert link_options_two.delay == link_two.delay
|
||||
assert link_options_two.per == link_two.per
|
||||
assert link_options_two.dup == link_two.dup
|
||||
assert link_options_two.jitter == link_two.jitter
|
||||
|
|
|
@ -68,9 +68,7 @@ sudo ln -s /usr/local/share/emane /usr/share/emane
|
|||
CORE supports custom developed EMANE models by way of dynamically loading user created python files that represent the model. Custom EMANE models should be placed within the path defined by **emane_models_dir** in the CORE configuration file. This path cannot end in **/emane**.
|
||||
|
||||
Here is an example model with documentation describing functionality:
|
||||
[Example Model](examplemodel.html)
|
||||
|
||||
|
||||
[Example Model](/daemon/examples/myemane/examplemodel.py)
|
||||
|
||||
## Single PC with EMANE
|
||||
|
||||
|
|
|
@ -1,239 +0,0 @@
|
|||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<meta http-equiv="content-type" content="text/html;charset=utf-8">
|
||||
<title>examplemodel.py</title>
|
||||
<link rel="stylesheet" href="pycco.css">
|
||||
</head>
|
||||
<body>
|
||||
<div id='container'>
|
||||
<div id="background"></div>
|
||||
<div class='section'>
|
||||
<div class='docs'><h1>examplemodel.py</h1></div>
|
||||
</div>
|
||||
<div class='clearall'>
|
||||
<div class='section' id='section-0'>
|
||||
<div class='docs'>
|
||||
<div class='octowrap'>
|
||||
<a class='octothorpe' href='#section-0'>#</a>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
<div class='code'>
|
||||
<div class="highlight"><pre><span></span><span class="kn">from</span> <span class="nn">core.emane</span> <span class="kn">import</span> <span class="n">emanemanifest</span>
|
||||
<span class="kn">from</span> <span class="nn">core.emane</span> <span class="kn">import</span> <span class="n">emanemodel</span></pre></div>
|
||||
</div>
|
||||
</div>
|
||||
<div class='clearall'></div>
|
||||
<div class='section' id='section-1'>
|
||||
<div class='docs'>
|
||||
<div class='octowrap'>
|
||||
<a class='octothorpe' href='#section-1'>#</a>
|
||||
</div>
|
||||
<h1>Custom EMANE Model</h1>
|
||||
</div>
|
||||
<div class='code'>
|
||||
<div class="highlight"><pre><span class="k">class</span> <span class="nc">ExampleModel</span><span class="p">(</span><span class="n">emanemodel</span><span class="o">.</span><span class="n">EmaneModel</span><span class="p">):</span></pre></div>
|
||||
</div>
|
||||
</div>
|
||||
<div class='clearall'></div>
|
||||
<div class='section' id='section-2'>
|
||||
<div class='docs'>
|
||||
<div class='octowrap'>
|
||||
<a class='octothorpe' href='#section-2'>#</a>
|
||||
</div>
|
||||
<h2>MAC Definition</h2>
|
||||
</div>
|
||||
<div class='code'>
|
||||
<div class="highlight"><pre></pre></div>
|
||||
</div>
|
||||
</div>
|
||||
<div class='clearall'></div>
|
||||
<div class='section' id='section-3'>
|
||||
<div class='docs'>
|
||||
<div class='octowrap'>
|
||||
<a class='octothorpe' href='#section-3'>#</a>
|
||||
</div>
|
||||
<p>Defines the emane model name that will show up in the GUI.</p>
|
||||
</div>
|
||||
<div class='code'>
|
||||
<div class="highlight"><pre> <span class="n">name</span> <span class="o">=</span> <span class="s2">"emane_example"</span></pre></div>
|
||||
</div>
|
||||
</div>
|
||||
<div class='clearall'></div>
|
||||
<div class='section' id='section-4'>
|
||||
<div class='docs'>
|
||||
<div class='octowrap'>
|
||||
<a class='octothorpe' href='#section-4'>#</a>
|
||||
</div>
|
||||
<p>Defines that mac library that the model will reference.</p>
|
||||
</div>
|
||||
<div class='code'>
|
||||
<div class="highlight"><pre> <span class="n">mac_library</span> <span class="o">=</span> <span class="s2">"rfpipemaclayer"</span></pre></div>
|
||||
</div>
|
||||
</div>
|
||||
<div class='clearall'></div>
|
||||
<div class='section' id='section-5'>
|
||||
<div class='docs'>
|
||||
<div class='octowrap'>
|
||||
<a class='octothorpe' href='#section-5'>#</a>
|
||||
</div>
|
||||
<p>Defines the mac manifest file that will be parsed to obtain configuration options, that will be displayed
|
||||
within the GUI.</p>
|
||||
</div>
|
||||
<div class='code'>
|
||||
<div class="highlight"><pre> <span class="n">mac_xml</span> <span class="o">=</span> <span class="s2">"/usr/share/emane/manifest/rfpipemaclayer.xml"</span></pre></div>
|
||||
</div>
|
||||
</div>
|
||||
<div class='clearall'></div>
|
||||
<div class='section' id='section-6'>
|
||||
<div class='docs'>
|
||||
<div class='octowrap'>
|
||||
<a class='octothorpe' href='#section-6'>#</a>
|
||||
</div>
|
||||
<p>Allows you to override options that are maintained within the manifest file above.</p>
|
||||
</div>
|
||||
<div class='code'>
|
||||
<div class="highlight"><pre> <span class="n">mac_defaults</span> <span class="o">=</span> <span class="p">{</span>
|
||||
<span class="s2">"pcrcurveuri"</span><span class="p">:</span> <span class="s2">"/usr/share/emane/xml/models/mac/rfpipe/rfpipepcr.xml"</span><span class="p">,</span>
|
||||
<span class="p">}</span></pre></div>
|
||||
</div>
|
||||
</div>
|
||||
<div class='clearall'></div>
|
||||
<div class='section' id='section-7'>
|
||||
<div class='docs'>
|
||||
<div class='octowrap'>
|
||||
<a class='octothorpe' href='#section-7'>#</a>
|
||||
</div>
|
||||
<p>Parses the manifest file and converts configurations into core supported formats.</p>
|
||||
</div>
|
||||
<div class='code'>
|
||||
<div class="highlight"><pre> <span class="n">mac_config</span> <span class="o">=</span> <span class="n">emanemanifest</span><span class="o">.</span><span class="n">parse</span><span class="p">(</span><span class="n">mac_xml</span><span class="p">,</span> <span class="n">mac_defaults</span><span class="p">)</span></pre></div>
|
||||
</div>
|
||||
</div>
|
||||
<div class='clearall'></div>
|
||||
<div class='section' id='section-8'>
|
||||
<div class='docs'>
|
||||
<div class='octowrap'>
|
||||
<a class='octothorpe' href='#section-8'>#</a>
|
||||
</div>
|
||||
<h2>PHY Definition</h2>
|
||||
<p><strong>NOTE: phy configuration will default to the universal model as seen below and the below section does not
|
||||
have to be included.</strong></p>
|
||||
</div>
|
||||
<div class='code'>
|
||||
<div class="highlight"><pre></pre></div>
|
||||
</div>
|
||||
</div>
|
||||
<div class='clearall'></div>
|
||||
<div class='section' id='section-9'>
|
||||
<div class='docs'>
|
||||
<div class='octowrap'>
|
||||
<a class='octothorpe' href='#section-9'>#</a>
|
||||
</div>
|
||||
<p>Defines that phy library that the model will reference, used if you need to provide a custom phy.</p>
|
||||
</div>
|
||||
<div class='code'>
|
||||
<div class="highlight"><pre> <span class="n">phy_library</span> <span class="o">=</span> <span class="bp">None</span></pre></div>
|
||||
</div>
|
||||
</div>
|
||||
<div class='clearall'></div>
|
||||
<div class='section' id='section-10'>
|
||||
<div class='docs'>
|
||||
<div class='octowrap'>
|
||||
<a class='octothorpe' href='#section-10'>#</a>
|
||||
</div>
|
||||
<p>Defines the phy manifest file that will be parsed to obtain configuration options, that will be displayed
|
||||
within the GUI.</p>
|
||||
</div>
|
||||
<div class='code'>
|
||||
<div class="highlight"><pre> <span class="n">phy_xml</span> <span class="o">=</span> <span class="s2">"/usr/share/emane/manifest/emanephy.xml"</span></pre></div>
|
||||
</div>
|
||||
</div>
|
||||
<div class='clearall'></div>
|
||||
<div class='section' id='section-11'>
|
||||
<div class='docs'>
|
||||
<div class='octowrap'>
|
||||
<a class='octothorpe' href='#section-11'>#</a>
|
||||
</div>
|
||||
<p>Allows you to override options that are maintained within the manifest file above or for the default universal
|
||||
model.</p>
|
||||
</div>
|
||||
<div class='code'>
|
||||
<div class="highlight"><pre> <span class="n">phy_defaults</span> <span class="o">=</span> <span class="p">{</span>
|
||||
<span class="s2">"subid"</span><span class="p">:</span> <span class="s2">"1"</span><span class="p">,</span>
|
||||
<span class="s2">"propagationmodel"</span><span class="p">:</span> <span class="s2">"2ray"</span><span class="p">,</span>
|
||||
<span class="s2">"noisemode"</span><span class="p">:</span> <span class="s2">"none"</span>
|
||||
<span class="p">}</span></pre></div>
|
||||
</div>
|
||||
</div>
|
||||
<div class='clearall'></div>
|
||||
<div class='section' id='section-12'>
|
||||
<div class='docs'>
|
||||
<div class='octowrap'>
|
||||
<a class='octothorpe' href='#section-12'>#</a>
|
||||
</div>
|
||||
<p>Parses the manifest file and converts configurations into core supported formats.</p>
|
||||
</div>
|
||||
<div class='code'>
|
||||
<div class="highlight"><pre> <span class="n">phy_config</span> <span class="o">=</span> <span class="n">emanemanifest</span><span class="o">.</span><span class="n">parse</span><span class="p">(</span><span class="n">phy_xml</span><span class="p">,</span> <span class="n">phy_defaults</span><span class="p">)</span></pre></div>
|
||||
</div>
|
||||
</div>
|
||||
<div class='clearall'></div>
|
||||
<div class='section' id='section-13'>
|
||||
<div class='docs'>
|
||||
<div class='octowrap'>
|
||||
<a class='octothorpe' href='#section-13'>#</a>
|
||||
</div>
|
||||
<h2>Custom override options</h2>
|
||||
<p><strong>NOTE: these options default to what's seen below and do not have to be included.</strong></p>
|
||||
</div>
|
||||
<div class='code'>
|
||||
<div class="highlight"><pre></pre></div>
|
||||
</div>
|
||||
</div>
|
||||
<div class='clearall'></div>
|
||||
<div class='section' id='section-14'>
|
||||
<div class='docs'>
|
||||
<div class='octowrap'>
|
||||
<a class='octothorpe' href='#section-14'>#</a>
|
||||
</div>
|
||||
<p>Allows you to ignore options within phy/mac, used typically if you needed to add a custom option for display
|
||||
within the gui.</p>
|
||||
</div>
|
||||
<div class='code'>
|
||||
<div class="highlight"><pre> <span class="n">config_ignore</span> <span class="o">=</span> <span class="nb">set</span><span class="p">()</span></pre></div>
|
||||
</div>
|
||||
</div>
|
||||
<div class='clearall'></div>
|
||||
<div class='section' id='section-15'>
|
||||
<div class='docs'>
|
||||
<div class='octowrap'>
|
||||
<a class='octothorpe' href='#section-15'>#</a>
|
||||
</div>
|
||||
<p>Allows you to override how options are displayed with the GUI, using the GUI format of
|
||||
"name:1-2|othername:3-4". This will be parsed into tabs, split by "|" and account for items based on the indexed
|
||||
numbers after ":" for including values in each tab.</p>
|
||||
</div>
|
||||
<div class='code'>
|
||||
<div class="highlight"><pre> <span class="n">config_groups_override</span> <span class="o">=</span> <span class="bp">None</span></pre></div>
|
||||
</div>
|
||||
</div>
|
||||
<div class='clearall'></div>
|
||||
<div class='section' id='section-16'>
|
||||
<div class='docs'>
|
||||
<div class='octowrap'>
|
||||
<a class='octothorpe' href='#section-16'>#</a>
|
||||
</div>
|
||||
<p>Allows you to override the default config matrix list. This value by default is the mac_config + phy_config, in
|
||||
that order.</p>
|
||||
</div>
|
||||
<div class='code'>
|
||||
<div class="highlight"><pre> <span class="n">config_matrix_override</span> <span class="o">=</span> <span class="bp">None</span>
|
||||
|
||||
</pre></div>
|
||||
</div>
|
||||
</div>
|
||||
<div class='clearall'></div>
|
||||
</div>
|
||||
</body>
|
|
@ -1,344 +0,0 @@
|
|||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<meta http-equiv="content-type" content="text/html;charset=utf-8">
|
||||
<title>sample.py</title>
|
||||
<link rel="stylesheet" href="pycco.css">
|
||||
</head>
|
||||
<body>
|
||||
<div id='container'>
|
||||
<div id="background"></div>
|
||||
<div class='section'>
|
||||
<div class='docs'><h1>sample.py</h1></div>
|
||||
</div>
|
||||
<div class='clearall'>
|
||||
<div class='section' id='section-0'>
|
||||
<div class='docs'>
|
||||
<div class='octowrap'>
|
||||
<a class='octothorpe' href='#section-0'>#</a>
|
||||
</div>
|
||||
<p>Sample user-defined service.</p>
|
||||
</div>
|
||||
<div class='code'>
|
||||
<div class="highlight"><pre><span></span><span class="kn">from</span> <span class="nn">core.service</span> <span class="kn">import</span> <span class="n">CoreService</span>
|
||||
<span class="kn">from</span> <span class="nn">core.service</span> <span class="kn">import</span> <span class="n">ServiceMode</span></pre></div>
|
||||
</div>
|
||||
</div>
|
||||
<div class='clearall'></div>
|
||||
<div class='section' id='section-1'>
|
||||
<div class='docs'>
|
||||
<div class='octowrap'>
|
||||
<a class='octothorpe' href='#section-1'>#</a>
|
||||
</div>
|
||||
<h1>Custom CORE Service</h1>
|
||||
</div>
|
||||
<div class='code'>
|
||||
<div class="highlight"><pre><span class="k">class</span> <span class="nc">MyService</span><span class="p">(</span><span class="n">CoreService</span><span class="p">):</span></pre></div>
|
||||
</div>
|
||||
</div>
|
||||
<div class='clearall'></div>
|
||||
<div class='section' id='section-2'>
|
||||
<div class='docs'>
|
||||
<div class='octowrap'>
|
||||
<a class='octothorpe' href='#section-2'>#</a>
|
||||
</div>
|
||||
<h2>Service Attributes</h2>
|
||||
</div>
|
||||
<div class='code'>
|
||||
<div class="highlight"><pre></pre></div>
|
||||
</div>
|
||||
</div>
|
||||
<div class='clearall'></div>
|
||||
<div class='section' id='section-3'>
|
||||
<div class='docs'>
|
||||
<div class='octowrap'>
|
||||
<a class='octothorpe' href='#section-3'>#</a>
|
||||
</div>
|
||||
<p>Name used as a unique ID for this service and is required, no spaces.</p>
|
||||
</div>
|
||||
<div class='code'>
|
||||
<div class="highlight"><pre> <span class="n">name</span> <span class="o">=</span> <span class="s2">"MyService"</span></pre></div>
|
||||
</div>
|
||||
</div>
|
||||
<div class='clearall'></div>
|
||||
<div class='section' id='section-4'>
|
||||
<div class='docs'>
|
||||
<div class='octowrap'>
|
||||
<a class='octothorpe' href='#section-4'>#</a>
|
||||
</div>
|
||||
<p>Allows you to group services within the GUI under a common name.</p>
|
||||
</div>
|
||||
<div class='code'>
|
||||
<div class="highlight"><pre> <span class="n">group</span> <span class="o">=</span> <span class="s2">"Utility"</span></pre></div>
|
||||
</div>
|
||||
</div>
|
||||
<div class='clearall'></div>
|
||||
<div class='section' id='section-5'>
|
||||
<div class='docs'>
|
||||
<div class='octowrap'>
|
||||
<a class='octothorpe' href='#section-5'>#</a>
|
||||
</div>
|
||||
<p>Executables this service depends on to function, if executable is not on the path, service will not be loaded.</p>
|
||||
</div>
|
||||
<div class='code'>
|
||||
<div class="highlight"><pre> <span class="n">executables</span> <span class="o">=</span> <span class="p">()</span></pre></div>
|
||||
</div>
|
||||
</div>
|
||||
<div class='clearall'></div>
|
||||
<div class='section' id='section-6'>
|
||||
<div class='docs'>
|
||||
<div class='octowrap'>
|
||||
<a class='octothorpe' href='#section-6'>#</a>
|
||||
</div>
|
||||
<p>Services that this service depends on for startup, tuple of service names.</p>
|
||||
</div>
|
||||
<div class='code'>
|
||||
<div class="highlight"><pre> <span class="n">dependencies</span> <span class="o">=</span> <span class="p">()</span></pre></div>
|
||||
</div>
|
||||
</div>
|
||||
<div class='clearall'></div>
|
||||
<div class='section' id='section-7'>
|
||||
<div class='docs'>
|
||||
<div class='octowrap'>
|
||||
<a class='octothorpe' href='#section-7'>#</a>
|
||||
</div>
|
||||
<p>Directories that this service will create within a node.</p>
|
||||
</div>
|
||||
<div class='code'>
|
||||
<div class="highlight"><pre> <span class="n">dirs</span> <span class="o">=</span> <span class="p">()</span></pre></div>
|
||||
</div>
|
||||
</div>
|
||||
<div class='clearall'></div>
|
||||
<div class='section' id='section-8'>
|
||||
<div class='docs'>
|
||||
<div class='octowrap'>
|
||||
<a class='octothorpe' href='#section-8'>#</a>
|
||||
</div>
|
||||
<p>Files that this service will generate, without a full path this file goes in the node’s directory.
|
||||
e.g. /tmp/pycore.12345/n1.conf/myfile</p>
|
||||
</div>
|
||||
<div class='code'>
|
||||
<div class="highlight"><pre> <span class="n">configs</span> <span class="o">=</span> <span class="p">(</span><span class="s2">"myservice1.sh"</span><span class="p">,</span> <span class="s2">"myservice2.sh"</span><span class="p">)</span></pre></div>
|
||||
</div>
|
||||
</div>
|
||||
<div class='clearall'></div>
|
||||
<div class='section' id='section-9'>
|
||||
<div class='docs'>
|
||||
<div class='octowrap'>
|
||||
<a class='octothorpe' href='#section-9'>#</a>
|
||||
</div>
|
||||
<p>Commands used to start this service, any non-zero exit code will cause a failure.</p>
|
||||
</div>
|
||||
<div class='code'>
|
||||
<div class="highlight"><pre> <span class="n">startup</span> <span class="o">=</span> <span class="p">(</span><span class="s2">"sh </span><span class="si">%s</span><span class="s2">"</span> <span class="o">%</span> <span class="n">configs</span><span class="p">[</span><span class="mi">0</span><span class="p">],</span> <span class="s2">"sh </span><span class="si">%s</span><span class="s2">"</span> <span class="o">%</span> <span class="n">configs</span><span class="p">[</span><span class="mi">1</span><span class="p">])</span></pre></div>
|
||||
</div>
|
||||
</div>
|
||||
<div class='clearall'></div>
|
||||
<div class='section' id='section-10'>
|
||||
<div class='docs'>
|
||||
<div class='octowrap'>
|
||||
<a class='octothorpe' href='#section-10'>#</a>
|
||||
</div>
|
||||
<p>Commands used to validate that a service was started, any non-zero exit code will cause a failure.</p>
|
||||
</div>
|
||||
<div class='code'>
|
||||
<div class="highlight"><pre> <span class="n">validate</span> <span class="o">=</span> <span class="p">()</span></pre></div>
|
||||
</div>
|
||||
</div>
|
||||
<div class='clearall'></div>
|
||||
<div class='section' id='section-11'>
|
||||
<div class='docs'>
|
||||
<div class='octowrap'>
|
||||
<a class='octothorpe' href='#section-11'>#</a>
|
||||
</div>
|
||||
<p>Validation mode, used to determine startup success.</p>
|
||||
<ul>
|
||||
<li>NON_BLOCKING - runs startup commands, and validates success with validation commands</li>
|
||||
<li>BLOCKING - runs startup commands, and validates success with the startup commands themselves</li>
|
||||
<li>TIMER - runs startup commands, and validates success by waiting for “validation_timer” alone</li>
|
||||
</ul>
|
||||
</div>
|
||||
<div class='code'>
|
||||
<div class="highlight"><pre> <span class="n">validation_mode</span> <span class="o">=</span> <span class="n">ServiceMode</span><span class="o">.</span><span class="n">NON_BLOCKING</span></pre></div>
|
||||
</div>
|
||||
</div>
|
||||
<div class='clearall'></div>
|
||||
<div class='section' id='section-12'>
|
||||
<div class='docs'>
|
||||
<div class='octowrap'>
|
||||
<a class='octothorpe' href='#section-12'>#</a>
|
||||
</div>
|
||||
<p>Time in seconds for a service to wait for validation, before determining success in TIMER/NON_BLOCKING modes.</p>
|
||||
</div>
|
||||
<div class='code'>
|
||||
<div class="highlight"><pre> <span class="n">validation_timer</span> <span class="o">=</span> <span class="mi">5</span></pre></div>
|
||||
</div>
|
||||
</div>
|
||||
<div class='clearall'></div>
|
||||
<div class='section' id='section-13'>
|
||||
<div class='docs'>
|
||||
<div class='octowrap'>
|
||||
<a class='octothorpe' href='#section-13'>#</a>
|
||||
</div>
|
||||
<p>Period in seconds to wait before retrying validation, only used in NON_BLOCKING mode.</p>
|
||||
</div>
|
||||
<div class='code'>
|
||||
<div class="highlight"><pre> <span class="n">validation_period</span> <span class="o">=</span> <span class="mf">0.5</span></pre></div>
|
||||
</div>
|
||||
</div>
|
||||
<div class='clearall'></div>
|
||||
<div class='section' id='section-14'>
|
||||
<div class='docs'>
|
||||
<div class='octowrap'>
|
||||
<a class='octothorpe' href='#section-14'>#</a>
|
||||
</div>
|
||||
<p>Shutdown commands to stop this service.</p>
|
||||
</div>
|
||||
<div class='code'>
|
||||
<div class="highlight"><pre> <span class="n">shutdown</span> <span class="o">=</span> <span class="p">()</span></pre></div>
|
||||
</div>
|
||||
</div>
|
||||
<div class='clearall'></div>
|
||||
<div class='section' id='section-15'>
|
||||
<div class='docs'>
|
||||
<div class='octowrap'>
|
||||
<a class='octothorpe' href='#section-15'>#</a>
|
||||
</div>
|
||||
<h2>On Load</h2>
|
||||
</div>
|
||||
<div class='code'>
|
||||
<div class="highlight"><pre> <span class="nd">@classmethod</span>
|
||||
<span class="k">def</span> <span class="nf">on_load</span><span class="p">(</span><span class="bp">cls</span><span class="p">):</span></pre></div>
|
||||
</div>
|
||||
</div>
|
||||
<div class='clearall'></div>
|
||||
<div class='section' id='section-16'>
|
||||
<div class='docs'>
|
||||
<div class='octowrap'>
|
||||
<a class='octothorpe' href='#section-16'>#</a>
|
||||
</div>
|
||||
<p>Provides a way to run some arbitrary logic when the service is loaded, possibly to help facilitate
|
||||
dynamic settings for the environment.</p>
|
||||
</div>
|
||||
<div class='code'>
|
||||
<div class="highlight"><pre> <span class="k">pass</span></pre></div>
|
||||
</div>
|
||||
</div>
|
||||
<div class='clearall'></div>
|
||||
<div class='section' id='section-17'>
|
||||
<div class='docs'>
|
||||
<div class='octowrap'>
|
||||
<a class='octothorpe' href='#section-17'>#</a>
|
||||
</div>
|
||||
<h2>Get Configs</h2>
|
||||
</div>
|
||||
<div class='code'>
|
||||
<div class="highlight"><pre> <span class="nd">@classmethod</span>
|
||||
<span class="k">def</span> <span class="nf">get_configs</span><span class="p">(</span><span class="bp">cls</span><span class="p">,</span> <span class="n">node</span><span class="p">):</span></pre></div>
|
||||
</div>
|
||||
</div>
|
||||
<div class='clearall'></div>
|
||||
<div class='section' id='section-18'>
|
||||
<div class='docs'>
|
||||
<div class='octowrap'>
|
||||
<a class='octothorpe' href='#section-18'>#</a>
|
||||
</div>
|
||||
<p>Provides a way to dynamically generate the config files from the node a service will run.
|
||||
Defaults to the class definition and can be left out entirely if not needed.</p>
|
||||
</div>
|
||||
<div class='code'>
|
||||
<div class="highlight"><pre> <span class="k">return</span> <span class="bp">cls</span><span class="o">.</span><span class="n">configs</span></pre></div>
|
||||
</div>
|
||||
</div>
|
||||
<div class='clearall'></div>
|
||||
<div class='section' id='section-19'>
|
||||
<div class='docs'>
|
||||
<div class='octowrap'>
|
||||
<a class='octothorpe' href='#section-19'>#</a>
|
||||
</div>
|
||||
<h2>Generate Config</h2>
|
||||
</div>
|
||||
<div class='code'>
|
||||
<div class="highlight"><pre> <span class="nd">@classmethod</span>
|
||||
<span class="k">def</span> <span class="nf">generate_config</span><span class="p">(</span><span class="bp">cls</span><span class="p">,</span> <span class="n">node</span><span class="p">,</span> <span class="n">filename</span><span class="p">):</span></pre></div>
|
||||
</div>
|
||||
</div>
|
||||
<div class='clearall'></div>
|
||||
<div class='section' id='section-20'>
|
||||
<div class='docs'>
|
||||
<div class='octowrap'>
|
||||
<a class='octothorpe' href='#section-20'>#</a>
|
||||
</div>
|
||||
<p>Returns a string representation for a file, given the node the service is starting on the config filename
|
||||
that this information will be used for. This must be defined, if “configs” are defined.</p>
|
||||
</div>
|
||||
<div class='code'>
|
||||
<div class="highlight"><pre> <span class="n">cfg</span> <span class="o">=</span> <span class="s2">"#!/bin/sh</span><span class="se">\n</span><span class="s2">"</span>
|
||||
|
||||
<span class="k">if</span> <span class="n">filename</span> <span class="o">==</span> <span class="bp">cls</span><span class="o">.</span><span class="n">configs</span><span class="p">[</span><span class="mi">0</span><span class="p">]:</span>
|
||||
<span class="n">cfg</span> <span class="o">+=</span> <span class="s2">"# auto-generated by MyService (sample.py)</span><span class="se">\n</span><span class="s2">"</span>
|
||||
<span class="k">for</span> <span class="n">ifc</span> <span class="ow">in</span> <span class="n">node</span><span class="o">.</span><span class="n">netifs</span><span class="p">():</span>
|
||||
<span class="n">cfg</span> <span class="o">+=</span> <span class="s1">'echo "Node </span><span class="si">%s</span><span class="s1"> has interface </span><span class="si">%s</span><span class="s1">"</span><span class="se">\n</span><span class="s1">'</span> <span class="o">%</span> <span class="p">(</span><span class="n">node</span><span class="o">.</span><span class="n">name</span><span class="p">,</span> <span class="n">ifc</span><span class="o">.</span><span class="n">name</span><span class="p">)</span>
|
||||
<span class="k">elif</span> <span class="n">filename</span> <span class="o">==</span> <span class="bp">cls</span><span class="o">.</span><span class="n">configs</span><span class="p">[</span><span class="mi">1</span><span class="p">]:</span>
|
||||
<span class="n">cfg</span> <span class="o">+=</span> <span class="s2">"echo hello"</span>
|
||||
|
||||
<span class="k">return</span> <span class="n">cfg</span></pre></div>
|
||||
</div>
|
||||
</div>
|
||||
<div class='clearall'></div>
|
||||
<div class='section' id='section-21'>
|
||||
<div class='docs'>
|
||||
<div class='octowrap'>
|
||||
<a class='octothorpe' href='#section-21'>#</a>
|
||||
</div>
|
||||
<h2>Get Startup</h2>
|
||||
</div>
|
||||
<div class='code'>
|
||||
<div class="highlight"><pre> <span class="nd">@classmethod</span>
|
||||
<span class="k">def</span> <span class="nf">get_startup</span><span class="p">(</span><span class="bp">cls</span><span class="p">,</span> <span class="n">node</span><span class="p">):</span></pre></div>
|
||||
</div>
|
||||
</div>
|
||||
<div class='clearall'></div>
|
||||
<div class='section' id='section-22'>
|
||||
<div class='docs'>
|
||||
<div class='octowrap'>
|
||||
<a class='octothorpe' href='#section-22'>#</a>
|
||||
</div>
|
||||
<p>Provides a way to dynamically generate the startup commands from the node a service will run.
|
||||
Defaults to the class definition and can be left out entirely if not needed.</p>
|
||||
</div>
|
||||
<div class='code'>
|
||||
<div class="highlight"><pre> <span class="k">return</span> <span class="bp">cls</span><span class="o">.</span><span class="n">startup</span></pre></div>
|
||||
</div>
|
||||
</div>
|
||||
<div class='clearall'></div>
|
||||
<div class='section' id='section-23'>
|
||||
<div class='docs'>
|
||||
<div class='octowrap'>
|
||||
<a class='octothorpe' href='#section-23'>#</a>
|
||||
</div>
|
||||
<h2>Get Validate</h2>
|
||||
</div>
|
||||
<div class='code'>
|
||||
<div class="highlight"><pre> <span class="nd">@classmethod</span>
|
||||
<span class="k">def</span> <span class="nf">get_validate</span><span class="p">(</span><span class="bp">cls</span><span class="p">,</span> <span class="n">node</span><span class="p">):</span></pre></div>
|
||||
</div>
|
||||
</div>
|
||||
<div class='clearall'></div>
|
||||
<div class='section' id='section-24'>
|
||||
<div class='docs'>
|
||||
<div class='octowrap'>
|
||||
<a class='octothorpe' href='#section-24'>#</a>
|
||||
</div>
|
||||
<p>Provides a way to dynamically generate the validate commands from the node a service will run.
|
||||
Defaults to the class definition and can be left out entirely if not needed.</p>
|
||||
</div>
|
||||
<div class='code'>
|
||||
<div class="highlight"><pre> <span class="k">return</span> <span class="bp">cls</span><span class="o">.</span><span class="n">validate</span>
|
||||
|
||||
</pre></div>
|
||||
</div>
|
||||
</div>
|
||||
<div class='clearall'></div>
|
||||
</div>
|
||||
</body>
|
9
docs/experimental.md
Normal file
9
docs/experimental.md
Normal file
|
@ -0,0 +1,9 @@
|
|||
# Experimental Features
|
||||
|
||||
Below are current features that are considered experimental and will likely have issues.
|
||||
|
||||
# NS3
|
||||
|
||||
Experimental support for scripting CORE nodes with NS3.
|
||||
|
||||
[NS# Overview](ns3.md)
|
121
docs/grpc.md
Normal file
121
docs/grpc.md
Normal file
|
@ -0,0 +1,121 @@
|
|||
# Using the gRPC API
|
||||
|
||||
By default the gRPC API is currently not turned on by default. There are a couple ways that this can be enabled
|
||||
to use.
|
||||
|
||||
## Enabling gRPC
|
||||
|
||||
### HTTP Proxy
|
||||
|
||||
Since gRPC is HTTP2 based, proxy configurations can cause issue. Clear out your proxy when running if needed.
|
||||
|
||||
### Daemon Options
|
||||
|
||||
The gRPC API is enabled through options provided to the **core-daemon**.
|
||||
|
||||
```shell
|
||||
usage: core-daemon [-h] [-f CONFIGFILE] [-p PORT] [-n NUMTHREADS] [--ovs]
|
||||
[--grpc] [--grpc-port GRPCPORT]
|
||||
[--grpc-address GRPCADDRESS]
|
||||
|
||||
CORE daemon v.5.3.0 instantiates Linux network namespace nodes.
|
||||
|
||||
optional arguments:
|
||||
-h, --help show this help message and exit
|
||||
-f CONFIGFILE, --configfile CONFIGFILE
|
||||
read config from specified file; default =
|
||||
/etc/core/core.conf
|
||||
-p PORT, --port PORT port number to listen on; default = 4038
|
||||
-n NUMTHREADS, --numthreads NUMTHREADS
|
||||
number of server threads; default = 1
|
||||
--ovs enable experimental ovs mode, default is false
|
||||
--grpc enable grpc api, default is false
|
||||
--grpc-port GRPCPORT grpc port to listen on; default 50051
|
||||
--grpc-address GRPCADDRESS
|
||||
grpc address to listen on; default localhost
|
||||
```
|
||||
|
||||
### Enabling in Service Files
|
||||
|
||||
Modify service files to append the --grpc options as desired.
|
||||
|
||||
For sysv services /etc/init.d/core-daemon
|
||||
```shell
|
||||
CMD="PYTHONPATH=/usr/lib/python3.6/site-packages python3 /usr/bin/$NAME --grpc"
|
||||
```
|
||||
|
||||
For systemd service /lib/systemd/system/core-daemon.service
|
||||
```shell
|
||||
ExecStart=@PYTHON@ @bindir@/core-daemon --grpc
|
||||
```
|
||||
|
||||
### Enabling from Command Line
|
||||
|
||||
```shell
|
||||
sudo core-daemon --grpc
|
||||
```
|
||||
|
||||
## Python Client
|
||||
|
||||
A python client wrapper is provided at **core.api.grpc.client.CoreGrpcClient**.
|
||||
|
||||
Below is a small example using it.
|
||||
|
||||
```python
|
||||
import logging
|
||||
from builtins import range
|
||||
|
||||
from core.api.grpc import client, core_pb2
|
||||
|
||||
|
||||
def log_event(event):
|
||||
logging.info("event: %s", event)
|
||||
|
||||
|
||||
def main():
|
||||
core = client.CoreGrpcClient()
|
||||
|
||||
with core.context_connect():
|
||||
# create session
|
||||
response = core.create_session()
|
||||
logging.info("created session: %s", response)
|
||||
|
||||
# handle events session may broadcast
|
||||
session_id = response.session_id
|
||||
core.events(session_id, log_event)
|
||||
|
||||
# change session state
|
||||
response = core.set_session_state(session_id, core_pb2.SessionState.CONFIGURATION)
|
||||
logging.info("set session state: %s", response)
|
||||
|
||||
# create switch node
|
||||
switch = core_pb2.Node(type=core_pb2.NodeType.SWITCH)
|
||||
response = core.add_node(session_id, switch)
|
||||
logging.info("created switch: %s", response)
|
||||
switch_id = response.node_id
|
||||
|
||||
# helper to create interfaces
|
||||
interface_helper = client.InterfaceHelper(ip4_prefix="10.83.0.0/16")
|
||||
|
||||
for i in range(2):
|
||||
# create node
|
||||
position = core_pb2.Position(x=50 + 50 * i, y=50)
|
||||
node = core_pb2.Node(position=position)
|
||||
response = core.add_node(session_id, node)
|
||||
logging.info("created node: %s", response)
|
||||
node_id = response.node_id
|
||||
|
||||
# create link
|
||||
interface_one = interface_helper.create_interface(node_id, 0)
|
||||
response = core.add_link(session_id, node_id, switch_id, interface_one)
|
||||
logging.info("created link: %s", response)
|
||||
|
||||
# change session state
|
||||
response = core.set_session_state(session_id, core_pb2.SessionState.INSTANTIATION)
|
||||
logging.info("set session state: %s", response)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
logging.basicConfig(level=logging.DEBUG)
|
||||
main()
|
||||
```
|
|
@ -2,9 +2,13 @@
|
|||
|
||||
## Introduction
|
||||
|
||||
CORE (Common Open Research Emulator) is a tool for building virtual networks. As an emulator, CORE builds a representation of a real computer network that runs in real time, as opposed to simulation, where abstract models are used. The live-running emulation can be connected to physical networks and routers. It provides an environment for running real applications and protocols, taking advantage of virtualization provided by the Linux operating system.
|
||||
CORE (Common Open Research Emulator) is a tool for building virtual networks. As an emulator, CORE builds a
|
||||
representation of a real computer network that runs in real time, as opposed to simulation, where abstract models are
|
||||
used. The live-running emulation can be connected to physical networks and routers. It provides an environment for
|
||||
running real applications and protocols, taking advantage of virtualization provided by the Linux operating system.
|
||||
|
||||
CORE is typically used for network and protocol research, demonstrations, application and platform testing, evaluating networking scenarios, security studies, and increasing the size of physical test networks.
|
||||
CORE is typically used for network and protocol research, demonstrations, application and platform testing, evaluating
|
||||
networking scenarios, security studies, and increasing the size of physical test networks.
|
||||
|
||||
### Key Features
|
||||
* Efficient and scalable
|
||||
|
@ -14,22 +18,31 @@ CORE is typically used for network and protocol research, demonstrations, applic
|
|||
|
||||
## Topics
|
||||
|
||||
* [Architecture](architecture.md)
|
||||
* [Installation](install.md)
|
||||
* [Usage](usage.md)
|
||||
* [Python Scripting](scripting.md)
|
||||
* [Node Types](machine.md)
|
||||
* [CTRLNET](ctrlnet.md)
|
||||
* [Services](services.md)
|
||||
* [EMANE](emane.md)
|
||||
* [NS3](ns3.md)
|
||||
* [Performance](performance.md)
|
||||
* [Developers Guide](devguide.md)
|
||||
| Topic | Description|
|
||||
|-------|------------|
|
||||
|[Architecture](architecture.md)|Overview of the architecture|
|
||||
|[Installation](install.md)|Installing from source, packages, & other dependencies|
|
||||
|[Using the GUI](usage.md)|Details on the different node types and options in the GUI|
|
||||
|[Python Scripting](scripting.md)|How to write python scripts for creating a CORE session|
|
||||
|[gRPC API](grpc.md)|How to enable and use the gRPC API|
|
||||
|[Node Types](machine.md)|Overview of node types supported within CORE|
|
||||
|[CTRLNET](ctrlnet.md)|How to use control networks to communicate with nodes from host|
|
||||
|[Services](services.md)|Overview of provided services and creating custom ones|
|
||||
|[EMANE](emane.md)|Overview of EMANE integration and integrating custom EMANE models|
|
||||
|[Performance](performance.md)|Notes on performance when using CORE|
|
||||
|[Developers Guide](devguide.md)|Overview of topics when developing CORE|
|
||||
|[Experimental](experimental.md)|Experimental features for use with or within CORE|
|
||||
|
||||
## Credits
|
||||
|
||||
The CORE project was derived from the open source IMUNES project from the University of Zagreb in 2004. In 2006, changes for CORE were released back to that project, some items of which were adopted. Marko Zec <zec@fer.hr> is the primary developer from the University of Zagreb responsible for the IMUNES (GUI) and VirtNet (kernel) projects. Ana Kukec and Miljenko Mikuc are known contributors.
|
||||
The CORE project was derived from the open source IMUNES project from the University of Zagreb in 2004. In 2006,
|
||||
changes for CORE were released back to that project, some items of which were adopted. Marko Zec <zec@fer.hr> is the
|
||||
primary developer from the University of Zagreb responsible for the IMUNES (GUI) and VirtNet (kernel) projects. Ana
|
||||
Kukec and Miljenko Mikuc are known contributors.
|
||||
|
||||
Jeff Ahrenholz has been the primary Boeing developer of CORE, and has written this manual. Tom Goff designed the Python framework and has made significant contributions. Claudiu Danilov, Rod Santiago, Kevin Larson, Gary Pei, Phil Spagnolo, and Ian Chakeres have contributed code to CORE. Dan Mackley helped develop the CORE API, originally to interface with a simulator. Jae Kim and Tom Henderson have supervised the project and provided direction.
|
||||
Jeff Ahrenholz has been the primary Boeing developer of CORE, and has written this manual. Tom Goff designed the
|
||||
Python framework and has made significant contributions. Claudiu Danilov, Rod Santiago, Kevin Larson, Gary Pei,
|
||||
Phil Spagnolo, and Ian Chakeres have contributed code to CORE. Dan Mackley helped develop the CORE API, originally to
|
||||
interface with a simulator. Jae Kim and Tom Henderson have supervised the project and provided direction.
|
||||
|
||||
Copyright (c) 2005-2018, the Boeing Company.
|
||||
|
|
330
docs/install.md
330
docs/install.md
|
@ -1,4 +1,3 @@
|
|||
|
||||
# CORE Installation
|
||||
|
||||
* Table of Contents
|
||||
|
@ -6,78 +5,129 @@
|
|||
|
||||
# Overview
|
||||
|
||||
This section will describe how to set up a CORE machine. Note that the easiest way to install CORE is using a binary package on Ubuntu or Fedora/CentOS (deb or rpm) using the distribution's package manager to automatically install dependencies.
|
||||
This section will describe how to install CORE from source or from a pre-built package.
|
||||
|
||||
Ubuntu and Fedora/CentOS Linux are the recommended distributions for running CORE. However, these distributions are not strictly required. CORE will likely work on other flavors of Linux as well.
|
||||
# Required Hardware
|
||||
|
||||
The primary dependencies are Tcl/Tk (8.5 or newer) for the GUI, and Python 2.7 for the CORE daemon.
|
||||
Any computer capable of running Linux should be able to run CORE. Since the physical machine will be hosting numerous
|
||||
virtual machines, as a general rule you should select a machine having as much RAM and CPU resources as possible.
|
||||
|
||||
CORE files are installed to the following directories, when the installation prefix is */usr*.
|
||||
# Operating System
|
||||
|
||||
CORE requires a Linux operating system because it uses virtualization provided by the kernel. It does not run on
|
||||
Windows or Mac OS X operating systems (unless it is running within a virtual machine guest.) The virtualization
|
||||
technology that CORE currently uses is Linux network namespaces.
|
||||
|
||||
Ubuntu and Fedora/CentOS Linux are the recommended distributions for running CORE. However, these distributions are
|
||||
not strictly required. CORE will likely work on other flavors of Linux as well, assuming dependencies are met.
|
||||
|
||||
**NOTE: CORE Services determine what run on each node. You may require other software packages depending on the
|
||||
services you wish to use. For example, the HTTP service will require the apache2 package.**
|
||||
|
||||
# Installed Files
|
||||
|
||||
CORE files are installed to the following directories, when the installation prefix is **/usr**.
|
||||
|
||||
Install Path | Description
|
||||
-------------|------------
|
||||
/usr/bin/core-gui|GUI startup command
|
||||
/usr/bin/core-daemon|Daemon startup command
|
||||
/usr/bin/|Misc. helper commands/scripts
|
||||
/usr/bin/{core-cleanup, coresendmsg, core-manage}|Misc. helper commands/scripts
|
||||
/usr/lib/core|GUI files
|
||||
/usr/lib/python2.7/dist-packages/core|Python modules for daemon/scripts
|
||||
/etc/core/|Daemon configuration files
|
||||
/usr/lib/python{2.7,3}/dist-packages/core|Python modules for daemon/scripts
|
||||
/etc/core/|Daemon and log configuration files
|
||||
~/.core/|User-specific GUI preferences and scenario files
|
||||
/usr/share/core/|Example scripts and scenarios
|
||||
/usr/share/man/man1/|Command man pages
|
||||
/etc/init.d/core-daemon|SysV startup script for daemon
|
||||
/etc/systemd/system/core-daemon.service|Systemd startup script for daemon
|
||||
|
||||
## Prerequisites
|
||||
# Pre-Req Python Requirements
|
||||
|
||||
A Linux operating system is required. The GUI uses the Tcl/Tk scripting toolkit, and the CORE daemon requires Python. Details of the individual software packages required can be found in the installation steps.
|
||||
## Ubuntu 19.04
|
||||
|
||||
## Required Hardware
|
||||
Ubuntu 19.04 can provide all the packages needed at the system level and can be installed as follows:
|
||||
|
||||
Any computer capable of running Linux should be able to run CORE. Since the physical machine will be hosting numerous virtual machines, as a general rule you should select a machine having as much RAM and CPU resources as possible.
|
||||
```shell
|
||||
# python 2
|
||||
sudo apt install python-configparser python-enum34 python-future python-grpcio python-lxml
|
||||
# python 3
|
||||
sudo apt install python3-configparser python3-enum34 python3-future python3-grpcio python3-lxml
|
||||
```
|
||||
|
||||
## Required Software
|
||||
## Other Distros
|
||||
|
||||
CORE requires a Linux operating system because it uses virtualization provided by the kernel. It does not run on Windows or Mac OS X operating systems (unless it is running within a virtual machine guest.) The virtualization technology that CORE currently uses is Linux network namespaces.
|
||||
The newly added gRPC API which depends on python library grpcio is not commonly found within system repos.
|
||||
To account for this it would be recommended to install the python dependencies using the **requirements.txt** found in
|
||||
the latest release.
|
||||
|
||||
The CORE GUI requires the X.Org X Window system (X11), or can run over a remote X11 session. For specific Tcl/Tk, Python, and other libraries required to run CORE.
|
||||
```shell
|
||||
# will need to pip3 for python3 usage
|
||||
sudo pip install -r requirements.txt
|
||||
```
|
||||
|
||||
**NOTE: CORE *Services* determine what run on each node. You may require other software packages depending on the services you wish to use. For example, the *HTTP* service will require the *apache2* package.**
|
||||
# Pre-Req Installing OSPF MDR
|
||||
|
||||
## Installing from Packages
|
||||
Virtual networks generally require some form of routing in order to work (e.g. to automatically populate routing
|
||||
tables for routing packets from one subnet to another.) CORE builds OSPF routing protocol configurations by
|
||||
default when the blue router node type is used.
|
||||
|
||||
The easiest way to install CORE is using the pre-built packages. The package managers on Ubuntu or Fedora/CentOS will automatically install dependencies for you. You can obtain the CORE packages from [CORE GitHub](https://github.com/coreemu/core/releases).
|
||||
* [OSPF MANET Designated Routers](http://www.nrl.navy.mil/itd/ncs/products/ospf-manet) (MDR) - the Quagga routing
|
||||
suite with a modified version of OSPFv3, optimized for use with mobile wireless networks. The **mdr** node type
|
||||
(and the MDR service) requires this variant of Quagga.
|
||||
|
||||
### Installing from Packages on Ubuntu
|
||||
## Ubuntu <= 16.04 and Fedora/CentOS
|
||||
|
||||
Install Quagga for routing. If you plan on working with wireless networks, we recommend installing [OSPF MDR](http://www.nrl.navy.mil/itd/ncs/products/ospf-manet) (replace *amd64* below with *i386* if needed to match your architecture):
|
||||
There is a built package which can be used.
|
||||
|
||||
```shell
|
||||
wget https://downloads.pf.itd.nrl.navy.mil/ospf-manet/quagga-0.99.21mr2.2/quagga-mr_0.99.21mr2.2_amd64.deb
|
||||
sudo dpkg -i quagga-mr_0.99.21mr2.2_amd64.deb
|
||||
```
|
||||
|
||||
Or, for the regular Ubuntu version of Quagga:
|
||||
## Ubuntu >= 18.04
|
||||
|
||||
Requires building from source, from the latest nightly snapshot.
|
||||
|
||||
```shell
|
||||
sudo apt-get install quagga
|
||||
wget https://downloads.pf.itd.nrl.navy.mil/ospf-manet/nightly_snapshots/quagga-svnsnap.tgz
|
||||
tar xzf quagga-svnsnap.tgz
|
||||
cd quagga
|
||||
./configure --enable-user=root --enable-group=root --with-cflags=-ggdb \
|
||||
--sysconfdir=/usr/local/etc/quagga --enable-vtysh \
|
||||
--localstatedir=/var/run/quagga
|
||||
make
|
||||
sudo make install
|
||||
```
|
||||
|
||||
Install the CORE deb packages for Ubuntu from command line.
|
||||
Note that the configuration directory */usr/local/etc/quagga* shown for Quagga above could be */etc/quagga*,
|
||||
if you create a symbolic link from */etc/quagga/Quagga.conf -> /usr/local/etc/quagga/Quagga.conf* on the host.
|
||||
The *quaggaboot.sh* script in a Linux network namespace will try and do this for you if needed.
|
||||
|
||||
If you try to run quagga after installing from source and get an error such as:
|
||||
|
||||
```shell
|
||||
sudo dpkg -i python-core_*.deb
|
||||
sudo dpkg -i core-gui_*.deb
|
||||
error while loading shared libraries libzebra.so.0
|
||||
```
|
||||
|
||||
Start the CORE daemon as root, the systemd installation will auto start the daemon, but you can use the commands below if need be.
|
||||
this is usually a sign that you have to run ```sudo ldconfig```` to refresh the cache file.
|
||||
|
||||
# Installing from Packages
|
||||
|
||||
The easiest way to install CORE is using the pre-built packages. The package managers on Ubuntu or Fedora/CentOS
|
||||
will help in automatically installing most dependencies for you.
|
||||
|
||||
You can obtain the CORE packages from [CORE Releases](https://github.com/coreemu/core/releases).
|
||||
|
||||
## Ubuntu
|
||||
|
||||
Ubuntu package defaults to using systemd for running as a service.
|
||||
|
||||
```shell
|
||||
# systemd
|
||||
sudo systemctl start core-daemon
|
||||
|
||||
# sysv
|
||||
sudo service core-daemon start
|
||||
# python2
|
||||
sudo apt install ./core_python_$VERSION_amd64.deb
|
||||
# python3
|
||||
sudo apt install ./core_python3_$VERSION_amd64.deb
|
||||
```
|
||||
|
||||
Run the CORE GUI as a normal user:
|
||||
|
@ -86,48 +136,35 @@ Run the CORE GUI as a normal user:
|
|||
core-gui
|
||||
```
|
||||
|
||||
After running the *core-gui* command, a GUI should appear with a canvas for drawing topologies. Messages will print out on the console about connecting to the CORE daemon.
|
||||
After running the *core-gui* command, a GUI should appear with a canvas for drawing topologies.
|
||||
Messages will print out on the console about connecting to the CORE daemon.
|
||||
|
||||
### Installing from Packages on Fedora/CentOS
|
||||
## Fedora/CentOS
|
||||
|
||||
The commands shown here should be run as root. The *x86_64* architecture is shown in the examples below, replace with *i686* is using a 32-bit architecture.
|
||||
|
||||
**CentOS 7 Only: in order to install *tkimg* package you must build from source.**
|
||||
|
||||
Make sure the system is up to date.
|
||||
**NOTE: tkimg is not required for the core-gui, but if you get an error message about it you can install the package
|
||||
on CentOS <= 6, or build from source otherwise**
|
||||
|
||||
```shell
|
||||
yum update
|
||||
# python2
|
||||
yum install ./core_python_$VERSION_x86_64.rpm
|
||||
# python3
|
||||
yum install ./core_python3_$VERSION_x86_64.rpm
|
||||
```
|
||||
|
||||
**Optional (Fedora 17+): Fedora 17 and newer have an additional prerequisite providing the required netem kernel modules (otherwise skip this step and have the package manager install it for you.)**
|
||||
Disabling SELINUX:
|
||||
|
||||
```shell
|
||||
yum install kernel-modules-extra
|
||||
# change the following in /etc/sysconfig/selinux
|
||||
SELINUX=disabled
|
||||
|
||||
# add the following to the kernel line in /etc/grub.conf
|
||||
selinux=0
|
||||
|
||||
# Fedora 15 and newer, disable sandboxd
|
||||
# reboot in order for this change to take effect
|
||||
chkconfig sandbox off
|
||||
```
|
||||
|
||||
Install Quagga for routing. If you plan on working with wireless networks, we recommend installing [OSPF MDR](http://www.nrl.navy.mil/itd/ncs/products/ospf-manet):
|
||||
|
||||
```shell
|
||||
wget https://downloads.pf.itd.nrl.navy.mil/ospf-manet/quagga-0.99.21mr2.2/quagga-0.99.21mr2.2-1.el6.x86_64.rpm
|
||||
sudo yum install quagga-0.99.21mr2.2-1.el6.x86_64.rpm
|
||||
```
|
||||
|
||||
Or, for the regular Fedora/CentOS version of Quagga:
|
||||
|
||||
```shell
|
||||
yum install quagga
|
||||
```
|
||||
|
||||
Install the CORE RPM packages and automatically resolve dependencies:
|
||||
|
||||
```shell
|
||||
yum install python-core_*.rpm
|
||||
yum install core-gui_*.rpm
|
||||
```
|
||||
|
||||
Turn off SELINUX by setting *SELINUX=disabled* in the */etc/sysconfig/selinux* file, and adding *selinux=0* to the kernel line in your */etc/grub.conf* file; on Fedora 15 and newer, disable sandboxd using ```chkconfig sandbox off```; you need to reboot in order for this change to take effect
|
||||
|
||||
Turn off firewalls:
|
||||
|
||||
```shell
|
||||
|
@ -145,7 +182,7 @@ iptables -F
|
|||
ip6tables -F
|
||||
```
|
||||
|
||||
Start the CORE daemon as root.
|
||||
Start the CORE daemon.
|
||||
|
||||
```shell
|
||||
# systemd
|
||||
|
@ -164,159 +201,88 @@ core-gui
|
|||
|
||||
After running the *core-gui* command, a GUI should appear with a canvas for drawing topologies. Messages will print out on the console about connecting to the CORE daemon.
|
||||
|
||||
### Installing from Source
|
||||
# Building and Installing from Source
|
||||
|
||||
This option is listed here for developers and advanced users who are comfortable patching and building source code. Please consider using the binary packages instead for a simplified install experience.
|
||||
This option is listed here for developers and advanced users who are comfortable patching and building source code.
|
||||
Please consider using the binary packages instead for a simplified install experience.
|
||||
|
||||
To build CORE from source on Ubuntu, first install these development packages. These packages are not required for normal binary package installs.
|
||||
## Download and Extract Source Code
|
||||
|
||||
You can obtain the CORE source from the [CORE GitHub](https://github.com/coreemu/core) page. Choose either a stable release version or the development snapshot available in the *nightly_snapshots* directory.
|
||||
You can obtain the CORE source from the [CORE GitHub](https://github.com/coreemu/core) page.
|
||||
|
||||
#### Install Requirements
|
||||
## Install grpcio-tools
|
||||
|
||||
##### Ubuntu 18.04 Requirements
|
||||
Python module grpcio-tools is currently needed to generate code from the CORE protobuf file during the build.
|
||||
|
||||
```shell
|
||||
sudo apt install automake pkg-config gcc libev-dev bridge-utils ebtables python-dev python-sphinx python-setuptools python-lxml python-enum34 tk libtk-img
|
||||
# python2
|
||||
pip2 install grpcio-tools
|
||||
# python3
|
||||
pip3 install grpcio-tools
|
||||
```
|
||||
|
||||
##### Ubuntu 16.04 Requirements
|
||||
## Distro Requirements
|
||||
|
||||
### Ubuntu 18.04 Requirements
|
||||
|
||||
```shell
|
||||
sudo apt-get install automake bridge-utils ebtables python-dev libev-dev python-sphinx python-setuptools python-enum34 python-lxml libtk-img
|
||||
sudo apt install automake pkg-config gcc libev-dev bridge-utils ebtables python-dev python-setuptools tk libtk-img
|
||||
```
|
||||
|
||||
|
||||
##### CentOS 7 with Gnome Desktop Requirements
|
||||
### Ubuntu 16.04 Requirements
|
||||
|
||||
```shell
|
||||
sudo yum -y install automake gcc python-devel libev-devel python-sphinx tk python-lxml python-enum34
|
||||
sudo apt-get install automake bridge-utils ebtables python-dev libev-dev python-setuptools libtk-img
|
||||
```
|
||||
|
||||
#### Download and Extract Source Code
|
||||
### CentOS 7 with Gnome Desktop Requirements
|
||||
|
||||
##### Download
|
||||
You can obtain the CORE source code from the [CORE GitHub](https://github.com/coreemu/core) page.
|
||||
|
||||
##### Extract
|
||||
```shell
|
||||
tar xzf core-*.tar.gz
|
||||
cd core-*
|
||||
sudo yum -y install automake gcc python-devel libev-devel tk
|
||||
```
|
||||
|
||||
#### Traditional Autotools Build
|
||||
## Build and Install
|
||||
|
||||
```shell
|
||||
./bootstrap.sh
|
||||
./configure
|
||||
# use python2 or python3 depending on desired version
|
||||
PYTHON=$VERSION ./configure
|
||||
make
|
||||
sudo make install
|
||||
```
|
||||
|
||||
#### Build Documentation
|
||||
# Building Documentation
|
||||
|
||||
Building documentation requires python-sphinx not noted above.
|
||||
|
||||
```shell
|
||||
# install python2 sphinx
|
||||
sudo apt install python-sphinx
|
||||
sudo yum install python-sphinx
|
||||
# install python3 sphinx
|
||||
sudo apt install python3-sphinx
|
||||
sudo yum install python3-sphinx
|
||||
|
||||
./bootstrap.sh
|
||||
./configure
|
||||
# use python2 or python3 depending on desired version
|
||||
PYTHON=$VERSION ./configure
|
||||
make doc
|
||||
```
|
||||
|
||||
#### Build Packages
|
||||
Install fpm: http://fpm.readthedocs.io/en/latest/installing.html
|
||||
Build package commands, DESTDIR is used for gui packaging only
|
||||
# Building Packages
|
||||
Build package commands, DESTDIR is used to make install into and then for packaging by fpm.
|
||||
|
||||
**NOTE: clean the DESTDIR if re-using the same directory**
|
||||
|
||||
* Install [fpm](http://fpm.readthedocs.io/en/latest/installing.html)
|
||||
|
||||
```shell
|
||||
./bootstrap.sh
|
||||
./configure
|
||||
# use python2 or python3 depending on desired version
|
||||
PYTHON=$VERSION ./configure
|
||||
make
|
||||
mkdir /tmp/core-gui
|
||||
make fpm DESTDIR=/tmp/core-gui
|
||||
|
||||
```
|
||||
This will produce:
|
||||
|
||||
* CORE GUI rpm/deb files
|
||||
* core-gui_$VERSION_$ARCH
|
||||
* CORE ns3 rpm/deb files
|
||||
* python-core-ns3_$VERSION_$ARCH
|
||||
* CORE python rpm/deb files for SysV and systemd service types
|
||||
* python-core-sysv_$VERSION_$ARCH
|
||||
* python-core-systemd_$VERSION_$ARCH
|
||||
|
||||
|
||||
### Quagga Routing Software
|
||||
|
||||
Virtual networks generally require some form of routing in order to work (e.g. to automatically populate routing tables for routing packets from one subnet to another.) CORE builds OSPF routing protocol configurations by default when the blue router node type is used. The OSPF protocol is available from the [Quagga open source routing suit](http://www.quagga.net).
|
||||
|
||||
Quagga is not specified as a dependency for the CORE packages because there are two different Quagga packages that you may use:
|
||||
|
||||
* [Quagga](http://www.quagga.net) - the standard version of Quagga, suitable for static wired networks, and usually available via your distribution's package manager.
|
||||
|
||||
* [OSPF MANET Designated Routers](http://www.nrl.navy.mil/itd/ncs/products/ospf-manet) (MDR) - the Quagga routing suite with a modified version of OSPFv3, optimized for use with mobile wireless networks. The *mdr* node type (and the MDR service) requires this variant of Quagga.
|
||||
|
||||
If you plan on working with wireless networks, we recommend installing OSPF MDR; otherwise install the standard version of Quagga using your package manager or from source.
|
||||
|
||||
#### Installing Quagga from Packages
|
||||
|
||||
To install the standard version of Quagga from packages, use your package manager (Linux).
|
||||
|
||||
Ubuntu users:
|
||||
|
||||
```shell
|
||||
sudo apt-get install quagga
|
||||
mkdir /tmp/core-build
|
||||
make fpm DESTDIR=/tmp/core-build
|
||||
```
|
||||
|
||||
Fedora/CentOS users:
|
||||
|
||||
```shell
|
||||
sudo yum install quagga
|
||||
```
|
||||
|
||||
To install the Quagga variant having OSPFv3 MDR, first download the appropriate package, and install using the package manager.
|
||||
|
||||
Ubuntu users:
|
||||
```shell
|
||||
wget https://downloads.pf.itd.nrl.navy.mil/ospf-manet/quagga-0.99.21mr2.2/quagga-mr_0.99.21mr2.2_amd64.deb
|
||||
sudo dpkg -i quagga-mr_0.99.21mr2.2_amd64.deb
|
||||
```
|
||||
|
||||
Replace *amd64* with *i686* if using a 32-bit architecture.
|
||||
|
||||
Fedora/CentOS users:
|
||||
|
||||
```shell
|
||||
wget https://downloads.pf.itd.nrl.navy.mil/ospf-manet/quagga-0.99.21mr2.2/quagga-0.99.21mr2.2-1.el6.x86_64.rpm
|
||||
sudo yum install quagga-0.99.21mr2.2-1.el6.x86_64.rpm
|
||||
````
|
||||
|
||||
Replace *x86_64* with *i686* if using a 32-bit architecture.
|
||||
|
||||
#### Compiling Quagga for CORE
|
||||
|
||||
To compile Quagga to work with CORE on Linux:
|
||||
|
||||
```shell
|
||||
wget https://downloads.pf.itd.nrl.navy.mil/ospf-manet/quagga-0.99.21mr2.2/quagga-0.99.21mr2.2.tar.gz
|
||||
tar xzf quagga-0.99.21mr2.2.tar.gz
|
||||
cd quagga-0.99
|
||||
./configure --enable-user=root --enable-group=root --with-cflags=-ggdb \\
|
||||
--sysconfdir=/usr/local/etc/quagga --enable-vtysh \\
|
||||
--localstatedir=/var/run/quagga
|
||||
make
|
||||
sudo make install
|
||||
```
|
||||
|
||||
Note that the configuration directory */usr/local/etc/quagga* shown for Quagga above could be */etc/quagga*, if you create a symbolic link from */etc/quagga/Quagga.conf -> /usr/local/etc/quagga/Quagga.conf* on the host. The *quaggaboot.sh* script in a Linux network namespace will try and do this for you if needed.
|
||||
|
||||
If you try to run quagga after installing from source and get an error such as:
|
||||
|
||||
```shell
|
||||
error while loading shared libraries libzebra.so.0
|
||||
```
|
||||
|
||||
this is usually a sign that you have to run ```sudo ldconfig```` to refresh the cache file.
|
||||
|
||||
### VCORE
|
||||
|
||||
CORE is capable of running inside of a virtual machine, using software such as VirtualBox, VMware Server or QEMU. However, CORE itself is performing machine virtualization in order to realize multiple emulated nodes, and running CORE virtually adds additional contention for the physical resources. **For performance reasons, this is not recommended.** Timing inside of a VM often has problems. If you do run CORE from within a VM, it is recommended that you view the GUI with remote X11 over SSH, so the virtual machine does not need to emulate the video card with the X11 application.
|
||||
|
||||
A CORE virtual machine is provided for download, named VCORE. This is the perhaps the easiest way to get CORE up and running as the machine is already set up for you. This may be adequate for initially evaluating the tool but keep in mind the performance limitations of running within VirtualBox or VMware. To install the virtual machine, you first need to obtain VirtualBox from http://www.virtualbox.org, or VMware Server or Player from http://www.vmware.com (this commercial software is distributed for free.) Once virtualization software has been installed, you can import the virtual machine appliance using the *vbox* file for VirtualBox or the *vmx* file for VMware. See the documentation that comes with VCORE for login information.
|
||||
|
||||
This will produce and RPM and Deb package for the currently configured python version.
|
||||
|
|
190
docs/pycco.css
190
docs/pycco.css
|
@ -1,190 +0,0 @@
|
|||
/*--------------------- Layout and Typography ----------------------------*/
|
||||
body {
|
||||
font-family: 'Palatino Linotype', 'Book Antiqua', Palatino, FreeSerif, serif;
|
||||
font-size: 16px;
|
||||
line-height: 24px;
|
||||
color: #252519;
|
||||
margin: 0; padding: 0;
|
||||
background: #f5f5ff;
|
||||
}
|
||||
a {
|
||||
color: #261a3b;
|
||||
}
|
||||
a:visited {
|
||||
color: #261a3b;
|
||||
}
|
||||
p {
|
||||
margin: 0 0 15px 0;
|
||||
}
|
||||
h1, h2, h3, h4, h5, h6 {
|
||||
margin: 40px 0 15px 0;
|
||||
}
|
||||
h2, h3, h4, h5, h6 {
|
||||
margin-top: 0;
|
||||
}
|
||||
#container {
|
||||
background: white;
|
||||
}
|
||||
#container, div.section {
|
||||
position: relative;
|
||||
}
|
||||
#background {
|
||||
position: absolute;
|
||||
top: 0; left: 580px; right: 0; bottom: 0;
|
||||
background: #f5f5ff;
|
||||
border-left: 1px solid #e5e5ee;
|
||||
z-index: 0;
|
||||
}
|
||||
#jump_to, #jump_page {
|
||||
background: white;
|
||||
-webkit-box-shadow: 0 0 25px #777; -moz-box-shadow: 0 0 25px #777;
|
||||
-webkit-border-bottom-left-radius: 5px; -moz-border-radius-bottomleft: 5px;
|
||||
font: 10px Arial;
|
||||
text-transform: uppercase;
|
||||
cursor: pointer;
|
||||
text-align: right;
|
||||
}
|
||||
#jump_to, #jump_wrapper {
|
||||
position: fixed;
|
||||
right: 0; top: 0;
|
||||
padding: 5px 10px;
|
||||
}
|
||||
#jump_wrapper {
|
||||
padding: 0;
|
||||
display: none;
|
||||
}
|
||||
#jump_to:hover #jump_wrapper {
|
||||
display: block;
|
||||
}
|
||||
#jump_page {
|
||||
padding: 5px 0 3px;
|
||||
margin: 0 0 25px 25px;
|
||||
}
|
||||
#jump_page .source {
|
||||
display: block;
|
||||
padding: 5px 10px;
|
||||
text-decoration: none;
|
||||
border-top: 1px solid #eee;
|
||||
}
|
||||
#jump_page .source:hover {
|
||||
background: #f5f5ff;
|
||||
}
|
||||
#jump_page .source:first-child {
|
||||
}
|
||||
div.docs {
|
||||
float: left;
|
||||
max-width: 500px;
|
||||
min-width: 500px;
|
||||
min-height: 5px;
|
||||
padding: 10px 25px 1px 50px;
|
||||
vertical-align: top;
|
||||
text-align: left;
|
||||
}
|
||||
.docs pre {
|
||||
margin: 15px 0 15px;
|
||||
padding-left: 15px;
|
||||
}
|
||||
.docs p tt, .docs p code {
|
||||
background: #f8f8ff;
|
||||
border: 1px solid #dedede;
|
||||
font-size: 12px;
|
||||
padding: 0 0.2em;
|
||||
}
|
||||
.octowrap {
|
||||
position: relative;
|
||||
}
|
||||
.octothorpe {
|
||||
font: 12px Arial;
|
||||
text-decoration: none;
|
||||
color: #454545;
|
||||
position: absolute;
|
||||
top: 3px; left: -20px;
|
||||
padding: 1px 2px;
|
||||
opacity: 0;
|
||||
-webkit-transition: opacity 0.2s linear;
|
||||
}
|
||||
div.docs:hover .octothorpe {
|
||||
opacity: 1;
|
||||
}
|
||||
div.code {
|
||||
margin-left: 580px;
|
||||
padding: 14px 15px 16px 50px;
|
||||
vertical-align: top;
|
||||
}
|
||||
.code pre, .docs p code {
|
||||
font-size: 12px;
|
||||
}
|
||||
pre, tt, code {
|
||||
line-height: 18px;
|
||||
font-family: Monaco, Consolas, "Lucida Console", monospace;
|
||||
margin: 0; padding: 0;
|
||||
}
|
||||
div.clearall {
|
||||
clear: both;
|
||||
}
|
||||
|
||||
|
||||
/*---------------------- Syntax Highlighting -----------------------------*/
|
||||
td.linenos { background-color: #f0f0f0; padding-right: 10px; }
|
||||
span.lineno { background-color: #f0f0f0; padding: 0 5px 0 5px; }
|
||||
body .hll { background-color: #ffffcc }
|
||||
body .c { color: #408080; font-style: italic } /* Comment */
|
||||
body .err { border: 1px solid #FF0000 } /* Error */
|
||||
body .k { color: #954121 } /* Keyword */
|
||||
body .o { color: #666666 } /* Operator */
|
||||
body .cm { color: #408080; font-style: italic } /* Comment.Multiline */
|
||||
body .cp { color: #BC7A00 } /* Comment.Preproc */
|
||||
body .c1 { color: #408080; font-style: italic } /* Comment.Single */
|
||||
body .cs { color: #408080; font-style: italic } /* Comment.Special */
|
||||
body .gd { color: #A00000 } /* Generic.Deleted */
|
||||
body .ge { font-style: italic } /* Generic.Emph */
|
||||
body .gr { color: #FF0000 } /* Generic.Error */
|
||||
body .gh { color: #000080; font-weight: bold } /* Generic.Heading */
|
||||
body .gi { color: #00A000 } /* Generic.Inserted */
|
||||
body .go { color: #808080 } /* Generic.Output */
|
||||
body .gp { color: #000080; font-weight: bold } /* Generic.Prompt */
|
||||
body .gs { font-weight: bold } /* Generic.Strong */
|
||||
body .gu { color: #800080; font-weight: bold } /* Generic.Subheading */
|
||||
body .gt { color: #0040D0 } /* Generic.Traceback */
|
||||
body .kc { color: #954121 } /* Keyword.Constant */
|
||||
body .kd { color: #954121; font-weight: bold } /* Keyword.Declaration */
|
||||
body .kn { color: #954121; font-weight: bold } /* Keyword.Namespace */
|
||||
body .kp { color: #954121 } /* Keyword.Pseudo */
|
||||
body .kr { color: #954121; font-weight: bold } /* Keyword.Reserved */
|
||||
body .kt { color: #B00040 } /* Keyword.Type */
|
||||
body .m { color: #666666 } /* Literal.Number */
|
||||
body .s { color: #219161 } /* Literal.String */
|
||||
body .na { color: #7D9029 } /* Name.Attribute */
|
||||
body .nb { color: #954121 } /* Name.Builtin */
|
||||
body .nc { color: #0000FF; font-weight: bold } /* Name.Class */
|
||||
body .no { color: #880000 } /* Name.Constant */
|
||||
body .nd { color: #AA22FF } /* Name.Decorator */
|
||||
body .ni { color: #999999; font-weight: bold } /* Name.Entity */
|
||||
body .ne { color: #D2413A; font-weight: bold } /* Name.Exception */
|
||||
body .nf { color: #0000FF } /* Name.Function */
|
||||
body .nl { color: #A0A000 } /* Name.Label */
|
||||
body .nn { color: #0000FF; font-weight: bold } /* Name.Namespace */
|
||||
body .nt { color: #954121; font-weight: bold } /* Name.Tag */
|
||||
body .nv { color: #19469D } /* Name.Variable */
|
||||
body .ow { color: #AA22FF; font-weight: bold } /* Operator.Word */
|
||||
body .w { color: #bbbbbb } /* Text.Whitespace */
|
||||
body .mf { color: #666666 } /* Literal.Number.Float */
|
||||
body .mh { color: #666666 } /* Literal.Number.Hex */
|
||||
body .mi { color: #666666 } /* Literal.Number.Integer */
|
||||
body .mo { color: #666666 } /* Literal.Number.Oct */
|
||||
body .sb { color: #219161 } /* Literal.String.Backtick */
|
||||
body .sc { color: #219161 } /* Literal.String.Char */
|
||||
body .sd { color: #219161; font-style: italic } /* Literal.String.Doc */
|
||||
body .s2 { color: #219161 } /* Literal.String.Double */
|
||||
body .se { color: #BB6622; font-weight: bold } /* Literal.String.Escape */
|
||||
body .sh { color: #219161 } /* Literal.String.Heredoc */
|
||||
body .si { color: #BB6688; font-weight: bold } /* Literal.String.Interpol */
|
||||
body .sx { color: #954121 } /* Literal.String.Other */
|
||||
body .sr { color: #BB6688 } /* Literal.String.Regex */
|
||||
body .s1 { color: #219161 } /* Literal.String.Single */
|
||||
body .ss { color: #19469D } /* Literal.String.Symbol */
|
||||
body .bp { color: #954121 } /* Name.Builtin.Pseudo */
|
||||
body .vc { color: #19469D } /* Name.Variable.Class */
|
||||
body .vg { color: #19469D } /* Name.Variable.Global */
|
||||
body .vi { color: #19469D } /* Name.Variable.Instance */
|
||||
body .il { color: #666666 } /* Literal.Number.Integer.Long */
|
|
@ -17,8 +17,8 @@ Here are the basic elements of a CORE Python script:
|
|||
```python
|
||||
from core.emulator.coreemu import CoreEmu
|
||||
from core.emulator.emudata import IpPrefixes
|
||||
from core.enumerations import EventTypes
|
||||
from core.enumerations import NodeTypes
|
||||
from core.emulator.enumerations import EventTypes
|
||||
from core.emulator.enumerations import NodeTypes
|
||||
|
||||
# ip generator for example
|
||||
prefixes = IpPrefixes(ip4_prefix="10.83.0.0/16")
|
||||
|
@ -34,7 +34,7 @@ session.set_state(EventTypes.CONFIGURATION_STATE)
|
|||
switch = session.add_node(_type=NodeTypes.SWITCH)
|
||||
|
||||
# create nodes
|
||||
for _ in xrange(options.nodes):
|
||||
for _ in range(2):
|
||||
node = session.add_node()
|
||||
interface = prefixes.create_interface(node)
|
||||
session.add_link(node.id, switch.id, interface_one=interface)
|
||||
|
|
109
docs/services.md
109
docs/services.md
|
@ -20,7 +20,7 @@ shutdown commands, and meta-data associated with a node.
|
|||
using the **init**, **upstart**, or **systemd** frameworks. These
|
||||
lightweight nodes use configured CORE *services*.
|
||||
|
||||
### Default Services and Node Types
|
||||
## Default Services and Node Types
|
||||
|
||||
Here are the default node types and their services:
|
||||
|
||||
|
@ -61,7 +61,7 @@ recommended that you do not change the default built-in node types. The
|
|||
**nodes.conf** file can be copied between CORE machines to save your custom
|
||||
types.
|
||||
|
||||
### Customizing a Service
|
||||
## Customizing a Service
|
||||
|
||||
A service can be fully customized for a particular node. From the node's
|
||||
configuration dialog, click on the button next to the service name to invoke
|
||||
|
@ -124,51 +124,65 @@ an error to be displayed in the Check Emulation Light.
|
|||
To start, stop, and restart services during run-time, right-click a
|
||||
node and use the *Services...* menu.
|
||||
|
||||
### Creating new Services
|
||||
## New Services
|
||||
|
||||
Services can save time required to configure nodes, especially if a number
|
||||
of nodes require similar configuration procedures. New services can be
|
||||
introduced to automate tasks.
|
||||
|
||||
### Leveraging UserDefined
|
||||
|
||||
The easiest way to capture the configuration of a new process into a service
|
||||
is by using the **UserDefined** service. This is a blank service where any
|
||||
aspect may be customized. The UserDefined service is convenient for testing
|
||||
ideas for a service before adding a new service type.
|
||||
|
||||
To introduce new service types, a **myservices/** directory exists in the
|
||||
user's CORE configuration directory, at **~/.core/myservices/**. A detailed
|
||||
**README.txt** file exists in that directory to outline the steps necessary
|
||||
for adding a new service. First, you need to create a small Python file that
|
||||
defines the service; then the **custom_services_dir** entry must be set
|
||||
in the **/etc/core/core.conf** configuration file. A sample is provided in
|
||||
the **myservices/** directory.
|
||||
### Creating New Service
|
||||
|
||||
**NOTE:**
|
||||
1. Modify the [Example Service File](/daemon/examples/myservices/sample.py)
|
||||
to do what you want. It could generate config/script files, mount per-node
|
||||
directories, start processes/scripts, etc. sample.py is a Python file that
|
||||
defines one or more classes to be imported. You can create multiple Python
|
||||
files that will be imported. Add any new filenames to the __init__.py file.
|
||||
|
||||
2. Put these files in a directory such as /home/username/.core/myservices
|
||||
Note that the last component of this directory name **myservices** should not
|
||||
be named something like **services** which conflicts with an existing Python
|
||||
name (the syntax 'from myservices import *' is used).
|
||||
|
||||
3. Add a **custom_services_dir = /home/username/.core/myservices** entry to the
|
||||
/etc/core/core.conf file.
|
||||
|
||||
**NOTE:**
|
||||
The directory name used in **custom_services_dir** should be unique and
|
||||
should not correspond to
|
||||
any existing Python module name. For example, don't use the name **subprocess**
|
||||
or **services**.
|
||||
|
||||
4. Restart the CORE daemon (core-daemon). Any import errors (Python syntax)
|
||||
should be displayed in the /var/log/core-daemon.log log file (or on screen).
|
||||
|
||||
5. Start using your custom service on your nodes. You can create a new node
|
||||
type that uses your service, or change the default services for an existing
|
||||
node type, or change individual nodes.
|
||||
|
||||
If you have created a new service type that may be useful to others, please
|
||||
consider contributing it to the CORE project.
|
||||
|
||||
Here is an example service with documentation describing functionality:
|
||||
[Example Service](exampleservice.html)
|
||||
## Available Services
|
||||
|
||||
### Available Services
|
||||
|
||||
#### BIRD Internet Routing Daemon
|
||||
### BIRD Internet Routing Daemon
|
||||
The [BIRD Internet Routing Daemon](https://bird.network.cz/) is a routing daemon; i.e., a software responsible for managing kernel packet forwarding tables. It aims to develop a dynamic IP routing daemon with full support of all modern routing protocols, easy to use configuration interface and powerful route filtering language, primarily targeted on (but not limited to) Linux and other UNIX-like systems and distributed under the GNU General Public License. BIRD has a free implementation of several well known and common routing and router-supplemental protocols, namely RIP, RIPng, OSPFv2, OSPFv3, BGP, BFD, and NDP/RA. BIRD supports IPv4 and IPv6 address families, Linux kernel and several BSD variants (tested on FreeBSD, NetBSD and OpenBSD). BIRD consists of bird daemon and birdc interactive CLI client used for supervision.
|
||||
|
||||
In order to be able to use the BIRD Internet Routing Protocol, you must first install the project on your machine.
|
||||
|
||||
|
||||
##### BIRD Package Install
|
||||
#### BIRD Package Install
|
||||
```shell
|
||||
sudo apt-get install bird
|
||||
```
|
||||
|
||||
##### BIRD Source Code Install
|
||||
#### BIRD Source Code Install
|
||||
You can download BIRD source code from it's [official repository.](https://gitlab.labs.nic.cz/labs/bird/)
|
||||
```shell
|
||||
./configure
|
||||
|
@ -182,7 +196,7 @@ The installation will place the bird directory inside */etc* where you will also
|
|||
In order to be able to do use the Bird Internet Routing Protocol, you must modify *bird.conf* due to the fact that the given configuration file is not configured beyond allowing the bird daemon to start, which means that nothing else will happen if you run it. Keeran Marquis has a very detailed example on [Configuring BGP using Bird on Ubuntu](https://blog.marquis.co/configuring-bgp-using-bird-on-ubuntu-14-04lts/) which can be used as a building block to implement your custom routing daemon.
|
||||
|
||||
|
||||
#### FRRouting
|
||||
### FRRouting
|
||||
FRRouting is a routing software package that provides TCP/IP based routing services with routing protocols support such as BGP, RIP, OSPF, IS-IS and more. FRR also supports special BGP Route Reflector and Route Server behavior. In addition to traditional IPv4 routing protocols, FRR also supports IPv6 routing protocols. With an SNMP daemon that supports the AgentX protocol, FRR provides routing protocol MIB read-only access (SNMP Support).
|
||||
|
||||
FRR currently supports the following protocols:
|
||||
|
@ -202,7 +216,7 @@ FRR currently supports the following protocols:
|
|||
* EIGRP (alpha)
|
||||
* NHRP (alpha)
|
||||
|
||||
##### FRRouting Package Install
|
||||
#### FRRouting Package Install
|
||||
```shell
|
||||
sudo apt install curl
|
||||
curl -s https://deb.frrouting.org/frr/keys.asc | sudo apt-key add -
|
||||
|
@ -211,7 +225,7 @@ echo deb https://deb.frrouting.org/frr $(lsb_release -s -c) $FRRVER | sudo tee -
|
|||
sudo apt update && sudo apt install frr frr-pythontools
|
||||
```
|
||||
|
||||
##### FRRouting Source Code Install
|
||||
#### FRRouting Source Code Install
|
||||
Building FRR from source is the best way to ensure you have the latest features and bug fixes. Details for each supported platform, including dependency package listings, permissions, and other gotchas, are in the developer’s documentation.
|
||||
|
||||
FRR’s source is available on the project [GitHub page](https://github.com/FRRouting/frr).
|
||||
|
@ -242,7 +256,7 @@ make && sudo make install
|
|||
If everything finishes successfully, FRR should be installed.
|
||||
|
||||
|
||||
#### Docker
|
||||
### Docker
|
||||
Docker service allows running docker containers within CORE nodes.
|
||||
The running of Docker within a CORE node allows for additional extensibility to
|
||||
the CORE services. This allows network applications and protocols to be easily
|
||||
|
@ -252,7 +266,7 @@ This service will add a new group to the services list. This will have a service
|
|||
|
||||
This requires a recent version of Docker. This was tested using a PPA on Ubuntu with version 1.2.0. The version in the standard Ubuntu repo is to old for this purpose (we need --net host).
|
||||
|
||||
##### Docker Installation
|
||||
#### Docker Installation
|
||||
To use Docker services, you must first install the Docker python image. This is used to interface with Docker from the python service.
|
||||
|
||||
```shell
|
||||
|
@ -279,8 +293,7 @@ This image will be listed in the services after we restart the core-daemon:
|
|||
sudo service core-daemon restart
|
||||
```
|
||||
|
||||
|
||||
#### NRL Services
|
||||
### NRL Services
|
||||
The Protean Protocol Prototyping Library (ProtoLib) is a cross-platform library that allows applications to be built while supporting a variety of platforms including Linux, Windows, WinCE/PocketPC, MacOS, FreeBSD, Solaris, etc as well as the simulation environments of NS2 and Opnet. The goal of the Protolib is to provide a set of simple, cross-platform C++ classes that allow development of network protocols and applications that can run on different platforms and in network simulation environments. While Protolib provides an overall framework for developing working protocol implementations, applications, and simulation modules, the individual classes are designed for use as stand-alone components when possible. Although Protolib is principally for research purposes, the code has been constructed to provide robust, efficient performance and adaptability to real applications. In some cases, the code consists of data structures, etc useful in protocol implementations and, in other cases, provides common, cross-platform interfaces to system services and functions (e.g., sockets, timers, routing tables, etc).
|
||||
|
||||
Currently the Naval Research Laboratory uses this library to develop a wide variety of protocols.The NRL Protolib currently supports the following protocols:
|
||||
|
@ -296,14 +309,14 @@ Currently the Naval Research Laboratory uses this library to develop a wide vari
|
|||
#### NRL Installation
|
||||
In order to be able to use the different protocols that NRL offers, you must first download the support library itself. You can get the source code from their [official nightly snapshots website](https://downloads.pf.itd.nrl.navy.mil/protolib/nightly_snapshots/).
|
||||
|
||||
##### Multi-Generator (MGEN)
|
||||
#### Multi-Generator (MGEN)
|
||||
Download MGEN from the [NRL MGEN nightly snapshots](https://downloads.pf.itd.nrl.navy.mil/mgen/nightly_snapshots/), unpack it and copy the protolib library into the main folder *mgen*. Execute the following commands to build the protocol.
|
||||
```shell
|
||||
cd mgen/makefiles
|
||||
make -f Makefile.{os} mgen
|
||||
```
|
||||
|
||||
##### Neighborhood Discovery Protocol (NHDP)
|
||||
#### Neighborhood Discovery Protocol (NHDP)
|
||||
Download NHDP from the [NRL NHDP nightly snapshots](https://downloads.pf.itd.nrl.navy.mil/nhdp/nightly_snapshots/).
|
||||
```shell
|
||||
sudo apt-get install libpcap-dev libboost-all-dev
|
||||
|
@ -320,31 +333,30 @@ cd nhdp/unix
|
|||
make -f Makefile.{os}
|
||||
```
|
||||
|
||||
##### Simplified Multicast Forwarding (SMF)
|
||||
#### Simplified Multicast Forwarding (SMF)
|
||||
Download SMF from the [NRL SMF nightly snapshot](https://downloads.pf.itd.nrl.navy.mil/smf/nightly_snapshots/) , unpack it and place the protolib library inside the *smf* main folder.
|
||||
```shell
|
||||
cd mgen/makefiles
|
||||
make -f Makefile.{os}
|
||||
```
|
||||
|
||||
##### Optimized Link State Routing Protocol (OLSR)
|
||||
#### Optimized Link State Routing Protocol (OLSR)
|
||||
To install the OLSR protocol, download their source code from their [nightly snapshots](https://downloads.pf.itd.nrl.navy.mil/olsr/nightly_snapshots/nrlolsr-svnsnap.tgz). Unpack it and place the previously downloaded protolib library inside the *nrlolsr* main directory. Then execute the following commands:
|
||||
```shell
|
||||
cd ./unix
|
||||
make -f Makefile.{os}
|
||||
```
|
||||
|
||||
|
||||
#### Quagga Routing Suite
|
||||
### Quagga Routing Suite
|
||||
Quagga is a routing software suite, providing implementations of OSPFv2, OSPFv3, RIP v1 and v2, RIPng and BGP-4 for Unix platforms, particularly FreeBSD, Linux, Solaris and NetBSD. Quagga is a fork of GNU Zebra which was developed by Kunihiro Ishiguro.
|
||||
The Quagga architecture consists of a core daemon, zebra, which acts as an abstraction layer to the underlying Unix kernel and presents the Zserv API over a Unix or TCP stream to Quagga clients. It is these Zserv clients which typically implement a routing protocol and communicate routing updates to the zebra daemon.
|
||||
|
||||
##### Quagga Package Install
|
||||
#### Quagga Package Install
|
||||
```shell
|
||||
sudo apt-get install quagga
|
||||
```
|
||||
|
||||
##### Quagga Source Install
|
||||
#### Quagga Source Install
|
||||
First, download the source code from their [official webpage](https://www.quagga.net/).
|
||||
```shell
|
||||
sudo apt-get install gawk
|
||||
|
@ -356,48 +368,45 @@ make
|
|||
sudo make install
|
||||
```
|
||||
|
||||
|
||||
#### Software Defined Networking
|
||||
### Software Defined Networking
|
||||
Ryu is a component-based software defined networking framework. Ryu provides software components with well defined API that make it easy for developers to create new network management and control applications. Ryu supports various protocols for managing network devices, such as OpenFlow, Netconf, OF-config, etc. About OpenFlow, Ryu supports fully 1.0, 1.2, 1.3, 1.4, 1.5 and Nicira Extensions. All of the code is freely available under the Apache 2.0 license.
|
||||
```shell
|
||||
```
|
||||
|
||||
##### Installation
|
||||
###### Prerequisites
|
||||
#### Installation
|
||||
##### Prerequisites
|
||||
```shell
|
||||
sudo apt-get install gcc python-dev libffi-dev libssl-dev libxml2-dev libxslt1-dev zlib1g-dev
|
||||
```
|
||||
###### Ryu Package Install
|
||||
##### Ryu Package Install
|
||||
```shell
|
||||
pip install ryu
|
||||
```
|
||||
###### Ryu Source Install
|
||||
##### Ryu Source Install
|
||||
```shell
|
||||
git clone git://github.com/osrg/ryu.git
|
||||
cd ryu; pip install .
|
||||
```
|
||||
|
||||
|
||||
#### Security Services
|
||||
### Security Services
|
||||
The security services offer a wide variety of protocols capable of satisfying the most use cases available. Security services such as IP security protocols, for providing security at the IP layer, as well as the suite of protocols designed to provide that security, through authentication and encryption of IP network packets. Virtual Private Networks (VPNs) and Firewalls are also available for use to the user.
|
||||
|
||||
##### Installation
|
||||
#### Installation
|
||||
```shell
|
||||
sudo apt-get install ipsec-tools racoon openvpn
|
||||
```
|
||||
|
||||
#### UCARP
|
||||
### UCARP
|
||||
UCARP allows a couple of hosts to share common virtual IP addresses in order to provide automatic failover. It is a portable userland implementation of the secure and patent-free Common Address Redundancy Protocol (CARP, OpenBSD's alternative to the patents-bloated VRRP).
|
||||
|
||||
Strong points of the CARP protocol are: very low overhead, cryptographically signed messages, interoperability between different operating systems and no need for any dedicated extra network link between redundant hosts.
|
||||
|
||||
##### Installation
|
||||
#### Installation
|
||||
```shell
|
||||
sudo apt-get install ucarp
|
||||
```
|
||||
|
||||
|
||||
#### Utilities Services
|
||||
### Utilities Services
|
||||
The following services are provided as utilities:
|
||||
* Default Routing
|
||||
* Default Muticast Routing
|
||||
|
@ -411,13 +420,13 @@ The following services are provided as utilities:
|
|||
* RADVD
|
||||
* ATD
|
||||
|
||||
##### Installation
|
||||
#### Installation
|
||||
To install the functionality of the previously metioned services you can run the following command:
|
||||
```shell
|
||||
sudo apt-get install isc-dhcp-server apache2 libpcap-dev radvd at
|
||||
```
|
||||
|
||||
#### XORP routing suite
|
||||
### XORP routing suite
|
||||
XORP is an open networking platform that supports OSPF, RIP, BGP, OLSR, VRRP, PIM, IGMP (Multicast) and other routing protocols. Most protocols support IPv4 and IPv6 where applicable. It is known to work on various Linux distributions and flavors of BSD.
|
||||
|
||||
XORP started life as a project at the ICSI Center for Open Networking (ICON) at the International Computer Science Institute in Berkeley, California, USA, and spent some time with the team at XORP, Inc. It is now maintained and improved on a volunteer basis by a core of long-term XORP developers and some newer contributors.
|
||||
|
@ -437,7 +446,7 @@ User-level XORP uses multi-process architecture with one process per routing pro
|
|||
|
||||
The lower-level subsystem can use traditional UNIX kernel forwarding, or Click modular router. The modularity and independency of the lower-level from the user-level subsystem allows for its easily replacement with other solutions including high-end hardware-based forwarding engines.
|
||||
|
||||
##### Installation
|
||||
#### Installation
|
||||
In order to be able to install the XORP Routing Suite, you must first install scons in order to compile it.
|
||||
```shell
|
||||
sudo apt-get install scons
|
||||
|
@ -450,5 +459,3 @@ sudo apt-get install libssl-dev ncurses-dev
|
|||
scons
|
||||
scons install
|
||||
```
|
||||
|
||||
|
||||
|
|
|
@ -243,7 +243,6 @@ proc moveNode { c node img xpos ypos dx dy } {
|
|||
"wlanlink && need_redraw"] {
|
||||
redrawWlanLink $wlanlink
|
||||
}
|
||||
$c dtag node selected
|
||||
$c delete -withtags selectmark
|
||||
$c dtag link need_redraw
|
||||
$c dtag wlanlink need_redraw
|
||||
|
|
|
@ -19,14 +19,14 @@ man_MANS = $(GUI_MANS) $(DAEMON_MANS)
|
|||
|
||||
.PHONY: generate-mans
|
||||
generate-mans:
|
||||
$(HELP2MAN) --source CORE 'sh $(top_srcdir)/gui/core-gui' -o core-gui.1.new
|
||||
$(HELP2MAN) --no-info --source CORE $(top_srcdir)/netns/vnoded -o vnoded.1.new
|
||||
$(HELP2MAN) --no-info --source CORE $(top_srcdir)/netns/vcmd -o vcmd.1.new
|
||||
$(HELP2MAN) --no-info --source CORE $(top_srcdir)/netns/netns -o netns.1.new
|
||||
$(HELP2MAN) --version-string=$(PACKAGE_VERSION) --no-info --source CORE $(top_srcdir)/daemon/scripts/core-daemon -o core-daemon.1.new
|
||||
$(HELP2MAN) --version-string=$(PACKAGE_VERSION) --no-info --source CORE $(top_srcdir)/daemon/scripts/coresendmsg -o coresendmsg.1.new
|
||||
$(HELP2MAN) --version-string=$(PACKAGE_VERSION) --no-info --source CORE $(top_srcdir)/daemon/scripts/core-cleanup -o core-cleanup.1.new
|
||||
$(HELP2MAN) --version-string=$(PACKAGE_VERSION) --no-info --source CORE $(top_srcdir)/daemon/scripts/core-manage -o core-manage.1.new
|
||||
$(HELP2MAN) --source CORE 'sh $(top_srcdir)/gui/core-gui' -o core-gui.1
|
||||
$(HELP2MAN) --no-info --source CORE $(top_srcdir)/netns/vnoded -o vnoded.1
|
||||
$(HELP2MAN) --no-info --source CORE $(top_srcdir)/netns/vcmd -o vcmd.1
|
||||
$(HELP2MAN) --no-info --source CORE $(top_srcdir)/netns/netns -o netns.1
|
||||
$(HELP2MAN) --version-string=$(PACKAGE_VERSION) --no-info --source CORE $(top_srcdir)/daemon/scripts/core-daemon -o core-daemon.1
|
||||
$(HELP2MAN) --version-string=$(PACKAGE_VERSION) --no-info --source CORE $(top_srcdir)/daemon/scripts/coresendmsg -o coresendmsg.1
|
||||
$(HELP2MAN) --version-string=$(PACKAGE_VERSION) --no-info --source CORE $(top_srcdir)/daemon/scripts/core-cleanup -o core-cleanup.1
|
||||
$(HELP2MAN) --version-string=$(PACKAGE_VERSION) --no-info --source CORE $(top_srcdir)/daemon/scripts/core-manage -o core-manage.1
|
||||
|
||||
.PHONY: diff
|
||||
diff:
|
||||
|
|
|
@ -1,9 +1,9 @@
|
|||
.\" DO NOT MODIFY THIS FILE! It was generated by help2man 1.40.4.
|
||||
.TH CORE-CLEANUP "1" "2014-08-06" "CORE-CLEANUP" "User Commands"
|
||||
.\" DO NOT MODIFY THIS FILE! It was generated by help2man 1.47.3.
|
||||
.TH CORE-CLEANUP "1" "June 2019" "CORE" "User Commands"
|
||||
.SH NAME
|
||||
core-cleanup \- clean-up script for CORE
|
||||
core-cleanup \- manual page for core-cleanup 5.3.0
|
||||
.SH DESCRIPTION
|
||||
usage: core\-cleanup [\-d [\-l]]
|
||||
usage: ../daemon/scripts/core\-cleanup [\-d [\-l]]
|
||||
.IP
|
||||
Clean up all CORE namespaces processes, bridges, interfaces, and session
|
||||
directories. Options:
|
||||
|
@ -15,15 +15,4 @@ show this help message and exit
|
|||
also kill the Python daemon
|
||||
.TP
|
||||
\fB\-l\fR
|
||||
remove the core-daemon.log file
|
||||
.SH "SEE ALSO"
|
||||
.BR core-gui(1),
|
||||
.BR core-daemon(1),
|
||||
.BR coresendmsg(1),
|
||||
.BR vcmd(1),
|
||||
.BR vnoded(1)
|
||||
.SH BUGS
|
||||
Report bugs to
|
||||
.BI https://github.com/coreemu/core/issues
|
||||
|
||||
|
||||
remove the core\-daemon.log file
|
||||
|
|
|
@ -1,52 +1,37 @@
|
|||
.\" DO NOT MODIFY THIS FILE! It was generated by help2man 1.40.4.
|
||||
.TH CORE-DAEMON "1" "2014-08-06" "CORE-DAEMON" "User Commands"
|
||||
.\" DO NOT MODIFY THIS FILE! It was generated by help2man 1.47.3.
|
||||
.TH CORE-DAEMON "1" "June 2019" "CORE" "User Commands"
|
||||
.SH NAME
|
||||
core-daemon \- CORE daemon manages emulation sessions started from GUI or scripts
|
||||
.SH SYNOPSIS
|
||||
.B core-daemon
|
||||
[\fI-h\fR] [\fIoptions\fR] [\fIargs\fR]
|
||||
core-daemon \- manual page for core-daemon 5.3.0
|
||||
.SH DESCRIPTION
|
||||
CORE daemon instantiates Linux network namespace nodes.
|
||||
.SH OPTIONS
|
||||
usage: core\-daemon [\-h] [\-f CONFIGFILE] [\-p PORT] [\-n NUMTHREADS] [\-\-ovs]
|
||||
.IP
|
||||
[\-\-grpc] [\-\-grpc\-port GRPCPORT]
|
||||
[\-\-grpc\-address GRPCADDRESS]
|
||||
.PP
|
||||
CORE daemon v.5.3.0 instantiates Linux network namespace nodes.
|
||||
.SS "optional arguments:"
|
||||
.TP
|
||||
\fB\-h\fR, \fB\-\-help\fR
|
||||
show this help message and exit
|
||||
.TP
|
||||
\fB\-f\fR CONFIGFILE, \fB\-\-configfile\fR=\fICONFIGFILE\fR
|
||||
\fB\-f\fR CONFIGFILE, \fB\-\-configfile\fR CONFIGFILE
|
||||
read config from specified file; default =
|
||||
/etc/core/core.conf
|
||||
\fI\,/etc/core/core.conf\/\fP
|
||||
.TP
|
||||
\fB\-d\fR, \fB\-\-daemonize\fR
|
||||
run in background as daemon; default=False
|
||||
.TP
|
||||
\fB\-e\fR EXECFILE, \fB\-\-execute\fR=\fIEXECFILE\fR
|
||||
execute a Python/XML\-based session
|
||||
.TP
|
||||
\fB\-l\fR LOGFILE, \fB\-\-logfile\fR=\fILOGFILE\fR
|
||||
log output to specified file; default =
|
||||
/var/log/core-daemon.log
|
||||
.TP
|
||||
\fB\-p\fR PORT, \fB\-\-port\fR=\fIPORT\fR
|
||||
\fB\-p\fR PORT, \fB\-\-port\fR PORT
|
||||
port number to listen on; default = 4038
|
||||
.TP
|
||||
\fB\-i\fR PIDFILE, \fB\-\-pidfile\fR=\fIPIDFILE\fR
|
||||
filename to write pid to; default = /var/run/core-daemon.pid
|
||||
.TP
|
||||
\fB\-t\fR NUMTHREADS, \fB\-\-numthreads\fR=\fINUMTHREADS\fR
|
||||
\fB\-n\fR NUMTHREADS, \fB\-\-numthreads\fR NUMTHREADS
|
||||
number of server threads; default = 1
|
||||
.TP
|
||||
\fB\-v\fR, \fB\-\-verbose\fR
|
||||
enable verbose logging; default = False
|
||||
\fB\-\-ovs\fR
|
||||
enable experimental ovs mode, default is false
|
||||
.TP
|
||||
\fB\-g\fR, \fB\-\-debug\fR
|
||||
enable debug logging; default = False
|
||||
.SH "SEE ALSO"
|
||||
.BR core-gui(1),
|
||||
.BR coresendmsg(1),
|
||||
.BR netns(1),
|
||||
.BR vcmd(1),
|
||||
.BR vnoded(1)
|
||||
.SH BUGS
|
||||
Report bugs to
|
||||
.BI https://github.com/coreemu/core/issues
|
||||
|
||||
\fB\-\-grpc\fR
|
||||
enable grpc api, default is false
|
||||
.TP
|
||||
\fB\-\-grpc\-port\fR GRPCPORT
|
||||
grpc port to listen on; default 50051
|
||||
.TP
|
||||
\fB\-\-grpc\-address\fR GRPCADDRESS
|
||||
grpc address to listen on; default localhost
|
||||
|
|
|
@ -1,11 +1,14 @@
|
|||
.\" DO NOT MODIFY THIS FILE! It was generated by help2man 1.40.4.
|
||||
.TH CORE-GUI "1" "2014-08-06" "CORE-GUI" "User Commands"
|
||||
.\" DO NOT MODIFY THIS FILE! It was generated by help2man 1.47.3.
|
||||
.TH CORE-GUI "1" "June 2019" "CORE" "User Commands"
|
||||
.SH NAME
|
||||
core-gui \- Common Open Research Emulator (CORE) graphical user interface
|
||||
core-gui \- manual page for core-gui version 5.3.0 (20190607)
|
||||
.SH SYNOPSIS
|
||||
.B core-gui
|
||||
[\fI-h|-v\fR] [\fI-b|-c <sessionid>\fR] [\fI-s\fR] [\fI-a address\fR] [\fI-p port\fR] [\fI<configfile.imn>\fR]
|
||||
[\fI\,-h|-v\/\fR] [\fI\,-b|-c <sessionid>\/\fR] [\fI\,-s\/\fR] [\fI\,-a address\/\fR] [\fI\,-p port\/\fR]
|
||||
.SH DESCRIPTION
|
||||
.IP
|
||||
[<configfile.imn>]
|
||||
.PP
|
||||
Launches the CORE Tcl/Tk X11 GUI or starts an imn\-based emulation.
|
||||
.TP
|
||||
\-(\fB\-h\fR)elp
|
||||
|
@ -26,7 +29,7 @@ start in execute mode, not edit mode
|
|||
\-(\fB\-a\fR)ddress
|
||||
connect to the specified IP address (default 127.0.0.1)
|
||||
.TP
|
||||
\-(\fB\-p\fR)ort
|
||||
\-(\fB\-p\fR)port
|
||||
connect to the specified TCP port (default 4038)
|
||||
.TP
|
||||
<configfile.imn>
|
||||
|
@ -34,11 +37,14 @@ connect to the specified TCP port (default 4038)
|
|||
.PP
|
||||
With no parameters, starts the GUI in edit mode with a blank canvas.
|
||||
.SH "SEE ALSO"
|
||||
.BR core-daemon(1),
|
||||
.BR coresendmsg(1),
|
||||
.BR vcmd(1),
|
||||
.BR vnoded(1)
|
||||
.SH BUGS
|
||||
Report bugs to
|
||||
.BI https://github.com/coreemu/core/issues
|
||||
|
||||
The full documentation for
|
||||
.B core-gui
|
||||
is maintained as a Texinfo manual. If the
|
||||
.B info
|
||||
and
|
||||
.B core-gui
|
||||
programs are properly installed at your site, the command
|
||||
.IP
|
||||
.B info core-gui
|
||||
.PP
|
||||
should give you access to the complete manual.
|
||||
|
|
|
@ -1,34 +1,31 @@
|
|||
.\" DO NOT MODIFY THIS FILE! It was generated by help2man 1.40.4.
|
||||
.TH CORE-MANAGE "1" "2014-08-06" "CORE-MANAGE" "User Commands"
|
||||
.\" DO NOT MODIFY THIS FILE! It was generated by help2man 1.47.3.
|
||||
.TH CORE-MANAGE "1" "June 2019" "CORE" "User Commands"
|
||||
.SH NAME
|
||||
core-manage \- helper tool to add, remove, or check for services and models in a CORE installation.
|
||||
core-manage \- manual page for core-manage 5.3.0
|
||||
.SH SYNOPSIS
|
||||
.B core-manage
|
||||
[\fI-h\fR] [\fIoptions\fR] \fI<action> <target> <string>\fR
|
||||
[\fI\,-h\/\fR] [\fI\,options\/\fR] \fI\,<action> <target> <string>\/\fR
|
||||
.SH DESCRIPTION
|
||||
.TP
|
||||
<action> should be one of: add, remove, check
|
||||
.TP
|
||||
<target> should be one of: service, model
|
||||
.TP
|
||||
<string> is the text to add, remove, check
|
||||
Helper tool to add, remove, or check for services, models, and node types
|
||||
in a CORE installation.
|
||||
.SH OPTIONS
|
||||
.TP
|
||||
\fB\-h\fR, \fB\-\-help\fR
|
||||
show this help message and exit
|
||||
.TP
|
||||
\fB\-\-userpath\fR=\fIUSERPATH\fR
|
||||
\fB\-\-userpath\fR=\fI\,USERPATH\/\fR
|
||||
use the specified user path (e.g. "$HOME/.core") to
|
||||
access nodes.conf
|
||||
.TP
|
||||
\fB\-v\fR, \fB\-\-verbose\fR
|
||||
be verbose when performing action
|
||||
.SH EXAMPLES
|
||||
.TP
|
||||
.IP
|
||||
core\-manage add service newrouting
|
||||
.TP
|
||||
core\-manage \fB\-v\fR check model RfPipe
|
||||
.TP
|
||||
core\-manage \fB\-\-userpath=\fR"$HOME/.core" add nodetype "{ftp ftp.gif ftp.gif {DefaultRoute FTP} netns {FTP server} }"
|
||||
.SH "SEE ALSO"
|
||||
.BR core-daemon(1)
|
||||
core\-manage \-v check model RfPipe
|
||||
core\-manage \-\-userpath="$HOME/.core" add nodetype "{ftp ftp.gif ftp.gif {DefaultRoute FTP} netns {FTP server} }"
|
||||
.SS "Arguments:"
|
||||
.IP
|
||||
<action> should be one of: add, remove, check
|
||||
<target> should be one of: service, model, nodetype
|
||||
<string> is the text to add, remove, check
|
||||
|
|
|
@ -1,28 +0,0 @@
|
|||
.\" 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 https://github.com/coreemu/core/issues
|
||||
|
||||
|
|
@ -1,17 +1,17 @@
|
|||
.\" DO NOT MODIFY THIS FILE! It was generated by help2man 1.40.4.
|
||||
.TH CORESENDMSG "1" "2014-08-06" "CORESENDMSG" "User Commands"
|
||||
.\" DO NOT MODIFY THIS FILE! It was generated by help2man 1.47.3.
|
||||
.TH CORESENDMSG "1" "June 2019" "CORE" "User Commands"
|
||||
.SH NAME
|
||||
coresendmsg \- send a CORE API message to the core-daemon daemon
|
||||
coresendmsg \- manual page for coresendmsg 5.3.0
|
||||
.SH SYNOPSIS
|
||||
.B coresendmsg
|
||||
[\fI-h|-H\fR] [\fIoptions\fR] [\fImessage-type\fR] [\fIflags=flags\fR] [\fImessage-TLVs\fR]
|
||||
[\fI\,-h|-H\/\fR] [\fI\,options\/\fR] [\fI\,message-type\/\fR] [\fI\,flags=flags\/\fR] [\fI\,message-TLVs\/\fR]
|
||||
.SH DESCRIPTION
|
||||
.SS "Supported message types:"
|
||||
.IP
|
||||
['node', 'link', 'exec', 'reg', 'conf', 'file', 'iface', 'event', 'sess', 'excp']
|
||||
['NODE', 'LINK', 'EXECUTE', 'REGISTER', 'CONFIG', 'FILE', 'INTERFACE', 'EVENT', 'SESSION', 'EXCEPTION']
|
||||
.SS "Supported message flags (flags=f1,f2,...):"
|
||||
.IP
|
||||
['add', 'del', 'cri', 'loc', 'str', 'txt', 'tty']
|
||||
['ADD', 'DELETE', 'CRI', 'LOCAL', 'STRING', 'TEXT', 'TTY']
|
||||
.SH OPTIONS
|
||||
.TP
|
||||
\fB\-h\fR, \fB\-\-help\fR
|
||||
|
@ -20,13 +20,13 @@ show this help message and exit
|
|||
\fB\-H\fR
|
||||
show example usage help message and exit
|
||||
.TP
|
||||
\fB\-p\fR PORT, \fB\-\-port\fR=\fIPORT\fR
|
||||
\fB\-p\fR PORT, \fB\-\-port\fR=\fI\,PORT\/\fR
|
||||
TCP port to connect to, default: 4038
|
||||
.TP
|
||||
\fB\-a\fR ADDRESS, \fB\-\-address\fR=\fIADDRESS\fR
|
||||
\fB\-a\fR ADDRESS, \fB\-\-address\fR=\fI\,ADDRESS\/\fR
|
||||
Address to connect to, default: localhost
|
||||
.TP
|
||||
\fB\-s\fR SESSION, \fB\-\-session\fR=\fISESSION\fR
|
||||
\fB\-s\fR SESSION, \fB\-\-session\fR=\fI\,SESSION\/\fR
|
||||
Session to join, default: None
|
||||
.TP
|
||||
\fB\-l\fR, \fB\-\-listen\fR
|
||||
|
@ -35,51 +35,6 @@ Listen for a response message and print it.
|
|||
\fB\-t\fR, \fB\-\-list\-tlvs\fR
|
||||
List TLVs for the specified message type.
|
||||
.TP
|
||||
\fB\-T\fR, \fB\-\-tcp\fR
|
||||
Use TCP instead of UDP and connect to a session,
|
||||
\fB\-\-tcp\fR
|
||||
Use TCP instead of UDP and connect to a session
|
||||
default: False
|
||||
.PP
|
||||
Usage: coresendmsg [\-h|\-H] [options] [message\-type] [flags=flags] [message\-TLVs]
|
||||
.SS "Supported message types:"
|
||||
.IP
|
||||
['node', 'link', 'exec', 'reg', 'conf', 'file', 'iface', 'event', 'sess', 'excp']
|
||||
.SS "Supported message flags (flags=f1,f2,...):"
|
||||
.IP
|
||||
['add', 'del', 'cri', 'loc', 'str', 'txt', 'tty']
|
||||
.TP
|
||||
\fB\-h\fR, \fB\-\-help\fR
|
||||
show this help message and exit
|
||||
.TP
|
||||
\fB\-H\fR
|
||||
show example usage help message and exit
|
||||
.TP
|
||||
\fB\-p\fR PORT, \fB\-\-port\fR=\fIPORT\fR
|
||||
TCP port to connect to, default: 4038
|
||||
.TP
|
||||
\fB\-a\fR ADDRESS, \fB\-\-address\fR=\fIADDRESS\fR
|
||||
Address to connect to, default: localhost
|
||||
.TP
|
||||
\fB\-s\fR SESSION, \fB\-\-session\fR=\fISESSION\fR
|
||||
Session to join, default: None
|
||||
.TP
|
||||
\fB\-l\fR, \fB\-\-listen\fR
|
||||
Listen for a response message and print it.
|
||||
.TP
|
||||
\fB\-t\fR, \fB\-\-list\-tlvs\fR
|
||||
List TLVs for the specified message type.
|
||||
.TP
|
||||
\fB\-T\fR, \fB\-\-tcp\fR
|
||||
Use TCP instead of UDP and connect to a session,
|
||||
default: False
|
||||
.SH "EXAMPLES"
|
||||
.TP
|
||||
A list of examples is available using the following command:
|
||||
coresendmsg \-H
|
||||
.SH "SEE ALSO"
|
||||
.BR core-gui(1),
|
||||
.BR core-daemon(1),
|
||||
.BR vcmd(1),
|
||||
.BR vnoded(1)
|
||||
.SH BUGS
|
||||
Report bugs to
|
||||
.BI https://github.com/coreemu/core/issues
|
||||
|
|
20
man/netns.1
20
man/netns.1
|
@ -1,10 +1,10 @@
|
|||
.\" DO NOT MODIFY THIS FILE! It was generated by help2man 1.40.4.
|
||||
.TH NETNS "1" "2014-08-06" "NETNS" "User Commands"
|
||||
.\" DO NOT MODIFY THIS FILE! It was generated by help2man 1.47.3.
|
||||
.TH NETNS "1" "June 2019" "CORE" "User Commands"
|
||||
.SH NAME
|
||||
netns \- run commands within a network namespace
|
||||
netns \- manual page for netns version 5.3.0
|
||||
.SH SYNOPSIS
|
||||
.B netns
|
||||
[\fI-h|-V\fR] [\fI-w\fR] \fI-- command \fR[\fIargs\fR...]
|
||||
[\fI\,-h|-V\/\fR] [\fI\,-w\/\fR] \fI\,-- command \/\fR[\fI\,args\/\fR...]
|
||||
.SH DESCRIPTION
|
||||
Run the specified command in a new network namespace.
|
||||
.SH OPTIONS
|
||||
|
@ -17,15 +17,3 @@ show version number and exit
|
|||
.TP
|
||||
\fB\-w\fR
|
||||
wait for command to complete (useful for interactive commands)
|
||||
.SH "SEE ALSO"
|
||||
.BR core-gui(1),
|
||||
.BR core-daemon(1),
|
||||
.BR coresendmsg(1),
|
||||
.BR unshare(1),
|
||||
.BR vcmd(1),
|
||||
.BR vnoded(1)
|
||||
.SH BUGS
|
||||
Report bugs to
|
||||
.BI https://github.com/coreemu/core/issues
|
||||
|
||||
|
||||
|
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Reference in a new issue