New class link to handle links between components and possible errors
This commit is contained in:
parent
99278ff7ef
commit
d91afc2acd
@ -2,7 +2,7 @@
|
||||
|
||||
- [ ] In most of the components from 'models/' we have a method to add input and output to the model, however this linking of components is already done in oc-schedule when parsing the MxGraph. We need to determine if adding relations between components inside the objects themself is necessary.
|
||||
- When running in debug mode with a breakpoint inside the first line of computing.addLink it is only called once
|
||||
- [ ]
|
||||
|
||||
|
||||
## MxGraph
|
||||
|
||||
@ -10,3 +10,4 @@
|
||||
- mxcell are put inside an <object> tag when the settings have been opened, wether values have been set or not. Maybe we could find a way to make mxgraph add these whenever we add a component to the graph.
|
||||
- then identify the links only
|
||||
- [ ] It is unclear what are the inputs and the ouputs. It seems like they were implemented to link two components, but it seems redundant with the identification of links
|
||||
- This has been potentially tackled with the creation of a class to handle links between components. The components do no handle their own connections with other components, this task is delegated to the Link and Worlflow classes.
|
25
docs/linking_errors.md
Normal file
25
docs/linking_errors.md
Normal file
@ -0,0 +1,25 @@
|
||||
# Handling errors during workflows' post
|
||||
|
||||
Every time an user modify a worflow through oc-search's GUI its representation is sent through an XML to oc-catalog API.
|
||||
|
||||
To ensure a correct execution of the workflow we must look for irregularities in the workflow's design. Of course an error can be the result of a workflow being currently built by the user, with the corrective action coming.
|
||||
|
||||
This document aims at laying down all the requirements that each component must respect and show wether they have been implemented in the code or not.
|
||||
|
||||
## Computing
|
||||
|
||||
- [x] A computing component must be paired with a datacenter component
|
||||
|
||||
## Data
|
||||
|
||||
- [x] A data component must be linked to at least one computing component
|
||||
|
||||
## Datacenter
|
||||
|
||||
- [x] A datacenter component must be linked to at least one computing component
|
||||
|
||||
## Storage
|
||||
|
||||
- [x] A storage component must have at least one target or be the source of another component
|
||||
|
||||
|
@ -14,17 +14,38 @@ type Link struct {
|
||||
// Use ResourceObject parameter to process certain components type differently
|
||||
// and Id's to identify each component as a node in an oriented graph
|
||||
|
||||
func NewLink(src ResourceObject, srcId string, dst ResourceObject, dstId string) (link Link) {
|
||||
// In the case of DCLink we choose to always consider the DC as the destination
|
||||
// in order to facilitate some logic
|
||||
|
||||
func NewLink(src ResourceObject, srcId string, dst ResourceObject, dstId string) (link Link) {
|
||||
link.Source = srcId
|
||||
link.Destination = dstId
|
||||
|
||||
// If the link is between a DC and a component make sure that the DC is destination
|
||||
// and if the component is computing, update the DataCenterID
|
||||
|
||||
if (src.getRtype() == rtype.DATACENTER || dst.getRtype() == rtype.DATACENTER){
|
||||
var linked ResourceObject
|
||||
|
||||
link.DCLink = true
|
||||
|
||||
if src.getRtype() == rtype.DATACENTER {
|
||||
linked = dst
|
||||
} else {
|
||||
linked = src
|
||||
}
|
||||
|
||||
if( link.DCLink && src.getRtype() == rtype.DATACENTER){
|
||||
link.Destination = srcId
|
||||
link.Source = dstId
|
||||
}
|
||||
|
||||
if (linked.getRtype() == rtype.COMPUTING){
|
||||
linked.(*ComputingObject).DataCenterID = link.Destination
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
|
@ -41,15 +41,35 @@ type MxObject struct {
|
||||
|
||||
// Didn't manage to differentiate Links and cells containing components using
|
||||
// only structures and unmarshal, so we use this method post-umarshalling
|
||||
func (g *MxGraphModel) createLinks() error {
|
||||
func (g *MxGraphModel) createLinks() {
|
||||
var cells_without_links []MxCell
|
||||
|
||||
|
||||
for i, mxcell := range g.Root.MxCell {
|
||||
|
||||
if mxcell.Edge != nil {
|
||||
mxcell.processLinks()
|
||||
newLink := MxLink{mxcell.ID,*mxcell.Source,*mxcell.Target}
|
||||
g.Root.MxLink = append(g.Root.MxLink,newLink)
|
||||
g.Root.MxCell = append(g.Root.MxCell[:i],g.Root.MxCell[i+1:]...)
|
||||
} else {
|
||||
cells_without_links = append(cells_without_links,g.Root.MxCell[i])
|
||||
}
|
||||
}
|
||||
return nil
|
||||
|
||||
|
||||
g.Root.MxCell = nil
|
||||
g.Root.MxCell = cells_without_links
|
||||
|
||||
}
|
||||
|
||||
func (cell *MxCell) processLinks() {
|
||||
v := ""
|
||||
if cell.Source == nil {
|
||||
cell.Source = &v
|
||||
}
|
||||
if cell.Target == nil {
|
||||
cell.Target = &v
|
||||
}
|
||||
}
|
||||
|
||||
type mxissue struct {
|
||||
|
@ -546,11 +546,7 @@ func ParseMxGraph(username, workflowName, xmlData string) (err error, mxissues [
|
||||
return err, nil
|
||||
}
|
||||
|
||||
err = xmlModel.createLinks()
|
||||
if err != nil {
|
||||
logs.Alert("Error creating links")
|
||||
return err, nil
|
||||
}
|
||||
xmlModel.createLinks()
|
||||
|
||||
|
||||
targetWorkspaceWorkflow, err, mxissues := userWorkspace.ConsumeMxGraphModel(xmlModel)
|
||||
@ -652,6 +648,23 @@ func (ws Workspace) ConsumeMxGraphModel(xmlmodel MxGraphModel) (returned_wf *Wor
|
||||
// issues = append(issues, errors.New("MxCell with ID "+cell.ID+" doesn't have a valid link"))
|
||||
|
||||
default:
|
||||
// Not root nor resource. Should be only links
|
||||
sourceObj := returned_wf.GetResource(cell.Source)
|
||||
targetObj := returned_wf.GetResource(cell.Target)
|
||||
|
||||
if sourceObj == nil || targetObj == nil {
|
||||
if sourceObj == nil && targetObj == nil {
|
||||
issues = append(issues, errors.New("Arrow "+cell.ID+" is alone"))
|
||||
} else if sourceObj == nil {
|
||||
issues = append(issues, errors.New("Arrow ("+cell.ID+") to "+*targetObj.getName()+" without parent"))
|
||||
} else {
|
||||
issues = append(issues, errors.New("Arrow "+cell.ID+" from "+*sourceObj.getName()+" without target"))
|
||||
}
|
||||
|
||||
// If is a invalid link, we can't save it in the DB
|
||||
continue
|
||||
}
|
||||
|
||||
// Not root nor resource. Should be only links
|
||||
// If is a invalid link, we can't save it in the DB
|
||||
// We should always get a ID because we already registered resources and discarded which doesn't correspond to existent models
|
||||
@ -665,58 +678,59 @@ func (ws Workspace) ConsumeMxGraphModel(xmlmodel MxGraphModel) (returned_wf *Wor
|
||||
}
|
||||
}
|
||||
|
||||
issues = ws.CreateLinks(returned_wf,xmlmodel.Root.MxLink, issues)
|
||||
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)
|
||||
// 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
|
||||
// // 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 _, 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 _, 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 _, 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 atached to any Computing"))
|
||||
}
|
||||
}
|
||||
// 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 atached 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"))
|
||||
// }
|
||||
// }
|
||||
|
||||
//////////////////////////////////////////////////////////
|
||||
// //
|
||||
@ -819,57 +833,116 @@ func (ws Workspace) ConsumeMxGraphModel(xmlmodel MxGraphModel) (returned_wf *Wor
|
||||
return
|
||||
}
|
||||
|
||||
func (w Workspace) CreateLinks(returned_wf *Workflow, links []MxLink, issues []error) []error {
|
||||
func (w *Workflow) CreateLinks(links []MxLink, issues []error) []error {
|
||||
|
||||
for _, link := range links {
|
||||
sourceObj := returned_wf.GetResource(&link.Source)
|
||||
targetObj := returned_wf.GetResource(&link.Target)
|
||||
|
||||
link_object := NewLink(sourceObj,link.Source, targetObj, link.Target)
|
||||
returned_wf.AddLinkToWorkflow(link_object,link.ID)
|
||||
|
||||
if sourceObj == nil || targetObj == nil {
|
||||
if sourceObj == nil && targetObj == nil {
|
||||
issues = append(issues, errors.New("Arrow "+link.ID +" is alone"))
|
||||
} else if sourceObj == nil {
|
||||
issues = append(issues, errors.New("Arrow ("+link.ID+") to "+*targetObj.getName()+" without parent"))
|
||||
} else {
|
||||
issues = append(issues, errors.New("Arrow "+link.ID+" from "+*sourceObj.getName()+" without target"))
|
||||
}
|
||||
|
||||
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)
|
||||
} else {
|
||||
issues = append(issues, w.processLinkErrors(link))
|
||||
}
|
||||
|
||||
|
||||
// if sourceObj.getRtype() == rtype.DATACENTER || targetObj.getRtype() == rtype.DATACENTER {
|
||||
// var datacenter, datacenterLinked *string
|
||||
|
||||
// if sourceObj.getRtype() == rtype.DATACENTER {
|
||||
// datacenter = &link.Source
|
||||
// datacenterLinked = &link.Target
|
||||
// } else {
|
||||
// datacenter = &link.Target
|
||||
// datacenterLinked = &link.Source
|
||||
// }
|
||||
|
||||
// switch returned_wf.GetResource(datacenterLinked).getRtype() {
|
||||
// case rtype.COMPUTING:
|
||||
// computingObj := returned_wf.GetResource(datacenterLinked).(*ComputingObject)
|
||||
|
||||
// computingObj.DataCenterID = *datacenter
|
||||
// returned_wf.UpdateObj(computingObj, *datacenterLinked)
|
||||
// }
|
||||
|
||||
// } else {
|
||||
// targetObj.addLink(INPUT, *link.Source)
|
||||
// returned_wf.UpdateObj(targetObj, *link.Target)
|
||||
|
||||
// sourceObj.addLink(OUTPUT, *link.Target)
|
||||
// returned_wf.UpdateObj(sourceObj, *link.Source)
|
||||
// }
|
||||
}
|
||||
return issues
|
||||
}
|
||||
|
||||
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{
|
||||
targetObj := w.GetResource(&link.Target)
|
||||
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")
|
||||
}
|
||||
|
||||
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)){
|
||||
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)){
|
||||
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)){
|
||||
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)){
|
||||
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 != ""){
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
func (w *Workflow) IsComponentDst(id string) bool {
|
||||
|
||||
for _, link := range w.Links{
|
||||
if(link.Destination == id && link.Source != ""){
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
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))){
|
||||
return true
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
func (w *Workflow) HasLinkageToDC(id string) bool {
|
||||
|
||||
for _, link := range w.Links{
|
||||
if(link.Source == id && link.DCLink){
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
func sumExecutionReqs(exeqReq ...ExecutionRequirementsModel) (ret ExecutionRequirementsModel) {
|
||||
for _, v := range exeqReq {
|
||||
ret.CPUs += v.CPUs
|
||||
|
Loading…
Reference in New Issue
Block a user