commit
74008ce744
60 changed files with 1210 additions and 1760 deletions
@ -1,51 +1,45 @@ |
|||||||
#!/bin/bash |
#!/bin/bash |
||||||
|
# |
||||||
|
# Note: libvirt hook scripts execute with uid=0(root) gid=0(root) for all |
||||||
|
# operations, i.e., started, stopped. |
||||||
|
# |
||||||
|
|
||||||
# TODO: re-enable after the possible libvirt deadlock is fixed. |
function log { |
||||||
exit 0 |
echo "$(date +"%Y/%m/%d %T"):libvirt_hooks:ws; $@" >>/var/log/anvil.log; |
||||||
|
} |
||||||
{ |
|
||||||
echo "wsargs=$@" |
log "wsargs=$@" |
||||||
|
|
||||||
domain_xml=$(</dev/stdin) |
domain_xml=$(</dev/stdin) |
||||||
guest_name="$1" |
operation="$2" |
||||||
operation="$2" |
|
||||||
|
# Operation migrate will: |
||||||
# Operation migrate will: |
# 1. Trigger migrate->prepare->start->started operation on the destination host. |
||||||
# 1. Trigger migrate->prepare->start->started operation on the destination host. |
# 2. Trigger stopped->release operations on the source host. |
||||||
# 2. Trigger stopped->release operations on the source host. |
if [[ ! $operation =~ ^(started|stopped)$ ]] |
||||||
if [[ "$operation" == "started" || "$operation" == "stopped" ]] |
then |
||||||
then |
exit |
||||||
ws_open_flag="" |
fi |
||||||
ws_port_flag="" |
|
||||||
ws_suuid_flag="" |
guest_uuid=$( sed -En "s/^.*<uuid>([^[:space:]]+)<.*$/\1/p" <<<"$domain_xml" ) |
||||||
|
ws_server_uuid_flag="--server-uuid $guest_uuid" |
||||||
if [[ "$operation" == "started" ]] |
|
||||||
then |
ws_open_flag="" |
||||||
ws_open_flag="--open" |
ws_port_flag="" |
||||||
|
|
||||||
# Cannot call $ virsh vncdisplay... because libvirt hooks |
if [[ $operation == "started" ]] |
||||||
# cannot call anything related to libvirt, i.e., virsh, because |
then |
||||||
# a deadlock will happen. |
ws_open_flag="--open" |
||||||
server_vnc_port=$( grep "<graphics.*type=['\"]vnc['\"]" <<<$domain_xml | grep -oPm1 "(?<=port=['\"])\d+" ) |
|
||||||
ws_port_flag="--server-vnc-port ${server_vnc_port}" |
# Cannot call $ virsh vncdisplay... because libvirt hooks |
||||||
|
# cannot call anything related to libvirt, i.e., virsh, because |
||||||
server_uuid=$( grep -oPm1 "(?<=<uuid>)[^\s]+(?=<)" <<<$domain_xml ) |
# a deadlock will happen. |
||||||
ws_suuid_flag="--server-uuid ${server_uuid}" |
server_vnc_port=$( sed -En "s/^.*<graphics.*type=['\"]vnc['\"].*port=['\"]([[:digit:]]+)['\"].*$/\1/p" <<<"$domain_xml" ) |
||||||
|
ws_port_flag="--server-vnc-port $server_vnc_port" |
||||||
local_host_uuid=$(</etc/anvil/host.uuid) |
fi |
||||||
|
|
||||||
update_sql="UPDATE servers SET server_host_uuid = '$local_host_uuid' WHERE server_name = '$guest_name';" |
ws_command_args="$ws_server_uuid_flag $ws_port_flag $ws_open_flag" |
||||||
echo "wsupdate=$update_sql" |
|
||||||
anvil-access-module --query "$update_sql" --mode write |
log "wscmd_args=$ws_command_args" |
||||||
fi |
|
||||||
|
anvil-manage-vnc-pipe $ws_command_args & |
||||||
ws_command_args="--server \"$guest_name\" $ws_suuid_flag --server-host-uuid local $ws_port_flag --component ws $ws_open_flag" |
|
||||||
echo "wscmd=$ws_command_args" |
|
||||||
striker-manage-vnc-pipes --server "$guest_name" $ws_suuid_flag --server-host-uuid local $ws_port_flag --component ws $ws_open_flag |
|
||||||
echo "wscmd_exit=$?" |
|
||||||
|
|
||||||
# Don't interrupt libvirt regardless of whether websockify gets setup |
|
||||||
# successfully. |
|
||||||
exit 0 |
|
||||||
fi |
|
||||||
} >>/var/log/anvil.log |
|
||||||
|
File diff suppressed because one or more lines are too long
@ -1,52 +0,0 @@ |
|||||||
import assert from 'assert'; |
|
||||||
import { RequestHandler } from 'express'; |
|
||||||
|
|
||||||
import { REP_UUID } from '../../consts'; |
|
||||||
|
|
||||||
import { vncpipe } from '../../accessModule'; |
|
||||||
import { sanitize } from '../../sanitize'; |
|
||||||
import { stderr, stdoutVar } from '../../shell'; |
|
||||||
|
|
||||||
export const manageVncSshTunnel: RequestHandler< |
|
||||||
unknown, |
|
||||||
{ forwardPort: number; protocol: string }, |
|
||||||
{ open: boolean; serverUuid: string } |
|
||||||
> = async (request, response) => { |
|
||||||
const { body: { open: rOpen, serverUuid: rServerUuid } = {} } = request; |
|
||||||
|
|
||||||
const isOpen = sanitize(rOpen, 'boolean'); |
|
||||||
const serverUuid = sanitize(rServerUuid, 'string'); |
|
||||||
|
|
||||||
try { |
|
||||||
assert( |
|
||||||
REP_UUID.test(serverUuid), |
|
||||||
`Server UUID must be a valid UUIDv4; got: [${serverUuid}]`, |
|
||||||
); |
|
||||||
} catch (error) { |
|
||||||
stderr(`Assert input failed when manage VNC SSH tunnel; CAUSE: ${error}`); |
|
||||||
|
|
||||||
return response.status(400).send(); |
|
||||||
} |
|
||||||
|
|
||||||
stdoutVar({ isOpen, serverUuid }, 'Manage VNC SSH tunnel params: '); |
|
||||||
|
|
||||||
let operation = 'close'; |
|
||||||
|
|
||||||
if (isOpen) { |
|
||||||
operation = 'open'; |
|
||||||
} |
|
||||||
|
|
||||||
let rsbody: { forwardPort: number; protocol: string }; |
|
||||||
|
|
||||||
try { |
|
||||||
rsbody = await vncpipe(serverUuid, isOpen); |
|
||||||
} catch (error) { |
|
||||||
stderr( |
|
||||||
`Failed to ${operation} VNC SSH tunnel to server ${serverUuid}; CAUSE: ${error}`, |
|
||||||
); |
|
||||||
|
|
||||||
return response.status(500).send(); |
|
||||||
} |
|
||||||
|
|
||||||
return response.json(rsbody); |
|
||||||
}; |
|
@ -0,0 +1,52 @@ |
|||||||
|
import { ServerResponse } from 'http'; |
||||||
|
import { createProxyMiddleware } from 'http-proxy-middleware'; |
||||||
|
|
||||||
|
import { P_UUID } from '../lib/consts'; |
||||||
|
|
||||||
|
import { stderr, stdout } from '../lib/shell'; |
||||||
|
import { getVncinfo } from '../lib/accessModule'; |
||||||
|
|
||||||
|
const WS_SVR_VNC_URL_PREFIX = '/ws/server/vnc'; |
||||||
|
|
||||||
|
export const proxyServerVnc = createProxyMiddleware({ |
||||||
|
changeOrigin: true, |
||||||
|
pathFilter: `${WS_SVR_VNC_URL_PREFIX}/*`, |
||||||
|
router: async (request) => { |
||||||
|
const { url = '' } = request; |
||||||
|
|
||||||
|
const serverUuid = url.replace( |
||||||
|
new RegExp(`^${WS_SVR_VNC_URL_PREFIX}/(${P_UUID})`), |
||||||
|
'$1', |
||||||
|
); |
||||||
|
|
||||||
|
stdout(`Got param [${serverUuid}] from [${url}]`); |
||||||
|
|
||||||
|
let domain: string; |
||||||
|
let port: number; |
||||||
|
let protocol: string; |
||||||
|
|
||||||
|
try { |
||||||
|
({ domain, port, protocol } = await getVncinfo(serverUuid)); |
||||||
|
} catch (error) { |
||||||
|
throw new Error( |
||||||
|
`Failed to get server ${serverUuid} VNC info; CAUSE: ${error}`, |
||||||
|
); |
||||||
|
} |
||||||
|
|
||||||
|
return { host: domain, protocol, port }; |
||||||
|
}, |
||||||
|
on: { |
||||||
|
error: (error, request, response) => { |
||||||
|
stderr(String(error)); |
||||||
|
|
||||||
|
(response as ServerResponse).writeHead(404).end(); |
||||||
|
}, |
||||||
|
}, |
||||||
|
ws: true, |
||||||
|
}); |
||||||
|
|
||||||
|
export const proxyServerVncUpgrade = |
||||||
|
proxyServerVnc.upgrade ?? |
||||||
|
(() => { |
||||||
|
stdout('No upgrade handler for server VNC connection(s).'); |
||||||
|
}); |
@ -1 +0,0 @@ |
|||||||
self.__BUILD_MANIFEST=function(s,a,c,e,t,n,i,d,f,u,k,b,h,j,r,g){return{__rewrites:{beforeFiles:[],afterFiles:[],fallback:[]},"/":[s,c,e,i,d,b,"static/chunks/717-8bd60b96d67fd464.js",a,t,n,f,h,j,"static/chunks/pages/index-2eaaa0d56c4c4f15.js"],"/_error":["static/chunks/pages/_error-2280fa386d040b66.js"],"/anvil":[s,c,e,i,d,b,a,t,n,f,h,"static/chunks/pages/anvil-fbef5033b416c0dd.js"],"/config":[s,c,e,u,a,t,n,k,"static/chunks/pages/config-0381f0311f2b572a.js"],"/file-manager":[s,c,e,i,"static/chunks/768-9ee3dcb62beecb53.js",a,t,"static/chunks/pages/file-manager-21a667deb0a17960.js"],"/init":[s,c,i,d,u,r,a,t,n,f,g,"static/chunks/pages/init-e60db40234cb8025.js"],"/login":[s,c,e,a,t,n,k,"static/chunks/pages/login-398afa43b3c54927.js"],"/manage-element":[s,c,e,i,d,u,r,"static/chunks/195-d5fd184cc249f755.js",a,t,n,f,k,g,"static/chunks/pages/manage-element-79adadce1efb772f.js"],"/server":[s,e,"static/chunks/227-a3756585a7ef09ae.js",a,j,"static/chunks/pages/server-bf2d408a68a09e13.js"],sortedPages:["/","/_app","/_error","/anvil","/config","/file-manager","/init","/login","/manage-element","/server"]}}("static/chunks/382-f51344f6f9208507.js","static/chunks/746-a9b6e396d532b9a2.js","static/chunks/483-f8013e38dca1620d.js","static/chunks/894-e57948de523bcf96.js","static/chunks/203-2f903a41f9b4e31e.js","static/chunks/899-83e9de2a35c6bcf0.js","static/chunks/182-08683bbe95fbb010.js","static/chunks/614-0ce04fd295045ffe.js","static/chunks/140-ec935fb15330b98a.js","static/chunks/644-c7c6e21c71345aed.js","static/chunks/903-dc2a40be612a10c3.js","static/chunks/485-77798bccc4308d0e.js","static/chunks/116-dfb7b81f7280bb6d.js","static/chunks/906-86856e16ad160b72.js","static/chunks/676-6159ce853338cc1f.js","static/chunks/692-59a4ae2829590f4c.js"),self.__BUILD_MANIFEST_CB&&self.__BUILD_MANIFEST_CB(); |
|
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
@ -0,0 +1 @@ |
|||||||
|
"use strict";(self.webpackChunk_N_E=self.webpackChunk_N_E||[]).push([[665],{4665:function(e,n,r){r.r(n);var t=r(5893),o=r(4460),c=r(7294);function i(e,n,r){return n in e?Object.defineProperty(e,n,{value:r,enumerable:!0,configurable:!0,writable:!0}):e[n]=r,e}var u=function(e){(null===e||void 0===e?void 0:e.current)&&(e.current.disconnect(),e.current=null)},l=function(e){var n=e.onConnect,r=e.onDisconnect,l=e.rfb,s=e.rfbConnectArgs,f=e.rfbScreen,v=e.url;(0,c.useEffect)((function(){if(s){var e=s.url,t=void 0===e?v:e;if(!t)return;var c=function(e){for(var n=1;n<arguments.length;n++){var r=null!=arguments[n]?arguments[n]:{},t=Object.keys(r);"function"===typeof Object.getOwnPropertySymbols&&(t=t.concat(Object.getOwnPropertySymbols(r).filter((function(e){return Object.getOwnPropertyDescriptor(r,e).enumerable})))),t.forEach((function(n){i(e,n,r[n])}))}return e}({onConnect:n,onDisconnect:r,rfb:l,rfbScreen:f,url:t},s);!function(e){var n=e.background,r=void 0===n?"":n,t=e.clipViewport,c=void 0!==t&&t,i=e.compressionLevel,u=void 0===i?2:i,l=e.dragViewport,s=void 0!==l&&l,f=e.focusOnClick,v=void 0!==f&&f,a=e.onConnect,d=e.onDisconnect,b=e.qualityLevel,p=void 0===b?6:b,y=e.resizeSession,w=void 0===y||y,m=e.rfb,h=e.rfbScreen,E=e.scaleViewport,O=void 0===E||E,g=e.showDotCursor,C=void 0!==g&&g,S=e.url,k=e.viewOnly,L=void 0!==k&&k;(null===h||void 0===h?void 0:h.current)&&!(null===m||void 0===m?void 0:m.current)&&(h.current.innerHTML="",m.current=new o.Z(h.current,S),m.current.background=r,m.current.clipViewport=c,m.current.compressionLevel=u,m.current.dragViewport=s,m.current.focusOnClick=v,m.current.qualityLevel=p,m.current.resizeSession=w,m.current.scaleViewport=O,m.current.showDotCursor=C,m.current.viewOnly=L,a&&m.current.addEventListener("connect",a),d&&m.current.addEventListener("disconnect",d))}(c)}else u(l)}),[v,n,r,l,s,f]),(0,c.useEffect)((function(){return function(){u(l)}}),[l]);return(0,t.jsx)("div",{style:{width:"100%",height:"75vh"},ref:f,onMouseEnter:function(){var e,n;document.activeElement&&(e=document.activeElement,null!=(n=HTMLElement)&&"undefined"!==typeof Symbol&&n[Symbol.hasInstance]?n[Symbol.hasInstance](e):e instanceof n)&&document.activeElement.blur(),(null===l||void 0===l?void 0:l.current)&&l.current.focus()}})};l.displayName="VncDisplay",n.default=l}}]); |
@ -1 +0,0 @@ |
|||||||
"use strict";(self.webpackChunk_N_E=self.webpackChunk_N_E||[]).push([[665],{4665:function(e,n,r){r.r(n);var t=r(5893),c=r(7294),u=r(4460);var o=function(e){var n=(0,c.useRef)(null),r=e.rfb,o=e.url,i=e.viewOnly,s=e.focusOnClick,l=e.clipViewport,a=e.dragViewport,d=e.scaleViewport,v=e.resizeSession,f=e.showDotCursor,m=e.background,p=e.qualityLevel,w=e.compressionLevel,h=e.onDisconnect;(0,c.useEffect)((function(){return n.current?(r.current||(n.current.innerHTML="",r.current=new u.Z(n.current,o),r.current.viewOnly=i,r.current.focusOnClick=s,r.current.clipViewport=l,r.current.dragViewport=a,r.current.resizeSession=v,r.current.scaleViewport=d,r.current.showDotCursor=f,r.current.background=m,r.current.qualityLevel=p,r.current.compressionLevel=w,r.current.addEventListener("disconnect",h)),r.current?function(){r.current&&(r.current.disconnect(),r.current=void 0)}:void 0):function(){r.current&&(null===r||void 0===r||r.current.disconnect(),r.current=void 0)}}),[r]);return(0,t.jsx)("div",{style:{width:"100%",height:"75vh"},ref:n,onMouseEnter:function(){var e,n;document.activeElement&&(e=document.activeElement,null!=(n=HTMLElement)&&"undefined"!==typeof Symbol&&n[Symbol.hasInstance]?n[Symbol.hasInstance](e):e instanceof n)&&document.activeElement.blur(),(null===r||void 0===r?void 0:r.current)&&r.current.focus()}})};n.default=(0,c.memo)(o)}}]); |
|
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
@ -1 +1 @@ |
|||||||
!function(){"use strict";var e={},t={};function n(r){var o=t[r];if(void 0!==o)return o.exports;var u=t[r]={exports:{}},i=!0;try{e[r].call(u.exports,u,u.exports,n),i=!1}finally{i&&delete t[r]}return u.exports}n.m=e,function(){var e=[];n.O=function(t,r,o,u){if(!r){var i=1/0;for(l=0;l<e.length;l++){r=e[l][0],o=e[l][1],u=e[l][2];for(var f=!0,c=0;c<r.length;c++)(!1&u||i>=u)&&Object.keys(n.O).every((function(e){return n.O[e](r[c])}))?r.splice(c--,1):(f=!1,u<i&&(i=u));if(f){e.splice(l--,1);var a=o();void 0!==a&&(t=a)}}return t}u=u||0;for(var l=e.length;l>0&&e[l-1][2]>u;l--)e[l]=e[l-1];e[l]=[r,o,u]}}(),n.n=function(e){var t=e&&e.__esModule?function(){return e.default}:function(){return e};return n.d(t,{a:t}),t},function(){var e,t=Object.getPrototypeOf?function(e){return Object.getPrototypeOf(e)}:function(e){return e.__proto__};n.t=function(r,o){if(1&o&&(r=this(r)),8&o)return r;if("object"===typeof r&&r){if(4&o&&r.__esModule)return r;if(16&o&&"function"===typeof r.then)return r}var u=Object.create(null);n.r(u);var i={};e=e||[null,t({}),t([]),t(t)];for(var f=2&o&&r;"object"==typeof f&&!~e.indexOf(f);f=t(f))Object.getOwnPropertyNames(f).forEach((function(e){i[e]=function(){return r[e]}}));return i.default=function(){return r},n.d(u,i),u}}(),n.d=function(e,t){for(var r in t)n.o(t,r)&&!n.o(e,r)&&Object.defineProperty(e,r,{enumerable:!0,get:t[r]})},n.f={},n.e=function(e){return Promise.all(Object.keys(n.f).reduce((function(t,r){return n.f[r](e,t),t}),[]))},n.u=function(e){return"static/chunks/"+e+"."+{460:"91d31c8392f2cdc4",665:"b5fa539eea66745a"}[e]+".js"},n.miniCssF=function(e){return"static/css/fc4c5db74ac4baf3.css"},n.g=function(){if("object"===typeof globalThis)return globalThis;try{return this||new Function("return this")()}catch(e){if("object"===typeof window)return window}}(),n.o=function(e,t){return Object.prototype.hasOwnProperty.call(e,t)},function(){var e={},t="_N_E:";n.l=function(r,o,u,i){if(e[r])e[r].push(o);else{var f,c;if(void 0!==u)for(var a=document.getElementsByTagName("script"),l=0;l<a.length;l++){var s=a[l];if(s.getAttribute("src")==r||s.getAttribute("data-webpack")==t+u){f=s;break}}f||(c=!0,(f=document.createElement("script")).charset="utf-8",f.timeout=120,n.nc&&f.setAttribute("nonce",n.nc),f.setAttribute("data-webpack",t+u),f.src=r),e[r]=[o];var d=function(t,n){f.onerror=f.onload=null,clearTimeout(p);var o=e[r];if(delete e[r],f.parentNode&&f.parentNode.removeChild(f),o&&o.forEach((function(e){return e(n)})),t)return t(n)},p=setTimeout(d.bind(null,void 0,{type:"timeout",target:f}),12e4);f.onerror=d.bind(null,f.onerror),f.onload=d.bind(null,f.onload),c&&document.head.appendChild(f)}}}(),n.r=function(e){"undefined"!==typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(e,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(e,"__esModule",{value:!0})},n.p="/_next/",function(){var e={272:0};n.f.j=function(t,r){var o=n.o(e,t)?e[t]:void 0;if(0!==o)if(o)r.push(o[2]);else if(272!=t){var u=new Promise((function(n,r){o=e[t]=[n,r]}));r.push(o[2]=u);var i=n.p+n.u(t),f=new Error;n.l(i,(function(r){if(n.o(e,t)&&(0!==(o=e[t])&&(e[t]=void 0),o)){var u=r&&("load"===r.type?"missing":r.type),i=r&&r.target&&r.target.src;f.message="Loading chunk "+t+" failed.\n("+u+": "+i+")",f.name="ChunkLoadError",f.type=u,f.request=i,o[1](f)}}),"chunk-"+t,t)}else e[t]=0},n.O.j=function(t){return 0===e[t]};var t=function(t,r){var o,u,i=r[0],f=r[1],c=r[2],a=0;if(i.some((function(t){return 0!==e[t]}))){for(o in f)n.o(f,o)&&(n.m[o]=f[o]);if(c)var l=c(n)}for(t&&t(r);a<i.length;a++)u=i[a],n.o(e,u)&&e[u]&&e[u][0](),e[u]=0;return n.O(l)},r=self.webpackChunk_N_E=self.webpackChunk_N_E||[];r.forEach(t.bind(null,0)),r.push=t.bind(null,r.push.bind(r))}()}(); |
!function(){"use strict";var e={},t={};function n(r){var o=t[r];if(void 0!==o)return o.exports;var u=t[r]={exports:{}},i=!0;try{e[r].call(u.exports,u,u.exports,n),i=!1}finally{i&&delete t[r]}return u.exports}n.m=e,function(){var e=[];n.O=function(t,r,o,u){if(!r){var i=1/0;for(l=0;l<e.length;l++){r=e[l][0],o=e[l][1],u=e[l][2];for(var f=!0,c=0;c<r.length;c++)(!1&u||i>=u)&&Object.keys(n.O).every((function(e){return n.O[e](r[c])}))?r.splice(c--,1):(f=!1,u<i&&(i=u));if(f){e.splice(l--,1);var a=o();void 0!==a&&(t=a)}}return t}u=u||0;for(var l=e.length;l>0&&e[l-1][2]>u;l--)e[l]=e[l-1];e[l]=[r,o,u]}}(),n.n=function(e){var t=e&&e.__esModule?function(){return e.default}:function(){return e};return n.d(t,{a:t}),t},function(){var e,t=Object.getPrototypeOf?function(e){return Object.getPrototypeOf(e)}:function(e){return e.__proto__};n.t=function(r,o){if(1&o&&(r=this(r)),8&o)return r;if("object"===typeof r&&r){if(4&o&&r.__esModule)return r;if(16&o&&"function"===typeof r.then)return r}var u=Object.create(null);n.r(u);var i={};e=e||[null,t({}),t([]),t(t)];for(var f=2&o&&r;"object"==typeof f&&!~e.indexOf(f);f=t(f))Object.getOwnPropertyNames(f).forEach((function(e){i[e]=function(){return r[e]}}));return i.default=function(){return r},n.d(u,i),u}}(),n.d=function(e,t){for(var r in t)n.o(t,r)&&!n.o(e,r)&&Object.defineProperty(e,r,{enumerable:!0,get:t[r]})},n.f={},n.e=function(e){return Promise.all(Object.keys(n.f).reduce((function(t,r){return n.f[r](e,t),t}),[]))},n.u=function(e){return"static/chunks/"+e+"."+{460:"91d31c8392f2cdc4",665:"ae67dcf3c1b6f7f6"}[e]+".js"},n.miniCssF=function(e){return"static/css/fc4c5db74ac4baf3.css"},n.g=function(){if("object"===typeof globalThis)return globalThis;try{return this||new Function("return this")()}catch(e){if("object"===typeof window)return window}}(),n.o=function(e,t){return Object.prototype.hasOwnProperty.call(e,t)},function(){var e={},t="_N_E:";n.l=function(r,o,u,i){if(e[r])e[r].push(o);else{var f,c;if(void 0!==u)for(var a=document.getElementsByTagName("script"),l=0;l<a.length;l++){var s=a[l];if(s.getAttribute("src")==r||s.getAttribute("data-webpack")==t+u){f=s;break}}f||(c=!0,(f=document.createElement("script")).charset="utf-8",f.timeout=120,n.nc&&f.setAttribute("nonce",n.nc),f.setAttribute("data-webpack",t+u),f.src=r),e[r]=[o];var d=function(t,n){f.onerror=f.onload=null,clearTimeout(p);var o=e[r];if(delete e[r],f.parentNode&&f.parentNode.removeChild(f),o&&o.forEach((function(e){return e(n)})),t)return t(n)},p=setTimeout(d.bind(null,void 0,{type:"timeout",target:f}),12e4);f.onerror=d.bind(null,f.onerror),f.onload=d.bind(null,f.onload),c&&document.head.appendChild(f)}}}(),n.r=function(e){"undefined"!==typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(e,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(e,"__esModule",{value:!0})},n.p="/_next/",function(){var e={272:0};n.f.j=function(t,r){var o=n.o(e,t)?e[t]:void 0;if(0!==o)if(o)r.push(o[2]);else if(272!=t){var u=new Promise((function(n,r){o=e[t]=[n,r]}));r.push(o[2]=u);var i=n.p+n.u(t),f=new Error;n.l(i,(function(r){if(n.o(e,t)&&(0!==(o=e[t])&&(e[t]=void 0),o)){var u=r&&("load"===r.type?"missing":r.type),i=r&&r.target&&r.target.src;f.message="Loading chunk "+t+" failed.\n("+u+": "+i+")",f.name="ChunkLoadError",f.type=u,f.request=i,o[1](f)}}),"chunk-"+t,t)}else e[t]=0},n.O.j=function(t){return 0===e[t]};var t=function(t,r){var o,u,i=r[0],f=r[1],c=r[2],a=0;if(i.some((function(t){return 0!==e[t]}))){for(o in f)n.o(f,o)&&(n.m[o]=f[o]);if(c)var l=c(n)}for(t&&t(r);a<i.length;a++)u=i[a],n.o(e,u)&&e[u]&&e[u][0](),e[u]=0;return n.O(l)},r=self.webpackChunk_N_E=self.webpackChunk_N_E||[];r.forEach(t.bind(null,0)),r.push=t.bind(null,r.push.bind(r))}()}(); |
@ -0,0 +1 @@ |
|||||||
|
self.__BUILD_MANIFEST=function(s,c,a,e,t,n,i,f,d,u,b,k,h,j,r,g){return{__rewrites:{beforeFiles:[],afterFiles:[],fallback:[]},"/":[s,a,e,i,f,k,"static/chunks/717-8bd60b96d67fd464.js",c,t,n,d,h,j,"static/chunks/pages/index-1f8f0ad3b3894dbc.js"],"/_error":["static/chunks/pages/_error-2280fa386d040b66.js"],"/anvil":[s,a,e,i,f,k,c,t,n,d,h,"static/chunks/pages/anvil-c1177b17efcafc34.js"],"/config":[s,a,e,u,c,t,n,b,"static/chunks/pages/config-0ecdb2b2b3f8c089.js"],"/file-manager":[s,a,e,i,"static/chunks/768-9ee3dcb62beecb53.js",c,t,"static/chunks/pages/file-manager-1a707639a4834587.js"],"/init":[s,a,i,f,u,r,c,t,n,d,g,"static/chunks/pages/init-ae1befa8975f7914.js"],"/login":[s,a,e,c,t,n,b,"static/chunks/pages/login-b5de0cd2f49998d6.js"],"/manage-element":[s,a,e,i,f,u,r,"static/chunks/195-d5fd184cc249f755.js",c,t,n,d,b,g,"static/chunks/pages/manage-element-2b1d8792c2a5bf47.js"],"/server":[s,e,"static/chunks/227-a3756585a7ef09ae.js",c,j,"static/chunks/pages/server-db52258419acacf3.js"],sortedPages:["/","/_app","/_error","/anvil","/config","/file-manager","/init","/login","/manage-element","/server"]}}("static/chunks/382-f51344f6f9208507.js","static/chunks/62-532ed713980da8db.js","static/chunks/483-f8013e38dca1620d.js","static/chunks/894-e57948de523bcf96.js","static/chunks/780-e8b3396d257460a4.js","static/chunks/899-83e9de2a35c6bcf0.js","static/chunks/182-08683bbe95fbb010.js","static/chunks/614-0ce04fd295045ffe.js","static/chunks/140-ec935fb15330b98a.js","static/chunks/644-c7c6e21c71345aed.js","static/chunks/903-dc2a40be612a10c3.js","static/chunks/485-77798bccc4308d0e.js","static/chunks/825-d34974d169ea09cc.js","static/chunks/94-db0af749b6e45543.js","static/chunks/676-6159ce853338cc1f.js","static/chunks/692-59a4ae2829590f4c.js"),self.__BUILD_MANIFEST_CB&&self.__BUILD_MANIFEST_CB(); |
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
@ -0,0 +1,6 @@ |
|||||||
|
type FullSizeProps = { |
||||||
|
onClickCloseButton?: import('@mui/material').IconButtonProps['onClick']; |
||||||
|
serverUUID: string; |
||||||
|
serverName: string; |
||||||
|
vncReconnectTimerStart?: number; |
||||||
|
}; |
@ -0,0 +1,32 @@ |
|||||||
|
type RfbRef = import('react').MutableRefObject< |
||||||
|
typeof import('@novnc/novnc/core/rfb') | null |
||||||
|
>; |
||||||
|
|
||||||
|
type RfbScreenRef = import('react').MutableRefObject<HTMLDivElement | null>; |
||||||
|
|
||||||
|
type RfbConnectArgs = { |
||||||
|
background?: string; |
||||||
|
clipViewport?: boolean; |
||||||
|
compressionLevel?: number; |
||||||
|
dragViewport?: boolean; |
||||||
|
focusOnClick?: boolean; |
||||||
|
onConnect?: () => void; |
||||||
|
onDisconnect?: (event: { detail: { clean: boolean } }) => void; |
||||||
|
qualityLevel?: number; |
||||||
|
resizeSession?: boolean; |
||||||
|
rfb: RfbRef; |
||||||
|
rfbScreen: RfbScreenRef; |
||||||
|
scaleViewport?: boolean; |
||||||
|
showDotCursor?: boolean; |
||||||
|
url: string; |
||||||
|
viewOnly?: boolean; |
||||||
|
}; |
||||||
|
|
||||||
|
type RfbConnectFunction = (args: RfbConnectArgs) => void; |
||||||
|
|
||||||
|
type RfbDisconnectFunction = (rfb: RfbRef) => void; |
||||||
|
|
||||||
|
type VncDisplayProps = Pick<RfbConnectArgs, 'rfb' | 'rfbScreen'> & |
||||||
|
Partial<RfbConnectArgs> & { |
||||||
|
rfbConnectArgs?: Partial<RfbConnectArgs>; |
||||||
|
}; |
@ -0,0 +1,519 @@ |
|||||||
|
#!/usr/bin/perl |
||||||
|
# |
||||||
|
# Manages VNC ports for server VMs that have VNC enabled. |
||||||
|
# |
||||||
|
|
||||||
|
use strict; |
||||||
|
use warnings; |
||||||
|
use Anvil::Tools; |
||||||
|
use Data::Dumper; |
||||||
|
|
||||||
|
$| = 1; |
||||||
|
|
||||||
|
my $THIS_FILE = ($0 =~ /^.*\/(.*)$/)[0]; |
||||||
|
my $running_directory = ($0 =~ /^(.*?)\/$THIS_FILE$/)[0]; |
||||||
|
if (($running_directory =~ /^\./) && ($ENV{PWD})) |
||||||
|
{ |
||||||
|
$running_directory =~ s/^\./$ENV{PWD}/; |
||||||
|
} |
||||||
|
|
||||||
|
my $anvil = Anvil::Tools->new(); |
||||||
|
|
||||||
|
my $echo = $anvil->data->{path}{exe}{'echo'}; |
||||||
|
my $grep = $anvil->data->{path}{exe}{'grep'}; |
||||||
|
my $kill = $anvil->data->{path}{exe}{'kill'}; |
||||||
|
my $pgrep = $anvil->data->{path}{exe}{'pgrep'}; |
||||||
|
my $ss = $anvil->data->{path}{exe}{'ss'}; |
||||||
|
my $sed = $anvil->data->{path}{exe}{'sed'}; |
||||||
|
my $websockify = $anvil->data->{path}{exe}{'websockify'}; |
||||||
|
|
||||||
|
$anvil->Get->switches; |
||||||
|
|
||||||
|
$anvil->Database->connect; |
||||||
|
$anvil->Log->entry({ source => $THIS_FILE, line => __LINE__, level => 2, secure => 0, key => "log_0132" }); |
||||||
|
if (not $anvil->data->{sys}{database}{connections}) |
||||||
|
{ |
||||||
|
# No databases, exit. |
||||||
|
$anvil->Log->entry({ source => $THIS_FILE, line => __LINE__, level => 0, 'print' => 1, priority => "err", key => "error_0003" }); |
||||||
|
$anvil->nice_exit({ exit_code => 1 }); |
||||||
|
} |
||||||
|
|
||||||
|
my $switch_debug = $anvil->data->{switches}{'debug'}; |
||||||
|
my $open = $anvil->data->{switches}{'open'}; |
||||||
|
my $server = $anvil->data->{switches}{'server'}; |
||||||
|
my $server_uuid = $anvil->data->{switches}{'server-uuid'}; |
||||||
|
my $server_vnc_port = $anvil->data->{switches}{'server-vnc-port'}; |
||||||
|
|
||||||
|
if (defined $server) |
||||||
|
{ |
||||||
|
$server_uuid //= is_uuid_v4($server) ? $server : $anvil->Get->server_uuid_from_name({ server_name => $server }); |
||||||
|
} |
||||||
|
|
||||||
|
$anvil->Log->variables({ source => $THIS_FILE, line => __LINE__, level => $switch_debug, list => { |
||||||
|
open => $open, |
||||||
|
server => $server, |
||||||
|
server_uuid => $server_uuid, |
||||||
|
server_vnc_port => $server_vnc_port |
||||||
|
} }); |
||||||
|
|
||||||
|
my $map_to_operation = { start => \&start_pipe, stop => \&stop_pipe }; |
||||||
|
|
||||||
|
if ($server_uuid) |
||||||
|
{ |
||||||
|
my $operation = $open ? "start" : "stop"; |
||||||
|
|
||||||
|
$anvil->Log->variables({ source => $THIS_FILE, line => __LINE__, level => $switch_debug, list => { operation => $operation } }); |
||||||
|
|
||||||
|
my ($rcode, $err_msg) = $map_to_operation->{$operation}({ |
||||||
|
debug => $switch_debug, |
||||||
|
svr_uuid => $server_uuid, |
||||||
|
svr_vnc_port => $server_vnc_port, |
||||||
|
}); |
||||||
|
|
||||||
|
if ($rcode) |
||||||
|
{ |
||||||
|
$anvil->Log->entry({ |
||||||
|
source => $THIS_FILE, |
||||||
|
line => __LINE__, |
||||||
|
level => $switch_debug || 2, |
||||||
|
raw => "[ Error ] - Operation $operation failed; CAUSE: $err_msg", |
||||||
|
}); |
||||||
|
} |
||||||
|
|
||||||
|
$anvil->nice_exit({ exit_code => $rcode }); |
||||||
|
} |
||||||
|
|
||||||
|
$anvil->nice_exit({ exit_code => 0 }); |
||||||
|
|
||||||
|
# |
||||||
|
# Functions |
||||||
|
# |
||||||
|
|
||||||
|
sub build_find_available_port_call |
||||||
|
{ |
||||||
|
my $parameters = shift; |
||||||
|
my $debug = $parameters->{debug} || 3; |
||||||
|
my $start = $parameters->{start}; |
||||||
|
my $step_operator = $parameters->{step_operator} // "+"; |
||||||
|
my $step_size = $parameters->{step_size} || 1; |
||||||
|
|
||||||
|
$anvil->Log->variables({ source => $THIS_FILE, line => __LINE__, level => $debug, list => $parameters, prefix => "build_find_available_port_call" }); |
||||||
|
|
||||||
|
return (1) if ( (not $step_operator =~ /^[+-]$/) |
||||||
|
|| (not is_int($step_size)) || ($step_size < 1) ); |
||||||
|
|
||||||
|
my $call = "ss_output=\$($ss -ant) && port=${start} && while $grep -Eq \":\${port}[[:space:]]+[^[:space:]]+\" <<<\$ss_output; do (( port ${step_operator}= $step_size )); done && $echo \$port"; |
||||||
|
|
||||||
|
return (0, $call); |
||||||
|
} |
||||||
|
|
||||||
|
sub build_vncinfo_variable_name |
||||||
|
{ |
||||||
|
my ($svr_uuid) = @_; |
||||||
|
|
||||||
|
return "server::${svr_uuid}::vncinfo"; |
||||||
|
} |
||||||
|
|
||||||
|
sub call |
||||||
|
{ |
||||||
|
my $parameters = shift; |
||||||
|
my $background = $parameters->{background} || 0; |
||||||
|
my $call = $parameters->{call}; |
||||||
|
my $debug = $parameters->{debug} || 3; |
||||||
|
|
||||||
|
$anvil->Log->variables({ source => $THIS_FILE, line => __LINE__, level => $debug, list => $parameters, prefix => "call" }); |
||||||
|
|
||||||
|
return (1) if ( (not defined $call) || ($call eq "") ); |
||||||
|
|
||||||
|
my ($output, $rcode) = $anvil->System->call({ background => $background, shell_call => $call }); |
||||||
|
|
||||||
|
$anvil->Log->variables({ source => $THIS_FILE, line => __LINE__, level => $debug, list => { |
||||||
|
output => $output, |
||||||
|
rcode => $rcode |
||||||
|
} }); |
||||||
|
|
||||||
|
# Output order reversed keep returns consistent. |
||||||
|
return ($rcode, $output); |
||||||
|
} |
||||||
|
|
||||||
|
sub find_available_port |
||||||
|
{ |
||||||
|
my $parameters = shift; |
||||||
|
my $debug = $parameters->{debug} || 3; |
||||||
|
|
||||||
|
$anvil->Log->variables({ source => $THIS_FILE, line => __LINE__, level => $debug, list => $parameters, prefix => "find_available_port" }); |
||||||
|
|
||||||
|
my ($build_rcode, $call) = build_find_available_port_call($parameters); |
||||||
|
|
||||||
|
return (1) if ($build_rcode); |
||||||
|
|
||||||
|
return call({ call => $call, debug => $debug }); |
||||||
|
} |
||||||
|
|
||||||
|
sub find_end_port |
||||||
|
{ |
||||||
|
my $parameters = shift; |
||||||
|
my $debug = $parameters->{debug} || 3; |
||||||
|
my $svr_host_name = $parameters->{svr_host_name} // $anvil->data->{sys}{host_name}; |
||||||
|
my $svr_uuid = $parameters->{svr_uuid}; |
||||||
|
|
||||||
|
$anvil->Log->variables({ source => $THIS_FILE, line => __LINE__, level => $debug, list => $parameters, prefix => "find_end_port" }); |
||||||
|
|
||||||
|
return (1) if (not defined $svr_uuid); |
||||||
|
|
||||||
|
my $variable_name = build_vncinfo_variable_name($svr_uuid); |
||||||
|
my $variable_value_pattern = "$svr_host_name:%"; |
||||||
|
|
||||||
|
# Look in the history variables table because libvirt hook operation |
||||||
|
# 'stopped' gets triggered **after** the 'started' operation on the |
||||||
|
# peer host during server migration. |
||||||
|
my $query = " |
||||||
|
SELECT |
||||||
|
SUBSTRING(variable_value, ".$anvil->Database->quote("(\\d+)\$").") |
||||||
|
FROM |
||||||
|
history.variables |
||||||
|
WHERE |
||||||
|
variable_name = ".$anvil->Database->quote($variable_name)." |
||||||
|
AND |
||||||
|
variable_value LIKE ".$anvil->Database->quote($variable_value_pattern)." |
||||||
|
ORDER BY |
||||||
|
modified_date DESC |
||||||
|
LIMIT 1 |
||||||
|
;"; |
||||||
|
|
||||||
|
$anvil->Log->variables({ source => $THIS_FILE, line => __LINE__, level => $debug, list => { query => $query } }); |
||||||
|
|
||||||
|
my $results = $anvil->Database->query({ source => $THIS_FILE, line => __LINE__, query => $query }); |
||||||
|
|
||||||
|
$anvil->Log->entry({ source => $THIS_FILE, line => __LINE__, level => $debug, raw => prettify($results, "results") }); |
||||||
|
|
||||||
|
return (1) if (not @{$results}); |
||||||
|
|
||||||
|
return (0, int($results->[0]->[0])); |
||||||
|
} |
||||||
|
|
||||||
|
sub find_server_vnc_port |
||||||
|
{ |
||||||
|
my $parameters = shift; |
||||||
|
my $debug = $parameters->{debug} || 3; |
||||||
|
my $svr_uuid = $parameters->{svr_uuid}; |
||||||
|
my $svr_vnc_port = $parameters->{svr_vnc_port}; |
||||||
|
|
||||||
|
$anvil->Log->variables({ source => $THIS_FILE, line => __LINE__, level => $debug, list => $parameters, prefix => "find_server_vnc_port" }); |
||||||
|
|
||||||
|
return (1) if (not defined $svr_uuid); |
||||||
|
|
||||||
|
return (0, $svr_vnc_port) if (is_int($svr_vnc_port)); |
||||||
|
|
||||||
|
# If we don't have the server's VNC port, find it in its qemu-kvm process. |
||||||
|
|
||||||
|
my ($rcode, $svr_processes) = $anvil->Server->find_processes({ debug => $debug }); |
||||||
|
|
||||||
|
return (1) if ($rcode); |
||||||
|
|
||||||
|
my $svr_process = $svr_processes->{uuids}{$svr_uuid}; |
||||||
|
my $svr_vnc_alive = $svr_process->{vnc_alive}; |
||||||
|
|
||||||
|
return (1) if (not $svr_vnc_alive); |
||||||
|
|
||||||
|
return (0, $svr_process->{vnc_port}); |
||||||
|
} |
||||||
|
|
||||||
|
sub find_ws_processes |
||||||
|
{ |
||||||
|
my $parameters = shift; |
||||||
|
my $debug = $parameters->{debug} || 3; |
||||||
|
my $ps_name = $parameters->{ps_name} // "websockify"; |
||||||
|
|
||||||
|
$anvil->Log->variables({ source => $THIS_FILE, line => __LINE__, level => $debug, list => $parameters, prefix => "find_ws_processes" }); |
||||||
|
|
||||||
|
my $ps_call = "$pgrep -a '$ps_name' | $sed -En 's/^([[:digit:]]+).*${ps_name}([[:space:]]+(--?[^[:space:]]+))*[[:space:]:]+([[:digit:]]+)[[:space:]:]+([[:digit:]]+).*\$/\\1,\\4,\\5/p'"; |
||||||
|
|
||||||
|
my ($rcode, $output) = call({ call => $ps_call, debug => $debug }); |
||||||
|
|
||||||
|
return (1) if ($rcode); |
||||||
|
|
||||||
|
my $result = { pids => {}, sources => {}, targets => {} }; |
||||||
|
|
||||||
|
foreach my $line (split(/\n/, $output)) |
||||||
|
{ |
||||||
|
chomp($line); |
||||||
|
|
||||||
|
$anvil->Log->variables({ source => $THIS_FILE, line => __LINE__, level => $debug, list => { ws_line => $line } }); |
||||||
|
|
||||||
|
my ($pid, $sport, $tport) = split(/,/, $line); |
||||||
|
|
||||||
|
my $process = { pid => $pid, sport => $sport, tport => $tport }; |
||||||
|
|
||||||
|
set_ws_process({ debug => $debug, entry => $process, entries => $result }); |
||||||
|
} |
||||||
|
|
||||||
|
$anvil->Log->entry({ source => $THIS_FILE, line => __LINE__, level => $debug, raw => prettify($result, "ws_processes") }); |
||||||
|
|
||||||
|
return (0, $result); |
||||||
|
} |
||||||
|
|
||||||
|
sub is_int |
||||||
|
{ |
||||||
|
return defined $_[0] && $_[0] =~ /^\d+$/; |
||||||
|
} |
||||||
|
|
||||||
|
sub is_uuid_v4 |
||||||
|
{ |
||||||
|
return defined $_[0] && $_[0] =~ /[a-f0-9]{8}-[a-f0-9]{4}-[1-5][a-f0-9]{3}-[89ab][a-f0-9]{3}-[a-f0-9]{12}/; |
||||||
|
} |
||||||
|
|
||||||
|
sub prettify |
||||||
|
{ |
||||||
|
my $var_value = shift; |
||||||
|
my $var_name = shift; |
||||||
|
|
||||||
|
local $Data::Dumper::Indent = 1; |
||||||
|
local $Data::Dumper::Varname = $var_name; |
||||||
|
local $Data::Dumper::Terse = (defined $var_name) ? 0 : 1; |
||||||
|
|
||||||
|
return Dumper($var_value); |
||||||
|
} |
||||||
|
|
||||||
|
sub set_entry |
||||||
|
{ |
||||||
|
my $parameters = shift; |
||||||
|
my $debug = $parameters->{debug} || 3; |
||||||
|
my $handle_delete = $parameters->{handle_delete}; |
||||||
|
my $handle_set = $parameters->{handle_set}; |
||||||
|
my $id = $parameters->{id}; |
||||||
|
my $entry = $parameters->{entry}; |
||||||
|
my $entries = $parameters->{entries}; |
||||||
|
|
||||||
|
$anvil->Log->variables({ source => $THIS_FILE, line => __LINE__, level => $debug, list => { |
||||||
|
%$parameters, |
||||||
|
p_entry => prettify($entry), |
||||||
|
p_entries => prettify($entries), |
||||||
|
}, prefix => "set_entry" }); |
||||||
|
|
||||||
|
return (1) if (not defined $entries); |
||||||
|
|
||||||
|
if (defined $entry) |
||||||
|
{ |
||||||
|
$handle_set->($id, $entry, $entries); |
||||||
|
} |
||||||
|
elsif (defined $id) |
||||||
|
{ |
||||||
|
$handle_delete->($id, $entry, $entries); |
||||||
|
} |
||||||
|
|
||||||
|
$anvil->Log->entry({ source => $THIS_FILE, line => __LINE__, level => $debug, raw => prettify($entries, "entries") }); |
||||||
|
|
||||||
|
return (0); |
||||||
|
} |
||||||
|
|
||||||
|
sub set_vncinfo_variable |
||||||
|
{ |
||||||
|
my $parameters = shift; |
||||||
|
my $debug = $parameters->{debug} || 3; |
||||||
|
my $end_port = $parameters->{end_port}; |
||||||
|
my $svr_uuid = $parameters->{svr_uuid}; |
||||||
|
|
||||||
|
$anvil->Log->variables({ source => $THIS_FILE, line => __LINE__, level => $debug, list => $parameters, prefix => "set_vncinfo_variable" }); |
||||||
|
|
||||||
|
my $local_host_name = $anvil->data->{sys}{host_name}; |
||||||
|
|
||||||
|
my ($variable_uuid) = $anvil->Database->insert_or_update_variables({ |
||||||
|
file => $THIS_FILE, |
||||||
|
line => __LINE__, |
||||||
|
variable_name => build_vncinfo_variable_name($svr_uuid), |
||||||
|
variable_value => "${local_host_name}:${end_port}", |
||||||
|
}); |
||||||
|
|
||||||
|
return (1) if (not is_uuid_v4($variable_uuid)); |
||||||
|
|
||||||
|
return (0); |
||||||
|
} |
||||||
|
|
||||||
|
sub set_ws_process |
||||||
|
{ |
||||||
|
my $parameters = shift; |
||||||
|
|
||||||
|
$parameters->{handle_delete} = sub { |
||||||
|
my ($pid, $process, $processes) = @_; |
||||||
|
|
||||||
|
$process = $processes->{pids}{$pid}; |
||||||
|
|
||||||
|
my $sport = $process->{sport}; |
||||||
|
my $tport = $process->{tport}; |
||||||
|
|
||||||
|
delete $processes->{pids}{$pid}; |
||||||
|
delete $processes->{sources}{$sport}; |
||||||
|
delete $processes->{targets}{$tport}; |
||||||
|
}; |
||||||
|
|
||||||
|
$parameters->{handle_set} = sub { |
||||||
|
my ($pid, $process, $processes) = @_; |
||||||
|
|
||||||
|
$pid = $process->{pid}; |
||||||
|
|
||||||
|
my $sport = $process->{sport}; |
||||||
|
my $tport = $process->{tport}; |
||||||
|
|
||||||
|
# The websockify daemon wrapper may remain alive, hence each |
||||||
|
# port can map to mutiple pids. |
||||||
|
my $spids = $processes->{sources}{$sport} // []; |
||||||
|
my $tpids = $processes->{targets}{$tport} // []; |
||||||
|
|
||||||
|
$processes->{pids}{$pid} = $process; |
||||||
|
# Process identifiers are already ordered by pgrep, record them |
||||||
|
# in ascending order. |
||||||
|
$processes->{sources}{$sport} = [@{$spids}, $pid]; |
||||||
|
$processes->{targets}{$tport} = [@{$tpids}, $pid]; |
||||||
|
}; |
||||||
|
|
||||||
|
return set_entry($parameters); |
||||||
|
} |
||||||
|
|
||||||
|
sub start_pipe |
||||||
|
{ |
||||||
|
my $parameters = shift; |
||||||
|
my $debug = $parameters->{debug} || 3; |
||||||
|
my $svr_uuid = $parameters->{svr_uuid}; |
||||||
|
my $svr_vnc_port = $parameters->{svr_vnc_port}; |
||||||
|
|
||||||
|
return (1, __LINE__.": [$svr_uuid]") if (not is_uuid_v4($svr_uuid)); |
||||||
|
|
||||||
|
my $common_params = { debug => $debug }; |
||||||
|
|
||||||
|
my $rcode; |
||||||
|
|
||||||
|
($rcode, $svr_vnc_port) = find_server_vnc_port($parameters); |
||||||
|
|
||||||
|
return ($rcode, __LINE__.": $rcode,[$svr_vnc_port]") if ($rcode); |
||||||
|
|
||||||
|
($rcode, my $ws_processes) = find_ws_processes($common_params); |
||||||
|
|
||||||
|
return ($rcode, __LINE__.": $rcode,[".prettify($ws_processes)."]") if ($rcode); |
||||||
|
|
||||||
|
($rcode, my $ws_pid) = start_ws({ svr_vnc_port => $svr_vnc_port, ws_processes => $ws_processes, %$common_params }); |
||||||
|
|
||||||
|
return ($rcode, __LINE__.": $rcode,[$ws_pid]") if ($rcode); |
||||||
|
|
||||||
|
my $ws_process = $ws_processes->{pids}{$ws_pid}; |
||||||
|
my $ws_sport = $ws_process->{sport}; |
||||||
|
|
||||||
|
($rcode) = set_vncinfo_variable({ end_port => $ws_sport, svr_uuid => $svr_uuid, %$common_params }); |
||||||
|
|
||||||
|
return ($rcode, __LINE__.": [$svr_uuid:$ws_sport],$rcode") if ($rcode); |
||||||
|
|
||||||
|
return (0); |
||||||
|
} |
||||||
|
|
||||||
|
sub start_ws |
||||||
|
{ |
||||||
|
my $parameters = shift; |
||||||
|
my $debug = $parameters->{debug} || 3; |
||||||
|
my $svr_vnc_port = $parameters->{svr_vnc_port}; |
||||||
|
my $ws_processes = $parameters->{ws_processes}; |
||||||
|
my $ws_sport_offset = $parameters->{ws_sport_offset} || 10000; |
||||||
|
|
||||||
|
$anvil->Log->variables({ source => $THIS_FILE, line => __LINE__, level => $debug, list => $parameters, prefix => "start_ws" }); |
||||||
|
|
||||||
|
return (1) if ( (not defined $ws_processes) |
||||||
|
|| (not is_int($svr_vnc_port)) |
||||||
|
|| (not is_int($ws_sport_offset)) ); |
||||||
|
|
||||||
|
my $existing_ws_pids = $ws_processes->{targets}{$svr_vnc_port}; |
||||||
|
|
||||||
|
$anvil->Log->entry({ source => $THIS_FILE, line => __LINE__, level => $debug, raw => prettify($existing_ws_pids, "existing_ws_pids") }); |
||||||
|
|
||||||
|
return (0, $existing_ws_pids->[0]) if (defined $existing_ws_pids); |
||||||
|
|
||||||
|
my $rcode; |
||||||
|
|
||||||
|
($rcode, my $ws_sport) = find_available_port({ debug => $debug, start => int($svr_vnc_port) + int($ws_sport_offset) }); |
||||||
|
|
||||||
|
return ($rcode) if ($rcode); |
||||||
|
|
||||||
|
# The daemon wrapper can tell us whether the daemon started correctly; |
||||||
|
# we won't know this if the process is started in the background. |
||||||
|
my $ws_call = "$websockify -D $ws_sport :$svr_vnc_port &>/dev/null"; |
||||||
|
|
||||||
|
($rcode) = call({ call => $ws_call, debug => $debug }); |
||||||
|
|
||||||
|
return ($rcode) if ($rcode); |
||||||
|
|
||||||
|
# Re-find to locate the new daemon. |
||||||
|
($rcode, my $re_ws_processes) = find_ws_processes({ debug => $debug }); |
||||||
|
|
||||||
|
return ($rcode) if ($rcode); |
||||||
|
|
||||||
|
my $ws_pid = $re_ws_processes->{targets}{$svr_vnc_port}->[0]; |
||||||
|
my $ws_process = $re_ws_processes->{pids}{$ws_pid}; |
||||||
|
|
||||||
|
# Remember the started daemon. |
||||||
|
set_ws_process({ debug => $debug, entry => $ws_process, entries => $ws_processes }); |
||||||
|
|
||||||
|
return (0, $ws_pid); |
||||||
|
} |
||||||
|
|
||||||
|
sub stop_pipe |
||||||
|
{ |
||||||
|
my $parameters = shift; |
||||||
|
my $debug = $parameters->{debug} || 3; |
||||||
|
my $svr_uuid = $parameters->{svr_uuid}; |
||||||
|
my $svr_vnc_port = $parameters->{svr_vnc_port}; |
||||||
|
|
||||||
|
return (1, __LINE__.": [$svr_uuid]") if (not is_uuid_v4($svr_uuid)); |
||||||
|
|
||||||
|
my $common_params = { debug => $debug }; |
||||||
|
|
||||||
|
my $rcode; |
||||||
|
|
||||||
|
($rcode, my $ws_processes) = find_ws_processes($common_params); |
||||||
|
|
||||||
|
return ($rcode, __LINE__.": $rcode,[".prettify($ws_processes)."]") if ($rcode); |
||||||
|
|
||||||
|
($rcode, $svr_vnc_port) = find_server_vnc_port($parameters); |
||||||
|
|
||||||
|
my $ws_pids; |
||||||
|
|
||||||
|
if ($rcode) |
||||||
|
{ |
||||||
|
# The server VNC port is not available during the libvirt hook |
||||||
|
# operation 'stopped'. Try to locate the websockify daemon by |
||||||
|
# its source port. |
||||||
|
($rcode, my $end_port) = find_end_port({ svr_uuid => $svr_uuid, %$common_params }); |
||||||
|
|
||||||
|
return ($rcode, __LINE__.": $rcode,[$end_port]") if ($rcode); |
||||||
|
|
||||||
|
$ws_pids = $ws_processes->{sources}{$end_port}; |
||||||
|
} |
||||||
|
else |
||||||
|
{ |
||||||
|
$ws_pids = $ws_processes->{targets}{$svr_vnc_port}; |
||||||
|
} |
||||||
|
|
||||||
|
foreach my $ws_pid (@{$ws_pids}) |
||||||
|
{ |
||||||
|
($rcode) = stop_ws({ ws_pid => $ws_pid, ws_processes => $ws_processes, %$common_params }); |
||||||
|
|
||||||
|
return ($rcode, __LINE__.": [$ws_pid],$rcode") if ($rcode); |
||||||
|
} |
||||||
|
|
||||||
|
return (0); |
||||||
|
} |
||||||
|
|
||||||
|
sub stop_ws |
||||||
|
{ |
||||||
|
my $parameters = shift; |
||||||
|
my $debug = $parameters->{debug} || 3; |
||||||
|
my $ws_pid = $parameters->{ws_pid}; |
||||||
|
my $ws_processes = $parameters->{ws_processes}; |
||||||
|
|
||||||
|
$anvil->Log->variables({ source => $THIS_FILE, line => __LINE__, level => $debug, list => $parameters, prefix => "stop_ws" }); |
||||||
|
|
||||||
|
return (1) if ( (not is_int($ws_pid)) || (not defined $ws_processes) ); |
||||||
|
|
||||||
|
call({ debug => $debug, call => "$kill $ws_pid || $kill -9 $ws_pid" }); |
||||||
|
|
||||||
|
set_ws_process({ debug => $debug, id => $ws_pid, entries => $ws_processes }); |
||||||
|
|
||||||
|
return (0); |
||||||
|
} |
File diff suppressed because it is too large
Load Diff
@ -1,136 +0,0 @@ |
|||||||
#!/usr/bin/perl |
|
||||||
# |
|
||||||
# Open an SSH tunnel using the Net::OpenSSH module and keep it opened with an infinite loop. |
|
||||||
# |
|
||||||
# Note: this is a temporary solution to avoid directly calling the SSH command. |
|
||||||
# |
|
||||||
|
|
||||||
use strict; |
|
||||||
use warnings; |
|
||||||
use Anvil::Tools; |
|
||||||
use Net::OpenSSH; |
|
||||||
|
|
||||||
$| = 1; |
|
||||||
|
|
||||||
my $THIS_FILE = ($0 =~ /^.*\/(.*)$/)[0]; |
|
||||||
my $running_directory = ($0 =~ /^(.*?)\/$THIS_FILE$/)[0]; |
|
||||||
if (($running_directory =~ /^\./) && ($ENV{PWD})) |
|
||||||
{ |
|
||||||
$running_directory =~ s/^\./$ENV{PWD}/; |
|
||||||
} |
|
||||||
|
|
||||||
my $anvil = Anvil::Tools->new(); |
|
||||||
my $ssh_fh; |
|
||||||
|
|
||||||
sub open_ssh_tunnel |
|
||||||
{ |
|
||||||
my $parameters = shift; |
|
||||||
|
|
||||||
# Required parameters: |
|
||||||
my $remote_user = $parameters->{remote_user}; |
|
||||||
my $target = $parameters->{target}; |
|
||||||
my $forward_local_port = $parameters->{forward_local_port}; |
|
||||||
my $forward_remote_port = $parameters->{forward_remote_port}; |
|
||||||
|
|
||||||
if ((not defined $remote_user) |
|
||||||
or (not defined $target) |
|
||||||
or (not defined $forward_local_port) |
|
||||||
or (not defined $forward_remote_port)) |
|
||||||
{ |
|
||||||
return 1; |
|
||||||
} |
|
||||||
|
|
||||||
# Optional parameters: |
|
||||||
my $port = $parameters->{port} ? $parameters->{port} : 22; |
|
||||||
|
|
||||||
my $ssh_fh_key = $remote_user."\@".$target.":".$port; |
|
||||||
my $query = " |
|
||||||
SELECT anv.anvil_password |
|
||||||
FROM hosts AS hos |
|
||||||
JOIN anvils AS anv |
|
||||||
ON hos.host_uuid = anv.anvil_node1_host_uuid |
|
||||||
OR hos.host_uuid = anv.anvil_node2_host_uuid |
|
||||||
WHERE hos.host_name = ".$anvil->Database->quote($target)." |
|
||||||
;"; |
|
||||||
|
|
||||||
my $password = $anvil->Database->query({ query => $query, source => $THIS_FILE, line => __LINE__ })->[0]->[0]; |
|
||||||
|
|
||||||
my ($output, $error, $return_code) = $anvil->Remote->call({ |
|
||||||
remote_user => $remote_user, |
|
||||||
target => $target, |
|
||||||
password => $password, |
|
||||||
shell_call => $anvil->data->{path}{exe}{echo}." 1", |
|
||||||
no_cache => 1, |
|
||||||
}); |
|
||||||
$anvil->Log->variables({ source => $THIS_FILE, line => __LINE__, level => 2, list => { |
|
||||||
output => $output, |
|
||||||
error => $error, |
|
||||||
return_code => $return_code |
|
||||||
} }); |
|
||||||
|
|
||||||
if ($output eq "1") |
|
||||||
{ |
|
||||||
$ssh_fh = $anvil->data->{cache}{ssh_fh}{$ssh_fh_key}; |
|
||||||
|
|
||||||
delete $anvil->data->{cache}{ssh_fh}{$ssh_fh_key}; |
|
||||||
|
|
||||||
$anvil->Log->variables({ source => $THIS_FILE, line => __LINE__, level => 2, list => { |
|
||||||
is_ssh_fh_defined => defined $ssh_fh ? 1 : 0 |
|
||||||
} }); |
|
||||||
} |
|
||||||
|
|
||||||
$ssh_fh->system({ ssh_opts => [ "-O", "forward", |
|
||||||
"-L0.0.0.0:".$forward_local_port.":0.0.0.0:".$forward_remote_port ] }); |
|
||||||
|
|
||||||
return 0; |
|
||||||
} |
|
||||||
|
|
||||||
sub close_ssh_tunnel |
|
||||||
{ |
|
||||||
if (defined $ssh_fh->disconnect) |
|
||||||
{ |
|
||||||
$ssh_fh->disconnect(); |
|
||||||
|
|
||||||
$anvil->Log->variables({ source => $THIS_FILE, line => __LINE__, level => 2, list => { |
|
||||||
message => "SSH tunnel disconnected." |
|
||||||
} }); |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
$SIG->{INT} = \&close_ssh_tunnel; |
|
||||||
$SIG->{TERM} = \&close_ssh_tunnel; |
|
||||||
|
|
||||||
$anvil->Get->switches; |
|
||||||
|
|
||||||
$anvil->Database->connect; |
|
||||||
$anvil->Log->entry({ source => $THIS_FILE, line => __LINE__, level => 2, secure => 0, key => "log_0132" }); |
|
||||||
if (not $anvil->data->{sys}{database}{connections}) |
|
||||||
{ |
|
||||||
# No databases, exit. |
|
||||||
$anvil->Log->entry({ source => $THIS_FILE, line => __LINE__, level => 0, 'print' => 1, priority => "err", key => "error_0003" }); |
|
||||||
$anvil->nice_exit({ exit_code => 1 }); |
|
||||||
} |
|
||||||
|
|
||||||
if (open_ssh_tunnel({ |
|
||||||
remote_user => $anvil->data->{switches}{'remote-user'}, |
|
||||||
target => $anvil->data->{switches}{'target'}, |
|
||||||
port => $anvil->data->{switches}{'port'}, |
|
||||||
forward_local_port => $anvil->data->{switches}{'forward-local-port'}, |
|
||||||
forward_remote_port => $anvil->data->{switches}{'forward-remote-port'} |
|
||||||
}) > 0) |
|
||||||
{ |
|
||||||
$anvil->nice_exit({ exit_code => 1 }); |
|
||||||
} |
|
||||||
|
|
||||||
my $is_ssh_tunnel_alive = 1; |
|
||||||
|
|
||||||
while($is_ssh_tunnel_alive) |
|
||||||
{ |
|
||||||
$is_ssh_tunnel_alive = $ssh_fh->test('echo'); |
|
||||||
|
|
||||||
sleep(60); |
|
||||||
} |
|
||||||
|
|
||||||
close_ssh_tunnel(); |
|
||||||
|
|
||||||
$anvil->nice_exit({ exit_code => 0 }); |
|
Loading…
Reference in new issue