github.com/altoros/juju-vmware@v0.0.0-20150312064031-f19ae857ccca/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  
     9  	"github.com/juju/errors"
    10  	"github.com/juju/names"
    11  	"github.com/juju/utils/set"
    12  
    13  	"github.com/juju/juju/apiserver/common"
    14  	"github.com/juju/juju/apiserver/params"
    15  	"github.com/juju/juju/constraints"
    16  	"github.com/juju/juju/container"
    17  	"github.com/juju/juju/instance"
    18  	"github.com/juju/juju/state"
    19  	"github.com/juju/juju/state/multiwatcher"
    20  	"github.com/juju/juju/state/watcher"
    21  	"github.com/juju/juju/storage"
    22  )
    23  
    24  func init() {
    25  	common.RegisterStandardFacade("Provisioner", 0, NewProvisionerAPI)
    26  }
    27  
    28  // ProvisionerAPI provides access to the Provisioner API facade.
    29  type ProvisionerAPI struct {
    30  	*common.Remover
    31  	*common.StatusSetter
    32  	*common.DeadEnsurer
    33  	*common.PasswordChanger
    34  	*common.LifeGetter
    35  	*common.StateAddresser
    36  	*common.APIAddresser
    37  	*common.EnvironWatcher
    38  	*common.EnvironMachinesWatcher
    39  	*common.InstanceIdGetter
    40  	*common.ToolsFinder
    41  
    42  	st          *state.State
    43  	resources   *common.Resources
    44  	authorizer  common.Authorizer
    45  	getAuthFunc common.GetAuthFunc
    46  }
    47  
    48  // NewProvisionerAPI creates a new server-side ProvisionerAPI facade.
    49  func NewProvisionerAPI(st *state.State, resources *common.Resources, authorizer common.Authorizer) (*ProvisionerAPI, error) {
    50  	if !authorizer.AuthMachineAgent() && !authorizer.AuthEnvironManager() {
    51  		return nil, common.ErrPerm
    52  	}
    53  	getAuthFunc := func() (common.AuthFunc, error) {
    54  		isEnvironManager := authorizer.AuthEnvironManager()
    55  		isMachineAgent := authorizer.AuthMachineAgent()
    56  		authEntityTag := authorizer.GetAuthTag()
    57  
    58  		return func(tag names.Tag) bool {
    59  			if isMachineAgent && tag == authEntityTag {
    60  				// A machine agent can always access its own machine.
    61  				return true
    62  			}
    63  			switch tag := tag.(type) {
    64  			case names.MachineTag:
    65  				parentId := state.ParentId(tag.Id())
    66  				if parentId == "" {
    67  					// All top-level machines are accessible by the
    68  					// environment manager.
    69  					return isEnvironManager
    70  				}
    71  				// All containers with the authenticated machine as a
    72  				// parent are accessible by it.
    73  				// TODO(dfc) sometimes authEntity tag is nil, which is fine because nil is
    74  				// only equal to nil, but it suggests someone is passing an authorizer
    75  				// with a nil tag.
    76  				return isMachineAgent && names.NewMachineTag(parentId) == authEntityTag
    77  			default:
    78  				return false
    79  			}
    80  		}, nil
    81  	}
    82  	env, err := st.Environment()
    83  	if err != nil {
    84  		return nil, err
    85  	}
    86  	urlGetter := common.NewToolsURLGetter(env.UUID(), st)
    87  	return &ProvisionerAPI{
    88  		Remover:                common.NewRemover(st, false, getAuthFunc),
    89  		StatusSetter:           common.NewStatusSetter(st, getAuthFunc),
    90  		DeadEnsurer:            common.NewDeadEnsurer(st, getAuthFunc),
    91  		PasswordChanger:        common.NewPasswordChanger(st, getAuthFunc),
    92  		LifeGetter:             common.NewLifeGetter(st, getAuthFunc),
    93  		StateAddresser:         common.NewStateAddresser(st),
    94  		APIAddresser:           common.NewAPIAddresser(st, resources),
    95  		EnvironWatcher:         common.NewEnvironWatcher(st, resources, authorizer),
    96  		EnvironMachinesWatcher: common.NewEnvironMachinesWatcher(st, resources, authorizer),
    97  		InstanceIdGetter:       common.NewInstanceIdGetter(st, getAuthFunc),
    98  		ToolsFinder:            common.NewToolsFinder(st, st, urlGetter),
    99  		st:                     st,
   100  		resources:              resources,
   101  		authorizer:             authorizer,
   102  		getAuthFunc:            getAuthFunc,
   103  	}, nil
   104  }
   105  
   106  func (p *ProvisionerAPI) getMachine(canAccess common.AuthFunc, tag names.MachineTag) (*state.Machine, error) {
   107  	if !canAccess(tag) {
   108  		return nil, common.ErrPerm
   109  	}
   110  	entity, err := p.st.FindEntity(tag)
   111  	if err != nil {
   112  		return nil, err
   113  	}
   114  	// The authorization function guarantees that the tag represents a
   115  	// machine.
   116  	return entity.(*state.Machine), nil
   117  }
   118  
   119  func (p *ProvisionerAPI) watchOneMachineContainers(arg params.WatchContainer) (params.StringsWatchResult, error) {
   120  	nothing := params.StringsWatchResult{}
   121  	canAccess, err := p.getAuthFunc()
   122  	if err != nil {
   123  		return nothing, common.ErrPerm
   124  	}
   125  	tag, err := names.ParseMachineTag(arg.MachineTag)
   126  	if err != nil {
   127  		return nothing, common.ErrPerm
   128  	}
   129  	if !canAccess(tag) {
   130  		return nothing, common.ErrPerm
   131  	}
   132  	machine, err := p.st.Machine(tag.Id())
   133  	if err != nil {
   134  		return nothing, err
   135  	}
   136  	var watch state.StringsWatcher
   137  	if arg.ContainerType != "" {
   138  		watch = machine.WatchContainers(instance.ContainerType(arg.ContainerType))
   139  	} else {
   140  		watch = machine.WatchAllContainers()
   141  	}
   142  	// Consume the initial event and forward it to the result.
   143  	if changes, ok := <-watch.Changes(); ok {
   144  		return params.StringsWatchResult{
   145  			StringsWatcherId: p.resources.Register(watch),
   146  			Changes:          changes,
   147  		}, nil
   148  	}
   149  	return nothing, watcher.EnsureErr(watch)
   150  }
   151  
   152  // WatchContainers starts a StringsWatcher to watch containers deployed to
   153  // any machine passed in args.
   154  func (p *ProvisionerAPI) WatchContainers(args params.WatchContainers) (params.StringsWatchResults, error) {
   155  	result := params.StringsWatchResults{
   156  		Results: make([]params.StringsWatchResult, len(args.Params)),
   157  	}
   158  	for i, arg := range args.Params {
   159  		watcherResult, err := p.watchOneMachineContainers(arg)
   160  		result.Results[i] = watcherResult
   161  		result.Results[i].Error = common.ServerError(err)
   162  	}
   163  	return result, nil
   164  }
   165  
   166  // WatchAllContainers starts a StringsWatcher to watch all containers deployed to
   167  // any machine passed in args.
   168  func (p *ProvisionerAPI) WatchAllContainers(args params.WatchContainers) (params.StringsWatchResults, error) {
   169  	return p.WatchContainers(args)
   170  }
   171  
   172  // SetSupportedContainers updates the list of containers supported by the machines passed in args.
   173  func (p *ProvisionerAPI) SetSupportedContainers(args params.MachineContainersParams) (params.ErrorResults, error) {
   174  	result := params.ErrorResults{
   175  		Results: make([]params.ErrorResult, len(args.Params)),
   176  	}
   177  
   178  	canAccess, err := p.getAuthFunc()
   179  	if err != nil {
   180  		return result, err
   181  	}
   182  	for i, arg := range args.Params {
   183  		tag, err := names.ParseMachineTag(arg.MachineTag)
   184  		if err != nil {
   185  			result.Results[i].Error = common.ServerError(common.ErrPerm)
   186  			continue
   187  		}
   188  		machine, err := p.getMachine(canAccess, tag)
   189  		if err != nil {
   190  			result.Results[i].Error = common.ServerError(err)
   191  			continue
   192  		}
   193  		if len(arg.ContainerTypes) == 0 {
   194  			err = machine.SupportsNoContainers()
   195  		} else {
   196  			err = machine.SetSupportedContainers(arg.ContainerTypes)
   197  		}
   198  		if err != nil {
   199  			result.Results[i].Error = common.ServerError(err)
   200  		}
   201  	}
   202  	return result, nil
   203  }
   204  
   205  // ContainerManagerConfig returns information from the environment config that is
   206  // needed for configuring the container manager.
   207  func (p *ProvisionerAPI) ContainerManagerConfig(args params.ContainerManagerConfigParams) (params.ContainerManagerConfig, error) {
   208  	var result params.ContainerManagerConfig
   209  	config, err := p.st.EnvironConfig()
   210  	if err != nil {
   211  		return result, err
   212  	}
   213  	cfg := make(map[string]string)
   214  	cfg[container.ConfigName] = container.DefaultNamespace
   215  	switch args.Type {
   216  	case instance.LXC:
   217  		if useLxcClone, ok := config.LXCUseClone(); ok {
   218  			cfg["use-clone"] = fmt.Sprint(useLxcClone)
   219  		}
   220  		if useLxcCloneAufs, ok := config.LXCUseCloneAUFS(); ok {
   221  			cfg["use-aufs"] = fmt.Sprint(useLxcCloneAufs)
   222  		}
   223  	}
   224  	result.ManagerConfig = cfg
   225  	return result, nil
   226  }
   227  
   228  // ContainerConfig returns information from the environment config that is
   229  // needed for container cloud-init.
   230  func (p *ProvisionerAPI) ContainerConfig() (params.ContainerConfig, error) {
   231  	result := params.ContainerConfig{}
   232  	config, err := p.st.EnvironConfig()
   233  	if err != nil {
   234  		return result, err
   235  	}
   236  
   237  	result.UpdateBehavior = &params.UpdateBehavior{
   238  		config.EnableOSRefreshUpdate(),
   239  		config.EnableOSUpgrade(),
   240  	}
   241  	result.ProviderType = config.Type()
   242  	result.AuthorizedKeys = config.AuthorizedKeys()
   243  	result.SSLHostnameVerification = config.SSLHostnameVerification()
   244  	result.Proxy = config.ProxySettings()
   245  	result.AptProxy = config.AptProxySettings()
   246  	result.PreferIPv6 = config.PreferIPv6()
   247  
   248  	return result, nil
   249  }
   250  
   251  // Status returns the status of each given machine entity.
   252  func (p *ProvisionerAPI) Status(args params.Entities) (params.StatusResults, error) {
   253  	result := params.StatusResults{
   254  		Results: make([]params.StatusResult, len(args.Entities)),
   255  	}
   256  	canAccess, err := p.getAuthFunc()
   257  	if err != nil {
   258  		return result, err
   259  	}
   260  	for i, entity := range args.Entities {
   261  		tag, err := names.ParseMachineTag(entity.Tag)
   262  		if err != nil {
   263  			result.Results[i].Error = common.ServerError(common.ErrPerm)
   264  			continue
   265  		}
   266  		machine, err := p.getMachine(canAccess, tag)
   267  		if err == nil {
   268  			r := &result.Results[i]
   269  			var st state.Status
   270  			st, r.Info, r.Data, err = machine.Status()
   271  			r.Status = params.Status(st)
   272  
   273  		}
   274  		result.Results[i].Error = common.ServerError(err)
   275  	}
   276  	return result, nil
   277  }
   278  
   279  // MachinesWithTransientErrors returns status data for machines with provisioning
   280  // errors which are transient.
   281  func (p *ProvisionerAPI) MachinesWithTransientErrors() (params.StatusResults, error) {
   282  	var results params.StatusResults
   283  	canAccessFunc, err := p.getAuthFunc()
   284  	if err != nil {
   285  		return results, err
   286  	}
   287  	// TODO (wallyworld) - add state.State API for more efficient machines query
   288  	machines, err := p.st.AllMachines()
   289  	if err != nil {
   290  		return results, err
   291  	}
   292  	for _, machine := range machines {
   293  		if !canAccessFunc(machine.Tag()) {
   294  			continue
   295  		}
   296  		if _, provisionedErr := machine.InstanceId(); provisionedErr == nil {
   297  			// Machine may have been provisioned but machiner hasn't set the
   298  			// status to Started yet.
   299  			continue
   300  		}
   301  		var result params.StatusResult
   302  		var st state.Status
   303  		st, result.Info, result.Data, err = machine.Status()
   304  		if err != nil {
   305  			continue
   306  		}
   307  		result.Status = params.Status(st)
   308  		if result.Status != params.StatusError {
   309  			continue
   310  		}
   311  		// Transient errors are marked as such in the status data.
   312  		if transient, ok := result.Data["transient"].(bool); !ok || !transient {
   313  			continue
   314  		}
   315  		result.Id = machine.Id()
   316  		result.Life = params.Life(machine.Life().String())
   317  		results.Results = append(results.Results, result)
   318  	}
   319  	return results, nil
   320  }
   321  
   322  // Series returns the deployed series for each given machine entity.
   323  func (p *ProvisionerAPI) Series(args params.Entities) (params.StringResults, error) {
   324  	result := params.StringResults{
   325  		Results: make([]params.StringResult, len(args.Entities)),
   326  	}
   327  	canAccess, err := p.getAuthFunc()
   328  	if err != nil {
   329  		return result, err
   330  	}
   331  	for i, entity := range args.Entities {
   332  		tag, err := names.ParseMachineTag(entity.Tag)
   333  		if err != nil {
   334  			result.Results[i].Error = common.ServerError(common.ErrPerm)
   335  			continue
   336  		}
   337  		machine, err := p.getMachine(canAccess, tag)
   338  		if err == nil {
   339  			result.Results[i].Result = machine.Series()
   340  		}
   341  		result.Results[i].Error = common.ServerError(err)
   342  	}
   343  	return result, nil
   344  }
   345  
   346  // ProvisioningInfo returns the provisioning information for each given machine entity.
   347  func (p *ProvisionerAPI) ProvisioningInfo(args params.Entities) (params.ProvisioningInfoResults, error) {
   348  	result := params.ProvisioningInfoResults{
   349  		Results: make([]params.ProvisioningInfoResult, 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, err = getProvisioningInfo(machine)
   364  		}
   365  		result.Results[i].Error = common.ServerError(err)
   366  	}
   367  	return result, nil
   368  }
   369  
   370  func getProvisioningInfo(m *state.Machine) (*params.ProvisioningInfo, error) {
   371  	cons, err := m.Constraints()
   372  	if err != nil {
   373  		return nil, err
   374  	}
   375  	volumes, err := machineVolumeParams(m)
   376  	if err != nil {
   377  		return nil, errors.Trace(err)
   378  	}
   379  	// TODO(dimitern) For now, since network names and
   380  	// provider ids are the same, we return what we got
   381  	// from state. In the future, when networks can be
   382  	// added before provisioning, we should convert both
   383  	// slices from juju network names to provider-specific
   384  	// ids before returning them.
   385  	networks, err := m.RequestedNetworks()
   386  	if err != nil {
   387  		return nil, err
   388  	}
   389  	var jobs []multiwatcher.MachineJob
   390  	for _, job := range m.Jobs() {
   391  		jobs = append(jobs, job.ToParams())
   392  	}
   393  	return &params.ProvisioningInfo{
   394  		Constraints: cons,
   395  		Series:      m.Series(),
   396  		Placement:   m.Placement(),
   397  		Networks:    networks,
   398  		Jobs:        jobs,
   399  		Volumes:     volumes,
   400  	}, nil
   401  }
   402  
   403  // DistributionGroup returns, for each given machine entity,
   404  // a slice of instance.Ids that belong to the same distribution
   405  // group as that machine. This information may be used to
   406  // distribute instances for high availability.
   407  func (p *ProvisionerAPI) DistributionGroup(args params.Entities) (params.DistributionGroupResults, error) {
   408  	result := params.DistributionGroupResults{
   409  		Results: make([]params.DistributionGroupResult, len(args.Entities)),
   410  	}
   411  	canAccess, err := p.getAuthFunc()
   412  	if err != nil {
   413  		return result, err
   414  	}
   415  	for i, entity := range args.Entities {
   416  		tag, err := names.ParseMachineTag(entity.Tag)
   417  		if err != nil {
   418  			result.Results[i].Error = common.ServerError(common.ErrPerm)
   419  			continue
   420  		}
   421  		machine, err := p.getMachine(canAccess, tag)
   422  		if err == nil {
   423  			// If the machine is an environment manager, return
   424  			// environment manager instances. Otherwise, return
   425  			// instances with services in common with the machine
   426  			// being provisioned.
   427  			if machine.IsManager() {
   428  				result.Results[i].Result, err = environManagerInstances(p.st)
   429  			} else {
   430  				result.Results[i].Result, err = commonServiceInstances(p.st, machine)
   431  			}
   432  		}
   433  		result.Results[i].Error = common.ServerError(err)
   434  	}
   435  	return result, nil
   436  }
   437  
   438  // environManagerInstances returns all environ manager instances.
   439  func environManagerInstances(st *state.State) ([]instance.Id, error) {
   440  	info, err := st.StateServerInfo()
   441  	if err != nil {
   442  		return nil, err
   443  	}
   444  	instances := make([]instance.Id, 0, len(info.MachineIds))
   445  	for _, id := range info.MachineIds {
   446  		machine, err := st.Machine(id)
   447  		if err != nil {
   448  			return nil, err
   449  		}
   450  		instanceId, err := machine.InstanceId()
   451  		if err == nil {
   452  			instances = append(instances, instanceId)
   453  		} else if !errors.IsNotProvisioned(err) {
   454  			return nil, err
   455  		}
   456  	}
   457  	return instances, nil
   458  }
   459  
   460  // commonServiceInstances returns instances with
   461  // services in common with the specified machine.
   462  func commonServiceInstances(st *state.State, m *state.Machine) ([]instance.Id, error) {
   463  	units, err := m.Units()
   464  	if err != nil {
   465  		return nil, err
   466  	}
   467  	instanceIdSet := make(set.Strings)
   468  	for _, unit := range units {
   469  		if !unit.IsPrincipal() {
   470  			continue
   471  		}
   472  		instanceIds, err := state.ServiceInstances(st, unit.ServiceName())
   473  		if err != nil {
   474  			return nil, err
   475  		}
   476  		for _, instanceId := range instanceIds {
   477  			instanceIdSet.Add(string(instanceId))
   478  		}
   479  	}
   480  	instanceIds := make([]instance.Id, instanceIdSet.Size())
   481  	// Sort values to simplify testing.
   482  	for i, instanceId := range instanceIdSet.SortedValues() {
   483  		instanceIds[i] = instance.Id(instanceId)
   484  	}
   485  	return instanceIds, nil
   486  }
   487  
   488  // Constraints returns the constraints for each given machine entity.
   489  func (p *ProvisionerAPI) Constraints(args params.Entities) (params.ConstraintsResults, error) {
   490  	result := params.ConstraintsResults{
   491  		Results: make([]params.ConstraintsResult, len(args.Entities)),
   492  	}
   493  	canAccess, err := p.getAuthFunc()
   494  	if err != nil {
   495  		return result, err
   496  	}
   497  	for i, entity := range args.Entities {
   498  		tag, err := names.ParseMachineTag(entity.Tag)
   499  		if err != nil {
   500  			result.Results[i].Error = common.ServerError(common.ErrPerm)
   501  			continue
   502  		}
   503  		machine, err := p.getMachine(canAccess, tag)
   504  		if err == nil {
   505  			var cons constraints.Value
   506  			cons, err = machine.Constraints()
   507  			if err == nil {
   508  				result.Results[i].Constraints = cons
   509  			}
   510  		}
   511  		result.Results[i].Error = common.ServerError(err)
   512  	}
   513  	return result, nil
   514  }
   515  
   516  // machineVolumeParams retrieves VolumeParams for the volumes that should be
   517  // provisioned with and attached to the machine. The client should ignore
   518  // parameters that it does not know how to handle.
   519  func machineVolumeParams(m *state.Machine) ([]storage.VolumeParams, error) {
   520  	blockDevices, err := m.BlockDevices()
   521  	if err != nil {
   522  		return nil, err
   523  	}
   524  	if len(blockDevices) == 0 {
   525  		return nil, nil
   526  	}
   527  	allParams := make([]storage.VolumeParams, len(blockDevices))
   528  	for i, dev := range blockDevices {
   529  		params, ok := dev.Params()
   530  		if !ok {
   531  			return nil, errors.Errorf("cannot get parameters for volume %q", dev.Name())
   532  		}
   533  		allParams[i] = storage.VolumeParams{
   534  			dev.Name(),
   535  			params.Size,
   536  			// TODO(axw) when pools are implemented,
   537  			// set Options here.
   538  			nil,
   539  			"", // no instance ID yet
   540  		}
   541  	}
   542  	return allParams, nil
   543  }
   544  
   545  // blockDevicesToState converts a slice of storage.BlockDevice to a mapping
   546  // of block device names to state.BlockDeviceInfo.
   547  func blockDevicesToState(in []storage.BlockDevice) (map[string]state.BlockDeviceInfo, error) {
   548  	m := make(map[string]state.BlockDeviceInfo)
   549  	for _, dev := range in {
   550  		if dev.Name == "" {
   551  			return nil, errors.New("Name is empty")
   552  		}
   553  		m[dev.Name] = state.BlockDeviceInfo{
   554  			dev.DeviceName,
   555  			dev.Label,
   556  			dev.UUID,
   557  			dev.Serial,
   558  			dev.Size,
   559  			dev.FilesystemType,
   560  			dev.InUse,
   561  		}
   562  	}
   563  	return m, nil
   564  }
   565  
   566  func networkParamsToStateParams(networks []params.Network, ifaces []params.NetworkInterface) (
   567  	[]state.NetworkInfo, []state.NetworkInterfaceInfo, error,
   568  ) {
   569  	stateNetworks := make([]state.NetworkInfo, len(networks))
   570  	for i, network := range networks {
   571  		tag, err := names.ParseNetworkTag(network.Tag)
   572  		if err != nil {
   573  			return nil, nil, err
   574  		}
   575  		stateNetworks[i] = state.NetworkInfo{
   576  			Name:       tag.Id(),
   577  			ProviderId: network.ProviderId,
   578  			CIDR:       network.CIDR,
   579  			VLANTag:    network.VLANTag,
   580  		}
   581  	}
   582  	stateInterfaces := make([]state.NetworkInterfaceInfo, len(ifaces))
   583  	for i, iface := range ifaces {
   584  		tag, err := names.ParseNetworkTag(iface.NetworkTag)
   585  		if err != nil {
   586  			return nil, nil, err
   587  		}
   588  		stateInterfaces[i] = state.NetworkInterfaceInfo{
   589  			MACAddress:    iface.MACAddress,
   590  			NetworkName:   tag.Id(),
   591  			InterfaceName: iface.InterfaceName,
   592  			IsVirtual:     iface.IsVirtual,
   593  			Disabled:      iface.Disabled,
   594  		}
   595  	}
   596  	return stateNetworks, stateInterfaces, nil
   597  }
   598  
   599  // RequestedNetworks returns the requested networks for each given
   600  // machine entity. Each entry in both lists is returned with its
   601  // provider specific id.
   602  func (p *ProvisionerAPI) RequestedNetworks(args params.Entities) (params.RequestedNetworksResults, error) {
   603  	result := params.RequestedNetworksResults{
   604  		Results: make([]params.RequestedNetworkResult, len(args.Entities)),
   605  	}
   606  	canAccess, err := p.getAuthFunc()
   607  	if err != nil {
   608  		return result, err
   609  	}
   610  	for i, entity := range args.Entities {
   611  		tag, err := names.ParseMachineTag(entity.Tag)
   612  		if err != nil {
   613  			result.Results[i].Error = common.ServerError(common.ErrPerm)
   614  			continue
   615  		}
   616  		machine, err := p.getMachine(canAccess, tag)
   617  		if err == nil {
   618  			var networks []string
   619  			networks, err = machine.RequestedNetworks()
   620  			if err == nil {
   621  				// TODO(dimitern) For now, since network names and
   622  				// provider ids are the same, we return what we got
   623  				// from state. In the future, when networks can be
   624  				// added before provisioning, we should convert both
   625  				// slices from juju network names to provider-specific
   626  				// ids before returning them.
   627  				result.Results[i].Networks = networks
   628  			}
   629  		}
   630  		result.Results[i].Error = common.ServerError(err)
   631  	}
   632  	return result, nil
   633  }
   634  
   635  // SetProvisioned sets the provider specific instance id, nonce and
   636  // metadata for each given machine. Once set, the instance id cannot
   637  // be changed.
   638  //
   639  // TODO(dimitern) This is not used anymore (as of 1.19.0) and is
   640  // retained only for backwards-compatibility. It should be removed as
   641  // deprecated. SetInstanceInfo is used instead.
   642  func (p *ProvisionerAPI) SetProvisioned(args params.SetProvisioned) (params.ErrorResults, error) {
   643  	result := params.ErrorResults{
   644  		Results: make([]params.ErrorResult, len(args.Machines)),
   645  	}
   646  	canAccess, err := p.getAuthFunc()
   647  	if err != nil {
   648  		return result, err
   649  	}
   650  	for i, arg := range args.Machines {
   651  		tag, err := names.ParseMachineTag(arg.Tag)
   652  		if err != nil {
   653  			result.Results[i].Error = common.ServerError(common.ErrPerm)
   654  			continue
   655  		}
   656  		machine, err := p.getMachine(canAccess, tag)
   657  		if err == nil {
   658  			err = machine.SetProvisioned(arg.InstanceId, arg.Nonce, arg.Characteristics)
   659  		}
   660  		result.Results[i].Error = common.ServerError(err)
   661  	}
   662  	return result, nil
   663  }
   664  
   665  // SetInstanceInfo sets the provider specific machine id, nonce,
   666  // metadata and network info for each given machine. Once set, the
   667  // instance id cannot be changed.
   668  func (p *ProvisionerAPI) SetInstanceInfo(args params.InstancesInfo) (params.ErrorResults, error) {
   669  	result := params.ErrorResults{
   670  		Results: make([]params.ErrorResult, len(args.Machines)),
   671  	}
   672  	canAccess, err := p.getAuthFunc()
   673  	if err != nil {
   674  		return result, err
   675  	}
   676  	setInstanceInfo := func(arg params.InstanceInfo) error {
   677  		tag, err := names.ParseMachineTag(arg.Tag)
   678  		if err != nil {
   679  			return common.ErrPerm
   680  		}
   681  		machine, err := p.getMachine(canAccess, tag)
   682  		if err != nil {
   683  			return err
   684  		}
   685  		networks, interfaces, err := networkParamsToStateParams(arg.Networks, arg.Interfaces)
   686  		if err != nil {
   687  			return err
   688  		}
   689  		blockDevices, err := blockDevicesToState(arg.Volumes)
   690  		if err != nil {
   691  			return err
   692  		}
   693  		if err = machine.SetInstanceInfo(
   694  			arg.InstanceId, arg.Nonce, arg.Characteristics,
   695  			networks, interfaces, blockDevices); err != nil {
   696  			return errors.Annotatef(
   697  				err,
   698  				"cannot record provisioning info for %q",
   699  				arg.InstanceId,
   700  			)
   701  		}
   702  		return nil
   703  	}
   704  	for i, arg := range args.Machines {
   705  		err := setInstanceInfo(arg)
   706  		result.Results[i].Error = common.ServerError(err)
   707  	}
   708  	return result, nil
   709  }
   710  
   711  // WatchMachineErrorRetry returns a NotifyWatcher that notifies when
   712  // the provisioner should retry provisioning machines with transient errors.
   713  func (p *ProvisionerAPI) WatchMachineErrorRetry() (params.NotifyWatchResult, error) {
   714  	result := params.NotifyWatchResult{}
   715  	if !p.authorizer.AuthEnvironManager() {
   716  		return result, common.ErrPerm
   717  	}
   718  	watch := newWatchMachineErrorRetry()
   719  	// Consume any initial event and forward it to the result.
   720  	if _, ok := <-watch.Changes(); ok {
   721  		result.NotifyWatcherId = p.resources.Register(watch)
   722  	} else {
   723  		return result, watcher.EnsureErr(watch)
   724  	}
   725  	return result, nil
   726  }