pool

package
v3.0.0-rc41 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Sep 22, 2026 License: Apache-2.0 Imports: 24 Imported by: 0

Documentation

Index

Constants

This section is empty.

Variables

This section is empty.

Functions

func ClaimFits

func ClaimFits(ctx context.Context, namespace, poolName, path string, size int64) (bool, string, error)

func ClaimHeldByPath

func ClaimHeldByPath(ctx context.Context, c *client.T, namespace, poolName string) (map[string]int64, error)

ClaimHeldByPath is what each volume of a namespace claims of a pool, counting the size it was created or resized with.

It counts what was asked for, not what is written: a pool hands out what it promised, and that promise is what is being rationed.

It is answered by object rather than as a sum because a claim granted a moment ago and not yet written has to be weighed against it, and weighing the two means knowing which object each is about.

func ClaimLimit

func ClaimLimit(namespace, poolName string) (int64, bool, error)

ClaimLimit is the most a namespace may claim of a pool, and whether it is capped on it at all.

func ClaimProbe

func ClaimProbe(ctx context.Context, namespace, poolName, path string, size int64) (bool, string, error)

ClaimFits says whether a namespace may have an object hold size bytes of a pool, and why not when it may not.

The size is what the object is to hold, not the increase: what it holds today is already counted in what the namespace holds, and is not claimed twice.

A namespace with no claim on the pool is not capped on it, and is answered from the local configuration alone, which is what most allocations are.

A capped one is brokered. The claim is asked of the daemon, which hands the question to the node speaking for the cluster: what the namespace holds is read from the configurations the cluster shares, a write reaches that reading a moment after it is made, and two claims answered from the same reading both fit where together they do not. One node answering them, counting what it has granted since, is what makes them fit one at a time.

Failing to reach the daemon leaves the claim unchecked rather than refused: a cap is something the cluster brokers, and where there is no daemon to ask there is nothing brokering. Allocating with the daemon down is an administrator acting directly, which is uncapped by design, and stopping a daemon is not something the capped user can do. ClaimProbe is ClaimFits without taking the claim: it says whether the namespace may have the object hold the size, and counts nothing as taken.

A pool lookup weighs every pool it could serve the volume from and writes to one of them. Taking the claim of each would ration the namespace on the pools it was only compared against.

func ConfigureVolume

func ConfigureVolume(ctx context.Context, p Pooler, vol Volumer, namespace, path string, size int64, format bool, acs volaccess.T, shared bool, nodes []string, env []string) error

func CopySize

func CopySize(p Pooler, logical int64) int64

CopySize is what one copy of a volume costs the storage behind a pool.

It is the size the volume hands out, unless the driver says otherwise.

func DiskName

func DiskName(p Pooler, vol Volumer) string

func Driver

func Driver(t string) func() Pooler

func GetMappings

func GetMappings(ctx context.Context, p ArrayPooler, nodes []string, pathType string) (array.Mappings, error)

func GetPaths

func GetPaths(ctx context.Context, p ArrayPooler, nodes []string, pathType string) (san.Paths, error)

func HasAccess

func HasAccess(p Pooler, acs volaccess.T) bool

func HasCapability

func HasCapability(p Pooler, s Capability) bool

func InitData

func InitData()

InitData reset package instances data, it can be used for tests.

func LogicalSize

func LogicalSize(p Pooler, physical int64) int64

LogicalSize is what a pool holding this many bytes can hand out to volumes.

It is the same number, unless the driver says otherwise.

func MappingsFromPaths

func MappingsFromPaths(paths san.Paths) (array.Mappings, error)

func MountPointFromName

func MountPointFromName(name string) string

func UsageByNode

func UsageByNode(ctx context.Context) (map[string]map[string]Usage, error)

UsageByNode is what each node holds of the pools, as the daemon knows it.

The pool of a node is read from the node itself, and a node cannot read the pool of a peer, so this is asked of the daemon: it is where every node reports what its storage holds. Every pool is asked for rather than the one in question, because a node that reports no pool at all and a node that reports every pool but this one are not the same thing: the first has not reported yet, the second says the pool is not one of its.

Types

type ArrayPooler

