github.com/cloudbase/juju-core@v0.0.0-20140504232958-a7271ac7912f/state/api/provisioner/machine.go (about)

     1  // Copyright 2013 Canonical Ltd.
     2  // Licensed under the AGPLv3, see LICENCE file for details.
     3  
     4  package provisioner
     5  
     6  import (
     7  	"errors"
     8  	"fmt"
     9  
    10  	"launchpad.net/juju-core/constraints"
    11  	"launchpad.net/juju-core/instance"
    12  	"launchpad.net/juju-core/names"
    13  	"launchpad.net/juju-core/state/api/params"
    14  	"launchpad.net/juju-core/state/api/watcher"
    15  )
    16  
    17  // Machine represents a juju machine as seen by the provisioner worker.
    18  type Machine struct {
    19  	tag  string
    20  	life params.Life
    21  	st   *State
    22  }
    23  
    24  // Tag returns the machine's tag.
    25  func (m *Machine) Tag() string {
    26  	return m.tag
    27  }
    28  
    29  // Id returns the machine id.
    30  func (m *Machine) Id() string {
    31  	_, machineId, err := names.ParseTag(m.tag, names.MachineTagKind)
    32  	if err != nil {
    33  		panic(fmt.Sprintf("%q is not a valid machine tag", m.tag))
    34  	}
    35  	return machineId
    36  }
    37  
    38  // String returns the machine as a string.
    39  func (m *Machine) String() string {
    40  	return m.Id()
    41  }
    42  
    43  // Life returns the machine's lifecycle value.
    44  func (m *Machine) Life() params.Life {
    45  	return m.life
    46  }
    47  
    48  // Refresh updates the cached local copy of the machine's data.
    49  func (m *Machine) Refresh() error {
    50  	life, err := m.st.machineLife(m.tag)
    51  	if err != nil {
    52  		return err
    53  	}
    54  	m.life = life
    55  	return nil
    56  }
    57  
    58  // SetStatus sets the status of the machine.
    59  func (m *Machine) SetStatus(status params.Status, info string) error {
    60  	var result params.ErrorResults
    61  	args := params.SetStatus{
    62  		Entities: []params.SetEntityStatus{
    63  			{Tag: m.tag, Status: status, Info: info},
    64  		},
    65  	}
    66  	err := m.st.caller.Call("Provisioner", "", "SetStatus", args, &result)
    67  	if err != nil {
    68  		return err
    69  	}
    70  	return result.OneError()
    71  }
    72  
    73  // Status returns the status of the machine.
    74  func (m *Machine) Status() (params.Status, string, error) {
    75  	var results params.StatusResults
    76  	args := params.Entities{
    77  		Entities: []params.Entity{{Tag: m.tag}},
    78  	}
    79  	err := m.st.caller.Call("Provisioner", "", "Status", args, &results)
    80  	if err != nil {
    81  		return "", "", err
    82  	}
    83  	if len(results.Results) != 1 {
    84  		return "", "", fmt.Errorf("expected one result, got %d", len(results.Results))
    85  	}
    86  	result := results.Results[0]
    87  	if result.Error != nil {
    88  		return "", "", result.Error
    89  	}
    90  	return result.Status, result.Info, nil
    91  }
    92  
    93  // Constraints returns the exact constraints that should apply when provisioning
    94  // an instance for the machine.
    95  func (m *Machine) Constraints() (constraints.Value, error) {
    96  	nothing := constraints.Value{}
    97  	var results params.ConstraintsResults
    98  	args := params.Entities{
    99  		Entities: []params.Entity{{Tag: m.tag}},
   100  	}
   101  	err := m.st.caller.Call("Provisioner", "", "Constraints", args, &results)
   102  	if err != nil {
   103  		return nothing, err
   104  	}
   105  	if len(results.Results) != 1 {
   106  		return nothing, fmt.Errorf("expected one result, got %d", len(results.Results))
   107  	}
   108  	result := results.Results[0]
   109  	if result.Error != nil {
   110  		return nothing, result.Error
   111  	}
   112  	return result.Constraints, nil
   113  }
   114  
   115  // EnsureDead sets the machine lifecycle to Dead if it is Alive or
   116  // Dying. It does nothing otherwise.
   117  func (m *Machine) EnsureDead() error {
   118  	var result params.ErrorResults
   119  	args := params.Entities{
   120  		Entities: []params.Entity{{Tag: m.tag}},
   121  	}
   122  	err := m.st.caller.Call("Provisioner", "", "EnsureDead", args, &result)
   123  	if err != nil {
   124  		return err
   125  	}
   126  	return result.OneError()
   127  }
   128  
   129  // Remove removes the machine from state. It will fail if the machine
   130  // is not Dead.
   131  func (m *Machine) Remove() error {
   132  	var result params.ErrorResults
   133  	args := params.Entities{
   134  		Entities: []params.Entity{{Tag: m.tag}},
   135  	}
   136  	err := m.st.caller.Call("Provisioner", "", "Remove", args, &result)
   137  	if err != nil {
   138  		return err
   139  	}
   140  	return result.OneError()
   141  }
   142  
   143  // Series returns the operating system series running on the machine.
   144  //
   145  // NOTE: Unlike state.Machine.Series(), this method returns an error
   146  // as well, because it needs to do an API call.
   147  func (m *Machine) Series() (string, error) {
   148  	var results params.StringResults
   149  	args := params.Entities{
   150  		Entities: []params.Entity{{Tag: m.tag}},
   151  	}
   152  	err := m.st.caller.Call("Provisioner", "", "Series", args, &results)
   153  	if err != nil {
   154  		return "", err
   155  	}
   156  	if len(results.Results) != 1 {
   157  		return "", fmt.Errorf("expected one result, got %d", len(results.Results))
   158  	}
   159  	result := results.Results[0]
   160  	if result.Error != nil {
   161  		return "", result.Error
   162  	}
   163  	return result.Result, nil
   164  }
   165  
   166  // SetProvisioned sets the provider specific machine id, nonce and also metadata for
   167  // this machine. Once set, the instance id cannot be changed.
   168  func (m *Machine) SetProvisioned(id instance.Id, nonce string, characteristics *instance.HardwareCharacteristics) error {
   169  	var result params.ErrorResults
   170  	args := params.SetProvisioned{
   171  		Machines: []params.MachineSetProvisioned{{
   172  			Tag:             m.tag,
   173  			InstanceId:      id,
   174  			Nonce:           nonce,
   175  			Characteristics: characteristics,
   176  		}},
   177  	}
   178  	err := m.st.caller.Call("Provisioner", "", "SetProvisioned", args, &result)
   179  	if err != nil {
   180  		return err
   181  	}
   182  	return result.OneError()
   183  }
   184  
   185  // InstanceId returns the provider specific instance id for the
   186  // machine or an CodeNotProvisioned error, if not set.
   187  func (m *Machine) InstanceId() (instance.Id, error) {
   188  	var results params.StringResults
   189  	args := params.Entities{
   190  		Entities: []params.Entity{{Tag: m.tag}},
   191  	}
   192  	err := m.st.caller.Call("Provisioner", "", "InstanceId", args, &results)
   193  	if err != nil {
   194  		return "", err
   195  	}
   196  	if len(results.Results) != 1 {
   197  		return "", fmt.Errorf("expected one result, got %d", len(results.Results))
   198  	}
   199  	result := results.Results[0]
   200  	if result.Error != nil {
   201  		return "", result.Error
   202  	}
   203  	return instance.Id(result.Result), nil
   204  }
   205  
   206  // SetPassword sets the machine's password.
   207  func (m *Machine) SetPassword(password string) error {
   208  	var result params.ErrorResults
   209  	args := params.PasswordChanges{
   210  		Changes: []params.PasswordChange{
   211  			{Tag: m.tag, Password: password},
   212  		},
   213  	}
   214  	err := m.st.caller.Call("Provisioner", "", "SetPasswords", args, &result)
   215  	if err != nil {
   216  		return err
   217  	}
   218  	return result.OneError()
   219  }
   220  
   221  // WatchContainers returns a StringsWatcher that notifies of changes
   222  // to the lifecycles of containers of the specified type on the machine.
   223  func (m *Machine) WatchContainers(ctype instance.ContainerType) (watcher.StringsWatcher, error) {
   224  	if string(ctype) == "" {
   225  		return nil, errors.New("container type must be specified")
   226  	}
   227  	supported := false
   228  	for _, c := range instance.ContainerTypes {
   229  		if ctype == c {
   230  			supported = true
   231  			break
   232  		}
   233  	}
   234  	if !supported {
   235  		return nil, fmt.Errorf("unsupported container type %q", ctype)
   236  	}
   237  	var results params.StringsWatchResults
   238  	args := params.WatchContainers{
   239  		Params: []params.WatchContainer{
   240  			{MachineTag: m.tag, ContainerType: string(ctype)},
   241  		},
   242  	}
   243  	err := m.st.caller.Call("Provisioner", "", "WatchContainers", args, &results)
   244  	if err != nil {
   245  		return nil, err
   246  	}
   247  	if len(results.Results) != 1 {
   248  		return nil, fmt.Errorf("expected one result, got %d", len(results.Results))
   249  	}
   250  	result := results.Results[0]
   251  	if result.Error != nil {
   252  		return nil, result.Error
   253  	}
   254  	w := watcher.NewStringsWatcher(m.st.caller, result)
   255  	return w, nil
   256  }
   257  
   258  // WatchAllContainers returns a StringsWatcher that notifies of changes
   259  // to the lifecycles of all containers on the machine.
   260  func (m *Machine) WatchAllContainers() (watcher.StringsWatcher, error) {
   261  	var results params.StringsWatchResults
   262  	args := params.WatchContainers{
   263  		Params: []params.WatchContainer{
   264  			{MachineTag: m.tag},
   265  		},
   266  	}
   267  	err := m.st.caller.Call("Provisioner", "", "WatchContainers", args, &results)
   268  	if err != nil {
   269  		return nil, err
   270  	}
   271  	if len(results.Results) != 1 {
   272  		return nil, fmt.Errorf("expected one result, got %d", len(results.Results))
   273  	}
   274  	result := results.Results[0]
   275  	if result.Error != nil {
   276  		return nil, result.Error
   277  	}
   278  	w := watcher.NewStringsWatcher(m.st.caller, result)
   279  	return w, nil
   280  }
   281  
   282  // SetSupportedContainers updates the list of containers supported by this machine.
   283  func (m *Machine) SetSupportedContainers(containerTypes ...instance.ContainerType) error {
   284  	var results params.ErrorResults
   285  	args := params.MachineContainersParams{
   286  		Params: []params.MachineContainers{
   287  			{MachineTag: m.tag, ContainerTypes: containerTypes},
   288  		},
   289  	}
   290  	err := m.st.caller.Call("Provisioner", "", "SetSupportedContainers", args, &results)
   291  	if err != nil {
   292  		return err
   293  	}
   294  	if len(results.Results) != 1 {
   295  		return fmt.Errorf("expected one result, got %d", len(results.Results))
   296  	}
   297  	apiError := results.Results[0].Error
   298  	if apiError != nil {
   299  		return apiError
   300  	}
   301  	return nil
   302  }
   303  
   304  // SupportsNoContainers records the fact that this machine doesn't support any containers.
   305  func (m *Machine) SupportsNoContainers() error {
   306  	return m.SetSupportedContainers([]instance.ContainerType{}...)
   307  }