~nskaggs/+junk/xenial-test

« back to all changes in this revision

Viewing changes to src/github.com/juju/juju/state/migration_export.go

  • Committer: Nicholas Skaggs
  • Date: 2016-10-24 20:56:05 UTC
  • Revision ID: nicholas.skaggs@canonical.com-20161024205605-z8lta0uvuhtxwzwl
Initi with beta15

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
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
)
 
18
 
 
19
// Export the current model for the State.
 
20
func (st *State) Export() (description.Model, error) {
 
21
        dbModel, err := st.Model()
 
22
        if err != nil {
 
23
                return nil, errors.Trace(err)
 
24
        }
 
25
 
 
26
        export := exporter{
 
27
                st:      st,
 
28
                dbModel: dbModel,
 
29
                logger:  loggo.GetLogger("juju.state.export-model"),
 
30
        }
 
31
        if err := export.readAllStatuses(); err != nil {
 
32
                return nil, errors.Annotate(err, "reading statuses")
 
33
        }
 
34
        if err := export.readAllStatusHistory(); err != nil {
 
35
                return nil, errors.Trace(err)
 
36
        }
 
37
        if err := export.readAllSettings(); err != nil {
 
38
                return nil, errors.Trace(err)
 
39
        }
 
40
        if err := export.readAllAnnotations(); err != nil {
 
41
                return nil, errors.Trace(err)
 
42
        }
 
43
        if err := export.readAllConstraints(); err != nil {
 
44
                return nil, errors.Trace(err)
 
45
        }
 
46
 
 
47
        modelConfig, found := export.modelSettings[modelGlobalKey]
 
48
        if !found {
 
49
                return nil, errors.New("missing model config")
 
50
        }
 
51
 
 
52
        blocks, err := export.readBlocks()
 
53
        if err != nil {
 
54
                return nil, errors.Trace(err)
 
55
        }
 
56
 
 
57
        args := description.ModelArgs{
 
58
                Cloud:              dbModel.Cloud(),
 
59
                CloudRegion:        dbModel.CloudRegion(),
 
60
                Owner:              dbModel.Owner(),
 
61
                Config:             modelConfig.Settings,
 
62
                LatestToolsVersion: dbModel.LatestToolsVersion(),
 
63
                Blocks:             blocks,
 
64
        }
 
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)
 
70
        }
 
71
        constraintsArgs, err := export.constraintsArgs(modelKey)
 
72
        if err != nil {
 
73
                return nil, errors.Trace(err)
 
74
        }
 
75
        export.model.SetConstraints(constraintsArgs)
 
76
 
 
77
        if err := export.modelUsers(); err != nil {
 
78
                return nil, errors.Trace(err)
 
79
        }
 
80
        if err := export.machines(); err != nil {
 
81
                return nil, errors.Trace(err)
 
82
        }
 
83
        if err := export.applications(); err != nil {
 
84
                return nil, errors.Trace(err)
 
85
        }
 
86
        if err := export.relations(); err != nil {
 
87
                return nil, errors.Trace(err)
 
88
        }
 
89
        if err := export.spaces(); err != nil {
 
90
                return nil, errors.Trace(err)
 
91
        }
 
92
        if err := export.subnets(); err != nil {
 
93
                return nil, errors.Trace(err)
 
94
        }
 
95
 
 
96
        if err := export.ipaddresses(); err != nil {
 
97
                return nil, errors.Trace(err)
 
98
        }
 
99
 
 
100
        if err := export.linklayerdevices(); err != nil {
 
101
                return nil, errors.Trace(err)
 
102
        }
 
103
 
 
104
        if err := export.sshHostKeys(); err != nil {
 
105
                return nil, errors.Trace(err)
 
106
        }
 
107
        if err := export.storage(); err != nil {
 
108
                return nil, errors.Trace(err)
 
109
        }
 
110
 
 
111
        if err := export.model.Validate(); err != nil {
 
112
                return nil, errors.Trace(err)
 
113
        }
 
114
 
 
115
        export.logExtras()
 
116
 
 
117
        return export.model, nil
 
118
}
 
119
 
 
120
type exporter struct {
 
121
        st      *State
 
122
        dbModel *Model
 
123
        model   description.Model
 
124
        logger  loggo.Logger
 
125
 
 
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
 
134
}
 
135
 
 
136
func (e *exporter) sequences() error {
 
137
        sequences, closer := e.st.getCollection(sequenceC)
 
138
        defer closer()
 
139
 
 
140
        var docs []sequenceDoc
 
141
        if err := sequences.Find(nil).All(&docs); err != nil {
 
142
                return errors.Trace(err)
 
143
        }
 
144
 
 
145
        for _, doc := range docs {
 
146
                e.model.SetSequence(doc.Name, doc.Counter)
 
147
        }
 
148
        return nil
 
149
}
 
150
 
 
151
func (e *exporter) readBlocks() (map[string]string, error) {
 
152
        blocks, closer := e.st.getCollection(blocksC)
 
153
        defer closer()
 
154
 
 
155
        var docs []blockDoc
 
156
        if err := blocks.Find(nil).All(&docs); err != nil {
 
157
                return nil, errors.Trace(err)
 
158
        }
 
159
 
 
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
 
166
        }
 
167
        return result, nil
 
168
}
 