type ArrayPooler interface {
	Pooler
	GetTargets(ctx context.Context) (san.Targets, error)
	CreateDisk(ctx context.Context, name string, size int64, nodenames []string) ([]Disk, error)
	DeleteDisk(ctx context.Context, name, wwid string) ([]Disk, error)
}

type BlkTranslater

type BlkTranslater interface {
	BlkTranslate(name string, size int64, shared bool) ([]string, error)
}

type By

type By func(p1, p2 *StatusItem) bool

func (By) Sort

func (by By) Sort(l StatusList)

type Capabilities

type Capabilities []Capability

func (Capabilities) String

func (t Capabilities) String() string

func (Capabilities) StringSlice

func (t Capabilities) StringSlice() []string

type Capability

type Capability string
const (
	CapBlk      Capability = "blk"
	CapFile     Capability = "file"
	CapMove     Capability = "move"
	CapROO      Capability = "roo"
	CapROX      Capability = "rox"
	CapRWO      Capability = "rwo"
	CapRWX      Capability = "rwx"
	CapShared   Capability = "shared"
	CapSnap     Capability = "snap"
	CapVolatile Capability = "volatile"
)

type Config

type Config interface {
	Eval(key.T) (any, error)
	GetInt(key.T) int
	GetString(key.T) string
	GetStringAs(key.T, string) string
	GetStringStrict(key.T) (string, error)
	GetStrings(key.T) []string
	GetBool(k key.T) bool
	GetSize(k key.T) *int64
	HasSectionString(s string) bool
}

type CopyCoster

type CopyCoster interface {
	// CopySize is what one copy of a volume of this size costs the
	// storage behind the pool.
	CopySize(logical int64) int64

	// LogicalSize is what the pool can hand out to volumes, holding this
	// many bytes of storage.
	LogicalSize(physical int64) int64
}

CopyCoster is implemented by a pool whose storage holds a copy of a volume in something other than the size the volume hands out: an array that compresses holds less of it, one keeping a fixed overhead per volume holds more.

Both directions are asked of the driver rather than derived from one another, because a relation is not always a ratio: an overhead per volume does not divide, and a driver that rounds does not invert.

The replication is not this: a pool whose storage is not shared holds a copy of a volume on every node the volume has an instance on, which is counted where the nodes are known.

type Data

type Data[T Dataer] struct {
	sync.RWMutex
	// contains filtered or unexported fields
}

Data defines a shared holder for all pool Dataer

var (

	// StatusData is the package data holder for all instances statuses
	StatusData *Data[Status]
)

func NewData

func NewData[T Dataer]() *Data[T]

func (*Data[T]) Get

func (c *Data[T]) Get(name, node string) *T

Get returns a pool data or nil if data is not found

func (*Data[T]) GetAll

func (c *Data[T]) GetAll() []DataElement[T]

GetAll returns all pool data as a list of DataElements

func (*Data[T]) GetByName

func (c *Data[T]) GetByName(name string) []DataElement[T]

GetByName returns pool instances on a specific node as a list of DataElements

func (*Data[T]) GetByNode

func (c *Data[T]) GetByNode(nodename string) []DataElement[T]

GetByNode returns pool instances on a specific node as a list of DataElements

func (*Data[T]) Set

func (c *Data[T]) Set(name, node string, v *T)

Set will add or update instance data

func (*Data[T]) Unset

func (c *Data[T]) Unset(name, node string)

Unset removes an instance data

type DataElement

type DataElement[T Dataer] struct {
	Name  string
	Node  string
	Value *T
}

type Dataer

type Dataer interface {
	Status
}

type Disk

type Disk struct {
	// ID is the created disk wwid
	ID string

	// Paths is the subset of requested san path actually setup for this disk
	Paths san.Paths

	// Driver is a driver-specific dataset
	Driver any
}

type FS

type FS struct {
	Pool               fsPooler
	Name               string
	Shared             bool
	FsIndex            int
	DiskIndex          int
	OnDisk             string
	DefaultMkfsOptions []string
	DefaultMntOptions  []string
}

func (*FS) Keywords

func (t *FS) Keywords() []string

type Grant

