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