169
 
 
170
func (e *exporter) modelUsers() error {
 
171
        users, err := e.dbModel.Users()
 
172
        if err != nil {
 
173
                return errors.Trace(err)
 
174
        }
 
175
        lastConnections, err := e.readLastConnectionTimes()
 
176
        if err != nil {
 
177
                return errors.Trace(err)
 
178
        }
 
179
        for _, user := range users {
 
180
                lastConn := lastConnections[strings.ToLower(user.UserName)]
 
181
                arg := description.UserArgs{
 
182
                        Name:           user.UserTag,
 
183
                        DisplayName:    user.DisplayName,
 
184
                        CreatedBy:      user.CreatedBy,
 
185
                        DateCreated:    user.DateCreated,
 
186
                        LastConnection: lastConn,
 
187
                        Access:         user.Access,
 
188
                }
 
189
                e.model.AddUser(arg)
 
190
        }
 
191
        return nil
 
192
}
 
193
 
 
194
func (e *exporter) machines() error {
 
195
        machines, err := e.st.AllMachines()
 
196
        if err != nil {
 
197
                return errors.Trace(err)
 
198
        }
 
199
        e.logger.Debugf("found %d machines", len(machines))
 
200
 
 
201
        instances, err := e.loadMachineInstanceData()
 
202
        if err != nil {
 
203
                return errors.Trace(err)
 
204
        }
 
205
        blockDevices, err := e.loadMachineBlockDevices()
 
206
        if err != nil {
 
207
                return errors.Trace(err)
 
208
        }
 
209
 
 
210
        // Read all the open ports documents.
 
211
        openedPorts, closer := e.st.getCollection(openedPortsC)
 
212
        defer closer()
 
213
        var portsData []portsDoc
 
214
        if err := openedPorts.Find(nil).All(&portsData); err != nil {
 
215
                return errors.Annotate(err, "opened ports")
 
216
        }
 
217
        e.logger.Debugf("found %d openedPorts docs", len(portsData))
 
218
 
 
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
 
222
        // any children.
 
223
        machineMap := make(map[string]description.Machine)
 
224
 
 
225
        for _, machine := range machines {
 
226
                e.logger.Debugf("export machine %s", machine.Id())
 
227
 
 
228
                var exParent description.Machine
 
229
                if parentId := ParentId(machine.Id()); parentId != "" {
 
230
                        var found bool
 
231
                        exParent, found = machineMap[parentId]
 
232
                        if !found {
 
233
                                return errors.Errorf("machine %s missing parent", machine.Id())
 
234
                        }
 
235
                }
 
236
 
 
237
                exMachine, err := e.newMachine(exParent, machine, instances, portsData, blockDevices)
 
238
                if err != nil {
 
239
                        return errors.Trace(err)
 
240
                }
 
241
                machineMap[machine.Id()] = exMachine
 
242
        }
 
243
 
 
244
        return nil
 
245
}
 
246
 
 
247
func (e *exporter) loadMachineInstanceData() (map[string]instanceData, error) {
 
248
        instanceDataCollection, closer := e.st.getCollection(instanceDataC)
 
249
        defer closer()
 
250
 
 
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")
 
255
        }
 
256
        e.logger.Debugf("found %d instanceData", len(instData))
 
257
        for _, data := range instData {
 
258
                instances[data.MachineId] = data
 
259
        }
 
260
        return instances, nil
 
261
}
 
262
 
 
263
func (e *exporter) loadMachineBlockDevices() (map[string][]BlockDeviceInfo, error) {
 
264
        coll, closer := e.st.getCollection(blockDevicesC)
 
265
        defer closer()
 
266
 
 
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")
 
271
        }
 
272
        e.logger.Debugf("found %d block device records", len(deviceData))
 
273
        for _, data := range deviceData {
 
274
                result[data.Machine] = data.BlockDevices
 
275
        }
 
276
        return result, nil
 
277
}
 
278
 
 
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,
 
287
        }
 
288
 
 
289
        if supported, ok := machine.SupportedContainers(); ok {
 
290
                containers := make([]string, len(supported))
 
291
                for i, containerType := range supported {
 
292
                        containers[i] = string(containerType)
 
293
                }
 
294
                args.SupportedContainers = &containers
 
295
        }
 
296
 
 
297
        for _, job := range machine.Jobs() {
 
298
                args.Jobs = append(args.Jobs, job.MigrationValue())
 
299
        }
 
300
 
 
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
 
304
        if exParent == nil {
 
305
                exMachine = e.model.AddMachine(args)
 
306
        } else {
 
307
                exMachine = exParent.AddContainer(args)
 
308
        }
 
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))
 
315
 
 
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]
 
319
        if !found {
 
320
                return nil, errors.NotValidf("missing instance data for machine %s", machine.Id())
 
321
        }
 
322
        exMachine.SetInstance(e.newCloudInstanceArgs(instData))
 
323
 
 
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,
 
330
                        Label:          device.Label,
 
331
                        UUID:           device.UUID,
 
332
                        HardwareID:     device.HardwareId,
 
333
                        BusAddress:     device.BusAddress,
 
334
                        Size:           device.Size,
 
335
                        FilesystemType: device.FilesystemType,
 
336
                        InUse:          device.InUse,
 
337
                        MountPoint:     device.MountPoint,
 
338
                })
 
339
        }
 
340
 
 
341
        // Find the current machine status.
 
342
        globalKey := machine.globalKey()
 
343
        statusArgs, err := e.statusArgs(globalKey)
 
