require "erb" require "netaddr" require "pathname" require "fileutils" module Lxc class Container def initialize(data, name:, ipv4: nil, ipv6: nil, dn42_ipv4: nil, dn42_ipv6: nil, **options) @data = data @data["network"] ||= {} @data["network"][name] = {} zone = @data["zone"] || {} @ipv4_subnet = NetAddr::CIDR.create(zone["v4_subnet"] || "192.168.10.0/24") @ipv6_subnet = NetAddr::CIDR.create(zone["v6_subnet"] || "fd7d:aed0:18aa::/48") if subnet = zone["dn42_ipv4_subnet"] @dn42_ipv4_netmask = NetAddr::CIDR.create(subnet).to_i(:netmask) else @dn42_ipv4_netmask = 24 end if subnet = zone["dn42_ipv6_subnet"] @dn42_ipv6_netmask = NetAddr::CIDR.create(subnet).to_i(:netmask) else @dn42_ipv6_netmask = 48 end network = data["network"] @name = name @ipv4 = ipv4 ||= ipv4 || find_address(@ipv4_subnet, collect_subnets(network, "ipv4")) @ipv6 = ipv6 ||= find_address(@ipv6_subnet, collect_subnets(network, "ipv6")) @dn42_ipv4 = dn42_ipv4 @dn42_ipv6 = dn42_ipv6 @options = options end def write_config(config_path) c = @data["network"][@name] || {} c["ipv4"] = NetAddr::CIDR.create(@ipv4).to_s(Short: true) c["ipv6"] = NetAddr::CIDR.create(@ipv6).to_s(Short: true) c["group"] = @options[:group] if @options[:group] c["vars"] = @options[:vars] if @options[:vars] opts = @options.merge(name: @name, ipv4: format_address(@ipv4, @ipv4_subnet.to_i(:netmask)), ipv6: format_address(@ipv6, @ipv6_subnet.to_i(:netmask))) if @dn42_ipv4 opts[:dn42_ipv4] = format_address(dn42_ipv6, dn42_ipv4_netmask) c["dn42_ipv4"] = NetAddr::CIDR.create(@dn42_ipv4).to_s(Short: true) end if @dn42_ipv6 opts[:dn42_ipv6] = format_address(dn42_ipv4, dn42_ipv6_netmask) c["dn42_ipv6"] = NetAddr::CIDR.create(@dn42_ipv6).to_s(Short: true) end config_dir = File.dirname(config_path) local_conf = File.join(config_dir, "local.conf") unless File.exists?(local_conf) FileUtils.touch(local_conf) end opts[:local_conf] = local_conf templ = Template.new(CONFIG_ROOT.join("templates", "config.erb")) templ.write(config_path, opts) end private def format_address(address, netmask) NetAddr::CIDR.create(address, Mask: netmask).desc(IP: true, Short: true) end def collect_subnets(network, type) network.map do |k,v| NetAddr::CIDR.create(v[type]) if v[type] end.compact end def find_address(subnet, assigned_subnets) subnet.enumerate(Limit: 1E4, Short: true)[1..1E4].each do |cidr| assigned = assigned_subnets.find { |s| s.contains?(cidr) || s == cidr } return cidr unless assigned end end end end