diff --git a/src/bosh-director/lib/bosh/director/api/controllers/deployments_controller.rb b/src/bosh-director/lib/bosh/director/api/controllers/deployments_controller.rb index 745e526edc3..b0dc7c5848b 100644 --- a/src/bosh-director/lib/bosh/director/api/controllers/deployments_controller.rb +++ b/src/bosh-director/lib/bosh/director/api/controllers/deployments_controller.rb @@ -626,6 +626,7 @@ def create_vm_response(instance, vm) 'id' => instance.uuid, 'az' => instance.availability_zone, 'ips' => vm&.ips || [], + 'ips_cidr' => vm&.ips_cidr || [], 'vm_created_at' => vm&.created_at&.utc&.iso8601, } end diff --git a/src/bosh-director/lib/bosh/director/deployment_plan/compilation_instance_pool.rb b/src/bosh-director/lib/bosh/director/deployment_plan/compilation_instance_pool.rb index 333218c53f4..882039a607e 100644 --- a/src/bosh-director/lib/bosh/director/deployment_plan/compilation_instance_pool.rb +++ b/src/bosh-director/lib/bosh/director/deployment_plan/compilation_instance_pool.rb @@ -189,6 +189,7 @@ def create_instance_plan(stemcell) compilation_network = @deployment_plan.network(@deployment_plan.compilation.network_name) reservation = DesiredNetworkReservation.new_dynamic(instance.model, compilation_network) + @logger.debug("Creating new dynamic reservation #{reservation.inspect} for instance '#{instance}' and compile instance group '#{compile_instance_group}'") desired_instance = DeploymentPlan::DesiredInstance.new(compile_instance_group) instance_plan = DeploymentPlan::InstancePlan.new( existing_instance: instance.model, diff --git a/src/bosh-director/lib/bosh/director/deployment_plan/dynamic_network.rb b/src/bosh-director/lib/bosh/director/deployment_plan/dynamic_network.rb index e80045480b1..1902e91dc78 100644 --- a/src/bosh-director/lib/bosh/director/deployment_plan/dynamic_network.rb +++ b/src/bosh-director/lib/bosh/director/deployment_plan/dynamic_network.rb @@ -17,20 +17,36 @@ def self.parse(network_spec, availability_zones, logger) if network_spec.has_key?('subnets') validate_network_has_no_key_while_subnets_present('dns', name, network_spec) validate_network_has_no_key_while_subnets_present('cloud_properties', name, network_spec) + validate_network_has_no_key_while_subnets_present('prefix', name, network_spec) subnets = network_spec['subnets'].map do |subnet_properties| name_servers = name_server_parser.parse(subnet_properties['name'], subnet_properties) cloud_properties = safe_property(subnet_properties, 'cloud_properties', class: Hash, default: {}) + prefix = safe_property(subnet_properties, 'prefix', class: Hash, default: {}) + if prefix.empty? || prefix.nil? + prefix = 32 + end subnet_availability_zones = parse_availability_zones(subnet_properties, availability_zones, name) - DynamicNetworkSubnet.new(name_servers, cloud_properties, subnet_availability_zones) + DynamicNetworkSubnet.new(name_servers, cloud_properties, subnet_availability_zones, prefix) end else cloud_properties = safe_property(network_spec, 'cloud_properties', class: Hash, default: {}) + prefix = safe_property(network_spec, 'prefix', class: Hash, default: {}) + if prefix.empty? || prefix.nil? + prefix = 32 + end name_servers = name_server_parser.parse(network_spec['name'], network_spec) - subnets = [DynamicNetworkSubnet.new(name_servers, cloud_properties, nil)] + subnets = [DynamicNetworkSubnet.new(name_servers, cloud_properties, nil, prefix)] end - new(name, subnets, logger) + unless subnets.empty? + prefix = subnets.first.prefix + else + prefix = 32 + end + + + new(name, subnets, logger, prefix) end def self.validate_network_has_no_key_while_subnets_present(key, name, network_spec) @@ -77,9 +93,10 @@ def self.check_validity_of_availability_zone(availability_zone, availability_zon end end - def initialize(name, subnets, logger) + def initialize(name, subnets, prefix, logger) super(name, logger) @subnets = subnets + @prefix = prefix end attr_reader :subnets @@ -94,7 +111,7 @@ def initialize(name, subnets, logger) def network_settings(reservation, default_properties = Network::REQUIRED_DEFAULTS, availability_zone = nil) unless reservation.dynamic? raise NetworkReservationWrongType, - "IP '#{format_ip(reservation.ip)}' on network '#{reservation.network.name}' does not belong to dynamic pool" + "IP '#{reservation.ip}' on network '#{reservation.network.name}' does not belong to dynamic pool" end if availability_zone.nil? diff --git a/src/bosh-director/lib/bosh/director/deployment_plan/dynamic_network_subnet.rb b/src/bosh-director/lib/bosh/director/deployment_plan/dynamic_network_subnet.rb index bcd6bb04f34..5867fd669cb 100644 --- a/src/bosh-director/lib/bosh/director/deployment_plan/dynamic_network_subnet.rb +++ b/src/bosh-director/lib/bosh/director/deployment_plan/dynamic_network_subnet.rb @@ -1,13 +1,14 @@ module Bosh::Director module DeploymentPlan class DynamicNetworkSubnet - def initialize(dns, cloud_properties, availability_zone_names) + def initialize(dns, cloud_properties, availability_zone_names, prefix) @dns = dns @cloud_properties = cloud_properties @availability_zone_names = availability_zone_names.nil? ? nil : availability_zone_names + @prefix = prefix end - attr_reader :dns, :cloud_properties, :availability_zone_names + attr_reader :dns, :cloud_properties, :availability_zone_names, :prefix end end end diff --git a/src/bosh-director/lib/bosh/director/deployment_plan/instance_network_reservations.rb b/src/bosh-director/lib/bosh/director/deployment_plan/instance_network_reservations.rb index de51af3e03b..694f9112f18 100644 --- a/src/bosh-director/lib/bosh/director/deployment_plan/instance_network_reservations.rb +++ b/src/bosh-director/lib/bosh/director/deployment_plan/instance_network_reservations.rb @@ -24,7 +24,7 @@ def self.create_from_db(instance_model, deployment, logger) # Dynamic network reservations are not saved in DB, recreating from instance spec instance_model.spec.fetch('networks', []).each do |network_name, network_config| next unless network_config['type'] == 'dynamic' - reservations.add_existing(instance_model, deployment, network_name, network_config['ip'], network_config['type']) + reservations.add_existing(instance_model, deployment, network_name, Bosh::Director::IpAddrOrCidr.new("#{network_config['ip']}/32"), network_config['type']) end end @@ -75,17 +75,17 @@ def find_network(deployment, cidr_ip, network_name, instance_model) ip_in_subnet = network.subnets.find { |snet| snet.is_reservable?(cidr_ip) } next unless ip_in_subnet - @logger.debug("Registering existing reservation with IP '#{format_ip(cidr_ip)}' for instance '#{instance_model}'"\ + @logger.debug("Registering existing reservation with IP '#{format_cidr_ip(cidr_ip)}' for instance '#{instance_model}'"\ "on network '#{network.name}'") return network end elsif network_match_on_name # dynamic and static vip - @logger.debug("Registering existing reservation with IP '#{format_ip(cidr_ip)}' for instance '#{instance_model}'"\ + @logger.debug("Registering existing reservation with IP '#{format_cidr_ip(cidr_ip)}' for instance '#{instance_model}'"\ "on network '#{network_name}'") return network_match_on_name end - @logger.debug("Failed to find network #{network_name} or a network with valid subnets for #{format_ip(cidr_ip)},"\ + @logger.debug("Failed to find network #{network_name} or a network with valid subnets for #{format_cidr_ip(cidr_ip)},"\ 'reservation will be marked as obsolete') Network.new(network_name, nil) end diff --git a/src/bosh-director/lib/bosh/director/deployment_plan/ip_provider/ip_provider.rb b/src/bosh-director/lib/bosh/director/deployment_plan/ip_provider/ip_provider.rb index b4de47f1aad..c6e2e17169f 100644 --- a/src/bosh-director/lib/bosh/director/deployment_plan/ip_provider/ip_provider.rb +++ b/src/bosh-director/lib/bosh/director/deployment_plan/ip_provider/ip_provider.rb @@ -66,7 +66,7 @@ def reserve_manual(reservation) filter_subnet_by_instance_az(reservation).each do |subnet| if (ip = @ip_repo.allocate_dynamic_ip(reservation, subnet)) - @logger.debug("Reserving dynamic IP '#{ip}' for manual network '#{reservation.network.name}'") + @logger.debug("Reserving dynamic IP '#{ip.to_cidr_s}' for manual network '#{reservation.network.name}'") reservation.resolve_ip(ip) reservation.resolve_type(:dynamic) break @@ -81,7 +81,7 @@ def reserve_manual(reservation) @logger.debug("Reserving #{reservation.desc} for manual network '#{reservation.network.name}'") if (subnet = reservation.network.find_subnet_containing(reservation.ip)) - if subnet.restricted_ips.include?(reservation.ip) + if subnet.restricted_ips.include?(reservation.ip.to_i) message = "Failed to reserve IP '#{format_ip(reservation.ip)}' for network '#{subnet.network_name}': IP belongs to reserved range" @logger.error(message) raise Bosh::Director::NetworkReservationIpReserved, message @@ -99,7 +99,8 @@ def reserve_manual_with_subnet(reservation, subnet) @ip_repo.add(reservation) subnet_az_names = subnet.availability_zone_names.to_a.join(', ') - if subnet.static_ips.include?(reservation.ip) + + if subnet.static_ips.include?(reservation.ip.to_i) reservation.resolve_type(:static) @logger.debug("Found subnet with azs '#{subnet_az_names}' for #{format_ip(reservation.ip)}. Reserved as static network reservation.") else diff --git a/src/bosh-director/lib/bosh/director/deployment_plan/ip_provider/ip_repo.rb b/src/bosh-director/lib/bosh/director/deployment_plan/ip_provider/ip_repo.rb index 8d37093a18a..81d9f29f846 100644 --- a/src/bosh-director/lib/bosh/director/deployment_plan/ip_provider/ip_repo.rb +++ b/src/bosh-director/lib/bosh/director/deployment_plan/ip_provider/ip_repo.rb @@ -3,6 +3,7 @@ class IpRepo include Bosh::Director::IpUtil class IpFoundInDatabaseAndCanBeRetried < StandardError; end class NoMoreIPsAvailableAndStopRetrying < StandardError; end + class PrefixOutOfRange < StandardError; end def initialize(logger) @logger = Bosh::Director::TaggedLogger.new(logger, 'network-configuration') @@ -11,7 +12,7 @@ def initialize(logger) def delete(ip) ip_or_cidr = Bosh::Director::IpAddrOrCidr.new(ip) - ip_address = Bosh::Director::Models::IpAddress.first(address_str: ip_or_cidr.to_i.to_s) + ip_address = Bosh::Director::Models::IpAddress.first(address_str: ip_or_cidr.to_cidr_s) if ip_address @logger.debug("Releasing ip '#{ip_or_cidr}'") @@ -22,7 +23,7 @@ def delete(ip) end def add(reservation) - ip_or_cidr = Bosh::Director::IpAddrOrCidr.new(reservation.ip) + ip_or_cidr = reservation.ip reservation_type = reservation.network.ip_type(ip_or_cidr) @@ -34,6 +35,7 @@ def add(reservation) ) reservation.resolve_type(reservation_type) + @logger.debug("Reserved ip '#{ip_or_cidr}' for #{reservation.network.name} as #{reservation_type}") end @@ -50,8 +52,8 @@ def allocate_dynamic_ip(reservation, subnet) retry end - @logger.debug("Allocated dynamic IP '#{ip_address}' for #{reservation.network.name}") - ip_address.to_i + @logger.debug("Allocated dynamic IP '#{ip_address.to_cidr_s}' for #{reservation.network.name}") + ip_address end def allocate_vip_ip(reservation, subnet) @@ -74,59 +76,91 @@ def allocate_vip_ip(reservation, subnet) private + def all_ip_addresses + Bosh::Director::Models::IpAddress.select(:address_str).all.map { |a| a.address } + end + def try_to_allocate_dynamic_ip(reservation, subnet) addresses_in_use = Set.new(all_ip_addresses) - first_range_address = subnet.range.to_range.first.to_i - 1 + first_range_address = Bosh::Director::IpAddrOrCidr.new(subnet.range.to_range.first.to_i - 1) + addresses_we_cant_allocate = addresses_in_use - addresses_we_cant_allocate << first_range_address - addresses_we_cant_allocate.merge(subnet.restricted_ips.to_a) unless subnet.restricted_ips.empty? - addresses_we_cant_allocate.merge(subnet.static_ips.to_a) unless subnet.static_ips.empty? - addr = find_first_available_address(addresses_we_cant_allocate, first_range_address) - ip_address = Bosh::Director::IpAddrOrCidr.new(addr) + addresses_we_cant_allocate.merge(subnet.restricted_ips.map { |int_ip| Bosh::Director::IpAddrOrCidr.new(int_ip)}) unless subnet.restricted_ips.empty? + addresses_we_cant_allocate.merge(subnet.static_ips.map { |int_ip| Bosh::Director::IpAddrOrCidr.new(int_ip)}) unless subnet.static_ips.empty? - unless subnet.range == ip_address || subnet.range.include?(ip_address) - raise NoMoreIPsAvailableAndStopRetrying + if subnet.range.ipv6? + addresses_we_cant_allocate.delete_if { |ipaddr| ipaddr.ipv4? } + else + addresses_we_cant_allocate.delete_if { |ipaddr| ipaddr.ipv6? } end - save_ip(ip_address, reservation, false) + prefix = subnet.prefix - ip_address + ip_address_cidr = find_next_available_ip(addresses_we_cant_allocate, first_range_address, prefix) + + if !(subnet.range == ip_address_cidr || subnet.range.include?(ip_address_cidr)) || + subnet.range.to_range.last.to_i < (ip_address_cidr.to_i + ip_address_cidr.count) + raise NoMoreIPsAvailableAndStopRetrying + end + + save_ip(ip_address_cidr.to_cidr_s, reservation, false) + + ip_address_cidr end - def find_first_available_address(addresses_we_cant_allocate, first_address) - last_address_we_cant_use = addresses_we_cant_allocate - .to_a - .reject { |a| a < first_address } - .sort - .find { |a| !addresses_we_cant_allocate.include?(a + 1) } - last_address_we_cant_use + 1 + def find_next_available_ip(ip_entries, first_range_address, prefix) + filtered_ips = ip_entries.sort_by { |ip| ip.to_i }.reject { |ip| ip.to_i < first_range_address.to_i } #remove ips that are below subnet range + + current_ip = Bosh::Director::IpAddrOrCidr.new(first_range_address.to_i + 1) + found = false + + while found == false + current_prefix = Bosh::Director::IpAddrOrCidr.new("#{current_ip}/#{prefix}") + + if filtered_ips.any? { |ip| current_prefix.include?(ip) } + filtered_ips.reject! { |ip| ip.to_i < current_prefix.to_i } + actual_ip_prefix = filtered_ips.first.count + if actual_ip_prefix > current_prefix.count + current_ip = Bosh::Director::IpAddrOrCidr.new(current_ip.to_i + actual_ip_prefix) + else + current_ip = Bosh::Director::IpAddrOrCidr.new(current_ip.to_i + current_prefix.count) + end + else + found_cidr = current_prefix + found = true + end + end + + found_cidr end def try_to_allocate_vip_ip(reservation, subnet) - addresses_in_use = Set.new(all_ip_addresses) + addresses_in_use = Set.new(all_ip_addresses.map { |ip| ip.to_i }) + + if Bosh::Director::IpAddrOrCidr.new(subnet.static_ips.first.to_i).ipv6? + prefix = 128 + else + prefix = 32 + end available_ips = subnet.static_ips - addresses_in_use raise NoMoreIPsAvailableAndStopRetrying if available_ips.empty? - ip_address = Bosh::Director::IpAddrOrCidr.new(available_ips.first) + ip_address = Bosh::Director::IpAddrOrCidr.new("#{Bosh::Director::IpAddrOrCidr.new(available_ips.first)}/#{prefix}") - save_ip(ip_address, reservation, false) + save_ip(ip_address.to_cidr_s, reservation, false) ip_address end - def all_ip_addresses - Bosh::Director::Models::IpAddress.select(:address_str).all.map { |a| a.address_str.to_i } - end - def reserve_with_instance_validation(instance_model, ip, reservation, is_static) # try to save IP first before validating its instance to prevent race conditions - save_ip(ip, reservation, is_static) + save_ip(ip.to_cidr_s, reservation, is_static) rescue IpFoundInDatabaseAndCanBeRetried - ip_address = Bosh::Director::Models::IpAddress.first(address_str: ip.to_i.to_s) + ip_address = Bosh::Director::Models::IpAddress.first(address_str: ip.to_cidr_s) retry unless ip_address @@ -156,17 +190,19 @@ def validate_instance_and_update_reservation_type(instance_model, ip, ip_address end def save_ip(ip, reservation, is_static) + @logger.debug("Adding IP Address: #{ip} from reservation: #{reservation}") ip_address = Bosh::Director::Models::IpAddress.new( - address_str: ip.to_i.to_s, + address_str: ip, network_name: reservation.network.name, task_id: Bosh::Director::Config.current_job.task_id, static: is_static, - ) + ) reservation.instance_model.add_ip_address(ip_address) rescue Sequel::ValidationFailed, Sequel::DatabaseError => e error_message = e.message.downcase + @logger.debug("ERROR!!! #{error_message}") if error_message.include?('unique') || error_message.include?('duplicate') - raise IpFoundInDatabaseAndCanBeRetried + raise IpFoundInDatabaseAndCanBeRetried, e.inspect else raise e end diff --git a/src/bosh-director/lib/bosh/director/deployment_plan/manual_network.rb b/src/bosh-director/lib/bosh/director/deployment_plan/manual_network.rb index 26c76cd5964..74a81c1aa65 100644 --- a/src/bosh-director/lib/bosh/director/deployment_plan/manual_network.rb +++ b/src/bosh-director/lib/bosh/director/deployment_plan/manual_network.rb @@ -13,24 +13,31 @@ def self.parse(network_spec, availability_zones, logger) managed = Config.network_lifecycle_enabled? && safe_property(network_spec, 'managed', default: false) subnet_specs = safe_property(network_spec, 'subnets', class: Array) subnets = [] + prefix = nil subnet_specs.each do |subnet_spec| new_subnet = ManualNetworkSubnet.parse(name, subnet_spec, availability_zones, managed) subnets.each do |subnet| raise NetworkOverlappingSubnets, "Network '#{name}' has overlapping subnets" if subnet.overlaps?(new_subnet) + if prefix.nil? + prefix = subnet.prefix + elsif prefix != subnet.prefix + raise NetworkPrefixSizesDiffer, "Network '#{name}' has subnets that define different prefixes" + end end subnets << new_subnet end validate_all_subnets_use_azs(subnets, name) - new(name, subnets, logger, managed) + new(name, subnets, prefix, logger, managed) end def managed? @managed end - def initialize(name, subnets, logger, managed = false) + def initialize(name, subnets, prefix, logger, managed = false) super(name, TaggedLogger.new(logger, 'network-configuration')) @subnets = subnets + @prefix = prefix @managed = managed end @@ -46,15 +53,21 @@ def network_settings(reservation, default_properties = REQUIRED_DEFAULTS, availa "Can't generate network settings without an IP" end - ip_or_cidr = Bosh::Director::IpAddrOrCidr.new(reservation.ip) + ip_or_cidr = reservation.ip subnet = find_subnet_containing(reservation.ip) + unless subnet raise NetworkReservationInvalidIp, "Provided IP '#{ip_or_cidr}' does not belong to any subnet" end + + unless subnet.prefix.to_i == ip_or_cidr.prefix.to_i + raise NetworkReservationInvalidPrefix, "Subnet Prefix #{subnet.prefix} and ip reservation prefix #{ip_or_cidr.prefix} do not match" + end config = { "type" => "manual", "ip" => ip_or_cidr.to_s, + "prefix" => ip_or_cidr.prefix.to_s, "netmask" => subnet.netmask, "cloud_properties" => subnet.cloud_properties } diff --git a/src/bosh-director/lib/bosh/director/deployment_plan/manual_network_subnet.rb b/src/bosh-director/lib/bosh/director/deployment_plan/manual_network_subnet.rb index 7d282155b07..a6661eab774 100644 --- a/src/bosh-director/lib/bosh/director/deployment_plan/manual_network_subnet.rb +++ b/src/bosh-director/lib/bosh/director/deployment_plan/manual_network_subnet.rb @@ -5,7 +5,7 @@ class ManualNetworkSubnet < Subnet extend IpUtil attr_reader :network_name, :name, :dns, - :availability_zone_names, :netmask_bits + :availability_zone_names, :netmask_bits, :prefix attr_accessor :cloud_properties, :range, :gateway, :restricted_ips, :static_ips, :netmask @@ -17,6 +17,7 @@ def self.parse(network_name, subnet_spec, availability_zones, managed = false) ignore_missing_gateway = Bosh::Director::Config.ignore_missing_gateway gateway_property = safe_property(subnet_spec, 'gateway', class: String, optional: ignore_missing_gateway || managed) reserved_property = safe_property(subnet_spec, 'reserved', optional: true) + prefix = safe_property(subnet_spec, 'prefix', optional: true) restricted_ips = Set.new static_ips = Set.new @@ -51,7 +52,7 @@ def self.parse(network_name, subnet_spec, availability_zones, managed = false) each_ip(reserved_property) do |ip| unless range.include?(ip) - raise NetworkReservedIpOutOfRange, "Reserved IP '#{format_ip(ip)}' is out of " \ + raise NetworkReservedIpOutOfRange, "Reserved IP '#{to_ipaddr(ip)}' is out of " \ "network '#{network_name}' range" end @@ -75,6 +76,28 @@ def self.parse(network_name, subnet_spec, availability_zones, managed = false) static_ips.add(ip) end + + if prefix.nil? + if range.ipv6? + prefix = 128 + else + prefix = 32 + end + else + if range.prefix > prefix.to_i + raise NetworkPrefixSizeTooBig, "Prefix size '#{prefix}' is larger than range prefix '#{range.prefix}'" + end + # if a prefix is provided the static ips can only be the base_addresses of the prefix otherwise we through an error + static_ips.each do |static_ip| + range.each_base_address(prefix) do |base_address_int| + if static_ip == base_address_int + break + elsif static_ip < base_address_int + raise NetworkPrefixStaticIpNotBaseAddress, "Static IP '#{to_ipaddr(static_ip)}' is not a base address of the prefix '#{prefix}'" + end + end + end + end end name_server_parser = NetworkParser::NameServersParser.new @@ -95,10 +118,11 @@ def self.parse(network_name, subnet_spec, availability_zones, managed = false) static_ips, sn_name, netmask_bits, + prefix ) end - def initialize(network_name, range, gateway, name_servers, cloud_properties, netmask, availability_zone_names, restricted_ips, static_ips, subnet_name = nil, netmask_bits = nil) + def initialize(network_name, range, gateway, name_servers, cloud_properties, netmask, availability_zone_names, restricted_ips, static_ips, subnet_name = nil, netmask_bits = nil, prefix = nil) @network_name = network_name @name = subnet_name @netmask_bits = netmask_bits @@ -110,6 +134,7 @@ def initialize(network_name, range, gateway, name_servers, cloud_properties, net @availability_zone_names = availability_zone_names @restricted_ips = restricted_ips @static_ips = static_ips + @prefix = prefix end def overlaps?(subnet) @@ -123,7 +148,13 @@ def overlaps?(subnet) end def is_reservable?(ip) - range.include?(ip) && !restricted_ips.include?(ip.to_i) + restricted_ips.reject! { |restricted_ip| restricted_ip.to_i < ip.to_range.first.to_i } + restricted_ips.reject! { |restricted_ip| restricted_ip.to_i > ip.to_range.last.to_i } + restricted_ips_contain_ip_from_prefix = false + if !restricted_ips.empty? + restricted_ips_contain_ip_from_prefix = true + end + range.include?(ip.to_cidr_s) && !restricted_ips_contain_ip_from_prefix end def self.parse_properties_from_database(network_name, subnet_name) diff --git a/src/bosh-director/lib/bosh/director/deployment_plan/network.rb b/src/bosh-director/lib/bosh/director/deployment_plan/network.rb index f970fc75d2e..64fd643433a 100644 --- a/src/bosh-director/lib/bosh/director/deployment_plan/network.rb +++ b/src/bosh-director/lib/bosh/director/deployment_plan/network.rb @@ -87,6 +87,10 @@ def has_azs?(az_names) def availability_zones @subnets.map(&:availability_zone_names).flatten.uniq end + + def prefix # for now the prefix should be considered the same for all subnets + @subnets.first.prefix + end end end end diff --git a/src/bosh-director/lib/bosh/director/deployment_plan/placement_planner/static_ips_availability_zone_picker.rb b/src/bosh-director/lib/bosh/director/deployment_plan/placement_planner/static_ips_availability_zone_picker.rb index 6a0cd6173c8..f1b015dd870 100644 --- a/src/bosh-director/lib/bosh/director/deployment_plan/placement_planner/static_ips_availability_zone_picker.rb +++ b/src/bosh-director/lib/bosh/director/deployment_plan/placement_planner/static_ips_availability_zone_picker.rb @@ -141,13 +141,15 @@ def create_network_plan_with_az(instance_plan, network, instance_plans) def create_instance_plan_based_on_existing_ips(desired_instances, existing_instance_model) instance_plan = nil + @job_networks.each do |network| next unless network.static? instance_ips_on_network = find_instance_ips_on_network(existing_instance_model, network) network_plan = nil instance_ips_on_network.each do |instance_ip| - ip_address = instance_ip.address + ip_address = instance_ip.address.to_i + # Instance is using IP in static IPs list, we have to use this instance @logger.debug("Existing instance '#{instance_name(existing_instance_model)}' is using static IP '#{format_ip(ip_address)}' on network '#{network.name}'") if instance_plan.nil? @@ -192,7 +194,8 @@ def create_existing_instance_plan_with_az(desired_instance, existing_instance_mo end def assign_az_based_on_ip(desired_instance, existing_instance_model, network, ip_address) - ip_az_names = @networks_to_static_ips.find_by_network_and_ip(network, ip_address).az_names + ip_az_names = @networks_to_static_ips.find_by_network_and_ip(network, ip_address.to_i).az_names + if ip_az_names.include?(existing_instance_model.availability_zone) az_name = existing_instance_model.availability_zone @logger.debug("Instance '#{instance_name(existing_instance_model)}' belongs to az '#{az_name}' that is in subnet az list, reusing instance az.") @@ -246,6 +249,7 @@ def create_existing_instance_plan(desired_instance, existing_instance_model) def create_network_plan_with_ip(instance_plan, network, ip_address) instance_az = instance_plan.desired_instance.az instance_az_name = instance_az.nil? ? nil : instance_az.name + ip_az_names = @networks_to_static_ips.find_by_network_and_ip(network, ip_address).az_names if ip_az_names.include?(instance_az_name) instance_plan.network_plans << @network_planner.network_plan_with_static_reservation(instance_plan, network, ip_address) diff --git a/src/bosh-director/lib/bosh/director/deployment_plan/steps/commit_instance_network_settings_step.rb b/src/bosh-director/lib/bosh/director/deployment_plan/steps/commit_instance_network_settings_step.rb index eb2dccc8742..2dad78d0d20 100644 --- a/src/bosh-director/lib/bosh/director/deployment_plan/steps/commit_instance_network_settings_step.rb +++ b/src/bosh-director/lib/bosh/director/deployment_plan/steps/commit_instance_network_settings_step.rb @@ -11,7 +11,7 @@ def perform(report) next if ip.nil? - ip_model = Models::IpAddress.find(address_str: ip.to_s) + ip_model = Models::IpAddress.find(address_str: ip.to_cidr_s) ip_model&.update(vm: report.vm) end end diff --git a/src/bosh-director/lib/bosh/director/deployment_plan/vip_network.rb b/src/bosh-director/lib/bosh/director/deployment_plan/vip_network.rb index 4e1125523c1..b3dece66c5b 100644 --- a/src/bosh-director/lib/bosh/director/deployment_plan/vip_network.rb +++ b/src/bosh-director/lib/bosh/director/deployment_plan/vip_network.rb @@ -6,7 +6,7 @@ class VipNetwork < NetworkWithSubnets # @return [Hash] Network cloud properties attr_reader :cloud_properties - attr_reader :subnets + attr_reader :subnets, :prefix def self.parse(network_spec, availability_zones, logger) name = safe_property(network_spec, 'name', class: String) @@ -15,8 +15,14 @@ def self.parse(network_spec, availability_zones, logger) DeploymentPlan::VipNetworkSubnet.parse(subnet_spec, name, availability_zones) end + unless subnets.empty? + prefix = subnets.first.prefix + else + prefix = 32 + end + cloud_properties = safe_property(network_spec, 'cloud_properties', class: Hash, default: {}) - new(name, cloud_properties, subnets, logger) + new(name, cloud_properties, subnets, prefix, logger) end ## @@ -25,10 +31,11 @@ def self.parse(network_spec, availability_zones, logger) # @param [Hash] network_spec parsed from the cloud config # @param [VipNetworkSubnet] vip network subnets parsed from the cloud config # @param [Logger] logger - def initialize(name, cloud_properties, subnets, logger) + def initialize(name, cloud_properties, subnets, prefix, logger) super(name, logger) @cloud_properties = cloud_properties @subnets = subnets + @prefix = prefix @logger = TaggedLogger.new(logger, 'network-configuration') end diff --git a/src/bosh-director/lib/bosh/director/deployment_plan/vip_network_subnet.rb b/src/bosh-director/lib/bosh/director/deployment_plan/vip_network_subnet.rb index 50e4f608127..6e077b4e228 100644 --- a/src/bosh-director/lib/bosh/director/deployment_plan/vip_network_subnet.rb +++ b/src/bosh-director/lib/bosh/director/deployment_plan/vip_network_subnet.rb @@ -4,11 +4,16 @@ class VipNetworkSubnet < Subnet extend ValidationHelper extend IpUtil - attr_reader :static_ips, :availability_zone_names + attr_reader :static_ips, :availability_zone_names, :prefix def self.parse(subnet_spec, network_name, azs) static_ips = Set.new + prefix = safe_property(subnet_spec, 'prefix', optional: true) + if prefix.nil? + prefix = 32 + end + static_property = safe_property(subnet_spec, 'static', class: Array, default: []) each_ip(static_property) do |ip| static_ips.add(ip) @@ -16,16 +21,17 @@ def self.parse(subnet_spec, network_name, azs) availability_zone_names = parse_availability_zones(subnet_spec, network_name, azs) - new(static_ips, availability_zone_names) + new(static_ips, availability_zone_names, prefix) end - def initialize(static_ips, availability_zone_names) + def initialize(static_ips, availability_zone_names, prefix) @static_ips = static_ips @availability_zone_names = availability_zone_names + @prefix = prefix end def is_reservable?(ip) - @static_ips.include?(ip) + @static_ips.include?(ip.to_i) end end end diff --git a/src/bosh-director/lib/bosh/director/errors.rb b/src/bosh-director/lib/bosh/director/errors.rb index 00e2699d8ba..4756d9904ce 100644 --- a/src/bosh-director/lib/bosh/director/errors.rb +++ b/src/bosh-director/lib/bosh/director/errors.rb @@ -165,6 +165,7 @@ def self.err(error_code, response_code = BAD_REQUEST) NetworkReservationIpOutsideSubnet = err(130012) NetworkReservationIpReserved = err(130013) NetworkReservationVipMisconfigured = err(130014) + NetworkReservationInvalidPrefix = err(130015) # Manifest parsing: instance group section InstanceGroupMissingRelease = err(140001) @@ -224,6 +225,9 @@ def self.err(error_code, response_code = BAD_REQUEST) NetworkInvalidIpRangeFormat = err(160010) NetworkDeletingUnorphanedError = err(160011) NetworkNotFoundError = err(16012) + NetworkPrefixSizeTooBig = err(16013) + NetworkPrefixSizesDiffer = err(16014) + NetworkPrefixStaticIpNotBaseAddress = err(16015) # ResourcePool ResourcePoolUnknownNetwork = err(170001) diff --git a/src/bosh-director/lib/bosh/director/ip_addr_or_cidr.rb b/src/bosh-director/lib/bosh/director/ip_addr_or_cidr.rb index 4a9f4154821..581c55af1c2 100644 --- a/src/bosh-director/lib/bosh/director/ip_addr_or_cidr.rb +++ b/src/bosh-director/lib/bosh/director/ip_addr_or_cidr.rb @@ -3,13 +3,13 @@ module Bosh module Director class IpAddrOrCidr - delegate :==, :include?, :ipv4?, :ipv6?, :netmask, :to_i, :to_range, :to_string, to: :@ipaddr + delegate :==, :include?, :ipv4?, :ipv6?, :netmask, :to_i, :to_range, :to_string, :prefix, to: :@ipaddr alias :to_s :to_string def initialize(ip_or_cidr) @ipaddr = if ip_or_cidr.kind_of?(IpAddrOrCidr) - IPAddr.new(ip_or_cidr.to_s) + IPAddr.new(ip_or_cidr.to_cidr_s) elsif ip_or_cidr.kind_of?(Integer) IPAddr.new(ip_or_cidr, inet_type_for(ip_or_cidr)) else @@ -17,12 +17,30 @@ def initialize(ip_or_cidr) end end + def each_base_address(prefix_length) + # Determine the base address for the given prefix_length + # We assume the prefix_length is a valid integer within the subnet + if @ipaddr.ipv4? + bits = 32 + elsif @ipaddr.ipv6? + bits = 128 + end + step_size = 2**(bits - prefix_length) # Calculate number of addresses per subnet + base_address_int = @ipaddr.to_i + + # Yield each base address in this network + while base_address_int <= @ipaddr.to_range.last.to_i + yield base_address_int + base_address_int += step_size + end + end + def count (@ipaddr.to_range.last.to_i - @ipaddr.to_range.first.to_i) + 1 end def to_cidr_s - "#{@ipaddr}/#{@ipaddr.prefix}" + "#{@ipaddr.to_string}/#{@ipaddr.prefix}" end private diff --git a/src/bosh-director/lib/bosh/director/ip_util.rb b/src/bosh-director/lib/bosh/director/ip_util.rb index 62c67f667c4..c6bd491ca3b 100644 --- a/src/bosh-director/lib/bosh/director/ip_util.rb +++ b/src/bosh-director/lib/bosh/director/ip_util.rb @@ -21,7 +21,11 @@ def to_ipaddr(ip) # @param [Integer] ip Integer IP representation # @return [String] Human-readable IP representation def format_ip(ip) - to_ipaddr(ip).to_s + to_ipaddr(ip) + end + + def format_cidr_ip(ip) + ip.to_cidr_s end def ip_address?(ip) diff --git a/src/bosh-director/lib/bosh/director/jobs/vm_state.rb b/src/bosh-director/lib/bosh/director/jobs/vm_state.rb index efa1d4cef9a..6d487355529 100644 --- a/src/bosh-director/lib/bosh/director/jobs/vm_state.rb +++ b/src/bosh-director/lib/bosh/director/jobs/vm_state.rb @@ -58,6 +58,7 @@ def process_vm_for_instance(instance, vm) disk_cid: instance.managed_persistent_disk_cid, disk_cids: instance.active_persistent_disks.collection.map { |d| d.model.disk_cid }, ips: vm&.ips || [], + ips_cidr: vm&.ips_cidr || [], agent_id: vm&.agent_id, job_name: instance.job, index: instance.index, diff --git a/src/bosh-director/lib/bosh/director/models/ip_address.rb b/src/bosh-director/lib/bosh/director/models/ip_address.rb index ecc441816a8..b4d6be4b487 100644 --- a/src/bosh-director/lib/bosh/director/models/ip_address.rb +++ b/src/bosh-director/lib/bosh/director/models/ip_address.rb @@ -23,12 +23,16 @@ def info [ "#{instance.deployment.name}.#{instance.job}/#{instance.index}", network_name, - "#{Bosh::Director::IpAddrOrCidr.new(address_str.to_i)} (#{type})" + "#{Bosh::Director::IpAddrOrCidr.new(address_str).to_cidr_s} (#{type})" ].join(' - ') end def formatted_ip - Bosh::Director::IpAddrOrCidr.new(address).to_s + address.to_cidr_s + end + + def base_address + address.to_string end def type @@ -36,7 +40,7 @@ def type end def address - unless address_str.match?(/\A\d+\z/) + unless address_str.include?('/') || address_str.match?(/\A\d+\z/) info_display = '' begin info_display = info @@ -45,7 +49,19 @@ def address end raise "Unexpected address '#{address_str}' (#{info_display})" end - address_str.to_i + + if address_str.include?('/') + return Bosh::Director::IpAddrOrCidr.new(address_str) + else address_str.match?(/\A\d+\z/) + ip = Bosh::Director::IpAddrOrCidr.new(address_str.to_i) + if ip.ipv6? + prefix = 128 + else + prefix = 32 + end + address_str = Bosh::Director::IpAddrOrCidr.new("#{ip}/#{prefix}") + return address_str + end end def to_s diff --git a/src/bosh-director/lib/bosh/director/models/vm.rb b/src/bosh-director/lib/bosh/director/models/vm.rb index a7465642fc9..a2f82b7c3ea 100644 --- a/src/bosh-director/lib/bosh/director/models/vm.rb +++ b/src/bosh-director/lib/bosh/director/models/vm.rb @@ -19,6 +19,10 @@ def network_spec=(spec) end def ips + ips_cidr.map{ | cidr_ip | cidr_ip.split('/')[0] } + end + + def ips_cidr manual_or_vip_ips.concat(dynamic_ips).uniq end @@ -29,7 +33,7 @@ def manual_or_vip_ips end def dynamic_ips - network_spec.map { |_, network| network['ip'] } + network_spec.map { |_, network| "#{network['ip']}/#{network['prefix']}" } end end end diff --git a/src/bosh-director/lib/bosh/director/network_reservation.rb b/src/bosh-director/lib/bosh/director/network_reservation.rb index f1f99702dda..0ed0e5487c5 100644 --- a/src/bosh-director/lib/bosh/director/network_reservation.rb +++ b/src/bosh-director/lib/bosh/director/network_reservation.rb @@ -19,7 +19,7 @@ def dynamic? private def formatted_ip - IpAddrOrCidr.new(@ip).to_s if @ip + IpAddrOrCidr.new(@ip).to_cidr_s if @ip end end @@ -28,7 +28,7 @@ class ExistingNetworkReservation < NetworkReservation def initialize(instance_model, network, ip, network_type) super(instance_model, network) - @ip = IpAddrOrCidr.new(ip).to_i if ip + @ip = IpAddrOrCidr.new(ip) if ip @network_type = network_type @obsolete = network.instance_of? Bosh::Director::DeploymentPlan::Network end @@ -52,17 +52,26 @@ def self.new_dynamic(instance_model, network) end def self.new_static(instance_model, network, ip) - new(instance_model, network, ip, :static) + cidr_ip = "#{IpAddrOrCidr.new(ip)}/#{network.prefix}" + new(instance_model, network, cidr_ip, :static) end def initialize(instance_model, network, ip, type) super(instance_model, network) - @ip = IpAddrOrCidr.new(ip).to_i if ip + @ip = resolve_ip(ip) if ip @type = type end def resolve_ip(ip) - @ip = IpAddrOrCidr.new(ip).to_i +# if !ip.to_s.match?(/\//) + @ip = IpAddrOrCidr.new(ip) +# else +# if IpAddrOrCidr.new(ip).ipv6? +# @ip = IpAddrOrCidr.new("#{ip}/128") +# else +# @ip = IpAddrOrCidr.new("#{ip}/32") +# end +# end end def resolve_type(type) diff --git a/src/bosh-director/spec/factories.rb b/src/bosh-director/spec/factories.rb index c0ebfa335e3..06193b0a3b7 100644 --- a/src/bosh-director/spec/factories.rb +++ b/src/bosh-director/spec/factories.rb @@ -170,7 +170,7 @@ factory :models_ip_address, class: Bosh::Director::Models::IpAddress do sequence(:network_name) { |i| "ip-address-network-name-#{i}" } sequence(:task_id) { |i| "ip-address-task-id-#{i}" } - address_str { IPAddr.new(Random.rand(IPAddr::IN4MASK.to_i), Socket::AF_INET).to_i.to_s } + address_str { IPAddr.new(Random.rand(IPAddr::IN4MASK), Socket::AF_INET).to_s } static { false } created_at { Time.now } association :instance, factory: :models_instance, strategy: :create diff --git a/src/bosh-director/spec/unit/api/controllers/deployments_controller_spec.rb b/src/bosh-director/spec/unit/api/controllers/deployments_controller_spec.rb index ea7469d0b56..1ca6a4c1817 100644 --- a/src/bosh-director/spec/unit/api/controllers/deployments_controller_spec.rb +++ b/src/bosh-director/spec/unit/api/controllers/deployments_controller_spec.rb @@ -1356,7 +1356,7 @@ def manifest_with_errand(deployment_name='errand') 'state' => 'started', 'uuid' => "instance-#{i}", 'variable_set_id' => (Models::VariableSet.create(deployment: deployment).id), - 'spec' => {'networks' => {'network1' => {'ip' => "#{i}.#{i}.#{i}.#{i}"}}}, + 'spec' => {'networks' => {'network1' => {'ip' => "#{i}.#{i}.#{i}.#{i}", 'prefix' => '32'}}}, } instance_params['availability_zone'] = 'az0' if i == 0 @@ -1368,7 +1368,7 @@ def manifest_with_errand(deployment_name='errand') 'cid' => "cid-#{i}-#{j}", 'instance_id' => instance.id, 'created_at' => time, - 'network_spec' => {'network1' => {'ip' => "#{i}.#{i}.#{j}.#{j}"}}, + 'network_spec' => {'network1' => {'ip' => "#{i}.#{i}.#{j}.#{j}", 'prefix' => '32'}}, } vm = Models::Vm.create(vm_params) @@ -1397,6 +1397,7 @@ def manifest_with_errand(deployment_name='errand') 'active' => vm_is_active, 'az' => {0 => 'az0', 1 => 'az1', nil => nil}[instance_idx], 'ips' => ["#{instance_idx}.#{instance_idx}.#{vm_by_instance}.#{vm_by_instance}"], + 'ips_cidr' => ["#{instance_idx}.#{instance_idx}.#{vm_by_instance}.#{vm_by_instance}/32"], 'vm_created_at' => time.utc.iso8601, 'permanent_nats_credentials' => false, ) @@ -1453,7 +1454,7 @@ def manifest_with_errand(deployment_name='errand') ip_addresses_params = { 'instance_id' => instance.id, 'task_id' => i.to_s, - 'address_str' => ip_to_i("1.2.#{i}.#{j}").to_s, + 'address_str' => ("1.2.#{i}.#{j}/32").to_s, 'vm_id' => vm.id, } Models::IpAddress.create(ip_addresses_params) @@ -1479,6 +1480,70 @@ def manifest_with_errand(deployment_name='errand') 'active' => vm_is_active, 'az' => { 0 => 'az0', 1 => 'az1', nil => nil }[instance_idx], 'ips' => ["1.2.#{instance_idx}.#{vm_by_instance}"], + 'ips_cidr' => ["1.2.#{instance_idx}.#{vm_by_instance}/32"], + 'vm_created_at' => time.utc.iso8601, + 'permanent_nats_credentials' => false, + ) + end + end + + it 'returns ip addresses with prefix as ips_cidr for a vm' do + 9.times do |i| + instance_params = { + 'deployment_id' => deployment.id, + 'job' => "job-#{i}", + 'index' => i, + 'state' => 'started', + 'uuid' => "instance-#{i}", + 'variable_set_id' => (Models::VariableSet.create(deployment: deployment).id) + } + + instance_params['availability_zone'] = 'az0' if i == 0 + instance_params['availability_zone'] = 'az1' if i == 1 + instance = Models::Instance.create(instance_params) + + vm_params = { + 'agent_id' => "agent-#{i}", + 'cid' => "cid-#{i}", + 'instance_id' => instance.id, + 'created_at' => time, + } + + vm = Models::Vm.create(vm_params) + + if j == 0 + instance.active_vm = vm + end + + ip_addresses_params = { + 'instance_id' => instance.id, + 'task_id' => i.to_s, + 'address_str' => ("1.2.#{i}.#{j}/32").to_s, + 'vm_id' => vm.id, + } + Models::IpAddress.create(ip_addresses_params) + end + + get '/test_deployment/vms' + + expect(last_response.status).to eq(200) + body = JSON.parse(last_response.body) + expect(body.size).to eq(18) + + body.sort_by { |instance| instance['agent_id'] }.each_with_index do |instance_with_vm, i| + instance_idx = i / 2 + vm_by_instance = i % 2 + vm_is_active = vm_by_instance == 0 + expect(instance_with_vm).to eq( + 'agent_id' => "agent-#{instance_idx}-#{vm_by_instance}", + 'job' => "job-#{instance_idx}", + 'index' => instance_idx, + 'cid' => "cid-#{instance_idx}-#{vm_by_instance}", + 'id' => "instance-#{instance_idx}", + 'active' => vm_is_active, + 'az' => { 0 => 'az0', 1 => 'az1', nil => nil }[instance_idx], + 'ips' => ["1.2.#{instance_idx}.#{vm_by_instance}"], + 'ips_cidr' => ["1.2.#{instance_idx}.#{vm_by_instance}/32"], 'vm_created_at' => time.utc.iso8601, 'permanent_nats_credentials' => false, ) @@ -1504,7 +1569,7 @@ def manifest_with_errand(deployment_name='errand') 'cid' => "cid-#{i}", 'instance_id' => instance.id, 'created_at' => time, - 'network_spec' => {'network1' => {'ip' => "1.2.3.#{i}"}}, + 'network_spec' => {'network1' => {'ip' => "1.2.3.#{i}", 'prefix' => '32'}}, } vm = Models::Vm.create(vm_params) @@ -1530,6 +1595,7 @@ def manifest_with_errand(deployment_name='errand') 'active' => vm_is_active, 'az' => {0 => 'az0', 1 => 'az1', nil => nil}[i], 'ips' => ["1.2.3.#{i}"], + 'ips_cidr' => ["1.2.3.#{i}/32"], 'vm_created_at' => time.utc.iso8601, 'permanent_nats_credentials' => false, ) @@ -1558,8 +1624,8 @@ def manifest_with_errand(deployment_name='errand') 'created_at' => time, 'instance_id' => instance.id, 'network_spec' => { - 'network1' => { 'ip' => network_spec_ip }, - 'network2' => { 'ip' => vip }, + 'network1' => { 'ip' => network_spec_ip, 'prefix' => '32' }, + 'network2' => { 'ip' => vip, 'prefix' => '32'}, }, } @@ -1567,7 +1633,7 @@ def manifest_with_errand(deployment_name='errand') instance.active_vm = vm ip_addresses_params = { - 'address_str' => ip_to_i(vip).to_s, + 'address_str' => "#{vip}/32", 'instance_id' => instance.id, 'task_id' => '1', 'vm_id' => vm.id, @@ -1587,7 +1653,8 @@ def manifest_with_errand(deployment_name='errand') 'cid' => 'cid', 'id' => 'instance-id', 'index' => 0, - 'ips' => [vip, network_spec_ip], + 'ips' => ["#{vip}", "#{network_spec_ip}"], + 'ips_cidr' => ["#{vip}/32", "#{network_spec_ip}/32"], 'job' => 'job', 'vm_created_at' => time.utc.iso8601, 'permanent_nats_credentials' => false, @@ -1666,6 +1733,7 @@ def manifest_with_errand(deployment_name='errand') 'id' => "instance-#{i}", 'az' => {0 => 'az0', 1 => 'az1', nil => nil}[i], 'ips' => [], + 'ips_cidr' => [], 'vm_created_at' => time.utc.iso8601, 'expects_vm' => true ) @@ -1678,6 +1746,7 @@ def manifest_with_errand(deployment_name='errand') 'id' => "instance-#{i}", 'az' => {0 => 'az0', 1 => 'az1', nil => nil}[i], 'ips' => [], + 'ips_cidr' => [], 'vm_created_at' => nil, 'expects_vm' => true ) @@ -1725,6 +1794,7 @@ def manifest_with_errand(deployment_name='errand') 'id' => "instance-#{i}", 'az' => {0 => 'az0', 1 => 'az1', nil => nil}[i], 'ips' => [], + 'ips_cidr' => [], 'vm_created_at' => nil, 'expects_vm' => true ) @@ -1763,6 +1833,7 @@ def manifest_with_errand(deployment_name='errand') 'id' => "instance-#{i}", 'az' => {0 => 'az0', 1 => 'az1', nil => nil}[i], 'ips' => [], + 'ips_cidr' => [], 'vm_created_at' => nil, 'expects_vm' => true ) @@ -1806,6 +1877,7 @@ def manifest_with_errand(deployment_name='errand') 'id' => 'instance-1', 'az' => nil, 'ips' => [], + 'ips_cidr' => [], 'vm_created_at' => nil, 'expects_vm' => true ) @@ -1830,6 +1902,7 @@ def manifest_with_errand(deployment_name='errand') 'id' => 'instance-1', 'az' => nil, 'ips' => [], + 'ips_cidr' => [], 'vm_created_at' => nil, 'expects_vm' => false ) @@ -1856,6 +1929,7 @@ def manifest_with_errand(deployment_name='errand') 'id' => 'instance-1', 'az' => nil, 'ips' => [], + 'ips_cidr' => [], 'vm_created_at' => nil, 'expects_vm' => false ) diff --git a/src/bosh-director/spec/unit/deployment_plan/dynamic_network_spec.rb b/src/bosh-director/spec/unit/deployment_plan/dynamic_network_spec.rb index 4dae3f9b50a..adcf735148f 100644 --- a/src/bosh-director/spec/unit/deployment_plan/dynamic_network_spec.rb +++ b/src/bosh-director/spec/unit/deployment_plan/dynamic_network_spec.rb @@ -341,7 +341,7 @@ end it 'should fail when for static reservation' do - reservation = Bosh::Director::DesiredNetworkReservation.new_static(instance_model, @network, 1) + reservation = Bosh::Director::DesiredNetworkReservation.new_static(instance_model, @network, "1.1.1.1") expect { @network.network_settings(reservation) }.to raise_error Bosh::Director::NetworkReservationWrongType @@ -537,7 +537,7 @@ describe :validate_reference_from_job do it 'returns true if job has a valid network spec' do - dynamic_network = Bosh::Director::DeploymentPlan::DynamicNetwork.new('dynamic', [], logger) + dynamic_network = Bosh::Director::DeploymentPlan::DynamicNetwork.new('dynamic', [], '32', logger) job_network_spec = {'name' => 'dynamic'} expect { @@ -547,7 +547,7 @@ context 'when network is dynamic but job network spec uses static ips' do it 'raises StaticIPNotSupportedOnDynamicNetwork' do - dynamic_network = Bosh::Director::DeploymentPlan::DynamicNetwork.new('dynamic', [], logger) + dynamic_network = Bosh::Director::DeploymentPlan::DynamicNetwork.new('dynamic', [], '32', logger) job_network_spec = { 'name' => 'dynamic', 'static_ips' => ['192.168.1.10'] diff --git a/src/bosh-director/spec/unit/deployment_plan/instance_group_networks_parser_spec.rb b/src/bosh-director/spec/unit/deployment_plan/instance_group_networks_parser_spec.rb index cf0331b2d70..b195a848def 100644 --- a/src/bosh-director/spec/unit/deployment_plan/instance_group_networks_parser_spec.rb +++ b/src/bosh-director/spec/unit/deployment_plan/instance_group_networks_parser_spec.rb @@ -11,10 +11,10 @@ module Bosh::Director::DeploymentPlan instance_group_network['static_ips'] = ['192.168.1.1', '192.168.1.2'] instance_group end - let(:manifest_networks) { [ManualNetwork.new('a', [], per_spec_logger)] } + let(:manifest_networks) { [ManualNetwork.new('a', [], '32', per_spec_logger)] } context 'when instance group references a network not mentioned in the networks spec' do - let(:manifest_networks) { [ManualNetwork.new('my-network', [], per_spec_logger)] } + let(:manifest_networks) { [ManualNetwork.new('my-network', [], '32', per_spec_logger)] } it 'raises JobUnknownNetwork' do expect do @@ -38,7 +38,7 @@ module Bosh::Director::DeploymentPlan end context 'when instance group network spec references dynamic network with static IPs' do - let(:dynamic_network) { Bosh::Director::DeploymentPlan::DynamicNetwork.new('a', [], per_spec_logger) } + let(:dynamic_network) { Bosh::Director::DeploymentPlan::DynamicNetwork.new('a', [], '32', per_spec_logger) } let(:instance_group_spec) do instance_group = SharedSupport::DeploymentManifestHelper.simple_manifest_with_instance_groups['instance_groups'].first instance_group['networks'] = [{ @@ -66,11 +66,29 @@ module Bosh::Director::DeploymentPlan it 'raises an error' do expect do instance_group_networks_parser.parse(instance_group_spec, 'instance-group-name', manifest_networks) - end.to raise_error Bosh::Director::JobInvalidStaticIPs, "Instance group 'instance-group-name' specifies static IP '192.168.1.2' more than once" + end.to raise_error Bosh::Director::JobInvalidStaticIPs, "Instance group 'instance-group-name' specifies static IP '192.168.1.2/32' more than once" end end context 'when called with a valid instance group spec' do + let(:subnet_spec) do + { + 'range' => '192.168.1.0/24', + 'gateway' => '192.168.1.1' + } + end + let(:manifest_networks) { [ManualNetwork.new('a', [ManualNetworkSubnet.parse('a', subnet_spec, "")], '32', per_spec_logger)] } + + let(:instance_group_spec) do + instance_group = SharedSupport::DeploymentManifestHelper.simple_manifest_with_instance_groups['instance_groups'].first + instance_group['networks'] = [{ + 'name' => 'a', + 'static_ips' => ['192.168.1.1', '192.168.1.2'], + }] + instance_group + end + + it 'adds static ips to instance group networks in order as they are in manifest' do networks = instance_group_networks_parser.parse(instance_group_spec, 'instance-group-name', manifest_networks) @@ -80,17 +98,16 @@ module Bosh::Director::DeploymentPlan name: 'a', static_ips: ['192.168.1.1', '192.168.1.2'], default_for: %w[dns gateway], - deployment_network: manifest_networks.first, - ), + deployment_network: manifest_networks.first ), ) - expect(networks.first.static_ips).to eq([ip_to_i('192.168.1.1'), ip_to_i('192.168.1.2')]) + expect(networks.first.static_ips).to eq([to_ipaddr('192.168.1.1'), to_ipaddr('192.168.1.2')]) end end RSpec::Matchers.define :be_an_instance_group_network do |expected| match do |actual| actual.name == expected.name && - actual.static_ips == expected.static_ips.map { |ip_to_i| IPAddr.new(ip_to_i) } && + actual.static_ips == expected.static_ips.map { |ip| IPAddr.new(ip) } && actual.deployment_network == expected.deployment_network end end diff --git a/src/bosh-director/spec/unit/deployment_plan/instance_group_spec_parser_spec.rb b/src/bosh-director/spec/unit/deployment_plan/instance_group_spec_parser_spec.rb index fa8d9d62ba1..0c13aa1dc5c 100644 --- a/src/bosh-director/spec/unit/deployment_plan/instance_group_spec_parser_spec.rb +++ b/src/bosh-director/spec/unit/deployment_plan/instance_group_spec_parser_spec.rb @@ -32,7 +32,7 @@ module DeploymentPlan ) end let(:deployment_model) { FactoryBot.create(:models_deployment) } - let(:network) { ManualNetwork.new('fake-network-name', [], per_spec_logger) } + let(:network) { ManualNetwork.new('fake-network-name', [], '32', per_spec_logger) } let(:task) { FactoryBot.create(:models_task, id: 42) } let(:task_writer) { Bosh::Director::TaskDBWriter.new(:event_output, task.id) } let(:event_log) { Bosh::Director::EventLog::Log.new(task_writer) } @@ -1002,7 +1002,7 @@ module DeploymentPlan instance_group_spec['instances'] = 3 instance_group_spec['networks'].first['default'] = %w[gateway dns] instance_group_spec['networks'] << instance_group_spec['networks'].first.merge('name' => 'duped-network') # dupe it - duped_network = ManualNetwork.new('duped-network', [], per_spec_logger) + duped_network = ManualNetwork.new('duped-network', [], '32', per_spec_logger) allow(deployment_plan).to receive(:networks).and_return([duped_network, network]) expect do @@ -1029,7 +1029,7 @@ module DeploymentPlan end context 'when there are two networks, each being a separate default' do - let(:network2) { ManualNetwork.new('fake-network-name-2', [], per_spec_logger) } + let(:network2) { ManualNetwork.new('fake-network-name-2', [], '32', per_spec_logger) } it 'picks the only network as default' do instance_group_spec['networks'].first['default'] = ['dns'] diff --git a/src/bosh-director/spec/unit/deployment_plan/instance_network_reservations_spec.rb b/src/bosh-director/spec/unit/deployment_plan/instance_network_reservations_spec.rb index 978dbb72031..033fecea4a4 100644 --- a/src/bosh-director/spec/unit/deployment_plan/instance_network_reservations_spec.rb +++ b/src/bosh-director/spec/unit/deployment_plan/instance_network_reservations_spec.rb @@ -54,15 +54,15 @@ module Bosh::Director describe 'create_from_db' do context 'when there are IP addresses in db' do - let(:ip1) { IPAddr.new('192.168.0.1').to_i } - let(:ip2) { IPAddr.new('192.168.0.2').to_i } + let(:ip1) { Bosh::Director::IpAddrOrCidr.new('192.168.0.1/32') } + let(:ip2) { Bosh::Director::IpAddrOrCidr.new('192.168.0.2/32') } let(:ip_model1) do - FactoryBot.create(:models_ip_address, address_str: ip1.to_s, instance: instance_model, network_name: 'fake-network') + FactoryBot.create(:models_ip_address, address_str: ip1.to_cidr_s, instance: instance_model, network_name: 'fake-network') end let(:ip_model2) do - FactoryBot.create(:models_ip_address, address_str: ip2.to_s, instance: instance_model, network_name: 'fake-network') + FactoryBot.create(:models_ip_address, address_str: ip2.to_cidr_s, instance: instance_model, network_name: 'fake-network') end context 'when there is a last VM with IP addresses' do @@ -209,7 +209,7 @@ module Bosh::Director end let(:dynamic_network) do - DeploymentPlan::DynamicNetwork.new('dynamic-network', [], per_spec_logger) + DeploymentPlan::DynamicNetwork.new('dynamic-network', [], '32', per_spec_logger) end before do allow(deployment).to receive(:network).with('dynamic-network').and_return(dynamic_network) diff --git a/src/bosh-director/spec/unit/deployment_plan/instance_plan_factory_spec.rb b/src/bosh-director/spec/unit/deployment_plan/instance_plan_factory_spec.rb index f5c4f9a67b9..3bdf2da1489 100644 --- a/src/bosh-director/spec/unit/deployment_plan/instance_plan_factory_spec.rb +++ b/src/bosh-director/spec/unit/deployment_plan/instance_plan_factory_spec.rb @@ -45,7 +45,7 @@ module DeploymentPlan let(:range) { IPAddr.new('192.168.1.1/24') } let(:manual_network_subnet) { ManualNetworkSubnet.new('name-7', range, nil, nil, nil, nil, nil, [], []) } - let(:network) { Bosh::Director::DeploymentPlan::ManualNetwork.new('name-7', [manual_network_subnet], per_spec_logger) } + let(:network) { Bosh::Director::DeploymentPlan::ManualNetwork.new('name-7', [manual_network_subnet], '32', per_spec_logger) } let(:ip_repo) { Bosh::Director::DeploymentPlan::IpRepo.new(per_spec_logger) } let(:deployment_plan) do instance_double( diff --git a/src/bosh-director/spec/unit/deployment_plan/instance_plan_spec.rb b/src/bosh-director/spec/unit/deployment_plan/instance_plan_spec.rb index 656ac4ac98e..32d83688c4d 100644 --- a/src/bosh-director/spec/unit/deployment_plan/instance_plan_spec.rb +++ b/src/bosh-director/spec/unit/deployment_plan/instance_plan_spec.rb @@ -120,10 +120,10 @@ module Bosh::Director::DeploymentPlan let(:network) { ManualNetwork.parse(network_spec, [availability_zone], per_spec_logger) } let(:reservation) do reservation = Bosh::Director::DesiredNetworkReservation.new_dynamic(instance_model, network) - reservation.resolve_ip('192.168.1.3') + reservation.resolve_ip('192.168.1.3/32') reservation end - let(:subnet) { DynamicNetworkSubnet.new('10.0.0.1', {}, ['foo-az']) } + let(:subnet) { DynamicNetworkSubnet.new('10.0.0.1', {}, ['foo-az'], '32') } let(:network_plans) do [ NetworkPlanner::Plan.new(reservation: reservation, existing: true), @@ -205,8 +205,7 @@ module Bosh::Director::DeploymentPlan describe 'networks_changed?' do context 'when the instance plan has desired network plans' do - let(:subnet) { DynamicNetworkSubnet.new('10.0.0.1', {}, ['foo-az']) } - let(:existing_network) { DynamicNetwork.new('existing-network', [subnet], per_spec_logger) } + let(:existing_network) { DynamicNetwork.new('existing-network', [subnet], '32', per_spec_logger) } let(:existing_reservation) { Bosh::Director::DesiredNetworkReservation.new_dynamic(existing_instance, existing_network) } let(:network_plans) do [ @@ -295,7 +294,7 @@ module Bosh::Director::DeploymentPlan allow(per_spec_logger).to receive(:debug) expect(per_spec_logger).to receive(:debug).with( 'networks_changed? obsolete reservations: ' \ - "[{type=dynamic, ip=10.0.0.5, network=existing-network, instance=#{instance_model}}]", + "[{type=dynamic, ip=10.0.0.5/32, network=existing-network, instance=#{instance_model}}]", ) instance_plan.networks_changed? end @@ -317,7 +316,7 @@ module Bosh::Director::DeploymentPlan allow(per_spec_logger).to receive(:debug) expect(per_spec_logger).to receive(:debug).with( 'networks_changed? desired reservations: ' \ - "[{type=dynamic, ip=10.0.0.5, network=existing-network, instance=#{instance_model}}]", + "[{type=dynamic, ip=10.0.0.5/32, network=existing-network, instance=#{instance_model}}]", ) instance_plan.networks_changed? end @@ -335,8 +334,7 @@ module Bosh::Director::DeploymentPlan describe 'network_settings_changed?' do context 'when the instance plan has desired network plans' do - let(:subnet) { DynamicNetworkSubnet.new('10.0.0.1', {}, ['foo-az']) } - let(:existing_network) { DynamicNetwork.new('existing-network', [subnet], per_spec_logger) } + let(:existing_network) { DynamicNetwork.new('existing-network', [subnet], '32', per_spec_logger) } let(:existing_reservation) { Bosh::Director::DesiredNetworkReservation.new_dynamic(existing_instance, existing_network) } let(:network_plans) do [ @@ -373,6 +371,7 @@ module Bosh::Director::DeploymentPlan 'a' => { 'type' => 'manual', 'ip' => '192.168.1.3', + 'prefix' => '32', 'netmask' => '255.255.255.0', 'cloud_properties' => {}, 'default' => %w[dns gateway], @@ -400,7 +399,7 @@ module Bosh::Director::DeploymentPlan }, } end - let(:subnet) { DynamicNetworkSubnet.new('8.8.8.8', subnet_cloud_properties, ['foo-az']) } + let(:subnet) { DynamicNetworkSubnet.new('8.8.8.8', subnet_cloud_properties, ['foo-az'], '32') } let(:network_plans) { [NetworkPlanner::Plan.new(reservation: existing_reservation, existing: true)] } let(:subnet_cloud_properties) do {} @@ -517,8 +516,8 @@ module Bosh::Director::DeploymentPlan end context 'when the vm type name has changed' do - let(:subnet) { DynamicNetworkSubnet.new(['10.0.0.1'], {}, ['foo-az']) } - let(:existing_network) { DynamicNetwork.new('a', [subnet], per_spec_logger) } + let(:subnet) { DynamicNetworkSubnet.new(['10.0.0.1'], {}, ['foo-az'], '32') } + let(:existing_network) { DynamicNetwork.new('a', [subnet], '32', per_spec_logger) } let(:existing_reservation) { Bosh::Director::DesiredNetworkReservation.new_dynamic(existing_instance, existing_network) } let(:network_plans) { [NetworkPlanner::Plan.new(reservation: existing_reservation, existing: true)] } @@ -560,8 +559,7 @@ module Bosh::Director::DeploymentPlan context 'when the network has changed' do # everything else should be the same let(:availability_zone) { AvailabilityZone.new('foo-az', 'old' => 'value') } - let(:subnet) { DynamicNetworkSubnet.new('10.0.0.1', {}, ['foo-az']) } - let(:existing_network) { DynamicNetwork.new('existing-network', [subnet], per_spec_logger) } + let(:existing_network) { DynamicNetwork.new('existing-network', [subnet], '32', per_spec_logger) } let(:existing_reservation) { Bosh::Director::DesiredNetworkReservation.new_dynamic(existing_instance, existing_network) } let(:network_plans) do @@ -891,8 +889,7 @@ module Bosh::Director::DeploymentPlan end context 'when the vm type name has changed' do - let(:subnet) { DynamicNetworkSubnet.new('10.0.0.1', {}, ['foo-az']) } - let(:existing_network) { DynamicNetwork.new('a', [subnet], per_spec_logger) } + let(:existing_network) { DynamicNetwork.new('a', [subnet], '32', per_spec_logger) } let(:existing_reservation) { Bosh::Director::DesiredNetworkReservation.new_dynamic(existing_instance, existing_network) } let(:network_plans) { [NetworkPlanner::Plan.new(reservation: existing_reservation, existing: true)] } @@ -934,8 +931,7 @@ module Bosh::Director::DeploymentPlan context 'when the network has changed' do # everything else should be the same let(:availability_zone) { AvailabilityZone.new('foo-az', 'old' => 'value') } - let(:subnet) { DynamicNetworkSubnet.new('10.0.0.1', {}, ['foo-az']) } - let(:existing_network) { DynamicNetwork.new('existing-network', [subnet], per_spec_logger) } + let(:existing_network) { DynamicNetwork.new('existing-network', [subnet], '32', per_spec_logger) } let(:existing_reservation) { Bosh::Director::DesiredNetworkReservation.new_dynamic(existing_instance, existing_network) } let(:network_plans) do @@ -1039,8 +1035,7 @@ module Bosh::Director::DeploymentPlan end context 'when the vm type name has changed' do - let(:subnet) { DynamicNetworkSubnet.new('10.0.0.1', {}, ['foo-az']) } - let(:existing_network) { DynamicNetwork.new('a', [subnet], per_spec_logger) } + let(:existing_network) { DynamicNetwork.new('a', [subnet], '32', per_spec_logger) } let(:existing_reservation) { Bosh::Director::DesiredNetworkReservation.new_dynamic(existing_instance, existing_network) } let(:network_plans) { [NetworkPlanner::Plan.new(reservation: existing_reservation, existing: true)] } @@ -1123,8 +1118,7 @@ module Bosh::Director::DeploymentPlan context 'when the network has changed' do # everything else should be the same let(:availability_zone) { AvailabilityZone.new('foo-az', 'old' => 'value') } - let(:subnet) { DynamicNetworkSubnet.new('10.0.0.1', {}, ['foo-az']) } - let(:existing_network) { DynamicNetwork.new('existing-network', [subnet], per_spec_logger) } + let(:existing_network) { DynamicNetwork.new('existing-network', [subnet], '32', per_spec_logger) } let(:existing_reservation) { Bosh::Director::DesiredNetworkReservation.new_dynamic(existing_instance, existing_network) } let(:network_plans) do @@ -1277,8 +1271,8 @@ module Bosh::Director::DeploymentPlan end describe '#persist_current_spec' do - let(:subnet) { DynamicNetworkSubnet.new('10.0.0.1', {}, ['foo-az']) } - let(:existing_network) { DynamicNetwork.new('a', [subnet], per_spec_logger) } + let(:subnet) { DynamicNetworkSubnet.new('10.0.0.1', {}, ['foo-az'], '32') } + let(:existing_network) { DynamicNetwork.new('a', [subnet], '32', per_spec_logger) } let(:existing_reservation) { Bosh::Director::DesiredNetworkReservation.new_dynamic(existing_instance, existing_network) } let(:network_plans) do @@ -1545,6 +1539,7 @@ module Bosh::Director::DeploymentPlan 'a' => { 'type' => 'manual', 'ip' => '192.168.1.3', + 'prefix' => '32', 'netmask' => '255.255.255.0', 'cloud_properties' => {}, 'dns' => ['192.168.1.1', '192.168.1.2'], @@ -2033,7 +2028,7 @@ module Bosh::Director::DeploymentPlan describe '#remove_network_plans_for_ips' do let(:plan1) do reservation = Bosh::Director::DesiredNetworkReservation.new_dynamic(instance_model, network) - reservation.resolve_ip('192.168.1.25') + reservation.resolve_ip('192.168.1.25/32') NetworkPlanner::Plan.new(reservation: reservation, existing: false, obsolete: true) end @@ -2061,8 +2056,8 @@ module Bosh::Director::DeploymentPlan let(:network_plans) { [plan1, plan2, plan3, plan4] } - let(:ip1) { IPAddr.new('192.168.1.25').to_i } - let(:ip2) { IPAddr.new('192.168.1.26').to_i } + let(:ip1) { IPAddr.new('192.168.1.25/32') } + let(:ip2) { IPAddr.new('192.168.1.26/32') } let(:ip_address1) { FactoryBot.create(:models_ip_address, address_str: ip1.to_s) } let(:ip_address2) { FactoryBot.create(:models_ip_address, address_str: ip2.to_s) } diff --git a/src/bosh-director/spec/unit/deployment_plan/instance_repository_spec.rb b/src/bosh-director/spec/unit/deployment_plan/instance_repository_spec.rb index 5d83da4aed9..e70b9ffb2d9 100644 --- a/src/bosh-director/spec/unit/deployment_plan/instance_repository_spec.rb +++ b/src/bosh-director/spec/unit/deployment_plan/instance_repository_spec.rb @@ -7,7 +7,7 @@ subject(:instance_repository) { Bosh::Director::DeploymentPlan::InstanceRepository.new(logger, variables_interpolator) } let(:variables_interpolator) { instance_double(Bosh::Director::ConfigServer::VariablesInterpolator) } - let(:network) { Bosh::Director::DeploymentPlan::DynamicNetwork.new('name-7', [], logger) } + let(:network) { Bosh::Director::DeploymentPlan::DynamicNetwork.new('name-7', [], '32', logger) } let(:deployment_plan) do ip_repo = Bosh::Director::DeploymentPlan::IpRepo.new(logger) diff --git a/src/bosh-director/spec/unit/deployment_plan/ip_provider/ip_provider_spec.rb b/src/bosh-director/spec/unit/deployment_plan/ip_provider/ip_provider_spec.rb index 23b00411e7f..1f9543ec54a 100644 --- a/src/bosh-director/spec/unit/deployment_plan/ip_provider/ip_provider_spec.rb +++ b/src/bosh-director/spec/unit/deployment_plan/ip_provider/ip_provider_spec.rb @@ -161,7 +161,7 @@ module Bosh::Director::DeploymentPlan Bosh::Director::ExistingNetworkReservation.new( instance_model, manual_network, - '192.168.1.2', + '192.168.1.2/32', 'manual', ) end @@ -289,7 +289,7 @@ module Bosh::Director::DeploymentPlan it 'raises an error' do reservation = Bosh::Director::DesiredNetworkReservation.new_dynamic(instance_model, manual_network) - reservation.resolve_ip(IPAddr.new('192.168.1.11').to_i) + reservation.resolve_ip(Bosh::Director::IpAddrOrCidr.new('192.168.1.11')) expect do ip_provider.reserve(reservation) end.to raise_error Bosh::Director::NetworkReservationIpReserved, @@ -307,7 +307,7 @@ module Bosh::Director::DeploymentPlan expect { ip_provider.reserve(reservation) }.to raise_error Bosh::Director::NetworkReservationWrongType, - "IP '192.168.1.2' on network 'my-manual-network' does not belong to dynamic pool" + "IP '192.168.1.2/32' on network 'my-manual-network' does not belong to dynamic pool" end end end @@ -348,7 +348,7 @@ module Bosh::Director::DeploymentPlan expect { ip_provider.reserve(reservation) }.to raise_error Bosh::Director::NetworkReservationWrongType, - "IP '192.168.1.2' on network 'my-manual-network' does not belong to dynamic pool" + "IP '192.168.1.2/32' on network 'my-manual-network' does not belong to dynamic pool" end end end @@ -468,7 +468,7 @@ module Bosh::Director::DeploymentPlan it 'adds the ip address to the ip repository' do ip_provider.reserve(reservation) - expect(reservation.ip).to eq(IPAddr.new('1.1.1.1').to_i) + expect(reservation.ip).to eq('1.1.1.1') end end @@ -477,7 +477,7 @@ module Bosh::Director::DeploymentPlan it 'allocates an ip address for the reservation' do ip_provider.reserve(reservation) - expect(reservation.ip).to eq(IPAddr.new('1.1.1.1').to_i) + expect(reservation.ip).to eq('1.1.1.1') end context 'and there are no available vips' do diff --git a/src/bosh-director/spec/unit/deployment_plan/ip_provider/ip_repo_spec.rb b/src/bosh-director/spec/unit/deployment_plan/ip_provider/ip_repo_spec.rb index 946047a9ce9..47cee418b16 100644 --- a/src/bosh-director/spec/unit/deployment_plan/ip_provider/ip_repo_spec.rb +++ b/src/bosh-director/spec/unit/deployment_plan/ip_provider/ip_repo_spec.rb @@ -36,6 +36,22 @@ module Bosh::Director::DeploymentPlan ) end + let(:subnet_with_prefix) do + ManualNetworkSubnet.parse( + network.name, + network_spec['subnets'].first.merge('prefix' => '31'), + availability_zones, + ) + end + + let(:subnet_with_too_big_prefix) do + ManualNetworkSubnet.parse( + network.name, + network_spec['subnets'].first.merge('prefix' => '30'), + availability_zones, + ) + end + let(:other_network_spec) { network_spec.merge('name' => 'my-other-manual-network') } let(:other_network) do ManualNetwork.parse( @@ -56,7 +72,7 @@ module Bosh::Director::DeploymentPlan before { fake_job } def cidr_ip(ip) - IPAddr.new(ip).to_i + Bosh::Director::IpAddrOrCidr.new(ip) end context :add do @@ -97,6 +113,7 @@ def dynamic_reservation_with_ip(ip) context 'from Dynamic to Static' do it 'update type of reservation' do dynamic_reservation = dynamic_reservation_with_ip('192.168.1.5') + ip_repo.add(dynamic_reservation) expect(Bosh::Director::Models::IpAddress.count).to eq(1) @@ -169,7 +186,7 @@ def dynamic_reservation_with_ip(ip) ip_repo.add(reservation) saved_address = Bosh::Director::Models::IpAddress.order(:address_str).last - expect(saved_address.address_str).to eq(cidr_ip('192.168.1.5').to_s) + expect(saved_address.address_str).to eq(cidr_ip('192.168.1.5').to_cidr_s) expect(saved_address.network_name).to eq('my-manual-network') expect(saved_address.task_id).to eq('fake-task-id') expect(saved_address.created_at).to_not be_nil @@ -310,22 +327,82 @@ def dynamic_reservation_with_ip(ip) end end + context 'when a prefix is assigned to the subnet' do + let(:reservation) { Bosh::Director::DesiredNetworkReservation.new_dynamic(instance_model, network) } + it 'reserves the prefix address' do + ip_address = ip_repo.allocate_dynamic_ip(reservation, subnet_with_prefix) + + expect(ip_address).to eq(cidr_ip('192.168.1.2/31')) + end + + it 'reserves the next available prefix address' do + ip_address = ip_repo.allocate_dynamic_ip(other_reservation, other_subnet) + + expected_ip_address = cidr_ip('192.168.1.2') + expect(ip_address).to eq(expected_ip_address) + + ip_address = ip_repo.allocate_dynamic_ip(reservation, subnet_with_prefix) + + expected_ip_address = cidr_ip('192.168.1.4/31') + expect(ip_address).to eq(expected_ip_address) + + ip_address = ip_repo.allocate_dynamic_ip(other_reservation, other_subnet) + + expected_ip_address = cidr_ip('192.168.1.3') + expect(ip_address).to eq(expected_ip_address) + + ip_address = ip_repo.allocate_dynamic_ip(other_reservation, other_subnet) + + expected_ip_address = cidr_ip('192.168.1.6') + expect(ip_address).to eq(expected_ip_address) + end + + it 'should stop retrying and return nil if no sufficient range is available' do + ip_address = ip_repo.allocate_dynamic_ip(other_reservation, other_subnet) + + expected_ip_address = cidr_ip('192.168.1.2') + expect(ip_address).to eq(expected_ip_address) + + ip_address = ip_repo.allocate_dynamic_ip(reservation, subnet_with_prefix) + + expected_ip_address = cidr_ip('192.168.1.4/31') + expect(ip_address).to eq(expected_ip_address) + + ip_address = ip_repo.allocate_dynamic_ip(other_reservation, other_subnet) + + expected_ip_address = cidr_ip('192.168.1.3') + expect(ip_address).to eq(expected_ip_address) + + expect do + ip_address = ip_repo.allocate_dynamic_ip(other_reservation, subnet_with_prefix) + expect(ip_address).to be_nil + end.not_to(change { Bosh::Director::Models::IpAddress.count }) + end + + it 'should stop retrying and return nil if no sufficient range is available' do + expect do + ip = ip_repo.allocate_dynamic_ip(reservation, subnet_with_too_big_prefix) + expect(ip).to be_nil + end.not_to(change { Bosh::Director::Models::IpAddress.count }) + end + end + context 'when reserving IP fails' do def fail_saving_ips(ips, fail_error) original_saves = {} ips.each do |ip| ip_address = Bosh::Director::Models::IpAddress.new( - address_str: ip.to_s, + address_str: ip.to_cidr_s, network_name: 'my-manual-network', instance: instance_model, task_id: Bosh::Director::Config.current_job.task_id ) original_save = ip_address.method(:save) - original_saves[ip.to_s] = original_save + original_saves[ip.to_cidr_s] = original_save end allow_any_instance_of(Bosh::Director::Models::IpAddress).to receive(:save) do |model| - if ips.map(&:to_s).include?(model.address_str) + if ips.map(&:to_cidr_s).include?(model.address_str) original_save = original_saves[model.address_str] original_save.call raise fail_error @@ -425,7 +502,7 @@ def fail_saving_ips(ips, fail_error) end.to change { Bosh::Director::Models::IpAddress.count }.by(1) ip_address = instance_model.ip_addresses.first - expect(ip_address.address_str.to_i).to eq(cidr_ip('69.69.69.69')) + expect(ip_address.address_str).to eq(cidr_ip('69.69.69.69').to_cidr_s) end context 'when there are no vips defined in the network' do @@ -506,7 +583,7 @@ def fail_saving_ips(ips, fail_error) it 'deletes IP address' do expect { - ip_repo.delete('192.168.1.5') + ip_repo.delete('192.168.1.5/32') }.to change { Bosh::Director::Models::IpAddress.all.size }.by(-1) diff --git a/src/bosh-director/spec/unit/deployment_plan/manual_network_spec.rb b/src/bosh-director/spec/unit/deployment_plan/manual_network_spec.rb index d09ba135424..4deadcea483 100644 --- a/src/bosh-director/spec/unit/deployment_plan/manual_network_spec.rb +++ b/src/bosh-director/spec/unit/deployment_plan/manual_network_spec.rb @@ -133,6 +133,7 @@ expect(manual_network.network_settings(reservation, [])).to eq( 'type' => 'manual', 'ip' => '192.168.1.2', + 'prefix' => '32', 'netmask' => '255.255.255.0', 'cloud_properties' => {}, 'gateway' => '192.168.1.1', @@ -151,6 +152,7 @@ expect(manual_network.network_settings(reservation)).to eq( 'type' => 'manual', 'ip' => '192.168.1.2', + 'prefix' => '32', 'netmask' => '255.255.255.0', 'cloud_properties' => {}, 'gateway' => '192.168.1.1', @@ -159,6 +161,32 @@ ) end + context 'when a prefix is maintained for a subnet' do + let(:network_spec) do + cloud_config_hash['networks'].first['subnets'].first['prefix'] = '30' + cloud_config_hash['networks'].first + end + + it 'should set the correct prefix if one is maintained for the subnet' do + reservation = Bosh::Director::DesiredNetworkReservation.new_static( + instance_model, + manual_network, + '192.168.1.2', + ) + + expect(manual_network.network_settings(reservation)).to eq( + 'type' => 'manual', + 'ip' => '192.168.1.0', + 'prefix' => '30', + 'netmask' => '255.255.255.0', + 'cloud_properties' => {}, + 'gateway' => '192.168.1.1', + 'dns' => ['192.168.1.1', '192.168.1.2'], + 'default' => %w[dns gateway], + ) + end + end + it 'should fail when there is no IP' do reservation = Bosh::Director::DesiredNetworkReservation.new_dynamic( instance_model, diff --git a/src/bosh-director/spec/unit/deployment_plan/manual_network_subnet_spec.rb b/src/bosh-director/spec/unit/deployment_plan/manual_network_subnet_spec.rb index 185363dccb1..ef23ba23195 100644 --- a/src/bosh-director/spec/unit/deployment_plan/manual_network_subnet_spec.rb +++ b/src/bosh-director/spec/unit/deployment_plan/manual_network_subnet_spec.rb @@ -328,6 +328,54 @@ def make_managed_subnet(properties, availability_zones) expect(subnet.restricted_ips).to include(ip1.to_i) expect(subnet.restricted_ips).to include(ip2.to_i) end + + it 'should create a subnet spec with prefix' do + subnet = make_subnet( + { + 'range' => '192.168.0.0/24', + 'gateway' => '192.168.0.254', + 'cloud_properties' => {'foo' => 'bar'}, + 'prefix' => 25 + }, + [], + ) + + expect(subnet.range.to_cidr_s).to eq('192.168.0.0/24') + expect(subnet.netmask).to eq('255.255.255.0') + expect(subnet.gateway).to eq('192.168.0.254') + expect(subnet.prefix).to eq(25) + expect(subnet.dns).to eq(nil) + end + + + it 'should fail if the prefix size is larger than the range' do + expect { + make_subnet( + { + 'range' => '192.168.0.0/24', + 'gateway' => '192.168.0.254', + 'cloud_properties' => {'foo' => 'bar'}, + 'prefix' => 23 + }, + [], + )}.to raise_error(Bosh::Director::NetworkPrefixSizeTooBig, + "Prefix size '23' is larger than range prefix '24'") + end + + it 'should fail if a static ip provided is not a base address of the prefix' do + expect { + make_subnet( + { + 'range' => '192.168.0.0/24', + 'gateway' => '192.168.0.254', + 'static' => ['192.168.0.64','192.168.0.128','192.168.0.191'], + 'cloud_properties' => {'foo' => 'bar'}, + 'prefix' => 26 + }, + [], + )}.to raise_error(Bosh::Director::NetworkPrefixStaticIpNotBaseAddress, + "Static IP '192.168.0.191' is not a base address of the prefix '26'") + end end describe :overlaps? do @@ -397,26 +445,34 @@ def make_managed_subnet(properties, availability_zones) let(:reserved) { ['192.168.0.50-192.168.0.60'] } it 'returns false' do - expect(subnet.is_reservable?(IPAddr.new('192.168.0.55'))).to be_falsey + expect(subnet.is_reservable?(Bosh::Director::IpAddrOrCidr.new('192.168.0.55'))).to be_falsey end end context 'when subnet reserved does not include IP' do it 'returns true' do - expect(subnet.is_reservable?(IPAddr.new('192.168.0.55'))).to be_truthy + expect(subnet.is_reservable?(Bosh::Director::IpAddrOrCidr.new('192.168.0.55'))).to be_truthy + end + end + + context 'when subnet reserved does not include any address of a cidr' do + it 'returns true' do + let(:reserved) { ['192.168.0.50-192.168.0.60'] } + + expect(subnet.is_reservable?(Bosh::Director::IpAddrOrCidr.new('192.168.0.55'))).to be_truthy end end end context 'when subnet range does not include IP' do it 'returns false' do - expect(subnet.is_reservable?(IPAddr.new('192.168.10.55'))).to be_falsey + expect(subnet.is_reservable?(Bosh::Director::IpAddrOrCidr.new('192.168.10.55'))).to be_falsey end end context 'when subnet range is not the same IP version' do it 'returns false' do - expect(subnet.is_reservable?(IPAddr.new('f1ee:0000:0000:0000:0000:0000:0000:0001'))).to be_falsey + expect(subnet.is_reservable?(Bosh::Director::IpAddrOrCidr.new('f1ee:0000:0000:0000:0000:0000:0000:0001'))).to be_falsey end end end diff --git a/src/bosh-director/spec/unit/deployment_plan/network_planner/reservation_reconciler_spec.rb b/src/bosh-director/spec/unit/deployment_plan/network_planner/reservation_reconciler_spec.rb index 1b090973a70..4e41552e31d 100644 --- a/src/bosh-director/spec/unit/deployment_plan/network_planner/reservation_reconciler_spec.rb +++ b/src/bosh-director/spec/unit/deployment_plan/network_planner/reservation_reconciler_spec.rb @@ -9,20 +9,20 @@ module Bosh::Director::DeploymentPlan let(:instance_model) { FactoryBot.create(:models_instance, availability_zone: initial_az) } let(:instance) { instance_double(Instance, model: instance_model) } let(:variables_interpolator) { instance_double(Bosh::Director::ConfigServer::VariablesInterpolator) } - let(:network) { ManualNetwork.new('my-network', subnets, per_spec_logger) } + let(:network) { ManualNetwork.new('my-network', subnets, '32', per_spec_logger) } let(:subnets) do [ ManualNetworkSubnet.new( 'my-network', IPAddr.new('192.168.1.0/24'), nil, nil, nil, nil, ['zone_1'], [], - ['192.168.1.10'] + ['192.168.1.10'], nil, nil, '32' ), ManualNetworkSubnet.new( 'my-network', IPAddr.new('192.168.2.0/24'), nil, nil, nil, nil, ['zone_2'], [], - ['192.168.2.10'] + ['192.168.2.10'], nil, nil, '32' ), ] end @@ -40,13 +40,13 @@ module Bosh::Director::DeploymentPlan let(:desired_az) { AvailabilityZone.new('zone_1', {}) } let(:existing_reservations) do [ - Bosh::Director::ExistingNetworkReservation.new(instance_model, network, '192.168.1.2', 'manual'), - Bosh::Director::ExistingNetworkReservation.new(instance_model, network, '192.168.1.3', 'manual'), + Bosh::Director::ExistingNetworkReservation.new(instance_model, network, '192.168.1.2/32', 'manual'), + Bosh::Director::ExistingNetworkReservation.new(instance_model, network, '192.168.1.3/32', 'manual'), ] end let(:dynamic_network_reservation) { Bosh::Director::DesiredNetworkReservation.new_dynamic(instance_model, network) } - let(:static_network_reservation) { Bosh::Director::DesiredNetworkReservation.new_static(instance_model, network, '192.168.1.2') } + let(:static_network_reservation) { Bosh::Director::DesiredNetworkReservation.new_static(instance_model, network, '192.168.1.2/32') } let(:should_create_swap_delete?) { false } @@ -55,9 +55,9 @@ module Bosh::Director::DeploymentPlan end context 'when the instance group is on a dynamic network' do - let(:network) { DynamicNetwork.new('my-network', subnets, per_spec_logger) } + let(:network) { DynamicNetwork.new('my-network', subnets, '32', per_spec_logger) } let(:desired_reservations) { [dynamic_network_reservation] } - let(:existing_reservations) { [Bosh::Director::ExistingNetworkReservation.new(instance_model, network, '192.168.1.2', 'dynamic')] } + let(:existing_reservations) { [Bosh::Director::ExistingNetworkReservation.new(instance_model, network, '192.168.1.2/32', 'dynamic')] } it 'uses the existing reservation' do existing_reservations.map { |reservation| reservation.resolve_type(:dynamic) } @@ -82,7 +82,7 @@ module Bosh::Director::DeploymentPlan let(:existing_reservations) do [ - Bosh::Director::ExistingNetworkReservation.new(instance_model, network, '192.168.1.2', 'manual'), + Bosh::Director::ExistingNetworkReservation.new(instance_model, network, '192.168.1.2/32', 'manual'), ] end @@ -178,8 +178,8 @@ module Bosh::Director::DeploymentPlan let(:existing_reservations) do [ - Bosh::Director::ExistingNetworkReservation.new(instance_model, network, '192.168.1.2', 'manual'), - Bosh::Director::ExistingNetworkReservation.new(instance_model, network, '192.168.1.3', 'manual'), + Bosh::Director::ExistingNetworkReservation.new(instance_model, network, '192.168.1.2/32', 'manual'), + Bosh::Director::ExistingNetworkReservation.new(instance_model, network, '192.168.1.3/32', 'manual'), ] end @@ -202,7 +202,7 @@ module Bosh::Director::DeploymentPlan end describe 'changes to specifications about the instances network' do - let(:existing_reservations) { [Bosh::Director::ExistingNetworkReservation.new(instance_model, network, '192.168.1.2', 'manual')] } + let(:existing_reservations) { [Bosh::Director::ExistingNetworkReservation.new(instance_model, network, '192.168.1.2/32', 'manual')] } let(:desired_reservations) { [Bosh::Director::DesiredNetworkReservation.new_dynamic(instance_model, network2)] } before do @@ -210,7 +210,7 @@ module Bosh::Director::DeploymentPlan end context 'when the existing ip is part of a vip static network but the desired network is a global vip network' do - let(:network) { VipNetwork.new('static-vip-network', nil, [], per_spec_logger) } + let(:network) { VipNetwork.new('static-vip-network', nil, [], '32', per_spec_logger) } let(:network_spec) do { 'name' => 'global-vip-network', @@ -221,7 +221,7 @@ module Bosh::Director::DeploymentPlan end let(:network2) { VipNetwork.parse(network_spec, [], per_spec_logger) } - let(:existing_reservations) { [Bosh::Director::ExistingNetworkReservation.new(instance_model, network, '192.168.1.2', 'vip')] } + let(:existing_reservations) { [Bosh::Director::ExistingNetworkReservation.new(instance_model, network, '192.168.1.2/32', 'vip')] } let(:initial_az) { 'zone_1' } it 'should keep the existing reservation' do network_plans = network_planner.reconcile(existing_reservations) @@ -241,7 +241,7 @@ module Bosh::Director::DeploymentPlan context 'when the network name changes' do let(:initial_az) { 'zone_1' } - let(:network2) { ManualNetwork.new('my-network-2', subnets, per_spec_logger) } + let(:network2) { ManualNetwork.new('my-network-2', subnets, '32', per_spec_logger) } it 'should keep existing reservations' do network_plans = network_planner.reconcile(existing_reservations) @@ -282,7 +282,7 @@ module Bosh::Director::DeploymentPlan 'my-network', IPAddr.new('192.168.1.0/24'), nil, nil, nil, nil, [], [], - ['192.168.1.10'] + ['192.168.1.10'], '32' ), ] end @@ -306,14 +306,14 @@ module Bosh::Director::DeploymentPlan context 'when the instance model has no az' do let(:initial_az) { '' } let(:desired_az) { nil } - let(:network2) { ManualNetwork.new('my-network-2', subnets, per_spec_logger) } + let(:network2) { ManualNetwork.new('my-network-2', subnets, '32', per_spec_logger) } let(:subnets) do [ ManualNetworkSubnet.new( 'my-network', IPAddr.new('192.168.1.0/24'), nil, nil, nil, nil, [], [], - ['192.168.1.10'] + ['192.168.1.10'], nil, nil, '32' ), ] end @@ -334,7 +334,7 @@ module Bosh::Director::DeploymentPlan end context 'when the network type changes to dynamic' do - let(:network2) { DynamicNetwork.new('my-network-2', subnets, per_spec_logger) } + let(:network2) { DynamicNetwork.new('my-network-2', subnets, '32', per_spec_logger) } it 'should have a new reservation' do network_plans = network_planner.reconcile(existing_reservations) @@ -352,8 +352,8 @@ module Bosh::Director::DeploymentPlan end context 'when the network type changes to manual' do - let(:network) { DynamicNetwork.new('my-network', subnets, per_spec_logger) } - let(:network2) { ManualNetwork.new('my-network-2', subnets, per_spec_logger) } + let(:network) { DynamicNetwork.new('my-network', subnets, '32', per_spec_logger) } + let(:network2) { ManualNetwork.new('my-network-2', subnets, '32', per_spec_logger) } it 'should have a new reservation' do network_plans = network_planner.reconcile(existing_reservations) @@ -428,7 +428,7 @@ module Bosh::Director::DeploymentPlan context 'when existing reservation availability zones do not match job availability zones' do let(:desired_az) { AvailabilityZone.new('zone_2', {}) } - let(:existing_reservations) { [Bosh::Director::ExistingNetworkReservation.new(instance_model, network, '192.168.1.2', 'manual')] } + let(:existing_reservations) { [Bosh::Director::ExistingNetworkReservation.new(instance_model, network, '192.168.1.2/32', 'manual')] } let(:desired_reservations) { [Bosh::Director::DesiredNetworkReservation.new_dynamic(instance_model, network)] } before { existing_reservations[0].resolve_type(:dynamic) } @@ -462,7 +462,7 @@ module Bosh::Director::DeploymentPlan context 'when existing reservation and job do not belong to any availability zone' do let(:desired_az) { nil } - let(:existing_reservations) { [Bosh::Director::ExistingNetworkReservation.new(instance_model, network, '192.168.1.2', 'manual')] } + let(:existing_reservations) { [Bosh::Director::ExistingNetworkReservation.new(instance_model, network, '192.168.1.2/32', 'manual')] } let(:desired_reservations) { [Bosh::Director::DesiredNetworkReservation.new_dynamic(instance_model, network)] } let(:subnets) do [ @@ -470,7 +470,9 @@ module Bosh::Director::DeploymentPlan 'my-network', IPAddr.new('192.168.1.0/24'), nil, nil, nil, nil, nil, [], - ['192.168.1.10'] + ['192.168.1.10'], + nil, nil, + '32' ), ] end @@ -485,7 +487,7 @@ module Bosh::Director::DeploymentPlan expect(obsolete_plans.count).to eq(0) expect(existing_plans.count).to eq(1) - expect(to_ipaddr(existing_plans.first.reservation.ip)).to eq('192.168.1.2') + expect(to_ipaddr(existing_plans.first.reservation.ip)).to eq('192.168.1.2/32') expect(desired_plans.count).to eq(0) end end @@ -494,8 +496,8 @@ module Bosh::Director::DeploymentPlan let(:dynamic_network_reservation) { Bosh::Director::DesiredNetworkReservation.new_dynamic(instance_model, network) } let(:desired_reservations) do [ - Bosh::Director::DesiredNetworkReservation.new_static(instance_model, network, '192.168.1.2'), - Bosh::Director::DesiredNetworkReservation.new_static(instance_model, network, '192.168.1.4'), + Bosh::Director::DesiredNetworkReservation.new_static(instance_model, network, '192.168.1.2/32'), + Bosh::Director::DesiredNetworkReservation.new_static(instance_model, network, '192.168.1.4/32'), dynamic_network_reservation, ] end diff --git a/src/bosh-director/spec/unit/deployment_plan/network_settings_spec.rb b/src/bosh-director/spec/unit/deployment_plan/network_settings_spec.rb index 8b5ca81aa51..b1048f0090e 100644 --- a/src/bosh-director/spec/unit/deployment_plan/network_settings_spec.rb +++ b/src/bosh-director/spec/unit/deployment_plan/network_settings_spec.rb @@ -49,6 +49,10 @@ module Bosh::Director::DeploymentPlan let(:plan) { instance_double(Planner, name: 'fake-deployment') } let(:use_short_dns_addresses) { false } let(:use_link_dns_addresses) { false } + let(:dynamic_network) do + subnets = [DynamicNetworkSubnet.new(['1.2.3.4'], {'foo' => 'bar'}, 'az-1', '32')] + DynamicNetwork.new('net_a', subnets, '32', per_spec_logger) + end before do allow_any_instance_of(Bosh::Director::DnsEncoder).to receive(:num_for_uuid) @@ -61,11 +65,6 @@ module Bosh::Director::DeploymentPlan describe '#to_hash' do context 'dynamic network' do - let(:dynamic_network) do - subnets = [DynamicNetworkSubnet.new(['1.2.3.4'], {'foo' => 'bar'}, 'az-1')] - DynamicNetwork.new('net_a', subnets, per_spec_logger) - end - let(:reservations) { [Bosh::Director::DesiredNetworkReservation.new_dynamic(nil, dynamic_network)] } it 'returns the network settings plus current IP, Netmask & Gateway from agent state' do @@ -130,10 +129,6 @@ module Bosh::Director::DeploymentPlan end context 'when it is a dynamic network' do - let(:dynamic_network) do - subnets = [DynamicNetworkSubnet.new(['1.2.3.4'], {'foo' => 'bar'}, 'az-1')] - DynamicNetwork.new('net_a', subnets, per_spec_logger) - end let(:reservations) {[Bosh::Director::DesiredNetworkReservation.new_dynamic(nil, dynamic_network)]} context 'when local dns is disabled' do @@ -251,10 +246,6 @@ module Bosh::Director::DeploymentPlan end context 'when it is a dynamic network' do - let(:dynamic_network) do - subnets = [DynamicNetworkSubnet.new(['1.2.3.4'], {'foo' => 'bar'}, 'az-1')] - DynamicNetwork.new('net_a', subnets, per_spec_logger) - end let(:reservations) {[Bosh::Director::DesiredNetworkReservation.new_dynamic(nil, dynamic_network)]} context 'when local dns is disabled' do @@ -291,11 +282,6 @@ module Bosh::Director::DeploymentPlan describe '#network_addresses' do context 'dynamic network' do - let(:dynamic_network) do - subnets = [DynamicNetworkSubnet.new(['1.2.3.4'], {'foo' => 'bar'}, 'az-1')] - DynamicNetwork.new('net_a', subnets, per_spec_logger) - end - let(:reservations) {[Bosh::Director::DesiredNetworkReservation.new_dynamic(nil, dynamic_network)]} context 'when DNS entries are requested' do it 'includes the network name and domain record' do @@ -379,8 +365,8 @@ module Bosh::Director::DeploymentPlan context 'when it is a dynamic network' do let(:dynamic_network) do - subnets = [DynamicNetworkSubnet.new(['1.2.3.4'], { 'foo' => 'bar' }, 'az-1')] - DynamicNetwork.new('net_a', subnets, per_spec_logger) + subnets = [DynamicNetworkSubnet.new(['1.2.3.4'], { 'foo' => 'bar' }, 'az-1', '32')] + DynamicNetwork.new('net_a', subnets, '32', per_spec_logger) end let(:reservations) { [Bosh::Director::DesiredNetworkReservation.new_dynamic(nil, dynamic_network)] } @@ -493,8 +479,8 @@ module Bosh::Director::DeploymentPlan context 'when it is a dynamic network' do let(:dynamic_network) do - subnets = [DynamicNetworkSubnet.new(['1.2.3.4'], { 'foo' => 'bar' }, 'az-1')] - DynamicNetwork.new('net_a', subnets, per_spec_logger) + subnets = [DynamicNetworkSubnet.new(['1.2.3.4'], { 'foo' => 'bar' }, 'az-1', '32')] + DynamicNetwork.new('net_a', subnets, '32', per_spec_logger) end let(:reservations) { [Bosh::Director::DesiredNetworkReservation.new_dynamic(nil, dynamic_network)] } @@ -531,13 +517,13 @@ module Bosh::Director::DeploymentPlan context 'when there are multiple network reservations' do let(:dynamic_network) do - subnets = [DynamicNetworkSubnet.new(['1.2.3.4'], { 'foo' => 'bar' }, 'az-1')] - DynamicNetwork.new('net_a', subnets, per_spec_logger) + subnets = [DynamicNetworkSubnet.new(['1.2.3.4'], { 'foo' => 'bar' }, 'az-1', '32')] + DynamicNetwork.new('net_a', subnets, '32', per_spec_logger) end let(:dynamic_network2) do - subnets = [DynamicNetworkSubnet.new(['9.8.7.6'], { 'bob' => 'joe' }, 'az-1')] - DynamicNetwork.new('net_b', subnets, per_spec_logger) + subnets = [DynamicNetworkSubnet.new(['9.8.7.6'], { 'bob' => 'joe' }, 'az-1', '32')] + DynamicNetwork.new('net_b', subnets, '32', per_spec_logger) end let(:reservations) do @@ -561,8 +547,8 @@ module Bosh::Director::DeploymentPlan context 'dynamic network' do let(:dynamic_network) do - subnets = [DynamicNetworkSubnet.new(['1.2.3.4'], { 'foo' => 'bar' }, 'az-1')] - DynamicNetwork.new('net_a', subnets, per_spec_logger) + subnets = [DynamicNetworkSubnet.new(['1.2.3.4'], { 'foo' => 'bar' }, 'az-1', '32')] + DynamicNetwork.new('net_a', subnets, '32', per_spec_logger) end let(:reservations) { [Bosh::Director::DesiredNetworkReservation.new_dynamic(nil, dynamic_network)] } diff --git a/src/bosh-director/spec/unit/deployment_plan/placement_planner/networks_to_static_ips_spec.rb b/src/bosh-director/spec/unit/deployment_plan/placement_planner/networks_to_static_ips_spec.rb index 52550914189..3d443475619 100644 --- a/src/bosh-director/spec/unit/deployment_plan/placement_planner/networks_to_static_ips_spec.rb +++ b/src/bosh-director/spec/unit/deployment_plan/placement_planner/networks_to_static_ips_spec.rb @@ -107,7 +107,7 @@ module Bosh::Director::DeploymentPlan ['192.168.1.10', '192.168.1.11', '192.168.1.12', '192.168.1.13', '192.168.1.14']) ] end - let(:deployment_network) { ManualNetwork.new('network_A', deployment_subnets, nil) } + let(:deployment_network) { ManualNetwork.new('network_A', deployment_subnets, '32', nil) } let(:job_networks) do [FactoryBot.build(:deployment_plan_job_network, name: 'network_A', static_ips: job_static_ips, deployment_network: deployment_network)] end diff --git a/src/bosh-director/spec/unit/deployment_plan/placement_planner/plan_spec.rb b/src/bosh-director/spec/unit/deployment_plan/placement_planner/plan_spec.rb index 3c7f5d6c11c..5e1fc2b2107 100644 --- a/src/bosh-director/spec/unit/deployment_plan/placement_planner/plan_spec.rb +++ b/src/bosh-director/spec/unit/deployment_plan/placement_planner/plan_spec.rb @@ -59,7 +59,9 @@ module Bosh::Director::DeploymentPlan 192.168.1.12 192.168.1.13 192.168.1.14 - ] + ], + nil, nil, + '32' ), ManualNetworkSubnet.new( 'network_A', @@ -71,7 +73,9 @@ module Bosh::Director::DeploymentPlan 10.10.1.12 10.10.1.13 10.10.1.14 - ] + ], + nil, nil, + '32' ), ManualNetworkSubnet.new( 'network_A', @@ -83,7 +87,9 @@ module Bosh::Director::DeploymentPlan 10.0.1.12 10.0.1.13 10.0.1.14 - ] + ], + nil, nil, + '32' ), ] end diff --git a/src/bosh-director/spec/unit/deployment_plan/placement_planner/static_ips_availability_zone_picker_spec.rb b/src/bosh-director/spec/unit/deployment_plan/placement_planner/static_ips_availability_zone_picker_spec.rb index 693138623b8..9c24b503d33 100644 --- a/src/bosh-director/spec/unit/deployment_plan/placement_planner/static_ips_availability_zone_picker_spec.rb +++ b/src/bosh-director/spec/unit/deployment_plan/placement_planner/static_ips_availability_zone_picker_spec.rb @@ -361,8 +361,29 @@ def make_subnet_spec(range, static_ips, zone_names) let(:static_ips) { ['192.168.1.10', '192.168.2.10'] } let(:existing_instances) do [ - existing_instance_with_az_and_ips('zone1', ['192.168.1.10']), - existing_instance_with_az_and_ips('zone2', ['192.168.2.10']), + existing_instance_with_az_and_ips('zone1', ['192.168.1.10/32']), + existing_instance_with_az_and_ips('zone2', ['192.168.2.10/32']), + ] + end + + it 'reuses existing instances' do + expect(new_instance_plans).to eq([]) + expect(obsolete_instance_plans).to eq([]) + expect(existing_instance_plans.size).to eq(2) + expect(existing_instance_plans[0].desired_instance.az.name).to eq('zone1') + expect(existing_instance_plans[0].network_plans.map(&:reservation).map(&:ip)).to eq([ip_to_i('192.168.1.10')]) + expect(existing_instance_plans[1].desired_instance.az.name).to eq('zone2') + expect(existing_instance_plans[1].network_plans.map(&:reservation).map(&:ip)).to eq([ip_to_i('192.168.2.10')]) + end + end + + context 'when all existing instances match static IPs and AZs but the ips are still stored as integer format' do + let(:desired_instance_count) { 2 } + let(:static_ips) { ['192.168.1.10', '192.168.2.10'] } + let(:existing_instances) do + [ + existing_instance_with_az_and_ips('zone1', ['3232235786']), + existing_instance_with_az_and_ips('zone2', ['3232236042']), ] end @@ -382,8 +403,8 @@ def make_subnet_spec(range, static_ips, zone_names) let(:static_ips) { ['192.168.1.10', '192.168.2.10'] } let(:existing_instances) do [ - existing_instance_with_az_and_ips('zone1', ['192.168.1.10']), - existing_instance_with_az_and_ips('zone2', ['192.168.2.10']), + existing_instance_with_az_and_ips('zone1', ['192.168.1.10/32']), + existing_instance_with_az_and_ips('zone2', ['192.168.2.10/32']), ] end let(:instance_group_availability_zones) { ['zone1'] } @@ -408,8 +429,8 @@ def make_subnet_spec(range, static_ips, zone_names) let(:static_ips) { ['192.168.1.14', '192.168.2.14'] } let(:existing_instances) do [ - existing_instance_with_az_and_ips('zone1', ['192.168.1.10']), - existing_instance_with_az_and_ips('zone2', ['192.168.2.10']), + existing_instance_with_az_and_ips('zone1', ['192.168.1.10/32']), + existing_instance_with_az_and_ips('zone2', ['192.168.2.10/32']), ] end let(:instance_group_availability_zones) { %w[zone1 zone2] } @@ -487,7 +508,7 @@ def make_subnet_spec(range, static_ips, zone_names) end let(:new_subnet_azs) { %w[zone2 zone1] } let(:static_ips) { ['192.168.1.10'] } - let(:existing_instances) { [existing_instance_with_az_and_ips('zone1', ['192.168.1.10'])] } + let(:existing_instances) { [existing_instance_with_az_and_ips('zone1', ['192.168.1.10/32'])] } it 'reuses AZ that existing instance with static IP belongs to' do expect(new_instance_plans).to eq([]) @@ -518,8 +539,8 @@ def make_subnet_spec(range, static_ips, zone_names) let(:static_ips) { ['192.168.1.10', '192.168.1.11', '192.168.1.12', '192.168.1.13'] } let(:existing_instances) do [ - existing_instance_with_az_and_ips('zone1', ['192.168.1.10']), - existing_instance_with_az_and_ips('zone1', ['192.168.1.12']), + existing_instance_with_az_and_ips('zone1', ['192.168.1.10/32']), + existing_instance_with_az_and_ips('zone1', ['192.168.1.12/32']), ] end it 'should distribute the instances across the azs taking into account the existing instances' do @@ -541,8 +562,8 @@ def make_subnet_spec(range, static_ips, zone_names) let(:static_ips) { ['192.168.1.10', '192.168.2.11'] } let(:existing_instances) do [ - existing_instance_with_az_and_ips('zone1', ['192.168.1.10']), - existing_instance_with_az_and_ips('zone2', ['192.168.2.10']), + existing_instance_with_az_and_ips('zone1', ['192.168.1.10/32']), + existing_instance_with_az_and_ips('zone2', ['192.168.2.10/32']), ] end @@ -577,10 +598,10 @@ def make_subnet_spec(range, static_ips, zone_names) context 'when all existing instances match specified static ips' do let(:existing_instances) do [ - existing_instance_with_az_and_ips('zone1', ['192.168.1.10', '10.10.1.10']), - existing_instance_with_az_and_ips('zone2', ['192.168.2.10', '10.10.2.10']), - existing_instance_with_az_and_ips('zone1', ['192.168.1.11', '10.10.1.11']), - existing_instance_with_az_and_ips('zone2', ['192.168.2.11', '10.10.2.11']), + existing_instance_with_az_and_ips('zone1', ['192.168.1.10/32', '10.10.1.10/32']), + existing_instance_with_az_and_ips('zone2', ['192.168.2.10/32', '10.10.2.10/32']), + existing_instance_with_az_and_ips('zone1', ['192.168.1.11/32', '10.10.1.11/32']), + existing_instance_with_az_and_ips('zone2', ['192.168.2.11/32', '10.10.2.11/32']), ] end @@ -614,10 +635,10 @@ def make_subnet_spec(range, static_ips, zone_names) context 'when some existing instances have IPs that are different from the instance group static IPs' do let(:existing_instances) do [ - existing_instance_with_az_and_ips('zone1', ['192.168.1.10', '10.10.1.10']), - existing_instance_with_az_and_ips('zone2', ['192.168.2.14', '10.10.2.14']), - existing_instance_with_az_and_ips('zone1', ['192.168.1.14', '10.10.1.14']), - existing_instance_with_az_and_ips('zone2', ['192.168.2.11', '10.10.2.11']), + existing_instance_with_az_and_ips('zone1', ['192.168.1.10/32', '10.10.1.10/32']), + existing_instance_with_az_and_ips('zone2', ['192.168.2.14/32', '10.10.2.14/32']), + existing_instance_with_az_and_ips('zone1', ['192.168.1.14/32', '10.10.1.14/32']), + existing_instance_with_az_and_ips('zone2', ['192.168.2.11/32', '10.10.2.11/32']), ] end @@ -655,7 +676,7 @@ def make_subnet_spec(range, static_ips, zone_names) let(:desired_instance_count) { 1 } let(:existing_instances) do [ - existing_instance_with_az_and_ips('zone1', ['192.168.1.10', '10.10.2.10']), + existing_instance_with_az_and_ips('zone1', ['192.168.1.10/32', '10.10.2.10/32']), ] end let(:a_static_ips) { ['192.168.1.10'] } @@ -676,8 +697,8 @@ def make_subnet_spec(range, static_ips, zone_names) let(:desired_instance_count) { 3 } let(:existing_instances) do [ - existing_instance_with_az_and_ips('zone1', ['192.168.1.10', '10.10.1.10']), - existing_instance_with_az_and_ips('zone1', ['192.168.1.11', '10.10.1.11']), + existing_instance_with_az_and_ips('zone1', ['192.168.1.10/32', '10.10.1.10/32']), + existing_instance_with_az_and_ips('zone1', ['192.168.1.11/32', '10.10.1.11/32']), ] end let(:a_static_ips) { ['192.168.1.10 - 192.168.1.11', '192.168.2.10'] } @@ -698,9 +719,9 @@ def make_subnet_spec(range, static_ips, zone_names) let(:desired_instance_count) { 2 } let(:existing_instances) do [ - existing_instance_with_az_and_ips('zone1', ['192.168.1.10', '10.10.1.10']), - existing_instance_with_az_and_ips('zone1', ['192.168.1.11', '10.10.1.11']), - existing_instance_with_az_and_ips('zone2', ['192.168.2.10', '10.10.2.10']), + existing_instance_with_az_and_ips('zone1', ['192.168.1.10/32', '10.10.1.10/32']), + existing_instance_with_az_and_ips('zone1', ['192.168.1.11/32', '10.10.1.11/32']), + existing_instance_with_az_and_ips('zone2', ['192.168.2.10/32', '10.10.2.10/32']), ] end let(:a_static_ips) { ['192.168.1.10', '192.168.2.10'] } @@ -725,8 +746,8 @@ def make_subnet_spec(range, static_ips, zone_names) let(:existing_instances) do [ - existing_instance_with_az_and_ips('zone1', ['192.168.1.10', '10.10.2.10']), - existing_instance_with_az_and_ips('zone2', ['192.168.2.10', '10.10.2.11']), + existing_instance_with_az_and_ips('zone1', ['192.168.1.10/32', '10.10.2.10/32']), + existing_instance_with_az_and_ips('zone2', ['192.168.2.10/32', '10.10.2.11/32']), ] end let(:a_static_ips) { ['192.168.1.10', '192.168.2.10'] } @@ -765,8 +786,8 @@ def make_subnet_spec(range, static_ips, zone_names) let(:desired_instance_count) { 2 } let(:existing_instances) do [ - existing_instance_with_az_and_ips(nil, ['192.168.1.10', '10.10.1.10']), - existing_instance_with_az_and_ips(nil, ['192.168.2.10', '10.10.2.11']), + existing_instance_with_az_and_ips(nil, ['192.168.1.10/32', '10.10.1.10/32']), + existing_instance_with_az_and_ips(nil, ['192.168.2.10/32', '10.10.2.11/32']), ] end let(:a_static_ips) { ['192.168.1.10', '192.168.2.10'] } @@ -780,8 +801,8 @@ def make_subnet_spec(range, static_ips, zone_names) context 'when existing instances have AZs' do let(:existing_instances) do [ - existing_instance_with_az_and_ips('zone1', ['192.168.1.10', '10.10.1.10']), - existing_instance_with_az_and_ips('zone2', ['192.168.2.10', '10.10.2.11']), + existing_instance_with_az_and_ips('zone1', ['192.168.1.10/32', '10.10.1.10/32']), + existing_instance_with_az_and_ips('zone2', ['192.168.2.10/32', '10.10.2.11/32']), ] end @@ -816,8 +837,8 @@ def make_subnet_spec(range, static_ips, zone_names) let(:desired_instance_count) { 2 } let(:existing_instances) do [ - existing_instance_with_az_and_ips('zone1', ['192.168.5.10', '10.10.5.10']), - existing_instance_with_az_and_ips('zone1', ['192.168.6.10', '10.10.6.11']), + existing_instance_with_az_and_ips('zone1', ['192.168.5.10/32', '10.10.5.10/32']), + existing_instance_with_az_and_ips('zone1', ['192.168.6.10/32', '10.10.6.11/32']), ] end let(:a_static_ips) { ['192.168.1.10', '192.168.2.10'] } @@ -848,7 +869,7 @@ def make_subnet_spec(range, static_ips, zone_names) end let(:existing_instances) do [ - existing_instance_with_az_and_ips('zone1', ['192.168.1.10', '192.168.2.10']), + existing_instance_with_az_and_ips('zone1', ['192.168.1.10/32', '192.168.2.10/32']), ] end let(:a_static_ips) { ['192.168.1.10 - 192.168.1.11'] } @@ -882,7 +903,7 @@ def make_subnet_spec(range, static_ips, zone_names) let(:existing_instances) do [ - existing_instance_with_az_and_ips('zone1', ['192.168.1.10', '192.168.2.10']), + existing_instance_with_az_and_ips('zone1', ['192.168.1.10/32', '192.168.2.10/32']), ] end @@ -909,8 +930,8 @@ def make_subnet_spec(range, static_ips, zone_names) let(:static_ips) { ['192.168.1.10', '192.168.2.10'] } let(:existing_instances) do [ - existing_instance_with_az_and_ips('zone1', ['192.168.1.10'], 'old-network-name'), - existing_instance_with_az_and_ips('zone2', ['192.168.2.10'], 'old-network-name'), + existing_instance_with_az_and_ips('zone1', ['192.168.1.10/32'], 'old-network-name'), + existing_instance_with_az_and_ips('zone2', ['192.168.2.10/32'], 'old-network-name'), ] end @@ -952,7 +973,7 @@ def existing_instance_with_az_and_ips(az, ips, network_name = 'a') ips.each do |ip| instance.add_ip_address( FactoryBot.create(:models_ip_address, - address_str: IPAddr.new(ip).to_i.to_s, + address_str: ip, network_name: network_name, ), ) diff --git a/src/bosh-director/spec/unit/deployment_plan/steps/commit_instance_network_settings_step_spec.rb b/src/bosh-director/spec/unit/deployment_plan/steps/commit_instance_network_settings_step_spec.rb index a80027ed712..4d4760368e9 100644 --- a/src/bosh-director/spec/unit/deployment_plan/steps/commit_instance_network_settings_step_spec.rb +++ b/src/bosh-director/spec/unit/deployment_plan/steps/commit_instance_network_settings_step_spec.rb @@ -14,13 +14,13 @@ module Steps ] end - let(:existing_ip_address_string) { 'existing_ip_address_string' } - let(:obsolete_ip_address_string) { 'obsolete_ip_address_string' } - let(:desired_ip_address_string) { 'desired_ip_address_string' } + let(:existing_ip_address_string) { '1.1.1.1/32' } + let(:obsolete_ip_address_string) { '2.2.2.2/32' } + let(:desired_ip_address_string) { '3.3.3.3/32' } - let(:existing_reservation) { instance_double(NetworkReservation, ip: existing_ip_address_string) } - let(:obsolete_reservation) { instance_double(NetworkReservation, ip: obsolete_ip_address_string) } - let(:desired_reservation) { instance_double(NetworkReservation, ip: desired_ip_address_string) } + let(:existing_reservation) { instance_double(NetworkReservation, ip: Bosh::Director::IpAddrOrCidr.new(existing_ip_address_string)) } + let(:obsolete_reservation) { instance_double(NetworkReservation, ip: Bosh::Director::IpAddrOrCidr.new(obsolete_ip_address_string)) } + let(:desired_reservation) { instance_double(NetworkReservation, ip: Bosh::Director::IpAddrOrCidr.new(desired_ip_address_string)) } let!(:existing_ip_address) { FactoryBot.create(:models_ip_address, address_str: existing_ip_address_string) } let!(:obsolete_ip_address) { FactoryBot.create(:models_ip_address, address_str: obsolete_ip_address_string) } diff --git a/src/bosh-director/spec/unit/deployment_plan/steps/create_vm_step_spec.rb b/src/bosh-director/spec/unit/deployment_plan/steps/create_vm_step_spec.rb index e28048a5253..ef11db74bb9 100644 --- a/src/bosh-director/spec/unit/deployment_plan/steps/create_vm_step_spec.rb +++ b/src/bosh-director/spec/unit/deployment_plan/steps/create_vm_step_spec.rb @@ -146,8 +146,8 @@ module Steps end let(:reservation) do - subnet = Bosh::Director::DeploymentPlan::DynamicNetworkSubnet.new('dns', network_cloud_properties, ['az-1']) - network = Bosh::Director::DeploymentPlan::DynamicNetwork.new('name', [subnet], per_spec_logger) + subnet = Bosh::Director::DeploymentPlan::DynamicNetworkSubnet.new('dns', network_cloud_properties, ['az-1'], '32') + network = Bosh::Director::DeploymentPlan::DynamicNetwork.new('name', [subnet], '32', per_spec_logger) reservation = Bosh::Director::DesiredNetworkReservation.new_dynamic(instance_model, network) reservation end diff --git a/src/bosh-director/spec/unit/ip_addr_or_cidr_spec.rb b/src/bosh-director/spec/unit/ip_addr_or_cidr_spec.rb index 9f097e31b99..ed272de954c 100644 --- a/src/bosh-director/spec/unit/ip_addr_or_cidr_spec.rb +++ b/src/bosh-director/spec/unit/ip_addr_or_cidr_spec.rb @@ -70,7 +70,7 @@ module Bosh::Director end context 'IPv6' do - let(:input) { 'fd00::/8' } + let(:input) { 'fd00:0000:0000:0000:0000:0000:0000:0000/8' } it 'returns a string representing the IP' do expect(ip_addr_or_cidr.to_cidr_s).to eq(input) diff --git a/src/bosh-director/spec/unit/ip_util_spec.rb b/src/bosh-director/spec/unit/ip_util_spec.rb index 6a917afca27..ebfb9599aba 100644 --- a/src/bosh-director/spec/unit/ip_util_spec.rb +++ b/src/bosh-director/spec/unit/ip_util_spec.rb @@ -67,7 +67,7 @@ describe 'format_ip' do it 'converts integer to CIDR IP' do - expect(ip_util_includer.format_ip(168427582)).to eq('10.10.0.62') + expect(ip_util_includer.format_ip(168427582)).to eq('10.10.0.62/32') end end diff --git a/src/bosh-director/spec/unit/jobs/vm_state_spec.rb b/src/bosh-director/spec/unit/jobs/vm_state_spec.rb index 36f54d641b6..a7082bf43a2 100644 --- a/src/bosh-director/spec/unit/jobs/vm_state_spec.rb +++ b/src/bosh-director/spec/unit/jobs/vm_state_spec.rb @@ -55,7 +55,7 @@ def stub_agent_get_state_to_return_state_with_vitals FactoryBot.create(:models_ip_address, instance_id: instance.id, vm_id: vm.id, - address_str: IPAddr.new('1.1.1.1').to_i.to_s, + address_str: Bosh::Director::IpAddrOrCidr.new('1.1.1.1').to_cidr_s, task_id: '12345', ) expect(agent).to receive(:get_state).with('full').and_return( @@ -82,13 +82,13 @@ def stub_agent_get_state_to_return_state_with_vitals FactoryBot.create(:models_ip_address, instance_id: instance.id, vm_id: vm.id, - address_str: IPAddr.new('1.1.1.1').to_i.to_s, + address_str: Bosh::Director::IpAddrOrCidr.new('1.1.1.1').to_cidr_s, task_id: '12345', ) FactoryBot.create(:models_ip_address, instance_id: instance.id, vm_id: vm.id, - address_str: IPAddr.new('2.2.2.2').to_i.to_s, + address_str: Bosh::Director::IpAddrOrCidr.new('2.2.2.2').to_cidr_s, task_id: '12345', ) end @@ -104,7 +104,7 @@ def stub_agent_get_state_to_return_state_with_vitals context "when 'ip_addresses' is empty for instance" do before do - vm.network_spec = { 'a' => { 'ip' => '3.3.3.3' }, 'b' => { 'ip' => '4.4.4.4' } } + vm.network_spec = { 'a' => { 'ip' => '3.3.3.3/32' }, 'b' => { 'ip' => '4.4.4.4/32' } } vm.save instance.spec = { 'networks' => { 'a' => { 'ip' => '1.1.1.1' }, 'b' => { 'ip' => '2.2.2.2' } } } instance.save @@ -125,19 +125,19 @@ def stub_agent_get_state_to_return_state_with_vitals FactoryBot.create(:models_ip_address, instance_id: instance.id, vm_id: vm.id, - address_str: IPAddr.new('1.1.1.1').to_i.to_s, + address_str: Bosh::Director::IpAddrOrCidr.new('1.1.1.1').to_cidr_s, task_id: '12345', ) FactoryBot.create(:models_ip_address, instance_id: instance.id, vm_id: vm.id, - address_str: IPAddr.new('2.2.2.2').to_i.to_s, + address_str: Bosh::Director::IpAddrOrCidr.new('2.2.2.2').to_cidr_s, task_id: '12345', ) vm.network_spec = { - 'a' => { 'ip' => '3.3.3.3' }, - 'b' => { 'ip' => '4.4.4.4' }, + 'a' => { 'ip' => '3.3.3.3/32' }, + 'b' => { 'ip' => '4.4.4.4/32' }, } vm.save @@ -168,7 +168,7 @@ def stub_agent_get_state_to_return_state_with_vitals FactoryBot.create(:models_ip_address, instance_id: instance.id, vm_id: vm.id, - address_str: IPAddr.new('1.1.1.1').to_i.to_s, + address_str: Bosh::Director::IpAddrOrCidr.new('1.1.1.1').to_cidr_s, task_id: '12345', ) stub_agent_get_state_to_return_state_with_vitals diff --git a/src/bosh-director/spec/unit/models/ip_address_spec.rb b/src/bosh-director/spec/unit/models/ip_address_spec.rb index 6531d13e5e5..968d0cb9375 100644 --- a/src/bosh-director/spec/unit/models/ip_address_spec.rb +++ b/src/bosh-director/spec/unit/models/ip_address_spec.rb @@ -5,7 +5,7 @@ module Bosh::Director::Models subject(:ip_address) do IpAddress.new(instance: instance, network_name: 'foonetwork', - address_str: IPAddr.new('10.10.0.1').to_i.to_s, + address_str: Bosh::Director::IpAddrOrCidr.new('10.10.0.1/32').to_cidr_s, task_id: 'fake-task-id', static: true, vm: vm) @@ -16,7 +16,7 @@ module Bosh::Director::Models context '#info' do it 'should display debugging information (job, index, network name and ip address)' do - expect(ip_address.info).to eq('foodeployment.foojob/1 - foonetwork - 10.10.0.1 (static)') + expect(ip_address.info).to eq('foodeployment.foojob/1 - foonetwork - 10.10.0.1/32 (static)') end end diff --git a/src/bosh-director/spec/unit/models/vm_spec.rb b/src/bosh-director/spec/unit/models/vm_spec.rb index b7c5a11f6ba..a3b59acf849 100644 --- a/src/bosh-director/spec/unit/models/vm_spec.rb +++ b/src/bosh-director/spec/unit/models/vm_spec.rb @@ -39,11 +39,11 @@ module Bosh::Director::Models end describe '#ips' do - let!(:ip_address) { FactoryBot.create(:models_ip_address, vm: vm, address_str: IPAddr.new('1.1.1.1').to_i.to_s) } - let!(:ip_address2) { FactoryBot.create(:models_ip_address, vm: vm, address_str: IPAddr.new('1.1.1.2').to_i.to_s) } + let!(:ip_address) { FactoryBot.create(:models_ip_address, vm: vm, address_str: Bosh::Director::IpAddrOrCidr.new('1.1.1.1/32').to_cidr_s) } + let!(:ip_address2) { FactoryBot.create(:models_ip_address, vm: vm, address_str: Bosh::Director::IpAddrOrCidr.new('1.1.1.2/32').to_cidr_s) } before do - vm.network_spec = { 'some' => { 'ip' => '1.1.1.3' } } + vm.network_spec = { 'some' => { 'ip' => '1.1.1.3/32' } } end it 'returns all ips for the vm' do diff --git a/src/bosh-director/spec/unit/orphaned_vm_deleter_spec.rb b/src/bosh-director/spec/unit/orphaned_vm_deleter_spec.rb index e3d74802c8a..8fda2239eac 100644 --- a/src/bosh-director/spec/unit/orphaned_vm_deleter_spec.rb +++ b/src/bosh-director/spec/unit/orphaned_vm_deleter_spec.rb @@ -20,7 +20,7 @@ module Director Bosh::Director::Models::IpAddress.create( orphaned_vm: orphaned_vm1, network_name: 'my-manual-network', - address_str: IPAddr.new('127.0.0.2').to_i, + address_str: Bosh::Director::IpAddrOrCidr.new('127.0.0.2/32').to_cidr_s, task_id: 1, ) end @@ -38,7 +38,7 @@ module Director Bosh::Director::Models::IpAddress.create( orphaned_vm: orphaned_vm2, network_name: 'my-manual-network', - address_str: IPAddr.new('127.0.0.1').to_i, + address_str: Bosh::Director::IpAddrOrCidr.new('127.0.0.1/32').to_cidr_s, task_id: 1, ) end