// 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 }