344
        if err != nil {
 
345
                return nil, errors.Annotatef(err, "status for machine %s", machine.Id())
 
346
        }
 
347
        exMachine.SetStatus(statusArgs)
 
348
        exMachine.SetStatusHistory(e.statusHistoryArgs(globalKey))
 
349
 
 
350
        tools, err := machine.AgentTools()
 
351
        if err != nil {
 
352
                // This means the tools aren't set, but they should be.
 
353
                return nil, errors.Trace(err)
 
354
        }
 
355
 
 
356
        exMachine.SetTools(description.AgentToolsArgs{
 
357
                Version: tools.Version,
 
358
                URL:     tools.URL,
 
359
                SHA256:  tools.SHA256,
 
360
                Size:    tools.Size,
 
361
        })
 
362
 
 
363
        for _, args := range e.openedPortsArgsForMachine(machine.Id(), portsData) {
 
364
                exMachine.AddOpenedPorts(args)
 
365
        }
 
366
 
 
367
        exMachine.SetAnnotations(e.getAnnotations(globalKey))
 
368
 
 
369
        constraintsArgs, err := e.constraintsArgs(globalKey)
 
370
        if err != nil {
 
371
                return nil, errors.Trace(err)
 
372
        }
 
373
        exMachine.SetConstraints(constraintsArgs)
 
374
 
 
375
        return exMachine, nil
 
376
}
 
377
 
 
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,
 
388
                                        ToPort:   p.ToPort,
 
389
                                        Protocol: p.Protocol,
 
390
                                })
 
391
                        }
 
392
                        result = append(result, args)
 
393
                }
 
394
        }
 
395
        return result
 
396
}
 
397
 
 
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))
 
402
        }
 
403
        return result
 
404
}
 
405
 
 
406
func (e *exporter) newAddressArgs(a address) description.AddressArgs {
 
407
        return description.AddressArgs{
 
408
                Value:  a.Value,
 
409
                Type:   a.AddressType,
 
410
                Scope:  a.Scope,
 
411
                Origin: a.Origin,
 
412
        }
 
413
}
 
414
 
 
415
func (e *exporter) newCloudInstanceArgs(data instanceData) description.CloudInstanceArgs {
 
416
        inst := description.CloudInstanceArgs{
 
417
                InstanceId: string(data.InstanceId),
 
418
                Status:     data.Status,
 
419
        }
 
420
        if data.Arch != nil {
 
421
                inst.Architecture = *data.Arch
 
422
        }
 
423
        if data.Mem != nil {
 
424
                inst.Memory = *data.Mem
 
425
        }
 
426
        if data.RootDisk != nil {
 
427
                inst.RootDisk = *data.RootDisk
 
428
        }
 
429
        if data.CpuCores != nil {
 
430
                inst.CpuCores = *data.CpuCores
 
431
        }
 
432
        if data.CpuPower != nil {
 
433
                inst.CpuPower = *data.CpuPower
 
434
        }
 
435
        if data.Tags != nil {
 
436
                inst.Tags = *data.Tags
 
437
        }
 
438
        if data.AvailZone != nil {
 
439
                inst.AvailabilityZone = *data.AvailZone
 
440
        }
 
441
        return inst
 
442
}
 
443
 
 
444
func (e *exporter) applications() error {
 
445
        applications, err := e.st.AllApplications()
 
446
        if err != nil {
 
447
                return errors.Trace(err)
 
448
        }
 
449
        e.logger.Debugf("found %d applications", len(applications))
 
450
 
 
451
        refcounts, err := e.readAllSettingsRefCounts()
 
452
        if err != nil {
 
453
                return errors.Trace(err)
 
454
        }
 
455
 
 
456
        e.units, err = e.readAllUnits()
 
457
        if err != nil {
 
458
                return errors.Trace(err)
 
459
        }
 
460
 
 
461
        meterStatus, err := e.readAllMeterStatus()
 
462
        if err != nil {
 
463
                return errors.Trace(err)
 
464
        }
 
465
 
 
466
        leaders, err := e.readApplicationLeaders()
 
467
        if err != nil {
 
468
                return errors.Trace(err)
 
469
        }
 
470
 
 
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)
 
476
                }
 
477
        }
 
478
        return nil
 
479
}
 
480
 
 
481
func (e *exporter) readApplicationLeaders() (map[string]string, error) {
 
482
        client, err := e.st.getLeadershipLeaseClient()
 
483
        if err != nil {
 
484
                return nil, errors.Trace(err)
 
485
        }
 
486
        leases := client.Leases()
 
487
        result := make(map[string]string, len(leases))
 
488
        for key, value := range leases {
 
489
                result[key] = value.Holder
 
490
        }
 
491
        return result, nil
 
492
}
 
493
 
 
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())
 
497
 
 
498
        applicationSettingsDoc, found := e.modelSettings[settingsKey]
 
499
        if !found {
 
500
                return errors.Errorf("missing settings for application %q", application.Name())
 
501
        }
 
502
        refCount, found := refcounts[settingsKey]
 
503
        if !found {
 
504
                return errors.Errorf("missing settings refcount for application %q", application.Name())
 
505
        }
 
506
        leadershipSettingsDoc, found := e.modelSettings[leadershipKey]
 
507
        if !found {
 
508
                return errors.Errorf("missing leadership settings for application %q", application.Name())
 
509
        }
 
