-
Notifications
You must be signed in to change notification settings - Fork 265
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat(xo-lite): display host console (#8136)
- Loading branch information
1 parent
e80b39e
commit 904273f
Showing
15 changed files
with
326 additions
and
21 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,27 @@ | ||
<template> | ||
<UiHeadBar> | ||
<template #icon> | ||
<UiObjectIcon size="medium" type="host" :state="getHostPowerState as HostState" /> | ||
</template> | ||
{{ host.name_label }} | ||
</UiHeadBar> | ||
</template> | ||
|
||
<script lang="ts" setup> | ||
import type { XenApiHost } from '@/libs/xen-api/xen-api.types' | ||
import { useHostStore } from '@/stores/xen-api/host.store' | ||
import type { HostState } from '@core/types/object-icon.type' | ||
import UiHeadBar from '@core/components/ui/head-bar/UiHeadBar.vue' | ||
import UiObjectIcon from '@core/components/ui/object-icon/UiObjectIcon.vue' | ||
import { computed } from 'vue' | ||
const props = defineProps<{ | ||
host: XenApiHost | ||
}>() | ||
const { runningHosts } = useHostStore().subscribe() | ||
const getHostPowerState = computed(() => { | ||
const isHostRunning = runningHosts.value.some(runningHost => runningHost.uuid === props.host.uuid) | ||
return isHostRunning ? 'running' : 'halted' | ||
}) | ||
</script> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,28 @@ | ||
<template> | ||
<TabList> | ||
<RouterTab :to="{ name: 'host.dashboard', params: { uuid } }" disabled> | ||
{{ $t('dashboard') }} | ||
</RouterTab> | ||
<RouterTab :to="{ name: 'host.console', params: { uuid } }"> | ||
{{ $t('console') }} | ||
</RouterTab> | ||
<RouterTab :to="{ name: 'host.network', params: { uuid } }" disabled> | ||
{{ $t('network') }} | ||
</RouterTab> | ||
<RouterTab :to="{ name: 'host.tasks', params: { uuid } }" disabled> | ||
{{ $t('tasks') }} | ||
</RouterTab> | ||
<RouterTab :to="{ name: 'host.vms', params: { uuid } }" disabled> | ||
{{ $t('vms') }} | ||
</RouterTab> | ||
</TabList> | ||
</template> | ||
|
||
<script lang="ts" setup> | ||
import RouterTab from '@/components/RouterTab.vue' | ||
import TabList from '@core/components/tab/TabList.vue' | ||
defineProps<{ | ||
uuid: string | ||
}>() | ||
</script> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,32 @@ | ||
export default { | ||
path: '/host/:uuid', | ||
component: () => import('@/views/host/HostRootView.vue'), | ||
redirect: { name: 'host.console' }, | ||
children: [ | ||
{ | ||
path: 'dashboard', | ||
name: 'host.dashboard', | ||
component: () => import('@/views/host/HostDashboardView.vue'), | ||
}, | ||
{ | ||
path: 'console', | ||
name: 'host.console', | ||
component: () => import('@/views/host/HostConsoleView.vue'), | ||
}, | ||
{ | ||
path: 'network', | ||
name: 'host.network', | ||
component: () => import('@/views/host/HostNetworkView.vue'), | ||
}, | ||
{ | ||
path: 'tasks', | ||
name: 'host.tasks', | ||
component: () => import('@/views/host/HostTasksView.vue'), | ||
}, | ||
{ | ||
path: 'vms', | ||
name: 'host.vms', | ||
component: () => import('@/views/host/HostVmsView.vue'), | ||
}, | ||
], | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,178 @@ | ||
<template> | ||
<div :class="{ 'no-ui': !uiStore.hasUi }" class="host-console-view"> | ||
<div v-if="hasError">{{ $t('error-occurred') }}</div> | ||
<UiSpinner v-else-if="!isReady" class="spinner" /> | ||
<UiStatusPanel v-else-if="!isHostRunning" :image-source="monitor" :title="$t('power-on-host-for-console')" /> | ||
<template v-else-if="host && hostConsole"> | ||
<VtsLayoutConsole> | ||
<RemoteConsole | ||
ref="consoleElement" | ||
:is-console-available="isConsoleAvailable" | ||
:location="hostConsole.location" | ||
class="remote-console" | ||
/> | ||
<template #actions> | ||
<VtsActionsConsole | ||
:open-in-new-tab="openInNewTab" | ||
:send-ctrl-alt-del="sendCtrlAltDel" | ||
:toggle-full-screen="toggleFullScreen" | ||
:is-fullscreen="!uiStore.hasUi" | ||
/> | ||
<VtsDivider type="stretch" /> | ||
<VtsClipboardConsole /> | ||
</template> | ||
</VtsLayoutConsole> | ||
</template> | ||
</div> | ||
</template> | ||
|
||
<script lang="ts" setup> | ||
import monitor from '@/assets/monitor.svg' | ||
import RemoteConsole from '@/components/RemoteConsole.vue' | ||
import UiSpinner from '@/components/ui/UiSpinner.vue' | ||
import UiStatusPanel from '@/components/ui/UiStatusPanel.vue' | ||
import { isHostOperationPending } from '@/libs/host' | ||
import { HOST_OPERATION } from '@/libs/xen-api/xen-api.enums' | ||
import type { XenApiHost } from '@/libs/xen-api/xen-api.types' | ||
import { usePageTitleStore } from '@/stores/page-title.store' | ||
import { useConsoleStore } from '@/stores/xen-api/console.store' | ||
import { useControlDomainStore } from '@/stores/xen-api/control-domain.store' | ||
import { useHostStore } from '@/stores/xen-api/host.store' | ||
import VtsActionsConsole from '@core/components/console/VtsActionsConsole.vue' | ||
import VtsClipboardConsole from '@core/components/console/VtsClipboardConsole.vue' | ||
import VtsLayoutConsole from '@core/components/console/VtsLayoutConsole.vue' | ||
import VtsDivider from '@core/components/divider/VtsDivider.vue' | ||
import { useUiStore } from '@core/stores/ui.store' | ||
import { computed, ref } from 'vue' | ||
import { useI18n } from 'vue-i18n' | ||
import { useRoute, useRouter } from 'vue-router' | ||
const STOP_OPERATIONS = [HOST_OPERATION.SHUTDOWN] | ||
usePageTitleStore().setTitle(useI18n().t('console')) | ||
const router = useRouter() | ||
const route = useRoute() | ||
const uiStore = useUiStore() | ||
const { | ||
isReady: isHostReady, | ||
getByUuid: getHostByUuid, | ||
hasError: hasHostError, | ||
runningHosts, | ||
} = useHostStore().subscribe() | ||
const { records: controlDomains } = useControlDomainStore().subscribe() | ||
const { | ||
isReady: isConsoleReady, | ||
getByOpaqueRef: getConsoleByOpaqueRef, | ||
hasError: hasConsoleError, | ||
} = useConsoleStore().subscribe() | ||
const hasError = computed(() => hasHostError.value || hasConsoleError.value) | ||
const host = computed(() => getHostByUuid(route.params.uuid as XenApiHost['uuid'])) | ||
const controlDomain = computed(() => { | ||
const controlDomainOpaqueRef = host.value?.control_domain | ||
return controlDomainOpaqueRef | ||
? controlDomains.value.find(controlDomain => controlDomain.$ref === controlDomainOpaqueRef) | ||
: undefined | ||
}) | ||
const hostConsole = computed(() => { | ||
const consoleOpaqueRef = controlDomain.value?.consoles[0] | ||
return consoleOpaqueRef ? getConsoleByOpaqueRef(consoleOpaqueRef) : undefined | ||
}) | ||
const isReady = computed(() => isHostReady.value && isConsoleReady.value && controlDomain.value) | ||
const isHostRunning = computed(() => { | ||
return runningHosts.value.some(runningHost => runningHost.uuid === host.value?.uuid) | ||
}) | ||
const isConsoleAvailable = computed(() => | ||
controlDomain.value !== undefined ? !isHostOperationPending(host.value!, STOP_OPERATIONS) : false | ||
) | ||
const consoleElement = ref() | ||
const sendCtrlAltDel = () => consoleElement.value?.sendCtrlAltDel() | ||
const toggleFullScreen = () => { | ||
uiStore.hasUi = !uiStore.hasUi | ||
} | ||
const openInNewTab = () => { | ||
const routeData = router.resolve({ query: { ui: '0' } }) | ||
window.open(routeData.href, '_blank') | ||
} | ||
</script> | ||
|
||
<style lang="postcss" scoped> | ||
.host-console-view { | ||
display: flex; | ||
height: calc(100% - 14.5rem); | ||
flex-direction: column; | ||
&.no-ui { | ||
height: 100%; | ||
} | ||
} | ||
.spinner { | ||
color: var(--color-info-txt-base); | ||
display: flex; | ||
margin: auto; | ||
width: 10rem; | ||
height: 10rem; | ||
} | ||
.remote-console { | ||
flex: 1; | ||
max-width: 100%; | ||
height: 100%; | ||
} | ||
.not-available { | ||
display: flex; | ||
align-items: center; | ||
justify-content: center; | ||
flex-direction: column; | ||
text-align: center; | ||
gap: 4rem; | ||
color: var(--color-info-txt-base); | ||
font-size: 3.6rem; | ||
} | ||
.open-in-new-window { | ||
position: absolute; | ||
top: 0; | ||
right: 0; | ||
overflow: hidden; | ||
& > .link { | ||
display: flex; | ||
align-items: center; | ||
gap: 1rem; | ||
background-color: var(--color-info-txt-base); | ||
color: var(--color-info-txt-item); | ||
text-decoration: none; | ||
padding: 1.5rem; | ||
font-size: 1.6rem; | ||
border-radius: 0 0 0 0.8rem; | ||
white-space: nowrap; | ||
transform: translateX(calc(100% - 4.5rem)); | ||
transition: transform 0.2s ease-in-out; | ||
&:hover { | ||
transform: translateX(0); | ||
} | ||
} | ||
} | ||
.host-console-view:deep(.menu-list) { | ||
background-color: transparent; | ||
align-self: center; | ||
} | ||
</style> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,11 @@ | ||
<template> | ||
<PageUnderConstruction /> | ||
</template> | ||
|
||
<script lang="ts" setup> | ||
import PageUnderConstruction from '@/components/PageUnderConstruction.vue' | ||
import { usePageTitleStore } from '@/stores/page-title.store' | ||
import { useI18n } from 'vue-i18n' | ||
usePageTitleStore().setTitle(useI18n().t('network')) | ||
</script> |
Oops, something went wrong.