mirror of
https://github.com/retspen/webvirtcloud
synced 2024-12-25 23:55:24 +00:00
Merge remote-tracking branch 'retspen/master'
Conflicts: conf/requirements.txt
This commit is contained in:
commit
db1ab88f51
11 changed files with 456 additions and 415 deletions
|
@ -9,4 +9,4 @@ register = template.Library()
|
||||||
def ssh_to_fingerprint(line):
|
def ssh_to_fingerprint(line):
|
||||||
key = base64.b64decode(line.strip().split()[1].encode('ascii'))
|
key = base64.b64decode(line.strip().split()[1].encode('ascii'))
|
||||||
fp_plain = hashlib.md5(key).hexdigest()
|
fp_plain = hashlib.md5(key).hexdigest()
|
||||||
return ':'.join(a+b for a, b in zip(fp_plain[::2], fp_plain[1::2]))
|
return ':'.join(a + b for a, b in zip(fp_plain[::2], fp_plain[1::2]))
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
Django==1.8.2
|
Django==1.8.11
|
||||||
websockify==0.7.0
|
websockify==0.8.0
|
||||||
gunicorn==19.3.0
|
gunicorn==19.3.0
|
||||||
libvirt-python==1.2.16
|
libvirt-python==1.3.2
|
||||||
http://github.com/retspen/retspen.github.io/raw/master/libxml2-python-2.9.1.tar.gz
|
#http://github.com/retspen/retspen.github.io/raw/master/libxml2-python-2.9.1.tar.gz
|
||||||
|
http://git.gnome.org/browse/libxml2/snapshot/libxml2-2.9.1.tar.gz#egg=libxml2-python&subdirectory=python
|
||||||
|
|
127
console/templates/console-base.html
Normal file
127
console/templates/console-base.html
Normal file
|
@ -0,0 +1,127 @@
|
||||||
|
{% load i18n %}
|
||||||
|
<html>
|
||||||
|
<head>
|
||||||
|
<meta charset="utf-8">
|
||||||
|
<link rel="shortcut icon" href="{{ STATIC_URL }}img/favicon.ico">
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||||
|
<link rel="stylesheet" href="{{ STATIC_URL }}css/bootstrap.min.css">
|
||||||
|
<link href="{{ STATIC_URL }}css/webvirtcloud.css" rel="stylesheet">
|
||||||
|
|
||||||
|
<style>
|
||||||
|
body {
|
||||||
|
margin: 0;
|
||||||
|
padding: 0;
|
||||||
|
background-color: #313131;
|
||||||
|
}
|
||||||
|
|
||||||
|
.navbar {
|
||||||
|
margin-bottom: 2px;
|
||||||
|
}
|
||||||
|
|
||||||
|
#main_container {
|
||||||
|
padding: 0;
|
||||||
|
width: 100%;
|
||||||
|
max-width: none;
|
||||||
|
height: 100%;
|
||||||
|
background-color:#494949;
|
||||||
|
border-bottom-right-radius: 800px 600px;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
#main_container canvas {
|
||||||
|
padding-left: 0;
|
||||||
|
padding-right: 0;
|
||||||
|
margin-left: auto;
|
||||||
|
margin-right: auto;
|
||||||
|
display: block;
|
||||||
|
margin: auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
#status {
|
||||||
|
z-index: 10000;
|
||||||
|
width: 80%;
|
||||||
|
position: absolute;
|
||||||
|
top: 5px;
|
||||||
|
left: 10%;
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
|
||||||
|
{% block head %}{% endblock %}
|
||||||
|
|
||||||
|
</head>
|
||||||
|
|
||||||
|
<body>
|
||||||
|
<nav class="navbar navbar-inverse navbar-static-top" role="navigation">
|
||||||
|
<div class="container">
|
||||||
|
<div class="navbar-header">
|
||||||
|
<button type="button" class="navbar-toggle" data-toggle="collapse" data-target=".navbar-collapse">
|
||||||
|
<span class="icon-bar"></span>
|
||||||
|
<span class="icon-bar"></span>
|
||||||
|
<span class="icon-bar"></span>
|
||||||
|
</button>
|
||||||
|
<a class="navbar-brand">{{ instance.name }}</a>
|
||||||
|
</div>
|
||||||
|
<div class="collapse navbar-collapse">
|
||||||
|
<ul class="nav navbar-nav">
|
||||||
|
<li class="dropdown">
|
||||||
|
<a href="#" class="dropdown-toggle" data-toggle="dropdown" role="button" aria-expanded="false">Send key(s) <span class="caret"></span></a>
|
||||||
|
<ul class="dropdown-menu" role="menu">
|
||||||
|
<li onclick='sendCtrlAltDel();'><a href='#'>Ctrl+Alt+Del</a></li>
|
||||||
|
<li class="divider"></li>
|
||||||
|
<li onclick='sendCtrlAltFN(0);'><a href='#'>Ctrl+Alt+F1</a></li>
|
||||||
|
<li onclick='sendCtrlAltFN(1);'><a href='#'>Ctrl+Alt+F2</a></li>
|
||||||
|
<li onclick='sendCtrlAltFN(2);'><a href='#'>Ctrl+Alt+F3</a></li>
|
||||||
|
<li onclick='sendCtrlAltFN(3);'><a href='#'>Ctrl+Alt+F4</a></li>
|
||||||
|
<li onclick='sendCtrlAltFN(4);'><a href='#'>Ctrl+Alt+F5</a></li>
|
||||||
|
<li onclick='sendCtrlAltFN(5);'><a href='#'>Ctrl+Alt+F6</a></li>
|
||||||
|
<li onclick='sendCtrlAltFN(6);'><a href='#'>Ctrl+Alt+F7</a></li>
|
||||||
|
<li onclick='sendCtrlAltFN(7);'><a href='#'>Ctrl+Alt+F8</a></li>
|
||||||
|
<li onclick='sendCtrlAltFN(8);'><a href='#'>Ctrl+Alt+F9</a></li>
|
||||||
|
<li onclick='sendCtrlAltFN(9);'><a href='#'>Ctrl+Alt+F10</a></li>
|
||||||
|
<li onclick='sendCtrlAltFN(10);'><a href='#'>Ctrl+Alt+F11</a></li>
|
||||||
|
<li onclick='sendCtrlAltFN(11);'><a href='#'>Ctrl+Alt+F12</a></li>
|
||||||
|
</ul>
|
||||||
|
</li>
|
||||||
|
<li onclick='fullscreen()'><a href='#'>{% trans "Fullscreen" %}</a></li>
|
||||||
|
{% block navbarmenu %}{% endblock %}
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</nav>
|
||||||
|
<div id='main_container' class="container">
|
||||||
|
{% block content %}{% endblock %}
|
||||||
|
</div>
|
||||||
|
<script src="{{ STATIC_URL }}js/jquery.js"></script>
|
||||||
|
<script src="{{ STATIC_URL }}js/bootstrap.min.js"></script>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
function log_message(msg,type) {
|
||||||
|
var exist=$('#status').is('div');
|
||||||
|
status_div=$('<div id="status" class="alert alert-'+type+' role="alert"><button type="button" class="close" data-dismiss="alert" aria-label="Close"><span aria-hidden="true">×</span></button>'+msg+'</div>');
|
||||||
|
if (exist) {
|
||||||
|
$('#status').remove();
|
||||||
|
$('body').prepend(status_div);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
status_div.hide();
|
||||||
|
$('body').prepend(status_div);
|
||||||
|
status_div.fadeIn(200);
|
||||||
|
}
|
||||||
|
if (type!='danger') {
|
||||||
|
status_div.delay(3000).fadeOut(200);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function log_error(msg) {
|
||||||
|
log_message(msg,'danger');
|
||||||
|
}
|
||||||
|
|
||||||
|
function log_info(msg) {
|
||||||
|
log_message(msg,'info');
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
{% block foot %}{% endblock %}
|
||||||
|
</body>
|
||||||
|
</html>
|
|
@ -1,333 +1,194 @@
|
||||||
|
{% extends "console-base.html" %}
|
||||||
{% load i18n %}
|
{% load i18n %}
|
||||||
<html>
|
{% load staticfiles %}
|
||||||
<head>
|
{% block head %}
|
||||||
<link rel="shortcut icon" href="{{ STATIC_URL }}img/favicon.ico">
|
<script src="{% static "js/spice-html5/spicearraybuffer.js" %}"></script>
|
||||||
<script src="{{ STATIC_URL }}js/spice-html5/spicearraybuffer.js"></script>
|
<script src="{% static "js/spice-html5/enums.js" %}"></script>
|
||||||
<script src="{{ STATIC_URL }}js/spice-html5/enums.js"></script>
|
<script src="{% static "js/spice-html5/atKeynames.js" %}"></script>
|
||||||
<script src="{{ STATIC_URL }}js/spice-html5/atKeynames.js"></script>
|
<script src="{% static "js/spice-html5/utils.js" %}"></script>
|
||||||
<script src="{{ STATIC_URL }}js/spice-html5/utils.js"></script>
|
<script src="{% static "js/spice-html5/png.js" %}"></script>
|
||||||
<script src="{{ STATIC_URL }}js/spice-html5/png.js"></script>
|
<script src="{% static "js/spice-html5/lz.js" %}"></script>
|
||||||
<script src="{{ STATIC_URL }}js/spice-html5/lz.js"></script>
|
<script src="{% static "js/spice-html5/quic.js" %}"></script>
|
||||||
<script src="{{ STATIC_URL }}js/spice-html5/quic.js"></script>
|
<script src="{% static "js/spice-html5/bitmap.js" %}"></script>
|
||||||
<script src="{{ STATIC_URL }}js/spice-html5/bitmap.js"></script>
|
<script src="{% static "js/spice-html5/spicedataview.js" %}"></script>
|
||||||
<script src="{{ STATIC_URL }}js/spice-html5/spicedataview.js"></script>
|
<script src="{% static "js/spice-html5/spicetype.js" %}"></script>
|
||||||
<script src="{{ STATIC_URL }}js/spice-html5/spicetype.js"></script>
|
<script src="{% static "js/spice-html5/spicemsg.js" %}"></script>
|
||||||
<script src="{{ STATIC_URL }}js/spice-html5/spicemsg.js"></script>
|
<script src="{% static "js/spice-html5/wire.js" %}"></script>
|
||||||
<script src="{{ STATIC_URL }}js/spice-html5/wire.js"></script>
|
<script src="{% static "js/spice-html5/spiceconn.js" %}"></script>
|
||||||
<script src="{{ STATIC_URL }}js/spice-html5/spiceconn.js"></script>
|
<script src="{% static "js/spice-html5/display.js" %}"></script>
|
||||||
<script src="{{ STATIC_URL }}js/spice-html5/display.js"></script>
|
<script src="{% static "js/spice-html5/main.js" %}"></script>
|
||||||
<script src="{{ STATIC_URL }}js/spice-html5/main.js"></script>
|
<script src="{% static "js/spice-html5/inputs.js" %}"></script>
|
||||||
<script src="{{ STATIC_URL }}js/spice-html5/inputs.js"></script>
|
<script src="{% static "js/spice-html5/webm.js" %}"></script>
|
||||||
<script src="{{ STATIC_URL }}js/spice-html5/webm.js"></script>
|
<script src="{% static "js/spice-html5/playback.js" %}"></script>
|
||||||
<script src="{{ STATIC_URL }}js/spice-html5/playback.js"></script>
|
<script src="{% static "js/spice-html5/simulatecursor.js" %}"></script>
|
||||||
<script src="{{ STATIC_URL }}js/spice-html5/simulatecursor.js"></script>
|
<script src="{% static "js/spice-html5/cursor.js" %}"></script>
|
||||||
<script src="{{ STATIC_URL }}js/spice-html5/cursor.js"></script>
|
<script src="{% static "js/spice-html5/thirdparty/jsbn.js" %}"></script>
|
||||||
<script src="{{ STATIC_URL }}js/spice-html5/thirdparty/jsbn.js"></script>
|
<script src="{% static "js/spice-html5/thirdparty/rsa.js" %}"></script>
|
||||||
<script src="{{ STATIC_URL }}js/spice-html5/thirdparty/rsa.js"></script>
|
<script src="{% static "js/spice-html5/thirdparty/prng4.js" %}"></script>
|
||||||
<script src="{{ STATIC_URL }}js/spice-html5/thirdparty/prng4.js"></script>
|
<script src="{% static "js/spice-html5/thirdparty/rng.js" %}"></script>
|
||||||
<script src="{{ STATIC_URL }}js/spice-html5/thirdparty/rng.js"></script>
|
<script src="{% static "js/spice-html5/thirdparty/sha1.js" %}"></script>
|
||||||
<script src="{{ STATIC_URL }}js/spice-html5/thirdparty/sha1.js"></script>
|
<script src="{% static "js/spice-html5/ticket.js" %}"></script>
|
||||||
<script src="{{ STATIC_URL }}js/spice-html5/ticket.js"></script>
|
<script src="{% static "js/spice-html5/resize.js" %}"></script>
|
||||||
<script src="{{ STATIC_URL }}js/spice-html5/resize.js"></script>
|
<script src="{% static "js/spice-html5/filexfer.js" %}"></script>
|
||||||
<script src="{{ STATIC_URL }}js/spice-html5/filexfer.js"></script>
|
|
||||||
|
|
||||||
<style>
|
{% endblock %}
|
||||||
body {
|
|
||||||
margin: 0;
|
{% block content %}
|
||||||
padding: 0;
|
<div id='spice_container'></div>
|
||||||
font-family: Helvetica;
|
{% endblock %}
|
||||||
background-color:#494949;
|
|
||||||
|
{% block foot %}
|
||||||
|
<script>
|
||||||
|
var sc;
|
||||||
|
|
||||||
|
function spice_set_cookie(name, value, days) {
|
||||||
|
var date, expires;
|
||||||
|
date = new Date();
|
||||||
|
date.setTime(date.getTime() + (days*24*60*60*1000));
|
||||||
|
expires = "; expires=" + date.toGMTString();
|
||||||
|
document.cookie = name + "=" + value + expires + "; path=/";
|
||||||
|
};
|
||||||
|
|
||||||
|
function spice_query_var(name, defvalue) {
|
||||||
|
var match = RegExp('[?&]' + name + '=([^&]*)')
|
||||||
|
.exec(window.location.search);
|
||||||
|
return match ?
|
||||||
|
decodeURIComponent(match[1].replace(/\+/g, ' '))
|
||||||
|
: defvalue;
|
||||||
|
}
|
||||||
|
|
||||||
|
function spice_error(e)
|
||||||
|
{
|
||||||
|
console.log(e);
|
||||||
|
disconnect();
|
||||||
|
if (e.message != undefined) {
|
||||||
|
log_error(e.message);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
log_error('Unknown error');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function spice_success(msg) {
|
||||||
|
log_info(msg);
|
||||||
|
}
|
||||||
|
|
||||||
|
function connect(uri,password)
|
||||||
|
{
|
||||||
|
// If a token variable is passed in, set the parameter in a cookie.
|
||||||
|
// This is used by nova-spiceproxy.
|
||||||
|
token = spice_query_var('token', null);
|
||||||
|
if (token) {
|
||||||
|
spice_set_cookie('token', token, 1)
|
||||||
}
|
}
|
||||||
|
|
||||||
#status {
|
if (sc) {
|
||||||
text-align: center;
|
sc.stop();
|
||||||
width: 100%;
|
|
||||||
margin: 0;
|
|
||||||
font-size: 1em;
|
|
||||||
height: 1.6em;
|
|
||||||
padding-top: 0.3em;
|
|
||||||
background-color: #222;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#spice-area {
|
try
|
||||||
border-bottom-right-radius: 800px 600px;
|
{
|
||||||
background-color: #313131;
|
sc = new SpiceMainConn({uri: uri, password: password, screen_id: "spice_container",
|
||||||
height: 100%;
|
onsuccess: spice_success, onerror: spice_error, onagent: agent_connected });
|
||||||
|
}
|
||||||
|
catch (e)
|
||||||
|
{
|
||||||
|
console.log(e);
|
||||||
|
log_error(e.toString());
|
||||||
|
disconnect();
|
||||||
}
|
}
|
||||||
|
|
||||||
#spice-screen canvas {
|
}
|
||||||
padding-left: 0;
|
|
||||||
padding-right: 0;
|
function disconnect()
|
||||||
margin-left: auto;
|
{
|
||||||
margin-right: auto;
|
console.log(">> disconnect");
|
||||||
display: block;
|
if (sc) {
|
||||||
|
sc.stop();
|
||||||
}
|
}
|
||||||
|
if (window.File && window.FileReader && window.FileList && window.Blob)
|
||||||
#spice-menu {
|
{
|
||||||
list-style-type: none;
|
console.log(" -> Disable drag/drop transfer");
|
||||||
margin: 0px;
|
var spice_xfer_area = document.getElementById('spice-xfer-area');
|
||||||
padding: 0px;
|
try {
|
||||||
border: 0px none;
|
document.getElementById('spice-area').removeChild(spice_xfer_area);
|
||||||
position: absolute;
|
document.getElementById('spice-area').removeEventListener('dragover', handle_file_dragover, false);
|
||||||
top: 0px;
|
document.getElementById('spice-area').removeEventListener('drop', handle_file_drop, false);
|
||||||
z-index: 100;
|
}
|
||||||
|
catch(e) {
|
||||||
|
console.log(' -> Error disabling drag/drop transfer');
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
console.log("<< disconnect");
|
||||||
|
}
|
||||||
|
|
||||||
#spice-menu li {
|
function agent_connected(sc) {
|
||||||
width: auto;
|
window.addEventListener('resize', handle_resize);
|
||||||
float: left;
|
window.spice_connection = this;
|
||||||
margin: 2px;
|
|
||||||
padding: 0px;
|
resize_helper(this);
|
||||||
border: 0px none;
|
|
||||||
position: relative;
|
if (window.File && window.FileReader && window.FileList && window.Blob)
|
||||||
height: 18px;
|
{
|
||||||
cursor: pointer;
|
var spice_xfer_area = document.createElement("div");
|
||||||
padding: 0.2em;
|
spice_xfer_area.setAttribute('id', 'spice-xfer-area');
|
||||||
background: #eee;
|
document.getElementById('spice-area').addEventListener('dragover', handle_file_dragover, false);
|
||||||
border-radius: 3px;
|
document.getElementById('spice-area').addEventListener('drop', handle_file_drop, false);
|
||||||
|
log_info('Drag and drop transfer enabled.');
|
||||||
}
|
}
|
||||||
|
else
|
||||||
#spice-menu .spice-submenu {
|
{
|
||||||
display: none;
|
console.log("File API is not supported");
|
||||||
opacity: 0;
|
log_info('Drag and drop transfer not supported.');
|
||||||
list-style-type: none;
|
|
||||||
margin: 0;
|
|
||||||
padding: 0;
|
|
||||||
border: 0;
|
|
||||||
overflow: hidden;
|
|
||||||
position: absolute;
|
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#spice-menu .spice-submenu li {
|
function sendCtrlAltFN(f) {
|
||||||
float: none;
|
if (sc && sc.inputs && sc.inputs.state === "ready"){
|
||||||
margin: 0;
|
var keys_code=[KEY_F1,KEY_F2,KEY_F3,KEY_F4,KEY_F5,KEY_F6,KEY_F7,KEY_F8,KEY_F9,KEY_F10,KEY_F11,KEY_F12];
|
||||||
padding: 0;
|
|
||||||
border: 0;
|
if (keys_code[f]==undefined) {
|
||||||
border-top: 1px solid white;
|
return;
|
||||||
border-right: 1px solid white;
|
}
|
||||||
font: normal 14px sans-serif;
|
var key = new SpiceMsgcKeyDown();
|
||||||
z-index: 100;
|
var msg = new SpiceMiniData();
|
||||||
white-space: nowrap;
|
|
||||||
min-width: 100px;
|
update_modifier(true, KEY_LCtrl, sc);
|
||||||
padding: 0.2em;
|
update_modifier(true, KEY_Alt, sc);
|
||||||
border-radius: 0;
|
|
||||||
|
key.code = keys_code[f];
|
||||||
|
msg.build_msg(SPICE_MSGC_INPUTS_KEY_DOWN, key);
|
||||||
|
sc.inputs.send_msg(msg);
|
||||||
|
msg.build_msg(SPICE_MSGC_INPUTS_KEY_UP, key);
|
||||||
|
sc.inputs.send_msg(msg);
|
||||||
|
|
||||||
|
if(Ctrl_state == false) update_modifier(false, KEY_LCtrl, sc);
|
||||||
|
if(Alt_state == false) update_modifier(false, KEY_Alt, sc);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#spice-menu li:hover > .spice-submenu {
|
function fullscreen() {
|
||||||
opacity: 1;
|
var screen=document.getElementById('spice_container');
|
||||||
display: block;
|
if(screen.requestFullscreen) {
|
||||||
margin: 2px 0 0 -2px;
|
screen.requestFullscreen();
|
||||||
|
} else if(screen.mozRequestFullScreen) {
|
||||||
|
screen.mozRequestFullScreen();
|
||||||
|
} else if(screen.webkitRequestFullscreen) {
|
||||||
|
screen.webkitRequestFullscreen();
|
||||||
|
} else if(screen.msRequestFullscreen) {
|
||||||
|
screen.msRequestFullscreen();
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#spice-menu li:hover > .spice-submenu li {
|
var uri;
|
||||||
height: auto;
|
if (window.location.protocol === "https:") {
|
||||||
}
|
uri = 'wss://{{ ws_host }}:{{ ws_port }}';
|
||||||
</style>
|
} else {
|
||||||
</head>
|
uri = 'ws://{{ ws_host }}:{{ ws_port }}';
|
||||||
<body>
|
}
|
||||||
<div id="header">
|
|
||||||
<ul id='spice-menu'>
|
|
||||||
<li>Send key(s)
|
|
||||||
<ul class='spice-submenu'>
|
|
||||||
<li onclick='sendCtrlAltDel();'>Ctrl+Alt+Del</li>
|
|
||||||
<li onclick='sendCtrlAltFN(0);'>Ctrl+Alt+F1</li>
|
|
||||||
<li onclick='sendCtrlAltFN(1);'>Ctrl+Alt+F2</li>
|
|
||||||
<li onclick='sendCtrlAltFN(2);'>Ctrl+Alt+F3</li>
|
|
||||||
<li onclick='sendCtrlAltFN(3);'>Ctrl+Alt+F4</li>
|
|
||||||
<li onclick='sendCtrlAltFN(4);'>Ctrl+Alt+F5</li>
|
|
||||||
<li onclick='sendCtrlAltFN(5);'>Ctrl+Alt+F6</li>
|
|
||||||
<li onclick='sendCtrlAltFN(6);'>Ctrl+Alt+F7</li>
|
|
||||||
<li onclick='sendCtrlAltFN(7);'>Ctrl+Alt+F8</li>
|
|
||||||
<li onclick='sendCtrlAltFN(8);'>Ctrl+Alt+F9</li>
|
|
||||||
<li onclick='sendCtrlAltFN(9);'>Ctrl+Alt+F10</li>
|
|
||||||
<li onclick='sendCtrlAltFN(10);'>Ctrl+Alt+F11</li>
|
|
||||||
<li onclick='sendCtrlAltFN(11);'>Ctrl+Alt+F12</li>
|
|
||||||
</ul>
|
|
||||||
</li>
|
|
||||||
<li onclick='fullscreen()'>Fullscreen</li>
|
|
||||||
</ul>
|
|
||||||
<div id="status"></div>
|
|
||||||
</div>
|
|
||||||
<div id="spice-area">
|
|
||||||
<div id="spice-screen" class="spice-screen"></div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<script>
|
var password = '{{ console_passwd }}';
|
||||||
var sc;
|
log_info('Connecting ...');
|
||||||
|
connect(uri,password);
|
||||||
function spice_set_cookie(name, value, days) {
|
</script>
|
||||||
var date, expires;
|
{% endblock %}
|
||||||
date = new Date();
|
|
||||||
date.setTime(date.getTime() + (days*24*60*60*1000));
|
|
||||||
expires = "; expires=" + date.toGMTString();
|
|
||||||
document.cookie = name + "=" + value + expires + "; path=/";
|
|
||||||
};
|
|
||||||
|
|
||||||
function spice_query_var(name, defvalue) {
|
|
||||||
var match = RegExp('[?&]' + name + '=([^&]*)')
|
|
||||||
.exec(window.location.search);
|
|
||||||
return match ?
|
|
||||||
decodeURIComponent(match[1].replace(/\+/g, ' '))
|
|
||||||
: defvalue;
|
|
||||||
}
|
|
||||||
|
|
||||||
var status_div=false;
|
|
||||||
function log_message(msg,color,bgcolor) {
|
|
||||||
if (!status_div) {
|
|
||||||
status_div=document.getElementById('status');
|
|
||||||
}
|
|
||||||
status_div.innerHTML=msg;
|
|
||||||
if (color) {
|
|
||||||
status_div.style.color=color;
|
|
||||||
}
|
|
||||||
if (bgcolor) {
|
|
||||||
status_div.style.backgroundColor=bgcolor;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function log_error(msg) {
|
|
||||||
log_message(msg,'#000','#f44');
|
|
||||||
}
|
|
||||||
|
|
||||||
function log_info(msg) {
|
|
||||||
log_message(msg,'#000','#eee');
|
|
||||||
}
|
|
||||||
|
|
||||||
function spice_error(e)
|
|
||||||
{
|
|
||||||
console.log(e);
|
|
||||||
disconnect();
|
|
||||||
if (e.message != undefined) {
|
|
||||||
log_error(e.message);
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
log_error('Unknown error');
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function spice_success(msg) {
|
|
||||||
log_info(msg);
|
|
||||||
}
|
|
||||||
|
|
||||||
function connect(uri,password)
|
|
||||||
{
|
|
||||||
// If a token variable is passed in, set the parameter in a cookie.
|
|
||||||
// This is used by nova-spiceproxy.
|
|
||||||
token = spice_query_var('token', null);
|
|
||||||
if (token) {
|
|
||||||
spice_set_cookie('token', token, 1)
|
|
||||||
}
|
|
||||||
|
|
||||||
if (sc) {
|
|
||||||
sc.stop();
|
|
||||||
}
|
|
||||||
|
|
||||||
try
|
|
||||||
{
|
|
||||||
sc = new SpiceMainConn({uri: uri, password: password, screen_id: "spice-screen",
|
|
||||||
onsuccess: spice_success, onerror: spice_error, onagent: agent_connected });
|
|
||||||
}
|
|
||||||
catch (e)
|
|
||||||
{
|
|
||||||
console.log(e);
|
|
||||||
log_error(e.toString());
|
|
||||||
disconnect();
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
function disconnect()
|
|
||||||
{
|
|
||||||
console.log(">> disconnect");
|
|
||||||
if (sc) {
|
|
||||||
sc.stop();
|
|
||||||
}
|
|
||||||
if (window.File && window.FileReader && window.FileList && window.Blob)
|
|
||||||
{
|
|
||||||
console.log(" -> Disable drag/drop transfer");
|
|
||||||
var spice_xfer_area = document.getElementById('spice-xfer-area');
|
|
||||||
try {
|
|
||||||
document.getElementById('spice-area').removeChild(spice_xfer_area);
|
|
||||||
document.getElementById('spice-area').removeEventListener('dragover', handle_file_dragover, false);
|
|
||||||
document.getElementById('spice-area').removeEventListener('drop', handle_file_drop, false);
|
|
||||||
}
|
|
||||||
catch(e) {
|
|
||||||
console.log(' -> Error disabling drag/drop transfer');
|
|
||||||
}
|
|
||||||
}
|
|
||||||
console.log("<< disconnect");
|
|
||||||
}
|
|
||||||
|
|
||||||
function agent_connected(sc) {
|
|
||||||
console.log('Connected');
|
|
||||||
window.addEventListener('resize', handle_resize);
|
|
||||||
window.spice_connection = this;
|
|
||||||
|
|
||||||
resize_helper(this);
|
|
||||||
|
|
||||||
if (window.File && window.FileReader && window.FileList && window.Blob)
|
|
||||||
{
|
|
||||||
var spice_xfer_area = document.createElement("div");
|
|
||||||
spice_xfer_area.setAttribute('id', 'spice-xfer-area');
|
|
||||||
document.getElementById('spice-area').addEventListener('dragover', handle_file_dragover, false);
|
|
||||||
document.getElementById('spice-area').addEventListener('drop', handle_file_drop, false);
|
|
||||||
log_info('Drag and drop transfer enabled.');
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
console.log("File API is not supported");
|
|
||||||
log_info('Drag and drop transfer not supported.');
|
|
||||||
}
|
|
||||||
log_info('Connected');
|
|
||||||
}
|
|
||||||
|
|
||||||
function sendCtrlAltFN(f) {
|
|
||||||
if (sc && sc.inputs && sc.inputs.state === "ready"){
|
|
||||||
var keys_code=[KEY_F1,KEY_F2,KEY_F3,KEY_F4,KEY_F5,KEY_F6,KEY_F7,KEY_F8,KEY_F9,KEY_F10,KEY_F11,KEY_F12];
|
|
||||||
|
|
||||||
if (keys_code[f]==undefined) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
var key = new SpiceMsgcKeyDown();
|
|
||||||
var msg = new SpiceMiniData();
|
|
||||||
|
|
||||||
update_modifier(true, KEY_LCtrl, sc);
|
|
||||||
update_modifier(true, KEY_Alt, sc);
|
|
||||||
|
|
||||||
key.code = keys_code[f];
|
|
||||||
msg.build_msg(SPICE_MSGC_INPUTS_KEY_DOWN, key);
|
|
||||||
sc.inputs.send_msg(msg);
|
|
||||||
msg.build_msg(SPICE_MSGC_INPUTS_KEY_UP, key);
|
|
||||||
sc.inputs.send_msg(msg);
|
|
||||||
|
|
||||||
if(Ctrl_state == false) update_modifier(false, KEY_LCtrl, sc);
|
|
||||||
if(Alt_state == false) update_modifier(false, KEY_Alt, sc);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function fullscreen() {
|
|
||||||
var screen=document.getElementById('spice-screen');
|
|
||||||
if(screen.requestFullscreen) {
|
|
||||||
screen.requestFullscreen();
|
|
||||||
} else if(screen.mozRequestFullScreen) {
|
|
||||||
screen.mozRequestFullScreen();
|
|
||||||
} else if(screen.webkitRequestFullscreen) {
|
|
||||||
screen.webkitRequestFullscreen();
|
|
||||||
} else if(screen.msRequestFullscreen) {
|
|
||||||
screen.msRequestFullscreen();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
var uri;
|
|
||||||
if (window.location.protocol === "https:") {
|
|
||||||
uri = 'wss://{{ ws_host }}:{{ ws_port }}';
|
|
||||||
} else {
|
|
||||||
uri = 'ws://{{ ws_host }}:{{ ws_port }}';
|
|
||||||
}
|
|
||||||
|
|
||||||
var password = '{{ console_passwd }}';
|
|
||||||
log_info('Connecting ...');
|
|
||||||
connect(uri,password);
|
|
||||||
</script>
|
|
||||||
</body>
|
|
||||||
</html>
|
|
||||||
|
|
|
@ -1,48 +1,37 @@
|
||||||
|
{% extends "console-base.html" %}
|
||||||
{% load i18n %}
|
{% load i18n %}
|
||||||
<html>
|
{% load staticfiles %}
|
||||||
<head>
|
{% block head %}
|
||||||
<link rel="shortcut icon" href="{{ STATIC_URL }}img/favicon.ico">
|
<script src="{% static "js/novnc/util.js" %}"></script>
|
||||||
<link rel="stylesheet" href="{{ STATIC_URL }}js/novnc/base.css" title="plain">
|
|
||||||
<!--
|
|
||||||
<script type='text/javascript'
|
|
||||||
src='http://getfirebug.com/releases/lite/1.2/firebug-lite-compressed.js'></script>
|
|
||||||
-->
|
|
||||||
<script src="{{ STATIC_URL }}js/novnc/util.js"></script>
|
|
||||||
</head>
|
|
||||||
<body style="margin: 0px;">
|
|
||||||
<div id="noVNC_screen">
|
|
||||||
<div id="noVNC_status_bar" class="noVNC_status_bar" style="margin-top: 0px;">
|
|
||||||
<table border=0 width="100%">
|
|
||||||
<tr>
|
|
||||||
<td>
|
|
||||||
<div id="noVNC_status">{% trans "Loading..." %}</div>
|
|
||||||
</td>
|
|
||||||
<td width="32%" style="text-align:right;">
|
|
||||||
<div id="noVNC_buttons">
|
|
||||||
<!-- dirty fix for keyboard on iOS devices -->
|
|
||||||
<input type="button" id="showKeyboard" value="Keyboard" title="Show Keyboard"/>
|
|
||||||
<!-- Note that Google Chrome on Android doesn't respect any of these,
|
|
||||||
html attributes which attempt to disable text suggestions on the
|
|
||||||
on-screen keyboard. Let's hope Chrome implements the ime-mode
|
|
||||||
style for example -->
|
|
||||||
<!-- TODO: check if this is needed on iOS -->
|
|
||||||
<textarea id="keyboardinput" autocapitalize="off"
|
|
||||||
autocorrect="off" autocomplete="off" spellcheck="false"
|
|
||||||
mozactionhint="Enter" onsubmit="return false;"
|
|
||||||
style="ime-mode: disabled;">
|
|
||||||
</textarea>
|
|
||||||
|
|
||||||
<input type=button value="Ctrl+Alt+Del" id="sendCtrlAltDelButton">
|
{% endblock %}
|
||||||
<input type=button value="Fullscreen" id="askFullscreen">
|
|
||||||
</div>
|
{% block navbarmenu %}
|
||||||
</td>
|
<!-- dirty fix for keyboard on iOS devices -->
|
||||||
</tr>
|
<li id="showKeyboard"><a href='#'>{% trans "Show Keyboad" %}</a></li>
|
||||||
</table>
|
{% endblock %}
|
||||||
</div>
|
|
||||||
|
{% block content %}
|
||||||
|
<div id='noVNC_area'>
|
||||||
<canvas id="noVNC_canvas" width="640px" height="20px">
|
<canvas id="noVNC_canvas" width="640px" height="20px">
|
||||||
{% trans "Canvas not supported." %}
|
{% trans "Canvas not supported." %}
|
||||||
</canvas>
|
</canvas>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<!-- Note that Google Chrome on Android doesn't respect any of these,
|
||||||
|
html attributes which attempt to disable text suggestions on the
|
||||||
|
on-screen keyboard. Let's hope Chrome implements the ime-mode
|
||||||
|
style for example -->
|
||||||
|
<!-- TODO: check if this is needed on iOS -->
|
||||||
|
<textarea id="keyboardinput" autocapitalize="off"
|
||||||
|
autocorrect="off" autocomplete="off" spellcheck="false"
|
||||||
|
mozactionhint="Enter" onsubmit="return false;"
|
||||||
|
style="display: none;">
|
||||||
|
</textarea>
|
||||||
|
|
||||||
|
{% endblock %}
|
||||||
|
|
||||||
|
{% block foot %}
|
||||||
<script>
|
<script>
|
||||||
/*jslint white: false */
|
/*jslint white: false */
|
||||||
/*global window, $, Util, RFB, */
|
/*global window, $, Util, RFB, */
|
||||||
|
@ -60,18 +49,32 @@
|
||||||
var rfb;
|
var rfb;
|
||||||
|
|
||||||
function passwordRequired(rfb) {
|
function passwordRequired(rfb) {
|
||||||
var msg;
|
var modal;
|
||||||
msg = '<form onsubmit="return setPassword();"';
|
modal = '<div class="modal fade">';
|
||||||
msg += 'role="form"';
|
modal += ' <div class="modal-dialog">';
|
||||||
msg += ' style="margin-bottom: 0px">';
|
modal += ' <div class="modal-content">';
|
||||||
msg += 'Password Required: ';
|
modal += ' <div class="modal-header">';
|
||||||
msg += '<input type=password size=10 id="password_input" class="noVNC_status">';
|
modal += ' <h4 class="modal-title">{% trans "Password required" %}</h4>';
|
||||||
msg += '<\/form>';
|
modal += ' </div>';
|
||||||
$D('noVNC_status_bar').setAttribute("class", "noVNC_status_warn");
|
modal += ' <div class="modal-body">';
|
||||||
$D('noVNC_status').innerHTML = msg;
|
modal += ' <form id="password_form" onsubmit="return setPassword();">';
|
||||||
|
modal += ' <div class="form-group">';
|
||||||
|
modal += ' <label for="password_input">Password</label>';
|
||||||
|
modal += ' <input type="password" class="form-control" id="password_input" placeholder="Password"/>';
|
||||||
|
modal += ' </div>';
|
||||||
|
modal += ' </form>';
|
||||||
|
modal += ' </div>';
|
||||||
|
modal += ' <div class="modal-footer">';
|
||||||
|
modal += ' <button type="button" class="btn btn-primary" data-dismiss="modal" onclick="return setPassword();">{% trans "OK" %}</button>';
|
||||||
|
modal += ' </div>';
|
||||||
|
modal += ' </div>';
|
||||||
|
modal += ' </div>';
|
||||||
|
modal += '</div>';
|
||||||
|
$('body').append(modal);
|
||||||
|
$('div.modal').modal();
|
||||||
}
|
}
|
||||||
function setPassword() {
|
function setPassword() {
|
||||||
rfb.sendPassword($D('password_input').value);
|
rfb.sendPassword($('#password_input').val());
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
function sendCtrlAltDel() {
|
function sendCtrlAltDel() {
|
||||||
|
@ -79,6 +82,19 @@
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function sendCtrlAltFN(f) {
|
||||||
|
var keys_code=[0xFFBE,0xFFBF,0xFFC0,0xFFC1,0xFFC2,0xFFC3,0xFFC4,0xFFC5,0xFFC6,0xFFC7,0xFFC8,0xFFC9];
|
||||||
|
if (keys_code[f]==undefined) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
rfb.sendKey(0xFFE3, 'down');
|
||||||
|
rfb.sendKey(0xFFE9, 'down');
|
||||||
|
rfb.sendKey(keys_code[f], 'down');
|
||||||
|
rfb.sendKey(keys_code[f], 'up');
|
||||||
|
rfb.sendKey(0xFFE9, 'up');
|
||||||
|
rfb.sendKey(0xFFE3, 'up');
|
||||||
|
};
|
||||||
|
|
||||||
// dirty fix for keyboard on iOS devices
|
// dirty fix for keyboard on iOS devices
|
||||||
function showKeyboard() {
|
function showKeyboard() {
|
||||||
var kbi, skb, l;
|
var kbi, skb, l;
|
||||||
|
@ -103,48 +119,36 @@
|
||||||
|
|
||||||
function updateState(rfb, state, oldstate, msg) {
|
function updateState(rfb, state, oldstate, msg) {
|
||||||
var s, sb, cad, af, level;
|
var s, sb, cad, af, level;
|
||||||
s = $D('noVNC_status');
|
|
||||||
sb = $D('noVNC_status_bar');
|
|
||||||
cad = $D('sendCtrlAltDelButton');
|
cad = $D('sendCtrlAltDelButton');
|
||||||
af = $D('askFullscreen');
|
af = $D('askFullscreen');
|
||||||
switch (state) {
|
switch (state) {
|
||||||
case 'failed':
|
case 'failed':
|
||||||
level = "error";
|
level = "danger";
|
||||||
break;
|
break;
|
||||||
case 'fatal':
|
case 'fatal':
|
||||||
level = "error";
|
level = "danger";
|
||||||
break;
|
break;
|
||||||
case 'normal':
|
case 'normal':
|
||||||
level = "normal";
|
level = "info";
|
||||||
break;
|
break;
|
||||||
case 'disconnected':
|
case 'disconnected':
|
||||||
level = "normal";
|
level = "info";
|
||||||
break;
|
break;
|
||||||
case 'loaded':
|
case 'loaded':
|
||||||
level = "normal";
|
level = "info";
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
level = "warn";
|
level = "warning";
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (state === "normal") {
|
|
||||||
cad.disabled = false;
|
|
||||||
af.disabled = false;
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
cad.disabled = true;
|
|
||||||
af.disabled = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (typeof(msg) !== 'undefined') {
|
if (typeof(msg) !== 'undefined') {
|
||||||
sb.setAttribute("class", "noVNC_status_" + level);
|
log_message(msg,level);
|
||||||
s.innerHTML = msg;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function fullscreen() {
|
function fullscreen() {
|
||||||
var screen=document.getElementById('noVNC_canvas');
|
var screen=document.getElementById('main_container');
|
||||||
if(screen.requestFullscreen) {
|
if(screen.requestFullscreen) {
|
||||||
screen.requestFullscreen();
|
screen.requestFullscreen();
|
||||||
} else if(screen.mozRequestFullScreen) {
|
} else if(screen.mozRequestFullScreen) {
|
||||||
|
@ -159,11 +163,6 @@
|
||||||
window.onscriptsload = function () {
|
window.onscriptsload = function () {
|
||||||
var host, port, password, path, token;
|
var host, port, password, path, token;
|
||||||
|
|
||||||
$D('sendCtrlAltDelButton').style.display = "inline";
|
|
||||||
$D('sendCtrlAltDelButton').onclick = sendCtrlAltDel;
|
|
||||||
$D('askFullscreen').style.display = "inline";
|
|
||||||
$D('askFullscreen').onclick = fullscreen;
|
|
||||||
|
|
||||||
// dirty fix for keyboard on iOS devices
|
// dirty fix for keyboard on iOS devices
|
||||||
if (isTouchDevice) {
|
if (isTouchDevice) {
|
||||||
$D('showKeyboard').onclick = showKeyboard;
|
$D('showKeyboard').onclick = showKeyboard;
|
||||||
|
@ -188,7 +187,7 @@
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
rfb = new RFB({'target': $D('noVNC_canvas'),
|
rfb = new RFB({'target': document.getElementById('noVNC_canvas'),
|
||||||
'encrypt': WebUtil.getQueryVar('encrypt',
|
'encrypt': WebUtil.getQueryVar('encrypt',
|
||||||
(window.location.protocol === "https:")),
|
(window.location.protocol === "https:")),
|
||||||
'repeaterID': WebUtil.getQueryVar('repeaterID', ''),
|
'repeaterID': WebUtil.getQueryVar('repeaterID', ''),
|
||||||
|
@ -201,5 +200,4 @@
|
||||||
rfb.connect(host, port, password, path);
|
rfb.connect(host, port, password, path);
|
||||||
};
|
};
|
||||||
</script>
|
</script>
|
||||||
</body>
|
{% endblock %}
|
||||||
</html>
|
|
||||||
|
|
|
@ -40,6 +40,7 @@ class NewVMForm(forms.Form):
|
||||||
storage = forms.CharField(max_length=20, required=False)
|
storage = forms.CharField(max_length=20, required=False)
|
||||||
template = forms.CharField(required=False)
|
template = forms.CharField(required=False)
|
||||||
images = forms.CharField(required=False)
|
images = forms.CharField(required=False)
|
||||||
|
cache_mode = forms.CharField(error_messages={'required': _('Please select HDD cache mode')})
|
||||||
hdd_size = forms.IntegerField(required=False)
|
hdd_size = forms.IntegerField(required=False)
|
||||||
meta_prealloc = forms.BooleanField(required=False)
|
meta_prealloc = forms.BooleanField(required=False)
|
||||||
virtio = forms.BooleanField(required=False)
|
virtio = forms.BooleanField(required=False)
|
||||||
|
|
|
@ -77,6 +77,23 @@
|
||||||
</select>
|
</select>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
<div class="form-group meta-prealloc">
|
||||||
|
<label class="col-sm-3 control-label">{% trans "Metadata" %}</label>
|
||||||
|
<div class="col-sm-6">
|
||||||
|
<input type="checkbox" name="meta_prealloc" title="Metadata preallocation" value="true">
|
||||||
|
</div>
|
||||||
|
<label class="col-lg-1 control-label">{% trans "Image" %}</label>
|
||||||
|
</div>
|
||||||
|
<div class="form-group">
|
||||||
|
<label class="col-sm-3 control-label">{% trans "HDD cache mode" %}</label>
|
||||||
|
<div class="col-sm-6">
|
||||||
|
<select id="cache_mode" name="cache_mode" class="form-control">
|
||||||
|
{% for mode, name in cache_modes %}
|
||||||
|
<option value="{{ mode }}">{% trans name %}</option>
|
||||||
|
{% endfor %}
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<label class="col-sm-3 control-label">{% trans "Network" %}</label>
|
<label class="col-sm-3 control-label">{% trans "Network" %}</label>
|
||||||
<div class="col-sm-6">
|
<div class="col-sm-6">
|
||||||
|
@ -159,6 +176,16 @@
|
||||||
</div>
|
</div>
|
||||||
<label class="col-lg-1 control-label">{% trans "Image" %}</label>
|
<label class="col-lg-1 control-label">{% trans "Image" %}</label>
|
||||||
</div>
|
</div>
|
||||||
|
<div class="form-group">
|
||||||
|
<label class="col-sm-3 control-label">{% trans "HDD cache mode" %}</label>
|
||||||
|
<div class="col-sm-6">
|
||||||
|
<select id="cache_mode" name="cache_mode" class="form-control">
|
||||||
|
{% for mode, name in cache_modes %}
|
||||||
|
<option value="{{ mode }}">{% trans name %}</option>
|
||||||
|
{% endfor %}
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<label class="col-sm-3 control-label">{% trans "Network" %}</label>
|
<label class="col-sm-3 control-label">{% trans "Network" %}</label>
|
||||||
<div class="col-sm-6">
|
<div class="col-sm-6">
|
||||||
|
@ -258,6 +285,7 @@
|
||||||
<label class="col-sm-3 control-label">{% trans "Storage" %}</label>
|
<label class="col-sm-3 control-label">{% trans "Storage" %}</label>
|
||||||
|
|
||||||
<div class="col-sm-6">
|
<div class="col-sm-6">
|
||||||
|
<input type="hidden" name="cache_mode" value="default">
|
||||||
<select name="storage" class="form-control">
|
<select name="storage" class="form-control">
|
||||||
{% if storages %}
|
{% if storages %}
|
||||||
{% for storage in storages %}
|
{% for storage in storages %}
|
||||||
|
@ -269,13 +297,6 @@
|
||||||
</select>
|
</select>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="form-group meta-prealloc">
|
|
||||||
<label class="col-sm-3 control-label">{% trans "Metadata" %}</label>
|
|
||||||
<div class="col-sm-6">
|
|
||||||
<input type="checkbox" name="meta_prealloc" title="Metadata preallocation" value="true">
|
|
||||||
</div>
|
|
||||||
<label class="col-lg-1 control-label">{% trans "Image" %}</label>
|
|
||||||
</div>
|
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<label class="col-sm-3 control-label">{% trans "Network" %}</label>
|
<label class="col-sm-3 control-label">{% trans "Network" %}</label>
|
||||||
<div class="col-sm-6">
|
<div class="col-sm-6">
|
||||||
|
|
|
@ -41,6 +41,7 @@ def create_instance(request, compute_id):
|
||||||
networks = sorted(conn.get_networks())
|
networks = sorted(conn.get_networks())
|
||||||
instances = conn.get_instances()
|
instances = conn.get_instances()
|
||||||
get_images = sorted(conn.get_storages_images())
|
get_images = sorted(conn.get_storages_images())
|
||||||
|
cache_modes = sorted(conn.get_cache_modes().items())
|
||||||
mac_auto = util.randomMAC()
|
mac_auto = util.randomMAC()
|
||||||
except libvirtError as lib_err:
|
except libvirtError as lib_err:
|
||||||
error_messages.append(lib_err)
|
error_messages.append(lib_err)
|
||||||
|
@ -122,11 +123,15 @@ def create_instance(request, compute_id):
|
||||||
volumes[path] = conn.get_volume_type(path)
|
volumes[path] = conn.get_volume_type(path)
|
||||||
except libvirtError as lib_err:
|
except libvirtError as lib_err:
|
||||||
error_messages.append(lib_err.message)
|
error_messages.append(lib_err.message)
|
||||||
|
if data['cache_mode'] not in conn.get_cache_modes():
|
||||||
|
error_msg = _("Invalid cache mode")
|
||||||
|
error_messages.append(error_msg)
|
||||||
if not error_messages:
|
if not error_messages:
|
||||||
uuid = util.randomUUID()
|
uuid = util.randomUUID()
|
||||||
try:
|
try:
|
||||||
conn.create_instance(data['name'], data['memory'], data['vcpu'], data['host_model'],
|
conn.create_instance(data['name'], data['memory'], data['vcpu'], data['host_model'],
|
||||||
uuid, volumes, data['networks'], data['virtio'], data['mac'])
|
uuid, volumes, data['cache_mode'], data['networks'], data['virtio'],
|
||||||
|
data['mac'])
|
||||||
create_instance = Instance(compute_id=compute_id, name=data['name'], uuid=uuid)
|
create_instance = Instance(compute_id=compute_id, name=data['name'], uuid=uuid)
|
||||||
create_instance.save()
|
create_instance.save()
|
||||||
return HttpResponseRedirect(reverse('instance', args=[compute_id, data['name']]))
|
return HttpResponseRedirect(reverse('instance', args=[compute_id, data['name']]))
|
||||||
|
|
|
@ -7,9 +7,17 @@ from webvirtcloud.settings import QEMU_CONSOLE_DEFAULT_TYPE
|
||||||
def get_rbd_storage_data(stg):
|
def get_rbd_storage_data(stg):
|
||||||
xml = stg.XMLDesc(0)
|
xml = stg.XMLDesc(0)
|
||||||
ceph_user = util.get_xml_path(xml, "/pool/source/auth/@username")
|
ceph_user = util.get_xml_path(xml, "/pool/source/auth/@username")
|
||||||
ceph_host = util.get_xml_path(xml, "/pool/source/host/@name")
|
|
||||||
secrt_uuid = util.get_xml_path(xml, "/pool/source/auth/secret/@uuid")
|
def get_ceph_hosts(ctx):
|
||||||
return ceph_user, secrt_uuid, ceph_host
|
hosts = []
|
||||||
|
for host in ctx.xpathEval("/pool/source/host"):
|
||||||
|
name = host.prop("name")
|
||||||
|
if name:
|
||||||
|
hosts.append({'name': name, 'port': host.prop("port")})
|
||||||
|
return hosts
|
||||||
|
ceph_hosts = util.get_xml_path(xml, func=get_ceph_hosts)
|
||||||
|
secret_uuid = util.get_xml_path(xml, "/pool/source/auth/secret/@uuid")
|
||||||
|
return ceph_user, secret_uuid, ceph_hosts
|
||||||
|
|
||||||
|
|
||||||
class wvmCreate(wvmConnect):
|
class wvmCreate(wvmConnect):
|
||||||
|
@ -40,6 +48,17 @@ class wvmCreate(wvmConnect):
|
||||||
"""Get guest capabilities"""
|
"""Get guest capabilities"""
|
||||||
return util.get_xml_path(self.get_cap_xml(), "/capabilities/host/cpu/arch")
|
return util.get_xml_path(self.get_cap_xml(), "/capabilities/host/cpu/arch")
|
||||||
|
|
||||||
|
def get_cache_modes(self):
|
||||||
|
"""Get cache available modes"""
|
||||||
|
return {
|
||||||
|
'default': 'Default',
|
||||||
|
'none': 'Disabled',
|
||||||
|
'writethrough': 'Write through',
|
||||||
|
'writeback': 'Write back',
|
||||||
|
'directsync': 'Direct sync', # since libvirt 0.9.5
|
||||||
|
'unsafe': 'Unsafe', # since libvirt 0.9.7
|
||||||
|
}
|
||||||
|
|
||||||
def create_volume(self, storage, name, size, format='qcow2', metadata=False):
|
def create_volume(self, storage, name, size, format='qcow2', metadata=False):
|
||||||
size = int(size) * 1073741824
|
size = int(size) * 1073741824
|
||||||
stg = self.get_storage(storage)
|
stg = self.get_storage(storage)
|
||||||
|
@ -121,7 +140,7 @@ class wvmCreate(wvmConnect):
|
||||||
vol = self.get_volume_by_path(path)
|
vol = self.get_volume_by_path(path)
|
||||||
vol.delete()
|
vol.delete()
|
||||||
|
|
||||||
def create_instance(self, name, memory, vcpu, host_model, uuid, images, networks, virtio, mac=None):
|
def create_instance(self, name, memory, vcpu, host_model, uuid, images, cache_mode, networks, virtio, mac=None):
|
||||||
"""
|
"""
|
||||||
Create VM function
|
Create VM function
|
||||||
"""
|
"""
|
||||||
|
@ -162,19 +181,27 @@ class wvmCreate(wvmConnect):
|
||||||
stg_type = util.get_xml_path(stg.XMLDesc(0), "/pool/@type")
|
stg_type = util.get_xml_path(stg.XMLDesc(0), "/pool/@type")
|
||||||
|
|
||||||
if stg_type == 'rbd':
|
if stg_type == 'rbd':
|
||||||
ceph_user, secrt_uuid, ceph_host = get_rbd_storage_data(stg)
|
ceph_user, secret_uuid, ceph_hosts = get_rbd_storage_data(stg)
|
||||||
xml += """<disk type='network' device='disk'>
|
xml += """<disk type='network' device='disk'>
|
||||||
<driver name='qemu' type='%s'/>
|
<driver name='qemu' type='%s' cache='%s'/>
|
||||||
<auth username='%s'>
|
<auth username='%s'>
|
||||||
<secret type='ceph' uuid='%s'/>
|
<secret type='ceph' uuid='%s'/>
|
||||||
</auth>
|
</auth>
|
||||||
<source protocol='rbd' name='%s'>
|
<source protocol='rbd' name='%s'>""" % (img_type, cache_mode, ceph_user, secret_uuid, image)
|
||||||
<host name='%s' port='6789'/>
|
if isinstance(ceph_hosts, list):
|
||||||
</source>""" % (img_type, ceph_user, secrt_uuid, image, ceph_host)
|
for host in ceph_hosts:
|
||||||
|
if host.get('port'):
|
||||||
|
xml += """
|
||||||
|
<host name='%s' port='%s'/>""" % (host.get('name'), host.get('port'))
|
||||||
|
else:
|
||||||
|
xml += """
|
||||||
|
<host name='%s'/>""" % host.get('name')
|
||||||
|
xml += """
|
||||||
|
</source>"""
|
||||||
else:
|
else:
|
||||||
xml += """<disk type='file' device='disk'>
|
xml += """<disk type='file' device='disk'>
|
||||||
<driver name='qemu' type='%s'/>
|
<driver name='qemu' type='%s' cache='%s'/>
|
||||||
<source file='%s'/>""" % (img_type, image)
|
<source file='%s'/>""" % (img_type, cache_mode, image)
|
||||||
|
|
||||||
if virtio:
|
if virtio:
|
||||||
xml += """<target dev='vd%s' bus='virtio'/>""" % (disk_letters.pop(0),)
|
xml += """<target dev='vd%s' bus='virtio'/>""" % (disk_letters.pop(0),)
|
||||||
|
|
Loading…
Reference in a new issue