1
// Copyright 2014 Canonical Ltd.
2
// Licensed under the AGPLv3, see LICENCE file for details.
9
"github.com/juju/errors"
10
"github.com/juju/names"
11
jujutxn "github.com/juju/txn"
12
"github.com/juju/utils"
14
"gopkg.in/mgo.v2/bson"
17
"github.com/juju/juju/instance"
18
"github.com/juju/juju/network"
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)
25
// This checks for a missing value as well as invalid values
26
ip := net.ParseIP(addr.Value)
28
return nil, errors.NotValidf("address")
31
// Generate the UUID for the new IP address.
32
uuid, err := utils.NewUUID()
37
addressID := st.docID(addr.Value)
38
ipDoc := ipaddressDoc{
40
ModelUUID: st.ModelUUID(),
43
State: AddressStateUnknown,
46
Type: string(addr.Type),
47
Scope: string(addr.Scope),
48
SpaceName: string(addr.SpaceName),
51
ipaddress = &IPAddress{doc: ipDoc, st: st}
53
assertModelAliveOp(st.ModelUUID()),
55
C: legacyipaddressesC,
57
Assert: txn.DocMissing,
62
err = st.runTransaction(ops)
65
if err := checkModeLife(st); err != nil {
66
return nil, errors.Trace(err)
68
if _, err = st.IPAddress(addr.Value); err == nil {
69
return nil, errors.AlreadyExistsf("address")
74
return nil, errors.Trace(err)
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)
83
doc := &ipaddressDoc{}
84
err := addresses.FindId(value).One(doc)
85
if err == mgo.ErrNotFound {
86
return nil, errors.NotFoundf("IP address %q", value)
89
return nil, errors.Annotatef(err, "cannot get IP address %q", value)
91
return &IPAddress{st, *doc}, nil
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)
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)
106
return nil, errors.Annotatef(err, "cannot get IP address %q", tag)
108
return &IPAddress{st, *doc}, nil
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{}
116
doc := ipaddressDoc{}
117
iter := addresses.Find(query).Iter()
118
for iter.Next(&doc) {
123
result = append(result, addr)
125
if err := iter.Close(); err != nil {
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
134
type AddressState string
137
// AddressStateUnknown is the initial state an IP address is
139
AddressStateUnknown AddressState = ""
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"
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"
152
// String implements fmt.Stringer.
153
func (s AddressState) String() string {
154
if s == AddressStateUnknown {
160
// IPAddress represents the state of an IP address.
161
type IPAddress struct {
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"`
183
// Life returns whether the IP address is Alive, Dying or Dead.
184
func (i *IPAddress) Life() Life {
188
// Id returns the ID of the IP address.
189
func (i *IPAddress) Id() string {
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)
198
// Tag returns the tag of the IP address.
199
func (i *IPAddress) Tag() names.Tag {
200
return names.NewIPAddressTag(i.doc.UUID)
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
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
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)
223
// MACAddress returns the MAC address of the container NIC the IP address is
225
func (i *IPAddress) MACAddress() string {
226
return i.doc.MACAddress
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
232
func (i *IPAddress) InterfaceId() string {
233
return i.doc.InterfaceId
236
// Value returns the IP address.
237
func (i *IPAddress) Value() string {
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())
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)
252
// Scope returns the scope of the IP address. If the scope is not set this
254
func (i *IPAddress) Scope() network.Scope {
255
return network.Scope(i.doc.Scope)
258
// State returns the state of an IP address.
259
func (i *IPAddress) State() AddressState {
263
// String implements fmt.Stringer.
264
func (i *IPAddress) String() string {
265
return i.Address().String()
268
// GoString implements fmt.GoStringer.
269
func (i *IPAddress) GoString() string {
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)
278
if i.doc.Life == Dead {
282
buildTxn := func(attempt int) ([]txn.Op, error) {
284
if err := i.Refresh(); err != nil {
285
// Address is either gone or
286
// another error occurred.
289
if i.Life() == Dead {
290
return nil, jujutxn.ErrNoOperations
292
return nil, errors.Errorf("unexpected life value: %s", i.Life().String())
294
op := ensureIPAddressDeadOp(i)
295
op.Assert = isAliveDoc
296
return []txn.Op{op}, nil
299
err = i.st.run(buildTxn)
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)
313
if i.doc.Life != Dead {
314
return errors.New("IP address is not dead")
317
buildTxn := func(attempt int) ([]txn.Op, error) {
319
if err := i.Refresh(); errors.IsNotFound(err) {
320
return nil, jujutxn.ErrNoOperations
321
} else if err != nil {
324
if i.Life() != Dead {
325
return nil, errors.New("address is not dead")
329
C: legacyipaddressesC,
336
return i.st.run(buildTxn)
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)
346
validStates := []AddressState{AddressStateUnknown, newState}
347
unknownOrSame := bson.DocElem{"state", bson.D{{"$in", validStates}}}
348
buildTxn := func(attempt int) ([]txn.Op, error) {
350
if err := i.Refresh(); errors.IsNotFound(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 {
362
C: legacyipaddressesC,
364
Assert: append(isAliveDoc, unknownOrSame),
365
Update: bson.D{{"$set", bson.D{{"state", string(newState)}}}},
369
err = i.st.run(buildTxn)
374
i.doc.State = newState
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)
384
var instId instance.Id
385
machine, err := i.st.Machine(machineId)
387
return errors.Annotatef(err, "cannot get allocated machine %q", machineId)
389
instId, err = machine.InstanceId()
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)
400
buildTxn := func(attempt int) ([]txn.Op, error) {
402
if err := checkModeLife(i.st); err != nil {
403
return nil, errors.Trace(err)
405
if err := i.Refresh(); errors.IsNotFound(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 {
417
assertModelAliveOp(i.st.ModelUUID()),
419
C: legacyipaddressesC,
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)},
432
err = i.st.run(buildTxn)
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)
444
// Refresh refreshes the contents of the IPAddress from the underlying
445
// state. It an error that satisfies errors.IsNotFound if the Subnet has
447
func (i *IPAddress) Refresh() error {
448
addresses, closer := i.st.getCollection(legacyipaddressesC)
451
err := addresses.FindId(i.doc.DocID).One(&i.doc)
452
if err == mgo.ErrNotFound {
453
return errors.NotFoundf("IP address %q", i)
456
return errors.Annotatef(err, "cannot refresh IP address %q", i)
461
func ensureIPAddressDeadOp(addr *IPAddress) txn.Op {
463
C: legacyipaddressesC,
465
Update: bson.D{{"$set", bson.D{{"life", Dead}}}},