Commit f2920af1 authored by Francesc Guasch's avatar Francesc Guasch
Browse files

Merge branch 'master' into develop

parents fb74bdbd 56c703c8
......@@ -7,8 +7,10 @@ pm_to_blib
## Contributing
# Contributing
First off, thank you for considering contributing to Ravada. It's people
like you that make it such a great tool.
### 1. Where do I go from here?
## 1. Where do I go from here?
If you've noticed a bug or have a question that doesn't belong on the
[mailing list](
[search the issue tracker](
to see if someone else in the community has already created a ticket.
If not, go ahead and [make one](!
You can also ask in our [telegram public group](
If it is not, go ahead and [create a new issue](!
### 2. Fork & create a branch
## 2. Fork & create a branch
If this is something you think you can fix, then
[fork Ravada](
and create a branch with a descriptive name.
and create a branch with a descriptive name. We prepend the issue number to
the branch so it is easier to follow.
A good branch name would be (where issue #325 is the ticket you're working on):
A good branch name would be (where issue #77 is the one you're working on):
git checkout -b 325_boost_performance
git checkout -b 77_start_machine
If you contribute code, *thank you* ! Plase, follow this rules so our
code is in sync:
## 3. Code Style
- Use spaces, don't do tabs.
- Add the issue number at the very beggining of the commit message
``[#44] Fixed flux capacitor leak``
See our
[editor configuration](
guidelines so your code gets along with old code. A recurrent problem for newcommers
is to submit code automatically cleaned by the editor. Usually, removed end of line
spaces or spaces converted to tabs.
Please make sure you don't do that. Run ``git diff`` before commit to see what you are
exactly contributing.
### 3. Get the tests running
## 4. Commit Format
If you contribute code, *thank you* ! Plase, follow this guide.
Each commit message consists of a header, a body, and a footer. The header has a special format that includes a type, a scope, and a description.
We use [conventional commits]( format. Each commit must be for
a reason, and we should have an [issue]( for that, so we
decided to add the issue number in the footer.
The commit message should be structured as follows:
type(optional scope): description
<blank line>
optional body
<blank line>
footer #issue
fix: active virtual machines can not be started
check the machine status before start
returns if machine active
before it crashed trying to start the machine
fixes #77
### 4.1 Header: Type
Commits must be prefixed with a type, which consists of a verb, feat, fix, build, followed by a colon and space.
Your options:
- build: Changes that affect the build system or external dependencies (example scopes: gulp, broccoli, npm).
- ci: Changes to our CI configuration files and scripts (example scopes: Travis, Circle, BrowserStack, SauceLabs).
- docs: Documentation only changes.
- feat: A new feature.
- fix: A bug fix.
- perf: A code change that improves performance.
- refactor: A code change that neither fixes a bug or adds a feature.
- style: Changes that do not affect the meaning of the code (white-space, formatting, missing semi-colons, etc).
- test: Adding missing tests or correcting existing tests.
### 4.2 Header: Optional Scope
Refers to the extent, subject matter or contextual information about your changes. A scope is a phrase describing the file modified or a section of the codebase, it is always enclosed in parenthesis.
Example for a (optional scope):
feat(parser): add ability to parse arrays
### 4.3 Header: Description
A description must immediately follow the type(optional scope): The description is a short description of the commit.
- About commit character length, keep it concise and don't write more than 50 characters.
- Use the imperative present tense: change, make, add, update, fix, etc; Do not use changed,
changes, added, fixes, fixed, etc.
- Don't capitalize the first letter.
- Do not use a dot (.) at the end.
### 4.4 Header Lenghth
The header cannot be longer than 100 characters. This allows the message to be easier to read on GitHub as well as in various git tools.
### 4.5 Writing the optional body
The body should include the motivation for the change and contrast this with previous behavior.
Example for optional body:
fix orthography
remove out of date paragraph
fix broken links
### 4.5 Writing the optional footer
The <optional footer> should contain a closing reference to an issue if any.
For example, to close an issue numbered 123, you could use the phrases Closes #123 in your
pull request description or commit message. Once the branch is merged into the default branch,
the issue will close.
## 5. Get the tests running
See this documentation about [testing]( the project.
#### 4. Did you find a bug?
## 6. Did you find a bug?
* **Ensure the bug was not already reported** by searching on GitHub under [Issues](
......@@ -43,7 +139,7 @@ See this documentation about [testing](
Be sure to include a **title and clear description**, as much relevant information as possible,
and a **code sample**, an **executable test case** or a step by step guide demonstrating the expected behavior that is not occurring.
### 5. Implement your fix or feature
## 7. Implement your fix or feature
At this point, you're ready to make your changes! Feel free to ask for help;
everyone is a beginner at first :smile_cat:
......@@ -52,9 +148,20 @@ Follow this guide about running [Ravada in development mode](http://ravada.readt
If you change a translation or language file make sure you follow this small [guide]( and don't forget to add the issue number when committing.
### 6. Make a Pull Request
## 8. Push your changes
Pushing refers to sending your committed changes to a remote repository, such as a repository
hosted on GitHub. Before that all the changes where local in the computer you are working in.
After working on your changes you need to Push it (upload) your newly created branch to GitHub
git push
## 9. Create a Pull Request
At this point, you should switch back to your master branch and make sure it's
Pull requests or PR are proposed changes to a repository submitted by a user and accepted or rejected by a repository's collaborators.
When your changes are done, you should switch back to your master branch and make sure it's
up to date with Ravada's master branch:
......@@ -71,11 +178,18 @@ git rebase master
git push --set-upstream origin 325_boost_performance
Finally, go to GitHub and
[make a Pull Request](
Finally, go to our GitHub repository and
[create a Pull Request](
### 9.1 How to Write a Title for a Pull Request
Pull Request should be named in reference to the main fix or feature you provide; minor information can be added to the description. Please be specific and don't use generic terms.
Keep it concise and don't write more than 50 characters in the title.
Read [more information about PR](
### 7. Keeping your Pull Request updated
### 9.2 Keeping your Pull Request updated
If a maintainer asks you to "rebase" your PR, they're saying that a lot of code
has changed, and that you need to update your branch so it's easier to merge.
......@@ -91,7 +205,7 @@ git pull --rebase origin master
git push --force-with-lease origin 325_boost_performance
### 8. Merging a PR (maintainers only)
### 9.3 Merging a PR (maintainers only)
A PR can only be merged into master by a maintainer if:
# ravada [![GitHub version](]( [![License: AGPL v3](]( [![Documentation Status](]( [![GitHub Stats](](
# ravada
[![GitHub version](]( [![License: AGPL v3](](
[![Documentation Status](](
[![Follow twitter](](
[![Telegram Group](](
[![Project Status: Active - The project has reached a stable, usable state and is being actively developed.](](
[![Conventional Commits](](
## Remote Virtual Desktops Manager
use warnings;
use strict;
use Benchmark;
use Carp qw(confess);
use Getopt::Long;
use Net::Ping;
use Data::Dumper;
use Ravada;
use Ravada::Domain;
use Ravada::Utils;
use Sys::Virt;
my ($HELP);
my $PORT = 3000;
my $HOST = 'localhost';
my $TIMEOUT = 60;
my $KEEP = 0;
my $CONSOLE = 1;
my $START = 1;
my $PING = 1;
my $DEBUG;
my $REMOTE_VIEWER = `which remote-viewer`;
my $CONT = 0;
my $RVD_BACK = Ravada->new();
my $RVD_FRONT = Ravada::Front->new();
my $USER_DAEMON = Ravada::Utils->user_daemon();
help => \$HELP
,debug => \$DEBUG
,'requests=s' => \$N_REQUESTS
,'timeout=s' => \$TIMEOUT
,'keep' => \$KEEP
,'console!' => \$CONSOLE
,'start!' => \$START
,'ping!' => \$PING
) or exit;
my ($ID_BASE) = shift @ARGV;
if ($HELP || !$ID_BASE) {
if (!$ID_BASE) {
warn "ERROR: I need the id of a domain to use as base for the benchmark.\n"
." You can use any virtual machine id, but it will be converted to base if\n"
." it already hasn't.\n";
my $rvd_back = Ravada->new();
for my $machine ($rvd_back->list_domains( )) {
next if $machine->is_volatile || !$machine->is_base;
print $machine->id."\t".$machine->name;
print " (base)" if $machine->is_base;
print "\n";
die "$0 [--help] [--requests=X] [--timeout=$TIMEOUT] [--keep] [--console] "
." [--start] [--ping] id-base\n"
." - requests: Number of requests for create machines.\n"
." - timeout: Max waiting time for machine to create.\n"
." - keep: Keep the machines created after run the test.\n"
." Machines are cleaned up by default unless this is enabled.\n"
." - console: Show the console of virtual machines.\n"
." - noconsole: Do not show de console, default is to show.\n"
." - start: Start the machines after creation.\n"
." - nostart: Do not start the machines, default is to start.\n"
." - ping: Ping the machines after startup.\n"
." - noping: Do not ping the machines, default is to ping.\n"
sub domain_ip {
my $id = shift;
my $domain;
eval { $domain = Ravada::Domain->open($id) };
warn $@ if $@;
return if !$domain;
my @ip;
eval { @ip = $domain->domain->get_interface_addresses(Sys::Virt::Domain::INTERFACE_ADDRESSES_SRC_LEASE) };
warn $@ if $@;
return $ip[0]->{addrs}->[0]->{addr} if $ip[0];
sub new_domain_name {
my $cont = ++$CONT;
$cont ="0$cont" while length($cont)<3;
return "bench_".$$."_".$cont;
sub request_create {
my @requests;
for ( 1 .. $N_REQUESTS ) {
my $name = new_domain_name();
my $user = Ravada::Auth::SQL::add_user(
name => "user_$name"
,is_temporary => 0
my @args = (
id_base => $ID_BASE
,id_owner => $user->id
,name => $name
,remote_ip => ''
push @args,(start => 1) if $START;
push @requests,(Ravada::Request->create_domain(@args));
print "Request create machine $name\n" if $DEBUG;
return map{ $_->id => $_ } @requests;
sub show_console {
my $domain = shift;
my $display = $domain->display($USER_DAEMON);
if (!$domain->spice_password && $display) {
warn "Show console for domain ".$domain->name."\n";
my $pid = fork();
if(!$pid) {
my $cmd="$REMOTE_VIEWER -z 50 $display";
print `$cmd`;
exit 0;
sub open_domain {
my $req = shift;
confess "Req is not request" if ref($req) !~ /Request/i;
my $domain_name = $req->args('name');
my $domain;
my $t0 = time;
my $t1 = time;
for ( ;; ) {
exit_timeout($domain_name) if time-$t0 > $TIMEOUT;
$domain = $RVD_BACK->search_domain($domain_name);
confess "Domain $domain_name not a domain " if ref($domain) !~ /Domain/;
return $domain if $domain;
if ($t1 - time > 1 ) {
$t1 = time;
sub wait_domain_active {
my $domain = shift;
my $t0 = time;
my $t1 = time;
my $msg = 0;
for ( ;; ) {
last if $domain->is_active;
last if !check_free_memory($domain->_vm);
exit_timeout($domain->name) if time-$t0 > $TIMEOUT;
if (time - $t1 > 1 ) {
print "\tWaiting for ".$domain->name." " if !$msg++;
print ".";
$t1 = time;
print "\n" if $msg;
sub check_free_memory {
my $vm = shift;
my $free_mem = $vm->free_memory / 1024 / 1024;
$free_mem =~ s/(\d+\.\d?).*/$1/;
return $free_mem >= 1;
sub wait_domain_up{
my $domain = shift;
my $p = Net::Ping->new('icmp');
my $p_tcp = Net::Ping->new('tcp');
my $t0 = time;
my $t1 = time;
my $ip_showed = 0;
my $msg = 0;
for ( ;; ) {
if ( time - $t1 > 1) {
print "\tWaiting for machine ".$domain->name if !$msg++;
print ".";
$t1 = time;
exit_timeout($domain->name) if time-$t0 > $TIMEOUT;
last if !check_free_memory($domain->_vm);
my $is_active;
eval { $is_active = $domain->is_active };
warn $@ if $@;
last if $@;
my ($ip) = domain_ip($domain->id);
if ($ip) {
if ($msg) {
print " ".$ip if !$ip_showed++;
last if $p->ping($ip,1) || $p_tcp->ping($ip,1);
print "\n" if $msg;
sub shutdown_all {
my $verbose = shift;
my @machines = $RVD_BACK->list_domains(active => 1);
my @reqs;
my $n = 0;
for my $machine( @machines ) {
push @reqs,(Ravada::Request->force_shutdown_domain(
uid => $USER_DAEMON->id
,id_domain => $machine->id
print "Shutting down ".$machine->name."\n" if $verbose;
return if !$n;
print "Waiting for $n machines to shut down\n" if $n;
for ( 1 .. $TIMEOUT * $n ) {
my $pending = 0;
for my $req(@reqs) {
$pending++ if $req->status ne 'done';
last if !$pending;
print ".";
sleep 1;
for ( 1 .. $TIMEOUT * $n) {
my $still_there = 0;
for my $machine( @machines ) {
my $domain;
eval { $domain = Ravada::Domain->open($machine->id) };
warn $@ if $@ && $@ !~ /Domain not found/;
$still_there++ if $domain && $domain->is_active;
last if !$still_there;
print ".";
sleep 1;
print "\n";
sub remove_old {
sub remove_old_users {
for my $user_f ( @{$RVD_FRONT->list_users} ) {
next if $user_f->{name} !~ /^user_bench/;
my $user = Ravada::Auth::SQL->search_by_id($user_f->{id});
sub remove_old_machines {
my @machines = $RVD_BACK->list_domains();
my @reqs;
for my $machine( @machines ) {
next if $machine->name !~ /^bench/;
warn "Removing ".$machine->name."\n" if $DEBUG;
push @reqs,(Ravada::Request->remove_domain(
uid => $USER_DAEMON->id
,name => $machine->name
return if !scalar @reqs;
print "Waiting for ".scalar(@reqs)." machines to be removed.\n";
for ( 1 .. $TIMEOUT * scalar(@reqs) ) {
my $pending = 0;
for my $req(@reqs) {
$pending++ if $req->status ne 'done';
last if !$pending;
print ".";
sleep 1;
sub set_base_volatile_anon {
my $domain = Ravada::Domain->open($ID_BASE);
if ( !$domain->is_base ) {
print "Preparing base for ".$domain->name."\n";
sub init {
my $base = Ravada::Domain->open($ID_BASE);
my $free_memory = $base->_vm->free_memory();
my $info = $base->get_info();
my $cpu_count = `grep -c -P '^processor\\s+:' /proc/cpuinfo`;
chomp $cpu_count;
my $rec_n_requests = int($free_memory / $info->{memory})-1;
$rec_n_requests = $base->_vm->active_limit
if $base->_vm->active_limit
&& $base->_vm->active_limit < int($free_memory / $info->{memory})-1
&& int($cpu_count);
$N_REQUESTS = $rec_n_requests if !$N_REQUESTS;
if ( $N_REQUESTS != $rec_n_requests) {
warn "WARNING: You requested the creation of $N_REQUESTS machines.\n"
."But with ".int($free_memory / 1024 /1024)." Gb free it is recommended"
." the creation of $rec_n_requests\n";
print "Benchmarking the creation of $N_REQUESTS machines cloned from "
sub exit_timeout {
my $name = shift;