1<template>
2  <div :class="marginClass">
3    <div ref="toolbar" class="kvm-toolbar">
4      <b-row class="d-flex">
5        <b-col class="d-flex flex-column justify-content-end" cols="4">
6          <dl class="mb-2" sm="2" md="2">
7            <dt class="d-inline font-weight-bold mr-1">
8              {{ $t('pageKvm.status') }}:
9            </dt>
10            <dd class="d-inline">
11              <status-icon :status="serverStatusIcon" />
12              <span class="d-none d-md-inline"> {{ serverStatus }}</span>
13            </dd>
14          </dl>
15        </b-col>
16
17        <b-col class="d-flex justify-content-end pr-1">
18          <b-button
19            v-if="isConnected"
20            variant="link"
21            type="button"
22            @click="sendCtrlAltDel"
23          >
24            <icon-arrow-down />
25            {{ $t('pageKvm.buttonCtrlAltDelete') }}
26          </b-button>
27          <b-button
28            v-if="!isFullWindow"
29            variant="link"
30            type="button"
31            @click="openConsoleWindow()"
32          >
33            <icon-launch />
34            {{ $t('pageKvm.openNewTab') }}
35          </b-button>
36        </b-col>
37      </b-row>
38    </div>
39    <div id="terminal-kvm" ref="panel" :class="terminalClass"></div>
40  </div>
41</template>
42
43<script>
44import RFB from '@novnc/novnc/core/rfb';
45import StatusIcon from '@/components/Global/StatusIcon';
46import IconLaunch from '@carbon/icons-vue/es/launch/20';
47import IconArrowDown from '@carbon/icons-vue/es/arrow--down/16';
48import { throttle } from 'lodash';
49import { useI18n } from 'vue-i18n';
50import i18n from '@/i18n';
51
52const Connecting = 0;
53const Connected = 1;
54const Disconnected = 2;
55
56export default {
57  name: 'KvmConsole',
58  components: { StatusIcon, IconLaunch, IconArrowDown },
59  props: {
60    isFullWindow: {
61      type: Boolean,
62      default: true,
63    },
64  },
65  data() {
66    return {
67      $t: useI18n().t,
68      rfb: null,
69      isConnected: false,
70      terminalClass: this.isFullWindow ? 'full-window' : '',
71      marginClass: this.isFullWindow ? 'margin-left-full-window' : '',
72      status: Connecting,
73      convasRef: null,
74      resizeKvmWindow: null,
75    };
76  },
77  computed: {
78    serverStatusIcon() {
79      if (this.status === Connected) {
80        return 'success';
81      } else if (this.status === Disconnected) {
82        return 'danger';
83      }
84      return 'secondary';
85    },
86    serverStatus() {
87      if (this.status === Connected) {
88        return i18n.global.t('pageKvm.connected');
89      } else if (this.status === Disconnected) {
90        return i18n.global.t('pageKvm.disconnected');
91      }
92      return i18n.global.t('pageKvm.connecting');
93    },
94  },
95  created() {
96    this.$store.dispatch('global/getSystemInfo');
97  },
98  mounted() {
99    this.openTerminal();
100  },
101  beforeUnmount() {
102    window.removeEventListener('resize', this.resizeKvmWindow);
103    this.closeTerminal();
104  },
105  methods: {
106    sendCtrlAltDel() {
107      this.rfb.sendCtrlAltDel();
108    },
109    closeTerminal() {
110      this.rfb.disconnect();
111      this.rfb = null;
112    },
113    openTerminal() {
114      const token = this.$store.getters['authentication/token'];
115      this.rfb = new RFB(
116        this.$refs.panel,
117        `wss://${window.location.host}/kvm/0`,
118        { wsProtocols: [token] },
119      );
120
121      this.rfb.scaleViewport = true;
122      this.rfb.clipViewport = true;
123      const that = this;
124
125      this.resizeKvmWindow = throttle(() => {
126        setTimeout(that.setWidthToolbar, 0);
127      }, 1000);
128      window.addEventListener('resize', this.resizeKvmWindow);
129
130      this.rfb.addEventListener('connect', () => {
131        that.isConnected = true;
132        that.status = Connected;
133        that.setWidthToolbar();
134      });
135
136      this.rfb.addEventListener('disconnect', () => {
137        this.isConnected = false;
138        that.status = Disconnected;
139      });
140    },
141    setWidthToolbar() {
142      if (
143        this.$refs.panel.children &&
144        this.$refs.panel.children.length > 0 &&
145        this.$refs.panel.children[0].children.length > 0
146      ) {
147        this.$refs.toolbar.style.width =
148          this.$refs.panel.children[0].children[0].clientWidth - 10 + 'px';
149      }
150    },
151    openConsoleWindow() {
152      // If consoleWindow is not null
153      // Check the newly opened window is closed or not
154      if (this.$eventBus.$consoleWindow) {
155        // If window is not closed set focus to new window
156        // If window is closed, do open new window
157        if (!this.$eventBus.$consoleWindow.closed) {
158          this.$eventBus.$consoleWindow.focus();
159          return;
160        } else {
161          this.openNewWindow();
162        }
163      } else {
164        // If consoleWindow is null, open new window
165        this.openNewWindow();
166      }
167    },
168    openNewWindow() {
169      this.$eventBus.$consoleWindow = window.open(
170        '#/console/kvm',
171        'kvmConsoleWindow',
172        'directories=no,titlebar=no,toolbar=no,location=no,status=no,menubar=no,scrollbars=no,resizable=yes,width=700,height=550',
173      );
174    },
175  },
176};
177</script>
178
179<style lang="scss" scoped>
180@import '@/assets/styles/bmc/helpers/_index.scss';
181@import '@/assets/styles/bootstrap/_helpers.scss';
182
183.button-ctrl-alt-delete {
184  float: right;
185}
186
187.kvm-status {
188  padding-top: $spacer / 2;
189  padding-left: $spacer / 4;
190  display: inline-block;
191}
192
193.margin-left-full-window {
194  margin-left: 5px;
195}
196</style>
197