Commit 6aeecf5a authored by Francesc Guasch's avatar Francesc Guasch
Browse files

Merge branch 'main' into refactor/grants2

parents df7cbd9c c5fdd43f
...@@ -4,7 +4,19 @@ ...@@ -4,7 +4,19 @@
- Machine schedule reservation [\#1337] - Machine schedule reservation [\#1337]
- Manage nodes and networks settings [\#1305] - Manage nodes and networks settings [\#1305]
- User management part in ravada [\#1500]
- Manage LDAP groups
- Limit to start virtual machines [\#1490]
- Add access filter to a machine with LDAP groups [\#1488]
- Debian 11 (Bullesye) ISO support [\#1580]
**Refactor**
- Confusing button placement in ports form [\#1469]
- Clones number sequence coherency [\#1454]
- Responsive grants form
**Bugfixes** **Bugfixes**
- Linux Mint ISO Download fails [\#1576]
- Machine in a node returns to KVMlocalhost when a machine option is modified [\#1440] - Machine in a node returns to KVMlocalhost when a machine option is modified [\#1440]
...@@ -18,6 +18,7 @@ my $DEBIAN = "DEBIAN"; ...@@ -18,6 +18,7 @@ my $DEBIAN = "DEBIAN";
my %COPY_RELEASES = ( my %COPY_RELEASES = (
'ubuntu-19.04'=> ['ubuntu-18.10','ubuntu-19.10'] 'ubuntu-19.04'=> ['ubuntu-18.10','ubuntu-19.10']
,'debian-10' => ['debian-11']
); );
my %DIR = ( my %DIR = (
templates => '/usr/share/ravada' templates => '/usr/share/ravada'
......
...@@ -567,7 +567,7 @@ sub _update_isos { ...@@ -567,7 +567,7 @@ sub _update_isos {
,debian_buster_64=> { ,debian_buster_64=> {
name =>'Debian Buster 64 bits' name =>'Debian Buster 64 bits'
,description => 'Debian 10 Buster 64 bits (XFCE desktop)' ,description => 'Debian 10 Buster 64 bits (XFCE desktop)'
,url => 'https://cdimage.debian.org/debian-cd/^10\..*\d$/amd64/iso-cd/' ,url => 'https://cdimage.debian.org/cdimage/archive/^10\..*\d$/amd64/iso-cd/'
,file_re => 'debian-10.[\d\.]+-amd64-xfce-CD-1.iso' ,file_re => 'debian-10.[\d\.]+-amd64-xfce-CD-1.iso'
,md5_url => '$url/MD5SUMS' ,md5_url => '$url/MD5SUMS'
,xml => 'jessie-amd64.xml' ,xml => 'jessie-amd64.xml'
...@@ -577,13 +577,34 @@ sub _update_isos { ...@@ -577,13 +577,34 @@ sub _update_isos {
,debian_buster_32=> { ,debian_buster_32=> {
name =>'Debian Buster 32 bits' name =>'Debian Buster 32 bits'
,description => 'Debian 10 Buster 32 bits (XFCE desktop)' ,description => 'Debian 10 Buster 32 bits (XFCE desktop)'
,url => 'https://cdimage.debian.org/debian-cd/^10\..*\d$/i386/iso-cd/' ,url => 'https://cdimage.debian.org/cdimage/archive/^10\..*\d$/i386/iso-cd/'
,file_re => 'debian-10.[\d\.]+-i386-(netinst|xfce-CD-1).iso' ,file_re => 'debian-10.[\d\.]+-i386-(netinst|xfce-CD-1).iso'
,md5_url => '$url/MD5SUMS' ,md5_url => '$url/MD5SUMS'
,xml => 'jessie-i386.xml' ,xml => 'jessie-i386.xml'
,xml_volume => 'jessie-volume.xml' ,xml_volume => 'jessie-volume.xml'
,min_disk_size => '10' ,min_disk_size => '10'
} }
,debian_bullseye_64=> {
name =>'Debian Bullseye 64 bits'
,description => 'Debian 11 Bullseye 64 bits (netinst)'
,url => 'https://cdimage.debian.org/debian-cd/^11\..*\d$/amd64/iso-cd/'
,file_re => 'debian-11.[\d\.]+-amd64-netinst.iso'
,sha256_url => '$url/SHA256SUMS'
,xml => 'jessie-amd64.xml'
,xml_volume => 'jessie-volume.xml'
,min_disk_size => '10'
}
,debian_bullseye_32=> {
name =>'Debian Bullseye 32 bits'
,description => 'Debian 10 Bullseye 32 bits (netinst)'
,url => 'https://cdimage.debian.org/debian-cd/^11\..*\d$/i386/iso-cd/'
,file_re => 'debian-11.[\d\.]+-i386-netinst.iso'
,sha256_url => '$url/SHA256SUMS'
,xml => 'jessie-i386.xml'
,xml_volume => 'jessie-volume.xml'
,min_disk_size => '10'
}
,kali_64 => { ,kali_64 => {
name => 'Kali Linux 2020' name => 'Kali Linux 2020'
,description => 'Kali Linux 2020 64 Bits' ,description => 'Kali Linux 2020 64 Bits'
...@@ -663,9 +684,29 @@ sub _update_isos { ...@@ -663,9 +684,29 @@ sub _update_isos {
); );
$self->_scheduled_fedora_releases(\%data) if $0 !~ /\.t$/; $self->_scheduled_fedora_releases(\%data) if $0 !~ /\.t$/;
$self->_update_table($table, $field, \%data); $self->_update_table($table, $field, \%data);
$self->_update_table_isos_url(\%data);
} }
sub _update_table_isos_url($self, $data) {
my $sth = $CONNECTOR->dbh->prepare("SELECT * FROM iso_images WHERE name=?");
for my $release (sort keys %$data) {
my $entry = $data->{$release};
$sth->execute($entry->{name});
my $row = $sth->fetchrow_hashref();
for my $field (keys %$entry) {
next if defined $row->{$field} && $row->{$field} eq $entry->{$field};
my $sth_update = $CONNECTOR->dbh->prepare(
"UPDATE iso_images SET $field=?"
." WHERE id=?"
);
$sth_update->execute($entry->{$field}, $row->{id});
warn("INFO: updating $release $field '".($row->{$field} or '')."' -> '$entry->{$field}'\n")
if !$FIRST_TIME_RUN && $0 !~ /\.t$/;
}
}
}
sub _scheduled_fedora_releases($self,$data) { sub _scheduled_fedora_releases($self,$data) {
return if !exists $VALID_VM{KVM} ||!$VALID_VM{KVM} || $>; return if !exists $VALID_VM{KVM} ||!$VALID_VM{KVM} || $>;
...@@ -1175,6 +1216,7 @@ sub _add_indexes_generic($self) { ...@@ -1175,6 +1216,7 @@ sub _add_indexes_generic($self) {
"unique(id_domain,n_order)" "unique(id_domain,n_order)"
,"unique(id_domain,driver)" ,"unique(id_domain,driver)"
,"unique(id_vm,port)" ,"unique(id_vm,port)"
,"index(id_domain)"
] ]
,domain_ports => [ ,domain_ports => [
"unique (id_domain,internal_port):domain_port" "unique (id_domain,internal_port):domain_port"
...@@ -1183,6 +1225,7 @@ sub _add_indexes_generic($self) { ...@@ -1183,6 +1225,7 @@ sub _add_indexes_generic($self) {
] ]
,group_access => [ ,group_access => [
"unique (id_domain,name)" "unique (id_domain,name)"
,"index(id_domain)"
] ]
,requests => [ ,requests => [
"index(status,at_time)" "index(status,at_time)"
...@@ -1213,17 +1256,23 @@ sub _add_indexes_generic($self) { ...@@ -1213,17 +1256,23 @@ sub _add_indexes_generic($self) {
] ]
,booking_entry_ldap_groups => [ ,booking_entry_ldap_groups => [
"index(id_booking_entry,ldap_group)" "index(id_booking_entry,ldap_group)"
,"index(id_booking_entry)"
] ]
,booking_entry_users => [ ,booking_entry_users => [
"index(id_booking_entry,id_user)" "index(id_booking_entry,id_user)"
,"index(id_booking_entry)"
,"index(id_user)"
] ]
,booking_entry_bases => [ ,booking_entry_bases => [
"index(id_booking_entry,id_base)" "index(id_booking_entry,id_base)"
,"index(id_base)"
,"index(id_booking_entry)"
] ]
,volumes => [ ,volumes => [
'UNIQUE (id_domain,name):id_domain', "index(id_domain)"
'UNIQUE (id_domain,n_order):id_domain2' ,'UNIQUE (id_domain,name):id_domain_name'
,'UNIQUE (id_domain,n_order):id_domain2'
] ]
,vms=> [ ,vms=> [
...@@ -3109,14 +3158,17 @@ sub process_requests { ...@@ -3109,14 +3158,17 @@ sub process_requests {
next if $request_type ne 'all' && $req->type ne $request_type; next if $request_type ne 'all' && $req->type ne $request_type;
next if $duplicated{$id_request}++; next if $duplicated{"id_req.$id_request"}++;
next if $req->command !~ /shutdown/i next if $req->command !~ /shutdown/i
&& $self->_domain_working($id_domain, $id_request); && $self->_domain_working($id_domain, $id_request);
my $domain = ''; my $domain = '';
$domain = $id_domain if $id_domain; $domain = $id_domain if $id_domain;
$domain .= ($req->defined_arg('name') or ''); $domain .= ($req->defined_arg('name') or '');
next if $duplicated{$req->command.":$domain"}++; next if $domain && $duplicated{$domain};
my $id_base = $req->defined_arg('id_base');
next if $id_base && $duplicated{$id_base};
$duplicated{"domain.$domain"}++;
push @reqs,($req); push @reqs,($req);
} }
$sth->finish; $sth->finish;
...@@ -3279,6 +3331,7 @@ sub _kill_stale_process($self) { ...@@ -3279,6 +3331,7 @@ sub _kill_stale_process($self) {
." WHERE start_time<? " ." WHERE start_time<? "
." AND ( command = 'refresh_vms' or command = 'screenshot' or command = 'set_time' " ." AND ( command = 'refresh_vms' or command = 'screenshot' or command = 'set_time' "
." OR command = 'open_exposed_ports' OR command='remove' " ." OR command = 'open_exposed_ports' OR command='remove' "
." OR command = 'refresh_machine_ports'"
.") " .") "
." AND status <> 'done' " ." AND status <> 'done' "
." AND start_time IS NOT NULL " ." AND start_time IS NOT NULL "
...@@ -4442,7 +4495,7 @@ sub _cmd_refresh_machine($self, $request) { ...@@ -4442,7 +4495,7 @@ sub _cmd_refresh_machine($self, $request) {
$domain->client_status(1) if $is_active; $domain->client_status(1) if $is_active;
Ravada::Request->refresh_machine_ports(id_domain => $domain->id, uid => $user->id Ravada::Request->refresh_machine_ports(id_domain => $domain->id, uid => $user->id
,retry => 20) ,timeout => 60, retry => 20)
if $is_active && $domain->ip; if $is_active && $domain->ip;
} }
...@@ -4841,7 +4894,11 @@ sub _check_duplicated_iptable($self, $request = undef ) { ...@@ -4841,7 +4894,11 @@ sub _check_duplicated_iptable($self, $request = undef ) {
my $rule = join(" ", map { $_." ".$args{$_} } sort keys %args); my $rule = join(" ", map { $_." ".$args{$_} } sort keys %args);
if ($dupe{$rule}) { if ($dupe{$rule}) {
warn "clean duplicated iptables rule ".Dumper($line); my %args2;
while (my ($key, $value) = each %args) {
$args2{"-$key"} = $value;
}
warn "clean duplicated iptables rule ".join(" ",%args2)."\n";
$self->_delete_iptables_rule($vm,'filter', \%args); $self->_delete_iptables_rule($vm,'filter', \%args);
} }
$dupe{$rule}++; $dupe{$rule}++;
......
...@@ -878,10 +878,12 @@ sub grant($self,$user,$permission,$value=1) { ...@@ -878,10 +878,12 @@ sub grant($self,$user,$permission,$value=1) {
.Dumper(\@perms); .Dumper(\@perms);
} }
if ( $value eq 'false' || !$value ) { if ( $self->grant_type($permission) eq 'boolean' ) {
$value = 0; if ($value eq 'false' || !$value ) {
} else { $value = 0;
$value = 1; } else {
$value = 1;
}
} }
return 0 if !$value && !$user->can_do($permission); return 0 if !$value && !$user->can_do($permission);
...@@ -955,6 +957,7 @@ sub list_all_permissions($self) { ...@@ -955,6 +957,7 @@ sub list_all_permissions($self) {
} }
sub grant_type($self, $permission) { sub grant_type($self, $permission) {
return 'boolean' if !exists $self->{_grant_type}->{$permission};
return $self->{_grant_type}->{$permission}; return $self->{_grant_type}->{$permission};
} }
......
...@@ -7,8 +7,8 @@ msgstr "" ...@@ -7,8 +7,8 @@ msgstr ""
"Project-Id-Version: 0.1.0-alpha\n" "Project-Id-Version: 0.1.0-alpha\n"
"Report-Msgid-Bugs-To: \n" "Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2017-01-03 10:12+0300\n" "POT-Creation-Date: 2017-01-03 10:12+0300\n"
"PO-Revision-Date: 2021-05-26 10:57+0000\n" "PO-Revision-Date: 2021-08-17 21:34+0000\n"
"Last-Translator: Barış Güvenkaya <bguvenkaya@tutanota.de>\n" "Last-Translator: Oğuz Ersen <oguzersen@protonmail.com>\n"
"Language-Team: Turkish <https://hosted.weblate.org/projects/ravada/" "Language-Team: Turkish <https://hosted.weblate.org/projects/ravada/"
"translation/tr/>\n" "translation/tr/>\n"
"Language: tr\n" "Language: tr\n"
...@@ -16,7 +16,7 @@ msgstr "" ...@@ -16,7 +16,7 @@ msgstr ""
"Content-Type: text/plain; charset=UTF-8\n" "Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n" "Content-Transfer-Encoding: 8bit\n"
"Plural-Forms: nplurals=2; plural=n > 1;\n" "Plural-Forms: nplurals=2; plural=n > 1;\n"
"X-Generator: Weblate 4.7-dev\n" "X-Generator: Weblate 4.8-dev\n"
msgid "Ravada broker" msgid "Ravada broker"
msgstr "Ravada Aracı" msgstr "Ravada Aracı"
...@@ -58,7 +58,7 @@ msgid "Log Out" ...@@ -58,7 +58,7 @@ msgid "Log Out"
msgstr "Oturumu Kapat" msgstr "Oturumu Kapat"
msgid "Available Machines" msgid "Available Machines"
msgstr "Hazır Makineler" msgstr "Kullanılabilir Makineler"
msgid "Help" msgid "Help"
msgstr "Yardım" msgstr "Yardım"
...@@ -79,13 +79,13 @@ msgid "New Machine" ...@@ -79,13 +79,13 @@ msgid "New Machine"
msgstr "Yeni Makine" msgstr "Yeni Makine"
msgid "I want to change my password" msgid "I want to change my password"
msgstr "Şifremi değiştirmek istiyorum" msgstr "Parolamı değiştirmek istiyorum"
msgid "I want to change my language" msgid "I want to change my language"
msgstr "Lisanımı değiştirmek istiyorum" msgstr "Dilimi değiştirmek istiyorum"
msgid "Language:" msgid "Language:"
msgstr "Lisan" msgstr "Dil:"
msgid "English" msgid "English"
msgstr "İngilizce" msgstr "İngilizce"
...@@ -118,10 +118,10 @@ msgid "German" ...@@ -118,10 +118,10 @@ msgid "German"
msgstr "Almanca" msgstr "Almanca"
msgid "New Password:" msgid "New Password:"
msgstr "Yeni Şifre" msgstr "Yeni Parola:"
msgid "Confirm Password:" msgid "Confirm Password:"
msgstr "Şifreyi Onayla" msgstr "Parolayı Doğrula:"
msgid "Submit" msgid "Submit"
msgstr "Gönder" msgstr "Gönder"
...@@ -130,16 +130,16 @@ msgid "Your language has been changed successfully" ...@@ -130,16 +130,16 @@ msgid "Your language has been changed successfully"
msgstr "Diliniz başarılı bir şekilde değiştirildi" msgstr "Diliniz başarılı bir şekilde değiştirildi"
msgid "Your password has been changed successfully" msgid "Your password has been changed successfully"
msgstr "Şifreniz başarılı bir şekilde değiştrildi" msgstr "Parolanız başarılı bir şekilde değiştirildi"
msgid "Password is too short" msgid "Password is too short"
msgstr "Şifre çok kısa" msgstr "Parola çok kısa"
msgid "Password fields do not match" msgid "Password fields do not match"
msgstr "Şifre alanları eşleşmiyor" msgstr "Parola alanları eşleşmiyor"
msgid "Some of the password fields are empty" msgid "Some of the password fields are empty"
msgstr "Şifre alanlarından bazıları boş" msgstr "Parola alanlarından bazıları boş"
msgid "Status" msgid "Status"
msgstr "Durum" msgstr "Durum"
...@@ -190,7 +190,7 @@ msgid "Mark as read" ...@@ -190,7 +190,7 @@ msgid "Mark as read"
msgstr "Okundu olarak işaretle" msgstr "Okundu olarak işaretle"
msgid "Mark as unread" msgid "Mark as unread"
msgstr "Okunmamış olarak işaretle" msgstr "Okunma olarak işaretle"
msgid "No messages to show" msgid "No messages to show"
msgstr "Gösterilecek mesaj yok" msgstr "Gösterilecek mesaj yok"
...@@ -223,7 +223,7 @@ msgid "Remove base" ...@@ -223,7 +223,7 @@ msgid "Remove base"
msgstr "Tabanı kaldır" msgstr "Tabanı kaldır"
msgid "Restore" msgid "Restore"
msgstr "Yeniden Yükle" msgstr "Geri yükle"
msgid "Hibernate" msgid "Hibernate"
msgstr "Hazırda beklet" msgstr "Hazırda beklet"
...@@ -244,7 +244,7 @@ msgid "Machine settings" ...@@ -244,7 +244,7 @@ msgid "Machine settings"
msgstr "Makine ayarları" msgstr "Makine ayarları"
msgid "Cannot remove base, machine has clones" msgid "Cannot remove base, machine has clones"
msgstr "Taban kaldırılamıyor, makinede klonlar mevcut" msgstr "Taban kaldırılamıyor, makinede klonlar var"
msgid "show clones" msgid "show clones"
msgstr "klonları göster" msgstr "klonları göster"
...@@ -253,19 +253,19 @@ msgid "hide clones" ...@@ -253,19 +253,19 @@ msgid "hide clones"
msgstr "klonları gizle" msgstr "klonları gizle"
msgid "For Spice redirection you will need to install" msgid "For Spice redirection you will need to install"
msgstr "Spice yönlendirmesi için yüklemeniz gerekecek" msgstr "Spice yönlendirmesi için kurmanız gerekecek"
msgid "in your computer." msgid "in your computer."
msgstr "bilgisayarınızda" msgstr "bilgisayarınızda."
msgid "Search in your distro, e.g. in Debian/Ubuntu with" msgid "Search in your distro, e.g. in Debian/Ubuntu with"
msgstr "Dağıtımınızda arayın, örneğin Debian/Ubuntu" msgstr "Dağıtımınızda arayın, örneğin Debian/Ubuntu"
msgid "You will need to install" msgid "You will need to install"
msgstr "Yüklemeniz gerekecek" msgstr "Kurmanız gerekecek"
msgid "and USB drivers (" msgid "and USB drivers ("
msgstr "ve USB sürücüleri(" msgstr "ve USB sürücüleri ("
msgid "" msgid ""
"Be aware that in Windows, Spice redirection is not automatic. It is " "Be aware that in Windows, Spice redirection is not automatic. It is "
...@@ -285,7 +285,7 @@ msgstr "" ...@@ -285,7 +285,7 @@ msgstr ""
"olarak kaydedin, ardından dosyayı çalıştırın." "olarak kaydedin, ardından dosyayı çalıştırın."
msgid "Login" msgid "Login"
msgstr "Giriş" msgstr "Oturum Aç"
msgid "Welcome" msgid "Welcome"
msgstr "Hoş Geldiniz" msgstr "Hoş Geldiniz"
...@@ -303,40 +303,40 @@ msgid "User" ...@@ -303,40 +303,40 @@ msgid "User"
msgstr "Kullanıcı" msgstr "Kullanıcı"
msgid "Password" msgid "Password"
msgstr "Şifre" msgstr "Parola"
msgid "Start session" msgid "Start session"
msgstr "oturumu başlat" msgstr "Oturumu başlat\n"
msgid "bits) in your computer." msgid "bits) in your computer."
msgstr "bit) bilgisayarınızda" msgstr "bit) bilgisayarınızda."
msgid "Permissions" msgid "Permissions"
msgstr "İzinler" msgstr "İzinler"
msgid "can change the settings of owned virtual machines." msgid "can change the settings of owned virtual machines."
msgstr "Kendi sanal makinelerin ayarlarını değiştirebilir." msgstr "sahip olunan sanal makinelerin ayarlarını değiştirebilir."
msgid "can change the settings of any virtual machines." msgid "can change the settings of any virtual machines."
msgstr "Herhangi bir sanal makinenin ayarlarını değiştirebilir." msgstr "herhangi bir sanal makinenin ayarlarını değiştirebilir."
msgid "can change the settings of any virtual machines " msgid "can change the settings of any virtual machines "
msgstr "Herhangi bir sanal makinenin ayarlarını değiştirebilir" msgstr "Herhangi bir sanal makinenin ayarlarını değiştirebilir"
msgid "cloned from one base owned by the user." msgid "cloned from one base owned by the user."
msgstr "sahip olunan bir tabandan klonlanmış." msgstr "kullanıcının sahip olduğu bir tabandan kopyalandı."
msgid "can clone public virtual machines." msgid "can clone public virtual machines."
msgstr "genel sanal makineleri klonlayabilir." msgstr "genel sanal makineleri klonlayabilir."
msgid "can clone any virtual machine." msgid "can clone any virtual machine."
msgstr "herhangi bir sanal makineyi klonlayabilir" msgstr "herhangi bir sanal makineyi klonlayabilir."
msgid "can create bases." msgid "can create bases."
msgstr "tabanlar oluşturabilir." msgstr "tabanlar oluşturabilir."
msgid "can create virtual machines." msgid "can create virtual machines."
msgstr "sanal makineler oluşturulabilir" msgstr "sanal makineler oluşturulabilir."
msgid "can grant permissions to other users" msgid "can grant permissions to other users"
msgstr "diğer kullanıcılara izin verebilir" msgstr "diğer kullanıcılara izin verebilir"
...@@ -346,36 +346,36 @@ msgstr "herhangi bir sanal makineyi hazırda bekletebilir." ...@@ -346,36 +346,36 @@ msgstr "herhangi bir sanal makineyi hazırda bekletebilir."
msgid "can hibernate clones from virtual machines owned by the user." msgid "can hibernate clones from virtual machines owned by the user."
msgstr "" msgstr ""
"Klonları, kullanıcının sahip olduğu sanal makinelerden hazırda bırakabilir." "kullanıcının sahip olduğu sanal makinelerden klonları hazırda bekletebilir."
msgid "can hibernate any clone." msgid "can hibernate any clone."
msgstr "herhangi bir klonu hazırda bekletebilir." msgstr "herhangi bir klonu hazırda bekletebilir."
msgid "can remove any virtual machines owned by the user." msgid "can remove any virtual machines owned by the user."
msgstr "Kullanıcının sahip olduğu herhangi bir sanal makineyi silebilir." msgstr "kullanıcının sahip olduğu herhangi bir sanal makineyi kaldırabilir."
msgid "can remove any virtual machine." msgid "can remove any virtual machine."
msgstr "herhangi bir sanal makineyi silebilir." msgstr "herhangi bir sanal makineyi kaldırabilir."
msgid "can remove clones from virtual machines owned by the user." msgid "can remove clones from virtual machines owned by the user."
msgstr "Kullanıcının sahip olduğu sanal makinelerden klonları silebilir." msgstr "kullanıcının sahip olduğu sanal makinelerden klonları kaldırabilir."
msgid "can remove any clone." msgid "can remove any clone."
msgstr "Herhangi bir klonu kaldırabilir." msgstr "herhangi bir klonu kaldırabilir."
msgid "can take a screenshot of any virtual machine owned by the user." msgid "can take a screenshot of any virtual machine owned by the user."
msgstr "" msgstr ""
"Kullanıcının sahip olduğu herhangi bir sanal makinenin ekran görüntüsünü " "kullanıcının sahip olduğu herhangi bir sanal makinenin ekran görüntüsünü "
"alabilir." "alabilir."
msgid "can take a screenshot of any virtual machine." msgid "can take a screenshot of any virtual machine."
msgstr "Herhangi bir sanal makinenin ekran görüntüsünü alabilir." msgstr "herhangi bir sanal makinenin ekran görüntüsünü alabilir."
msgid "can shutdown any virtual machine." msgid "can shutdown any virtual machine."
msgstr "Herhangi bir sanal makineyi kapatabilir." msgstr "herhangi bir sanal makineyi kapatabilir."
msgid "can shutdown clones from virtual machines owned by the user." msgid "can shutdown clones from virtual machines owned by the user."
msgstr "Klonları, kullanıcının sahip olduğu sanal makinelerden kapatabilir." msgstr "kullanıcının sahip olduğu sanal makinelerden klonları kapatabilir."