xref: /openbmc/webui-vue/src/views/Operations/Kvm/KvmConsole.vue (revision 373d243c98f8b465e9315e95f88cdf26b887a626)
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 { mapState } from 'vuex';
50
51const Connecting = 0;
52const Connected = 1;
53const Disconnected = 2;
54
55export default {
56  name: 'KvmConsole',
57  components: { StatusIcon, IconLaunch, IconArrowDown },
58  props: {
59    isFullWindow: {
60      type: Boolean,
61      default: true,
62    },
63  },
64  data() {
65    return {
66      isConsoleWindow: null,
67      rfb: null,
68      isConnected: false,
69      terminalClass: this.isFullWindow ? 'full-window' : '',
70      marginClass: this.isFullWindow ? 'margin-left-full-window' : '',
71      status: Connecting,
72      convasRef: null,
73      resizeKvmWindow: null,
74    };
75  },
76  computed: {
77    ...mapState('authentication', ['consoleWindow']),
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 this.$t('pageKvm.connected');
89      } else if (this.status === Disconnected) {
90        return this.$t('pageKvm.disconnected');
91      }
92      return this.$t('pageKvm.connecting');
93    },
94  },
95  watch: {
96    consoleWindow() {
97      if (this.consoleWindow == false) this.isConsoleWindow.close();
98    },
99  },
100  mounted() {
101    this.openTerminal();
102  },
103  beforeDestroy() {
104    window.removeEventListener('resize', this.resizeKvmWindow);
105    this.closeTerminal();
106  },
107  methods: {
108    sendCtrlAltDel() {
109      this.rfb.sendCtrlAltDel();
110    },
111    closeTerminal() {
112      this.rfb.disconnect();
113      this.rfb = null;
114    },
115    openTerminal() {
116      const token = this.$store.getters['authentication/token'];
117      this.rfb = new RFB(
118        this.$refs.panel,
119        `wss://${window.location.host}/kvm/0`,
120        { wsProtocols: [token] }
121      );
122
123      this.rfb.scaleViewport = true;
124      this.rfb.clipViewport = true;
125      const that = this;
126
127      this.resizeKvmWindow = throttle(() => {
128        setTimeout(that.setWidthToolbar, 0);
129      }, 1000);
130      window.addEventListener('resize', this.resizeKvmWindow);
131
132      this.rfb.addEventListener('connect', () => {
133        that.isConnected = true;
134        that.status = Connected;
135        that.setWidthToolbar();
136      });
137
138      this.rfb.addEventListener('disconnect', () => {
139        this.isConnected = false;
140        that.status = Disconnected;
141      });
142    },
143    setWidthToolbar() {
144      if (
145        this.$refs.panel.children &&
146        this.$refs.panel.children.length > 0 &&
147        this.$refs.panel.children[0].children.length > 0
148      ) {
149        this.$refs.toolbar.style.width =
150          this.$refs.panel.children[0].children[0].clientWidth - 10 + 'px';
151      }
152    },
153    openConsoleWindow() {
154      // If isConsoleWindow is not null
155      // Check the newly opened window is closed or not
156      if (this.isConsoleWindow) {
157        // If window is not closed set focus to new window
158        // If window is closed, do open new window
159        if (!this.isConsoleWindow.closed) {
160          this.isConsoleWindow.focus();
161          return;
162        } else {
163          this.openNewWindow();
164        }
165      } else {
166        // If isConsoleWindow is null, open new window
167        this.openNewWindow();
168      }
169    },
170    openNewWindow() {
171      this.isConsoleWindow = window.open(
172        '#/console/kvm',
173        'kvmConsoleWindow',
174        'directories=no,titlebar=no,toolbar=no,location=no,status=no,menubar=no,scrollbars=no,resizable=yes,width=700,height=550'
175      );
176    },
177  },
178};
179</script>
180
181<style scoped lang="scss">
182.button-ctrl-alt-delete {
183  float: right;
184}
185
186.kvm-status {
187  padding-top: $spacer / 2;
188  padding-left: $spacer / 4;
189  display: inline-block;
190}
191
192.margin-left-full-window {
193  margin-left: 5px;
194}
195</style>
196