github.com/axw/juju@v0.0.0-20161005053422-4bd6544d08d4/state/migration_export.go (about) 1 // Copyright 2016 Canonical Ltd. 2 // Licensed under the AGPLv3, see LICENCE file for details. 3 4 package state 5 6 import ( 7 "strings" 8 "time" 9 10 "github.com/juju/errors" 11 "github.com/juju/loggo" 12 "github.com/juju/utils/set" 13 "gopkg.in/juju/names.v2" 14 "gopkg.in/mgo.v2/bson" 15 16 "github.com/juju/juju/core/description" 17 "github.com/juju/juju/payload" 18 "github.com/juju/juju/storage/poolmanager" 19 ) 20 21 // Export the current model for the State. 22 func (st *State) Export() (description.Model, error) { 23 dbModel, err := st.Model() 24 if err != nil { 25 return nil, errors.Trace(err) 26 } 27 28 export := exporter{ 29 st: st, 30 dbModel: dbModel, 31 logger: loggo.GetLogger("juju.state.export-model"), 32 } 33 if err := export.readAllStatuses(); err != nil { 34 return nil, errors.Annotate(err, "reading statuses") 35 } 36 if err := export.readAllStatusHistory(); err != nil { 37 return nil, errors.Trace(err) 38 } 39 if err := export.readAllSettings(); err != nil { 40 return nil, errors.Trace(err) 41 } 42 if err := export.readAllStorageConstraints(); err != nil { 43 return nil, errors.Trace(err) 44 } 45 if err := export.readAllAnnotations(); err != nil { 46 return nil, errors.Trace(err) 47 } 48 if err := export.readAllConstraints(); err != nil { 49 return nil, errors.Trace(err) 50 } 51 52 modelConfig, found := export.modelSettings[modelGlobalKey] 53 if !found { 54 return nil, errors.New("missing model config") 55 } 56 57 blocks, err := export.readBlocks() 58 if err != nil { 59 return nil, errors.Trace(err) 60 } 61 62 args := description.ModelArgs{ 63 Cloud: dbModel.Cloud(), 64 CloudRegion: dbModel.CloudRegion(), 65 Owner: dbModel.Owner(), 66 Config: modelConfig.Settings, 67 LatestToolsVersion: dbModel.LatestToolsVersion(), 68 Blocks: blocks, 69 } 70 export.model = description.NewModel(args) 71 modelKey := dbModel.globalKey() 72 export.model.SetAnnotations(export.getAnnotations(modelKey)) 73 if err := export.sequences(); err != nil { 74 return nil, errors.Trace(err) 75 } 76 constraintsArgs, err := export.constraintsArgs(modelKey) 77 if err != nil { 78 return nil, errors.Trace(err) 79 } 80 export.model.SetConstraints(constraintsArgs) 81 82 if err := export.modelUsers(); err != nil { 83 return nil, errors.Trace(err) 84 } 85 if err := export.machines(); err != nil { 86 return nil, errors.Trace(err) 87 } 88 if err := export.applications(); err != nil { 89 return nil, errors.Trace(err) 90 } 91 if err := export.relations(); err != nil { 92 return nil, errors.Trace(err) 93 } 94 if err := export.spaces(); err != nil { 95 return nil, errors.Trace(err) 96 } 97 if err := export.subnets(); err != nil { 98 return nil, errors.Trace(err) 99 } 100 101 if err := export.ipaddresses(); err != nil { 102 return nil, errors.Trace(err) 103 } 104 105 if err := export.linklayerdevices(); err != nil { 106 return nil, errors.Trace(err) 107 } 108 109 if err := export.sshHostKeys(); err != nil { 110 return nil, errors.Trace(err) 111 } 112 if err := export.storage(); err != nil { 113 return nil, errors.Trace(err) 114 } 115 116 if err := export.actions(); err != nil { 117 return nil, errors.Trace(err) 118 } 119 120 if err := export.cloudimagemetadata(); err != nil { 121 return nil, errors.Trace(err) 122 } 123 124 if err := export.model.Validate(); err != nil { 125 return nil, errors.Trace(err) 126 } 127 128 export.logExtras() 129 130 return export.model, nil 131 } 132 133 type exporter struct { 134 st *State 135 dbModel *Model 136 model description.Model 137 logger loggo.Logger 138 139 annotations map[string]annotatorDoc 140 constraints map[string]bson.M 141 modelSettings map[string]settingsDoc 142 modelStorageConstraints map[string]storageConstraintsDoc 143 status map[string]bson.M 144 statusHistory map[string][]historicalStatusDoc 145 // Map of application name to units. Populated as part 146 // of the applications export. 147 units map[string][]*Unit 148 } 149 150 func (e *exporter) sequences() error { 151 sequences, closer := e.st.getCollection(sequenceC) 152 defer closer() 153 154 var docs []sequenceDoc 155 if err := sequences.Find(nil).All(&docs); err != nil { 156 return errors.Trace(err) 157 } 158 159 for _, doc := range docs { 160 e.model.SetSequence(doc.Name, doc.Counter) 161 } 162 return nil 163 } 164 165 func (e *exporter) readBlocks() (map[string]string, error) { 166 blocks, closer := e.st.getCollection(blocksC) 167 defer closer() 168 169 var docs []blockDoc 170 if err := blocks.Find(nil).All(&docs); err != nil { 171 return nil, errors.Trace(err) 172 } 173 174 result := make(map[string]string) 175 for _, doc := range docs { 176 // We don't care about the id, uuid, or tag. 177 // The uuid and tag both refer to the model uuid, and the 178 // id is opaque - even though it is sequence generated. 179 result[doc.Type.MigrationValue()] = doc.Message 180 } 181 return result, nil 182 } 183 184 func (e *exporter) modelUsers() error { 185 users, err := e.dbModel.Users() 186 if err != nil { 187 return errors.Trace(err) 188 } 189 lastConnections, err := e.readLastConnectionTimes() 190 if err != nil { 191 return errors.Trace(err) 192 } 193 for _, user := range users { 194 lastConn := lastConnections[strings.ToLower(user.UserName)] 195 arg := description.UserArgs{ 196 Name: user.UserTag, 197 DisplayName: user.DisplayName, 198 CreatedBy: user.CreatedBy, 199 DateCreated: user.DateCreated, 200 LastConnection: lastConn, 201 Access: string(user.Access), 202 } 203 e.model.AddUser(arg) 204 } 205 return nil 206 } 207 208 func (e *exporter) machines() error { 209 machines, err := e.st.AllMachines() 210 if err != nil { 211 return errors.Trace(err) 212 } 213 e.logger.Debugf("found %d machines", len(machines)) 214 215 instances, err := e.loadMachineInstanceData() 216 if err != nil { 217 return errors.Trace(err) 218 } 219 blockDevices, err := e.loadMachineBlockDevices() 220 if err != nil { 221 return errors.Trace(err) 222 } 223 224 // Read all the open ports documents. 225 openedPorts, closer := e.st.getCollection(openedPortsC) 226 defer closer() 227 var portsData []portsDoc 228 if err := openedPorts.Find(nil).All(&portsData); err != nil { 229 return errors.Annotate(err, "opened ports") 230 } 231 e.logger.Debugf("found %d openedPorts docs", len(portsData)) 232 233 // We are iterating through a flat list of machines, but the migration 234 // model stores the nesting. The AllMachines method assures us that the 235 // machines are returned in an order so the parent will always before 236 // any children. 237 machineMap := make(map[string]description.Machine) 238 239 for _, machine := range machines { 240 e.logger.Debugf("export machine %s", machine.Id()) 241 242 var exParent description.Machine 243 if parentId := ParentId(machine.Id()); parentId != "" { 244 var found bool 245 exParent, found = machineMap[parentId] 246 if !found { 247 return errors.Errorf("machine %s missing parent", machine.Id()) 248 } 249 } 250 251 exMachine, err := e.newMachine(exParent, machine, instances, portsData, blockDevices) 252 if err != nil { 253 return errors.Trace(err) 254 } 255 machineMap[machine.Id()] = exMachine 256 } 257 258 return nil 259 } 260 261 func (e *exporter) loadMachineInstanceData() (map[string]instanceData, error) { 262 instanceDataCollection, closer := e.st.getCollection(instanceDataC) 263 defer closer() 264 265 var instData []instanceData 266 instances := make(map[string]instanceData) 267 if err := instanceDataCollection.Find(nil).All(&instData); err != nil { 268 return nil, errors.Annotate(err, "instance data") 269 } 270 e.logger.Debugf("found %d instanceData", len(instData)) 271 for _, data := range instData { 272 instances[data.MachineId] = data 273 } 274 return instances, nil 275 } 276 277 func (e *exporter) loadMachineBlockDevices() (map[string][]BlockDeviceInfo, error) { 278 coll, closer := e.st.getCollection(blockDevicesC) 279 defer closer() 280 281 var deviceData []blockDevicesDoc 282 result := make(map[string][]BlockDeviceInfo) 283 if err := coll.Find(nil).All(&deviceData); err != nil { 284 return nil, errors.Annotate(err, "block devices") 285 } 286 e.logger.Debugf("found %d block device records", len(deviceData)) 287 for _, data := range deviceData { 288 result[data.Machine] = data.BlockDevices 289 } 290 return result, nil 291 } 292 293 func (e *exporter) newMachine(exParent description.Machine, machine *Machine, instances map[string]instanceData, portsData []portsDoc, blockDevices map[string][]BlockDeviceInfo) (description.Machine, error) { 294 args := description.MachineArgs{ 295 Id: machine.MachineTag(), 296 Nonce: machine.doc.Nonce, 297 PasswordHash: machine.doc.PasswordHash, 298 Placement: machine.doc.Placement, 299 Series: machine.doc.Series, 300 ContainerType: machine.doc.ContainerType, 301 } 302 303 if supported, ok := machine.SupportedContainers(); ok { 304 containers := make([]string, len(supported)) 305 for i, containerType := range supported { 306 containers[i] = string(containerType) 307 } 308 args.SupportedContainers = &containers 309 } 310 311 for _, job := range machine.Jobs() { 312 args.Jobs = append(args.Jobs, job.MigrationValue()) 313 } 314 315 // A null value means that we don't yet know which containers 316 // are supported. An empty slice means 'no containers are supported'. 317 var exMachine description.Machine 318 if exParent == nil { 319 exMachine = e.model.AddMachine(args) 320 } else { 321 exMachine = exParent.AddContainer(args) 322 } 323 exMachine.SetAddresses( 324 e.newAddressArgsSlice(machine.doc.MachineAddresses), 325 e.newAddressArgsSlice(machine.doc.Addresses)) 326 exMachine.SetPreferredAddresses( 327 e.newAddressArgs(machine.doc.PreferredPublicAddress), 328 e.newAddressArgs(machine.doc.PreferredPrivateAddress)) 329 330 // We fully expect the machine to have tools set, and that there is 331 // some instance data. 332 instData, found := instances[machine.doc.Id] 333 if !found { 334 return nil, errors.NotValidf("missing instance data for machine %s", machine.Id()) 335 } 336 exMachine.SetInstance(e.newCloudInstanceArgs(instData)) 337 338 // We don't rely on devices being there. If they aren't, we get an empty slice, 339 // which is fine to iterate over with range. 340 for _, device := range blockDevices[machine.doc.Id] { 341 exMachine.AddBlockDevice(description.BlockDeviceArgs{ 342 Name: device.DeviceName, 343 Links: device.DeviceLinks, 344 Label: device.Label, 345 UUID: device.UUID, 346 HardwareID: device.HardwareId, 347 BusAddress: device.BusAddress, 348 Size: device.Size, 349 FilesystemType: device.FilesystemType, 350 InUse: device.InUse, 351 MountPoint: device.MountPoint, 352 }) 353 } 354 355 // Find the current machine status. 356 globalKey := machine.globalKey() 357 statusArgs, err := e.statusArgs(globalKey) 358 if err != nil { 359 return nil, errors.Annotatef(err, "status for machine %s", machine.Id()) 360 } 361 exMachine.SetStatus(statusArgs) 362 exMachine.SetStatusHistory(e.statusHistoryArgs(globalKey)) 363 364 tools, err := machine.AgentTools() 365 if err != nil { 366 // This means the tools aren't set, but they should be. 367 return nil, errors.Trace(err) 368 } 369 370 exMachine.SetTools(description.AgentToolsArgs{ 371 Version: tools.Version, 372 URL: tools.URL, 373 SHA256: tools.SHA256, 374 Size: tools.Size, 375 }) 376 377 for _, args := range e.openedPortsArgsForMachine(machine.Id(), portsData) { 378 exMachine.AddOpenedPorts(args) 379 } 380 381 exMachine.SetAnnotations(e.getAnnotations(globalKey)) 382 383 constraintsArgs, err := e.constraintsArgs(globalKey) 384 if err != nil { 385 return nil, errors.Trace(err) 386 } 387 exMachine.SetConstraints(constraintsArgs) 388 389 return exMachine, nil 390 } 391 392 func (e *exporter) openedPortsArgsForMachine(machineId string, portsData []portsDoc) []description.OpenedPortsArgs { 393 var result []description.OpenedPortsArgs 394 for _, doc := range portsData { 395 // Don't bother including a subnet if there are no ports open on it. 396 if doc.MachineID == machineId && len(doc.Ports) > 0 { 397 args := description.OpenedPortsArgs{SubnetID: doc.SubnetID} 398 for _, p := range doc.Ports { 399 args.OpenedPorts = append(args.OpenedPorts, description.PortRangeArgs{ 400 UnitName: p.UnitName, 401 FromPort: p.FromPort, 402 ToPort: p.ToPort, 403 Protocol: p.Protocol, 404 }) 405 } 406 result = append(result, args) 407 } 408 } 409 return result 410 } 411 412 func (e *exporter) newAddressArgsSlice(a []address) []description.AddressArgs { 413 result := []description.AddressArgs{} 414 for _, addr := range a { 415 result = append(result, e.newAddressArgs(addr)) 416 } 417 return result 418 } 419 420 func (e *exporter) newAddressArgs(a address) description.AddressArgs { 421 return description.AddressArgs{ 422 Value: a.Value, 423 Type: a.AddressType, 424 Scope: a.Scope, 425 Origin: a.Origin, 426 } 427 } 428 429 func (e *exporter) newCloudInstanceArgs(data instanceData) description.CloudInstanceArgs { 430 inst := description.CloudInstanceArgs{ 431 InstanceId: string(data.InstanceId), 432 Status: data.Status, 433 } 434 if data.Arch != nil { 435 inst.Architecture = *data.Arch 436 } 437 if data.Mem != nil { 438 inst.Memory = *data.Mem 439 } 440 if data.RootDisk != nil { 441 inst.RootDisk = *data.RootDisk 442 } 443 if data.CpuCores != nil { 444 inst.CpuCores = *data.CpuCores 445 } 446 if data.CpuPower != nil { 447 inst.CpuPower = *data.CpuPower 448 } 449 if data.Tags != nil { 450 inst.Tags = *data.Tags 451 } 452 if data.AvailZone != nil { 453 inst.AvailabilityZone = *data.AvailZone 454 } 455 return inst 456 } 457 458 func (e *exporter) applications() error { 459 applications, err := e.st.AllApplications() 460 if err != nil { 461 return errors.Trace(err) 462 } 463 e.logger.Debugf("found %d applications", len(applications)) 464 465 e.units, err = e.readAllUnits() 466 if err != nil { 467 return errors.Trace(err) 468 } 469 470 meterStatus, err := e.readAllMeterStatus() 471 if err != nil { 472 return errors.Trace(err) 473 } 474 475 leaders, err := e.st.ApplicationLeaders() 476 if err != nil { 477 return errors.Trace(err) 478 } 479 480 payloads, err := e.readAllPayloads() 481 if err != nil { 482 return errors.Trace(err) 483 } 484 485 for _, application := range applications { 486 applicationUnits := e.units[application.Name()] 487 leader := leaders[application.Name()] 488 if err := e.addApplication(addApplicationContext{ 489 application: application, 490 units: applicationUnits, 491 meterStatus: meterStatus, 492 leader: leader, 493 payloads: payloads, 494 }); err != nil { 495 return errors.Trace(err) 496 } 497 } 498 return nil 499 } 500 501 func (e *exporter) readAllStorageConstraints() error { 502 coll, closer := e.st.getCollection(storageConstraintsC) 503 defer closer() 504 505 storageConstraints := make(map[string]storageConstraintsDoc) 506 var doc storageConstraintsDoc 507 iter := coll.Find(nil).Iter() 508 defer iter.Close() 509 for iter.Next(&doc) { 510 storageConstraints[e.st.localID(doc.DocID)] = doc 511 } 512 if err := iter.Err(); err != nil { 513 return errors.Annotate(err, "failed to read storage constraints") 514 } 515 e.logger.Debugf("read %d storage constraint documents", len(storageConstraints)) 516 e.modelStorageConstraints = storageConstraints 517 return nil 518 } 519 520 func (e *exporter) storageConstraints(doc storageConstraintsDoc) map[string]description.StorageConstraintArgs { 521 result := make(map[string]description.StorageConstraintArgs) 522 for key, value := range doc.Constraints { 523 result[key] = description.StorageConstraintArgs{ 524 Pool: value.Pool, 525 Size: value.Size, 526 Count: value.Count, 527 } 528 } 529 return result 530 } 531 532 func (e *exporter) readAllPayloads() (map[string][]payload.FullPayloadInfo, error) { 533 result := make(map[string][]payload.FullPayloadInfo) 534 all, err := ModelPayloads{db: e.st.database}.ListAll() 535 if err != nil { 536 return nil, errors.Trace(err) 537 } 538 for _, payload := range all { 539 result[payload.Unit] = append(result[payload.Unit], payload) 540 } 541 return result, nil 542 } 543 544 type addApplicationContext struct { 545 application *Application 546 units []*Unit 547 meterStatus map[string]*meterStatusDoc 548 leader string 549 payloads map[string][]payload.FullPayloadInfo 550 } 551 552 func (e *exporter) addApplication(ctx addApplicationContext) error { 553 application := ctx.application 554 appName := application.Name() 555 settingsKey := application.settingsKey() 556 leadershipKey := leadershipSettingsKey(appName) 557 storageConstraintsKey := application.storageConstraintsKey() 558 559 applicationSettingsDoc, found := e.modelSettings[settingsKey] 560 if !found { 561 return errors.Errorf("missing settings for application %q", appName) 562 } 563 leadershipSettingsDoc, found := e.modelSettings[leadershipKey] 564 if !found { 565 return errors.Errorf("missing leadership settings for application %q", appName) 566 } 567 568 args := description.ApplicationArgs{ 569 Tag: application.ApplicationTag(), 570 Series: application.doc.Series, 571 Subordinate: application.doc.Subordinate, 572 CharmURL: application.doc.CharmURL.String(), 573 Channel: application.doc.Channel, 574 CharmModifiedVersion: application.doc.CharmModifiedVersion, 575 ForceCharm: application.doc.ForceCharm, 576 Exposed: application.doc.Exposed, 577 MinUnits: application.doc.MinUnits, 578 Settings: applicationSettingsDoc.Settings, 579 Leader: ctx.leader, 580 LeadershipSettings: leadershipSettingsDoc.Settings, 581 MetricsCredentials: application.doc.MetricCredentials, 582 } 583 if constraints, found := e.modelStorageConstraints[storageConstraintsKey]; found { 584 args.StorageConstraints = e.storageConstraints(constraints) 585 } 586 exApplication := e.model.AddApplication(args) 587 // Find the current application status. 588 globalKey := application.globalKey() 589 statusArgs, err := e.statusArgs(globalKey) 590 if err != nil { 591 return errors.Annotatef(err, "status for application %s", appName) 592 } 593 exApplication.SetStatus(statusArgs) 594 exApplication.SetStatusHistory(e.statusHistoryArgs(globalKey)) 595 exApplication.SetAnnotations(e.getAnnotations(globalKey)) 596 597 constraintsArgs, err := e.constraintsArgs(globalKey) 598 if err != nil { 599 return errors.Trace(err) 600 } 601 exApplication.SetConstraints(constraintsArgs) 602 603 for _, unit := range ctx.units { 604 agentKey := unit.globalAgentKey() 605 unitMeterStatus, found := ctx.meterStatus[agentKey] 606 if !found { 607 return errors.Errorf("missing meter status for unit %s", unit.Name()) 608 } 609 610 workloadVersion, err := unit.WorkloadVersion() 611 if err != nil { 612 return errors.Trace(err) 613 } 614 args := description.UnitArgs{ 615 Tag: unit.UnitTag(), 616 Machine: names.NewMachineTag(unit.doc.MachineId), 617 WorkloadVersion: workloadVersion, 618 PasswordHash: unit.doc.PasswordHash, 619 MeterStatusCode: unitMeterStatus.Code, 620 MeterStatusInfo: unitMeterStatus.Info, 621 } 622 if principalName, isSubordinate := unit.PrincipalName(); isSubordinate { 623 args.Principal = names.NewUnitTag(principalName) 624 } 625 if subs := unit.SubordinateNames(); len(subs) > 0 { 626 for _, subName := range subs { 627 args.Subordinates = append(args.Subordinates, names.NewUnitTag(subName)) 628 } 629 } 630 exUnit := exApplication.AddUnit(args) 631 632 if err := e.setUnitPayloads(exUnit, ctx.payloads[unit.UnitTag().Id()]); err != nil { 633 return errors.Trace(err) 634 } 635 636 // workload uses globalKey, agent uses globalAgentKey, 637 // workload version uses globalWorkloadVersionKey. 638 globalKey := unit.globalKey() 639 statusArgs, err := e.statusArgs(globalKey) 640 if err != nil { 641 return errors.Annotatef(err, "workload status for unit %s", unit.Name()) 642 } 643 exUnit.SetWorkloadStatus(statusArgs) 644 exUnit.SetWorkloadStatusHistory(e.statusHistoryArgs(globalKey)) 645 646 statusArgs, err = e.statusArgs(agentKey) 647 if err != nil { 648 return errors.Annotatef(err, "agent status for unit %s", unit.Name()) 649 } 650 exUnit.SetAgentStatus(statusArgs) 651 exUnit.SetAgentStatusHistory(e.statusHistoryArgs(agentKey)) 652 653 workloadVersionKey := unit.globalWorkloadVersionKey() 654 exUnit.SetWorkloadVersionHistory(e.statusHistoryArgs(workloadVersionKey)) 655 656 tools, err := unit.AgentTools() 657 if err != nil { 658 // This means the tools aren't set, but they should be. 659 return errors.Trace(err) 660 } 661 exUnit.SetTools(description.AgentToolsArgs{ 662 Version: tools.Version, 663 URL: tools.URL, 664 SHA256: tools.SHA256, 665 Size: tools.Size, 666 }) 667 exUnit.SetAnnotations(e.getAnnotations(globalKey)) 668 669 constraintsArgs, err := e.constraintsArgs(agentKey) 670 if err != nil { 671 return errors.Trace(err) 672 } 673 exUnit.SetConstraints(constraintsArgs) 674 } 675 676 return nil 677 } 678 679 func (e *exporter) setUnitPayloads(exUnit description.Unit, payloads []payload.FullPayloadInfo) error { 680 unitID := exUnit.Tag().Id() 681 machineID := exUnit.Machine().Id() 682 for _, payload := range payloads { 683 if payload.Machine != machineID { 684 return errors.NotValidf("payload for unit %q reports wrong machine %q (should be %q)", unitID, payload.Machine, machineID) 685 } 686 args := description.PayloadArgs{ 687 Name: payload.Name, 688 Type: payload.Type, 689 RawID: payload.ID, 690 State: payload.Status, 691 Labels: payload.Labels, 692 } 693 exUnit.AddPayload(args) 694 } 695 return nil 696 } 697 698 func (e *exporter) relations() error { 699 rels, err := e.st.AllRelations() 700 if err != nil { 701 return errors.Trace(err) 702 } 703 e.logger.Debugf("read %d relations", len(rels)) 704 705 relationScopes, err := e.readAllRelationScopes() 706 if err != nil { 707 return errors.Trace(err) 708 } 709 710 for _, relation := range rels { 711 exRelation := e.model.AddRelation(description.RelationArgs{ 712 Id: relation.Id(), 713 Key: relation.String(), 714 }) 715 for _, ep := range relation.Endpoints() { 716 exEndPoint := exRelation.AddEndpoint(description.EndpointArgs{ 717 ApplicationName: ep.ApplicationName, 718 Name: ep.Name, 719 Role: string(ep.Role), 720 Interface: ep.Interface, 721 Optional: ep.Optional, 722 Limit: ep.Limit, 723 Scope: string(ep.Scope), 724 }) 725 // We expect a relationScope and settings for each of the 726 // units of the specified application. 727 units := e.units[ep.ApplicationName] 728 for _, unit := range units { 729 ru, err := relation.Unit(unit) 730 if err != nil { 731 return errors.Trace(err) 732 } 733 key := ru.key() 734 if !relationScopes.Contains(key) { 735 return errors.Errorf("missing relation scope for %s and %s", relation, unit.Name()) 736 } 737 settingsDoc, found := e.modelSettings[key] 738 if !found { 739 return errors.Errorf("missing relation settings for %s and %s", relation, unit.Name()) 740 } 741 exEndPoint.SetUnitSettings(unit.Name(), settingsDoc.Settings) 742 } 743 } 744 } 745 return nil 746 } 747 748 func (e *exporter) spaces() error { 749 spaces, err := e.st.AllSpaces() 750 if err != nil { 751 return errors.Trace(err) 752 } 753 e.logger.Debugf("read %d spaces", len(spaces)) 754 755 for _, space := range spaces { 756 e.model.AddSpace(description.SpaceArgs{ 757 Name: space.Name(), 758 Public: space.IsPublic(), 759 ProviderID: string(space.ProviderId()), 760 }) 761 } 762 return nil 763 } 764 765 func (e *exporter) linklayerdevices() error { 766 linklayerdevices, err := e.st.AllLinkLayerDevices() 767 if err != nil { 768 return errors.Trace(err) 769 } 770 e.logger.Debugf("read %d ip devices", len(linklayerdevices)) 771 for _, device := range linklayerdevices { 772 e.model.AddLinkLayerDevice(description.LinkLayerDeviceArgs{ 773 ProviderID: string(device.ProviderID()), 774 MachineID: device.MachineID(), 775 Name: device.Name(), 776 MTU: device.MTU(), 777 Type: string(device.Type()), 778 MACAddress: device.MACAddress(), 779 IsAutoStart: device.IsAutoStart(), 780 IsUp: device.IsUp(), 781 ParentName: device.ParentName(), 782 }) 783 } 784 return nil 785 } 786 787 func (e *exporter) subnets() error { 788 subnets, err := e.st.AllSubnets() 789 if err != nil { 790 return errors.Trace(err) 791 } 792 e.logger.Debugf("read %d subnets", len(subnets)) 793 794 for _, subnet := range subnets { 795 e.model.AddSubnet(description.SubnetArgs{ 796 CIDR: subnet.CIDR(), 797 ProviderId: string(subnet.ProviderId()), 798 VLANTag: subnet.VLANTag(), 799 AvailabilityZone: subnet.AvailabilityZone(), 800 SpaceName: subnet.SpaceName(), 801 }) 802 } 803 return nil 804 } 805 806 func (e *exporter) ipaddresses() error { 807 ipaddresses, err := e.st.AllIPAddresses() 808 if err != nil { 809 return errors.Trace(err) 810 } 811 e.logger.Debugf("read %d ip addresses", len(ipaddresses)) 812 for _, addr := range ipaddresses { 813 e.model.AddIPAddress(description.IPAddressArgs{ 814 ProviderID: string(addr.ProviderID()), 815 DeviceName: addr.DeviceName(), 816 MachineID: addr.MachineID(), 817 SubnetCIDR: addr.SubnetCIDR(), 818 ConfigMethod: string(addr.ConfigMethod()), 819 Value: addr.Value(), 820 DNSServers: addr.DNSServers(), 821 DNSSearchDomains: addr.DNSSearchDomains(), 822 GatewayAddress: addr.GatewayAddress(), 823 }) 824 } 825 return nil 826 } 827 828 func (e *exporter) sshHostKeys() error { 829 machines, err := e.st.AllMachines() 830 if err != nil { 831 return errors.Trace(err) 832 } 833 for _, machine := range machines { 834 keys, err := e.st.GetSSHHostKeys(machine.MachineTag()) 835 if errors.IsNotFound(err) { 836 continue 837 } else if err != nil { 838 return errors.Trace(err) 839 } 840 if len(keys) == 0 { 841 continue 842 } 843 e.model.AddSSHHostKey(description.SSHHostKeyArgs{ 844 MachineID: machine.Id(), 845 Keys: keys, 846 }) 847 } 848 return nil 849 } 850 851 func (e *exporter) cloudimagemetadata() error { 852 cloudimagemetadata, err := e.st.CloudImageMetadataStorage.AllCloudImageMetadata() 853 if err != nil { 854 return errors.Trace(err) 855 } 856 e.logger.Debugf("read %d cloudimagemetadata", len(cloudimagemetadata)) 857 for _, metadata := range cloudimagemetadata { 858 e.model.AddCloudImageMetadata(description.CloudImageMetadataArgs{ 859 Stream: metadata.Stream, 860 Region: metadata.Region, 861 Version: metadata.Version, 862 Series: metadata.Series, 863 Arch: metadata.Arch, 864 VirtType: metadata.VirtType, 865 RootStorageType: metadata.RootStorageType, 866 RootStorageSize: metadata.RootStorageSize, 867 DateCreated: metadata.DateCreated, 868 Source: metadata.Source, 869 Priority: metadata.Priority, 870 ImageId: metadata.ImageId, 871 }) 872 } 873 return nil 874 } 875 876 func (e *exporter) actions() error { 877 actions, err := e.st.AllActions() 878 if err != nil { 879 return errors.Trace(err) 880 } 881 e.logger.Debugf("read %d actions", len(actions)) 882 for _, action := range actions { 883 results, message := action.Results() 884 e.model.AddAction(description.ActionArgs{ 885 Receiver: action.Receiver(), 886 Name: action.Name(), 887 Parameters: action.Parameters(), 888 Enqueued: action.Enqueued(), 889 Started: action.Started(), 890 Completed: action.Completed(), 891 Status: string(action.Status()), 892 Results: results, 893 Message: message, 894 Id: action.Id(), 895 }) 896 } 897 return nil 898 } 899 900 func (e *exporter) readAllRelationScopes() (set.Strings, error) { 901 relationScopes, closer := e.st.getCollection(relationScopesC) 902 defer closer() 903 904 docs := []relationScopeDoc{} 905 err := relationScopes.Find(nil).All(&docs) 906 if err != nil { 907 return nil, errors.Annotate(err, "cannot get all relation scopes") 908 } 909 e.logger.Debugf("found %d relationScope docs", len(docs)) 910 911 result := set.NewStrings() 912 for _, doc := range docs { 913 result.Add(doc.Key) 914 } 915 return result, nil 916 } 917 918 func (e *exporter) readAllUnits() (map[string][]*Unit, error) { 919 unitsCollection, closer := e.st.getCollection(unitsC) 920 defer closer() 921 922 docs := []unitDoc{} 923 err := unitsCollection.Find(nil).All(&docs) 924 if err != nil { 925 return nil, errors.Annotate(err, "cannot get all units") 926 } 927 e.logger.Debugf("found %d unit docs", len(docs)) 928 result := make(map[string][]*Unit) 929 for _, doc := range docs { 930 units := result[doc.Application] 931 result[doc.Application] = append(units, newUnit(e.st, &doc)) 932 } 933 return result, nil 934 } 935 936 func (e *exporter) readAllMeterStatus() (map[string]*meterStatusDoc, error) { 937 meterStatuses, closer := e.st.getCollection(meterStatusC) 938 defer closer() 939 940 docs := []meterStatusDoc{} 941 err := meterStatuses.Find(nil).All(&docs) 942 if err != nil { 943 return nil, errors.Annotate(err, "cannot get all meter status docs") 944 } 945 e.logger.Debugf("found %d meter status docs", len(docs)) 946 result := make(map[string]*meterStatusDoc) 947 for _, doc := range docs { 948 result[e.st.localID(doc.DocID)] = &doc 949 } 950 return result, nil 951 } 952 953 func (e *exporter) readLastConnectionTimes() (map[string]time.Time, error) { 954 lastConnections, closer := e.st.getCollection(modelUserLastConnectionC) 955 defer closer() 956 957 var docs []modelUserLastConnectionDoc 958 if err := lastConnections.Find(nil).All(&docs); err != nil { 959 return nil, errors.Trace(err) 960 } 961 962 result := make(map[string]time.Time) 963 for _, doc := range docs { 964 result[doc.UserName] = doc.LastConnection.UTC() 965 } 966 return result, nil 967 } 968 969 func (e *exporter) readAllAnnotations() error { 970 annotations, closer := e.st.getCollection(annotationsC) 971 defer closer() 972 973 var docs []annotatorDoc 974 if err := annotations.Find(nil).All(&docs); err != nil { 975 return errors.Trace(err) 976 } 977 e.logger.Debugf("read %d annotations docs", len(docs)) 978 979 e.annotations = make(map[string]annotatorDoc) 980 for _, doc := range docs { 981 e.annotations[doc.GlobalKey] = doc 982 } 983 return nil 984 } 985 986 func (e *exporter) readAllConstraints() error { 987 constraintsCollection, closer := e.st.getCollection(constraintsC) 988 defer closer() 989 990 // Since the constraintsDoc doesn't include any global key or _id 991 // fields, we can't just deserialize the entire collection into a slice 992 // of docs, so we get them all out with bson maps. 993 var docs []bson.M 994 err := constraintsCollection.Find(nil).All(&docs) 995 if err != nil { 996 return errors.Annotate(err, "failed to read constraints collection") 997 } 998 999 e.logger.Debugf("read %d constraints docs", len(docs)) 1000 e.constraints = make(map[string]bson.M) 1001 for _, doc := range docs { 1002 docId, ok := doc["_id"].(string) 1003 if !ok { 1004 return errors.Errorf("expected string, got %s (%T)", doc["_id"], doc["_id"]) 1005 } 1006 id := e.st.localID(docId) 1007 e.constraints[id] = doc 1008 e.logger.Debugf("doc[%q] = %#v", id, doc) 1009 } 1010 return nil 1011 } 1012 1013 // getAnnotations doesn't really care if there are any there or not 1014 // for the key, but if they were there, they are removed so we can 1015 // check at the end of the export for anything we have forgotten. 1016 func (e *exporter) getAnnotations(key string) map[string]string { 1017 result, found := e.annotations[key] 1018 if found { 1019 delete(e.annotations, key) 1020 } 1021 return result.Annotations 1022 } 1023 1024 func (e *exporter) readAllSettings() error { 1025 settings, closer := e.st.getCollection(settingsC) 1026 defer closer() 1027 1028 var docs []settingsDoc 1029 if err := settings.Find(nil).All(&docs); err != nil { 1030 return errors.Trace(err) 1031 } 1032 1033 e.modelSettings = make(map[string]settingsDoc) 1034 for _, doc := range docs { 1035 key := e.st.localID(doc.DocID) 1036 e.modelSettings[key] = doc 1037 } 1038 return nil 1039 } 1040 1041 func (e *exporter) readAllStatuses() error { 1042 statuses, closer := e.st.getCollection(statusesC) 1043 defer closer() 1044 1045 var docs []bson.M 1046 err := statuses.Find(nil).All(&docs) 1047 if err != nil { 1048 return errors.Annotate(err, "failed to read status collection") 1049 } 1050 1051 e.logger.Debugf("read %d status documents", len(docs)) 1052 e.status = make(map[string]bson.M) 1053 for _, doc := range docs { 1054 docId, ok := doc["_id"].(string) 1055 if !ok { 1056 return errors.Errorf("expected string, got %s (%T)", doc["_id"], doc["_id"]) 1057 } 1058 id := e.st.localID(docId) 1059 e.status[id] = doc 1060 } 1061 1062 return nil 1063 } 1064 1065 func (e *exporter) readAllStatusHistory() error { 1066 statuses, closer := e.st.getCollection(statusesHistoryC) 1067 defer closer() 1068 1069 count := 0 1070 e.statusHistory = make(map[string][]historicalStatusDoc) 1071 var doc historicalStatusDoc 1072 // In tests, sorting by time can leave the results 1073 // underconstrained - include document id for deterministic 1074 // ordering in those cases. 1075 iter := statuses.Find(nil).Sort("-updated", "-_id").Iter() 1076 defer iter.Close() 1077 for iter.Next(&doc) { 1078 history := e.statusHistory[doc.GlobalKey] 1079 e.statusHistory[doc.GlobalKey] = append(history, doc) 1080 count++ 1081 } 1082 1083 if err := iter.Err(); err != nil { 1084 return errors.Annotate(err, "failed to read status history collection") 1085 } 1086 1087 e.logger.Debugf("read %d status history documents", count) 1088 1089 return nil 1090 } 1091 1092 func (e *exporter) statusArgs(globalKey string) (description.StatusArgs, error) { 1093 result := description.StatusArgs{} 1094 statusDoc, found := e.status[globalKey] 1095 if !found { 1096 return result, errors.NotFoundf("status data for %s", globalKey) 1097 } 1098 1099 status, ok := statusDoc["status"].(string) 1100 if !ok { 1101 return result, errors.Errorf("expected string for status, got %T", statusDoc["status"]) 1102 } 1103 info, ok := statusDoc["statusinfo"].(string) 1104 if !ok { 1105 return result, errors.Errorf("expected string for statusinfo, got %T", statusDoc["statusinfo"]) 1106 } 1107 // data is an embedded map and comes out as a bson.M 1108 // A bson.M is map[string]interface{}, so we can type cast it. 1109 data, ok := statusDoc["statusdata"].(bson.M) 1110 if !ok { 1111 return result, errors.Errorf("expected map for data, got %T", statusDoc["statusdata"]) 1112 } 1113 dataMap := map[string]interface{}(data) 1114 updated, ok := statusDoc["updated"].(int64) 1115 if !ok { 1116 return result, errors.Errorf("expected int64 for updated, got %T", statusDoc["updated"]) 1117 } 1118 1119 result.Value = status 1120 result.Message = info 1121 result.Data = dataMap 1122 result.Updated = time.Unix(0, updated) 1123 return result, nil 1124 } 1125 1126 func (e *exporter) statusHistoryArgs(globalKey string) []description.StatusArgs { 1127 history := e.statusHistory[globalKey] 1128 result := make([]description.StatusArgs, len(history)) 1129 e.logger.Debugf("found %d status history docs for %s", len(history), globalKey) 1130 for i, doc := range history { 1131 result[i] = description.StatusArgs{ 1132 Value: string(doc.Status), 1133 Message: doc.StatusInfo, 1134 Data: doc.StatusData, 1135 Updated: time.Unix(0, doc.Updated), 1136 } 1137 } 1138 1139 return result 1140 } 1141 1142 func (e *exporter) constraintsArgs(globalKey string) (description.ConstraintsArgs, error) { 1143 doc, found := e.constraints[globalKey] 1144 if !found { 1145 // No constraints for this key. 1146 e.logger.Debugf("no constraints found for key %q", globalKey) 1147 return description.ConstraintsArgs{}, nil 1148 } 1149 // We capture any type error using a closure to avoid having to return 1150 // multiple values from the optional functions. This does mean that we will 1151 // only report on the last one, but that is fine as there shouldn't be any. 1152 var optionalErr error 1153 optionalString := func(name string) string { 1154 switch value := doc[name].(type) { 1155 case nil: 1156 case string: 1157 return value 1158 default: 1159 optionalErr = errors.Errorf("expected uint64 for %s, got %T", name, value) 1160 } 1161 return "" 1162 } 1163 optionalInt := func(name string) uint64 { 1164 switch value := doc[name].(type) { 1165 case nil: 1166 case uint64: 1167 return value 1168 case int64: 1169 return uint64(value) 1170 default: 1171 optionalErr = errors.Errorf("expected uint64 for %s, got %T", name, value) 1172 } 1173 return 0 1174 } 1175 optionalStringSlice := func(name string) []string { 1176 switch value := doc[name].(type) { 1177 case nil: 1178 case []string: 1179 return value 1180 default: 1181 optionalErr = errors.Errorf("expected []string] for %s, got %T", name, value) 1182 } 1183 return nil 1184 } 1185 result := description.ConstraintsArgs{ 1186 Architecture: optionalString("arch"), 1187 Container: optionalString("container"), 1188 CpuCores: optionalInt("cpucores"), 1189 CpuPower: optionalInt("cpupower"), 1190 InstanceType: optionalString("instancetype"), 1191 Memory: optionalInt("mem"), 1192 RootDisk: optionalInt("rootdisk"), 1193 Spaces: optionalStringSlice("spaces"), 1194 Tags: optionalStringSlice("tags"), 1195 VirtType: optionalString("virttype"), 1196 } 1197 if optionalErr != nil { 1198 return description.ConstraintsArgs{}, errors.Trace(optionalErr) 1199 } 1200 return result, nil 1201 } 1202 1203 func (e *exporter) logExtras() { 1204 // As annotations are saved into the model, they are removed from the 1205 // exporter's map. If there are any left at the end, we are missing 1206 // things. Not an error just now, just a warning that we have missed 1207 // something. Could potentially be an error at a later date when 1208 // migrations are complete (but probably not). 1209 for key, doc := range e.annotations { 1210 e.logger.Warningf("unexported annotation for %s, %s", doc.Tag, key) 1211 } 1212 } 1213 1214 func (e *exporter) storage() error { 1215 if err := e.volumes(); err != nil { 1216 return errors.Trace(err) 1217 } 1218 if err := e.filesystems(); err != nil { 1219 return errors.Trace(err) 1220 } 1221 if err := e.storageInstances(); err != nil { 1222 return errors.Trace(err) 1223 } 1224 if err := e.storagePools(); err != nil { 1225 return errors.Trace(err) 1226 } 1227 return nil 1228 } 1229 1230 func (e *exporter) volumes() error { 1231 coll, closer := e.st.getCollection(volumesC) 1232 defer closer() 1233 1234 attachments, err := e.readVolumeAttachments() 1235 if err != nil { 1236 return errors.Trace(err) 1237 } 1238 1239 var doc volumeDoc 1240 iter := coll.Find(nil).Sort("_id").Iter() 1241 defer iter.Close() 1242 for iter.Next(&doc) { 1243 vol := &volume{e.st, doc} 1244 if err := e.addVolume(vol, attachments[doc.Name]); err != nil { 1245 return errors.Trace(err) 1246 } 1247 } 1248 if err := iter.Err(); err != nil { 1249 return errors.Annotate(err, "failed to read volumes") 1250 } 1251 return nil 1252 } 1253 1254 func (e *exporter) addVolume(vol *volume, volAttachments []volumeAttachmentDoc) error { 1255 args := description.VolumeArgs{ 1256 Tag: vol.VolumeTag(), 1257 Binding: vol.LifeBinding(), 1258 } 1259 if tag, err := vol.StorageInstance(); err == nil { 1260 // only returns an error when no storage tag. 1261 args.Storage = tag 1262 } else { 1263 if !errors.IsNotAssigned(err) { 1264 // This is an unexpected error. 1265 return errors.Trace(err) 1266 } 1267 } 1268 logger.Debugf("addVolume: %#v", vol.doc) 1269 if info, err := vol.Info(); err == nil { 1270 logger.Debugf(" info %#v", info) 1271 args.Provisioned = true 1272 args.Size = info.Size 1273 args.Pool = info.Pool 1274 args.HardwareID = info.HardwareId 1275 args.VolumeID = info.VolumeId 1276 args.Persistent = info.Persistent 1277 } else { 1278 params, _ := vol.Params() 1279 logger.Debugf(" params %#v", params) 1280 args.Size = params.Size 1281 args.Pool = params.Pool 1282 } 1283 1284 globalKey := vol.globalKey() 1285 statusArgs, err := e.statusArgs(globalKey) 1286 if err != nil { 1287 return errors.Annotatef(err, "status for volume %s", vol.doc.Name) 1288 } 1289 1290 exVolume := e.model.AddVolume(args) 1291 exVolume.SetStatus(statusArgs) 1292 exVolume.SetStatusHistory(e.statusHistoryArgs(globalKey)) 1293 if count := len(volAttachments); count != vol.doc.AttachmentCount { 1294 return errors.Errorf("volume attachment count mismatch, have %d, expected %d", 1295 count, vol.doc.AttachmentCount) 1296 } 1297 for _, doc := range volAttachments { 1298 va := volumeAttachment{doc} 1299 logger.Debugf(" attachment %#v", doc) 1300 args := description.VolumeAttachmentArgs{ 1301 Machine: va.Machine(), 1302 } 1303 if info, err := va.Info(); err == nil { 1304 logger.Debugf(" info %#v", info) 1305 args.Provisioned = true 1306 args.ReadOnly = info.ReadOnly 1307 args.DeviceName = info.DeviceName 1308 args.DeviceLink = info.DeviceLink 1309 args.BusAddress = info.BusAddress 1310 } else { 1311 params, _ := va.Params() 1312 logger.Debugf(" params %#v", params) 1313 args.ReadOnly = params.ReadOnly 1314 } 1315 exVolume.AddAttachment(args) 1316 } 1317 return nil 1318 } 1319 1320 func (e *exporter) readVolumeAttachments() (map[string][]volumeAttachmentDoc, error) { 1321 coll, closer := e.st.getCollection(volumeAttachmentsC) 1322 defer closer() 1323 1324 result := make(map[string][]volumeAttachmentDoc) 1325 var doc volumeAttachmentDoc 1326 var count int 1327 iter := coll.Find(nil).Iter() 1328 defer iter.Close() 1329 for iter.Next(&doc) { 1330 result[doc.Volume] = append(result[doc.Volume], doc) 1331 count++ 1332 } 1333 if err := iter.Err(); err != nil { 1334 return nil, errors.Annotate(err, "failed to read volumes attachments") 1335 } 1336 e.logger.Debugf("read %d volume attachment documents", count) 1337 return result, nil 1338 } 1339 1340 func (e *exporter) filesystems() error { 1341 coll, closer := e.st.getCollection(filesystemsC) 1342 defer closer() 1343 1344 attachments, err := e.readFilesystemAttachments() 1345 if err != nil { 1346 return errors.Trace(err) 1347 } 1348 1349 var doc filesystemDoc 1350 iter := coll.Find(nil).Sort("_id").Iter() 1351 defer iter.Close() 1352 for iter.Next(&doc) { 1353 fs := &filesystem{e.st, doc} 1354 if err := e.addFilesystem(fs, attachments[doc.FilesystemId]); err != nil { 1355 return errors.Trace(err) 1356 } 1357 } 1358 if err := iter.Err(); err != nil { 1359 return errors.Annotate(err, "failed to read filesystems") 1360 } 1361 return nil 1362 } 1363 1364 func (e *exporter) addFilesystem(fs *filesystem, fsAttachments []filesystemAttachmentDoc) error { 1365 // Here we don't care about the cases where the filesystem is not assigned to storage instances 1366 // nor no backing volues. In both those situations we have empty tags. 1367 storage, _ := fs.Storage() 1368 volume, _ := fs.Volume() 1369 args := description.FilesystemArgs{ 1370 Tag: fs.FilesystemTag(), 1371 Storage: storage, 1372 Volume: volume, 1373 Binding: fs.LifeBinding(), 1374 } 1375 logger.Debugf("addFilesystem: %#v", fs.doc) 1376 if info, err := fs.Info(); err == nil { 1377 logger.Debugf(" info %#v", info) 1378 args.Provisioned = true 1379 args.Size = info.Size 1380 args.Pool = info.Pool 1381 args.FilesystemID = info.FilesystemId 1382 } else { 1383 params, _ := fs.Params() 1384 logger.Debugf(" params %#v", params) 1385 args.Size = params.Size 1386 args.Pool = params.Pool 1387 } 1388 1389 globalKey := fs.globalKey() 1390 statusArgs, err := e.statusArgs(globalKey) 1391 if err != nil { 1392 return errors.Annotatef(err, "status for filesystem %s", fs.doc.FilesystemId) 1393 } 1394 1395 exFilesystem := e.model.AddFilesystem(args) 1396 exFilesystem.SetStatus(statusArgs) 1397 exFilesystem.SetStatusHistory(e.statusHistoryArgs(globalKey)) 1398 if count := len(fsAttachments); count != fs.doc.AttachmentCount { 1399 return errors.Errorf("filesystem attachment count mismatch, have %d, expected %d", 1400 count, fs.doc.AttachmentCount) 1401 } 1402 for _, doc := range fsAttachments { 1403 va := filesystemAttachment{doc} 1404 logger.Debugf(" attachment %#v", doc) 1405 args := description.FilesystemAttachmentArgs{ 1406 Machine: va.Machine(), 1407 } 1408 if info, err := va.Info(); err == nil { 1409 logger.Debugf(" info %#v", info) 1410 args.Provisioned = true 1411 args.ReadOnly = info.ReadOnly 1412 args.MountPoint = info.MountPoint 1413 } else { 1414 params, _ := va.Params() 1415 logger.Debugf(" params %#v", params) 1416 args.ReadOnly = params.ReadOnly 1417 args.MountPoint = params.Location 1418 } 1419 exFilesystem.AddAttachment(args) 1420 } 1421 return nil 1422 } 1423 1424 func (e *exporter) readFilesystemAttachments() (map[string][]filesystemAttachmentDoc, error) { 1425 coll, closer := e.st.getCollection(filesystemAttachmentsC) 1426 defer closer() 1427 1428 result := make(map[string][]filesystemAttachmentDoc) 1429 var doc filesystemAttachmentDoc 1430 var count int 1431 iter := coll.Find(nil).Iter() 1432 defer iter.Close() 1433 for iter.Next(&doc) { 1434 result[doc.Filesystem] = append(result[doc.Filesystem], doc) 1435 count++ 1436 } 1437 if err := iter.Err(); err != nil { 1438 return nil, errors.Annotate(err, "failed to read filesystem attachments") 1439 } 1440 e.logger.Debugf("read %d filesystem attachment documents", count) 1441 return result, nil 1442 } 1443 1444 func (e *exporter) storageInstances() error { 1445 coll, closer := e.st.getCollection(storageInstancesC) 1446 defer closer() 1447 1448 attachments, err := e.readStorageAttachments() 1449 if err != nil { 1450 return errors.Trace(err) 1451 } 1452 1453 var doc storageInstanceDoc 1454 iter := coll.Find(nil).Sort("_id").Iter() 1455 defer iter.Close() 1456 for iter.Next(&doc) { 1457 instance := &storageInstance{e.st, doc} 1458 if err := e.addStorage(instance, attachments[doc.Id]); err != nil { 1459 return errors.Trace(err) 1460 } 1461 } 1462 if err := iter.Err(); err != nil { 1463 return errors.Annotate(err, "failed to read storage instances") 1464 } 1465 return nil 1466 } 1467 1468 func (e *exporter) addStorage(instance *storageInstance, attachments []names.UnitTag) error { 1469 args := description.StorageArgs{ 1470 Tag: instance.StorageTag(), 1471 Kind: instance.Kind().String(), 1472 Owner: instance.Owner(), 1473 Name: instance.StorageName(), 1474 Attachments: attachments, 1475 } 1476 e.model.AddStorage(args) 1477 return nil 1478 } 1479 1480 func (e *exporter) readStorageAttachments() (map[string][]names.UnitTag, error) { 1481 coll, closer := e.st.getCollection(storageAttachmentsC) 1482 defer closer() 1483 1484 result := make(map[string][]names.UnitTag) 1485 var doc storageAttachmentDoc 1486 var count int 1487 iter := coll.Find(nil).Iter() 1488 defer iter.Close() 1489 for iter.Next(&doc) { 1490 unit := names.NewUnitTag(doc.Unit) 1491 result[doc.StorageInstance] = append(result[doc.StorageInstance], unit) 1492 count++ 1493 } 1494 if err := iter.Err(); err != nil { 1495 return nil, errors.Annotate(err, "failed to read storage attachments") 1496 } 1497 e.logger.Debugf("read %d storage attachment documents", count) 1498 return result, nil 1499 } 1500 1501 func (e *exporter) storagePools() error { 1502 registry, err := e.st.storageProviderRegistry() 1503 if err != nil { 1504 return errors.Annotate(err, "getting provider registry") 1505 } 1506 pm := poolmanager.New(storagePoolSettingsManager{e: e}, registry) 1507 poolConfigs, err := pm.List() 1508 if err != nil { 1509 return errors.Annotate(err, "listing pools") 1510 } 1511 for _, cfg := range poolConfigs { 1512 e.model.AddStoragePool(description.StoragePoolArgs{ 1513 Name: cfg.Name(), 1514 Provider: string(cfg.Provider()), 1515 Attributes: cfg.Attrs(), 1516 }) 1517 } 1518 return nil 1519 } 1520 1521 type storagePoolSettingsManager struct { 1522 poolmanager.SettingsManager 1523 e *exporter 1524 } 1525 1526 func (m storagePoolSettingsManager) ListSettings(keyPrefix string) (map[string]map[string]interface{}, error) { 1527 result := make(map[string]map[string]interface{}) 1528 for key, doc := range m.e.modelSettings { 1529 if strings.HasPrefix(key, keyPrefix) { 1530 result[key] = doc.Settings 1531 } 1532 } 1533 return result, nil 1534 }