Example #1
0
func (s *suite) breakMethods(c *gc.C, e environs.NetworkingEnviron, names ...string) {
	cfg := e.Config()
	brokenCfg, err := cfg.Apply(map[string]interface{}{
		"broken": strings.Join(names, " "),
	})
	c.Assert(err, jc.ErrorIsNil)
	err = e.SetConfig(brokenCfg)
	c.Assert(err, jc.ErrorIsNil)
}
Example #2
0
// allocateAddress tries to pick an address out of the given subnet and
// allocates it to the container.
func (p *ProvisionerAPI) allocateAddress(
	environ environs.NetworkingEnviron,
	subnet *state.Subnet,
	host, container *state.Machine,
	instId instance.Id,
	macAddress string,
) (*state.IPAddress, error) {

	subnetId := network.Id(subnet.ProviderId())
	name := names.NewMachineTag(container.Id()).String()
	for {
		addr, err := subnet.PickNewAddress()
		if err != nil {
			return nil, err
		}
		logger.Tracef("picked new address %q on subnet %q", addr.String(), subnetId)
		// Attempt to allocate with environ.
		err = environ.AllocateAddress(instId, subnetId, addr.Address(), macAddress, name)
		if err != nil {
			logger.Warningf(
				"allocating address %q on instance %q and subnet %q failed: %v (retrying)",
				addr.String(), instId, subnetId, err,
			)
			// It's as good as unavailable for us, so mark it as
			// such.
			err = setAddrState(addr, state.AddressStateUnavailable)
			if err != nil {
				logger.Warningf(
					"cannot set address %q to %q: %v (ignoring and retrying)",
					addr.String(), state.AddressStateUnavailable, err,
				)
				continue
			}
			logger.Tracef(
				"setting address %q to %q and retrying",
				addr.String(), state.AddressStateUnavailable,
			)
			continue
		}
		logger.Infof(
			"allocated address %q on instance %q and subnet %q",
			addr.String(), instId, subnetId,
		)
		err = p.setAllocatedOrRelease(addr, environ, instId, container, subnetId, macAddress)
		if err != nil {
			// Something went wrong - retry.
			continue
		}
		return addr, nil
	}
}
Example #3
0
// setAllocatedOrRelease tries to associate the newly allocated
// address addr with the container. On failure it makes the best
// effort to cleanup and release addr, logging issues along the way.
func (p *ProvisionerAPI) setAllocatedOrRelease(
	addr *state.IPAddress,
	environ environs.NetworkingEnviron,
	instId instance.Id,
	container *state.Machine,
	subnetId network.Id,
	macAddress string,
) (err error) {
	defer func() {
		if errors.Cause(err) == nil {
			// Success!
			return
		}
		logger.Warningf(
			"failed to mark address %q as %q to container %q: %v (releasing and retrying)",
			addr.String(), state.AddressStateAllocated, container, err,
		)
		// It's as good as unavailable for us, so mark it as
		// such.
		err = setAddrState(addr, state.AddressStateUnavailable)
		if err != nil {
			logger.Warningf(
				"cannot set address %q to %q: %v (ignoring and releasing)",
				addr.String(), state.AddressStateUnavailable, err,
			)
		}
		err = environ.ReleaseAddress(instId, subnetId, addr.Address(), addr.MACAddress(), "")
		if err == nil {
			logger.Infof("address %q released; trying to allocate new", addr.String())
			return
		}
		logger.Warningf(
			"failed to release address %q on instance %q and subnet %q: %v (ignoring and retrying)",
			addr.String(), instId, subnetId, err,
		)
	}()

	// Any errors returned below will trigger the release/cleanup
	// steps above.
	if err = allocateAddrTo(addr, container, macAddress); err != nil {
		return errors.Trace(err)
	}
	if err = setAddrsTo(addr, container); err != nil {
		return errors.Trace(err)
	}

	logger.Infof("assigned address %q to container %q", addr.String(), container)
	return nil
}
Example #4
0
// allocateAddress tries to pick an address out of the given subnet and
// allocates it to the container.
func (p *ProvisionerAPI) allocateAddress(
	environ environs.NetworkingEnviron,
	subnet *state.Subnet,
	host, container *state.Machine,
	instId instance.Id,
	macAddress string,
) (*state.IPAddress, error) {
	hostname := containerHostname(container.Tag())

	if !environs.AddressAllocationEnabled() {
		// Even if the address allocation feature flag is not enabled, we might
		// be running on MAAS 1.8+ with devices support, which we can use to
		// register containers getting IPs via DHCP. However, most of the usual
		// allocation code can be bypassed, we just need the parent instance ID
		// and a MAC address (no subnet or IP address).
		allocatedAddress := network.Address{}
		err := environ.AllocateAddress(instId, network.AnySubnet, &allocatedAddress, macAddress, hostname)
		if err != nil {
			// Not using MAAS 1.8+ or some other error.
			return nil, errors.Trace(err)
		}

		logger.Infof(
			"allocated address %q on instance %q for container %q",
			allocatedAddress.String(), instId, hostname,
		)

		// Add the address to state, so we can look it up later by MAC address.
		stateAddr, err := p.st.AddIPAddress(allocatedAddress, string(network.AnySubnet))
		if err != nil {
			return nil, errors.Annotatef(err, "failed to save address %q", allocatedAddress)
		}

		err = p.setAllocatedOrRelease(stateAddr, environ, instId, container, network.AnySubnet, macAddress)
		if err != nil {
			return nil, errors.Trace(err)
		}

		return stateAddr, nil
	}

	subnetId := network.Id(subnet.ProviderId())
	for {
		addr, err := subnet.PickNewAddress()
		if err != nil {
			return nil, err
		}
		netAddr := addr.Address()
		logger.Tracef("picked new address %q on subnet %q", addr.String(), subnetId)
		// Attempt to allocate with environ.
		err = environ.AllocateAddress(instId, subnetId, &netAddr, macAddress, hostname)
		if err != nil {
			logger.Warningf(
				"allocating address %q on instance %q and subnet %q failed: %v (retrying)",
				addr.String(), instId, subnetId, err,
			)
			// It's as good as unavailable for us, so mark it as
			// such.
			err = setAddrState(addr, state.AddressStateUnavailable)
			if err != nil {
				logger.Warningf(
					"cannot set address %q to %q: %v (ignoring and retrying)",
					addr.String(), state.AddressStateUnavailable, err,
				)
				continue
			}
			logger.Tracef(
				"setting address %q to %q and retrying",
				addr.String(), state.AddressStateUnavailable,
			)
			continue
		}
		logger.Infof(
			"allocated address %q on instance %q and subnet %q",
			addr.String(), instId, subnetId,
		)
		err = p.setAllocatedOrRelease(addr, environ, instId, container, subnetId, macAddress)
		if err != nil {
			// Something went wrong - retry.
			continue
		}
		return addr, nil
	}
}
Example #5
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
}
Example #6
0
func (dw *discoverspacesWorker) handleSubnets(env environs.NetworkingEnviron) error {
	ok, err := env.SupportsSpaceDiscovery()
	if err != nil {
		return errors.Trace(err)
	}
	if !ok {
		// Nothing to do.
		return nil
	}
	providerSpaces, err := env.Spaces()
	if err != nil {
		return errors.Trace(err)
	}
	listSpacesResult, err := dw.api.ListSpaces()
	if err != nil {
		return errors.Trace(err)
	}

	stateSubnets, err := dw.api.ListSubnets(params.SubnetsFilters{})
	if err != nil {
		return errors.Trace(err)
	}
	stateSubnetIds := make(set.Strings)
	for _, subnet := range stateSubnets.Results {
		stateSubnetIds.Add(subnet.ProviderId)
	}
	stateSpaceMap := make(map[string]params.ProviderSpace)
	spaceNames := make(set.Strings)
	for _, space := range listSpacesResult.Results {
		stateSpaceMap[space.ProviderId] = space
		spaceNames.Add(space.Name)
	}

	// TODO(mfoord): we need to delete spaces and subnets that no longer
	// exist, so long as they're not in use.
	for _, space := range providerSpaces {
		// Check if the space is already in state, in which case we know
		// its name.
		stateSpace, ok := stateSpaceMap[string(space.ProviderId)]
		var spaceTag names.SpaceTag
		if ok {
			spaceName := stateSpace.Name
			if !names.IsValidSpace(spaceName) {
				// Can only happen if an invalid name is stored
				// in state.
				logger.Errorf("space %q has an invalid name, ignoring", spaceName)
				continue

			}
			spaceTag = names.NewSpaceTag(spaceName)

		} else {
			// The space is new, we need to create a valid name for it
			// in state.
			spaceName := string(space.ProviderId)
			// Convert the name into a valid name that isn't already in
			// use.
			spaceName = convertSpaceName(spaceName, spaceNames)
			spaceNames.Add(spaceName)
			spaceTag = names.NewSpaceTag(spaceName)
			// We need to create the space.
			args := params.CreateSpacesParams{
				Spaces: []params.CreateSpaceParams{{
					Public:     false,
					SpaceTag:   spaceTag.String(),
					ProviderId: string(space.ProviderId),
				}}}
			result, err := dw.api.CreateSpaces(args)
			if err != nil {
				logger.Errorf("error creating space %v", err)
				return errors.Trace(err)
			}
			if len(result.Results) != 1 {
				return errors.Errorf("unexpected number of results from CreateSpaces, should be 1: %v", result)
			}
			if result.Results[0].Error != nil {
				return errors.Errorf("error from CreateSpaces: %v", result.Results[0].Error)
			}
		}
		// TODO(mfoord): currently no way of removing subnets, or
		// changing the space they're in, so we can only add ones we
		// don't already know about.
		logger.Debugf("Created space %v with %v subnets", spaceTag.String(), len(space.Subnets))
		for _, subnet := range space.Subnets {
			if stateSubnetIds.Contains(string(subnet.ProviderId)) {
				continue
			}
			zones := subnet.AvailabilityZones
			if len(zones) == 0 {
				zones = []string{"default"}
			}
			args := params.AddSubnetsParams{
				Subnets: []params.AddSubnetParams{{
					SubnetProviderId: string(subnet.ProviderId),
					SpaceTag:         spaceTag.String(),
					Zones:            zones,
				}}}
			logger.Tracef("Adding subnet %v", subnet.CIDR)
			result, err := dw.api.AddSubnets(args)
			if err != nil {
				logger.Errorf("invalid creating subnet %v", err)
				return errors.Trace(err)
			}
			if len(result.Results) != 1 {
				return errors.Errorf("unexpected number of results from AddSubnets, should be 1: %v", result)
			}
			if result.Results[0].Error != nil {
				logger.Errorf("error creating subnet %v", result.Results[0].Error)
				return errors.Errorf("error creating subnet %v", result.Results[0].Error)
			}
		}
	}
	return nil
}