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 }