mirror of https://github.com/rancher/dashboard.git
299 lines
7.3 KiB
Vue
299 lines
7.3 KiB
Vue
<script>
|
|
import Loading from '@shell/components/Loading';
|
|
import { Banner } from '@components/Banner';
|
|
import { computeDashboardUrl } from '@shell/utils/grafana';
|
|
|
|
export default {
|
|
components: { Banner, Loading },
|
|
props: {
|
|
url: {
|
|
type: String,
|
|
required: true,
|
|
},
|
|
vars: {
|
|
type: Object,
|
|
default: () => ({})
|
|
},
|
|
range: {
|
|
type: String,
|
|
default: null
|
|
},
|
|
refreshRate: {
|
|
type: String,
|
|
default: null
|
|
},
|
|
backgroundColor: {
|
|
type: String,
|
|
default: '#1b1c21'
|
|
},
|
|
theme: {
|
|
type: String,
|
|
default: 'dark'
|
|
}
|
|
},
|
|
data() {
|
|
return {
|
|
loading: false, error: false, interval: null, initialUrl: this.computeUrl(), errorTimer: null
|
|
};
|
|
},
|
|
computed: {
|
|
currentUrl() {
|
|
return this.computeUrl();
|
|
},
|
|
grafanaUrl() {
|
|
return this.currentUrl.replace('&kiosk', '');
|
|
},
|
|
graphWindow() {
|
|
return this.$refs.frame?.contentWindow;
|
|
},
|
|
graphHistory() {
|
|
return this.graphWindow?.history;
|
|
},
|
|
graphDocument() {
|
|
return this.graphWindow?.document;
|
|
}
|
|
},
|
|
watch: {
|
|
currentUrl() {
|
|
if (this.graphHistory && this.graphWindow?.angular) {
|
|
const angularElement = this.graphWindow.angular.element(this.graphDocument.querySelector('.grafana-app'));
|
|
const injector = angularElement.injector();
|
|
|
|
this.graphHistory.pushState({}, '', this.currentUrl);
|
|
injector.get('$route').updateParams(this.computeParams());
|
|
injector.get('$route').reload();
|
|
}
|
|
},
|
|
|
|
error(neu) {
|
|
if (neu) {
|
|
this.errorTimer = setInterval(() => {
|
|
this.reload();
|
|
}, 45000);
|
|
} else {
|
|
clearInterval(this.errorTimer);
|
|
this.errorTimer = null;
|
|
}
|
|
}
|
|
},
|
|
mounted() {
|
|
this.$refs.frame.onload = this.inject;
|
|
this.poll();
|
|
},
|
|
beforeDestroy() {
|
|
if (this.interval) {
|
|
clearInterval(this.interval);
|
|
}
|
|
|
|
if (this.errorTimer) {
|
|
clearInterval(this.errorTimer);
|
|
}
|
|
},
|
|
methods: {
|
|
poll() {
|
|
if (this.interval) {
|
|
clearInterval(this.interval);
|
|
this.interval = null;
|
|
}
|
|
|
|
this.interval = setInterval(() => {
|
|
try {
|
|
const graphWindow = this.$refs.frame?.contentWindow;
|
|
const errorElements = graphWindow.document.getElementsByClassName('alert-error');
|
|
const errorCornerElements = graphWindow.document.getElementsByClassName('panel-info-corner--error');
|
|
const panelInFullScreenElements = graphWindow.document.getElementsByClassName('panel-in-fullscreen');
|
|
const panelContainerElements = graphWindow.document.getElementsByClassName('panel-container');
|
|
const error = errorElements.length > 0 || errorCornerElements.length > 0;
|
|
const loaded = panelInFullScreenElements.length > 0 || panelContainerElements.length > 0;
|
|
const errorMessageElms = graphWindow.document.getElementsByTagName('pre');
|
|
const errorMessage = errorMessageElms.length > 0 ? errorMessageElms[0].innerText : '';
|
|
const isFailure = errorMessage.includes('"status": "Failure"');
|
|
|
|
if (error) {
|
|
throw new Error('An error was detected in the iframe');
|
|
}
|
|
|
|
this.$set(this, 'loading', !loaded);
|
|
this.$set(this, 'error', isFailure);
|
|
} catch (ex) {
|
|
this.$set(this, 'error', true);
|
|
this.$set(this, 'loading', false);
|
|
clearInterval(this.interval);
|
|
this.interval = null;
|
|
}
|
|
}, 100);
|
|
},
|
|
computeFromTo() {
|
|
return {
|
|
from: `now-${ this.range }`,
|
|
to: `now`
|
|
};
|
|
},
|
|
computeUrl() {
|
|
const embedUrl = this.url;
|
|
const clusterId = this.$store.getters['currentCluster'].id;
|
|
const params = this.computeParams();
|
|
|
|
return computeDashboardUrl(embedUrl, clusterId, params);
|
|
},
|
|
computeParams() {
|
|
const params = {};
|
|
const fromTo = this.computeFromTo();
|
|
|
|
if (fromTo.from) {
|
|
params.from = fromTo.from;
|
|
}
|
|
|
|
if (fromTo.to) {
|
|
params.to = fromTo.to;
|
|
}
|
|
|
|
if (this.refreshRate) {
|
|
params.refresh = this.refreshRate;
|
|
}
|
|
|
|
if (Object.keys(this.vars).length > 0) {
|
|
Object.entries(this.vars).forEach((entry) => {
|
|
const paramName = `var-${ entry[0] }`;
|
|
|
|
params[paramName] = entry[1];
|
|
});
|
|
}
|
|
|
|
params.theme = this.theme;
|
|
|
|
return params;
|
|
},
|
|
reload(ev) {
|
|
ev && ev.preventDefault();
|
|
this.$refs.frame.contentWindow.location.reload();
|
|
this.poll();
|
|
},
|
|
injectCss() {
|
|
const style = document.createElement('style');
|
|
|
|
style.innerHTML = `
|
|
body .grafana-app .dashboard-content {
|
|
background: ${ this.backgroundColor };
|
|
padding: 0;
|
|
}
|
|
|
|
body .grafana-app .layout {
|
|
background: ${ this.backgroundColor };
|
|
}
|
|
|
|
|
|
body .grafana-app .dashboard-content .panel-container {
|
|
background-color: initial;
|
|
border: none;
|
|
}
|
|
|
|
body .grafana-app .dashboard-content .panel-wrapper {
|
|
height: 100%;
|
|
}
|
|
|
|
body .grafana-app .panel-menu-container {
|
|
display: none;
|
|
}
|
|
|
|
body .grafana-app .panel-title {
|
|
cursor: default;
|
|
}
|
|
|
|
body .grafana-app .panel-title .panel-title-text div {
|
|
display: none;
|
|
}
|
|
`;
|
|
|
|
const graphWindow = this.$refs.frame?.contentWindow;
|
|
const graphDocument = graphWindow?.document;
|
|
|
|
if (graphDocument.head) {
|
|
graphDocument.head.appendChild(style);
|
|
}
|
|
},
|
|
|
|
inject() {
|
|
this.injectCss();
|
|
}
|
|
}
|
|
};
|
|
</script>
|
|
|
|
<template>
|
|
<div class="grafana-graph">
|
|
<Banner
|
|
v-if="error"
|
|
color="error"
|
|
style="z-index: 1000"
|
|
>
|
|
<div class="text-center">
|
|
{{ t('grafanaDashboard.failedToLoad') }} <a
|
|
href="#"
|
|
@click="reload"
|
|
>{{ t('grafanaDashboard.reload') }}</a>
|
|
</div>
|
|
</Banner>
|
|
<iframe
|
|
v-show="!error"
|
|
ref="frame"
|
|
:class="{loading, frame: true}"
|
|
:src="initialUrl"
|
|
frameborder="0"
|
|
scrolling="no"
|
|
/>
|
|
<div v-if="loading">
|
|
<Loading />
|
|
</div>
|
|
<div
|
|
v-if="!loading && !error"
|
|
class="external-link"
|
|
>
|
|
<a
|
|
:href="grafanaUrl"
|
|
target="_blank"
|
|
rel="noopener noreferrer nofollow"
|
|
>{{ t('grafanaDashboard.grafana') }} <i class="icon icon-external-link" /></a>
|
|
</div>
|
|
</div>
|
|
</template>
|
|
|
|
<style lang='scss' scoped>
|
|
.grafana-graph {
|
|
position: relative;
|
|
min-height: 100%;
|
|
min-width: 100%;
|
|
|
|
& ::v-deep .content {
|
|
position: relative;
|
|
display: flex;
|
|
flex-direction: column;
|
|
justify-content: center;
|
|
align-items: center;
|
|
width: 100%;
|
|
height: 100%;
|
|
padding: 0;
|
|
}
|
|
|
|
& ::v-deep .overlay {
|
|
position: static;
|
|
background-color: initial;
|
|
}
|
|
|
|
iframe {
|
|
position: absolute;
|
|
left: 0;
|
|
right: 0;
|
|
top: 20px;
|
|
bottom: 0;
|
|
width: 100%;
|
|
height: 100%;
|
|
overflow: hidden;
|
|
|
|
&.loading {
|
|
visibility: hidden;
|
|
}
|
|
}
|
|
}
|
|
</style>
|