github.com/juju/juju@v0.0.0-20240430160146-1752b71fcf00/apiserver/common/modelstatus.go (about)

     1  // Copyright 2016 Canonical Ltd.
     2  // Licensed under the AGPLv3, see LICENCE file for details.
     3  
     4  package common
     5  
     6  import (
     7  	"github.com/juju/collections/transform"
     8  	"github.com/juju/errors"
     9  	"github.com/juju/names/v5"
    10  
    11  	apiservererrors "github.com/juju/juju/apiserver/errors"
    12  	"github.com/juju/juju/apiserver/facade"
    13  	"github.com/juju/juju/core/life"
    14  	"github.com/juju/juju/rpc/params"
    15  	"github.com/juju/juju/state"
    16  )
    17  
    18  // ModelStatusAPI implements the ModelStatus() API.
    19  type ModelStatusAPI struct {
    20  	authorizer facade.Authorizer
    21  	apiUser    names.UserTag
    22  	backend    ModelManagerBackend
    23  }
    24  
    25  // ModelApplicationInfo returns information about applications.
    26  func ModelApplicationInfo(applications []Application) ([]params.ModelApplicationInfo, error) {
    27  	applicationInfo := transform.Slice(applications, func(app Application) params.ModelApplicationInfo {
    28  		return params.ModelApplicationInfo{Name: app.Name()}
    29  	})
    30  	return applicationInfo, nil
    31  }
    32  
    33  // NewModelStatusAPI creates an implementation providing the ModelStatus() API.
    34  func NewModelStatusAPI(backend ModelManagerBackend, authorizer facade.Authorizer, apiUser names.UserTag) *ModelStatusAPI {
    35  	return &ModelStatusAPI{
    36  		authorizer: authorizer,
    37  		apiUser:    apiUser,
    38  		backend:    backend,
    39  	}
    40  }
    41  
    42  // ModelStatus returns a summary of the model.
    43  func (c *ModelStatusAPI) ModelStatus(req params.Entities) (params.ModelStatusResults, error) {
    44  	models := req.Entities
    45  	status := make([]params.ModelStatus, len(models))
    46  	for i, model := range models {
    47  		modelStatus, err := c.modelStatus(model.Tag)
    48  		if err != nil {
    49  			status[i].Error = apiservererrors.ServerError(err)
    50  			continue
    51  		}
    52  		status[i] = modelStatus
    53  	}
    54  	return params.ModelStatusResults{Results: status}, nil
    55  }
    56  
    57  func (c *ModelStatusAPI) modelStatus(tag string) (params.ModelStatus, error) {
    58  	var status params.ModelStatus
    59  	modelTag, err := names.ParseModelTag(tag)
    60  	if err != nil {
    61  		return status, errors.Trace(err)
    62  	}
    63  	st := c.backend
    64  	if modelTag != c.backend.ModelTag() {
    65  		otherSt, releaser, err := c.backend.GetBackend(modelTag.Id())
    66  		if err != nil {
    67  			return status, errors.Trace(err)
    68  		}
    69  		defer releaser()
    70  		st = otherSt
    71  	}
    72  
    73  	model, err := st.Model()
    74  	if err != nil {
    75  		return status, errors.Trace(err)
    76  	}
    77  	isAdmin, err := HasModelAdmin(c.authorizer, c.backend.ControllerTag(), model.ModelTag())
    78  	if err != nil {
    79  		return status, errors.Trace(err)
    80  	}
    81  	if !isAdmin {
    82  		return status, apiservererrors.ErrPerm
    83  	}
    84  
    85  	machines, err := st.AllMachines()
    86  	if err != nil {
    87  		return status, errors.Trace(err)
    88  	}
    89  
    90  	var hostedMachines []Machine
    91  	for _, m := range machines {
    92  		if !m.IsManager() {
    93  			hostedMachines = append(hostedMachines, m)
    94  		}
    95  	}
    96  
    97  	applications, err := st.AllApplications()
    98  	if err != nil {
    99  		return status, errors.Trace(err)
   100  	}
   101  	var unitCount int
   102  	for _, app := range applications {
   103  		unitCount += app.UnitCount()
   104  	}
   105  
   106  	modelMachines, err := ModelMachineInfo(st)
   107  	if err != nil {
   108  		return status, errors.Trace(err)
   109  	}
   110  
   111  	// TODO (Anvial): we need to think about common parameter list (maybe "st") to all these functions:
   112  	// ModelMachineInfo, ModelApplicationInfo, ModelVolumeInfo, ModelFilesystemInfo. Looks like better to do in
   113  	// ModelMachineInfo style and optimize st.*() calls.
   114  
   115  	modelApplications, err := ModelApplicationInfo(applications)
   116  	if err != nil {
   117  		return status, errors.Trace(err)
   118  	}
   119  
   120  	volumes, err := st.AllVolumes()
   121  	if err != nil {
   122  		return status, errors.Trace(err)
   123  	}
   124  	modelVolumes := ModelVolumeInfo(volumes)
   125  
   126  	filesystems, err := st.AllFilesystems()
   127  	if err != nil {
   128  		return status, errors.Trace(err)
   129  	}
   130  	modelFilesystems := ModelFilesystemInfo(filesystems)
   131  
   132  	result := params.ModelStatus{
   133  		ModelTag:           tag,
   134  		OwnerTag:           model.Owner().String(),
   135  		Life:               life.Value(model.Life().String()),
   136  		Type:               string(model.Type()),
   137  		HostedMachineCount: len(hostedMachines),
   138  		ApplicationCount:   len(modelApplications),
   139  		UnitCount:          unitCount,
   140  		Applications:       modelApplications,
   141  		Machines:           modelMachines,
   142  		Volumes:            modelVolumes,
   143  		Filesystems:        modelFilesystems,
   144  	}
   145  
   146  	return result, nil
   147  }
   148  
   149  // ModelFilesystemInfo returns information about filesystems in the model.
   150  func ModelFilesystemInfo(in []state.Filesystem) []params.ModelFilesystemInfo {
   151  	out := make([]params.ModelFilesystemInfo, len(in))
   152  	for i, in := range in {
   153  		var statusString string
   154  		status, err := in.Status()
   155  		if err != nil {
   156  			statusString = err.Error()
   157  		} else {
   158  			statusString = string(status.Status)
   159  		}
   160  		var providerId string
   161  		if info, err := in.Info(); err == nil {
   162  			providerId = info.FilesystemId
   163  		}
   164  		out[i] = params.ModelFilesystemInfo{
   165  			Id:         in.Tag().Id(),
   166  			ProviderId: providerId,
   167  			Status:     statusString,
   168  			Message:    status.Message,
   169  			Detachable: in.Detachable(),
   170  		}
   171  	}
   172  	return out
   173  }
   174  
   175  // ModelVolumeInfo returns information about volumes in the model.
   176  func ModelVolumeInfo(in []state.Volume) []params.ModelVolumeInfo {
   177  	out := make([]params.ModelVolumeInfo, len(in))
   178  	for i, in := range in {
   179  		var statusString string
   180  		status, err := in.Status()
   181  		if err != nil {
   182  			statusString = err.Error()
   183  		} else {
   184  			statusString = string(status.Status)
   185  		}
   186  		var providerId string
   187  		if info, err := in.Info(); err == nil {
   188  			providerId = info.VolumeId
   189  		}
   190  		out[i] = params.ModelVolumeInfo{
   191  			Id:         in.Tag().Id(),
   192  			ProviderId: providerId,
   193  			Status:     statusString,
   194  			Message:    status.Message,
   195  			Detachable: in.Detachable(),
   196  		}
   197  	}
   198  	return out
   199  }