~juju-qa/ubuntu/xenial/juju/xenial-2.0-beta3

« back to all changes in this revision

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

  • Committer: Martin Packman
  • Date: 2016-03-30 19:31:08 UTC
  • mfrom: (1.1.41)
  • Revision ID: martin.packman@canonical.com-20160330193108-h9iz3ak334uk0z5r
Merge new upstream source 2.0~beta3

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
// Copyright 2014 Canonical Ltd.
 
2
// Licensed under the AGPLv3, see LICENCE file for details.
 
3
 
 
4
package state
 
5
 
 
6
import (
 
7
        "net"
 
8
 
 
9
        "github.com/juju/errors"
 
10
        "github.com/juju/names"
 
11
        jujutxn "github.com/juju/txn"
 
12
        "github.com/juju/utils"
 
13
        "gopkg.in/mgo.v2"
 
14
        "gopkg.in/mgo.v2/bson"
 
15
        "gopkg.in/mgo.v2/txn"
 
16
 
 
17
        "github.com/juju/juju/instance"
 
18
        "github.com/juju/juju/network"
 
19
)
 
20
 
 
21
// addIPAddress implements the State method to add an IP address.
 
22
func addIPAddress(st *State, addr network.Address, subnetid string) (ipaddress *IPAddress, err error) {
 
23
        defer errors.DeferredAnnotatef(&err, "cannot add IP address %q", addr)
 
24
 
 
25
        // This checks for a missing value as well as invalid values
 
26
        ip := net.ParseIP(addr.Value)
 
27
        if ip == nil {
 
28
                return nil, errors.NotValidf("address")
 
29
        }
 
30
 
 
31
        // Generate the UUID for the new IP address.
 
32
        uuid, err := utils.NewUUID()
 
33
        if err != nil {
 
34
                return nil, err
 
35
        }
 
36
 
 
37
        addressID := st.docID(addr.Value)
 
38
        ipDoc := ipaddressDoc{
 
39
                DocID:     addressID,
 
40
                ModelUUID: st.ModelUUID(),
 
41
                UUID:      uuid.String(),
 
42
                Life:      Alive,
 
43
                State:     AddressStateUnknown,
 
44
                SubnetId:  subnetid,
 
45
                Value:     addr.Value,
 
46
                Type:      string(addr.Type),
 
47
                Scope:     string(addr.Scope),
 
48
                SpaceName: string(addr.SpaceName),
 
49
        }
 
50
 
 
51
        ipaddress = &IPAddress{doc: ipDoc, st: st}
 
52
        ops := []txn.Op{
 
53
                assertModelAliveOp(st.ModelUUID()),
 
54
                {
 
55
                        C:      legacyipaddressesC,
 
56
                        Id:     addressID,
 
57
                        Assert: txn.DocMissing,
 
58
                        Insert: ipDoc,
 
59
                },
 
60
        }
 
61
 
 
62
        err = st.runTransaction(ops)
 
63
        switch err {
 
64
        case txn.ErrAborted:
 
65
                if err := checkModeLife(st); err != nil {
 
66
                        return nil, errors.Trace(err)
 
67
                }
 
68
                if _, err = st.IPAddress(addr.Value); err == nil {
 
69
                        return nil, errors.AlreadyExistsf("address")
 
70
                }
 
71
        case nil:
 
72
                return ipaddress, nil
 
73
        }
 
74
        return nil, errors.Trace(err)
 
75
}
 
76
 
 
77
// ipAddress implements the State method to return an existing IP
 
78
// address by its value.
 
79
func ipAddress(st *State, value string) (*IPAddress, error) {
 
80
        addresses, closer := st.getCollection(legacyipaddressesC)
 
81
        defer closer()
 
82
 
 
83
        doc := &ipaddressDoc{}
 
84
        err := addresses.FindId(value).One(doc)
 
85
        if err == mgo.ErrNotFound {
 
86
                return nil, errors.NotFoundf("IP address %q", value)
 
87
        }
 
88
        if err != nil {
 
89
                return nil, errors.Annotatef(err, "cannot get IP address %q", value)
 
90
        }
 
91
        return &IPAddress{st, *doc}, nil
 
92
}
 
