You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
179 lines
5.9 KiB
179 lines
5.9 KiB
3 months ago
|
<html>
|
||
|
<meta charset="utf-8">
|
||
|
|
||
|
<head>
|
||
|
<title>虚拟机管理</title>
|
||
|
</head>
|
||
|
<style>
|
||
|
td {
|
||
|
padding: 5px;
|
||
|
}
|
||
|
|
||
|
.button {
|
||
|
background-color: white;
|
||
|
border: 2px solid black;
|
||
|
color: black;
|
||
|
padding: 5px;
|
||
|
text-align: center;
|
||
|
text-decoration: none;
|
||
|
display: inline-block;
|
||
|
font-size: 16px;
|
||
|
-webkit-transition-duration: 0.4s;
|
||
|
/* Safari */
|
||
|
transition-duration: 0.4s;
|
||
|
}
|
||
|
|
||
|
.button:hover {
|
||
|
background-color: black;
|
||
|
color: white;
|
||
|
cursor: pointer;
|
||
|
}
|
||
|
|
||
|
button[disabled] {
|
||
|
opacity: .65;
|
||
|
}
|
||
|
|
||
|
button[disabled]:hover {
|
||
|
color: black;
|
||
|
background: white;
|
||
|
}
|
||
|
|
||
|
#loginForm {
|
||
|
width: 100%;
|
||
|
height: 100%;
|
||
|
display: flex;
|
||
|
align-items: center;
|
||
|
justify-content: center;
|
||
|
}
|
||
|
|
||
|
#loginFormDov {
|
||
|
padding: 20px;
|
||
|
border-radius: 10px;
|
||
|
box-shadow: 0 0 10px 2px rgba(0, 0, 0, 0.099);
|
||
|
display: flex;
|
||
|
flex-direction: column;
|
||
|
gap: 5px;
|
||
|
}
|
||
|
|
||
|
.inputDiv {
|
||
|
display: flex;
|
||
|
flex-wrap: nowrap;
|
||
|
gap: 20px;
|
||
|
}
|
||
|
|
||
|
#notAccountText,
|
||
|
#notPasswordText,
|
||
|
#ErrorAccountOrPassword {
|
||
|
display: none;
|
||
|
color: red;
|
||
|
font-size: 12px;
|
||
|
}
|
||
|
</style>
|
||
|
<!-- Promise polyfill for IE11 -->
|
||
|
<script src="vendor/promise.js"></script>
|
||
|
|
||
|
<!-- ES2015/ES6 modules polyfill -->
|
||
|
<script nomodule src="vendor/browser-es-module-loader/dist/browser-es-module-loader.js"></script>
|
||
|
<script src="vendor/crypto-js.min.js"></script>
|
||
|
|
||
|
<script type="module" crossorigin="anonymous">
|
||
|
import * as WebUtil from "./app/webutil.js";
|
||
|
const apiPrefix = 'k8s/apis'
|
||
|
function loadVMI(namespace) {
|
||
|
WebUtil.fetchJSON('/' + apiPrefix + '/kubevirt.io/v1alpha3/namespaces/' + namespace + '/virtualmachineinstances/')
|
||
|
.then((resp) => {
|
||
|
let vmis = [];
|
||
|
resp.items.forEach(i => {
|
||
|
let tr = document.createElement('tr');
|
||
|
tr.innerHTML = "<td>" + i.metadata.name + "</td><td>" + String(i.status.phase) + "</td><td>" + String(i.status.interfaces !== undefined ? i.status.interfaces[0].ipAddress : '') + "</td><td>" + String(i.status.nodeName !== undefined ? i.status.nodeName : '') + "</td><td><button class='button' " + String(i.status.phase == "Running" ? "" : "disabled") + " onclick=\"window.open('vnc_lite.html?path=" + apiPrefix + "/subresources.kubevirt.io/v1alpha3/namespaces/" + namespace + "/virtualmachineinstances/" + i.metadata.name + "/vnc', 'novnc_window', 'resizable=yes,toolbar=no,location=no,status=no,scrollbars=no,menubar=no,width=1030,height=800')\">VNC</button></td>";
|
||
|
document.getElementById("vmis").appendChild(tr);
|
||
|
});
|
||
|
if (resp.items.length === 0) {
|
||
|
document.body.append("No virtual machines in the namespace.");
|
||
|
}
|
||
|
})
|
||
|
.catch(err => console.log("Failed to get vmis: " + err));
|
||
|
}
|
||
|
let namespace = WebUtil.getQueryVar('namespace', 'default');
|
||
|
const setCookie = (data) => {
|
||
|
const sessionToken = btoa(`${data.account}:${data.password}`);
|
||
|
document.cookie = `educoder_WebUtil_login_session_token=${sessionToken}; max-age=${60 * 60}; path=/`;
|
||
|
}
|
||
|
function getCookie(name) {
|
||
|
const value = `; ${document.cookie}`;
|
||
|
const parts = value.split(`; ${name}=`);
|
||
|
if (parts.length === 2) return parts.pop().split(';').shift();
|
||
|
}
|
||
|
const hashWithSHA256 = async (text) => {
|
||
|
return CryptoJS.SHA256(text).toString(CryptoJS.enc.Hex);
|
||
|
};
|
||
|
async function handleIsloginOk({ account, password }) {
|
||
|
const loginpassword = "8cc900e6c764589e16af6ecaa823c467f908938f59f0eaf67b0ed094dfc5a6c4";
|
||
|
const userLogin = await hashWithSHA256(`${account}:${password}`)
|
||
|
if (userLogin === loginpassword) {
|
||
|
return true
|
||
|
}
|
||
|
return false
|
||
|
}
|
||
|
window.onload = () => {
|
||
|
const loginButton = document.getElementById("loginButton");
|
||
|
const loginForm = document.getElementById("loginForm");
|
||
|
if (loginButton && loginForm) {
|
||
|
loginButton.addEventListener("click", async () => {
|
||
|
const account = document.getElementById("account").value;
|
||
|
const password = document.getElementById("password").value;
|
||
|
const notAccountText = document.getElementById("notAccountText");
|
||
|
const notPasswordText = document.getElementById("notPasswordText");
|
||
|
const ErrorAccountOrPassword = document.getElementById("ErrorAccountOrPassword");
|
||
|
const isloginOk = await handleIsloginOk({ account, password })
|
||
|
notAccountText.style.display = !account ? "block" : "none"
|
||
|
notPasswordText.style.display = !password ? "block" : "none"
|
||
|
ErrorAccountOrPassword.style.display = ((!!account && !!password) && !isloginOk) ? "block" : "none"
|
||
|
if (isloginOk) {
|
||
|
setCookie({ account, password })
|
||
|
document.body.removeChild(loginForm);
|
||
|
loadVMI(namespace);
|
||
|
}
|
||
|
});
|
||
|
}
|
||
|
const sessionToken = getCookie('educoder_WebUtil_login_session_token');
|
||
|
if (sessionToken && loginForm) {
|
||
|
document.body.removeChild(loginForm);
|
||
|
loadVMI(namespace);
|
||
|
}
|
||
|
}
|
||
|
</script>
|
||
|
</meta>
|
||
|
|
||
|
<body>
|
||
|
<table>
|
||
|
<tbody id="vmis"></tbody>
|
||
|
</table>
|
||
|
<div id="loginForm">
|
||
|
<div id="loginFormDov">
|
||
|
<div style="font-size: 20px; text-align: center;font-weight: 600;margin-bottom: 20px;">虚拟机管理</div>
|
||
|
<div class="inputDiv">
|
||
|
<span style="line-height: 30px;">账号</span>
|
||
|
<div style="display: flex;flex-direction: column;">
|
||
|
<input id="account" type="text" style="height: 30px;" />
|
||
|
<div style="height: 20px;">
|
||
|
<span id="notAccountText">请输入账号</span>
|
||
|
</div>
|
||
|
</div>
|
||
|
</div>
|
||
|
<div class="inputDiv">
|
||
|
<span style="line-height: 30px;">密码</span>
|
||
|
<div style="display: flex;flex-direction: column;">
|
||
|
<input id="password" type="password" style="height: 30px;" />
|
||
|
<div style="height: 20px;">
|
||
|
<span id="notPasswordText">请输入密码</span>
|
||
|
<span id="ErrorAccountOrPassword">账号或密码错误</span>
|
||
|
</div>
|
||
|
</div>
|
||
|
</div>
|
||
|
<button id="loginButton" style="cursor:pointer;height: 30px;">登录</button>
|
||
|
</div>
|
||
|
</div>
|
||
|
</body>
|
||
|
|
||
|
</html>
|