187 lines
4.6 KiB
Go
187 lines
4.6 KiB
Go
package utils
|
|
|
|
import (
|
|
"encoding/json"
|
|
"errors"
|
|
"sync"
|
|
"time"
|
|
|
|
oclib "cloud.o-forge.io/core/oc-lib"
|
|
"cloud.o-forge.io/core/oc-lib/models/resources"
|
|
"cloud.o-forge.io/core/oc-lib/models/workflow"
|
|
"cloud.o-forge.io/core/oc-lib/tools"
|
|
)
|
|
|
|
type BookingResource struct {
|
|
ID string // resource MongoDB _id
|
|
PeerPID string // peer public PeerID (PID) — PlannerCache key
|
|
InstanceID string // resolved from WorkflowSchedule.SelectedInstances
|
|
}
|
|
|
|
// collectBookingResources returns unique storage and compute resources from the
|
|
// workflow graph. For each resource the selected instance ID is resolved from
|
|
// selectedInstances (the scheduler's SelectedInstances ConfigItem) so the planner
|
|
// check targets the exact instance chosen by the user.
|
|
func CollectBookingResources(wf *workflow.Workflow, selectedInstances workflow.ConfigItem) map[string]BookingResource {
|
|
if wf.Graph == nil {
|
|
return nil
|
|
}
|
|
seen := map[string]bool{}
|
|
result := map[string]BookingResource{}
|
|
|
|
// Resolve MongoDB peer _id (DID) → public PeerID (PID) used as PlannerCache key.
|
|
peerAccess := oclib.NewRequestAdmin(oclib.LibDataEnum(oclib.PEER), nil)
|
|
didToPID := map[string]string{}
|
|
resolvePID := func(did string) string {
|
|
if pid, ok := didToPID[did]; ok {
|
|
return pid
|
|
}
|
|
if data := peerAccess.LoadOne(did); data.Data != nil {
|
|
if p := data.ToPeer(); p != nil {
|
|
didToPID[did] = p.PeerID
|
|
return p.PeerID
|
|
}
|
|
}
|
|
return ""
|
|
}
|
|
|
|
resolveInstanceID := func(res interface {
|
|
GetID() string
|
|
GetCreatorID() string
|
|
}) string {
|
|
idx := selectedInstances.Get(res.GetID())
|
|
switch r := res.(type) {
|
|
case *resources.StorageResource:
|
|
if inst := r.GetSelectedInstance(idx); inst != nil {
|
|
return inst.GetID()
|
|
}
|
|
case *resources.ComputeResource:
|
|
if inst := r.GetSelectedInstance(idx); inst != nil {
|
|
return inst.GetID()
|
|
}
|
|
}
|
|
return ""
|
|
}
|
|
|
|
for _, item := range wf.GetGraphItems(wf.Graph.IsStorage) {
|
|
_, res := item.GetResource()
|
|
if res == nil {
|
|
continue
|
|
}
|
|
id := res.GetID()
|
|
if seen[id] {
|
|
continue
|
|
}
|
|
pid := resolvePID(res.GetCreatorID())
|
|
if pid == "" {
|
|
continue
|
|
}
|
|
seen[id] = true
|
|
result[pid] = BookingResource{
|
|
ID: id,
|
|
PeerPID: pid,
|
|
InstanceID: resolveInstanceID(res),
|
|
}
|
|
}
|
|
|
|
for _, item := range wf.GetGraphItems(wf.Graph.IsCompute) {
|
|
_, res := item.GetResource()
|
|
if res == nil {
|
|
continue
|
|
}
|
|
id := res.GetID()
|
|
if seen[id] {
|
|
continue
|
|
}
|
|
pid := resolvePID(res.GetCreatorID())
|
|
if pid == "" {
|
|
continue
|
|
}
|
|
seen[id] = true
|
|
result[pid] = BookingResource{
|
|
ID: id,
|
|
PeerPID: pid,
|
|
InstanceID: resolveInstanceID(res),
|
|
}
|
|
}
|
|
|
|
return result
|
|
}
|
|
|
|
// GetWorkflowPeerIDs loads the workflow and returns the deduplicated list of
|
|
// creator peer IDs for all its storage and compute resources.
|
|
// These are the peers whose planners must be watched by a check stream.
|
|
func GetWorkflowPeerIDs(wfID string, request *tools.APIRequest) ([]string, error) {
|
|
obj, code, err := workflow.NewAccessor(request).LoadOne(wfID)
|
|
if code != 200 || err != nil {
|
|
msg := "could not load workflow " + wfID
|
|
if err != nil {
|
|
msg += ": " + err.Error()
|
|
}
|
|
return nil, errors.New(msg)
|
|
}
|
|
wf := obj.(*workflow.Workflow)
|
|
if wf.Graph == nil {
|
|
return nil, nil
|
|
}
|
|
seen := map[string]bool{}
|
|
var peerIDs []string
|
|
for _, item := range wf.GetGraphItems(wf.Graph.IsStorage) {
|
|
_, res := item.GetResource()
|
|
if res == nil {
|
|
continue
|
|
}
|
|
if id := res.GetCreatorID(); id != "" && !seen[id] {
|
|
seen[id] = true
|
|
peerIDs = append(peerIDs, id)
|
|
}
|
|
}
|
|
for _, item := range wf.GetGraphItems(wf.Graph.IsCompute) {
|
|
_, res := item.GetResource()
|
|
if res == nil {
|
|
continue
|
|
}
|
|
if id := res.GetCreatorID(); id != "" && !seen[id] {
|
|
seen[id] = true
|
|
peerIDs = append(peerIDs, id)
|
|
}
|
|
}
|
|
realPeersID := []string{}
|
|
access := oclib.NewRequestAdmin(oclib.LibDataEnum(tools.PEER), nil)
|
|
for _, id := range peerIDs {
|
|
if data := access.LoadOne(id); data.Data != nil {
|
|
realPeersID = append(realPeersID, data.ToPeer().PeerID)
|
|
}
|
|
}
|
|
return realPeersID, nil
|
|
}
|
|
|
|
func FormatOptTime(t *time.Time) string {
|
|
if t == nil {
|
|
return "open"
|
|
}
|
|
return t.Format(time.RFC3339)
|
|
}
|
|
|
|
func Notify[T interface{}](mu *sync.RWMutex, registry map[string][]chan T, key string, toAdd T) {
|
|
mu.RLock()
|
|
subs := registry[key]
|
|
mu.RUnlock()
|
|
for _, ch := range subs {
|
|
select {
|
|
case ch <- toAdd:
|
|
default:
|
|
}
|
|
}
|
|
}
|
|
|
|
func Propalgate(peerID string, message tools.PropalgationMessage) {
|
|
b, _ := json.Marshal(message)
|
|
tools.NewNATSCaller().SetNATSPub(tools.PROPALGATION_EVENT, tools.NATSResponse{
|
|
FromApp: "oc-scheduler",
|
|
Datatype: -1,
|
|
Method: int(tools.PROPALGATION_EVENT),
|
|
Payload: b,
|
|
})
|
|
}
|