93
 
 
94
// ipAddressByTag implements the State method to return an existing IP
 
95
// address by its tag.
 
96
func ipAddressByTag(st *State, tag names.IPAddressTag) (*IPAddress, error) {
 
97
        addresses, closer := st.getCollection(legacyipaddressesC)
 
98
        defer closer()
 
99
 
 
100
        doc := &ipaddressDoc{}
 
101
        err := addresses.Find(bson.D{{"uuid", tag.Id()}}).One(doc)
 
102
        if err == mgo.ErrNotFound {
 
103
                return nil, errors.NotFoundf("IP address %q", tag)
 
104
        }
 
105
        if err != nil {
 
106
                return nil, errors.Annotatef(err, "cannot get IP address %q", tag)
 
107
        }
 
108
        return &IPAddress{st, *doc}, nil
 
109
}
 
110
 
 
111
// fetchIPAddresses is a helper function for finding IP addresses
 
112
func fetchIPAddresses(st *State, query bson.D) ([]*IPAddress, error) {
 
113
        addresses, closer := st.getCollection(legacyipaddressesC)
 
114
        result := []*IPAddress{}
 
115
        defer closer()
 
116
        doc := ipaddressDoc{}
 
117
        iter := addresses.Find(query).Iter()
 
118
        for iter.Next(&doc) {
 
119
                addr := &IPAddress{
 
120
                        st:  st,
 
121
                        doc: doc,
 
122
                }
 
123
                result = append(result, addr)
 
124
        }
 
125
        if err := iter.Close(); err != nil {
 
126
                return result, err
 
127
        }
 
128
        return result, nil
 
129
}
 
130
 
 
131
// AddressState represents the states an IP address can be in. They are created
 
132
// in an unknown state and then either become allocated or unavailable if
 
133
// allocation fails.
 
134
type AddressState string
 
135
 
 
136
const (
 
137
        // AddressStateUnknown is the initial state an IP address is
 
138
        // created with.
 
139
        AddressStateUnknown AddressState = ""
 
140
 
 
141
        // AddressStateAllocated means that the IP address has
 
142
        // successfully been allocated by the provider and is now in use
 
143
        // by an interface on a machine.
 
144
        AddressStateAllocated AddressState = "allocated"
 
145
 
 
146
        // AddressStateUnavailable means that allocating the address with
 
147
        // the provider failed. We shouldn't use this address, nor should
 
148
        // we attempt to allocate it again in the future.
 
149
        AddressStateUnavailable AddressState = "unavailable"
 
150
)
 
151
 
 
152
// String implements fmt.Stringer.
 
153
func (s AddressState) String() string {
 
154
        if s == AddressStateUnknown {
 
155
                return "<unknown>"
 
156
        }
 
157
        return string(s)
 
158
}
 
159
 
 
160
// IPAddress represents the state of an IP address.
 
161
type IPAddress struct {
 
162
        st  *State
 
163
        doc ipaddressDoc
 
164
}
 
165
 
 
166
type ipaddressDoc struct {
 
167
        DocID       string       `bson:"_id"`
 
168
        ModelUUID   string       `bson:"model-uuid"`
 
169
        UUID        string       `bson:"uuid"`
 
170
        Life        Life         `bson:"life"`
 
171
        SubnetId    string       `bson:"subnetid,omitempty"`
 
172
        MachineId   string       `bson:"machineid,omitempty"`
 
173
        MACAddress  string       `bson:"macaddress,omitempty"`
 
174
        InstanceId  string       `bson:"instanceid,omitempty"`
 
175
        InterfaceId string       `bson:"interfaceid,omitempty"`
 
176
        Value       string       `bson:"value"`
 
177
        Type        string       `bson:"type"`
 
178
        Scope       string       `bson:"networkscope,omitempty"`
 
179
        State       AddressState `bson:"state"`
 
180
        SpaceName   string       `bson:"spacename,omitempty"`
 
181
}
 
