├── .gitignore ├── Containerfile ├── package.json ├── README.md ├── LICENSE ├── test-single-subnet-mdns.js └── lib └── mininet.js /.gitignore: -------------------------------------------------------------------------------- 1 | *.img 2 | node_modules 3 | Makefile 4 | *.swp 5 | -------------------------------------------------------------------------------- /Containerfile: -------------------------------------------------------------------------------- 1 | FROM ubuntu:xenial 2 | RUN rm -f /etc/resolv.conf && echo '8.8.8.8' > /etc/resolv.conf 3 | RUN apt-get update 4 | RUN apt-get install -y git vim curl 5 | RUN curl -fs https://raw.githubusercontent.com/mafintosh/node-install/master/install | sh 6 | RUN node-install 8.9.1 7 | RUN npm install -g dat 8 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "dat-network-simulator", 3 | "version": "0.0.1", 4 | "description": "Mininet-based simulator for testing Dat", 5 | "scripts": { 6 | "test": "standard && tape test-single-subnet-mdns.js" 7 | }, 8 | "author": "Jim Pick", 9 | "license": "BSD-3-Clause", 10 | "dependencies": { 11 | "node-pty": "^0.7.3", 12 | "npm-execspawn": "^1.3.0", 13 | "tape": "^4.8.0" 14 | }, 15 | "devDependencies": { 16 | "standard": "^10.0.3" 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # dat-network-simulator 2 | 3 | Work-in-progress. 4 | 5 | This is an experiment to see how feasible it is to build a network 6 | simulation environment so we can do integrated testing of all of the 7 | parts of the Dat ecosystem. 8 | 9 | ## Parts of the simulator 10 | 11 | * Runs on Linux (I'm developing with Ubuntu 16.04) 12 | * tape - test runner 13 | * mininet - sets up complex virtual network topologies 14 | * mkbootstrap - build disk images for virtual machines (similar to Dockerfiles) 15 | * systemd-nspawn - a Linux virtual machine system based on KVM 16 | 17 | ## Progress 18 | 19 | It can currently set up a mininet network - the next step is to add some more 20 | library code to enable installing and running software and tests on the 21 | individual nodes. 22 | 23 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2017 Jim Pick. All rights reserved. 2 | Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: 3 | 4 | 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. 5 | 6 | 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. 7 | 8 | 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. 9 | 10 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 11 | -------------------------------------------------------------------------------- /test-single-subnet-mdns.js: -------------------------------------------------------------------------------- 1 | var test = require('tape') 2 | var execspawn = require('npm-execspawn') 3 | var mininet = require('./lib/mininet') 4 | 5 | function setup (cb) { 6 | buildImage(function (err) { 7 | if (err) fatal(err) 8 | console.log('starting mininet') 9 | mininet.start(function (err, nodes) { 10 | if (err) fatal(err) 11 | console.log('mininet started') 12 | cb(nodes) 13 | }) 14 | }) 15 | } 16 | 17 | function tests (nodes, cb) { 18 | test.onFinish(teardown(cb)) 19 | 20 | test('local network, 2 nodes, dat share and dat clone, mdns', function (t) { 21 | console.log('nodes', nodes) 22 | console.log('waiting 3 seconds') 23 | setTimeout(function () { 24 | t.ok(true, 'Bogus test') 25 | t.end() 26 | }, 3000) 27 | }) 28 | } 29 | 30 | function teardown (cb) { 31 | return function () { 32 | console.log('stopping mininet') 33 | mininet.stop(function (err) { 34 | if (err) fatal(err) 35 | console.log('mininet stopped') 36 | cb() 37 | }) 38 | } 39 | } 40 | 41 | function buildImage (cb) { 42 | var mc = execspawn('mkcontainer') 43 | mc.stderr.pipe(process.stderr) 44 | mc.stdout.pipe(process.stdout) 45 | mc.on('close', function (code) { 46 | if (code) return cb(new Error('mkcontainer failed')) 47 | cb() 48 | }) 49 | } 50 | 51 | function fatal (err) { 52 | console.error('Fatal error', err) 53 | process.exit(1) 54 | } 55 | 56 | setup(function (nodes) { 57 | tests(nodes, function () { 58 | console.log('Done.') 59 | }) 60 | }) 61 | -------------------------------------------------------------------------------- /lib/mininet.js: -------------------------------------------------------------------------------- 1 | var fs = require('fs') 2 | var cp = require('child_process') 3 | var pty = require('node-pty') 4 | 5 | exports.start = start 6 | exports.stop = stop 7 | 8 | var mn 9 | 10 | function start (cb) { 11 | var callbackUsed = false 12 | mn = cp.spawn('sudo', [ 'mn' ]) 13 | mn.stderr.pipe(process.stderr) 14 | mn.stdout.pipe(process.stdout) 15 | mn.on('close', function (code) { 16 | if (code) { 17 | console.error(`mn exited with code ${code}`) 18 | if (!callbackUsed) { 19 | callbackUsed = true 20 | cb(new Error('mn existed with non-zero code')) 21 | } 22 | } 23 | }) 24 | mn.stderr.on('data', function (data) { 25 | var line = data.toString() 26 | if (line.match(/Starting CLI/)) { 27 | if (!callbackUsed) { 28 | var nodes = getNodes() 29 | callbackUsed = true 30 | cb(null, nodes) 31 | } 32 | } 33 | }) 34 | } 35 | 36 | function stop (cb) { 37 | mn.on('close', function (code, signal) { 38 | console.log(`mn terminated due to signal ${signal}`) 39 | cb() 40 | }) 41 | // Allocate another pty because sudo will refuse to 42 | // send signals to subprocess otherwise 43 | var bash = pty.spawn('bash') 44 | bash.write(`sudo kill ${mn.pid}\r`) 45 | bash.write(`exit\r`) 46 | } 47 | 48 | function getNodes () { 49 | var procFiles = fs.readdirSync('/proc') 50 | var nodes = procFiles.reduce(function (acc, procFile) { 51 | var pid = Number.parseInt(procFile, 10) 52 | if (!pid) return acc 53 | var content = fs.readFileSync('/proc/' + procFile + '/cmdline', 'utf8') 54 | var regex = /^bash\u0000--norc\u0000-is\u0000mininet:(.*)\u0000$/ 55 | var match = content.match(regex) 56 | if (match) { 57 | var nodeName = match[1] 58 | acc[nodeName] = pid 59 | } 60 | return acc 61 | }, {}) 62 | return nodes 63 | } 64 | --------------------------------------------------------------------------------