github.com/mhilton/juju-juju@v0.0.0-20150901100907-a94dd2c73455/worker/machiner/machiner.go (about)

     1  // Copyright 2012, 2013 Canonical Ltd.
     2  // Licensed under the AGPLv3, see LICENCE file for details.
     3  package machiner
     4  
     5  import (
     6  	"net"
     7  
     8  	"github.com/juju/errors"
     9  	"github.com/juju/loggo"
    10  	"github.com/juju/names"
    11  
    12  	"github.com/juju/juju/agent"
    13  	"github.com/juju/juju/api/watcher"
    14  	"github.com/juju/juju/apiserver/params"
    15  	"github.com/juju/juju/network"
    16  	"github.com/juju/juju/worker"
    17  )
    18  
    19  var logger = loggo.GetLogger("juju.worker.machiner")
    20  
    21  // Machiner is responsible for a machine agent's lifecycle.
    22  type Machiner struct {
    23  	st                     MachineAccessor
    24  	tag                    names.MachineTag
    25  	machine                Machine
    26  	ignoreAddressesOnStart bool
    27  }
    28  
    29  // NewMachiner returns a Worker that will wait for the identified machine
    30  // to become Dying and make it Dead; or until the machine becomes Dead by
    31  // other means.
    32  func NewMachiner(st MachineAccessor, agentConfig agent.Config, ignoreAddressesOnStart bool) worker.Worker {
    33  	mr := &Machiner{st: st, tag: agentConfig.Tag().(names.MachineTag), ignoreAddressesOnStart: ignoreAddressesOnStart}
    34  	return worker.NewNotifyWorker(mr)
    35  }
    36  
    37  func (mr *Machiner) SetUp() (watcher.NotifyWatcher, error) {
    38  	// Find which machine we're responsible for.
    39  	m, err := mr.st.Machine(mr.tag)
    40  	if params.IsCodeNotFoundOrCodeUnauthorized(err) {
    41  		return nil, worker.ErrTerminateAgent
    42  	} else if err != nil {
    43  		return nil, errors.Trace(err)
    44  	}
    45  	mr.machine = m
    46  
    47  	if mr.ignoreAddressesOnStart {
    48  		logger.Debugf("machine addresses ignored on start - resetting machine addresses")
    49  		if err := m.SetMachineAddresses(nil); err != nil {
    50  			return nil, errors.Annotate(err, "reseting machine addresses")
    51  		}
    52  	} else {
    53  		// Set the addresses in state to the host's addresses.
    54  		if err := setMachineAddresses(mr.tag, m); err != nil {
    55  			return nil, errors.Annotate(err, "setting machine addresses")
    56  		}
    57  	}
    58  
    59  	// Mark the machine as started and log it.
    60  	if err := m.SetStatus(params.StatusStarted, "", nil); err != nil {
    61  		return nil, errors.Annotatef(err, "%s failed to set status started", mr.tag)
    62  	}
    63  	logger.Infof("%q started", mr.tag)
    64  
    65  	return m.Watch()
    66  }
    67  
    68  var interfaceAddrs = net.InterfaceAddrs
    69  
    70  // setMachineAddresses sets the addresses for this machine to all of the
    71  // host's non-loopback interface IP addresses.
    72  func setMachineAddresses(tag names.MachineTag, m Machine) error {
    73  	addrs, err := interfaceAddrs()
    74  	if err != nil {
    75  		return err
    76  	}
    77  	var hostAddresses []network.Address
    78  	for _, addr := range addrs {
    79  		var ip net.IP
    80  		switch addr := addr.(type) {
    81  		case *net.IPAddr:
    82  			ip = addr.IP
    83  		case *net.IPNet:
    84  			ip = addr.IP
    85  		default:
    86  			continue
    87  		}
    88  		address := network.NewAddress(ip.String())
    89  		// Filter out link-local addresses as we cannot reliably use them.
    90  		if address.Scope == network.ScopeLinkLocal {
    91  			continue
    92  		}
    93  		hostAddresses = append(hostAddresses, address)
    94  	}
    95  	if len(hostAddresses) == 0 {
    96  		return nil
    97  	}
    98  	// Filter out any LXC bridge addresses.
    99  	hostAddresses = network.FilterLXCAddresses(hostAddresses)
   100  	logger.Infof("setting addresses for %v to %q", tag, hostAddresses)
   101  	return m.SetMachineAddresses(hostAddresses)
   102  }
   103  
   104  func (mr *Machiner) Handle(_ <-chan struct{}) error {
   105  	if err := mr.machine.Refresh(); params.IsCodeNotFoundOrCodeUnauthorized(err) {
   106  		return worker.ErrTerminateAgent
   107  	} else if err != nil {
   108  		return err
   109  	}
   110  	life := mr.machine.Life()
   111  	if life == params.Alive {
   112  		return nil
   113  	}
   114  	logger.Debugf("%q is now %s", mr.tag, life)
   115  	if err := mr.machine.SetStatus(params.StatusStopped, "", nil); err != nil {
   116  		return errors.Annotatef(err, "%s failed to set status stopped", mr.tag)
   117  	}
   118  
   119  	// Attempt to mark the machine Dead. If the machine still has units
   120  	// assigned, or storage attached, this will fail with
   121  	// CodeHasAssignedUnits or CodeMachineHasAttachedStorage respectively.
   122  	// Once units or storage are removed, the watcher will trigger again
   123  	// and we'll reattempt.
   124  	if err := mr.machine.EnsureDead(); err != nil {
   125  		if params.IsCodeHasAssignedUnits(err) {
   126  			return nil
   127  		}
   128  		if params.IsCodeMachineHasAttachedStorage(err) {
   129  			logger.Tracef("machine still has storage attached")
   130  			return nil
   131  		}
   132  		return errors.Annotatef(err, "%s failed to set machine to dead", mr.tag)
   133  	}
   134  	return worker.ErrTerminateAgent
   135  }
   136  
   137  func (mr *Machiner) TearDown() error {
   138  	// Nothing to do here.
   139  	return nil
   140  }