github.com/cloudbase/juju-core@v0.0.0-20140504232958-a7271ac7912f/state/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 "errors" 8 "fmt" 9 10 "launchpad.net/juju-core/constraints" 11 "launchpad.net/juju-core/instance" 12 "launchpad.net/juju-core/names" 13 "launchpad.net/juju-core/state/api/params" 14 "launchpad.net/juju-core/state/api/watcher" 15 ) 16 17 // Machine represents a juju machine as seen by the provisioner worker. 18 type Machine struct { 19 tag string 20 life params.Life 21 st *State 22 } 23 24 // Tag returns the machine's tag. 25 func (m *Machine) Tag() string { 26 return m.tag 27 } 28 29 // Id returns the machine id. 30 func (m *Machine) Id() string { 31 _, machineId, err := names.ParseTag(m.tag, names.MachineTagKind) 32 if err != nil { 33 panic(fmt.Sprintf("%q is not a valid machine tag", m.tag)) 34 } 35 return machineId 36 } 37 38 // String returns the machine as a string. 39 func (m *Machine) String() string { 40 return m.Id() 41 } 42 43 // Life returns the machine's lifecycle value. 44 func (m *Machine) Life() params.Life { 45 return m.life 46 } 47 48 // Refresh updates the cached local copy of the machine's data. 49 func (m *Machine) Refresh() error { 50 life, err := m.st.machineLife(m.tag) 51 if err != nil { 52 return err 53 } 54 m.life = life 55 return nil 56 } 57 58 // SetStatus sets the status of the machine. 59 func (m *Machine) SetStatus(status params.Status, info string) error { 60 var result params.ErrorResults 61 args := params.SetStatus{ 62 Entities: []params.SetEntityStatus{ 63 {Tag: m.tag, Status: status, Info: info}, 64 }, 65 } 66 err := m.st.caller.Call("Provisioner", "", "SetStatus", args, &result) 67 if err != nil { 68 return err 69 } 70 return result.OneError() 71 } 72 73 // Status returns the status of the machine. 74 func (m *Machine) Status() (params.Status, string, error) { 75 var results params.StatusResults 76 args := params.Entities{ 77 Entities: []params.Entity{{Tag: m.tag}}, 78 } 79 err := m.st.caller.Call("Provisioner", "", "Status", args, &results) 80 if err != nil { 81 return "", "", err 82 } 83 if len(results.Results) != 1 { 84 return "", "", fmt.Errorf("expected one result, got %d", len(results.Results)) 85 } 86 result := results.Results[0] 87 if result.Error != nil { 88 return "", "", result.Error 89 } 90 return result.Status, result.Info, nil 91 } 92 93 // Constraints returns the exact constraints that should apply when provisioning 94 // an instance for the machine. 95 func (m *Machine) Constraints() (constraints.Value, error) { 96 nothing := constraints.Value{} 97 var results params.ConstraintsResults 98 args := params.Entities{ 99 Entities: []params.Entity{{Tag: m.tag}}, 100 } 101 err := m.st.caller.Call("Provisioner", "", "Constraints", args, &results) 102 if err != nil { 103 return nothing, err 104 } 105 if len(results.Results) != 1 { 106 return nothing, fmt.Errorf("expected one result, got %d", len(results.Results)) 107 } 108 result := results.Results[0] 109 if result.Error != nil { 110 return nothing, result.Error 111 } 112 return result.Constraints, nil 113 } 114 115 // EnsureDead sets the machine lifecycle to Dead if it is Alive or 116 // Dying. It does nothing otherwise. 117 func (m *Machine) EnsureDead() error { 118 var result params.ErrorResults 119 args := params.Entities{ 120 Entities: []params.Entity{{Tag: m.tag}}, 121 } 122 err := m.st.caller.Call("Provisioner", "", "EnsureDead", args, &result) 123 if err != nil { 124 return err 125 } 126 return result.OneError() 127 } 128 129 // Remove removes the machine from state. It will fail if the machine 130 // is not Dead. 131 func (m *Machine) Remove() error { 132 var result params.ErrorResults 133 args := params.Entities{ 134 Entities: []params.Entity{{Tag: m.tag}}, 135 } 136 err := m.st.caller.Call("Provisioner", "", "Remove", args, &result) 137 if err != nil { 138 return err 139 } 140 return result.OneError() 141 } 142 143 // Series returns the operating system series running on the machine. 144 // 145 // NOTE: Unlike state.Machine.Series(), this method returns an error 146 // as well, because it needs to do an API call. 147 func (m *Machine) Series() (string, error) { 148 var results params.StringResults 149 args := params.Entities{ 150 Entities: []params.Entity{{Tag: m.tag}}, 151 } 152 err := m.st.caller.Call("Provisioner", "", "Series", args, &results) 153 if err != nil { 154 return "", err 155 } 156 if len(results.Results) != 1 { 157 return "", fmt.Errorf("expected one result, got %d", len(results.Results)) 158 } 159 result := results.Results[0] 160 if result.Error != nil { 161 return "", result.Error 162 } 163 return result.Result, nil 164 } 165 166 // SetProvisioned sets the provider specific machine id, nonce and also metadata for 167 // this machine. Once set, the instance id cannot be changed. 168 func (m *Machine) SetProvisioned(id instance.Id, nonce string, characteristics *instance.HardwareCharacteristics) error { 169 var result params.ErrorResults 170 args := params.SetProvisioned{ 171 Machines: []params.MachineSetProvisioned{{ 172 Tag: m.tag, 173 InstanceId: id, 174 Nonce: nonce, 175 Characteristics: characteristics, 176 }}, 177 } 178 err := m.st.caller.Call("Provisioner", "", "SetProvisioned", args, &result) 179 if err != nil { 180 return err 181 } 182 return result.OneError() 183 } 184 185 // InstanceId returns the provider specific instance id for the 186 // machine or an CodeNotProvisioned error, if not set. 187 func (m *Machine) InstanceId() (instance.Id, error) { 188 var results params.StringResults 189 args := params.Entities{ 190 Entities: []params.Entity{{Tag: m.tag}}, 191 } 192 err := m.st.caller.Call("Provisioner", "", "InstanceId", args, &results) 193 if err != nil { 194 return "", err 195 } 196 if len(results.Results) != 1 { 197 return "", fmt.Errorf("expected one result, got %d", len(results.Results)) 198 } 199 result := results.Results[0] 200 if result.Error != nil { 201 return "", result.Error 202 } 203 return instance.Id(result.Result), nil 204 } 205 206 // SetPassword sets the machine's password. 207 func (m *Machine) SetPassword(password string) error { 208 var result params.ErrorResults 209 args := params.PasswordChanges{ 210 Changes: []params.PasswordChange{ 211 {Tag: m.tag, Password: password}, 212 }, 213 } 214 err := m.st.caller.Call("Provisioner", "", "SetPasswords", args, &result) 215 if err != nil { 216 return err 217 } 218 return result.OneError() 219 } 220 221 // WatchContainers returns a StringsWatcher that notifies of changes 222 // to the lifecycles of containers of the specified type on the machine. 223 func (m *Machine) WatchContainers(ctype instance.ContainerType) (watcher.StringsWatcher, error) { 224 if string(ctype) == "" { 225 return nil, errors.New("container type must be specified") 226 } 227 supported := false 228 for _, c := range instance.ContainerTypes { 229 if ctype == c { 230 supported = true 231 break 232 } 233 } 234 if !supported { 235 return nil, fmt.Errorf("unsupported container type %q", ctype) 236 } 237 var results params.StringsWatchResults 238 args := params.WatchContainers{ 239 Params: []params.WatchContainer{ 240 {MachineTag: m.tag, ContainerType: string(ctype)}, 241 }, 242 } 243 err := m.st.caller.Call("Provisioner", "", "WatchContainers", args, &results) 244 if err != nil { 245 return nil, err 246 } 247 if len(results.Results) != 1 { 248 return nil, fmt.Errorf("expected one result, got %d", len(results.Results)) 249 } 250 result := results.Results[0] 251 if result.Error != nil { 252 return nil, result.Error 253 } 254 w := watcher.NewStringsWatcher(m.st.caller, result) 255 return w, nil 256 } 257 258 // WatchAllContainers returns a StringsWatcher that notifies of changes 259 // to the lifecycles of all containers on the machine. 260 func (m *Machine) WatchAllContainers() (watcher.StringsWatcher, error) { 261 var results params.StringsWatchResults 262 args := params.WatchContainers{ 263 Params: []params.WatchContainer{ 264 {MachineTag: m.tag}, 265 }, 266 } 267 err := m.st.caller.Call("Provisioner", "", "WatchContainers", args, &results) 268 if err != nil { 269 return nil, err 270 } 271 if len(results.Results) != 1 { 272 return nil, fmt.Errorf("expected one result, got %d", len(results.Results)) 273 } 274 result := results.Results[0] 275 if result.Error != nil { 276 return nil, result.Error 277 } 278 w := watcher.NewStringsWatcher(m.st.caller, result) 279 return w, nil 280 } 281 282 // SetSupportedContainers updates the list of containers supported by this machine. 283 func (m *Machine) SetSupportedContainers(containerTypes ...instance.ContainerType) error { 284 var results params.ErrorResults 285 args := params.MachineContainersParams{ 286 Params: []params.MachineContainers{ 287 {MachineTag: m.tag, ContainerTypes: containerTypes}, 288 }, 289 } 290 err := m.st.caller.Call("Provisioner", "", "SetSupportedContainers", args, &results) 291 if err != nil { 292 return err 293 } 294 if len(results.Results) != 1 { 295 return fmt.Errorf("expected one result, got %d", len(results.Results)) 296 } 297 apiError := results.Results[0].Error 298 if apiError != nil { 299 return apiError 300 } 301 return nil 302 } 303 304 // SupportsNoContainers records the fact that this machine doesn't support any containers. 305 func (m *Machine) SupportsNoContainers() error { 306 return m.SetSupportedContainers([]instance.ContainerType{}...) 307 }