func (m *mockStorageAccessor) StorageAttachmentLife(ids []params.StorageAttachmentId) ([]params.LifeResult, error) { if m.storageAttachmentLife != nil { return m.storageAttachmentLife(ids) } results := make([]params.LifeResult, len(ids)) for i, id := range ids { storageTag, err := names.ParseStorageTag(id.StorageTag) if err != nil { results[i].Error = common.ServerError(err) continue } unitTag, err := names.ParseUnitTag(id.UnitTag) if err != nil { results[i].Error = common.ServerError(err) continue } att, err := m.storageAttachment(storageTag, unitTag) if err != nil { results[i].Error = common.ServerError(err) continue } results[i].Life = att.Life } return results, nil }
// Refresh fetches all of the unit's storage attachments and processes each // one as in UpdateStorage. func (a *Attachments) Refresh() error { attachmentIds, err := a.st.UnitStorageAttachments(a.unitTag) if err != nil { return errors.Annotate(err, "getting unit attachments") } storageTags := make([]names.StorageTag, len(attachmentIds)) for i, attachmentId := range attachmentIds { storageTag, err := names.ParseStorageTag(attachmentId.StorageTag) if err != nil { return errors.Trace(err) } storageTags[i] = storageTag } // Remove non-existent storage from pending. for pending := range a.pending { var found bool for _, active := range storageTags { if pending == active { found = true break } } if !found { a.pending.Remove(pending) } } return a.UpdateStorage(storageTags) }
func (s *StorageAPI) watchOneStorageAttachment(id params.StorageAttachmentId, canAccess func(names.Tag) bool) (params.NotifyWatchResult, error) { // Watching a storage attachment is implemented as watching the // underlying volume or filesystem attachment. The only thing // we don't necessarily see in doing this is the lifecycle state // changes, but these may be observed by using the // WatchUnitStorageAttachments watcher. nothing := params.NotifyWatchResult{} unitTag, err := names.ParseUnitTag(id.UnitTag) if err != nil || !canAccess(unitTag) { return nothing, common.ErrPerm } storageTag, err := names.ParseStorageTag(id.StorageTag) if err != nil { return nothing, err } machineTag, err := s.st.UnitAssignedMachine(unitTag) if err != nil { return nothing, err } watch, err := common.WatchStorageAttachment(s.st, storageTag, machineTag, unitTag) if err != nil { return nothing, errors.Trace(err) } if _, ok := <-watch.Changes(); ok { return params.NotifyWatchResult{ NotifyWatcherId: s.resources.Register(watch), }, nil } return nothing, watcher.EnsureErr(watch) }
func (s *StorageAPI) removeOneStorageAttachment(id params.StorageAttachmentId, canAccess func(names.Tag) bool) error { unitTag, err := names.ParseUnitTag(id.UnitTag) if err != nil { return err } if !canAccess(unitTag) { return common.ErrPerm } storageTag, err := names.ParseStorageTag(id.StorageTag) if err != nil { return err } return s.st.RemoveStorageAttachment(storageTag, unitTag) }
func (s *StorageAPI) getOneStateStorageAttachment(canAccess common.AuthFunc, id params.StorageAttachmentId) (state.StorageAttachment, error) { unitTag, err := names.ParseUnitTag(id.UnitTag) if err != nil { return nil, err } if !canAccess(unitTag) { return nil, common.ErrPerm } storageTag, err := names.ParseStorageTag(id.StorageTag) if err != nil { return nil, err } return s.st.StorageAttachment(storageTag, unitTag) }
// init processes the storage state directory and creates storagers // for the state files found. func (a *Attachments) init() error { if err := os.MkdirAll(a.storageStateDir, 0755); err != nil { return errors.Annotate(err, "creating storage state dir") } // Query all remote, known storage attachments for the unit, // so we can cull state files, and store current context. attachmentIds, err := a.st.UnitStorageAttachments(a.unitTag) if err != nil { return errors.Annotate(err, "getting unit attachments") } attachmentsByTag := make(map[names.StorageTag]struct{}) for _, attachmentId := range attachmentIds { storageTag, err := names.ParseStorageTag(attachmentId.StorageTag) if err != nil { return errors.Trace(err) } attachmentsByTag[storageTag] = struct{}{} } stateFiles, err := readAllStateFiles(a.storageStateDir) if err != nil { return errors.Annotate(err, "reading storage state dirs") } for storageTag, stateFile := range stateFiles { if _, ok := attachmentsByTag[storageTag]; !ok { if err := stateFile.Remove(); err != nil { return errors.Trace(err) } continue } // Since there's a state file, we must previously have handled // at least "storage-attached", so there is no possibility of // short-circuiting the storage's removal. if err := a.add(storageTag, stateFile); err != nil { return errors.Trace(err) } } for storageTag := range attachmentsByTag { if _, ok := stateFiles[storageTag]; !ok { // There is no state file for the attachment, so no // hooks have been committed for it. a.pending.Add(storageTag) } // Non-locally recorded attachments will be further handled // by UpdateStorage. } return nil }
func createStorageInfo(details params.StorageDetails) (names.StorageTag, StorageInfo, error) { storageTag, err := names.ParseStorageTag(details.StorageTag) if err != nil { return names.StorageTag{}, StorageInfo{}, errors.Trace(err) } info := StorageInfo{ Kind: details.Kind.String(), Status: EntityStatus{ details.Status.Status, details.Status.Info, // TODO(axw) we should support formatting as ISO time common.FormatTime(details.Status.Since, false), }, Persistent: details.Persistent, } if len(details.Attachments) > 0 { unitStorageAttachments := make(map[string]UnitStorageAttachment) for unitTagString, attachmentDetails := range details.Attachments { unitTag, err := names.ParseUnitTag(unitTagString) if err != nil { return names.StorageTag{}, StorageInfo{}, errors.Trace(err) } var machineId string if attachmentDetails.MachineTag != "" { machineTag, err := names.ParseMachineTag(attachmentDetails.MachineTag) if err != nil { return names.StorageTag{}, StorageInfo{}, errors.Trace(err) } machineId = machineTag.Id() } unitStorageAttachments[unitTag.Id()] = UnitStorageAttachment{ machineId, attachmentDetails.Location, } } info.Attachments = &StorageAttachments{unitStorageAttachments} } return storageTag, info, nil }
// Show retrieves and returns detailed information about desired storage // identified by supplied tags. If specified storage cannot be retrieved, // individual error is returned instead of storage information. func (api *API) Show(entities params.Entities) (params.StorageDetailsResults, error) { var all []params.StorageDetailsResult for _, entity := range entities.Entities { storageTag, err := names.ParseStorageTag(entity.Tag) if err != nil { all = append(all, params.StorageDetailsResult{ Error: common.ServerError(err), }) continue } found, instance, serverErr := api.getStorageInstance(storageTag) if err != nil { all = append(all, params.StorageDetailsResult{Error: serverErr}) continue } if found { results := api.createStorageDetailsResult(storageTag, instance) all = append(all, results...) } } return params.StorageDetailsResults{Results: all}, nil }
// StorageDetails retrieves and returns detailed information about desired // storage identified by supplied tags. If specified storage cannot be // retrieved, individual error is returned instead of storage information. func (api *API) StorageDetails(entities params.Entities) (params.StorageDetailsResults, error) { results := make([]params.StorageDetailsResult, len(entities.Entities)) for i, entity := range entities.Entities { storageTag, err := names.ParseStorageTag(entity.Tag) if err != nil { results[i].Error = common.ServerError(err) continue } storageInstance, err := api.storage.StorageInstance(storageTag) if err != nil { results[i].Error = common.ServerError(err) continue } details, err := createStorageDetails(api.storage, storageInstance) if err != nil { results[i].Error = common.ServerError(err) continue } results[i].Result = details } return params.StorageDetailsResults{Results: results}, nil }
// formatStorageDetails takes a set of StorageDetail and creates a // mapping keyed on unit and storage id. func formatStorageDetails(storages []params.StorageDetails) (map[string]map[string]StorageInfo, error) { if len(storages) == 0 { return nil, nil } output := make(map[string]map[string]StorageInfo) for _, one := range storages { storageTag, err := names.ParseStorageTag(one.StorageTag) if err != nil { return nil, errors.Annotate(err, "invalid storage tag") } unitTag, err := names.ParseTag(one.UnitTag) if err != nil { return nil, errors.Annotate(err, "invalid unit tag") } storageName, err := names.StorageName(storageTag.Id()) if err != nil { panic(err) // impossible } si := StorageInfo{ StorageName: storageName, Kind: one.Kind.String(), Status: one.Status, Location: one.Location, Persistent: one.Persistent, } unit := unitTag.Id() unitColl, ok := output[unit] if !ok { unitColl = map[string]StorageInfo{} output[unit] = unitColl } unitColl[storageTag.Id()] = si } return output, nil }
func assertParseStorageTagInvalid(c *gc.C, tag string, expect error) { _, err := names.ParseStorageTag(tag) c.Assert(err, gc.ErrorMatches, expect.Error()) }
func assertParseStorageTag(c *gc.C, tag string, expect names.StorageTag) { t, err := names.ParseStorageTag(tag) c.Assert(err, gc.IsNil) c.Assert(t, gc.Equals, expect) }