massive draft for payment process (UNCOMPLETE)

This commit is contained in:
mr
2024-12-12 16:25:47 +01:00
parent fbbce7817b
commit 02d1e93c78
55 changed files with 3018 additions and 1177 deletions

View File

@@ -1,7 +1,12 @@
package resources
import (
"cloud.o-forge.io/core/oc-lib/models/resources/resource_model"
"errors"
"strings"
"time"
"cloud.o-forge.io/core/oc-lib/models/common"
"cloud.o-forge.io/core/oc-lib/models/common/pricing"
"cloud.o-forge.io/core/oc-lib/models/utils"
"cloud.o-forge.io/core/oc-lib/tools"
)
@@ -11,68 +16,171 @@ import (
* it defines the resource compute
*/
type ComputeResource struct {
resource_model.AbstractResource
Technology TechnologyEnum `json:"technology" bson:"technology" default:"0"` // Technology is the technology
Architecture string `json:"architecture,omitempty" bson:"architecture,omitempty"` // Architecture is the architecture
Access AccessEnum `json:"access" bson:"access" default:"0"` // Access is the access
Localisation string `json:"localisation,omitempty" bson:"localisation,omitempty"` // Localisation is the localisation
CPUs []*CPU `bson:"cpus,omitempty" json:"cpus,omitempty"` // CPUs is the list of CPUs
RAM *RAM `bson:"ram,omitempty" json:"ram,omitempty"` // RAM is the RAM
GPUs []*GPU `bson:"gpus,omitempty" json:"gpus,omitempty"` // GPUs is the list of GPUs
AbstractResource[*ComputeResourceInstance]
Architecture string `json:"architecture,omitempty" bson:"architecture,omitempty"` // Architecture is the architecture
Infrastructure common.InfrastructureType `json:"infrastructure,omitempty" bson:"infrastructure,omitempty"`
}
func (d *ComputeResource) GetAccessor(username string, peerID string, groups []string, caller *tools.HTTPCaller) utils.Accessor {
return New[*ComputeResource](tools.COMPUTE_RESOURCE, username, peerID, groups, caller, func() utils.DBObject { return &ComputeResource{} })
func (d *ComputeResource) GetAccessor(request *tools.APIRequest) utils.Accessor {
return NewAccessor[*ComputeResource](tools.COMPUTE_RESOURCE, request, func() utils.DBObject { return &ComputeResource{} })
}
// CPU is a struct that represents a CPU
type CPU struct {
Cores uint `bson:"cores,omitempty" json:"cores,omitempty"` //TODO: validate
Architecture string `bson:"architecture,omitempty" json:"architecture,omitempty"` //TOOD: enum
Shared bool `bson:"shared,omitempty" json:"shared,omitempty"`
MinimumMemory uint `bson:"minimum_memory,omitempty" json:"minimum_memory,omitempty"`
Platform string `bson:"platform,omitempty" json:"platform,omitempty"`
type ComputeResourceInstance struct {
ResourceInstance[*ComputeResourcePartnership]
SecurityLevel string `json:"security_level,omitempty" bson:"security_level,omitempty"`
PowerSource string `json:"power_source,omitempty" bson:"power_source,omitempty"`
CPUs map[string]*common.CPU `bson:"cpus,omitempty" json:"cpus,omitempty"` // CPUs is the list of CPUs key is model
GPUs map[string]*common.GPU `bson:"gpus,omitempty" json:"gpus,omitempty"` // GPUs is the list of GPUs key is model
RAM *common.RAM `bson:"ram,omitempty" json:"ram,omitempty"` // RAM is the RAM
}
type RAM struct {
Size uint `bson:"size,omitempty" json:"size,omitempty" description:"Units in MB"`
Ecc bool `bson:"ecc,omitempty" json:"ecc,omitempty"`
type ComputeResourcePartnership struct {
ResourcePartnerShip[*ComputeResourcePricingProfile]
MaxAllowedCPUsCores map[string]int `json:"allowed_cpus,omitempty" bson:"allowed_cpus,omitempty"`
MaxAllowedGPUsMemoryGB map[string]float64 `json:"allowed_gpus,omitempty" bson:"allowed_gpus,omitempty"`
MaxAllowedRAMSize float64 `json:"allowed_ram,omitempty" bson:"allowed_ram,omitempty"`
}
type GPU struct {
CudaCores uint `bson:"cuda_cores,omitempty" json:"cuda_cores,omitempty"`
Model string `bson:"model,omitempty" json:"model,omitempty"`
Memory uint `bson:"memory,omitempty" json:"memory,omitempty" description:"Units in MB"`
TensorCores uint `bson:"tensor_cores,omitempty" json:"tensor_cores,omitempty"`
func (r *ComputeResourceInstance) VerifyPartnerships() bool {
peersMultiple := map[string]int{}
for _, p := range r.Partnerships {
for k, g := range p.PeerGroups {
for _, v := range g {
if _, ok := peersMultiple[k+"_"+v]; !ok {
peersMultiple[k + "_" + v] = 0
}
peersMultiple[k+"_"+v]++
}
}
for k, p := range peersMultiple {
if p > 1 {
return false
}
}
}
type TechnologyEnum int
const (
DOCKER TechnologyEnum = iota
KUBERNETES
SLURM
HW
CONDOR
)
func (t TechnologyEnum) String() string {
return [...]string{"DOCKER", "KUBERNETES", "SLURM", "HW", "CONDOR"}[t]
type ComputeResourcePricingProfileOptions struct {
CPUCore int `json:"cpu_core" bson:"cpu_core" default:"1"`
GPUMemoryGB float64 `json:"gpu_memory_gb" bson:"gpu_memory_gb" default:"1"`
RAMSizeGB float64 `json:"ram_size_gb" bson:"ram_size_gb" default:"1"`
}
type AccessEnum int
const (
SSH AccessEnum = iota
SSH_KUBE_API
SSH_SLURM
SSH_DOCKER
OPENCLOUD
VPN
)
func (a AccessEnum) String() string {
return [...]string{"SSH", "SSH_KUBE_API", "SSH_SLURM", "SSH_DOCKER", "OPENCLOUD", "VPN"}[a]
type ComputeResourcePricingProfile struct {
pricing.ExploitPricingProfile
Pricing pricing.PricingStrategy[pricing.TimePricingStrategy] `json:"price,omitempty" bson:"price,omitempty"` // Price is the price of the resource
Options ComputeResourcePricingProfileOptions `json:"options,omitempty" bson:"options,omitempty"` // Options is the options of the pricing profile
// ExploitPricingProfile is the pricing profile of a compute it means that we exploit the resource for an amount of continuous time
OverrideCPUsPrices map[string]float64 `json:"cpus_prices,omitempty" bson:"cpus_prices,omitempty"` // CPUsPrices is the prices of the CPUs
OverrideGPUsPrices map[string]float64 `json:"gpus_prices,omitempty" bson:"gpus_prices,omitempty"` // GPUsPrices is the prices of the GPUs
OverrideRAMPrice float64 `json:"ram_price" bson:"ram_price" default:"-1"` // RAMPrice is the price of the RAM
}
// PROBLEM
func (p *ComputeResourcePricingProfile) IsBuying() bool {
return p.Pricing.BuyingStrategy != pricing.PAY_PER_USE
}
func (p *ComputeResourcePricingProfile) GetOverrideStrategyValue() int {
return -1
}
// NOT A PROPER QUANTITY
// amountOfData is the number of CPUs, GPUs or RAM dependings on the params
func (p *ComputeResourcePricingProfile) GetPrice(amountOfData float64, explicitDuration float64, start time.Time, end time.Time, request *tools.APIRequest, params ...string) (float64, error) {
if len(params) < 1 {
return 0, errors.New("params must be set")
}
pp := float64(0)
model := params[1]
if strings.Contains(params[0], "cpus") && len(params) > 1 {
if _, ok := p.OverrideCPUsPrices[model]; ok {
p.Pricing.Price = p.OverrideCPUsPrices[model]
}
r, err := p.Pricing.GetPrice(amountOfData/float64(p.Options.CPUCore), explicitDuration, start, &end)
if err != nil {
return 0, err
}
pp += r
}
if strings.Contains(params[0], "gpus") && len(params) > 1 {
if _, ok := p.OverrideGPUsPrices[model]; ok {
p.Pricing.Price = p.OverrideGPUsPrices[model]
}
r, err := p.Pricing.GetPrice(amountOfData/float64(p.Options.GPUMemoryGB), explicitDuration, start, &end)
if err != nil {
return 0, err
}
pp += r
}
if strings.Contains(params[0], "ram") {
if p.OverrideRAMPrice >= 0 {
p.Pricing.Price = p.OverrideRAMPrice
}
r, err := p.Pricing.GetPrice(float64(amountOfData)/p.Options.RAMSizeGB, explicitDuration, start, &end)
if err != nil {
return 0, err
}
pp += r
}
return pp, nil
}
type CustomizedComputeResource struct {
AbstractCustomizedResource[*ComputeResourceInstance]
CPUsLocated map[string]float64 `json:"cpus_in_use" bson:"cpus_in_use"` // CPUsInUse is the list of CPUs in use
GPUsLocated map[string]float64 `json:"gpus_in_use" bson:"gpus_in_use"` // GPUsInUse is the list of GPUs in use
RAMLocated float64 `json:"ram_in_use" bson:"ram_in_use"` // RAMInUse is the RAM in use
}
func (r *CustomizedComputeResource) GetType() tools.DataType {
return tools.COMPUTE_RESOURCE
}
func (r *CustomizedComputeResource) GetPrice(request *tools.APIRequest) (float64, error) {
if r.UsageStart == nil || r.UsageEnd == nil {
return 0, errors.New("Usage start and end must be set")
}
partner := r.GetPartnership(request)
if partner != nil && partner.GetPricing(r.SelectedPricing) != nil {
return 0, errors.New("Pricing strategy not found")
}
pricing := partner.GetPricing(r.SelectedPricing)
price := float64(0)
for _, l := range []map[string]float64{r.CPUsLocated, r.GPUsLocated} {
for model, amountOfData := range l {
cpus, err := pricing.GetPrice(float64(amountOfData), r.ExplicitBookingDurationS, *r.UsageStart, *r.UsageEnd, request, "cpus", model)
if err != nil {
return 0, err
}
price += cpus
}
}
ram, err := pricing.GetPrice(r.RAMLocated, r.ExplicitBookingDurationS, *r.UsageStart, *r.UsageEnd, request, "ram")
if err != nil {
return 0, err
}
price += ram
return price, nil
}
/*
* FillWithDefaultProcessingUsage fills the order item with the default processing usage
* it depends on the processing usage only if nothing is set, during order
*/
func (i *CustomizedComputeResource) FillWithDefaultProcessingUsage(usage *ProcessingUsage) {
for _, cpu := range usage.CPUs {
if _, ok := i.CPUsLocated[cpu.Model]; !ok {
i.CPUsLocated[cpu.Model] = 0
}
if i.CPUsLocated[cpu.Model] < float64(cpu.Cores) {
i.CPUsLocated[cpu.Model] = float64(cpu.Cores)
}
}
for _, cpu := range usage.GPUs {
i.GPUsLocated[cpu.Model] = 1
}
i.RAMLocated = usage.RAM.SizeGb
}

View File

@@ -1,7 +1,10 @@
package resources
import (
"cloud.o-forge.io/core/oc-lib/models/resources/resource_model"
"errors"
"time"
"cloud.o-forge.io/core/oc-lib/models/common/pricing"
"cloud.o-forge.io/core/oc-lib/models/utils"
"cloud.o-forge.io/core/oc-lib/tools"
)
@@ -15,12 +18,15 @@ const (
LICENCED
)
/*
* Struct of Usage Conditions
*/
type UsageConditions struct {
Usage string `json:"usage,omitempty" bson:"usage,omitempty" description:"usage of the data"` // Usage is the usage of the data
Actors []string `json:"actors,omitempty" bson:"actors,omitempty" description:"actors of the data"` // Actors is the actors of the data
type abstractDataResource struct {
Type string `bson:"type,omitempty" json:"type,omitempty"`
Quality string `bson:"quality,omitempty" json:"quality,omitempty"`
OpenData bool `bson:"open_data" json:"open_data" default:"false"` // Type is the type of the storage
Static bool `bson:"static" json:"static" default:"false"`
UpdatePeriod time.Time `bson:"update_period,omitempty" json:"update_period,omitempty"`
PersonalData bool `bson:"personal_data,omitempty" json:"personal_data,omitempty"`
AnonymizedPersonalData bool `bson:"anonymized_personal_data,omitempty" json:"anonymized_personal_data,omitempty"`
SizeGB float64 `json:"size_gb,omitempty" bson:"size_gb,omitempty"` // SizeGB is the size of the data
}
/*
@@ -28,15 +34,105 @@ type UsageConditions struct {
* it defines the resource data
*/
type DataResource struct {
resource_model.AbstractResource // AbstractResource contains the basic fields of an object (id, name)
resource_model.WebResource
Type string `bson:"type,omitempty" json:"type,omitempty"` // Type is the type of the storage
UsageConditions UsageConditions `json:"usage_conditions,omitempty" bson:"usage_conditions,omitempty" description:"usage conditions of the data"` // UsageConditions is the usage conditions of the data
License DataLicense `json:"license" bson:"license" description:"license of the data" default:"0"` // License is the license of the data
Interest DataLicense `json:"interest" bson:"interest" description:"interest of the data" default:"0"` // Interest is the interest of the data
Example string `json:"example,omitempty" bson:"example,omitempty" description:"base64 encoded data"` // Example is an example of the data
AbstractResource[*ResourceInstance[*DataResourcePartnership]]
abstractDataResource // AbstractResource contains the basic fields of an object (id, name)
License DataLicense `json:"license" bson:"license" description:"license of the data" default:"0"` // License is the license of the data
// ? Interest DataLicense `json:"interest" bson:"interest" description:"interest of the data" default:"0"` // Interest is the interest of the data
Example string `json:"example,omitempty" bson:"example,omitempty" description:"base64 encoded data"` // Example is an example of the data
}
func (d *DataResource) GetAccessor(username string, peerID string, groups []string, caller *tools.HTTPCaller) utils.Accessor {
return New[*DataResource](tools.DATA_RESOURCE, username, peerID, groups, caller, func() utils.DBObject { return &DataResource{} }) // Create a new instance of the accessor
func (d *DataResource) GetAccessor(request *tools.APIRequest) utils.Accessor {
return NewAccessor[*DataResource](tools.DATA_RESOURCE, request, func() utils.DBObject { return &DataResource{} }) // Create a new instance of the accessor
}
type DataResourcePartnership struct {
ResourcePartnerShip[*DataResourcePricingProfile]
MaxDownloadableGbAllowed float64 `json:"allowed_gb,omitempty" bson:"allowed_gb,omitempty"`
PersonalDataAllowed bool `json:"personal_data_allowed,omitempty" bson:"personal_data_allowed,omitempty"`
AnonymizedPersonalDataAllowed bool `json:"anonymized_personal_data_allowed,omitempty" bson:"anonymized_personal_data_allowed,omitempty"`
}
type DataResourcePricingStrategy int
const (
PER_DOWNLOAD DataResourcePricingStrategy = iota
PER_TB_DOWNLOADED
PER_GB_DOWNLOADED
PER_MB_DOWNLOADED
PER_KB_DOWNLOADED
)
func ToDataResourcePricingStrategy(i int) DataResourcePricingStrategy {
return DataResourcePricingStrategy(i)
}
func (t DataResourcePricingStrategy) GetStrategy() string {
return [...]string{"PER_DOWNLOAD", "PER_GB", "PER_MB", "PER_KB"}[t]
}
func (t DataResourcePricingStrategy) GetStrategyValue() int {
return int(t)
}
func (t DataResourcePricingStrategy) GetQuantity(amountOfDataGB float64) (float64, error) {
switch t {
case PER_DOWNLOAD:
return 1, nil
case PER_TB_DOWNLOADED:
return amountOfDataGB * 1000, nil
case PER_GB_DOWNLOADED:
return amountOfDataGB, nil
case PER_MB_DOWNLOADED:
return amountOfDataGB / 1000, nil
case PER_KB_DOWNLOADED:
return amountOfDataGB / 1000000, nil
}
return 0, errors.New("Pricing strategy not found")
}
type DataResourcePricingProfile struct {
Pricing *pricing.PricingStrategy[DataResourcePricingStrategy] `json:"cpus_prices,omitempty" bson:"cpus_prices,omitempty"` // CPUsPrices is the prices of the CPUs
pricing.AccessPricingProfile // AccessPricingProfile is the pricing profile of a data it means that we can access the data for an amount of time
}
func (p *DataResourcePricingProfile) GetOverrideStrategyValue() int {
return p.Pricing.OverrideStrategy.GetStrategyValue()
}
func (p *DataResourcePricingProfile) GetPrice(amountOfData float64, explicitDuration float64, start time.Time, end time.Time, request *tools.APIRequest, params ...string) (float64, error) {
return p.Pricing.GetPrice(amountOfData, explicitDuration, start, &end)
}
func (p *DataResourcePricingProfile) IsBuying() bool {
return p.Pricing.BuyingStrategy != pricing.PAY_PER_USE
}
type CustomizedDataResource struct {
AbstractCustomizedResource[*ResourceInstance[*DataResourcePartnership]]
abstractDataResource
StorageGB float64 `json:"storage_gb,omitempty" bson:"storage_gb,omitempty"`
}
func (r *CustomizedDataResource) GetType() tools.DataType {
return tools.DATA_RESOURCE
}
func (r *CustomizedDataResource) GetPrice(request *tools.APIRequest) (float64, error) {
if r.UsageStart == nil || r.UsageEnd == nil {
return 0, errors.New("Usage start and end must be set")
}
partner := r.GetPartnership(request)
if partner != nil && partner.GetPricing(r.SelectedPricing) != nil {
return 0, errors.New("Pricing strategy not found")
}
pricing := partner.GetPricing(r.SelectedPricing)
var err error
amountOfData := float64(1)
if pricing.GetOverrideStrategyValue() >= 0 {
amountOfData, err = ToDataResourcePricingStrategy(pricing.GetOverrideStrategyValue()).GetQuantity(r.StorageGB)
if err != nil {
return 0, err
}
}
return pricing.GetPrice(amountOfData, r.ExplicitBookingDurationS, *r.UsageStart, *r.UsageEnd, request)
}

View File

@@ -0,0 +1,47 @@
package resources
import (
"time"
"cloud.o-forge.io/core/oc-lib/models/common/pricing"
"cloud.o-forge.io/core/oc-lib/models/resources/resource_model"
"cloud.o-forge.io/core/oc-lib/models/utils"
"cloud.o-forge.io/core/oc-lib/tools"
)
type ShallowResourceInterface interface {
utils.DBObject
GetType() tools.DataType
GetCreatorID() string
GetPricingID() string
GetLocationStart() *time.Time
GetLocationEnd() *time.Time
GetExplicitDurationInS() float64
SetStartUsage(start time.Time)
SetEndUsage(end time.Time)
GetPartnership(request *tools.APIRequest) ResourcePartnerITF
SetResourceModel(model *resource_model.ResourceModel)
}
type ResourceInterface interface {
utils.DBObject
Trim()
GetCreatorID() string
VerifyPartnerships() bool
GetPartnership(request *tools.APIRequest) ResourcePartnerITF
SetAllowedInstances(request *tools.APIRequest)
SetResourceModel(model *resource_model.ResourceModel)
}
type InstanceITF interface {
GetID() string
VerifyPartnerships() bool
GetPeerGroups() ([]ResourcePartnerITF, []map[string][]string)
ClearPeerGroups()
}
type ResourcePartnerITF interface {
GetPricing(id string) pricing.PricingProfileITF
GetPeerGroups() map[string][]string
ClearPeerGroups()
}

View File

@@ -0,0 +1,92 @@
package resources
import (
"time"
"cloud.o-forge.io/core/oc-lib/models/utils"
"cloud.o-forge.io/core/oc-lib/tools"
)
type ExploitedResourceSet struct {
DataResources []*CustomizedDataResource `bson:"-" json:"data_resources,omitempty"`
StorageResources []*CustomizedStorageResource `bson:"-" json:"storage_resources,omitempty"`
ProcessingResources []*CustomizedProcessingResource `bson:"-" json:"processing_resources,omitempty"`
ComputeResources []*CustomizedComputeResource `bson:"-" json:"compute_resources,omitempty"`
WorkflowResources []*CustomizedWorkflowResource `bson:"-" json:"workflow_resources,omitempty"`
}
type ResourceSet struct {
Datas []string `bson:"datas,omitempty" json:"datas,omitempty"`
Storages []string `bson:"storages,omitempty" json:"storages,omitempty"`
Processings []string `bson:"processings,omitempty" json:"processings,omitempty"`
Computes []string `bson:"computes,omitempty" json:"computes,omitempty"`
Workflows []string `bson:"workflows,omitempty" json:"workflows,omitempty"`
DataResources []*DataResource `bson:"-" json:"data_resources,omitempty"`
StorageResources []*StorageResource `bson:"-" json:"storage_resources,omitempty"`
ProcessingResources []*ProcessingResource `bson:"-" json:"processing_resources,omitempty"`
ComputeResources []*ComputeResource `bson:"-" json:"compute_resources,omitempty"`
WorkflowResources []*WorkflowResource `bson:"-" json:"workflow_resources,omitempty"`
}
func (r *ResourceSet) Clear() {
r.DataResources = nil
r.StorageResources = nil
r.ProcessingResources = nil
r.ComputeResources = nil
r.WorkflowResources = nil
}
func (r *ResourceSet) Fill(request *tools.APIRequest) {
for k, v := range map[utils.DBObject][]string{
(&DataResource{}): r.Datas,
(&ComputeResource{}): r.Computes,
(&StorageResource{}): r.Storages,
(&ProcessingResource{}): r.Processings,
(&WorkflowResource{}): r.Workflows,
} {
for _, id := range v {
d, _, e := k.GetAccessor(request).LoadOne(id)
if e == nil {
switch k.(type) {
case *DataResource:
r.DataResources = append(r.DataResources, d.(*DataResource))
case *ComputeResource:
r.ComputeResources = append(r.ComputeResources, d.(*ComputeResource))
case *StorageResource:
r.StorageResources = append(r.StorageResources, d.(*StorageResource))
case *ProcessingResource:
r.ProcessingResources = append(r.ProcessingResources, d.(*ProcessingResource))
case *WorkflowResource:
r.WorkflowResources = append(r.WorkflowResources, d.(*WorkflowResource))
}
}
}
}
}
type ItemExploitedResource struct {
Data *CustomizedDataResource `bson:"data,omitempty" json:"data,omitempty"`
Processing *CustomizedProcessingResource `bson:"processing,omitempty" json:"processing,omitempty"`
Storage *CustomizedStorageResource `bson:"storage,omitempty" json:"storage,omitempty"`
Compute *CustomizedComputeResource `bson:"compute,omitempty" json:"compute,omitempty"`
Workflow *CustomizedWorkflowResource `bson:"workflow,omitempty" json:"workflow,omitempty"`
}
func (w *ItemExploitedResource) SetItemEndUsage(end time.Time) {
for _, item := range []ShallowResourceInterface{w.Data, w.Processing, w.Storage, w.Compute, w.Workflow} {
if item != nil {
item.SetItemEndUsage(end)
}
}
}
func (w *ItemExploitedResource) SetItemStartUsage(start time.Time) {
for _, item := range []ShallowResourceInterface{w.Data, w.Processing, w.Storage, w.Compute, w.Workflow} {
if item != nil {
item.SetItemStartUsage(start)
}
}
}

View File

@@ -1,23 +1,28 @@
package resources
import (
"cloud.o-forge.io/core/oc-lib/models/resources/resource_model"
"time"
"cloud.o-forge.io/core/oc-lib/models/common"
"cloud.o-forge.io/core/oc-lib/models/common/pricing"
"cloud.o-forge.io/core/oc-lib/models/utils"
"cloud.o-forge.io/core/oc-lib/tools"
)
type Container struct {
Image string `json:"image,omitempty" bson:"image,omitempty"` // Image is the container image
Command string `json:"command,omitempty" bson:"command,omitempty"` // Command is the container command
Args string `json:"args,omitempty" bson:"args,omitempty"` // Args is the container arguments
Env map[string]string `json:"env,omitempty" bson:"env,omitempty"` // Env is the container environment variables
Volumes map[string]string `json:"volumes,omitempty" bson:"volumes,omitempty"` // Volumes is the container volumes
type ProcessingUsage struct {
CPUs map[string]*common.CPU `bson:"cpus,omitempty" json:"cpus,omitempty"` // CPUs is the list of CPUs key is model
GPUs map[string]*common.GPU `bson:"gpus,omitempty" json:"gpus,omitempty"` // GPUs is the list of GPUs key is model
RAM *common.RAM `bson:"ram,omitempty" json:"ram,omitempty"` // RAM is the RAM
StorageGb float64 `bson:"storage,omitempty" json:"storage,omitempty"` // Storage is the storage
Hypothesis string `bson:"hypothesis,omitempty" json:"hypothesis,omitempty"`
ScalingModel string `bson:"scaling_model,omitempty" json:"scaling_model,omitempty"` // ScalingModel is the scaling model
}
type Expose struct {
Port int `json:"port,omitempty" bson:"port,omitempty"` // Port is the port
Reverse string `json:"reverse,omitempty" bson:"reverse,omitempty"` // Reverse is the reverse
PAT int `json:"pat,omitempty" bson:"pat,omitempty"` // PAT is the PAT
type abstractProcessingResource struct {
Infrastructure common.InfrastructureType `json:"infrastructure,omitempty" bson:"infrastructure,omitempty"`
Service bool `json:"is_service,omitempty" bson:"is_service,omitempty"` // IsService is a flag that indicates if the processing is a service
Usage *ProcessingUsage `bson:"usage,omitempty" json:"usage,omitempty"` // Usage is the usage of the processing
}
/*
@@ -25,19 +30,49 @@ type Expose struct {
* it defines the resource processing
*/
type ProcessingResource struct {
resource_model.AbstractResource
IsService bool `json:"is_service,omitempty" bson:"is_service,omitempty"` // IsService is a flag that indicates if the processing is a service
CPUs []*CPU `bson:"cpus,omitempty" json:"cp_us,omitempty"` // CPUs is the list of CPUs
GPUs []*GPU `bson:"gpus,omitempty" json:"gp_us,omitempty"` // GPUs is the list of GPUs
RAM *RAM `bson:"ram,omitempty" json:"ram,omitempty"` // RAM is the RAM
Storage uint `bson:"storage,omitempty" json:"storage,omitempty"` // Storage is the storage
Parallel bool `bson:"parallel,omitempty" json:"parallel,omitempty"` // Parallel is a flag that indicates if the processing is parallel
ScalingModel uint `bson:"scaling_model,omitempty" json:"scaling_model,omitempty"` // ScalingModel is the scaling model
DiskIO string `bson:"disk_io,omitempty" json:"disk_io,omitempty"` // DiskIO is the disk IO
Container *Container `bson:"container,omitempty" json:"container,omitempty"` // Container is the container
Expose []Expose `bson:"expose,omitempty" json:"expose,omitempty"` // Expose is the execution
AbstractResource[*ResourceInstance[*ResourcePartnerShip[*ProcessingResourcePricingProfile]]]
abstractProcessingResource
OpenSource bool `json:"open_source" bson:"open_source" default:"false"`
License string `json:"license,omitempty" bson:"license,omitempty"`
Maturity string `json:"maturity,omitempty" bson:"maturity,omitempty"`
}
func (d *ProcessingResource) GetAccessor(username string, peerID string, groups []string, caller *tools.HTTPCaller) utils.Accessor {
return New[*ProcessingResource](tools.PROCESSING_RESOURCE, username, peerID, groups, caller, func() utils.DBObject { return &ProcessingResource{} }) // Create a new instance of the accessor
type CustomizedProcessingResource struct {
AbstractCustomizedResource[*ResourceInstance[*ResourcePartnerShip[*ProcessingResourcePricingProfile]]]
abstractProcessingResource
Container common.Container `json:"container,omitempty" bson:"container,omitempty"` // Container is the container
}
func (r *CustomizedProcessingResource) GetType() tools.DataType {
return tools.PROCESSING_RESOURCE
}
func (a *CustomizedProcessingResource) GetExplicitDurationInS() float64 {
if a.ExplicitBookingDurationS == 0 {
if a.UsageEnd == nil || a.UsageStart == nil {
if a.Service {
return -1
}
return time.Duration(1 * time.Hour).Seconds()
}
return a.UsageEnd.Sub(*a.UsageStart).Seconds()
}
return a.ExplicitBookingDurationS
}
func (d *ProcessingResource) GetAccessor(request *tools.APIRequest) utils.Accessor {
return NewAccessor[*ProcessingResource](tools.PROCESSING_RESOURCE, request, func() utils.DBObject { return &ProcessingResource{} }) // Create a new instance of the accessor
}
type ProcessingResourcePricingProfile struct {
Pricing *pricing.PricingStrategy[pricing.TimePricingStrategy] `json:"cpus_prices,omitempty" bson:"cpus_prices,omitempty"` // CPUsPrices is the prices of the CPUs
pricing.AccessPricingProfile // AccessPricingProfile is the pricing profile of a data it means that we can access the data for an amount of time
}
func (p *ProcessingResourcePricingProfile) IsBuying() bool {
return p.Pricing.BuyingStrategy != pricing.PAY_PER_USE
}
func (p *ProcessingResourcePricingProfile) GetPrice(amountOfData float64, val float64, start time.Time, end time.Time, request *tools.APIRequest, params ...string) (float64, error) {
return p.Pricing.GetPrice(amountOfData, val, start, &end)
}

View File

@@ -1,8 +1,18 @@
package resources
import (
"errors"
"slices"
"time"
"cloud.o-forge.io/core/oc-lib/config"
"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/resources/resource_model"
"cloud.o-forge.io/core/oc-lib/models/utils"
"cloud.o-forge.io/core/oc-lib/tools"
"github.com/biter777/countries"
"github.com/marcinwyszynski/geopoint"
)
// AbstractResource is the struct containing all of the attributes commons to all ressources
@@ -10,86 +20,265 @@ import (
// Resource is the interface to be implemented by all classes inheriting from Resource to have the same behavior
// http://www.inanzzz.com/index.php/post/wqbs/a-basic-usage-of-int-and-string-enum-types-in-golang
type ResourceInterface interface {
utils.DBObject
Trim() *resource_model.AbstractResource
SetResourceModel(model *resource_model.ResourceModel)
/*
* AbstractResource is a struct that represents a resource
* it defines the resource data
*/
type abstractResource struct {
utils.AbstractObject // AbstractObject contains the basic fields of an object (id, name)
Logo string `json:"logo,omitempty" bson:"logo,omitempty" validate:"required"` // Logo is the logo of the resource
Description string `json:"description,omitempty" bson:"description,omitempty"` // Description is the description of the resource
ShortDescription string `json:"short_description,omitempty" bson:"short_description,omitempty" validate:"required"` // ShortDescription is the short description of the resource
Owners []utils.Owner `json:"owners,omitempty" bson:"owners,omitempty"` // Owners is the list of owners of the resource
ResourceModel *resource_model.ResourceModel `json:"resource_model,omitempty" bson:"resource_model,omitempty"` // ResourceModel is the model of the resource
UsageRestrictions string `bson:"usage_restrictions,omitempty" json:"usage_restrictions,omitempty"`
}
type ResourceSet struct {
Datas []string `bson:"datas,omitempty" json:"datas,omitempty"`
Storages []string `bson:"storages,omitempty" json:"storages,omitempty"`
Processings []string `bson:"processings,omitempty" json:"processings,omitempty"`
Computes []string `bson:"computes,omitempty" json:"computes,omitempty"`
Workflows []string `bson:"workflows,omitempty" json:"workflows,omitempty"`
DataResources []*DataResource `bson:"-" json:"data_resources,omitempty"`
StorageResources []*StorageResource `bson:"-" json:"storage_resources,omitempty"`
ProcessingResources []*ProcessingResource `bson:"-" json:"processing_resources,omitempty"`
ComputeResources []*ComputeResource `bson:"-" json:"compute_resources,omitempty"`
WorkflowResources []*WorkflowResource `bson:"-" json:"workflow_resources,omitempty"`
func (r *abstractResource) StoreDraftDefault() {
r.IsDraft = true
}
func (r *ResourceSet) Clear() {
r.DataResources = nil
r.StorageResources = nil
r.ProcessingResources = nil
r.ComputeResources = nil
r.WorkflowResources = nil
func (r *AbstractCustomizedResource[T]) CanUpdate(set utils.DBObject) (bool, utils.DBObject) {
if r.IsDraft != set.IsDrafted() && set.IsDrafted() {
return true, set // only state can be updated
}
return r.IsDraft != set.IsDrafted() && set.IsDrafted(), set
}
func (r *ResourceSet) Fill(username string, peerID string, groups []string) {
for k, v := range map[utils.DBObject][]string{
(&DataResource{}): r.Datas,
(&ComputeResource{}): r.Computes,
(&StorageResource{}): r.Storages,
(&ProcessingResource{}): r.Processings,
(&WorkflowResource{}): r.Workflows,
} {
for _, id := range v {
d, _, e := k.GetAccessor(username, peerID, groups, nil).LoadOne(id)
if e == nil {
switch k.(type) {
case *DataResource:
r.DataResources = append(r.DataResources, d.(*DataResource))
case *ComputeResource:
r.ComputeResources = append(r.ComputeResources, d.(*ComputeResource))
case *StorageResource:
r.StorageResources = append(r.StorageResources, d.(*StorageResource))
case *ProcessingResource:
r.ProcessingResources = append(r.ProcessingResources, d.(*ProcessingResource))
case *WorkflowResource:
r.WorkflowResources = append(r.WorkflowResources, d.(*WorkflowResource))
func (r *abstractResource) CanDelete() bool {
return r.IsDraft // only draft bookings can be deleted
}
func (ao *abstractResource) GetAccessor(request *tools.APIRequest) utils.Accessor {
return nil
}
func (ao *abstractResource) GetCreatorID() string {
return ao.CreatorID
}
func (abs *abstractResource) SetResourceModel(model *resource_model.ResourceModel) {
abs.ResourceModel = model
}
type AbstractResource[T InstanceITF] struct {
abstractResource
Instances []T `json:"instances,omitempty" bson:"instances,omitempty"` // Bill is the bill of the resource // Bill is the bill of the resource
}
func (abs *AbstractResource[T]) SetAllowedInstances(request *tools.APIRequest) {
abs.Instances = verifyAuthAction[T](abs.Instances, request)
}
func (abs *AbstractResource[T]) GetPartnership(request *tools.APIRequest) ResourcePartnerITF {
for _, instance := range abs.Instances {
partners, grps := instance.GetPeerGroups()
for i, p := range grps {
if request == nil {
continue
}
if _, ok := p[request.PeerID]; ok {
return partners[i]
}
}
}
return nil
}
type AbstractCustomizedResource[T InstanceITF] struct {
abstractResource
ExplicitBookingDurationS float64 `json:"explicit_location_duration_s,omitempty" bson:"explicit_location_duration_s,omitempty"`
UsageStart *time.Time `json:"start,omitempty" bson:"start,omitempty"`
UsageEnd *time.Time `json:"end,omitempty" bson:"end,omitempty"`
SelectedInstance T `json:"selected_instance,omitempty" bson:"selected_instance,omitempty"`
SelectedPricing string `json:"selected_pricing,omitempty" bson:"selected_pricing,omitempty"`
}
func (abs *AbstractCustomizedResource[T]) SetStartUsage(start time.Time) {
if abs.UsageStart == nil {
abs.UsageStart = &start
}
}
func (abs *AbstractCustomizedResource[T]) SetEndUsage(end time.Time) {
if abs.UsageEnd == nil {
abs.UsageEnd = &end
}
}
func (abs *AbstractCustomizedResource[T]) IsBuying(request *tools.APIRequest) bool {
return abs.GetPartnership(request).GetPricing(abs.SelectedPricing).IsBuying()
}
func (abs *AbstractCustomizedResource[T]) GetLocationEnd() *time.Time {
return abs.UsageEnd
}
func (abs *AbstractCustomizedResource[T]) GetLocationStart() *time.Time {
return abs.UsageStart
}
func (abs *AbstractCustomizedResource[T]) GetExplicitDurationInS() float64 {
if abs.ExplicitBookingDurationS == 0 {
if abs.UsageEnd == nil || abs.UsageStart == nil {
return time.Duration(1 * time.Hour).Seconds()
}
return abs.UsageEnd.Sub(*abs.UsageStart).Seconds()
}
return abs.ExplicitBookingDurationS
}
func (abs *AbstractCustomizedResource[T]) GetPricingID() string {
return abs.SelectedPricing
}
func (r *AbstractCustomizedResource[T]) GetPrice(request *tools.APIRequest) (float64, error) {
if r.UsageStart == nil || r.UsageEnd == nil {
return 0, errors.New("Usage start and end must be set")
}
partner := r.GetPartnership(request)
if partner != nil && partner.GetPricing(r.SelectedPricing) != nil {
return 0, errors.New("Pricing strategy not found")
}
return partner.GetPricing(r.SelectedPricing).GetPrice(1, 0, *r.UsageStart, *r.UsageEnd, request)
}
func (abs *AbstractCustomizedResource[T]) GetPartnership(request *tools.APIRequest) ResourcePartnerITF {
partners, grps := abs.SelectedInstance.GetPeerGroups()
for i, p := range grps {
if request == nil {
continue
}
if _, ok := p[request.PeerID]; ok {
return partners[i]
}
}
return nil
}
func verifyAuthAction[T InstanceITF](baseInstance []T, request *tools.APIRequest) []T {
instances := []T{}
for _, instance := range baseInstance {
_, peerGroups := instance.GetPeerGroups()
for _, peers := range peerGroups {
if request == nil {
continue
}
if grps, ok := peers[request.PeerID]; ok || config.GetConfig().Whitelist {
if (ok && slices.Contains(grps, "*")) || (!ok && config.GetConfig().Whitelist) {
instances = append(instances, instance)
}
for _, grp := range grps {
if slices.Contains(request.Groups, grp) {
instances = append(instances, instance)
}
}
}
}
}
return instances
}
type ItemResource struct {
Data *DataResource `bson:"data,omitempty" json:"data,omitempty"`
Processing *ProcessingResource `bson:"processing,omitempty" json:"processing,omitempty"`
Storage *StorageResource `bson:"storage,omitempty" json:"storage,omitempty"`
Compute *ComputeResource `bson:"compute,omitempty" json:"compute,omitempty"`
Workflow *WorkflowResource `bson:"workflow,omitempty" json:"workflow,omitempty"`
/*
* VerifyPartnerships is a function that verifies the partnerships of the resource
* an instance can only have one partnership available for a peer
* it returns a boolean
*/
func (abs *AbstractResource[T]) VerifyPartnerships() bool {
// a peer can be part of only one partnership by instance
// may we need to define partnership in a different DB
for _, instance := range abs.Instances {
if !instance.VerifyPartnerships() {
return false
}
}
return true
}
func (i *ItemResource) GetAbstractRessource() *resource_model.AbstractResource {
if i.Data != nil {
return &i.Data.AbstractResource
func (d *AbstractResource[T]) Trim() {
if ok, _ := (&peer.Peer{AbstractObject: utils.AbstractObject{UUID: d.CreatorID}}).IsMySelf(); !ok {
// TODO clean up the peer groups that are not in the allowed peers group
for _, instance := range d.Instances {
instance.ClearPeerGroups()
}
}
if i.Processing != nil {
return &i.Processing.AbstractResource
}
if i.Storage != nil {
return &i.Storage.AbstractResource
}
if i.Compute != nil {
return &i.Compute.AbstractResource
}
if i.Workflow != nil {
return &i.Workflow.AbstractResource
}
return nil
}
func (abs *AbstractResource[T]) VerifyAuth(request *tools.APIRequest) bool {
return len(verifyAuthAction[T](abs.Instances, request)) > 0 || abs.AbstractObject.VerifyAuth(request)
}
type ResourceInstance[T ResourcePartnerITF] struct {
UUID string `json:"id,omitempty" bson:"id,omitempty"`
Location geopoint.GeoPoint `json:"location,omitempty" bson:"location,omitempty"`
Country countries.CountryCode `json:"country,omitempty" bson:"country,omitempty"`
// Url string `json:"url,omitempty" bson:"url,omitempty"`
AccessProtocol string `json:"access_protocol,omitempty" bson:"access_protocol,omitempty"`
Partnerships []T `json:"partner_resource,omitempty" bson:"partner_resource,omitempty"`
}
func (ri *ResourceInstance[T]) GetID() string {
return ri.UUID
}
func (r *ResourceInstance[T]) VerifyPartnerships() bool {
peersMultiple := map[string]int{}
for _, p := range r.Partnerships {
for k, g := range p.GetPeerGroups() {
for _, v := range g {
if _, ok := peersMultiple[k+"_"+v]; !ok {
peersMultiple[k+"_"+v] = 0
}
peersMultiple[k+"_"+v]++
}
}
}
for _, p := range peersMultiple {
if p > 1 {
return false
}
}
return true
}
func (ri *ResourceInstance[T]) GetPeerGroups() ([]ResourcePartnerITF, []map[string][]string) {
groups := []map[string][]string{}
partners := []ResourcePartnerITF{}
for _, p := range ri.Partnerships {
partners = append(partners, p)
groups = append(groups, p.GetPeerGroups())
}
return partners, groups
}
func (ri *ResourceInstance[T]) ClearPeerGroups() {
for _, p := range ri.Partnerships {
p.ClearPeerGroups()
}
}
type ResourcePartnerShip[T pricing.PricingProfileITF] struct {
Namespace string `json:"namespace" bson:"namespace" default:"default-namespace"`
PeerGroups map[string][]string `json:"peer_groups,omitempty" bson:"peer_groups,omitempty"`
PricingProfiles map[string]T `json:"pricing,omitempty" bson:"pricing,omitempty"`
}
func (rp *ResourcePartnerShip[T]) GetPricing(id string) pricing.PricingProfileITF {
return rp.PricingProfiles[id]
}
func (rp *ResourcePartnerShip[T]) GetPeerGroups() map[string][]string {
return rp.PeerGroups
}
func (rp *ResourcePartnerShip[T]) ClearPeerGroups() {
rp.PeerGroups = map[string][]string{}
}
/*
-> when a workflow should book a resource
-> it must be able to book a resource for a particular time
-> the resource should be available for the time
-> we must be able to parameter the resource for the time
-> before bookin'
*/

View File

@@ -1,6 +1,8 @@
package resources
import (
"slices"
"cloud.o-forge.io/core/oc-lib/dbs"
"cloud.o-forge.io/core/oc-lib/logs"
"cloud.o-forge.io/core/oc-lib/models/resources/resource_model"
@@ -14,15 +16,15 @@ type resourceMongoAccessor[T ResourceInterface] struct {
}
// New creates a new instance of the computeMongoAccessor
func New[T ResourceInterface](t tools.DataType, username string, peerID string, groups []string, caller *tools.HTTPCaller, g func() utils.DBObject) *resourceMongoAccessor[T] {
func NewAccessor[T ResourceInterface](t tools.DataType, request *tools.APIRequest, g func() utils.DBObject) *resourceMongoAccessor[T] {
if !slices.Contains([]tools.DataType{tools.COMPUTE_RESOURCE, tools.STORAGE_RESOURCE, tools.PROCESSING_RESOURCE, tools.WORKFLOW_RESOURCE, tools.DATA_RESOURCE}, t) {
return nil
}
return &resourceMongoAccessor[T]{
AbstractAccessor: utils.AbstractAccessor{
ResourceModelAccessor: resource_model.New(),
ResourceModelAccessor: resource_model.NewAccessor(),
Logger: logs.CreateLogger(t.String()), // Create a logger with the data type
Caller: caller,
PeerID: peerID,
User: username, // Set the caller
Groups: groups, // Set the caller
Request: request,
Type: t,
},
generateData: g,
@@ -39,12 +41,14 @@ func (dca *resourceMongoAccessor[T]) DeleteOne(id string) (utils.DBObject, int,
func (dca *resourceMongoAccessor[T]) UpdateOne(set utils.DBObject, id string) (utils.DBObject, int, error) {
set.(T).SetResourceModel(nil)
return utils.GenericUpdateOne(set.(T).Trim(), id, dca, dca.generateData()) // TODO CHANGE
set.(T).Trim()
return utils.GenericUpdateOne(set, id, dca, dca.generateData())
}
func (dca *resourceMongoAccessor[T]) StoreOne(data utils.DBObject) (utils.DBObject, int, error) {
data.(T).SetResourceModel(nil)
return utils.GenericStoreOne(data.(T).Trim(), dca)
data.(T).Trim()
return utils.GenericStoreOne(data, dca)
}
func (dca *resourceMongoAccessor[T]) CopyOne(data utils.DBObject) (utils.DBObject, int, error) {
@@ -53,33 +57,36 @@ func (dca *resourceMongoAccessor[T]) CopyOne(data utils.DBObject) (utils.DBObjec
func (dca *resourceMongoAccessor[T]) LoadOne(id string) (utils.DBObject, int, error) {
return utils.GenericLoadOne[T](id, func(d utils.DBObject) (utils.DBObject, int, error) {
resources, _, err := dca.ResourceModelAccessor.Search(nil, dca.GetType().String())
resources, _, err := dca.ResourceModelAccessor.Search(nil, dca.GetType().String(), false)
if err == nil && len(resources) > 0 {
d.(T).SetResourceModel(resources[0].(*resource_model.ResourceModel))
}
d.(T).SetAllowedInstances(dca.Request)
return d, 200, nil
}, dca)
}
func (wfa *resourceMongoAccessor[T]) LoadAll() ([]utils.ShallowDBObject, int, error) {
resources, _, err := wfa.ResourceModelAccessor.Search(nil, wfa.GetType().String())
func (wfa *resourceMongoAccessor[T]) LoadAll(isDraft bool) ([]utils.ShallowDBObject, int, error) {
resources, _, err := wfa.ResourceModelAccessor.Search(nil, wfa.GetType().String(), isDraft)
return utils.GenericLoadAll[T](func(d utils.DBObject) utils.ShallowDBObject {
if err == nil && len(resources) > 0 {
d.(T).SetResourceModel(resources[0].(*resource_model.ResourceModel))
}
d.(T).SetAllowedInstances(wfa.Request)
return d
}, wfa)
}, isDraft, wfa)
}
func (wfa *resourceMongoAccessor[T]) Search(filters *dbs.Filters, search string) ([]utils.ShallowDBObject, int, error) {
resources, _, err := wfa.ResourceModelAccessor.Search(nil, wfa.GetType().String())
func (wfa *resourceMongoAccessor[T]) Search(filters *dbs.Filters, search string, isDraft bool) ([]utils.ShallowDBObject, int, error) {
resources, _, err := wfa.ResourceModelAccessor.Search(nil, wfa.GetType().String(), false)
return utils.GenericSearch[T](filters, search, wfa.getResourceFilter(search),
func(d utils.DBObject) utils.ShallowDBObject {
if err == nil && len(resources) > 0 {
d.(T).SetResourceModel(resources[0].(*resource_model.ResourceModel))
}
d.(T).SetAllowedInstances(wfa.Request)
return d
}, wfa)
}, isDraft, wfa)
}
func (abs *resourceMongoAccessor[T]) getResourceFilter(search string) *dbs.Filters {

View File

@@ -2,13 +2,9 @@ package resource_model
import (
"encoding/json"
"slices"
"cloud.o-forge.io/core/oc-lib/config"
"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/tools"
"github.com/google/uuid"
)
type WebResource struct {
@@ -16,94 +12,6 @@ type WebResource struct {
Path string `bson:"path,omitempty" json:"path,omitempty"` // Path is the path of the URL
}
/*
* AbstractResource is a struct that represents a resource
* it defines the resource data
*/
type AbstractResource struct {
utils.AbstractObject // AbstractObject contains the basic fields of an object (id, name)
ShortDescription string `json:"short_description,omitempty" bson:"short_description,omitempty" validate:"required"` // ShortDescription is the short description of the resource
Description string `json:"description,omitempty" bson:"description,omitempty"` // Description is the description of the resource
Logo string `json:"logo,omitempty" bson:"logo,omitempty" validate:"required"` // Logo is the logo of the resource
Owner string `json:"owner,omitempty" bson:"owner,omitempty" validate:"required"` // Owner is the owner of the resource
OwnerLogo string `json:"owner_logo,omitempty" bson:"owner_logo,omitempty"` // OwnerLogo is the owner logo of the resource
SourceUrl string `json:"source_url,omitempty" bson:"source_url,omitempty" validate:"required"` // SourceUrl is the source URL of the resource
PeerID string `json:"peer_id,omitempty" bson:"peer_id,omitempty" validate:"required"` // PeerID is the ID of the peer getting this resource
License string `json:"license,omitempty" bson:"license,omitempty"` // License is the license of the resource
ResourceModel *ResourceModel `json:"resource_model,omitempty" bson:"resource_model,omitempty"` // ResourceModel is the model of the resource
AllowedPeersGroup map[string][]string `json:"allowed_peers_group,omitempty" bson:"allowed_peers_group,omitempty"` // AllowedPeersGroup is the group of allowed peers
Price string `json:"price,omitempty" bson:"price,omitempty"` // Price is the price of access to the resource
Currency string `json:"currency,omitempty" bson:"currency,omitempty"` // Currency is the currency of the price
}
func (ao *AbstractResource) GetAccessor(username string, peerID string, groups []string, caller *tools.HTTPCaller) utils.Accessor {
return nil
}
func (abs *AbstractResource) SetResourceModel(model *ResourceModel) {
abs.ResourceModel = model
}
func (abs *AbstractResource) VerifyAuth(username string, peerID string, groups []string) bool {
if grps, ok := abs.AllowedPeersGroup[peerID]; ok || config.GetConfig().Whitelist {
if (ok && slices.Contains(grps, "*")) || (!ok && config.GetConfig().Whitelist) {
return true
}
for _, grp := range grps {
if slices.Contains(groups, grp) {
return true
}
}
}
return abs.AbstractObject.VerifyAuth(username, peerID, groups)
}
/*
* GetModelType returns the type of the model key
*/
func (abs *AbstractResource) GetModelType(cat string, key string) interface{} {
if abs.ResourceModel == nil || abs.ResourceModel.Model == nil {
return nil
}
if _, ok := abs.ResourceModel.Model[key]; !ok {
return nil
}
return abs.ResourceModel.Model[cat][key].Type
}
/*
* GetModelKeys returns the keys of the model
*/
func (abs *AbstractResource) GetModelKeys() []string {
keys := make([]string, 0)
for k := range abs.ResourceModel.Model {
keys = append(keys, k)
}
return keys
}
/*
* GetModelReadOnly returns the readonly of the model key
*/
func (abs *AbstractResource) GetModelReadOnly(cat string, key string) interface{} {
if abs.ResourceModel == nil || abs.ResourceModel.Model == nil {
return nil
}
if _, ok := abs.ResourceModel.Model[key]; !ok {
return nil
}
return abs.ResourceModel.Model[cat][key].ReadOnly
}
func (d *AbstractResource) Trim() *AbstractResource {
if ok, _ := (&peer.Peer{AbstractObject: utils.AbstractObject{UUID: d.PeerID}}).IsMySelf(); !ok {
d.AllowedPeersGroup = map[string][]string{}
}
return d
}
type Model struct {
Type string `json:"type,omitempty" bson:"type,omitempty"` // Type is the type of the model
ReadOnly bool `json:"readonly,omitempty" bson:"readonly,omitempty"` // ReadOnly is the readonly of the model
@@ -115,38 +23,26 @@ type Model struct {
* Warning: This struct is not user available, it is only used by the system
*/
type ResourceModel struct {
UUID string `json:"id,omitempty" bson:"id,omitempty" validate:"required"`
utils.AbstractObject
ResourceType string `json:"resource_type,omitempty" bson:"resource_type,omitempty" validate:"required"`
VarRefs map[string]string `json:"var_refs,omitempty" bson:"var_refs,omitempty"` // VarRefs is the variable references of the model
Model map[string]map[string]Model `json:"model,omitempty" bson:"model,omitempty"`
}
func (ao *ResourceModel) GetID() string {
return ao.UUID
func (d *ResourceModel) StoreDraftDefault() {
d.Name = d.ResourceType + " Resource Model"
d.IsDraft = false
}
func (ao *ResourceModel) UpToDate(user string, create bool) {}
func (r *ResourceModel) GenerateID() {
r.UUID = uuid.New().String()
}
func (d *ResourceModel) GetName() string {
return d.UUID
}
func (abs *ResourceModel) VerifyAuth(username string, peerID string, groups []string) bool {
func (abs *ResourceModel) VerifyAuth(request *tools.APIRequest) bool {
return true
}
func (d *ResourceModel) GetAccessor(username string, peerID string, groups []string, caller *tools.HTTPCaller) utils.Accessor {
func (d *ResourceModel) GetAccessor(request *tools.APIRequest) utils.Accessor {
return &ResourceModelMongoAccessor{
utils.AbstractAccessor{
Type: tools.RESOURCE_MODEL,
PeerID: peerID,
Groups: groups,
User: username,
Caller: caller,
Type: tools.RESOURCE_MODEL,
Request: request,
},
}
}

View File

@@ -15,7 +15,7 @@ type ResourceModelMongoAccessor struct {
* Nothing special here, just the basic CRUD operations
*/
func New() *ResourceModelMongoAccessor {
func NewAccessor() *ResourceModelMongoAccessor {
return &ResourceModelMongoAccessor{
utils.AbstractAccessor{
Type: tools.RESOURCE_MODEL,
@@ -46,17 +46,17 @@ func (a *ResourceModelMongoAccessor) LoadOne(id string) (utils.DBObject, int, er
}, a)
}
func (a *ResourceModelMongoAccessor) LoadAll() ([]utils.ShallowDBObject, int, error) {
func (a *ResourceModelMongoAccessor) LoadAll(isDraft bool) ([]utils.ShallowDBObject, int, error) {
return utils.GenericLoadAll[*ResourceModel](func(d utils.DBObject) utils.ShallowDBObject {
return d
}, a)
}, isDraft, a)
}
func (a *ResourceModelMongoAccessor) Search(filters *dbs.Filters, search string) ([]utils.ShallowDBObject, int, error) {
func (a *ResourceModelMongoAccessor) Search(filters *dbs.Filters, search string, isDraft bool) ([]utils.ShallowDBObject, int, error) {
return utils.GenericSearch[*ResourceModel](filters, search,
&dbs.Filters{
Or: map[string][]dbs.Filter{
"resource_type": {{Operator: dbs.LIKE.String(), Value: search}},
},
}, func(d utils.DBObject) utils.ShallowDBObject { return d }, a)
}, func(d utils.DBObject) utils.ShallowDBObject { return d }, isDraft, a)
}

View File

@@ -1,61 +1,138 @@
package resources
import (
"cloud.o-forge.io/core/oc-lib/models/resources/resource_model"
"errors"
"time"
"cloud.o-forge.io/core/oc-lib/models/common"
"cloud.o-forge.io/core/oc-lib/models/common/pricing"
"cloud.o-forge.io/core/oc-lib/models/utils"
"cloud.o-forge.io/core/oc-lib/tools"
)
type StorageSize int
// StorageType - Enum that defines the type of storage
const (
GB StorageSize = iota
MB
KB
)
var argoType = [...]string{
"Gi",
"Mi",
"Ki",
}
// New creates a new instance of the StorageResource struct
func (dma StorageSize) ToArgo() string {
return argoType[dma]
}
// enum of a data type
type StorageType int
const (
FILE = iota
STREAM
API
DATABASE
S3
MEMORY
HARDWARE
)
/*
* StorageResource is a struct that represents a storage resource
* it defines the resource storage
*/
type StorageResource struct {
resource_model.AbstractResource // AbstractResource contains the basic fields of an object (id, name)
resource_model.WebResource
Type StorageType `bson:"type,omitempty" json:"type,omitempty"` // Type is the type of the storage
Acronym string `bson:"acronym,omitempty" json:"acronym,omitempty"` // Acronym is the acronym of the storage
SizeType StorageSize `bson:"size_type" json:"size_type" default:"0"` // SizeType is the type of the storage size
Size uint `bson:"size,omitempty" json:"size,omitempty"` // Size is the size of the storage
Local bool `bson:"local" json:"local"` // Local is a flag that indicates if the storage is local
Encryption bool `bson:"encryption,omitempty" json:"encryption,omitempty"` // Encryption is a flag that indicates if the storage is encrypted
Redundancy string `bson:"redundancy,omitempty" json:"redundancy,omitempty"` // Redundancy is the redundancy of the storage
Throughput string `bson:"throughput,omitempty" json:"throughput,omitempty"` // Throughput is the throughput of the storage
AbstractResource[*StorageResourceInstance] // AbstractResource contains the basic fields of an object (id, name)
Type common.StorageType `bson:"type,omitempty" json:"type,omitempty"` // Type is the type of the storage
Acronym string `bson:"acronym,omitempty" json:"acronym,omitempty"` // Acronym is the acronym of the storage
}
func (d *StorageResource) GetAccessor(username string, peerID string, groups []string, caller *tools.HTTPCaller) utils.Accessor {
return New[*StorageResource](tools.STORAGE_RESOURCE, username, peerID, groups, caller, func() utils.DBObject { return &StorageResource{} }) // Create a new instance of the accessor
func (d *StorageResource) GetAccessor(request *tools.APIRequest) utils.Accessor {
return NewAccessor[*StorageResource](tools.STORAGE_RESOURCE, request, func() utils.DBObject { return &StorageResource{} }) // Create a new instance of the accessor
}
type StorageResourceInstance struct {
ResourceInstance[*StorageResourcePartnership]
Local bool `bson:"local" json:"local"`
SecurityLevel string `bson:"security_level,omitempty" json:"security_level,omitempty"`
SizeType common.StorageSize `bson:"size_type" json:"size_type" default:"0"` // SizeType is the type of the storage size
SizeGB uint `bson:"size,omitempty" json:"size,omitempty"` // Size is the size of the storage
Encryption bool `bson:"encryption,omitempty" json:"encryption,omitempty"` // Encryption is a flag that indicates if the storage is encrypted
Redundancy string `bson:"redundancy,omitempty" json:"redundancy,omitempty"` // Redundancy is the redundancy of the storage
Throughput string `bson:"throughput,omitempty" json:"throughput,omitempty"` // Throughput is the throughput of the storage
}
func (i *StorageResourceInstance) GetID() string {
return i.UUID
}
type StorageResourcePartnership struct {
ResourcePartnerShip[*StorageResourcePricingProfile]
MaxSizeGBAllowed float64 `json:"allowed_gb,omitempty" bson:"allowed_gb,omitempty"`
OnlyEncryptedAllowed bool `json:"personal_data_allowed,omitempty" bson:"personal_data_allowed,omitempty"`
}
type PrivilegeStoragePricingStrategy int
const (
BASIC_STORAGE PrivilegeStoragePricingStrategy = iota
GARANTED_ON_DELAY_STORAGE
GARANTED_STORAGE
)
func (t PrivilegeStoragePricingStrategy) String() string {
return [...]string{"BASIC_STORAGE", "GARANTED_ON_DELAY_STORAGE", "GARANTED_STORAGE"}[t]
}
type StorageResourcePricingStrategy int
const (
PER_DATA_STORED StorageResourcePricingStrategy = iota
PER_TB_STORED
PER_GB_STORED
PER_MB_STORED
PER_KB_STORED
)
func (t StorageResourcePricingStrategy) GetStrategy() string {
return [...]string{"PER_DATA_STORED", "PER_GB_STORED", "PER_MB_STORED", "PER_KB_STORED"}[t]
}
func (t StorageResourcePricingStrategy) GetStrategyValue() int {
return int(t)
}
func ToStorageResourcePricingStrategy(i int) StorageResourcePricingStrategy {
return StorageResourcePricingStrategy(i)
}
func (t StorageResourcePricingStrategy) GetQuantity(amountOfDataGB float64) (float64, error) {
switch t {
case PER_DATA_STORED:
return amountOfDataGB, nil
case PER_TB_STORED:
return amountOfDataGB * 1000, nil
case PER_GB_STORED:
return amountOfDataGB, nil
case PER_MB_STORED:
return (amountOfDataGB * 1000), nil
case PER_KB_STORED:
return amountOfDataGB * 1000000, nil
}
return 0, errors.New("Pricing strategy not found")
}
type StorageResourcePricingProfile struct {
Pricing *pricing.PricingStrategy[StorageResourcePricingStrategy] `json:"cpus_prices,omitempty" bson:"cpus_prices,omitempty"` // CPUsPrices is the prices of the CPUs
pricing.ExploitPricingProfile // ExploitPricingProfile is the pricing profile of a storage it means that we exploit the resource for an amount of continuous time
}
func (p *StorageResourcePricingProfile) IsBuying() bool {
return p.Pricing.BuyingStrategy != pricing.PAY_PER_USE
}
func (p *StorageResourcePricingProfile) GetPrice(amountOfData float64, val float64, start time.Time, end time.Time, request *tools.APIRequest, params ...string) (float64, error) {
return p.Pricing.GetPrice(amountOfData, val, start, &end)
}
type CustomizedStorageResource struct {
AbstractCustomizedResource[*StorageResourceInstance]
StorageGB float64 `json:"storage_gb,omitempty" bson:"storage_gb,omitempty"`
}
func (r *CustomizedStorageResource) GetType() tools.DataType {
return tools.STORAGE_RESOURCE
}
func (r *CustomizedStorageResource) GetPrice(request *tools.APIRequest) (float64, error) {
if r.UsageStart == nil || r.UsageEnd == nil {
return 0, errors.New("Usage start and end must be set")
}
partner := r.GetPartnership(request)
if partner != nil && partner.GetPricing(r.SelectedPricing) != nil {
return 0, errors.New("Pricing strategy not found")
}
pricing := partner.GetPricing(r.SelectedPricing)
var err error
amountOfData := float64(1)
if pricing.GetOverrideStrategyValue() >= 0 {
amountOfData, err = ToStorageResourcePricingStrategy(pricing.GetOverrideStrategyValue()).GetQuantity(r.StorageGB)
if err != nil {
return 0, err
}
}
return pricing.GetPrice(amountOfData, r.ExplicitBookingDurationS, *r.UsageStart, *r.UsageEnd, request)
}

View File

@@ -1,18 +1,96 @@
package resources
import (
"cloud.o-forge.io/core/oc-lib/models/resources/resource_model"
"time"
"cloud.o-forge.io/core/oc-lib/models/common/pricing"
"cloud.o-forge.io/core/oc-lib/models/utils"
"cloud.o-forge.io/core/oc-lib/tools"
)
// WorkflowResource is a struct that represents a workflow resource
// it defines the resource workflow
type WorkflowResource struct {
resource_model.AbstractResource
// COMPLEX SHOULD BE REFACTORED
// we don't have any information about the accessor
type abstractWorkflowResource struct {
ExploitedResourceSet
WorkflowID string `bson:"workflow_id,omitempty" json:"workflow_id,omitempty"` // WorkflowID is the ID of the native workflow
}
func (d *WorkflowResource) GetAccessor(username string, peerID string, groups []string, caller *tools.HTTPCaller) utils.Accessor {
return New[*WorkflowResource](tools.WORKFLOW_RESOURCE, username, peerID, groups, caller, func() utils.DBObject { return &WorkflowResource{} }) // Create a new instance of the accessor
// WorkflowResource is a struct that represents a workflow resource
// it defines the resource workflow
type WorkflowResource struct {
AbstractResource[*ResourceInstance[*ResourcePartnerShip[*WorkflowResourcePricingProfile]]]
abstractWorkflowResource
}
type CustomizedWorkflowResource struct {
AbstractCustomizedResource[*ResourceInstance[*ResourcePartnerShip[*WorkflowResourcePricingProfile]]]
abstractWorkflowResource
}
func (r *CustomizedWorkflowResource) GetType() tools.DataType {
return tools.WORKFLOW_RESOURCE
}
func (d *WorkflowResource) GetAccessor(request *tools.APIRequest) utils.Accessor {
return NewAccessor[*WorkflowResource](tools.WORKFLOW_RESOURCE, request, func() utils.DBObject { return &WorkflowResource{} }) // Create a new instance of the accessor
}
type WorkflowResourcePricingProfile struct {
ExploitedResourceSet
pricing.ExploitPricingProfile // ExploitPricingProfile is the pricing profile of a workflow it means that we exploit the resource for an amount of continuous time
}
func (p *WorkflowResourcePricingProfile) IsBuying() bool {
return false
}
/*
* Missing wich Instance is selected
*
*/
func (p *WorkflowResourcePricingProfile) GetPrice(amountOfData float64, val float64, start time.Time, end time.Time, request *tools.APIRequest, params ...string) (float64, error) {
// load workflow
price := float64(0)
pp, err := getPrice[*CustomizedDataResource](p.DataResources, amountOfData, val, start, end, request, params...)
if err != nil {
return 0, err
}
price += pp
pp, err = getPrice[*CustomizedStorageResource](p.StorageResources, amountOfData, val, start, end, request, params...)
if err != nil {
return 0, err
}
price += pp
pp, err = getPrice[*CustomizedProcessingResource](p.ProcessingResources, amountOfData, val, start, end, request, params...)
if err != nil {
return 0, err
}
price += pp
pp, err = getPrice[*CustomizedComputeResource](p.ComputeResources, amountOfData, val, start, end, request, params...)
if err != nil {
return 0, err
}
price += pp
pp, err = getPrice[*CustomizedWorkflowResource](p.WorkflowResources, amountOfData, val, start, end, request, params...)
if err != nil {
return 0, err
}
price += pp
return price, nil
}
func getPrice[T ShallowResourceInterface](arr []T, amountOfData float64, val float64, start time.Time, end time.Time, request *tools.APIRequest, params ...string) (float64, error) {
// load workflow
price := float64(0)
for _, data := range arr {
partner := data.GetPartnership(request)
pricing := partner.GetPricing(data.GetPricingID())
pp, err := pricing.GetPrice(amountOfData, val, start, end, request)
if err != nil {
return 0, err
}
price += pp
}
return price, nil
}