// Usage knows how to measure usage associated with item. func (p *pvcEvaluator) Usage(item runtime.Object) (api.ResourceList, error) { result := api.ResourceList{} pvc, err := toInternalPersistentVolumeClaimOrError(item) if err != nil { return result, err } storageClassRef := util.GetClaimStorageClass(pvc) // charge for claim result[api.ResourcePersistentVolumeClaims] = resource.MustParse("1") if len(storageClassRef) > 0 { storageClassClaim := api.ResourceName(storageClassRef + storageClassSuffix + string(api.ResourcePersistentVolumeClaims)) result[storageClassClaim] = resource.MustParse("1") } // charge for storage if request, found := pvc.Spec.Resources.Requests[api.ResourceStorage]; found { result[api.ResourceRequestsStorage] = request // charge usage to the storage class (if present) if len(storageClassRef) > 0 { storageClassStorage := api.ResourceName(storageClassRef + storageClassSuffix + string(api.ResourceRequestsStorage)) result[storageClassStorage] = request } } return result, nil }
func (r *glusterfsVolumeProvisioner) Provision() (*api.PersistentVolume, error) { var err error if r.options.PVC.Spec.Selector != nil { glog.V(4).Infof("glusterfs: not able to parse your claim Selector") return nil, fmt.Errorf("glusterfs: not able to parse your claim Selector") } glog.V(4).Infof("glusterfs: Provison VolumeOptions %v", r.options) scName := storageutil.GetClaimStorageClass(r.options.PVC) cfg, err := parseClassParameters(r.options.Parameters, r.plugin.host.GetKubeClient()) if err != nil { return nil, err } r.provisioningConfig = *cfg glog.V(4).Infof("glusterfs: creating volume with configuration %+v", r.provisioningConfig) gidTable, err := r.plugin.getGidTable(scName, cfg.gidMin, cfg.gidMax) if err != nil { return nil, fmt.Errorf("glusterfs: failed to get gidTable: %v", err) } gid, _, err := gidTable.AllocateNext() if err != nil { glog.Errorf("glusterfs: failed to reserve gid from table: %v", err) return nil, fmt.Errorf("glusterfs: failed to reserve gid from table: %v", err) } glog.V(2).Infof("glusterfs: got gid [%d] for PVC %s", gid, r.options.PVC.Name) glusterfs, sizeGB, err := r.CreateVolume(gid) if err != nil { if release_err := gidTable.Release(gid); release_err != nil { glog.Errorf("glusterfs: error when releasing gid in storageclass: %s", scName) } glog.Errorf("glusterfs: create volume err: %v.", err) return nil, fmt.Errorf("glusterfs: create volume err: %v.", err) } pv := new(api.PersistentVolume) pv.Spec.PersistentVolumeSource.Glusterfs = glusterfs pv.Spec.PersistentVolumeReclaimPolicy = r.options.PersistentVolumeReclaimPolicy pv.Spec.AccessModes = r.options.PVC.Spec.AccessModes if len(pv.Spec.AccessModes) == 0 { pv.Spec.AccessModes = r.plugin.GetAccessModes() } gidStr := strconv.FormatInt(int64(gid), 10) pv.Annotations = map[string]string{volumehelper.VolumeGidAnnotationKey: gidStr} pv.Spec.Capacity = api.ResourceList{ api.ResourceName(api.ResourceStorage): resource.MustParse(fmt.Sprintf("%dGi", sizeGB)), } return pv, nil }
// Constraints verifies that all required resources are present on the item. func (p *pvcEvaluator) Constraints(required []api.ResourceName, item runtime.Object) error { pvc, ok := item.(*api.PersistentVolumeClaim) if !ok { return fmt.Errorf("unexpected input object %v", item) } // these are the items that we will be handling based on the objects actual storage-class pvcRequiredSet := append([]api.ResourceName{}, pvcResources...) if storageClassRef := util.GetClaimStorageClass(pvc); len(storageClassRef) > 0 { pvcRequiredSet = append(pvcRequiredSet, ResourceByStorageClass(storageClassRef, api.ResourcePersistentVolumeClaims)) pvcRequiredSet = append(pvcRequiredSet, ResourceByStorageClass(storageClassRef, api.ResourceRequestsStorage)) } // in effect, this will remove things from the required set that are not tied to this pvcs storage class // for example, if a quota has bronze and gold storage class items defined, we should not error a bronze pvc for not being gold. // but we should error a bronze pvc if it doesn't make a storage request size... requiredResources := quota.Intersection(required, pvcRequiredSet) requiredSet := quota.ToSet(requiredResources) // usage for this pvc will only include global pvc items + this storage class specific items pvcUsage, err := p.Usage(item) if err != nil { return err } // determine what required resources were not tracked by usage. missingSet := sets.NewString() pvcSet := quota.ToSet(quota.ResourceNames(pvcUsage)) if diff := requiredSet.Difference(pvcSet); len(diff) > 0 { missingSet.Insert(diff.List()...) } if len(missingSet) == 0 { return nil } return fmt.Errorf("must specify %s", strings.Join(missingSet.List(), ",")) }
// find returns the nearest PV from the ordered list or nil if a match is not found func (pvIndex *persistentVolumeOrderedIndex) findByClaim(claim *api.PersistentVolumeClaim, matchPredicate matchPredicate) (*api.PersistentVolume, error) { // PVs are indexed by their access modes to allow easier searching. Each // index is the string representation of a set of access modes. There is a // finite number of possible sets and PVs will only be indexed in one of // them (whichever index matches the PV's modes). // // A request for resources will always specify its desired access modes. // Any matching PV must have at least that number of access modes, but it // can have more. For example, a user asks for ReadWriteOnce but a GCEPD // is available, which is ReadWriteOnce+ReadOnlyMany. // // Searches are performed against a set of access modes, so we can attempt // not only the exact matching modes but also potential matches (the GCEPD // example above). allPossibleModes := pvIndex.allPossibleMatchingAccessModes(claim.Spec.AccessModes) var smallestVolume *api.PersistentVolume var smallestVolumeSize int64 requestedQty := claim.Spec.Resources.Requests[api.ResourceName(api.ResourceStorage)] requestedSize := requestedQty.Value() requestedClass := storageutil.GetClaimStorageClass(claim) var selector labels.Selector if claim.Spec.Selector != nil { internalSelector, err := unversioned.LabelSelectorAsSelector(claim.Spec.Selector) if err != nil { // should be unreachable code due to validation return nil, fmt.Errorf("error creating internal label selector for claim: %v: %v", claimToClaimKey(claim), err) } selector = internalSelector } for _, modes := range allPossibleModes { volumes, err := pvIndex.listByAccessModes(modes) if err != nil { return nil, err } // Go through all available volumes with two goals: // - find a volume that is either pre-bound by user or dynamically // provisioned for this claim. Because of this we need to loop through // all volumes. // - find the smallest matching one if there is no volume pre-bound to // the claim. for _, volume := range volumes { if isVolumeBoundToClaim(volume, claim) { // this claim and volume are pre-bound; return // the volume if the size request is satisfied, // otherwise continue searching for a match volumeQty := volume.Spec.Capacity[api.ResourceStorage] volumeSize := volumeQty.Value() if volumeSize < requestedSize { continue } return volume, nil } // In Alpha dynamic provisioning, we do now want not match claims // with existing PVs, findByClaim must find only PVs that are // pre-bound to the claim (by dynamic provisioning). TODO: remove in // 1.5 if api.HasAnnotation(claim.ObjectMeta, storageutil.AlphaStorageClassAnnotation) { continue } // filter out: // - volumes bound to another claim // - volumes whose labels don't match the claim's selector, if specified // - volumes in Class that is not requested if volume.Spec.ClaimRef != nil { continue } else if selector != nil && !selector.Matches(labels.Set(volume.Labels)) { continue } if storageutil.GetVolumeStorageClass(volume) != requestedClass { continue } volumeQty := volume.Spec.Capacity[api.ResourceStorage] volumeSize := volumeQty.Value() if volumeSize >= requestedSize { if smallestVolume == nil || smallestVolumeSize > volumeSize { smallestVolume = volume smallestVolumeSize = volumeSize } } } if smallestVolume != nil { // Found a matching volume return smallestVolume, nil } } return nil, nil }