From f04b3f589d7800653b9fb3ad08131f4edd2e2828 Mon Sep 17 00:00:00 2001 From: "A. Wilcox" Date: Wed, 18 Mar 2020 21:34:25 -0500 Subject: hscript: Support creation of /etc/network/interfaces --- hscript/network.cc | 94 +++++++++++++++---- hscript/script.cc | 2 + hscript/script_e.cc | 108 ++++++++++++++++------ tests/fixtures/0227-netconfigtype-eni.installfile | 4 +- tests/spec/simulator_spec.rb | 10 ++ 5 files changed, 169 insertions(+), 49 deletions(-) diff --git a/hscript/network.cc b/hscript/network.cc index a7b2bee..445ccbb 100644 --- a/hscript/network.cc +++ b/hscript/network.cc @@ -294,47 +294,107 @@ bool NetAddress::validate() const { return true; /* LCOV_EXCL_LINE */ } -bool NetAddress::execute() const { - output_info("installfile:" + std::to_string(this->lineno()), - "netaddress: adding configuration for " + _iface); - - std::ofstream config("/tmp/horizon/netifrc/config_" + this->iface(), +bool execute_address_netifrc(const NetAddress *addr) { + std::ofstream config("/tmp/horizon/netifrc/config_" + addr->iface(), std::ios_base::app); if(!config) { - output_error("installfile:" + std::to_string(this->lineno()), + output_error("installfile:" + std::to_string(addr->lineno()), "netaddress: couldn't write network configuration for " - + this->iface()); + + addr->iface()); return false; } - switch(this->type()) { - case DHCP: + switch(addr->type()) { + case NetAddress::DHCP: config << "dhcp"; break; - case SLAAC: + case NetAddress::SLAAC: /* automatically handled by netifrc */ break; - case Static: - config << this->address() << "/" << std::to_string(this->prefix()) + case NetAddress::Static: + config << addr->address() << "/" << std::to_string(addr->prefix()) << std::endl; break; } - if(!this->gateway().empty()) { - std::ofstream route("/tmp/horizon/netifrc/routes_" + this->iface(), + if(!addr->gateway().empty()) { + std::ofstream route("/tmp/horizon/netifrc/routes_" + addr->iface(), std::ios_base::app); if(!route) { - output_error("installfile:" + std::to_string(this->lineno()), + output_error("installfile:" + std::to_string(addr->lineno()), "netaddress: couldn't write route configuration for " - + this->iface()); + + addr->iface()); return false; } - route << "default via " << this->gateway() << std::endl; + route << "default via " << addr->gateway() << std::endl; } return true; } +bool execute_address_eni(const NetAddress *addr) { + std::ofstream config("/tmp/horizon/eni/" + addr->iface(), + std::ios_base::app); + if(!config) { + output_error("installfile:" + std::to_string(addr->lineno()), + "netaddress: couldn't write network configuration for " + + addr->iface()); + return false; + } + + switch(addr->type()) { + case NetAddress::DHCP: + config << "iface " << addr->iface() << " inet dhcp" << std::endl; + break; + case NetAddress::SLAAC: + config << "iface " << addr->iface() << " inet6 manual" << std::endl + << "\tpre-up echo 1 > /proc/sys/net/ipv6/conf/" << addr->iface() + << "/accept_ra" << std::endl; + break; + case NetAddress::Static: + config << "iface " << addr->iface() << " "; + + if(addr->address().find(':') != std::string::npos) { + /* Disable SLAAC when using static IPv6 addressing. */ + config << "inet6 static" << std::endl + << "\tpre-up echo 0 > /proc/sys/net/ipv6/conf/" + << addr->iface() << "/accept_ra" << std::endl;; + } else { + config << "inet static" << std::endl; + } + + config << "\taddress " << addr->address() << std::endl + << "\tnetmask " << std::to_string(addr->prefix()) << std::endl; + + if(!addr->gateway().empty()) { + config << "\tgateway " << addr->gateway() << std::endl; + } + } + + return true; +} + +bool NetAddress::execute() const { + output_info("installfile:" + std::to_string(this->lineno()), + "netaddress: adding configuration for " + _iface); + + NetConfigType::ConfigSystem system = NetConfigType::Netifrc; + + const Key *key = script->getOneValue("netconfigtype"); + if(key != nullptr) { + const NetConfigType *nct = static_cast(key); + system = nct->type(); + } + + switch(system) { + case NetConfigType::Netifrc: + default: + return execute_address_netifrc(this); + case NetConfigType::ENI: + return execute_address_eni(this); + } +} + Key *Nameserver::parseFromData(const std::string &data, int lineno, int *errors, int *, const Script *script) { diff --git a/hscript/script.cc b/hscript/script.cc index fd9fc5a..7d1d4e2 100644 --- a/hscript/script.cc +++ b/hscript/script.cc @@ -334,6 +334,8 @@ void Script::setTargetDirectory(const std::string &dir) { const Keys::Key *Script::getOneValue(std::string name) const { if(name == "network") { return this->internal->network.get(); + } else if(name == "netconfigtype") { + return this->internal->netconfig.get(); } else if(name == "hostname") { return this->internal->hostname.get(); } else if(name == "arch") { diff --git a/hscript/script_e.cc b/hscript/script_e.cc index 2b9f8f4..14d997d 100644 --- a/hscript/script_e.cc +++ b/hscript/script_e.cc @@ -58,6 +58,8 @@ void maybe_create_icon_dir(ScriptOptions opts, const std::string &target) { bool Script::execute() const { bool success; error_code ec; + std::string netconf_file; + NetConfigType::ConfigSystem netconfsys = NetConfigType::Netifrc; std::set ifaces; const std::string targ_etc(targetDirectory() + "/etc"); @@ -220,6 +222,10 @@ bool Script::execute() const { /**************** NETWORK ****************/ output_step_start("net"); + if(internal->netconfig) { + netconfsys = internal->netconfig->type(); + } + if(!this->internal->ssids.empty()) { std::ofstream wpao("/tmp/horizon/wpa_supplicant.conf", std::ios_base::trunc); @@ -274,9 +280,23 @@ bool Script::execute() const { bool dhcp = false; if(!internal->addresses.empty()) { - fs::path netifrc_dir("/tmp/horizon/netifrc"); - if(!fs::exists(netifrc_dir) && - !fs::create_directory(netifrc_dir, ec)) { + fs::path conf_dir, targ_netconf_dir, targ_netconf_file; + switch(netconfsys) { + case NetConfigType::Netifrc: + conf_dir = fs::path("/tmp/horizon/netifrc"); + netconf_file = "/etc/conf.d/net"; + targ_netconf_dir = fs::path(targ_etc + "/conf.d"); + break; + case NetConfigType::ENI: + conf_dir = fs::path("/tmp/horizon/eni"); + netconf_file = "/etc/network/interfaces"; + targ_netconf_dir = fs::path(targ_etc + "/network"); + break; + } + targ_netconf_file = fs::path(targetDirectory() + netconf_file); + + if(!fs::exists(conf_dir) && + !fs::create_directory(conf_dir, ec)) { output_error("internal", "cannot create temporary directory", ec.message()); } @@ -292,36 +312,55 @@ bool Script::execute() const { } std::ostringstream conf; - for(auto &&var_dent : fs::directory_iterator(netifrc_dir)) { - const std::string variable(var_dent.path().filename().string()); - std::ifstream contents(var_dent.path().string()); + + if(netconfsys == NetConfigType::ENI) { + conf << "auto lo" << std::endl + << "iface lo inet loopback" << std::endl << std::endl; + } + + for(auto &&varfile : fs::directory_iterator(conf_dir)) { + std::ifstream contents(varfile.path().string()); if(!contents) { - output_error("internal", "cannot read network configuration"); + output_error("internal", "cannot read network " + "configuration"); EXECUTE_FAILURE("net-internal"); continue; } - conf << variable << "=\""; - if(contents.rdbuf()->in_avail()) conf << contents.rdbuf(); - conf << "\"" << std::endl; + + switch(netconfsys) { + case NetConfigType::Netifrc: { + const std::string variable(varfile.path().filename().string()); + conf << variable << "=\""; + if(contents.rdbuf()->in_avail()) conf << contents.rdbuf(); + conf << "\"" << std::endl; + break; + } + case NetConfigType::ENI: { + const std::string iface(varfile.path().filename().string()); + conf << "auto " << iface << std::endl; + if(contents.rdbuf()->in_avail()) conf << contents.rdbuf(); + conf << std::endl; + break; + } + } } if(opts.test(Simulate)) { - std::cout << "mkdir -p " << targ_etc << "/conf.d" << std::endl; - std::cout << "cat >>/" << targ_etc << "/conf.d/net <<- NETCONF_EOF" + std::cout << "mkdir -p " << targ_netconf_dir << std::endl; + std::cout << "cat >>" << targ_netconf_file << " <<- NETCONF_EOF" << std::endl << conf.str() << std::endl << "NETCONF_EOF" << std::endl; } #ifdef HAS_INSTALL_ENV else { - if(!fs::exists(targ_etc + "/conf.d")) { - fs::create_directory(targ_etc + "/conf.d", ec); + if(!fs::exists(targ_netconf_dir)) { + fs::create_directory(targ_netconf_dir, ec); if(ec) { - output_error("internal", "could not create /etc/conf.d " - "directory", ec.message()); + output_error("internal", "could not create network " + "configuration directory", ec.message()); } } - std::ofstream conf_file(targ_etc + "/conf.d/net", - std::ios_base::app); + std::ofstream conf_file(targ_netconf_file, std::ios_base::app); if(!conf_file) { output_error("internal", "cannot save network configuration " "to target"); @@ -397,8 +436,8 @@ bool Script::execute() const { << "/etc/wpa_supplicant/wpa_supplicant.conf" << std::endl; } - std::cout << "cp " << targ_etc << "/conf.d/net /etc/conf.d/net" - << std::endl; + std::cout << "cp " << targetDirectory() << netconf_file << " " + << netconf_file << std::endl; if(!internal->nses.empty()) { std::cout << "cp " << targ_etc << "/resolv.conf* /etc/" << std::endl; @@ -416,7 +455,7 @@ bool Script::execute() const { EXECUTE_FAILURE("network"); } } - fs::copy_file(targ_etc + "/conf.d/net", "/etc/conf.d/net", + fs::copy_file(targetDirectory() + netconf_file, netconf_file, fs_overwrite, ec); if(ec) { output_error("internal", "cannot use networking configuration " @@ -424,17 +463,26 @@ bool Script::execute() const { EXECUTE_FAILURE("network"); return false; } - for(auto &iface : ifaces) { - fs::create_symlink("/etc/init.d/net.lo", - "/etc/init.d/net." + iface, ec); - if(ec && ec.value() != EEXIST) { - output_error("internal", "could not use networking on " - + iface, ec.message()); - EXECUTE_FAILURE("network"); - } else { - run_command("service", {"net." + iface, "start"}); + + switch(netconfsys) { + case NetConfigType::Netifrc: + for(auto &iface : ifaces) { + fs::create_symlink("/etc/init.d/net.lo", + "/etc/init.d/net." + iface, ec); + if(ec && ec.value() != EEXIST) { + output_error("internal", "could not use networking on " + + iface, ec.message()); + EXECUTE_FAILURE("network"); + } else { + run_command("service", {"net." + iface, "start"}); + } } + break; + case NetConfigType::ENI: + run_command("/etc/init.d/networking", {"restart"}); + break; } + if(!internal->nses.empty()) { if(dhcp) { fs::copy_file(targ_etc + "/resolv.conf.head", diff --git a/tests/fixtures/0227-netconfigtype-eni.installfile b/tests/fixtures/0227-netconfigtype-eni.installfile index 2eb6712..e5beb22 100644 --- a/tests/fixtures/0227-netconfigtype-eni.installfile +++ b/tests/fixtures/0227-netconfigtype-eni.installfile @@ -1,8 +1,8 @@ -hostname horizon-netifrc-test +hostname horizon-eni-test rootpw $6$gumtLGmHwOVIRpQR$2M9PUO24hy5mofzWWf9a.YLbzOgOlUby1g0hDj.wG67E2wrrvys59fq02PPdxBdbgkLZFtjfEx6MHZwMBamwu/ network true netconfigtype eni -netaddress eth0 dhcp +netaddress eth0 static 192.168.1.2 24 192.168.1.1 netaddress eth0 slaac nameserver 9.9.9.10 diskid /dev/sda QEMU_HARDDISK diff --git a/tests/spec/simulator_spec.rb b/tests/spec/simulator_spec.rb index 66f1d8f..9d41356 100644 --- a/tests/spec/simulator_spec.rb +++ b/tests/spec/simulator_spec.rb @@ -213,6 +213,16 @@ printf '%s\\t%s\\t%s\\t%s\\t0\\t0\\n' /dev/gwyn/source /usr/src auto noatime >> run_simulate expect(last_command_started.stdout).to include("ln -s /etc/init.d/net.lo /target/etc/init.d/net.eth0") end + it "configures IPv4 addressing correctly with eni" do + use_fixture '0227-netconfigtype-eni.installfile' + run_simulate + expect(last_command_started.stdout).to include("auto eth0\niface eth0 inet static") + end + it "configures IPv6 SLAAC addressing correctly with eni" do + use_fixture '0227-netconfigtype-eni.installfile' + run_simulate + expect(last_command_started.stdout).to include("iface eth0 inet6 manual") + end end context "simulating 'nameserver' execution" do it "configures nameservers correctly" do -- cgit v1.2.3-60-g2f50