510
 
 
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,
 
523
                Leader:               leader,
 
524
                LeadershipSettings:   leadershipSettingsDoc.Settings,
 
525
                MetricsCredentials:   application.doc.MetricCredentials,
 
526
        }
 
527
        exApplication := e.model.AddApplication(args)
 
528
        // Find the current application status.
 
529
        globalKey := application.globalKey()
 
530
        statusArgs, err := e.statusArgs(globalKey)
 
531
        if err != nil {
 
532
                return errors.Annotatef(err, "status for application %s", application.Name())
 
533
        }
 
534
        exApplication.SetStatus(statusArgs)
 
535
        exApplication.SetStatusHistory(e.statusHistoryArgs(globalKey))
 
536
        exApplication.SetAnnotations(e.getAnnotations(globalKey))
 
537
 
 
538
        constraintsArgs, err := e.constraintsArgs(globalKey)
 
539
        if err != nil {
 
540
                return errors.Trace(err)
 
541
        }
 
542
        exApplication.SetConstraints(constraintsArgs)
 
543
 
 
544
        for _, unit := range units {
 
545
                agentKey := unit.globalAgentKey()
 
546
                unitMeterStatus, found := meterStatus[agentKey]
 
547
                if !found {
 
548
                        return errors.Errorf("missing meter status for unit %s", unit.Name())
 
549
                }
 
550
 
 
551
                workloadVersion, err := unit.WorkloadVersion()
 
552
                if err != nil {
 
553
                        return errors.Trace(err)
 
554
                }
 
555
                args := description.UnitArgs{
 
556
                        Tag:             unit.UnitTag(),
 
557
                        Machine:         names.NewMachineTag(unit.doc.MachineId),
 
558
                        WorkloadVersion: workloadVersion,
 
559
                        PasswordHash:    unit.doc.PasswordHash,
 
560
                        MeterStatusCode: unitMeterStatus.Code,
 
561
                        MeterStatusInfo: unitMeterStatus.Info,
 
562
                }
 
563
                if principalName, isSubordinate := unit.PrincipalName(); isSubordinate {
 
564
                        args.Principal = names.NewUnitTag(principalName)
 
565
                }
 
566
                if subs := unit.SubordinateNames(); len(subs) > 0 {
 
567
                        for _, subName := range subs {
 
568
                                args.Subordinates = append(args.Subordinates, names.NewUnitTag(subName))
 
569
                        }
 
570
                }
 
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)
 
576
                if err != nil {
 
577
                        return errors.Annotatef(err, "workload status for unit %s", unit.Name())
 
578
                }
 
579
                exUnit.SetWorkloadStatus(statusArgs)
 
580
                exUnit.SetWorkloadStatusHistory(e.statusHistoryArgs(globalKey))
 
581
 
 
582
                statusArgs, err = e.statusArgs(agentKey)
 
583
                if err != nil {
 
584
                        return errors.Annotatef(err, "agent status for unit %s", unit.Name())
 
585
                }
 
586
                exUnit.SetAgentStatus(statusArgs)
 
587
                exUnit.SetAgentStatusHistory(e.statusHistoryArgs(agentKey))
 
588
 
 
589
                workloadVersionKey := unit.globalWorkloadVersionKey()
 
590
                exUnit.SetWorkloadVersionHistory(e.statusHistoryArgs(workloadVersionKey))
 
591
 
 
592
                tools, err := unit.AgentTools()
 
593
                if err != nil {
 
594
                        // This means the tools aren't set, but they should be.
 
595
                        return errors.Trace(err)
 
596
                }
 
597
                exUnit.SetTools(description.AgentToolsArgs{
 
598
                        Version: tools.Version,
 
599
                        URL:     tools.URL,
 
600
                        SHA256:  tools.SHA256,
 
601
                        Size:    tools.Size,
 
602
                })
 
603
                exUnit.SetAnnotations(e.getAnnotations(globalKey))
 
604
 
 
605
                constraintsArgs, err := e.constraintsArgs(agentKey)
 
606
                if err != nil {
 
607
                        return errors.Trace(err)
 
608
                }
 
609
                exUnit.SetConstraints(constraintsArgs)
 
610
        }
 
611
 
 
612
        return nil
 
613
}
 
614
 
 
615
func (e *exporter) relations() error {
 
616
        rels, err := e.st.AllRelations()
 
617
        if err != nil {
 
618
                return errors.Trace(err)
 
619
        }
 
620
        e.logger.Debugf("read %d relations", len(rels))
 
621
 
 
622
        relationScopes, err := e.readAllRelationScopes()
 
623
        if err != nil {
 
624
                return errors.Trace(err)
 
625
        }
 
626
 
 
627
        for _, relation := range rels {
 
628
                exRelation := e.model.AddRelation(description.RelationArgs{
 
629
                        Id:  relation.Id(),
 
630
                        Key: relation.String(),
 
631
                })
 
632
                for _, ep := range relation.Endpoints() {
 
633
                        exEndPoint := exRelation.AddEndpoint(description.EndpointArgs{
 
634
                                ApplicationName: ep.ApplicationName,
 
635
                                Name:            ep.Name,
 
636
                                Role:            string(ep.Role),
 
637
                                Interface:       ep.Interface,
 
638
                                Optional:        ep.Optional,
 
639
                                Limit:           ep.Limit,
 
640
                                Scope:           string(ep.Scope),
 
641
                        })
 
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)
 
647
                                if err != nil {
 
648
                                        return errors.Trace(err)
 
649
                                }
 
650
                                key := ru.key()
 
651
                                if !relationScopes.Contains(key) {
 
652
                                        return errors.Errorf("missing relation scope for %s and %s", relation, unit.Name())
 
653
                                }
 
654
                                settingsDoc, found := e.modelSettings[key]
 
655
                                if !found {
 
656
                                        return errors.Errorf("missing relation settings for %s and %s", relation, unit.Name())
 
657
                                }
 
658
                                exEndPoint.SetUnitSettings(unit.Name(), settingsDoc.Settings)
 
659
                        }
 
660
                }
 
