require "erb" ROOT_PATH = Pathname.new(File.expand_path("../../..", __FILE__)) class TemplateContext def initialize(hash) hash.each do |key, value| singleton_class.send(:define_method, key) { value } end end def get_binding binding end end class Container def initialize(data, ip4:, ip6:, dn42_ipv4:, dn42_ipv6:, **options) network = @data["network"] || {} 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 ip4 ||= find_address(ipv4_subnet, collect_subnets(network, "ipv4")) ip6 ||= find_address(ipv6_subnet, collect_subnets(network, "ipv6")) @opts = options.merge(ipv4: format_address(ip4, ipv4_subnet.to_i(:netmask)), ipv6: format_address(ip6, ipv6_subnet.to_i(:netmask))) @opts[:dn42_ipv4] = format_address(dn42_ipv6, dn42_ipv4_netmask) @opts[:dn42_ipv6] = format_address(dn42_ipv4, dn42_ipv6_netmask) end def write_config(config_path) context = TemplateContext.new(@opts) erb = ERB.new(File.read(ROOT_PATH.join("templates", "config.erb"))) open(config_path, "w+") do |f| f.write(erb.result(context.get_binding)) end 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