github.com/altoros/juju-vmware@v0.0.0-20150312064031-f19ae857ccca/state/networkinterfaces.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  	"fmt"
     8  	"strings"
     9  
    10  	"github.com/juju/errors"
    11  	"github.com/juju/names"
    12  	"gopkg.in/mgo.v2"
    13  	"gopkg.in/mgo.v2/bson"
    14  	"gopkg.in/mgo.v2/txn"
    15  )
    16  
    17  // NetworkInterface represents the state of a machine network
    18  // interface.
    19  type NetworkInterface struct {
    20  	st  *State
    21  	doc networkInterfaceDoc
    22  }
    23  
    24  // NetworkInterfaceInfo describes a single network interface available
    25  // on an instance.
    26  type NetworkInterfaceInfo struct {
    27  	// MACAddress is the network interface's hardware MAC address
    28  	// (e.g. "aa:bb:cc:dd:ee:ff").
    29  	MACAddress string
    30  
    31  	// InterfaceName is the OS-specific network device name (e.g.
    32  	// "eth0", or "eth1.42" for a VLAN virtual interface, or
    33  	// "eth1:suffix" for a network alias).
    34  	InterfaceName string
    35  
    36  	// NetworkName is this interface's network name.
    37  	NetworkName string
    38  
    39  	// IsVirtual is true when the interface is a virtual device, as
    40  	// opposed to a physical device (e.g. a VLAN or a network alias).
    41  	IsVirtual bool
    42  
    43  	// Disabled returns whether the interface is disabled.
    44  	Disabled bool
    45  }
    46  
    47  // networkInterfaceDoc represents a network interface for a machine on
    48  // a given network.
    49  type networkInterfaceDoc struct {
    50  	Id            bson.ObjectId `bson:"_id"`
    51  	EnvUUID       string        `bson:"env-uuid"`
    52  	MACAddress    string
    53  	InterfaceName string
    54  	NetworkName   string
    55  	MachineId     string
    56  	IsVirtual     bool
    57  	IsDisabled    bool
    58  }
    59  
    60  // GoString implements fmt.GoStringer.
    61  func (ni *NetworkInterface) GoString() string {
    62  	return fmt.Sprintf(
    63  		"&state.NetworkInterface{machineId: %q, mac: %q, name: %q, networkName: %q, isVirtual: %t, isDisabled: %t}",
    64  		ni.MachineId(), ni.MACAddress(), ni.InterfaceName(), ni.NetworkName(), ni.IsVirtual(), ni.IsDisabled())
    65  }
    66  
    67  // Id returns the internal juju-specific id of the interface.
    68  func (ni *NetworkInterface) Id() string {
    69  	return ni.doc.Id.String()
    70  }
    71  
    72  // MACAddress returns the MAC address of the interface.
    73  func (ni *NetworkInterface) MACAddress() string {
    74  	return ni.doc.MACAddress
    75  }
    76  
    77  // InterfaceName returns the name of the interface.
    78  func (ni *NetworkInterface) InterfaceName() string {
    79  	return ni.doc.InterfaceName
    80  }
    81  
    82  // RawInterfaceName return the name of the raw interface.
    83  func (ni *NetworkInterface) RawInterfaceName() string {
    84  	nw, err := ni.st.Network(ni.doc.NetworkName)
    85  	if err == nil {
    86  		return strings.TrimSuffix(ni.doc.InterfaceName, fmt.Sprintf(".%d", nw.VLANTag()))
    87  	}
    88  	return ni.doc.InterfaceName
    89  }
    90  
    91  // NetworkName returns the network name of the interface.
    92  func (ni *NetworkInterface) NetworkName() string {
    93  	return ni.doc.NetworkName
    94  }
    95  
    96  // NetworkTag returns the network tag of the interface.
    97  func (ni *NetworkInterface) NetworkTag() names.NetworkTag {
    98  	return names.NewNetworkTag(ni.doc.NetworkName)
    99  }
   100  
   101  // MachineId returns the machine id of the interface.
   102  func (ni *NetworkInterface) MachineId() string {
   103  	return ni.doc.MachineId
   104  }
   105  
   106  // MachineTag returns the machine tag of the interface.
   107  func (ni *NetworkInterface) MachineTag() names.MachineTag {
   108  	return names.NewMachineTag(ni.doc.MachineId)
   109  }
   110  
   111  // IsVirtual returns whether the interface represents a virtual
   112  // device.
   113  func (ni *NetworkInterface) IsVirtual() bool {
   114  	return ni.doc.IsVirtual
   115  }
   116  
   117  // IsPhysical returns whether the interface represents a physical
   118  // device.
   119  func (ni *NetworkInterface) IsPhysical() bool {
   120  	return !ni.doc.IsVirtual
   121  }
   122  
   123  // IsDisabled returns whether the interface is disabled.
   124  func (ni *NetworkInterface) IsDisabled() bool {
   125  	return ni.doc.IsDisabled
   126  }
   127  
   128  // Disable changes the state of the network interface to disabled. In
   129  // case of a physical interface that has dependent virtual interfaces
   130  // (e.g. VLANs), those will be disabled along with their parent
   131  // interface. If the interface is already disabled, nothing happens
   132  // and no error is returned.
   133  func (ni *NetworkInterface) Disable() (err error) {
   134  	defer errors.DeferredAnnotatef(&err, "cannot disable network interface %q", ni)
   135  	return ni.setDisabled(true)
   136  }
   137  
   138  // Enable changes the state of the network interface to enabled. If
   139  // the interface is already enabled, nothing happens and no error is
   140  // returned.
   141  func (ni *NetworkInterface) Enable() (err error) {
   142  	defer errors.DeferredAnnotatef(&err, "cannot enable network interface %q", ni)
   143  
   144  	return ni.setDisabled(false)
   145  }
   146  
   147  // Refresh refreshes the contents of the network interface from the underlying
   148  // state. It returns an error that satisfies errors.IsNotFound if the
   149  // machine has been removed.
   150  func (ni *NetworkInterface) Refresh() error {
   151  	networkInterfaces, closer := ni.st.getCollection(networkInterfacesC)
   152  	defer closer()
   153  
   154  	doc := networkInterfaceDoc{}
   155  	err := networkInterfaces.FindId(ni.doc.Id).One(&doc)
   156  	if err == mgo.ErrNotFound {
   157  		return errors.NotFoundf("network interface %#v", ni)
   158  	}
   159  	if err != nil {
   160  		return fmt.Errorf("cannot refresh network interface %q on machine %q: %v",
   161  			ni.InterfaceName(), ni.MachineId(), err)
   162  	}
   163  	ni.doc = doc
   164  	return nil
   165  }
   166  
   167  // Remove removes the network interface from state.
   168  func (ni *NetworkInterface) Remove() (err error) {
   169  	defer errors.DeferredAnnotatef(&err, "cannot remove network interface %q", ni)
   170  
   171  	ops := []txn.Op{{
   172  		C:      networkInterfacesC,
   173  		Id:     ni.doc.Id,
   174  		Remove: true,
   175  	}}
   176  	// The only abort conditions in play indicate that the network interface
   177  	// has already been removed.
   178  	return onAbort(ni.st.runTransaction(ops), nil)
   179  }
   180  
   181  func newNetworkInterface(st *State, doc *networkInterfaceDoc) *NetworkInterface {
   182  	return &NetworkInterface{st, *doc}
   183  }
   184  
   185  func newNetworkInterfaceDoc(machineID, envUUID string, args NetworkInterfaceInfo) *networkInterfaceDoc {
   186  	return &networkInterfaceDoc{
   187  		Id:            bson.NewObjectId(),
   188  		EnvUUID:       envUUID,
   189  		MachineId:     machineID,
   190  		MACAddress:    args.MACAddress,
   191  		InterfaceName: args.InterfaceName,
   192  		NetworkName:   args.NetworkName,
   193  		IsVirtual:     args.IsVirtual,
   194  		IsDisabled:    args.Disabled,
   195  	}
   196  }
   197  
   198  // setDisabled is the internal implementation for Enable() and
   199  // Disable().
   200  func (ni *NetworkInterface) setDisabled(shouldDisable bool) error {
   201  	if shouldDisable == ni.doc.IsDisabled {
   202  		// Nothing to do.
   203  		return nil
   204  	}
   205  	ops, err := ni.disableOps(shouldDisable)
   206  	if err != nil {
   207  		return err
   208  	}
   209  	err = ni.st.runTransaction(ops)
   210  	if err != nil {
   211  		return onAbort(err, errors.NotFoundf("network interface"))
   212  	}
   213  	ni.doc.IsDisabled = shouldDisable
   214  	return nil
   215  }
   216  
   217  // disableOps generates a list of transaction operations to disable or
   218  // enable the network interface.
   219  func (ni *NetworkInterface) disableOps(shouldDisable bool) ([]txn.Op, error) {
   220  	ops := []txn.Op{{
   221  		C:      networkInterfacesC,
   222  		Id:     ni.doc.Id,
   223  		Assert: txn.DocExists,
   224  		Update: bson.D{{"$set", bson.D{{"isdisabled", shouldDisable}}}},
   225  	}}
   226  	if shouldDisable && ni.IsPhysical() {
   227  		// Fetch and dependent virtual interfaces on the same machine,
   228  		// so we can disable them along with their parent.
   229  		m, err := ni.st.Machine(ni.MachineId())
   230  		if err != nil {
   231  			return nil, err
   232  		}
   233  		ifaces, err := m.NetworkInterfaces()
   234  		if err != nil {
   235  			return nil, err
   236  		}
   237  		for _, iface := range ifaces {
   238  			if iface.Id() == ni.Id() {
   239  				continue
   240  			}
   241  			if iface.MACAddress() == ni.MACAddress() && iface.IsVirtual() {
   242  				ops = append(ops, txn.Op{
   243  					C:      networkInterfacesC,
   244  					Id:     iface.doc.Id,
   245  					Assert: txn.DocExists,
   246  					Update: bson.D{{"$set", bson.D{{"isdisabled", shouldDisable}}}},
   247  				})
   248  			}
   249  		}
   250  	}
   251  	return ops, nil
   252  }