661
        }
 
662
        return nil
 
663
}
 
664
 
 
665
func (e *exporter) spaces() error {
 
666
        spaces, err := e.st.AllSpaces()
 
667
        if err != nil {
 
668
                return errors.Trace(err)
 
669
        }
 
670
        e.logger.Debugf("read %d spaces", len(spaces))
 
671
 
 
672
        for _, space := range spaces {
 
673
                e.model.AddSpace(description.SpaceArgs{
 
674
                        Name:       space.Name(),
 
675
                        Public:     space.IsPublic(),
 
676
                        ProviderID: string(space.ProviderId()),
 
677
                })
 
678
        }
 
679
        return nil
 
680
}
 
681
 
 
682
func (e *exporter) linklayerdevices() error {
 
683
        linklayerdevices, err := e.st.AllLinkLayerDevices()
 
684
        if err != nil {
 
685
                return errors.Trace(err)
 
686
        }
 
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(),
 
692
                        Name:        device.Name(),
 
693
                        MTU:         device.MTU(),
 
694
                        Type:        string(device.Type()),
 
695
                        MACAddress:  device.MACAddress(),
 
696
                        IsAutoStart: device.IsAutoStart(),
 
697
                        IsUp:        device.IsUp(),
 
698
                        ParentName:  device.ParentName(),
 
699
                })
 
700
        }
 
701
        return nil
 
702
}
 
703
 
 
704
func (e *exporter) subnets() error {
 
705
        subnets, err := e.st.AllSubnets()
 
706
        if err != nil {
 
707
                return errors.Trace(err)
 
708
        }
 
709
        e.logger.Debugf("read %d subnets", len(subnets))
 
710
 
 
711
        for _, subnet := range subnets {
 
712
                e.model.AddSubnet(description.SubnetArgs{
 
713
                        CIDR:             subnet.CIDR(),
 
714
                        ProviderId:       string(subnet.ProviderId()),
 
715
                        VLANTag:          subnet.VLANTag(),
 
716
                        AvailabilityZone: subnet.AvailabilityZone(),
 
717
                        SpaceName:        subnet.SpaceName(),
 
718
                })
 
719
        }
 
720
        return nil
 
721
}
 
722
 
 
723
func (e *exporter) ipaddresses() error {
 
724
        ipaddresses, err := e.st.AllIPAddresses()
 
725
        if err != nil {
 
726
                return errors.Trace(err)
 
727
        }
 
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()),
 
736
                        Value:            addr.Value(),
 
737
                        DNSServers:       addr.DNSServers(),
 
738
                        DNSSearchDomains: addr.DNSSearchDomains(),
 
739
                        GatewayAddress:   addr.GatewayAddress(),
 
740
                })
 
741
        }
 
742
        return nil
 
743
}
 
744
 
 
745
func (e *exporter) sshHostKeys() error {
 
746
        machines, err := e.st.AllMachines()
 
747
        if err != nil {
 
748
                return errors.Trace(err)
 
749
        }
 
750
        for _, machine := range machines {
 
751
                keys, err := e.st.GetSSHHostKeys(machine.MachineTag())
 
752
                if errors.IsNotFound(err) {
 
753
                        continue
 
754
                } else if err != nil {
 
755
                        return errors.Trace(err)
 
756
                }
 
757
                if len(keys) == 0 {
 
758
                        continue
 
759
                }
 
760
                e.model.AddSSHHostKey(description.SSHHostKeyArgs{
 
761
                        MachineID: machine.Id(),
 
762
                        Keys:      keys,
 
763
                })
 
764
        }
 
765
        return nil
 
766
}
 
767
 
 
768
func (e *exporter) readAllRelationScopes() (set.Strings, error) {
 
769
        relationScopes, closer := e.st.getCollection(relationScopesC)
 
770
        defer closer()
 
771
 
 
772
        docs := []relationScopeDoc{}
 
773
        err := relationScopes.Find(nil).All(&docs)
 
774
        if err != nil {
 
775
                return nil, errors.Annotate(err, "cannot get all relation scopes")
 
776
        }
 
777
        e.logger.Debugf("found %d relationScope docs", len(docs))
 
778
 
 
779
        result := set.NewStrings()
 
780
        for _, doc := range docs {
 
781
                result.Add(doc.Key)
 
782
        }
 
783
        return result, nil
 
784
}
 
785
 
 
786
func (e *exporter) readAllUnits() (map[string][]*Unit, error) {
 
787
        unitsCollection, closer := e.st.getCollection(unitsC)
 
788
        defer closer()
 
789
 
 
790
        docs := []unitDoc{}
 
791
        err := unitsCollection.Find(nil).All(&docs)
 
792
        if err != nil {
 
793
                return nil, errors.Annotate(err, "cannot get all units")
 
794
        }
 
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))
 
800
        }
 
801
        return result, nil
 
802
}
 
803
 
 
804
func (e *exporter) readAllMeterStatus() (map[string]*meterStatusDoc, error) {
 
805
        meterStatuses, closer := e.st.getCollection(meterStatusC)
 
806
        defer closer()
 
807
 
 
808
        docs := []meterStatusDoc{}
 
809
        err := meterStatuses.Find(nil).All(&docs)
 
810
        if err != nil {
 
811
                return nil, errors.Annotate(err, "cannot get all meter status docs")
 
812
        }
 
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
 
817
        }
 
818
        return result, nil
 
819
}
 
820
 
 
821
func (e *exporter) readLastConnectionTimes() (map[string]time.Time, error) {
 
822
        lastConnections, closer := e.st.getCollection(modelUserLastConnectionC)
 
823
        defer closer()
 
824
 
 
825
        var docs []modelUserLastConnectionDoc
 
826
        if err := lastConnections.Find(nil).All(&docs); err != nil {
 
827
                return nil, errors.Trace(err)
 
828
        }
 
829
 
 
830
        result := make(map[string]time.Time)
 
831
        for _, doc := range docs {
 
832
                result[doc.UserName] = doc.LastConnection.UTC()
 
833
        }
 
834
        return result, nil
 
835
}
 
836
 
 
837
func (e *exporter) readAllAnnotations() error {
 
838
        annotations, closer := e.st.getCollection(annotationsC)
 
839
        defer closer()
 
840
 
 
841
        var docs []annotatorDoc
 
842
        if err := annotations.Find(nil).All(&docs); err != nil {
 
843
                return errors.Trace(err)
 
844
        }
 
845
        e.logger.Debugf("read %d annotations docs", len(docs))
 
846
 
 
847
        e.annotations = make(map[string]annotatorDoc)
 
848
        for _, doc := range docs {
 
849
                e.annotations[doc.GlobalKey] = doc
 
850
        }
 
851
        return nil
 
852
}
 
853
 
 
854
func (e *exporter) readAllConstraints() error {
 
855
        constraintsCollection, closer := e.st.getCollection(constraintsC)
 
856
        defer closer()
 
857
 
 
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.
 
861
        var docs []bson.M
 
862
        err := constraintsCollection.Find(nil).All(&docs)
 
863
        if err != nil {
 
864
                return errors.Annotate(err, "failed to read constraints collection")
 
865
        }
 
866
 
 
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)
 
871
                if !ok {
 
872
                        return errors.Errorf("expected string, got %s (%T)", doc["_id"], doc["_id"])
 
873
                }
 
874
                id := e.st.localID(docId)
 
875
                e.constraints[id] = doc
 
876
                e.logger.Debugf("doc[%q] = %#v", id, doc)
 
877
        }
 
878
        return nil
 
879
}
 
880
 
 
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]
 
886
        if found {
 
887
                delete(e.annotations, key)
 
888
        }
 
889
        return result.Annotations
 
890
}
 
891
 
 
892
func (e *exporter) readAllSettings() error {
 
893
        settings, closer := e.st.getCollection(settingsC)
 
894
        defer closer()
 
895
 
 
896
        var docs []settingsDoc
 
897
        if err := settings.Find(nil).All(&docs); err != nil {
 
898
                return errors.Trace(err)
 
899
        }
 
900
 
 
901
        e.modelSettings = make(map[string]settingsDoc)
 
902
        for _, doc := range docs {
 
903
                key := e.st.localID(doc.DocID)
 
904
                e.modelSettings[key] = doc
 
905
        }
 
906
        return nil
 
907
}
 
908
 
 
909
func (e *exporter) readAllStatuses() error {
 
910
        statuses, closer := e.st.getCollection(statusesC)
 
911
        defer closer()
 
912
 
 
913
        var docs []bson.M
 
914
        err := statuses.Find(nil).All(&docs)
 
915
        if err != nil {
 
916
                return errors.Annotate(err, "failed to read status collection")
 
917
        }
 
918
 
 
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)
 
923
                if !ok {
 
924
                        return errors.Errorf("expected string, got %s (%T)", doc["_id"], doc["_id"])
 
925
                }
 
926
                id := e.st.localID(docId)
 
927
                e.status[id] = doc
 
928
        }
 
929
 
 
930
        return nil
 
931
}
 
932
 
 
933
func (e *exporter) readAllStatusHistory() error {
 
934
        statuses, closer := e.st.getCollection(statusesHistoryC)
 
935
        defer closer()
 
936
 
 
937
        count := 0
 
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()
 
944
        defer iter.Close()
 
945
        for iter.Next(&doc) {
 
946
                history := e.statusHistory[doc.GlobalKey]
 
947
                e.statusHistory[doc.GlobalKey] = append(history, doc)
 
948
                count++
 
949
        }
 
950
 
 
951
        if err := iter.Err(); err != nil {
 
952
                return errors.Annotate(err, "failed to read status history collection")
 
953
        }
 
954
 
 
955
        e.logger.Debugf("read %d status history documents", count)
 
956
 
 
957
        return nil
 
958
}
 
