Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Spice web client addon #1225

Merged
merged 6 commits into from
Jul 3, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 8 additions & 1 deletion Makefile.am
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,12 @@ else
WITH_SPICE=no
endif

if WITH_SPICE_WEB_CLIENT
WITH_SPICE_WEB_CLIENT=yes
else
WITH_SPICE_WEB_CLIENT=no
endif

wokdir = $(pythondir)/wok
kimchidir = $(pythondir)/wok/plugins/kimchi

Expand Down Expand Up @@ -104,7 +110,8 @@ do_substitution = \
-e 's,[@]kimchidir[@],$(kimchidir),g' \
-e 's,[@]kimchiversion[@],$(PACKAGE_VERSION),g' \
-e 's,[@]kimchirelease[@],$(PACKAGE_RELEASE),g' \
-e 's,[@]withspice[@],$(WITH_SPICE),g'
-e 's,[@]withspice[@],$(WITH_SPICE),g' \
-e 's,[@]withspicewebclient[@],$(WITH_SPICE_WEB_CLIENT),g'

config.py: config.py.in Makefile
$(do_substitution) < $(srcdir)/config.py.in > config.py
Expand Down
3 changes: 2 additions & 1 deletion autogen.sh
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,8 @@ if [ ! -f "configure" ]; then
fi

if [ "x$1" == "x--system" ]; then
./configure --prefix=/usr --sysconfdir=/etc --localstatedir=/var
shift
./configure --prefix=/usr --sysconfdir=/etc --localstatedir=/var $@
else
if [ $# -gt 0 ]; then
./configure $@
Expand Down
26 changes: 19 additions & 7 deletions config.py.in
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ from wok.xmlutils.utils import xpath_get_text
kimchiLock = threading.Lock()

__with_spice__ = "@withspice@"
__with_spice_web_client__ = "@withspicewebclient@"
__version__ = "@kimchiversion@"
__release__ = "@kimchirelease@"

Expand All @@ -41,6 +42,10 @@ def get_kimchi_version():
return "-".join([__version__, __release__])


def with_spice_web_client():
return __with_spice_web_client__ == 'yes'


def get_distros_store():
return os.path.join(kimchiPaths.sysconf_dir, 'distros.d')

Expand Down Expand Up @@ -102,15 +107,20 @@ class KimchiPaths(PluginPaths):

def __init__(self):
super(KimchiPaths, self).__init__('kimchi')
self.spice_file = os.path.join(self.ui_dir,
'spice-html5/pages/spice_auto.html')

if __with_spice__ == 'yes':
self.spice_dir = os.path.join(self.ui_dir, 'spice-html5')
elif os.path.exists('@datadir@/spice-html5'):
self.spice_dir = '@datadir@/spice-html5'
if __with_spice_web_client__ == 'yes':
self.spice_file = os.path.join(self.ui_dir,
'spice-web-client/index.html')
self.spice_dir = os.path.join(self.ui_dir, 'spice-web-client')
else:
self.spice_dir = '/usr/share/spice-html5'
self.spice_file = os.path.join(self.ui_dir,
'spice-html5/pages/spice_auto.html')
if __with_spice__ == 'yes':
self.spice_dir = os.path.join(self.ui_dir, 'spice-html5')
elif os.path.exists('@datadir@/spice-html5'):
self.spice_dir = '@datadir@/spice-html5'
else:
self.spice_dir = '/usr/share/spice-html5'

if os.path.exists('@datadir@/novnc'):
self.novnc_dir = '@datadir@/novnc'
Expand Down Expand Up @@ -145,6 +155,8 @@ class KimchiConfig(PluginConfig):
'path': kimchiPaths.spice_file},
'/spice-html5/spice.css': {'type': 'file',
'path': kimchiPaths.spice_css_file},
'/spice-web-client': {'type': 'dir',
'path': kimchiPaths.spice_dir},
'/serial': {'type': 'dir',
'path': kimchiPaths.serial_dir}}

Expand Down
21 changes: 21 additions & 0 deletions configure.ac
Original file line number Diff line number Diff line change
Expand Up @@ -84,6 +84,15 @@ AC_ARG_WITH(
)
AM_CONDITIONAL([WITH_SPICE], [test "x$with_spice_html5" = xyes])

AC_ARG_WITH(
[spice-web-client],
[AS_HELP_STRING([--with-spice-web-client],
[Build Kimchi with spice-web-client @<:@default=no@:>@])],
,
[with_spice_web_client="no"]
)
AM_CONDITIONAL([WITH_SPICE_WEB_CLIENT], [test "x$with_spice_web_client" = xyes])

