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 }