Esempio n. 1
0
func (p *ProvisionerAPI) prepareOrGetContainerInterfaceInfo(args params.Entities, maintain bool) (params.MachineNetworkConfigResults, error) {
	result := params.MachineNetworkConfigResults{
		Results: make([]params.MachineNetworkConfigResult, len(args.Entities)),
	}

	netEnviron, hostMachine, canAccess, err := p.prepareContainerAccessEnvironment()
	if err != nil {
		return result, errors.Trace(err)
	}
	instId, err := hostMachine.InstanceId()
	if errors.IsNotProvisioned(err) {
		err = errors.NotProvisionedf("cannot prepare container network config: host machine %q", hostMachine)
		return result, err
	} else if err != nil {
		return result, errors.Trace(err)
	}

	for i, entity := range args.Entities {
		machineTag, err := names.ParseMachineTag(entity.Tag)
		if err != nil {
			result.Results[i].Error = common.ServerError(err)
			continue
		}
		// The auth function (canAccess) checks that the machine is a
		// top level machine (we filter those out next) or that the
		// machine has the host as a parent.
		container, err := p.getMachine(canAccess, machineTag)
		if err != nil {
			result.Results[i].Error = common.ServerError(err)
			continue
		} else if !container.IsContainer() {
			err = errors.Errorf("cannot prepare network config for %q: not a container", machineTag)
			result.Results[i].Error = common.ServerError(err)
			continue
		} else if ciid, cerr := container.InstanceId(); maintain == true && cerr == nil {
			// Since we want to configure and create NICs on the
			// container before it starts, it must also be not
			// provisioned yet.
			err = errors.Errorf("container %q already provisioned as %q", container, ciid)
			result.Results[i].Error = common.ServerError(err)
			continue
		} else if cerr != nil && !errors.IsNotProvisioned(cerr) {
			// Any other error needs to be reported.
			result.Results[i].Error = common.ServerError(cerr)
			continue
		}

		if err := hostMachine.SetContainerLinkLayerDevices(container); err != nil {
			result.Results[i].Error = common.ServerError(err)
			continue
		}

		containerDevices, err := container.AllLinkLayerDevices()
		if err != nil {
			result.Results[i].Error = common.ServerError(err)
			continue
		}

		preparedInfo := make([]network.InterfaceInfo, len(containerDevices))
		preparedOK := true
		for j, device := range containerDevices {
			parentDevice, err := device.ParentDevice()
			if err != nil || parentDevice == nil {
				err = errors.Errorf(
					"cannot get parent %q of container device %q: %v",
					device.ParentName(), device.Name(), err,
				)
				result.Results[i].Error = common.ServerError(err)
				preparedOK = false
				break
			}
			parentAddrs, err := parentDevice.Addresses()
			if err != nil {
				result.Results[i].Error = common.ServerError(err)
				preparedOK = false
				break
			}

			info := network.InterfaceInfo{
				InterfaceName:       device.Name(),
				MACAddress:          device.MACAddress(),
				ConfigType:          network.ConfigManual,
				InterfaceType:       network.InterfaceType(device.Type()),
				NoAutoStart:         !device.IsAutoStart(),
				Disabled:            !device.IsUp(),
				MTU:                 int(device.MTU()),
				ParentInterfaceName: parentDevice.Name(),
			}

			if len(parentAddrs) > 0 {
				logger.Infof("host machine device %q has addresses %v", parentDevice.Name(), parentAddrs)

				firstAddress := parentAddrs[0]
				parentDeviceSubnet, err := firstAddress.Subnet()
				if err != nil {
					err = errors.Annotatef(err,
						"cannot get subnet %q used by address %q of host machine device %q",
						firstAddress.SubnetCIDR(), firstAddress.Value(), parentDevice.Name(),
					)
					result.Results[i].Error = common.ServerError(err)
					preparedOK = false
					break
				}
				info.ConfigType = network.ConfigStatic
				info.CIDR = parentDeviceSubnet.CIDR()
				info.ProviderSubnetId = parentDeviceSubnet.ProviderId()
				info.VLANTag = parentDeviceSubnet.VLANTag()
			} else {
				logger.Infof("host machine device %q has no addresses %v", parentDevice.Name(), parentAddrs)
			}

			logger.Tracef("prepared info for container interface %q: %+v", info.InterfaceName, info)
			preparedOK = true
			preparedInfo[j] = info
		}

		if !preparedOK {
			// Error result is already set.
			continue
		}

		allocatedInfo, err := netEnviron.AllocateContainerAddresses(instId, machineTag, preparedInfo)
		if err != nil {
			result.Results[i].Error = common.ServerError(err)
			continue
		}
		logger.Debugf("got allocated info from provider: %+v", allocatedInfo)

		allocatedConfig := networkingcommon.NetworkConfigFromInterfaceInfo(allocatedInfo)
		logger.Tracef("allocated network config: %+v", allocatedConfig)
		result.Results[i].Config = allocatedConfig
	}
	return result, nil
}
Esempio n. 2
0
// legacyPrepareOrGetContainerInterfaceInfo optionally allocates an address and
// returns information for configuring networking on a container. It accepts
// container tags as arguments.
func (p *ProvisionerAPI) legacyPrepareOrGetContainerInterfaceInfo(
	args params.Entities,
	provisionContainer bool,
) (
	params.MachineNetworkConfigResults,
	error,
) {
	result := params.MachineNetworkConfigResults{
		Results: make([]params.MachineNetworkConfigResult, len(args.Entities)),
	}

	// Some preparations first.
	environ, host, canAccess, err := p.prepareContainerAccessEnvironment()
	if err != nil {
		return result, errors.Trace(err)
	}
	instId, err := host.InstanceId()
	if err != nil && errors.IsNotProvisioned(err) {
		// If the host machine is not provisioned yet, we have nothing
		// to do. NotProvisionedf will append " not provisioned" to
		// the message.
		err = errors.NotProvisionedf("cannot allocate addresses: host machine %q", host)
		return result, err
	}
	var subnet *state.Subnet
	var subnetInfo network.SubnetInfo
	var interfaceInfo network.InterfaceInfo
	if environs.AddressAllocationEnabled() {
		// We don't need a subnet unless we need to allocate a static IP.
		subnet, subnetInfo, interfaceInfo, err = p.prepareAllocationNetwork(environ, instId)
		if err != nil {
			return result, errors.Annotate(err, "cannot allocate addresses")
		}
	} else {
		var allInterfaceInfos []network.InterfaceInfo
		allInterfaceInfos, err = environ.NetworkInterfaces(instId)
		if err != nil {
			return result, errors.Annotatef(err, "cannot instance %q interfaces", instId)
		} else if len(allInterfaceInfos) == 0 {
			return result, errors.New("no interfaces available")
		}
		// Currently we only support a single NIC per container, so we only need
		// the information from the host instance's first NIC.
		logger.Tracef("interfaces for instance %q: %v", instId, allInterfaceInfos)
		interfaceInfo = allInterfaceInfos[0]
	}

	// Loop over the passed container tags.
	for i, entity := range args.Entities {
		tag, err := names.ParseMachineTag(entity.Tag)
		if err != nil {
			result.Results[i].Error = common.ServerError(err)
			continue
		}

		// The auth function (canAccess) checks that the machine is a
		// top level machine (we filter those out next) or that the
		// machine has the host as a parent.
		container, err := p.getMachine(canAccess, tag)
		if err != nil {
			result.Results[i].Error = common.ServerError(err)
			continue
		} else if !container.IsContainer() {
			err = errors.Errorf("cannot allocate address for %q: not a container", tag)
			result.Results[i].Error = common.ServerError(err)
			continue
		} else if ciid, cerr := container.InstanceId(); provisionContainer == true && cerr == nil {
			// Since we want to configure and create NICs on the
			// container before it starts, it must also be not
			// provisioned yet.
			err = errors.Errorf("container %q already provisioned as %q", container, ciid)
			result.Results[i].Error = common.ServerError(err)
			continue
		} else if cerr != nil && !errors.IsNotProvisioned(cerr) {
			// Any other error needs to be reported.
			result.Results[i].Error = common.ServerError(cerr)
			continue
		}

		var macAddress string
		var address *state.IPAddress
		if provisionContainer {
			// Allocate and set an address.
			macAddress = generateMACAddress()
			address, err = p.allocateAddress(environ, subnet, host, container, instId, macAddress)
			if err != nil {
				err = errors.Annotatef(err, "failed to allocate an address for %q", container)
				result.Results[i].Error = common.ServerError(err)
				continue
			}
		} else {
			id := container.Id()
			addresses, err := p.st.AllocatedIPAddresses(id)
			if err != nil {
				logger.Warningf("failed to get Id for container %q: %v", tag, err)
				result.Results[i].Error = common.ServerError(err)
				continue
			}
			// TODO(dooferlad): if we get more than 1 address back, we ignore everything after
			// the first. The calling function expects exactly one result though,
			// so we don't appear to have a way of allocating >1 address to a
			// container...
			if len(addresses) != 1 {
				logger.Warningf("got %d addresses for container %q - expected 1: %v", len(addresses), tag, err)
				result.Results[i].Error = common.ServerError(err)
				continue
			}
			address = addresses[0]
			macAddress = address.MACAddress()
		}

		// Store it on the machine, construct and set an interface result.
		dnsServers := make([]string, len(interfaceInfo.DNSServers))
		for l, dns := range interfaceInfo.DNSServers {
			dnsServers[l] = dns.Value
		}

		if macAddress == "" {
			macAddress = interfaceInfo.MACAddress
		}

		interfaceType := string(interfaceInfo.InterfaceType)
		if interfaceType == "" {
			interfaceType = string(network.EthernetInterface)
		}

		// TODO(dimitern): Support allocating one address per NIC on
		// the host, effectively creating the same number of NICs in
		// the container.
		result.Results[i] = params.MachineNetworkConfigResult{
			Config: []params.NetworkConfig{{
				DeviceIndex:      interfaceInfo.DeviceIndex,
				MACAddress:       macAddress,
				CIDR:             subnetInfo.CIDR,
				NetworkName:      interfaceInfo.NetworkName,
				ProviderId:       string(interfaceInfo.ProviderId),
				ProviderSubnetId: string(subnetInfo.ProviderId),
				VLANTag:          interfaceInfo.VLANTag,
				InterfaceType:    interfaceType,
				InterfaceName:    interfaceInfo.InterfaceName,
				Disabled:         interfaceInfo.Disabled,
				NoAutoStart:      interfaceInfo.NoAutoStart,
				DNSServers:       dnsServers,
				ConfigType:       string(network.ConfigStatic),
				Address:          address.Value(),
				GatewayAddress:   interfaceInfo.GatewayAddress.Value,
				ExtraConfig:      interfaceInfo.ExtraConfig,
			}},
		}
	}
	return result, nil
}