diff --git a/examples/LiveUpdate/config.json b/examples/LiveUpdate/config.json index 240ab9efae..44b02e3db3 100644 --- a/examples/LiveUpdate/config.json +++ b/examples/LiveUpdate/config.json @@ -1,5 +1,11 @@ { "net" : [ - ["10.0.0.42", "255.255.255.0", "10.0.0.1"] + { + "iface": 0, + "config": "dhcp-with-fallback", + "address": "10.0.0.42", + "netmask": "255.255.255.0", + "gateway": "10.0.0.1" + } ] } diff --git a/examples/STREAM/README.md b/examples/STREAM/README.md index 93fabd400e..d52503fd07 100644 --- a/examples/STREAM/README.md +++ b/examples/STREAM/README.md @@ -1,11 +1,8 @@ ### STREAM: Sustainable Memory Bandwidth in High Performance Computers +Using Qemu and the IncludeOS boot program: ``` -mkdir build -cd build -cmake .. -make -../run.sh stream_example +boot . ``` Output should show estimated memory bandwidth inside virtual machine. diff --git a/examples/STREAM/vm.json b/examples/STREAM/vm.json index 1aa86f54af..5b7323a45c 100644 --- a/examples/STREAM/vm.json +++ b/examples/STREAM/vm.json @@ -1,3 +1,3 @@ { - "mem" : 1024 + "mem" : 256 } diff --git a/examples/acorn/config.json b/examples/acorn/config.json new file mode 100644 index 0000000000..44b02e3db3 --- /dev/null +++ b/examples/acorn/config.json @@ -0,0 +1,11 @@ +{ + "net" : [ + { + "iface": 0, + "config": "dhcp-with-fallback", + "address": "10.0.0.42", + "netmask": "255.255.255.0", + "gateway": "10.0.0.1" + } + ] +} diff --git a/examples/acorn/disk1/public/app/js/services/cpusage.js b/examples/acorn/disk1/public/app/js/services/cpusage.js index b65cfacfc1..52490ed891 100644 --- a/examples/acorn/disk1/public/app/js/services/cpusage.js +++ b/examples/acorn/disk1/public/app/js/services/cpusage.js @@ -3,10 +3,9 @@ angular.module('acornWebApp') .factory('CPUsage', function() { // CPU usage chart - var date = new Date(); - var total_data = ['total']; + var time_data = ['x', new Date()]; + var idle_data = ['idle']; var active_data = ['active']; - var time_data = ['x', date]; var cpu_usage_chart = {}; @@ -21,17 +20,19 @@ angular.module('acornWebApp') x: 'x', columns: [ time_data, - total_data, + idle_data, active_data ], colors: { - total: '#3A8BF1', + idle: '#3A8BF1', active: '#F87E0C' }, types: { - total: 'area-spline', - active: 'area-spline' - // 'line', 'spline', 'step', 'area', 'area-step' are also available to stack + idle: 'area', + active: 'area' + }, + area : { + zerobased: true } }, axis: { @@ -50,12 +51,12 @@ angular.module('acornWebApp') }, y: { label: { - text: 'cycles', + text: 'percent', position: 'outer-middle' }, tick: { format: function (d) { - return d + " mill."; + return d + " %"; } } } @@ -68,35 +69,23 @@ angular.module('acornWebApp') }; CPUsage.prototype.update = function(usage) { - // Showing interval in number of seconds - var interval = usage.interval / 1000000; - if(total_data.length > 20) { + if(idle_data.length > 20) { // Remove second element in each array (first element is name) - total_data.splice(1, 1); - active_data.splice(1, 1); time_data.splice(1, 1); + idle_data.splice(1, 1); + active_data.splice(1, 1); } - var total = usage.total; - var halt = usage.halt; - var active = total - halt; - - // Showing cycles in millions - total /= 1000000; - active /= 1000000; - - var d = new Date(); - - total_data.push(total.toFixed(3)); - active_data.push(active.toFixed(3)); - time_data.push(d); + time_data.push(new Date()); + idle_data.push(usage.idle.toFixed(3)); + active_data.push(usage.active.toFixed(3)); - cpu_usage_chart.axis.labels({x: 'CPU data updated at an interval of ' + interval + ((interval > 1) ? ' seconds' : ' second')}); + cpu_usage_chart.axis.labels({x: 'CPU usage over time'}); cpu_usage_chart.load({ columns: [ time_data, - total_data, + idle_data, active_data ] }); diff --git a/examples/acorn/service.cpp b/examples/acorn/service.cpp index 2707a1b7e1..09a4ffbdf2 100644 --- a/examples/acorn/service.cpp +++ b/examples/acorn/service.cpp @@ -36,7 +36,7 @@ Disk_ptr disk; #include #include -void Service::start() +static void start_acorn(net::Inet& inet) { /** SETUP LOGGER */ const int LOGBUFFER_LEN = 1024*16; @@ -45,7 +45,8 @@ void Service::start() logger_->flush(); logger_->log("LUL\n"); - OS::add_stdout([] (const char* data, size_t len) { + OS::add_stdout( + [] (const char* data, size_t len) { // append timestamp auto entry = "[" + isotime::now() + "]" + std::string{data, len}; logger_->log(entry); @@ -55,28 +56,10 @@ void Service::start() // init the first legit partition/filesystem disk->init_fs( - [] (fs::error_t err, auto& fs) + [&inet] (fs::error_t err, auto& fs) { if (err) panic("Could not mount filesystem...\n"); - /** IP STACK SETUP **/ - // Bring up IPv4 stack on network interface 0 - auto& stack = net::Inet4::ifconfig(5.0, - [] (bool timeout) { - printf("DHCP resolution %s\n", timeout ? "failed" : "succeeded"); - if (timeout) - { - /** - * Default Manual config. Can only be done after timeout to work - * with DHCP offers going to unicast IP (e.g. in GCE) - **/ - net::Inet4::stack().network_config({ 10,0,0,42 }, // IP - { 255,255,255,0 }, // Netmask - { 10,0,0,1 }, // Gateway - { 8,8,8,8 }); // DNS - } - }); - // only works with synchronous disks (memdisk) list_static_content(disk); @@ -125,8 +108,8 @@ void Service::start() dashboard_->add(dashboard::Status::instance()); // Construct component dashboard_->construct(Statman::get()); - dashboard_->construct(stack.tcp()); - dashboard_->construct(500ms); + dashboard_->construct(inet.tcp()); + dashboard_->construct(); dashboard_->construct(*logger_, static_cast(50)); // Add Dashboard routes to "/api/dashboard" @@ -154,7 +137,7 @@ void Service::start() /** SERVER SETUP **/ - server_ = std::make_unique(stack.tcp()); + server_ = std::make_unique(inet.tcp()); // set routes and start listening server_->set_routes(router).listen(80); @@ -178,3 +161,15 @@ void Service::start() }); // < disk } + +void Service::start() +{ + auto& inet = net::Super_stack::get(0); + if (not inet.is_configured()) + { + inet.on_config(start_acorn); + } + else { + start_acorn(inet); + } +} diff --git a/examples/demo_service/CMakeLists.txt b/examples/demo_service/CMakeLists.txt index 267c8cb98d..79ee12f99b 100644 --- a/examples/demo_service/CMakeLists.txt +++ b/examples/demo_service/CMakeLists.txt @@ -5,7 +5,6 @@ if (NOT DEFINED ENV{INCLUDEOS_PREFIX}) set(ENV{INCLUDEOS_PREFIX} /usr/local) endif() include($ENV{INCLUDEOS_PREFIX}/includeos/pre.service.cmake) - project (demo_service) # Human-readable name of your service @@ -19,19 +18,9 @@ set(SOURCES service.cpp # ...add more here ) -# -# Service CMake options -# (uncomment to enable) -# - -# MISC: - # To add your own include paths: # set(LOCAL_INCLUDES ".") -# Adding memdisk (expects my.disk to exist in current dir): -# set(MEMDISK ${CMAKE_SOURCE_DIR}/my.disk) - # DRIVERS / PLUGINS: if ("$ENV{PLATFORM}" STREQUAL "x86_solo5") @@ -47,10 +36,9 @@ else() endif() set(PLUGINS - # syslogd # Syslog over UDP + autoconf # configuration from config.json # ...others ) - # include service build script include($ENV{INCLUDEOS_PREFIX}/includeos/post.service.cmake) diff --git a/examples/demo_service/README.md b/examples/demo_service/README.md index aedf456e70..3b904c641c 100644 --- a/examples/demo_service/README.md +++ b/examples/demo_service/README.md @@ -1,11 +1,10 @@ ### IncludeOS Demo Service +To start, using boot: ``` -mkdir build -cd build -cmake .. -make -../run.sh IncludeOS_example +boot . ``` This demo-service should start an instance of IncludeOS that brings up a minimal web service on port 80 with static content. + +The default static IP is 10.0.0.42, unless running on a network with DHCP. diff --git a/examples/demo_service/config.json b/examples/demo_service/config.json new file mode 100644 index 0000000000..44b02e3db3 --- /dev/null +++ b/examples/demo_service/config.json @@ -0,0 +1,11 @@ +{ + "net" : [ + { + "iface": 0, + "config": "dhcp-with-fallback", + "address": "10.0.0.42", + "netmask": "255.255.255.0", + "gateway": "10.0.0.1" + } + ] +} diff --git a/examples/demo_service/service.cpp b/examples/demo_service/service.cpp index 10a845f3d3..6c6155219d 100644 --- a/examples/demo_service/service.cpp +++ b/examples/demo_service/service.cpp @@ -79,21 +79,12 @@ http::Response handle_request(const http::Request& req) return res; } -void Service::start(const std::string&) +void Service::start() { - // DHCP on interface 0 - printf("*** Waiting up to 10 sec. for DHCP... ***\n"); - auto& inet = net::Inet4::ifconfig(5.0, [](bool timeout) { - if (timeout) { - printf("*** Falling back to static network config ***\n"); - // static IP in case DHCP fails - net::Inet4::stack().network_config( - { 10,0,0,42 }, // IP - { 255,255,255,0 }, // Netmask - { 10,0,0,1 }, // Gateway - { 10,0,0,1 }); // DNS - } - }); + // Get the first IP stack + // It should have configuration from config.json + auto& inet = net::Super_stack::get(0); + // Print some useful netstats every 30 secs Timers::periodic(5s, 30s, [&inet] (uint32_t) { diff --git a/examples/tcp/CMakeLists.txt b/examples/tcp/CMakeLists.txt index f2ae07f175..1e3f8b72d4 100644 --- a/examples/tcp/CMakeLists.txt +++ b/examples/tcp/CMakeLists.txt @@ -18,13 +18,6 @@ set(SOURCES service.cpp # ...add more here ) -# -# Service CMake options -# (uncomment to enable) -# - -# MISC: - # To add your own include paths: # set(LOCAL_INCLUDES ".") diff --git a/examples/tcp/config.json b/examples/tcp/config.json new file mode 100644 index 0000000000..44b02e3db3 --- /dev/null +++ b/examples/tcp/config.json @@ -0,0 +1,11 @@ +{ + "net" : [ + { + "iface": 0, + "config": "dhcp-with-fallback", + "address": "10.0.0.42", + "netmask": "255.255.255.0", + "gateway": "10.0.0.1" + } + ] +} diff --git a/examples/tcp/service.cpp b/examples/tcp/service.cpp index 47e9955370..333dd7332c 100644 --- a/examples/tcp/service.cpp +++ b/examples/tcp/service.cpp @@ -51,14 +51,9 @@ void handle_python_on_read(Connection_ptr client, const std::string& response) { client->write(response); } -void Service::start(const std::string&) +void Service::start() { - // Static IP configuration will get overwritten by DHCP, if found - auto& inet = net::Inet4::ifconfig<0>(10); - inet.network_config({ 10,0,0,42 }, // IP - { 255,255,255,0 }, // Netmask - { 10,0,0,1 }, // Gateway - { 8,8,8,8 }); // DNS + auto& inet = net::Super_stack::get(0); // Set up a TCP server on port 80 auto& server = inet.tcp().listen(80); diff --git a/examples/websocket/config.json b/examples/websocket/config.json index d0d39a07b1..5e79a3a34f 100644 --- a/examples/websocket/config.json +++ b/examples/websocket/config.json @@ -1,5 +1,13 @@ { "net": [ - ["10.0.0.42", "255.255.255.0", "10.0.0.1"] + { + "iface": 0, + "config": "static", + "address": "10.0.0.42", + "netmask": "255.255.255.0", + "gateway": "10.0.0.1" + } ] } + + diff --git a/lib/mana/include/mana/components/dashboard/components/cpusage.hpp b/lib/mana/include/mana/components/dashboard/components/cpusage.hpp index 7955e915d7..7342869cd4 100644 --- a/lib/mana/include/mana/components/dashboard/components/cpusage.hpp +++ b/lib/mana/include/mana/components/dashboard/components/cpusage.hpp @@ -20,73 +20,43 @@ #define DASHBOARD_COMPONENTS_CPUSAGE_HPP #include "../component.hpp" - #include #include +#include namespace mana { namespace dashboard { class CPUsage : public Component { - public: - - CPUsage(Timers::duration_t interval) - : interval_{interval}, - timer_id_{Timers::periodic(interval, interval, {this, &CPUsage::update_values})} - {} - - ~CPUsage() { - Timers::stop(timer_id_); - } + CPUsage() = default; + ~CPUsage() = default; std::string key() const override { return "cpu_usage"; } - void serialize(Writer& writer) override { - writer.StartObject(); - - writer.Key("halt"); - - if(new_halt_ > old_halt_) - serialized_halt_ = new_halt_ - old_halt_; - - writer.Uint64(serialized_halt_); + void serialize(Writer& writer) override + { + uint64_t tdiff = ::StackSampler::samples_total() - last_total; + last_total = ::StackSampler::samples_total(); + uint64_t adiff = ::StackSampler::samples_asleep() - last_asleep; + last_asleep = ::StackSampler::samples_asleep(); - writer.Key("total"); + double asleep = 1.0; + if (tdiff > 0) asleep = adiff / (double) tdiff; - if(new_total_ > old_total_) - serialized_total_ = new_total_ - old_total_; - - writer.Uint64(serialized_total_); - - writer.Key("interval"); - writer.Double(interval_.count()); + writer.StartObject(); + writer.Key("idle"); + writer.Uint64(asleep * 100.0); + writer.Key("active"); + writer.Uint64((1.0 - asleep) * 100.0); writer.EndObject(); } private: - uint64_t old_halt_ = 0; - uint64_t new_halt_ = 0; - uint64_t old_total_ = 0; - uint64_t new_total_ = 0; - - uint64_t serialized_halt_ = 0; - uint64_t serialized_total_ = 0; - - Timers::duration_t interval_; - Timers::id_t timer_id_; - - void update_values(Timers::id_t) { - uint64_t temp_halt = new_halt_; - uint64_t temp_total = new_total_; - - new_halt_ = OS::cycles_asleep(); - new_total_ = OS::cycles_since_boot(); - old_halt_ = temp_halt; - old_total_ = temp_total; - } + uint64_t last_asleep = 0; + uint64_t last_total = 0; }; } // < namespace dashboard diff --git a/src/arch/x86_64/linker.ld b/src/arch/x86_64/linker.ld index 283acc1fb2..5061e3999e 100644 --- a/src/arch/x86_64/linker.ld +++ b/src/arch/x86_64/linker.ld @@ -187,14 +187,7 @@ SECTIONS LONG (0); } - /* - * XXX: We need this padding because in solo5 initialization some variables - * in the bss are set to non-zero values. So, without the padding the ELFs - * symbol table would get modified. - */ - . += 0x1000000; - - .bss : + .bss ALIGN(0x1000) : { _BSS_START_ = .; *(.bss .bss.* .gnu.linkonce.b.*) @@ -208,5 +201,4 @@ SECTIONS PROVIDE (end = .); PROVIDE (_ELF_END_ = .); PROVIDE (_LOAD_END_ = .); - } diff --git a/src/net/configure.cpp b/src/net/configure.cpp index 6cdf878f58..2a71cee8b1 100644 --- a/src/net/configure.cpp +++ b/src/net/configure.cpp @@ -29,24 +29,17 @@ using Addresses = std::vector; template Addresses parse_iface(T& obj) { - Expects(obj.IsArray()); + Expects(obj.HasMember("address")); + Expects(obj.HasMember("netmask")); + Expects(obj.HasMember("gateway")); - // Iface can either be empty: [] or contain 3 to 4 IP's (see Inet static config) - if(not obj.Empty()) - { - Expects(obj.Size() >= 3 and obj.Size() <= 4); - - Addresses addresses; - - for(auto& addr : obj.GetArray()) - { - addresses.emplace_back(std::string{addr.GetString()}); - } - - return addresses; - } + ip4::Addr address{obj["address"].GetString()}; + ip4::Addr netmask{obj["netmask"].GetString()}; + ip4::Addr gateway{obj["gateway"].GetString()}; + ip4::Addr dns = (not obj.HasMember("dns")) ? gateway : ip4::Addr{obj["dns"].GetString()}; - return {}; + Addresses addresses{address, netmask, gateway, dns}; + return addresses; } inline void config_stack(Inet& stack, const Addresses& addrs) @@ -69,23 +62,45 @@ void configure(const rapidjson::Value& net) Expects(net.IsArray() && "Member net is not an array"); - int N = 0; - for(auto& val : net.GetArray()) + auto configs = net.GetArray(); + if(configs.Size() > Super_stack::inet().ip4_stacks().size()) + MYINFO("! WARNING: Found more configs than there are interfaces"); + // Iterate all interfaces in config + for(auto& val : configs) { - if(N >= static_cast(Super_stack::inet().ip4_stacks().size())) - break; + Expects(val.HasMember("iface") + && "Missing member iface - don't know which interface to configure"); + + auto N = val["iface"].GetInt(); auto& stack = Super_stack::get(N); - // static configs - if(val.IsArray()) + + // if config is not set, just ignore + if(not val.HasMember("config")) + continue; + + std::string method = val["config"].GetString(); + + if(method == "dhcp") + { + stack.negotiate_dhcp(5.0); + } + else if(method == "static") { config_stack(stack, parse_iface(val)); } - else if(val.IsString() and strcmp(val.GetString(), "dhcp") == 0) + else if(method == "dhcp-with-fallback") { - stack.negotiate_dhcp(5.0); + auto addresses = parse_iface(val); + auto static_cfg = [addresses, &stack] (bool timeout) + { + if(timeout) { + MYINFO("DHCP timeout (%s) - falling back to static configuration", stack.ifname().c_str()); + config_stack(stack, addresses); + } + }; + stack.negotiate_dhcp(5.0, static_cfg); } - ++N; } MYINFO("Configuration complete"); diff --git a/test/kernel/integration/LiveUpdate/config.json b/test/kernel/integration/LiveUpdate/config.json index 194ceffed1..60fa334855 100644 --- a/test/kernel/integration/LiveUpdate/config.json +++ b/test/kernel/integration/LiveUpdate/config.json @@ -1,5 +1,11 @@ { "net" : [ - ["10.0.0.49", "255.255.255.0", "10.0.0.1"] + { + "iface": 0, + "config": "static", + "address": "10.0.0.49", + "netmask": "255.255.255.0", + "gateway": "10.0.0.1" + } ] } diff --git a/test/net/integration/configure/config.json b/test/net/integration/configure/config.json index e60a3448b5..203e16d1d0 100644 --- a/test/net/integration/configure/config.json +++ b/test/net/integration/configure/config.json @@ -1,8 +1,34 @@ { "net": [ - ["10.0.0.42", "255.255.255.0", "10.0.0.1"], - ["10.0.0.43", "255.255.255.0", "10.0.0.1", "8.8.8.8"], - "none", - "dhcp" + { + "iface": 0, + "config": "static", + "address": "10.0.0.42", + "netmask": "255.255.255.0", + "gateway": "10.0.0.1" + }, + { + "iface": 1, + "config": "static", + "address": "10.0.0.43", + "netmask": "255.255.255.0", + "gateway": "10.0.0.1", + "dns": "8.8.8.8" + }, + { + "iface": 2, + "config": "none" + }, + { + "iface": 3, + "config": "dhcp" + }, + { + "iface": 5, + "config": "dhcp-with-fallback", + "address": "10.0.0.44", + "netmask": "255.255.255.0", + "gateway": "10.0.0.1" + } ] } diff --git a/test/net/integration/configure/service.cpp b/test/net/integration/configure/service.cpp index 9cf96fab04..0341ff3844 100644 --- a/test/net/integration/configure/service.cpp +++ b/test/net/integration/configure/service.cpp @@ -16,7 +16,6 @@ // limitations under the License. #include -#include #include #include @@ -25,14 +24,7 @@ void Service::start() using namespace net; auto& stacks = Super_stack::inet().ip4_stacks(); - CHECKSERT(stacks.size() == 5, "There is 5 interfaces"); - - const auto& cfg = Config::get(); - using namespace rapidjson; - Document doc; - doc.Parse(cfg.data()); - - net::configure(doc["net"]); + CHECKSERT(stacks.size() == 6, "There are 6 interfaces"); INFO("Test", "Verify eth0"); CHECKSERT(stacks[0] != nullptr, "eth0 is initialized"); @@ -56,7 +48,7 @@ void Service::start() CHECKSERT(stacks[2] != nullptr, "eth2 is initialized"); auto& eth2 = *stacks[2]; - const ip4::Addr EMPTY{0}; + static const ip4::Addr EMPTY{0}; CHECKSERT(eth2.ip_addr() == EMPTY, "IP address is 0.0.0.0"); CHECKSERT(eth2.netmask() == EMPTY, "Netmask is 0.0.0.0"); CHECKSERT(eth2.gateway() == EMPTY, "Gateway is 0.0.0.0"); @@ -68,5 +60,15 @@ void Service::start() INFO("Test", "Verify eth4"); CHECKSERT(stacks[4] == nullptr, "eth4 is uninitialized"); - printf("SUCCESS\n"); + auto& eth5 = *stacks[5]; + auto verify_eth5 = [](auto& eth5) { + INFO("Test", "Verify eth5"); + CHECKSERT(eth5.ip_addr() != EMPTY, "IP not empty (%s)", eth5.ip_addr().str().c_str()); + CHECKSERT(eth5.netmask() != EMPTY, "Netmask not empty (%s)", eth5.netmask().str().c_str()); + CHECKSERT(eth5.gateway() != EMPTY, "Gateway not empty (%s)", eth5.gateway().str().c_str()); + CHECKSERT(eth5.dns_addr() != EMPTY, "DNS addr not empty (%s)", eth5.dns_addr().str().c_str()); + printf("SUCCESS\n"); + }; + eth5.on_config(verify_eth5); + } diff --git a/test/net/integration/configure/vm.json b/test/net/integration/configure/vm.json index 9dbd6da319..b1492772b9 100644 --- a/test/net/integration/configure/vm.json +++ b/test/net/integration/configure/vm.json @@ -5,7 +5,8 @@ {"device" : "virtio", "mac" : "c0:01:0a:00:00:3a"}, {"device" : "virtio", "mac" : "c0:01:0a:00:00:4a"}, {"device" : "virtio", "mac" : "c0:01:0a:00:00:5a"}, - {"device" : "virtio", "mac" : "c0:01:0a:00:00:6a"} + {"device" : "virtio", "mac" : "c0:01:0a:00:00:6a"}, + {"device" : "virtio", "mac" : "c0:01:0a:00:00:7a"} ], "mem" : 256 }