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
> n...@scylladb.com
> 
> On Tue, Aug 7, 2018 at 5:49 AM, Charles Myers <
> charles.my...@spirent.com> 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 <charles.my...@spirent.com>
> > ---
> >  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 osv-dev+unsubscr...@googlegroups.com.
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