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  }