2015-07-28 23:03:15 +00:00
|
|
|
package scheduler
|
|
|
|
|
|
|
|
import (
|
|
|
|
"fmt"
|
2015-08-07 00:25:14 +00:00
|
|
|
"log"
|
2015-07-28 23:03:15 +00:00
|
|
|
|
2015-07-28 23:15:32 +00:00
|
|
|
"github.com/hashicorp/go-memdb"
|
2015-07-28 23:03:15 +00:00
|
|
|
"github.com/hashicorp/nomad/nomad/structs"
|
|
|
|
)
|
|
|
|
|
2016-10-26 21:52:48 +00:00
|
|
|
const (
|
|
|
|
// SchedulerVersion is the version of the scheduler. Changes to the
|
|
|
|
// scheduler that are incompatible with prior schedulers will increment this
|
|
|
|
// version. It is used to disallow dequeueing when the versions do not match
|
|
|
|
// across the leader and the dequeueing scheduler.
|
|
|
|
SchedulerVersion uint16 = 1
|
|
|
|
)
|
|
|
|
|
2015-07-28 23:03:15 +00:00
|
|
|
// BuiltinSchedulers contains the built in registered schedulers
|
|
|
|
// which are available
|
2015-08-07 00:25:14 +00:00
|
|
|
var BuiltinSchedulers = map[string]Factory{
|
2015-10-23 23:32:45 +00:00
|
|
|
"service": NewServiceScheduler,
|
|
|
|
"batch": NewBatchScheduler,
|
|
|
|
"system": NewSystemScheduler,
|
2015-08-07 00:25:14 +00:00
|
|
|
}
|
2015-07-28 23:03:15 +00:00
|
|
|
|
|
|
|
// NewScheduler is used to instantiate and return a new scheduler
|
|
|
|
// given the scheduler name, initial state, and planner.
|
2015-08-07 00:25:14 +00:00
|
|
|
func NewScheduler(name string, logger *log.Logger, state State, planner Planner) (Scheduler, error) {
|
2015-07-28 23:03:15 +00:00
|
|
|
// Lookup the factory function
|
|
|
|
factory, ok := BuiltinSchedulers[name]
|
|
|
|
if !ok {
|
|
|
|
return nil, fmt.Errorf("unknown scheduler '%s'", name)
|
|
|
|
}
|
|
|
|
|
|
|
|
// Instantiate the scheduler
|
2015-08-07 00:25:14 +00:00
|
|
|
sched := factory(logger, state, planner)
|
2015-07-28 23:03:15 +00:00
|
|
|
return sched, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
// Factory is used to instantiate a new Scheduler
|
2015-08-07 00:25:14 +00:00
|
|
|
type Factory func(*log.Logger, State, Planner) Scheduler
|
2015-07-28 23:03:15 +00:00
|
|
|
|
|
|
|
// Scheduler is the top level instance for a scheduler. A scheduler is
|
|
|
|
// meant to only encapsulate business logic, pushing the various plumbing
|
|
|
|
// into Nomad itself. They are invoked to process a single evaluation at
|
|
|
|
// a time. The evaluation may result in task allocations which are computed
|
|
|
|
// optimistically, as there are many concurrent evaluations being processed.
|
|
|
|
// The task allocations are submitted as a plan, and the current leader will
|
|
|
|
// coordinate the commmits to prevent oversubscription or improper allocations
|
|
|
|
// based on stale state.
|
|
|
|
type Scheduler interface {
|
|
|
|
// Process is used to handle a new evaluation. The scheduler is free to
|
|
|
|
// apply any logic necessary to make the task placements. The state and
|
|
|
|
// planner will be provided prior to any invocations of process.
|
|
|
|
Process(*structs.Evaluation) error
|
|
|
|
}
|
|
|
|
|
|
|
|
// State is an immutable view of the global state. This allows schedulers
|
|
|
|
// to make intelligent decisions based on allocations of other schedulers
|
|
|
|
// and to enforce complex constraints that require more information than
|
|
|
|
// is available to a local state scheduler.
|
|
|
|
type State interface {
|
|
|
|
// Nodes returns an iterator over all the nodes.
|
|
|
|
// The type of each result is *structs.Node
|
2015-07-28 23:15:32 +00:00
|
|
|
Nodes() (memdb.ResultIterator, error)
|
2015-08-07 00:46:14 +00:00
|
|
|
|
|
|
|
// AllocsByJob returns the allocations by JobID
|
|
|
|
AllocsByJob(jobID string) ([]*structs.Allocation, error)
|
2015-08-11 21:04:04 +00:00
|
|
|
|
2015-08-13 18:54:59 +00:00
|
|
|
// AllocsByNode returns all the allocations by node
|
|
|
|
AllocsByNode(node string) ([]*structs.Allocation, error)
|
|
|
|
|
2016-02-20 19:29:15 +00:00
|
|
|
// AllocsByNodeTerminal returns all the allocations by node filtering by terminal status
|
|
|
|
AllocsByNodeTerminal(node string, terminal bool) ([]*structs.Allocation, error)
|
|
|
|
|
2015-08-11 21:04:04 +00:00
|
|
|
// GetNodeByID is used to lookup a node by ID
|
2015-09-07 03:56:38 +00:00
|
|
|
NodeByID(nodeID string) (*structs.Node, error)
|
2015-08-11 23:41:48 +00:00
|
|
|
|
|
|
|
// GetJobByID is used to lookup a job by ID
|
2015-09-07 03:56:38 +00:00
|
|
|
JobByID(id string) (*structs.Job, error)
|
2015-07-28 23:03:15 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// Planner interface is used to submit a task allocation plan.
|
|
|
|
type Planner interface {
|
|
|
|
// SubmitPlan is used to submit a plan for consideration.
|
|
|
|
// This will return a PlanResult or an error. It is possible
|
|
|
|
// that this will result in a state refresh as well.
|
|
|
|
SubmitPlan(*structs.Plan) (*structs.PlanResult, State, error)
|
2015-08-15 21:25:00 +00:00
|
|
|
|
|
|
|
// UpdateEval is used to update an evaluation. This should update
|
|
|
|
// a copy of the input evaluation since that should be immutable.
|
|
|
|
UpdateEval(*structs.Evaluation) error
|
2015-09-07 21:26:29 +00:00
|
|
|
|
|
|
|
// CreateEval is used to create an evaluation. This should set the
|
|
|
|
// PreviousEval to that of the current evaluation.
|
|
|
|
CreateEval(*structs.Evaluation) error
|
2016-05-20 23:03:53 +00:00
|
|
|
|
|
|
|
// ReblockEval takes a blocked evaluation and re-inserts it into the blocked
|
|
|
|
// evaluation tracker. This update occurs only in-memory on the leader. The
|
|
|
|
// evaluation must exist in a blocked state prior to this being called such
|
|
|
|
// that on leader changes, the evaluation will be reblocked properly.
|
|
|
|
ReblockEval(*structs.Evaluation) error
|
2015-07-28 23:03:15 +00:00
|
|
|
}
|