AC_CONFIG_FILES([
po/Makefile.in
po/gen-pot
Expand All @@ -102,6 +111,18 @@ AC_CONFIG_FILES([
ui/spice-html5/css/Makefile
ui/spice-html5/pages/Makefile
ui/spice-html5/thirdparty/Makefile
ui/spice-web-client/Makefile
ui/spice-web-client/application/Makefile
ui/spice-web-client/keymaps/Makefile
ui/spice-web-client/lib/Makefile
ui/spice-web-client/lib/images/Makefile
ui/spice-web-client/network/Makefile
ui/spice-web-client/process/Makefile
ui/spice-web-client/resources/Makefile
ui/spice-web-client/spiceobjects/Makefile
ui/spice-web-client/spiceobjects/generated/Makefile
ui/spice-web-client/spiceproxy/Makefile
ui/spice-web-client/swcanvas/Makefile
ui/pages/Makefile
ui/pages/help/Makefile
ui/pages/tabs/Makefile
Expand Down
1 change: 1 addition & 0 deletions docs/API.md
Original file line number Diff line number Diff line change
Expand Up @@ -824,6 +824,7 @@ Contains information about Kimchi configuration.

* **GET**: Retrieve configuration information
* version: The version of the kimchi service
* with_spice_web_client: True is buit with spice web client support
* **POST**: *See Configuration Actions*

**Actions (POST):**
Expand Down
4 changes: 3 additions & 1 deletion model/config.py
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@
from wok.exception import NotFoundError
from wok.plugins.kimchi.config import find_qemu_binary
from wok.plugins.kimchi.config import get_kimchi_version
from wok.plugins.kimchi.config import with_spice_web_client
from wok.plugins.kimchi.distroloader import DistroLoader
from wok.plugins.kimchi.model.featuretests import FEATURETEST_POOL_NAME
from wok.plugins.kimchi.model.featuretests import FEATURETEST_VM_NAME
Expand All @@ -39,7 +40,8 @@ def __init__(self, **kargs):
pass

def lookup(self, name):
return {'version': get_kimchi_version()}
return {'version': get_kimchi_version(),
'with_spice_web_client': with_spice_web_client()}


class CapabilitiesModel(object, metaclass=Singleton):
Expand Down
2 changes: 1 addition & 1 deletion tests/test_rest.py
Original file line number Diff line number Diff line change
Expand Up @@ -1683,7 +1683,7 @@ def test_tasks(self):
def test_config(self):
resp = self.request('/plugins/kimchi/config').read()
conf = json.loads(resp)
keys = ['version']
keys = ["version", "with_spice_web_client"]
self.assertEqual(keys, sorted(conf.keys()))

def test_capabilities(self):
Expand Down
2 changes: 1 addition & 1 deletion ui/Makefile.am
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,6 @@
# See the License for the specific language governing permissions and
# limitations under the License.

SUBDIRS = config css images js pages spice-html5 serial
SUBDIRS = config css images js pages spice-html5 spice-web-client serial

uidir = $(datadir)/wok/plugins/kimchi/ui
47 changes: 31 additions & 16 deletions ui/js/src/kimchi.api.js
Original file line number Diff line number Diff line change
Expand Up @@ -372,22 +372,37 @@ var kimchi = {
}).done(function(data, textStatus, xhr) {
url = 'https://' + location.hostname + ':' + proxy_port;
url += server_root;
url += "/plugins/kimchi/spice_auto.html";
/*
* When using server_root we need pass the value with port
* argument to be used correctly by spice_auto.html scripts
*/
url += "?port=" + proxy_port + server_root;
url += "&listen=" + location.hostname;
/*
* From python documentation base64.urlsafe_b64encode(s)
* substitutes - instead of + and _ instead of / in the
* standard Base64 alphabet, BUT the result can still
* contain = which is not safe in a URL query component.
* So remove it when needed as base64 can work well without it.
* */
url += "&token=" + wok.urlSafeB64Encode(vm).replace(/=*$/g, "");
url += '&encrypt=1';
if(kimchi.config['with_spice_web_client'])
{
/*
* Slightly different api for spice-web-client
*/
url += "/plugins/kimchi/spice-web-client/index.html";
url += "?port=" + proxy_port + server_root;
url += "&host=" + location.hostname;
url += "&vmInfoToken=" + wok.urlSafeB64Encode(vm).replace(/=*$/g, "");
url += '&protocol=wss';
}
else
{
// Using spice-html5 by default
url += "/plugins/kimchi/spice_auto.html";
/*
* When using server_root we need pass the value with port
* argument to be used correctly by spice_auto.html scripts
*/
url += "?port=" + proxy_port + server_root;
url += "&listen=" + location.hostname;
/*
* From python documentation base64.urlsafe_b64encode(s)
* substitutes - instead of + and _ instead of / in the
* standard Base64 alphabet, BUT the result can still
* contain = which is not safe in a URL query component.
* So remove it when needed as base64 can work well without it.
* */
url += "&token=" + wok.urlSafeB64Encode(vm).replace(/=*$/g, "");
url += '&encrypt=1';
}
window.open(url);
});
},
Expand Down
8 changes: 8 additions & 0 deletions ui/spice-web-client/LICENSE
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
The MIT License (MIT)
Copyright (c) 2016 eyeOS S.L.

Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
5 changes: 5 additions & 0 deletions ui/spice-web-client/Makefile.am
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
if WITH_SPICE_WEB_CLIENT
SUBDIRS = application keymaps lib network process resources spiceobjects spiceproxy swcanvas
spicewebclientdir = $(datadir)/wok/plugins/kimchi/ui/spice-web-client
dist_spicewebclient_DATA = index.html run.js $(NULL)
endif
99 changes: 99 additions & 0 deletions ui/spice-web-client/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,99 @@
#Complete Spice Web Client written in HTML5 and Javascript
Full and complete implementation of the SPICE protocol (by Red Hat) written in HTML5 and JavaScript. It allows any standard HTML5-ready Web Browser to connect to remote
virtual sessions just by accessing a single URL.

The client can be deployed through a normal web server to connect to spice sessions. To use it you would need to proxy your spice session through a websockets-to-tcp
proxy like Kanaka, Websockify or similar projects.

NOTE: This project is NOT based on the spice-html5 prototype.

## Features

- Full QXL Support of the entire spice protocol, including clipping, masking, scaling etc (accelerated mode)
- Audio support, but only for raw audio samples, not for celt
- Full KeyBoard support including English, Spanish and Catalan layouts
- Clipboard sharing support with customizble interface
- Video streaming support with excellent performance even at 60fps FHD 1080p
- Extremly high performant LZ decoder with sub <10ms for a FHD 1080P image
- Pure Javascript codec for quic
- Configurable multi core support using webworkers (by default it uses 4 CPU Cores)
- Spice Agent support
- Set resolution support
- Honors spice cache for images, cursors and palettes
- Very low memory footprint for a javascript application like this
- Spice authentication tokens support
- Supports graphic live debugging the spice protocol and to replay packets to fix bugs

##Missing features

There are some SPICE features still to be implemented, the most important ones are:

- Celt or other audio codec
- USB redirection (not possible at browser level, maybe with a plugin?)

##Client System requirements

To get the best result we recommend at least 1GB of ram and at least two cores at 1,5ghz.

It should work decently on 512mb of ram and 1ghz.

We have made tests in raspberry pi 2 with very good results.

##Network requirements

Only Binary websockets are used to send and receive server data, so you should expect similar network requirements than SPICE itself.
for a normal 1080p session the performance is very good up to 150-200ms of latency and 100kb/s bandwidth.

The network consumption of a spice session depends a lot on the usage patterns.

##Performance

Writing a web client for a protocol like spice is challenge because of the limited access to system resources like GPU and the way the javascript VM works.

We have spent almost 2 years profiling the entire project. The lz decoder has been optimized to <10Ms for full hd images. Quic codec has been hacked a lot
to get acceptable performance even being executed in javascript.

We have created a graphic pipeline to remove unnecesary draw operations that are going to be overdrawn at the next known packets. We have minimized the work
for the javascript GC and refined all our canvas operations and all the entire stack to prevent big data structures to be copied.

You should expect a near perfect experience if you meet the client requirements and the network requirements.

##Browser support

We strongly recommend use the spice web client with Chromium/Chrome or Firefox, however it should work at least on:

- Google Chrome
- Firefox
- Internet Explorer 11
- Edge


##How to use it

In order to work you only need to provide the IP address of the websockets proxy and the port
of the websockets proxy.

You can do it permanently editing run.js or through the URL using the parameters:

http://example.com/spice-web-client/index.html?host=IP_ADDRESS_OF_WEBSOCKIFY&port=TCP_PORT_OF_WEBSOCKIFY

By doing this you will connect to the remote spice session and the resolution will be adapted to your browser viewport area.

##Notes For linux sessions
If you are planning to use this to connect to remote linux sessions you should consider disabling compositing on your desktop. The best performance is achieved with
kde with compositing and visual effects disabled.

Always install the spice-vdagent and xorg-qxl to get the best results and to have custom resolutions etc.

##Notes For Windows sessions

Spice web client has a very good performance connecting to remote windows sessions. Always install the spice-agent package including the qxl video driver to get the best results and to have custom resolutions etc.

##More information

For more information about the implementation or questions about roadmap etc contact Jose Carlos Norte (jcarlosn) at [email protected]

##License

Spice Web Client is distributed under the terms of the [MIT license](https://opensource.org/licenses/MIT).

3 changes: 3 additions & 0 deletions ui/spice-web-client/application/Makefile.am
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
spiceappdir = $(datadir)/wok/plugins/kimchi/ui/spice-web-client/application

dist_spiceapp_DATA = $(wildcard *.js) $(NULL)
Loading