182
 
 
183
// Life returns whether the IP address is Alive, Dying or Dead.
 
184
func (i *IPAddress) Life() Life {
 
185
        return i.doc.Life
 
186
}
 
187
 
 
188
// Id returns the ID of the IP address.
 
189
func (i *IPAddress) Id() string {
 
190
        return i.doc.DocID
 
191
}
 
192
 
 
193
// UUID returns the globally unique ID of the IP address.
 
194
func (i *IPAddress) UUID() (utils.UUID, error) {
 
195
        return utils.UUIDFromString(i.doc.UUID)
 
196
}
 
197
 
 
198
// Tag returns the tag of the IP address.
 
199
func (i *IPAddress) Tag() names.Tag {
 
200
        return names.NewIPAddressTag(i.doc.UUID)
 
201
}
 
202
 
 
203
// SubnetId returns the ID of the subnet the IP address is associated with. If
 
204
// the address is not associated with a subnet this returns "".
 
205
func (i *IPAddress) SubnetId() string {
 
206
        return i.doc.SubnetId
 
207
}
 
208
 
 
209
// MachineId returns the ID of the machine the IP address is associated with. If
 
210
// the address is not associated with a machine this returns "".
 
211
func (i *IPAddress) MachineId() string {
 
212
        return i.doc.MachineId
 
213
}
 
214
 
 
215
// InstanceId returns the provider ID of the instance the IP address is
 
216
// associated with. For a container this will be the ID of the host. If
 
217
// the address is not associated with an instance this returns "" (the same as
 
218
// instance.UnknownId).
 
219
func (i *IPAddress) InstanceId() instance.Id {
 
220
        return instance.Id(i.doc.InstanceId)
 
221
}
 
222
 
 
223
// MACAddress returns the MAC address of the container NIC the IP address is
 
224
// associated with.
 
225
func (i *IPAddress) MACAddress() string {
 
226
        return i.doc.MACAddress
 
227
}
 
228
 
 
229
// InterfaceId returns the ID of the network interface the IP address is
 
230
// associated with. If the address is not associated with a network interface
 
231
// this returns "".
 
232
func (i *IPAddress) InterfaceId() string {
 
233
        return i.doc.InterfaceId
 
234
}
 
235
 
 
236
// Value returns the IP address.
 
237
func (i *IPAddress) Value() string {
 
238
        return i.doc.Value
 
239
}
 
240
 
 
241
// Address returns the network.Address represent the IP address
 
242
func (i *IPAddress) Address() network.Address {
 
243
        return network.NewScopedAddress(i.doc.Value, i.Scope())
 
244
}
 
245
 
 
246
// Type returns the type of the IP address. The IP address will have a type of
 
247
// IPv4, IPv6 or hostname.
 
248
func (i *IPAddress) Type() network.AddressType {
 
249
        return network.AddressType(i.doc.Type)
 
250
}
 
251
 
 
252
// Scope returns the scope of the IP address. If the scope is not set this
 
253
// returns "".
 
254
func (i *IPAddress) Scope() network.Scope {
 
255
        return network.Scope(i.doc.Scope)
 
256
}
 
257
 
 
258
// State returns the state of an IP address.
 
259
func (i *IPAddress) State() AddressState {
 
260
        return i.doc.State
 
261
}
 
262
 
 
263
// String implements fmt.Stringer.
 
264
func (i *IPAddress) String() string {
 
265
        return i.Address().String()
 
266
}
 
267
 
 
268
// GoString implements fmt.GoStringer.
 
269
func (i *IPAddress) GoString() string {
 
270
        return i.String()
 
271
}
 
272
 
 
273
// EnsureDead sets the Life of the IP address to Dead, if it's Alive. It
 
274
// does nothing otherwise.
 
275
func (i *IPAddress) EnsureDead() (err error) {
 
276
        defer errors.DeferredAnnotatef(&err, "cannot set address %q to dead", i)
 
277
 
 
278
        if i.doc.Life == Dead {
 
279
                return nil
 
280
        }
 
281
 
 
282
        buildTxn := func(attempt int) ([]txn.Op, error) {
 
283
                if attempt > 0 {
 
284
                        if err := i.Refresh(); err != nil {
 
285
                                // Address is either gone or
 
286
                                // another error occurred.
 
287
                                return nil, err
 
288
                        }
 
289
                        if i.Life() == Dead {
 
290
                                return nil, jujutxn.ErrNoOperations
 
291
                        }
 
292
                        return nil, errors.Errorf("unexpected life value: %s", i.Life().String())
 
293
                }
 
294
                op := ensureIPAddressDeadOp(i)
 
295
                op.Assert = isAliveDoc
 
296
                return []txn.Op{op}, nil
 
297
        }
 
298
 
 
299
        err = i.st.run(buildTxn)
 
300
        if err != nil {
 
301
                return err
 
302
        }
 
303
 
 
304
        i.doc.Life = Dead
 
305
        return nil
 
306
}
 
307
 
 
308
// Remove removes an existing IP address. Trying to remove a missing
 
309
// address is not an error.
 
310
func (i *IPAddress) Remove() (err error) {
 
311
        defer errors.DeferredAnnotatef(&err, "cannot remove IP address %q", i)
 
312
 
 
313
        if i.doc.Life != Dead {
 
314
                return errors.New("IP address is not dead")
 
315
        }
 
316
 
 
317
        buildTxn := func(attempt int) ([]txn.Op, error) {
 
318
                if attempt > 0 {
 
319
                        if err := i.Refresh(); errors.IsNotFound(err) {
 
320
                                return nil, jujutxn.ErrNoOperations
 
321
                        } else if err != nil {
 
322
                                return nil, err
 
323
                        }
 
324
                        if i.Life() != Dead {
 
325
                                return nil, errors.New("address is not dead")
 
326
                        }
 
327
                }
 
328
                return []txn.Op{{
 
329
                        C:      legacyipaddressesC,
 
330
                        Id:     i.doc.DocID,
 
331
                        Assert: isDeadDoc,
 
332
                        Remove: true,
 
333
                }}, nil
 
334
        }
 
335
 
 
336
        return i.st.run(buildTxn)
 
337
}
 
338
 
 
339
// SetState sets the State of an IPAddress. Valid state transitions
 
340
// are Unknown to Allocated or Unavailable, as well as setting the
 
341
// same state more than once. Any other transition will result in
 
342
// returning an error satisfying errors.IsNotValid().
 
343
func (i *IPAddress) SetState(newState AddressState) (err error) {
 
344
        defer errors.DeferredAnnotatef(&err, "cannot set IP address %q to state %q", i, newState)
 
345
 
 
346
        validStates := []AddressState{AddressStateUnknown, newState}
 
347
        unknownOrSame := bson.DocElem{"state", bson.D{{"$in", validStates}}}
 
348
        buildTxn := func(attempt int) ([]txn.Op, error) {
 
349
                if attempt > 0 {
 
350
                        if err := i.Refresh(); errors.IsNotFound(err) {
 
351
                                return nil, err
 
352
                        } else if i.Life() == Dead {
 
353
                                return nil, errors.New("address is dead")
 
354
                        } else if i.State() != AddressStateUnknown {
 
355
                                return nil, errors.NotValidf("transition from %q", i.doc.State)
 
356
                        } else if err != nil {
 
357
                                return nil, err
 
358
                        }
 
359
 
 
360
                }
 
361
                return []txn.Op{{
 
362
                        C:      legacyipaddressesC,
 
363
                        Id:     i.doc.DocID,
 
364
                        Assert: append(isAliveDoc, unknownOrSame),
 
365
                        Update: bson.D{{"$set", bson.D{{"state", string(newState)}}}},
 
366
                }}, nil
 
367
        }
 
368
 
 
369
        err = i.st.run(buildTxn)
 
370
        if err != nil {
 
371
                return err
 
372
        }
 
373
 
 
374
        i.doc.State = newState
 
375
        return nil
 
376
}
 
377
 
 
378
// AllocateTo sets the machine ID, MAC address and interface ID of the IP address.
 
379
// It will fail if the state is not AddressStateUnknown. On success,
 
380
// the address state will also change to AddressStateAllocated.
 
381
func (i *IPAddress) AllocateTo(machineId, interfaceId, macAddress string) (err error) {
 
382
        defer errors.DeferredAnnotatef(&err, "cannot allocate IP address %q to machine %q, interface %q", i, machineId, interfaceId)
 
383
 
 
384
        var instId instance.Id
 
385
        machine, err := i.st.Machine(machineId)
 
386
        if err != nil {
 
387
                return errors.Annotatef(err, "cannot get allocated machine %q", machineId)
 
388
        } else {
 
389
                instId, err = machine.InstanceId()
 
390
 
 
391
                if errors.IsNotProvisioned(err) {
 
392
                        // The machine is not yet provisioned. The instance ID will be
 
393
                        // set on provisioning.
 
394
                        instId = instance.UnknownId
 
395
                } else if err != nil {
 
396
                        return errors.Annotatef(err, "cannot get machine %q instance ID", machineId)
 
397
                }
 
398
        }
 
399
 
 
400
        buildTxn := func(attempt int) ([]txn.Op, error) {
 
401
                if attempt > 0 {
 
402
                        if err := checkModeLife(i.st); err != nil {
 
403
                                return nil, errors.Trace(err)
 
404
                        }
 
405
                        if err := i.Refresh(); errors.IsNotFound(err) {
 
406
                                return nil, err
 
407
                        } else if i.Life() == Dead {
 
408
                                return nil, errors.New("address is dead")
 
409
                        } else if i.State() != AddressStateUnknown {
 
410
                                return nil, errors.Errorf("already allocated or unavailable")
 
411
                        } else if err != nil {
 
412
                                return nil, err
 
413
                        }
 
414
 
 
415
                }
 
416
                return []txn.Op{
 
417
                        assertModelAliveOp(i.st.ModelUUID()),
 
418
                        {
 
419
                                C:      legacyipaddressesC,
 
420
                                Id:     i.doc.DocID,
 
421
                                Assert: append(isAliveDoc, bson.DocElem{"state", AddressStateUnknown}),
 
422
                                Update: bson.D{{"$set", bson.D{
 
423
                                        {"machineid", machineId},
 
424
                                        {"interfaceid", interfaceId},
 
425
                                        {"instanceid", instId},
 
426
                                        {"macaddress", macAddress},
 
427
                                        {"state", string(AddressStateAllocated)},
 
428
                                }}},
 
429
                        }}, nil
 
430
        }
 
431
 
 
432
        err = i.st.run(buildTxn)
 
433
        if err != nil {
 
434
                return err
 
435
        }
 
436
        i.doc.MachineId = machineId
 
437
        i.doc.MACAddress = macAddress
 
438
        i.doc.InterfaceId = interfaceId
 
439
        i.doc.State = AddressStateAllocated
 
440
        i.doc.InstanceId = string(instId)
 
441
        return nil
 
442
}
 
443
 
 
444
// Refresh refreshes the contents of the IPAddress from the underlying
 
445
// state. It an error that satisfies errors.IsNotFound if the Subnet has
 
446
// been removed.
 
447
func (i *IPAddress) Refresh() error {
 
448
        addresses, closer := i.st.getCollection(legacyipaddressesC)
 
449
        defer closer()
 
450
 
 
451
        err := addresses.FindId(i.doc.DocID).One(&i.doc)
 
452
        if err == mgo.ErrNotFound {
 
453
                return errors.NotFoundf("IP address %q", i)
 
454
        }
 
455
        if err != nil {
 
456
                return errors.Annotatef(err, "cannot refresh IP address %q", i)
 
457
        }
 
458
        return nil
 
459
}
 
460
 
 
461
func ensureIPAddressDeadOp(addr *IPAddress) txn.Op {
 
462
        op := txn.Op{
 
463
                C:      legacyipaddressesC,
 
464
                Id:     addr.Id(),
 
465
                Update: bson.D{{"$set", bson.D{{"life", Dead}}}},
 
466
        }
 
467
        return op
 
468
}