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 }