I don't see that this was applied? The reason I ask is that we're using
cloud-init to set the IP addresses on multiple interfaces inside OSv
(to separate database traffic from the protocols). I have a secondary
patch to turn off dhcp that we also use.

I have my own network-module.cc, as I missed this one on the list. I've
just fixed up the cloud-init code in my tree to deal with the
libboost_program_options fall out and *then* I discover this code.

So how to proceed - guidance please? Charles' code is more complete
than mine, so I could try and merge that and get it working on the
current codebase - would that be acceptable?

Rick

On Mon, 2018-08-13 at 12:32 +0300, Nadav Har'El wrote:
> All looks good. Thanks.
> 
> 
> --
> Nadav Har'El
> [email protected]
> 
> On Tue, Aug 7, 2018 at 5:49 AM, Charles Myers <
> [email protected]> wrote:
> > https://cloudinit.readthedocs.io/en/latest/topics/network-config-format-v1.html
> > 
> > Currently only interface naming, static IP, routes and DNS are
> > supported.e
> > 
> > Signed-off-by: Charles Myers <[email protected]>
> > ---
> >  modules/cloud-init/Makefile          |   2 +-
> >  modules/cloud-init/main.cc           |  77 +++++---
> >  modules/cloud-init/network-module.cc | 369
> > +++++++++++++++++++++++++++++++++++
> >  modules/cloud-init/network-module.hh |  43 ++++
> >  4 files changed, 465 insertions(+), 26 deletions(-)
> >  create mode 100644 modules/cloud-init/network-module.cc
> >  create mode 100644 modules/cloud-init/network-module.hh
> > 
> > diff --git a/modules/cloud-init/Makefile b/modules/cloud-
> > init/Makefile
> > index daee8c3..ae45b24 100644
> > --- a/modules/cloud-init/Makefile
> > +++ b/modules/cloud-init/Makefile
> > @@ -13,7 +13,7 @@ INCLUDES += -I$(HTTPSERVER_API_DIR)
> > 
> >  # the build target executable:
> >  TARGET = cloud-init
> > -CPP_FILES := client.cc cloud-init.cc data-source.cc main.cc
> > template.cc cassandra-module.cc json.cc
> > +CPP_FILES := client.cc cloud-init.cc data-source.cc main.cc
> > template.cc network-module.cc cassandra-module.cc json.cc
> >  OBJ_FILES := $(addprefix obj/,$(CPP_FILES:.cc=.o))
> >  DEPS := $(OBJ_FILES:.o=.d)
> > 
> > diff --git a/modules/cloud-init/main.cc b/modules/cloud-
> > init/main.cc
> > index 5af23f0..a6674ef 100644
> > --- a/modules/cloud-init/main.cc
> > +++ b/modules/cloud-init/main.cc
> > @@ -9,6 +9,7 @@
> >  #include <sys/stat.h>
> >  #include <unistd.h>
> >  #include "cloud-init.hh"
> > +#include "network-module.hh"
> >  #include "files-module.hh"
> >  #include "server-module.hh"
> >  #include "cassandra-module.hh"
> > @@ -24,59 +25,84 @@ using namespace std;
> >  using namespace init;
> >  namespace po = boost::program_options;
> > 
> > -// config_disk() allows to use NoCloud VM configuration method -
> > see
> > +// config_disk() allows to use NoCloud and ConfigDrive VM
> > configuration method - see
> >  // 
> > http://cloudinit.readthedocs.io/en/0.7.9/topics/datasources/nocloud.html
> > .
> > +// 
> > http://cloudinit.readthedocs.io/en/0.7.9/topics/datasources/configdrive.html
> > +//
> >  // NoCloud method provides two files with cnfiguration data
> > (/user-data and
> >  // /meta-data) on a disk. The disk is required to have label
> > "cidata".
> >  // It can contain ISO9660 or FAT filesystem.
> >  //
> > +// ConfigDrive (version 2) method uses an unpartitioned VFAT or
> > ISO9660 disk 
> > +// with files.
> > +// openstack/
> > +//  - 2012-08-10/ or latest/
> > +//    - meta_data.json
> > +//    - user_data (not mandatory)
> > +//  - content/
> > +//    - 0000 (referenced content files)
> > +//    - 0001
> > +//    - ....
> > +// ec2
> > +//  - latest/
> > +//    - meta-data.json (not mandatory)
> > +//    - user-data
> > +//
> >  // config_disk() checks whether we have a second disk (/dev/vblkX)
> > with
> >  // ISO image, and if there is, it copies the configuration file
> > from
> > -// /user-data to the given file.
> > +// the user user-data file to the given file.
> >  // config_disk() returns true if it has successfully read the
> > configuration
> > -// into the requested file. It triest to get configuratioe from
> > first few
> > +// into the requested file. It tries to get configuration from
> > first few
> >  // vblk devices, namely vblk1 to vblk10.
> >  //
> >  // OSv implementation limitations:
> >  // The /meta-data file is currently ignored.
> >  // Only ISO9660 filesystem is supported.
> > -// The mandatory "cidata" volume label is not checked.
> > +// The mandatory "cidata" (NoCloud) and "config-2" (ConfigDrive)
> > volume labels are not checked.
> >  //
> >  // Example ISO image can be created by running
> >  // cloud-localds cloud-init.img cloud-init.yaml
> >  // The cloud-localds command is provided by cloud-utils package
> > (fedora).
> >  static bool config_disk(const char* outfile) {
> > +    const char * userdata_file_paths[] {
> > +        "/user-data",                  // NoCloud
> > +        "/openstack/latest/user_data", // ConfigDrive OpenStack
> > +        "/ec2/latest/user-data",       // ConfigDrive EC2
> > +    };
> > +
> >      for (int ii=1; ii<=10; ii++) {
> >          char disk[20];
> > -        char srcfile[] = "/user-data";
> >          struct stat sb;
> > -        int ret;
> > -        int app_ret = -1;
> > 
> >          snprintf(disk, sizeof(disk), "/dev/vblk%d", ii);
> > -        ret = stat(disk, &sb);
> > -        if (ret != 0) {
> > -            continue;
> > -        }
> > 
> > -        std::vector<std::string> cmd = {"/usr/bin/iso-read.so", "-
> > e", srcfile, "-o", outfile, disk};
> > -        osv::run(cmd[0], cmd, &app_ret);
> > -        if (app_ret != 0) {
> > -            debug("cloud-init: warning, %s exited with code %d (%s
> > is not ISO image?)\n", cmd[0], app_ret, disk);
> > +        if (stat(disk, &sb) != 0) {
> >              continue;
> >          }
> > -        ret = stat(outfile, &sb);
> > -        if (ret != 0) {
> > -            debug("cloud-init: disk %s, stat(%s) failed,
> > errno=%d\n", disk, outfile, errno);
> > -            continue;
> > -        }
> > -        if ((sb.st_mode & S_IFMT) != S_IFREG) {
> > -            debug("cloud-init: disk %s, %s is not a file\n", disk,
> > outfile);
> > -            return false;
> > +
> > +        debug("cloud-init: checking disk %s\n", disk);
> > +
> > +        for (auto & srcfile : userdata_file_paths) {
> > +            int app_ret = -1;
> > +            std::vector<std::string> cmd = {"/usr/bin/iso-
> > read.so", "-e", srcfile , "-o", outfile, disk};
> > +            osv::run(cmd[0], cmd, &app_ret);
> > +            if (app_ret != 0) {
> > +                debug("cloud-init: warning, %s exited with code %d
> > (%s is not ISO image?)\n", cmd[0], app_ret, disk);
> > +                continue;
> > +            }
> > +            if (stat(outfile, &sb) != 0) {
> > +                debug("cloud-init: stat(%s) failed, errno=%d\n",
> > outfile, errno);
> > +                continue;
> > +            }
> > +            if ((sb.st_mode & S_IFMT) != S_IFREG) {
> > +                debug("cloud-init: %s is not a file\n", outfile);
> > +                continue;
> > +            }
> > +            debug("cloud-init: copied file %s -> %s from ISO image
> > %s\n", srcfile, outfile, disk);
> > +            return true;
> >          }
> > -        debug("cloud-init: copied file %s -> %s from ISO image
> > %s\n", srcfile, outfile, disk);
> > -        return true;
> >      }
> > +
> >      return false;
> >  }
> > 
> > @@ -106,6 +132,7 @@ int main(int argc, char* argv[])
> >          osvinit init(config.count("skip-error") > 0,
> > config.count("force-probe") > 0);
> >          auto scripts = make_shared<script_module>();
> >          init.add_module(scripts);
> > +        init.add_module(make_shared<network_module>());
> >          init.add_module(make_shared<mount_module>());
> >          init.add_module(make_shared<hostname_module>());
> >          init.add_module(make_shared<files_module>());
> > diff --git a/modules/cloud-init/network-module.cc b/modules/cloud-
> > init/network-module.cc
> > new file mode 100644
> > index 0000000..cf73e52
> > --- /dev/null
> > +++ b/modules/cloud-init/network-module.cc
> > @@ -0,0 +1,369 @@
> > +/*
> > + * Copyright (C) 2014 Cloudius Systems, Ltd.
> > + *
> > + * This work is open source software, licensed under the terms of
> > the
> > + * BSD license as described in the LICENSE file in the top-level
> > directory.
> > + */
> > +
> > +#include <bsd/porting/networking.hh>
> > +#include <bsd/porting/route.h>
> > +#include <osv/debug.hh>
> > +
> > +#include "network-module.hh"
> > +#include <boost/asio/ip/address.hpp>
> > +#include <boost/algorithm/string.hpp>
> > +
> > +#include <ifaddrs.h>
> > +#include <api/netpacket/packet.h>
> > +
> > +#include "libc/network/__dns.hh"
> > +
> > +static int mac_str_to_addr(const std::string &str, uint8_t *addr)
> > +{
> > +    int values[6];
> > +
> > +    if (sscanf(str.c_str(), "%x:%x:%x:%x:%x:%x",
> > +               &values[0], &values[1], &values[2],
> > +               &values[3], &values[4], &values[5]) != 6) {
> > +        return -1;
> > +    }
> > +
> > +    for (int i=0; i<6; ++i) {
> > +        addr[i] = values[i];
> > +    }
> > +    return 0;
> > +}
> > +
> > +int if_rename(const std::string &ifname, const std::string
> > &new_ifname)
> > +{
> > +    struct ifreq req;
> > +    int sock;
> > +    int result = 0;
> > +
> > +    memset(&req, 0, sizeof(req));
> > +    strncpy(req.ifr_name, ifname.c_str(), sizeof(req.ifr_name)-1);
> > +    strncpy(req.ifr_newname, new_ifname.c_str(),
> > sizeof(req.ifr_newname)-1);
> > +
> > +    sock = socket(AF_INET, SOCK_DGRAM, 0);
> > +    if (sock < 0) {
> > +        debug("cloud-init: %s error.  socket() failed.  %s\n",
> > __FUNCTION__, strerror(errno));
> > +        return -1;
> > +    }
> > +
> > +    if (ioctl(sock, SIOCSIFNAME, &req) < 0) {
> > +        debug("cloud-init: %s error.  ioctl() SIOSIFNAME failed. 
> > %s\n", __FUNCTION__, strerror(errno));
> > +        result = -1;
> > +    }
> > +    close(sock);
> > +    return result;
> > +}
> > +
> > +int if_set_mac(const std::string &ifname, const std::string
> > &mac_addr)
> > +{
> > +    struct ifreq req;
> > +    int sock;
> > +    int result = 0;
> > +
> > +    memset(&req, 0, sizeof(req));
> > +    strncpy(req.ifr_name, ifname.c_str(), sizeof(req.ifr_name)-1);
> > +
> > +    if (mac_str_to_addr(mac_addr, (uint8_t*
> > )req.ifr_hwaddr.sa_data) < 0) {
> > +        debug("cloud-init: %s error.  Invalid MAC %s\n",
> > __FUNCTION__, mac_addr.c_str());
> > +        return -1;
> > +    }
> > +
> > +    sock = socket(AF_INET, SOCK_DGRAM, 0);
> > +    if (sock < 0) {
> > +        debug("cloud-init: %s error.  socket() failed.  %s\n",
> > __FUNCTION__, strerror(errno));
> > +        return -1;
> > +    }
> > +    if (ioctl(sock, SIOCSIFHWADDR, &req) < 0) {
> > +        debug("cloud-init: %s error. ioctl() SIOCSIFHWADDR
> > failed.  %s\n", __FUNCTION__, strerror(errno));
> > +        result = -1;
> > +    }
> > +    close(sock);
> > +    return result;
> > +}
> > +
> > +int if_find_name_by_mac(const std::string &mac_addr, std::string
> > &ifname)
> > +{
> > +    struct ifaddrs *ifaddr = NULL;
> > +    struct ifaddrs *ifa = NULL;
> > +    uint8_t hwaddr[6];
> > +    int result = -1;
> > +
> > +    if (mac_str_to_addr(mac_addr, hwaddr) < 0) {
> > +        debug("cloud-init: %s error.  Invalid MAC %s\n",
> > __FUNCTION__, mac_addr.c_str());
> > +        return -1;
> > +    }
> > +
> > +    if (getifaddrs(&ifaddr) == -1) {
> > +        debug("cloud-init: %s error.  getifaddrs() failed: %s\n",
> > __FUNCTION__, strerror(errno));
> > +        return -1;
> > +    }
> > +
> > +    for (ifa = ifaddr; ifa != NULL; ifa = ifa->ifa_next) {
> > +        if (!ifa->ifa_addr) continue;
> > +        if (ifa->ifa_addr->sa_family != AF_PACKET) continue;
> > +        struct sockaddr_ll *sa = (struct sockaddr_ll*)ifa-
> > >ifa_addr;
> > +        if (sa->sll_halen != 6)
> > +            continue;
> > +        if (memcmp(hwaddr, sa->sll_addr, 6) != 0)
> > +            continue;
> > +        ifname = ifa->ifa_name;
> > +        result = 0;
> > +        break;
> > +    }
> > +
> > +    freeifaddrs(ifaddr);
> > +    return result;
> > +}
> > +
> > +void network_module::configure_interface(const YAML::Node& node,
> > network_module::config_state& state)
> > +{
> > +    if (node["type"]) {
> > +        std::string type = node["type"].as<std::string>();
> > +        if (type == "physical") {
> > +            configure_physical_interface(node, state);
> > +        }
> > +    }
> > +}
> > +
> > +void network_module::configure_physical_interface(const
> > YAML::Node& node, network_module::config_state& state)
> > +{
> > +    std::string if_name;
> > +    std::string mac_addr;
> > +
> > +    if (node["name"]) {
> > +        if_name = node["name"].as<std::string>();
> > +        if (state.configured_interfaces.find(if_name) !=
> > state.configured_interfaces.end()) {
> > +            debug("cloud-init: %s error.  interface %s included in
> > configuration multiple times\n", __FUNCTION__, if_name.c_str());
> > +            return;
> > +        }
> > +    }
> > +    if (node["mac_address"]) {
> > +        // Find interface with MAC address
> > +        mac_addr = node["mac_address"].as<std::string>();
> > +        std::string fname;
> > +        if_find_name_by_mac(mac_addr, fname);
> > +        if (fname != "") {
> > +            // Found interface matching the MAC address
> > +            if (if_name != "" && fname != if_name) {
> > +                // Different name specified so try to rename the
> > interface
> > +                if (if_rename(fname, if_name) != 0) {
> > +                    debug("cloud-init: %s error.  Failed to rename
> > interface %s to %s\n", __FUNCTION__, fname.c_str(),
> > if_name.c_str());
> > +                    if_name = fname;
> > +                } else {
> > +                    // Update the physical interface list
> > +                    auto iter =
> > std::find(state.physical_interfaces.begin(),
> > state.physical_interfaces.end(), fname);
> > +                    if (iter != state.physical_interfaces.end()) {
> > +                        (*iter) = if_name;
> > +                    }
> > +                }
> > +            } else {
> > +                // No name specified so use existing interface
> > name
> > +                if_name = fname; 
> > +            }
> > +        } else {
> > +            // No interface matching the MAC address
> > +            if (if_name == "") {
> > +                debug("cloud-init: %s error.  Failed to find
> > interface with MAC address %s\n", __FUNCTION__, mac_addr.c_str());
> > +                return;
> > +            }
> > +#if 0 // Setting MAC is not supported
> > +            // Set interface MAC
> > +            if_set_mac(if_name, mac_addr);
> > +#endif
> > +        }
> > +    }
> > +    if (if_name == "") {
> > +        // Unable to find matching interface
> > +        for (auto &tmp_if : state.physical_interfaces) {
> > +            if (state.configured_interfaces.find(tmp_if) !=
> > state.configured_interfaces.end())
> > +                continue;
> > +            if_name = tmp_if;
> > +            break;
> > +        }
> > +        if (if_name == "") {
> > +            debug("cloud-init: %s error.  Interface name not
> > specified.\n", __FUNCTION__);
> > +            return;
> > +        }
> > +    } else {
> > +        // Validate interface name is okay to use
> > +        if (std::find(state.physical_interfaces.begin(),
> > state.physical_interfaces.end(), if_name) ==
> > state.physical_interfaces.end()) {
> > +            debug("cloud-init: %s error.  Interface %s not
> > found\n", __FUNCTION__, if_name.c_str());
> > +            return;
> > +        }
> > +    }
> > +
> > +    state.configured_interfaces.insert(if_name);
> > +
> > +    if (node["mtu"]) {
> > +        // Configure MTU
> > +        int err;
> > +        int mtu = node["mtu"].as<int>();
> > +        if ((err = osv::if_set_mtu(if_name, mtu)) != 0){
> > +            debug("cloud-init: %s errror.  Failed to set %s mtu
> > %d. err=%d\n", __FUNCTION__, if_name.c_str(), mtu, err);
> > +        }
> > +    }
> > +    if (node["subnets"]) {
> > +        for (auto& subnet : node["subnets"]) {
> > +            std::string subnet_type;
> > +            if (subnet["type"]) {
> > +                subnet_type = subnet["type"].as<std::string>();
> > +            } else {
> > +                subnet_type = "static";
> > +            }
> > +            if (subnet_type == "static" ||
> > +                subnet_type == "static6") {
> > +                if (!subnet["address"]) {
> > +                    continue;
> > +                }
> > +                // TODO: Disable DHCP per interface
> > +                // dhcp_release();
> > +                std::string address =
> > subnet["address"].as<std::string>();
> > +                std::string netmask;
> > +                std::vector<std::string> addr_prefix;
> > +                bool ipv6 = false;
> > +
> > +                boost::split(addr_prefix, address,
> > boost::is_any_of("/"), boost::token_compress_on);
> > +                if (addr_prefix.size() == 2) {
> > +                    address = addr_prefix[0];
> > +                    netmask = addr_prefix[1];
> > +                } else {
> > +                    if (subnet["netmask"]) {
> > +                        netmask =
> > subnet["netmask"].as<std::string>();
> > +                    }
> > +                }
> > +
> > +                try {
> > +                    boost::asio::ip::address addr;
> > +                    ipv6 =
> > boost::asio::ip::address::from_string(address).is_v6();
> > +                } catch(std::exception const &ex) {
> > +                    debug("cloud-init: %s error.  Not a valid IP
> > address %s\n",
> > +                          __FUNCTION__, address.c_str());
> > +                    continue;
> > +                }
> > +
> > +                // Add address to interface
> > +                if (osv::if_add_addr(if_name, address, netmask) !=
> > 0){
> > +                    debug("cloud-init: %s error.  Failed adding
> > address %s/%s to interface %s\n",
> > +                          __FUNCTION__,
> > +                          address.c_str(), netmask.c_str(),
> > if_name.c_str());
> > +                    continue;
> > +                }
> > +                if (subnet["gateway"]) {
> > +                    std::string gateway =
> > node["gateway"].as<std::string>();
> > +                    std::string network = ipv6 ? "::" : "0.0.0.0";
> > +                    std::string netmask = ipv6 ? "::" : "0.0.0.0";
> > +
> > +                    osv_route_add_network(network.c_str(),
> > +                                          netmask.c_str(),
> > +                                          gateway.c_str());
> > +                }
> > +
> > +                if (subnet["routes"]) {
> > +                    for (auto& route : subnet["routes"]) {
> > +                        if (route["gateway"]) {
> > +                            std::string gateway =
> > node["gateway"].as<std::string>();
> > +                            std::string network;
> > +                            std::string netmask;
> > +                            if (route["network"]) {
> > +                                network =
> > route["network"].as<std::string>();
> > +                            } else {
> > +                                network = ipv6 ? "::" : "0.0.0.0";
> > +                            }
> > +                            if (route["netmask"]) {
> > +                                netmask =
> > route["netmask"].as<std::string>();
> > +                            } else {
> > +                                netmask = ipv6 ? "::" : "0.0.0.0";
> > +                            }
> > +
> > +                            osv_route_add_network(network.c_str(),
> > +                                                  netmask.c_str(),
> > +                                                 
> > gateway.c_str());
> > +                        }
> > +                    }
> > +                }
> > +                if (subnet["dns_nameservers"]) {
> > +                    auto& dns_servers =
> > subnet["dns_nameservers"].as<std::vector<std::string>>();
> > +                   
> > state.dns_servers.insert(state.dns_servers.end(),
> > +                                             dns_servers.begin(),
> > +                                             dns_servers.end());
> > +                }
> > +            }
> > +            else if (subnet_type == "dhcp") {
> > +                // TODO: Enable DHCP per interface
> > +                // dhcp_start(true)
> > +            }
> > +            else if (subnet_type == "nameserver") {
> > +                if (!subnet["address"])
> > +                    continue;
> > +                auto& dns_servers =
> > subnet["address"].as<std::vector<std::string>>();
> > +                state.dns_servers.insert(state.dns_servers.end(),
> > +                                         dns_servers.begin(),
> > +                                         dns_servers.end());
> > +            }
> > +        }
> > +    }
> > +}
> > +
> > +void network_module::handle(const YAML::Node& doc)
> > +{
> > +    if (doc["version"]) {
> > +        int version = doc["version"].as<int>();
> > +        if (version != 1) {
> > +            debug("cloud-init: version %d is not supported\n",
> > version);
> > +            return;
> > +        }
> > +    }
> > +
> > +    if (doc["config"]) {
> > +        const YAML::Node &config = doc["config"];
> > +        network_module::config_state state;
> > +
> > +        init_config_state(state);
> > +        for (auto& ifc_node : config) {
> > +            configure_interface(ifc_node, state);
> > +        }
> > +
> > +        // Configure DNS servers
> > +        if (!state.dns_servers.empty()) {
> > +            std::set<std::string> dns_server_set;
> > +            std::vector<boost::asio::ip::address> dns_servers;
> > +            for (auto t : state.dns_servers) {
> > +                if (dns_server_set.find(t) !=
> > dns_server_set.end())
> > +                    continue; // Skip duplicates
> > +                auto addr =
> > boost::asio::ip::address::from_string(t);
> > +                dns_servers.push_back(addr);
> > +                dns_server_set.insert(t);
> > +            }
> > +            osv::set_dns_config(dns_servers,
> > std::vector<std::string>());
> > +        }
> > +    }
> > +}
> > +
> > +void
> > network_module::init_config_state(network_module::config_state&
> > state)
> > +{
> > +    struct ifaddrs *ifaddr = NULL;
> > +    struct ifaddrs *ifa = NULL;
> > +
> > +    state.physical_interfaces.clear();
> > +    state.configured_interfaces.clear();
> > +    state.dns_servers.clear();
> > +    
> > +    if (getifaddrs(&ifaddr) == -1) {
> > +        debug("cloud-init: %s failed.  getifaddrs() failed: %s\n",
> > __FUNCTION__, strerror(errno));
> > +        return;
> > +    }
> > +
> > +    for (ifa = ifaddr; ifa != NULL; ifa = ifa->ifa_next) {
> > +        if (!ifa->ifa_addr) continue;
> > +        if (ifa->ifa_addr->sa_family != AF_PACKET) continue;
> > +        if (ifa->ifa_flags & IFF_LOOPBACK) continue;
> > +        state.physical_interfaces.push_back(ifa->ifa_name);
> > +    }
> > +
> > +    freeifaddrs(ifaddr);
> > +}
> > +
> > diff --git a/modules/cloud-init/network-module.hh b/modules/cloud-
> > init/network-module.hh
> > new file mode 100644
> > index 0000000..175edf5
> > --- /dev/null
> > +++ b/modules/cloud-init/network-module.hh
> > @@ -0,0 +1,43 @@
> > +/*
> > + * Copyright (C) 2014 Cloudius Systems, Ltd.
> > + *
> > + * This work is open source software, licensed under the terms of
> > the
> > + * BSD license as described in the LICENSE file in the top-level
> > directory.
> > + */
> > +
> > +#ifndef CLOUDINIT_NETWORK_HH_
> > +#define CLOUDINIT_NETWORK_HH_
> > +
> > +#include "cloud-init.hh"
> > +
> > +#include <fstream>
> > +#include <vector>
> > +#include <set>
> > +
> > +class network_module : public init::config_module
> > +{
> > +public:
> > +
> > +    virtual void handle(const YAML::Node& doc) override;
> > +
> > +    virtual std::string get_label()
> > +    {
> > +        return "network";
> > +    }
> > +
> > +private:
> > +
> > +    class config_state 
> > +    {
> > +    public:
> > +        std::set<std::string> configured_interfaces;
> > +        std::vector<std::string> physical_interfaces;
> > +        std::vector<std::string> dns_servers;
> > +    };
> > +    static void init_config_state(config_state& state);
> > +    static void configure_interface(const YAML::Node& node,
> > config_state& state);
> > +    static void configure_physical_interface(const YAML::Node&
> > node, config_state& state);
> > +
> > +};
> > +
> > +#endif
> > -- 
> > 2.7.4
> > 

-- 
You received this message because you are subscribed to the Google Groups "OSv 
Development" group.
To unsubscribe from this group and stop receiving emails from it, send an email 
to [email protected].
To view this discussion on the web visit 
https://groups.google.com/d/msgid/osv-dev/338f62e13ff2228946c08f782d92f7ed286c186b.camel%40rossfell.co.uk.

Reply via email to