github.com/axw/juju@v0.0.0-20161005053422-4bd6544d08d4/state/machine.go (about) 1 // Copyright 2012, 2013 Canonical Ltd. 2 // Licensed under the AGPLv3, see LICENCE file for details. 3 4 package state 5 6 import ( 7 "fmt" 8 "strings" 9 "time" 10 11 "github.com/juju/errors" 12 jujutxn "github.com/juju/txn" 13 "github.com/juju/utils" 14 "github.com/juju/utils/set" 15 "github.com/juju/version" 16 "gopkg.in/juju/names.v2" 17 "gopkg.in/mgo.v2" 18 "gopkg.in/mgo.v2/bson" 19 "gopkg.in/mgo.v2/txn" 20 21 "github.com/juju/juju/constraints" 22 "github.com/juju/juju/core/actions" 23 "github.com/juju/juju/instance" 24 "github.com/juju/juju/mongo" 25 "github.com/juju/juju/network" 26 "github.com/juju/juju/state/multiwatcher" 27 "github.com/juju/juju/state/presence" 28 "github.com/juju/juju/status" 29 "github.com/juju/juju/tools" 30 ) 31 32 // Machine represents the state of a machine. 33 type Machine struct { 34 st *State 35 doc machineDoc 36 } 37 38 // MachineJob values define responsibilities that machines may be 39 // expected to fulfil. 40 type MachineJob int 41 42 const ( 43 _ MachineJob = iota 44 JobHostUnits 45 JobManageModel 46 ) 47 48 var ( 49 jobNames = map[MachineJob]multiwatcher.MachineJob{ 50 JobHostUnits: multiwatcher.JobHostUnits, 51 JobManageModel: multiwatcher.JobManageModel, 52 } 53 jobMigrationValue = map[MachineJob]string{ 54 JobHostUnits: "host-units", 55 JobManageModel: "api-server", 56 } 57 ) 58 59 // AllJobs returns all supported machine jobs. 60 func AllJobs() []MachineJob { 61 return []MachineJob{ 62 JobHostUnits, 63 JobManageModel, 64 } 65 } 66 67 // ToParams returns the job as multiwatcher.MachineJob. 68 func (job MachineJob) ToParams() multiwatcher.MachineJob { 69 if jujuJob, ok := jobNames[job]; ok { 70 return jujuJob 71 } 72 return multiwatcher.MachineJob(fmt.Sprintf("<unknown job %d>", int(job))) 73 } 74 75 // params.JobsFromJobs converts state jobs to juju jobs. 76 func paramsJobsFromJobs(jobs []MachineJob) []multiwatcher.MachineJob { 77 jujuJobs := make([]multiwatcher.MachineJob, len(jobs)) 78 for i, machineJob := range jobs { 79 jujuJobs[i] = machineJob.ToParams() 80 } 81 return jujuJobs 82 } 83 84 // MigrationValue converts the state job into a useful human readable 85 // string for model migration. 86 func (job MachineJob) MigrationValue() string { 87 if value, ok := jobMigrationValue[job]; ok { 88 return value 89 } 90 return "unknown" 91 } 92 93 func (job MachineJob) String() string { 94 return string(job.ToParams()) 95 } 96 97 // manualMachinePrefix signals as prefix of Nonce that a machine is 98 // manually provisioned. 99 const manualMachinePrefix = "manual:" 100 101 // machineDoc represents the internal state of a machine in MongoDB. 102 // Note the correspondence with MachineInfo in apiserver/juju. 103 type machineDoc struct { 104 DocID string `bson:"_id"` 105 Id string `bson:"machineid"` 106 ModelUUID string `bson:"model-uuid"` 107 Nonce string 108 Series string 109 ContainerType string 110 Principals []string 111 Life Life 112 Tools *tools.Tools `bson:",omitempty"` 113 Jobs []MachineJob 114 NoVote bool 115 HasVote bool 116 PasswordHash string 117 Clean bool 118 119 // Volumes contains the names of volumes attached to the machine. 120 Volumes []string `bson:"volumes,omitempty"` 121 // Filesystems contains the names of filesystems attached to the machine. 122 Filesystems []string `bson:"filesystems,omitempty"` 123 124 // We store 2 different sets of addresses for the machine, obtained 125 // from different sources. 126 // Addresses is the set of addresses obtained by asking the provider. 127 Addresses []address 128 129 // MachineAddresses is the set of addresses obtained from the machine itself. 130 MachineAddresses []address 131 132 // PreferredPublicAddress is the preferred address to be used for 133 // the machine when a public address is requested. 134 PreferredPublicAddress address `bson:",omitempty"` 135 136 // PreferredPrivateAddress is the preferred address to be used for 137 // the machine when a private address is requested. 138 PreferredPrivateAddress address `bson:",omitempty"` 139 140 // The SupportedContainers attributes are used to advertise what containers this 141 // machine is capable of hosting. 142 SupportedContainersKnown bool 143 SupportedContainers []instance.ContainerType `bson:",omitempty"` 144 // Placement is the placement directive that should be used when provisioning 145 // an instance for the machine. 146 Placement string `bson:",omitempty"` 147 148 // StopMongoUntilVersion holds the version that must be checked to 149 // know if mongo must be stopped. 150 StopMongoUntilVersion string `bson:",omitempty"` 151 } 152 153 func newMachine(st *State, doc *machineDoc) *Machine { 154 machine := &Machine{ 155 st: st, 156 doc: *doc, 157 } 158 return machine 159 } 160 161 func wantsVote(jobs []MachineJob, noVote bool) bool { 162 return hasJob(jobs, JobManageModel) && !noVote 163 } 164 165 // Id returns the machine id. 166 func (m *Machine) Id() string { 167 return m.doc.Id 168 } 169 170 // Principals returns the principals for the machine. 171 func (m *Machine) Principals() []string { 172 return m.doc.Principals 173 } 174 175 // Series returns the operating system series running on the machine. 176 func (m *Machine) Series() string { 177 return m.doc.Series 178 } 179 180 // ContainerType returns the type of container hosting this machine. 181 func (m *Machine) ContainerType() instance.ContainerType { 182 return instance.ContainerType(m.doc.ContainerType) 183 } 184 185 // machineGlobalKey returns the global database key for the identified machine. 186 func machineGlobalKey(id string) string { 187 return "m#" + id 188 } 189 190 // machineGlobalInstanceKey returns the global database key for the identified machine's instance. 191 func machineGlobalInstanceKey(id string) string { 192 return machineGlobalKey(id) + "#instance" 193 } 194 195 // globalInstanceKey returns the global database key for the machinei's instance. 196 func (m *Machine) globalInstanceKey() string { 197 return machineGlobalInstanceKey(m.doc.Id) 198 } 199 200 // globalKey returns the global database key for the machine. 201 func (m *Machine) globalKey() string { 202 return machineGlobalKey(m.doc.Id) 203 } 204 205 // instanceData holds attributes relevant to a provisioned machine. 206 type instanceData struct { 207 DocID string `bson:"_id"` 208 MachineId string `bson:"machineid"` 209 InstanceId instance.Id `bson:"instanceid"` 210 ModelUUID string `bson:"model-uuid"` 211 Status string `bson:"status,omitempty"` 212 Arch *string `bson:"arch,omitempty"` 213 Mem *uint64 `bson:"mem,omitempty"` 214 RootDisk *uint64 `bson:"rootdisk,omitempty"` 215 CpuCores *uint64 `bson:"cpucores,omitempty"` 216 CpuPower *uint64 `bson:"cpupower,omitempty"` 217 Tags *[]string `bson:"tags,omitempty"` 218 AvailZone *string `bson:"availzone,omitempty"` 219 } 220 221 func hardwareCharacteristics(instData instanceData) *instance.HardwareCharacteristics { 222 return &instance.HardwareCharacteristics{ 223 Arch: instData.Arch, 224 Mem: instData.Mem, 225 RootDisk: instData.RootDisk, 226 CpuCores: instData.CpuCores, 227 CpuPower: instData.CpuPower, 228 Tags: instData.Tags, 229 AvailabilityZone: instData.AvailZone, 230 } 231 } 232 233 // TODO(wallyworld): move this method to a service. 234 func (m *Machine) HardwareCharacteristics() (*instance.HardwareCharacteristics, error) { 235 instData, err := getInstanceData(m.st, m.Id()) 236 if err != nil { 237 return nil, err 238 } 239 return hardwareCharacteristics(instData), nil 240 } 241 242 func getInstanceData(st *State, id string) (instanceData, error) { 243 instanceDataCollection, closer := st.getCollection(instanceDataC) 244 defer closer() 245 246 var instData instanceData 247 err := instanceDataCollection.FindId(id).One(&instData) 248 if err == mgo.ErrNotFound { 249 return instanceData{}, errors.NotFoundf("instance data for machine %v", id) 250 } 251 if err != nil { 252 return instanceData{}, fmt.Errorf("cannot get instance data for machine %v: %v", id, err) 253 } 254 return instData, nil 255 } 256 257 // Tag returns a tag identifying the machine. The String method provides a 258 // string representation that is safe to use as a file name. The returned name 259 // will be different from other Tag values returned by any other entities 260 // from the same state. 261 func (m *Machine) Tag() names.Tag { 262 return m.MachineTag() 263 } 264 265 // MachineTag returns the more specific MachineTag type as opposed 266 // to the more generic Tag type. 267 func (m *Machine) MachineTag() names.MachineTag { 268 return names.NewMachineTag(m.Id()) 269 } 270 271 // Life returns whether the machine is Alive, Dying or Dead. 272 func (m *Machine) Life() Life { 273 return m.doc.Life 274 } 275 276 // Jobs returns the responsibilities that must be fulfilled by m's agent. 277 func (m *Machine) Jobs() []MachineJob { 278 return m.doc.Jobs 279 } 280 281 // WantsVote reports whether the machine is a controller 282 // that wants to take part in peer voting. 283 func (m *Machine) WantsVote() bool { 284 return wantsVote(m.doc.Jobs, m.doc.NoVote) 285 } 286 287 // HasVote reports whether that machine is currently a voting 288 // member of the replica set. 289 func (m *Machine) HasVote() bool { 290 return m.doc.HasVote 291 } 292 293 // SetHasVote sets whether the machine is currently a voting 294 // member of the replica set. It should only be called 295 // from the worker that maintains the replica set. 296 func (m *Machine) SetHasVote(hasVote bool) error { 297 ops := []txn.Op{{ 298 C: machinesC, 299 Id: m.doc.DocID, 300 Assert: notDeadDoc, 301 Update: bson.D{{"$set", bson.D{{"hasvote", hasVote}}}}, 302 }} 303 if err := m.st.runTransaction(ops); err != nil { 304 return fmt.Errorf("cannot set HasVote of machine %v: %v", m, onAbort(err, ErrDead)) 305 } 306 m.doc.HasVote = hasVote 307 return nil 308 } 309 310 // SetStopMongoUntilVersion sets a version that is to be checked against 311 // the agent config before deciding if mongo must be started on a 312 // state server. 313 func (m *Machine) SetStopMongoUntilVersion(v mongo.Version) error { 314 ops := []txn.Op{{ 315 C: machinesC, 316 Id: m.doc.DocID, 317 Update: bson.D{{"$set", bson.D{{"stopmongountilversion", v.String()}}}}, 318 }} 319 if err := m.st.runTransaction(ops); err != nil { 320 return fmt.Errorf("cannot set StopMongoUntilVersion %v: %v", m, onAbort(err, ErrDead)) 321 } 322 m.doc.StopMongoUntilVersion = v.String() 323 return nil 324 } 325 326 // StopMongoUntilVersion returns the current minimum version that 327 // is required for this machine to have mongo running. 328 func (m *Machine) StopMongoUntilVersion() (mongo.Version, error) { 329 return mongo.NewVersion(m.doc.StopMongoUntilVersion) 330 } 331 332 // IsManager returns true if the machine has JobManageModel. 333 func (m *Machine) IsManager() bool { 334 return hasJob(m.doc.Jobs, JobManageModel) 335 } 336 337 // IsManual returns true if the machine was manually provisioned. 338 func (m *Machine) IsManual() (bool, error) { 339 // Apart from the bootstrap machine, manually provisioned 340 // machines have a nonce prefixed with "manual:". This is 341 // unique to manual provisioning. 342 if strings.HasPrefix(m.doc.Nonce, manualMachinePrefix) { 343 return true, nil 344 } 345 // The bootstrap machine uses BootstrapNonce, so in that 346 // case we need to check if its provider type is "manual". 347 // We also check for "null", which is an alias for manual. 348 if m.doc.Id == "0" { 349 cfg, err := m.st.ModelConfig() 350 if err != nil { 351 return false, err 352 } 353 t := cfg.Type() 354 return t == "null" || t == "manual", nil 355 } 356 return false, nil 357 } 358 359 // AgentTools returns the tools that the agent is currently running. 360 // It returns an error that satisfies errors.IsNotFound if the tools 361 // have not yet been set. 362 func (m *Machine) AgentTools() (*tools.Tools, error) { 363 if m.doc.Tools == nil { 364 return nil, errors.NotFoundf("agent tools for machine %v", m) 365 } 366 tools := *m.doc.Tools 367 return &tools, nil 368 } 369 370 // checkVersionValidity checks whether the given version is suitable 371 // for passing to SetAgentVersion. 372 func checkVersionValidity(v version.Binary) error { 373 if v.Series == "" || v.Arch == "" { 374 return fmt.Errorf("empty series or arch") 375 } 376 return nil 377 } 378 379 // SetAgentVersion sets the version of juju that the agent is 380 // currently running. 381 func (m *Machine) SetAgentVersion(v version.Binary) (err error) { 382 defer errors.DeferredAnnotatef(&err, "cannot set agent version for machine %v", m) 383 if err = checkVersionValidity(v); err != nil { 384 return err 385 } 386 tools := &tools.Tools{Version: v} 387 ops := []txn.Op{{ 388 C: machinesC, 389 Id: m.doc.DocID, 390 Assert: notDeadDoc, 391 Update: bson.D{{"$set", bson.D{{"tools", tools}}}}, 392 }} 393 // A "raw" transaction is needed here because this function gets 394 // called before database migraions have run so we don't 395 // necessarily want the env UUID added to the id. 396 if err := m.st.runRawTransaction(ops); err != nil { 397 return onAbort(err, ErrDead) 398 } 399 m.doc.Tools = tools 400 return nil 401 } 402 403 // SetMongoPassword sets the password the agent responsible for the machine 404 // should use to communicate with the controllers. Previous passwords 405 // are invalidated. 406 func (m *Machine) SetMongoPassword(password string) error { 407 if !m.IsManager() { 408 return errors.NotSupportedf("setting mongo password for non-controller machine %v", m) 409 } 410 return mongo.SetAdminMongoPassword(m.st.session, m.Tag().String(), password) 411 } 412 413 // SetPassword sets the password for the machine's agent. 414 func (m *Machine) SetPassword(password string) error { 415 if len(password) < utils.MinAgentPasswordLength { 416 return fmt.Errorf("password is only %d bytes long, and is not a valid Agent password", len(password)) 417 } 418 return m.setPasswordHash(utils.AgentPasswordHash(password)) 419 } 420 421 // setPasswordHash sets the underlying password hash in the database directly 422 // to the value supplied. This is split out from SetPassword to allow direct 423 // manipulation in tests (to check for backwards compatibility). 424 func (m *Machine) setPasswordHash(passwordHash string) error { 425 ops := []txn.Op{{ 426 C: machinesC, 427 Id: m.doc.DocID, 428 Assert: notDeadDoc, 429 Update: bson.D{{"$set", bson.D{{"passwordhash", passwordHash}}}}, 430 }} 431 // A "raw" transaction is used here because this code has to work 432 // before the machine env UUID DB migration has run. In this case 433 // we don't want the automatic env UUID prefixing to the doc _id 434 // to occur. 435 if err := m.st.runRawTransaction(ops); err != nil { 436 return fmt.Errorf("cannot set password of machine %v: %v", m, onAbort(err, ErrDead)) 437 } 438 m.doc.PasswordHash = passwordHash 439 return nil 440 } 441 442 // Return the underlying PasswordHash stored in the database. Used by the test 443 // suite to check that the PasswordHash gets properly updated to new values 444 // when compatibility mode is detected. 445 func (m *Machine) getPasswordHash() string { 446 return m.doc.PasswordHash 447 } 448 449 // PasswordValid returns whether the given password is valid 450 // for the given machine. 451 func (m *Machine) PasswordValid(password string) bool { 452 agentHash := utils.AgentPasswordHash(password) 453 return agentHash == m.doc.PasswordHash 454 } 455 456 // Destroy sets the machine lifecycle to Dying if it is Alive. It does 457 // nothing otherwise. Destroy will fail if the machine has principal 458 // units assigned, or if the machine has JobManageModel. 459 // If the machine has assigned units, Destroy will return 460 // a HasAssignedUnitsError. 461 func (m *Machine) Destroy() error { 462 return m.advanceLifecycle(Dying) 463 } 464 465 // ForceDestroy queues the machine for complete removal, including the 466 // destruction of all units and containers on the machine. 467 func (m *Machine) ForceDestroy() error { 468 ops, err := m.forceDestroyOps() 469 if err != nil { 470 return errors.Trace(err) 471 } 472 if err := m.st.runTransaction(ops); err != txn.ErrAborted { 473 return errors.Trace(err) 474 } 475 return nil 476 } 477 478 var managerMachineError = errors.New("machine is required by the model") 479 480 func (m *Machine) forceDestroyOps() ([]txn.Op, error) { 481 if m.IsManager() { 482 return nil, errors.Trace(managerMachineError) 483 } 484 485 return []txn.Op{{ 486 C: machinesC, 487 Id: m.doc.DocID, 488 Assert: bson.D{{"jobs", bson.D{{"$nin", []MachineJob{JobManageModel}}}}}, 489 }, newCleanupOp(cleanupForceDestroyedMachine, m.doc.Id)}, nil 490 } 491 492 // EnsureDead sets the machine lifecycle to Dead if it is Alive or Dying. 493 // It does nothing otherwise. EnsureDead will fail if the machine has 494 // principal units assigned, or if the machine has JobManageModel. 495 // If the machine has assigned units, EnsureDead will return 496 // a HasAssignedUnitsError. 497 func (m *Machine) EnsureDead() error { 498 return m.advanceLifecycle(Dead) 499 } 500 501 type HasAssignedUnitsError struct { 502 MachineId string 503 UnitNames []string 504 } 505 506 func (e *HasAssignedUnitsError) Error() string { 507 return fmt.Sprintf("machine %s has unit %q assigned", e.MachineId, e.UnitNames[0]) 508 } 509 510 func IsHasAssignedUnitsError(err error) bool { 511 _, ok := err.(*HasAssignedUnitsError) 512 return ok 513 } 514 515 // Containers returns the container ids belonging to a parent machine. 516 // TODO(wallyworld): move this method to a service 517 func (m *Machine) Containers() ([]string, error) { 518 containerRefs, closer := m.st.getCollection(containerRefsC) 519 defer closer() 520 521 var mc machineContainers 522 err := containerRefs.FindId(m.doc.DocID).One(&mc) 523 if err == nil { 524 return mc.Children, nil 525 } 526 if err == mgo.ErrNotFound { 527 return nil, errors.NotFoundf("container info for machine %v", m.Id()) 528 } 529 return nil, err 530 } 531 532 // ParentId returns the Id of the host machine if this machine is a container. 533 func (m *Machine) ParentId() (string, bool) { 534 parentId := ParentId(m.Id()) 535 return parentId, parentId != "" 536 } 537 538 // IsContainer returns true if the machine is a container. 539 func (m *Machine) IsContainer() bool { 540 _, isContainer := m.ParentId() 541 return isContainer 542 } 543 544 type HasContainersError struct { 545 MachineId string 546 ContainerIds []string 547 } 548 549 func (e *HasContainersError) Error() string { 550 return fmt.Sprintf("machine %s is hosting containers %q", e.MachineId, strings.Join(e.ContainerIds, ",")) 551 } 552 553 // IsHasContainersError reports whether or not the error is a 554 // HasContainersError, indicating that an attempt to destroy 555 // a machine failed due to it having containers. 556 func IsHasContainersError(err error) bool { 557 _, ok := errors.Cause(err).(*HasContainersError) 558 return ok 559 } 560 561 // HasAttachmentsError is the error returned by EnsureDead if the machine 562 // has attachments to resources that must be cleaned up first. 563 type HasAttachmentsError struct { 564 MachineId string 565 Attachments []names.Tag 566 } 567 568 func (e *HasAttachmentsError) Error() string { 569 return fmt.Sprintf( 570 "machine %s has attachments %s", 571 e.MachineId, e.Attachments, 572 ) 573 } 574 575 // IsHasAttachmentsError reports whether or not the error is a 576 // HasAttachmentsError, indicating that an attempt to destroy 577 // a machine failed due to it having storage attachments. 578 func IsHasAttachmentsError(err error) bool { 579 _, ok := errors.Cause(err).(*HasAttachmentsError) 580 return ok 581 } 582 583 // advanceLifecycle ensures that the machine's lifecycle is no earlier 584 // than the supplied value. If the machine already has that lifecycle 585 // value, or a later one, no changes will be made to remote state. If 586 // the machine has any responsibilities that preclude a valid change in 587 // lifecycle, it will return an error. 588 func (original *Machine) advanceLifecycle(life Life) (err error) { 589 containers, err := original.Containers() 590 if err != nil { 591 return err 592 } 593 if len(containers) > 0 { 594 return &HasContainersError{ 595 MachineId: original.doc.Id, 596 ContainerIds: containers, 597 } 598 } 599 m := original 600 defer func() { 601 if err == nil { 602 // The machine's lifecycle is known to have advanced; it may be 603 // known to have already advanced further than requested, in 604 // which case we set the latest known valid value. 605 if m == nil { 606 life = Dead 607 } else if m.doc.Life > life { 608 life = m.doc.Life 609 } 610 original.doc.Life = life 611 } 612 }() 613 // op and 614 op := txn.Op{ 615 C: machinesC, 616 Id: m.doc.DocID, 617 Update: bson.D{{"$set", bson.D{{"life", life}}}}, 618 } 619 // noUnits asserts that the machine has no principal units. 620 noUnits := bson.DocElem{ 621 "$or", []bson.D{ 622 {{"principals", bson.D{{"$size", 0}}}}, 623 {{"principals", bson.D{{"$exists", false}}}}, 624 }, 625 } 626 cleanupOp := newCleanupOp(cleanupDyingMachine, m.doc.Id) 627 // multiple attempts: one with original data, one with refreshed data, and a final 628 // one intended to determine the cause of failure of the preceding attempt. 629 buildTxn := func(attempt int) ([]txn.Op, error) { 630 advanceAsserts := bson.D{ 631 {"jobs", bson.D{{"$nin", []MachineJob{JobManageModel}}}}, 632 {"hasvote", bson.D{{"$ne", true}}}, 633 } 634 // Grab a fresh copy of the machine data. 635 // We don't write to original, because the expectation is that state- 636 // changing methods only set the requested change on the receiver; a case 637 // could perhaps be made that this is not a helpful convention in the 638 // context of the new state API, but we maintain consistency in the 639 // face of uncertainty. 640 if m, err = m.st.Machine(m.doc.Id); errors.IsNotFound(err) { 641 return nil, jujutxn.ErrNoOperations 642 } else if err != nil { 643 return nil, err 644 } 645 // Check that the life change is sane, and collect the assertions 646 // necessary to determine that it remains so. 647 switch life { 648 case Dying: 649 if m.doc.Life != Alive { 650 return nil, jujutxn.ErrNoOperations 651 } 652 advanceAsserts = append(advanceAsserts, isAliveDoc...) 653 case Dead: 654 if m.doc.Life == Dead { 655 return nil, jujutxn.ErrNoOperations 656 } 657 advanceAsserts = append(advanceAsserts, notDeadDoc...) 658 default: 659 panic(fmt.Errorf("cannot advance lifecycle to %v", life)) 660 } 661 // Check that the machine does not have any responsibilities that 662 // prevent a lifecycle change. 663 if hasJob(m.doc.Jobs, JobManageModel) { 664 // (NOTE: When we enable multiple JobManageModel machines, 665 // this restriction will be lifted, but we will assert that the 666 // machine is not voting) 667 return nil, fmt.Errorf("machine %s is required by the model", m.doc.Id) 668 } 669 if m.doc.HasVote { 670 return nil, fmt.Errorf("machine %s is a voting replica set member", m.doc.Id) 671 } 672 // If there are no alive units left on the machine, or all the services are dying, 673 // then the machine may be soon destroyed by a cleanup worker. 674 // In that case, we don't want to return any error about not being able to 675 // destroy a machine with units as it will be a lie. 676 if life == Dying { 677 canDie := true 678 var principalUnitnames []string 679 for _, principalUnit := range m.doc.Principals { 680 principalUnitnames = append(principalUnitnames, principalUnit) 681 u, err := m.st.Unit(principalUnit) 682 if err != nil { 683 return nil, errors.Annotatef(err, "reading machine %s principal unit %v", m, m.doc.Principals[0]) 684 } 685 svc, err := u.Application() 686 if err != nil { 687 return nil, errors.Annotatef(err, "reading machine %s principal unit service %v", m, u.doc.Application) 688 } 689 if u.Life() == Alive && svc.Life() == Alive { 690 canDie = false 691 break 692 } 693 } 694 if canDie { 695 containers, err := m.Containers() 696 if err != nil { 697 return nil, errors.Annotatef(err, "reading machine %s containers", m) 698 } 699 canDie = len(containers) == 0 700 } 701 if canDie { 702 checkUnits := bson.DocElem{ 703 "$or", []bson.D{ 704 {{"principals", principalUnitnames}}, 705 {{"principals", bson.D{{"$size", 0}}}}, 706 {{"principals", bson.D{{"$exists", false}}}}, 707 }, 708 } 709 op.Assert = append(advanceAsserts, checkUnits) 710 containerCheck := txn.Op{ 711 C: containerRefsC, 712 Id: m.doc.DocID, 713 Assert: bson.D{{"$or", []bson.D{ 714 {{"children", bson.D{{"$size", 0}}}}, 715 {{"children", bson.D{{"$exists", false}}}}, 716 }}}, 717 } 718 return []txn.Op{op, containerCheck, cleanupOp}, nil 719 } 720 } 721 722 if len(m.doc.Principals) > 0 { 723 return nil, &HasAssignedUnitsError{ 724 MachineId: m.doc.Id, 725 UnitNames: m.doc.Principals, 726 } 727 } 728 advanceAsserts = append(advanceAsserts, noUnits) 729 730 if life == Dead { 731 // A machine may not become Dead until it has no more 732 // attachments to inherently machine-bound storage. 733 storageAsserts, err := m.assertNoPersistentStorage() 734 if err != nil { 735 return nil, errors.Trace(err) 736 } 737 advanceAsserts = append(advanceAsserts, storageAsserts...) 738 } 739 740 // Add the additional asserts needed for this transaction. 741 op.Assert = advanceAsserts 742 return []txn.Op{op, cleanupOp}, nil 743 } 744 if err = m.st.run(buildTxn); err == jujutxn.ErrExcessiveContention { 745 err = errors.Annotatef(err, "machine %s cannot advance lifecycle", m) 746 } 747 return err 748 } 749 750 // assertNoPersistentStorage ensures that there are no persistent volumes or 751 // filesystems attached to the machine, and returns any mgo/txn assertions 752 // required to ensure that remains true. 753 func (m *Machine) assertNoPersistentStorage() (bson.D, error) { 754 attachments := make(set.Tags) 755 for _, v := range m.doc.Volumes { 756 tag := names.NewVolumeTag(v) 757 machineBound, err := isVolumeInherentlyMachineBound(m.st, tag) 758 if err != nil { 759 return nil, errors.Trace(err) 760 } 761 if !machineBound { 762 attachments.Add(tag) 763 } 764 } 765 for _, f := range m.doc.Filesystems { 766 tag := names.NewFilesystemTag(f) 767 machineBound, err := isFilesystemInherentlyMachineBound(m.st, tag) 768 if err != nil { 769 return nil, errors.Trace(err) 770 } 771 if !machineBound { 772 attachments.Add(tag) 773 } 774 } 775 if len(attachments) > 0 { 776 return nil, &HasAttachmentsError{ 777 MachineId: m.doc.Id, 778 Attachments: attachments.SortedValues(), 779 } 780 } 781 if m.doc.Life == Dying { 782 return nil, nil 783 } 784 // A Dying machine cannot have attachments added to it, 785 // but if we're advancing from Alive to Dead then we 786 // must ensure no concurrent attachments are made. 787 noNewVolumes := bson.DocElem{ 788 "volumes", bson.D{{ 789 "$not", bson.D{{ 790 "$elemMatch", bson.D{{ 791 "$nin", m.doc.Volumes, 792 }}, 793 }}, 794 }}, 795 // There are no volumes that are not in 796 // the set of volumes we previously knew 797 // about => the current set of volumes 798 // is a subset of the previously known set. 799 } 800 noNewFilesystems := bson.DocElem{ 801 "filesystems", bson.D{{ 802 "$not", bson.D{{ 803 "$elemMatch", bson.D{{ 804 "$nin", m.doc.Filesystems, 805 }}, 806 }}, 807 }}, 808 } 809 return bson.D{noNewVolumes, noNewFilesystems}, nil 810 } 811 812 func (m *Machine) removePortsOps() ([]txn.Op, error) { 813 if m.doc.Life != Dead { 814 return nil, errors.Errorf("machine is not dead") 815 } 816 ports, err := m.AllPorts() 817 if err != nil { 818 return nil, err 819 } 820 var ops []txn.Op 821 for _, p := range ports { 822 ops = append(ops, p.removeOps()...) 823 } 824 return ops, nil 825 } 826 827 func (m *Machine) removeOps() ([]txn.Op, error) { 828 if m.doc.Life != Dead { 829 return nil, fmt.Errorf("machine is not dead") 830 } 831 ops := []txn.Op{ 832 { 833 C: machinesC, 834 Id: m.doc.DocID, 835 Assert: txn.DocExists, 836 Remove: true, 837 }, 838 { 839 C: machinesC, 840 Id: m.doc.DocID, 841 Assert: isDeadDoc, 842 }, 843 removeStatusOp(m.st, m.globalKey()), 844 removeStatusOp(m.st, m.globalInstanceKey()), 845 removeConstraintsOp(m.st, m.globalKey()), 846 annotationRemoveOp(m.st, m.globalKey()), 847 removeRebootDocOp(m.st, m.globalKey()), 848 removeMachineBlockDevicesOp(m.Id()), 849 removeModelMachineRefOp(m.st, m.Id()), 850 removeSSHHostKeyOp(m.st, m.globalKey()), 851 } 852 linkLayerDevicesOps, err := m.removeAllLinkLayerDevicesOps() 853 if err != nil { 854 return nil, errors.Trace(err) 855 } 856 devicesAddressesOps, err := m.removeAllAddressesOps() 857 if err != nil { 858 return nil, errors.Trace(err) 859 } 860 portsOps, err := m.removePortsOps() 861 if err != nil { 862 return nil, errors.Trace(err) 863 } 864 filesystemOps, err := m.st.removeMachineFilesystemsOps(m.MachineTag()) 865 if err != nil { 866 return nil, errors.Trace(err) 867 } 868 volumeOps, err := m.st.removeMachineVolumesOps(m.MachineTag()) 869 if err != nil { 870 return nil, errors.Trace(err) 871 } 872 ops = append(ops, linkLayerDevicesOps...) 873 ops = append(ops, devicesAddressesOps...) 874 ops = append(ops, portsOps...) 875 ops = append(ops, removeContainerRefOps(m.st, m.Id())...) 876 ops = append(ops, filesystemOps...) 877 ops = append(ops, volumeOps...) 878 return ops, nil 879 } 880 881 // Remove removes the machine from state. It will fail if the machine 882 // is not Dead. 883 func (m *Machine) Remove() (err error) { 884 defer errors.DeferredAnnotatef(&err, "cannot remove machine %s", m.doc.Id) 885 logger.Tracef("removing machine %q", m.Id()) 886 // Local variable so we can re-get the machine without disrupting 887 // the caller. 888 machine := m 889 buildTxn := func(attempt int) ([]txn.Op, error) { 890 if attempt != 0 { 891 machine, err = machine.st.Machine(machine.Id()) 892 if errors.IsNotFound(err) { 893 // The machine's gone away, that's fine. 894 return nil, jujutxn.ErrNoOperations 895 } 896 if err != nil { 897 return nil, errors.Trace(err) 898 } 899 } 900 ops, err := machine.removeOps() 901 if err != nil { 902 return nil, errors.Trace(err) 903 } 904 return ops, nil 905 } 906 return m.st.run(buildTxn) 907 } 908 909 // Refresh refreshes the contents of the machine from the underlying 910 // state. It returns an error that satisfies errors.IsNotFound if the 911 // machine has been removed. 912 func (m *Machine) Refresh() error { 913 mdoc, err := m.st.getMachineDoc(m.Id()) 914 if err != nil { 915 if errors.IsNotFound(err) { 916 return err 917 } 918 return errors.Annotatef(err, "cannot refresh machine %v", m) 919 } 920 m.doc = *mdoc 921 return nil 922 } 923 924 // AgentPresence returns whether the respective remote agent is alive. 925 func (m *Machine) AgentPresence() (bool, error) { 926 pwatcher := m.st.workers.PresenceWatcher() 927 return pwatcher.Alive(m.globalKey()) 928 } 929 930 // WaitAgentPresence blocks until the respective agent is alive. 931 func (m *Machine) WaitAgentPresence(timeout time.Duration) (err error) { 932 defer errors.DeferredAnnotatef(&err, "waiting for agent of machine %v", m) 933 ch := make(chan presence.Change) 934 pwatcher := m.st.workers.PresenceWatcher() 935 pwatcher.Watch(m.globalKey(), ch) 936 defer pwatcher.Unwatch(m.globalKey(), ch) 937 for i := 0; i < 2; i++ { 938 select { 939 case change := <-ch: 940 if change.Alive { 941 return nil 942 } 943 case <-time.After(timeout): 944 // TODO(fwereade): 2016-03-17 lp:1558657 945 return fmt.Errorf("still not alive after timeout") 946 case <-pwatcher.Dead(): 947 return pwatcher.Err() 948 } 949 } 950 panic(fmt.Sprintf("presence reported dead status twice in a row for machine %v", m)) 951 } 952 953 // SetAgentPresence signals that the agent for machine m is alive. 954 // It returns the started pinger. 955 func (m *Machine) SetAgentPresence() (*presence.Pinger, error) { 956 presenceCollection := m.st.getPresenceCollection() 957 p := presence.NewPinger(presenceCollection, m.st.modelTag, m.globalKey()) 958 err := p.Start() 959 if err != nil { 960 return nil, err 961 } 962 // We preform a manual sync here so that the 963 // presence pinger has the most up-to-date information when it 964 // starts. This ensures that commands run immediately after bootstrap 965 // like status or enable-ha will have an accurate values 966 // for agent-state. 967 // 968 // TODO: Does not work for multiple controllers. Trigger a sync across all controllers. 969 if m.IsManager() { 970 m.st.workers.PresenceWatcher().Sync() 971 } 972 return p, nil 973 } 974 975 // InstanceId returns the provider specific instance id for this 976 // machine, or a NotProvisionedError, if not set. 977 func (m *Machine) InstanceId() (instance.Id, error) { 978 instData, err := getInstanceData(m.st, m.Id()) 979 if errors.IsNotFound(err) { 980 err = errors.NotProvisionedf("machine %v", m.Id()) 981 } 982 if err != nil { 983 return "", err 984 } 985 return instData.InstanceId, err 986 } 987 988 // InstanceStatus returns the provider specific instance status for this machine, 989 // or a NotProvisionedError if instance is not yet provisioned. 990 func (m *Machine) InstanceStatus() (status.StatusInfo, error) { 991 machineStatus, err := getStatus(m.st, m.globalInstanceKey(), "instance") 992 if err != nil { 993 logger.Warningf("error when retrieving instance status for machine: %s, %v", m.Id(), err) 994 return status.StatusInfo{}, err 995 } 996 return machineStatus, nil 997 } 998 999 // SetInstanceStatus sets the provider specific instance status for a machine. 1000 func (m *Machine) SetInstanceStatus(sInfo status.StatusInfo) (err error) { 1001 return setStatus(m.st, setStatusParams{ 1002 badge: "instance", 1003 globalKey: m.globalInstanceKey(), 1004 status: sInfo.Status, 1005 message: sInfo.Message, 1006 rawData: sInfo.Data, 1007 updated: sInfo.Since, 1008 }) 1009 1010 } 1011 1012 // InstanceStatusHistory returns a slice of at most filter.Size StatusInfo items 1013 // or items as old as filter.Date or items newer than now - filter.Delta time 1014 // representing past statuses for this machine instance. 1015 // Instance represents the provider underlying [v]hardware or container where 1016 // this juju machine is deployed. 1017 func (m *Machine) InstanceStatusHistory(filter status.StatusHistoryFilter) ([]status.StatusInfo, error) { 1018 args := &statusHistoryArgs{ 1019 st: m.st, 1020 globalKey: m.globalInstanceKey(), 1021 filter: filter, 1022 } 1023 return statusHistory(args) 1024 } 1025 1026 // AvailabilityZone returns the provier-specific instance availability 1027 // zone in which the machine was provisioned. 1028 func (m *Machine) AvailabilityZone() (string, error) { 1029 instData, err := getInstanceData(m.st, m.Id()) 1030 if errors.IsNotFound(err) { 1031 return "", errors.Trace(errors.NotProvisionedf("machine %v", m.Id())) 1032 } 1033 if err != nil { 1034 return "", errors.Trace(err) 1035 } 1036 var zone string 1037 if instData.AvailZone != nil { 1038 zone = *instData.AvailZone 1039 } 1040 return zone, nil 1041 } 1042 1043 // Units returns all the units that have been assigned to the machine. 1044 func (m *Machine) Units() (units []*Unit, err error) { 1045 defer errors.DeferredAnnotatef(&err, "cannot get units assigned to machine %v", m) 1046 unitsCollection, closer := m.st.getCollection(unitsC) 1047 defer closer() 1048 1049 pudocs := []unitDoc{} 1050 err = unitsCollection.Find(bson.D{{"machineid", m.doc.Id}}).All(&pudocs) 1051 if err != nil { 1052 return nil, err 1053 } 1054 for _, pudoc := range pudocs { 1055 units = append(units, newUnit(m.st, &pudoc)) 1056 docs := []unitDoc{} 1057 err = unitsCollection.Find(bson.D{{"principal", pudoc.Name}}).All(&docs) 1058 if err != nil { 1059 return nil, err 1060 } 1061 for _, doc := range docs { 1062 units = append(units, newUnit(m.st, &doc)) 1063 } 1064 } 1065 return units, nil 1066 } 1067 1068 // SetProvisioned sets the provider specific machine id, nonce and also metadata for 1069 // this machine. Once set, the instance id cannot be changed. 1070 // 1071 // When provisioning an instance, a nonce should be created and passed 1072 // when starting it, before adding the machine to the state. This means 1073 // that if the provisioner crashes (or its connection to the state is 1074 // lost) after starting the instance, we can be sure that only a single 1075 // instance will be able to act for that machine. 1076 func (m *Machine) SetProvisioned(id instance.Id, nonce string, characteristics *instance.HardwareCharacteristics) (err error) { 1077 defer errors.DeferredAnnotatef(&err, "cannot set instance data for machine %q", m) 1078 1079 if id == "" || nonce == "" { 1080 return fmt.Errorf("instance id and nonce cannot be empty") 1081 } 1082 1083 if characteristics == nil { 1084 characteristics = &instance.HardwareCharacteristics{} 1085 } 1086 instData := &instanceData{ 1087 DocID: m.doc.DocID, 1088 MachineId: m.doc.Id, 1089 InstanceId: id, 1090 ModelUUID: m.doc.ModelUUID, 1091 Arch: characteristics.Arch, 1092 Mem: characteristics.Mem, 1093 RootDisk: characteristics.RootDisk, 1094 CpuCores: characteristics.CpuCores, 1095 CpuPower: characteristics.CpuPower, 1096 Tags: characteristics.Tags, 1097 AvailZone: characteristics.AvailabilityZone, 1098 } 1099 1100 ops := []txn.Op{ 1101 { 1102 C: machinesC, 1103 Id: m.doc.DocID, 1104 Assert: append(isAliveDoc, bson.DocElem{"nonce", ""}), 1105 Update: bson.D{{"$set", bson.D{{"nonce", nonce}}}}, 1106 }, { 1107 C: instanceDataC, 1108 Id: m.doc.DocID, 1109 Assert: txn.DocMissing, 1110 Insert: instData, 1111 }, 1112 } 1113 1114 if err = m.st.runTransaction(ops); err == nil { 1115 m.doc.Nonce = nonce 1116 return nil 1117 } else if err != txn.ErrAborted { 1118 return err 1119 } else if alive, err := isAlive(m.st, machinesC, m.doc.DocID); err != nil { 1120 return err 1121 } else if !alive { 1122 return errNotAlive 1123 } 1124 return fmt.Errorf("already set") 1125 } 1126 1127 // SetInstanceInfo is used to provision a machine and in one steps set it's 1128 // instance id, nonce, hardware characteristics, add link-layer devices and set 1129 // their addresses as needed. 1130 func (m *Machine) SetInstanceInfo( 1131 id instance.Id, nonce string, characteristics *instance.HardwareCharacteristics, 1132 devicesArgs []LinkLayerDeviceArgs, devicesAddrs []LinkLayerDeviceAddress, 1133 volumes map[names.VolumeTag]VolumeInfo, 1134 volumeAttachments map[names.VolumeTag]VolumeAttachmentInfo, 1135 ) error { 1136 1137 if err := m.SetParentLinkLayerDevicesBeforeTheirChildren(devicesArgs); err != nil { 1138 return errors.Trace(err) 1139 } 1140 if err := m.SetDevicesAddressesIdempotently(devicesAddrs); err != nil { 1141 return errors.Trace(err) 1142 } 1143 if err := setProvisionedVolumeInfo(m.st, volumes); err != nil { 1144 return errors.Trace(err) 1145 } 1146 if err := setMachineVolumeAttachmentInfo(m.st, m.Id(), volumeAttachments); err != nil { 1147 return errors.Trace(err) 1148 } 1149 return m.SetProvisioned(id, nonce, characteristics) 1150 } 1151 1152 // Addresses returns any hostnames and ips associated with a machine, 1153 // determined both by the machine itself, and by asking the provider. 1154 // 1155 // The addresses returned by the provider shadow any of the addresses 1156 // that the machine reported with the same address value. 1157 // Provider-reported addresses always come before machine-reported 1158 // addresses. Duplicates are removed. 1159 func (m *Machine) Addresses() (addresses []network.Address) { 1160 return network.MergedAddresses(networkAddresses(m.doc.MachineAddresses), networkAddresses(m.doc.Addresses)) 1161 } 1162 1163 func containsAddress(addresses []address, address address) bool { 1164 for _, addr := range addresses { 1165 if addr.Value == address.Value { 1166 return true 1167 } 1168 } 1169 return false 1170 } 1171 1172 // PublicAddress returns a public address for the machine. If no address is 1173 // available it returns an error that satisfies network.IsNoAddressError(). 1174 func (m *Machine) PublicAddress() (network.Address, error) { 1175 publicAddress := m.doc.PreferredPublicAddress.networkAddress() 1176 var err error 1177 if publicAddress.Value == "" { 1178 err = network.NoAddressError("public") 1179 } 1180 return publicAddress, err 1181 } 1182 1183 // maybeGetNewAddress determines if the current address is the most appropriate 1184 // match, and if not it selects the best from the slice of all available 1185 // addresses. It returns the new address and a bool indicating if a different 1186 // one was picked. 1187 func maybeGetNewAddress(addr address, providerAddresses, machineAddresses []address, getAddr func([]address) network.Address, checkScope func(address) bool) (address, bool) { 1188 // For picking the best address, try provider addresses first. 1189 var newAddr address 1190 netAddr := getAddr(providerAddresses) 1191 if netAddr.Value == "" { 1192 netAddr = getAddr(machineAddresses) 1193 newAddr = fromNetworkAddress(netAddr, OriginMachine) 1194 } else { 1195 newAddr = fromNetworkAddress(netAddr, OriginProvider) 1196 } 1197 // The order of these checks is important. If the stored address is 1198 // empty we *always* want to check for a new address so we do that 1199 // first. If the stored address is unavilable we also *must* check for 1200 // a new address so we do that next. If the original is a machine 1201 // address and a provider address is available we want to switch to 1202 // that. Finally we check to see if a better match on scope from the 1203 // same origin is available. 1204 if addr.Value == "" { 1205 return newAddr, newAddr.Value != "" 1206 } 1207 if !containsAddress(providerAddresses, addr) && !containsAddress(machineAddresses, addr) { 1208 return newAddr, true 1209 } 1210 if Origin(addr.Origin) != OriginProvider && Origin(newAddr.Origin) == OriginProvider { 1211 return newAddr, true 1212 } 1213 if !checkScope(addr) { 1214 // If addr.Origin is machine and newAddr.Origin is provider we will 1215 // have already caught that, and for the inverse we don't want to 1216 // replace the address. 1217 if addr.Origin == newAddr.Origin { 1218 return newAddr, checkScope(newAddr) 1219 } 1220 } 1221 return addr, false 1222 } 1223 1224 // PrivateAddress returns a private address for the machine. If no address is 1225 // available it returns an error that satisfies network.IsNoAddressError(). 1226 func (m *Machine) PrivateAddress() (network.Address, error) { 1227 privateAddress := m.doc.PreferredPrivateAddress.networkAddress() 1228 var err error 1229 if privateAddress.Value == "" { 1230 err = network.NoAddressError("private") 1231 } 1232 return privateAddress, err 1233 } 1234 1235 func (m *Machine) setPreferredAddressOps(addr address, isPublic bool) []txn.Op { 1236 fieldName := "preferredprivateaddress" 1237 current := m.doc.PreferredPrivateAddress 1238 if isPublic { 1239 fieldName = "preferredpublicaddress" 1240 current = m.doc.PreferredPublicAddress 1241 } 1242 // Assert that the field is either missing (never been set) or is 1243 // unchanged from its previous value. 1244 1245 // Since using a struct in the assert also asserts ordering, and we know that mgo 1246 // can change the ordering, we assert on the dotted values, effectively checking each 1247 // of the attributes of the address. 1248 currentD := []bson.D{ 1249 {{fieldName + ".value", current.Value}}, 1250 {{fieldName + ".addresstype", current.AddressType}}, 1251 } 1252 // Since scope, origin, and space have omitempty, we don't add them if they are empty. 1253 if current.Scope != "" { 1254 currentD = append(currentD, bson.D{{fieldName + ".networkscope", current.Scope}}) 1255 } 1256 if current.Origin != "" { 1257 currentD = append(currentD, bson.D{{fieldName + ".origin", current.Origin}}) 1258 } 1259 if current.SpaceName != "" { 1260 currentD = append(currentD, bson.D{{fieldName + ".spacename", current.SpaceName}}) 1261 } 1262 1263 assert := bson.D{{"$or", []bson.D{ 1264 {{"$and", currentD}}, 1265 {{fieldName, nil}}}}} 1266 1267 ops := []txn.Op{{ 1268 C: machinesC, 1269 Id: m.doc.DocID, 1270 Update: bson.D{{"$set", bson.D{{fieldName, addr}}}}, 1271 Assert: assert, 1272 }} 1273 return ops 1274 } 1275 1276 func (m *Machine) setPublicAddressOps(providerAddresses []address, machineAddresses []address) ([]txn.Op, address, bool) { 1277 publicAddress := m.doc.PreferredPublicAddress 1278 // Always prefer an exact match if available. 1279 checkScope := func(addr address) bool { 1280 return network.ExactScopeMatch(addr.networkAddress(), network.ScopePublic) 1281 } 1282 // Without an exact match, prefer a fallback match. 1283 getAddr := func(addresses []address) network.Address { 1284 addr, _ := network.SelectPublicAddress(networkAddresses(addresses)) 1285 return addr 1286 } 1287 1288 newAddr, changed := maybeGetNewAddress(publicAddress, providerAddresses, machineAddresses, getAddr, checkScope) 1289 if !changed { 1290 // No change, so no ops. 1291 return []txn.Op{}, publicAddress, false 1292 } 1293 1294 ops := m.setPreferredAddressOps(newAddr, true) 1295 return ops, newAddr, true 1296 } 1297 1298 func (m *Machine) setPrivateAddressOps(providerAddresses []address, machineAddresses []address) ([]txn.Op, address, bool) { 1299 privateAddress := m.doc.PreferredPrivateAddress 1300 // Always prefer an exact match if available. 1301 checkScope := func(addr address) bool { 1302 return network.ExactScopeMatch(addr.networkAddress(), network.ScopeMachineLocal, network.ScopeCloudLocal) 1303 } 1304 // Without an exact match, prefer a fallback match. 1305 getAddr := func(addresses []address) network.Address { 1306 addr, _ := network.SelectInternalAddress(networkAddresses(addresses), false) 1307 return addr 1308 } 1309 1310 newAddr, changed := maybeGetNewAddress(privateAddress, providerAddresses, machineAddresses, getAddr, checkScope) 1311 if !changed { 1312 // No change, so no ops. 1313 return []txn.Op{}, privateAddress, false 1314 } 1315 ops := m.setPreferredAddressOps(newAddr, false) 1316 return ops, newAddr, true 1317 } 1318 1319 // SetProviderAddresses records any addresses related to the machine, sourced 1320 // by asking the provider. 1321 func (m *Machine) SetProviderAddresses(addresses ...network.Address) (err error) { 1322 mdoc, err := m.st.getMachineDoc(m.Id()) 1323 if err != nil { 1324 return errors.Annotatef(err, "cannot refresh provider addresses for machine %s", m) 1325 } 1326 if err = m.setAddresses(addresses, &mdoc.Addresses, "addresses"); err != nil { 1327 return fmt.Errorf("cannot set addresses of machine %v: %v", m, err) 1328 } 1329 m.doc.Addresses = mdoc.Addresses 1330 return nil 1331 } 1332 1333 // ProviderAddresses returns any hostnames and ips associated with a machine, 1334 // as determined by asking the provider. 1335 func (m *Machine) ProviderAddresses() (addresses []network.Address) { 1336 for _, address := range m.doc.Addresses { 1337 addresses = append(addresses, address.networkAddress()) 1338 } 1339 return 1340 } 1341 1342 // MachineAddresses returns any hostnames and ips associated with a machine, 1343 // determined by asking the machine itself. 1344 func (m *Machine) MachineAddresses() (addresses []network.Address) { 1345 for _, address := range m.doc.MachineAddresses { 1346 addresses = append(addresses, address.networkAddress()) 1347 } 1348 return 1349 } 1350 1351 // SetMachineAddresses records any addresses related to the machine, sourced 1352 // by asking the machine. 1353 func (m *Machine) SetMachineAddresses(addresses ...network.Address) (err error) { 1354 mdoc, err := m.st.getMachineDoc(m.Id()) 1355 if err != nil { 1356 return errors.Annotatef(err, "cannot refresh machine addresses for machine %s", m) 1357 } 1358 if err = m.setAddresses(addresses, &mdoc.MachineAddresses, "machineaddresses"); err != nil { 1359 return fmt.Errorf("cannot set machine addresses of machine %v: %v", m, err) 1360 } 1361 m.doc.MachineAddresses = mdoc.MachineAddresses 1362 return nil 1363 } 1364 1365 // setAddresses updates the machine's addresses (either Addresses or 1366 // MachineAddresses, depending on the field argument). Changes are 1367 // only predicated on the machine not being Dead; concurrent address 1368 // changes are ignored. 1369 func (m *Machine) setAddresses(addresses []network.Address, field *[]address, fieldName string) error { 1370 addressesToSet := make([]network.Address, len(addresses)) 1371 copy(addressesToSet, addresses) 1372 1373 // Update addresses now. 1374 network.SortAddresses(addressesToSet) 1375 origin := OriginProvider 1376 if fieldName == "machineaddresses" { 1377 origin = OriginMachine 1378 } 1379 stateAddresses := fromNetworkAddresses(addressesToSet, origin) 1380 1381 var ( 1382 newPrivate, newPublic address 1383 changedPrivate, changedPublic bool 1384 err error 1385 ) 1386 machine := m 1387 buildTxn := func(attempt int) ([]txn.Op, error) { 1388 if attempt != 0 { 1389 if machine, err = machine.st.Machine(machine.doc.Id); err != nil { 1390 return nil, err 1391 } 1392 } 1393 if machine.doc.Life == Dead { 1394 return nil, errNotAlive 1395 } 1396 ops := []txn.Op{{ 1397 C: machinesC, 1398 Id: machine.doc.DocID, 1399 Assert: notDeadDoc, 1400 Update: bson.D{{"$set", bson.D{{fieldName, stateAddresses}}}}, 1401 }} 1402 1403 var providerAddresses []address 1404 var machineAddresses []address 1405 if fieldName == "machineaddresses" { 1406 providerAddresses = machine.doc.Addresses 1407 machineAddresses = stateAddresses 1408 } else { 1409 machineAddresses = machine.doc.MachineAddresses 1410 providerAddresses = stateAddresses 1411 } 1412 1413 var setPrivateAddressOps, setPublicAddressOps []txn.Op 1414 setPrivateAddressOps, newPrivate, changedPrivate = machine.setPrivateAddressOps(providerAddresses, machineAddresses) 1415 setPublicAddressOps, newPublic, changedPublic = machine.setPublicAddressOps(providerAddresses, machineAddresses) 1416 ops = append(ops, setPrivateAddressOps...) 1417 ops = append(ops, setPublicAddressOps...) 1418 return ops, nil 1419 } 1420 err = m.st.run(buildTxn) 1421 if err == txn.ErrAborted { 1422 return ErrDead 1423 } else if err != nil { 1424 return errors.Trace(err) 1425 } 1426 1427 *field = stateAddresses 1428 if changedPrivate { 1429 oldPrivate := m.doc.PreferredPrivateAddress.networkAddress() 1430 m.doc.PreferredPrivateAddress = newPrivate 1431 logger.Infof( 1432 "machine %q preferred private address changed from %q to %q", 1433 m.Id(), oldPrivate, newPrivate.networkAddress(), 1434 ) 1435 } 1436 if changedPublic { 1437 oldPublic := m.doc.PreferredPublicAddress.networkAddress() 1438 m.doc.PreferredPublicAddress = newPublic 1439 logger.Infof( 1440 "machine %q preferred public address changed from %q to %q", 1441 m.Id(), oldPublic, newPublic.networkAddress(), 1442 ) 1443 } 1444 return nil 1445 } 1446 1447 // CheckProvisioned returns true if the machine was provisioned with the given nonce. 1448 func (m *Machine) CheckProvisioned(nonce string) bool { 1449 return nonce == m.doc.Nonce && nonce != "" 1450 } 1451 1452 // String returns a unique description of this machine. 1453 func (m *Machine) String() string { 1454 return m.doc.Id 1455 } 1456 1457 // Placement returns the machine's Placement structure that should be used when 1458 // provisioning an instance for the machine. 1459 func (m *Machine) Placement() string { 1460 return m.doc.Placement 1461 } 1462 1463 // Constraints returns the exact constraints that should apply when provisioning 1464 // an instance for the machine. 1465 func (m *Machine) Constraints() (constraints.Value, error) { 1466 return readConstraints(m.st, m.globalKey()) 1467 } 1468 1469 // SetConstraints sets the exact constraints to apply when provisioning an 1470 // instance for the machine. It will fail if the machine is Dead, or if it 1471 // is already provisioned. 1472 func (m *Machine) SetConstraints(cons constraints.Value) (err error) { 1473 defer errors.DeferredAnnotatef(&err, "cannot set constraints") 1474 unsupported, err := m.st.validateConstraints(cons) 1475 if len(unsupported) > 0 { 1476 logger.Warningf( 1477 "setting constraints on machine %q: unsupported constraints: %v", m.Id(), strings.Join(unsupported, ",")) 1478 } else if err != nil { 1479 return err 1480 } 1481 notSetYet := bson.D{{"nonce", ""}} 1482 ops := []txn.Op{{ 1483 C: machinesC, 1484 Id: m.doc.DocID, 1485 Assert: append(isAliveDoc, notSetYet...), 1486 }} 1487 mcons, err := m.st.resolveMachineConstraints(cons) 1488 if err != nil { 1489 return err 1490 } 1491 1492 ops = append(ops, setConstraintsOp(m.st, m.globalKey(), mcons)) 1493 // make multiple attempts to push the ErrExcessiveContention case out of the 1494 // realm of plausibility: it implies local state indicating unprovisioned, 1495 // and remote state indicating provisioned (reasonable); but which changes 1496 // back to unprovisioned and then to provisioned again with *very* specific 1497 // timing in the course of this loop. 1498 buildTxn := func(attempt int) ([]txn.Op, error) { 1499 if attempt > 0 { 1500 if m, err = m.st.Machine(m.doc.Id); err != nil { 1501 return nil, err 1502 } 1503 } 1504 if m.doc.Life != Alive { 1505 return nil, errNotAlive 1506 } 1507 if _, err := m.InstanceId(); err == nil { 1508 return nil, fmt.Errorf("machine is already provisioned") 1509 } else if !errors.IsNotProvisioned(err) { 1510 return nil, err 1511 } 1512 return ops, nil 1513 } 1514 return m.st.run(buildTxn) 1515 } 1516 1517 // Status returns the status of the machine. 1518 func (m *Machine) Status() (status.StatusInfo, error) { 1519 mStatus, err := getStatus(m.st, m.globalKey(), "machine") 1520 if err != nil { 1521 return mStatus, err 1522 } 1523 return mStatus, nil 1524 } 1525 1526 // SetStatus sets the status of the machine. 1527 func (m *Machine) SetStatus(statusInfo status.StatusInfo) error { 1528 switch statusInfo.Status { 1529 case status.Started, status.Stopped: 1530 case status.Error: 1531 if statusInfo.Message == "" { 1532 return errors.Errorf("cannot set status %q without info", statusInfo.Status) 1533 } 1534 case status.Pending: 1535 // If a machine is not yet provisioned, we allow its status 1536 // to be set back to pending (when a retry is to occur). 1537 _, err := m.InstanceId() 1538 allowPending := errors.IsNotProvisioned(err) 1539 if allowPending { 1540 break 1541 } 1542 fallthrough 1543 case status.Down: 1544 return errors.Errorf("cannot set status %q", statusInfo.Status) 1545 default: 1546 return errors.Errorf("cannot set invalid status %q", statusInfo.Status) 1547 } 1548 return setStatus(m.st, setStatusParams{ 1549 badge: "machine", 1550 globalKey: m.globalKey(), 1551 status: statusInfo.Status, 1552 message: statusInfo.Message, 1553 rawData: statusInfo.Data, 1554 updated: statusInfo.Since, 1555 }) 1556 } 1557 1558 // StatusHistory returns a slice of at most filter.Size StatusInfo items 1559 // or items as old as filter.Date or items newer than now - filter.Delta time 1560 // representing past statuses for this machine. 1561 func (m *Machine) StatusHistory(filter status.StatusHistoryFilter) ([]status.StatusInfo, error) { 1562 args := &statusHistoryArgs{ 1563 st: m.st, 1564 globalKey: m.globalKey(), 1565 filter: filter, 1566 } 1567 return statusHistory(args) 1568 } 1569 1570 // Clean returns true if the machine does not have any deployed units or containers. 1571 func (m *Machine) Clean() bool { 1572 return m.doc.Clean 1573 } 1574 1575 // SupportedContainers returns any containers this machine is capable of hosting, and a bool 1576 // indicating if the supported containers have been determined or not. 1577 func (m *Machine) SupportedContainers() ([]instance.ContainerType, bool) { 1578 return m.doc.SupportedContainers, m.doc.SupportedContainersKnown 1579 } 1580 1581 // SupportsNoContainers records the fact that this machine doesn't support any containers. 1582 func (m *Machine) SupportsNoContainers() (err error) { 1583 if err = m.updateSupportedContainers([]instance.ContainerType{}); err != nil { 1584 return err 1585 } 1586 return m.markInvalidContainers() 1587 } 1588 1589 // SetSupportedContainers sets the list of containers supported by this machine. 1590 func (m *Machine) SetSupportedContainers(containers []instance.ContainerType) (err error) { 1591 if len(containers) == 0 { 1592 return fmt.Errorf("at least one valid container type is required") 1593 } 1594 for _, container := range containers { 1595 if container == instance.NONE { 1596 return fmt.Errorf("%q is not a valid container type", container) 1597 } 1598 } 1599 if err = m.updateSupportedContainers(containers); err != nil { 1600 return err 1601 } 1602 return m.markInvalidContainers() 1603 } 1604 1605 func isSupportedContainer(container instance.ContainerType, supportedContainers []instance.ContainerType) bool { 1606 for _, supportedContainer := range supportedContainers { 1607 if supportedContainer == container { 1608 return true 1609 } 1610 } 1611 return false 1612 } 1613 1614 // updateSupportedContainers sets the supported containers on this host machine. 1615 func (m *Machine) updateSupportedContainers(supportedContainers []instance.ContainerType) (err error) { 1616 ops := []txn.Op{ 1617 { 1618 C: machinesC, 1619 Id: m.doc.DocID, 1620 Assert: notDeadDoc, 1621 Update: bson.D{ 1622 {"$set", bson.D{ 1623 {"supportedcontainers", supportedContainers}, 1624 {"supportedcontainersknown", true}, 1625 }}}, 1626 }, 1627 } 1628 if err = m.st.runTransaction(ops); err != nil { 1629 err = onAbort(err, ErrDead) 1630 logger.Errorf("cannot update supported containers of machine %v: %v", m, err) 1631 return err 1632 } 1633 m.doc.SupportedContainers = supportedContainers 1634 m.doc.SupportedContainersKnown = true 1635 return nil 1636 } 1637 1638 // markInvalidContainers sets the status of any container belonging to this machine 1639 // as being in error if the container type is not supported. 1640 func (m *Machine) markInvalidContainers() error { 1641 currentContainers, err := m.Containers() 1642 if err != nil { 1643 return err 1644 } 1645 for _, containerId := range currentContainers { 1646 if !isSupportedContainer(ContainerTypeFromId(containerId), m.doc.SupportedContainers) { 1647 container, err := m.st.Machine(containerId) 1648 if err != nil { 1649 logger.Errorf("loading container %v to mark as invalid: %v", containerId, err) 1650 continue 1651 } 1652 // There should never be a circumstance where an unsupported container is started. 1653 // Nonetheless, we check and log an error if such a situation arises. 1654 statusInfo, err := container.Status() 1655 if err != nil { 1656 logger.Errorf("finding status of container %v to mark as invalid: %v", containerId, err) 1657 continue 1658 } 1659 if statusInfo.Status == status.Pending { 1660 containerType := ContainerTypeFromId(containerId) 1661 now := m.st.clock.Now() 1662 s := status.StatusInfo{ 1663 Status: status.Error, 1664 Message: "unsupported container", 1665 Data: map[string]interface{}{"type": containerType}, 1666 Since: &now, 1667 } 1668 container.SetStatus(s) 1669 } else { 1670 logger.Errorf("unsupported container %v has unexpected status %v", containerId, statusInfo.Status) 1671 } 1672 } 1673 } 1674 return nil 1675 } 1676 1677 // SetMachineBlockDevices sets the block devices visible on the machine. 1678 func (m *Machine) SetMachineBlockDevices(info ...BlockDeviceInfo) error { 1679 return setMachineBlockDevices(m.st, m.Id(), info) 1680 } 1681 1682 // VolumeAttachments returns the machine's volume attachments. 1683 func (m *Machine) VolumeAttachments() ([]VolumeAttachment, error) { 1684 return m.st.MachineVolumeAttachments(m.MachineTag()) 1685 } 1686 1687 // AddAction is part of the ActionReceiver interface. 1688 func (m *Machine) AddAction(name string, payload map[string]interface{}) (Action, error) { 1689 spec, ok := actions.PredefinedActionsSpec[name] 1690 if !ok { 1691 return nil, errors.Errorf("cannot add action %q to a machine; only predefined actions allowed", name) 1692 } 1693 1694 // Reject bad payloads before attempting to insert defaults. 1695 err := spec.ValidateParams(payload) 1696 if err != nil { 1697 return nil, err 1698 } 1699 payloadWithDefaults, err := spec.InsertDefaults(payload) 1700 if err != nil { 1701 return nil, err 1702 } 1703 return m.st.EnqueueAction(m.Tag(), name, payloadWithDefaults) 1704 } 1705 1706 // CancelAction is part of the ActionReceiver interface. 1707 func (m *Machine) CancelAction(action Action) (Action, error) { 1708 return action.Finish(ActionResults{Status: ActionCancelled}) 1709 } 1710 1711 // WatchActionNotifications is part of the ActionReceiver interface. 1712 func (m *Machine) WatchActionNotifications() StringsWatcher { 1713 return m.st.watchEnqueuedActionsFilteredBy(m) 1714 } 1715 1716 // Actions is part of the ActionReceiver interface. 1717 func (m *Machine) Actions() ([]Action, error) { 1718 return m.st.matchingActions(m) 1719 } 1720 1721 // CompletedActions is part of the ActionReceiver interface. 1722 func (m *Machine) CompletedActions() ([]Action, error) { 1723 return m.st.matchingActionsCompleted(m) 1724 } 1725 1726 // PendingActions is part of the ActionReceiver interface. 1727 func (m *Machine) PendingActions() ([]Action, error) { 1728 return m.st.matchingActionsPending(m) 1729 } 1730 1731 // RunningActions is part of the ActionReceiver interface. 1732 func (m *Machine) RunningActions() ([]Action, error) { 1733 return m.st.matchingActionsRunning(m) 1734 }