From 87693a368332c7e60541cc5c174ff5a3cdf27108 Mon Sep 17 00:00:00 2001 From: Francesc Guasch Date: Wed, 28 Jun 2023 12:31:39 +0200 Subject: [PATCH 01/38] wip: update hw info after change --- public/js/ravada.js | 15 +++++++++++---- 1 file changed, 11 insertions(+), 4 deletions(-) diff --git a/public/js/ravada.js b/public/js/ravada.js index 7133a2bb1..ea7da1136 100644 --- a/public/js/ravada.js +++ b/public/js/ravada.js @@ -427,6 +427,7 @@ } if ($scope.edit) { $scope.lock_info = true } $scope.topology_changed(); + update_info_settings(); }); _select_new_base(); } @@ -545,6 +546,13 @@ }; var url_ws; + + var update_info_settings = function() { + $scope.new_n_virt_cpu= $scope.showmachine.n_virt_cpu; + $scope.new_memory = ($scope.showmachine.memory / 1024); + $scope.new_max_mem = ($scope.showmachine.max_mem / 1024); + }; + $scope.init = function(id, url,is_admin) { url_ws = url; $scope.showmachineId=id; @@ -561,10 +569,7 @@ if (typeof $scope.new_name == 'undefined' ) { $scope.new_name=$scope.showmachine.alias+"-2"; $scope.validate_new_name($scope.showmachine.alias); - $scope.new_n_virt_cpu= $scope.showmachine.n_virt_cpu; - $scope.new_memory = ($scope.showmachine.memory / 1024); - $scope.new_max_mem = ($scope.showmachine.max_mem / 1024); - + update_info_settings(); $scope.new_run_timeout = ($scope.showmachine.run_timeout / 60); if (!$scope.new_run_timeout) $scope.new_run_timeout = undefined; @@ -1103,6 +1108,8 @@ $scope.request = function(request, args) { $scope.showmachine.requests++; $scope.pending_request = undefined; + $scope.lock_info = false; + $scope.edit = ''; $http.post('/request/'+request+'/' ,JSON.stringify(args) ).then(function(response) { From b107795fe465c7999e33ff80c3c5594370a13fbf Mon Sep 17 00:00:00 2001 From: Francesc Guasch Date: Wed, 28 Jun 2023 13:35:45 +0200 Subject: [PATCH 02/38] test: change CPU settings --- t/request/33_hw_cpu.t | 89 +++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 89 insertions(+) diff --git a/t/request/33_hw_cpu.t b/t/request/33_hw_cpu.t index 2cf02e61b..da280b25f 100644 --- a/t/request/33_hw_cpu.t +++ b/t/request/33_hw_cpu.t @@ -198,6 +198,92 @@ sub test_add_threads($domain) { wait_request(); } +sub _custom_cpu_susana($domain) { + + my $xml = XML::LibXML->load_xml(string => $domain->domain->get_xml_description); + my ($vcpu) = $xml->findnodes("/domain/vcpu"); + $vcpu->setAttribute('placement' => 'static'); + $vcpu->setAttribute('current' => '2'); + + my ($vcpu_max) = $xml->findnodes("/domain/vcpu/text()"); + $vcpu_max->setData('4'); + + my ($cpu) = $xml->findnodes("/domain/cpu"); + my %data = ( mode => 'custom' , match => 'exact', check => 'partial'); + for my $field( keys %data) { + $cpu->setAttribute( $field => $data{$field}); + } + $cpu->removeChildNodes(); + my $model = $cpu->addNewChild(undef,'model'); + $model->setAttribute('fallback' => 'allow'); + $model->appendText('qemu64'); + + $domain->reload_config($xml); + +} + +sub test_change_vcpu_topo($vm) { + return if $vm->type ne 'KVM'; + + my $domain = create_domain($vm); + _custom_cpu_susana($domain); + + my %data = ( + 'hardware' => 'cpu', + 'id_domain' => $domain->id, + 'uid' => user_admin->id, + 'index' => 0, + 'data' => { + '_can_edit' => 1, + 'vcpu' => { + 'placement' => 'static', + '#text' =>undef + }, + '_cat_remove' => 0, + '_order' => 0, + 'cpu' => { + 'model' => { + '#text' => 'qemu64', + 'fallback' => 'allow' + }, + 'check' => 'partial', + 'match' => 'exact', + 'mode' => 'custom', + 'topology' => { + 'sockets' => 2 + }, + 'feature' => [ + { + 'name' => 'svm', + '$$hashKey' => 'object:64', + 'policy' => 'optional' + } + ] + } + } + ); + my $req = Ravada::Request->change_hardware(%data); + wait_request(); + + my $domain2 = Ravada::Front::Domain->open($domain->id); + my $info = $domain2->info(user_admin); + is($info->{n_virt_cpu},2); + is($info->{hardware}->{cpu}->[0]->{vcpu}->{'#text'},2); + + delete $data{data}->{cpu}->{topology}; + $data{data}->{vcpu}->{'#text'}=3; + + Ravada::Request->change_hardware(%data); + wait_request(); + + my $domain3 = Ravada::Front::Domain->open($domain->id); + my $info3 = $domain3->info(user_admin); + is($info3->{n_virt_cpu},3); + is($info3->{hardware}->{cpu}->[0]->{vcpu}->{'#text'},3); + + $domain->remove(user_admin); +} + ############################################################################### clean(); @@ -217,6 +303,9 @@ for my $vm_name ( vm_names() ) { } skip($msg,10) if !$vm; + + test_change_vcpu_topo($vm); + my $domain = create_domain($vm); test_add_vcpu($domain); test_less_vcpu_down($domain); From 0d536dff384bf8be95ee0f1d932531ca7ddfcd03 Mon Sep 17 00:00:00 2001 From: Francesc Guasch Date: Mon, 3 Jul 2023 14:39:30 +0200 Subject: [PATCH 03/38] wip: change current CPU --- lib/Ravada/Domain/KVM.pm | 15 +++++++++++++-- t/request/33_hw_cpu.t | 24 ++++++++++++++++++++++-- 2 files changed, 35 insertions(+), 4 deletions(-) diff --git a/lib/Ravada/Domain/KVM.pm b/lib/Ravada/Domain/KVM.pm index d42c912a4..d57a7140c 100644 --- a/lib/Ravada/Domain/KVM.pm +++ b/lib/Ravada/Domain/KVM.pm @@ -2996,8 +2996,11 @@ sub _change_hardware_vcpus($self, $index, $data) { my ($topology) = $cpu->findnodes('topology'); $cpu->removeChild($topology) if $topology; - my ($vcpus) = ($doc->findnodes('/domain/vcpu/text()')); - $vcpus->setData($n_virt_cpu); + my ($vcpus_max) = ($doc->findnodes('/domain/vcpu/text()')); + $vcpus_max->setData($n_virt_cpu); + + my ($vcpus) = ($doc->findnodes('/domain/vcpu')); + $vcpus->setAttribute('current' => $n_virt_cpu); $self->reload_config($doc); } @@ -3203,11 +3206,19 @@ sub _change_hardware_cpu($self, $index, $data) { if (exists $data->{vcpu} && $n_vcpu ne $data->{vcpu}->{'#text'}) { $vcpu->removeChildNodes(); $vcpu->appendText($data->{vcpu}->{'#text'}); + $changed++; + } + + if (exists $data->{vcpu} && defined $vcpu->getAttribute('current') + && $vcpu->getAttribute('current') ne $data->{vcpu}->{'#text'}) { + $vcpu->setAttribute('current' => $data->{vcpu}->{'#text'}); + $changed++; } my ($domain) = $doc->findnodes('/domain'); my ($cpu) = $doc->findnodes('/domain/cpu'); if (!$cpu) { $cpu = $domain->addNewChild(undef,'cpu'); + $changed++; } my $feature = delete $data->{cpu}->{feature}; diff --git a/t/request/33_hw_cpu.t b/t/request/33_hw_cpu.t index da280b25f..4e28975d5 100644 --- a/t/request/33_hw_cpu.t +++ b/t/request/33_hw_cpu.t @@ -270,17 +270,37 @@ sub test_change_vcpu_topo($vm) { is($info->{n_virt_cpu},2); is($info->{hardware}->{cpu}->[0]->{vcpu}->{'#text'},2); + _custom_cpu_susana($domain); delete $data{data}->{cpu}->{topology}; $data{data}->{vcpu}->{'#text'}=3; Ravada::Request->change_hardware(%data); - wait_request(); + wait_request(debug => 0); my $domain3 = Ravada::Front::Domain->open($domain->id); my $info3 = $domain3->info(user_admin); - is($info3->{n_virt_cpu},3); + is($info3->{n_virt_cpu},3) or die $domain3->name; + is($info3->{hardware}->{cpu}->[0]->{vcpu}->{'#text'},3); + _custom_cpu_susana($domain); + + $data{data}->{n_virt_cpu} = 5; + $data{hardware} = 'vcpus'; + delete $data{data}->{vcpu}; + delete $data{data}->{cpu}; + + my $req3 = Ravada::Request->change_hardware(%data); + wait_request(debug => 0); + + my $domain4 = Ravada::Front::Domain->open($domain->id); + my $info4 = $domain4->info(user_admin); + is($info4->{n_virt_cpu},5); + is($info4->{hardware}->{cpu}->[0]->{vcpu}->{'#text'},5); + + my $xml = XML::LibXML->load_xml(string => $domain->domain->get_xml_description); + my ($cpu) = $xml->findnodes("/domain/vcpu"); + $domain->remove(user_admin); } From 09af343501db039dd59894658ed707f239e19713 Mon Sep 17 00:00:00 2001 From: Francesc Guasch Date: Mon, 3 Jul 2023 16:54:29 +0200 Subject: [PATCH 04/38] wip: vcpus manage if need restart --- lib/Ravada/Domain.pm | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/Ravada/Domain.pm b/lib/Ravada/Domain.pm index d2b8e98ba..3890780d0 100644 --- a/lib/Ravada/Domain.pm +++ b/lib/Ravada/Domain.pm @@ -5685,7 +5685,7 @@ sub _post_change_hardware($self, $hardware, $index, $data=undef) { } $self->info(Ravada::Utils->user_daemon) if $self->is_known(); - $self->needs_restart(1) if $self->is_known && $self->_data('status') eq 'active' && $hardware ne 'memory'; + $self->needs_restart(1) if $self->is_known && $self->_data('status') eq 'active' && $hardware ne 'memory' && $hardware !~ /vcpus/; $self->post_prepare_base() if $self->is_base(); } From 14271afbea790b9809e38fbb820bf336bb93f80d Mon Sep 17 00:00:00 2001 From: Francesc Guasch Date: Tue, 4 Jul 2023 13:27:18 +0200 Subject: [PATCH 05/38] refactor: improved re-start --- lib/Ravada.pm | 46 ++++++++++++++++++++++++++++ lib/Ravada/Domain.pm | 3 +- lib/Ravada/Request.pm | 2 ++ public/js/ravada.js | 4 +-- script/rvd_front | 15 +++------ t/vm/10_domain.t | 23 ++++++++++++-- templates/main/needs_restart.html.ep | 2 +- 7 files changed, 79 insertions(+), 16 deletions(-) diff --git a/lib/Ravada.pm b/lib/Ravada.pm index 46f1affc6..b7f06a0a6 100644 --- a/lib/Ravada.pm +++ b/lib/Ravada.pm @@ -5157,6 +5157,51 @@ sub _cmd_reboot { } +sub _cmd_shutdown_start($self, $request) { + my $uid = $request->args('uid'); + my $id_domain = $request->args('id_domain'); + my $id_vm = $request->defined_arg('id_vm'); + + my $domain; + if ($id_vm) { + my $vm = Ravada::VM->open($id_vm); + $domain = $vm->search_domain_by_id($id_domain); + } else { + $domain = $self->search_domain_by_id($id_domain); + } + die "Unknown domain '$id_domain'\n" if !$domain; + + my $user = Ravada::Auth::SQL->search_by_id( $uid); + + die "USER $uid not authorized to restart machine ".$domain->name + unless $domain->_data('id_owner') == $user->id || $user->is_operator; + + my $timeout = ($request->defined_arg('timeout') or $domain->_timeout_shutdown() or 60); + + for my $try ( 0 .. 1 ) { + $domain->shutdown(timeout => $timeout, user => $user + , request => $request); + + for ( 0 .. $timeout+1 ) { + last if !$domain->is_active; + sleep 1; + } + last if !$domain->is_active; + } + + my $req_shutdown = Ravada::Request->force_shutdown_domain( + uid => $user->id + ,id_domain => $domain->id + ,after_request => $request->id + ); + + Ravada::Request->start_domain( + uid => $user->id + ,id_domain => $domain->id + ,after_request => $req_shutdown->id + ); +} + sub _cmd_force_reboot { my $self = shift; my $request = shift; @@ -6098,6 +6143,7 @@ sub _req_method { ,enforce_limits => \&_cmd_enforce_limits ,force_shutdown => \&_cmd_force_shutdown ,force_reboot => \&_cmd_force_reboot +,shutdown_start => \&_cmd_shutdown_start ,rebase => \&_cmd_rebase ,refresh_storage => \&_cmd_refresh_storage diff --git a/lib/Ravada/Domain.pm b/lib/Ravada/Domain.pm index 3890780d0..d71c3b0d2 100644 --- a/lib/Ravada/Domain.pm +++ b/lib/Ravada/Domain.pm @@ -4018,7 +4018,7 @@ sub _post_resume { return $self->_post_start(@_); } -sub _timeout_shutdown($self, $value) { +sub _timeout_shutdown($self, $value=undef) { $TIMEOUT_SHUTDOWN = $value if defined $value; return $TIMEOUT_SHUTDOWN; } @@ -5662,6 +5662,7 @@ hardware change can be applied. =cut sub needs_restart($self, $value=undef) { + return $self->_data('needs_restart') if !defined $value; return $self->_data('needs_restart',$value); } diff --git a/lib/Ravada/Request.pm b/lib/Ravada/Request.pm index 890b63fc8..4b4ecef6b 100644 --- a/lib/Ravada/Request.pm +++ b/lib/Ravada/Request.pm @@ -74,6 +74,8 @@ our %VALID_ARG = ( ,reboot_domain => { name => 2, id_domain => 2, uid => 1, timeout => 2, at => 2 , id_vm => 2 } ,force_reboot_domain => { id_domain => 1, uid => 1, at => 2, id_vm => 2 } + ,shutdown_start =>{ name => 2, id_domain => 2, uid => 1, timeout => 2 + , at => 2 , id_vm => 2 } ,screenshot => { id_domain => 1 } ,domain_autostart => { id_domain => 1 , uid => 1, value => 2 } ,copy_screenshot => { id_domain => 1 } diff --git a/public/js/ravada.js b/public/js/ravada.js index ea7da1136..fce7281a5 100644 --- a/public/js/ravada.js +++ b/public/js/ravada.js @@ -1158,8 +1158,8 @@ }); }; - $scope.reboot = function() { - $http.get("/machine/stop_start/"+$scope.showmachine.id+".json") + $scope.shutdown_start = function() { + $http.get("/machine/shutdown_start/"+$scope.showmachine.id+".json") .then(function(response) { }); }; diff --git a/script/rvd_front b/script/rvd_front index a57855ccf..de6f2e20a 100644 --- a/script/rvd_front +++ b/script/rvd_front @@ -898,9 +898,9 @@ get '/machine/reboot/(:id).(:type)' => sub { return reboot_machine($c); }; -get '/machine/stop_start/(:id).(:type)' => sub($c) { +get '/machine/shutdown_start/(:id).(:type)' => sub($c) { return access_denied($c) if !$USER ->can_reboot($c->stash('id')); - return stop_start($c); + return shutdown_start($c); }; @@ -3376,20 +3376,15 @@ sub reboot_machine { return $c->render(json => { req => $req->id }); } -sub stop_start($c) { +sub shutdown_start($c) { return login($c) if !_logged_in($c); my ($domain, $type) = _search_requested_machine($c); - my $req = Ravada::Request->shutdown_domain(name => $domain->name + my $req = Ravada::Request->shutdown_start(id_domain => $domain->id ,uid => $USER->id ); - my $req2 = Ravada::Request->start_domain(id_domain => $domain->id - ,uid => $USER->id - ,after_request => $req->id - ); - - return $c->render(json => { req => $req2->id }); + return $c->render(json => { req => $req->id }); } sub force_shutdown_machine { diff --git a/t/vm/10_domain.t b/t/vm/10_domain.t index 94f56efdb..f32451d0c 100644 --- a/t/vm/10_domain.t +++ b/t/vm/10_domain.t @@ -223,6 +223,23 @@ sub test_open { is($domain2->vm, $domain->vm); } +sub test_shutdown_start($domain) { + $domain->start(user_admin); + $domain->needs_restart(1); + is($domain->needs_restart,1); + my $req = Ravada::Request->shutdown_start( + uid => user_admin->id + ,id_domain => $domain->id + ,timeout => 10 + ); + wait_request(debug => 0); + wait_request(debug => 0); + my $domain2 = Ravada::Front::Domain->open($domain->id); + is($domain2->needs_restart,0); + is($domain2->is_active,1); + $domain->shutdown_now(user_admin); +} + sub test_manage_domain { my $vm_name = shift; my $domain = shift; @@ -605,8 +622,8 @@ sub test_permissions { my ($stat) = @_; for my $vol ( keys %$stat ) { my @stat_new = stat($vol); - my $mode = sprintf('%o',$stat_new[2] & 07777); - my $mode_expected = sprintf('%o',$stat->{$vol}->[2] & 07777); + my $mode = sprintf('%o',$stat_new[2] & oct(7777)); + my $mode_expected = sprintf('%o',$stat->{$vol}->[2] & oct(7777)); is($mode, $mode_expected); is($stat_new[4],$stat->{$vol}->[4]); is($stat_new[5],$stat->{$vol}->[5]); @@ -672,6 +689,8 @@ for my $vm_name ( vm_names() ) { my $domain = test_create_domain($vm_name, $host); test_open($vm_name, $domain); + test_shutdown_start($domain); + test_description($vm_name, $domain); test_change_interface($vm_name,$domain); ok($domain->has_clones==0,"[$vm_name] has_clones expecting 0, got ".$domain->has_clones); diff --git a/templates/main/needs_restart.html.ep b/templates/main/needs_restart.html.ep index 07a551922..a21714e39 100644 --- a/templates/main/needs_restart.html.ep +++ b/templates/main/needs_restart.html.ep @@ -2,7 +2,7 @@
<%=l 'The changes will apply on next restart' %>
From 8326b905ab5eea07d83c0a72076321d3934ddfc4 Mon Sep 17 00:00:00 2001 From: Francesc Guasch Date: Wed, 5 Jul 2023 13:00:40 +0200 Subject: [PATCH 06/38] wip: set current CPUs --- lib/Ravada/Domain/KVM.pm | 23 ++++++++++---- lib/Ravada/Domain/Void.pm | 2 ++ lib/Ravada/Front/Domain/KVM.pm | 3 ++ public/js/ravada.js | 1 - t/request/33_hw_cpu.t | 30 +++++++++++++++++++ templates/main/manage_machine_edit.html.ep | 1 - .../main/manage_machine_edit_cpu.html.ep | 9 ++++++ .../main/settings_machine_tabs_head.html.ep | 22 +++++++------- 8 files changed, 72 insertions(+), 19 deletions(-) diff --git a/lib/Ravada/Domain/KVM.pm b/lib/Ravada/Domain/KVM.pm index d57a7140c..e82dfa144 100644 --- a/lib/Ravada/Domain/KVM.pm +++ b/lib/Ravada/Domain/KVM.pm @@ -3202,18 +3202,29 @@ sub _change_hardware_cpu($self, $index, $data) { $self->_fix_vcpu_from_topology($data); lock_hash(%$data); + my $data_n_cpus = delete $data->{vcpu}->{'#text'}; + my ($vcpu) = $doc->findnodes('/domain/vcpu'); - if (exists $data->{vcpu} && $n_vcpu ne $data->{vcpu}->{'#text'}) { + if (exists $data->{vcpu} && $n_vcpu ne $data_n_cpus) { $vcpu->removeChildNodes(); - $vcpu->appendText($data->{vcpu}->{'#text'}); + $vcpu->appendText($data_n_cpus); $changed++; } + for my $key ( keys %{$data->{vcpu}} ) { + next if $vcpu->getAttribute($key) + && exists $data->{vcpu}->{$key} + && defined $data->{vcpu}->{$key} + && $vcpu->getAttribute($key) eq $data->{vcpu}->{$key}; - if (exists $data->{vcpu} && defined $vcpu->getAttribute('current') - && $vcpu->getAttribute('current') ne $data->{vcpu}->{'#text'}) { - $vcpu->setAttribute('current' => $data->{vcpu}->{'#text'}); - $changed++; + $vcpu->setAttribute($key => $data->{vcpu}->{$key}); + $changed++; } + for my $attrib ($vcpu->attributes) { + next if exists $data->{vcpu}->{$attrib->name}; + $vcpu->removeAttribute($attrib->name); + $changed++; + } + my ($domain) = $doc->findnodes('/domain'); my ($cpu) = $doc->findnodes('/domain/cpu'); if (!$cpu) { diff --git a/lib/Ravada/Domain/Void.pm b/lib/Ravada/Domain/Void.pm index f29f4aee2..6504fd8d9 100644 --- a/lib/Ravada/Domain/Void.pm +++ b/lib/Ravada/Domain/Void.pm @@ -1039,6 +1039,8 @@ sub _change_hardware_vcpus($self, $index, $data) { my $info = $self->_value('info'); $info->{n_virt_cpu} = $n; $self->_store(info => $info); + + $self->needs_restart(1); } sub _change_hardware_memory($self, $index, $data) { diff --git a/lib/Ravada/Front/Domain/KVM.pm b/lib/Ravada/Front/Domain/KVM.pm index 5093ff533..8bce25c6d 100644 --- a/lib/Ravada/Front/Domain/KVM.pm +++ b/lib/Ravada/Front/Domain/KVM.pm @@ -192,6 +192,9 @@ sub _get_controller_cpu($self) { my ($xml_vcpu) = $doc->findnodes("/domain/vcpu"); _xml_elements($xml_vcpu, $item->{vcpu}); + $item->{vcpu}->{current} = $item->{vcpu}->{'#text'} + if ! exists $item->{vcpu}->{'#text'} || ! $item->{vcpu}->{'current'}; + if (exists $item->{cpu}->{feature} && ref($item->{cpu}->{feature}) ne 'ARRAY') { $item->{cpu}->{feature} = [ $item->{cpu}->{feature} ]; } diff --git a/public/js/ravada.js b/public/js/ravada.js index fce7281a5..3a89c5643 100644 --- a/public/js/ravada.js +++ b/public/js/ravada.js @@ -1109,7 +1109,6 @@ $scope.showmachine.requests++; $scope.pending_request = undefined; $scope.lock_info = false; - $scope.edit = ''; $http.post('/request/'+request+'/' ,JSON.stringify(args) ).then(function(response) { diff --git a/t/request/33_hw_cpu.t b/t/request/33_hw_cpu.t index 4e28975d5..da5867f5c 100644 --- a/t/request/33_hw_cpu.t +++ b/t/request/33_hw_cpu.t @@ -220,6 +220,34 @@ sub _custom_cpu_susana($domain) { $domain->reload_config($xml); +} +sub test_current_max($vm) { + return if $vm->type ne 'KVM'; + + my $domain = create_domain($vm); + + Ravada::Request->change_hardware( + hardware => 'cpu' + ,id_domain => $domain->id + ,uid => user_admin->id + ,'data' => { + '_can_edit' => 1, + 'vcpu' => { + 'placement' => 'static', + '#text' => 3, + current => 2 + }, + } + ); + wait_request(); + my $domain2 = Ravada::Front::Domain->open($domain->id); + my $info = $domain2->info(user_admin); + is($info->{n_virt_cpu},2) or exit; + + is($info->{hardware}->{cpu}->[0]->{vcpu}->{'#text'},3); + is($info->{hardware}->{cpu}->[0]->{vcpu}->{'placement'},'static'); + is($info->{hardware}->{cpu}->[0]->{vcpu}->{'current'},2) or exit; + } sub test_change_vcpu_topo($vm) { @@ -324,6 +352,8 @@ for my $vm_name ( vm_names() ) { skip($msg,10) if !$vm; + test_current_max($vm); + test_change_vcpu_topo($vm); my $domain = create_domain($vm); diff --git a/templates/main/manage_machine_edit.html.ep b/templates/main/manage_machine_edit.html.ep index 2ad225452..e3abbcc17 100644 --- a/templates/main/manage_machine_edit.html.ep +++ b/templates/main/manage_machine_edit.html.ep @@ -28,7 +28,6 @@
diff --git a/templates/main/settings_machine_tabs_head.html.ep b/templates/main/settings_machine_tabs_head.html.ep index 758ce4bd0..4186d1c36 100644 --- a/templates/main/settings_machine_tabs_head.html.ep +++ b/templates/main/settings_machine_tabs_head.html.ep @@ -3,25 +3,25 @@ <%=l 'Actions' %> % } % if ($USER->can_change_settings($domain->id)) { - <%=l 'Description' %> + <%=l 'Description' %> % } % if ($USER->can_change_settings($domain->id) && $USER->is_admin) { - <%=l 'Rename' %> + <%=l 'Rename' %> % } % if ($USER->can_change_settings($domain->id) ) { - <%=l 'Options' %> + <%=l 'Options' %> % } % if ($USER->can_change_settings($domain->id) || $USER->can_create_disk || $USER->is_admin){ - <%=l 'Hardware' %> + <%=l 'Hardware' %> % } % if ($USER->can_change_settings($domain->id) && !$domain->is_base) { - <%=l 'Screenshot' %> + <%=l 'Screenshot' %> % } % if ($USER->is_admin && !$domain->is_volatile) { <%=l 'Base' %> % } % if ($USER->is_admin || $USER->can_clone_all ){ - <%=l 'Copy' %> + <%=l 'Copy' %> % } % if ( $USER->can_expose_ports($domain->id)) { <%=l 'Ports' %> @@ -30,20 +30,20 @@ <%=l 'Access' %> % } % if ( $USER->can_remove_machine($domain->id)) { - <%=l 'Remove' %> + <%=l 'Remove' %> % } % if ($USER->is_admin && $domain->is_base){ - <%=l 'Clones' %> + <%=l 'Clones' %> % } % if ($USER->is_admin){ - <%=l 'Pool' %> - <%=l 'Host Devices' %> % } % if ( $monitoring && $USER->is_admin && $domain->is_active ) { - <%=l 'System overview' %> + <%=l 'System overview' %> % }
From e1da564e6928971b659623ead994f655971d88d8 Mon Sep 17 00:00:00 2001 From: Francesc Guasch Date: Fri, 7 Jul 2023 07:23:08 +0200 Subject: [PATCH 07/38] wip: change current cpu --- lib/Ravada/Domain.pm | 2 +- lib/Ravada/Domain/KVM.pm | 71 ++++++--- public/js/ravada.js | 4 +- t/request/33_hw_cpu.t | 137 ++++++++++++++++-- .../main/manage_machine_edit_cpu.html.ep | 2 +- templates/main/vm_options.html.ep | 22 ++- 6 files changed, 198 insertions(+), 40 deletions(-) diff --git a/lib/Ravada/Domain.pm b/lib/Ravada/Domain.pm index d71c3b0d2..3bf8bc865 100644 --- a/lib/Ravada/Domain.pm +++ b/lib/Ravada/Domain.pm @@ -5686,7 +5686,7 @@ sub _post_change_hardware($self, $hardware, $index, $data=undef) { } $self->info(Ravada::Utils->user_daemon) if $self->is_known(); - $self->needs_restart(1) if $self->is_known && $self->_data('status') eq 'active' && $hardware ne 'memory' && $hardware !~ /vcpus/; + $self->needs_restart(1) if $self->is_known && $self->_data('status') eq 'active' && $hardware ne 'memory' && $hardware !~ /cpu/; $self->post_prepare_base() if $self->is_base(); } diff --git a/lib/Ravada/Domain/KVM.pm b/lib/Ravada/Domain/KVM.pm index e82dfa144..3c6fb57d7 100644 --- a/lib/Ravada/Domain/KVM.pm +++ b/lib/Ravada/Domain/KVM.pm @@ -1644,8 +1644,16 @@ sub get_info { $info->{max_mem} = $mem_xml if $mem_xml ne $info->{max_mem}; $info->{cpu_time} = $info->{cpuTime}; - $info->{n_virt_cpu} = $info->{nrVirtCpu}; - confess Dumper($info) if !$info->{n_virt_cpu}; + + my ($cpu_text) = $doc->findnodes('/domain/vcpu/text()'); + $info->{max_virt_cpu} = 0+$cpu_text->getData(); + my ($cpu_node) = $doc->findnodes('/domain/vcpu'); + + if ($cpu_node->getAttribute('current')) { + $info->{n_virt_cpu} = 0+$cpu_node->getAttribute('current') + } else { + $info->{n_virt_cpu} = $info->{max_virt_cpu}; + } if ( $self->is_active() ) { $info->{ip} = $self->ip(); @@ -2976,14 +2984,18 @@ sub _change_hardware_display($self, $index, $data) { sub _change_hardware_vcpus($self, $index, $data) { + confess "Error: I don't understand vcpus index = '$index' , only 0" if defined $index && $index != 0; - my $n_virt_cpu = delete $data->{n_virt_cpu}; + my $req_max = delete $data->{max_virt_cpu}; + my $req_current = delete $data->{n_virt_cpu}; + confess "Error: Unkown args ".Dumper($data) if keys %$data; if ($self->domain->is_active) { eval { - $self->domain->set_vcpus($n_virt_cpu, Sys::Virt::Domain::VCPU_GUEST); + $self->domain->set_vcpus($req_current, Sys::Virt::Domain::VCPU_GUEST) if $req_current; + }; if ($@) { warn $@; @@ -2996,11 +3008,17 @@ sub _change_hardware_vcpus($self, $index, $data) { my ($topology) = $cpu->findnodes('topology'); $cpu->removeChild($topology) if $topology; - my ($vcpus_max) = ($doc->findnodes('/domain/vcpu/text()')); - $vcpus_max->setData($n_virt_cpu); + if ($req_max) { + my ($vcpus_max) = ($doc->findnodes('/domain/vcpu/text()')); + if ( $vcpus_max ne $req_max ) { + $vcpus_max->setData($req_max); + $self->needs_restart(1) if $self->is_active; + } + } my ($vcpus) = ($doc->findnodes('/domain/vcpu')); - $vcpus->setAttribute('current' => $n_virt_cpu); + $vcpus->removeAttribute('current'); + $self->reload_config($doc); } @@ -3185,6 +3203,7 @@ sub _fix_vcpu_from_topology($self, $data) { } sub _change_hardware_cpu($self, $index, $data) { + $data = $self->_default_cpu() if !keys %$data; @@ -3203,12 +3222,13 @@ sub _change_hardware_cpu($self, $index, $data) { lock_hash(%$data); my $data_n_cpus = delete $data->{vcpu}->{'#text'}; + my $data_current_cpus = $data->{vcpu}->{'current'}; + $data_n_cpus = $data_current_cpus if !defined $data_n_cpus && defined $data_current_cpus; my ($vcpu) = $doc->findnodes('/domain/vcpu'); - if (exists $data->{vcpu} && $n_vcpu ne $data_n_cpus) { + if (defined $data_n_cpus && exists $data->{vcpu} && $n_vcpu ne $data_n_cpus) { $vcpu->removeChildNodes(); $vcpu->appendText($data_n_cpus); - $changed++; } for my $key ( keys %{$data->{vcpu}} ) { next if $vcpu->getAttribute($key) @@ -3217,30 +3237,35 @@ sub _change_hardware_cpu($self, $index, $data) { && $vcpu->getAttribute($key) eq $data->{vcpu}->{$key}; $vcpu->setAttribute($key => $data->{vcpu}->{$key}); - $changed++; } for my $attrib ($vcpu->attributes) { next if exists $data->{vcpu}->{$attrib->name}; $vcpu->removeAttribute($attrib->name); - $changed++; } my ($domain) = $doc->findnodes('/domain'); my ($cpu) = $doc->findnodes('/domain/cpu'); + my $cpu_string = ''; + $cpu_string = $cpu->toString(); + $cpu_string = join("",split(/\n/,$cpu->toString)) if $cpu; if (!$cpu) { $cpu = $domain->addNewChild(undef,'cpu'); - $changed++; } - my $feature = delete $data->{cpu}->{feature}; + my $feature = $data->{cpu}->{feature}; - $changed += _change_xml($domain, 'cpu', $data->{cpu}); + _change_xml($domain, 'cpu', $data->{cpu}); if ( $feature ) { _change_xml_list($cpu, 'feature', $feature, 'name'); - $changed++; } + $cpu_string =~ s/\s\s+/ /g; + my $cpu_string2 = join("",grep(/./,split(/\n/,$cpu->toString))); + $cpu_string2 =~ s/\s\s+/ /g; - $self->reload_config($doc) if $changed; + if ( $cpu_string ne $cpu->toString() ) { + $self->needs_restart(1) if $self->is_active; + $self->reload_config($doc); + } } @@ -3388,9 +3413,10 @@ sub _change_xml_list($xml,$name, $data, $field='name') { $node = $curr if $curr->getAttribute($field) eq $entry->{$field}; } $node = $xml->addNewChild(undef, $name) if !$node; - for my $field (keys %$entry) { - next if $field eq '$$hashKey'; - $node->setAttribute($field, $entry->{$field}); + $node->setAttribute($field, $entry->{$field}); + for my $field2 (keys %$entry) { + next if $field2 eq '$$hashKey' || $field2 eq $field; + $node->setAttribute($field2, $entry->{$field2}); } } @@ -3401,8 +3427,10 @@ sub _change_xml_list($xml,$name, $data, $field='name') { } sub _change_xml($xml, $name, $data) { + return 0 if ref($data) eq 'ARRAY'; + confess Dumper([$name, $data]) - if !ref($data) || ( ref($data) ne 'HASH' && ref($data) ne 'ARRAY'); + if !ref($data) || ( ref($data) ne 'HASH' ); my $changed = 0; @@ -3442,12 +3470,13 @@ sub _change_xml($xml, $name, $data) { && $node->getAttribute($field) eq $data->{$field}; $node->setAttribute($field, $data->{$field}); + $changed++; } } for my $child ( $node->childNodes() ) { my $name = $child->nodeName(); - if (!exists $data->{$name} || !defined $data->{$name} ) { + if ($name ne '#text' && (!exists $data->{$name} || !defined $data->{$name}) ) { $node->removeChild($child); $changed++; } diff --git a/public/js/ravada.js b/public/js/ravada.js index 3a89c5643..53313a637 100644 --- a/public/js/ravada.js +++ b/public/js/ravada.js @@ -548,7 +548,8 @@ var url_ws; var update_info_settings = function() { - $scope.new_n_virt_cpu= $scope.showmachine.n_virt_cpu; + $scope.new_n_virt_cpu= 0+$scope.showmachine.n_virt_cpu; + $scope.new_max_virt_cpu= 0+$scope.showmachine.max_virt_cpu; $scope.new_memory = ($scope.showmachine.memory / 1024); $scope.new_max_mem = ($scope.showmachine.max_mem / 1024); }; @@ -1112,6 +1113,7 @@ $http.post('/request/'+request+'/' ,JSON.stringify(args) ).then(function(response) { + console.log(response.status); if (typeof(response) == null || response.status == 401 || response.status == 403 ) { window.location.href="/logout"; } diff --git a/t/request/33_hw_cpu.t b/t/request/33_hw_cpu.t index da5867f5c..3b4887461 100644 --- a/t/request/33_hw_cpu.t +++ b/t/request/33_hw_cpu.t @@ -11,6 +11,9 @@ use Test::Ravada; no warnings "experimental::signatures"; use feature qw(signatures); +my $BASE_NAME = "zz-test-base-ubuntu"; +my $BASE; + ############################################################################### sub test_add_vcpu($domain) { my $info = $domain->info(user_admin); @@ -20,7 +23,7 @@ sub test_add_vcpu($domain) { my $req = Ravada::Request->change_hardware( uid => user_admin->id ,id_domain => $domain->id - ,data => { n_virt_cpu => $n+2 } + ,data => { n_virt_cpu => $n+2, max_virt_cpu => $n+3 } ,hardware => 'vcpus' ); wait_request(); @@ -31,7 +34,9 @@ sub test_add_vcpu($domain) { return if ($domain->type eq 'Void'); - is($info2->{hardware}->{cpu}->[0]->{vcpu}->{'#text'},$n+2) + is($info2->{hardware}->{cpu}->[0]->{vcpu}->{'current'},$n+2); + + is($info2->{hardware}->{cpu}->[0]->{vcpu}->{'#text'},$n+3) or die $domain->name."\n".Dumper($info2->{hardware}->{cpu}); $domain->start(user_admin); @@ -40,7 +45,7 @@ sub test_add_vcpu($domain) { my $req2 = Ravada::Request->change_hardware( uid => user_admin->id ,id_domain => $domain->id - ,data => { n_virt_cpu => $n+2 } + ,data => { n_virt_cpu => $n+2 , max_virt_cpu => $n+3 } ,hardware => 'vcpus' ); wait_request(); @@ -49,7 +54,7 @@ sub test_add_vcpu($domain) { is($domain2->_data('needs_restart'),1) or exit; my $info3 = $domain->info(user_admin); - is($info3->{n_virt_cpu},$n); + is($info3->{n_virt_cpu},$n+2); } @@ -70,7 +75,7 @@ sub test_less_vcpu_down($domain) { my $domain2 = Ravada::Domain->open($domain->id); my $info2 = $domain2->info(user_admin); if ($domain->type eq 'KVM') { - is($info2->{hardware}->{cpu}->[0]->{vcpu}->{'#text'},$n-1) + is($info2->{hardware}->{cpu}->[0]->{vcpu}->{'current'},$n-1) or die Dumper($info2->{hardware}->{cpu}); } is($info2->{n_virt_cpu},$n-1) or die $domain->name."\n" @@ -96,11 +101,11 @@ sub test_less_vcpu_up($domain) { my $domain2 = Ravada::Domain->open($domain->id); my $info2 = $domain2->info(user_admin); - is($info2->{hardware}->{cpu}->[0]->{vcpu}->{'#text'},$n-1) + is($info2->{hardware}->{cpu}->[0]->{vcpu}->{'current'},$n-1) if $domain->type eq 'KVM'; if ( $domain->type eq 'Void') { - is($info2->{n_virt_cpu},$n-1); + is($info2->{n_virt_cpu},$n); } elsif ($domain->type eq 'KVM') { is($info2->{n_virt_cpu},$n); } @@ -113,7 +118,7 @@ sub test_less_vcpu_up($domain) { my $domain3 = Ravada::Domain->open($domain->id); my $info3 = $domain3->info(user_admin); - is($info3->{hardware}->{cpu}->[0]->{vcpu}->{'#text'},$n-1) + is($info3->{hardware}->{cpu}->[0]->{vcpu}->{'current'},$n-1) if $domain->type eq 'KVM'; is($info3->{n_virt_cpu},$n-1) or die $domain->name; @@ -191,7 +196,7 @@ sub test_add_threads($domain) { my $req2 = Ravada::Request->change_hardware( hardware => 'vcpus' - ,data => { n_virt_cpu => 3 } + ,data => { n_virt_cpu => 1 } ,id_domain => $domain->id ,uid => user_admin->id ); @@ -221,12 +226,30 @@ sub _custom_cpu_susana($domain) { $domain->reload_config($xml); } + +sub test_current_max_live($vm) { + return if $vm->type ne 'KVM'; + + my $domain = $BASE->clone(name => new_domain_name, user => user_admin); + my $info0 = $domain->info(user_admin); + + my $req = Ravada::Request->change_hardware( + hardware => 'vcpus' + ,id_domain => $domain->id + ,uid => user_admin->id + ,data => { n_virt_cpu => 2 , max_virt_cpu => 5 } + ); + wait_request(); + _wait_ip($domain); +} + sub test_current_max($vm) { return if $vm->type ne 'KVM'; - my $domain = create_domain($vm); + my $domain = $BASE->clone(name => new_domain_name, user => user_admin); + my $info0 = $domain->info(user_admin); - Ravada::Request->change_hardware( + my $req = Ravada::Request->change_hardware( hardware => 'cpu' ,id_domain => $domain->id ,uid => user_admin->id @@ -237,7 +260,8 @@ sub test_current_max($vm) { '#text' => 3, current => 2 }, - } + 'cpu'=> $info0->{hardware}->{cpu}->[0]->{cpu} + }, ); wait_request(); my $domain2 = Ravada::Front::Domain->open($domain->id); @@ -248,6 +272,81 @@ sub test_current_max($vm) { is($info->{hardware}->{cpu}->[0]->{vcpu}->{'placement'},'static'); is($info->{hardware}->{cpu}->[0]->{vcpu}->{'current'},2) or exit; + $domain->start(user_admin); + + _wait_ip($domain); + + $req->status('requested'); + wait_request(); + is($domain->needs_restart,0); + + my $req2 = Ravada::Request->change_hardware( + hardware => 'cpu' + ,id_domain => $domain->id + ,uid => user_admin->id + ,'data' => { + '_can_edit' => 1, + 'vcpu' => { + 'placement' => 'static', + '#text' => undef, + current => 2 + }, + 'cpu'=> $info0->{hardware}->{cpu}->[0]->{cpu} + }, + ); + wait_request(); + + $domain->remove(user_admin); +} + +sub test_needs_restart($vm) { + return if $vm->type ne 'KVM'; + + my $domain = $BASE->clone(name => new_domain_name, user => user_admin); + my $info = $domain->info(user_admin); + my $cpu = $info->{hardware}->{cpu}->[0]; + $cpu->{vcpu}->{'#text'} = 6; + $cpu->{vcpu}->{'current'} = 4; + + my $req0 = Ravada::Request->change_hardware( + hardware => 'cpu' + ,id_domain => $domain->id + ,uid => user_admin->id + ,'data' => $cpu + ); + + _wait_ip($domain); + is($domain->needs_restart(),0); + my $req = Ravada::Request->change_hardware( + hardware => 'cpu' + ,id_domain => $domain->id + ,uid => user_admin->id + ,'data' => $cpu + ); + wait_request(debug => 0); + is($domain->needs_restart(),0); + + $cpu->{vcpu}->{current} = 1; + + my $req2 = Ravada::Request->change_hardware( + hardware => 'cpu' + ,id_domain => $domain->id + ,uid => user_admin->id + ,'data' => $cpu + ); + wait_request(debug => 0); + is($domain->needs_restart(),0); + + $domain->remove(user_admin); +} + +sub _wait_ip($domain) { + $domain->start(user_admin) if !$domain->is_active; + for my $n ( 1 .. 120 ) { + return if $domain->ip; + sleep 1; + diag("Waiting for ip in ".$domain->name) if ! $n%10; + } } sub test_change_vcpu_topo($vm) { @@ -313,6 +412,7 @@ sub test_change_vcpu_topo($vm) { _custom_cpu_susana($domain); + $data{data}->{max_virt_cpu} = 6; $data{data}->{n_virt_cpu} = 5; $data{hardware} = 'vcpus'; delete $data{data}->{vcpu}; @@ -324,7 +424,9 @@ sub test_change_vcpu_topo($vm) { my $domain4 = Ravada::Front::Domain->open($domain->id); my $info4 = $domain4->info(user_admin); is($info4->{n_virt_cpu},5); - is($info4->{hardware}->{cpu}->[0]->{vcpu}->{'#text'},5); + is($info4->{max_virt_cpu},6); + is($info4->{hardware}->{cpu}->[0]->{vcpu}->{'#text'},6); + is($info4->{hardware}->{cpu}->[0]->{vcpu}->{'current'},5); my $xml = XML::LibXML->load_xml(string => $domain->domain->get_xml_description); my ($cpu) = $xml->findnodes("/domain/vcpu"); @@ -352,10 +454,17 @@ for my $vm_name ( vm_names() ) { skip($msg,10) if !$vm; - test_current_max($vm); + if ($vm_name eq 'KVM') { + $BASE = import_domain($vm, $BASE_NAME, 1); + } test_change_vcpu_topo($vm); + test_current_max_live($vm); + + test_needs_restart($vm); + test_current_max($vm); + my $domain = create_domain($vm); test_add_vcpu($domain); test_less_vcpu_down($domain); diff --git a/templates/main/manage_machine_edit_cpu.html.ep b/templates/main/manage_machine_edit_cpu.html.ep index a0a1026c4..c0000eb1c 100644 --- a/templates/main/manage_machine_edit_cpu.html.ep +++ b/templates/main/manage_machine_edit_cpu.html.ep @@ -9,7 +9,7 @@ ng-class='{"disabled": topology}' ng-disabled="topology" /> - +
current
- +
- +
+
+ +
+
+ +
+
+ domain->is_active) { + my $doc = XML::LibXML->load_xml(string => $self->xml_description); + my $changed =0; + + if ($self->domain->is_active && $req_current) { eval { $self->domain->set_vcpus($req_current, Sys::Virt::Domain::VCPU_GUEST) if $req_current; @@ -3001,9 +3022,13 @@ sub _change_hardware_vcpus($self, $index, $data) { warn $@; $self->_data('needs_restart' => 1); } + my ($vcpus) = $doc->findnodes('/domain/vcpu'); + if ($vcpus->getAttribute('current') != $req_current) { + $vcpus->setAttribute(current => $req_current); + $changed++; + } } - my $doc = XML::LibXML->load_xml(string => $self->xml_description); my ($cpu) = $doc->findnodes('/domain/cpu'); my ($topology) = $cpu->findnodes('topology'); $cpu->removeChild($topology) if $topology; @@ -3013,13 +3038,11 @@ sub _change_hardware_vcpus($self, $index, $data) { if ( $vcpus_max ne $req_max ) { $vcpus_max->setData($req_max); $self->needs_restart(1) if $self->is_active; + $changed++; } } - my ($vcpus) = ($doc->findnodes('/domain/vcpu')); - $vcpus->removeAttribute('current'); - - $self->reload_config($doc); + $self->reload_config($doc) if $changed; } diff --git a/public/js/ravada.js b/public/js/ravada.js index 53313a637..cfd2a2b5e 100644 --- a/public/js/ravada.js +++ b/public/js/ravada.js @@ -363,6 +363,7 @@ * item.cores * item.threads; + cpu.vcpu['current']=undefined; $scope.topology = true; }; diff --git a/templates/main/manage_machine_edit_cpu.html.ep b/templates/main/manage_machine_edit_cpu.html.ep index c0000eb1c..f8fc24f54 100644 --- a/templates/main/manage_machine_edit_cpu.html.ep +++ b/templates/main/manage_machine_edit_cpu.html.ep @@ -13,8 +13,14 @@
current diff --git a/templates/main/settings_machine_tabs_head.html.ep b/templates/main/settings_machine_tabs_head.html.ep index 4186d1c36..85ecbcb43 100644 --- a/templates/main/settings_machine_tabs_head.html.ep +++ b/templates/main/settings_machine_tabs_head.html.ep @@ -1,6 +1,6 @@
".$data->{vcpu}->{$key}; + $changed++ if $key ne 'current'; } for my $attrib ($vcpu->attributes) { next if exists $data->{vcpu}->{$attrib->name}; $vcpu->removeAttribute($attrib->name); + $changed++; } my ($domain) = $doc->findnodes('/domain'); @@ -3285,7 +3290,8 @@ sub _change_hardware_cpu($self, $index, $data) { my $cpu_string2 = join("",grep(/./,split(/\n/,$cpu->toString))); $cpu_string2 =~ s/\s\s+/ /g; - if ( $cpu_string ne $cpu->toString() ) { + if ( $cpu_string ne $cpu_string2 || $changed ) { + warn Dumper([$changed, $cpu_string, $cpu_string2]); $self->needs_restart(1) if $self->is_active; $self->reload_config($doc); } diff --git a/lib/Ravada/Domain/Void.pm b/lib/Ravada/Domain/Void.pm index 6504fd8d9..24ad2532e 100644 --- a/lib/Ravada/Domain/Void.pm +++ b/lib/Ravada/Domain/Void.pm @@ -1034,10 +1034,12 @@ sub _change_hardware_disk($self, $index, $data_new) { sub _change_hardware_vcpus($self, $index, $data) { my $n = delete $data->{n_virt_cpu}; + my $max = delete $data->{max_virt_cpu}; confess "Error: unknown args ".Dumper($data) if keys %$data; my $info = $self->_value('info'); - $info->{n_virt_cpu} = $n; + $info->{n_virt_cpu} = $n if defined $n; + $info->{max_virt_cpu} = $max if defined $max; $self->_store(info => $info); $self->needs_restart(1); diff --git a/t/request/33_hw_cpu.t b/t/request/33_hw_cpu.t index 3b4887461..514b28f10 100644 --- a/t/request/33_hw_cpu.t +++ b/t/request/33_hw_cpu.t @@ -85,10 +85,14 @@ sub test_less_vcpu_down($domain) { } sub test_less_vcpu_up($domain) { - $domain->start(user_admin); + $domain->shutdown(user => user_admin) if $domain->is_active; + wait_request(); + my $info = $domain->info(user_admin); my $cpu = $info->{hardware}->{cpu}; - my $n = $info->{n_virt_cpu}; + my $n = $info->{max_virt_cpu}; + + $domain->start(user_admin); my $req = Ravada::Request->change_hardware( uid => user_admin->id @@ -105,9 +109,9 @@ sub test_less_vcpu_up($domain) { if $domain->type eq 'KVM'; if ( $domain->type eq 'Void') { - is($info2->{n_virt_cpu},$n); + is($info2->{n_virt_cpu},$n-1); } elsif ($domain->type eq 'KVM') { - is($info2->{n_virt_cpu},$n); + is($info2->{n_virt_cpu},$n-1) or die $domain->name; } is($domain2->_data('needs_restart'),1) or exit; @@ -118,10 +122,11 @@ sub test_less_vcpu_up($domain) { my $domain3 = Ravada::Domain->open($domain->id); my $info3 = $domain3->info(user_admin); - is($info3->{hardware}->{cpu}->[0]->{vcpu}->{'current'},$n-1) + is($info3->{hardware}->{cpu}->[0]->{vcpu}->{'current'} + ,$info3->{hardware}->{cpu}->[0]->{vcpu}->{'#text'}) if $domain->type eq 'KVM'; - is($info3->{n_virt_cpu},$n-1) or die $domain->name; + is($info3->{n_virt_cpu},$n) if $domain->type eq 'KVM'; $domain->shutdown_now(user_admin); } @@ -227,6 +232,29 @@ sub _custom_cpu_susana($domain) { } +sub test_change_vcpu_feature($vm) { + return if $vm->type ne 'KVM'; + + my $domain = $BASE->clone(name => new_domain_name, user => user_admin); + $domain->start(user_admin); + _wait_ip($domain); + my $info0 = $domain->info(user_admin); + my $cpu = $info0->{hardware}->{cpu}->[0]; + $cpu->{vcpu}->{current}=1; + warn Dumper($cpu); + my $req = Ravada::Request->change_hardware( + hardware => 'cpu' + ,id_domain => $domain->id + ,uid => user_admin->id + ,data => $cpu + ); + wait_request(); + my $domain2=Ravada::Front::Domain->open($domain->id); + is($domain2->needs_restart(),0) or exit; + + $domain->remove(user_admin); +} + sub test_current_max_live($vm) { return if $vm->type ne 'KVM'; @@ -241,6 +269,8 @@ sub test_current_max_live($vm) { ); wait_request(); _wait_ip($domain); + is($domain->needs_restart,0); + $domain->remove(user_admin); } sub test_current_max($vm) { @@ -263,7 +293,7 @@ sub test_current_max($vm) { 'cpu'=> $info0->{hardware}->{cpu}->[0]->{cpu} }, ); - wait_request(); + wait_request(debug => 1); my $domain2 = Ravada::Front::Domain->open($domain->id); my $info = $domain2->info(user_admin); is($info->{n_virt_cpu},2) or exit; @@ -288,14 +318,14 @@ sub test_current_max($vm) { '_can_edit' => 1, 'vcpu' => { 'placement' => 'static', - '#text' => undef, - current => 2 + '#text' => 5, }, 'cpu'=> $info0->{hardware}->{cpu}->[0]->{cpu} }, ); wait_request(); + is($domain->needs_restart,1) or exit; $domain->remove(user_admin); } @@ -423,7 +453,7 @@ sub test_change_vcpu_topo($vm) { my $domain4 = Ravada::Front::Domain->open($domain->id); my $info4 = $domain4->info(user_admin); - is($info4->{n_virt_cpu},5); + is($info4->{n_virt_cpu},5) or die $domain->name; is($info4->{max_virt_cpu},6); is($info4->{hardware}->{cpu}->[0]->{vcpu}->{'#text'},6); is($info4->{hardware}->{cpu}->[0]->{vcpu}->{'current'},5); @@ -458,12 +488,14 @@ for my $vm_name ( vm_names() ) { $BASE = import_domain($vm, $BASE_NAME, 1); } + test_current_max($vm); + + test_change_vcpu_feature($vm); test_change_vcpu_topo($vm); test_current_max_live($vm); test_needs_restart($vm); - test_current_max($vm); my $domain = create_domain($vm); test_add_vcpu($domain); From 2764ff1cc9d4dcb098a35780d7fef22e60a214f2 Mon Sep 17 00:00:00 2001 From: Francesc Guasch Date: Mon, 10 Jul 2023 09:53:59 +0200 Subject: [PATCH 10/38] wip: change current cpu on hw --- lib/Ravada/Domain/KVM.pm | 11 ++++++++--- public/js/ravada.js | 17 +++++++++++++++-- t/request/33_hw_cpu.t | 14 +++++++------- templates/main/manage_machine_edit_cpu.html.ep | 8 ++------ 4 files changed, 32 insertions(+), 18 deletions(-) diff --git a/lib/Ravada/Domain/KVM.pm b/lib/Ravada/Domain/KVM.pm index d4ebeb414..97e228734 100644 --- a/lib/Ravada/Domain/KVM.pm +++ b/lib/Ravada/Domain/KVM.pm @@ -50,6 +50,8 @@ after 'shutdown' => \&_post_shutdown; after 'shutdown_now' => \&_post_shutdown; after 'force_shutdown' => \&_post_shutdown; +before 'start' => \&_pre_start; + ################################################## # our $TIMEOUT_SHUTDOWN = 60; @@ -974,6 +976,11 @@ sub shutdown { } +sub _pre_start($self,@args) { + # remove current CPU before start because we want max cpu the next start + $self->_remove_current_cpu(); +} + sub _post_shutdown($self,@args) { # remove current CPU after shutdown because we want max cpu the next start $self->_remove_current_cpu(); @@ -3262,13 +3269,12 @@ sub _change_hardware_cpu($self, $index, $data) { && $vcpu->getAttribute($key) eq $data->{vcpu}->{$key}; $vcpu->setAttribute($key => $data->{vcpu}->{$key}); - warn "$key => ".$data->{vcpu}->{$key}; $changed++ if $key ne 'current'; } for my $attrib ($vcpu->attributes) { next if exists $data->{vcpu}->{$attrib->name}; $vcpu->removeAttribute($attrib->name); - $changed++; + $changed++ if $attrib->name ne 'current'; } my ($domain) = $doc->findnodes('/domain'); @@ -3291,7 +3297,6 @@ sub _change_hardware_cpu($self, $index, $data) { $cpu_string2 =~ s/\s\s+/ /g; if ( $cpu_string ne $cpu_string2 || $changed ) { - warn Dumper([$changed, $cpu_string, $cpu_string2]); $self->needs_restart(1) if $self->is_active; $self->reload_config($doc); } diff --git a/public/js/ravada.js b/public/js/ravada.js index cfd2a2b5e..5a2ee4b9e 100644 --- a/public/js/ravada.js +++ b/public/js/ravada.js @@ -394,7 +394,9 @@ } $scope.$apply(function () { if ($scope.lock_info) { - $scope.showmachine.requests = data.requests; + if(data.requests) { + $scope.showmachine.requests = data.requests; + } return; } $scope.hardware = Object.keys(data.hardware); @@ -851,6 +853,9 @@ } }; $scope.check_access = function() { + if (!$scope.user_name) { + return; + } $http.get('/machine/check_access/'+$scope.showmachine.id+"/"+$scope.user_name).then(function(response) { $scope.check_allowed=response.data.ok; }); @@ -1106,7 +1111,15 @@ }); }); }; - + $scope.req_change_current = function(n_cpu) { + if (!$scope.topology && $scope.showmachine.is_active) { + request('change_hardware',{ + 'id_domain': $scope.showmachine.id + ,'hardware': 'vcpus' + ,'data': { 'n_virt_cpu': n_cpu} + }) + } + }; $scope.request = function(request, args) { $scope.showmachine.requests++; $scope.pending_request = undefined; diff --git a/t/request/33_hw_cpu.t b/t/request/33_hw_cpu.t index 514b28f10..59c4ac6e3 100644 --- a/t/request/33_hw_cpu.t +++ b/t/request/33_hw_cpu.t @@ -279,6 +279,8 @@ sub test_current_max($vm) { my $domain = $BASE->clone(name => new_domain_name, user => user_admin); my $info0 = $domain->info(user_admin); + my $max_cpu = 8; + my $req = Ravada::Request->change_hardware( hardware => 'cpu' ,id_domain => $domain->id @@ -287,8 +289,7 @@ sub test_current_max($vm) { '_can_edit' => 1, 'vcpu' => { 'placement' => 'static', - '#text' => 3, - current => 2 + '#text' => $max_cpu, }, 'cpu'=> $info0->{hardware}->{cpu}->[0]->{cpu} }, @@ -296,11 +297,9 @@ sub test_current_max($vm) { wait_request(debug => 1); my $domain2 = Ravada::Front::Domain->open($domain->id); my $info = $domain2->info(user_admin); - is($info->{n_virt_cpu},2) or exit; - is($info->{hardware}->{cpu}->[0]->{vcpu}->{'#text'},3); + is($info->{hardware}->{cpu}->[0]->{vcpu}->{'#text'},$max_cpu); is($info->{hardware}->{cpu}->[0]->{vcpu}->{'placement'},'static'); - is($info->{hardware}->{cpu}->[0]->{vcpu}->{'current'},2) or exit; $domain->start(user_admin); @@ -318,14 +317,15 @@ sub test_current_max($vm) { '_can_edit' => 1, 'vcpu' => { 'placement' => 'static', - '#text' => 5, + '#text' => $max_cpu, + ,'current' => 1 }, 'cpu'=> $info0->{hardware}->{cpu}->[0]->{cpu} }, ); wait_request(); - is($domain->needs_restart,1) or exit; + is($domain->needs_restart,0) or exit; $domain->remove(user_admin); } diff --git a/templates/main/manage_machine_edit_cpu.html.ep b/templates/main/manage_machine_edit_cpu.html.ep index f8fc24f54..5dcf8249c 100644 --- a/templates/main/manage_machine_edit_cpu.html.ep +++ b/templates/main/manage_machine_edit_cpu.html.ep @@ -13,14 +13,10 @@
current From 09137da7fa23db635f7f786ce01d3f508f444aba Mon Sep 17 00:00:00 2001 From: Francesc Guasch Date: Mon, 10 Jul 2023 13:57:35 +0200 Subject: [PATCH 11/38] wip: removed debug --- lib/Ravada/Domain/KVM.pm | 2 ++ public/js/ravada.js | 4 ---- t/request/33_hw_cpu.t | 2 +- 3 files changed, 3 insertions(+), 5 deletions(-) diff --git a/lib/Ravada/Domain/KVM.pm b/lib/Ravada/Domain/KVM.pm index 97e228734..5150d891d 100644 --- a/lib/Ravada/Domain/KVM.pm +++ b/lib/Ravada/Domain/KVM.pm @@ -3300,6 +3300,8 @@ sub _change_hardware_cpu($self, $index, $data) { $self->needs_restart(1) if $self->is_active; $self->reload_config($doc); } + $self->_change_hardware_vcpus(0,{n_virt_cpu => $data_current_cpus}) + if $data_current_cpus && $self->is_active; } diff --git a/public/js/ravada.js b/public/js/ravada.js index 5a2ee4b9e..aa0cb6c20 100644 --- a/public/js/ravada.js +++ b/public/js/ravada.js @@ -1072,7 +1072,6 @@ var _host_device_in_machine = function(id_hd) { for ( var i=0;i<$scope.showmachine.host_devices.length; i++ ) { var hd = $scope.showmachine.host_devices[i]; - console.log(hd.id+ " "+id_hd); if (hd.id_host_device == id_hd) { return true; } @@ -1127,7 +1126,6 @@ $http.post('/request/'+request+'/' ,JSON.stringify(args) ).then(function(response) { - console.log(response.status); if (typeof(response) == null || response.status == 401 || response.status == 403 ) { window.location.href="/logout"; } @@ -1257,10 +1255,8 @@ try { var successful = document.execCommand('copy'); var msg = successful ? 'successful' : 'unsuccessful'; - console.log('Copying text command was ' + msg); $scope.password_clipboard=successful; } catch (err) { - console.log('Oops, unable to copy'); } } diff --git a/t/request/33_hw_cpu.t b/t/request/33_hw_cpu.t index 59c4ac6e3..9de4e7943 100644 --- a/t/request/33_hw_cpu.t +++ b/t/request/33_hw_cpu.t @@ -294,7 +294,7 @@ sub test_current_max($vm) { 'cpu'=> $info0->{hardware}->{cpu}->[0]->{cpu} }, ); - wait_request(debug => 1); + wait_request(debug => 0); my $domain2 = Ravada::Front::Domain->open($domain->id); my $info = $domain2->info(user_admin); From 6256a7dc9ba08f11762b1e2254ea77172acfc1d4 Mon Sep 17 00:00:00 2001 From: Francesc Guasch Date: Tue, 11 Jul 2023 11:15:36 +0200 Subject: [PATCH 12/38] wip: pre start and post shutdown internal --- lib/Ravada/Domain.pm | 10 ++++++++++ lib/Ravada/Domain/KVM.pm | 10 ++-------- 2 files changed, 12 insertions(+), 8 deletions(-) diff --git a/lib/Ravada/Domain.pm b/lib/Ravada/Domain.pm index 3bf8bc865..5edbc8cfb 100644 --- a/lib/Ravada/Domain.pm +++ b/lib/Ravada/Domain.pm @@ -303,6 +303,8 @@ sub _around_start($orig, $self, @arg) { $self->_start_preconditions(@arg); + $self->_pre_start_internal(); + $self->_data( 'post_shutdown' => 0); $self->_data( 'post_hibernated' => 0); @@ -3037,9 +3039,17 @@ sub _remove_start_requests($self) { } } +# it may be superceeded in child class +sub _post_shutdown_internal {} + +# it may be superceeded in child class +sub _pre_start_internal {} + sub _post_shutdown { my $self = shift; + $self->_post_shutdown_internal(); + my %arg = @_; my $timeout = delete $arg{timeout}; if (!defined $timeout) { diff --git a/lib/Ravada/Domain/KVM.pm b/lib/Ravada/Domain/KVM.pm index 5150d891d..9e1f58527 100644 --- a/lib/Ravada/Domain/KVM.pm +++ b/lib/Ravada/Domain/KVM.pm @@ -46,12 +46,6 @@ has readonly => ( ,default => 0 ); -after 'shutdown' => \&_post_shutdown; -after 'shutdown_now' => \&_post_shutdown; -after 'force_shutdown' => \&_post_shutdown; - -before 'start' => \&_pre_start; - ################################################## # our $TIMEOUT_SHUTDOWN = 60; @@ -976,12 +970,12 @@ sub shutdown { } -sub _pre_start($self,@args) { +sub _pre_start_internal($self,@args) { # remove current CPU before start because we want max cpu the next start $self->_remove_current_cpu(); } -sub _post_shutdown($self,@args) { +sub _post_shutdown_internal($self,@args) { # remove current CPU after shutdown because we want max cpu the next start $self->_remove_current_cpu(); } From 887017462a225e5c8a1dbebbc1c96f39a9ec4226 Mon Sep 17 00:00:00 2001 From: Francesc Guasch Date: Tue, 11 Jul 2023 11:26:26 +0200 Subject: [PATCH 13/38] wip: deal with nonexistant vcpu in req --- lib/Ravada/Domain/KVM.pm | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/lib/Ravada/Domain/KVM.pm b/lib/Ravada/Domain/KVM.pm index 9e1f58527..cc06e129d 100644 --- a/lib/Ravada/Domain/KVM.pm +++ b/lib/Ravada/Domain/KVM.pm @@ -3203,6 +3203,9 @@ sub _default_cpu($self) { } sub _fix_vcpu_from_topology($self, $data) { + + $data->{vcpu} = {} if !exists $data->{vcpu}; + if (!exists $data->{cpu}->{topology} || !defined($data->{cpu}->{topology})) { @@ -3244,10 +3247,13 @@ sub _change_hardware_cpu($self, $index, $data) { my ($n_vcpu) = $doc->findnodes('/domain/vcpu/text()'); $self->_fix_vcpu_from_topology($data); + lock_hash(%$data); - my $data_n_cpus = delete $data->{vcpu}->{'#text'}; - my $data_current_cpus = $data->{vcpu}->{'current'}; + my ($data_n_cpus, $data_current_cpus); + $data_n_cpus = delete $data->{vcpu}->{'#text'} if exists $data->{vcpu}->{'#text'}; + + $data_current_cpus = $data->{vcpu}->{'current'} if exists $data->{vcpu}->{'current'}; $data_n_cpus = $data_current_cpus if !defined $data_n_cpus && defined $data_current_cpus; my ($vcpu) = $doc->findnodes('/domain/vcpu'); From 817a5dbdd05f193336e6f93cfa59857586ac5b60 Mon Sep 17 00:00:00 2001 From: Francesc Guasch Date: Fri, 14 Jul 2023 13:27:33 +0200 Subject: [PATCH 14/38] wip: change hw after install --- lib/Ravada/VM.pm | 15 +++++++++++++++ lib/Ravada/VM/KVM.pm | 2 ++ t/kvm/uefi.t | 27 +++++++++++++++++++++++++++ 3 files changed, 44 insertions(+) diff --git a/lib/Ravada/VM.pm b/lib/Ravada/VM.pm index 82c2b52a2..a35072917 100644 --- a/lib/Ravada/VM.pm +++ b/lib/Ravada/VM.pm @@ -410,6 +410,7 @@ sub _around_create_domain { my %args = @_; my $remote_ip = delete $args{remote_ip}; my $add_to_pool = delete $args{add_to_pool}; + my $hardware = delete $args{options}->{hardware}; my %args_create = %args; my $id_owner = delete $args{id_owner} or confess "ERROR: Missing id_owner"; @@ -496,6 +497,7 @@ sub _around_create_domain { $domain->_data('is_compacted' => 1); $domain->_data('alias' => $alias) if $alias; $domain->_data('date_status_change', Ravada::Utils::now()); + _change_hardware_install($domain,$hardware) if $hardware; if ($id_base) { $domain->run_timeout($base->run_timeout) @@ -539,6 +541,19 @@ sub _around_create_domain { return $domain; } +sub _change_hardware_install($domain, $hardware) { + + for my $item (sort keys %$hardware) { + Ravada::Request->change_hardware( + uid => Ravada::Utils::user_daemon->id + ,id_domain=> $domain->id + ,hardware => $item + ,data => $hardware->{$item} + ); + } + +} + sub _set_ascii_name($self, $name) { my $length = length($name); $name =~ tr/.âêîôûáéíóúàèìòùäëïöüçñ'/-aeiouaeiouaeiouaeioucn_/; diff --git a/lib/Ravada/VM/KVM.pm b/lib/Ravada/VM/KVM.pm index 6fd216f9b..5d01672d6 100644 --- a/lib/Ravada/VM/KVM.pm +++ b/lib/Ravada/VM/KVM.pm @@ -1828,12 +1828,14 @@ sub _xml_modify_options($self, $doc, $options=undef) { my $machine = delete $options->{machine}; my $arch = delete $options->{arch}; my $bios = delete $options->{'bios'}; + confess "Error: bios=$bios and uefi=$uefi clash" if defined $uefi && defined $bios && ($bios !~/uefi/i && $uefi || $bios =~/uefi/i && !$uefi); $uefi = 1 if $bios && $bios =~ /uefi/i; + confess "Arguments unknown ".Dumper($options) if keys %$options; if ($machine) { $self->_xml_set_machine($doc, $machine); diff --git a/t/kvm/uefi.t b/t/kvm/uefi.t index 101e5f2af..2d295d2f9 100644 --- a/t/kvm/uefi.t +++ b/t/kvm/uefi.t @@ -16,6 +16,31 @@ use feature qw(signatures); ######################################################################## +sub test_threads($vm) { + my $name = new_domain_name(); + my $id_iso = search_id_iso('Alpine'); + + my $req = Ravada::Request->create_domain( + name => $name + ,vm => $vm->type + ,id_iso => $id_iso + ,id_owner => user_admin->id + ,memory => 512 * 1024 + ,disk => 1024 * 1024 + ,options => { hardware => { cpu => { topology => { threads => 4 } }} } + ); + wait_request( debug => 1); + my $domain = $vm->search_domain($name); + ok($domain); + my $config = $domain->xml_description(); + + my $doc = XML::LibXML->load_xml(string => $config); + my ($topology) = $doc->findnodes("/domain/vcpu/topology"); + ok($topology) or die $domain->name; + die $topology->toString; + +} + sub test_uefi($vm) { my $name = new_domain_name(); my $id_iso = search_id_iso('Alpine'); @@ -322,6 +347,8 @@ for my $vm_name ( 'KVM' ) { diag($msg) if !$vm; skip $msg,10 if !$vm; + test_threads($vm); + test_isos($vm); test_req_machine_types($vm); test_req_machine_types2($vm); From 74e95848ad6cddea61ce6c78ddce223c8c4b5768 Mon Sep 17 00:00:00 2001 From: Francesc Guasch Date: Fri, 14 Jul 2023 16:18:24 +0200 Subject: [PATCH 15/38] wip: pass cpu topology on creation --- lib/Ravada.pm | 8 ++++++-- lib/Ravada/VM.pm | 4 ++-- lib/Ravada/VM/KVM.pm | 2 ++ public/js/admin.js | 11 ++++++++--- script/rvd_front | 1 + t/kvm/uefi.t | 9 +++++---- 6 files changed, 24 insertions(+), 11 deletions(-) diff --git a/lib/Ravada.pm b/lib/Ravada.pm index b7f06a0a6..4c01311df 100644 --- a/lib/Ravada.pm +++ b/lib/Ravada.pm @@ -494,7 +494,9 @@ sub _update_isos { ,file_re => 'alpine-standard-3.16.*-x86_64.iso' ,sha256_url => '$url/alpine-standard-3.16.*.iso.sha256' ,min_disk_size => '2' - ,options => { machine => 'pc-q35', bios => 'UEFI' } + ,options => { machine => 'pc-q35', bios => 'UEFI' + ,hardware => { cpu => { cpu => { topology => { threads => 2, cores => 2}}}} + } } ,alpine381_32 => { name => 'Alpine 3.16 32 bits' @@ -866,7 +868,9 @@ sub _update_isos { ,min_ram => 4 ,arch => 'x86_64' ,extra_iso => 'https://fedorapeople.org/groups/virt/virtio-win/direct-downloads/archive-virtio/virtio-win-0.1.2\d+-\d+/virtio-win-0.1.2\d+.iso' - ,options => { machine => 'pc-q35', bios => 'UEFI' } + ,options => { machine => 'pc-q35', bios => 'UEFI' + ,hardware => { cpu => { cpu => { topology => { threads => 2, cores => 2}}}} + } } ,empty_32bits => { name => 'Empty Machine 32 bits' diff --git a/lib/Ravada/VM.pm b/lib/Ravada/VM.pm index a35072917..2aef9c762 100644 --- a/lib/Ravada/VM.pm +++ b/lib/Ravada/VM.pm @@ -497,7 +497,7 @@ sub _around_create_domain { $domain->_data('is_compacted' => 1); $domain->_data('alias' => $alias) if $alias; $domain->_data('date_status_change', Ravada::Utils::now()); - _change_hardware_install($domain,$hardware) if $hardware; + $self->_change_hardware_install($domain,$hardware) if $hardware; if ($id_base) { $domain->run_timeout($base->run_timeout) @@ -541,7 +541,7 @@ sub _around_create_domain { return $domain; } -sub _change_hardware_install($domain, $hardware) { +sub _change_hardware_install($self, $domain, $hardware) { for my $item (sort keys %$hardware) { Ravada::Request->change_hardware( diff --git a/lib/Ravada/VM/KVM.pm b/lib/Ravada/VM/KVM.pm index 5d01672d6..b98bd34e3 100644 --- a/lib/Ravada/VM/KVM.pm +++ b/lib/Ravada/VM/KVM.pm @@ -1078,6 +1078,8 @@ sub _domain_create_from_iso { if $spice_password; $domain->xml_description(); + $self->_change_hardware_install($domain,$iso->{options}->{hardware}); + return $domain; } diff --git a/public/js/admin.js b/public/js/admin.js index 62d6a6a72..13181bdf7 100644 --- a/public/js/admin.js +++ b/public/js/admin.js @@ -215,9 +215,14 @@ ravadaApp.directive("solShowMachine", swMach) } } } - if ($scope.id_iso && $scope.id_iso.options - && $scope.id_iso.options['bios']) { - $scope.bios = $scope.id_iso.options['bios']; + if ($scope.id_iso && $scope.id_iso.options) { + if( $scope.id_iso.options['bios']) { + $scope.bios = $scope.id_iso.options['bios']; + } + if( $scope.id_iso.options['hardware']) { + $scope.hardware = $scope.id_iso.options['hardware']; + } + } }; diff --git a/script/rvd_front b/script/rvd_front index de6f2e20a..1c63b4303 100644 --- a/script/rvd_front +++ b/script/rvd_front @@ -2837,6 +2837,7 @@ sub req_new_domain { my $vm = ( $c->param('backend') or 'KVM'); $swap = int($swap * 1024*1024*1024); my $bios = $c->param('bios'); + my $hardware = $c->param('hardware'); my $machine = ($c->param('machine') or ''); $machine =~ s/^string://; $machine = '' if $machine eq '?'; diff --git a/t/kvm/uefi.t b/t/kvm/uefi.t index 2d295d2f9..5236d09a5 100644 --- a/t/kvm/uefi.t +++ b/t/kvm/uefi.t @@ -20,6 +20,7 @@ sub test_threads($vm) { my $name = new_domain_name(); my $id_iso = search_id_iso('Alpine'); + my $n_threads = 4; my $req = Ravada::Request->create_domain( name => $name ,vm => $vm->type @@ -27,17 +28,17 @@ sub test_threads($vm) { ,id_owner => user_admin->id ,memory => 512 * 1024 ,disk => 1024 * 1024 - ,options => { hardware => { cpu => { topology => { threads => 4 } }} } + ,options => { hardware => { cpu => { cpu => { topology => { threads => $n_threads }}}} } ); - wait_request( debug => 1); + wait_request( debug => 0); my $domain = $vm->search_domain($name); ok($domain); my $config = $domain->xml_description(); my $doc = XML::LibXML->load_xml(string => $config); - my ($topology) = $doc->findnodes("/domain/vcpu/topology"); + my ($topology) = $doc->findnodes("/domain/cpu/topology"); ok($topology) or die $domain->name; - die $topology->toString; + is($topology->getAttribute('threads'), $n_threads); } From dd91c0a36d6bc675d9bb93950a40fcdf03e02fb2 Mon Sep 17 00:00:00 2001 From: Francesc Guasch Date: Fri, 14 Jul 2023 18:25:39 +0200 Subject: [PATCH 16/38] wip: allow option arg --- lib/Ravada/VM.pm | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/Ravada/VM.pm b/lib/Ravada/VM.pm index 2aef9c762..8e44ec4a0 100644 --- a/lib/Ravada/VM.pm +++ b/lib/Ravada/VM.pm @@ -946,7 +946,7 @@ sub _check_require_base { delete $args{start}; delete $args{remote_ip}; - delete @args{'_vm','name','vm', 'memory','description','id_iso','listen_ip','spice_password','from_pool', 'volatile', 'alias','storage'}; + delete @args{'_vm','name','vm', 'memory','description','id_iso','listen_ip','spice_password','from_pool', 'volatile', 'alias','storage', 'options'}; confess "ERROR: Unknown arguments ".join(",",keys %args) if keys %args; From 3fc544e950ff45f50eaf1c169fe6ca72df3d6b25 Mon Sep 17 00:00:00 2001 From: Francesc Guasch Date: Tue, 18 Jul 2023 15:31:09 +0200 Subject: [PATCH 17/38] Fix import volume referenced by name (#1975) fix: import volume defined by name --- lib/Ravada/Domain/KVM.pm | 18 +++++- lib/Ravada/Volume.pm | 4 ++ t/kvm/40_import.t | 128 +++++++++++++++++++++++++++++++++++++++ 3 files changed, 148 insertions(+), 2 deletions(-) diff --git a/lib/Ravada/Domain/KVM.pm b/lib/Ravada/Domain/KVM.pm index d42c912a4..3920c356d 100644 --- a/lib/Ravada/Domain/KVM.pm +++ b/lib/Ravada/Domain/KVM.pm @@ -376,7 +376,9 @@ sub _disk_device($self, $with_info=undef, $attribute=undef, $value=undef) { for my $disk ($doc->findnodes('/domain/devices/disk')) { my ($source_node) = $disk->findnodes('source'); my $file; - $file = $source_node->getAttribute('file') if $source_node; + if ( $source_node ) { + $file = $self->_get_volume_file($source_node); + } my ($target_node) = $disk->findnodes('target'); my ($driver_node) = $disk->findnodes('driver'); @@ -571,7 +573,7 @@ sub _set_volumes_backing_store($self) { for my $disk ($doc->findnodes('/domain/devices/disk')) { next if $disk->getAttribute('device') ne 'disk'; for my $source( $disk->findnodes('source')) { - my $file = $source->getAttribute('file'); + my $file = $self->_get_volume_file($source); my $backing_file = $vol{$file}->backing_file(); $self->_set_backing_store($disk, $backing_file); @@ -581,6 +583,18 @@ sub _set_volumes_backing_store($self) { $self->reload_config($doc); } +sub _get_volume_file($self, $source) { + return $source->getAttribute('file') if $source->getAttribute('file'); + + my $pool_name = $source->getAttribute('pool') or die "Error: I need pool or file in ".$source->toString(); + my $volume = $source->getAttribute('volume') or die "Error: I need pool or file in ".$source->toString(); + my $pool = $self->_vm->vm->get_storage_pool_by_name($pool_name) + or die "Error: no pool $pool_name"; + my $vol = $pool->get_volume_by_name($volume); + return $vol->get_path; + +} + sub _store_xml($self) { my $xml = $self->domain->get_xml_description(Sys::Virt::Domain::XML_INACTIVE); diff --git a/lib/Ravada/Volume.pm b/lib/Ravada/Volume.pm index 6a414cbe0..ae2841236 100644 --- a/lib/Ravada/Volume.pm +++ b/lib/Ravada/Volume.pm @@ -111,6 +111,10 @@ sub BUILD($self, $arg) { } elsif (exists $arg->{info}) { if (exists $arg->{info}->{device} && $arg->{info}->{device} eq 'cdrom') { $class = "Ravada::Volume::ISO"; + } elsif(exists $arg->{info}->{driver} && exists $arg->{info}->{driver}->{type}) { + my $name = 'unknown'; + $name = $arg->{info}->{name} if exists $arg->{info}->{name}; + $class = "Ravada::Volume::"._type_from_extension("$name.".$arg->{info}->{driver}->{type}); } else { confess "I can't guess class from ".Dumper($arg); } diff --git a/t/kvm/40_import.t b/t/kvm/40_import.t index 36ec2d4de..c8fe26f47 100644 --- a/t/kvm/40_import.t +++ b/t/kvm/40_import.t @@ -2,6 +2,7 @@ use warnings; use strict; use Data::Dumper; +use IPC::Run3 qw(run3); use Test::More; use lib 't/lib'; @@ -149,6 +150,131 @@ sub test_import_spinoff { } +} + +sub _create_vol($vm, $name) { +my @cmd = ("qemu-img",'create','-f','qcow2', $vm->dir_img."/".$name,'512M'); +my ($in, $out, $err); +run3(\@cmd , \$in, \$out, \$err); +die $err if $err; +diag($out) if $out; +} + +sub test_volume($vm) { + + my $dom_name = new_domain_name(); + my $vol_name = new_domain_name(); + _create_vol($vm, $vol_name); + $vm->refresh_storage_pools(); + return if $vm->type ne 'KVM'; +my $xml =< + $dom_name + 6f6c9b78-3ce4-4a4e-a025-b1c7ae1965e0 + + + + + + 1273856 + 1273856 + 1 + + hvm + + + + + + + + qemu64 + + + destroy + restart + restart + + /usr/bin/qemu-system-x86_64 + + + + +
+ + +
+ + +
+ + + + + +
+ + + +
+ + + + +
+ + + + +
+ + + + + +
+ + + + + + + + + + +
+ + + + + + +
@@ -124,7 +129,7 @@
Date: Tue, 29 Aug 2023 12:28:05 +0200 Subject: [PATCH 27/38] fix: W11 CPU set to custom default --- etc/xml/windows_11.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/etc/xml/windows_11.xml b/etc/xml/windows_11.xml index 8e4db065d..12e575d3f 100644 --- a/etc/xml/windows_11.xml +++ b/etc/xml/windows_11.xml @@ -23,7 +23,7 @@ - + From d464ff27418ac10da94f6557dae7c2f446c0f6aa Mon Sep 17 00:00:00 2001 From: Francesc Guasch Date: Thu, 31 Aug 2023 11:45:43 +0200 Subject: [PATCH 28/38] wip: set cpu in inactive config --- lib/Ravada/Domain/KVM.pm | 8 ++++++-- t/request/33_hw_cpu.t | 19 +++++++++++-------- 2 files changed, 17 insertions(+), 10 deletions(-) diff --git a/lib/Ravada/Domain/KVM.pm b/lib/Ravada/Domain/KVM.pm index 83ca22ab5..ce9c19020 100644 --- a/lib/Ravada/Domain/KVM.pm +++ b/lib/Ravada/Domain/KVM.pm @@ -3246,12 +3246,13 @@ sub _change_hardware_cpu($self, $index, $data) { if !$data->{cpu}->{'model'}->{'#text'}; delete $data->{cpu}->{model}->{'$$hashKey'}; - - my $doc = XML::LibXML->load_xml(string => $self->xml_description); + my @flags = (Sys::Virt::Domain::XML_INACTIVE); + my $doc = XML::LibXML->load_xml( string => $self->domain->get_xml_description( @flags )); my $count = 0; my $changed = 0; my ($n_vcpu) = $doc->findnodes('/domain/vcpu/text()'); + my ($cpu0) = $doc->findnodes('/domain/cpu'); $self->_fix_vcpu_from_topology($data); @@ -3622,6 +3623,9 @@ sub reload_config($self, $doc) { $self->_validate_xml($doc) if $self->_vm->vm->get_major_version >= 4; my $new_domain = $self->_vm->vm->define_domain($doc->toString); $self->domain($new_domain); + + $self->_data_extra('xml', $doc->toString) if $self->is_known + } sub _save_xml_tmp($self,$doc) { diff --git a/t/request/33_hw_cpu.t b/t/request/33_hw_cpu.t index 414088f15..3d8a2fc84 100644 --- a/t/request/33_hw_cpu.t +++ b/t/request/33_hw_cpu.t @@ -308,6 +308,9 @@ sub test_current_max($vm) { wait_request(); is($domain->needs_restart,0); + my $domain3 = Ravada::Front::Domain->open($domain->id); + my $info3 = $domain3->info(user_admin); + # change current cpu to 2 my $req2 = Ravada::Request->change_hardware( hardware => 'cpu' @@ -320,13 +323,13 @@ sub test_current_max($vm) { '#text' => $max_cpu, ,'current' => 2 }, - 'cpu'=> $info0->{hardware}->{cpu}->[0]->{cpu} + 'cpu'=> $info3->{hardware}->{cpu}->[0]->{cpu} }, ); - wait_request( debug => 0 ); + wait_request( debug => 1 ); my $domain22 = Ravada::Domain->open($domain->id); - is($domain22->needs_restart,0) or exit; + is($domain22->needs_restart,0, $domain22->name) or exit; my $info22 = $domain22->info(user_admin); is($info22->{hardware}->{cpu}->[0]->{vcpu}->{'current'},2) or die $domain->name; @@ -354,12 +357,12 @@ sub test_current_max($vm) { ); wait_request(debug => 0); - my $domain3 = Ravada::Front::Domain->open($domain->id); - is($domain3->needs_restart,1) or exit; + my $domain4 = Ravada::Front::Domain->open($domain->id); + is($domain4->needs_restart,1) or exit; - my $info3 = $domain3->info(user_admin); - is($info3->{hardware}->{cpu}->[0]->{vcpu}->{current},2) or die $domain3->name; - is($info3->{hardware}->{cpu}->[0]->{vcpu}->{'#text'}, $max_cpu+1) or die $domain3->name; + my $info4 = $domain3->info(user_admin); + is($info4->{hardware}->{cpu}->[0]->{vcpu}->{current},2) or die $domain4->name; + is($info4->{hardware}->{cpu}->[0]->{vcpu}->{'#text'}, $max_cpu+1) or die $domain3->name; $domain->remove(user_admin); } From 31c20a67d3365228f0c1d86796cc5ed552dc450c Mon Sep 17 00:00:00 2001 From: Francesc Guasch Date: Thu, 31 Aug 2023 17:03:37 +0200 Subject: [PATCH 29/38] wip: properly check vcpu changed --- lib/Ravada/Domain.pm | 4 ++++ t/request/33_hw_cpu.t | 4 ++-- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/lib/Ravada/Domain.pm b/lib/Ravada/Domain.pm index 5edbc8cfb..b096827b4 100644 --- a/lib/Ravada/Domain.pm +++ b/lib/Ravada/Domain.pm @@ -1661,6 +1661,10 @@ sub _data($self, $field, $value=undef, $table='domains') { sub _data_extra($self, $field, $value=undef) { $self->_insert_db_extra() if !$self->is_known_extra(); + if (defined $value) { + my $old = $self->_data_extra($field); + return if defined $old && $old eq $value; + } return $self->_data($field, $value, "domains_".lc($self->type)); } diff --git a/t/request/33_hw_cpu.t b/t/request/33_hw_cpu.t index 3d8a2fc84..01f357701 100644 --- a/t/request/33_hw_cpu.t +++ b/t/request/33_hw_cpu.t @@ -326,7 +326,7 @@ sub test_current_max($vm) { 'cpu'=> $info3->{hardware}->{cpu}->[0]->{cpu} }, ); - wait_request( debug => 1 ); + wait_request( debug => 0 ); my $domain22 = Ravada::Domain->open($domain->id); is($domain22->needs_restart,0, $domain22->name) or exit; @@ -360,7 +360,7 @@ sub test_current_max($vm) { my $domain4 = Ravada::Front::Domain->open($domain->id); is($domain4->needs_restart,1) or exit; - my $info4 = $domain3->info(user_admin); + my $info4 = $domain4->info(user_admin); is($info4->{hardware}->{cpu}->[0]->{vcpu}->{current},2) or die $domain4->name; is($info4->{hardware}->{cpu}->[0]->{vcpu}->{'#text'}, $max_cpu+1) or die $domain3->name; From 90b05a427f5b22ffcf2e09643a378fed4a11959c Mon Sep 17 00:00:00 2001 From: Francesc Guasch Date: Fri, 1 Sep 2023 13:12:48 +0200 Subject: [PATCH 30/38] wip: do not change current cpu when active --- lib/Ravada/Domain/KVM.pm | 17 ++++++++++++++--- 1 file changed, 14 insertions(+), 3 deletions(-) diff --git a/lib/Ravada/Domain/KVM.pm b/lib/Ravada/Domain/KVM.pm index ce9c19020..40e57e82c 100644 --- a/lib/Ravada/Domain/KVM.pm +++ b/lib/Ravada/Domain/KVM.pm @@ -878,7 +878,7 @@ sub start { $self->status('starting'); my $error; - for ( ;; ) { + for ( 1 .. 60 ) { eval { $self->domain->create() }; $error = $@; next if $error && $error =~ /libvirt error code: 1, .* pool .* asynchronous/; @@ -981,6 +981,9 @@ sub _post_shutdown_internal($self,@args) { } sub _remove_current_cpu($self) { + + return if $self->is_active; + my $doc = XML::LibXML->load_xml(string => $self->domain->get_xml_description(Sys::Virt::Domain::XML_INACTIVE)); my ($cpu_node) = $doc->findnodes('/domain/vcpu'); $cpu_node->removeAttribute('current'); @@ -3621,10 +3624,18 @@ sub _validate_xml($self, $doc) { sub reload_config($self, $doc) { $self->_validate_xml($doc) if $self->_vm->vm->get_major_version >= 4; - my $new_domain = $self->_vm->vm->define_domain($doc->toString); + + my $new_domain; + + eval { + $new_domain = $self->_vm->vm->define_domain($doc->toString); + }; + + cluck ''.$@ if $@; + $self->domain($new_domain); - $self->_data_extra('xml', $doc->toString) if $self->is_known + $self->_data_extra('xml', $doc->toString) if $self->is_known && $self->is_local; } From a09e062042156a53f48afe0397a3018bc120d680 Mon Sep 17 00:00:00 2001 From: Francesc Guasch Date: Fri, 1 Sep 2023 13:42:03 +0200 Subject: [PATCH 31/38] wip: do not change if domain went away --- lib/Ravada/Domain/KVM.pm | 1 + 1 file changed, 1 insertion(+) diff --git a/lib/Ravada/Domain/KVM.pm b/lib/Ravada/Domain/KVM.pm index 40e57e82c..fa80f3548 100644 --- a/lib/Ravada/Domain/KVM.pm +++ b/lib/Ravada/Domain/KVM.pm @@ -982,6 +982,7 @@ sub _post_shutdown_internal($self,@args) { sub _remove_current_cpu($self) { + return if !$self->domain; return if $self->is_active; my $doc = XML::LibXML->load_xml(string => $self->domain->get_xml_description(Sys::Virt::Domain::XML_INACTIVE)); From c14ffbc03b980a446729f7cffed5eddbb23a44cd Mon Sep 17 00:00:00 2001 From: Francesc Guasch Date: Tue, 5 Sep 2023 17:11:13 +0200 Subject: [PATCH 32/38] wip: volatile domains disapear --- lib/Ravada/Domain/KVM.pm | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/lib/Ravada/Domain/KVM.pm b/lib/Ravada/Domain/KVM.pm index fa80f3548..812552c48 100644 --- a/lib/Ravada/Domain/KVM.pm +++ b/lib/Ravada/Domain/KVM.pm @@ -981,11 +981,15 @@ sub _post_shutdown_internal($self,@args) { } sub _remove_current_cpu($self) { + my ($is_active,$doc); + eval { + $is_active = $self->is_active if $self->domain; + $doc = XML::LibXML->load_xml(string => $self->domain->get_xml_description(Sys::Virt::Domain::XML_INACTIVE)) if $self->domain; + }; + warn $@ if $@; + return if $is_active || !$doc; - return if !$self->domain; - return if $self->is_active; - - my $doc = XML::LibXML->load_xml(string => $self->domain->get_xml_description(Sys::Virt::Domain::XML_INACTIVE)); + $doc = XML::LibXML->load_xml(string => $self->domain->get_xml_description(Sys::Virt::Domain::XML_INACTIVE)); my ($cpu_node) = $doc->findnodes('/domain/vcpu'); $cpu_node->removeAttribute('current'); From 2452bb0d234ea8771be28ba5ae57a72a997af98b Mon Sep 17 00:00:00 2001 From: Francesc Guasch Date: Thu, 7 Sep 2023 16:06:42 +0200 Subject: [PATCH 33/38] wip: do not render cpu when no XML --- lib/Ravada/Front/Domain/KVM.pm | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/lib/Ravada/Front/Domain/KVM.pm b/lib/Ravada/Front/Domain/KVM.pm index 8e88d86a8..ea290bace 100644 --- a/lib/Ravada/Front/Domain/KVM.pm +++ b/lib/Ravada/Front/Domain/KVM.pm @@ -176,7 +176,9 @@ sub _get_controller_generic($self,$type) { } sub _get_controller_cpu($self) { - my $doc = XML::LibXML->load_xml(string => $self->_data_extra('xml')); + my $xml = $self->_data_extra('xml'); + return if !$xml; + my $doc = XML::LibXML->load_xml(string => $xml); my $item = { _name => 'cpu' ,_order => 0 From 23ea911a72d16582c6b96f057bfde9e5958a2ddf Mon Sep 17 00:00:00 2001 From: Francesc Guasch Date: Fri, 8 Sep 2023 13:34:57 +0200 Subject: [PATCH 34/38] wip: improved tests --- lib/Ravada.pm | 2 ++ t/mojo/10_login.t | 36 +++++++++++++++++++++++++++--------- t/vm/20_base.t | 2 +- t/vm/92_ports.t | 5 ++--- 4 files changed, 32 insertions(+), 13 deletions(-) diff --git a/lib/Ravada.pm b/lib/Ravada.pm index fb2b5d3fe..af23b6d79 100644 --- a/lib/Ravada.pm +++ b/lib/Ravada.pm @@ -6517,6 +6517,8 @@ sub _cmd_close_exposed_ports($self, $request) { my $user = Ravada::Auth::SQL->search_by_id( $uid ) or die "Error: user $uid not found"; my $domain = Ravada::Domain->open($request->id_domain); + return if !$domain; + die "Error: user ".$user->name." not authorized to delete iptables rule" unless $user->is_admin || $domain->_data('id_owner') == $uid; diff --git a/t/mojo/10_login.t b/t/mojo/10_login.t index a4894063a..e9b4af26d 100644 --- a/t/mojo/10_login.t +++ b/t/mojo/10_login.t @@ -300,8 +300,8 @@ sub test_list_ldap_attributes($t, $expected_code=200) { sub test_login_non_admin_req($t, $base, $clone){ mojo_check_login($t, $USERNAME, $PASSWORD); + my $clone2; for ( 1 .. 3 ) { - my $clone2; if (!$clone->is_base) { mojo_request($t,"shutdown", {id_domain => $clone->id, timeout => 1 }); @@ -310,7 +310,7 @@ sub test_login_non_admin_req($t, $base, $clone){ .$t->tx->res->body() if $t->tx->res->code() != 200; - for ( 1 .. 10 ) { + for ( 1 .. 60 ) { $clone2 = rvd_front->search_domain($clone->name); last if $clone2->is_base || !$clone2->list_requests; _wait_request(debug => 1, background => 1, check_error => 1); @@ -318,8 +318,8 @@ sub test_login_non_admin_req($t, $base, $clone){ } last if $clone2->is_base; } - is($clone2->is_base,1,"Expecting ".$clone2->name." is base") or exit; } + is($clone2->is_base,1,"Expecting ".$clone2->name." is base") or exit; $clone->is_public(1); my $name = new_domain_name(); @@ -550,9 +550,12 @@ sub test_logout_ldap { sub _add_displays($t, $domain) { # mojo_request($t, "add_hardware", { id_domain => $base->id, name => 'network' }); + mojo_request($t, "refresh_machine", { id_domain => $domain->id }); my $info = $domain->info(user_admin); my $options = $info->{drivers}->{display}; for my $driver (@$options) { + wait_request(background => 1); + $info = $domain->info(user_admin); next if grep { $_->{driver} eq $driver } @{$info->{hardware}->{display}}; my $req = Ravada::Request->add_hardware( @@ -574,7 +577,7 @@ sub _clone_and_base($vm_name, $t, $base0) { die "Error: test base $BASE_NAME not found" if !$base; my $name = new_domain_name()."-".$vm_name."-$$"; mojo_request_url_post($t,"/machine/copy",{id_base => $base->id, new_name => $name, copy_ram => 0.128, copy_number => 1}); - for ( 1 .. 10 ) { + for ( 1 .. 60 ) { $base1 = rvd_front->search_domain($name); last if $base1; wait_request(); @@ -596,8 +599,15 @@ sub _clone_and_base($vm_name, $t, $base0) { } sub test_clone($base1) { - mojo_request($t,"prepare_base", {id_domain => $base1->id }) - if !$base1->is_base(); + if (!$base1->is_base()) { + mojo_request($t,"force_shutdown_domain", {id_domain => $base1->id }); + mojo_request($t,"prepare_base", {id_domain => $base1->id }); + } + + for ( 1 .. 10 ) { + last if $base1->is_base; + sleep 1; + } $t->get_ok("/machine/clone/".$base1->id.".html")->status_is(200); my $body = $t->tx->res->body; @@ -621,7 +631,7 @@ sub test_clone($base1) { like($clone->name, qr/^$clone_name/); ok($clone->name); is($clone->is_volatile,0) or exit; - is(scalar($clone->list_ports),2); + is(scalar($clone->list_ports),scalar($base1->list_ports)); return $clone; } @@ -731,8 +741,13 @@ sub test_new_machine_default($t, $vm_name, $empty_iso_file=undef) { my ($swap ) = grep { $_->{file} =~ /SWAP/ } @$disks; ok($swap,"Expecting a swap disk volume"); - my ($data) = grep { $_->{file} =~ /DATA/ } @$disks; - ok($data,"Expecting a data disk volume"); + my $data; + for ( 1 .. 10 ) { + ($data) = grep { $_->{file} =~ /DATA/ } @$disks; + last if $data; + sleep 1; + } + ok($data,"Expecting a data disk volume") or exit; my ($iso) = grep { $_->{file} =~ /iso$/ } @$disks; ok($iso,"Expecting an ISO cdrom disk volume"); @@ -761,6 +776,9 @@ sub test_new_machine_advanced_options($t, $vm_name, $swap=undef ,$data=undef) { } )->status_is(302); + wait_request(); + my $domain0 = rvd_front->search_domain($name); + mojo_request($t,"refresh_machine", {id_domain => $domain0->id }); wait_request(); my $domain = rvd_front->search_domain($name); diff --git a/t/vm/20_base.t b/t/vm/20_base.t index a87be9541..df26050d6 100644 --- a/t/vm/20_base.t +++ b/t/vm/20_base.t @@ -2007,7 +2007,7 @@ sub test_already_requested_working($vm) { wait_request( request => [ $req->id, $req2->id]); is($req->status, 'done'); is($req2->status, 'done'); - is($req->error, ''); + like($req->error,qr/waiting/) if $req->error; is($req2->error, ''); $domain->remove(user_admin); diff --git a/t/vm/92_ports.t b/t/vm/92_ports.t index 398919cbc..2c630f617 100644 --- a/t/vm/92_ports.t +++ b/t/vm/92_ports.t @@ -243,8 +243,7 @@ sub test_one_port($vm) { , 'to-destination' => $domain_ip.":".$internal_port ); last if $n_rule; - $domain->start(user => user_admin, remote_ip => $remote_ip - ,_force => 1); + $domain->start(user => user_admin, remote_ip => $remote_ip); wait_request(); } @@ -1265,7 +1264,7 @@ sub test_open_port_duplicated($vm) { sleep 1; } is($req->status,'done'); - is($req->error, '') or exit; + like($req->error,qr/checking /) if $req->error; my (@out3,@open3); for ( 1 .. 5 ) { From 78954603c73404439da8e12b99deb5845bdf6b82 Mon Sep 17 00:00:00 2001 From: Francesc Guasch Date: Thu, 14 Sep 2023 10:41:57 +0200 Subject: [PATCH 35/38] fix: allow volumes without extension (#1978) * fix: allow volumes without extension * test: volume without extension --- lib/Ravada/Domain.pm | 1 + lib/Ravada/Domain/KVM.pm | 6 +++- lib/Ravada/Volume.pm | 5 +-- lib/Ravada/Volume/Void.pm | 4 ++- t/20_volumes.t | 73 +++++++++++++++++++++++++++++++++++++++ t/kvm/40_import.t | 48 +++++++++++++++++++++---- t/mojo/10_login.t | 3 +- t/vm/40_volumes.t | 5 ++- 8 files changed, 130 insertions(+), 15 deletions(-) diff --git a/lib/Ravada/Domain.pm b/lib/Ravada/Domain.pm index b096827b4..f35d21220 100644 --- a/lib/Ravada/Domain.pm +++ b/lib/Ravada/Domain.pm @@ -4528,6 +4528,7 @@ Check if the domain has swap volumes defined, and clean them sub clean_swap_volumes { my $self = shift; + return if $self->is_active(); for my $vol ( $self->list_volumes_info) { confess if !$vol->domain; if ($vol->file && $vol->file =~ /\.SWAP\.\w+$/) { diff --git a/lib/Ravada/Domain/KVM.pm b/lib/Ravada/Domain/KVM.pm index b04f4f04b..aeb5e05b2 100644 --- a/lib/Ravada/Domain/KVM.pm +++ b/lib/Ravada/Domain/KVM.pm @@ -153,7 +153,10 @@ sub list_disks { for my $child ($disk->childNodes) { if ($child->nodeName eq 'source') { my $file = $child->getAttribute('file'); - next if $file =~ /\.iso$/; + if (!$file) { + $file = $child->getAttribute('name'); + } + next if !$file || $file =~ /\.iso$/; push @disks,($file); } } @@ -660,6 +663,7 @@ sub _detect_disks_driver($self) { my ( $source ) = $disk->findnodes('source'); my $file = $source->getAttribute('file'); + next if !$file; next if $file =~ /iso$/; next unless $self->_vm->file_exists($file); diff --git a/lib/Ravada/Volume.pm b/lib/Ravada/Volume.pm index ae2841236..d0d450c3c 100644 --- a/lib/Ravada/Volume.pm +++ b/lib/Ravada/Volume.pm @@ -76,12 +76,13 @@ sub _type_from_file($file, $vm) { my ($out, $err) = $vm->run_command("file","-L",$file); return 'QCOW2' if $out =~ /QEMU QCOW/; + return 'Void' if $out =~ /ASCII text/; return 'RAW'; } sub _type_from_extension($file) { my ($ext) = $file =~ m{.*\.(.*)}; - confess if !defined $ext; + return if !defined $ext; confess if $ext =~ /-/; my %type = ( void => 'Void' @@ -95,7 +96,7 @@ sub _type_from_extension($file) { sub _type($file,$vm = undef) { return _type_from_file($file,$vm) if $vm; - return _type_from_extension($file); + return (_type_from_extension($file) or 'QCOW2'); } sub BUILD($self, $arg) { diff --git a/lib/Ravada/Volume/Void.pm b/lib/Ravada/Volume/Void.pm index 83cd1fb55..33a688ed0 100644 --- a/lib/Ravada/Volume/Void.pm +++ b/lib/Ravada/Volume/Void.pm @@ -33,7 +33,9 @@ sub prepare_base($self) { } sub capacity($self) { - my $info = $self->_load(); + my $info = {}; + eval { $info = $self->_load(); }; + warn $@ if $@; confess "Unknown capacity for ".$self->file.Dumper($info) if !exists $info->{capacity}; diff --git a/t/20_volumes.t b/t/20_volumes.t index 6d41a163c..76daaed9d 100644 --- a/t/20_volumes.t +++ b/t/20_volumes.t @@ -363,6 +363,77 @@ sub test_defaults($vm, $volume_type=undef) { $domain->remove(user_admin); } +sub remove_extension($domain, $vol) { + if ($domain->type eq 'KVM') { + _remove_extension_kvm($domain,$vol); + } elsif ($domain->type eq 'Void') { + _remove_extension_void($domain,$vol); + } else { + die "I don't know how to rename ".$domain->type; + } + +} + +sub _remove_extension_void($domain, $vol) { + my $yml = $domain->_load(); + my $hw = $yml->{hardware}; + for my $dev ( @{$yml->{hardware}->{device}}) { + if ($dev->{file} eq $vol) { + $dev->{file} =~ s/(.*)\.\w+/$1/; + $dev->{file} =~ s/\./_/g; + $dev->{name} =~ s/(.*)\.\w+/$1/; + $domain->_store(hardware => $yml->{hardware}); + rename($vol, $dev->{file}) + or die "$! $vol -> $dev->{file}"; + } + } +} + +sub _remove_extension_kvm($domain, $vol) { + my $doc = XML::LibXML->load_xml(string => $domain->domain->get_xml_description); + for my $disk ($doc->findnodes('/domain/devices/disk')) { + my ($source_node) = $disk->findnodes('source'); + my $file; + if ( $source_node ) { + my $file = $source_node->getAttribute('file'); + if ($file && $file eq $vol) { + $file =~ s/(.*)\.\w+/$1/; + $file =~ s/\./_/g; + + $source_node->setAttribute('file' => $file); + rename($vol, $file) + or die "$! $vol -> $file"; + } + } + } + $domain->reload_config($doc); +} + +sub test_no_extension($vm) { + my $base = create_domain($vm); + $base->add_volume(type => 'swap', size => 1024*1024); + $base->add_volume(type => 'data', size => 1024*1024); + + my @vols = $base->list_volumes(); + + for my $vol ( @vols ) { + next if $vol =~ /\.iso$/; + remove_extension($base, $vol); + } + my $sth = connector->dbh->prepare( + "DELETE FROM volumes WHERE id_domain=?" + ); + $sth->execute($base->id); + + my $info = $base->info(user_admin); + my @vols2 = $base->list_volumes_info(); + + for my $vol (@vols2) { + my $ref = ref($vol); + unlike($ref,qr/HASH|RAW$/); + } +} + sub test_qcow_format($vm) { return if $vm->type ne 'KVM'; my $base = create_domain($vm); @@ -441,6 +512,8 @@ for my $vm_name (reverse vm_names() ) { diag("Testing volumes in $vm_name"); init_vm($vm); + test_no_extension($vm); + test_qcow_format($vm); test_raw($vm); diff --git a/t/kvm/40_import.t b/t/kvm/40_import.t index c8fe26f47..37a1910fe 100644 --- a/t/kvm/40_import.t +++ b/t/kvm/40_import.t @@ -153,15 +153,45 @@ sub test_import_spinoff { } sub _create_vol($vm, $name) { -my @cmd = ("qemu-img",'create','-f','qcow2', $vm->dir_img."/".$name,'512M'); -my ($in, $out, $err); -run3(\@cmd , \$in, \$out, \$err); -die $err if $err; -diag($out) if $out; + my $sp = $vm->vm->get_storage_pool_by_name('default'); + + my $old_vol = $sp->get_volume_by_name($name); + $old_vol->delete() if $old_vol; + + my $xml = < + $name + /var/lib/libvirt/images/$name + 21474836480 + 3485696 + 21478375424 + + /var/lib/libvirt/images/$name + + + 0644 + 0 + 0 + + + 1538407038.168298505 + 1538406915.308849295 + 1538407050.036621775 + + 1.1 + + + + + +EOT + + $sp->create_volume($xml); } sub test_volume($vm) { + return if $vm->type ne 'KVM'; my $dom_name = new_domain_name(); my $vol_name = new_domain_name(); _create_vol($vm, $vol_name); @@ -228,9 +258,9 @@ my $xml =<
- + - +
@@ -274,6 +304,10 @@ EOT is(''.$@,'') or exit; ok($domain,"Importing domain $dom_name") or exit; + my $domain_f = Ravada::Front::Domain->open($domain->id); + $domain_f->info(user_admin); + + $domain->remove(user_admin); } diff --git a/t/mojo/10_login.t b/t/mojo/10_login.t index e9b4af26d..15341eed7 100644 --- a/t/mojo/10_login.t +++ b/t/mojo/10_login.t @@ -577,12 +577,13 @@ sub _clone_and_base($vm_name, $t, $base0) { die "Error: test base $BASE_NAME not found" if !$base; my $name = new_domain_name()."-".$vm_name."-$$"; mojo_request_url_post($t,"/machine/copy",{id_base => $base->id, new_name => $name, copy_ram => 0.128, copy_number => 1}); + for ( 1 .. 60 ) { $base1 = rvd_front->search_domain($name); last if $base1; wait_request(); } - ok($base1, "Expecting domain $name create") or exit; + ok($base1, "Expecting domain $name created") or exit; } mojo_check_login($t); diff --git a/t/vm/40_volumes.t b/t/vm/40_volumes.t index 4b4666b2b..d12527180 100644 --- a/t/vm/40_volumes.t +++ b/t/vm/40_volumes.t @@ -5,6 +5,7 @@ use Carp qw(confess croak); use Data::Dumper; use File::Copy; use Test::More; +use YAML qw(DumpFile); use v5.22; use feature qw(signatures); no warnings "experimental::signatures"; @@ -300,9 +301,7 @@ sub test_add_volume_path { my $file_path = $vm->dir_img."/mock.img"; - open my $out,'>',$file_path or die "$! $file_path"; - print $out "hi\n"; - close $out; + DumpFile($file_path, {capacity => 100, data => 'hi'}); $domain->add_volume(file => $file_path); From 6db872a5844b5f7180b567328d2212603f6684d6 Mon Sep 17 00:00:00 2001 From: Francesc Guasch Date: Fri, 15 Sep 2023 12:40:43 +0200 Subject: [PATCH 36/38] refactor(test): verify still logged in --- t/mojo/10_login.t | 1 + 1 file changed, 1 insertion(+) diff --git a/t/mojo/10_login.t b/t/mojo/10_login.t index 15341eed7..a13ccf7eb 100644 --- a/t/mojo/10_login.t +++ b/t/mojo/10_login.t @@ -974,6 +974,7 @@ sub test_clone_same_name($t, $base) { last if $cloneb->is_base; sleep 1; } + mojo_check_login($t); $t->get_ok("/machine/clone/".$base->id.".html") ->status_is(200); From 5fe0f053d21a4c824cb160f542ad82dfcb837a53 Mon Sep 17 00:00:00 2001 From: Francesc Guasch Date: Wed, 20 Sep 2023 10:12:59 +0200 Subject: [PATCH 37/38] fix: properly check free storage size (#1980) fix: properly check free storage size --- lib/Ravada/Domain.pm | 13 ++++++------ t/lib/Test/Ravada.pm | 9 ++++++--- t/vm/20_base.t | 24 +++++++++++----------- t/vm/s30_storage.t | 48 ++++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 73 insertions(+), 21 deletions(-) diff --git a/lib/Ravada/Domain.pm b/lib/Ravada/Domain.pm index f35d21220..75fd05df2 100644 --- a/lib/Ravada/Domain.pm +++ b/lib/Ravada/Domain.pm @@ -704,7 +704,9 @@ sub _around_add_volume { ($name) = $file =~ m{.*/(.*)} if !$name && $file; $name = $self->name if !$name; - $name .= "-".$args{target}."-".Ravada::Utils::random_name(4); + $name .= "-".$args{target}."-".Ravada::Utils::random_name(4) + if $name !~ /\.iso$/; + $args{name} = $name; } @@ -717,10 +719,12 @@ sub _around_add_volume { $args{allocation} = Ravada::Utils::size_to_number($args{allocation}) if exists $args{allocation} && defined $args{allocation}; - my $free = $self->_vm->free_disk(); + my $storage = $args{storage}; + + my $free = $self->_vm->free_disk($storage); my $free_out = int($free / 1024 / 1024 / 1024 ) * 1024 *1024 *1024; - confess "Error creating volume, out of space $size . Disk free: " + die "Error creating volume, out of space $size . Disk free: " .Ravada::Utils::number_to_size($free_out) ."\n" if exists $args{size} && $args{size} && $args{size} >= $free; @@ -1945,9 +1949,6 @@ sub display($self, $user) { my ($display_info) = grep { $_->{driver} !~ /-tls$/ } @display_info; - confess "Error: I can't find builtin display info for ".$self->name." ".ref($self)."\n".Dumper($display_info) - if !exists $display_info->{port}; - return '' if !$display_info->{driver} || !$display_info->{ip} || !$display_info->{port}; diff --git a/t/lib/Test/Ravada.pm b/t/lib/Test/Ravada.pm index b0be1e64d..d693d7232 100644 --- a/t/lib/Test/Ravada.pm +++ b/t/lib/Test/Ravada.pm @@ -86,6 +86,8 @@ create_domain remove_old_users remove_old_users_ldap + remove_qemu_pools + mangle_volume test_volume_contents test_volume_format @@ -273,6 +275,7 @@ sub create_domain_v2(%args) { my $user = (delete $args{user} or $USER_ADMIN); my $iso_name = delete $args{iso_name}; my $id_iso = delete $args{id_iso}; + my $disk = delete $args{disk}; croak "Error: supply either iso_name or id_iso ".Dumper(\%args) if $iso_name && $id_iso; @@ -298,13 +301,12 @@ sub create_domain_v2(%args) { if keys %args; my $iso; - my $disk; if ($vm->type eq 'KVM' && (!$iso_name || $iso_name !~ /Alpine/i)) { $iso = $vm->_search_iso($id_iso); - $disk = ($iso->{min_disk_size} or 2 ); + $disk = ($iso->{min_disk_size} or 2 ) if !$disk; diag("Creating [$id_iso] $iso->{name}"); } else { - $disk = 2; + $disk = 2 if !$disk; } if ($vm->type eq 'KVM' && !$options ) { @@ -1510,6 +1512,7 @@ sub remove_old_storage_pools_void() { } sub remove_old_storage_pools_kvm() { + remove_qemu_pools(); } sub remove_old_storage_pools() { diff --git a/t/vm/20_base.t b/t/vm/20_base.t index df26050d6..fe9d12073 100644 --- a/t/vm/20_base.t +++ b/t/vm/20_base.t @@ -300,17 +300,15 @@ sub test_displays_added_on_refresh($domain, $n_expected, $delete=1) { _wait_ip($domain); + my $domain_f0 = Ravada::Front::Domain->open($domain->id); + my $display0_raw = $domain_f0->info(user_admin)->{hardware}->{display}; + + my @display0 = grep { $_->{is_secondary} == 0 } @$display0_raw; + my $n_expected2 = scalar(@display0); + if ($delete) { my $sth = connector->dbh->prepare("DELETE FROM domain_displays WHERE id_domain=?"); $sth->execute($domain->id); - }else { - my $sth = connector->dbh->prepare("SELECT count(*) FROM domain_displays WHERE id_domain=?"); - $sth->execute($domain->id); - my ($n_exp2) = $sth->fetchrow; - if ($n_exp2 && $n_expected != $n_exp2) { - cluck "n_expected was $n_expected, but it should be $n_exp2"; - $n_expected = $n_exp2; - } } my $req; for ( 1 .. 3 ) { @@ -333,7 +331,7 @@ sub test_displays_added_on_refresh($domain, $n_expected, $delete=1) { my $count; for ( 1 .. 10 ) { my $sth_count = connector->dbh->prepare( - "SELECT count(*) FROM domain_displays WHERE id_domain=?"); + "SELECT count(*) FROM domain_displays WHERE id_domain=? AND is_secondary=0"); $sth_count->execute($domain->id); ($count) = $sth_count->fetchrow; last if $count; @@ -344,11 +342,13 @@ sub test_displays_added_on_refresh($domain, $n_expected, $delete=1) { ); wait_request(); } - ok($count>=$n_expected,"Got $count, expecting >$n_expected displays on table domain_displays for ".$domain->name) or confess; + ok($count>=$n_expected || $count >=$n_expected2,"Got $count, expecting >$n_expected or $n_expected2 displays on table domain_displays for ".$domain->name) or confess; my $domain_f = Ravada::Front::Domain->open($domain->id); - my $display = $domain_f->info(user_admin)->{hardware}->{display}; - is(scalar(@$display), $n_expected,"Expecting $n_expected displays on info->{hardware}->{display} in ".$domain->name) or confess Dumper($display); + my $display_raw = $domain_f->info(user_admin)->{hardware}->{display}; + my @display = grep { $_->{is_secondary} == 0 } @$display_raw; + ok(scalar(@display) == $n_expected || scalar(@display) == $n_expected2 + ,"Expecting $n_expected or $n_expected2 displays on info->{hardware}->{display} in ".$domain->name) or confess Dumper(@display); } diff --git a/t/vm/s30_storage.t b/t/vm/s30_storage.t index 2ea8e4b46..a13f2de23 100644 --- a/t/vm/s30_storage.t +++ b/t/vm/s30_storage.t @@ -85,6 +85,48 @@ sub _clean_local { my $file = "$dir/check_storage";# or die "$!"; unlink $file or die "$! $file" if -e $file; } + +sub test_storage_full($vm) { + + my $dir = "/run"; + $dir.="/user/".$< if $<; + my $storage_name = new_domain_name(); + + $dir.= "/".$storage_name; + + mkdir $dir or die "$! $dir" if ! -e $dir; + + if (! grep { $_ eq $storage_name} $vm->list_storage_pools) { + $vm->create_storage_pool($storage_name,$dir); + } + + my($out,$err) = $vm->run_command("df"); + + my ($available) = $out =~ m{(\d+)\s+\d+\% /run}ms; + + $available = $available*10; + + $vm->default_storage_pool_name($storage_name); + + my $name = new_domain_name; + my $req = Ravada::Request->create_domain( + vm => $vm->type + ,id_owner => user_admin->id + ,name => $name + ,disk => $available + ,storage => 'default' + ,id_iso => search_id_iso('Alpine%64') + ); + wait_request(debug => 0); + + my $domain = $vm->search_domain($name); + for my $vol ($domain->list_volumes) { + next if $vol =~ /iso$/; + unlike($vol,qr{^/run}); + } + +} + ########################################################### _clean_local(); @@ -101,6 +143,12 @@ for my $vm_name (vm_names() ) { } skip($msg,10) if !$vm; + + if ($vm->type eq 'KVM') { + remove_qemu_pools($vm); + } + test_storage_full($vm); + test_storage_pools($vm); test_storage_pools_fail($vm); } From 125b2dc4b94f194ecfef9fdc0bc417a4e0fa2b57 Mon Sep 17 00:00:00 2001 From: Francesc Guasch Date: Wed, 12 Apr 2023 13:28:46 +0200 Subject: [PATCH 38/38] fix: SSO user management --- lib/Ravada/Auth.pm | 1 + lib/Ravada/Auth/SSO.pm | 6 ++++++ templates/main/manage_user.html.ep | 2 +- 3 files changed, 8 insertions(+), 1 deletion(-) diff --git a/lib/Ravada/Auth.pm b/lib/Ravada/Auth.pm index 10109c4f0..6fcb8a071 100644 --- a/lib/Ravada/Auth.pm +++ b/lib/Ravada/Auth.pm @@ -6,6 +6,7 @@ use strict; our $LDAP_OK; our $SSO_OK; +use Data::Dumper; use Ravada::Auth::SQL; =head1 NAME diff --git a/lib/Ravada/Auth/SSO.pm b/lib/Ravada/Auth/SSO.pm index 240cd1aa4..c01043ed6 100644 --- a/lib/Ravada/Auth/SSO.pm +++ b/lib/Ravada/Auth/SSO.pm @@ -72,6 +72,7 @@ sub _get_session_userid_by_ticket my ($cookie) = @_; my $result; die 'Can\'t read pubkey file (sso->cookie->pub_key value at ravada.conf file)' if (! -r $$CONFIG->{sso}->{cookie}->{pub_key}); + eval { $result = Authen::ModAuthPubTkt::pubtkt_verify(publickey => $$CONFIG->{sso}->{cookie}->{pub_key}, keytype => $$CONFIG->{sso}->{cookie}->{type}, ticket => $cookie); }; die $@ ? $@ : 'Cannot validate ticket' if ((! $result) || ($@)); my %data = Authen::ModAuthPubTkt::pubtkt_parse($cookie); @@ -123,6 +124,11 @@ sub init { return 0; } } + if (!$$CONFIG->{sso}->{cookie}->{type}) { + $ERR = "Error: missing sso / cookie / type in config file\n"; + warn $ERR unless $warn++; + return 0; + } for my $field (qw(priv_key pub_key)) { if ( !exists $$CONFIG->{sso}->{cookie}->{$field} || ! $$CONFIG->{sso}->{cookie}->{$field}) { diff --git a/templates/main/manage_user.html.ep b/templates/main/manage_user.html.ep index 8d784a992..18e49a446 100644 --- a/templates/main/manage_user.html.ep +++ b/templates/main/manage_user.html.ep @@ -13,7 +13,7 @@ (<%= ($user->external_auth or $origin) %>) % } -% if ($user->external_auth && $user->ldap_entry ) { +% if ($user->external_auth && $user->external_auth eq 'ldap' && $user->ldap_entry ) { <%= $user->ldap_entry->dn %> % }