diff --git a/CHANGELOG.md b/CHANGELOG.md index ed9ca9e4c..6011d2c4d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,25 +3,8 @@ **Implemented enhancements:** -- Show information about downloading ISO [\#185] -- Create new machine from ISO [\#138] -- Add graphics options in VM settings [\#260] -- Add a description associated with the machine [\#275] -- Improve show clones in admin [\#279] -- Running development release [\#281] -- Add Windows' definitions for new machines [\#289] -- Give feedback on rename machine [\#291] -- Copy spice password to clipboard [\#300] -- API for opening a Virtual Machine [\#306] -- Disable running Base [\#327] +- Review volatile desktops and Kiosk mode [\#320] +- Manage user permissions [\#222] **Fixed bugs:** -- KVM domains start when creating base [\#271] -- Swap volume should not be mandatory [\#278] -- Allow dot, underscore and dash in the username [\#311] -- Check for duplicate machine name on copy [\#313] -- Wait for prepare base before create vm [\#314] -- rvd services start before mysql [\#321] -- Download Debian stretch iso fails [\#326] -- Xubuntu ISO files conflict [\#335] diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 509a969e0..018176c22 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -50,6 +50,8 @@ everyone is a beginner at first :smile_cat: Follow this guide about running [Ravada in development mode](http://ravada.readthedocs.io/en/latest/devel-docs/run.html). +If you change a translation or language file make sure you follow this small [guide](http://ravada.readthedocs.io/en/latest/devel-docs/translations.html?highlight=translate) and don't forget to add the issue number when committing. + ### 6. Make a Pull Request At this point, you should switch back to your master branch and make sure it's diff --git a/Makefile.PL b/Makefile.PL index 55dd35e79..c51cad995 100644 --- a/Makefile.PL +++ b/Makefile.PL @@ -6,7 +6,7 @@ use ExtUtils::MakeMaker; WriteMakefile( VERSION => '0.01', PREREQ_PM => { - 'Mojolicious' => '7.14' + 'Mojolicious' => '7.01' ,'DBIx::Connector' => 0 ,'Authen::Passphrase' => 0 ,'IPC::Run3' => 0 diff --git a/bin/rvd_back.pl b/bin/rvd_back.pl index 01012b07d..ee92cc9bd 100755 --- a/bin/rvd_back.pl +++ b/bin/rvd_back.pl @@ -39,6 +39,8 @@ my $START_DOMAIN; my $SHUTDOWN_DOMAIN; +my $IMPORT_DOMAIN_OWNER; + my $USAGE = "$0 " ." [--debug] [--config=$FILE_CONFIG_DEFAULT] [--add-user=name] [--add-user-ldap=name]" ." [--change-password] [--make-admin=username] [--import-vbox=image_file.vdi]" @@ -48,6 +50,7 @@ ." --add-user-ldap : adds a new LDAP user\n" ." --change-password : changes the password of an user\n" ." --import-domain : import a domain\n" + ." --import-domain-owner : owner of the domain to import\n" ." --make-admin : make user admin\n" ." --config : config file, defaults to $FILE_CONFIG_DEFAULT" ." -X : start in foreground\n" @@ -87,6 +90,7 @@ ,'add-user-ldap=s'=> \$ADD_USER_LDAP ,'import-domain=s' => \$IMPORT_DOMAIN ,'import-vbox=s' => \$IMPORT_VBOX +,'import-domain-owner=s' => \$IMPORT_DOMAIN_OWNER ) or exit; $START = 1 if $DEBUG || $FILE_CONFIG || $NOFORK; @@ -259,9 +263,12 @@ sub remove_admin { sub import_domain { my $name = shift; print "Virtual Manager: KVM\n"; - print "User name that will own the domain in Ravada : "; - my $user = ; - chomp $user; + my $user = $IMPORT_DOMAIN_OWNER; + if (!$user) { + print "User name that will own the domain in Ravada : "; + $user = ; + chomp $user; + } my $ravada = Ravada->new( %CONFIG ); $ravada->import_domain(name => $name, vm => 'KVM', user => $user); } diff --git a/etc/xml/dsl-i386.xml b/etc/xml/dsl-i386.xml index c57ef09e8..daf79105a 100644 --- a/etc/xml/dsl-i386.xml +++ b/etc/xml/dsl-i386.xml @@ -5,7 +5,7 @@ 1048576 1 - hvm + hvm @@ -52,6 +52,10 @@ + + +
+ @@ -74,4 +78,3 @@ - diff --git a/etc/xml/jessie-amd64.xml b/etc/xml/jessie-amd64.xml index 8fb6d90f7..16a648f83 100644 --- a/etc/xml/jessie-amd64.xml +++ b/etc/xml/jessie-amd64.xml @@ -5,7 +5,7 @@ 1048576 1 - hvm + hvm @@ -52,6 +52,10 @@ + + +
+ diff --git a/etc/xml/jessie-i386.xml b/etc/xml/jessie-i386.xml index 62388faf2..3085e1574 100644 --- a/etc/xml/jessie-i386.xml +++ b/etc/xml/jessie-i386.xml @@ -5,7 +5,7 @@ 1048576 1 - hvm + hvm @@ -52,10 +52,19 @@ + + +
+ - - + + + + + + +
@@ -69,4 +78,3 @@ - diff --git a/etc/xml/trusty-amd64.xml b/etc/xml/trusty-amd64.xml index c633f1f68..fee43d768 100644 --- a/etc/xml/trusty-amd64.xml +++ b/etc/xml/trusty-amd64.xml @@ -5,7 +5,7 @@ 1048576 1 - hvm + hvm @@ -52,10 +52,14 @@ + + +
+ - - + + @@ -70,7 +74,7 @@
- +
diff --git a/etc/xml/trusty-i386.xml b/etc/xml/trusty-i386.xml index 247b835f6..10107683a 100644 --- a/etc/xml/trusty-i386.xml +++ b/etc/xml/trusty-i386.xml @@ -5,7 +5,7 @@ 1048576 1 - hvm + hvm @@ -52,10 +52,19 @@ + + +
+ - - + + + + + + +
@@ -69,4 +78,3 @@ - diff --git a/etc/xml/win10.xml b/etc/xml/win10.xml index 1e1e0cc78..6d8c1c687 100644 --- a/etc/xml/win10.xml +++ b/etc/xml/win10.xml @@ -5,7 +5,7 @@ 4194304 4 - hvm + hvm @@ -87,8 +87,8 @@ - - + + @@ -113,4 +113,3 @@ - diff --git a/etc/xml/win7pro.xml b/etc/xml/win7pro.xml index 89f072c6f..d1569eb67 100644 --- a/etc/xml/win7pro.xml +++ b/etc/xml/win7pro.xml @@ -12,7 +12,7 @@ or other application using the libvirt API. 4194304 2 - hvm + hvm @@ -104,7 +104,13 @@ or other application using the libvirt API. - + + + + + + +
diff --git a/etc/xml/win8.1.xml b/etc/xml/win8.1.xml index 01c6621d7..49f78995c 100644 --- a/etc/xml/win8.1.xml +++ b/etc/xml/win8.1.xml @@ -12,7 +12,7 @@ or other application using the libvirt API. 4194304 2 - hvm + hvm @@ -104,7 +104,13 @@ or other application using the libvirt API. - + + + + + + +
diff --git a/etc/xml/windows_10.xml b/etc/xml/windows_10.xml index 19d648ffb..bffe462e0 100644 --- a/etc/xml/windows_10.xml +++ b/etc/xml/windows_10.xml @@ -5,7 +5,7 @@ 1048576 1 - hvm + hvm @@ -82,7 +82,11 @@ - + + + + +
diff --git a/etc/xml/windows_12.xml b/etc/xml/windows_12.xml index 7e0c6e2ea..1efa0b296 100644 --- a/etc/xml/windows_12.xml +++ b/etc/xml/windows_12.xml @@ -5,7 +5,7 @@ 1048576 1 - hvm + hvm @@ -91,7 +91,11 @@ - + + + + +
diff --git a/etc/xml/windows_7.xml b/etc/xml/windows_7.xml index 87976833d..137555ac8 100644 --- a/etc/xml/windows_7.xml +++ b/etc/xml/windows_7.xml @@ -5,7 +5,7 @@ 1048576 1 - hvm + hvm @@ -91,7 +91,11 @@ - + + + + +
diff --git a/etc/xml/windows_8.xml b/etc/xml/windows_8.xml index f31ac6ff5..51bd2a1a4 100644 --- a/etc/xml/windows_8.xml +++ b/etc/xml/windows_8.xml @@ -8,7 +8,7 @@ /machine - hvm + hvm @@ -102,8 +102,7 @@ - - +
@@ -118,7 +117,11 @@ - + + + + + diff --git a/etc/xml/windows_xp.xml b/etc/xml/windows_xp.xml index 30e84b1ba..44fcb8d49 100644 --- a/etc/xml/windows_xp.xml +++ b/etc/xml/windows_xp.xml @@ -5,7 +5,7 @@ 524288 1 - hvm + hvm @@ -91,7 +91,11 @@ - + + + + +
diff --git a/etc/xml/xenial-i386.xml b/etc/xml/xenial-i386.xml index 1067e8245..4883c42a6 100644 --- a/etc/xml/xenial-i386.xml +++ b/etc/xml/xenial-i386.xml @@ -5,7 +5,7 @@ 1048576 1 - hvm + hvm @@ -52,6 +52,10 @@ + + +
+ @@ -70,7 +74,7 @@
- +
diff --git a/etc/xml/xenial64-amd64.xml b/etc/xml/xenial64-amd64.xml index 0ae7ee439..859ceedc3 100644 --- a/etc/xml/xenial64-amd64.xml +++ b/etc/xml/xenial64-amd64.xml @@ -5,7 +5,7 @@ 1048576 1 - hvm + hvm @@ -52,6 +52,10 @@ + + +
+ @@ -70,7 +74,7 @@
- +
diff --git a/etc/xml/yakkety64-amd64.xml b/etc/xml/yakkety64-amd64.xml index 7236e04cf..e27acbd9b 100644 --- a/etc/xml/yakkety64-amd64.xml +++ b/etc/xml/yakkety64-amd64.xml @@ -5,7 +5,7 @@ 1048576 1 - hvm + hvm @@ -52,6 +52,10 @@ + + +
+ @@ -70,7 +74,7 @@
- +
diff --git a/etc/xml/zesty-amd64.xml b/etc/xml/zesty-amd64.xml index 7236e04cf..f4330cbda 100644 --- a/etc/xml/zesty-amd64.xml +++ b/etc/xml/zesty-amd64.xml @@ -5,7 +5,7 @@ 1048576 1 - hvm + hvm @@ -52,10 +52,14 @@ + + +
+ - - + + @@ -70,7 +74,7 @@
- +
diff --git a/lib/Ravada.pm b/lib/Ravada.pm index 6a71772eb..96a424145 100644 --- a/lib/Ravada.pm +++ b/lib/Ravada.pm @@ -3,7 +3,7 @@ package Ravada; use warnings; use strict; -our $VERSION = '0.2.10'; +our $VERSION = '0.3.0'; use Carp qw(carp croak); use Data::Dumper; @@ -171,7 +171,17 @@ sub _update_isos { my $table = 'iso_images'; my $field = 'name'; my %data = ( - zesty => { + artful => { + name => 'Ubuntu Artful Aardvark' + ,description => 'Ubuntu 17.10 Artful Aardvark 64 bits' + ,arch => 'amd64' + ,xml => 'yakkety64-amd64.xml' + ,xml_volume => 'yakkety64-volume.xml' + ,url => 'http://releases.ubuntu.com/17.10/' + ,file_re => ,'ubuntu-17.10.*desktop-amd64.iso' + ,md5_url => ,'http://releases.ubuntu.com/17.10/MD5SUMS' + } + ,zesty => { name => 'Ubuntu Zesty Zapus' ,description => 'Ubuntu 17.04 Zesty Zapus 64 bits' ,arch => 'amd64' @@ -202,6 +212,16 @@ sub _update_isos { ,xml_volume => 'xenial64-volume.xml' ,sha256_url => 'http://fedora.mirrors.ovh.net/linux/releases/25/Workstation/x86_64/iso/Fedora-Workstation-25-.*-x86_64-CHECKSUM' } + ,xubuntu_artful => { + name => 'Xubuntu Artful Aardvark' + ,description => 'Xubuntu 17.10 Artful Aardvark 64 bits' + ,arch => 'amd64' + ,xml => 'yakkety64-amd64.xml' + ,xml_volume => 'yakkety64-volume.xml' + ,md5_url => 'http://archive.ubuntu.com/ubuntu/dists/artful/main/installer-amd64/current/images/MD5SUMS' + ,url => 'http://archive.ubuntu.com/ubuntu/dists/artful/main/installer-amd64/current/images/netboot/mini.iso' + ,rename_file => 'xubuntu_artful.iso' + } ,xubuntu_zesty => { name => 'Xubuntu Zesty Zapus' ,description => 'Xubuntu 17.04 Zesty Zapus 64 bits' @@ -221,6 +241,14 @@ sub _update_isos { ,md5 => 'fe495d34188a9568c8d166efc5898d22' ,rename_file => 'xubuntu_xenial_mini.iso' } + ,lubuntu_aardvark => { + name => 'Lubuntu Artful Aardvark' + ,description => 'Lubuntu 17.10 Artful Aardvark 64 bits' + ,url => 'http://cdimage.ubuntu.com/lubuntu/releases/17.10/release/lubuntu-17.10-desktop-amd64.iso' + ,md5_url => 'http://cdimage.ubuntu.com/lubuntu/releases/17.10/release/MD5SUMS' + ,xml => 'yakkety64-amd64.xml' + ,xml_volume => 'yakkety64-volume.xml' + } ,lubuntu_zesty => { name => 'Lubuntu Zesty Zapus' ,description => 'Lubuntu 17.04 Zesty Zapus 64 bits' @@ -295,32 +323,32 @@ sub _update_domain_drivers_types($self) { id => 4, ,name => 'image' ,description => 'Graphics Options' - ,vm => 'qemu' + ,vm => 'KVM' }, jpeg => { id => 5, ,name => 'jpeg' ,description => 'Graphics Options' - ,vm => 'qemu' + ,vm => 'KVM' }, zlib => { id => 6, ,name => 'zlib' ,description => 'Graphics Options' - ,vm => 'qemu' + ,vm => 'KVM' }, playback => { id => 7, ,name => 'playback' ,description => 'Graphics Options' - ,vm => 'qemu' + ,vm => 'KVM' }, streaming => { id => 8, ,name => 'streaming' ,description => 'Graphics Options' - ,vm => 'qemu' + ,vm => 'KVM' } }; @@ -515,13 +543,32 @@ sub _update_table($self, $table, $field, $data) { } } +sub _remove_old_isos { + my $self = shift; + my $sth = $CONNECTOR->dbh->prepare("DELETE FROM iso_images " + ." WHERE url like '%debian-9.0%iso'" + ); + $sth->execute(); + $sth->finish; +} + sub _update_data { my $self = shift; + $self->_remove_old_isos(); $self->_update_isos(); $self->_update_user_grants(); $self->_update_domain_drivers_types(); $self->_update_domain_drivers_options(); + $self->_update_old_qemus(); +} + +sub _update_old_qemus($self) { + my $sth = $CONNECTOR->dbh->prepare("UPDATE vms SET vm_type='KVM'" + ." WHERE vm_type='qemu' AND name ='KVM_localhost'" + ); + $sth->execute; + } sub _set_url_isos($self, $new_url='http://localhost/iso/') { @@ -674,6 +721,10 @@ sub _upgrade_tables { $self->_upgrade_table('domains','description','text DEFAULT NULL'); $self->_upgrade_table('domains','run_timeout','int DEFAULT NULL'); $self->_upgrade_table('domains','start_time','int DEFAULT 0'); + $self->_upgrade_table('domains','is_volatile','int NOT NULL DEFAULT 0'); + + $self->_upgrade_table('domains_network','allowed','int not null default 1'); + } @@ -721,7 +772,13 @@ sub _init_config { my $connector = shift; confess "Deprecated connector" if $connector; - $CONFIG = YAML::LoadFile($file); + die "ERROR: Missing config file $file\n" + if !-e $file; + + eval { $CONFIG = YAML::LoadFile($file) }; + + die "ERROR: Format error in config file $file\n$@" if $@; + $CONFIG->{vm} = [] if !$CONFIG->{vm}; $LIMIT_PROCESS = $CONFIG->{limit_process} @@ -755,7 +812,7 @@ sub _create_vm_kvm { my $vm_kvm; - $vm_kvm = Ravada::VM::KVM->new( connector => ( $self->connector or $CONNECTOR )); + $vm_kvm = Ravada::VM::KVM->new( ); my ($internal_vm , $storage); $storage = $vm_kvm->dir_img(); @@ -896,7 +953,10 @@ sub create_domain { $vm = $self->search_vm($vm_name); confess "ERROR: vm $vm_name not found" if !$vm; } - $vm = $self->vm->[0] if !$vm; + if ($args{id_base}) { + my $base = Ravada::Domain->open($args{id_base}); + $vm = Ravada::VM->open($base->_vm->id); + } confess "No vm found" if !$vm; @@ -1679,6 +1739,21 @@ sub _cmd_open_iptables { ); } +sub _cmd_clone($self, $request) { + my $domain = Ravada::Domain->open($request->args('id_domain')); + + my @args = ( request => $request); + push @args, ( memory => $request->args('memory')) + if $request->defined_arg('memory'); + + $domain->clone( + name => $request->args('name') + ,user => Ravada::Auth::SQL->search_by_id($request->args('uid')) + ,@args + ); + +} + sub _cmd_start { my $self = shift; my $request = shift; @@ -1874,13 +1949,25 @@ sub _cmd_set_driver { $domain->set_driver_id($request->args('id_option')); } +sub _cmd_refresh_storage($self, $request) { + + my $vm; + if ($request->defined_arg('id_vm')) { + $vm = Ravada::VM->open($request->defined_arg('id_vm')); + } else { + $vm = $self->search_vm('KVM'); + } + $vm->refresh_storage(); +} + sub _req_method { my $self = shift; my $cmd = shift; my %methods = ( - start => \&_cmd_start + clone => \&_cmd_clone + ,start => \&_cmd_start ,pause => \&_cmd_pause ,create => \&_cmd_create ,remove => \&_cmd_remove @@ -1898,6 +1985,7 @@ sub _req_method { ,open_iptables => \&_cmd_open_iptables ,list_vm_types => \&_cmd_list_vm_types ,force_shutdown => \&_cmd_force_shutdown +,refresh_storage => \&_cmd_refresh_storage ); return $methods{$cmd}; diff --git a/lib/Ravada/Auth/LDAP.pm b/lib/Ravada/Auth/LDAP.pm index 32974de31..1236ebc6b 100644 --- a/lib/Ravada/Auth/LDAP.pm +++ b/lib/Ravada/Auth/LDAP.pm @@ -18,6 +18,7 @@ use Moose; use Net::LDAP; use Net::LDAPS; use Net::LDAP::Entry; +use Net::LDAP::Util qw(escape_filter_value); use Net::Domain qw(hostdomain); use Ravada::Auth::SQL; @@ -65,6 +66,9 @@ sub add_user { _init_ldap_admin(); + $name = escape_filter_value($name); + $password = escape_filter_value($password); + confess "No dc base in config ".Dumper($$CONFIG->{ldap}) if !_dc_base(); my ($givenName, $sn) = $name =~ m{(\w+)\.(.*)}; @@ -124,6 +128,8 @@ Search user by uid sub search_user { my $username = shift; + $username = escape_filter_value($username); + _init_ldap(); my $ldap = (shift or $LDAP_ADMIN); @@ -172,6 +178,8 @@ sub add_group { my $name = shift; my $base = (shift or _dc_base()); + $name = escape_filter_value($name); + my $mesg = $LDAP_ADMIN->add( cn => $name ,dn => "cn=$name,ou=groups,$base" @@ -224,6 +232,8 @@ sub search_group { my $base = ( $args{base} or "ou=groups,"._dc_base() ); my $ldap = ( $args{ldap} or $LDAP ); + $name = escape_filter_value($name); + my $mesg = $ldap ->search ( filter => "cn=$name" ,base => $base diff --git a/lib/Ravada/Auth/SQL.pm b/lib/Ravada/Auth/SQL.pm index 5f9772df3..6f6613b60 100644 --- a/lib/Ravada/Auth/SQL.pm +++ b/lib/Ravada/Auth/SQL.pm @@ -420,6 +420,33 @@ sub change_password { $sth->execute(sha1_hex($password), $self->name); } +=head2 compare_password + +Changes the input with the password of an User + + $user->compare_password(); + +Arguments: password + +=cut + +sub compare_password { + my $self = shift; + my $password = shift or die "ERROR: password required\n"; + + _init_connector(); + + my $sth= $$CON->dbh->prepare("SELECT password FROM users WHERE name=?"); + $sth->execute($self->name); + my $hex_pass = $sth->fetchrow(); + if ($hex_pass eq sha1_hex($password)) { + return 1; + } + else { + return 0; + } +} + =head2 language Updates or selects the language selected for an User diff --git a/lib/Ravada/Domain.pm b/lib/Ravada/Domain.pm index 1cbea15f0..302c3c70e 100644 --- a/lib/Ravada/Domain.pm +++ b/lib/Ravada/Domain.pm @@ -11,6 +11,7 @@ Ravada::Domain - Domains ( Virtual Machines ) library for Ravada use Carp qw(carp confess croak cluck); use Data::Dumper; +use File::Copy; use Hash::Util qw(lock_hash); use Image::Magick; use JSON::XS; @@ -41,6 +42,8 @@ requires 'display'; requires 'is_active'; requires 'is_hibernated'; requires 'is_paused'; +requires 'is_removed'; + requires 'start'; requires 'shutdown'; requires 'shutdown_now'; @@ -160,6 +163,8 @@ after 'remove_base' => \&_post_remove_base; before 'rename' => \&_pre_rename; after 'rename' => \&_post_rename; +before 'clone' => \&_pre_clone; + after 'screenshot' => \&_post_screenshot; after '_select_domain_db' => \&_post_select_domain_db; @@ -172,6 +177,11 @@ sub BUILD { $self->is_known(); } +sub BUILD { + my $self = shift; + $self->_init_connector(); +} + sub _vm_connect { my $self = shift; $self->_vm->connect(); @@ -238,9 +248,9 @@ sub _allow_manage { } -sub _allow_remove { - my $self = shift; - my ($user) = @_; +sub _allow_remove($self, $user) { + + confess "ERROR: Undefined user" if !defined $user; die "ERROR: remove not allowed for user ".$user->name if !$user->can_remove(); @@ -261,7 +271,7 @@ sub _allow_shutdown { my $user = $args{user} || confess "ERROR: Missing user arg"; if ( $self->id_base() && $user->can_shutdown_clone()) { - my $base = $self->open($self->id_base); + my $base = Ravada::Domain->open($self->id_base); return if $base->id_owner == $user->id; } elsif($user->can_shutdown_all) { return; @@ -370,6 +380,7 @@ sub _check_used_memory { next if !$alive; my $info = $domain->get_info; + confess "No info memory ".Dumper($info) if !exists $info->{memory}; $used_memory += $info->{memory}; } @@ -469,6 +480,8 @@ Returns: Domain object read only =cut sub open($class, $id) { + confess "Missing id" if !defined $id; + my $self = {}; if (ref($class)) { @@ -486,7 +499,9 @@ sub open($class, $id) { my $vm_class = "Ravada::VM::".$row->{vm}; bless $vm0, $vm_class; - my $vm = $vm0->new( readonly => 1); + my @ro = (); + @ro = (readonly => 1 ) if $>; + my $vm = $vm0->new( @ro ); return $vm->search_domain($row->{name}); } @@ -924,6 +939,7 @@ sub _convert_png { my $err = $in->Read($file_in); confess $err if $err; + $in->Scale(width => 250, height => 188); $in->Write("png24:$file_out"); chmod 0755,$file_out or die "$! chmod 0755 $file_out"; @@ -995,25 +1011,80 @@ sub clone { my $self = shift; my %args = @_; - my $name = $args{name} or confess "ERROR: Missing domain cloned name"; - confess "ERROR: Missing request user" if !$args{user}; + my $name = delete $args{name} + or confess "ERROR: Missing domain cloned name"; + + my $user = delete $args{user} + or confess "ERROR: Missing request user"; + + return $self->_copy_clone(@_) if $self->id_base(); + + my $request = delete $args{request}; + my $memory = delete $args{memory}; + + confess "ERROR: Unknown args ".join(",",sort keys %args) + if keys %args; - my $uid = $args{user}->id; + my $uid = $user->id; - $self->prepare_base($args{user}) if !$self->is_base(); + if ( !$self->is_base() ) { + $request->status("working","Preparing base") if $request; + $self->prepare_base($user) + } my $id_base = $self->id; + my @args_copy = (); + push @args_copy, ( memory => $memory ) if $memory; + push @args_copy, ( request => $request ) if $request; + my $clone = $self->_vm->create_domain( name => $name ,id_base => $id_base ,id_owner => $uid ,vm => $self->vm ,_vm => $self->_vm + ,@args_copy ); return $clone; } +sub _copy_clone($self, %args) { + my $name = delete $args{name} or confess "ERROR: Missing name"; + my $user = delete $args{user} or confess "ERROR: Missing user"; + my $memory = delete $args{memory}; + my $request = delete $args{request}; + + confess "ERROR: Unknown arguments ".join(",",sort keys %args) + if keys %args; + + my $base = Ravada::Domain->open($self->id_base); + + my @copy_arg; + push @copy_arg, ( memory => $memory ) if $memory; + + $request->status("working","Copying domain ".$self->name + ." to $name") if $request; + + my $copy = $self->_vm->create_domain( + name => $name + ,id_base => $base->id + ,id_owner => $user->id + ,_vm => $self->_vm + ,@copy_arg + ); + my @volumes = $self->list_volumes_target; + my @copy_volumes = $copy->list_volumes_target; + + my %volumes = map { $_->[1] => $_->[0] } @volumes; + my %copy_volumes = map { $_->[1] => $_->[0] } @copy_volumes; + for my $target (keys %volumes) { + copy($volumes{$target}, $copy_volumes{$target}) + or die "$! $volumes{$target}, $copy_volumes{$target}" + } + return $copy; +} + sub _post_pause { my $self = shift; my $user = shift; @@ -1047,14 +1118,16 @@ sub _post_shutdown { my %arg = @_; my $timeout = $arg{timeout}; - $self->_remove_temporary_machine(@_); $self->_remove_iptables(@_); - $self->clean_swap_volumes(@_) if $self->id_base() && !$self->is_active; + if ($self->id_base()) { + $self->clean_swap_volumes(@_) if !$self->is_removed && !$self->is_removed; + } + $self->_remove_temporary_machine(@_); - if (defined $timeout) { - if ($timeout<2 && $self->is_active) { + if (defined $timeout && !$self->is_removed) { + if ($timeout<2 && !$self->is_removed && $self->is_active) { sleep $timeout; - return $self->_do_force_shutdown() if $self->is_active; + return $self->_do_force_shutdown() if !$self->is_removed && $self->is_active; } my $req = Ravada::Request->force_shutdown_domain( @@ -1137,16 +1210,12 @@ sub _remove_iptables { sub _remove_temporary_machine { my $self = shift; + return if !$self->is_volatile; my %args = @_; - my $user; - - return if !$self->is_known(); + my $user = delete $args{user} or confess "ERROR: Missing user"; - eval { $user = Ravada::Auth::SQL->search_by_id($self->id_owner) }; - return if !$user; - - if ($user->is_temporary) { - $self->remove($user); + if ($self->is_volatile) { +# return $self->_after_remove_domain() if !$self->is_known(); my $req= $args{request}; $req->status( "removing" @@ -1154,6 +1223,12 @@ sub _remove_temporary_machine { ." because user " .$user->name." is temporary") if $req; + + if ($self->is_removed) { + $self->_after_remove_domain(); + } else { + $self->remove($user); + } } } @@ -1386,6 +1461,16 @@ sub is_public { return $self->_data('is_public'); } +=head2 is_volatile + +Returns if the domain is volatile, so it will be removed on shutdown + +=cut + +sub is_volatile($self, $value=undef) { + return $self->_set_data('is_volatile', $value); +} + =head2 run_timeout Sets or get the domain run timeout. When it expires it is shut down. @@ -1482,19 +1567,32 @@ List the drivers available for a domain. It may filter for a given type. sub drivers { my $self = shift; my $name = shift; - my $type = (shift or $self->_vm->type); + my $type = shift; + $type = $self->_vm->type if $self && !$type; _init_connector(); - $type = 'qemu' if $type =~ /^KVM$/; - my $query = "SELECT id from domain_drivers_types " - ." WHERE vm=?"; - $query .= " AND name=?" if $name; + my $query = "SELECT id from domain_drivers_types "; - my $sth = $$CONNECTOR->dbh->prepare($query); + my @sql_args = (); - my @sql_args = ($type); - push @sql_args,($name) if $name; + my @where; + if ($name) { + push @where,("name=?"); + push @sql_args,($name); + } + if ($type) { + my $type2 = $type; + if ($type =~ /qemu/) { + $type2 = 'KVM'; + } elsif ($type =~ /KVM/) { + $type2 = 'qemu'; + } + push @where, ("( vm=? OR vm=?)"); + push @sql_args, ($type,$type2); + } + $query .= "WHERE ".join(" AND ",@where) if @where; + my $sth = $$CONNECTOR->dbh->prepare($query); $sth->execute(@sql_args); @@ -1586,6 +1684,19 @@ sub list_all_requests { return list_requests(@_,'all'); } +=head2 get_driver + +Returns the driver from a domain + +Argument: name of the device [ optional ] +Returns all the drivers if not passwed + + my $driver = $domain->get_driver('video'); + +=cut + +sub get_driver {} + sub _dbh { my $self = shift; _init_connector() if !$CONNECTOR || !$$CONNECTOR; @@ -1632,4 +1743,18 @@ sub type { return $type; } +sub _pre_clone($self,%args) { + my $name = delete $args{name}; + my $user = delete $args{user}; + my $memory = delete $args{memory}; + delete $args{request}; + + confess "ERROR: Missing clone name " if !$name; + confess "ERROR: Invalid name '$name'" if $name !~ /^[a-z0-9_-]+$/i; + + confess "ERROR: Missing user owner of new domain" if !$user; + + confess "ERROR: Unknown arguments ".join(",",sort keys %args) if keys %args; +} + 1; diff --git a/lib/Ravada/Domain/KVM.pm b/lib/Ravada/Domain/KVM.pm index 415d0ba89..47e439ab2 100644 --- a/lib/Ravada/Domain/KVM.pm +++ b/lib/Ravada/Domain/KVM.pm @@ -186,16 +186,20 @@ sub remove { $self->_do_force_shutdown(); } - $self->remove_disks(); + + eval { $self->remove_disks(); }; + die $@ if $@ && $@ !~ /libvirt error code: 42/; # warn "WARNING: Problem removing disks for ".$self->name." : $@" if $@ && $0 !~ /\.t$/; - $self->_remove_file_image(); + eval { $self->_remove_file_image() }; + die $@ if $@ && $@ !~ /libvirt error code: 42/; # warn "WARNING: Problem removing file image for ".$self->name." : $@" if $@ && $0 !~ /\.t$/; # warn "WARNING: Problem removing ".$self->file_base_img." for ".$self->name # ." , I will try again later : $@" if $@; - $self->domain->undefine(); + eval { $self->domain->undefine() }; + die $@ if $@ && $@ !~ /libvirt error code: 42/; } @@ -858,6 +862,17 @@ Takes a screenshot, it stores it in file. =cut +sub handler { + my ($stream, $data, $n) = @_; + my $file_tmp = "/var/tmp/$$.tmp"; + + open my $out ,'>>',$file_tmp; + print $out $data; + close $out; + + return $n; +} + sub screenshot { my $self = shift; my $file = (shift or $self->_file_screenshot); @@ -869,24 +884,13 @@ sub screenshot { my $stream = $self->{_vm}->vm->new_stream(); my $mimetype = $self->domain->screenshot($stream,0); + $stream->recv_all(\&handler); - my $file_tmp = "$file.tmp"; - my $data; - my $bytes = 0; - open my $out, '>', $file_tmp or die "$! $file_tmp"; - while ( my $rv =$stream->recv($data,1024)) { - $bytes += $rv; - last if $rv<=0; - print $out $data; - } - close $out; + my $file_tmp = "/var/tmp/$$.tmp"; + $stream->finish; $self->_convert_png($file_tmp,$file); unlink $file_tmp or warn "$! removing $file_tmp"; - - $stream->finish; - - return $bytes; } sub _file_screenshot { @@ -1593,4 +1597,12 @@ sub pre_remove { $self->domain->managed_save_remove if $self->domain->has_managed_save_image; } +sub is_removed($self) { + my $is_removed = 0; + eval { $self->domain->get_xml_description}; + return 1 if $@ && $@ =~ /libvirt error code: 42/; + die $@ if $@; + return 0; +} + 1; diff --git a/lib/Ravada/Domain/Void.pm b/lib/Ravada/Domain/Void.pm index 5a52daafb..3dec17762 100644 --- a/lib/Ravada/Domain/Void.pm +++ b/lib/Ravada/Domain/Void.pm @@ -27,8 +27,8 @@ has '_ip' => ( our $DIR_TMP = "/var/tmp/rvd_void"; -our $IMPORT = `which import`; -chomp $IMPORT; +our $CONVERT = `which convert`; +chomp $CONVERT; #######################################3 sub BUILD { @@ -41,6 +41,10 @@ sub BUILD { if ($args->{id_base}) { my $base = Ravada::Domain->open($args->{id_base}); + + confess "ERROR: Wrong base ".ref($base)." ".$base->type + ."for domain in vm ".$self->_vm->type + if $base->type ne $self->_vm->type; my $drivers = $base->_value('drivers'); $self->_store(drivers => $drivers ); } @@ -333,8 +337,7 @@ sub screenshot { my $self = shift; my $file = (shift or $self->_file_screenshot); - my @cmd =($IMPORT,'-window','root' - ,'-resize','400x300' + my @cmd =($CONVERT,'-size', '400x300', 'xc:white' ,$file ); my ($in,$out,$err); @@ -346,11 +349,14 @@ sub _file_screenshot { return $DIR_TMP."/".$self->name.".png"; } -sub can_screenshot { return $IMPORT; } +sub can_screenshot { return $CONVERT; } sub get_info { my $self = shift; my $info = $self->_value('info'); + $self->_set_default_info() + if !$info->{memory}; + $info = $self->_value('info'); lock_keys(%$info); return $info; } @@ -467,4 +473,9 @@ sub clean_swap_volumes { sub hybernate { confess "Not supported"; } sub type { 'Void' } + +sub is_removed { + my $self = shift; + return !-e $self->_config_file(); +} 1; diff --git a/lib/Ravada/Front.pm b/lib/Ravada/Front.pm index cc6f5cdaf..9bba430b7 100644 --- a/lib/Ravada/Front.pm +++ b/lib/Ravada/Front.pm @@ -75,7 +75,7 @@ Returns a list of the base domains as a listref sub list_bases { my $self = shift; - my $sth = $CONNECTOR->dbh->prepare("SELECT * FROM domains where is_base=1"); + my $sth = $CONNECTOR->dbh->prepare("SELECT name, id, is_base FROM domains where is_base=1"); $sth->execute(); my @bases = (); @@ -84,6 +84,7 @@ sub list_bases { eval { $domain = $self->search_domain($row->{name}) }; next if !$domain; $row->{has_clones} = $domain->has_clones; + delete $row->{spice_password}; push @bases, ($row); } $sth->finish; @@ -126,7 +127,7 @@ sub list_machines_user { ,id_base => $id ); my %base = ( id => $id, name => $name - , is_public => $is_public + , is_public => ($is_public or 0) , screenshot => ($screenshot or '') , is_active => 0 , id_clone => undef @@ -185,7 +186,7 @@ sub list_domains { my $self = shift; my %args = @_; - my $query = "SELECT * FROM domains ORDER BY name"; + my $query = "SELECT name, id, id_base, is_base, is_public, is_volatile FROM domains "; my $where = ''; for my $field ( sort keys %args ) { @@ -194,13 +195,19 @@ sub list_domains { } $where = "WHERE $where" if $where; - my $sth = $CONNECTOR->dbh->prepare("$query $where"); + my $sth = $CONNECTOR->dbh->prepare("$query $where ORDER BY name"); $sth->execute(map { $args{$_} } sort keys %args); my @domains = (); while ( my $row = $sth->fetchrow_hashref) { my $domain ; eval { $domain = $self->search_domain($row->{name}) }; + if ( $row->{is_volatile} && !$domain ) { + $self->_remove_domain_db($row->{id}); + next; + } + $row->{has_clones} = 0 if !exists $row->{has_clones}; + $row->{is_locked} = 0 if !exists $row->{is_locked}; if ( $domain ) { $row->{is_active} = 1 if $domain->is_active; $row->{is_locked} = $domain->is_locked; @@ -212,6 +219,7 @@ sub list_domains { # $row->{disk_size} = 1 if $row->{disk_size} < 1; $row->{remote_ip} = $domain->remote_ip if $domain->is_active(); } + delete $row->{spice_password}; push @domains, ($row); } $sth->finish; @@ -219,6 +227,12 @@ sub list_domains { return \@domains; } +sub _remove_domain_db($self, $id) { + my $sth = $CONNECTOR->dbh->prepare("DELETE FROM domains WHERE id=?"); + $sth->execute($id); + $sth->finish; +} + =head2 domain_info Returns information of a domain @@ -298,6 +312,7 @@ sub list_iso_images { while (my $row = $sth->fetchrow_hashref) { push @iso,($row); + delete $row->{device} if $row->{device} && !-e $row->{device}; next if $row->{device}; my ($file); @@ -371,7 +386,7 @@ Returns a reference to a list of the users =cut sub list_users($self,$name=undef) { - my $sth = $CONNECTOR->dbh->prepare("SELECT * FROM users "); + my $sth = $CONNECTOR->dbh->prepare("SELECT id, name FROM users "); $sth->execute(); my @users = (); @@ -571,7 +586,7 @@ sub search_domain { my $name = shift; - my $sth = $CONNECTOR->dbh->prepare("SELECT * FROM domains WHERE name=?"); + my $sth = $CONNECTOR->dbh->prepare("SELECT vm, id_owner, id_base, description FROM domains WHERE name=?"); $sth->execute($name); my $row = $sth->fetchrow_hashref; @@ -672,7 +687,7 @@ sub search_domain_by_id { my $self = shift; my $id = shift; - my $sth = $CONNECTOR->dbh->prepare("SELECT * FROM domains WHERE id=?"); + my $sth = $CONNECTOR->dbh->prepare("SELECT name, id, id_base, is_base FROM domains WHERE id=?"); $sth->execute($id); my $row = $sth->fetchrow_hashref; @@ -737,7 +752,7 @@ sub list_bases_anonymous { my $net = Ravada::Network->new(address => $ip); - my $sth = $CONNECTOR->dbh->prepare("SELECT * FROM domains where is_base=1 AND is_public=1"); + my $sth = $CONNECTOR->dbh->prepare("SELECT id, name, id_base, is_public FROM domains where is_base=1 AND is_public=1"); $sth->execute(); my @bases = (); diff --git a/lib/Ravada/I18N/ca.po b/lib/Ravada/I18N/ca.po index 170aeeb8d..5ec753fcb 100644 --- a/lib/Ravada/I18N/ca.po +++ b/lib/Ravada/I18N/ca.po @@ -155,7 +155,7 @@ msgid "ShutDown" msgstr "Atura" msgid "Screenshot" -msgstr "" +msgstr "Captura de pantalla" msgid "Paused" msgstr "Pausat" @@ -164,7 +164,7 @@ msgid "Machine locked by requested" msgstr "Màquina bloquejada per" msgid "process" -msgstr "procés" +msgstr " procés" msgid "Copy" msgstr "Còpia" @@ -172,8 +172,11 @@ msgstr "Còpia" msgid "Error!" msgstr "Error!" +msgid "Error" +msgstr "Error" + msgid "Backend no available!" -msgstr "Backend no disponible!" +msgstr " Backend no disponible!" msgid "Subject" msgstr "Assumpte" @@ -203,7 +206,7 @@ msgid "messages" msgstr "missatges" msgid "Admin tools" -msgstr "" +msgstr "Eines d'administrador" msgid "Remove base" msgstr "Esborra base" @@ -236,7 +239,7 @@ msgid "Yes" msgstr "Si" msgid "No" -msgstr "" +msgstr "No" msgid "Make public" msgstr "Fes públic" @@ -291,12 +294,18 @@ msgstr "Benvingut" msgid "It is required a viewer to run the virtual machines." msgstr "Es necessita un visor per executar les màquines virtuals." -msgid "can create bases." -msgstr "pot crear bases." - msgid "Virtual Machines" msgstr "Màquines Virtuals" +msgid "user" +msgstr "usuari" + +msgid "password" +msgstr "contrasenya" + +msgid "Permissions" +msgstr "Permissos" + msgid "User" msgstr "Usuari" @@ -311,3 +320,203 @@ msgstr "Identifiqueu-vos amb el nom d'usuari de la Intranet UPC." msgid "bits) in your computer." msgstr "bits) al teu ordinador." + +msgid "For more information, check " +msgstr "Per a més informació, reviseu " + +msgid "the Windows Clients documentation" +msgstr "la documentació de Windows Clients" + +msgid "Version" +msgstr "Versió" + +msgid "Authors" +msgstr "Autors" + +msgid "Development" +msgstr "Desenvolupament" + +msgid "Below are listed the technologies used in this project:" +msgstr "A continuació hi ha la llista de tecnologies usades en aquest projecte:" + +msgid "The code is available on " +msgstr "El codi està disponible a " + +msgid "The code is " +msgstr "El codi està " + +msgid "License" +msgstr "Licència" + +msgid "hide clones" +msgstr "amaga els clons" + +msgid "Machine locked by" +msgstr "Màquina bloquejada per" + +msgid "This Machine is a base" +msgstr "Aquesta màquina és una base" + +msgid "Cloned" +msgstr "Clonada" + +msgid "is admin" +msgstr "és administrador" + +msgid "Add new user" +msgstr "Afegeix nou usuari" + +msgid "Register" +msgstr "Registrar" + +msgid "Search" +msgstr "Cerca" + +msgid "Admin tools" +msgstr "Eines d'administració" + +msgid "Admin tools" +msgstr "Eines d'administració" + +msgid "There are no public bases available in this system." +msgstr "No hi ha bases públiques disponibles en aquest sistema." + +msgid "There are no machines available in this system." +msgstr "No hi ha màquines disponibles en aquest sistema." + +msgid "Create one." +msgstr "Crea'n una." + +msgid "not public" +msgstr "no pública" + +msgid "User Settings" +msgstr "Configuració d'usuari" + +msgid "can change the settings of owned virtual machines." +msgstr "pot canviar les preferències de les seves màquines virtuals" + +msgid "can change the settings of any virtual machines." +msgstr "pot canviar les preferències de qualsevol màquina virtual." + +msgid "can change the settings of any virtual machines " +msgstr "pot canviar les preferències de qualsevol màquina virtual " + +msgid "cloned from one base owned by the user." +msgstr "clonat des d'una base de l'usuari." + +msgid "can clone public virtual machines." +msgstr "pot clonar màquines virtuals públiques." + +msgid "can clone any virtual machine." +msgstr "pot clonar qualsevol màquina virtual." + +msgid "can create bases." +msgstr "pot crear bases." + +msgid "can create virtual machines." +msgstr "pot crear màquines virtuals." + +msgid "can grant permissions to other users" +msgstr "pot donar permissos a altres usuaris" + +msgid "can hibernate any virtual machine." +msgstr "pot hibernar qualsevol màquina virtual." + +msgid "can hibernate clones from virtual machines owned by the user." +msgstr "pot hibernar clons des de qualsevol màquina virtual propietat de l'usuari." + +msgid "can hibernate any clone." +msgstr "pot hibernar qualsevol clon." + +msgid "can remove any virtual machines owned by the user." +msgstr "pot borrar qualsevol màquina virtual propietat de l'usuari." + +msgid "can remove any virtual machine." +msgstr "pot borrar qualsevol màquina virtual." + +msgid "can remove clones from virtual machines owned by the user." +msgstr "pot borrar clons de màquines virtuals propietat de l'usuari." + +msgid "can remove any clone." +msgstr "pot borrar qualsevol clon." + +msgid "can take a screenshot of any virtual machine owned by the user." +msgstr "pot realitzar una captura de pantalla de qualsevol màquina virtual propietat de l'usuari." + +msgid "can take a screenshot of any virtual machine." +msgstr "pot realitzar una captura de pantalla de qualsevol màquina virtual." + +msgid "can shutdown any virtual machine." +msgstr "pot apagar qualsevol màquina virtual." + +msgid "can shutdown clones from virtual machines owned by the user." +msgstr "pot apagar clons de màquines virtuals propietat de l'usuari." + +msgid "Submit Query" +msgstr "Enviar Pregunta" + +msgid "From Template" +msgstr "Des d'una plantilla" + +msgid "From Machine" +msgstr "Des d'una màquina" + +msgid "Backend" +msgstr "Backend" + +msgid "Select Template" +msgstr "Escull una plantilla" + +msgid "This ISO image has not been already downloaded. This may take many minutes, even hours until the file is fetched from Internet." +msgstr "Aquesta imatge ISO encara no ha estat descarregada. Pot trigar molts minuts, fins i tot hores, fins que l'arxiu hagi sigut extret de l'Internet." + +msgid "Download now" +msgstr "Descarrega ara" + +msgid "Select ISO" +msgstr "Escull ISO" + +msgid "Template" +msgstr "Plantilla" + +msgid "Disk Size: (GB)" +msgstr "Espai de disk: (GB)" + +msgid "Swap Size: (GB)" +msgstr "Espai d'swap: (GB)" + +msgid "Ram: (GB)" +msgstr "Ram: (GB)" + +msgid "Machine name is required." +msgstr "Fa falta el nom de la màquina." + +msgid "Machine name can't exceed 20 characters." +msgstr "El nom de la màquina no pot superar 20 caràcters." + +msgid "Backend selection is required." +msgstr "Fa falta seleccionar el backend." + +msgid "ISO image selection is required." +msgstr "Fa falta seleccionar una imatge ISO." + +msgid "Template selection is required." +msgstr "Fa falta seleccionar una plantilla." + +msgid "A machine with that name already exists." +msgstr "Una màquina amb aquest nom ja existeix." + +msgid "The machine name must contain only alphabetic, numbers, dashes and points." +msgstr "El nom de la màquina ha de tindre nomès caràcters alfanumèrics, línies i punts." + +msgid "Create" +msgstr "Crear" + +msgid "" +"can change the settings of any virtual machines " +"cloned from one base owned by the user." +msgstr "" +"pot canviar les preferències de qualsevol màquina virtual " +"clonat d'una base propietat de l'usuari." + diff --git a/lib/Ravada/I18N/de.po b/lib/Ravada/I18N/de.po new file mode 100644 index 000000000..fcd30f3f7 --- /dev/null +++ b/lib/Ravada/I18N/de.po @@ -0,0 +1,422 @@ +# Translators: +# Pascal Scholz , 2017 +msgid "" +msgstr "" +"Project-Id-Version: 0.1.0-alpha\n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2017-10-26 16:40+0200\n" +"Last-Translator: Pascal Scholz \n" +"Language-Team: \n" +"Language: ca\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"X-Generator: Poedit 1.8.7.1\n" + +msgid "Ravada broker" +msgstr "" + +msgid "Name of the machine" +msgstr "Name der Maschine" + +msgid "Tools" +msgstr "Werkzeuge" + +msgid "Machines list" +msgstr "Maschinenliste" + +msgid "Choose a machine to start" +msgstr "Wähle eine Maschine zum starten aus" + +msgid "Start" +msgstr "Start" + +msgid "Stop" +msgstr "Stop" + +msgid "View" +msgstr "Anzeigen" + +msgid "Prepare base" +msgstr "Basis Vorbereiten" + +msgid "Clone" +msgstr "Klonen" + +msgid "Can't prepare base, remove base or delete. Machine has" +msgstr "Basis kann nicht vorbereitet werden, entferne oder lösche die Basis" + +msgid "Clone/s" +msgstr "Klone" + +msgid "New" +msgstr "Neu" + +msgid "Users List" +msgstr "Benutzerliste" + +msgid "Name" +msgstr "Name" + +msgid "Log Out" +msgstr "Abmelden" + +msgid "Available machines" +msgstr "Verfügbare Maschinen" + +msgid "Help" +msgstr "Hilfe" + +msgid "Requirements" +msgstr "Anforderungen" + +msgid "About" +msgstr "Über" + +msgid "Messages" +msgstr "Nachrichten" + +msgid "Mark all as read" +msgstr "Alle als gelesen markieren" + +msgid "Settings" +msgstr "Einstellungen" + +msgid "New machine" +msgstr "Neue Maschine" + +msgid "I want to change my password" +msgstr "Ich möchte mein Passwort ändern" + +msgid "I want to change my Language" +msgstr "Ich will meine Sprache ändern" + +msgid "Language:" +msgstr "Sprache:" + +msgid "English" +msgstr "Englisch" + +msgid "Spanish" +msgstr "Spanisch" + +msgid "Catalan" +msgstr "Katalanisch" + +msgid "New Password:" +msgstr "Neues Passwort:" + +msgid "Confirm Password:" +msgstr "Bestätige Passwort:" + +msgid "Submit" +msgstr "Einreichen" + +msgid "Your language has been successfully changed" +msgstr "Deine Sprache wurde erfolgreich geändert" + +msgid "Your password has been successfully changed" +msgstr "Dein Passwort wurde erfolgreich ge#ndert" + +msgid "Password is too short" +msgstr "Passwort ist zu kurz" + +msgid "Password fields are not matching" +msgstr "Die Passwörter stimmen nicht überein" + +msgid "Some of the password fields are empty" +msgstr "Einige der Passwortfelder sind leer" + +msgid "Public" +msgstr "Öffentlich" + +msgid "Status" +msgstr "Status" + +msgid "Actions" +msgstr "Aktion" + +msgid "Down" +msgstr "Runter" + +msgid "Running" +msgstr "Läuft" + +msgid "Screenshot" +msgstr "Bildschirmfoto" + +msgid "Paused" +msgstr "Pause" + +msgid "Machine locked by request" +msgstr "Maschine auf Anfrage gesperrt" + +msgid "Process" +msgstr "Prozess" + +msgid "Copy" +msgstr "Kopieren" + +msgid "Error!" +msgstr "Fehler!" + +msgid "Error" +msgstr "Fehler" + +msgid "Backend not available!" +msgstr "Server nicht verfuegbar!" + +msgid "Subject" +msgstr "Betreff" + +msgid "Date" +msgstr "Datum" + +msgid "Mark as read" +msgstr "Als gelesen markieren" + +msgid "Mark as unread" +msgstr "Als ungelesen markieren" + +msgid "No messages to show" +msgstr "Keine Nachrichten zum anzeigen" + +msgid "Admin" +msgstr "Administrator" + +msgid "Machines" +msgstr "Maschinen" + +msgid "Users" +msgstr "Benutzer" + +msgid "Admin tools" +msgstr "Administratorwerkzeuge" + +msgid "Remove base" +msgstr "Entferne Basis" + +msgid "New Base" +msgstr "Neue Basis" + +msgid "Restore" +msgstr "Wiederherstellen" + +msgid "Hibernate" +msgstr "Ruhezustand" + +msgid "Shutdown" +msgstr "Herunterfahren" + +msgid "Action" +msgstr "Aktion" + +msgid "Will remove all the contents of the machine" +msgstr "Entfernt den gesamten Inhalt der Maschine" + +msgid "Are you sure?" +msgstr "Sind Sie sicher?" + +msgid "Yes" +msgstr "Ja" + +msgid "No" +msgstr "Nein" + +msgid "Make public" +msgstr "Öffentlich machen" + +msgid "Machine settings" +msgstr "Maschineneinstellungen" + +msgid "Cannot remove base, machine has clones" +msgstr "Basis kann nicht entfernt werden, Maschine hat Klone" + +msgid "Show/hide clones" +msgstr "Zeige/Verstecke Klone" + +msgid "For Spice redirection you'll need to install" +msgstr "Für die Spice-Umleitung müssen Sie installieren" + +msgid "on your computer." +msgstr "auf deinem Computer" + +msgid "Search in your distro, e.g. in Debian/Ubuntu with" +msgstr "Suche in deiner Distribution (Debian/Ubuntu)" + +msgid "You'll need to install" +msgstr "Suchen Sie in Ihrer Distribution, z.B. in Debian / Ubuntu mit" + +msgid "e.g. VirtViewer v5.0-256, and USB drivers (" +msgstr "z.B. VirtViewer v5.0-256 und USB-Treiber (" + +msgid "" +"Be aware that in Windows, Spice redirection is not automatic. It's " +"necessary to associate protocol with the app." +msgstr "" +"Beachten Sie, dass die Spice-Umleitung in Windows nicht automatisch erfolgt. Es ist " +"notwendig, um das Protokoll mit der App zu verknüpfen." + +msgid "To make this possible, download" +msgstr "Um dies zu ermöglichen, laden Sie" + +msgid "" +"or copy the following lines in an ASCII file and save with extension .reg, " +"then execute the file." +msgstr "" +"oder kopieren Sie folgende zeilen in eine ASCII Datei und Speichern Sie diese mit" +"der .reg Erweiterung" + +msgid "Login" +msgstr "Login" + +msgid "Welcome" +msgstr "Willkommen" + +msgid "A viewer is required to run the virtual machines." +msgstr "Ein Viewer ist erforderlich, um die virtuellen Maschinen auszuführen." + +msgid "Virtual Machines" +msgstr "virtuelle Maschine" + +msgid "User" +msgstr "Benutzer" + +msgid "Password" +msgstr "Passwort" + +msgid "Start session" +msgstr "Starte Sitzung" + +msgid "Login with Intranet UPC's username." +msgstr "Melden Sie sich mit dem Benutzernamen des Intranets UPC an." + +msgid "bits) in your computer." +msgstr "Bits) in Ihrem Computer." + +msgid "Permissions" +msgstr "Berechtigung" + +msgid "can change the settings of owned virtual machines." +msgstr "kann die Einstellungen von eigenen virtuellen Maschinen ändern." + +msgid "can change the settings of any virtual machines." +msgstr "kann die Einstellungen aller virtuellen Maschinen ändern." + +msgid "cloned from one base owned by the user." +msgstr "von einer Basis geklont, die dem Benutzer gehört." + +msgid "can clone public virtual machines." +msgstr "kann öffentliche virtuelle Maschinen klonen." + +msgid "can clone any virtual machine." +msgstr "kann alle virtuelle Maschinen klonen." + +msgid "can create bases." +msgstr "kann Basis erstellen " + +msgid "can create virtual machines." +msgstr "kann virtuelle Maschinen erstellen" + +msgid "can grant permissions to other users" +msgstr "kann Berechtigung für andere Benutzer erteilen" + +msgid "can hibernate any virtual machine." +msgstr "kann jede virtuelle Maschine in den Ruhezustand versetzen." + +msgid "can hibernate clones from virtual machines owned by the user." +msgstr "kann Klone von virtuellen Maschinen, die dem Benutzer gehören, in den Ruhezustand bringen." + +msgid "can hibernate any clone." +msgstr "kann jeden Klon in den Ruhezustand bringen." + +msgid "can remove any virtual machines owned by the user." +msgstr "kann alle virtuellen Maschinen entfernen, die dem Benutzer gehören." + +msgid "can remove any virtual machine." +msgstr "kann jede virtuelle Maschine entfernen." + +msgid "can remove clones from virtual machines owned by the user." +msgstr "kann Klone von virtuellen Maschinen entfernen, die dem Benutzer gehören." + +msgid "can remove any clone." +msgstr "kann jeden Klon entfernen." + +msgid "can take a screenshot of any virtual machine owned by the user." +msgstr "kann einen Screenshot einer virtuellen Maschine erstellen, die dem Benutzer gehört" + +msgid "can take a screenshot of any virtual machine." +msgstr "kann einen Screenshot einer virtuellen Maschine erstellen." + +msgid "can shutdown any virtual machine." +msgstr "kann jede virtuelle Maschine herunterfahren." + +msgid "can shutdown clones from virtual machines owned by the user." +msgstr "kann Klone von virtuellen Maschinen herunterfahren, die dem Benutzer gehören." + +msgid "Submit Query" +msgstr "Anfrage absenden" + +msgid "From Template" +msgstr "Von Vorlage" + +msgid "From Machine" +msgstr "Von Maschine" + +msgid "Backend" +msgstr "Server" + +msgid "Select Template" +msgstr "Vorlage auswählen" + +msgid "This ISO image has not been downloaded yet. It may take some minutes, even hours until the file is fetched from the Internet." +msgstr "Dieses ISO-Image wurde noch nicht heruntergeladen. Es kann einige Minuten dauern, sogar Stunden, bis die Datei aus dem Internet geholt wird." + +msgid "Download now" +msgstr "Jetzt herunterladen" + +msgid "Select ISO" +msgstr "Auswahl ISO" + +msgid "Template" +msgstr "Vorlage" + +msgid "Disk Size: (GB)" +msgstr "Festplattengröße: (GB)" + +msgid "Swap Size: (GB)" +msgstr "Größe tauschen:(GB)" + +msgid "Ram: (GB)" +msgstr "Arbeitsspeicher: (GB)" + +msgid "Machine name is required." +msgstr "Name der Maschinen ist erforderlich" + +msgid "Machine name can't exceed 20 characters." +msgstr "Der Computername darf nicht länger als 20 Zeichen sein." + +msgid "Backend selection is required." +msgstr "Backend-Auswahl ist erforderlich." + +msgid "ISO image selection is required." +msgstr "ISO-Image ist erforderlich." + +msgid "Template selection is required." +msgstr "Vorlagen auswahl ist erforderlich" + +msgid "A machine with that name already exists." +msgstr "Eine Maschine mit diesem Namen existiert bereits." + +msgid "The machine name is only allowed to consist of alphabetic characters, numbers, dashes and points." +msgstr "Der Maschinenname darf nur aus Buchstaben, Zahlen, Bindestrichen und Punkten bestehen." + +msgid "Create" +msgstr "Erstellen" + +msgid "" +"can change the settings of all virtual machines " +"cloned from one base owned by the user." +msgstr "" +"kann die Einstellungen aller virtuellen Maschinen ändern " +"geklont von einer Basis, die dem Benutzer gehört." \ No newline at end of file diff --git a/lib/Ravada/I18N/en.po b/lib/Ravada/I18N/en.po index cfe3ba57f..76b00c8f8 100644 --- a/lib/Ravada/I18N/en.po +++ b/lib/Ravada/I18N/en.po @@ -18,19 +18,16 @@ msgstr "" msgid "Ravada broker" msgstr "" -msgid "Machine Name" +msgid "Machine name" msgstr "" msgid "Tools" msgstr "" -msgid "Machines List" +msgid "Machines list" msgstr "" -msgid "Users" -msgstr "" - -msgid "Choose a Machine to Start" +msgid "Choose a machine to start" msgstr "" msgid "Start" @@ -48,13 +45,13 @@ msgstr "" msgid "Clone" msgstr "" -msgid "Can't Prepare Base, Remove Base nor Delete. Machine has" +msgid "Can't prepare base, remove base or delete. Machine has" msgstr "" msgid "Clone/s" msgstr "" -msgid "NEW" +msgid "New" msgstr "" msgid "Users List" @@ -66,7 +63,7 @@ msgstr "" msgid "Log Out" msgstr "" -msgid "Available Machines" +msgid "Available machines" msgstr "" msgid "Help" @@ -78,16 +75,13 @@ msgstr "" msgid "About" msgstr "" -msgid "Messages" -msgstr "" - -msgid "Mark all as Read" +msgid "Mark all as read" msgstr "" msgid "Settings" msgstr "" -msgid "New Machine" +msgid "New machine" msgstr "" msgid "I want to change my password" @@ -117,22 +111,19 @@ msgstr "" msgid "Submit" msgstr "" -msgid "Your language has been changed successfully" +msgid "Your language has been successfully changed" msgstr "" -msgid "Your password has been changed successfully" +msgid "Your password has been successfully changed" msgstr "" -msgid "Password too small" +msgid "Password is too short" msgstr "" -msgid "Password fields aren't equal" +msgid "Password fields do not match" msgstr "" -msgid "Some of the password's fields are empty" -msgstr "" - -msgid "Public" +msgid "Some of the password fields are empty" msgstr "" msgid "Status" @@ -141,16 +132,13 @@ msgstr "" msgid "Actions" msgstr "" -msgid "Machines" -msgstr "" - msgid "Down" msgstr "" msgid "Running" msgstr "" -msgid "ShutDown" +msgid "Shutdown" msgstr "" msgid "Screenshot" @@ -159,10 +147,10 @@ msgstr "" msgid "Paused" msgstr "" -msgid "Machine locked by" +msgid "Machine locked by request" msgstr "" -msgid "process" +msgid "Process" msgstr "" msgid "Copy" @@ -174,7 +162,7 @@ msgstr "" msgid "Error" msgstr "" -msgid "Backend no available!" +msgid "Backend not available!" msgstr "" msgid "Subject" @@ -183,25 +171,25 @@ msgstr "" msgid "Date" msgstr "" -msgid "Mark as Read" +msgid "Mark as read" msgstr "" -msgid "Mark as UnRead" +msgid "Mark as unread" msgstr "" -msgid "No message to show!" +msgid "No messages to show" msgstr "" msgid "Admin" msgstr "" -msgid "machines" +msgid "Machines" msgstr "" -msgid "users" +msgid "Users" msgstr "" -msgid "messages" +msgid "Messages" msgstr "" msgid "Admin tools" @@ -210,25 +198,22 @@ msgstr "" msgid "Remove base" msgstr "" -msgid "public" -msgstr "" - -msgid "Restore" +msgid "Public" msgstr "" -msgid "Hybernate" +msgid "New Base" msgstr "" -msgid "Shutdown" +msgid "Restore" msgstr "" -msgid "action" +msgid "Hibernate" msgstr "" -msgid "New Base" +msgid "Action" msgstr "" -msgid "will remove all the contents of the machine" +msgid "Will remove all the contents of the machine" msgstr "" msgid "Are you sure?" @@ -249,13 +234,13 @@ msgstr "" msgid "Cannot remove base, machine has clones" msgstr "" -msgid "Show/Hide clones" +msgid "Show/hide clones" msgstr "" -msgid "For spice redirection you'll need to install" +msgid "For Spice redirection you'll need to install" msgstr "" -msgid "in your computer." +msgid "on your computer." msgstr "" msgid "Search in your distro, e.g. in Debian/Ubuntu with" @@ -268,8 +253,8 @@ msgid "e.g. VirtViewer v5.0-256, and USB drivers (" msgstr "" msgid "" -"Be aware that in Windows, spice redirection is not automatically. It's " -"necessary associate protocol with the app." +"Be aware that in Windows, Spice redirection is not automatic. It's " +"necessary to associate protocol with the app." msgstr "" msgid "To make this possible, download" @@ -286,16 +271,16 @@ msgstr "" msgid "Welcome" msgstr "" -msgid "It is required a viewer to run the virtual machines." +msgid "A viewer is required to run the virtual machines." msgstr "" msgid "Virtual Machines" msgstr "" -msgid "user" +msgid "User" msgstr "" -msgid "password" +msgid "Password" msgstr "" msgid "Start session" @@ -385,7 +370,7 @@ msgstr "" msgid "Select Template" msgstr "" -msgid "This ISO image has not been already downloaded. This may take many minutes, even hours until the file is fetched from Internet." +msgid "This ISO image has not been downloaded yet. It may take some minutes, even hours until the file is fetched from the Internet." msgstr "" msgid "Download now" @@ -424,13 +409,13 @@ msgstr "" msgid "A machine with that name already exists." msgstr "" -msgid "The machine name must contain only alphabetic, numbers, dashes and points." +msgid "The machine name is only allowed to consist of alphabetic characters, numbers, dashes and points." msgstr "" msgid "Create" msgstr "" msgid "" -"can change the settings of any virtual machines " +"can change the settings of all virtual machines " "cloned from one base owned by the user." msgstr "" diff --git a/lib/Ravada/I18N/es.po b/lib/Ravada/I18N/es.po index 61b3485de..6add6ef2b 100644 --- a/lib/Ravada/I18N/es.po +++ b/lib/Ravada/I18N/es.po @@ -154,7 +154,7 @@ msgid "ShutDown" msgstr "Apagar" msgid "Screenshot" -msgstr "" +msgstr "Captura de pantalla" msgid "Paused" msgstr "Pausado" @@ -168,8 +168,44 @@ msgstr "proceso" msgid "Copy" msgstr "Copia" +msgid "Error!" +msgstr "¡Error!" + +msgid "Error" +msgstr "Error" + +msgid "Backend no available!" +msgstr "¡Backend no disponible!" + +msgid "Subject" +msgstr "Asunto" + +msgid "Date" +msgstr "Fecha" + +msgid "Mark as Read" +msgstr "Marcar como leído" + +msgid "Mark as UnRead" +msgstr "Marcar como no leído" + +msgid "No message to show!" +msgstr "¡No hay mensaje por mostrar!" + +msgid "Admin" +msgstr "Admin" + +msgid "machines" +msgstr "máquinas" + +msgid "users" +msgstr "usuarios" + +msgid "messages" +msgstr "mensajes" + msgid "Admin tools" -msgstr "" +msgstr "Herramientas de admin" msgid "Remove base" msgstr "Elimina base" @@ -189,17 +225,20 @@ msgstr "Apagar" msgid "action" msgstr "acción" +msgid "New Base" +msgstr "Nueva Base" + msgid "will remove all the contents of the machine" msgstr "borrará todo el contenido de la máquina" msgid "Are you sure?" -msgstr "Estas seguro/a?" +msgstr "Estás seguro/a?" msgid "Yes" -msgstr "Si" +msgstr "Sí" msgid "No" -msgstr "" +msgstr "No" msgid "Make public" msgstr "Haz público" @@ -212,3 +251,284 @@ msgstr "No se puede borrar la base, la máquina tiene clones" msgid "Show/Hide clones" msgstr "Ver/Esconde clones" + +msgid "User Settings" +msgstr "Configuración de usuario" + +msgid "For spice redirection you'll need to install" +msgstr "Para redirección a spice necesitarás instalar" + +msgid "Search in your distro, e.g. in Debian/Ubuntu with" +msgstr "Busca en tu distribución, p.e. Debian/Ubuntu con" + +msgid "For spice redirection you'll need to install" +msgstr "Para la redirección de spice, necesitarás instalar" + +msgid "in your computer." +msgstr "en tu computadora." + +msgid "Search in your distro, e.g. in Debian/Ubuntu with" +msgstr "Busca en tu distro, p.e. en Debian/Ubuntu con" + + +msgid "You'll need to install" +msgstr "Necesitarás instalar" + +msgid "e.g. VirtViewer v5.0-256, and USB drivers (" +msgstr "p.e. VirtViewer v5.0-256, y USB drivers (" + +msgid "or" +msgstr "o" + +msgid "bits) in your computer." +msgstr "bits) en tu ordenador." + +msgid "Be aware that in Windows, spice redirection is not automatically. It\'s necessary associate protocol with the app." +msgstr "Atención a que en Windows, la redirección spice no es automàtica. Es necesario asociar el protocolo con la app." + +msgid "To make this possible, download" +msgstr "Para hacer esto posible, descarga" + +msgid "or copy the following lines in an ASCII file and save with extension .reg, then execute the file." +msgstr "o copia las linias siguientes en un documento ASCII y guárdalo con extensión .reg, después ejécutalo." + +msgid "For more information, check " +msgstr "Para más información, revisa " + +msgid "the Windows Clients documentation" +msgstr "la documentación de Windows Clients" + +msgid "in your computer." +msgstr "en tu ordenador." + +msgid "Admin tools" +msgstr "Herramientas de administración" + +msgid "machines" +msgstr "máquinas" + +msgid "users" +msgstr "usuarios" + +msgid "messages" +msgstr "mensajes" + +msgid "Virtual Machines" +msgstr "Máquinas Virtuales" + +msgid "hide clones" +msgstr "esconder clones" + +msgid "This Machine is a base" +msgstr "Esta Máquina es base" + +msgid "Machine locked by" +msgstr "Máquina bloqueada por" + +msgid "Cloned" +msgstr "Clonada" + +msgid "Search" +msgstr "Buscar" + +msgid "Register" +msgstr "Registrar" + +msgid "Subject" +msgstr "Asunto" + +msgid "Date" +msgstr "Fecha" + +msgid "There are no public bases available in this system." +msgstr "No hay bases públicas disponibles en este sistema." + +msgid "There are no machines available in this system." +msgstr "No hay máquinas disponibles en este sistema." + +msgid "not public" +msgstr "no pública" + +msgid "Create one." +msgstr "Crea una." + +msgid "Error!" +msgstr "Error!" + +msgid "Backend no available!" +msgstr " Backend no disponible!" + +msgid "" +"Be aware that in Windows, spice redirection is not automatically. It's " +"necessary associate protocol with the app." +msgstr "" +"Se consciente que en Windows, redirección con spice no es automático. Es " +"necesario asociar el protocolo con la app" + +msgid "To make this possible, download" +msgstr "Para hacer esto posible, descarga" + +msgid "" +"or copy the following lines in an ASCII file and save with extension .reg, " +"then execute the file." +msgstr "" +"o copia las siguientes líneas en un archivo ASCII y guarda con la extensión .reg, " +"luego ejecuta el archivo." + +msgid "Login" +msgstr "Conéctate" + +msgid "Welcome" +msgstr "Bienvenido" + +msgid "It is required a viewer to run the virtual machines." +msgstr "Se requiere de un visor para arrancar la máquina virtual." + +msgid "Virtual Machines" +msgstr "Máquinas Virtuales" + +msgid "user" +msgstr "usuario" + +msgid "password" +msgstr "contraseña" + +msgid "Start session" +msgstr "Iniciar sesión" + +msgid "Login with Intranet UPC's username." +msgstr "Conectarse con el usuario de la Intranet de la UPC" + +msgid "bits) in your computer." +msgstr "bits) en tu computadora." + +msgid "Permissions" +msgstr "Permisos" + +msgid "can change the settings of owned virtual machines." +msgstr "puede cambiar los ajustes de las máquinas virtuales en posesión." + +msgid "can change the settings of any virtual machines." +msgstr "puede cambiar los ajustes de cualquier máquina virtual." + +msgid "can change the settings of any virtual machines " +msgstr "puede cambiar los ajustes de cualquier máquina virtual " + +msgid "cloned from one base owned by the user." +msgstr "clonado desde una base en posesión del usuario." + +msgid "can clone public virtual machines." +msgstr "puede clonar máquinas virtuales públicas." + +msgid "can clone any virtual machine." +msgstr "puede clonar cualquier máquina virtual." + +msgid "can create bases." +msgstr "puede crear bases." + +msgid "can create virtual machines." +msgstr "puede crear máquinas virtuales." + +msgid "can grant permissions to other users" +msgstr "puede otorgar permisos a otros usuarios" + +msgid "can hibernate any virtual machine." +msgstr "puede hibernar cualquier máquina virtual." + +msgid "can hibernate clones from virtual machines owned by the user." +msgstr "puede hibernar clones de máquinas virtuales en posesión del usuario." + +msgid "can hibernate any clone." +msgstr "puede hibernar cualquier clon." + +msgid "can remove any virtual machines owned by the user." +msgstr "puede quitar cualquier máquina virtual en posesión del usuario." + +msgid "can remove any virtual machine." +msgstr "puede quitar cualquier máquina virtual." + +msgid "can remove clones from virtual machines owned by the user." +msgstr "puede quitar clones de máquinas virtuales en posesión del usuario." + +msgid "can remove any clone." +msgstr "puede quitar cualquier clon." + +msgid "can take a screenshot of any virtual machine owned by the user." +msgstr "puede tomar una captura de pantalla de cualquier máquina virtual en posesión del usuario" + +msgid "can take a screenshot of any virtual machine." +msgstr "puede tomar una captura de pantalla de cualquier máquina virtual." + +msgid "can shutdown any virtual machine." +msgstr "puede apagar cualquier máquina virtual." + +msgid "can shutdown clones from virtual machines owned by the user." +msgstr "puede apagar clones de máquinas virtuales en posesión del usuario." + +msgid "Submit Query" +msgstr "Enviar Consulta" + +msgid "From Template" +msgstr "Desde Plantilla" + +msgid "From Machine" +msgstr "Desde Máquina" + +msgid "Backend" +msgstr "Backend" + +msgid "Select Template" +msgstr "Selecciona Plantilla" + +msgid "This ISO image has not been already downloaded. This may take many minutes, even hours until the file is fetched from Internet." +msgstr "Esta imagen ISO no está ya descargada. Puede tardar bastantes minutos, incluso horas hasta que el archivo sea recuperado de Internet." + +msgid "Download now" +msgstr "Descargar ahora" + +msgid "Select ISO" +msgstr "Seleccionar ISO" + +msgid "Template" +msgstr "Plantilla" + +msgid "Disk Size: (GB)" +msgstr "Tamaño del Disco: (GB)" + +msgid "Swap Size: (GB)" +msgstr "Tamaño de Swap: (GB)" + +msgid "Ram: (GB)" +msgstr "Ram: (GB)" + +msgid "Machine name is required." +msgstr "El nombre de la máquina es necesario." + +msgid "Machine name can't exceed 20 characters." +msgstr "El nombre de la máquina no puede superar los 20 carácteres." + +msgid "Backend selection is required." +msgstr "Hace falta seleccionar un backend." + +msgid "ISO image selection is required." +msgstr "Hace falta seleccionar una imagen de ISO." + +msgid "Template selection is required." +msgstr "Hace falta seleccionar una plantilla." + +msgid "A machine with that name already exists." +msgstr "Una máquina con ese nombre ya existe." + +msgid "The machine name must contain only alphabetic, numbers, dashes and points." +msgstr "El nombre de la máquina debe contener solo carácteres alfanuméricos, líneas y puntos." + +msgid "Create" +msgstr "Crear" + +msgid "" +"can change the settings of any virtual machines " +"cloned from one base owned by the user." +msgstr "" +"puede cambiar los ajustes de cualquier máquina virtual clonada de una base " +"propiedad del usuario." + diff --git a/lib/Ravada/I18N/fr.po b/lib/Ravada/I18N/fr.po index 9a5d606ed..ae69d7537 100644 --- a/lib/Ravada/I18N/fr.po +++ b/lib/Ravada/I18N/fr.po @@ -1,5 +1,5 @@ # Translators: -# Kimia mirehbar kimia@etsetb.upc.edu +# Kimia mirehbar kimia@etsetb.upc.edu # french msgid "" msgstr "" @@ -22,43 +22,43 @@ msgid "Restore" msgstr "Restaurer" msgid "Machine Name" -msgstr "Nom de Machine" +msgstr "Nom de la machine" msgid "Tools" msgstr "Outils" msgid "Machines List" -msgstr "Liste de Machines" +msgstr "Liste des machines" msgid "Users" msgstr "Utilisateurs" msgid "Choose a Machine to Start" -msgstr "Choisir un Machine Pour Commencer" +msgstr "Choisissez une machine pour commencer" msgid "Start" -msgstr "Début" +msgstr "Commencer" msgid "Stop" -msgstr "Stop" +msgstr "Arrêter" msgid "View" -msgstr "Vue" +msgstr "Voir" msgid "Prepare Base" -msgstr "Preparer Base" +msgstr "Préparer la base" msgid "Clone" msgstr "Cloner" msgid "Can't Prepare Base, Remove Base nor Delete. Machine has" -msgstr "Ne Se Peux pas Preparer Base, Retirer ou Effacer la Base" +msgstr "La base ne peut pas être préparée, supprimée ou effacée. La machine a" msgid "Clone/s" -msgstr "Clones" +msgstr "Clone(s)" msgid "NEW" -msgstr "Nouveau" +msgstr "NOUVEAU" msgid "Users List" msgstr "Liste d'utilisateurs" @@ -67,52 +67,52 @@ msgid "Name" msgstr "Nom" msgid "Log Out" -msgstr "Sortir" +msgstr "Déconnexion" msgid "Available Machines" -msgstr "Machines Disponibles" +msgstr "Machines disponibles" msgid "Help" msgstr "Aide" msgid "Requirements" -msgstr "Exigences" +msgstr "Prérequis" msgid "About" -msgstr "Sur" +msgstr "À propos" msgid "Messages" msgstr "Messages" msgid "Mark all as Read" -msgstr "Marquer Tout Comme Lu" +msgstr "Marquer tout comme lus" msgid "Settings" -msgstr "Réglages" +msgstr "Paramètres" msgid "New Machine" -msgstr "Machine Nouveau" +msgstr "Nouvelle machine" msgid "Paused" -msgstr "En Pause" +msgstr "En pause" msgid "Prepare base" -msgstr "Preparer Base" +msgstr "Préparer la base" msgid "Machine locked by requested" -msgstr "Machine Férme par demande" +msgstr "Machine verrouillée sur demande" msgid "process" -msgstr "Procès" +msgstr "Processus" msgid "Copy" msgstr "Copie" msgid "Error!" -msgstr "Erreur" +msgstr "Erreur !" msgid "Backend no available!" -msgstr "Backend Indisponible" +msgstr "Système indisponible" msgid "Subject" msgstr "Sujet" @@ -131,28 +131,28 @@ msgstr "" "Actiones" msgid "Mark as Read" -msgstr "Marquer Comme Lu" +msgstr "Marquer comme lu" msgid "Mark as UnRead" -msgstr "Marquer Comme Non Lu" +msgstr "Marquer comme non lu" msgid "No message to show!" -msgstr "Pas de Messages à Lire" +msgstr "Aucun message à lire" msgid "Admin" msgstr "Administrateur" msgid "Show/Hide clones" -msgstr "Afficher/Masquer des clones" +msgstr "Afficher/Masquer les clones" msgid "I want to change my password" -msgstr "je voudrais changer mon mot de passe" +msgstr "Je veut changer mon mot de passe" msgid "I want to change my language" -msgstr "je voudrais modifier ma langue" +msgstr "Je veut modifier ma langue" msgid "Language:" -msgstr "Langue" +msgstr "Langue :" msgid "English" msgstr "Anglais" @@ -164,34 +164,34 @@ msgid "Catalan" msgstr "Catalan" msgid "New Password:" -msgstr "Mot de passe Nouveau" +msgstr "Nouveau mot de passe :" msgid "Confirm Password:" -msgstr "confirmer mot de passe" +msgstr "Confirmer le mot de passe :" msgid "Submit" -msgstr "soumettre" +msgstr "Soumettre" msgid "Your language has been changed successfully" -msgstr "votre langue a été modifiée avec succès" +msgstr "Votre langue a été modifiée avec succès" msgid "Your password has been changed successfully" -msgstr "votre mot de passe a été modifiée avec succès" +msgstr "Votre mot de passe a été modifié avec succès" msgid "Password too small" -msgstr "Mot de Passe petite" +msgstr "Votre mot de passe est trop court" msgid "Password fields aren't equal" -msgstr "les espaces du mot de passe n'est pas egaleés" +msgstr "Les mots de passe sont différents" msgid "Some of the password's fields are empty" -msgstr "quelques espaces du mot de passe sont vides" +msgstr "Un mot de passe n'a pas été renseigné" msgid "Public" msgstr "Public" msgid "Status" -msgstr "Statut" +msgstr "État" msgid "Machines" msgstr "Machines" @@ -200,52 +200,52 @@ msgid "Down" msgstr "Arrêté" msgid "Running" -msgstr "fonctionne" +msgstr "En marche" msgid "ShutDown" -msgstr "Fermer" +msgstr "Éteindre" msgid "Screenshot" -msgstr "capture d'écran" +msgstr "Capture d'écran" msgid "Machine locked by" msgstr "Machine verrouillée par" msgid "machines" -msgstr "Machines" +msgstr "machines" msgid "users" -msgstr "Utilisateurs" +msgstr "utilisateurs" msgid "messages" -msgstr "Messages" +msgstr "messages" msgid "Admin tools" msgstr "Outils d'administration" msgid "Remove base" -msgstr "Supprimer base" +msgstr "Supprimer la base" msgid "public" -msgstr "Public" +msgstr "public" msgid "Hybernate" msgstr "Hiberner" msgid "Shutdown" -msgstr "Fermer" +msgstr "Éteindre" msgid "action" msgstr "action" msgid "New Base" -msgstr "Base Nouveau" +msgstr "Nouvelle base" msgid "will remove all the contents of the machine" -msgstr "supprimer tout les contenus de machine" +msgstr "supprimera tout le contenu de la machine" msgid "Are you sure?" -msgstr "vous etes certain?" +msgstr "Êtes-vous certain ?" msgid "Yes" msgstr "Oui" @@ -254,58 +254,58 @@ msgid "No" msgstr "Non" msgid "Make public" -msgstr "Rendre publique" +msgstr "Rendre public" msgid "Machine settings" -msgstr "Réglage de machine" +msgstr "Réglage de la machine" msgid "Cannot remove base, machine has clones" -msgstr "clones Ne peut pas enlever la base, la machine a un clone" +msgstr "La base ne peut pas être supprimée car elle a des clones" msgid "For spice redirection you'll need to install" -msgstr "Pour Spice redirection vous necessitez installer" +msgstr "Pour utiliser la redirection Spice vous devez installer" msgid "in your computer." -msgstr "dans votre ordinateur" +msgstr "sur votre ordinateur." msgid "Search in your distro, e.g. in Debian/Ubuntu with" -msgstr "Cherchez dans votre distro, par example dans Debian/Ubuntu avec" +msgstr "Cherchez dans votre distro, par exemple dans Debian/Ubuntu avec" msgid "You'll need to install" -msgstr "vous necessitez installer " +msgstr "Vous devez installer" msgid "e.g. VirtViewer v5.0-256 in your computer." -msgstr "par example VirtViewer v5.0-256 dans votre ordinateur. " +msgstr "par exemple VirtViewer v5.0-256 sur votre ordinateur." msgid "" "Be aware that in Windows, spice redirection is not automatically. It's " "necessary associate protocol with the app." msgstr "" -"Faites attention quoi dans Windows, Spice redirection no est pas automatiquement." -" il est necessaire associer le protocole avec l'app." +"Notez que sur Windows, la redirection Spice n'est pas automatiquement activée." +"Il est nécessaire d'associer le protocole à l'application." msgid "To make this possible, download" -msgstr "pour rendre cela possible, téléchargez" +msgstr "Pour que ce soit possible, téléchargez" msgid "" "or copy the following lines in an ASCII file and save with extension .reg, " "then execute the file." msgstr "" -"ou copiez les lingnes suivants dans un fichier et Enregistrez avec extension .reg" -"ensuit exécutez le fichier" +"ou copiez les lignes suivantes dans un fichier ASCII et enregistrez le avec l'extension .reg, " +"ensuite exécutez le fichier" msgid "Login" -msgstr "S'identifier" +msgstr "Connexion" msgid "Welcome" msgstr "Bienvenue" msgid "It is required a viewer to run the virtual machines." -msgstr "C'est necessaire un logiciel de Viewer pour fonctionner les machines virtuelles." +msgstr "Il est nécessaire d'avoir une visionneuse pour démarrer les machines virtuelles." msgid "Virtual Machines" -msgstr "Machine Virtuelle" +msgstr "Machines Virtuelles" msgid "user" msgstr "Utilisateur" @@ -314,74 +314,74 @@ msgid "password" msgstr "Mot de passe" msgid "Start session" -msgstr "Commencer session" +msgstr "Commencer la session" msgid "Login with Intranet UPC's username." -msgstr "s'identifier avec Nom d'utilisateur de la UPC" +msgstr "Connectez avec le nom d'utilisateur de l'intranet UPC." msgid "Permissions" msgstr "Permissions" msgid "can change the settings of owned virtual machines." -msgstr "On peut échanger les reglages des machines virtuelle Sur leur possession" +msgstr "peut changer les réglages des machines virtuelles appartenant à l'utilisateur." msgid "can change the settings of any virtual machines." -msgstr "On peut échanger les reglages des quelques machines virtuelles" +msgstr "peut changer les réglages de n'importe quelle machine virtuelle." msgid "" "can change the settings of any virtual machines " "cloned from one base owned by the user." msgstr "" -"On peut échanger les reglages des quelques machines virtuelle" -"clonées d'une base sur la posession d'utilisateur" +"peut changer les réglages des n'importe quelle machine virtuelle " +"clonée d'une base appartenant à l'utilisateur." msgid "can clone public virtual machines." -msgstr "On peut cloner machines virtuelles publiques" +msgstr "peut cloner les machines virtuelles publiques." msgid "can clone any virtual machine." -msgstr "On peut cloner quelques machines virtuelles" +msgstr "peut cloner n'importe quelle machine virtuelle." msgid "can create bases." -msgstr "On Peut créer bases" +msgstr "peut créer des bases." msgid "can create virtual machines." -msgstr "On peut créer machines virtuelles" +msgstr "peut créer des machines virtuelles." msgid "can grant permissions to other users" -msgstr "On peut donner la permission á autres utilisateurs" +msgstr "peut donner des permissions à d'autres utilisateurs" msgid "can hibernate any virtual machine." -msgstr "On peut hiberner quelques machines virtuelles" +msgstr "peut hiberner n'importe quelle machine virtuelle." msgid "can hibernate clones from virtual machines owned by the user." -msgstr "On peut hiberner clones des machines virtuelle Sur leur possession" +msgstr "peut hiberner les clones des machines virtuelles appartenant à l'utilisateur." msgid "can hibernate any clone." -msgstr "On peut hiberner quelques clones" +msgstr "peut hiberner n'importe quel clone." msgid "can remove any virtual machines owned by the user." -msgstr "On peut supprimer quelques machines virtuelles machines virtuelle Sur leur possession" +msgstr "peut supprimer n'importe quelle machine virtuelle appartenant à l'utilisateur." msgid "can remove any virtual machine." -msgstr "On peut supprimer quelques machine virtuelles" +msgstr "peut supprimer n'importe quelle machine virtuelle." msgid "can remove clones from virtual machines owned by the user." -msgstr "On peut supprimer clones de machines virtuelle Sur leur possession" +msgstr "peut supprimer les clones des machines virtuelles appartenant à l'utilisateur." msgid "can remove any clone." -msgstr "On peut supprimer quelques clones" +msgstr "peut supprimer n'importe quel clone." msgid "can take a screenshot of any virtual machine owned by the user." -msgstr "On peut Prendre une capture d'écran de quelques machines virtuelle Sur leur possession" +msgstr "peut prendre une capture d'écran de n'importe quelle machine virtuelle appartenant à l'utilisateur." msgid "can take a screenshot of any virtual machine." -msgstr "On peut Prendre une capture d'écran de quelques machines virtuelles" +msgstr "peut prendre une capture d'écran de n'importe quelle machine virtuelle." msgid "can shutdown any virtual machine." -msgstr "On peut fermer quelques machines virtuelles" +msgstr "peut éteindre n'importe quelle machine virtuelle." msgid "can shutdown clones from virtual machines owned by the user." -msgstr "On peut fermer les clone des machines virtuelle Sur leur possession" +msgstr "peut éteindre les clones des machines virtuelles appartenant à l'utilizateur" msgid "Submit Query" -msgstr "Soumettre Question" +msgstr "Soumettre la requête" diff --git a/lib/Ravada/I18N/gl.po b/lib/Ravada/I18N/gl.po index d33f2c810..28627b98c 100644 --- a/lib/Ravada/I18N/gl.po +++ b/lib/Ravada/I18N/gl.po @@ -15,7 +15,7 @@ msgstr "" "Plural-Forms: nplurals=2; plural=(n != 1);\n" msgid "Ravada broker" -msgstr "" +msgstr "Ravada broker" msgid "Machine Name" msgstr "Nome da máquina" @@ -33,10 +33,10 @@ msgid "Choose a Machine to Start" msgstr "Escolle unha máquina para iniciar" msgid "Start" -msgstr "Inicia" +msgstr "Iniciar" msgid "Stop" -msgstr "Para" +msgstr "Parar" msgid "View" msgstr "Ver" @@ -51,7 +51,7 @@ msgid "Can't Prepare Base, Remove Base nor Delete. Machine has" msgstr "Non se pode preparar ou eliminar a base. A máquina ten" msgid "Clone/s" -msgstr "clon/s" +msgstr "Clon/s" msgid "NEW" msgstr "NOVOS" @@ -81,7 +81,7 @@ msgid "Messages" msgstr "Mensaxes" msgid "Mark all as Read" -msgstr "Marca como lido" +msgstr "Marcar como lido" msgid "Settings" msgstr "Preferencias" @@ -90,43 +90,43 @@ msgid "New Machine" msgstr "Nova máquina" msgid "I want to change my password" -msgstr "" +msgstr "Quero cambiar o meu contrasinal" msgid "I want to change my language" -msgstr "" +msgstr "Quero cambiar a miña lingua" msgid "Language:" -msgstr "" +msgstr "Linguaxe" msgid "English" -msgstr "" +msgstr "Inglés" msgid "Spanish" -msgstr "" +msgstr "Español" msgid "Catalan" -msgstr "" +msgstr "Catalán" msgid "New Password:" -msgstr "" +msgstr "Novo Contrasinal" msgid "Confirm Password:" -msgstr "" +msgstr "Confirmar Contrasinal" msgid "Submit" -msgstr "" +msgstr "Enviar" msgid "Your language has been changed successfully" -msgstr "" +msgstr "A vosa lingua foi cambiada con éxito" msgid "Your password has been changed successfully" -msgstr "" +msgstr "O voso contrasinal foi cambiado con éxito" msgid "Password too small" -msgstr "" +msgstr "O Contrasinal é demasiado pequeno" msgid "Password fields aren't equal" -msgstr "" +msgstr "Os campos do contrasinal non son iguais" msgid "Some of the password's fields are empty" -msgstr "" +msgstr "Algúns dos campos do contrasinal están baleiros" diff --git a/lib/Ravada/I18N/hi.po b/lib/Ravada/I18N/hi.po new file mode 100644 index 000000000..c171abae1 --- /dev/null +++ b/lib/Ravada/I18N/hi.po @@ -0,0 +1,442 @@ +# Translator: +# Raghav Mittal , 2017 +# Hindi +msgid "" +msgstr "" +"Project-Id-Version: 0.1.0-alpha\n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2017-01-03 10:12+0300\n" +"PO-Revision-Date: 2017-10-26 16:20+0200\n" +"Last-Translator: Raghav Mittal raghavmittal101@gmail.com\n" +"Language-Team: Hindi raghavmittal101@gmail.com\n" +"Language: hi\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"X-Generator: Poedit 1.8.7.1\n" + +msgid "Ravada broker" +msgstr "रावदा ब्रोकर" + +msgid "Machine Name" +msgstr "मशीन का नाम" + +msgid "Tools" +msgstr "उपकरण" + +msgid "Machines List" +msgstr "मशीन की सूची" + +msgid "Users" +msgstr "उपयोगकर्ता" + +msgid "Choose a Machine to Start" +msgstr "शुरू करने के लिए एक मशीन चुनें" + +msgid "Start" +msgstr "शुरू करो" + +msgid "Stop" +msgstr "रोको" + +msgid "View" +msgstr "देखें" + +msgid "Prepare base" +msgstr "बेस तैयार करें" + +msgid "Clone" +msgstr "क्लोन" + +msgid "Can't Prepare Base, Remove Base or Delete. Machine has" +msgstr "आधार तैयार नहीं कर सकते, आधार निकालें या हटाएं |" + +msgid "Clone/s" +msgstr "क्लोन" + +msgid "NEW" +msgstr "नया" + +msgid "Users List" +msgstr "उपयोगकर्ता की सूची" + +msgid "Name" +msgstr "नाम" + +msgid "Log Out" +msgstr "लोग आउट" + +msgid "Available Machines" +msgstr "उपलब्ध मशीनें" + +msgid "Help" +msgstr "मदद" + +msgid "Requirements" +msgstr "आवश्यकताएँ" + +msgid "About" +msgstr "इसके बारे में" + +msgid "Messages" +msgstr "संदेश" + +msgid "Mark all as Read" +msgstr "सभी को पढ़ा हुआ मार्क करें" + +msgid "Settings" +msgstr "सेटिंग्स" + +msgid "New Machine" +msgstr "नई मशीन" + +msgid "I want to change my password" +msgstr "मैं अपना पासवर्ड बदलना चाहता हूं" + +msgid "I want to change my language" +msgstr "मैं अपनी भाषा बदलना चाहता हूं" + +msgid "Language:" +msgstr "भाषा:" + +msgid "English" +msgstr "अंग्रेज़ी" + +msgid "Spanish" +msgstr "स्पेनिश" + +msgid "Catalan" +msgstr "कैटलन" + +msgid "New Password:" +msgstr "नया पासवर्ड:" + +msgid "Confirm Password:" +msgstr "पासवर्ड की पुष्टि कीजिये:" + +msgid "Submit" +msgstr "जमा करें" + +msgid "Your language has been changed successfully" +msgstr "आपकी भाषा सफलतापूर्वक बदल दी गई है" + +msgid "Your password has been changed successfully" +msgstr "आपका पासवर्ड सफलतापूर्वक बदल दिया गया है" + +msgid "Password too short" +msgstr "पासवर्ड बहुत छोटा है" + +msgid "Password fields aren't equal" +msgstr "पासवर्ड फ़ील्ड समान नहीं हैं " + +msgid "Some of the password's fields are empty" +msgstr "कुछ पासवर्ड के क्षेत्र रिक्त हैं" + +msgid "Public" +msgstr "सार्वजनिक" + +msgid "Status" +msgstr "स्थिति" + +msgid "Actions" +msgstr "क्रिया" + +msgid "Machines" +msgstr "मशीनें" + +msgid "Down" +msgstr "नीचे" + +msgid "Running" +msgstr "चल रहा है" + +msgid "ShutDown" +msgstr "बंद करें" + +msgid "Screenshot" +msgstr "स्क्रीन की तस्वीर" + +msgid "Paused" +msgstr "रोके गए" + +msgid "Machine locked by request" +msgstr "अनुरोध द्वारा मशीन को लॉक किया गया" + +msgid "process" +msgstr "प्रक्रिया" + +msgid "Copy" +msgstr "प्रतिलिपि" + +msgid "Error!" +msgstr "समस्या है!" + +msgid "Error" +msgstr "समस्या है" + +msgid "Backend not available!" +msgstr "बैकएण्ड उपलब्ध नहीं है!" + +msgid "Subject" +msgstr "विषय" + +msgid "Date" +msgstr "दिनांक" + +msgid "Mark as Read" +msgstr "पढ़ा हुआ चिह्नित करें" + +msgid "Mark as Unread" +msgstr "न पढ़ा हुआ चिह्नित करें" + +msgid "No message to show!" +msgstr "दिखाने के लिए कोई संदेश नहीं है!" + +msgid "Admin" +msgstr "व्यवस्थापक" + +msgid "machines" +msgstr "मशीन" + +msgid "users" +msgstr "उपयोगकर्ताओं" + +msgid "messages" +msgstr "संदेश" + +msgid "Admin tools" +msgstr "व्यवस्थापक उपकरण" + +msgid "Remove base" +msgstr "आधार निकालें" + +msgid "public" +msgstr "सार्वजनिक" + +msgid "New Base" +msgstr "नया आधार" + +msgid "Restore" +msgstr "बहाल" + +msgid "Hibernate" +msgstr "हाइबरनेट करें" + +msgid "Shutdown" +msgstr "बंद करें" + +msgid "action" +msgstr "क्रिया" + +msgid "will remove all the contents of the machine" +msgstr "मशीन की सभी सामग्रियों को हटा देगा" + +msgid "Are you sure?" +msgstr "क्या आपको यकीन है?" + +msgid "Yes" +msgstr "हाँ" + +msgid "No" +msgstr "नहीं" + +msgid "Make public" +msgstr "सार्वजनिक करें" + +msgid "Machine settings" +msgstr "मशीन सेटिंग्स" + +msgid "Cannot remove base, machine has clones" +msgstr "आधार को हटा नहीं सकते, मशीन में क्लोन हैं I" + +msgid "Show/Hide clones" +msgstr "क्लोन दिखाएँ / छिपाएं" + +msgid "For Spice redirection you'll need to install" +msgstr "स्पाइस रीडायरेक्शन के लिए आपको इंस्टॉल करना होगा" + +msgid "on your computer." +msgstr "आपके कंप्युटर पर" + +msgid "Search in your distro, e.g. in Debian/Ubuntu with" +msgstr "अपने डिस्टो में खोज करें, जैसे डेबियन / उबंटू में के साथ" + +msgid "You'll need to install" +msgstr "आपको इंस्टॉल करना होगा" + +msgid "e.g. VirtViewer v5.0-256, and USB drivers (" +msgstr "जैसे VirtViewer v5.0-256, और यूएसबी ड्राइवर (" + +msgid "" +"Be aware that in Windows, Spice redirection is not automatically. It's " +"necessary to associate protocol with the app." +msgstr "" +"ध्यान रखें कि विंडोज में स्पाइस रीडायरेक्शन स्वचालित रूप से नहीं है, यह" +"एप्लिकेशन के साथ प्रोटोकॉल को संबद्ध करने के लिए आवश्यक है।" + +msgid "To make this possible, download" +msgstr "यह संभव बनाने के लिए, डाउनलोड करें" + +msgid "" +"or copy the following lines in an ASCII file and save with extension .reg, " +"then execute the file." +msgstr "" +"या किसी ASCII फ़ाइल में निम्न पंक्तियों की प्रतिलिपि बनाएँ और विस्तार के रूप में सहेजें .reg," +"फिर फ़ाइल निष्पादित करें।" + +msgid "Login" +msgstr "लॉग इन करें" + +msgid "Welcome" +msgstr "स्वागत हे" + +msgid "A viewer is required to run the virtual machines." +msgstr "वर्चुअल मशीन चलाने के लिए एक दर्शक की आवश्यकता है" + +msgid "Virtual Machines" +msgstr "वर्चुअल मशीन" + +msgid "user" +msgstr "उपयोगकर्ता" + +msgid "password" +msgstr "पासवर्ड " + +msgid "Start session" +msgstr "सत्र प्रारंभ करें" + +msgid "Login with Intranet UPC's username." +msgstr "इंट्रानेट UPC के उपयोगकर्ता नाम के साथ लॉगिन करें" + +msgid "bits) in your computer." +msgstr "बिट्स) आपके कंप्यूटर में" + +msgid "Permissions" +msgstr "अनुमतियां" + +msgid "can change the settings of owned virtual machines." +msgstr "स्वामित्व वाली वर्चुअल मशीन की सेटिंग बदल सकते हैं" + +msgid "can change the settings of any virtual machines." +msgstr "किसी भी वर्चुअल मशीन की सेटिंग बदल सकते हैं |" + +msgid "can change the settings of any virtual machines " +msgstr "किसी भी वर्चुअल मशीन की सेटिंग बदल सकते हैं" + +msgid "cloned from one base owned by the user." +msgstr "उपयोगकर्ता द्वारा स्वामित्व वाले एक आधार से क्लोन किया गया" + +msgid "can clone public virtual machines." +msgstr "सार्वजनिक वर्चुअल मशीन क्लोन कर सकते हैं" + +msgid "can clone any virtual machine." +msgstr "किसी भी वर्चुअल मशीन क्लोन कर सकते हैं" + +msgid "can create bases." +msgstr "आधार बना सकते हैं" + +msgid "can create virtual machines." +msgstr "वर्चुअल मशीन बना सकते हैं |" + +msgid "can grant permissions to other users" +msgstr "अन्य उपयोगकर्ताओं को अनुमति दे सकते हैं" + +msgid "can hibernate any virtual machine." +msgstr "कोई भी वर्चुअल मशीन को हाइबरनेट कर सकते हैं।" + +msgid "can hibernate clones from virtual machines owned by the user." +msgstr "उपयोगकर्ता के स्वामित्व वाले वर्चुअल मशीनों से क्लोन को हाइबरनेट कर सकते हैं" + +msgid "can hibernate any clone." +msgstr "कोई भी क्लोन हाइबरनेट कर सकते हैं।" + +msgid "can remove any virtual machines owned by the user." +msgstr "उपयोगकर्ता के स्वामित्व वाले किसी भी वर्चुअल मशीन को निकाल सकते हैं" + +msgid "can remove any virtual machine." +msgstr "किसी भी आभासी मशीन को निकाल सकते हैं" + +msgid "can remove clones from virtual machines owned by the user." +msgstr "उपयोगकर्ता के स्वामित्व वाले वर्चुअल मशीनों से क्लोन को निकाल सकते हैं" + +msgid "can remove any clone." +msgstr "किसी भी क्लोन को निकाल सकते हैं" + +msgid "can take a screenshot of any virtual machine owned by the user." +msgstr "उपयोगकर्ता के स्वामित्व वाले किसी भी आभासी मशीन की स्क्रीन की तस्वीर ले सकते हैं।" + +msgid "can take a screenshot of any virtual machine." +msgstr "किसी भी वर्चुअल मशीन की एक स्क्रीन की तस्वीर ले सकते हैं |" + +msgid "can shutdown any virtual machine." +msgstr "किसी भी वर्चुअल मशीन को बंद कर सकते हैं" + +msgid "can shutdown clones from virtual machines owned by the user." +msgstr "उपयोगकर्ता के स्वामित्व वाले वर्चुअल मशीनों से क्लोन को बंद कर सकते हैं।" + +msgid "Submit Query" +msgstr "प्रश्न दर्ज करें" + +msgid "From Template" +msgstr "टेम्पलेट से" + +msgid "From Machine" +msgstr "मशीन से" + +msgid "Backend" +msgstr "बैकएंड" + +msgid "Select Template" +msgstr "टेम्पलेट चुनें" + +msgid "This ISO image has not been downloaded yet. It may take some minutes, even hours until the file is fetched from the Internet." +msgstr "यह ISO छवि अभी तक डाउनलोड नहीं की गई है। फ़ाइल को इंटरनेट से प्राप्त होने तक कुछ मिनट लग सकते हैं।" + +msgid "Download now" +msgstr "अभी डाउनलोड करें" + +msgid "Select ISO" +msgstr "ISO का चयन करें" + +msgid "Template" +msgstr "टेम्पलेट" + +msgid "Disk Size: (GB)" +msgstr "डिस्क परिमाण: (जीबी)" + +msgid "Swap Size: (GB)" +msgstr "स्वैप परिमाण: (जीबी)" + +msgid "Ram: (GB)" +msgstr "रैम: (जीबी)" + +msgid "Machine name is required." +msgstr "मशीन का नाम आवश्यक है" + +msgid "Machine name can't exceed 20 characters." +msgstr "मशीन का नाम 20 वर्णों से अधिक नहीं हो सकता।" + +msgid "Backend selection is required." +msgstr "बैकएण्ड चयन आवश्यक है।" + +msgid "ISO image selection is required." +msgstr "ISO छवि चयन आवश्यक है" + +msgid "Template selection is required." +msgstr "टेम्पलेट चयन आवश्यक है" + +msgid "A machine with that name already exists." +msgstr "उस नाम वाला एक मशीन पहले से मौजूद है।" + +msgid "The machine name is only allowed to consist of alphabetic characters, numbers, dashes and points." +msgstr "मशीन के नाम में केवल वर्णानुक्रम वर्ण, संख्याएं, डैश और अंक शामिल हो सकते हैं।" + +msgid "Create" +msgstr "बनाओ" + +msgid "" +"can change the settings of any virtual machines " +"cloned from one base owned by the user." +msgstr "" +"किसी भी वर्चुअल मशीन की सेटिंग बदल सकते हैं" +"उपयोगकर्ता द्वारा स्वामित्व वाले एक आधार से क्लोन किया गया।" diff --git a/lib/Ravada/I18N/jp.po b/lib/Ravada/I18N/jp.po new file mode 100644 index 000000000..21b558cf8 --- /dev/null +++ b/lib/Ravada/I18N/jp.po @@ -0,0 +1,304 @@ +# Translators: +# Fernando Verdugo , 2017 +# Amparo Gombau , 2017 +msgid "" +msgstr "" +"Project-Id-Version: 0.1.0-alpha\n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2017-01-03 10:12+0300\n" +"PO-Revision-Date: 2017-05-02 16:20+0200\n" +"Last-Translator: Fernando Verdugo fernando@etsetb.upc.edu\n" +"Language-Team: \n" +"Language: jp\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" + +msgid "Ravada broker" +msgstr "ラバダブローカー" + +msgid "Machine Name" +msgstr "マシン名" + +msgid "Tools" +msgstr "ツール" + +msgid "Machines List" +msgstr "マシンの一覧" + +msgid "Users" +msgstr "ユーザー" + +msgid "Choose a Machine to Start" +msgstr "開始するマシンを選択します。" + +msgid "Start" +msgstr "スタート" + +msgid "Stop" +msgstr "ストップ" + +msgid "View" +msgstr "ビュー" + +msgid "Prepare base" +msgstr "ベースを準備する" + +msgid "Clone" +msgstr "クローン" + +msgid "Can't Prepare Base, Remove Base nor Delete. Machine has" +msgstr "私はベースを準備することはできません。 ベースを消去するか、マシンを消去してください。" + +msgid "Clone/s" +msgstr "クローン" + +msgid "NEW" +msgstr "新しい" + +msgid "Users List" +msgstr "ユーザーリスト" + +msgid "Name" +msgstr "名" + +msgid "Log Out" +msgstr "ログアウト" + +msgid "Available Machines" +msgstr "利用可能なマシン" + +msgid "Help" +msgstr "助けて" + +msgid "Requirements" +msgstr "要件" + +msgid "About" +msgstr "約" + +msgid "Messages" +msgstr "メッセージ" + +msgid "Mark all as Read" +msgstr "既読にする] を選択します。" + +msgid "Settings" +msgstr "設定" + +msgid "New Machine" +msgstr "新しいマシン" + +msgid "I want to change my password" +msgstr "" + +msgid "I want to change my language" +msgstr "" + +msgid "Language:" +msgstr "言語" + +msgid "English" +msgstr "英語" + +msgid "Spanish" +msgstr "スペイン語" + +msgid "Catalan" +msgstr "カタロニア語" + +msgid "New Password:" +msgstr "新しいパスワード:" + +msgid "Confirm Password:" +msgstr "新しいパスワード:" + +msgid "Submit" +msgstr "提出する" + +msgid "Your language has been changed successfully" +msgstr "言語が変更されました" + +msgid "Your password has been changed successfully" +msgstr "パスワードが正常に変更されました" + +msgid "Password too small" +msgstr "パスワードが小さすぎます" + +msgid "Password fields aren't equal" +msgstr "" + +msgid "Some of the password's fields are empty" +msgstr "" + +msgid "Public" +msgstr "パブリック" + +msgid "Status" +msgstr "状態" + +msgid "Actions" +msgstr "行動" + +msgid "Machines" +msgstr "マシン" + +msgid "Down" +msgstr "ダウン" + +msgid "Running" +msgstr "ランニング" + +msgid "ShutDown" +msgstr "シャットダウン" + +msgid "Screenshot" +msgstr "スクリーンショット" + +msgid "Paused" +msgstr "一時停止中" + +msgid "Machine locked by" +msgstr "" + +msgid "process" +msgstr "" + +msgid "Copy" +msgstr "" + +msgid "Error!" +msgstr "" + +msgid "Backend no available!" +msgstr "" + +msgid "Subject" +msgstr "" + +msgid "Date" +msgstr "" + +msgid "Mark as Read" +msgstr "" + +msgid "Mark as UnRead" +msgstr "" + +msgid "No message to show!" +msgstr "" + +msgid "Admin" +msgstr "" + +msgid "machines" +msgstr "機械" + +msgid "users" +msgstr "ユーザー" + +msgid "messages" +msgstr "メッセージ" + +msgid "Admin tools" +msgstr "管理ツール" + +msgid "Remove base" +msgstr "ベースを取り外す" + +msgid "public" +msgstr "パブリック" + +msgid "Restore" +msgstr "リストア" + +msgid "Hybernate" +msgstr "休止状態" + +msgid "Shutdown" +msgstr "シャットダウン" + +msgid "action" +msgstr "アクション" + +msgid "New Base" +msgstr "新しい基地" + +msgid "will remove all the contents of the machine" +msgstr "" + +msgid "Are you sure?" +msgstr "本気ですか?" + +msgid "Yes" +msgstr "はい" + +msgid "No" +msgstr "いいえ" + +msgid "Make public" +msgstr "" + +msgid "Machine settings" +msgstr "" + +msgid "Cannot remove base, machine has clones" +msgstr "" + +msgid "Show/Hide clones" +msgstr "" + +msgid "For spice redirection you'll need to install" +msgstr "" + +msgid "in your computer." +msgstr "" + +msgid "Search in your distro, e.g. in Debian/Ubuntu with" +msgstr "" + +msgid "You'll need to install" +msgstr "" + +msgid "e.g. VirtViewer v5.0-256, and USB drivers (" +msgstr "" + +msgid "" +"Be aware that in Windows, spice redirection is not automatically. It's " +"necessary associate protocol with the app." +msgstr "" + +msgid "To make this possible, download" +msgstr "" + +msgid "" +"or copy the following lines in an ASCII file and save with extension .reg, " +"then execute the file." +msgstr "" + +msgid "Login" +msgstr "" + +msgid "Welcome" +msgstr "" + +msgid "It is required a viewer to run the virtual machines." +msgstr "" + +msgid "Virtual Machines" +msgstr "仮想マシン" + +msgid "user" +msgstr "ユーザー" + +msgid "password" +msgstr "" + +msgid "Start session" +msgstr "" + +msgid "Login with Intranet UPC's username." +msgstr "" + +msgid "bits) in your computer." +msgstr "" diff --git a/lib/Ravada/I18N/pt.po b/lib/Ravada/I18N/pt.po new file mode 100644 index 000000000..ee8c301a9 --- /dev/null +++ b/lib/Ravada/I18N/pt.po @@ -0,0 +1,444 @@ +# Translators: +# Joel Alarcón , 2017 +# Fernando Verdugo , 2017 +# Luca Bezerra , 2017 +msgid "" +msgstr "" +"Project-Id-Version: 0.1.0-alpha\n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2017-01-03 10:12+0300\n" +"PO-Revision-Date: 2017-05-02 16:20+0200\n" +"Last-Translator: Luca Bezerra lucabezerra@gmail.com\n" +"Language-Team: \n" +"Language: ca\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" + +msgid "Ravada broker" +msgstr "Agente do Ravada" + +msgid "Machine Name" +msgstr "Nome da Máquina" + +msgid "Tools" +msgstr "Ferramentas" + +msgid "Machines List" +msgstr "Lista de Máquinas" + +msgid "Users" +msgstr "Usuários" + +msgid "Choose a Machine to Start" +msgstr "Escolha uma Máquina para Começar" + +msgid "Start" +msgstr "Começar" + +msgid "Stop" +msgstr "Parar" + +msgid "View" +msgstr "Ver" + +msgid "Prepare base" +msgstr "Preparar base" + +msgid "Clone" +msgstr "Clonar" + +msgid "Can't Prepare Base, Remove Base nor Delete. Machine has" +msgstr "Não é possível Preparar Base, Remover Base ou Deletar. A Máquina tem" + +msgid "Clone/s" +msgstr "Clone/s" + +msgid "NEW" +msgstr "NOVO" + +msgid "Users List" +msgstr "Lista de Usuários" + +msgid "Name" +msgstr "Nome" + +msgid "Log Out" +msgstr "Sair" + +msgid "Available Machines" +msgstr "Máquinas Disponíveis" + +msgid "Help" +msgstr "Ajuda" + +msgid "Requirements" +msgstr "Requisitos" + +msgid "About" +msgstr "Sobre" + +msgid "Messages" +msgstr "Mensagens" + +msgid "Mark all as Read" +msgstr "Marcar tudo como Lido" + +msgid "Settings" +msgstr "Configurações" + +msgid "New Machine" +msgstr "Nova Máquina" + +msgid "I want to change my password" +msgstr "Eu quero mudar a minha senha" + +msgid "I want to change my language" +msgstr "Eu quero alterar o meu idioma" + +msgid "Language:" +msgstr "Idioma:" + +msgid "English" +msgstr "Inglês" + +msgid "Spanish" +msgstr "Espanhol" + +msgid "Catalan" +msgstr "Catalão" + +msgid "New Password:" +msgstr "Nova Senha:" + +msgid "Confirm Password:" +msgstr "Confirmar Senha:" + +msgid "Submit" +msgstr "Enviar" + +msgid "Your language has been changed successfully" +msgstr "Seu idioma foi alterado com sucesso" + +msgid "Your password has been changed successfully" +msgstr "Sua senha foi alterada com sucesso" + +msgid "Password too small" +msgstr "Senha muito pequena" + +msgid "Password fields aren't equal" +msgstr "Campos de senha não são iguais" + +msgid "Some of the password's fields are empty" +msgstr "Algum dos campos de senha está vazio" + +msgid "Public" +msgstr "Público" + +msgid "Status" +msgstr "Estado" + +msgid "Actions" +msgstr "Ações" + +msgid "Machines" +msgstr "Máquinas" + +msgid "Down" +msgstr "Parada" + +msgid "Running" +msgstr "Em execução" + +msgid "ShutDown" +msgstr "Desligar" + +msgid "Screenshot" +msgstr "Captura de Tela" + +msgid "Paused" +msgstr "Pausada" + +msgid "Machine locked by requested" +msgstr "Máquina travada por requisição" + +msgid "process" +msgstr "processo" + +msgid "Copy" +msgstr "Copiar" + +msgid "Error!" +msgstr "Erro!" + +msgid "Error" +msgstr "Erro" + +msgid "Backend no available!" +msgstr "Backend indisponível!" + +msgid "Subject" +msgstr "Assunto" + +msgid "Date" +msgstr "Data" + +msgid "Mark as Read" +msgstr "Marcar como Lido" + +msgid "Mark as UnRead" +msgstr "Marcar como Não Lido" + +msgid "No message to show!" +msgstr "Nenhuma mensagem para mostrar!" + +msgid "Admin" +msgstr "Administrador" + +msgid "machines" +msgstr "máquinas" + +msgid "users" +msgstr "usuários" + +msgid "messages" +msgstr "mensagens" + +msgid "Admin tools" +msgstr "Ferramentas de administração" + +msgid "Remove base" +msgstr "Remover base" + +msgid "public" +msgstr "público" + +msgid "New Base" +msgstr "Nova base" + +msgid "Restore" +msgstr "Restaurar" + +msgid "Hybernate" +msgstr "Hibernar" + +msgid "Shutdown" +msgstr "Desligar" + +msgid "action" +msgstr "ação" + +msgid "will remove all the contents of the machine" +msgstr "vai remover todo o conteúdo da máquina" + +msgid "Are you sure?" +msgstr "Você tem certeza?" + +msgid "Yes" +msgstr "Sim" + +msgid "No" +msgstr "Não" + +msgid "Make public" +msgstr "Tornar pública" + +msgid "Machine settings" +msgstr "Configurações da Máquina" + +msgid "Cannot remove base, machine has clones" +msgstr "Não é possível remover a base, a máquina tem clones" + +msgid "Show/Hide clones" +msgstr "Mostrar/Esconder clones" + +msgid "For spice redirection you'll need to install" +msgstr "Para redirecionamento de spice, você vai precisar instalar" + +msgid "in your computer." +msgstr "no seu computador." + +msgid "Search in your distro, e.g. in Debian/Ubuntu with" +msgstr "Pesquisar na sua distro, ex. no Debian/Ubuntu com" + +msgid "You'll need to install" +msgstr "Você vai precisar instalar" + +msgid "e.g. VirtViewer v5.0-256, and USB drivers (" +msgstr "ex. VirtViewer v5.0-256, e drivers USB (" + +msgid "" +"Be aware that in Windows, spice redirection is not automatically. It's " +"necessary associate protocol with the app." +msgstr "" +"Note que no Windows o redirecionamento de spice não é automático. É " +"necessário associar o protocolo com a aplicação." + +msgid "To make this possible, download" +msgstr "Para tornar isto possível, faça o download de" + +msgid "" +"or copy the following lines in an ASCII file and save with extension .reg, " +"then execute the file." +msgstr "" +"ou copie as seguintes linhas em um arquivo ASCII e salve-o com uma extensão " +".reg, então o execute." + +msgid "Login" +msgstr "Entrar" + +msgid "Welcome" +msgstr "Bem vindo" + +msgid "It is required a viewer to run the virtual machines." +msgstr "É necessário um visualizador para execute as máquinas virtuais." + +msgid "Virtual Machines" +msgstr "Máquinas Virtuais" + +msgid "user" +msgstr "usuário" + +msgid "password" +msgstr "senha" + +msgid "Start session" +msgstr "Iniciar sessão" + +msgid "Login with Intranet UPC's username." +msgstr "Entrar com o nome de usuário da Intranet UPC." + +msgid "bits) in your computer." +msgstr "bits) em seu computador." + +msgid "Permissions" +msgstr "Permissões" + +msgid "can change the settings of owned virtual machines." +msgstr "pode alterar configurações de máquinas virtuais que possuir." + +msgid "can change the settings of any virtual machines." +msgstr "pode alterar as configurações de quaisquer máquinas virtuais." + +msgid "can change the settings of any virtual machines " +msgstr "pode alterar as configurações de quaisquer máquinas virtuais " + +msgid "cloned from one base owned by the user." +msgstr "clonada de uma das bases que o usuário possui." + +msgid "can clone public virtual machines." +msgstr "pode clinar máquinas virtuais públicas." + +msgid "can clone any virtual machine." +msgstr "pode clonar qualquer máquina virtual." + +msgid "can create bases." +msgstr "pode criar bases." + +msgid "can create virtual machines." +msgstr "pode criar máquinas virtuais." + +msgid "can grant permissions to other users" +msgstr "pode conceder permissões para outros usuários." + +msgid "can hibernate any virtual machine." +msgstr "pode hibernar qualquer máquina virtual." + +msgid "can hibernate clones from virtual machines owned by the user." +msgstr "pode hibernar clones de máquinas virtuais que o usuário possua." + +msgid "can hibernate any clone." +msgstr "pode hibernar qualquer clone." + +msgid "can remove any virtual machines owned by the user." +msgstr "pode remover quaisquer máquinas virtuais que o usuário possua." + +msgid "can remove any virtual machine." +msgstr "pode remover qualquer máquina virtual." + +msgid "can remove clones from virtual machines owned by the user." +msgstr "pode remover clones de máquinas virtuais que o usuário possua." + +msgid "can remove any clone." +msgstr "pode remover qualquer clone." + +msgid "can take a screenshot of any virtual machine owned by the user." +msgstr "pode tirar uma captura de tela de qualquer máquina virtual que o usuário possua." + +msgid "can take a screenshot of any virtual machine." +msgstr "pode tirar uma captura de tela de qualquer máquina virtual." + +msgid "can shutdown any virtual machine." +msgstr "pode desligar qualquer máquina virtual." + +msgid "can shutdown clones from virtual machines owned by the user." +msgstr "pode desligar clones de máquinas virtuais que o usuário possua." + +msgid "Submit Query" +msgstr "Submeter Consulta" + +msgid "From Template" +msgstr "A partir de um Template" + +msgid "From Machine" +msgstr "A partir de uma Máquina" + +msgid "Backend" +msgstr "Backend" + +msgid "Select Template" +msgstr "Selecione um Template" + +msgid "This ISO image has not been already downloaded. This may take many minutes, even hours until the file is fetched from Internet." +msgstr "" +"O download desta imagem ISO ainda não foi feito. Isto pode levar alguns " +"minutos ou até horas até que o arquivo seja recuperado da Internet." + +msgid "Download now" +msgstr "Faça o Download agora" + +msgid "Select ISO" +msgstr "Selecione uma ISO" + +msgid "Template" +msgstr "Template" + +msgid "Disk Size: (GB)" +msgstr "Tamanho do Disco: (GB)" + +msgid "Swap Size: (GB)" +msgstr "Tamanho do Swap: (GB)" + +msgid "Ram: (GB)" +msgstr "Ram: (GB)" + +msgid "Machine name is required." +msgstr "Nome da máquina é exigido." + +msgid "Machine name can't exceed 20 characters." +msgstr "Nome da máquina não pode exceder 20 caracteres." + +msgid "Backend selection is required." +msgstr "Seleção de um Backend é exigida." + +msgid "ISO image selection is required." +msgstr "Seleção de uma imagem ISO é exigida." + +msgid "Template selection is required." +msgstr "Seleção de um template é exigida." + +msgid "A machine with that name already exists." +msgstr "Uma máquina com esse nome já existe." + +msgid "The machine name must contain only alphabetic, numbers, dashes and points." +msgstr "O nome da máquina deve conter apenas caracteres alfanuméricos, hífens e pontos" + +msgid "Create" +msgstr "Criar" + +msgid "" +"can change the settings of any virtual machines " +"cloned from one base owned by the user." +msgstr "" +"pode alterar as configurações de quaisquer máquinas virtuais " +"clonadas de uma base possuída pelo usuário." diff --git a/lib/Ravada/Request.pm b/lib/Ravada/Request.pm index c349fb6d9..a231f41cc 100644 --- a/lib/Ravada/Request.pm +++ b/lib/Ravada/Request.pm @@ -60,6 +60,8 @@ our %VALID_ARG = ( ,set_driver => {uid => 1, id_domain => 1, id_option => 1} ,hybernate=> {uid => 1, id_domain => 1} ,download => {uid => 2, id_iso => 1, id_vm => 2, delay => 2} + ,refresh_storage => { id_vm => 2 } + ,clone => { uid => 1, id_domain => 1, name => 1, memory => 2 } ); our %CMD_SEND_MESSAGE = map { $_ => 1 } @@ -245,6 +247,7 @@ sub resume_domain { sub _check_args { my $sub = shift; + confess "Odd number of elements ".Dumper(\@_) if scalar(@_) % 2; my $args = { @_ }; my $valid_args = $VALID_ARG{$sub}; @@ -813,6 +816,55 @@ sub download { } +=head2 refresh_storage + +Refreshes a storage pool + +=cut + +sub refresh_storage { + my $proto = shift; + my $class = ref($proto) || $proto; + + my $args = _check_args('refresh_storage', @_ ); + + my $self = {}; + bless($self,$class); + + return $self->_new_request( + command => 'refresh_storage' + , args => $args + ); + + +} + +=head2 clone + +Copies a virtual machine + + my $req = Ravada::Request->clone( + ,uid => $user->id + id_domain => $domain->id + ); + +=cut + +sub clone { + my $proto = shift; + my $class = ref($proto) || $proto; + + my $args = _check_args('clone', @_ ); + + my $self = {}; + bless($self,$class); + + return _new_request($self + , command => 'clone' + , args =>$args + ); +} + sub AUTOLOAD { my $self = shift; diff --git a/lib/Ravada/VM.pm b/lib/Ravada/VM.pm index 28b77737f..8c95b1f39 100644 --- a/lib/Ravada/VM.pm +++ b/lib/Ravada/VM.pm @@ -132,6 +132,8 @@ sub _around_create_domain { my $self = shift; my %args = @_; + my $id_owner = delete $args{id_owner} or confess "ERROR: Missing id_owner"; + $self->_pre_create_domain(@_); my $domain = $self->$orig(@_); @@ -143,6 +145,9 @@ sub _around_create_domain { $domain->run_timeout($base->run_timeout) if defined $base->run_timeout(); } + my $user = Ravada::Auth::SQL->search_by_id($id_owner); + $domain->is_volatile(1) if $user->is_temporary(); + return $domain; } @@ -301,15 +306,25 @@ sub _check_require_base { my $self = shift; my %args = @_; - return if !$args{id_base}; - - my $base = Ravada::Domain->open($args{id_base}); - if ($base->list_requests) { - die "ERROR: Domain ".$base->name." has ".$base->list_requests - ." requests.\n"; + + my $id_base = delete $args{id_base} or return; + my $request = delete $args{request}; + my $id_owner = delete $args{id_owner} + or confess "ERROR: id_owner required "; + + delete @args{'_vm','name','vm', 'memory','description'}; + + confess "ERROR: Unknown arguments ".join(",",keys %args) + if keys %args; + + my $base = Ravada::Domain->open($id_base); + if (my @requests = $base->list_requests) { + confess "ERROR: Domain ".$base->name." has ".$base->list_requests + ." requests.\n" + unless scalar @requests == 1 && $request + && $requests[0]->id eq $request->id; } - my $id_owner = $args{id_owner} or confess "ERROR: id_owner required "; die "ERROR: Domain ".$self->name." is not base" if !$base->is_base(); diff --git a/lib/Ravada/VM/KVM.pm b/lib/Ravada/VM/KVM.pm index 06996a5bd..36c5da2ae 100644 --- a/lib/Ravada/VM/KVM.pm +++ b/lib/Ravada/VM/KVM.pm @@ -33,6 +33,7 @@ no warnings "experimental::signatures"; use Ravada::Domain::KVM; use Ravada::NetInterface::KVM; use Ravada::NetInterface::MacVTap; +use Ravada::Utils; with 'Ravada::VM'; @@ -265,13 +266,25 @@ sub search_volume_re($self,$pattern,$refresh=0) { sub _refresh_storage_pools($self) { for my $pool ($self->vm->list_storage_pools) { - eval { $pool->refresh() }; - last if !$@; - warn $@ if $@ !~ /pool .* has asynchronous jobs running/; - sleep 1; + for (;;) { + eval { $pool->refresh() }; + last if !$@; + warn $@ if $@ !~ /pool .* has asynchronous jobs running/; + sleep 1; + } } } +=head2 refresh_storage + +Refreshes all the storage pools + +=cut + +sub refresh_storage($self) { + $self->_refresh_storage_pools(); +} + =head2 search_volume_path_re Searches for a volume in all the storage pools known to the Virtual Manager @@ -601,8 +614,11 @@ sub _domain_create_from_iso { $self->_xml_modify_usb($xml); _xml_modify_video($xml); - my $domain = $self->_domain_create_common($xml,%args); + my ($domain, $spice_password) + = $self->_domain_create_common($xml,%args); $domain->_insert_db(name=> $args{name}, id_owner => $args{id_owner}); + $domain->_set_spice_password($spice_password) + if $spice_password; return $domain; } @@ -612,18 +628,27 @@ sub _domain_create_common { my $xml = shift; my %args = @_; + my $id_owner = delete $args{id_owner} or confess "ERROR: The id_owner is mandatory"; + my $user = Ravada::Auth::SQL->search_by_id($id_owner) + or confess "ERROR: User id $id_owner doesn't exist"; + + my $spice_password = Ravada::Utils::random_name(4); $self->_xml_modify_memory($xml,$args{memory}) if $args{memory}; $self->_xml_modify_network($xml , $args{network}) if $args{network}; $self->_xml_modify_mac($xml); $self->_xml_modify_uuid($xml); - $self->_xml_modify_spice_port($xml); + $self->_xml_modify_spice_port($xml, $spice_password); $self->_fix_pci_slots($xml); my $dom; eval { - $dom = $self->vm->define_domain($xml->toString()); - $dom->create if $args{active}; + if ($user->is_temporary) { + $dom = $self->vm->create_domain($xml->toString()); + } else { + $dom = $self->vm->define_domain($xml->toString()); + $dom->create if $args{active}; + } }; if ($@) { my $out; @@ -643,8 +668,7 @@ sub _domain_create_common { , domain => $dom , storage => $self->storage_pool ); - - return $domain; + return ($domain, $spice_password); } sub _create_disk { @@ -767,8 +791,10 @@ sub _domain_create_from_base { _xml_modify_disk($xml, \@device_disk);#, \@swap_disk); - my $domain = $self->_domain_create_common($xml,%args); + my ($domain, $spice_password) + = $self->_domain_create_common($xml,%args); $domain->_insert_db(name=> $args{name}, id_base => $base->id, id_owner => $args{id_owner}); + $domain->_set_spice_password($spice_password); return $domain; } @@ -869,6 +895,7 @@ sub _iso_name { ); $sth->execute($device,$iso->{id}); } + $self->_refresh_storage_pools(); return $device; } @@ -1220,12 +1247,14 @@ sub _xml_modify_video { sub _xml_modify_spice_port { my $self = shift; my $doc = shift or confess "Missing XML doc"; + my $password = shift; my ($graph) = $doc->findnodes('/domain/devices/graphics') or die "ERROR: I can't find graphic"; $graph->setAttribute(type => 'spice'); $graph->setAttribute(autoport => 'yes'); $graph->setAttribute(listen=> $self->ip() ); + $graph->setAttribute(passwd => $password) if $password; my ($listen) = $doc->findnodes('/domain/devices/graphics/listen'); @@ -1243,15 +1272,32 @@ sub _xml_modify_uuid { my $doc = shift; my ($uuid) = $doc->findnodes('/domain/uuid/text()'); - random:while (1) { - my $new_uuid = _new_uuid($uuid); - next if $new_uuid eq $uuid; + my @known_uuids; + for my $dom ($self->vm->list_all_domains) { + push @known_uuids,($dom->get_uuid_string); + } + my $new_uuid = _unique_uuid($uuid,@known_uuids); + $uuid->setData($new_uuid); +} + +sub _unique_uuid($self, $uuid='1805fb4f-ca45-aaaa-bbbb-94124e760434',@) { + my @uuids = @_; + if (!scalar @uuids) { for my $dom ($self->vm->list_all_domains) { - next random if $dom->get_uuid_string eq $new_uuid; + push @uuids,($dom->get_uuid_string); } - $uuid->setData($new_uuid); - last; } + my ($first,$last) = $uuid =~ m{(.*)([0-9a-f]{6})}; + + for (1..1000) { + my $new_last = int(rand(0x100000)); + my $new_uuid = sprintf("%s%06d",$first,substr($new_last,0,6)); + + confess "Wrong uuid size ".length($new_uuid)." <> ".length($uuid) + if length($new_uuid) != length($uuid); + return $new_uuid if !grep /^$new_uuid$/,@uuids; + } + confess "I can't find a new unique uuid"; } sub _xml_modify_cdrom { @@ -1592,7 +1638,10 @@ sub _unique_mac { for my $nic ( $doc->findnodes('/domain/devices/interface/mac')) { my $nic_mac = $nic->getAttribute('address'); - return 0 if $mac eq lc($nic_mac); + if ( $mac eq lc($nic_mac) ) { + warn "mac clashes with domain ".$dom->get_name; + return 0; + } } } return 1; @@ -1617,31 +1666,37 @@ sub _xml_modify_mac { my @macparts = split/:/,$mac; + my @old_macs; + + for my $dom ($self->vm->list_all_domains) { + my $doc = $XML->load_xml(string => $dom->get_xml_description()) or die "ERROR: $!\n"; + + for my $nic ( $doc->findnodes('/domain/devices/interface/mac')) { + my $nic_mac = $nic->getAttribute('address'); + push @old_macs,($nic_mac); + } + } + + my $new_mac; - my $n_part = scalar(@macparts) -2; + for my $cont ( 1 .. 1000 ) { + my $pos = int(rand(2))+4; + my $num =sprintf "%02X", rand(0xff); + die "Missing num " if !defined $num; + $macparts[$pos] = $num; + $new_mac = lc(join(":",@macparts)); + my $n_part = scalar(@macparts) -2; + + last if (! grep /^$new_mac$/i,@old_macs); + } - for (;;) { - for my $last ( 0 .. 254 ) { - $last = sprintf("%X", $last); - $last = "0$last" if length($last)<2; - $macparts[-1] = $last; - $new_mac = join(":",@macparts); - if ( $self->_unique_mac($new_mac) ) { + if ( $self->_unique_mac($new_mac) ) { $if_mac->setAttribute(address => $new_mac); return; - } - $new_mac = undef; - } - my $new_part = hex($macparts[$n_part])+1; - if ($new_part > 255) { - $n_part--; - $new_part = 0; - die "I can't find a new unique mac" if !$n_part<0; - } - $macparts[$n_part] = sprintf("%X", $new_part); + } else { + die "I can't find a new unique mac"; } - die "I can't find a new unique mac" if !$new_mac; } diff --git a/lib/Ravada/VM/Void.pm b/lib/Ravada/VM/Void.pm index 716c71a23..a134eec9a 100644 --- a/lib/Ravada/VM/Void.pm +++ b/lib/Ravada/VM/Void.pm @@ -52,6 +52,7 @@ sub create_domain { , id_base => $args{id_base} ); if ($args{id_base}) { + my $owner = Ravada::Auth::SQL->search_by_id($args{id_owner}); my $domain_base = $self->search_domain_by_id($args{id_base}); confess "I can't find base domain id=$args{id_base}" if !$domain_base; @@ -63,6 +64,7 @@ sub create_domain { , path => "$dir/$new_name" ,type => 'file'); } + $domain->start(user => $owner) if $owner->is_temporary; } else { my ($file_img) = $domain->disk_device(); $domain->add_volume(name => 'void-diska' , size => ( $args{disk} or 1) @@ -72,9 +74,9 @@ sub create_domain { ); $domain->_set_default_drivers(); $domain->_set_default_info(); - $domain->set_memory($args{memory}) if $args{memory}; } + $domain->set_memory($args{memory}) if $args{memory}; # $domain->start(); return $domain; } @@ -169,6 +171,8 @@ sub import_domain { confess "Not implemented"; } +sub refresh_storage {} + #########################################################################3 1; diff --git a/public/css/admin.css b/public/css/admin.css index 663461979..65a2d7b30 100644 --- a/public/css/admin.css +++ b/public/css/admin.css @@ -16,3 +16,38 @@ .lgMachActions{ width: 200px; } + +#slidecontainer { + width: 100%; /* Width of the outside container */ +} +/* The slider itself */ +.slider { + -webkit-appearance: none; /* Override default CSS styles */ + appearance: none; + width: 100%; /* Full-width */ + height: 25px; /* Specified height */ + background: #d3d3d3; /* Grey background */ + outline: none; /* Remove outline */ + opacity: 0.7; /* Set transparency (for mouse-over effects on hover) */ + -webkit-transition: .2s; /* 0.2 seconds transition on hover */ + transition: opacity .2s; +} +/* Mouse-over effects */ +.slider:hover { + opacity: 1; /* Fully shown on mouse-over */ +} +/* The slider handle (use webkit (Chrome, Opera, Safari, Edge) and moz (Firefox) to override default look) */ +.slider::-webkit-slider-thumb { + -webkit-appearance: none; /* Override default look */ + appearance: none; + width: 25px; /* Set a specific slider handle width */ + height: 25px; /* Slider handle height */ + background: #4CAF50; /* Green background */ + cursor: pointer; /* Cursor on hover */ +} +.slider::-moz-range-thumb { + width: 25px; /* Set a specific slider handle width */ + height: 25px; /* Slider handle height */ + background: #4CAF50; /* Green background */ + cursor: pointer; /* Cursor on hover */ +} diff --git a/public/css/sb-admin.css b/public/css/sb-admin.css index f94c802f0..4a77abcdf 100644 --- a/public/css/sb-admin.css +++ b/public/css/sb-admin.css @@ -388,3 +388,8 @@ ul.alert-dropdown { border-left:1px solid #d73a49; border-right:1px solid #d73a49; } + +.screenshot-default{ + opacity: 0.5; + filter: alpha(opacity=50); /* For IE8 and earlier */ +} diff --git a/public/img/default_screenshot.png b/public/img/default_screenshot.png new file mode 100755 index 000000000..40bd22328 Binary files /dev/null and b/public/img/default_screenshot.png differ diff --git a/public/img/screenshot.jpg b/public/img/screenshot.jpg deleted file mode 100644 index 4dd398419..000000000 Binary files a/public/img/screenshot.jpg and /dev/null differ diff --git a/public/js/admin.js b/public/js/admin.js index 3a0041b2a..467a6ed4f 100644 --- a/public/js/admin.js +++ b/public/js/admin.js @@ -74,7 +74,8 @@ ravadaApp.directive("solShowMachine", swMach) $scope.show_swap = function() { $scope.seeswap = !($scope.seeswap); - $scope.swapsize.value=0; + if ($scope.seeswap == 1) $scope.swapsize.value=1; + else $scope.swapsize.value = 0; }; $http.get('/list_machines.json').then(function(response) { @@ -172,17 +173,10 @@ ravadaApp.directive("solShowMachine", swMach) $http.get('/pingbackend.json').then(function(response) { $scope.pingbe_fail = !response.data; }); - $scope.getUsers = function() { - $http.get('/list_users.json').then(function(response) { - $scope.list_users= response.data; - }); - } $scope.action = function(target,action,machineId){ $http.get('/'+target+'/'+action+'/'+machineId+'.json'); }; //On load code - $scope.getUsers(); - $scope.updatePromise = $interval($scope.getUsers,3000); }; function messagesPageC($scope, $http, $interval, request) { diff --git a/public/js/ravada.js b/public/js/ravada.js index 663fe3588..29f5e61df 100644 --- a/public/js/ravada.js +++ b/public/js/ravada.js @@ -12,7 +12,6 @@ .service("request", gtRequest) .service("listMach", gtListMach) .service("listMess", gtListMess) - .service("listUsers", gtListUsers) .controller("SupportForm", suppFormCtrl) .controller("AddUserForm",addUserFormCrtl) // .controller("machines", machinesCrtl) @@ -131,7 +130,7 @@ $http.get('/pingbackend.json').then(function(response) { $scope.pingbe_fail = !response.data; }); - $scope.getSingleMachine = function(){ +/* $scope.getSingleMachine = function(){ $http.get("/list_machines.json").then(function(response) { for (var i=0, iLength=response.data.length; iis_temporary; + } return login($c) if - $url !~ m{^/(anonymous|login|logout|requirements|robots.txt)} + $url !~ m{^/(anonymous|login|logout|requirements|request|robots.txt)} && $url !~ m{^/(css|font|img|js)} && !_logged_in($c); @@ -272,6 +276,7 @@ get '/list_machines.json' => sub { my $c = shift; + return access_denied($c) if !_logged_in($c) || !$USER->is_admin(); $c->render(json => $RAVADA->list_domains); }; @@ -282,11 +287,6 @@ $c->render(json => $RAVADA->list_bases_anonymous(_remote_ip($c))); }; -get '/list_users.json' => sub { - my $c = shift; - $c->render(json => $RAVADA->list_users); -}; - get '/list_lxc_templates.json' => sub { my $c = shift; $c->render(json => $RAVADA->list_lxc_templates); @@ -304,6 +304,13 @@ my $c = shift; my $id = $c->stash('id'); die "No id " if !$id; + + my ($domain) = _search_requested_machine($c); + return access_denied($c) if !$domain; + + return access_denied($c) unless $USER->is_admin + || $domain->id_owner == $USER->id; + $c->render(json => $RAVADA->domain_info(id => $id)); }; @@ -315,6 +322,13 @@ any '/machine/manage/(:id).(:type)' => sub { my $c = shift; + + my ($domain) = _search_requested_machine($c); + return access_denied($c) if !$domain; + + return access_denied($c) unless $USER->is_admin + || $domain->id_owner == $USER->id; + return manage_machine($c); }; @@ -323,6 +337,12 @@ my $id = $c->stash('id'); my $type = $c->stash('type'); + my ($domain) = _search_requested_machine($c); + return access_denied($c) if !$domain; + + return access_denied($c) unless $USER->is_admin + || $domain->id_owner == $USER->id; + return view_machine($c); }; @@ -422,8 +442,7 @@ any '/machine/copy' => sub { my $c = shift; - return access_denied($c) if !$USER -> can_copy(); -# return access_denied($c) if !$USER -> can_clone_all(); + return access_denied($c) if !$USER -> can_clone_all(); return copy_machine($c); }; @@ -512,6 +531,15 @@ return _show_request($c,$id); }; +get '/anonymous/request/(:id).(:type)' => sub { + my $c = shift; + my $id = $c->stash('id'); + + $USER = _anonymous_user($c); + + return _show_request($c,$id); +}; + get '/requests.json' => sub { my $c = shift; return list_requests($c); @@ -628,25 +656,31 @@ sub user_settings { $c->param('tongue' => $USER->language); my @errors; if ($c->param('button_click')) { - if (($c->param('password') eq "") || ($c->param('conf_password') eq "")) { + if (($c->param('password') eq "") || ($c->param('conf_password') eq "") || ($c->param('current_password') eq "")) { push @errors,("Some of the password's fields are empty"); } else { - if ($c->param('password') eq $c->param('conf_password')) { - eval { - $USER->change_password($c->param('password')); - _logged_in($c); - }; - if ($@ =~ /Password too small/) { - push @errors,("Password too small") + my $comp_password = $USER->compare_password($c->param('current_password')); + if ($comp_password) { + if ($c->param('password') eq $c->param('conf_password')) { + eval { + $USER->change_password($c->param('password')); + _logged_in($c); + }; + if ($@ =~ /Password too small/) { + push @errors,("New Password is too small"); + } + else { + $changed_pass = 1; + }; } else { - $changed_pass = 1; - }; - } - else { - push @errors,("Password fields aren't equal") - } + push @errors,("Password fields aren't equal"); + } + } + else { + push @errors, ("Invalid Current Password"); + } } } $c->render(template => 'bootstrap/user_settings', changed_lang=> $changed_lang, changed_pass => $changed_pass @@ -932,6 +966,9 @@ sub new_machine { req_new_domain($c); $c->redirect_to("/admin/machines"); } + } else { + my $req = Ravada::Request->refresh_storage(); + # TODO handle possible errors } $c->stash(errors => \@error); push @{$c->stash->{js}}, '/js/admin.js'; @@ -957,8 +994,8 @@ sub req_new_domain { ,id_owner => $USER->id ,swap => $swap ); - $args{memory} = int($c->param('memory')*1024*1024) if $args{memory}; - $args{disk} = int($c->param('disk')*1024*1024*1024) if $args{disk}; + $args{memory} = int($c->param('memory')*1024*1024) if $c->param('memory'); + $args{disk} = int($c->param('disk')*1024*1024*1024) if $c->param('disk'); $args{id_template} = $c->param('id_template') if $vm =~ /^LX/; $args{id_iso} = $c->param('id_iso') if $vm eq 'KVM'; @@ -1066,7 +1103,10 @@ sub provision { $c->stash(error => "Domain provisioning request not finished, status='".$req->status."'."); - $c->stash(link => "/request/".$req->id.".html"); + my $req_link = "/request/".$req->id.".html"; + $req_link = "/anonymous$req_link" if $USER->is_temporary; + + $c->stash(link => $req_link); $c->stash(link_msg => ''); return; } @@ -1104,7 +1144,9 @@ sub show_link { && $req->error !~ /already running/i && $req->status ne 'waiting'; - return $c->redirect_to("/request/".$req->id.".html"); + my $req_link = "/request/".$req->id.".html"; + $req_link = "/anonymous$req_link" if $USER->is_temporary; + return $c->redirect_to($req_link); # if !$req->status eq 'done'; } if ( $domain->is_paused) { @@ -1157,6 +1199,7 @@ sub _message_timeout { my $domain = shift; my $msg_timeout = "in ".int($domain->run_timeout / 60 ) ." minutes."; + for my $request ( $domain->list_requests ) { if ( $request->command eq 'shutdown' ) { my $t1 = Time::Piece->localtime($request->at_time); @@ -1332,6 +1375,13 @@ sub manage_machine { sub settings_machine { my $c = shift; my ($domain) = _search_requested_machine($c); + + return access_denied($c) if !$domain; + + return access_denied($c) + unless $USER->is_admin + || $domain->id_owner == $USER->id; + return $c->render("Domain not found") if !$domain; $c->stash(domain => $domain); @@ -1408,10 +1458,11 @@ sub view_machine { my $c = shift; my $domain = shift; - return login($c) if !_logged_in($c); + return login($c) unless ( defined $USER && $USER->is_temporary) || _logged_in($c); $domain = _search_requested_machine($c) if !$domain; return $c->render(template => 'main/fail') if !$domain; + return show_link($c, $domain); } @@ -1570,24 +1621,13 @@ sub copy_machine { my $name = $c->req->param($param_name) if $param_name; $name = $base->name."-".$USER->name if !$name; - if (!$base->is_base || $base->is_locked) { - my $req = Ravada::Request->prepare_base( - id_domain => $id_base - ,uid => $USER->id - ); - return $c->render("Problem preparing base for domain ".$base->name) - if !$req; - - sleep 1; - - } my @create_args =( memory => $ram ) if $ram; push @create_args , ( disk => $disk ) if $disk; - my $req2 = Ravada::Request->create_domain( - name => $name - , id_base => $id_base - , id_owner => $USER->id - ,@create_args + my $req2 = Ravada::Request->clone( + uid => $USER->id + ,name => $name + , id_domain => $base->id + ,@create_args ); $c->redirect_to("/admin/machines");# if !@error; } @@ -1679,10 +1719,11 @@ sub list_bases_anonymous { return access_denied($c) if !scalar @$bases_anonymous; - $c->render(template => 'main/list_bases' + $c->render(template => 'main/list_bases2' , _logged_in => undef , _anonymous => 1 - , _user => undef + , machines => $bases_anonymous + , user => undef , url => undef ); } @@ -1698,6 +1739,21 @@ sub _remote_ip { ); } +sub _get_anonymous_user { + my $c = shift; + + $c->stash(_user => undef); + my $name = $c->session('anonymous_user'); + + my $user= Ravada::Auth::SQL->new( name => $name ); + + confess "user ".$user->name." has no id, may not be in table users" + if !$user->id; + + return $user; +} + +# get or create a new anonymous user sub _anonymous_user { my $c = shift; @@ -1718,7 +1774,7 @@ sub _anonymous_user { sub _random_name { my $length = shift; - my $ret = 'O'.substr($$,3); + my $ret = substr($$,3); my $max = ord('z') - ord('a'); for ( 0 .. $length ) { my $n = int rand($max + 1); @@ -1737,7 +1793,7 @@ sub _new_anonymous_user { my $name; for my $n ( 4 .. 32 ) { - $name = substr($name_mojo,0,$n); + $name = "anon".substr($name_mojo,0,$n); my $user; eval { $user = Ravada::Auth::SQL->new( name => $name ); diff --git a/t/05_ravada.t b/t/05_ravada.t index 6c74b848b..4628bafa1 100644 --- a/t/05_ravada.t +++ b/t/05_ravada.t @@ -19,4 +19,16 @@ ok($Ravada::CONNECTOR, "Now we should have a DB connector "); ok($Ravada::CONNECTOR,"No connector defined "); eval { ok($Ravada::CONNECTOR->dbh,"No dbh defined ") }; +eval { + my $config_err = "t/etc/ravada_miss.conf"; + my $rvd_err = Ravada->new( connector => $test->connector, config => $config_err); +}; +like($@,qr/Missing config file/); + +eval { + my $config_err = "t/etc/ravada_err.conf"; + my $rvd_err = Ravada->new( connector => $test->connector, config => $config_err); +}; +like($@,qr/Format error/); + done_testing(); diff --git a/t/etc/ravada_err.conf b/t/etc/ravada_err.conf new file mode 100644 index 000000000..ac6024022 --- /dev/null +++ b/t/etc/ravada_err.conf @@ -0,0 +1,2 @@ +db: + user: pepe diff --git a/t/front/10_load.t b/t/front/10_load.t index 7ba29ef97..3a60543bf 100644 --- a/t/front/10_load.t +++ b/t/front/10_load.t @@ -69,9 +69,11 @@ sub test_add_domain_db { $bases = $RVD_FRONT->list_bases(); ok($bases,"No bases list returned"); ok(scalar @$bases == 1, "There should 1 base, got ".scalar(@$bases)) or exit; + + is($bases->[0]->{name}, $domain_name); for my $base ( @$bases ) { - ok($base->{is_base} ); + ok($base->{is_base},"[$vm_name] Expecting base for ".Dumper($base) ); } } diff --git a/t/kvm/40_import.t b/t/kvm/40_import.t index 0ef7d94d5..5d245366c 100644 --- a/t/kvm/40_import.t +++ b/t/kvm/40_import.t @@ -163,6 +163,10 @@ for my $vm_name (@VMS) { my $vm = $RVD_BACK->search_vm($vm_name); SKIP : { my $msg = "SKIPPED test: No $vm_name VM found "; + if ($vm_name eq 'KVM') { + $msg = "SKIPPED test: $vm_name must be tested from root user"; + $vm = undef; + } diag($msg) if !$vm; skip $msg,10 if !$vm; diff --git a/t/kvm/62_images.t b/t/kvm/62_images.t index 07b2a8d6c..571a70d43 100644 --- a/t/kvm/62_images.t +++ b/t/kvm/62_images.t @@ -72,6 +72,8 @@ sub test_drivers_type { isa_ok(\@drivers,'ARRAY'); my $driver_type = $domain->drivers($type); + ok($driver_type,"[$vm_name] Expecting a driver type $type") or return; + isa_ok($driver_type, "Ravada::Domain::Driver") or return; my $value = $driver_type->get_value(); is($value,undef,"Expecting no value for $type"); diff --git a/t/kvm/65_dom_settings_req.t b/t/kvm/65_dom_settings_req.t index ba2abbe12..d3881af8a 100644 --- a/t/kvm/65_dom_settings_req.t +++ b/t/kvm/65_dom_settings_req.t @@ -21,6 +21,8 @@ my %ARG_CREATE_DOM = ( ); our $TIMEOUT_SHUTDOWN = 10; +our %SKIP_DEFAULT_VALUE = map { $_ => 1 } qw(image jpeg playback streaming zlib); + ################################################################ sub test_create_domain { my $vm_name = shift; @@ -66,8 +68,10 @@ sub test_drivers_id { my $driver_type = $domain->drivers($type); - my $value = $driver_type->get_value(); - ok($value); + if (!$SKIP_DEFAULT_VALUE{$type}) { + my $value = $driver_type->get_value(); + ok($value,"[$vm_name] Expecting a value for driver $type"); + } my @options = $driver_type->get_options(); isa_ok(\@options,'ARRAY'); @@ -121,7 +125,7 @@ sub test_settings { my $vm_name = shift; for my $driver ( Ravada::Domain::drivers(undef,undef,$vm_name) ) { - next if $driver->name ne 'video'; +# next if $driver->name ne 'video'; diag("Testing drivers for $vm_name ".$driver->name); test_drivers_id($vm_name, $driver->name); } diff --git a/t/request/20_download_local.t b/t/request/20_download_local.t index d45e8d9ff..ab20378c9 100644 --- a/t/request/20_download_local.t +++ b/t/request/20_download_local.t @@ -6,6 +6,7 @@ use Data::Dumper; use IPC::Run3; use Test::More; use Test::SQL::Data; +use Mojo::UserAgent; use lib 't/lib'; use Test::Ravada; @@ -100,6 +101,19 @@ sub search_id_isos { } return @id_iso; } + +sub httpd_localhost { + my $ua = Mojo::UserAgent->new; + eval { + my $res = $ua->get('http://localhost/iso')->res; + return 1 if $res->code == 200; + diag($res->message); + return 0; + }; + return if !$@; + is($@,qr/Connection refused/); +} + ################################################################## @@ -113,6 +127,10 @@ for my $vm_name ('KVM') { $msg = "SKIPPED: Test must run as root"; $vm = undef; } + if (!httpd_localhost()) { + $vm = undef; + $msg = "SKIPPED: No http on localhost with /iso"; + } diag($msg) if !$vm; skip($msg,10) if !$vm; diff --git a/t/request/50_refresh_storage.t b/t/request/50_refresh_storage.t new file mode 100644 index 000000000..683e90bb1 --- /dev/null +++ b/t/request/50_refresh_storage.t @@ -0,0 +1,102 @@ +use warnings; +use strict; + +use Carp qw(carp confess cluck); +use Data::Dumper; +use POSIX qw(WNOHANG); +use Test::More; +use Test::SQL::Data; + +use_ok('Ravada'); +use_ok('Ravada::Request'); + +use lib 't/lib'; +use Test::Ravada; + +my $test = Test::SQL::Data->new(config => 't/etc/sql.conf'); + +init($test->connector, 't/etc/ravada.conf'); + +my $USER = create_user("foo","bar"); + +################################################################### + +sub test_request { + my $vm_name = shift; + my $id_vm = shift; + + my $file = "a.iso"; + + my $vm = rvd_back->search_vm($vm_name); + + # clean old files + my @files = $vm->search_volume_path($file); + for my $old_file (@files) { + unlink $old_file or die "$! $old_file" + if -e $old_file; + } + $vm->_refresh_storage_pools(); + + # check there are no files + @files = $vm->search_volume_path($file); + ok(!scalar @files) or next; + + my $file_out = $vm->dir_img."/$file"; + unlink $file_out or die "$! $file_out" + if -e $file_out; + + open my $out,'>',$file_out or die "$! $file_out"; + print $out "rosa d'abril\n"; + close $out; + ok(-e $file_out,"Expecting a file $file_out"); + + my $request; + + eval { + my @args; + @args = (id_vm => $id_vm ) if $id_vm; + $request = Ravada::Request->refresh_storage(@args); + }; + is($@,''); + ok($request,"Expecting a request") or next; + rvd_back->_process_all_requests_dont_fork(); + + is($request->status,'done'); + is($request->error,''); + + @files = $vm->search_volume_path($file); + ok(scalar @files,"Expecting $file exists on storage pool"); +} + +######################################################### + +clean(); + +for my $vm_name (qw(KVM)) { + my $vm; + my $msg = "SKIPPED: virtual manager $vm_name not found"; + eval { + $vm= rvd_back->search_vm($vm_name) if rvd_back(); + + if ($vm && $vm_name =~ /kvm/i && $>) { + $msg = "SKIPPED: Test must run as root"; + $vm= undef; + } + + }; + + SKIP: { + diag($msg) if !$vm; + skip($msg,10) if !$vm; + + diag("Testing requests with $vm_name"); + + test_request($vm_name, $vm->id); + test_request($vm_name); + } + + +} + +clean(); +done_testing(); diff --git a/t/vm/05_open.t b/t/vm/05_open.t index b13ddd499..fd21c54dc 100644 --- a/t/vm/05_open.t +++ b/t/vm/05_open.t @@ -40,7 +40,7 @@ sub test_create_domain { } } -my $id = 1; +my $id = 10; for my $vm_type( @{rvd_front->list_vm_types}) { diag($vm_type); my $exp_class = "Ravada::VM::$vm_type"; diff --git a/t/vm/40_volumes.t b/t/vm/40_volumes.t index e7352314d..06880a185 100644 --- a/t/vm/40_volumes.t +++ b/t/vm/40_volumes.t @@ -379,6 +379,12 @@ sub test_domain_swap { sub test_search($vm_name) { my $vm = rvd_back->search_vm($vm_name); + $vm->set_default_storage_pool_name('default') if $vm eq 'KVM'; + + my $file_old = $vm->search_volume_path("file.iso"); + unlink $file_old if -e $file_old; + + $vm->default_storage_pool_name('default'); my $file_out = $vm->dir_img."/file.iso"; @@ -389,6 +395,8 @@ sub test_search($vm_name) { print $out "foo.bar\n"; close $out; + $vm->refresh_storage(); + my $file = $vm->search_volume_path("file.iso"); is($file_out, $file); diff --git a/t/vm/45_vol_swap.t b/t/vm/45_vol_swap.t new file mode 100644 index 000000000..e844212a2 --- /dev/null +++ b/t/vm/45_vol_swap.t @@ -0,0 +1,75 @@ +use warnings; +use strict; + +use Carp qw(confess); +use Data::Dumper; +use Test::More; +use Test::SQL::Data; + +use lib 't/lib'; +use Test::Ravada; + +my $test = Test::SQL::Data->new(config => 't/etc/sql.conf'); + +init($test); + +#################################################################### + +sub test_domain_with_swap { + my $vm_name = shift; + + my $domain = create_domain($vm_name); + $domain->add_volume_swap( size => 1000 * 1024); + + my @vol = $domain->list_volumes(); + is(scalar(@vol),2); + + my $clone = $domain->clone( + name => new_domain_name + ,user => user_admin + ); + is($domain->is_base,1); + is(scalar($clone->list_volumes),2); + + $clone->start(user_admin); + $clone->shutdown_now(user_admin); + + is(scalar($clone->list_volumes),2); + + my $clone2 = $clone->clone( + name => new_domain_name + ,user => user_admin + ); + is($clone->is_base,0); + + $clone2->start(user_admin); + $clone2->shutdown_now(user_admin); + + is(scalar($clone2->list_volumes),2); + +} +#################################################################### + +clean(); +for my $vm_name ('Void','KVM') { + + my $vm; + eval { $vm = rvd_back->search_vm($vm_name) }; + + SKIP: { + my $msg = "SKIPPED test: No $vm_name VM found "; + if ($vm && $vm_name =~ /kvm/i && $>) { + $msg = "SKIPPED: Test must run as root"; + $vm = undef; + } + + diag($msg) if !$vm; + skip $msg,10 if !$vm; + + my $domain = test_domain_with_swap($vm_name); + } +} + +clean(); + +done_testing(); diff --git a/t/vm/c10_copy.t b/t/vm/c10_copy.t new file mode 100644 index 000000000..4ae8d396e --- /dev/null +++ b/t/vm/c10_copy.t @@ -0,0 +1,223 @@ +use warnings; +use strict; + +use Data::Dumper; +use Test::More; +use Test::SQL::Data; + +use lib 't/lib'; +use Test::Ravada; + +my $test = Test::SQL::Data->new(config => 't/etc/sql.conf'); +init($test->connector); + +use_ok('Ravada'); + +my $FILE_CONFIG = 't/etc/ravada.conf'; + +##########################################################################3 + +sub add_volumes { + my ($base, $volumes) = @_; + $base->add_volume_swap(name => "vol_swap", size => 512 * 1024); + for my $n ( 1 .. $volumes ) { + $base->add_volume(name => "vol_$n", size => 512 * 1024); + } +} + +sub test_copy_clone { + my $vm_name = shift; + my $volumes = shift; + + my $base = create_domain($vm_name); + + add_volumes($base, $volumes) if $volumes; + + my $name_clone = new_domain_name(); + + my $clone = $base->clone( + name => $name_clone + ,user => user_admin + ); + + is($clone->is_base,0); + for ( $clone->list_volumes ) { + open my $out,'>',$_ or die $!; + print $out "hola $_\n"; + close $out; + } + + my $name_copy = new_domain_name(); + my $copy = $clone->clone( + name => $name_copy + ,user => user_admin + ); + is($clone->is_base,0); + is($copy->is_base,0); + + is($copy->id_base, $base->id); + + is(scalar($copy->list_volumes),scalar($clone->list_volumes)); + + my @copy_volumes = $copy->list_volumes_target(); + my %copy_volumes = map { $_->[1] => $_->[0] } @copy_volumes; + my @clone_volumes = $clone->list_volumes_target(); + my %clone_volumes = map { $_->[1] => $_->[0] } @clone_volumes; + + for my $target ( keys %copy_volumes ) { + isnt($copy_volumes{$target}, $clone_volumes{$target}); + my @stat_copy = stat($copy_volumes{$target}); + my @stat_clone = stat($clone_volumes{$target}); + is($stat_copy[7],$stat_clone[7],"[$vm_name] size different " + ."\n$copy_volumes{$target} ".($stat_copy[7]) + ."\n$clone_volumes{$target} ".($stat_clone[7]) + ) or exit; + + } + $clone->remove(user_admin); + $copy->remove(user_admin); + $base->remove(user_admin); +} + +sub test_copy_request { + my $vm_name = shift; + + my $base = create_domain($vm_name); + my $memory = $base->get_info->{memory}; + + my $name_clone = new_domain_name(); + my $mem_clone = int($memory * 1.5); + + my $clone = $base->clone( + name => $name_clone + ,user => user_admin + ,memory => $mem_clone + ); + + is($clone->get_info->{memory}, $mem_clone,"[$vm_name] memory"); + + my $name_copy = new_domain_name(); + my $mem_copy = ($mem_clone * 1.5); + my $req; + + my $clone_mem = int ( $memory * 1.5); + eval { $req = Ravada::Request->clone( + id_domain => $clone->id + ,memory => $mem_copy + , name => $name_copy + , uid => user_admin->id + ); + }; + is($@,'') or return; + is($req->status(),'requested'); + rvd_back->_process_all_requests_dont_fork(); + + is($req->status(),'done'); + is($req->error,''); + + my $copy = rvd_back->search_domain($name_copy); + ok($copy,"[$vm_name] Expecting domain $name_copy"); + is($copy->get_info->{memory}, $mem_copy); + + my $clone2 = rvd_back->search_domain($name_clone); + is($clone2->is_base,0); + + is($clone2->get_info->{memory}, $mem_clone); + + isnt($clone2->get_info->{memory}, $base->get_info->{memory}); + isnt($clone2->get_info->{memory}, $copy->get_info->{memory}); +} + +sub test_copy_change_ram { + my $vm_name = shift; + + my $base = create_domain($vm_name); + + my $name_clone = new_domain_name(); + + my $clone = $base->clone( + name => $name_clone + ,user => user_admin + ); + my $clone_mem = $clone->get_info->{memory}; + + my $name_copy = new_domain_name(); + my $copy = $clone->clone( + name => $name_copy + ,memory => int($clone_mem * 1.5) + ,user => user_admin + ); + is($clone->is_base,0); + is($copy->is_base,0); + + is ($copy->get_info->{memory},int($clone_mem * 1.5),"[$vm_name] Expecting memory"); + $clone->remove(user_admin); + $copy->remove(user_admin); + $base->remove(user_admin); +} + +sub test_copy_req_nonbase { + my $vm_name = shift; + my $domain = create_domain($vm_name); + + my $name_copy = new_domain_name(); + + my $req; + eval { $req = Ravada::Request->clone( + id_domain => $domain->id + , name => $name_copy + , uid => user_admin->id + ); + }; + is($@,'') or return; + is($req->status(),'requested'); + rvd_back->_process_all_requests_dont_fork(); + is($req->status(),'done'); + is($req->error,''); + + my $copy = rvd_back->search_domain($name_copy); + ok($copy,"[$vm_name] Expecting domain $name_copy"); + + is($domain->is_base,1); + + $copy->remove(user_admin); + $domain->remove(user_admin); + +} + +##########################################################################3 + +clean(); + + +for my $vm_name ('Void', 'KVM') { + my $vm = rvd_back->search_vm($vm_name); + + SKIP: { + + my $msg = "SKIPPED: No virtual managers found"; + if ($vm && $vm_name =~ /kvm/i && $>) { + $msg = "SKIPPED: Test must run as root"; + $vm = undef; + } + + skip($msg,10) if !$vm; + + test_copy_clone($vm_name); + test_copy_clone($vm_name,1); + test_copy_clone($vm_name,2); + test_copy_clone($vm_name,10); + + test_copy_request($vm_name); + + test_copy_change_ram($vm_name); + + test_copy_req_nonbase($vm_name); + } + +} + +clean(); + +done_testing(); + diff --git a/t/vm/v10_volatile.t b/t/vm/v10_volatile.t new file mode 100644 index 000000000..d2f80a1e7 --- /dev/null +++ b/t/vm/v10_volatile.t @@ -0,0 +1,241 @@ +#!/usr/bin/perl +# test volatile anonymous domains kiosk mode + +use warnings; +use strict; + +use Carp qw(confess); +use Data::Dumper; +use POSIX qw(WNOHANG); +use Test::More; +use Test::SQL::Data; + +use_ok('Ravada'); +use_ok('Ravada::Network'); + +use lib 't/lib'; +use Test::Ravada; + +my $test = Test::SQL::Data->new(config => 't/etc/sql.conf'); +init($test->connector); + +my $IP = "10.0.0.1"; +my $NETWORK = $IP; +$NETWORK =~ s{(.*\.).*}{$1.0/24}; + +################################################################################ + +sub create_network { + + my $sth = $test->dbh->prepare( + "INSERT INTO networks (name, address) " + ." VALUES (?,?)" + ); + $sth->execute('foo',$NETWORK); + $sth->finish; +} + +sub delete_network { + my $sth = $test->dbh->prepare( + "DELETE FROM networks WHERE address=?" + ); + $sth->execute($NETWORK); + $sth->finish; +} + +sub id_network { + my $address = shift; + + my $sth = $test->dbh->prepare( + "SELECT id FROM networks WHERE address=?" + ); + $sth->execute($address); + my ($id) = $sth->fetchrow; + + return $id; +} + +sub allow_anonymous { + my $base = shift; + + my $id_network = id_network($NETWORK); + my $sth = $test->dbh->prepare( + "INSERT INTO domains_network " + ." (id_domain, id_network, anonymous )" + ." VALUES (?,?,?) " + ); + $sth->execute($base->id, $id_network, 1); + $sth->finish; +} + +sub test_volatile { + my ($vm_name, $base) = @_; + + my $vm = rvd_back->search_vm($vm_name); + my $name = new_domain_name(); + + { + my $user_name = "user_".new_domain_name(); + my $user = Ravada::Auth::SQL::add_user(name => $user_name, is_temporary => 1); + + my $clone = $base->clone( + user => $user + , name => $name + ); + is($clone->is_active,1,"[$vm_name] Expecting clone active"); + $clone->start($user) if !$clone->is_active; + + like($clone->spice_password,qr{..+}) if $vm_name eq 'KVM'; + + is($clone->is_volatile,1,"[$vm_name] Expecting is_volatile"); + + my $clone2 = rvd_back->search_domain($name); + is($clone2->is_volatile,1,"[$vm_name] Expecting is_volatile"); + + my $clone3 = $vm->search_domain($name); + is($clone3->is_volatile,1,"[$vm_name] Expecting is_volatile"); + + eval { $clone->shutdown_now(user_admin) if $clone->is_active}; + is(''.$@,'',"[$vm_name] Expecting no error after shutdown"); + + my $domain2 = $vm->search_domain($name); + ok(!$domain2,"[$vm_name] Expecting domain $name removed after shutdown") or exit; + + my $domain_f = rvd_front->search_domain($name); + ok(!$domain_f,"[$vm_name] Expecting domain removed after shutdown"); + + my $domain_b = rvd_back->search_domain($name); + ok(!$domain_b,"[$vm_name] Expecting domain removed after shutdown"); + + my $domains_f = rvd_front->list_domains(); + ok(!grep({ $_->{name} eq $name } @$domains_f),"[$vm_name] Expecting $name not listed"); + + $name = undef; + } + + # now a normal clone + my $name2 = new_domain_name(); + my $clone_normal = $base->clone( + user => user_admin, + name => $name2 + ); + + is($clone_normal->is_volatile,0,"[$vm_name] Expecting not volatile"); + + $clone_normal->shutdown_now(user_admin); + + my $domain_n2 = $vm->search_domain($name2); + ok($domain_n2,"[$vm_name] Expecting domain $name2 there after shutdown") or exit; + + my $domain_nf = rvd_front->search_domain($name2); + ok($domain_nf,"[$vm_name] Expecting domain there after shutdown"); + + my $domain_nb = rvd_back->search_domain($name2); + ok($domain_nb,"[$vm_name] Expecting domain there after shutdown"); + + my $domains_nf = rvd_front->list_domains(); + ok(grep({ $_->{name} eq $name2 } @$domains_nf),"[$vm_name] Expecting $name2 listed"); + + $clone_normal->remove(user_admin); +} + +# KVM volatiles get auto-removed +sub test_volatile_auto_kvm { + my ($vm_name, $base) = @_; + + my $name = new_domain_name(); + + my $user_name = "user_".new_domain_name(); + my $user = Ravada::Auth::SQL::add_user(name => $user_name, is_temporary => 1); + + my $clone = $base->clone( + user => $user + , name => $name + ); + is($clone->is_active,1,"[$vm_name] Expecting clone active"); + $clone->start($user) if !$clone->is_active; + + is($clone->is_volatile,1,"[$vm_name] Expecting is_volatile"); + is(''.$@,'',"[$vm_name] Expecting no error after shutdown"); + + my $spice_password = $clone->spice_password(); + like($spice_password,qr(..+)); + + $clone->domain->destroy(); + $clone=undef; + + my $vm = rvd_back->search_vm($vm_name); + my $domain2 = $vm->search_domain($name); + ok(!$domain2,"[$vm_name] Expecting domain $name removed after shutdown") or exit; + + my $domain_f = rvd_front->search_domain($name); + ok(!$domain_f,"[$vm_name] Expecting domain removed after shutdown"); + + my $domain_b = rvd_back->search_domain($name); + ok(!$domain_b,"[$vm_name] Expecting domain removed after shutdown"); + + my $domains_f = rvd_front->list_domains(); + ok(!grep({ $_->{name} eq $name } @$domains_f),"[$vm_name] Expecting $name not listed"); + + my $clone2; + eval { + $clone2 = $base->clone( + user => $user + ,name => $name + ); + }; + is(''.$@,'',"[$vm_name] Expecting clone called $name created"); + isnt($clone2->spice_password, $spice_password + ,"[$vm_name] Expecting spice password different") if $clone2; + + is($clone2->is_active,1,"[$vm_name] Expecting clone active"); + + my $clone3= $vm->search_domain($name); + ok($clone3,"[$vm_name] Expecting clone $name"); + + eval { $clone2->remove(user_admin) if $clone2 }; + is(''.$@,''); + + my $sth = $test->dbh->prepare("SELECT * FROM domains WHERE name=?"); + $sth->execute($name); + my $row = $sth->fetchrow_hashref; + is(keys(%$row),0); +} + +################################################################################ + +clean(); + + +for my $vm_name ('Void', 'KVM') { + my $vm = rvd_back->search_vm($vm_name); + + SKIP: { + + my $msg = "SKIPPED: No virtual managers found"; + if ($vm && $vm_name =~ /kvm/i && $>) { + $msg = "SKIPPED: Test must run as root"; + $vm = undef; + } + + skip($msg,10) if !$vm; + diag("Testing volatile for $vm_name"); + + create_network(); + + my $base= create_domain($vm_name); + $base->prepare_base(user_admin()); + $base->is_public(1); + allow_anonymous($base); + + test_volatile($vm_name, $base); + test_volatile_auto_kvm($vm_name, $base) if $vm_name eq'KVM'; + + delete_network(); + } + +} + +clean(); + +done_testing(); diff --git a/t/vm/z_101.t b/t/vm/z_101.t new file mode 100644 index 000000000..65ad41e63 --- /dev/null +++ b/t/vm/z_101.t @@ -0,0 +1,97 @@ +use warnings; +use strict; + +use Data::Dumper; +use Test::More; +use Test::SQL::Data; + +use lib 't/lib'; +use Test::Ravada; + +my $test = Test::SQL::Data->new(config => 't/etc/sql.conf'); + +use_ok('Ravada'); + +my $FILE_CONFIG = 't/etc/ravada.conf'; + +my $RVD_BACK = rvd_back($test->connector, $FILE_CONFIG); +my $RVD_FRONT= rvd_front($test->connector, $FILE_CONFIG); + +my %ARG_CREATE_DOM = ( + KVM => [ id_iso => 1 ] + ,Void => [ ] +); + +my @ARG_RVD = ( config => $FILE_CONFIG, connector => $test->connector); + +my @VMS = keys %ARG_CREATE_DOM; + +############################################################# + +clean(); + +for my $vm_name (reverse sort @VMS) { + + diag("Testing $vm_name VM"); + + my $vm; + + eval { $vm = rvd_back->search_vm($vm_name) }; + + SKIP: { + my $msg = "SKIPPED test: No $vm_name VM found "; + if ($vm && $vm_name =~ /kvm/i && $>) { + $msg = "SKIPPED: Test must run as root"; + $vm = undef; + } + + diag($msg) if !$vm; + skip $msg,10 if !$vm; + + my $domain = create_domain($vm_name); + my $t0 = time; + my $clone0; + + my $n_clones = 102; + for my $count ( 1 .. $n_clones ) { + my $name = new_domain_name(); + my $clone; + eval { + $clone = $domain->clone( + name => $name + ,user => user_admin + ); + }; + is(''.$@,'') or next; + ok($clone,"Expecting a clone from ".$domain->name) or next; + + eval { $clone->start(user_admin) }; + is(''.$@,''); + is($clone->is_active,1); + + if ($clone0 ) { + eval { $clone0->shutdown_now(user_admin) }; + is(''.$@,''); + is($clone0->is_active,0); + + if (time - $t0 > 5 ) { + $t0 = time; + diag("[$vm_name] testing clone $count of $n_clones ".$clone0->name); + } + } + $clone0 = $clone; + if ($clone->can_hybernate) { + eval { $clone->hybernate(user_admin) }; + is(''.$@,''); + is($clone->is_paused,1); + + eval { $clone->start(user_admin) }; + is(''.$@,''); + is($clone->is_active,1); + } + } + } +} + +clean(); +done_testing(); diff --git a/templates/bootstrap/header.html.ep b/templates/bootstrap/header.html.ep index c299e32ac..b9f42c163 100644 --- a/templates/bootstrap/header.html.ep +++ b/templates/bootstrap/header.html.ep @@ -9,7 +9,7 @@ % } Ravada VDI - + diff --git a/templates/bootstrap/scripts.html.ep b/templates/bootstrap/scripts.html.ep index 3f1d5eaf5..c848dc03b 100644 --- a/templates/bootstrap/scripts.html.ep +++ b/templates/bootstrap/scripts.html.ep @@ -1,6 +1,6 @@ - + diff --git a/templates/bootstrap/user_settings.html.ep b/templates/bootstrap/user_settings.html.ep index ae3d9595e..67d4148d8 100644 --- a/templates/bootstrap/user_settings.html.ep +++ b/templates/bootstrap/user_settings.html.ep @@ -7,7 +7,7 @@
-
+