Skip to content
GitLab
Explore
Sign in
Primary navigation
Search or go to…
Project
N
Nephele-Dashboard
Manage
Activity
Members
Labels
Plan
Issues
0
Issue boards
Milestones
Iterations
Wiki
Requirements
Code
Merge requests
0
Repository
Branches
Commits
Tags
Repository graph
Compare revisions
Snippets
Locked files
Build
Pipelines
Jobs
Pipeline schedules
Test cases
Artifacts
Deploy
Releases
Model registry
Operate
Environments
Monitor
Incidents
Analyze
Value stream analytics
Contributor analytics
CI/CD analytics
Repository analytics
Code review analytics
Issue analytics
Insights
Model experiments
Help
Help
Support
GitLab documentation
Compare GitLab plans
Community forum
Contribute to GitLab
Provide feedback
Terms and privacy
Keyboard shortcuts
?
Snippets
Groups
Projects
Show more breadcrumbs
Eclipse Research Labs
NEPHELE Project
Nephele-Dashboard
Commits
477bbf5d
Commit
477bbf5d
authored
3 weeks ago
by
Nikos Filinis
Browse files
Options
Downloads
Patches
Plain Diff
Update application graph
parent
b90cc15a
No related branches found
Branches containing commit
No related tags found
No related merge requests found
Pipeline
#65368
passed with stage
in 3 minutes and 13 seconds
Changes
1
Pipelines
1
Hide whitespace changes
Inline
Side-by-side
Showing
1 changed file
frontend/src/pages/ApplicationGraph.vue
+214
-122
214 additions, 122 deletions
frontend/src/pages/ApplicationGraph.vue
with
214 additions
and
122 deletions
frontend/src/pages/ApplicationGraph.vue
+
214
−
122
View file @
477bbf5d
...
...
@@ -3,7 +3,6 @@
<q-page
padding
v-if=
"!$route.meta.isChild"
>
<h6>
Application Graphs
</h6>
<q-btn
label=
"Create New HDAG"
color=
"primary"
@
click=
"showDialog = true"
/>
<div
v-if=
"isLoading"
>
Loading...
</div>
<div
v-if=
"error"
>
{{
error
}}
</div>
<div
v-else
class=
"q-gutter-md"
style=
"display: flex; flex-wrap: wrap"
>
...
...
@@ -27,7 +26,6 @@
</q-btn>
</div>
</div>
<div><strong>
Type:
</strong>
{{
artifact
.
artifactType
}}
</div>
<div><strong>
Name:
</strong>
{{
artifact
.
descriptor
?.
name
}}
</div>
<div><strong>
ID:
</strong>
{{
artifact
.
descriptor
?.
id
}}
</div>
...
...
@@ -43,7 +41,7 @@
dependency
.
ref
.
split
(
'
/
'
).
length
-
1
]
}}
(
{{
dependency
.
descriptor
Type
}}
)
(
{{
dependency
.
artifact
Type
}}
)
</li>
</ul>
</div>
...
...
@@ -54,24 +52,20 @@
</q-card>
</div>
</div>
<q-dialog
v-model=
"showDialog"
>
<q-card
style=
"width: 800px; max-width: 800px"
>
<q-card-section>
<div
class=
"text-h6"
>
Create New HDAG
</div>
</q-card-section>
<q-card-section>
<textarea
style=
"width: 100%"
v-model=
"hdagJson"
class=
"editor-popup"
></textarea>
</q-card-section>
<q-card-section>
<q-btn
label=
"Start"
color=
"primary"
@
click=
"saveHdag"
/>
<q-btn
label=
"Cancel"
color=
"primary"
flat
@
click=
"showDialog = false"
/>
</q-card-section>
</q-card>
</q-dialog>
<q-dialog
v-model=
"showDescriptorDialog"
>
<q-card
style=
"width: 800px; max-width: 800px"
>
<q-card-section
class=
"q-pa-md"
>
...
...
@@ -84,27 +78,31 @@
</q-card-actions>
</q-card>
</q-dialog>
<!-- Edit Intent Dialog -->
<q-dialog
v-model=
"showEditIntentDialog"
>
<q-card
style=
"width: 800px; max-width: 800px"
>
<q-card-section>
<div
class=
"text-h6"
>
Edit Intent
</div>
</q-card-section>
<q-card-section>
<div
style=
"display: flex; gap:20px; align-items: center; padding-bottom: 50px; padding-left: 50px;"
>
<label>
<strong>
Artifact Intent:
</strong></label>
<q-select
style=
"width:150px;"
v-model=
"editedIntent.hdaGraphIntent.type"
:options=
"artifactIntentOptions"
<div
style=
"
display: flex;
gap: 20px;
align-items: center;
padding-bottom: 50px;
padding-left: 50px;
"
>
<label><strong>
Artifact Intent:
</strong></label>
<q-select
style=
"width: 150px"
v-model=
"editedIntent.hdaGraphIntent.type"
:options=
"artifactIntentOptions"
@
update:model-value=
"updateServiceOptions"
/>
</div>
<strong
style=
"padding-left: 50px; padding-bottom: 10px"
>
Services (Deployment Intent):
</strong>
<div
v-for=
"(service, sIndex) in editedIntent.services"
:key=
"sIndex"
class=
"q-mb-md"
style=
"padding-top: 10px; border-top: 1px dashed #ccc"
>
<div
style=
"padding: 10px 0 10px 50px"
>
<strong>
{{
service
.
id
}}
</strong>
</div>
<div
style=
"display: flex; gap: 20px; padding-left: 50px"
>
<!--
<q-separator
/>
-->
<strong
style=
"padding-left: 50px; padding-bottom: 10px;"
>
Services:
</strong>
<div
v-for=
"(service, index) in editedIntent.services"
:key=
"index"
class=
"q-mb-md"
style=
"display: flex; justify-content: space-between; align-items: center; padding-top:10px"
>
<div
style=
"padding-left: 50px;"
><strong>
{{
service
.
id
}}
</strong></div>
<div
style=
"display: flex; gap:20px; padding-right: 50px;"
>
<div>
<label>
CPU:
</label>
<q-select
v-model=
"service.deployment.intent.compute.cpu"
:options=
"cpuOptions"
/>
...
...
@@ -119,7 +117,29 @@
</div>
<div>
<label>
GPU Enabled:
</label>
<q-select
v-model=
"service.deployment.intent.compute.gpu.enabled"
:options=
"booleanOptions"
/>
<q-option-group
color=
"primary"
v-model=
"service.deployment.intent.compute.gpu.enabled"
:options=
"[
{ label: '', value: true }]" type="toggle" inline
:disable="editedIntent.hdaGraphIntent.type !== 'custom'" />
</div>
<div
v-if=
"shouldShowDeviceProximity(sIndex)"
>
<label>
Device Proximity Enabled:
</label>
<q-option-group
color=
"primary"
v-model=
"service.deployment.intent.network.deviceProximity.enabled"
:options=
"[
{ label: '', value: true }]" type="toggle" inline
:disable="editedIntent.hdaGraphIntent.type !== 'custom'" />
</div>
</div>
<div
v-if=
"service.deployment.intent.network.latencies.length > 0"
style=
"padding: 10px 0 0 50px; display: flex; gap: 20px; justify-content: flex-start; align-items: center;"
>
<label>
QoS per Connection Point:
</label>
<div
v-for=
"(cp, cpIndex) in service.deployment.intent.network
.latencies"
:key=
"cpIndex"
style=
"display: flex; gap: 20px; margin-top: 20px;"
>
<div
style=
"width:100px"
>
<strong>
{{
cp
.
connectionPoint
}}
</strong>
<q-select
v-model=
"cp.qos"
:options=
"qosOptions"
style=
"min-width: 150px"
/>
</div>
</div>
</div>
</div>
...
...
@@ -131,8 +151,8 @@
</q-card>
</q-dialog>
</q-page>
<router-view
/>
</div>
<router-view
/>
</
template
>
<
script
setup
>
...
...
@@ -141,6 +161,7 @@ import { useRouter } from 'vue-router';
import
{
QPage
,
QCard
,
QCardSection
,
QDialog
}
from
'
quasar
'
;
import
{
useFetchArtifacts
}
from
'
../hooks/artifacts
'
;
import
{
useFetchDescriptors
}
from
'
../hooks/descriptors
'
;
import
yaml
from
'
js-yaml
'
;
const
router
=
useRouter
();
const
{
isLoading
,
error
,
artifacts
,
fetchArtifacts
}
=
useFetchArtifacts
();
...
...
@@ -150,16 +171,21 @@ const descriptorContent = ref('');
const
showDialog
=
ref
(
false
);
const
showEditIntentDialog
=
ref
(
false
);
const
editedIntent
=
ref
(
null
);
onMounted
(()
=>
{
fetchArtifacts
(
'
HDAG
'
);
});
const
cpuOptions
=
ref
([
'
light
'
,
'
small
'
,
'
medium
'
,
'
large
'
]);
const
ramOptions
=
ref
([
'
light
'
,
'
small
'
,
'
medium
'
,
'
large
'
]);
const
storageOptions
=
[
'
small
'
,
'
medium
'
,
'
large
'
];
const
booleanOptions
=
[
true
,
false
];
const
artifactIntentOptions
=
[
'
security
'
,
'
highAvailability
'
,
'
highPerformance
'
,
'
energyEfficiency
'
];
const
storageOptions
=
ref
([
'
small
'
,
'
medium
'
,
'
large
'
]);
const
qosOptions
=
ref
([
'
best-effort
'
,
'
low
'
,
'
ultralow
'
]);
const
artifactIntentOptions
=
[
'
default
'
,
'
highAvailability
'
,
'
highPerformance
'
,
'
energyEfficiency
'
,
'
custom
'
,
];
const
{
descriptor
,
fetchDescriptor
,
publishHdag
}
=
useFetchDescriptors
();
const
hdagJson
=
ref
(
JSON
.
stringify
(
{
...
...
@@ -183,139 +209,204 @@ const hdagJson = ref(
2
,
),
);
onMounted
(()
=>
{
fetchArtifacts
(
'
HDAG
'
);
});
async
function
showDescriptor
(
id
)
{
console
.
log
(
'
id
'
);
await
fetchDescriptor
(
id
);
var
fileType
=
'
.yaml
'
;
const
selectedDescriptor
=
descriptor
.
value
.
find
((
desc
)
=>
desc
.
fileName
.
endsWith
(
fileType
),
desc
.
fileName
.
endsWith
(
'
.yaml
'
),
);
if
(
d
escriptor
)
{
if
(
selectedD
escriptor
)
{
descriptorTitle
.
value
=
selectedDescriptor
.
fileName
;
descriptorContent
.
value
=
selectedDescriptor
.
content
;
showDescriptorDialog
.
value
=
true
;
}
}
function
editIntent
(
artifact
)
{
// Create a minimal structure for `editedIntent`
editedIntent
.
value
=
{
hdaGraphIntent
:
{
// read whichever of the possible keys is true
type
:
Object
.
keys
(
artifact
.
hdaGraphIntent
||
{}).
find
(
key
=>
artifact
.
hdaGraphIntent
[
key
]?.
enabled
)
||
'
security
'
,
},
services
:
artifact
.
dependencies
.
map
(
service
=>
({
id
:
service
.
ref
.
split
(
'
/
'
).
pop
(),
async
function
editIntent
(
artifact
)
{
if
(
!
artifact
?.
descriptor
?.
id
)
return
;
await
fetchDescriptor
(
artifact
.
descriptor
.
id
);
const
selectedDescriptor
=
descriptor
.
value
.
find
((
desc
)
=>
desc
.
fileName
.
endsWith
(
'
.yaml
'
),
);
if
(
!
selectedDescriptor
)
return
;
const
descJson
=
yaml
.
load
(
selectedDescriptor
.
content
);
const
activeIntentType
=
getActiveIntentType
(
descJson
.
hdaGraph
.
hdaGraphIntent
,
);
const
servicesMapped
=
descJson
.
hdaGraph
.
services
?.
map
((
svc
)
=>
{
const
netIntent
=
svc
.
deployment
?.
intent
?.
network
||
{};
const
computeIntent
=
svc
.
deployment
?.
intent
?.
compute
||
{};
let
cpArray
=
netIntent
.
latencies
||
[];
if
(
netIntent
.
latencies
&&
netIntent
.
latencies
.
length
>
0
)
{
cpArray
=
netIntent
.
latencies
.
map
((
latency
)
=>
({
connectionPoint
:
latency
.
connectionPoint
,
qos
:
latency
.
qos
||
'
best-effort
'
}));
}
return
{
id
:
svc
.
id
,
deployment
:
{
intent
:
{
compute
:
{
cpu
:
service
.
deployment
?.
intent
?.
compute
?.
cpu
||
'
small
'
,
ram
:
service
.
deployment
?.
intent
?.
compute
?.
ram
||
'
small
'
,
storage
:
service
.
deployment
?.
intent
?.
compute
?.
storage
||
'
small
'
,
gpu
:
{
enabled
:
service
.
deployment
?.
intent
?.
compute
?.
gpu
?.
enabled
||
false
,
},
},
network
:
{
deviceProximity
:
{
enabled
:
service
.
deployment
?.
intent
?.
network
?.
deviceProximity
?.
enabled
||
false
,
enabled
:
netIntent
.
deviceProximity
?.
enabled
??
false
,
},
latencies
:
cpArray
,
},
compute
:
{
cpu
:
computeIntent
.
cpu
??
'
small
'
,
ram
:
computeIntent
.
ram
??
'
small
'
,
storage
:
computeIntent
.
storage
??
'
small
'
,
gpu
:
{
enabled
:
computeIntent
.
gpu
?.
enabled
??
false
,
},
latencies
:
service
.
deployment
?.
intent
?.
network
?.
latencies
||
[],
connectionPoints
:
service
.
deployment
?.
intent
?.
network
?.
connectionPoints
||
[],
},
},
},
})),
};
});
editedIntent
.
value
=
{
originalDescriptor
:
selectedDescriptor
,
descriptorObject
:
descJson
,
hdaGraphIntent
:
{
type
:
activeIntentType
,
},
services
:
servicesMapped
||
[],
};
updateServiceOptions
(
editedIntent
.
value
.
hdaGraphIntent
.
type
);
updateServiceOptions
(
activeIntentType
);
showEditIntentDialog
.
value
=
true
;
}
/**
* Called when user changes the artifact intent (e.g. from "security" to "highPerformance").
* Adjusts `cpuOptions` and `ramOptions` as well as all services' CPU/RAM if needed.
*/
function
getActiveIntentType
(
hdaIntent
)
{
if
(
!
hdaIntent
)
return
'
default
'
;
const
keys
=
Object
.
keys
(
hdaIntent
);
const
foundKey
=
keys
.
find
((
k
)
=>
hdaIntent
[
k
]?.
enabled
===
true
);
return
foundKey
||
'
default
'
;
}
function
updateServiceOptions
(
selectedIntent
)
{
let
newCpuOptions
=
[];
let
newRamOptions
=
[];
switch
(
selectedIntent
)
{
case
'
security
'
:
newCpuOptions
=
[
'
small
'
,
'
medium
'
];
newRamOptions
=
[
'
small
'
,
'
medium
'
];
break
;
case
'
highAvailability
'
:
newCpuOptions
=
[
'
medium
'
,
'
large
'
];
newRamOptions
=
[
'
medium
'
,
'
large
'
];
break
;
case
'
highPerformance
'
:
newCpuOptions
=
[
'
large
'
];
newRamOptions
=
[
'
large
'
];
break
;
case
'
energyEfficiency
'
:
newCpuOptions
=
[
'
light
'
,
'
small
'
];
newRamOptions
=
[
'
light
'
,
'
small
'
];
break
;
default
:
// Fallback to all possible values
newCpuOptions
=
[
'
light
'
,
'
small
'
,
'
medium
'
,
'
large
'
];
newRamOptions
=
[
'
light
'
,
'
small
'
,
'
medium
'
,
'
large
'
];
break
;
}
const
intentRules
=
{
default
:
{
cpu
:
[
'
medium
'
],
ram
:
[
'
medium
'
],
storage
:
[
'
medium
'
],
gpu
:
false
,
qos
:
[
'
best-effort
'
],
deviceProximity
:
true
,
},
highAvailability
:
{
cpu
:
[
'
light
'
,
'
small
'
,
'
medium
'
,
'
large
'
],
ram
:
[
'
light
'
,
'
small
'
,
'
medium
'
,
'
large
'
],
storage
:
[
'
small
'
,
'
medium
'
,
'
large
'
],
gpu
:
false
,
qos
:
[
'
best-effort
'
,
'
low
'
,
'
ultralow
'
],
deviceProximity
:
null
,
},
highPerformance
:
{
cpu
:
[
'
medium
'
,
'
large
'
],
ram
:
[
'
medium
'
,
'
large
'
],
storage
:
[
'
large
'
],
gpu
:
true
,
qos
:
[
'
low
'
,
'
ultralow
'
],
deviceProximity
:
true
,
},
energyEfficiency
:
{
cpu
:
[
'
light
'
,
'
small
'
,
'
medium
'
],
ram
:
[
'
light
'
,
'
small
'
,
'
medium
'
],
storage
:
[
'
small
'
,
'
medium
'
],
gpu
:
false
,
qos
:
[
'
best-effort
'
,
'
low
'
],
deviceProximity
:
false
,
},
custom
:
{
cpu
:
[
'
light
'
,
'
small
'
,
'
medium
'
,
'
large
'
],
ram
:
[
'
light
'
,
'
small
'
,
'
medium
'
,
'
large
'
],
storage
:
[
'
small
'
,
'
medium
'
,
'
large
'
],
gpu
:
null
,
qos
:
[
'
best-effort
'
,
'
low
'
,
'
ultralow
'
],
deviceProximity
:
null
,
},
};
const
rule
=
intentRules
[
selectedIntent
]
||
intentRules
.
default
;
// Overwrite the reactive arrays so that the dropdowns update
cpuOptions
.
value
.
splice
(
0
,
cpuOptions
.
value
.
length
,
...
newCpuOptions
);
ramOptions
.
value
.
splice
(
0
,
ramOptions
.
value
.
length
,
...
newRamOptions
);
cpuOptions
.
value
.
splice
(
0
,
cpuOptions
.
value
.
length
,
...
rule
.
cpu
);
ramOptions
.
value
.
splice
(
0
,
ramOptions
.
value
.
length
,
...
rule
.
ram
);
storageOptions
.
value
.
splice
(
0
,
storageOptions
.
value
.
length
,
...
rule
.
storage
);
qosOptions
.
value
.
splice
(
0
,
qosOptions
.
value
.
length
,
...
rule
.
qos
);
// Now "fix" any services whose CPU/RAM are no longer valid
editedIntent
.
value
?.
services
?.
forEach
(
service
=>
{
const
svcCpu
=
service
.
deployment
.
intent
.
compute
.
cpu
;
const
svcRam
=
service
.
deployment
.
intent
.
compute
.
ram
;
editedIntent
.
value
?.
services
?.
forEach
((
service
,
idx
)
=>
{
// For string selects, if the current value is not allowed, default it to the first option.
if
(
!
rule
.
cpu
.
includes
(
service
.
deployment
.
intent
.
compute
.
cpu
))
{
service
.
deployment
.
intent
.
compute
.
cpu
=
rule
.
cpu
[
0
];
}
if
(
!
rule
.
ram
.
includes
(
service
.
deployment
.
intent
.
compute
.
ram
))
{
service
.
deployment
.
intent
.
compute
.
ram
=
rule
.
ram
[
0
];
}
if
(
!
rule
.
storage
.
includes
(
service
.
deployment
.
intent
.
compute
.
storage
))
{
service
.
deployment
.
intent
.
compute
.
storage
=
rule
.
storage
[
0
];
}
if
(
service
.
deployment
.
intent
.
network
.
latencies
?.
length
>
0
&&
!
rule
.
qos
.
includes
(
service
.
deployment
.
intent
.
network
.
latencies
[
0
].
qos
)
)
{
service
.
deployment
.
intent
.
network
.
latencies
[
0
].
qos
=
rule
.
qos
[
0
];
}
//
If the service's current CPU isn't valid, default it to the first in the new array
if
(
!
cpuOptions
.
value
.
includes
(
svcCpu
)
)
{
service
.
deployment
.
intent
.
compute
.
c
pu
=
cpuOptions
.
value
[
0
]
;
//
For booleans, only force update if the rule is defined.
if
(
rule
.
gpu
!==
null
)
{
service
.
deployment
.
intent
.
compute
.
g
pu
.
enabled
=
rule
.
gpu
;
}
// Same logic for RAM
if
(
!
ramOptions
.
value
.
includes
(
svcRam
))
{
service
.
deployment
.
intent
.
compute
.
ram
=
ramOptions
.
value
[
0
];
if
(
rule
.
deviceProximity
!==
null
)
{
// For deviceProximity, if enabled by rule, only the first service is true.
if
(
rule
.
deviceProximity
===
true
)
{
service
.
deployment
.
intent
.
network
.
deviceProximity
.
enabled
=
(
idx
===
0
);
}
else
{
service
.
deployment
.
intent
.
network
.
deviceProximity
.
enabled
=
false
;
}
}
});
}
function
shouldShowDeviceProximity
(
sIndex
)
{
const
intent
=
editedIntent
.
value
?.
hdaGraphIntent
?.
type
;
if
(
!
intent
)
return
false
;
if
(
intent
===
'
energyEfficiency
'
)
{
return
false
;
}
return
sIndex
===
0
;
}
async
function
saveIntent
()
{
const
updatedYaml
=
editedIntent
.
value
;
// Convert back to YAML if necessary
await
publishHdag
(
updatedYaml
);
const
descJson
=
editedIntent
.
value
.
descriptorObject
;
const
chosen
=
editedIntent
.
value
.
hdaGraphIntent
.
type
;
Object
.
keys
(
descJson
.
hdaGraph
.
hdaGraphIntent
||
{}).
forEach
((
k
)
=>
{
descJson
.
hdaGraph
.
hdaGraphIntent
[
k
].
enabled
=
k
===
chosen
;
});
editedIntent
.
value
.
services
.
forEach
((
svc
)
=>
{
const
target
=
descJson
.
hdaGraph
.
services
.
find
((
d
)
=>
d
.
id
===
svc
.
id
);
if
(
!
target
)
return
;
target
.
deployment
.
intent
.
compute
.
cpu
=
svc
.
deployment
.
intent
.
compute
.
cpu
;
target
.
deployment
.
intent
.
compute
.
ram
=
svc
.
deployment
.
intent
.
compute
.
ram
;
target
.
deployment
.
intent
.
compute
.
storage
=
svc
.
deployment
.
intent
.
compute
.
storage
;
target
.
deployment
.
intent
.
compute
.
gpu
.
enabled
=
svc
.
deployment
.
intent
.
compute
.
gpu
.
enabled
;
target
.
deployment
.
intent
.
network
.
deviceProximity
.
enabled
=
svc
.
deployment
.
intent
.
network
.
deviceProximity
.
enabled
;
target
.
deployment
.
intent
.
network
.
latencies
=
svc
.
deployment
.
intent
.
network
.
latencies
.
map
((
cpObj
)
=>
{
return
cpObj
;
});
});
const
updatedYaml
=
yaml
.
dump
(
descJson
);
descriptorContent
.
value
=
updatedYaml
;
showEditIntentDialog
.
value
=
false
;
}
async
function
publishDescriptor
()
{
await
publishHdag
(
descriptorContent
.
value
);
showDescriptorDialog
.
value
=
false
;
}
function
viewGraph
(
artifactName
)
{
router
.
push
(
`application_graph/
${
artifactName
}
`
);
}
function
saveHdag
()
{
publishHdag
(
hdagJson
.
value
);
showDialog
.
value
=
false
;
}
function
viewGraph
(
artifactName
)
{
router
.
push
(
`application_graph/
${
artifactName
}
`
);
}
</
script
>
<
style
scoped
>
...
...
@@ -346,3 +437,4 @@ function saveHdag() {
height
:
500px
;
}
</
style
>
This diff is collapsed.
Click to expand it.
Preview
0%
Loading
Try again
or
attach a new file
.
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Save comment
Cancel
Please
register
or
sign in
to comment