Unverified Commit 759c4a42 authored by Francesc Guasch's avatar Francesc Guasch Committed by GitHub
Browse files

Feat: cpu settings (#1736)

featt: manage CPU and features settings

closes #1726
parent 9a49862c
......@@ -926,6 +926,43 @@ sub _add_domain_drivers_display($self) {
}
}
sub _add_domain_drivers_cpu($self) {
my %data = (
'KVM' => [
'custom'
,'host-model'
,'host-passthrough'
]
);
my $id_type = Ravada::Utils::max_id($CONNECTOR->dbh, 'domain_drivers_types')+1;
my $id_option = Ravada::Utils::max_id($CONNECTOR->dbh, 'domain_drivers_options');
for my $vm ( keys %data) {
my $type = {
id => $id_type
,name => 'cpu'
,description => 'CPU'
,vm => $vm
};
$self->_update_table('domain_drivers_types','name,vm',$type)
and do {
for my $option ( @{$data{$vm}} ) {
if (!ref($option)) {
$option = { name => $option
,value => $option
};
}
$option->{value} = $option->{name} if !exists $option->{value};
$option->{id_driver_type} = $id_type;
$option->{id} = ++$id_option;
$self->_update_table('domain_drivers_options','id_driver_type,name',$option)
}
$id_type++;
};
}
}
sub _update_domain_drivers_types($self) {
my $data = {
......@@ -1299,6 +1336,7 @@ sub _update_data {
$self->_update_old_qemus();
$self->_add_domain_drivers_display();
$self->_add_domain_drivers_cpu();
$self->_add_indexes();
}
......@@ -4967,6 +5005,20 @@ sub _cmd_list_machine_types($self, $request) {
$request->output(encode_json(\%out));
}
sub _cmd_list_cpu_models($self, $request) {
my $id_domain = $request->args('id_domain');
my $domain = Ravada::Domain->open($id_domain);
my $info = $domain->get_info();
warn Dumper($info);
my $vm = $domain->_vm->vm;
my @out = $vm->get_cpu_model_names('x86_64');
$request->output(encode_json(\@out));
}
sub _cmd_set_time($self, $request) {
my $id_domain = $request->args('id_domain');
my $domain = Ravada::Domain->open($id_domain)
......@@ -5534,6 +5586,7 @@ sub _req_method {
,open_iptables => \&_cmd_open_iptables
,list_vm_types => \&_cmd_list_vm_types
,list_machine_types => \&_cmd_list_machine_types
,list_cpu_models => \&_cmd_list_cpu_models
,enforce_limits => \&_cmd_enforce_limits
,force_shutdown => \&_cmd_force_shutdown
,force_reboot => \&_cmd_force_reboot
......
......@@ -4388,24 +4388,10 @@ sub get_controllers($self) {
for my $name ( sort keys %controllers ) {
$info->{$name} = [$self->get_controller($name)];
}
my %sub = (
);
for my $name ( sort $self->_list_hardware_common ) {
confess "Error: hardware $name already parsed in ".$self->name
if $info->{$name};
my $sub = $sub{$name} or confess "Error: no sub for $name ".Dumper(\%sub);
$info->{$name} = $sub->($self);
}
return $info;
}
sub _list_hardware_common($self) {
# return ('display');
}
=head2 drivers
List the drivers available for a domain. It may filter for a given type.
......
......@@ -90,7 +90,9 @@ our %REMOVE_CONTROLLER_SUB = (
our %CHANGE_HARDWARE_SUB = (
disk => \&_change_hardware_disk
,cpu => \&_change_hardware_cpu
,display => \&_change_hardware_display
,features => \&_change_hardware_features
,vcpus => \&_change_hardware_vcpus
,memory => \&_change_hardware_memory
,network => \&_change_hardware_network
......@@ -2775,6 +2777,103 @@ sub _fix_hw_video_args($data) {
delete $data->{acceleration} unless $driver eq 'virtio';
}
sub _change_hardware_features($self, $index, $data) {
$data = { 'acpi' => 1, 'apic' => 1, 'kvm' => undef, 'hap' => 0 }
if !keys %$data;
$data->{kvm} = {hidden=> { state => 'off'}} if exists $data->{kvm} && $data->{kvm} == 1;
lock_hash(%$data);
my $doc = XML::LibXML->load_xml(string => $self->xml_description);
my $count = 0;
my $changed = 0;
my ($features) = $doc->findnodes('/domain/features');
for my $field (keys %$data) {
next if $field =~ /^_/;
my ($item) = $features->findnodes($field);
next if !$item && !$data->{$field};
if (ref($data->{$field})) {
if (!$item) {
$item = $features->addNewChild(undef,$field);
$changed++;
}
_change_xml($features,$field,$data->{$field});
$changed++;
}
if (!$item) {
$item = $features->addNewChild(undef,$field);
$changed++;
} elsif (!$data->{$field}) {
$features->removeChild($item);
$changed++;
}
}
$self->reload_config($doc) if $changed;
}
sub _default_cpu($self) {
my $doc = XML::LibXML->load_xml(string => $self->xml_description);
my ($type) = $doc->findnodes("/domain/os/type");
my $data = {
'vcpu'=> {_text => 1 , 'placement' => 'static'}
,'cpu' => { 'model' => { '_text' => 'qemu64' } }
};
my ($x86) = $type->getAttribute('arch') =~ /^x86_(\d+)/;
if ($x86) {
$data->{cpu} = { 'mode' =>'custom'
, 'model' => { '_text' => 'qemu'.$x86 } };
} else {
warn "I don't know default CPU for arch ".$type->getAttribute()
." in domain ".$self->name;
$data->{cpu} = { 'mode' => 'host-model' };
}
return $data;
}
sub _change_hardware_cpu($self, $index, $data) {
$data = $self->_default_cpu()
if !keys %$data;
lock_hash(%$data);
delete $data->{cpu}->{model}->{'$$hashKey'};
my $doc = XML::LibXML->load_xml(string => $self->xml_description);
my $count = 0;
my $changed = 0;
my ($n_vcpu) = $doc->findnodes('/domain/vcpu/text()');
if ($n_vcpu ne $data->{vcpu}->{_text}) {
my ($vcpu) = $doc->findnodes('/domain/vcpu');
$vcpu->removeChildNodes();
$vcpu->appendText($data->{vcpu}->{_text});
}
my ($cpu) = $doc->findnodes('/domain/cpu');
for my $field (keys %{$data->{cpu}}) {
if (ref($data->{cpu}->{$field})) {
_change_xml($cpu, $field, $data->{cpu}->{$field});
$changed++;
next;
}
if ( !defined $cpu->getAttribute($field)
|| $cpu->getAttribute($field) ne $data->{cpu}->{$field}) {
$cpu->setAttribute($field, $data->{cpu}->{$field});
$changed++;
}
}
$self->reload_config($doc) if $changed;
}
sub _change_hardware_sound($self, $index, $data) {
confess "Error: nothing to change ".Dumper($data)
if !keys %$data;
......@@ -2859,8 +2958,19 @@ sub _change_xml($xml, $name, $data) {
my ($node) = $xml->findnodes($name);
$node = $xml->addNewChild(undef,$name) if !$node;
confess Dumper([$name, $data]) if !ref($data) || ref($data) ne 'HASH';
my $text = delete $data->{_text};
if ($text) {
$node->removeChildNodes();
$node->appendText($text);
}
for my $field (keys %$data) {
$node->setAttribute($field, $data->{$field});
if (ref($data->{$field})) {
_change_xml($node,$field,$data->{$field});
} else {
$node->setAttribute($field, $data->{$field});
}
}
}
......
......@@ -1524,6 +1524,38 @@ sub list_machine_types($self, $uid, $vm_type) {
return $types;
}
=head2 list_cpu_models
Returns a reference to a list of the CPU models
=cut
sub list_cpu_models($self, $uid, $id_domain) {
my $key="list_cpu_models";
my $dom = Ravada::Front::Domain->open($id_domain);
$key.='#'.$dom->type;
my $cache = $self->_cache_get($key);
return $cache if $cache;
my $req = Ravada::Request->list_cpu_models(
id_domain => $id_domain
,uid => $uid
);
return {} if !$req;
$self->wait_request($req);
return {} if $req->status ne 'done';
my $models= {};
$models = decode_json($req->output()) if $req->output;
$self->_cache_store($key,$models);
return $models;
}
=head2 version
......
......@@ -14,8 +14,10 @@ use feature qw(signatures);
our %GET_CONTROLLER_SUB = (
usb => \&_get_controller_usb
,'0cpu' => \&_get_controller_cpu
,disk => \&_get_controller_disk
,display => \&_get_controller_display
,'1features' => \&_get_controller_features
,network => \&_get_controller_network
,video => \&_get_controller_video
,sound => \&_get_controller_sound
......@@ -105,8 +107,48 @@ sub _get_controller_generic($self,$type) {
}
sub _get_controller_cpu($self) {
my $doc = XML::LibXML->load_xml(string => $self->_data_extra('xml'));
my $item = {
_name => 'cpu'
,_order => 0
,cpu => {}
,vcpu => {}
};
my ($xml_cpu) = $doc->findnodes("/domain/cpu");
_xml_elements($xml_cpu, $item->{cpu});
my ($xml_vcpu) = $doc->findnodes("/domain/vcpu");
_xml_elements($xml_vcpu, $item->{vcpu});
lock_hash(%$item);
return ($item);
}
sub _get_controller_features($self) {
my $doc = XML::LibXML->load_xml(string => $self->_data_extra('xml'));
my $item = {
_name => 'features'
,_order => 1
};
my ($xml) = $doc->findnodes("/domain/features");
_xml_elements($xml, $item);
for my $feat (sort qw(acpi pae apic hap kvm vmport)) {
$item->{$feat} = 0 if !exists $item->{$feat};
}
lock_hash(%$item);
return ($item);
}
sub _xml_elements($xml, $item) {
my $text = $xml->textContent;
$item->{_text} = $text if $text && $text !~ /\n/m;
for my $attribute ( $xml->attributes ) {
$item->{$attribute->name} = $attribute->value;
}
......@@ -114,6 +156,7 @@ sub _xml_elements($xml, $item) {
for my $node ( $xml->findnodes('*') ) {
my $h_node = {};
_xml_elements($node, $h_node);
$h_node = 1 if !keys %$h_node;
$item->{$node->nodeName} = $h_node;
}
}
......
......@@ -127,6 +127,7 @@ our %VALID_ARG = (
,purge => { uid => 1, id_domain => 1 }
,list_machine_types => { uid => 1, id_vm => 2, vm_type => 2}
,list_cpu_models => { uid => 1, id_domain => 1}
#users
,post_login => { user => 1, locale => 2 }
......
......@@ -451,6 +451,7 @@
$scope.list_ldap_attributes();
list_ldap_groups();
}
$scope.list_cpu_models();
};
var list_interfaces = function() {
......@@ -831,10 +832,10 @@
$scope.change_hardware= function(item,hardware,index) {
var new_settings = $scope.showmachine.hardware[hardware][index];
delete new_settings._edit;
delete new_settings._name;
var hw2 = hardware.replace(/\d+(.*)/,'$1');
$scope.request('change_hardware',
{'id_domain': $scope.showmachine.id
,'hardware': hardware
,'hardware': hw2
,'index': index
,'data': new_settings
}
......@@ -963,6 +964,14 @@
list_access_groups();
});
};
$scope.list_cpu_models = function() {
$http.get("/list_cpu_models.json?id_domain="
+$scope.showmachineId).then(function(response) {
$scope.cpu_models=response.data;
});
};
$scope.message = [];
$scope.disk_remove = [];
$scope.pending_before = 10;
......
......@@ -609,6 +609,18 @@ get '/list_machine_types.json' => sub {
$c->render(json => $types);
};
get '/list_cpu_models.json' => sub {
my $c = shift;
return access_denied($c) unless _logged_in($c)
&& $USER->can_create_machine();
my $id_domain = $c->param('id_domain');
my $models = $RAVADA->list_cpu_models($USER->id, $id_domain);
$c->render(json => $models);
};
get '/iso_file.json' => sub {
my $c = shift;
......@@ -1553,6 +1565,8 @@ post '/request/(:name)/' => sub {
delete($args->{exec_sequentially});
delete($args->{at}) if ((! $args->{at}) || ($args->{at} < time()));
delete $args->{data}->{_name} if $name eq 'change_hardware';
if ($name eq 'start_clones_sequentially') {
my $domain = $RAVADA->search_domain_by_id($args->{'id_domain'}) or do {
......
<div ng-show="name=='0cpu' && is_edit(name,$index)">
<div class="p-2">
<label for="vcpu"><%=l 'vCPU allocation' %></label>
<input type="text" ng-model="item.vcpu._text" name="vcpu"/>
</div>
<ul class="list-group list-group-horizontal-md">
<li class="list-group-item list-group-item-primary"><%=l 'mode' %></li>
<li class="list-group-item">
<select ng-model="item.cpu.mode"
ng-options="mode.toLowerCase() for mode in showmachine.drivers['cpu']"
>
</select>
</li>
<li class="list-group-item list-group-item-primary"><%=l 'check' %></li>
<li class="list-group-item">
<select ng-model="item.cpu.check"
ng-options="check for check in ['none','partial','full']"
>
</select>
</li>
<li ng-show="item.cpu.mode=='custom'"
class="list-group-item list-group-item-primary"><%=l 'match' %></li>
<li ng-show="item.cpu.mode=='custom'"
class="list-group-item">
<select ng-model="item.cpu.match"
ng-options="match for match in ['exact','strict']"
>
</select>
</li>
</ul>
<div class="p-2" ng-show="item.cpu.mode=='custom'">
<label for="model">
<%=l 'model' %>
</label>
<select ng-model="item.cpu.model._text" name="model"
ng-options="cpu for cpu in cpu_models"
>
</select>
<label for="fallback">
<%=l 'fallback' %>
</label>
<select ng-model="item.cpu.model.fallback" name="fallback"
ng-options="option for option in ['allow','forbid']"
>
</select>
</div>
</div>
<div ng-show="name=='1features' && is_edit(name,$index)">
<div ng-repeat="(feat,value) in item "
ng-show="feat.substr(0,1) != '_'"
>
<b>{{feat}}:</b>
<input type="checkbox" ng-model="item[feat]"
ng-true-value="1" ng-false-value="0"
ng-show="value=='0' || value=='1'"
/>
<span ng-show="(feat=='vmport' || feat == 'hap')
&& ( value != '1' && value != '0' )">
state
<select ng-model="item[feat].state" >
<option>on</option>
<option>off</option>
</select>
</span>
<span ng-show="feat == 'kvm' && value != '1' && value != '0'">
hidden
<select ng-model="item[feat].hidden.state">
<option>on</option>
<option>off</option>
</select>
</span>
<small>
<button type="badge"
ng-show="(feat=='kvm' || feat=='hap' || feat=='vmport')
&& value !='0' && value!='1'"
ng-click="item[feat]=0"
>
<span aria-hidden="true">&times;</span>
</button>
</small>
</div>
</div>
......@@ -9,24 +9,24 @@
<div class="panel-body">
%= include "main/needs_restart"
<div ng-repeat="(name,value) in showmachine.hardware" ng-hide="show_new_disk||show_new_display">
<h2>{{name}}</h2>
<h2 ng-hide="name=='0cpu' || name=='1features'">{{name}}</h2>
<div ng-show="value">
<div ng-repeat="item in value track by $index"
ng-class='{"border rounded border-primary mb-4 p-4": item._edit}'
>
<button title="edit {{name}} {{$index+1}}"
ng-show="(name == 'network' ||name == 'disk' || name == 'video' || name == 'sound') && !item._edit && !item.remove"
ng-show="(name == 'network' ||name == 'disk' || name == 'video' || name == 'sound' || name=='0cpu' || name=='1features') && !item._edit && !item.remove"
ng-disabled="showmachine.requests > 0 || showmachine.is_active || showmachine.is_base"
ng-click="toggle_edit(item)"
><i class="fas fa-pencil-alt"></i>
</button>
<button title="remove {{name}} {{$index+1}}"
ng-hide="item._edit"
ng-hide="item._edit || name=='0cpu' || name=='1features'"
ng-disabled="showmachine.requests > 0 || item.is_secondary || showmachine.is_active || showmachine.is_base"
ng-click="confirm_remove='';remove_hardware(name,$index,item)"><i class="fa fa-times"></i>
</button>
<span ng-class='{"h5": item._edit }'>
{{item.name || item.driver || item.type || item.model}}
{{item.name || item._name || item.driver || item.type || item.model}}
</span>
<div ng-show="name == 'disk' && item.remove"
ng-class='{"border border-danger p-4 m-4": item.device == "disk"}'
......@@ -50,6 +50,8 @@
><%=l 'Cancel' %></button>
</p>
</div>
%= include 'main/manage_machine_edit_cpu'
%= include 'main/manage_machine_edit_features'
%= include 'main/manage_machine_edit_net'
%= include 'main/manage_machine_edit_video'
%= include 'main/manage_machine_edit_sound'
......
Supports Markdown
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment