github.com/makyo/juju@v0.0.0-20160425123129-2608902037e9/state/legacy_ipaddresses.go (about) 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 assertModelActiveOp(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 := checkModelActive(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 := checkModelActive(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 assertModelActiveOp(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 }