type Grant struct {
	// To is the size the object was let hold, which is what it will
	// claim of the pool once its configuration is written.
	To int64

	// ExpiresAt is when the grant stops being counted. A claim answered
	// yes is not always written: the write can fail after the answer, or
	// the caller can go away between the two, and a grant nothing ever
	// claims would ration the namespace for ever.
	ExpiresAt time.Time
}

Grant is a claim answered yes and not yet seen in the configurations the cluster shares.

type Grants

type Grants struct {
	// contains filtered or unexported fields
}

Grants is what a node has let namespaces take of the pools, waiting for the configurations saying so to come back around.

A claim counts the size each volume of the namespace is configured to hold, read from the configurations the cluster shares. A write reaches that reading a moment after it is made, so a claim answered from it alone misses everything granted since: three volumes grown one after the other each read the sizes of before the first grow. Counting the grants too closes that window, and holding them here is what makes the answer and the record one step rather than two.

func NewGrants

func NewGrants(ttl time.Duration) *Grants

NewGrants returns a table forgetting a grant nothing claimed after ttl.

func (*Grants) ExpiresAt

func (t *Grants) ExpiresAt(now time.Time) time.Time

ExpiresAt is when a grant made now stops being counted.

func (*Grants) Fits

func (t *Grants) Fits(namespace, poolName, path string, to, limit int64, held map[string]int64, now time.Time) (bool, string)

Fits says whether the namespace may have an object hold to bytes of a pool, and records the grant when it may.

held is the size each volume of the namespace is configured to hold of the pool, by object path, which is the reading the grants complete.

What is weighed is what the namespace would hold once the write lands: every other object counts for the larger of what its configuration says and what it was let take, and the object asked about counts for the size asked for. So asking twice for the same size is answered the same way twice, and a grant stops adding anything as soon as the configuration it was granted for says the same thing.

func (*Grants) Probe

func (t *Grants) Probe(namespace, poolName, path string, to, limit int64, held map[string]int64, now time.Time) (bool, string)

Probe says whether the namespace may have an object hold to bytes of a pool, and records nothing.

It is what a pool lookup asks of every pool it weighs: a claim is taken where a volume is written, and a pool the lookup only looked at is a pool nothing was written to. Recording there would ration the namespace on every pool it was compared against, for as long as a grant lasts.

func (*Grants) Seed

func (t *Grants) Seed(namespace, poolName, path string, to int64, now time.Time)

Seed records a grant this node did not answer.

It is how a table lost is rebuilt. The grants of a node are in memory, so a node that has just started, or has just become the one answering claims, has none of them and answers from the configurations the cluster shares alone, which is the reading the grants exist to complete.

What was granted and written is not lost, though: it is on the node that wrote it, whose own reading of it has no lag. What was granted and never written is lost, and expiring is what would have become of it anyway.

A grant already held for the object is left alone unless the seed is larger, so rebuilding cannot lower what a namespace is counted as holding.

type Lookup

type Lookup struct {
	Name      string
	Type      string
	Access    volaccess.T
	Size      int64
	Namespace string

	// Path is the volume the storage is looked up for, which the claim
	// on the pool is granted to. It is empty where the lookup is not
	// about a volume of its own, and the grant is then held until it
	// expires rather than until the volume says it was claimed.
	Path     string
	Format   bool
	Shared   bool
	Usage    bool
	Volatile bool
	Nodes    []string
	// contains filtered or unexported fields
}

func NewLookup

func NewLookup(m manager) *Lookup

func (Lookup) ConfigureVolume

func (t Lookup) ConfigureVolume(ctx context.Context, volume Volumer, obj interface{}) error

func (Lookup) Do

func (t Lookup) Do(ctx context.Context) (Pooler, error)

func (Lookup) Env

func (t Lookup) Env(p Pooler, c consumer, optional bool) ([]string, error)

type Pooler

type Pooler interface {
	SetName(string)
	SetDriver(string)
	Name() string
	Type() string
	Head() string
	Mappings() map[string]string
	Capabilities() Capabilities
	Usage(context.Context) (Usage, error)
	SetConfig(Config)
	Config() Config
	Separator() string
}

func New

func New(name string, config Config) Pooler

type Status