959
 
 
960
func (e *exporter) statusArgs(globalKey string) (description.StatusArgs, error) {
 
961
        result := description.StatusArgs{}
 
962
        statusDoc, found := e.status[globalKey]
 
963
        if !found {
 
964
                return result, errors.NotFoundf("status data for %s", globalKey)
 
965
        }
 
966
 
 
967
        status, ok := statusDoc["status"].(string)
 
968
        if !ok {
 
969
                return result, errors.Errorf("expected string for status, got %T", statusDoc["status"])
 
970
        }
 
971
        info, ok := statusDoc["statusinfo"].(string)
 
972
        if !ok {
 
973
                return result, errors.Errorf("expected string for statusinfo, got %T", statusDoc["statusinfo"])
 
974
        }
 
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)
 
978
        if !ok {
 
979
                return result, errors.Errorf("expected map for data, got %T", statusDoc["statusdata"])
 
980
        }
 
981
        dataMap := map[string]interface{}(data)
 
982
        updated, ok := statusDoc["updated"].(int64)
 
983
        if !ok {
 
984
                return result, errors.Errorf("expected int64 for updated, got %T", statusDoc["updated"])
 
985
        }
 
986
 
 
987
        result.Value = status
 
988
        result.Message = info
 
989
        result.Data = dataMap
 
990
        result.Updated = time.Unix(0, updated)
 
991
        return result, nil
 
992
}
 
993
 
 
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),
 
1004
                }
 
1005
        }
 
1006
 
 
1007
        return result
 
1008
}
 
1009
 
 
1010
func (e *exporter) constraintsArgs(globalKey string) (description.ConstraintsArgs, error) {
 
1011
        doc, found := e.constraints[globalKey]
 
1012
        if !found {
 
1013
                // No constraints for this key.
 
1014
                e.logger.Debugf("no constraints found for key %q", globalKey)
 
1015
                return description.ConstraintsArgs{}, nil
 
1016
        }
 
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) {
 
1023
                case nil:
 
1024
                case string:
 
1025
                        return value
 
1026
                default:
 
1027
                        optionalErr = errors.Errorf("expected uint64 for %s, got %T", name, value)
 
1028
                }
 
1029
                return ""
 
1030
        }
 
1031
        optionalInt := func(name string) uint64 {
 
1032
                switch value := doc[name].(type) {
 
1033
                case nil:
 
1034
                case uint64:
 
1035
                        return value
 
1036
                case int64:
 
1037
                        return uint64(value)
 
1038
                default:
 
1039
                        optionalErr = errors.Errorf("expected uint64 for %s, got %T", name, value)
 
1040
                }
 
1041
                return 0
 
1042
        }
 
1043
        optionalStringSlice := func(name string) []string {
 
1044
                switch value := doc[name].(type) {
 
1045
                case nil:
 
1046
                case []string:
 
1047
                        return value
 
1048
                default:
 
1049
                        optionalErr = errors.Errorf("expected []string] for %s, got %T", name, value)
 
1050
                }
 
1051
                return nil
 
1052
        }
 
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"),
 
1064
        }
 
1065
        if optionalErr != nil {
 
1066
                return description.ConstraintsArgs{}, errors.Trace(optionalErr)
 
1067
        }
 
1068
        return result, nil
 
1069
}
 
1070
 
 
1071
func (e *exporter) readAllSettingsRefCounts() (map[string]int, error) {
 
1072
        refCounts, closer := e.st.getCollection(settingsrefsC)
 
1073
        defer closer()
 
1074
 
 
1075
        var docs []bson.M
 
1076
        err := refCounts.Find(nil).All(&docs)
 
1077
        if err != nil {
 
1078
                return nil, errors.Annotate(err, "failed to read settings refcount collection")
 
1079
        }
 
1080
 
 
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)
 
1085
                if !ok {
 
1086
                        return nil, errors.Errorf("expected string, got %s (%T)", doc["_id"], doc["_id"])
 
1087
                }
 
1088
                id := e.st.localID(docId)
 
1089
                count, ok := doc["refcount"].(int)
 
1090
                if !ok {
 
1091
                        return nil, errors.Errorf("expected int, got %s (%T)", doc["refcount"], doc["refcount"])
 
1092
                }
 
1093
                result[id] = count
 
1094
        }
 
1095
 
 
1096
        return result, nil
 
1097
}
 
1098
 
 
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)
 
1107
        }
 
1108
}
 
1109
 
 
1110
func (e *exporter) storage() error {
 
1111
        if err := e.volumes(); err != nil {
 
1112
                return errors.Trace(err)
 
1113
        }
 
1114
        if err := e.filesystems(); err != nil {
 
1115
                return errors.Trace(err)
 
1116
        }
 
1117
        return nil
 
1118
}
 
1119
 
 
1120
func (e *exporter) volumes() error {
 
1121
        coll, closer := e.st.getCollection(volumesC)
 
1122
        defer closer()
 
1123
 
 
1124
        attachments, err := e.readVolumeAttachments()
 
1125
        if err != nil {
 
1126
                return errors.Trace(err)
 
1127
        }
 
1128
 
 
1129
        var doc volumeDoc
 
1130
        iter := coll.Find(nil).Sort("_id").Iter()
 
1131
        defer iter.Close()
 
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)
 
1136
                }
 
1137
        }
 
1138
        if err := iter.Err(); err != nil {
 
1139
                return errors.Annotate(err, "failed to read volumes")
 
1140
        }
 
1141
        return nil
 
1142
}
 
1143
 
 
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
 
