github.com/Pankov404/juju@v0.0.0-20150703034450-be266991dceb/apiserver/provisioner/provisioner.go (about)

     1  // Copyright 2012, 2013 Canonical Ltd.
     2  // Licensed under the AGPLv3, see LICENCE file for details.
     3  
     4  package provisioner
     5  
     6  import (
     7  	"fmt"
     8  	"sort"
     9  	"strings"
    10  
    11  	"github.com/juju/errors"
    12  	"github.com/juju/loggo"
    13  	"github.com/juju/names"
    14  	"github.com/juju/utils/set"
    15  
    16  	"github.com/juju/juju/apiserver/common"
    17  	"github.com/juju/juju/apiserver/params"
    18  	"github.com/juju/juju/cloudconfig/instancecfg"
    19  	"github.com/juju/juju/constraints"
    20  	"github.com/juju/juju/container"
    21  	"github.com/juju/juju/environs"
    22  	"github.com/juju/juju/environs/tags"
    23  	"github.com/juju/juju/instance"
    24  	"github.com/juju/juju/network"
    25  	"github.com/juju/juju/provider"
    26  	"github.com/juju/juju/state"
    27  	"github.com/juju/juju/state/multiwatcher"
    28  	"github.com/juju/juju/state/watcher"
    29  	"github.com/juju/juju/storage"
    30  	"github.com/juju/juju/storage/poolmanager"
    31  	"github.com/juju/juju/storage/provider/registry"
    32  )
    33  
    34  var logger = loggo.GetLogger("juju.apiserver.provisioner")
    35  
    36  func init() {
    37  	common.RegisterStandardFacade("Provisioner", 1, NewProvisionerAPI)
    38  }
    39  
    40  // ProvisionerAPI provides access to the Provisioner API facade.
    41  type ProvisionerAPI struct {
    42  	*common.Remover
    43  	*common.StatusSetter
    44  	*common.StatusGetter
    45  	*common.DeadEnsurer
    46  	*common.PasswordChanger
    47  	*common.LifeGetter
    48  	*common.StateAddresser
    49  	*common.APIAddresser
    50  	*common.EnvironWatcher
    51  	*common.EnvironMachinesWatcher
    52  	*common.InstanceIdGetter
    53  	*common.ToolsFinder
    54  
    55  	st          *state.State
    56  	resources   *common.Resources
    57  	authorizer  common.Authorizer
    58  	getAuthFunc common.GetAuthFunc
    59  }
    60  
    61  // NewProvisionerAPI creates a new server-side ProvisionerAPI facade.
    62  func NewProvisionerAPI(st *state.State, resources *common.Resources, authorizer common.Authorizer) (*ProvisionerAPI, error) {
    63  	if !authorizer.AuthMachineAgent() && !authorizer.AuthEnvironManager() {
    64  		return nil, common.ErrPerm
    65  	}
    66  	getAuthFunc := func() (common.AuthFunc, error) {
    67  		isEnvironManager := authorizer.AuthEnvironManager()
    68  		isMachineAgent := authorizer.AuthMachineAgent()
    69  		authEntityTag := authorizer.GetAuthTag()
    70  
    71  		return func(tag names.Tag) bool {
    72  			if isMachineAgent && tag == authEntityTag {
    73  				// A machine agent can always access its own machine.
    74  				return true
    75  			}
    76  			switch tag := tag.(type) {
    77  			case names.MachineTag:
    78  				parentId := state.ParentId(tag.Id())
    79  				if parentId == "" {
    80  					// All top-level machines are accessible by the
    81  					// environment manager.
    82  					return isEnvironManager
    83  				}
    84  				// All containers with the authenticated machine as a
    85  				// parent are accessible by it.
    86  				// TODO(dfc) sometimes authEntity tag is nil, which is fine because nil is
    87  				// only equal to nil, but it suggests someone is passing an authorizer
    88  				// with a nil tag.
    89  				return isMachineAgent && names.NewMachineTag(parentId) == authEntityTag
    90  			default:
    91  				return false
    92  			}
    93  		}, nil
    94  	}
    95  	env, err := st.Environment()
    96  	if err != nil {
    97  		return nil, err
    98  	}
    99  	urlGetter := common.NewToolsURLGetter(env.UUID(), st)
   100  	return &ProvisionerAPI{
   101  		Remover:                common.NewRemover(st, false, getAuthFunc),
   102  		StatusSetter:           common.NewStatusSetter(st, getAuthFunc),
   103  		StatusGetter:           common.NewStatusGetter(st, getAuthFunc),
   104  		DeadEnsurer:            common.NewDeadEnsurer(st, getAuthFunc),
   105  		PasswordChanger:        common.NewPasswordChanger(st, getAuthFunc),
   106  		LifeGetter:             common.NewLifeGetter(st, getAuthFunc),
   107  		StateAddresser:         common.NewStateAddresser(st),
   108  		APIAddresser:           common.NewAPIAddresser(st, resources),
   109  		EnvironWatcher:         common.NewEnvironWatcher(st, resources, authorizer),
   110  		EnvironMachinesWatcher: common.NewEnvironMachinesWatcher(st, resources, authorizer),
   111  		InstanceIdGetter:       common.NewInstanceIdGetter(st, getAuthFunc),
   112  		ToolsFinder:            common.NewToolsFinder(st, st, urlGetter),
   113  		st:                     st,
   114  		resources:              resources,
   115  		authorizer:             authorizer,
   116  		getAuthFunc:            getAuthFunc,
   117  	}, nil
   118  }
   119  
   120  func (p *ProvisionerAPI) getMachine(canAccess common.AuthFunc, tag names.MachineTag) (*state.Machine, error) {
   121  	if !canAccess(tag) {
   122  		return nil, common.ErrPerm
   123  	}
   124  	entity, err := p.st.FindEntity(tag)
   125  	if err != nil {
   126  		return nil, err
   127  	}
   128  	// The authorization function guarantees that the tag represents a
   129  	// machine.
   130  	return entity.(*state.Machine), nil
   131  }
   132  
   133  func (p *ProvisionerAPI) watchOneMachineContainers(arg params.WatchContainer) (params.StringsWatchResult, error) {
   134  	nothing := params.StringsWatchResult{}
   135  	canAccess, err := p.getAuthFunc()
   136  	if err != nil {
   137  		return nothing, common.ErrPerm
   138  	}
   139  	tag, err := names.ParseMachineTag(arg.MachineTag)
   140  	if err != nil {
   141  		return nothing, common.ErrPerm
   142  	}
   143  	if !canAccess(tag) {
   144  		return nothing, common.ErrPerm
   145  	}
   146  	machine, err := p.st.Machine(tag.Id())
   147  	if err != nil {
   148  		return nothing, err
   149  	}
   150  	var watch state.StringsWatcher
   151  	if arg.ContainerType != "" {
   152  		watch = machine.WatchContainers(instance.ContainerType(arg.ContainerType))
   153  	} else {
   154  		watch = machine.WatchAllContainers()
   155  	}
   156  	// Consume the initial event and forward it to the result.
   157  	if changes, ok := <-watch.Changes(); ok {
   158  		return params.StringsWatchResult{
   159  			StringsWatcherId: p.resources.Register(watch),
   160  			Changes:          changes,
   161  		}, nil
   162  	}
   163  	return nothing, watcher.EnsureErr(watch)
   164  }
   165  
   166  // WatchContainers starts a StringsWatcher to watch containers deployed to
   167  // any machine passed in args.
   168  func (p *ProvisionerAPI) WatchContainers(args params.WatchContainers) (params.StringsWatchResults, error) {
   169  	result := params.StringsWatchResults{
   170  		Results: make([]params.StringsWatchResult, len(args.Params)),
   171  	}
   172  	for i, arg := range args.Params {
   173  		watcherResult, err := p.watchOneMachineContainers(arg)
   174  		result.Results[i] = watcherResult
   175  		result.Results[i].Error = common.ServerError(err)
   176  	}
   177  	return result, nil
   178  }
   179  
   180  // WatchAllContainers starts a StringsWatcher to watch all containers deployed to
   181  // any machine passed in args.
   182  func (p *ProvisionerAPI) WatchAllContainers(args params.WatchContainers) (params.StringsWatchResults, error) {
   183  	return p.WatchContainers(args)
   184  }
   185  
   186  // SetSupportedContainers updates the list of containers supported by the machines passed in args.
   187  func (p *ProvisionerAPI) SetSupportedContainers(args params.MachineContainersParams) (params.ErrorResults, error) {
   188  	result := params.ErrorResults{
   189  		Results: make([]params.ErrorResult, len(args.Params)),
   190  	}
   191  
   192  	canAccess, err := p.getAuthFunc()
   193  	if err != nil {
   194  		return result, err
   195  	}
   196  	for i, arg := range args.Params {
   197  		tag, err := names.ParseMachineTag(arg.MachineTag)
   198  		if err != nil {
   199  			result.Results[i].Error = common.ServerError(common.ErrPerm)
   200  			continue
   201  		}
   202  		machine, err := p.getMachine(canAccess, tag)
   203  		if err != nil {
   204  			result.Results[i].Error = common.ServerError(err)
   205  			continue
   206  		}
   207  		if len(arg.ContainerTypes) == 0 {
   208  			err = machine.SupportsNoContainers()
   209  		} else {
   210  			err = machine.SetSupportedContainers(arg.ContainerTypes)
   211  		}
   212  		if err != nil {
   213  			result.Results[i].Error = common.ServerError(err)
   214  		}
   215  	}
   216  	return result, nil
   217  }
   218  
   219  // ContainerManagerConfig returns information from the environment config that is
   220  // needed for configuring the container manager.
   221  func (p *ProvisionerAPI) ContainerManagerConfig(args params.ContainerManagerConfigParams) (params.ContainerManagerConfig, error) {
   222  	var result params.ContainerManagerConfig
   223  	config, err := p.st.EnvironConfig()
   224  	if err != nil {
   225  		return result, err
   226  	}
   227  	cfg := make(map[string]string)
   228  	cfg[container.ConfigName] = container.DefaultNamespace
   229  
   230  	switch args.Type {
   231  	case instance.LXC:
   232  		if useLxcClone, ok := config.LXCUseClone(); ok {
   233  			cfg["use-clone"] = fmt.Sprint(useLxcClone)
   234  		}
   235  		if useLxcCloneAufs, ok := config.LXCUseCloneAUFS(); ok {
   236  			cfg["use-aufs"] = fmt.Sprint(useLxcCloneAufs)
   237  		}
   238  		if lxcDefaultMTU, ok := config.LXCDefaultMTU(); ok {
   239  			logger.Debugf("using default MTU %v for all LXC containers NICs", lxcDefaultMTU)
   240  			cfg[container.ConfigLXCDefaultMTU] = fmt.Sprintf("%d", lxcDefaultMTU)
   241  		}
   242  	}
   243  
   244  	if !environs.AddressAllocationEnabled() {
   245  		// No need to even try checking the environ for support.
   246  		logger.Debugf("address allocation feature flag not enabled")
   247  		result.ManagerConfig = cfg
   248  		return result, nil
   249  	}
   250  
   251  	// Create an environment to verify networking support.
   252  	env, err := environs.New(config)
   253  	if err != nil {
   254  		return result, err
   255  	}
   256  	if netEnv, ok := environs.SupportsNetworking(env); ok {
   257  		// Passing network.AnySubnet below should be interpreted by
   258  		// the provider as "does ANY subnet support this".
   259  		supported, err := netEnv.SupportsAddressAllocation(network.AnySubnet)
   260  		if err == nil && supported {
   261  			cfg[container.ConfigIPForwarding] = "true"
   262  		} else if err != nil {
   263  			// We log the error, but it's safe to ignore as it's not
   264  			// critical.
   265  			logger.Debugf("address allocation not supported (%v)", err)
   266  		}
   267  		// AWS requires NAT in place in order for hosted containers to
   268  		// reach outside.
   269  		if config.Type() == provider.EC2 {
   270  			cfg[container.ConfigEnableNAT] = "true"
   271  		}
   272  	}
   273  
   274  	result.ManagerConfig = cfg
   275  	return result, nil
   276  }
   277  
   278  // ContainerConfig returns information from the environment config that is
   279  // needed for container cloud-init.
   280  func (p *ProvisionerAPI) ContainerConfig() (params.ContainerConfig, error) {
   281  	result := params.ContainerConfig{}
   282  	config, err := p.st.EnvironConfig()
   283  	if err != nil {
   284  		return result, err
   285  	}
   286  
   287  	result.UpdateBehavior = &params.UpdateBehavior{
   288  		config.EnableOSRefreshUpdate(),
   289  		config.EnableOSUpgrade(),
   290  	}
   291  	result.ProviderType = config.Type()
   292  	result.AuthorizedKeys = config.AuthorizedKeys()
   293  	result.SSLHostnameVerification = config.SSLHostnameVerification()
   294  	result.Proxy = config.ProxySettings()
   295  	result.AptProxy = config.AptProxySettings()
   296  	result.PreferIPv6 = config.PreferIPv6()
   297  	result.AllowLXCLoopMounts, _ = config.AllowLXCLoopMounts()
   298  
   299  	return result, nil
   300  }
   301  
   302  // MachinesWithTransientErrors returns status data for machines with provisioning
   303  // errors which are transient.
   304  func (p *ProvisionerAPI) MachinesWithTransientErrors() (params.StatusResults, error) {
   305  	var results params.StatusResults
   306  	canAccessFunc, err := p.getAuthFunc()
   307  	if err != nil {
   308  		return results, err
   309  	}
   310  	// TODO (wallyworld) - add state.State API for more efficient machines query
   311  	machines, err := p.st.AllMachines()
   312  	if err != nil {
   313  		return results, err
   314  	}
   315  	for _, machine := range machines {
   316  		if !canAccessFunc(machine.Tag()) {
   317  			continue
   318  		}
   319  		if _, provisionedErr := machine.InstanceId(); provisionedErr == nil {
   320  			// Machine may have been provisioned but machiner hasn't set the
   321  			// status to Started yet.
   322  			continue
   323  		}
   324  		var result params.StatusResult
   325  		statusInfo, err := machine.Status()
   326  		if err != nil {
   327  			continue
   328  		}
   329  		result.Status = params.Status(statusInfo.Status)
   330  		result.Info = statusInfo.Message
   331  		result.Data = statusInfo.Data
   332  		if result.Status != params.StatusError {
   333  			continue
   334  		}
   335  		// Transient errors are marked as such in the status data.
   336  		if transient, ok := result.Data["transient"].(bool); !ok || !transient {
   337  			continue
   338  		}
   339  		result.Id = machine.Id()
   340  		result.Life = params.Life(machine.Life().String())
   341  		results.Results = append(results.Results, result)
   342  	}
   343  	return results, nil
   344  }
   345  
   346  // Series returns the deployed series for each given machine entity.
   347  func (p *ProvisionerAPI) Series(args params.Entities) (params.StringResults, error) {
   348  	result := params.StringResults{
   349  		Results: make([]params.StringResult, len(args.Entities)),
   350  	}
   351  	canAccess, err := p.getAuthFunc()
   352  	if err != nil {
   353  		return result, err
   354  	}
   355  	for i, entity := range args.Entities {
   356  		tag, err := names.ParseMachineTag(entity.Tag)
   357  		if err != nil {
   358  			result.Results[i].Error = common.ServerError(common.ErrPerm)
   359  			continue
   360  		}
   361  		machine, err := p.getMachine(canAccess, tag)
   362  		if err == nil {
   363  			result.Results[i].Result = machine.Series()
   364  		}
   365  		result.Results[i].Error = common.ServerError(err)
   366  	}
   367  	return result, nil
   368  }
   369  
   370  // ProvisioningInfo returns the provisioning information for each given machine entity.
   371  func (p *ProvisionerAPI) ProvisioningInfo(args params.Entities) (params.ProvisioningInfoResults, error) {
   372  	result := params.ProvisioningInfoResults{
   373  		Results: make([]params.ProvisioningInfoResult, len(args.Entities)),
   374  	}
   375  	canAccess, err := p.getAuthFunc()
   376  	if err != nil {
   377  		return result, err
   378  	}
   379  	for i, entity := range args.Entities {
   380  		tag, err := names.ParseMachineTag(entity.Tag)
   381  		if err != nil {
   382  			result.Results[i].Error = common.ServerError(common.ErrPerm)
   383  			continue
   384  		}
   385  		machine, err := p.getMachine(canAccess, tag)
   386  		if err == nil {
   387  			result.Results[i].Result, err = p.getProvisioningInfo(machine)
   388  		}
   389  		result.Results[i].Error = common.ServerError(err)
   390  	}
   391  	return result, nil
   392  }
   393  
   394  func (p *ProvisionerAPI) getProvisioningInfo(m *state.Machine) (*params.ProvisioningInfo, error) {
   395  	cons, err := m.Constraints()
   396  	if err != nil {
   397  		return nil, err
   398  	}
   399  	volumes, err := p.machineVolumeParams(m)
   400  	if err != nil {
   401  		return nil, errors.Trace(err)
   402  	}
   403  	// TODO(dimitern) For now, since network names and
   404  	// provider ids are the same, we return what we got
   405  	// from state. In the future, when networks can be
   406  	// added before provisioning, we should convert both
   407  	// slices from juju network names to provider-specific
   408  	// ids before returning them.
   409  	networks, err := m.RequestedNetworks()
   410  	if err != nil {
   411  		return nil, err
   412  	}
   413  	var jobs []multiwatcher.MachineJob
   414  	for _, job := range m.Jobs() {
   415  		jobs = append(jobs, job.ToParams())
   416  	}
   417  	tags, err := p.machineTags(m, jobs)
   418  	if err != nil {
   419  		return nil, errors.Trace(err)
   420  	}
   421  	return &params.ProvisioningInfo{
   422  		Constraints: cons,
   423  		Series:      m.Series(),
   424  		Placement:   m.Placement(),
   425  		Networks:    networks,
   426  		Jobs:        jobs,
   427  		Volumes:     volumes,
   428  		Tags:        tags,
   429  	}, nil
   430  }
   431  
   432  // DistributionGroup returns, for each given machine entity,
   433  // a slice of instance.Ids that belong to the same distribution
   434  // group as that machine. This information may be used to
   435  // distribute instances for high availability.
   436  func (p *ProvisionerAPI) DistributionGroup(args params.Entities) (params.DistributionGroupResults, error) {
   437  	result := params.DistributionGroupResults{
   438  		Results: make([]params.DistributionGroupResult, len(args.Entities)),
   439  	}
   440  	canAccess, err := p.getAuthFunc()
   441  	if err != nil {
   442  		return result, err
   443  	}
   444  	for i, entity := range args.Entities {
   445  		tag, err := names.ParseMachineTag(entity.Tag)
   446  		if err != nil {
   447  			result.Results[i].Error = common.ServerError(common.ErrPerm)
   448  			continue
   449  		}
   450  		machine, err := p.getMachine(canAccess, tag)
   451  		if err == nil {
   452  			// If the machine is an environment manager, return
   453  			// environment manager instances. Otherwise, return
   454  			// instances with services in common with the machine
   455  			// being provisioned.
   456  			if machine.IsManager() {
   457  				result.Results[i].Result, err = environManagerInstances(p.st)
   458  			} else {
   459  				result.Results[i].Result, err = commonServiceInstances(p.st, machine)
   460  			}
   461  		}
   462  		result.Results[i].Error = common.ServerError(err)
   463  	}
   464  	return result, nil
   465  }
   466  
   467  // environManagerInstances returns all environ manager instances.
   468  func environManagerInstances(st *state.State) ([]instance.Id, error) {
   469  	info, err := st.StateServerInfo()
   470  	if err != nil {
   471  		return nil, err
   472  	}
   473  	instances := make([]instance.Id, 0, len(info.MachineIds))
   474  	for _, id := range info.MachineIds {
   475  		machine, err := st.Machine(id)
   476  		if err != nil {
   477  			return nil, err
   478  		}
   479  		instanceId, err := machine.InstanceId()
   480  		if err == nil {
   481  			instances = append(instances, instanceId)
   482  		} else if !errors.IsNotProvisioned(err) {
   483  			return nil, err
   484  		}
   485  	}
   486  	return instances, nil
   487  }
   488  
   489  // commonServiceInstances returns instances with
   490  // services in common with the specified machine.
   491  func commonServiceInstances(st *state.State, m *state.Machine) ([]instance.Id, error) {
   492  	units, err := m.Units()
   493  	if err != nil {
   494  		return nil, err
   495  	}
   496  	instanceIdSet := make(set.Strings)
   497  	for _, unit := range units {
   498  		if !unit.IsPrincipal() {
   499  			continue
   500  		}
   501  		instanceIds, err := state.ServiceInstances(st, unit.ServiceName())
   502  		if err != nil {
   503  			return nil, err
   504  		}
   505  		for _, instanceId := range instanceIds {
   506  			instanceIdSet.Add(string(instanceId))
   507  		}
   508  	}
   509  	instanceIds := make([]instance.Id, instanceIdSet.Size())
   510  	// Sort values to simplify testing.
   511  	for i, instanceId := range instanceIdSet.SortedValues() {
   512  		instanceIds[i] = instance.Id(instanceId)
   513  	}
   514  	return instanceIds, nil
   515  }
   516  
   517  // Constraints returns the constraints for each given machine entity.
   518  func (p *ProvisionerAPI) Constraints(args params.Entities) (params.ConstraintsResults, error) {
   519  	result := params.ConstraintsResults{
   520  		Results: make([]params.ConstraintsResult, len(args.Entities)),
   521  	}
   522  	canAccess, err := p.getAuthFunc()
   523  	if err != nil {
   524  		return result, err
   525  	}
   526  	for i, entity := range args.Entities {
   527  		tag, err := names.ParseMachineTag(entity.Tag)
   528  		if err != nil {
   529  			result.Results[i].Error = common.ServerError(common.ErrPerm)
   530  			continue
   531  		}
   532  		machine, err := p.getMachine(canAccess, tag)
   533  		if err == nil {
   534  			var cons constraints.Value
   535  			cons, err = machine.Constraints()
   536  			if err == nil {
   537  				result.Results[i].Constraints = cons
   538  			}
   539  		}
   540  		result.Results[i].Error = common.ServerError(err)
   541  	}
   542  	return result, nil
   543  }
   544  
   545  // machineVolumeParams retrieves VolumeParams for the volumes that should be
   546  // provisioned with, and attached to, the machine. The client should ignore
   547  // parameters that it does not know how to handle.
   548  func (p *ProvisionerAPI) machineVolumeParams(m *state.Machine) ([]params.VolumeParams, error) {
   549  	volumeAttachments, err := m.VolumeAttachments()
   550  	if err != nil {
   551  		return nil, err
   552  	}
   553  	if len(volumeAttachments) == 0 {
   554  		return nil, nil
   555  	}
   556  	envConfig, err := p.st.EnvironConfig()
   557  	if err != nil {
   558  		return nil, err
   559  	}
   560  	poolManager := poolmanager.New(state.NewStateSettings(p.st))
   561  	allVolumeParams := make([]params.VolumeParams, 0, len(volumeAttachments))
   562  	for _, volumeAttachment := range volumeAttachments {
   563  		volumeTag := volumeAttachment.Volume()
   564  		volume, err := p.st.Volume(volumeTag)
   565  		if err != nil {
   566  			return nil, errors.Annotatef(err, "getting volume %q", volumeTag.Id())
   567  		}
   568  		storageInstance, err := common.MaybeAssignedStorageInstance(
   569  			volume.StorageInstance, p.st.StorageInstance,
   570  		)
   571  		if err != nil {
   572  			return nil, errors.Annotatef(err, "getting volume %q storage instance", volumeTag.Id())
   573  		}
   574  		volumeParams, err := common.VolumeParams(volume, storageInstance, envConfig, poolManager)
   575  		if err != nil {
   576  			return nil, errors.Annotatef(err, "getting volume %q parameters", volumeTag.Id())
   577  		}
   578  		provider, err := registry.StorageProvider(storage.ProviderType(volumeParams.Provider))
   579  		if err != nil {
   580  			return nil, errors.Annotate(err, "getting storage provider")
   581  		}
   582  		if provider.Dynamic() {
   583  			// Leave dynamic storage to the storage provisioner.
   584  			continue
   585  		}
   586  		volumeAttachmentParams, ok := volumeAttachment.Params()
   587  		if !ok {
   588  			// Attachment is already provisioned; this is an insane
   589  			// state, so we should not proceed with the volume.
   590  			return nil, errors.Errorf(
   591  				"volume %s already attached to machine %s",
   592  				volumeTag.Id(), m.Id(),
   593  			)
   594  		}
   595  		// Not provisioned yet, so ask the cloud provisioner do it.
   596  		volumeParams.Attachment = &params.VolumeAttachmentParams{
   597  			volumeTag.String(),
   598  			m.Tag().String(),
   599  			"", // we're creating the volume, so it has no volume ID.
   600  			"", // we're creating the machine, so it has no instance ID.
   601  			volumeParams.Provider,
   602  			volumeAttachmentParams.ReadOnly,
   603  		}
   604  		allVolumeParams = append(allVolumeParams, volumeParams)
   605  	}
   606  	return allVolumeParams, nil
   607  }
   608  
   609  // storageConfig returns the provider type and config attributes for the
   610  // specified poolName. If no such pool exists, we check to see if poolName is
   611  // actually a provider type, in which case config will be empty.
   612  func storageConfig(st *state.State, poolName string) (storage.ProviderType, map[string]interface{}, error) {
   613  	pm := poolmanager.New(state.NewStateSettings(st))
   614  	p, err := pm.Get(poolName)
   615  	// If not a storage pool, then maybe a provider type.
   616  	if errors.IsNotFound(err) {
   617  		providerType := storage.ProviderType(poolName)
   618  		if _, err1 := registry.StorageProvider(providerType); err1 != nil {
   619  			return "", nil, errors.Trace(err)
   620  		}
   621  		return providerType, nil, nil
   622  	}
   623  	if err != nil {
   624  		return "", nil, errors.Trace(err)
   625  	}
   626  	return p.Provider(), p.Attrs(), nil
   627  }
   628  
   629  // volumeAttachmentsToState converts a slice of storage.VolumeAttachment to a
   630  // mapping of volume names to state.VolumeAttachmentInfo.
   631  func volumeAttachmentsToState(in []params.VolumeAttachment) (map[names.VolumeTag]state.VolumeAttachmentInfo, error) {
   632  	m := make(map[names.VolumeTag]state.VolumeAttachmentInfo)
   633  	for _, v := range in {
   634  		if v.VolumeTag == "" {
   635  			return nil, errors.New("Tag is empty")
   636  		}
   637  		volumeTag, err := names.ParseVolumeTag(v.VolumeTag)
   638  		if err != nil {
   639  			return nil, errors.Trace(err)
   640  		}
   641  		m[volumeTag] = state.VolumeAttachmentInfo{
   642  			v.Info.DeviceName,
   643  			v.Info.ReadOnly,
   644  		}
   645  	}
   646  	return m, nil
   647  }
   648  
   649  func networkParamsToStateParams(networks []params.Network, ifaces []params.NetworkInterface) (
   650  	[]state.NetworkInfo, []state.NetworkInterfaceInfo, error,
   651  ) {
   652  	stateNetworks := make([]state.NetworkInfo, len(networks))
   653  	for i, net := range networks {
   654  		tag, err := names.ParseNetworkTag(net.Tag)
   655  		if err != nil {
   656  			return nil, nil, err
   657  		}
   658  		stateNetworks[i] = state.NetworkInfo{
   659  			Name:       tag.Id(),
   660  			ProviderId: network.Id(net.ProviderId),
   661  			CIDR:       net.CIDR,
   662  			VLANTag:    net.VLANTag,
   663  		}
   664  	}
   665  	stateInterfaces := make([]state.NetworkInterfaceInfo, len(ifaces))
   666  	for i, iface := range ifaces {
   667  		tag, err := names.ParseNetworkTag(iface.NetworkTag)
   668  		if err != nil {
   669  			return nil, nil, err
   670  		}
   671  		stateInterfaces[i] = state.NetworkInterfaceInfo{
   672  			MACAddress:    iface.MACAddress,
   673  			NetworkName:   tag.Id(),
   674  			InterfaceName: iface.InterfaceName,
   675  			IsVirtual:     iface.IsVirtual,
   676  			Disabled:      iface.Disabled,
   677  		}
   678  	}
   679  	return stateNetworks, stateInterfaces, nil
   680  }
   681  
   682  // RequestedNetworks returns the requested networks for each given
   683  // machine entity. Each entry in both lists is returned with its
   684  // provider specific id.
   685  func (p *ProvisionerAPI) RequestedNetworks(args params.Entities) (params.RequestedNetworksResults, error) {
   686  	result := params.RequestedNetworksResults{
   687  		Results: make([]params.RequestedNetworkResult, len(args.Entities)),
   688  	}
   689  	canAccess, err := p.getAuthFunc()
   690  	if err != nil {
   691  		return result, err
   692  	}
   693  	for i, entity := range args.Entities {
   694  		tag, err := names.ParseMachineTag(entity.Tag)
   695  		if err != nil {
   696  			result.Results[i].Error = common.ServerError(common.ErrPerm)
   697  			continue
   698  		}
   699  		machine, err := p.getMachine(canAccess, tag)
   700  		if err == nil {
   701  			var networks []string
   702  			networks, err = machine.RequestedNetworks()
   703  			if err == nil {
   704  				// TODO(dimitern) For now, since network names and
   705  				// provider ids are the same, we return what we got
   706  				// from state. In the future, when networks can be
   707  				// added before provisioning, we should convert both
   708  				// slices from juju network names to provider-specific
   709  				// ids before returning them.
   710  				result.Results[i].Networks = networks
   711  			}
   712  		}
   713  		result.Results[i].Error = common.ServerError(err)
   714  	}
   715  	return result, nil
   716  }
   717  
   718  // SetProvisioned sets the provider specific instance id, nonce and
   719  // metadata for each given machine. Once set, the instance id cannot
   720  // be changed.
   721  //
   722  // TODO(dimitern) This is not used anymore (as of 1.19.0) and is
   723  // retained only for backwards-compatibility. It should be removed as
   724  // deprecated. SetInstanceInfo is used instead.
   725  func (p *ProvisionerAPI) SetProvisioned(args params.SetProvisioned) (params.ErrorResults, error) {
   726  	result := params.ErrorResults{
   727  		Results: make([]params.ErrorResult, len(args.Machines)),
   728  	}
   729  	canAccess, err := p.getAuthFunc()
   730  	if err != nil {
   731  		return result, err
   732  	}
   733  	for i, arg := range args.Machines {
   734  		tag, err := names.ParseMachineTag(arg.Tag)
   735  		if err != nil {
   736  			result.Results[i].Error = common.ServerError(common.ErrPerm)
   737  			continue
   738  		}
   739  		machine, err := p.getMachine(canAccess, tag)
   740  		if err == nil {
   741  			err = machine.SetProvisioned(arg.InstanceId, arg.Nonce, arg.Characteristics)
   742  		}
   743  		result.Results[i].Error = common.ServerError(err)
   744  	}
   745  	return result, nil
   746  }
   747  
   748  // SetInstanceInfo sets the provider specific machine id, nonce,
   749  // metadata and network info for each given machine. Once set, the
   750  // instance id cannot be changed.
   751  func (p *ProvisionerAPI) SetInstanceInfo(args params.InstancesInfo) (params.ErrorResults, error) {
   752  	result := params.ErrorResults{
   753  		Results: make([]params.ErrorResult, len(args.Machines)),
   754  	}
   755  	canAccess, err := p.getAuthFunc()
   756  	if err != nil {
   757  		return result, err
   758  	}
   759  	setInstanceInfo := func(arg params.InstanceInfo) error {
   760  		tag, err := names.ParseMachineTag(arg.Tag)
   761  		if err != nil {
   762  			return common.ErrPerm
   763  		}
   764  		machine, err := p.getMachine(canAccess, tag)
   765  		if err != nil {
   766  			return err
   767  		}
   768  		networks, interfaces, err := networkParamsToStateParams(arg.Networks, arg.Interfaces)
   769  		if err != nil {
   770  			return err
   771  		}
   772  		volumes, err := common.VolumesToState(arg.Volumes)
   773  		if err != nil {
   774  			return err
   775  		}
   776  		volumeAttachments, err := common.VolumeAttachmentInfosToState(arg.VolumeAttachments)
   777  		if err != nil {
   778  			return err
   779  		}
   780  		if err = machine.SetInstanceInfo(
   781  			arg.InstanceId, arg.Nonce, arg.Characteristics,
   782  			networks, interfaces, volumes, volumeAttachments); err != nil {
   783  			return errors.Annotatef(
   784  				err,
   785  				"cannot record provisioning info for %q",
   786  				arg.InstanceId,
   787  			)
   788  		}
   789  		return nil
   790  	}
   791  	for i, arg := range args.Machines {
   792  		err := setInstanceInfo(arg)
   793  		result.Results[i].Error = common.ServerError(err)
   794  	}
   795  	return result, nil
   796  }
   797  
   798  // WatchMachineErrorRetry returns a NotifyWatcher that notifies when
   799  // the provisioner should retry provisioning machines with transient errors.
   800  func (p *ProvisionerAPI) WatchMachineErrorRetry() (params.NotifyWatchResult, error) {
   801  	result := params.NotifyWatchResult{}
   802  	if !p.authorizer.AuthEnvironManager() {
   803  		return result, common.ErrPerm
   804  	}
   805  	watch := newWatchMachineErrorRetry()
   806  	// Consume any initial event and forward it to the result.
   807  	if _, ok := <-watch.Changes(); ok {
   808  		result.NotifyWatcherId = p.resources.Register(watch)
   809  	} else {
   810  		return result, watcher.EnsureErr(watch)
   811  	}
   812  	return result, nil
   813  }
   814  
   815  // ReleaseContainerAddresses finds addresses allocated to a container
   816  // and marks them as Dead, to be released and removed. It accepts
   817  // container tags as arguments. If address allocation feature flag is
   818  // not enabled, it will return a NotSupported error.
   819  func (p *ProvisionerAPI) ReleaseContainerAddresses(args params.Entities) (params.ErrorResults, error) {
   820  	result := params.ErrorResults{
   821  		Results: make([]params.ErrorResult, len(args.Entities)),
   822  	}
   823  
   824  	if !environs.AddressAllocationEnabled() {
   825  		return result, errors.NotSupportedf("address allocation")
   826  	}
   827  
   828  	canAccess, err := p.getAuthFunc()
   829  	if err != nil {
   830  		logger.Errorf("failed to get an authorisation function: %v", err)
   831  		return result, errors.Trace(err)
   832  	}
   833  	// Loop over the passed container tags.
   834  	for i, entity := range args.Entities {
   835  		tag, err := names.ParseMachineTag(entity.Tag)
   836  		if err != nil {
   837  			logger.Warningf("failed to parse machine tag %q: %v", entity.Tag, err)
   838  			result.Results[i].Error = common.ServerError(common.ErrPerm)
   839  			continue
   840  		}
   841  
   842  		// The auth function (canAccess) checks that the machine is a
   843  		// top level machine (we filter those out next) or that the
   844  		// machine has the host as a parent.
   845  		container, err := p.getMachine(canAccess, tag)
   846  		if err != nil {
   847  			logger.Warningf("failed to get machine %q: %v", tag, err)
   848  			result.Results[i].Error = common.ServerError(err)
   849  			continue
   850  		} else if !container.IsContainer() {
   851  			err = errors.Errorf("cannot mark addresses for removal for %q: not a container", tag)
   852  			result.Results[i].Error = common.ServerError(err)
   853  			continue
   854  		}
   855  
   856  		id := container.Id()
   857  		addresses, err := p.st.AllocatedIPAddresses(id)
   858  		if err != nil {
   859  			logger.Warningf("failed to get Id for container %q: %v", tag, err)
   860  			result.Results[i].Error = common.ServerError(err)
   861  			continue
   862  		}
   863  
   864  		deadErrors := []error{}
   865  		logger.Debugf("for container %q found addresses %v", tag, addresses)
   866  		for _, addr := range addresses {
   867  			err = addr.EnsureDead()
   868  			if err != nil {
   869  				deadErrors = append(deadErrors, err)
   870  				continue
   871  			}
   872  		}
   873  		if len(deadErrors) != 0 {
   874  			err = errors.Errorf("failed to mark all addresses for removal for %q: %v", tag, deadErrors)
   875  			result.Results[i].Error = common.ServerError(err)
   876  		}
   877  	}
   878  
   879  	return result, nil
   880  }
   881  
   882  // PrepareContainerInterfaceInfo allocates an address and returns
   883  // information to configure networking for a container. It accepts
   884  // container tags as arguments. If the address allocation feature flag
   885  // is not enabled, it returns a NotSupported error.
   886  func (p *ProvisionerAPI) PrepareContainerInterfaceInfo(args params.Entities) (
   887  	params.MachineNetworkConfigResults, error) {
   888  	return p.prepareOrGetContainerInterfaceInfo(args, true)
   889  }
   890  
   891  // GetContainerInterfaceInfo returns information to configure networking
   892  // for a container. It accepts container tags as arguments. If the address
   893  // allocation feature flag is not enabled, it returns a NotSupported error.
   894  func (p *ProvisionerAPI) GetContainerInterfaceInfo(args params.Entities) (
   895  	params.MachineNetworkConfigResults, error) {
   896  	return p.prepareOrGetContainerInterfaceInfo(args, false)
   897  }
   898  
   899  // prepareOrGetContainerInterfaceInfo optionally allocates an address and returns information
   900  // for configuring networking on a container. It accepts container tags as arguments.
   901  func (p *ProvisionerAPI) prepareOrGetContainerInterfaceInfo(
   902  	args params.Entities, provisionContainer bool) (
   903  	params.MachineNetworkConfigResults, error) {
   904  	result := params.MachineNetworkConfigResults{
   905  		Results: make([]params.MachineNetworkConfigResult, len(args.Entities)),
   906  	}
   907  
   908  	if !environs.AddressAllocationEnabled() {
   909  		return result, errors.NotSupportedf("address allocation")
   910  	}
   911  
   912  	// Some preparations first.
   913  	environ, host, canAccess, err := p.prepareContainerAccessEnvironment()
   914  	if err != nil {
   915  		return result, errors.Trace(err)
   916  	}
   917  	instId, err := host.InstanceId()
   918  	if err != nil && errors.IsNotProvisioned(err) {
   919  		// If the host machine is not provisioned yet, we have nothing
   920  		// to do. NotProvisionedf will append " not provisioned" to
   921  		// the message.
   922  		err = errors.NotProvisionedf("cannot allocate addresses: host machine %q", host)
   923  		return result, err
   924  	}
   925  	subnet, subnetInfo, interfaceInfo, err := p.prepareAllocationNetwork(environ, host, instId)
   926  	if err != nil {
   927  		return result, errors.Annotate(err, "cannot allocate addresses")
   928  	}
   929  
   930  	// Loop over the passed container tags.
   931  	for i, entity := range args.Entities {
   932  		tag, err := names.ParseMachineTag(entity.Tag)
   933  		if err != nil {
   934  			result.Results[i].Error = common.ServerError(err)
   935  			continue
   936  		}
   937  
   938  		// The auth function (canAccess) checks that the machine is a
   939  		// top level machine (we filter those out next) or that the
   940  		// machine has the host as a parent.
   941  		container, err := p.getMachine(canAccess, tag)
   942  		if err != nil {
   943  			result.Results[i].Error = common.ServerError(err)
   944  			continue
   945  		} else if !container.IsContainer() {
   946  			err = errors.Errorf("cannot allocate address for %q: not a container", tag)
   947  			result.Results[i].Error = common.ServerError(err)
   948  			continue
   949  		} else if ciid, cerr := container.InstanceId(); provisionContainer == true && cerr == nil {
   950  			// Since we want to configure and create NICs on the
   951  			// container before it starts, it must also be not
   952  			// provisioned yet.
   953  			err = errors.Errorf("container %q already provisioned as %q", container, ciid)
   954  			result.Results[i].Error = common.ServerError(err)
   955  			continue
   956  		} else if cerr != nil && !errors.IsNotProvisioned(cerr) {
   957  			// Any other error needs to be reported.
   958  			result.Results[i].Error = common.ServerError(cerr)
   959  			continue
   960  		}
   961  
   962  		var addresses []*state.IPAddress
   963  		if provisionContainer {
   964  			// Allocate and set address.
   965  			addr, err := p.allocateAddress(environ, subnet, host, container, instId)
   966  			addresses = append(addresses, addr)
   967  			if err != nil {
   968  				err = errors.Annotatef(err, "failed to allocate an address for %q", container)
   969  				result.Results[i].Error = common.ServerError(err)
   970  				continue
   971  			}
   972  		} else {
   973  			id := container.Id()
   974  			addresses, err = p.st.AllocatedIPAddresses(id)
   975  			if err != nil {
   976  				logger.Warningf("failed to get Id for container %q: %v", tag, err)
   977  				result.Results[i].Error = common.ServerError(err)
   978  				continue
   979  			}
   980  			// TODO(dooferlad): if we get more than 1 address back, we ignore everything after
   981  			// the first. The calling function expects exactly one result though,
   982  			// so we don't appear to have a way of allocating >1 address to a
   983  			// container...
   984  			if len(addresses) != 1 {
   985  				logger.Warningf("got %d addresses for container %q - expected 1: %v", len(addresses), tag, err)
   986  				result.Results[i].Error = common.ServerError(err)
   987  				continue
   988  			}
   989  		}
   990  		// Store it on the machine, construct and set an interface result.
   991  		dnsServers := make([]string, len(interfaceInfo.DNSServers))
   992  		for i, dns := range interfaceInfo.DNSServers {
   993  			dnsServers[i] = dns.Value
   994  		}
   995  		// TODO(dimitern): Support allocating one address per NIC on
   996  		// the host, effectively creating the same number of NICs in
   997  		// the container.
   998  		result.Results[i] = params.MachineNetworkConfigResult{
   999  			Config: []params.NetworkConfig{{
  1000  				DeviceIndex:      interfaceInfo.DeviceIndex,
  1001  				MACAddress:       interfaceInfo.MACAddress,
  1002  				CIDR:             subnetInfo.CIDR,
  1003  				NetworkName:      interfaceInfo.NetworkName,
  1004  				ProviderId:       string(interfaceInfo.ProviderId),
  1005  				ProviderSubnetId: string(subnetInfo.ProviderId),
  1006  				VLANTag:          interfaceInfo.VLANTag,
  1007  				InterfaceName:    interfaceInfo.InterfaceName,
  1008  				Disabled:         interfaceInfo.Disabled,
  1009  				NoAutoStart:      interfaceInfo.NoAutoStart,
  1010  				DNSServers:       dnsServers,
  1011  				ConfigType:       string(network.ConfigStatic),
  1012  				Address:          addresses[0].Value(),
  1013  				// container's gateway is the host's primary NIC's IP.
  1014  				GatewayAddress: interfaceInfo.Address.Value,
  1015  				ExtraConfig:    interfaceInfo.ExtraConfig,
  1016  			}},
  1017  		}
  1018  	}
  1019  	return result, nil
  1020  }
  1021  
  1022  // prepareContainerAccessEnvironment retrieves the environment, host machine, and access
  1023  // for working with containers.
  1024  func (p *ProvisionerAPI) prepareContainerAccessEnvironment() (environs.NetworkingEnviron, *state.Machine, common.AuthFunc, error) {
  1025  	cfg, err := p.st.EnvironConfig()
  1026  	if err != nil {
  1027  		return nil, nil, nil, errors.Annotate(err, "failed to get environment config")
  1028  	}
  1029  	environ, err := environs.New(cfg)
  1030  	if err != nil {
  1031  		return nil, nil, nil, errors.Annotate(err, "failed to construct an environment from config")
  1032  	}
  1033  	netEnviron, supported := environs.SupportsNetworking(environ)
  1034  	if !supported {
  1035  		// " not supported" will be appended to the message below.
  1036  		return nil, nil, nil, errors.NotSupportedf("environment %q networking", cfg.Name())
  1037  	}
  1038  
  1039  	canAccess, err := p.getAuthFunc()
  1040  	if err != nil {
  1041  		return nil, nil, nil, errors.Annotate(err, "cannot authenticate request")
  1042  	}
  1043  	hostAuthTag := p.authorizer.GetAuthTag()
  1044  	if hostAuthTag == nil {
  1045  		return nil, nil, nil, errors.Errorf("authenticated entity tag is nil")
  1046  	}
  1047  	hostTag, err := names.ParseMachineTag(hostAuthTag.String())
  1048  	if err != nil {
  1049  		return nil, nil, nil, errors.Trace(err)
  1050  	}
  1051  	host, err := p.getMachine(canAccess, hostTag)
  1052  	if err != nil {
  1053  		return nil, nil, nil, errors.Trace(err)
  1054  	}
  1055  	return netEnviron, host, canAccess, nil
  1056  }
  1057  
  1058  // prepareAllocationNetwork retrieves the subnet, its info, and the interface info
  1059  // for the allocations.
  1060  func (p *ProvisionerAPI) prepareAllocationNetwork(
  1061  	environ environs.NetworkingEnviron,
  1062  	host *state.Machine,
  1063  	instId instance.Id,
  1064  ) (
  1065  	*state.Subnet,
  1066  	network.SubnetInfo,
  1067  	network.InterfaceInfo,
  1068  	error,
  1069  ) {
  1070  	var subnetInfo network.SubnetInfo
  1071  	var interfaceInfo network.InterfaceInfo
  1072  
  1073  	interfaces, err := environ.NetworkInterfaces(instId)
  1074  	if err != nil {
  1075  		return nil, subnetInfo, interfaceInfo, errors.Trace(err)
  1076  	} else if len(interfaces) == 0 {
  1077  		return nil, subnetInfo, interfaceInfo, errors.Errorf("no interfaces available")
  1078  	}
  1079  	logger.Tracef("interfaces for instance %q: %v", instId, interfaces)
  1080  
  1081  	subnetSet := make(set.Strings)
  1082  	subnetIds := []network.Id{}
  1083  	subnetIdToInterface := make(map[network.Id]network.InterfaceInfo)
  1084  	for _, iface := range interfaces {
  1085  		if iface.ProviderSubnetId == "" {
  1086  			logger.Debugf("no subnet associated with interface %#v (skipping)", iface)
  1087  			continue
  1088  		} else if iface.Disabled {
  1089  			logger.Debugf("interface %#v disabled (skipping)", iface)
  1090  			continue
  1091  		}
  1092  		if !subnetSet.Contains(string(iface.ProviderSubnetId)) {
  1093  			subnetIds = append(subnetIds, iface.ProviderSubnetId)
  1094  			subnetSet.Add(string(iface.ProviderSubnetId))
  1095  
  1096  			// This means that multiple interfaces on the same subnet will
  1097  			// only appear once.
  1098  			subnetIdToInterface[iface.ProviderSubnetId] = iface
  1099  		}
  1100  	}
  1101  	subnets, err := environ.Subnets(instId, subnetIds)
  1102  	if err != nil {
  1103  		return nil, subnetInfo, interfaceInfo, errors.Trace(err)
  1104  	} else if len(subnets) == 0 {
  1105  		return nil, subnetInfo, interfaceInfo, errors.Errorf("no subnets available")
  1106  	}
  1107  	logger.Tracef("subnets for instance %q: %v", instId, subnets)
  1108  
  1109  	// TODO(mfoord): we need a better strategy for picking a subnet to
  1110  	// allocate an address on. (dimitern): Right now we just pick the
  1111  	// first subnet with allocatable range set. Instead, we should
  1112  	// allocate an address per interface, assuming each interface is
  1113  	// on a subnet with allocatable range set, and skipping those
  1114  	// which do not have a range set.
  1115  	var success bool
  1116  	for _, sub := range subnets {
  1117  		logger.Tracef("trying to allocate a static IP on subnet %q", sub.ProviderId)
  1118  		if sub.AllocatableIPHigh == nil {
  1119  			logger.Tracef("ignoring subnet %q - no allocatable range set", sub.ProviderId)
  1120  			// this subnet has no allocatable IPs
  1121  			continue
  1122  		}
  1123  		ok, err := environ.SupportsAddressAllocation(sub.ProviderId)
  1124  		if err == nil && ok {
  1125  			subnetInfo = sub
  1126  			interfaceInfo = subnetIdToInterface[sub.ProviderId]
  1127  			success = true
  1128  			break
  1129  		}
  1130  		logger.Tracef(
  1131  			"subnet %q supports address allocation: %v (error: %v)",
  1132  			sub.ProviderId, ok, err,
  1133  		)
  1134  	}
  1135  	if !success {
  1136  		// " not supported" will be appended to the message below.
  1137  		return nil, subnetInfo, interfaceInfo, errors.NotSupportedf(
  1138  			"address allocation on any available subnets is",
  1139  		)
  1140  	}
  1141  	subnet, err := p.createOrFetchStateSubnet(subnetInfo)
  1142  
  1143  	return subnet, subnetInfo, interfaceInfo, nil
  1144  }
  1145  
  1146  // These are defined like this to allow mocking in tests.
  1147  var (
  1148  	allocateAddrTo = func(a *state.IPAddress, m *state.Machine) error {
  1149  		// TODO(mfoord): populate proper interface ID (in state).
  1150  		return a.AllocateTo(m.Id(), "")
  1151  	}
  1152  	setAddrsTo = func(a *state.IPAddress, m *state.Machine) error {
  1153  		return m.SetProviderAddresses(a.Address())
  1154  	}
  1155  	setAddrState = func(a *state.IPAddress, st state.AddressState) error {
  1156  		return a.SetState(st)
  1157  	}
  1158  )
  1159  
  1160  // allocateAddress tries to pick an address out of the given subnet and
  1161  // allocates it to the container.
  1162  func (p *ProvisionerAPI) allocateAddress(
  1163  	environ environs.NetworkingEnviron,
  1164  	subnet *state.Subnet,
  1165  	host, container *state.Machine,
  1166  	instId instance.Id,
  1167  ) (*state.IPAddress, error) {
  1168  
  1169  	subnetId := network.Id(subnet.ProviderId())
  1170  	for {
  1171  		addr, err := subnet.PickNewAddress()
  1172  		if err != nil {
  1173  			return nil, err
  1174  		}
  1175  		logger.Tracef("picked new address %q on subnet %q", addr.String(), subnetId)
  1176  		// Attempt to allocate with environ.
  1177  		err = environ.AllocateAddress(instId, subnetId, addr.Address())
  1178  		if err != nil {
  1179  			logger.Warningf(
  1180  				"allocating address %q on instance %q and subnet %q failed: %v (retrying)",
  1181  				addr.String(), instId, subnetId, err,
  1182  			)
  1183  			// It's as good as unavailable for us, so mark it as
  1184  			// such.
  1185  			err = setAddrState(addr, state.AddressStateUnavailable)
  1186  			if err != nil {
  1187  				logger.Warningf(
  1188  					"cannot set address %q to %q: %v (ignoring and retrying)",
  1189  					addr.String(), state.AddressStateUnavailable, err,
  1190  				)
  1191  				continue
  1192  			}
  1193  			logger.Tracef(
  1194  				"setting address %q to %q and retrying",
  1195  				addr.String(), state.AddressStateUnavailable,
  1196  			)
  1197  			continue
  1198  		}
  1199  		logger.Infof(
  1200  			"allocated address %q on instance %q and subnet %q",
  1201  			addr.String(), instId, subnetId,
  1202  		)
  1203  		err = p.setAllocatedOrRelease(addr, environ, instId, container, subnetId)
  1204  		if err != nil {
  1205  			// Something went wrong - retry.
  1206  			continue
  1207  		}
  1208  		return addr, nil
  1209  	}
  1210  }
  1211  
  1212  // setAllocatedOrRelease tries to associate the newly allocated
  1213  // address addr with the container. On failure it makes the best
  1214  // effort to cleanup and release addr, logging issues along the way.
  1215  func (p *ProvisionerAPI) setAllocatedOrRelease(
  1216  	addr *state.IPAddress,
  1217  	environ environs.NetworkingEnviron,
  1218  	instId instance.Id,
  1219  	container *state.Machine,
  1220  	subnetId network.Id,
  1221  ) (err error) {
  1222  	defer func() {
  1223  		if errors.Cause(err) == nil {
  1224  			// Success!
  1225  			return
  1226  		}
  1227  		logger.Warningf(
  1228  			"failed to mark address %q as %q to container %q: %v (releasing and retrying)",
  1229  			addr.String(), state.AddressStateAllocated, container, err,
  1230  		)
  1231  		// It's as good as unavailable for us, so mark it as
  1232  		// such.
  1233  		err = setAddrState(addr, state.AddressStateUnavailable)
  1234  		if err != nil {
  1235  			logger.Warningf(
  1236  				"cannot set address %q to %q: %v (ignoring and releasing)",
  1237  				addr.String(), state.AddressStateUnavailable, err,
  1238  			)
  1239  		}
  1240  		err = environ.ReleaseAddress(instId, subnetId, addr.Address())
  1241  		if err == nil {
  1242  			logger.Infof("address %q released; trying to allocate new", addr.String())
  1243  			return
  1244  		}
  1245  		logger.Warningf(
  1246  			"failed to release address %q on instance %q and subnet %q: %v (ignoring and retrying)",
  1247  			addr.String(), instId, subnetId, err,
  1248  		)
  1249  	}()
  1250  
  1251  	// Any errors returned below will trigger the release/cleanup
  1252  	// steps above.
  1253  	if err = allocateAddrTo(addr, container); err != nil {
  1254  		return errors.Trace(err)
  1255  	}
  1256  	if err = setAddrsTo(addr, container); err != nil {
  1257  		return errors.Trace(err)
  1258  	}
  1259  
  1260  	logger.Infof("assigned address %q to container %q", addr.String(), container)
  1261  	return nil
  1262  }
  1263  
  1264  func (p *ProvisionerAPI) createOrFetchStateSubnet(subnetInfo network.SubnetInfo) (*state.Subnet, error) {
  1265  	stateSubnetInfo := state.SubnetInfo{
  1266  		ProviderId:        string(subnetInfo.ProviderId),
  1267  		CIDR:              subnetInfo.CIDR,
  1268  		VLANTag:           subnetInfo.VLANTag,
  1269  		AllocatableIPHigh: subnetInfo.AllocatableIPHigh.String(),
  1270  		AllocatableIPLow:  subnetInfo.AllocatableIPLow.String(),
  1271  	}
  1272  	subnet, err := p.st.AddSubnet(stateSubnetInfo)
  1273  	if err != nil {
  1274  		if errors.IsAlreadyExists(err) {
  1275  			subnet, err = p.st.Subnet(subnetInfo.CIDR)
  1276  		}
  1277  		if err != nil {
  1278  			return subnet, errors.Trace(err)
  1279  		}
  1280  	}
  1281  	return subnet, nil
  1282  }
  1283  
  1284  // machineTags returns machine-specific tags to set on the instance.
  1285  func (p *ProvisionerAPI) machineTags(m *state.Machine, jobs []multiwatcher.MachineJob) (map[string]string, error) {
  1286  	// Names of all units deployed to the machine.
  1287  	//
  1288  	// TODO(axw) 2015-06-02 #1461358
  1289  	// We need a worker that periodically updates
  1290  	// instance tags with current deployment info.
  1291  	units, err := m.Units()
  1292  	if err != nil {
  1293  		return nil, errors.Trace(err)
  1294  	}
  1295  	unitNames := make([]string, 0, len(units))
  1296  	for _, unit := range units {
  1297  		if !unit.IsPrincipal() {
  1298  			continue
  1299  		}
  1300  		unitNames = append(unitNames, unit.Name())
  1301  	}
  1302  	sort.Strings(unitNames)
  1303  
  1304  	cfg, err := p.st.EnvironConfig()
  1305  	if err != nil {
  1306  		return nil, errors.Trace(err)
  1307  	}
  1308  	machineTags := instancecfg.InstanceTags(cfg, jobs)
  1309  	if len(unitNames) > 0 {
  1310  		machineTags[tags.JujuUnitsDeployed] = strings.Join(unitNames, " ")
  1311  	}
  1312  	return machineTags, nil
  1313  }