From a1f04dc2f0f4499fdb7ab4b0780386d3b747e619 Mon Sep 17 00:00:00 2001 From: pb Date: Thu, 25 Apr 2024 15:48:32 +0200 Subject: [PATCH] bugfix empty workflow --- models/workflow.go | 197 +++++++++++++++++---------------------------- scripts/demo.json | 37 +++++---- 2 files changed, 94 insertions(+), 140 deletions(-) diff --git a/models/workflow.go b/models/workflow.go index c0aff7f..d7b275b 100644 --- a/models/workflow.go +++ b/models/workflow.go @@ -52,11 +52,11 @@ const SchedulesDB = "schedules" type Workflow struct { // The key of the map is the ID of the object itself - Data map[string]DataObject `json:"data"` - Computing map[string]ComputingObject `json:"computing"` - Storage map[string]StorageObject `json:"storage"` - Datacenter map[string]DatacenterObject `json:"datacenter"` //TODO: Decide if there should be multiple objects of a datacenter - Links map[string]Link `json:"link"` + Data map[string]DataObject `json:"data"` + Computing map[string]ComputingObject `json:"computing"` + Storage map[string]StorageObject `json:"storage"` + Datacenter map[string]DatacenterObject `json:"datacenter"` //TODO: Decide if there should be multiple objects of a datacenter + Links map[string]Link `json:"link"` Schedules WorkflowSchedule `json:"schedules"` @@ -78,7 +78,7 @@ type ResourceObject interface { // This type allows to process computing and storage component // which can get input from the user -type EditableResourceObject interface{ +type EditableResourceObject interface { ResourceObject addUserInput(map[string]interface{}) } @@ -119,13 +119,13 @@ func (w *Workflow) GetResource(rObjID *string) (retObj ResourceObject) { return nil } - if storVal, ok := w.Data[*rObjID]; ok { - retObj = &storVal + if datVal, ok := w.Data[*rObjID]; ok { + retObj = &datVal return } - if storVal, ok := w.Computing[*rObjID]; ok { - retObj = &storVal + if compVal, ok := w.Computing[*rObjID]; ok { + retObj = &compVal return } @@ -134,8 +134,8 @@ func (w *Workflow) GetResource(rObjID *string) (retObj ResourceObject) { return } - if storVal, ok := w.Datacenter[*rObjID]; ok { - retObj = &storVal + if dcVal, ok := w.Datacenter[*rObjID]; ok { + retObj = &dcVal return } @@ -182,7 +182,7 @@ func (w *Workflow) AddObj(robj ResourceObject) *primitive.ObjectID { return &outputID } -func (w *Workflow) AddLinkToWorkflow (link Link, id string){ +func (w *Workflow) AddLinkToWorkflow(link Link, id string) { if w.Links == nil { w.Links = make(map[string]Link) } @@ -547,7 +547,6 @@ func ParseMxGraph(username, workflowName, xmlData string) (err error, mxissues [ } xmlModel.createLinks() - targetWorkspaceWorkflow, err, mxissues := userWorkspace.ConsumeMxGraphModel(xmlModel) if err != nil { @@ -605,37 +604,38 @@ func (ws Workspace) ConsumeMxGraphModel(xmlmodel MxGraphModel) (returned_wf *Wor return xmlmodel.Root.MxCell[i].RID != nil }) - // Create the object and add it to the appropriate list + // Create the object and add it to the appropriate list // for all the components with setting, which are identified // by a MxObject tag in the xml - for _, object := range *xmlmodel.Root.MxObject{ - - resObj, err, mxissues := returned_wf.mxCellToComponent(object.MxCell,ws) - if err != nil { - issues = append(issues, mxissues...) - } - - // add the component to the worflow's attribute that stores - // all components in a map[string]Component where the key - // is the component's ID in the mxGraph and the value the Component object - returned_wf.UpdateObj(resObj,object.ID) - - // Construct the object corresponding to the componant's type and use its addUserInput method - if(resObj.getRtype() == rtype.COMPUTING){ - comp_obj := returned_wf.GetResource(&object.ID).(*ComputingObject) - comp_obj.AddUserInput(object.Settings) - returned_wf.UpdateObj(comp_obj,object.ID) - } - // if(resObj.getRtype() == rtype.DATA){ - // } - - } + if ok := xmlmodel.Root.MxObject != nil; ok { + for _, object := range *xmlmodel.Root.MxObject { + resObj, err, mxissues := returned_wf.mxCellToComponent(object.MxCell, ws) + if err != nil { + issues = append(issues, mxissues...) + } + + // add the component to the worflow's attribute that stores + // all components in a map[string]Component where the key + // is the component's ID in the mxGraph and the value the Component object + returned_wf.UpdateObj(resObj, object.ID) + + // Construct the object corresponding to the componant's type and use its addUserInput method + if resObj.getRtype() == rtype.COMPUTING { + comp_obj := returned_wf.GetResource(&object.ID).(*ComputingObject) + comp_obj.AddUserInput(object.Settings) + returned_wf.UpdateObj(comp_obj, object.ID) + } + // if(resObj.getRtype() == rtype.DATA){ + // } + } + } + for _, cell := range xmlmodel.Root.MxCell { switch { case cell.RID != nil: - resObj, err, mxissues := returned_wf.mxCellToComponent(cell,ws) + resObj, err, mxissues := returned_wf.mxCellToComponent(cell, ws) if err != nil { issues = append(issues, mxissues...) } @@ -674,63 +674,13 @@ func (ws Workspace) ConsumeMxGraphModel(xmlmodel MxGraphModel) (returned_wf *Wor // // The Source will be in the INPUTs of the Target. // But we also must make sure that the Target will be in the OUTPUTs of the Source - + } } issues = returned_wf.CreateLinks(xmlmodel.Root.MxLink, issues) issues = returned_wf.CheckLinks(issues) - // dcslist := make(map[string]bool) - // dataslist := make(map[string]bool) - // // datalist := make(map[string]bool) - - - // // Test wether the computing components are linked with a DC - // for _, comp := range returned_wf.Computing { - // if comp.DataCenterID == "" { - // issues = append(issues, errors.New("Computing "+*comp.getName()+" without a Datacenter")) - // } else { - // // If doesn't exist in the list, means is new element to register as used - // dcslist[comp.DataCenterID] = true - - // } - - // for _, dcin := range comp.Inputs { - // switch returned_wf.GetResource(&dcin).getRtype() { - // case rtype.DATA: - // dataslist[dcin] = true - // } - // } - - // for _, dcout := range comp.Outputs { - // switch returned_wf.GetResource(&dcout).getRtype() { - // case rtype.DATA: - // dataslist[dcout] = true - // } - // } - - // } - - // for _, storage_component := range returned_wf.Storage { - // if storage_component.Inputs == nil && storage_component.Outputs == nil { - // issues = append(issues, errors.New("Storage "+*storage_component.getName()+" without compatible inputs and outputs")) - // } - // } - - // for dcID, dc_component := range returned_wf.Datacenter { - // // if rID doesn't exist in the list, it means that it's not used - // if _, ok := dcslist[dcID]; !ok { - // issues = append(issues, errors.New("DC "+*dc_component.getName()+" not attached to any Computing")) - // } - // } - - // for dcID, data_component := range returned_wf.Data { - // // if rID doesn't exist in the list, it means that it's not used - // if _, ok := dataslist[dcID]; !ok { - // issues = append(issues, errors.New("Data "+*data_component.getName()+" not attached to any Computing")) - // } - // } ////////////////////////////////////////////////////////// // // @@ -834,13 +784,13 @@ func (ws Workspace) ConsumeMxGraphModel(xmlmodel MxGraphModel) (returned_wf *Wor } func (w *Workflow) CreateLinks(links []MxLink, issues []error) []error { - - for _, link := range links { - if (len(link.Source) > 0 && len(link.Target) > 0){ + + for _, link := range links { + if len(link.Source) > 0 && len(link.Target) > 0 { sourceObj := w.GetResource(&link.Source) targetObj := w.GetResource(&link.Target) - link_object := NewLink(sourceObj,link.Source, targetObj, link.Target) - w.AddLinkToWorkflow(link_object,link.ID) + link_object := NewLink(sourceObj, link.Source, targetObj, link.Target) + w.AddLinkToWorkflow(link_object, link.ID) } else { issues = append(issues, w.processLinkErrors(link)) } @@ -849,59 +799,58 @@ func (w *Workflow) CreateLinks(links []MxLink, issues []error) []error { } func (w *Workflow) processLinkErrors(link MxLink) (issue error) { - if len(link.Source) == 0 && len(link.Target) == 0 { - issue = errors.New("Arrow "+link.ID+" is alone") - } else if len(link.Source) == 0{ + if len(link.Source) == 0 && len(link.Target) == 0 { + issue = errors.New("Arrow " + link.ID + " is alone") + } else if len(link.Source) == 0 { targetObj := w.GetResource(&link.Target) - issue = errors.New("Arrow ("+link.ID+") to "+*targetObj.getName()+" without parent") + issue = errors.New("Arrow (" + link.ID + ") to " + *targetObj.getName() + " without parent") } else { sourceObj := w.GetResource(&link.Source) - issue = errors.New("Arrow "+link.ID+" from "+*sourceObj.getName()+" without target") + issue = errors.New("Arrow " + link.ID + " from " + *sourceObj.getName() + " without target") } return issue } func (w *Workflow) CheckLinks(issues []error) []error { - + // Check that storage components have a valid link for id, storage := range w.Storage { - if(!w.IsComponentSrc(id) && !w.IsComponentDst(id)){ + if !w.IsComponentSrc(id) && !w.IsComponentDst(id) { issues = append(issues, errors.New("Storage "+*storage.getName()+" without compatible inputs and outputs")) } } - + // Check that data components are linked to a computing component for id, data := range w.Data { - if(!w.HasLinkageToComputing(id)){ + if !w.HasLinkageToComputing(id) { issues = append(issues, errors.New("Data "+*data.getName()+" not attached to any Computing")) } } // Check that DC is linked to a computing component - for id, dc:= range w.Datacenter { - if(!w.HasLinkageToComputing(id)){ + for id, dc := range w.Datacenter { + if !w.HasLinkageToComputing(id) { issues = append(issues, errors.New("Datacenter "+*dc.getName()+" not attached to any Computing")) } } - + // Check that all data computing components are linked to a DC - for id,comp:= range w.Computing { - if(!w.HasLinkageToDC(id)){ + for id, comp := range w.Computing { + if !w.HasLinkageToDC(id) { issues = append(issues, errors.New("Computing "+*comp.getName()+" not attached to any datacenter")) } - } + } return issues } - func (w *Workflow) IsComponentSrc(id string) bool { - - for _, link := range w.Links{ - if(link.Source == id && link.Source != ""){ + + for _, link := range w.Links { + if link.Source == id && link.Source != "" { return true } } @@ -910,9 +859,9 @@ func (w *Workflow) IsComponentSrc(id string) bool { } func (w *Workflow) IsComponentDst(id string) bool { - - for _, link := range w.Links{ - if(link.Destination == id && link.Source != ""){ + + for _, link := range w.Links { + if link.Destination == id && link.Source != "" { return true } } @@ -923,10 +872,10 @@ func (w *Workflow) IsComponentDst(id string) bool { func (w *Workflow) HasLinkageToComputing(id string) bool { for idComputing, _ := range w.Computing { - if( (w.IsComponentSrc(id) && w.IsComponentDst(idComputing)) || (w.IsComponentSrc(idComputing) && w.IsComponentDst(id))){ + if (w.IsComponentSrc(id) && w.IsComponentDst(idComputing)) || (w.IsComponentSrc(idComputing) && w.IsComponentDst(id)) { return true } - + } return false @@ -934,8 +883,8 @@ func (w *Workflow) HasLinkageToComputing(id string) bool { func (w *Workflow) HasLinkageToDC(id string) bool { - for _, link := range w.Links{ - if(link.Source == id && link.DCLink){ + for _, link := range w.Links { + if link.Source == id && link.DCLink { return true } } @@ -1124,15 +1073,15 @@ func CheckAndBookWorkflowSchedule(username, workflowName string, book bool) (myR SchedulesDB: ¤tWorkflow.Schedules}}, ) - if err != nil { + if err != nil { logs.Critical("Internal error when updating in DB: " + err.Error()) } return myRet, nil } -// Not sure if this method handles error propagation well -func (wf Workflow) mxCellToComponent(cell MxCell, ws Workspace) (resObj ResourceObject,err error, issues []error){ +// Not sure if this method handles error propagation well +func (wf Workflow) mxCellToComponent(cell MxCell, ws Workspace) (resObj ResourceObject, err error, issues []error) { rType := ws.getRtype(*cell.RID) if rType == rtype.INVALID { @@ -1151,7 +1100,7 @@ func (wf Workflow) mxCellToComponent(cell MxCell, ws Workspace) (resObj Resource resObj = wf.CreateResourceObject(rType) resObj.setReference(rIDObj) - + return } diff --git a/scripts/demo.json b/scripts/demo.json index d59a324..745d855 100644 --- a/scripts/demo.json +++ b/scripts/demo.json @@ -236,6 +236,27 @@ }, "inputs": [], "outputs": [] + }, + { + "name": "Mosquito server", + "short_description": "open source message broker that implements the MQTT protocol versions 5.0, 3.1.1 and 3.1.", + "logo": "./local_imgs/mosquitto-logo.png", + "description": "A very long description of what this storage is", + "type": "computing", + "owner": "IRT", + "price": 300, + "license": "GPLv2", + "execution_requirements": { + "cpus": 1, + "ram": 1024, + "storage": 300, + "gpus": 1, + "disk_io": "30 MB/s", + "parallel": true, + "scaling_model": 2 + }, + "inputs": [], + "outputs": [] } ] }, @@ -273,22 +294,6 @@ "inputs": [], "outputs": [], "URL" : "" - }, - { - "name": "Mosquito server", - "short_description": "open source message broker that implements the MQTT protocol versions 5.0, 3.1.1 and 3.1.", - "logo": "./local_imgs/mosquitto-logo.png", - "description": "A very long description of what this storage is", - "type": "storage", - "DCacronym": "DC_myDC", - "size": 40000, - "encryption": false, - "redundancy": "RAID5S", - "throughput": "r:300,w:350", - "bookingPrice": 90, - "inputs": [], - "outputs": [], - "URL" : "" } ] },