github.com/Pankov404/juju@v0.0.0-20150703034450-be266991dceb/worker/provisioner/provisioner_task.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  	"regexp"
     9  	"time"
    10  
    11  	"github.com/juju/errors"
    12  	"github.com/juju/names"
    13  	"github.com/juju/utils"
    14  	"github.com/juju/utils/set"
    15  	"launchpad.net/tomb"
    16  
    17  	apiprovisioner "github.com/juju/juju/api/provisioner"
    18  	apiwatcher "github.com/juju/juju/api/watcher"
    19  	"github.com/juju/juju/apiserver/params"
    20  	"github.com/juju/juju/cloudconfig/instancecfg"
    21  	"github.com/juju/juju/constraints"
    22  	"github.com/juju/juju/environmentserver/authentication"
    23  	"github.com/juju/juju/environs"
    24  	"github.com/juju/juju/environs/config"
    25  	"github.com/juju/juju/instance"
    26  	"github.com/juju/juju/network"
    27  	"github.com/juju/juju/state/watcher"
    28  	"github.com/juju/juju/storage"
    29  	coretools "github.com/juju/juju/tools"
    30  	"github.com/juju/juju/version"
    31  	"github.com/juju/juju/worker"
    32  )
    33  
    34  type ProvisionerTask interface {
    35  	worker.Worker
    36  	Stop() error
    37  	Dying() <-chan struct{}
    38  	Err() error
    39  
    40  	// SetHarvestMode sets a flag to indicate how the provisioner task
    41  	// should harvest machines. See config.HarvestMode for
    42  	// documentation of behavior.
    43  	SetHarvestMode(mode config.HarvestMode)
    44  }
    45  
    46  type MachineGetter interface {
    47  	Machine(names.MachineTag) (*apiprovisioner.Machine, error)
    48  	MachinesWithTransientErrors() ([]*apiprovisioner.Machine, []params.StatusResult, error)
    49  }
    50  
    51  // ToolsFinder is an interface used for finding tools to run on
    52  // provisioned instances.
    53  type ToolsFinder interface {
    54  	// FindTools returns a list of tools matching the specified
    55  	// version and series, and optionally arch.
    56  	FindTools(version version.Number, series string, arch *string) (coretools.List, error)
    57  }
    58  
    59  var _ MachineGetter = (*apiprovisioner.State)(nil)
    60  var _ ToolsFinder = (*apiprovisioner.State)(nil)
    61  
    62  func NewProvisionerTask(
    63  	machineTag names.MachineTag,
    64  	harvestMode config.HarvestMode,
    65  	machineGetter MachineGetter,
    66  	toolsFinder ToolsFinder,
    67  	machineWatcher apiwatcher.StringsWatcher,
    68  	retryWatcher apiwatcher.NotifyWatcher,
    69  	broker environs.InstanceBroker,
    70  	auth authentication.AuthenticationProvider,
    71  	imageStream string,
    72  	secureServerConnection bool,
    73  ) ProvisionerTask {
    74  	task := &provisionerTask{
    75  		machineTag:             machineTag,
    76  		machineGetter:          machineGetter,
    77  		toolsFinder:            toolsFinder,
    78  		machineWatcher:         machineWatcher,
    79  		retryWatcher:           retryWatcher,
    80  		broker:                 broker,
    81  		auth:                   auth,
    82  		harvestMode:            harvestMode,
    83  		harvestModeChan:        make(chan config.HarvestMode, 1),
    84  		machines:               make(map[string]*apiprovisioner.Machine),
    85  		imageStream:            imageStream,
    86  		secureServerConnection: secureServerConnection,
    87  	}
    88  	go func() {
    89  		defer task.tomb.Done()
    90  		task.tomb.Kill(task.loop())
    91  	}()
    92  	return task
    93  }
    94  
    95  type provisionerTask struct {
    96  	machineTag             names.MachineTag
    97  	machineGetter          MachineGetter
    98  	toolsFinder            ToolsFinder
    99  	machineWatcher         apiwatcher.StringsWatcher
   100  	retryWatcher           apiwatcher.NotifyWatcher
   101  	broker                 environs.InstanceBroker
   102  	tomb                   tomb.Tomb
   103  	auth                   authentication.AuthenticationProvider
   104  	imageStream            string
   105  	secureServerConnection bool
   106  	harvestMode            config.HarvestMode
   107  	harvestModeChan        chan config.HarvestMode
   108  	// instance id -> instance
   109  	instances map[instance.Id]instance.Instance
   110  	// machine id -> machine
   111  	machines map[string]*apiprovisioner.Machine
   112  }
   113  
   114  // Kill implements worker.Worker.Kill.
   115  func (task *provisionerTask) Kill() {
   116  	task.tomb.Kill(nil)
   117  }
   118  
   119  // Wait implements worker.Worker.Wait.
   120  func (task *provisionerTask) Wait() error {
   121  	return task.tomb.Wait()
   122  }
   123  
   124  func (task *provisionerTask) Stop() error {
   125  	task.Kill()
   126  	return task.Wait()
   127  }
   128  
   129  func (task *provisionerTask) Dying() <-chan struct{} {
   130  	return task.tomb.Dying()
   131  }
   132  
   133  func (task *provisionerTask) Err() error {
   134  	return task.tomb.Err()
   135  }
   136  
   137  func (task *provisionerTask) loop() error {
   138  	logger.Infof("Starting up provisioner task %s", task.machineTag)
   139  	defer watcher.Stop(task.machineWatcher, &task.tomb)
   140  
   141  	// Don't allow the harvesting mode to change until we have read at
   142  	// least one set of changes, which will populate the task.machines
   143  	// map. Otherwise we will potentially see all legitimate instances
   144  	// as unknown.
   145  	var harvestModeChan chan config.HarvestMode
   146  
   147  	// Not all provisioners have a retry channel.
   148  	var retryChan <-chan struct{}
   149  	if task.retryWatcher != nil {
   150  		retryChan = task.retryWatcher.Changes()
   151  	}
   152  
   153  	// When the watcher is started, it will have the initial changes be all
   154  	// the machines that are relevant. Also, since this is available straight
   155  	// away, we know there will be some changes right off the bat.
   156  	for {
   157  		select {
   158  		case <-task.tomb.Dying():
   159  			logger.Infof("Shutting down provisioner task %s", task.machineTag)
   160  			return tomb.ErrDying
   161  		case ids, ok := <-task.machineWatcher.Changes():
   162  			if !ok {
   163  				return watcher.EnsureErr(task.machineWatcher)
   164  			}
   165  			if err := task.processMachines(ids); err != nil {
   166  				return errors.Annotate(err, "failed to process updated machines")
   167  			}
   168  			// We've seen a set of changes. Enable modification of
   169  			// harvesting mode.
   170  			harvestModeChan = task.harvestModeChan
   171  		case harvestMode := <-harvestModeChan:
   172  			if harvestMode == task.harvestMode {
   173  				break
   174  			}
   175  
   176  			logger.Infof("harvesting mode changed to %s", harvestMode)
   177  			task.harvestMode = harvestMode
   178  
   179  			if harvestMode.HarvestUnknown() {
   180  
   181  				logger.Infof("harvesting unknown machines")
   182  				if err := task.processMachines(nil); err != nil {
   183  					return errors.Annotate(err, "failed to process machines after safe mode disabled")
   184  				}
   185  			}
   186  		case <-retryChan:
   187  			if err := task.processMachinesWithTransientErrors(); err != nil {
   188  				return errors.Annotate(err, "failed to process machines with transient errors")
   189  			}
   190  		}
   191  	}
   192  }
   193  
   194  // SetHarvestMode implements ProvisionerTask.SetHarvestMode().
   195  func (task *provisionerTask) SetHarvestMode(mode config.HarvestMode) {
   196  	select {
   197  	case task.harvestModeChan <- mode:
   198  	case <-task.Dying():
   199  	}
   200  }
   201  
   202  func (task *provisionerTask) processMachinesWithTransientErrors() error {
   203  	machines, statusResults, err := task.machineGetter.MachinesWithTransientErrors()
   204  	if err != nil {
   205  		return nil
   206  	}
   207  	logger.Tracef("processMachinesWithTransientErrors(%v)", statusResults)
   208  	var pending []*apiprovisioner.Machine
   209  	for i, status := range statusResults {
   210  		if status.Error != nil {
   211  			logger.Errorf("cannot retry provisioning of machine %q: %v", status.Id, status.Error)
   212  			continue
   213  		}
   214  		machine := machines[i]
   215  		if err := machine.SetStatus(params.StatusPending, "", nil); err != nil {
   216  			logger.Errorf("cannot reset status of machine %q: %v", status.Id, err)
   217  			continue
   218  		}
   219  		task.machines[machine.Tag().String()] = machine
   220  		pending = append(pending, machine)
   221  	}
   222  	return task.startMachines(pending)
   223  }
   224  
   225  func (task *provisionerTask) processMachines(ids []string) error {
   226  	logger.Tracef("processMachines(%v)", ids)
   227  
   228  	// Populate the tasks maps of current instances and machines.
   229  	if err := task.populateMachineMaps(ids); err != nil {
   230  		return err
   231  	}
   232  
   233  	// Find machines without an instance id or that are dead
   234  	pending, dead, maintain, err := task.pendingOrDeadOrMaintain(ids)
   235  	if err != nil {
   236  		return err
   237  	}
   238  
   239  	// Stop all machines that are dead
   240  	stopping := task.instancesForMachines(dead)
   241  
   242  	// Find running instances that have no machines associated
   243  	unknown, err := task.findUnknownInstances(stopping)
   244  	if err != nil {
   245  		return err
   246  	}
   247  	if !task.harvestMode.HarvestUnknown() {
   248  		logger.Infof(
   249  			"%s is set to %s; unknown instances not stopped %v",
   250  			config.ProvisionerHarvestModeKey,
   251  			task.harvestMode.String(),
   252  			instanceIds(unknown),
   253  		)
   254  		unknown = nil
   255  	}
   256  	if task.harvestMode.HarvestNone() || !task.harvestMode.HarvestDestroyed() {
   257  		logger.Infof(
   258  			`%s is set to "%s"; will not harvest %s`,
   259  			config.ProvisionerHarvestModeKey,
   260  			task.harvestMode.String(),
   261  			instanceIds(stopping),
   262  		)
   263  		stopping = nil
   264  	}
   265  
   266  	if len(stopping) > 0 {
   267  		logger.Infof("stopping known instances %v", stopping)
   268  	}
   269  	if len(unknown) > 0 {
   270  		logger.Infof("stopping unknown instances %v", instanceIds(unknown))
   271  	}
   272  	// It's important that we stop unknown instances before starting
   273  	// pending ones, because if we start an instance and then fail to
   274  	// set its InstanceId on the machine we don't want to start a new
   275  	// instance for the same machine ID.
   276  	if err := task.stopInstances(append(stopping, unknown...)); err != nil {
   277  		return err
   278  	}
   279  
   280  	// Remove any dead machines from state.
   281  	for _, machine := range dead {
   282  		logger.Infof("removing dead machine %q", machine)
   283  		if err := machine.Remove(); err != nil {
   284  			logger.Errorf("failed to remove dead machine %q", machine)
   285  		}
   286  		delete(task.machines, machine.Id())
   287  	}
   288  
   289  	// Any machines that require maintenance get pinged
   290  	task.maintainMachines(maintain)
   291  
   292  	// Start an instance for the pending ones
   293  	return task.startMachines(pending)
   294  }
   295  
   296  func instanceIds(instances []instance.Instance) []string {
   297  	ids := make([]string, 0, len(instances))
   298  	for _, inst := range instances {
   299  		ids = append(ids, string(inst.Id()))
   300  	}
   301  	return ids
   302  }
   303  
   304  // populateMachineMaps updates task.instances. Also updates
   305  // task.machines map if a list of IDs is given.
   306  func (task *provisionerTask) populateMachineMaps(ids []string) error {
   307  	task.instances = make(map[instance.Id]instance.Instance)
   308  
   309  	instances, err := task.broker.AllInstances()
   310  	if err != nil {
   311  		return errors.Annotate(err, "failed to get all instances from broker")
   312  	}
   313  	for _, i := range instances {
   314  		task.instances[i.Id()] = i
   315  	}
   316  
   317  	// Update the machines map with new data for each of the machines in the
   318  	// change list.
   319  	// TODO(thumper): update for API server later to get all machines in one go.
   320  	for _, id := range ids {
   321  		machineTag := names.NewMachineTag(id)
   322  		machine, err := task.machineGetter.Machine(machineTag)
   323  		switch {
   324  		case params.IsCodeNotFoundOrCodeUnauthorized(err):
   325  			logger.Debugf("machine %q not found in state", id)
   326  			delete(task.machines, id)
   327  		case err == nil:
   328  			task.machines[id] = machine
   329  		default:
   330  			return errors.Annotatef(err, "failed to get machine %v", id)
   331  		}
   332  	}
   333  	return nil
   334  }
   335  
   336  // pendingOrDead looks up machines with ids and returns those that do not
   337  // have an instance id assigned yet, and also those that are dead.
   338  func (task *provisionerTask) pendingOrDeadOrMaintain(ids []string) (pending, dead, maintain []*apiprovisioner.Machine, err error) {
   339  	for _, id := range ids {
   340  		machine, found := task.machines[id]
   341  		if !found {
   342  			logger.Infof("machine %q not found", id)
   343  			continue
   344  		}
   345  		switch machine.Life() {
   346  		case params.Dying:
   347  			if _, err := machine.InstanceId(); err == nil {
   348  				continue
   349  			} else if !params.IsCodeNotProvisioned(err) {
   350  				return nil, nil, nil, errors.Annotatef(err, "failed to load machine %q instance id: %v", machine)
   351  			}
   352  			logger.Infof("killing dying, unprovisioned machine %q", machine)
   353  			if err := machine.EnsureDead(); err != nil {
   354  				return nil, nil, nil, errors.Annotatef(err, "failed to ensure machine dead %q: %v", machine)
   355  			}
   356  			fallthrough
   357  		case params.Dead:
   358  			dead = append(dead, machine)
   359  			continue
   360  		}
   361  		if instId, err := machine.InstanceId(); err != nil {
   362  			if !params.IsCodeNotProvisioned(err) {
   363  				logger.Errorf("failed to load machine %q instance id: %v", machine, err)
   364  				continue
   365  			}
   366  			status, _, err := machine.Status()
   367  			if err != nil {
   368  				logger.Infof("cannot get machine %q status: %v", machine, err)
   369  				continue
   370  			}
   371  			if status == params.StatusPending {
   372  				pending = append(pending, machine)
   373  				logger.Infof("found machine %q pending provisioning", machine)
   374  				continue
   375  			}
   376  		} else {
   377  			logger.Infof("machine %v already started as instance %q", machine, instId)
   378  			if err != nil {
   379  				logger.Infof("Error fetching provisioning info")
   380  			} else {
   381  				isLxc := regexp.MustCompile(`\d+/lxc/\d+`)
   382  				if isLxc.MatchString(machine.Id()) {
   383  					maintain = append(maintain, machine)
   384  				}
   385  			}
   386  		}
   387  	}
   388  	logger.Tracef("pending machines: %v", pending)
   389  	logger.Tracef("dead machines: %v", dead)
   390  	return
   391  }
   392  
   393  // findUnknownInstances finds instances which are not associated with a machine.
   394  func (task *provisionerTask) findUnknownInstances(stopping []instance.Instance) ([]instance.Instance, error) {
   395  	// Make a copy of the instances we know about.
   396  	instances := make(map[instance.Id]instance.Instance)
   397  	for k, v := range task.instances {
   398  		instances[k] = v
   399  	}
   400  
   401  	for _, m := range task.machines {
   402  		instId, err := m.InstanceId()
   403  		switch {
   404  		case err == nil:
   405  			delete(instances, instId)
   406  		case params.IsCodeNotProvisioned(err):
   407  		case params.IsCodeNotFoundOrCodeUnauthorized(err):
   408  		default:
   409  			return nil, err
   410  		}
   411  	}
   412  	// Now remove all those instances that we are stopping already as we
   413  	// know about those and don't want to include them in the unknown list.
   414  	for _, inst := range stopping {
   415  		delete(instances, inst.Id())
   416  	}
   417  	var unknown []instance.Instance
   418  	for _, inst := range instances {
   419  		unknown = append(unknown, inst)
   420  	}
   421  	return unknown, nil
   422  }
   423  
   424  // instancesForMachines returns a list of instance.Instance that represent
   425  // the list of machines running in the provider. Missing machines are
   426  // omitted from the list.
   427  func (task *provisionerTask) instancesForMachines(machines []*apiprovisioner.Machine) []instance.Instance {
   428  	var instances []instance.Instance
   429  	for _, machine := range machines {
   430  		instId, err := machine.InstanceId()
   431  		if err == nil {
   432  			instance, found := task.instances[instId]
   433  			// If the instance is not found we can't stop it.
   434  			if found {
   435  				instances = append(instances, instance)
   436  			}
   437  		}
   438  	}
   439  	return instances
   440  }
   441  
   442  func (task *provisionerTask) stopInstances(instances []instance.Instance) error {
   443  	// Although calling StopInstance with an empty slice should produce no change in the
   444  	// provider, environs like dummy do not consider this a noop.
   445  	if len(instances) == 0 {
   446  		return nil
   447  	}
   448  	ids := make([]instance.Id, len(instances))
   449  	for i, inst := range instances {
   450  		ids[i] = inst.Id()
   451  	}
   452  	if err := task.broker.StopInstances(ids...); err != nil {
   453  		return errors.Annotate(err, "broker failed to stop instances")
   454  	}
   455  	return nil
   456  }
   457  
   458  func (task *provisionerTask) constructInstanceConfig(
   459  	machine *apiprovisioner.Machine,
   460  	auth authentication.AuthenticationProvider,
   461  	pInfo *params.ProvisioningInfo,
   462  ) (*instancecfg.InstanceConfig, error) {
   463  
   464  	stateInfo, apiInfo, err := auth.SetupAuthentication(machine)
   465  	if err != nil {
   466  		return nil, errors.Annotate(err, "failed to setup authentication")
   467  	}
   468  
   469  	// Generated a nonce for the new instance, with the format: "machine-#:UUID".
   470  	// The first part is a badge, specifying the tag of the machine the provisioner
   471  	// is running on, while the second part is a random UUID.
   472  	uuid, err := utils.NewUUID()
   473  	if err != nil {
   474  		return nil, errors.Annotate(err, "failed to generate a nonce for machine "+machine.Id())
   475  	}
   476  
   477  	nonce := fmt.Sprintf("%s:%s", task.machineTag, uuid)
   478  	return instancecfg.NewInstanceConfig(
   479  		machine.Id(),
   480  		nonce,
   481  		task.imageStream,
   482  		pInfo.Series,
   483  		task.secureServerConnection,
   484  		nil,
   485  		stateInfo,
   486  		apiInfo,
   487  	)
   488  }
   489  
   490  func constructStartInstanceParams(
   491  	machine *apiprovisioner.Machine,
   492  	instanceConfig *instancecfg.InstanceConfig,
   493  	provisioningInfo *params.ProvisioningInfo,
   494  	possibleTools coretools.List,
   495  ) (environs.StartInstanceParams, error) {
   496  
   497  	volumes := make([]storage.VolumeParams, len(provisioningInfo.Volumes))
   498  	for i, v := range provisioningInfo.Volumes {
   499  		volumeTag, err := names.ParseVolumeTag(v.VolumeTag)
   500  		if err != nil {
   501  			return environs.StartInstanceParams{}, errors.Trace(err)
   502  		}
   503  		if v.Attachment == nil {
   504  			return environs.StartInstanceParams{}, errors.Errorf("volume params missing attachment")
   505  		}
   506  		machineTag, err := names.ParseMachineTag(v.Attachment.MachineTag)
   507  		if err != nil {
   508  			return environs.StartInstanceParams{}, errors.Trace(err)
   509  		}
   510  		if machineTag != machine.Tag() {
   511  			return environs.StartInstanceParams{}, errors.Errorf("volume attachment params has invalid machine tag")
   512  		}
   513  		if v.Attachment.InstanceId != "" {
   514  			return environs.StartInstanceParams{}, errors.Errorf("volume attachment params specifies instance ID")
   515  		}
   516  		volumes[i] = storage.VolumeParams{
   517  			volumeTag,
   518  			v.Size,
   519  			storage.ProviderType(v.Provider),
   520  			v.Attributes,
   521  			v.Tags,
   522  			&storage.VolumeAttachmentParams{
   523  				AttachmentParams: storage.AttachmentParams{
   524  					Machine:  machineTag,
   525  					ReadOnly: v.Attachment.ReadOnly,
   526  				},
   527  				Volume: volumeTag,
   528  			},
   529  		}
   530  	}
   531  
   532  	return environs.StartInstanceParams{
   533  		Constraints:       provisioningInfo.Constraints,
   534  		Tools:             possibleTools,
   535  		InstanceConfig:    instanceConfig,
   536  		Placement:         provisioningInfo.Placement,
   537  		DistributionGroup: machine.DistributionGroup,
   538  		Volumes:           volumes,
   539  	}, nil
   540  }
   541  
   542  func (task *provisionerTask) maintainMachines(machines []*apiprovisioner.Machine) error {
   543  	for _, m := range machines {
   544  		logger.Infof("maintainMachines: %v", m)
   545  		startInstanceParams := environs.StartInstanceParams{}
   546  		startInstanceParams.InstanceConfig = &instancecfg.InstanceConfig{}
   547  		startInstanceParams.InstanceConfig.MachineId = m.Id()
   548  		if err := task.broker.MaintainInstance(startInstanceParams); err != nil {
   549  			return errors.Annotatef(err, "cannot maintain machine %v", m)
   550  		}
   551  	}
   552  	return nil
   553  }
   554  
   555  func (task *provisionerTask) startMachines(machines []*apiprovisioner.Machine) error {
   556  	for _, m := range machines {
   557  
   558  		pInfo, err := task.blockUntilProvisioned(m.ProvisioningInfo)
   559  		if err != nil {
   560  			return err
   561  		}
   562  
   563  		instanceCfg, err := task.constructInstanceConfig(m, task.auth, pInfo)
   564  		if err != nil {
   565  			return err
   566  		}
   567  
   568  		assocProvInfoAndMachCfg(pInfo, instanceCfg)
   569  
   570  		possibleTools, err := task.toolsFinder.FindTools(
   571  			version.Current.Number,
   572  			pInfo.Series,
   573  			pInfo.Constraints.Arch,
   574  		)
   575  		if err != nil {
   576  			return task.setErrorStatus("cannot find tools for machine %q: %v", m, err)
   577  		}
   578  
   579  		startInstanceParams, err := constructStartInstanceParams(
   580  			m,
   581  			instanceCfg,
   582  			pInfo,
   583  			possibleTools,
   584  		)
   585  		if err != nil {
   586  			return task.setErrorStatus("cannot construct params for machine %q: %v", m, err)
   587  		}
   588  
   589  		if err := task.startMachine(m, pInfo, startInstanceParams); err != nil {
   590  			return errors.Annotatef(err, "cannot start machine %v", m)
   591  		}
   592  	}
   593  	return nil
   594  }
   595  
   596  func (task *provisionerTask) setErrorStatus(message string, machine *apiprovisioner.Machine, err error) error {
   597  	logger.Errorf(message, machine, err)
   598  	if err1 := machine.SetStatus(params.StatusError, err.Error(), nil); err1 != nil {
   599  		// Something is wrong with this machine, better report it back.
   600  		return errors.Annotatef(err1, "cannot set error status for machine %q", machine)
   601  	}
   602  	return nil
   603  }
   604  
   605  func (task *provisionerTask) prepareNetworkAndInterfaces(networkInfo []network.InterfaceInfo) (
   606  	networks []params.Network, ifaces []params.NetworkInterface, err error) {
   607  	if len(networkInfo) == 0 {
   608  		return nil, nil, nil
   609  	}
   610  	visitedNetworks := set.NewStrings()
   611  	for _, info := range networkInfo {
   612  		if !names.IsValidNetwork(info.NetworkName) {
   613  			return nil, nil, errors.Errorf("invalid network name %q", info.NetworkName)
   614  		}
   615  		networkTag := names.NewNetworkTag(info.NetworkName).String()
   616  		if !visitedNetworks.Contains(networkTag) {
   617  			networks = append(networks, params.Network{
   618  				Tag:        networkTag,
   619  				ProviderId: string(info.ProviderId),
   620  				CIDR:       info.CIDR,
   621  				VLANTag:    info.VLANTag,
   622  			})
   623  			visitedNetworks.Add(networkTag)
   624  		}
   625  		ifaces = append(ifaces, params.NetworkInterface{
   626  			InterfaceName: info.ActualInterfaceName(),
   627  			MACAddress:    info.MACAddress,
   628  			NetworkTag:    networkTag,
   629  			IsVirtual:     info.IsVirtual(),
   630  			Disabled:      info.Disabled,
   631  		})
   632  	}
   633  	return networks, ifaces, nil
   634  }
   635  
   636  func (task *provisionerTask) startMachine(
   637  	machine *apiprovisioner.Machine,
   638  	provisioningInfo *params.ProvisioningInfo,
   639  	startInstanceParams environs.StartInstanceParams,
   640  ) error {
   641  
   642  	result, err := task.broker.StartInstance(startInstanceParams)
   643  	if err != nil {
   644  		// If this is a retryable error, we retry once
   645  		if instance.IsRetryableCreationError(errors.Cause(err)) {
   646  			logger.Infof("retryable error received on start instance - retrying instance creation")
   647  			result, err = task.broker.StartInstance(startInstanceParams)
   648  			if err != nil {
   649  				return task.setErrorStatus("cannot start instance for machine after a retry %q: %v", machine, err)
   650  			}
   651  		} else {
   652  			// Set the state to error, so the machine will be skipped next
   653  			// time until the error is resolved, but don't return an
   654  			// error; just keep going with the other machines.
   655  			return task.setErrorStatus("cannot start instance for machine %q: %v", machine, err)
   656  		}
   657  	}
   658  
   659  	inst := result.Instance
   660  	hardware := result.Hardware
   661  	nonce := startInstanceParams.InstanceConfig.MachineNonce
   662  	networks, ifaces, err := task.prepareNetworkAndInterfaces(result.NetworkInfo)
   663  	if err != nil {
   664  		return task.setErrorStatus("cannot prepare network for machine %q: %v", machine, err)
   665  	}
   666  	volumes := volumesToApiserver(result.Volumes)
   667  	volumeAttachments := volumeAttachmentsToApiserver(result.VolumeAttachments)
   668  
   669  	// TODO(dimitern) In a newer Provisioner API version, change
   670  	// SetInstanceInfo or add a new method that takes and saves in
   671  	// state all the information available on a network.InterfaceInfo
   672  	// for each interface, so we can later manage interfaces
   673  	// dynamically at run-time.
   674  	err = machine.SetInstanceInfo(inst.Id(), nonce, hardware, networks, ifaces, volumes, volumeAttachments)
   675  	if err != nil && params.IsCodeNotImplemented(err) {
   676  		return fmt.Errorf("cannot provision instance %v for machine %q with networks: not implemented", inst.Id(), machine)
   677  	} else if err == nil {
   678  		logger.Infof(
   679  			"started machine %s as instance %s with hardware %q, networks %v, interfaces %v, volumes %v, volume attachments %v",
   680  			machine, inst.Id(), hardware, networks, ifaces, volumes, volumeAttachments,
   681  		)
   682  		return nil
   683  	}
   684  	// We need to stop the instance right away here, set error status and go on.
   685  	task.setErrorStatus("cannot register instance for machine %v: %v", machine, err)
   686  	if err := task.broker.StopInstances(inst.Id()); err != nil {
   687  		// We cannot even stop the instance, log the error and quit.
   688  		return errors.Annotatef(err, "cannot stop instance %q for machine %v", inst.Id(), machine)
   689  	}
   690  	return nil
   691  }
   692  
   693  type provisioningInfo struct {
   694  	Constraints    constraints.Value
   695  	Series         string
   696  	Placement      string
   697  	InstanceConfig *instancecfg.InstanceConfig
   698  }
   699  
   700  func assocProvInfoAndMachCfg(
   701  	provInfo *params.ProvisioningInfo,
   702  	instanceConfig *instancecfg.InstanceConfig,
   703  ) *provisioningInfo {
   704  
   705  	instanceConfig.Networks = provInfo.Networks
   706  	instanceConfig.Tags = provInfo.Tags
   707  
   708  	if len(provInfo.Jobs) > 0 {
   709  		instanceConfig.Jobs = provInfo.Jobs
   710  	}
   711  
   712  	return &provisioningInfo{
   713  		Constraints:    provInfo.Constraints,
   714  		Series:         provInfo.Series,
   715  		Placement:      provInfo.Placement,
   716  		InstanceConfig: instanceConfig,
   717  	}
   718  }
   719  
   720  func volumesToApiserver(volumes []storage.Volume) []params.Volume {
   721  	result := make([]params.Volume, len(volumes))
   722  	for i, v := range volumes {
   723  		result[i] = params.Volume{
   724  			v.Tag.String(),
   725  			params.VolumeInfo{
   726  				v.VolumeId,
   727  				v.HardwareId,
   728  				v.Size,
   729  				v.Persistent,
   730  			},
   731  		}
   732  	}
   733  	return result
   734  }
   735  
   736  func volumeAttachmentsToApiserver(attachments []storage.VolumeAttachment) map[string]params.VolumeAttachmentInfo {
   737  	result := make(map[string]params.VolumeAttachmentInfo)
   738  	for _, a := range attachments {
   739  		result[a.Volume.String()] = params.VolumeAttachmentInfo{
   740  			a.DeviceName,
   741  			a.ReadOnly,
   742  		}
   743  	}
   744  	return result
   745  }
   746  
   747  // ProvisioningInfo is new in 1.20; wait for the API server to be
   748  // upgraded so we don't spew errors on upgrade.
   749  func (task *provisionerTask) blockUntilProvisioned(
   750  	provision func() (*params.ProvisioningInfo, error),
   751  ) (*params.ProvisioningInfo, error) {
   752  
   753  	var pInfo *params.ProvisioningInfo
   754  	var err error
   755  	for {
   756  		if pInfo, err = provision(); err == nil {
   757  			break
   758  		}
   759  		if params.IsCodeNotImplemented(err) {
   760  			logger.Infof("waiting for state server to be upgraded")
   761  			select {
   762  			case <-task.tomb.Dying():
   763  				return nil, tomb.ErrDying
   764  			case <-time.After(15 * time.Second):
   765  				continue
   766  			}
   767  		}
   768  		return nil, err
   769  	}
   770  
   771  	return pInfo, nil
   772  }