type Status struct {
	Usage
	Capabilities Capabilities `json:"capabilities"`
	Errors       []string     `json:"errors"`
	Head         string       `json:"head"`
	Type         string       `json:"type"`
	UpdatedAt    time.Time    `json:"updated_at"`
	VolumeCount  int          `json:"volume_count"`
}

func GetStatus

func GetStatus(ctx context.Context, t Pooler, withUsage bool) Status

func NewStatus

func NewStatus() Status

func (*Status) DeepCopy

func (t *Status) DeepCopy() *Status

func (*Status) HasAccess

func (t *Status) HasAccess(acs volaccess.T) bool

func (*Status) HasCapability

func (t *Status) HasCapability(s Capability) bool

type StatusItem

type StatusItem struct {
	Status
	Name string `json:"name"`
}

type StatusList

type StatusList []StatusItem

func NewStatusList

func NewStatusList() StatusList

func (StatusList) Add

func (t StatusList) Add(ctx context.Context, p Pooler, withUsage bool) StatusList

func (StatusList) Len

func (t StatusList) Len() int

func (StatusList) Less

func (t StatusList) Less(i, j int) bool

func (StatusList) Swap

func (t StatusList) Swap(i, j int)

type T

type T struct {
	// contains filtered or unexported fields
}

func (*T) Config

func (t *T) Config() Config

func (*T) FSType

func (t *T) FSType() string

func (*T) GetBool

func (t *T) GetBool(s string) bool

func (*T) GetInt

func (t *T) GetInt(s string) int

func (*T) GetSize

func (t *T) GetSize(s string) *int64

func (*T) GetString

func (t *T) GetString(s string) string

func (*T) GetStringAs

func (t *T) GetStringAs(s, nodename string) string

func (*T) GetStrings

func (t *T) GetStrings(s string) []string

func (*T) Mappings

func (t *T) Mappings() map[string]string

func (*T) MkblkOptions

func (t *T) MkblkOptions() string

func (*T) MkfsOptions

func (t *T) MkfsOptions() string

func (*T) MntOptions

func (t *T) MntOptions() string

func (T) Name

func (t T) Name() string

func (T) Separator

func (t T) Separator() string

Separator is the string to use as the separator between name and hostname in the array-side disk name. Some array have a restricted characterset for such names, so better let the pool driver decide.

func (*T) SetConfig

func (t *T) SetConfig(c Config)

func (*T) SetDriver

func (t *T) SetDriver(driver string)

func (*T) SetName

func (t *T) SetName(name string)

func (T) Type

func (t T) Type() string

type Translater

type Translater interface {
	Translate(name string, size int64, shared bool) ([]string, error)
}

type Usage

type Usage struct {
	Shared bool `json:"shared"`
	// Free unit is Bytes
	Free int64 `json:"free"`
	// Used unit is Bytes
	Used int64 `json:"used"`
	// Size unit is Bytes
	Size int64 `json:"size"`
	// LogicalFree unit is Bytes
	LogicalFree int64 `json:"logical_free"`
	// LogicalUsed unit is Bytes
	LogicalUsed int64 `json:"logical_used"`
	// LogicalSize unit is Bytes
	LogicalSize int64 `json:"logical_size"`
}

Usage is what a pool holds, in the two currencies a pool is counted in.

The physical figures are what the storage behind the pool really holds. The logical ones are what the pool can hand out as volume sizes, which is the currency a volume is asked for in, a claim is written in, and a limit rations. The two differ wherever a copy of a volume costs the storage something other than the size it hands out.

type VolumeStatus

type VolumeStatus struct {
	Pool     string       `json:"pool"`
	Path     naming.Path  `json:"path"`
	Children naming.Paths `json:"children"`
	IsOrphan bool         `json:"is_orphan"`
	Size     int64        `json:"size"`
}

type VolumeStatusList

type VolumeStatusList []VolumeStatus

func (VolumeStatusList) Len

func (t VolumeStatusList) Len() int

func (VolumeStatusList) Less

func (t VolumeStatusList) Less(i, j int) bool

func (VolumeStatusList) Swap

func (t VolumeStatusList) Swap(i, j int)

type Volumer

type Volumer interface {
	FQDN() string
	Config() *xconfig.T
}

type WeightedPools

type WeightedPools []Pooler

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL