github.com/mhilton/juju-juju@v0.0.0-20150901100907-a94dd2c73455/api/uniter/unit.go (about)

     1  // Copyright 2013 Canonical Ltd.
     2  // Licensed under the AGPLv3, see LICENCE file for details.
     3  
     4  package uniter
     5  
     6  import (
     7  	"fmt"
     8  
     9  	"github.com/juju/errors"
    10  	"github.com/juju/names"
    11  	"gopkg.in/juju/charm.v5"
    12  
    13  	"github.com/juju/juju/api/common"
    14  	"github.com/juju/juju/api/watcher"
    15  	"github.com/juju/juju/apiserver/params"
    16  )
    17  
    18  // Unit represents a juju unit as seen by a uniter worker.
    19  type Unit struct {
    20  	st   *State
    21  	tag  names.UnitTag
    22  	life params.Life
    23  }
    24  
    25  // Tag returns the unit's tag.
    26  func (u *Unit) Tag() names.UnitTag {
    27  	return u.tag
    28  }
    29  
    30  // Name returns the name of the unit.
    31  func (u *Unit) Name() string {
    32  	return u.tag.Id()
    33  }
    34  
    35  // String returns the unit as a string.
    36  func (u *Unit) String() string {
    37  	return u.Name()
    38  }
    39  
    40  // Life returns the unit's lifecycle value.
    41  func (u *Unit) Life() params.Life {
    42  	return u.life
    43  }
    44  
    45  // Refresh updates the cached local copy of the unit's data.
    46  func (u *Unit) Refresh() error {
    47  	life, err := u.st.life(u.tag)
    48  	if err != nil {
    49  		return err
    50  	}
    51  	u.life = life
    52  	return nil
    53  }
    54  
    55  // SetUnitStatus sets the status of the unit.
    56  func (u *Unit) SetUnitStatus(status params.Status, info string, data map[string]interface{}) error {
    57  	if u.st.facade.BestAPIVersion() < 2 {
    58  		return errors.NotImplementedf("SetUnitStatus")
    59  	}
    60  	var result params.ErrorResults
    61  	args := params.SetStatus{
    62  		Entities: []params.EntityStatusArgs{
    63  			{Tag: u.tag.String(), Status: status, Info: info, Data: data},
    64  		},
    65  	}
    66  	err := u.st.facade.FacadeCall("SetUnitStatus", args, &result)
    67  	if err != nil {
    68  		return errors.Trace(err)
    69  	}
    70  	return result.OneError()
    71  }
    72  
    73  // UnitStatus gets the status details of the unit.
    74  func (u *Unit) UnitStatus() (params.StatusResult, error) {
    75  	var results params.StatusResults
    76  	args := params.Entities{
    77  		Entities: []params.Entity{
    78  			{Tag: u.tag.String()},
    79  		},
    80  	}
    81  	err := u.st.facade.FacadeCall("UnitStatus", args, &results)
    82  	if err != nil {
    83  		if params.IsCodeNotImplemented(err) {
    84  			return params.StatusResult{}, errors.NotImplementedf("UnitStatus")
    85  		}
    86  		return params.StatusResult{}, errors.Trace(err)
    87  	}
    88  	if len(results.Results) != 1 {
    89  		panic(errors.Errorf("expected 1 result, got %d", len(results.Results)))
    90  	}
    91  	result := results.Results[0]
    92  	if result.Error != nil {
    93  		return params.StatusResult{}, result.Error
    94  	}
    95  	return result, nil
    96  }
    97  
    98  // SetAgentStatus sets the status of the unit agent.
    99  func (u *Unit) SetAgentStatus(status params.Status, info string, data map[string]interface{}) error {
   100  	var result params.ErrorResults
   101  	args := params.SetStatus{
   102  		Entities: []params.EntityStatusArgs{
   103  			{Tag: u.tag.String(), Status: status, Info: info, Data: data},
   104  		},
   105  	}
   106  	setStatusFacadeCall := "SetAgentStatus"
   107  	if u.st.facade.BestAPIVersion() < 2 {
   108  		setStatusFacadeCall = "SetStatus"
   109  	}
   110  	err := u.st.facade.FacadeCall(setStatusFacadeCall, args, &result)
   111  	if err != nil {
   112  		return err
   113  	}
   114  	return result.OneError()
   115  }
   116  
   117  // AddMetrics adds the metrics for the unit.
   118  func (u *Unit) AddMetrics(metrics []params.Metric) error {
   119  	var result params.ErrorResults
   120  	args := params.MetricsParams{
   121  		Metrics: []params.MetricsParam{{
   122  			Tag:     u.tag.String(),
   123  			Metrics: metrics,
   124  		}},
   125  	}
   126  	err := u.st.facade.FacadeCall("AddMetrics", args, &result)
   127  	if err != nil {
   128  		return errors.Annotate(err, "unable to add metric")
   129  	}
   130  	return result.OneError()
   131  }
   132  
   133  // AddMetricsBatches makes an api call to the uniter requesting it to store metrics batches in state.
   134  func (u *Unit) AddMetricBatches(batches []params.MetricBatch) (map[string]error, error) {
   135  	p := params.MetricBatchParams{
   136  		Batches: make([]params.MetricBatchParam, len(batches)),
   137  	}
   138  
   139  	batchResults := make(map[string]error, len(batches))
   140  
   141  	for i, batch := range batches {
   142  		p.Batches[i].Tag = u.tag.String()
   143  		p.Batches[i].Batch = batch
   144  
   145  		batchResults[batch.UUID] = nil
   146  	}
   147  	results := new(params.ErrorResults)
   148  	err := u.st.facade.FacadeCall("AddMetricBatches", p, results)
   149  	if params.IsCodeNotImplemented(err) {
   150  		for _, batch := range batches {
   151  			err = u.AddMetrics(batch.Metrics)
   152  			if err != nil {
   153  				batchResults[batch.UUID] = errors.Annotate(err, "failed to send metric batch")
   154  			}
   155  		}
   156  		return batchResults, nil
   157  	} else if err != nil {
   158  		return nil, errors.Annotate(err, "failed to send metric batches")
   159  	}
   160  	for i, result := range results.Results {
   161  		batchResults[batches[i].UUID] = result.Error
   162  	}
   163  	return batchResults, nil
   164  }
   165  
   166  // EnsureDead sets the unit lifecycle to Dead if it is Alive or
   167  // Dying. It does nothing otherwise.
   168  func (u *Unit) EnsureDead() error {
   169  	var result params.ErrorResults
   170  	args := params.Entities{
   171  		Entities: []params.Entity{{Tag: u.tag.String()}},
   172  	}
   173  	err := u.st.facade.FacadeCall("EnsureDead", args, &result)
   174  	if err != nil {
   175  		return err
   176  	}
   177  	return result.OneError()
   178  }
   179  
   180  // Watch returns a watcher for observing changes to the unit.
   181  func (u *Unit) Watch() (watcher.NotifyWatcher, error) {
   182  	return common.Watch(u.st.facade, u.tag)
   183  }
   184  
   185  // Service returns the service.
   186  func (u *Unit) Service() (*Service, error) {
   187  	service := &Service{
   188  		st:  u.st,
   189  		tag: u.ServiceTag(),
   190  	}
   191  	// Call Refresh() immediately to get the up-to-date
   192  	// life and other needed locally cached fields.
   193  	err := service.Refresh()
   194  	if err != nil {
   195  		return nil, err
   196  	}
   197  	return service, nil
   198  }
   199  
   200  // ConfigSettings returns the complete set of service charm config settings
   201  // available to the unit. Unset values will be replaced with the default
   202  // value for the associated option, and may thus be nil when no default is
   203  // specified.
   204  func (u *Unit) ConfigSettings() (charm.Settings, error) {
   205  	var results params.ConfigSettingsResults
   206  	args := params.Entities{
   207  		Entities: []params.Entity{{Tag: u.tag.String()}},
   208  	}
   209  	err := u.st.facade.FacadeCall("ConfigSettings", args, &results)
   210  	if err != nil {
   211  		return nil, err
   212  	}
   213  	if len(results.Results) != 1 {
   214  		return nil, fmt.Errorf("expected 1 result, got %d", len(results.Results))
   215  	}
   216  	result := results.Results[0]
   217  	if result.Error != nil {
   218  		return nil, result.Error
   219  	}
   220  	return charm.Settings(result.Settings), nil
   221  }
   222  
   223  // ServiceName returns the service name.
   224  func (u *Unit) ServiceName() string {
   225  	service, err := names.UnitService(u.Name())
   226  	if err != nil {
   227  		panic(err)
   228  	}
   229  	return service
   230  }
   231  
   232  // ServiceTag returns the service tag.
   233  func (u *Unit) ServiceTag() names.ServiceTag {
   234  	return names.NewServiceTag(u.ServiceName())
   235  }
   236  
   237  // Destroy, when called on a Alive unit, advances its lifecycle as far as
   238  // possible; it otherwise has no effect. In most situations, the unit's
   239  // life is just set to Dying; but if a principal unit that is not assigned
   240  // to a provisioned machine is Destroyed, it will be removed from state
   241  // directly.
   242  func (u *Unit) Destroy() error {
   243  	var result params.ErrorResults
   244  	args := params.Entities{
   245  		Entities: []params.Entity{{Tag: u.tag.String()}},
   246  	}
   247  	err := u.st.facade.FacadeCall("Destroy", args, &result)
   248  	if err != nil {
   249  		return err
   250  	}
   251  	return result.OneError()
   252  }
   253  
   254  // DestroyAllSubordinates destroys all subordinates of the unit.
   255  func (u *Unit) DestroyAllSubordinates() error {
   256  	var result params.ErrorResults
   257  	args := params.Entities{
   258  		Entities: []params.Entity{{Tag: u.tag.String()}},
   259  	}
   260  	err := u.st.facade.FacadeCall("DestroyAllSubordinates", args, &result)
   261  	if err != nil {
   262  		return err
   263  	}
   264  	return result.OneError()
   265  }
   266  
   267  // Resolved returns the resolved mode for the unit.
   268  //
   269  // NOTE: This differs from state.Unit.Resolved() by returning an
   270  // error as well, because it needs to make an API call
   271  func (u *Unit) Resolved() (params.ResolvedMode, error) {
   272  	var results params.ResolvedModeResults
   273  	args := params.Entities{
   274  		Entities: []params.Entity{{Tag: u.tag.String()}},
   275  	}
   276  	err := u.st.facade.FacadeCall("Resolved", args, &results)
   277  	if err != nil {
   278  		return "", err
   279  	}
   280  	if len(results.Results) != 1 {
   281  		return "", fmt.Errorf("expected 1 result, got %d", len(results.Results))
   282  	}
   283  	result := results.Results[0]
   284  	if result.Error != nil {
   285  		return "", result.Error
   286  	}
   287  	return result.Mode, nil
   288  }
   289  
   290  // AssignedMachine returns the unit's assigned machine tag or an error
   291  // satisfying params.IsCodeNotAssigned when the unit has no assigned
   292  // machine..
   293  func (u *Unit) AssignedMachine() (names.MachineTag, error) {
   294  	if u.st.BestAPIVersion() < 1 {
   295  		return names.MachineTag{}, errors.NotImplementedf("unit.AssignedMachine() (need V1+)")
   296  	}
   297  	var invalidTag names.MachineTag
   298  	var results params.StringResults
   299  	args := params.Entities{
   300  		Entities: []params.Entity{{Tag: u.tag.String()}},
   301  	}
   302  	err := u.st.facade.FacadeCall("AssignedMachine", args, &results)
   303  	if err != nil {
   304  		return invalidTag, err
   305  	}
   306  	if len(results.Results) != 1 {
   307  		return invalidTag, fmt.Errorf("expected 1 result, got %d", len(results.Results))
   308  	}
   309  	result := results.Results[0]
   310  	if result.Error != nil {
   311  		return invalidTag, result.Error
   312  	}
   313  	return names.ParseMachineTag(result.Result)
   314  }
   315  
   316  // IsPrincipal returns whether the unit is deployed in its own container,
   317  // and can therefore have subordinate services deployed alongside it.
   318  //
   319  // NOTE: This differs from state.Unit.IsPrincipal() by returning an
   320  // error as well, because it needs to make an API call.
   321  func (u *Unit) IsPrincipal() (bool, error) {
   322  	var results params.StringBoolResults
   323  	args := params.Entities{
   324  		Entities: []params.Entity{{Tag: u.tag.String()}},
   325  	}
   326  	err := u.st.facade.FacadeCall("GetPrincipal", args, &results)
   327  	if err != nil {
   328  		return false, err
   329  	}
   330  	if len(results.Results) != 1 {
   331  		return false, fmt.Errorf("expected 1 result, got %d", len(results.Results))
   332  	}
   333  	result := results.Results[0]
   334  	if result.Error != nil {
   335  		return false, result.Error
   336  	}
   337  	// GetPrincipal returns false when the unit is subordinate.
   338  	return !result.Ok, nil
   339  }
   340  
   341  // HasSubordinates returns the tags of any subordinate units.
   342  func (u *Unit) HasSubordinates() (bool, error) {
   343  	var results params.BoolResults
   344  	args := params.Entities{
   345  		Entities: []params.Entity{{Tag: u.tag.String()}},
   346  	}
   347  	err := u.st.facade.FacadeCall("HasSubordinates", args, &results)
   348  	if err != nil {
   349  		return false, err
   350  	}
   351  	if len(results.Results) != 1 {
   352  		return false, fmt.Errorf("expected 1 result, got %d", len(results.Results))
   353  	}
   354  	result := results.Results[0]
   355  	if result.Error != nil {
   356  		return false, result.Error
   357  	}
   358  	return result.Result, nil
   359  }
   360  
   361  // PublicAddress returns the public address of the unit and whether it
   362  // is valid.
   363  //
   364  // NOTE: This differs from state.Unit.PublicAddres() by returning
   365  // an error instead of a bool, because it needs to make an API call.
   366  //
   367  // TODO(dimitern): We might be able to drop this, once we have machine
   368  // addresses implemented fully. See also LP bug 1221798.
   369  func (u *Unit) PublicAddress() (string, error) {
   370  	var results params.StringResults
   371  	args := params.Entities{
   372  		Entities: []params.Entity{{Tag: u.tag.String()}},
   373  	}
   374  	err := u.st.facade.FacadeCall("PublicAddress", args, &results)
   375  	if err != nil {
   376  		return "", err
   377  	}
   378  	if len(results.Results) != 1 {
   379  		return "", fmt.Errorf("expected 1 result, got %d", len(results.Results))
   380  	}
   381  	result := results.Results[0]
   382  	if result.Error != nil {
   383  		return "", result.Error
   384  	}
   385  	return result.Result, nil
   386  }
   387  
   388  // PrivateAddress returns the private address of the unit and whether
   389  // it is valid.
   390  //
   391  // NOTE: This differs from state.Unit.PrivateAddress() by returning
   392  // an error instead of a bool, because it needs to make an API call.
   393  //
   394  // TODO(dimitern): We might be able to drop this, once we have machine
   395  // addresses implemented fully. See also LP bug 1221798.
   396  func (u *Unit) PrivateAddress() (string, error) {
   397  	var results params.StringResults
   398  	args := params.Entities{
   399  		Entities: []params.Entity{{Tag: u.tag.String()}},
   400  	}
   401  	err := u.st.facade.FacadeCall("PrivateAddress", args, &results)
   402  	if err != nil {
   403  		return "", err
   404  	}
   405  	if len(results.Results) != 1 {
   406  		return "", fmt.Errorf("expected 1 result, got %d", len(results.Results))
   407  	}
   408  	result := results.Results[0]
   409  	if result.Error != nil {
   410  		return "", result.Error
   411  	}
   412  	return result.Result, nil
   413  }
   414  
   415  // AvailabilityZone returns the availability zone of the unit.
   416  func (u *Unit) AvailabilityZone() (string, error) {
   417  	var results params.StringResults
   418  	args := params.Entities{
   419  		Entities: []params.Entity{{Tag: u.tag.String()}},
   420  	}
   421  	if err := u.st.facade.FacadeCall("AvailabilityZone", args, &results); err != nil {
   422  		return "", errors.Trace(err)
   423  	}
   424  	if len(results.Results) != 1 {
   425  		return "", errors.Errorf("expected 1 result, got %d", len(results.Results))
   426  	}
   427  	result := results.Results[0]
   428  	if result.Error != nil {
   429  		return "", errors.Trace(result.Error)
   430  	}
   431  	return result.Result, nil
   432  }
   433  
   434  // OpenPorts sets the policy of the port range with protocol to be
   435  // opened.
   436  func (u *Unit) OpenPorts(protocol string, fromPort, toPort int) error {
   437  	var result params.ErrorResults
   438  	args := params.EntitiesPortRanges{
   439  		Entities: []params.EntityPortRange{{
   440  			Tag:      u.tag.String(),
   441  			Protocol: protocol,
   442  			FromPort: fromPort,
   443  			ToPort:   toPort,
   444  		}},
   445  	}
   446  	err := u.st.facade.FacadeCall("OpenPorts", args, &result)
   447  	if err != nil {
   448  		return err
   449  	}
   450  	return result.OneError()
   451  }
   452  
   453  // ClosePorts sets the policy of the port range with protocol to be
   454  // closed.
   455  func (u *Unit) ClosePorts(protocol string, fromPort, toPort int) error {
   456  	var result params.ErrorResults
   457  	args := params.EntitiesPortRanges{
   458  		Entities: []params.EntityPortRange{{
   459  			Tag:      u.tag.String(),
   460  			Protocol: protocol,
   461  			FromPort: fromPort,
   462  			ToPort:   toPort,
   463  		}},
   464  	}
   465  	err := u.st.facade.FacadeCall("ClosePorts", args, &result)
   466  	if err != nil {
   467  		return err
   468  	}
   469  	return result.OneError()
   470  }
   471  
   472  // OpenPort sets the policy of the port with protocol and number to be
   473  // opened.
   474  //
   475  // TODO(dimitern): This is deprecated and is kept for
   476  // backwards-compatibility. Use OpenPorts instead.
   477  func (u *Unit) OpenPort(protocol string, number int) error {
   478  	var result params.ErrorResults
   479  	args := params.EntitiesPorts{
   480  		Entities: []params.EntityPort{
   481  			{Tag: u.tag.String(), Protocol: protocol, Port: number},
   482  		},
   483  	}
   484  	err := u.st.facade.FacadeCall("OpenPort", args, &result)
   485  	if err != nil {
   486  		return err
   487  	}
   488  	return result.OneError()
   489  }
   490  
   491  // ClosePort sets the policy of the port with protocol and number to
   492  // be closed.
   493  //
   494  // TODO(dimitern): This is deprecated and is kept for
   495  // backwards-compatibility. Use ClosePorts instead.
   496  func (u *Unit) ClosePort(protocol string, number int) error {
   497  	var result params.ErrorResults
   498  	args := params.EntitiesPorts{
   499  		Entities: []params.EntityPort{
   500  			{Tag: u.tag.String(), Protocol: protocol, Port: number},
   501  		},
   502  	}
   503  	err := u.st.facade.FacadeCall("ClosePort", args, &result)
   504  	if err != nil {
   505  		return err
   506  	}
   507  	return result.OneError()
   508  }
   509  
   510  var ErrNoCharmURLSet = errors.New("unit has no charm url set")
   511  
   512  // CharmURL returns the charm URL this unit is currently using.
   513  //
   514  // NOTE: This differs from state.Unit.CharmURL() by returning
   515  // an error instead of a bool, because it needs to make an API call.
   516  func (u *Unit) CharmURL() (*charm.URL, error) {
   517  	var results params.StringBoolResults
   518  	args := params.Entities{
   519  		Entities: []params.Entity{{Tag: u.tag.String()}},
   520  	}
   521  	err := u.st.facade.FacadeCall("CharmURL", args, &results)
   522  	if err != nil {
   523  		return nil, err
   524  	}
   525  	if len(results.Results) != 1 {
   526  		return nil, fmt.Errorf("expected 1 result, got %d", len(results.Results))
   527  	}
   528  	result := results.Results[0]
   529  	if result.Error != nil {
   530  		return nil, result.Error
   531  	}
   532  	if result.Result != "" {
   533  		curl, err := charm.ParseURL(result.Result)
   534  		if err != nil {
   535  			return nil, err
   536  		}
   537  		return curl, nil
   538  	}
   539  	return nil, ErrNoCharmURLSet
   540  }
   541  
   542  // SetCharmURL marks the unit as currently using the supplied charm URL.
   543  // An error will be returned if the unit is dead, or the charm URL not known.
   544  func (u *Unit) SetCharmURL(curl *charm.URL) error {
   545  	if curl == nil {
   546  		return fmt.Errorf("charm URL cannot be nil")
   547  	}
   548  	var result params.ErrorResults
   549  	args := params.EntitiesCharmURL{
   550  		Entities: []params.EntityCharmURL{
   551  			{Tag: u.tag.String(), CharmURL: curl.String()},
   552  		},
   553  	}
   554  	err := u.st.facade.FacadeCall("SetCharmURL", args, &result)
   555  	if err != nil {
   556  		return err
   557  	}
   558  	return result.OneError()
   559  }
   560  
   561  // ClearResolved removes any resolved setting on the unit.
   562  func (u *Unit) ClearResolved() error {
   563  	var result params.ErrorResults
   564  	args := params.Entities{
   565  		Entities: []params.Entity{{Tag: u.tag.String()}},
   566  	}
   567  	err := u.st.facade.FacadeCall("ClearResolved", args, &result)
   568  	if err != nil {
   569  		return err
   570  	}
   571  	return result.OneError()
   572  }
   573  
   574  // WatchConfigSettings returns a watcher for observing changes to the
   575  // unit's service configuration settings. The unit must have a charm URL
   576  // set before this method is called, and the returned watcher will be
   577  // valid only while the unit's charm URL is not changed.
   578  func (u *Unit) WatchConfigSettings() (watcher.NotifyWatcher, error) {
   579  	var results params.NotifyWatchResults
   580  	args := params.Entities{
   581  		Entities: []params.Entity{{Tag: u.tag.String()}},
   582  	}
   583  	err := u.st.facade.FacadeCall("WatchConfigSettings", args, &results)
   584  	if err != nil {
   585  		return nil, err
   586  	}
   587  	if len(results.Results) != 1 {
   588  		return nil, fmt.Errorf("expected 1 result, got %d", len(results.Results))
   589  	}
   590  	result := results.Results[0]
   591  	if result.Error != nil {
   592  		return nil, result.Error
   593  	}
   594  	w := watcher.NewNotifyWatcher(u.st.facade.RawAPICaller(), result)
   595  	return w, nil
   596  }
   597  
   598  // WatchAddresses returns a watcher for observing changes to the
   599  // unit's addresses. The unit must be assigned to a machine before
   600  // this method is called, and the returned watcher will be valid only
   601  // while the unit's assigned machine is not changed.
   602  func (u *Unit) WatchAddresses() (watcher.NotifyWatcher, error) {
   603  	var results params.NotifyWatchResults
   604  	args := params.Entities{
   605  		Entities: []params.Entity{{Tag: u.tag.String()}},
   606  	}
   607  	err := u.st.facade.FacadeCall("WatchUnitAddresses", args, &results)
   608  	if err != nil {
   609  		return nil, err
   610  	}
   611  	if len(results.Results) != 1 {
   612  		return nil, fmt.Errorf("expected 1 result, got %d", len(results.Results))
   613  	}
   614  	result := results.Results[0]
   615  	if result.Error != nil {
   616  		return nil, result.Error
   617  	}
   618  	w := watcher.NewNotifyWatcher(u.st.facade.RawAPICaller(), result)
   619  	return w, nil
   620  }
   621  
   622  // WatchActionNotifications returns a StringsWatcher for observing the
   623  // ids of Actions added to the Unit. The initial event will contain the
   624  // ids of any Actions pending at the time the Watcher is made.
   625  func (u *Unit) WatchActionNotifications() (watcher.StringsWatcher, error) {
   626  	var results params.StringsWatchResults
   627  	args := params.Entities{
   628  		Entities: []params.Entity{{Tag: u.tag.String()}},
   629  	}
   630  	err := u.st.facade.FacadeCall("WatchActionNotifications", args, &results)
   631  	if err != nil {
   632  		return nil, err
   633  	}
   634  	if len(results.Results) != 1 {
   635  		return nil, fmt.Errorf("expected 1 result, got %d", len(results.Results))
   636  	}
   637  	result := results.Results[0]
   638  	if result.Error != nil {
   639  		return nil, result.Error
   640  	}
   641  	w := watcher.NewStringsWatcher(u.st.facade.RawAPICaller(), result)
   642  	return w, nil
   643  }
   644  
   645  // RequestReboot sets the reboot flag for its machine agent
   646  func (u *Unit) RequestReboot() error {
   647  	machineId, err := u.AssignedMachine()
   648  	if err != nil {
   649  		return err
   650  	}
   651  	var result params.ErrorResults
   652  	args := params.Entities{
   653  		Entities: []params.Entity{{Tag: machineId.String()}},
   654  	}
   655  	err = u.st.facade.FacadeCall("RequestReboot", args, &result)
   656  	if err != nil {
   657  		return err
   658  	}
   659  	return result.OneError()
   660  }
   661  
   662  // JoinedRelations returns the tags of the relations the unit has joined.
   663  func (u *Unit) JoinedRelations() ([]names.RelationTag, error) {
   664  	var results params.StringsResults
   665  	args := params.Entities{
   666  		Entities: []params.Entity{{Tag: u.tag.String()}},
   667  	}
   668  	err := u.st.facade.FacadeCall("JoinedRelations", args, &results)
   669  	if err != nil {
   670  		return nil, err
   671  	}
   672  	if len(results.Results) != 1 {
   673  		return nil, fmt.Errorf("expected 1 result, got %d", len(results.Results))
   674  	}
   675  	result := results.Results[0]
   676  	if result.Error != nil {
   677  		return nil, result.Error
   678  	}
   679  	var relTags []names.RelationTag
   680  	for _, rel := range result.Result {
   681  		tag, err := names.ParseRelationTag(rel)
   682  		if err != nil {
   683  			return nil, err
   684  		}
   685  		relTags = append(relTags, tag)
   686  	}
   687  	return relTags, nil
   688  }
   689  
   690  // MeterStatus returns the meter status of the unit.
   691  func (u *Unit) MeterStatus() (statusCode, statusInfo string, rErr error) {
   692  	var results params.MeterStatusResults
   693  	args := params.Entities{
   694  		Entities: []params.Entity{{Tag: u.tag.String()}},
   695  	}
   696  	err := u.st.facade.FacadeCall("GetMeterStatus", args, &results)
   697  	if err != nil {
   698  		return "", "", errors.Trace(err)
   699  	}
   700  	if len(results.Results) != 1 {
   701  		return "", "", errors.Errorf("expected 1 result, got %d", len(results.Results))
   702  	}
   703  	result := results.Results[0]
   704  	if result.Error != nil {
   705  		return "", "", errors.Trace(result.Error)
   706  	}
   707  	return result.Code, result.Info, nil
   708  }
   709  
   710  // WatchMeterStatus returns a watcher for observing changes to the
   711  // unit's meter status.
   712  func (u *Unit) WatchMeterStatus() (watcher.NotifyWatcher, error) {
   713  	var results params.NotifyWatchResults
   714  	args := params.Entities{
   715  		Entities: []params.Entity{{Tag: u.tag.String()}},
   716  	}
   717  	err := u.st.facade.FacadeCall("WatchMeterStatus", args, &results)
   718  	if err != nil {
   719  		return nil, err
   720  	}
   721  	if len(results.Results) != 1 {
   722  		return nil, fmt.Errorf("expected 1 result, got %d", len(results.Results))
   723  	}
   724  	result := results.Results[0]
   725  	if result.Error != nil {
   726  		return nil, result.Error
   727  	}
   728  	w := watcher.NewNotifyWatcher(u.st.facade.RawAPICaller(), result)
   729  	return w, nil
   730  }
   731  
   732  // WatchStorage returns a watcher for observing changes to the
   733  // unit's storage attachments.
   734  func (u *Unit) WatchStorage() (watcher.StringsWatcher, error) {
   735  	return u.st.WatchUnitStorageAttachments(u.tag)
   736  }
   737  
   738  // AddStorage adds desired storage instances to a unit.
   739  func (u *Unit) AddStorage(constraints map[string][]params.StorageConstraints) error {
   740  	if u.st.facade.BestAPIVersion() < 2 {
   741  		return errors.NotImplementedf("AddStorage() (need V2+)")
   742  	}
   743  
   744  	all := make([]params.StorageAddParams, 0, len(constraints))
   745  	for storage, cons := range constraints {
   746  		for _, one := range cons {
   747  			all = append(all, params.StorageAddParams{u.Tag().String(), storage, one})
   748  		}
   749  	}
   750  
   751  	args := params.StoragesAddParams{Storages: all}
   752  	var results params.ErrorResults
   753  	err := u.st.facade.FacadeCall("AddUnitStorage", args, &results)
   754  	if err != nil {
   755  		return err
   756  	}
   757  
   758  	return results.Combine()
   759  }