github.com/mhilton/juju-juju@v0.0.0-20150901100907-a94dd2c73455/api/provisioner/machine.go (about) 1 // Copyright 2013 Canonical Ltd. 2 // Licensed under the AGPLv3, see LICENCE file for details. 3 4 package provisioner 5 6 import ( 7 "fmt" 8 9 "github.com/juju/names" 10 11 "github.com/juju/juju/api/watcher" 12 "github.com/juju/juju/apiserver/params" 13 "github.com/juju/juju/instance" 14 ) 15 16 // Machine represents a juju machine as seen by the provisioner worker. 17 type Machine struct { 18 tag names.MachineTag 19 life params.Life 20 st *State 21 } 22 23 // Tag returns the machine's tag. 24 func (m *Machine) Tag() names.Tag { 25 return m.tag 26 } 27 28 // Id returns the machine id. 29 func (m *Machine) Id() string { 30 return m.tag.Id() 31 } 32 33 // String returns the machine as a string. 34 func (m *Machine) String() string { 35 return m.Id() 36 } 37 38 // Life returns the machine's lifecycle value. 39 func (m *Machine) Life() params.Life { 40 return m.life 41 } 42 43 // Refresh updates the cached local copy of the machine's data. 44 func (m *Machine) Refresh() error { 45 life, err := m.st.machineLife(m.tag) 46 if err != nil { 47 return err 48 } 49 m.life = life 50 return nil 51 } 52 53 // ProvisioningInfo returns the information required to provision a machine. 54 func (m *Machine) ProvisioningInfo() (*params.ProvisioningInfo, error) { 55 var results params.ProvisioningInfoResults 56 args := params.Entities{Entities: []params.Entity{{m.tag.String()}}} 57 err := m.st.facade.FacadeCall("ProvisioningInfo", args, &results) 58 if err != nil { 59 return nil, err 60 } 61 if len(results.Results) != 1 { 62 return nil, fmt.Errorf("expected 1 result, got %d", len(results.Results)) 63 } 64 result := results.Results[0] 65 if result.Error != nil { 66 return nil, result.Error 67 } 68 return result.Result, nil 69 } 70 71 // SetStatus sets the status of the machine. 72 func (m *Machine) SetStatus(status params.Status, info string, data map[string]interface{}) error { 73 var result params.ErrorResults 74 args := params.SetStatus{ 75 Entities: []params.EntityStatusArgs{ 76 {Tag: m.tag.String(), Status: status, Info: info, Data: data}, 77 }, 78 } 79 err := m.st.facade.FacadeCall("SetStatus", args, &result) 80 if err != nil { 81 return err 82 } 83 return result.OneError() 84 } 85 86 // Status returns the status of the machine. 87 func (m *Machine) Status() (params.Status, string, error) { 88 var results params.StatusResults 89 args := params.Entities{ 90 Entities: []params.Entity{{Tag: m.tag.String()}}, 91 } 92 err := m.st.facade.FacadeCall("Status", args, &results) 93 if err != nil { 94 return "", "", err 95 } 96 if len(results.Results) != 1 { 97 return "", "", fmt.Errorf("expected 1 result, got %d", len(results.Results)) 98 } 99 result := results.Results[0] 100 if result.Error != nil { 101 return "", "", result.Error 102 } 103 return result.Status, result.Info, nil 104 } 105 106 // EnsureDead sets the machine lifecycle to Dead if it is Alive or 107 // Dying. It does nothing otherwise. 108 func (m *Machine) EnsureDead() error { 109 var result params.ErrorResults 110 args := params.Entities{ 111 Entities: []params.Entity{{Tag: m.tag.String()}}, 112 } 113 err := m.st.facade.FacadeCall("EnsureDead", args, &result) 114 if err != nil { 115 return err 116 } 117 return result.OneError() 118 } 119 120 // Remove removes the machine from state. It will fail if the machine 121 // is not Dead. 122 func (m *Machine) Remove() error { 123 var result params.ErrorResults 124 args := params.Entities{ 125 Entities: []params.Entity{{Tag: m.tag.String()}}, 126 } 127 err := m.st.facade.FacadeCall("Remove", args, &result) 128 if err != nil { 129 return err 130 } 131 return result.OneError() 132 } 133 134 // Series returns the operating system series running on the machine. 135 // 136 // NOTE: Unlike state.Machine.Series(), this method returns an error 137 // as well, because it needs to do an API call. 138 func (m *Machine) Series() (string, error) { 139 var results params.StringResults 140 args := params.Entities{ 141 Entities: []params.Entity{{Tag: m.tag.String()}}, 142 } 143 err := m.st.facade.FacadeCall("Series", args, &results) 144 if err != nil { 145 return "", err 146 } 147 if len(results.Results) != 1 { 148 return "", fmt.Errorf("expected 1 result, got %d", len(results.Results)) 149 } 150 result := results.Results[0] 151 if result.Error != nil { 152 return "", result.Error 153 } 154 return result.Result, nil 155 } 156 157 // DistributionGroup returns a slice of instance.Ids 158 // that belong to the same distribution group as this 159 // Machine. The provisioner may use this information 160 // to distribute instances for high availability. 161 func (m *Machine) DistributionGroup() ([]instance.Id, error) { 162 var results params.DistributionGroupResults 163 args := params.Entities{ 164 Entities: []params.Entity{{Tag: m.tag.String()}}, 165 } 166 err := m.st.facade.FacadeCall("DistributionGroup", args, &results) 167 if err != nil { 168 return nil, err 169 } 170 if len(results.Results) != 1 { 171 return nil, fmt.Errorf("expected 1 result, got %d", len(results.Results)) 172 } 173 result := results.Results[0] 174 if result.Error != nil { 175 return nil, result.Error 176 } 177 return result.Result, nil 178 } 179 180 // SetInstanceInfo sets the provider specific instance id, nonce, 181 // metadata, networks and interfaces for this machine. Once set, the 182 // instance id cannot be changed. 183 func (m *Machine) SetInstanceInfo( 184 id instance.Id, nonce string, characteristics *instance.HardwareCharacteristics, 185 networks []params.Network, interfaces []params.NetworkInterface, volumes []params.Volume, 186 volumeAttachments map[string]params.VolumeAttachmentInfo, 187 ) error { 188 var result params.ErrorResults 189 args := params.InstancesInfo{ 190 Machines: []params.InstanceInfo{{ 191 Tag: m.tag.String(), 192 InstanceId: id, 193 Nonce: nonce, 194 Characteristics: characteristics, 195 Networks: networks, 196 Interfaces: interfaces, 197 Volumes: volumes, 198 VolumeAttachments: volumeAttachments, 199 }}, 200 } 201 err := m.st.facade.FacadeCall("SetInstanceInfo", args, &result) 202 if err != nil { 203 return err 204 } 205 return result.OneError() 206 } 207 208 // InstanceId returns the provider specific instance id for the 209 // machine or an CodeNotProvisioned error, if not set. 210 func (m *Machine) InstanceId() (instance.Id, error) { 211 var results params.StringResults 212 args := params.Entities{ 213 Entities: []params.Entity{{Tag: m.tag.String()}}, 214 } 215 err := m.st.facade.FacadeCall("InstanceId", args, &results) 216 if err != nil { 217 return "", err 218 } 219 if len(results.Results) != 1 { 220 return "", fmt.Errorf("expected 1 result, got %d", len(results.Results)) 221 } 222 result := results.Results[0] 223 if result.Error != nil { 224 return "", result.Error 225 } 226 return instance.Id(result.Result), nil 227 } 228 229 // SetPassword sets the machine's password. 230 func (m *Machine) SetPassword(password string) error { 231 var result params.ErrorResults 232 args := params.EntityPasswords{ 233 Changes: []params.EntityPassword{ 234 {Tag: m.tag.String(), Password: password}, 235 }, 236 } 237 err := m.st.facade.FacadeCall("SetPasswords", args, &result) 238 if err != nil { 239 return err 240 } 241 return result.OneError() 242 } 243 244 // WatchContainers returns a StringsWatcher that notifies of changes 245 // to the lifecycles of containers of the specified type on the machine. 246 func (m *Machine) WatchContainers(ctype instance.ContainerType) (watcher.StringsWatcher, error) { 247 if string(ctype) == "" { 248 return nil, fmt.Errorf("container type must be specified") 249 } 250 supported := false 251 for _, c := range instance.ContainerTypes { 252 if ctype == c { 253 supported = true 254 break 255 } 256 } 257 if !supported { 258 return nil, fmt.Errorf("unsupported container type %q", ctype) 259 } 260 var results params.StringsWatchResults 261 args := params.WatchContainers{ 262 Params: []params.WatchContainer{ 263 {MachineTag: m.tag.String(), ContainerType: string(ctype)}, 264 }, 265 } 266 err := m.st.facade.FacadeCall("WatchContainers", args, &results) 267 if err != nil { 268 return nil, err 269 } 270 if len(results.Results) != 1 { 271 return nil, fmt.Errorf("expected 1 result, got %d", len(results.Results)) 272 } 273 result := results.Results[0] 274 if result.Error != nil { 275 return nil, result.Error 276 } 277 w := watcher.NewStringsWatcher(m.st.facade.RawAPICaller(), result) 278 return w, nil 279 } 280 281 // WatchAllContainers returns a StringsWatcher that notifies of changes 282 // to the lifecycles of all containers on the machine. 283 func (m *Machine) WatchAllContainers() (watcher.StringsWatcher, error) { 284 var results params.StringsWatchResults 285 args := params.WatchContainers{ 286 Params: []params.WatchContainer{ 287 {MachineTag: m.tag.String()}, 288 }, 289 } 290 err := m.st.facade.FacadeCall("WatchContainers", args, &results) 291 if err != nil { 292 return nil, err 293 } 294 if len(results.Results) != 1 { 295 return nil, fmt.Errorf("expected 1 result, got %d", len(results.Results)) 296 } 297 result := results.Results[0] 298 if result.Error != nil { 299 return nil, result.Error 300 } 301 w := watcher.NewStringsWatcher(m.st.facade.RawAPICaller(), result) 302 return w, nil 303 } 304 305 // SetSupportedContainers updates the list of containers supported by this machine. 306 func (m *Machine) SetSupportedContainers(containerTypes ...instance.ContainerType) error { 307 var results params.ErrorResults 308 args := params.MachineContainersParams{ 309 Params: []params.MachineContainers{ 310 {MachineTag: m.tag.String(), ContainerTypes: containerTypes}, 311 }, 312 } 313 err := m.st.facade.FacadeCall("SetSupportedContainers", args, &results) 314 if err != nil { 315 return err 316 } 317 if len(results.Results) != 1 { 318 return fmt.Errorf("expected 1 result, got %d", len(results.Results)) 319 } 320 apiError := results.Results[0].Error 321 if apiError != nil { 322 return apiError 323 } 324 return nil 325 } 326 327 // SupportsNoContainers records the fact that this machine doesn't support any containers. 328 func (m *Machine) SupportsNoContainers() error { 329 return m.SetSupportedContainers([]instance.ContainerType{}...) 330 }