github.com/mwhudson/juju@v0.0.0-20160512215208-90ff01f3497f/provider/vsphere/environ_broker.go (about)

     1  // Copyright 2015 Canonical Ltd.
     2  // Licensed under the AGPLv3, see LICENCE file for details.
     3  
     4  // +build !gccgo
     5  
     6  package vsphere
     7  
     8  import (
     9  	"github.com/juju/errors"
    10  	"github.com/juju/govmomi/vim25/mo"
    11  
    12  	"github.com/juju/juju/cloudconfig/cloudinit"
    13  	"github.com/juju/juju/cloudconfig/instancecfg"
    14  	"github.com/juju/juju/cloudconfig/providerinit"
    15  	"github.com/juju/juju/environs"
    16  	"github.com/juju/juju/instance"
    17  	"github.com/juju/juju/provider/common"
    18  	"github.com/juju/juju/state/multiwatcher"
    19  	"github.com/juju/juju/tools"
    20  )
    21  
    22  const (
    23  	DefaultCpuCores = uint64(2)
    24  	DefaultCpuPower = uint64(2000)
    25  	DefaultMemMb    = uint64(2000)
    26  )
    27  
    28  func isController(mcfg *instancecfg.InstanceConfig) bool {
    29  	return multiwatcher.AnyJobNeedsState(mcfg.Jobs...)
    30  }
    31  
    32  // MaintainInstance is specified in the InstanceBroker interface.
    33  func (*environ) MaintainInstance(args environs.StartInstanceParams) error {
    34  	return nil
    35  }
    36  
    37  // StartInstance implements environs.InstanceBroker.
    38  func (env *environ) StartInstance(args environs.StartInstanceParams) (*environs.StartInstanceResult, error) {
    39  	img, err := findImageMetadata(env, args)
    40  	if err != nil {
    41  		return nil, errors.Trace(err)
    42  	}
    43  	if err := env.finishMachineConfig(args, img); err != nil {
    44  		return nil, errors.Trace(err)
    45  	}
    46  
    47  	raw, hwc, err := env.newRawInstance(args, img)
    48  	if err != nil {
    49  		return nil, errors.Trace(err)
    50  	}
    51  
    52  	logger.Infof("started instance %q", raw.Name)
    53  	inst := newInstance(raw, env)
    54  
    55  	result := environs.StartInstanceResult{
    56  		Instance: inst,
    57  		Hardware: hwc,
    58  	}
    59  	return &result, nil
    60  }
    61  
    62  //this variable is exported, because it has to be rewritten in external unit tests
    63  var FinishInstanceConfig = instancecfg.FinishInstanceConfig
    64  
    65  // finishMachineConfig updates args.MachineConfig in place. Setting up
    66  // the API, StateServing, and SSHkeys information.
    67  func (env *environ) finishMachineConfig(args environs.StartInstanceParams, img *OvaFileMetadata) error {
    68  	envTools, err := args.Tools.Match(tools.Filter{Arch: img.Arch})
    69  	if err != nil {
    70  		return err
    71  	}
    72  
    73  	if err := args.InstanceConfig.SetTools(envTools); err != nil {
    74  		return errors.Trace(err)
    75  	}
    76  	return FinishInstanceConfig(args.InstanceConfig, env.Config())
    77  }
    78  
    79  // newRawInstance is where the new physical instance is actually
    80  // provisioned, relative to the provided args and spec. Info for that
    81  // low-level instance is returned.
    82  func (env *environ) newRawInstance(args environs.StartInstanceParams, img *OvaFileMetadata) (*mo.VirtualMachine, *instance.HardwareCharacteristics, error) {
    83  	machineID := common.MachineFullName(env.Config().UUID(), args.InstanceConfig.MachineId)
    84  
    85  	cloudcfg, err := cloudinit.New(args.Tools.OneSeries())
    86  	if err != nil {
    87  		return nil, nil, errors.Trace(err)
    88  	}
    89  	cloudcfg.AddPackage("open-vm-tools")
    90  	cloudcfg.AddPackage("iptables-persistent")
    91  	userData, err := providerinit.ComposeUserData(args.InstanceConfig, cloudcfg, VsphereRenderer{})
    92  	if err != nil {
    93  		return nil, nil, errors.Annotate(err, "cannot make user data")
    94  	}
    95  	logger.Debugf("Vmware user data; %d bytes", len(userData))
    96  
    97  	rootDisk := common.MinRootDiskSizeGiB(args.InstanceConfig.Series) * 1024
    98  	if args.Constraints.RootDisk != nil && *args.Constraints.RootDisk > rootDisk {
    99  		rootDisk = *args.Constraints.RootDisk
   100  	}
   101  	cpuCores := DefaultCpuCores
   102  	if args.Constraints.CpuCores != nil {
   103  		cpuCores = *args.Constraints.CpuCores
   104  	}
   105  	cpuPower := DefaultCpuPower
   106  	if args.Constraints.CpuPower != nil {
   107  		cpuPower = *args.Constraints.CpuPower
   108  	}
   109  	mem := DefaultMemMb
   110  	if args.Constraints.Mem != nil {
   111  		mem = *args.Constraints.Mem
   112  	}
   113  
   114  	hwc := &instance.HardwareCharacteristics{
   115  		Arch:     &img.Arch,
   116  		Mem:      &mem,
   117  		CpuCores: &cpuCores,
   118  		CpuPower: &cpuPower,
   119  		RootDisk: &rootDisk,
   120  	}
   121  	zones, err := env.parseAvailabilityZones(args)
   122  	if err != nil {
   123  		return nil, nil, errors.Trace(err)
   124  	}
   125  	var inst *mo.VirtualMachine
   126  	for _, zone := range zones {
   127  		var availZone *vmwareAvailZone
   128  		availZone, err = env.availZone(zone)
   129  		if err != nil {
   130  			logger.Warningf("Error while getting availability zone %s: %s", zone, err)
   131  			continue
   132  		}
   133  		apiPort := 0
   134  		if isController(args.InstanceConfig) {
   135  			apiPort = args.InstanceConfig.StateServingInfo.APIPort
   136  		}
   137  		spec := &instanceSpec{
   138  			machineID: machineID,
   139  			zone:      availZone,
   140  			hwc:       hwc,
   141  			img:       img,
   142  			userData:  userData,
   143  			sshKey:    args.InstanceConfig.AuthorizedKeys,
   144  			isState:   isController(args.InstanceConfig),
   145  			apiPort:   apiPort,
   146  		}
   147  		inst, err = env.client.CreateInstance(env.ecfg, spec)
   148  		if err != nil {
   149  			logger.Warningf("Error while trying to create instance in %s availability zone: %s", zone, err)
   150  			continue
   151  		}
   152  		break
   153  	}
   154  	if err != nil {
   155  		return nil, nil, errors.Annotate(err, "Can't create instance in any of availability zones, last error")
   156  	}
   157  	return inst, hwc, err
   158  }
   159  
   160  // AllInstances implements environs.InstanceBroker.
   161  func (env *environ) AllInstances() ([]instance.Instance, error) {
   162  	instances, err := env.instances()
   163  	return instances, errors.Trace(err)
   164  }
   165  
   166  // StopInstances implements environs.InstanceBroker.
   167  func (env *environ) StopInstances(instances ...instance.Id) error {
   168  	var ids []string
   169  	for _, id := range instances {
   170  		ids = append(ids, string(id))
   171  	}
   172  
   173  	err := env.client.RemoveInstances(ids...)
   174  	return errors.Trace(err)
   175  }