1149
        }
 
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
 
1159
        } else {
 
1160
                params, _ := vol.Params()
 
1161
                logger.Debugf("  params %#v", params)
 
1162
                args.Size = params.Size
 
1163
                args.Pool = params.Pool
 
1164
        }
 
1165
 
 
1166
        globalKey := vol.globalKey()
 
1167
        statusArgs, err := e.statusArgs(globalKey)
 
1168
        if err != nil {
 
1169
                return errors.Annotatef(err, "status for volume %s", vol.doc.Name)
 
1170
        }
 
1171
 
 
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)
 
1178
        }
 
1179
        for _, doc := range volAttachments {
 
1180
                va := volumeAttachment{doc}
 
1181
                logger.Debugf("  attachment %#v", doc)
 
1182
                args := description.VolumeAttachmentArgs{
 
1183
                        Machine: va.Machine(),
 
1184
                }
 
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
 
1192
                } else {
 
1193
                        params, _ := va.Params()
 
1194
                        logger.Debugf("    params %#v", params)
 
1195
                        args.ReadOnly = params.ReadOnly
 
1196
                }
 
1197
                exVolume.AddAttachment(args)
 
1198
        }
 
1199
        return nil
 
1200
}
 
1201
 
 
1202
func (e *exporter) readVolumeAttachments() (map[string][]volumeAttachmentDoc, error) {
 
1203
        coll, closer := e.st.getCollection(volumeAttachmentsC)
 
1204
        defer closer()
 
1205
 
 
1206
        result := make(map[string][]volumeAttachmentDoc)
 
1207
        var doc volumeAttachmentDoc
 
1208
        var count int
 
1209
        iter := coll.Find(nil).Iter()
 
1210
        defer iter.Close()
 
1211
        for iter.Next(&doc) {
 
1212
                result[doc.Volume] = append(result[doc.Volume], doc)
 
1213
                count++
 
1214
        }
 
1215
        if err := iter.Err(); err != nil {
 
1216
                return nil, errors.Annotate(err, "failed to read volumes attachments")
 
1217
        }
 
1218
        e.logger.Debugf("read %d volume attachment documents", count)
 
1219
        return result, nil
 
1220
}
 
1221
 
 
1222
func (e *exporter) filesystems() error {
 
1223
        coll, closer := e.st.getCollection(filesystemsC)
 
1224
        defer closer()
 
1225
 
 
1226
        attachments, err := e.readFilesystemAttachments()
 
1227
        if err != nil {
 
1228
                return errors.Trace(err)
 
1229
        }
 
1230
 
 
1231
        var doc filesystemDoc
 
1232
        iter := coll.Find(nil).Sort("_id").Iter()
 
1233
        defer iter.Close()
 
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)
 
1238
                }
 
1239
        }
 
1240
        if err := iter.Err(); err != nil {
 
1241
                return errors.Annotate(err, "failed to read filesystems")
 
1242
        }
 
1243
        return nil
 
1244
}
 
1245
 
 
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(),
 
1253
                Storage: storage,
 
1254
                Volume:  volume,
 
1255
                Binding: fs.LifeBinding(),
 
1256
        }
 
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
 
1264
        } else {
 
1265
                params, _ := fs.Params()
 
1266
                logger.Debugf("  params %#v", params)
 
1267
                args.Size = params.Size
 
1268
                args.Pool = params.Pool
 
1269
        }
 
1270
 
 
1271
        globalKey := fs.globalKey()
 
1272
        statusArgs, err := e.statusArgs(globalKey)
 
1273
        if err != nil {
 
1274
                return errors.Annotatef(err, "status for filesystem %s", fs.doc.FilesystemId)
 
1275
        }
 
1276
 
 
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)
 
1283
        }
 
1284
        for _, doc := range fsAttachments {
 
1285
                va := filesystemAttachment{doc}
 
1286
                logger.Debugf("  attachment %#v", doc)
 
1287
                args := description.FilesystemAttachmentArgs{
 
1288
                        Machine: va.Machine(),
 
1289
                }
 
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
 
1295
                } else {
 
1296
                        params, _ := va.Params()
 
1297
                        logger.Debugf("    params %#v", params)
 
1298
                        args.ReadOnly = params.ReadOnly
 
1299
                        args.MountPoint = params.Location
 
1300
                }
 
1301
                exFilesystem.AddAttachment(args)
 
1302
        }
 
1303
        return nil
 
1304
}
 
1305
 
 
1306
func (e *exporter) readFilesystemAttachments() (map[string][]filesystemAttachmentDoc, error) {
 
1307
        coll, closer := e.st.getCollection(filesystemAttachmentsC)
 
1308
        defer closer()
 
1309
 
 
1310
        result := make(map[string][]filesystemAttachmentDoc)
 
1311
        var doc filesystemAttachmentDoc
 
1312
        var count int
 
1313
        iter := coll.Find(nil).Iter()
 
1314
        defer iter.Close()
 
1315
        for iter.Next(&doc) {
 
1316
                result[doc.Filesystem] = append(result[doc.Filesystem], doc)
 
1317
                count++
 
1318
        }
 
1319
        if err := iter.Err(); err != nil {
 
1320
                return nil, errors.Annotate(err, "failed to read filesystem attachments")
 
1321
        }
 
1322
        e.logger.Debugf("read %d filesystem attachment documents", count)
 
1323
        return result, nil
 
1324
}