github.com/altoros/juju-vmware@v0.0.0-20150312064031-f19ae857ccca/apiserver/diskformatter/diskformatter.go (about) 1 // Copyright 2015 Canonical Ltd. 2 // Licensed under the AGPLv3, see LICENCE file for details. 3 4 package diskformatter 5 6 import ( 7 "github.com/juju/loggo" 8 "github.com/juju/names" 9 10 "github.com/juju/juju/apiserver/common" 11 "github.com/juju/juju/apiserver/params" 12 "github.com/juju/juju/state" 13 "github.com/juju/juju/state/watcher" 14 "github.com/juju/juju/storage" 15 ) 16 17 func init() { 18 common.RegisterStandardFacade("DiskFormatter", 1, NewDiskFormatterAPI) 19 } 20 21 var logger = loggo.GetLogger("juju.apiserver.diskformatter") 22 23 // DiskFormatterAPI provides access to the DiskFormatter API facade. 24 type DiskFormatterAPI struct { 25 st stateInterface 26 resources *common.Resources 27 authorizer common.Authorizer 28 getAuthFunc common.GetAuthFunc 29 } 30 31 // NewDiskFormatterAPI creates a new server-side DiskFormatter API facade. 32 func NewDiskFormatterAPI( 33 st *state.State, 34 resources *common.Resources, 35 authorizer common.Authorizer, 36 ) (*DiskFormatterAPI, error) { 37 38 if !authorizer.AuthUnitAgent() { 39 return nil, common.ErrPerm 40 } 41 42 getAuthFunc := func() (common.AuthFunc, error) { 43 return authorizer.AuthOwner, nil 44 } 45 46 return &DiskFormatterAPI{ 47 st: getState(st), 48 resources: resources, 49 authorizer: authorizer, 50 getAuthFunc: getAuthFunc, 51 }, nil 52 } 53 54 // WatchBlockDevices returns a StringsWatcher for observing changes 55 // to block devices associated with the unit's machine. 56 func (a *DiskFormatterAPI) WatchBlockDevices(args params.Entities) (params.StringsWatchResults, error) { 57 result := params.StringsWatchResults{ 58 Results: make([]params.StringsWatchResult, len(args.Entities)), 59 } 60 canAccess, err := a.getAuthFunc() 61 if err != nil { 62 return params.StringsWatchResults{}, err 63 } 64 for i, entity := range args.Entities { 65 unit, err := names.ParseUnitTag(entity.Tag) 66 if err != nil { 67 result.Results[i].Error = common.ServerError(common.ErrPerm) 68 continue 69 } 70 err = common.ErrPerm 71 var watcherId string 72 var changes []string 73 if canAccess(unit) { 74 watcherId, changes, err = a.watchOneBlockDevices(unit) 75 } 76 result.Results[i].StringsWatcherId = watcherId 77 result.Results[i].Changes = changes 78 result.Results[i].Error = common.ServerError(err) 79 } 80 return result, nil 81 } 82 83 func (a *DiskFormatterAPI) watchOneBlockDevices(tag names.UnitTag) (string, []string, error) { 84 w, err := a.st.WatchUnitMachineBlockDevices(tag) 85 if err != nil { 86 return "", nil, err 87 } 88 // Consume the initial event. Technically, API 89 // calls to Watch 'transmit' the initial event 90 // in the Watch response. 91 if changes, ok := <-w.Changes(); ok { 92 return a.resources.Register(w), changes, nil 93 } 94 return "", nil, watcher.EnsureErr(w) 95 } 96 97 // BlockDevices returns details about each specified block device. 98 func (a *DiskFormatterAPI) BlockDevices(args params.Entities) (params.BlockDeviceResults, error) { 99 result := params.BlockDeviceResults{ 100 Results: make([]params.BlockDeviceResult, len(args.Entities)), 101 } 102 canAccess, err := a.getAuthFunc() 103 if err != nil { 104 return params.BlockDeviceResults{}, err 105 } 106 one := func(entity params.Entity) (storage.BlockDevice, error) { 107 blockDevice, _, err := a.oneBlockDevice(entity.Tag, canAccess) 108 if err != nil { 109 return storage.BlockDevice{}, err 110 } 111 return storageBlockDevice(blockDevice) 112 } 113 for i, entity := range args.Entities { 114 blockDevice, err := one(entity) 115 result.Results[i].Result = blockDevice 116 result.Results[i].Error = common.ServerError(err) 117 } 118 return result, nil 119 } 120 121 // BlockDeviceStorageInstances returns details of storage instances corresponding 122 // to each specified block device. 123 func (a *DiskFormatterAPI) BlockDeviceStorageInstances(args params.Entities) (params.StorageInstanceResults, error) { 124 result := params.StorageInstanceResults{ 125 Results: make([]params.StorageInstanceResult, len(args.Entities)), 126 } 127 canAccess, err := a.getAuthFunc() 128 if err != nil { 129 return params.StorageInstanceResults{}, err 130 } 131 one := func(entity params.Entity) (storage.StorageInstance, error) { 132 _, storageInstance, err := a.oneBlockDevice(entity.Tag, canAccess) 133 if err != nil { 134 return storage.StorageInstance{}, err 135 } 136 return storageStorageInstance(storageInstance) 137 } 138 for i, entity := range args.Entities { 139 storageInstance, err := one(entity) 140 result.Results[i].Result = storageInstance 141 result.Results[i].Error = common.ServerError(err) 142 } 143 return result, nil 144 } 145 146 func (a *DiskFormatterAPI) oneBlockDevice(tag string, canAccess common.AuthFunc) (state.BlockDevice, state.StorageInstance, error) { 147 diskTag, err := names.ParseDiskTag(tag) 148 if err != nil { 149 return nil, nil, common.ErrPerm 150 } 151 blockDevice, err := a.st.BlockDevice(diskTag.Id()) 152 if err != nil { 153 return nil, nil, common.ErrPerm 154 } 155 if !blockDevice.Attached() { 156 // ignore unattached block devices 157 return nil, nil, common.ErrPerm 158 } 159 storageInstanceId, ok := blockDevice.StorageInstance() 160 if !ok { 161 // not assigned to any storage instance 162 return nil, nil, common.ErrPerm 163 } 164 storageInstance, err := a.st.StorageInstance(storageInstanceId) 165 if err != nil || !canAccess(storageInstance.Owner()) { 166 return nil, nil, common.ErrPerm 167 } 168 return blockDevice, storageInstance, nil 169 } 170 171 // NOTE: purposefully not using field keys below, so 172 // the code breaks if structures change. 173 174 func storageBlockDevice(dev state.BlockDevice) (storage.BlockDevice, error) { 175 if dev == nil { 176 return storage.BlockDevice{}, nil 177 } 178 info, err := dev.Info() 179 if err != nil { 180 return storage.BlockDevice{}, err 181 } 182 return storage.BlockDevice{ 183 dev.Name(), 184 "", // TODO(axw) ProviderId 185 info.DeviceName, 186 info.Label, 187 info.UUID, 188 info.Serial, 189 info.Size, 190 info.FilesystemType, 191 info.InUse, 192 }, nil 193 } 194 195 func storageStorageInstance(st state.StorageInstance) (storage.StorageInstance, error) { 196 if st == nil { 197 return storage.StorageInstance{}, nil 198 } 199 return storage.StorageInstance{ 200 st.Id(), 201 storageStorageKind(st.Kind()), 202 "", // TODO(wallyworld) Location 203 }, nil 204 } 205 206 func storageStorageKind(k state.StorageKind) storage.StorageKind { 207 switch k { 208 case state.StorageKindBlock: 209 return storage.StorageKindBlock 210 case state.StorageKindFilesystem: 211 return storage.StorageKindFilesystem 212 default: 213 return storage.StorageKindUnknown 214 } 215 }