示例#1
0
// prepareAllocationNetwork retrieves the subnet, its info, and the interface info
// for the allocations.
func (p *ProvisionerAPI) prepareAllocationNetwork(
	environ environs.NetworkingEnviron,
	instId instance.Id,
) (
	*state.Subnet,
	network.SubnetInfo,
	network.InterfaceInfo,
	error,
) {
	var subnetInfo network.SubnetInfo
	var interfaceInfo network.InterfaceInfo

	interfaces, err := environ.NetworkInterfaces(instId)
	if err != nil {
		return nil, subnetInfo, interfaceInfo, errors.Trace(err)
	} else if len(interfaces) == 0 {
		return nil, subnetInfo, interfaceInfo, errors.New("no interfaces available")
	}
	logger.Tracef("interfaces for instance %q: %v", instId, interfaces)

	subnetSet := make(set.Strings)
	subnetIds := []network.Id{}
	subnetIdToInterface := make(map[network.Id]network.InterfaceInfo)
	for _, iface := range interfaces {
		if iface.ProviderSubnetId == "" {
			logger.Debugf("no subnet associated with interface %#v (skipping)", iface)
			continue
		} else if iface.Disabled {
			logger.Debugf("interface %#v disabled (skipping)", iface)
			continue
		}
		if !subnetSet.Contains(string(iface.ProviderSubnetId)) {
			subnetIds = append(subnetIds, iface.ProviderSubnetId)
			subnetSet.Add(string(iface.ProviderSubnetId))

			// This means that multiple interfaces on the same subnet will
			// only appear once.
			subnetIdToInterface[iface.ProviderSubnetId] = iface
		}
	}
	subnets, err := environ.Subnets(instId, subnetIds)
	if err != nil {
		return nil, subnetInfo, interfaceInfo, errors.Trace(err)
	} else if len(subnets) == 0 {
		return nil, subnetInfo, interfaceInfo, errors.Errorf("no subnets available")
	}
	logger.Tracef("subnets for instance %q: %v", instId, subnets)

	// TODO(mfoord): we need a better strategy for picking a subnet to
	// allocate an address on. (dimitern): Right now we just pick the
	// first subnet with allocatable range set. Instead, we should
	// allocate an address per interface, assuming each interface is
	// on a subnet with allocatable range set, and skipping those
	// which do not have a range set.
	var success bool
	for _, sub := range subnets {
		logger.Tracef("trying to allocate a static IP on subnet %q", sub.ProviderId)
		if sub.AllocatableIPHigh == nil {
			logger.Tracef("ignoring subnet %q - no allocatable range set", sub.ProviderId)
			// this subnet has no allocatable IPs
			continue
		}
		if sub.AllocatableIPLow != nil && sub.AllocatableIPLow.To4() == nil {
			logger.Tracef("ignoring IPv6 subnet %q - allocating IPv6 addresses not yet supported", sub.ProviderId)
			// Until we change the way we pick addresses, IPv6 subnets with
			// their *huge* ranges (/64 being the default), there is no point in
			// allowing such subnets (it won't even work as PickNewAddress()
			// assumes IPv4 allocatable range anyway).
			continue
		}
		ok, err := environ.SupportsAddressAllocation(sub.ProviderId)
		if err == nil && ok {
			subnetInfo = sub
			interfaceInfo = subnetIdToInterface[sub.ProviderId]

			// Since with addressable containers the host acts like a gateway
			// for the containers, instead of using the same gateway for the
			// containers as their host's
			interfaceInfo.GatewayAddress.Value = interfaceInfo.Address.Value

			success = true
			break
		}
		logger.Tracef(
			"subnet %q supports address allocation: %v (error: %v)",
			sub.ProviderId, ok, err,
		)
	}
	if !success {
		// " not supported" will be appended to the message below.
		return nil, subnetInfo, interfaceInfo, errors.NotSupportedf(
			"address allocation on any available subnets is",
		)
	}
	subnet, err := p.createOrFetchStateSubnet(subnetInfo)

	return subnet, subnetInfo, interfaceInfo, nil
}