This commit is contained in:
mr 2025-06-19 08:11:11 +02:00
parent 29bc21735d
commit 938f9f1326
17 changed files with 104 additions and 72 deletions

View File

@ -9,6 +9,7 @@ import (
type BuyingStrategy int type BuyingStrategy int
// should except... on
const ( const (
UNLIMITED BuyingStrategy = iota UNLIMITED BuyingStrategy = iota
SUBSCRIPTION SUBSCRIPTION
@ -40,6 +41,10 @@ const (
PER_MONTH PER_MONTH
) )
func IsTimeStrategy(i int) bool {
return len(TimePricingStrategyList()) < i
}
func (t TimePricingStrategy) String() string { func (t TimePricingStrategy) String() string {
return [...]string{"ONCE", "PER SECOND", "PER MINUTE", "PER HOUR", "PER DAY", "PER WEEK", "PER MONTH"}[t] return [...]string{"ONCE", "PER SECOND", "PER MINUTE", "PER HOUR", "PER DAY", "PER WEEK", "PER MONTH"}[t]
} }
@ -101,6 +106,7 @@ func BookingEstimation(t TimePricingStrategy, price float64, locationDurationInS
return 0, errors.New("pricing strategy not found") return 0, errors.New("pricing strategy not found")
} }
// may suppress in pricing strategy -> to set in map
type PricingStrategy[T Strategy] struct { type PricingStrategy[T Strategy] struct {
Price float64 `json:"price" bson:"price" default:"0"` // Price is the Price of the pricing Price float64 `json:"price" bson:"price" default:"0"` // Price is the Price of the pricing
Currency string `json:"currency" bson:"currency" default:"USD"` // Currency is the currency of the pricing Currency string `json:"currency" bson:"currency" default:"USD"` // Currency is the currency of the pricing

View File

@ -106,10 +106,14 @@ func (o *Order) draftStoreFromModel(scheduler *workflow_execution.WorkflowSchedu
// set the name of the order // set the name of the order
resourcesByPeer := map[string][]pricing.PricedItemITF{} // create a map of resources by peer resourcesByPeer := map[string][]pricing.PricedItemITF{} // create a map of resources by peer
processings := scheduler.Workflow.GetPricedItem(scheduler.Workflow.Graph.IsProcessing, request) // get the processing items processings := scheduler.Workflow.GetPricedItem(scheduler.Workflow.Graph.IsProcessing, request,
datas := scheduler.Workflow.GetPricedItem(scheduler.Workflow.Graph.IsData, request) // get the data items int(scheduler.SelectedBuyingStrategy), scheduler.SelectedPricingStrategy) // get the processing items
storages := scheduler.Workflow.GetPricedItem(scheduler.Workflow.Graph.IsStorage, request) // get the storage items datas := scheduler.Workflow.GetPricedItem(scheduler.Workflow.Graph.IsData, request,
workflows := scheduler.Workflow.GetPricedItem(scheduler.Workflow.Graph.IsWorkflow, request) // get the workflow items int(scheduler.SelectedBuyingStrategy), scheduler.SelectedPricingStrategy) // get the data items
storages := scheduler.Workflow.GetPricedItem(scheduler.Workflow.Graph.IsStorage, request,
int(scheduler.SelectedBuyingStrategy), scheduler.SelectedPricingStrategy) // get the storage items
workflows := scheduler.Workflow.GetPricedItem(scheduler.Workflow.Graph.IsWorkflow, request,
int(scheduler.SelectedBuyingStrategy), scheduler.SelectedPricingStrategy) // get the workflow items
for _, items := range []map[string]pricing.PricedItemITF{processings, datas, storages, workflows} { for _, items := range []map[string]pricing.PricedItemITF{processings, datas, storages, workflows} {
for _, item := range items { for _, item := range items {
if _, ok := resourcesByPeer[item.GetCreatorID()]; !ok { if _, ok := resourcesByPeer[item.GetCreatorID()]; !ok {
@ -167,7 +171,8 @@ func (o *Order) draftBookOrder(scheduler *workflow_execution.WorkflowSchedule, r
return draftedBookings, errors.New("no request found") return draftedBookings, errors.New("no request found")
} }
for _, exec := range scheduler.WorkflowExecution { for _, exec := range scheduler.WorkflowExecution {
_, priceds, _, err := scheduler.Workflow.Planify(exec.ExecDate, exec.EndDate, request) _, priceds, _, err := scheduler.Workflow.Planify(exec.ExecDate, exec.EndDate, request,
int(scheduler.SelectedBuyingStrategy), scheduler.SelectedPricingStrategy)
if err != nil { if err != nil {
return draftedBookings, errors.New("could not planify the workflow" + fmt.Sprintf("%v", err)) return draftedBookings, errors.New("could not planify the workflow" + fmt.Sprintf("%v", err))
} }

View File

@ -31,11 +31,11 @@ func (r *ComputeResource) GetType() string {
} }
func (abs *ComputeResource) ConvertToPricedResource( func (abs *ComputeResource) ConvertToPricedResource(
t tools.DataType, request *tools.APIRequest) pricing.PricedItemITF { t tools.DataType, request *tools.APIRequest, buyingStrategy int, pricingStrategy int) pricing.PricedItemITF {
if t != tools.COMPUTE_RESOURCE { if t != tools.COMPUTE_RESOURCE {
return nil return nil
} }
p := abs.AbstractInstanciatedResource.ConvertToPricedResource(t, request) p := abs.AbstractInstanciatedResource.ConvertToPricedResource(t, request, buyingStrategy, pricingStrategy)
priced := p.(*PricedResource) priced := p.(*PricedResource)
return &PricedComputeResource{ return &PricedComputeResource{
PricedResource: *priced, PricedResource: *priced,

View File

@ -38,11 +38,11 @@ func (r *DataResource) GetType() string {
} }
func (abs *DataResource) ConvertToPricedResource( func (abs *DataResource) ConvertToPricedResource(
t tools.DataType, request *tools.APIRequest) pricing.PricedItemITF { t tools.DataType, request *tools.APIRequest, buyingStrategy int, pricingStrategy int) pricing.PricedItemITF {
if t != tools.DATA_RESOURCE { if t != tools.DATA_RESOURCE {
return nil return nil
} }
p := abs.AbstractInstanciatedResource.ConvertToPricedResource(t, request) p := abs.AbstractInstanciatedResource.ConvertToPricedResource(t, request, buyingStrategy, pricingStrategy)
priced := p.(*PricedResource) priced := p.(*PricedResource)
return &PricedDataResource{ return &PricedDataResource{
PricedResource: *priced, PricedResource: *priced,
@ -82,7 +82,7 @@ type DataResourcePartnership struct {
type DataResourcePricingStrategy int type DataResourcePricingStrategy int
const ( const (
PER_DOWNLOAD DataResourcePricingStrategy = iota PER_DOWNLOAD DataResourcePricingStrategy = iota + 6
PER_TB_DOWNLOADED PER_TB_DOWNLOADED
PER_GB_DOWNLOADED PER_GB_DOWNLOADED
PER_MB_DOWNLOADED PER_MB_DOWNLOADED

View File

@ -9,7 +9,7 @@ import (
type ResourceInterface interface { type ResourceInterface interface {
utils.DBObject utils.DBObject
Trim() Trim()
ConvertToPricedResource(t tools.DataType, request *tools.APIRequest) pricing.PricedItemITF ConvertToPricedResource(t tools.DataType, request *tools.APIRequest, buyingStrategy int, pricingStrategy int) pricing.PricedItemITF
GetType() string GetType() string
GetSelectedInstance() utils.DBObject GetSelectedInstance() utils.DBObject
ClearEnv() utils.DBObject ClearEnv() utils.DBObject
@ -22,7 +22,7 @@ type ResourceInstanceITF interface {
GetName() string GetName() string
StoreDraftDefault() StoreDraftDefault()
ClearEnv() ClearEnv()
GetPricingsProfiles(peerID string, groups []string) []pricing.PricingProfileITF GetPricingsProfiles(peerID string, groups []string, buyingStrategy int, pricingStrategy int) []pricing.PricingProfileITF
GetPeerGroups() ([]ResourcePartnerITF, []map[string][]string) GetPeerGroups() ([]ResourcePartnerITF, []map[string][]string)
ClearPeerGroups() ClearPeerGroups()
GetSelectedPartnership() ResourcePartnerITF GetSelectedPartnership() ResourcePartnerITF
@ -30,7 +30,7 @@ type ResourceInstanceITF interface {
} }
type ResourcePartnerITF interface { type ResourcePartnerITF interface {
GetPricingsProfiles(peerID string, groups []string) []pricing.PricingProfileITF GetPricingsProfiles(peerID string, groups []string, buyingStrategy int, pricingStrategy int) []pricing.PricingProfileITF
GetPeerGroups() map[string][]string GetPeerGroups() map[string][]string
ClearPeerGroups() ClearPeerGroups()
} }

View File

@ -52,13 +52,12 @@ type AbstractInstanciatedResource[T ResourceInstanceITF] struct {
Instances []T `json:"instances,omitempty" bson:"instances,omitempty"` // Bill is the bill of the resource // Bill is the bill of the resource Instances []T `json:"instances,omitempty" bson:"instances,omitempty"` // Bill is the bill of the resource // Bill is the bill of the resource
} }
func (abs *AbstractInstanciatedResource[T]) ConvertToPricedResource( func (abs *AbstractInstanciatedResource[T]) ConvertToPricedResource(t tools.DataType, request *tools.APIRequest, buyingStrategy int, pricingStrategy int) pricing.PricedItemITF {
t tools.DataType, request *tools.APIRequest) pricing.PricedItemITF {
instances := map[string]string{} instances := map[string]string{}
profiles := []pricing.PricingProfileITF{} profiles := []pricing.PricingProfileITF{}
for _, instance := range abs.Instances { for _, instance := range abs.Instances {
instances[instance.GetID()] = instance.GetName() instances[instance.GetID()] = instance.GetName()
profiles = instance.GetPricingsProfiles(request.PeerID, request.Groups) profiles = instance.GetPricingsProfiles(request.PeerID, request.Groups, buyingStrategy, pricingStrategy)
} }
return &PricedResource{ return &PricedResource{
Name: abs.Name, Name: abs.Name,
@ -184,10 +183,10 @@ func (ri *ResourceInstance[T]) GetPartnerships(peerID string, groups []string) [
return partners return partners
} }
func (ri *ResourceInstance[T]) GetPricingsProfiles(peerID string, groups []string) []pricing.PricingProfileITF { func (ri *ResourceInstance[T]) GetPricingsProfiles(peerID string, groups []string, buyingStrategy int, pricingStrategy int) []pricing.PricingProfileITF {
pricings := []pricing.PricingProfileITF{} pricings := []pricing.PricingProfileITF{}
for _, p := range ri.Partnerships { for _, p := range ri.Partnerships {
pricings = append(pricings, p.GetPricingsProfiles(peerID, groups)...) pricings = append(pricings, p.GetPricingsProfiles(peerID, groups, buyingStrategy, pricingStrategy)...)
} }
return pricings return pricings
} }
@ -211,27 +210,32 @@ func (ri *ResourceInstance[T]) ClearPeerGroups() {
type ResourcePartnerShip[T pricing.PricingProfileITF] struct { type ResourcePartnerShip[T pricing.PricingProfileITF] struct {
Namespace string `json:"namespace" bson:"namespace" default:"default-namespace"` Namespace string `json:"namespace" bson:"namespace" default:"default-namespace"`
PeerGroups map[string][]string `json:"peer_groups,omitempty" bson:"peer_groups,omitempty"` PeerGroups map[string][]string `json:"peer_groups,omitempty" bson:"peer_groups,omitempty"`
PricingProfiles []T `json:"pricing_profiles,omitempty" bson:"pricing_profiles,omitempty"` PricingProfiles map[int]map[int]T `json:"pricing_profiles,omitempty" bson:"pricing_profiles,omitempty"`
// to upgrade pricing profiles. to be a map BuyingStrategy, map of Strategy
} }
func (ri *ResourcePartnerShip[T]) GetPricingsProfiles(peerID string, groups []string) []pricing.PricingProfileITF { /*
Le pricing doit être selectionné lors d'un scheduling...
le type de paiement défini le type de stratégie de paiement
note : il faut rajouté - une notion de facturation
Une order est l'ensemble de la commande... un booking une réservation, une purchase un acte d'achat.
Une bill (facture) représente alors... l'emission d'une facture à un instant T en but d'être honorée, envoyée ... etc.
*/
func (ri *ResourcePartnerShip[T]) GetPricingsProfiles(peerID string, groups []string, buyingStrategy int, pricingStrategy int) []pricing.PricingProfileITF {
profiles := []pricing.PricingProfileITF{} profiles := []pricing.PricingProfileITF{}
if ri.PeerGroups[peerID] != nil { if ri.PeerGroups[peerID] == nil {
if slices.Contains(groups, "*") {
for _, ri := range ri.PricingProfiles {
profiles = append(profiles, ri)
}
return profiles return profiles
} }
for _, p := range ri.PeerGroups[peerID] { for _, p := range ri.PeerGroups[peerID] {
if slices.Contains(groups, p) { if slices.Contains(groups, p) || slices.Contains(groups, "*") {
for _, ri := range ri.PricingProfiles { for buyingS, ri := range ri.PricingProfiles {
profiles = append(profiles, ri) if _, ok := ri[pricingStrategy]; ok && buyingS == buyingStrategy {
profiles = append(profiles, ri[pricingStrategy])
}
} }
return profiles return profiles
} }
} }
}
return profiles return profiles
} }

View File

@ -31,11 +31,11 @@ func (r *StorageResource) GetType() string {
} }
func (abs *StorageResource) ConvertToPricedResource( func (abs *StorageResource) ConvertToPricedResource(
t tools.DataType, request *tools.APIRequest) pricing.PricedItemITF { t tools.DataType, request *tools.APIRequest, buyingStrategy int, pricingStrategy int) pricing.PricedItemITF {
if t != tools.STORAGE_RESOURCE { if t != tools.STORAGE_RESOURCE {
return nil return nil
} }
p := abs.AbstractInstanciatedResource.ConvertToPricedResource(t, request) p := abs.AbstractInstanciatedResource.ConvertToPricedResource(t, request, buyingStrategy, pricingStrategy)
priced := p.(*PricedResource) priced := p.(*PricedResource)
return &PricedStorageResource{ return &PricedStorageResource{
PricedResource: *priced, PricedResource: *priced,
@ -105,7 +105,7 @@ func (t PrivilegeStoragePricingStrategy) String() string {
type StorageResourcePricingStrategy int type StorageResourcePricingStrategy int
const ( const (
PER_DATA_STORED StorageResourcePricingStrategy = iota PER_DATA_STORED StorageResourcePricingStrategy = iota + 6
PER_TB_STORED PER_TB_STORED
PER_GB_STORED PER_GB_STORED
PER_MB_STORED PER_MB_STORED

View File

@ -30,7 +30,7 @@ func TestComputeResource_ConvertToPricedResource(t *testing.T) {
cr := &resources.ComputeResource{} cr := &resources.ComputeResource{}
cr.UUID = "comp123" cr.UUID = "comp123"
cr.AbstractInstanciatedResource.UUID = cr.UUID cr.AbstractInstanciatedResource.UUID = cr.UUID
result := cr.ConvertToPricedResource(tools.COMPUTE_RESOURCE, req) result := cr.ConvertToPricedResource(tools.COMPUTE_RESOURCE, req, 0, 0)
assert.NotNil(t, result) assert.NotNil(t, result)
assert.IsType(t, &resources.PricedComputeResource{}, result) assert.IsType(t, &resources.PricedComputeResource{}, result)
} }

View File

@ -27,10 +27,10 @@ func TestDataResource_GetAccessor(t *testing.T) {
func TestDataResource_ConvertToPricedResource(t *testing.T) { func TestDataResource_ConvertToPricedResource(t *testing.T) {
d := &resources.DataResource{} d := &resources.DataResource{}
d.UUID = "123" d.UUID = "123"
res := d.ConvertToPricedResource(tools.DATA_RESOURCE, &tools.APIRequest{}) res := d.ConvertToPricedResource(tools.DATA_RESOURCE, &tools.APIRequest{}, 0, 0)
assert.IsType(t, &resources.PricedDataResource{}, res) assert.IsType(t, &resources.PricedDataResource{}, res)
nilRes := d.ConvertToPricedResource(tools.PROCESSING_RESOURCE, &tools.APIRequest{}) nilRes := d.ConvertToPricedResource(tools.PROCESSING_RESOURCE, &tools.APIRequest{}, 0, 0)
assert.Nil(t, nilRes) assert.Nil(t, nilRes)
} }

View File

@ -20,7 +20,9 @@ func (m *MockInstance) GetID() string
func (m *MockInstance) GetName() string { return m.Name } func (m *MockInstance) GetName() string { return m.Name }
func (m *MockInstance) ClearEnv() {} func (m *MockInstance) ClearEnv() {}
func (m *MockInstance) ClearPeerGroups() {} func (m *MockInstance) ClearPeerGroups() {}
func (m *MockInstance) GetPricingsProfiles(string, []string) []pricing.PricingProfileITF { return nil } func (m *MockInstance) GetPricingsProfiles(string, []string, int, int) []pricing.PricingProfileITF {
return nil
}
func (m *MockInstance) GetPeerGroups() ([]resources.ResourcePartnerITF, []map[string][]string) { func (m *MockInstance) GetPeerGroups() ([]resources.ResourcePartnerITF, []map[string][]string) {
return nil, []map[string][]string{ return nil, []map[string][]string{
{"peer1": {"group1"}}, {"peer1": {"group1"}},
@ -35,7 +37,7 @@ func (m *MockPartner) GetPeerGroups() map[string][]string {
return m.groups return m.groups
} }
func (m *MockPartner) ClearPeerGroups() {} func (m *MockPartner) ClearPeerGroups() {}
func (m *MockPartner) GetPricingsProfiles(string, []string) []pricing.PricingProfileITF { func (m *MockPartner) GetPricingsProfiles(string, []string, int, int) []pricing.PricingProfileITF {
return nil return nil
} }

View File

@ -28,14 +28,14 @@ func TestStorageResource_ConvertToPricedResource_ValidType(t *testing.T) {
res := &resources.StorageResource{} res := &resources.StorageResource{}
res.AbstractInstanciatedResource.CreatorID = "creator" res.AbstractInstanciatedResource.CreatorID = "creator"
res.AbstractInstanciatedResource.UUID = "res-id" res.AbstractInstanciatedResource.UUID = "res-id"
priced := res.ConvertToPricedResource(tools.STORAGE_RESOURCE, &tools.APIRequest{}) priced := res.ConvertToPricedResource(tools.STORAGE_RESOURCE, &tools.APIRequest{}, 0, 0)
assert.NotNil(t, priced) assert.NotNil(t, priced)
assert.IsType(t, &resources.PricedStorageResource{}, priced) assert.IsType(t, &resources.PricedStorageResource{}, priced)
} }
func TestStorageResource_ConvertToPricedResource_InvalidType(t *testing.T) { func TestStorageResource_ConvertToPricedResource_InvalidType(t *testing.T) {
res := &resources.StorageResource{} res := &resources.StorageResource{}
priced := res.ConvertToPricedResource(tools.COMPUTE_RESOURCE, &tools.APIRequest{}) priced := res.ConvertToPricedResource(tools.COMPUTE_RESOURCE, &tools.APIRequest{}, 0, 0)
assert.Nil(t, priced) assert.Nil(t, priced)
} }

View File

@ -32,7 +32,7 @@ func TestWorkflowResource_ConvertToPricedResource(t *testing.T) {
Groups: []string{"group1"}, Groups: []string{"group1"},
} }
pr := w.ConvertToPricedResource(tools.WORKFLOW_RESOURCE, req) pr := w.ConvertToPricedResource(tools.WORKFLOW_RESOURCE, req, 0, 0)
assert.Equal(t, "creator-id", pr.GetCreatorID()) assert.Equal(t, "creator-id", pr.GetCreatorID())
assert.Equal(t, tools.WORKFLOW_RESOURCE, pr.GetType()) assert.Equal(t, tools.WORKFLOW_RESOURCE, pr.GetType())
} }

View File

@ -35,7 +35,7 @@ func (w *WorkflowResource) SetAllowedInstances(request *tools.APIRequest) {
} }
func (w *WorkflowResource) ConvertToPricedResource( func (w *WorkflowResource) ConvertToPricedResource(
t tools.DataType, request *tools.APIRequest) pricing.PricedItemITF { t tools.DataType, request *tools.APIRequest, buyingStrategy int, pricingStrategy int) pricing.PricedItemITF {
return &PricedResource{ return &PricedResource{
Name: w.Name, Name: w.Name,
Logo: w.Logo, Logo: w.Logo,

View File

@ -55,7 +55,7 @@ func (wf *Graph) IsWorkflow(item GraphItem) bool {
} }
func (g *Graph) GetAverageTimeRelatedToProcessingActivity(start time.Time, processings []*resources.ProcessingResource, resource resources.ResourceInterface, func (g *Graph) GetAverageTimeRelatedToProcessingActivity(start time.Time, processings []*resources.ProcessingResource, resource resources.ResourceInterface,
f func(GraphItem) resources.ResourceInterface, request *tools.APIRequest) (float64, float64) { f func(GraphItem) resources.ResourceInterface, request *tools.APIRequest, buyingStrategy int, pricingStrategy int) (float64, float64) {
nearestStart := float64(10000000000) nearestStart := float64(10000000000)
oneIsInfinite := false oneIsInfinite := false
longestDuration := float64(0) longestDuration := float64(0)
@ -67,7 +67,7 @@ func (g *Graph) GetAverageTimeRelatedToProcessingActivity(start time.Time, proce
} else if link.Source.ID == processing.GetID() && f(g.Items[link.Source.ID]) != nil && f(g.Items[link.Source.ID]).GetID() == resource.GetID() { // if the source is the processing and the destination is not a compute } else if link.Source.ID == processing.GetID() && f(g.Items[link.Source.ID]) != nil && f(g.Items[link.Source.ID]).GetID() == resource.GetID() { // if the source is the processing and the destination is not a compute
source = link.Destination.ID source = link.Destination.ID
} }
priced := processing.ConvertToPricedResource(tools.PROCESSING_RESOURCE, request) priced := processing.ConvertToPricedResource(tools.PROCESSING_RESOURCE, request, buyingStrategy, pricingStrategy)
if source != "" { if source != "" {
if priced.GetLocationStart() != nil { if priced.GetLocationStart() != nil {
near := float64(priced.GetLocationStart().Sub(start).Seconds()) near := float64(priced.GetLocationStart().Sub(start).Seconds())
@ -96,7 +96,7 @@ func (g *Graph) GetAverageTimeRelatedToProcessingActivity(start time.Time, proce
/* /*
* GetAverageTimeBeforeStart is a function that returns the average time before the start of a processing * GetAverageTimeBeforeStart is a function that returns the average time before the start of a processing
*/ */
func (g *Graph) GetAverageTimeProcessingBeforeStart(average float64, processingID string, request *tools.APIRequest) float64 { func (g *Graph) GetAverageTimeProcessingBeforeStart(average float64, processingID string, request *tools.APIRequest, buyingStrategy int, pricingStrategy int) float64 {
currents := []float64{} // list of current time currents := []float64{} // list of current time
for _, link := range g.Links { // for each link for _, link := range g.Links { // for each link
var source string // source is the source of the link var source string // source is the source of the link
@ -112,12 +112,12 @@ func (g *Graph) GetAverageTimeProcessingBeforeStart(average float64, processingI
if r == nil { // if item is nil, continue if r == nil { // if item is nil, continue
continue continue
} }
priced := r.ConvertToPricedResource(dt, request) priced := r.ConvertToPricedResource(dt, request, buyingStrategy, pricingStrategy)
current := priced.GetExplicitDurationInS() // get the explicit duration of the item current := priced.GetExplicitDurationInS() // get the explicit duration of the item
if current < 0 { // if current is negative, its means that duration of a before could be infinite continue if current < 0 { // if current is negative, its means that duration of a before could be infinite continue
return current return current
} }
current += g.GetAverageTimeProcessingBeforeStart(current, source, request) // get the average time before start of the source current += g.GetAverageTimeProcessingBeforeStart(current, source, request, buyingStrategy, pricingStrategy) // get the average time before start of the source
currents = append(currents, current) // append the current to the currents currents = append(currents, current) // append the current to the currents
} }
var max float64 // get the max time to wait dependancies to finish var max float64 // get the max time to wait dependancies to finish

View File

@ -77,12 +77,13 @@ func (w *Workflow) GetGraphItems(f func(item graph.GraphItem) bool) (list_datas
return return
} }
func (w *Workflow) GetPricedItem(f func(item graph.GraphItem) bool, request *tools.APIRequest) map[string]pricing.PricedItemITF { func (w *Workflow) GetPricedItem(
f func(item graph.GraphItem) bool, request *tools.APIRequest, buyingStrategy int, pricingStrategy int) map[string]pricing.PricedItemITF {
list_datas := map[string]pricing.PricedItemITF{} list_datas := map[string]pricing.PricedItemITF{}
for _, item := range w.Graph.Items { for _, item := range w.Graph.Items {
if f(item) { if f(item) {
dt, res := item.GetResource() dt, res := item.GetResource()
ord := res.ConvertToPricedResource(dt, request) ord := res.ConvertToPricedResource(dt, request, buyingStrategy, pricingStrategy)
list_datas[res.GetID()] = ord list_datas[res.GetID()] = ord
} }
} }
@ -165,11 +166,12 @@ func (wfa *Workflow) CheckBooking(caller *tools.HTTPCaller) (bool, error) {
return true, nil return true, nil
} }
func (wf *Workflow) Planify(start time.Time, end *time.Time, request *tools.APIRequest) (float64, map[tools.DataType]map[string]pricing.PricedItemITF, *Workflow, error) { func (wf *Workflow) Planify(start time.Time, end *time.Time, request *tools.APIRequest, buyingStrategy int, pricingStrategy int) (float64, map[tools.DataType]map[string]pricing.PricedItemITF, *Workflow, error) {
priceds := map[tools.DataType]map[string]pricing.PricedItemITF{} priceds := map[tools.DataType]map[string]pricing.PricedItemITF{}
ps, priceds, err := plan[*resources.ProcessingResource](tools.PROCESSING_RESOURCE, wf, priceds, request, wf.Graph.IsProcessing, ps, priceds, err := plan[*resources.ProcessingResource](tools.PROCESSING_RESOURCE, wf, priceds, request,
func(res resources.ResourceInterface, priced pricing.PricedItemITF) (time.Time, float64) { buyingStrategy, pricingStrategy,
return start.Add(time.Duration(wf.Graph.GetAverageTimeProcessingBeforeStart(0, res.GetID(), request)) * time.Second), priced.GetExplicitDurationInS() wf.Graph.IsProcessing, func(res resources.ResourceInterface, priced pricing.PricedItemITF) (time.Time, float64) {
return start.Add(time.Duration(wf.Graph.GetAverageTimeProcessingBeforeStart(0, res.GetID(), request, buyingStrategy, pricingStrategy)) * time.Second), priced.GetExplicitDurationInS()
}, func(started time.Time, duration float64) *time.Time { }, func(started time.Time, duration float64) *time.Time {
s := started.Add(time.Duration(duration)) s := started.Add(time.Duration(duration))
return &s return &s
@ -177,8 +179,8 @@ func (wf *Workflow) Planify(start time.Time, end *time.Time, request *tools.APIR
if err != nil { if err != nil {
return 0, priceds, nil, err return 0, priceds, nil, err
} }
if _, priceds, err = plan[resources.ResourceInterface](tools.DATA_RESOURCE, wf, priceds, request, wf.Graph.IsData, if _, priceds, err = plan[resources.ResourceInterface](tools.DATA_RESOURCE, wf, priceds, request, buyingStrategy, pricingStrategy,
func(res resources.ResourceInterface, priced pricing.PricedItemITF) (time.Time, float64) { wf.Graph.IsData, func(res resources.ResourceInterface, priced pricing.PricedItemITF) (time.Time, float64) {
return start, 0 return start, 0
}, func(started time.Time, duration float64) *time.Time { }, func(started time.Time, duration float64) *time.Time {
return end return end
@ -186,14 +188,14 @@ func (wf *Workflow) Planify(start time.Time, end *time.Time, request *tools.APIR
return 0, priceds, nil, err return 0, priceds, nil, err
} }
for k, f := range map[tools.DataType]func(graph.GraphItem) bool{tools.STORAGE_RESOURCE: wf.Graph.IsStorage, tools.COMPUTE_RESOURCE: wf.Graph.IsCompute} { for k, f := range map[tools.DataType]func(graph.GraphItem) bool{tools.STORAGE_RESOURCE: wf.Graph.IsStorage, tools.COMPUTE_RESOURCE: wf.Graph.IsCompute} {
if _, priceds, err = plan[resources.ResourceInterface](k, wf, priceds, request, f, if _, priceds, err = plan[resources.ResourceInterface](k, wf, priceds, request, buyingStrategy, pricingStrategy,
func(res resources.ResourceInterface, priced pricing.PricedItemITF) (time.Time, float64) { f, func(res resources.ResourceInterface, priced pricing.PricedItemITF) (time.Time, float64) {
nearestStart, longestDuration := wf.Graph.GetAverageTimeRelatedToProcessingActivity(start, ps, res, func(i graph.GraphItem) (r resources.ResourceInterface) { nearestStart, longestDuration := wf.Graph.GetAverageTimeRelatedToProcessingActivity(start, ps, res, func(i graph.GraphItem) (r resources.ResourceInterface) {
if f(i) { if f(i) {
_, r = i.GetResource() _, r = i.GetResource()
} }
return r return r
}, request) }, request, buyingStrategy, pricingStrategy)
return start.Add(time.Duration(nearestStart) * time.Second), longestDuration return start.Add(time.Duration(nearestStart) * time.Second), longestDuration
}, func(started time.Time, duration float64) *time.Time { }, func(started time.Time, duration float64) *time.Time {
s := started.Add(time.Duration(duration)) s := started.Add(time.Duration(duration))
@ -203,7 +205,8 @@ func (wf *Workflow) Planify(start time.Time, end *time.Time, request *tools.APIR
} }
} }
longest := common.GetPlannerLongestTime(end, priceds, request) longest := common.GetPlannerLongestTime(end, priceds, request)
if _, priceds, err = plan[resources.ResourceInterface](tools.WORKFLOW_RESOURCE, wf, priceds, request, wf.Graph.IsWorkflow, if _, priceds, err = plan[resources.ResourceInterface](tools.WORKFLOW_RESOURCE, wf, priceds, request,
buyingStrategy, pricingStrategy, wf.Graph.IsWorkflow,
func(res resources.ResourceInterface, priced pricing.PricedItemITF) (time.Time, float64) { func(res resources.ResourceInterface, priced pricing.PricedItemITF) (time.Time, float64) {
start := start.Add(time.Duration(common.GetPlannerNearestStart(start, priceds, request)) * time.Second) start := start.Add(time.Duration(common.GetPlannerNearestStart(start, priceds, request)) * time.Second)
longest := float64(-1) longest := float64(-1)
@ -211,7 +214,7 @@ func (wf *Workflow) Planify(start time.Time, end *time.Time, request *tools.APIR
if code != 200 || err != nil { if code != 200 || err != nil {
return start, longest return start, longest
} }
if neoLongest, _, _, err := r.(*Workflow).Planify(start, end, request); err != nil { if neoLongest, _, _, err := r.(*Workflow).Planify(start, end, request, buyingStrategy, pricingStrategy); err != nil {
return start, longest return start, longest
} else if neoLongest > longest { } else if neoLongest > longest {
longest = neoLongest longest = neoLongest
@ -226,7 +229,9 @@ func (wf *Workflow) Planify(start time.Time, end *time.Time, request *tools.APIR
return longest, priceds, wf, nil return longest, priceds, wf, nil
} }
func plan[T resources.ResourceInterface](dt tools.DataType, wf *Workflow, priceds map[tools.DataType]map[string]pricing.PricedItemITF, request *tools.APIRequest, func plan[T resources.ResourceInterface](
dt tools.DataType, wf *Workflow, priceds map[tools.DataType]map[string]pricing.PricedItemITF, request *tools.APIRequest,
buyingStrategy int, pricingStrategy int,
f func(graph.GraphItem) bool, start func(resources.ResourceInterface, pricing.PricedItemITF) (time.Time, float64), end func(time.Time, float64) *time.Time) ([]T, map[tools.DataType]map[string]pricing.PricedItemITF, error) { f func(graph.GraphItem) bool, start func(resources.ResourceInterface, pricing.PricedItemITF) (time.Time, float64), end func(time.Time, float64) *time.Time) ([]T, map[tools.DataType]map[string]pricing.PricedItemITF, error) {
resources := []T{} resources := []T{}
for _, item := range wf.GetGraphItems(f) { for _, item := range wf.GetGraphItems(f) {
@ -237,7 +242,7 @@ func plan[T resources.ResourceInterface](dt tools.DataType, wf *Workflow, priced
if realItem == nil { if realItem == nil {
return resources, priceds, errors.New("could not load the processing resource") return resources, priceds, errors.New("could not load the processing resource")
} }
priced := realItem.ConvertToPricedResource(dt, request) priced := realItem.ConvertToPricedResource(dt, request, buyingStrategy, pricingStrategy)
started, duration := start(realItem, priced) started, duration := start(realItem, priced)
priced.SetLocationStart(started) priced.SetLocationStart(started)
if duration >= 0 { if duration >= 0 {

View File

@ -109,11 +109,17 @@ func (d *WorkflowExecution) VerifyAuth(request *tools.APIRequest) bool {
return true return true
} }
/*
booking is an activity reserved for not a long time investment.
... purchase is dependant of a one time buying.
use of a datacenter or storage can't be buy for permanent.
*/
func (d *WorkflowExecution) Book(executionsID string, wfID string, priceds map[tools.DataType]map[string]pricing.PricedItemITF) []*booking.Booking { func (d *WorkflowExecution) Book(executionsID string, wfID string, priceds map[tools.DataType]map[string]pricing.PricedItemITF) []*booking.Booking {
booking := d.bookEach(executionsID, wfID, tools.STORAGE_RESOURCE, priceds[tools.STORAGE_RESOURCE]) booking := d.bookEach(executionsID, wfID, tools.STORAGE_RESOURCE, priceds[tools.STORAGE_RESOURCE])
booking = append(booking, d.bookEach(executionsID, wfID, tools.PROCESSING_RESOURCE, priceds[tools.PROCESSING_RESOURCE])...) booking = append(booking, d.bookEach(executionsID, wfID, tools.PROCESSING_RESOURCE, priceds[tools.PROCESSING_RESOURCE])...)
booking = append(booking,d.bookEach(executionsID, wfID, tools.COMPUTE_RESOURCE, priceds[tools.COMPUTE_RESOURCE])...) booking = append(booking, d.bookEach(executionsID, wfID, tools.COMPUTE_RESOURCE, priceds[tools.COMPUTE_RESOURCE])...)
booking = append(booking,d.bookEach(executionsID, wfID, tools.DATA_RESOURCE, priceds[tools.DATA_RESOURCE])...) booking = append(booking, d.bookEach(executionsID, wfID, tools.DATA_RESOURCE, priceds[tools.DATA_RESOURCE])...)
return booking return booking
} }

View File

@ -9,6 +9,7 @@ import (
"cloud.o-forge.io/core/oc-lib/models/booking" "cloud.o-forge.io/core/oc-lib/models/booking"
"cloud.o-forge.io/core/oc-lib/models/common/enum" "cloud.o-forge.io/core/oc-lib/models/common/enum"
"cloud.o-forge.io/core/oc-lib/models/common/pricing"
"cloud.o-forge.io/core/oc-lib/models/peer" "cloud.o-forge.io/core/oc-lib/models/peer"
"cloud.o-forge.io/core/oc-lib/models/utils" "cloud.o-forge.io/core/oc-lib/models/utils"
"cloud.o-forge.io/core/oc-lib/models/workflow" "cloud.o-forge.io/core/oc-lib/models/workflow"
@ -32,6 +33,9 @@ type WorkflowSchedule struct {
End *time.Time `json:"end,omitempty"` // End is the end time of the schedule, is required and must be greater than the Start time End *time.Time `json:"end,omitempty"` // End is the end time of the schedule, is required and must be greater than the Start time
DurationS float64 `json:"duration_s" default:"-1"` // End is the end time of the schedule DurationS float64 `json:"duration_s" default:"-1"` // End is the end time of the schedule
Cron string `json:"cron,omitempty"` // here the cron format : ss mm hh dd MM dw task Cron string `json:"cron,omitempty"` // here the cron format : ss mm hh dd MM dw task
SelectedBuyingStrategy pricing.BuyingStrategy `json:"selected_buying_strategy"`
SelectedPricingStrategy int `json:"selected_pricing_strategy"`
} }
func NewScheduler(start string, end string, durationInS float64, cron string) *WorkflowSchedule { func NewScheduler(start string, end string, durationInS float64, cron string) *WorkflowSchedule {
@ -62,7 +66,7 @@ func (ws *WorkflowSchedule) CheckBooking(wfID string, request *tools.APIRequest)
return false, nil, []*WorkflowExecution{}, []*booking.Booking{}, errors.New("could not load the workflow with id: " + err.Error()) return false, nil, []*WorkflowExecution{}, []*booking.Booking{}, errors.New("could not load the workflow with id: " + err.Error())
} }
wf := res.(*workflow.Workflow) wf := res.(*workflow.Workflow)
longest, priceds, wf, err := wf.Planify(ws.Start, ws.End, request) longest, priceds, wf, err := wf.Planify(ws.Start, ws.End, request, int(ws.SelectedBuyingStrategy), ws.SelectedPricingStrategy)
if err != nil { if err != nil {
return false, wf, []*WorkflowExecution{}, []*booking.Booking{}, err return false, wf, []*WorkflowExecution{}, []*booking.Booking{}, err
} }