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