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