1
// Copyright 2016 Canonical Ltd.
2
// Licensed under the AGPLv3, see LICENCE file for details.
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"
16
"github.com/juju/juju/core/description"
19
// Export the current model for the State.
20
func (st *State) Export() (description.Model, error) {
21
dbModel, err := st.Model()
23
return nil, errors.Trace(err)
29
logger: loggo.GetLogger("juju.state.export-model"),
31
if err := export.readAllStatuses(); err != nil {
32
return nil, errors.Annotate(err, "reading statuses")
34
if err := export.readAllStatusHistory(); err != nil {
35
return nil, errors.Trace(err)
37
if err := export.readAllSettings(); err != nil {
38
return nil, errors.Trace(err)
40
if err := export.readAllAnnotations(); err != nil {
41
return nil, errors.Trace(err)
43
if err := export.readAllConstraints(); err != nil {
44
return nil, errors.Trace(err)
47
modelConfig, found := export.modelSettings[modelGlobalKey]
49
return nil, errors.New("missing model config")
52
blocks, err := export.readBlocks()
54
return nil, errors.Trace(err)
57
args := description.ModelArgs{
58
Cloud: dbModel.Cloud(),
59
CloudRegion: dbModel.CloudRegion(),
60
Owner: dbModel.Owner(),
61
Config: modelConfig.Settings,
62
LatestToolsVersion: dbModel.LatestToolsVersion(),
65
export.model = description.NewModel(args)
66
modelKey := dbModel.globalKey()
67
export.model.SetAnnotations(export.getAnnotations(modelKey))
68
if err := export.sequences(); err != nil {
69
return nil, errors.Trace(err)
71
constraintsArgs, err := export.constraintsArgs(modelKey)
73
return nil, errors.Trace(err)
75
export.model.SetConstraints(constraintsArgs)
77
if err := export.modelUsers(); err != nil {
78
return nil, errors.Trace(err)
80
if err := export.machines(); err != nil {
81
return nil, errors.Trace(err)
83
if err := export.applications(); err != nil {
84
return nil, errors.Trace(err)
86
if err := export.relations(); err != nil {
87
return nil, errors.Trace(err)
89
if err := export.spaces(); err != nil {
90
return nil, errors.Trace(err)
92
if err := export.subnets(); err != nil {
93
return nil, errors.Trace(err)
96
if err := export.ipaddresses(); err != nil {
97
return nil, errors.Trace(err)
100
if err := export.linklayerdevices(); err != nil {
101
return nil, errors.Trace(err)
104
if err := export.sshHostKeys(); err != nil {
105
return nil, errors.Trace(err)
107
if err := export.storage(); err != nil {
108
return nil, errors.Trace(err)
111
if err := export.model.Validate(); err != nil {
112
return nil, errors.Trace(err)
117
return export.model, nil
120
type exporter struct {
123
model description.Model
126
annotations map[string]annotatorDoc
127
constraints map[string]bson.M
128
modelSettings map[string]settingsDoc
129
status map[string]bson.M
130
statusHistory map[string][]historicalStatusDoc
131
// Map of application name to units. Populated as part
132
// of the applications export.
133
units map[string][]*Unit
136
func (e *exporter) sequences() error {
137
sequences, closer := e.st.getCollection(sequenceC)
140
var docs []sequenceDoc
141
if err := sequences.Find(nil).All(&docs); err != nil {
142
return errors.Trace(err)
145
for _, doc := range docs {
146
e.model.SetSequence(doc.Name, doc.Counter)
151
func (e *exporter) readBlocks() (map[string]string, error) {
152
blocks, closer := e.st.getCollection(blocksC)
156
if err := blocks.Find(nil).All(&docs); err != nil {
157
return nil, errors.Trace(err)
160
result := make(map[string]string)
161
for _, doc := range docs {
162
// We don't care about the id, uuid, or tag.
163
// The uuid and tag both refer to the model uuid, and the
164
// id is opaque - even though it is sequence generated.
165
result[doc.Type.MigrationValue()] = doc.Message
170
func (e *exporter) modelUsers() error {
171
users, err := e.dbModel.Users()
173
return errors.Trace(err)
175
lastConnections, err := e.readLastConnectionTimes()
177
return errors.Trace(err)
179
for _, user := range users {
180
lastConn := lastConnections[strings.ToLower(user.UserName)]
181
arg := description.UserArgs{
183
DisplayName: user.DisplayName,
184
CreatedBy: user.CreatedBy,
185
DateCreated: user.DateCreated,
186
LastConnection: lastConn,
194
func (e *exporter) machines() error {
195
machines, err := e.st.AllMachines()
197
return errors.Trace(err)
199
e.logger.Debugf("found %d machines", len(machines))
201
instances, err := e.loadMachineInstanceData()
203
return errors.Trace(err)
205
blockDevices, err := e.loadMachineBlockDevices()
207
return errors.Trace(err)
210
// Read all the open ports documents.
211
openedPorts, closer := e.st.getCollection(openedPortsC)
213
var portsData []portsDoc
214
if err := openedPorts.Find(nil).All(&portsData); err != nil {
215
return errors.Annotate(err, "opened ports")
217
e.logger.Debugf("found %d openedPorts docs", len(portsData))
219
// We are iterating through a flat list of machines, but the migration
220
// model stores the nesting. The AllMachines method assures us that the
221
// machines are returned in an order so the parent will always before
223
machineMap := make(map[string]description.Machine)
225
for _, machine := range machines {
226
e.logger.Debugf("export machine %s", machine.Id())
228
var exParent description.Machine
229
if parentId := ParentId(machine.Id()); parentId != "" {
231
exParent, found = machineMap[parentId]
233
return errors.Errorf("machine %s missing parent", machine.Id())
237
exMachine, err := e.newMachine(exParent, machine, instances, portsData, blockDevices)
239
return errors.Trace(err)
241
machineMap[machine.Id()] = exMachine
247
func (e *exporter) loadMachineInstanceData() (map[string]instanceData, error) {
248
instanceDataCollection, closer := e.st.getCollection(instanceDataC)
251
var instData []instanceData
252
instances := make(map[string]instanceData)
253
if err := instanceDataCollection.Find(nil).All(&instData); err != nil {
254
return nil, errors.Annotate(err, "instance data")
256
e.logger.Debugf("found %d instanceData", len(instData))
257
for _, data := range instData {
258
instances[data.MachineId] = data
260
return instances, nil
263
func (e *exporter) loadMachineBlockDevices() (map[string][]BlockDeviceInfo, error) {
264
coll, closer := e.st.getCollection(blockDevicesC)
267
var deviceData []blockDevicesDoc
268
result := make(map[string][]BlockDeviceInfo)
269
if err := coll.Find(nil).All(&deviceData); err != nil {
270
return nil, errors.Annotate(err, "block devices")
272
e.logger.Debugf("found %d block device records", len(deviceData))
273
for _, data := range deviceData {
274
result[data.Machine] = data.BlockDevices
279
func (e *exporter) newMachine(exParent description.Machine, machine *Machine, instances map[string]instanceData, portsData []portsDoc, blockDevices map[string][]BlockDeviceInfo) (description.Machine, error) {
280
args := description.MachineArgs{
281
Id: machine.MachineTag(),
282
Nonce: machine.doc.Nonce,
283
PasswordHash: machine.doc.PasswordHash,
284
Placement: machine.doc.Placement,
285
Series: machine.doc.Series,
286
ContainerType: machine.doc.ContainerType,
289
if supported, ok := machine.SupportedContainers(); ok {
290
containers := make([]string, len(supported))
291
for i, containerType := range supported {
292
containers[i] = string(containerType)
294
args.SupportedContainers = &containers
297
for _, job := range machine.Jobs() {
298
args.Jobs = append(args.Jobs, job.MigrationValue())
301
// A null value means that we don't yet know which containers
302
// are supported. An empty slice means 'no containers are supported'.
303
var exMachine description.Machine
305
exMachine = e.model.AddMachine(args)
307
exMachine = exParent.AddContainer(args)
309
exMachine.SetAddresses(
310
e.newAddressArgsSlice(machine.doc.MachineAddresses),
311
e.newAddressArgsSlice(machine.doc.Addresses))
312
exMachine.SetPreferredAddresses(
313
e.newAddressArgs(machine.doc.PreferredPublicAddress),
314
e.newAddressArgs(machine.doc.PreferredPrivateAddress))
316
// We fully expect the machine to have tools set, and that there is
317
// some instance data.
318
instData, found := instances[machine.doc.Id]
320
return nil, errors.NotValidf("missing instance data for machine %s", machine.Id())
322
exMachine.SetInstance(e.newCloudInstanceArgs(instData))
324
// We don't rely on devices being there. If they aren't, we get an empty slice,
325
// which is fine to iterate over with range.
326
for _, device := range blockDevices[machine.doc.Id] {
327
exMachine.AddBlockDevice(description.BlockDeviceArgs{
328
Name: device.DeviceName,
329
Links: device.DeviceLinks,
332
HardwareID: device.HardwareId,
333
BusAddress: device.BusAddress,
335
FilesystemType: device.FilesystemType,
337
MountPoint: device.MountPoint,
341
// Find the current machine status.
342
globalKey := machine.globalKey()
343
statusArgs, err := e.statusArgs(globalKey)
345
return nil, errors.Annotatef(err, "status for machine %s", machine.Id())
347
exMachine.SetStatus(statusArgs)
348
exMachine.SetStatusHistory(e.statusHistoryArgs(globalKey))
350
tools, err := machine.AgentTools()
352
// This means the tools aren't set, but they should be.
353
return nil, errors.Trace(err)
356
exMachine.SetTools(description.AgentToolsArgs{
357
Version: tools.Version,
359
SHA256: tools.SHA256,
363
for _, args := range e.openedPortsArgsForMachine(machine.Id(), portsData) {
364
exMachine.AddOpenedPorts(args)
367
exMachine.SetAnnotations(e.getAnnotations(globalKey))
369
constraintsArgs, err := e.constraintsArgs(globalKey)
371
return nil, errors.Trace(err)
373
exMachine.SetConstraints(constraintsArgs)
375
return exMachine, nil
378
func (e *exporter) openedPortsArgsForMachine(machineId string, portsData []portsDoc) []description.OpenedPortsArgs {
379
var result []description.OpenedPortsArgs
380
for _, doc := range portsData {
381
// Don't bother including a subnet if there are no ports open on it.
382
if doc.MachineID == machineId && len(doc.Ports) > 0 {
383
args := description.OpenedPortsArgs{SubnetID: doc.SubnetID}
384
for _, p := range doc.Ports {
385
args.OpenedPorts = append(args.OpenedPorts, description.PortRangeArgs{
386
UnitName: p.UnitName,
387
FromPort: p.FromPort,
389
Protocol: p.Protocol,
392
result = append(result, args)
398
func (e *exporter) newAddressArgsSlice(a []address) []description.AddressArgs {
399
result := []description.AddressArgs{}
400
for _, addr := range a {
401
result = append(result, e.newAddressArgs(addr))
406
func (e *exporter) newAddressArgs(a address) description.AddressArgs {
407
return description.AddressArgs{
415
func (e *exporter) newCloudInstanceArgs(data instanceData) description.CloudInstanceArgs {
416
inst := description.CloudInstanceArgs{
417
InstanceId: string(data.InstanceId),
420
if data.Arch != nil {
421
inst.Architecture = *data.Arch
424
inst.Memory = *data.Mem
426
if data.RootDisk != nil {
427
inst.RootDisk = *data.RootDisk
429
if data.CpuCores != nil {
430
inst.CpuCores = *data.CpuCores
432
if data.CpuPower != nil {
433
inst.CpuPower = *data.CpuPower
435
if data.Tags != nil {
436
inst.Tags = *data.Tags
438
if data.AvailZone != nil {
439
inst.AvailabilityZone = *data.AvailZone
444
func (e *exporter) applications() error {
445
applications, err := e.st.AllApplications()
447
return errors.Trace(err)
449
e.logger.Debugf("found %d applications", len(applications))
451
refcounts, err := e.readAllSettingsRefCounts()
453
return errors.Trace(err)
456
e.units, err = e.readAllUnits()
458
return errors.Trace(err)
461
meterStatus, err := e.readAllMeterStatus()
463
return errors.Trace(err)
466
leaders, err := e.readApplicationLeaders()
468
return errors.Trace(err)
471
for _, application := range applications {
472
applicationUnits := e.units[application.Name()]
473
leader := leaders[application.Name()]
474
if err := e.addApplication(application, refcounts, applicationUnits, meterStatus, leader); err != nil {
475
return errors.Trace(err)
481
func (e *exporter) readApplicationLeaders() (map[string]string, error) {
482
client, err := e.st.getLeadershipLeaseClient()
484
return nil, errors.Trace(err)
486
leases := client.Leases()
487
result := make(map[string]string, len(leases))
488
for key, value := range leases {
489
result[key] = value.Holder
494
func (e *exporter) addApplication(application *Application, refcounts map[string]int, units []*Unit, meterStatus map[string]*meterStatusDoc, leader string) error {
495
settingsKey := application.settingsKey()
496
leadershipKey := leadershipSettingsKey(application.Name())
498
applicationSettingsDoc, found := e.modelSettings[settingsKey]
500
return errors.Errorf("missing settings for application %q", application.Name())
502
refCount, found := refcounts[settingsKey]
504
return errors.Errorf("missing settings refcount for application %q", application.Name())
506
leadershipSettingsDoc, found := e.modelSettings[leadershipKey]
508
return errors.Errorf("missing leadership settings for application %q", application.Name())
511
args := description.ApplicationArgs{
512
Tag: application.ApplicationTag(),
513
Series: application.doc.Series,
514
Subordinate: application.doc.Subordinate,
515
CharmURL: application.doc.CharmURL.String(),
516
Channel: application.doc.Channel,
517
CharmModifiedVersion: application.doc.CharmModifiedVersion,
518
ForceCharm: application.doc.ForceCharm,
519
Exposed: application.doc.Exposed,
520
MinUnits: application.doc.MinUnits,
521
Settings: applicationSettingsDoc.Settings,
522
SettingsRefCount: refCount,
524
LeadershipSettings: leadershipSettingsDoc.Settings,
525
MetricsCredentials: application.doc.MetricCredentials,
527
exApplication := e.model.AddApplication(args)
528
// Find the current application status.
529
globalKey := application.globalKey()
530
statusArgs, err := e.statusArgs(globalKey)
532
return errors.Annotatef(err, "status for application %s", application.Name())
534
exApplication.SetStatus(statusArgs)
535
exApplication.SetStatusHistory(e.statusHistoryArgs(globalKey))
536
exApplication.SetAnnotations(e.getAnnotations(globalKey))
538
constraintsArgs, err := e.constraintsArgs(globalKey)
540
return errors.Trace(err)
542
exApplication.SetConstraints(constraintsArgs)
544
for _, unit := range units {
545
agentKey := unit.globalAgentKey()
546
unitMeterStatus, found := meterStatus[agentKey]
548
return errors.Errorf("missing meter status for unit %s", unit.Name())
551
workloadVersion, err := unit.WorkloadVersion()
553
return errors.Trace(err)
555
args := description.UnitArgs{
557
Machine: names.NewMachineTag(unit.doc.MachineId),
558
WorkloadVersion: workloadVersion,
559
PasswordHash: unit.doc.PasswordHash,
560
MeterStatusCode: unitMeterStatus.Code,
561
MeterStatusInfo: unitMeterStatus.Info,
563
if principalName, isSubordinate := unit.PrincipalName(); isSubordinate {
564
args.Principal = names.NewUnitTag(principalName)
566
if subs := unit.SubordinateNames(); len(subs) > 0 {
567
for _, subName := range subs {
568
args.Subordinates = append(args.Subordinates, names.NewUnitTag(subName))
571
exUnit := exApplication.AddUnit(args)
572
// workload uses globalKey, agent uses globalAgentKey,
573
// workload version uses globalWorkloadVersionKey.
574
globalKey := unit.globalKey()
575
statusArgs, err := e.statusArgs(globalKey)
577
return errors.Annotatef(err, "workload status for unit %s", unit.Name())
579
exUnit.SetWorkloadStatus(statusArgs)
580
exUnit.SetWorkloadStatusHistory(e.statusHistoryArgs(globalKey))
582
statusArgs, err = e.statusArgs(agentKey)
584
return errors.Annotatef(err, "agent status for unit %s", unit.Name())
586
exUnit.SetAgentStatus(statusArgs)
587
exUnit.SetAgentStatusHistory(e.statusHistoryArgs(agentKey))
589
workloadVersionKey := unit.globalWorkloadVersionKey()
590
exUnit.SetWorkloadVersionHistory(e.statusHistoryArgs(workloadVersionKey))
592
tools, err := unit.AgentTools()
594
// This means the tools aren't set, but they should be.
595
return errors.Trace(err)
597
exUnit.SetTools(description.AgentToolsArgs{
598
Version: tools.Version,
600
SHA256: tools.SHA256,
603
exUnit.SetAnnotations(e.getAnnotations(globalKey))
605
constraintsArgs, err := e.constraintsArgs(agentKey)
607
return errors.Trace(err)
609
exUnit.SetConstraints(constraintsArgs)
615
func (e *exporter) relations() error {
616
rels, err := e.st.AllRelations()
618
return errors.Trace(err)
620
e.logger.Debugf("read %d relations", len(rels))
622
relationScopes, err := e.readAllRelationScopes()
624
return errors.Trace(err)
627
for _, relation := range rels {
628
exRelation := e.model.AddRelation(description.RelationArgs{
630
Key: relation.String(),
632
for _, ep := range relation.Endpoints() {
633
exEndPoint := exRelation.AddEndpoint(description.EndpointArgs{
634
ApplicationName: ep.ApplicationName,
636
Role: string(ep.Role),
637
Interface: ep.Interface,
638
Optional: ep.Optional,
640
Scope: string(ep.Scope),
642
// We expect a relationScope and settings for each of the
643
// units of the specified application.
644
units := e.units[ep.ApplicationName]
645
for _, unit := range units {
646
ru, err := relation.Unit(unit)
648
return errors.Trace(err)
651
if !relationScopes.Contains(key) {
652
return errors.Errorf("missing relation scope for %s and %s", relation, unit.Name())
654
settingsDoc, found := e.modelSettings[key]
656
return errors.Errorf("missing relation settings for %s and %s", relation, unit.Name())
658
exEndPoint.SetUnitSettings(unit.Name(), settingsDoc.Settings)
665
func (e *exporter) spaces() error {
666
spaces, err := e.st.AllSpaces()
668
return errors.Trace(err)
670
e.logger.Debugf("read %d spaces", len(spaces))
672
for _, space := range spaces {
673
e.model.AddSpace(description.SpaceArgs{
675
Public: space.IsPublic(),
676
ProviderID: string(space.ProviderId()),
682
func (e *exporter) linklayerdevices() error {
683
linklayerdevices, err := e.st.AllLinkLayerDevices()
685
return errors.Trace(err)
687
e.logger.Debugf("read %d ip devices", len(linklayerdevices))
688
for _, device := range linklayerdevices {
689
e.model.AddLinkLayerDevice(description.LinkLayerDeviceArgs{
690
ProviderID: string(device.ProviderID()),
691
MachineID: device.MachineID(),
694
Type: string(device.Type()),
695
MACAddress: device.MACAddress(),
696
IsAutoStart: device.IsAutoStart(),
698
ParentName: device.ParentName(),
704
func (e *exporter) subnets() error {
705
subnets, err := e.st.AllSubnets()
707
return errors.Trace(err)
709
e.logger.Debugf("read %d subnets", len(subnets))
711
for _, subnet := range subnets {
712
e.model.AddSubnet(description.SubnetArgs{
714
ProviderId: string(subnet.ProviderId()),
715
VLANTag: subnet.VLANTag(),
716
AvailabilityZone: subnet.AvailabilityZone(),
717
SpaceName: subnet.SpaceName(),
723
func (e *exporter) ipaddresses() error {
724
ipaddresses, err := e.st.AllIPAddresses()
726
return errors.Trace(err)
728
e.logger.Debugf("read %d ip addresses", len(ipaddresses))
729
for _, addr := range ipaddresses {
730
e.model.AddIPAddress(description.IPAddressArgs{
731
ProviderID: string(addr.ProviderID()),
732
DeviceName: addr.DeviceName(),
733
MachineID: addr.MachineID(),
734
SubnetCIDR: addr.SubnetCIDR(),
735
ConfigMethod: string(addr.ConfigMethod()),
737
DNSServers: addr.DNSServers(),
738
DNSSearchDomains: addr.DNSSearchDomains(),
739
GatewayAddress: addr.GatewayAddress(),
745
func (e *exporter) sshHostKeys() error {
746
machines, err := e.st.AllMachines()
748
return errors.Trace(err)
750
for _, machine := range machines {
751
keys, err := e.st.GetSSHHostKeys(machine.MachineTag())
752
if errors.IsNotFound(err) {
754
} else if err != nil {
755
return errors.Trace(err)
760
e.model.AddSSHHostKey(description.SSHHostKeyArgs{
761
MachineID: machine.Id(),
768
func (e *exporter) readAllRelationScopes() (set.Strings, error) {
769
relationScopes, closer := e.st.getCollection(relationScopesC)
772
docs := []relationScopeDoc{}
773
err := relationScopes.Find(nil).All(&docs)
775
return nil, errors.Annotate(err, "cannot get all relation scopes")
777
e.logger.Debugf("found %d relationScope docs", len(docs))
779
result := set.NewStrings()
780
for _, doc := range docs {
786
func (e *exporter) readAllUnits() (map[string][]*Unit, error) {
787
unitsCollection, closer := e.st.getCollection(unitsC)
791
err := unitsCollection.Find(nil).All(&docs)
793
return nil, errors.Annotate(err, "cannot get all units")
795
e.logger.Debugf("found %d unit docs", len(docs))
796
result := make(map[string][]*Unit)
797
for _, doc := range docs {
798
units := result[doc.Application]
799
result[doc.Application] = append(units, newUnit(e.st, &doc))
804
func (e *exporter) readAllMeterStatus() (map[string]*meterStatusDoc, error) {
805
meterStatuses, closer := e.st.getCollection(meterStatusC)
808
docs := []meterStatusDoc{}
809
err := meterStatuses.Find(nil).All(&docs)
811
return nil, errors.Annotate(err, "cannot get all meter status docs")
813
e.logger.Debugf("found %d meter status docs", len(docs))
814
result := make(map[string]*meterStatusDoc)
815
for _, doc := range docs {
816
result[e.st.localID(doc.DocID)] = &doc
821
func (e *exporter) readLastConnectionTimes() (map[string]time.Time, error) {
822
lastConnections, closer := e.st.getCollection(modelUserLastConnectionC)
825
var docs []modelUserLastConnectionDoc
826
if err := lastConnections.Find(nil).All(&docs); err != nil {
827
return nil, errors.Trace(err)
830
result := make(map[string]time.Time)
831
for _, doc := range docs {
832
result[doc.UserName] = doc.LastConnection.UTC()
837
func (e *exporter) readAllAnnotations() error {
838
annotations, closer := e.st.getCollection(annotationsC)
841
var docs []annotatorDoc
842
if err := annotations.Find(nil).All(&docs); err != nil {
843
return errors.Trace(err)
845
e.logger.Debugf("read %d annotations docs", len(docs))
847
e.annotations = make(map[string]annotatorDoc)
848
for _, doc := range docs {
849
e.annotations[doc.GlobalKey] = doc
854
func (e *exporter) readAllConstraints() error {
855
constraintsCollection, closer := e.st.getCollection(constraintsC)
858
// Since the constraintsDoc doesn't include any global key or _id
859
// fields, we can't just deserialize the entire collection into a slice
860
// of docs, so we get them all out with bson maps.
862
err := constraintsCollection.Find(nil).All(&docs)
864
return errors.Annotate(err, "failed to read constraints collection")
867
e.logger.Debugf("read %d constraints docs", len(docs))
868
e.constraints = make(map[string]bson.M)
869
for _, doc := range docs {
870
docId, ok := doc["_id"].(string)
872
return errors.Errorf("expected string, got %s (%T)", doc["_id"], doc["_id"])
874
id := e.st.localID(docId)
875
e.constraints[id] = doc
876
e.logger.Debugf("doc[%q] = %#v", id, doc)
881
// getAnnotations doesn't really care if there are any there or not
882
// for the key, but if they were there, they are removed so we can
883
// check at the end of the export for anything we have forgotten.
884
func (e *exporter) getAnnotations(key string) map[string]string {
885
result, found := e.annotations[key]
887
delete(e.annotations, key)
889
return result.Annotations
892
func (e *exporter) readAllSettings() error {
893
settings, closer := e.st.getCollection(settingsC)
896
var docs []settingsDoc
897
if err := settings.Find(nil).All(&docs); err != nil {
898
return errors.Trace(err)
901
e.modelSettings = make(map[string]settingsDoc)
902
for _, doc := range docs {
903
key := e.st.localID(doc.DocID)
904
e.modelSettings[key] = doc
909
func (e *exporter) readAllStatuses() error {
910
statuses, closer := e.st.getCollection(statusesC)
914
err := statuses.Find(nil).All(&docs)
916
return errors.Annotate(err, "failed to read status collection")
919
e.logger.Debugf("read %d status documents", len(docs))
920
e.status = make(map[string]bson.M)
921
for _, doc := range docs {
922
docId, ok := doc["_id"].(string)
924
return errors.Errorf("expected string, got %s (%T)", doc["_id"], doc["_id"])
926
id := e.st.localID(docId)
933
func (e *exporter) readAllStatusHistory() error {
934
statuses, closer := e.st.getCollection(statusesHistoryC)
938
e.statusHistory = make(map[string][]historicalStatusDoc)
939
var doc historicalStatusDoc
940
// In tests, sorting by time can leave the results
941
// underconstrained - include document id for deterministic
942
// ordering in those cases.
943
iter := statuses.Find(nil).Sort("-updated", "-_id").Iter()
945
for iter.Next(&doc) {
946
history := e.statusHistory[doc.GlobalKey]
947
e.statusHistory[doc.GlobalKey] = append(history, doc)
951
if err := iter.Err(); err != nil {
952
return errors.Annotate(err, "failed to read status history collection")
955
e.logger.Debugf("read %d status history documents", count)
960
func (e *exporter) statusArgs(globalKey string) (description.StatusArgs, error) {
961
result := description.StatusArgs{}
962
statusDoc, found := e.status[globalKey]
964
return result, errors.NotFoundf("status data for %s", globalKey)
967
status, ok := statusDoc["status"].(string)
969
return result, errors.Errorf("expected string for status, got %T", statusDoc["status"])
971
info, ok := statusDoc["statusinfo"].(string)
973
return result, errors.Errorf("expected string for statusinfo, got %T", statusDoc["statusinfo"])
975
// data is an embedded map and comes out as a bson.M
976
// A bson.M is map[string]interface{}, so we can type cast it.
977
data, ok := statusDoc["statusdata"].(bson.M)
979
return result, errors.Errorf("expected map for data, got %T", statusDoc["statusdata"])
981
dataMap := map[string]interface{}(data)
982
updated, ok := statusDoc["updated"].(int64)
984
return result, errors.Errorf("expected int64 for updated, got %T", statusDoc["updated"])
987
result.Value = status
988
result.Message = info
989
result.Data = dataMap
990
result.Updated = time.Unix(0, updated)
994
func (e *exporter) statusHistoryArgs(globalKey string) []description.StatusArgs {
995
history := e.statusHistory[globalKey]
996
result := make([]description.StatusArgs, len(history))
997
e.logger.Debugf("found %d status history docs for %s", len(history), globalKey)
998
for i, doc := range history {
999
result[i] = description.StatusArgs{
1000
Value: string(doc.Status),
1001
Message: doc.StatusInfo,
1002
Data: doc.StatusData,
1003
Updated: time.Unix(0, doc.Updated),
1010
func (e *exporter) constraintsArgs(globalKey string) (description.ConstraintsArgs, error) {
1011
doc, found := e.constraints[globalKey]
1013
// No constraints for this key.
1014
e.logger.Debugf("no constraints found for key %q", globalKey)
1015
return description.ConstraintsArgs{}, nil
1017
// We capture any type error using a closure to avoid having to return
1018
// multiple values from the optional functions. This does mean that we will
1019
// only report on the last one, but that is fine as there shouldn't be any.
1020
var optionalErr error
1021
optionalString := func(name string) string {
1022
switch value := doc[name].(type) {
1027
optionalErr = errors.Errorf("expected uint64 for %s, got %T", name, value)
1031
optionalInt := func(name string) uint64 {
1032
switch value := doc[name].(type) {
1037
return uint64(value)
1039
optionalErr = errors.Errorf("expected uint64 for %s, got %T", name, value)
1043
optionalStringSlice := func(name string) []string {
1044
switch value := doc[name].(type) {
1049
optionalErr = errors.Errorf("expected []string] for %s, got %T", name, value)
1053
result := description.ConstraintsArgs{
1054
Architecture: optionalString("arch"),
1055
Container: optionalString("container"),
1056
CpuCores: optionalInt("cpucores"),
1057
CpuPower: optionalInt("cpupower"),
1058
InstanceType: optionalString("instancetype"),
1059
Memory: optionalInt("mem"),
1060
RootDisk: optionalInt("rootdisk"),
1061
Spaces: optionalStringSlice("spaces"),
1062
Tags: optionalStringSlice("tags"),
1063
VirtType: optionalString("virttype"),
1065
if optionalErr != nil {
1066
return description.ConstraintsArgs{}, errors.Trace(optionalErr)
1071
func (e *exporter) readAllSettingsRefCounts() (map[string]int, error) {
1072
refCounts, closer := e.st.getCollection(settingsrefsC)
1076
err := refCounts.Find(nil).All(&docs)
1078
return nil, errors.Annotate(err, "failed to read settings refcount collection")
1081
e.logger.Debugf("read %d settings refcount documents", len(docs))
1082
result := make(map[string]int)
1083
for _, doc := range docs {
1084
docId, ok := doc["_id"].(string)
1086
return nil, errors.Errorf("expected string, got %s (%T)", doc["_id"], doc["_id"])
1088
id := e.st.localID(docId)
1089
count, ok := doc["refcount"].(int)
1091
return nil, errors.Errorf("expected int, got %s (%T)", doc["refcount"], doc["refcount"])
1099
func (e *exporter) logExtras() {
1100
// As annotations are saved into the model, they are removed from the
1101
// exporter's map. If there are any left at the end, we are missing
1102
// things. Not an error just now, just a warning that we have missed
1103
// something. Could potentially be an error at a later date when
1104
// migrations are complete (but probably not).
1105
for key, doc := range e.annotations {
1106
e.logger.Warningf("unexported annotation for %s, %s", doc.Tag, key)
1110
func (e *exporter) storage() error {
1111
if err := e.volumes(); err != nil {
1112
return errors.Trace(err)
1114
if err := e.filesystems(); err != nil {
1115
return errors.Trace(err)
1120
func (e *exporter) volumes() error {
1121
coll, closer := e.st.getCollection(volumesC)
1124
attachments, err := e.readVolumeAttachments()
1126
return errors.Trace(err)
1130
iter := coll.Find(nil).Sort("_id").Iter()
1132
for iter.Next(&doc) {
1133
vol := &volume{e.st, doc}
1134
if err := e.addVolume(vol, attachments[doc.Name]); err != nil {
1135
return errors.Trace(err)
1138
if err := iter.Err(); err != nil {
1139
return errors.Annotate(err, "failed to read volumes")
1144
func (e *exporter) addVolume(vol *volume, volAttachments []volumeAttachmentDoc) error {
1145
args := description.VolumeArgs{
1146
Tag: vol.VolumeTag(),
1147
Binding: vol.LifeBinding(),
1148
// TODO: add storage link
1150
logger.Debugf("addVolume: %#v", vol.doc)
1151
if info, err := vol.Info(); err == nil {
1152
logger.Debugf(" info %#v", info)
1153
args.Provisioned = true
1154
args.Size = info.Size
1155
args.Pool = info.Pool
1156
args.HardwareID = info.HardwareId
1157
args.VolumeID = info.VolumeId
1158
args.Persistent = info.Persistent
1160
params, _ := vol.Params()
1161
logger.Debugf(" params %#v", params)
1162
args.Size = params.Size
1163
args.Pool = params.Pool
1166
globalKey := vol.globalKey()
1167
statusArgs, err := e.statusArgs(globalKey)
1169
return errors.Annotatef(err, "status for volume %s", vol.doc.Name)
1172
exVolume := e.model.AddVolume(args)
1173
exVolume.SetStatus(statusArgs)
1174
exVolume.SetStatusHistory(e.statusHistoryArgs(globalKey))
1175
if count := len(volAttachments); count != vol.doc.AttachmentCount {
1176
return errors.Errorf("volume attachment count mismatch, have %d, expected %d",
1177
count, vol.doc.AttachmentCount)
1179
for _, doc := range volAttachments {
1180
va := volumeAttachment{doc}
1181
logger.Debugf(" attachment %#v", doc)
1182
args := description.VolumeAttachmentArgs{
1183
Machine: va.Machine(),
1185
if info, err := va.Info(); err == nil {
1186
logger.Debugf(" info %#v", info)
1187
args.Provisioned = true
1188
args.ReadOnly = info.ReadOnly
1189
args.DeviceName = info.DeviceName
1190
args.DeviceLink = info.DeviceLink
1191
args.BusAddress = info.BusAddress
1193
params, _ := va.Params()
1194
logger.Debugf(" params %#v", params)
1195
args.ReadOnly = params.ReadOnly
1197
exVolume.AddAttachment(args)
1202
func (e *exporter) readVolumeAttachments() (map[string][]volumeAttachmentDoc, error) {
1203
coll, closer := e.st.getCollection(volumeAttachmentsC)
1206
result := make(map[string][]volumeAttachmentDoc)
1207
var doc volumeAttachmentDoc
1209
iter := coll.Find(nil).Iter()
1211
for iter.Next(&doc) {
1212
result[doc.Volume] = append(result[doc.Volume], doc)
1215
if err := iter.Err(); err != nil {
1216
return nil, errors.Annotate(err, "failed to read volumes attachments")
1218
e.logger.Debugf("read %d volume attachment documents", count)
1222
func (e *exporter) filesystems() error {
1223
coll, closer := e.st.getCollection(filesystemsC)
1226
attachments, err := e.readFilesystemAttachments()
1228
return errors.Trace(err)
1231
var doc filesystemDoc
1232
iter := coll.Find(nil).Sort("_id").Iter()
1234
for iter.Next(&doc) {
1235
fs := &filesystem{e.st, doc}
1236
if err := e.addFilesystem(fs, attachments[doc.FilesystemId]); err != nil {
1237
return errors.Trace(err)
1240
if err := iter.Err(); err != nil {
1241
return errors.Annotate(err, "failed to read filesystems")
1246
func (e *exporter) addFilesystem(fs *filesystem, fsAttachments []filesystemAttachmentDoc) error {
1247
// Here we don't care about the cases where the filesystem is not assigned to storage instances
1248
// nor no backing volues. In both those situations we have empty tags.
1249
storage, _ := fs.Storage()
1250
volume, _ := fs.Volume()
1251
args := description.FilesystemArgs{
1252
Tag: fs.FilesystemTag(),
1255
Binding: fs.LifeBinding(),
1257
logger.Debugf("addFilesystem: %#v", fs.doc)
1258
if info, err := fs.Info(); err == nil {
1259
logger.Debugf(" info %#v", info)
1260
args.Provisioned = true
1261
args.Size = info.Size
1262
args.Pool = info.Pool
1263
args.FilesystemID = info.FilesystemId
1265
params, _ := fs.Params()
1266
logger.Debugf(" params %#v", params)
1267
args.Size = params.Size
1268
args.Pool = params.Pool
1271
globalKey := fs.globalKey()
1272
statusArgs, err := e.statusArgs(globalKey)
1274
return errors.Annotatef(err, "status for filesystem %s", fs.doc.FilesystemId)
1277
exFilesystem := e.model.AddFilesystem(args)
1278
exFilesystem.SetStatus(statusArgs)
1279
exFilesystem.SetStatusHistory(e.statusHistoryArgs(globalKey))
1280
if count := len(fsAttachments); count != fs.doc.AttachmentCount {
1281
return errors.Errorf("filesystem attachment count mismatch, have %d, expected %d",
1282
count, fs.doc.AttachmentCount)
1284
for _, doc := range fsAttachments {
1285
va := filesystemAttachment{doc}
1286
logger.Debugf(" attachment %#v", doc)
1287
args := description.FilesystemAttachmentArgs{
1288
Machine: va.Machine(),
1290
if info, err := va.Info(); err == nil {
1291
logger.Debugf(" info %#v", info)
1292
args.Provisioned = true
1293
args.ReadOnly = info.ReadOnly
1294
args.MountPoint = info.MountPoint
1296
params, _ := va.Params()
1297
logger.Debugf(" params %#v", params)
1298
args.ReadOnly = params.ReadOnly
1299
args.MountPoint = params.Location
1301
exFilesystem.AddAttachment(args)
1306
func (e *exporter) readFilesystemAttachments() (map[string][]filesystemAttachmentDoc, error) {
1307
coll, closer := e.st.getCollection(filesystemAttachmentsC)
1310
result := make(map[string][]filesystemAttachmentDoc)
1311
var doc filesystemAttachmentDoc
1313
iter := coll.Find(nil).Iter()
1315
for iter.Next(&doc) {
1316
result[doc.Filesystem] = append(result[doc.Filesystem], doc)
1319
if err := iter.Err(); err != nil {
1320
return nil, errors.Annotate(err, "failed to read filesystem attachments")
1322
e.logger.Debugf("read %d filesystem attachment documents", count)