Skip to content
GitLab
Menu
Projects
Groups
Snippets
Help
Help
Support
Community forum
Keyboard shortcuts
?
Submit feedback
Sign in
Toggle navigation
Menu
Open sidebar
Projets publics
Ravada-Mirror
Commits
0546fe27
Unverified
Commit
0546fe27
authored
May 10, 2022
by
Francesc Guasch
Committed by
GitHub
May 10, 2022
Browse files
Feature: cpu features (#1752)
feat(KVM): features policy in CPU
parent
d1fa73a6
Changes
6
Hide whitespace changes
Inline
Side-by-side
lib/Ravada/Domain.pm
View file @
0546fe27
...
...
@@ -1305,6 +1305,9 @@ sub _insert_display( $self, $display ) {
$used_port
->
{
$display
->
{
port
}}
++
;
$display
->
{
port
}
=
$self
->
_vm
->
_new_free_port
(
$used_port
);
}
}
elsif
(
$field
=~
/\.id_domain_driver/
)
{
warn
"
Warning: Already added
"
.
Dumper
(
$display
);
return
;
}
else
{
confess
"
Error: I don't know how to deal with duplicated
$field
on
"
.
$self
->
name
.
Dumper
(
$display
);
...
...
@@ -5425,11 +5428,15 @@ sub _fix_hw_booleans($data) {
next
if
!
ref
(
$data
->
{
$key
});
if
(
ref
(
$data
->
{
$key
})
eq
'
HASH
')
{
_fix_hw_booleans
(
$data
->
{
$key
});
}
elsif
(
ref
(
$data
->
{
$key
})
eq
'
ARRAY
')
{
for
my
$item
(
@
{
$data
->
{
$key
}})
{
_fix_hw_booleans
(
$item
);
}
}
elsif
(
ref
(
$data
->
{
$key
})
eq
'
JSON::PP::Boolean
')
{
$data
->
{
$key
}
=
''
.
$data
->
{
$key
};
}
else
{
confess
"
Error: expecting scalar or hash or boolean
"
.Dumper($data);
.
Dumper
(
ref
(
$data
->
{
$key
})
,
$data
->
{
$key
}
);
}
}
}
...
...
lib/Ravada/Domain/KVM.pm
View file @
0546fe27
...
...
@@ -2881,6 +2881,8 @@ sub _change_hardware_cpu($self, $index, $data) {
$vcpu
->
appendText
(
$data
->
{
vcpu
}
->
{
_text
});
}
my
(
$cpu
)
=
$doc
->
findnodes
('
/domain/cpu
');
my
$feature
=
delete
$data
->
{
cpu
}
->
{
feature
};
for
my
$field
(
keys
%
{
$data
->
{
cpu
}})
{
if
(
ref
(
$data
->
{
cpu
}
->
{
$field
}))
{
_change_xml
(
$cpu
,
$field
,
$data
->
{
cpu
}
->
{
$field
});
...
...
@@ -2894,6 +2896,10 @@ sub _change_hardware_cpu($self, $index, $data) {
$changed
++
;
}
}
if
(
$feature
)
{
_change_xml_list
(
$cpu
,
'
feature
',
$feature
,
'
name
');
$changed
++
;
}
$self
->
reload_config
(
$doc
)
if
$changed
;
}
...
...
@@ -2988,10 +2994,34 @@ sub _remove_acceleration($video) {
$video
->
removeChild
(
$acceleration
)
if
$acceleration
;
}
sub
_change_xml_list
($xml,$name, $data, $field='name') {
my
%keep
;
for
my
$entry
(
@$data
)
{
next
if
!
defined
$entry
||!
$entry
;
$keep
{
$entry
->
{
$field
}}
++
;
my
$node
;
for
my
$curr
(
$xml
->
findnodes
(
$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
});
}
}
for
my
$curr
(
$xml
->
findnodes
(
$name
))
{
my
$curr_name
=
$curr
->
getAttribute
(
$field
);
$xml
->
removeChild
(
$curr
)
if
!
$keep
{
$curr_name
};
}
}
sub
_change_xml
($xml, $name, $data) {
confess
Dumper
([
$name
,
$data
])
if
!
ref
(
$data
)
||
(
ref
(
$data
)
ne
'
HASH
'
&&
ref
(
$data
)
ne
'
ARRAY
');
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
)
{
...
...
lib/Ravada/Front/Domain/KVM.pm
View file @
0546fe27
...
...
@@ -124,6 +124,17 @@ sub _get_controller_cpu($self) {
my
(
$xml_vcpu
)
=
$doc
->
findnodes
("
/domain/vcpu
");
_xml_elements
(
$xml_vcpu
,
$item
->
{
vcpu
});
if
(
exists
$item
->
{
cpu
}
->
{
feature
}
&&
ref
(
$item
->
{
cpu
}
->
{
feature
})
ne
'
ARRAY
')
{
$item
->
{
cpu
}
->
{
feature
}
=
[
$item
->
{
cpu
}
->
{
feature
}
];
}
$item
->
{
cpu
}
->
{
feature
}
=
[]
if
!
exists
$item
->
{
cpu
}
->
{
feature
};
$item
->
{
cpu
}
->
{
feature
}
=
_sort_xml_list
(
$item
->
{
cpu
}
->
{
feature
},'
name
');
lock_hash
(
%$item
);
return
(
$item
);
}
...
...
@@ -152,6 +163,15 @@ sub _get_controller_features($self) {
return
(
$item
);
}
sub
_sort_xml_list
($list, $field) {
my
@sorted
=
sort
{
my
(
$name_a
)
=
(
$a
->
{
$field
}
or
'');
my
(
$name_b
)
=
(
$b
->
{
$field
}
or
'');
$name_a
cmp
$name_b
}
@$list
;
return
\
@sorted
;
}
sub
_xml_elements
($xml, $item) {
confess
if
!
defined
$xml
;
...
...
@@ -166,7 +186,17 @@ sub _xml_elements($xml, $item) {
my
$h_node
=
{};
_xml_elements
(
$node
,
$h_node
);
$h_node
=
1
if
!
keys
%$h_node
;
$item
->
{
$node
->
nodeName
}
=
$h_node
;
my
$name
=
$node
->
nodeName
;
if
(
!
exists
$item
->
{
$name
})
{
$item
->
{
$node
->
nodeName
}
=
$h_node
;
}
else
{
my
$entry
=
$item
->
{
$name
};
if
(
ref
(
$entry
)
eq
'
HASH
')
{
$item
->
{
$name
}
=
[
$entry
,
$h_node
];
}
else
{
push
@
{
$item
->
{
$name
}},(
$h_node
);
}
}
}
}
...
...
lib/Ravada/Request.pm
View file @
0546fe27
...
...
@@ -1710,6 +1710,20 @@ sub requirements_done($self) {
return
$ok
;
}
=head2 redo
Set the request to be executed again
=cut
sub
redo
($self) {
my
$sth
=
$self
->
_dbh
->
prepare
(
"
UPDATE requests SET status='requested',start_time=NULL
"
.
"
WHERE id=?
"
);
$sth
->
execute
(
$self
->
id
);
}
sub
AUTOLOAD
{
my
$self
=
shift
;
...
...
t/kvm/feature_policy.t
0 → 100644
View file @
0546fe27
use
warnings
;
use
strict
;
use
Carp
qw(confess)
;
use
Data::
Dumper
;
use
IPC::
Run3
;
use
JSON::
XS
;
use
Test::
More
;
use
XML::
LibXML
;
use
lib
'
t/lib
';
use
Test::
Ravada
;
no
warnings
"
experimental::signatures
";
use
feature
qw(signatures)
;
=pod
"<feature policy='require' name='ss'/>"
<feature policy='require' name='vmx'/>
<feature policy='require' name='pcid'/>
<feature policy='require' name='hypervisor'/>
<feature policy='require' name='arat'/>
<feature policy='require' name='tsc_adjust'/>
<feature policy='require' name='umip'/>
<feature policy='require' name='md-clear'/>
<feature policy='require' name='stibp'/>
<feature policy='require' name='arch-capabilities'/>
<feature policy='require' name='ssbd'/>
<feature policy='require' name='xsaveopt'/>
<feature policy='require' name='pdpe1gb'/>
<feature policy='require' name='ibpb'/>
<feature policy='require' name='ibrs'/>
<feature policy='require' name='amd-stibp'/>
<feature policy='require' name='amd-ssbd'/>
<feature policy='require' name='skip-l1dfl-vmentry'/>
<feature policy='require' name='pschange-mc-no'/>
=cut
sub
test_feat_policy
($vm) {
my
$domain
=
create_domain_v2
(
vm
=>
$vm
,
id_iso
=>
search_id_iso
('
alpine%64
')
);
Ravada::
Request
->
change_hardware
(
uid
=>
user_admin
->
id
,
id_domain
=>
$domain
->
id
,
hardware
=>
'
cpu
'
,
data
=>
{
cpu
=>
{
'
mode
'
=>
'
host-model
',
check
=>
'
partial
'
}
}
);
wait_request
();
my
$req
=
Ravada::
Request
->
start_domain
(
uid
=>
user_admin
->
id
,
id_domain
=>
$domain
->
id
);
wait_request
(
check_error
=>
0
);
test_add_feature_policy
(
$domain
,
$req
->
error
)
if
$req
->
error
;
$domain
->
remove
(
user_admin
);
}
sub
_verify_features
($domain, @features) {
diag
("
Verify features
@features
");
my
$xml
=
$domain
->
xml_description
();
my
$doc
=
XML::
LibXML
->
load_xml
(
string
=>
$xml
);
my
@found
=
$doc
->
findnodes
("
/domain/cpu/feature
");
my
%missing
=
map
{
$_
=>
}
@features
;
my
%dupe
;
for
my
$feat
(
@found
)
{
my
$name
=
$feat
->
getAttribute
('
name
');
delete
$missing
{
$name
};
$dupe
{
$name
}
++
;
}
ok
(
!
keys
%missing
)
or
die
"
Missing features
"
.
Dumper
([
keys
%missing
]);
for
my
$feat
(
keys
%dupe
)
{
delete
$dupe
{
$feat
}
if
$dupe
{
$feat
}
<
2
;
}
ok
(
!
keys
%dupe
)
or
die
"
Duplicated features
"
.
Dumper
(
\
%dupe
);
}
sub
test_feature_policy
($domain, $feature, $policy='require') {
my
$req_change
=
Ravada::
Request
->
change_hardware
(
uid
=>
user_admin
->
id
,
id_domain
=>
$domain
->
id
,
hardware
=>
'
cpu
'
,
data
=>
{
cpu
=>
{
'
mode
'
=>
'
host-model
',
check
=>
'
partial
'
,
feature
=>
[
{
'
policy
'
=>
$policy
,
name
=>
$feature
}
]
}
}
);
wait_request
(
debug
=>
0
);
my
$domain2
=
Ravada::Front::
Domain
->
open
(
$domain
->
id
);
my
$xml
=
$domain2
->
xml_description
();
my
$doc
=
XML::
LibXML
->
load_xml
(
string
=>
$xml
);
my
@features
=
$doc
->
findnodes
("
/domain/cpu/feature
");
my
@found
=
grep
{
$_
->
getAttribute
('
name
')
eq
$feature
}
$doc
->
findnodes
("
/domain/cpu/feature
");
is
(
scalar
(
@found
),
1
);
is
(
$found
[
0
]
->
getAttribute
('
name
'),
$feature
)
or
exit
;
is
(
$found
[
0
]
->
getAttribute
('
policy
'),
$policy
,
"
Expecting feature policy=
$policy
name=
$feature
"
.
$found
[
0
]
->
toString
())
or
exit
;
}
sub
test_remove_feature
($domain, $feature) {
my
$cpu
=
$domain
->
get_controller
(
'
cpu
'
);
my
$old
=
$cpu
->
{
cpu
}
->
{
feature
};
my
@keep
;
for
(
@$old
)
{
push
@keep
,(
$_
)
if
$_
->
{
name
}
ne
$feature
;
}
my
$req_change
=
Ravada::
Request
->
change_hardware
(
uid
=>
user_admin
->
id
,
id_domain
=>
$domain
->
id
,
hardware
=>
'
cpu
'
,
data
=>
{
cpu
=>
{
'
mode
'
=>
'
host-model
',
check
=>
'
partial
'
,
feature
=>
\
@keep
}
}
);
wait_request
();
my
$xml
=
$domain
->
xml_description
();
my
$doc
=
XML::
LibXML
->
load_xml
(
string
=>
$xml
);
my
@found
=
grep
{
$_
->
getAttribute
('
name
')
eq
$feature
}
$doc
->
findnodes
("
/domain/cpu/feature
");
is
(
scalar
(
@found
),
0
)
or
die
Dumper
([
map
{
$_
->
toString
}
@found
]);
}
sub
test_add_feature_policy
($domain, $error) {
my
(
$features
)
=
$error
=~
/required features: (.*?)($|\s)/
;
my
@features
=
split
/,/
,
$features
;
my
$req_change
=
Ravada::
Request
->
change_hardware
(
uid
=>
user_admin
->
id
,
id_domain
=>
$domain
->
id
,
hardware
=>
'
cpu
'
,
data
=>
{
cpu
=>
{
'
mode
'
=>
'
host-model
',
check
=>
'
partial
'
,
feature
=>
[
{
'
policy
'
=>
'
disable
',
name
=>
'
svm
'}
,{
'
policy
'
=>
'
require
',
name
=>
'
vmx
'}
]
}
}
);
wait_request
();
_verify_features
(
$domain
,
'
svm
',
'
vmx
');
my
$cpu
=
$domain
->
get_controller
(
'
cpu
'
);
is
(
scalar
(
@
{
$cpu
->
{
cpu
}
->
{
feature
}}),
2
);
my
$req
=
Ravada::
Request
->
start_domain
(
uid
=>
user_admin
->
id
,
id_domain
=>
$domain
->
id
);
wait_request
();
_verify_features
(
$domain
,
'
svm
',
'
vmx
');
$domain
->
shutdown_now
(
user_admin
);
$req_change
->
redo
();
wait_request
(
debug
=>
0
);
_verify_features
(
$domain
,
'
svm
',
'
vmx
');
test_feature_policy
(
$domain
,
'
vmx
','
require
');
test_feature_policy
(
$domain
,
'
vmx
','
disable
');
test_remove_feature
(
$domain
,
'
vmx
');
}
########################################################################
init
();
clean
();
for
my
$vm_name
(
'
KVM
'
)
{
SKIP:
{
my
$vm
=
rvd_back
->
search_vm
(
$vm_name
);
my
$msg
=
"
SKIPPED test: No
$vm_name
VM found
";
if
(
$vm
&&
$>
)
{
$msg
=
"
SKIPPED: Test must run as root
";
$vm
=
undef
;
}
diag
(
$msg
)
if
!
$vm
;
skip
$msg
,
10
if
!
$vm
;
test_feat_policy
(
$vm
);
}
}
end
();
done_testing
();
templates/main/manage_machine_edit_cpu.html.ep
View file @
0546fe27
...
...
@@ -45,4 +45,58 @@
</select>
</div>
<hr>
<small><b><%=l 'CPU Features' %></b></small>
<div class="card-body ml-2" ng-show=item.cpu.feature.length">
<ul class="list-group list-group-horizontal-md"
ng-show="feature"
ng-repeat="feature in item.cpu.feature">
<li class="list-group-item list-group-item-primary col-md-3">
{{feature.name}}
</li>
<li class="list-group-item">
<select ng-model="feature.policy"
ng-options="option for option in
[ 'disable','forbid', 'force', 'optional','require']">
</select>
</li>
<li class="list-group-item">
<small>
<button type="badge" title="remove"
ng-click="feature=0;
item.cpu.feature[$index]=undefined;
form_edit.$pristine=false"
>
<span aria-hidden="true">×</span>
</button>
</small>
</li>
</ul>
</div>
<div class="ml-4">
<div class="row">
<div class="col-md-6">
<label for="new_feature">
<%=l 'New feature' %>
</label>
<input type="text" ng-model="new_feature" name="new_feature"/>
<button type="badge"
ng-disabled="!new_feature"
ng-click="item.cpu.feature[item.cpu.feature.length]=
{'name': new_feature, 'policy': 'require'};
form_edit.$pristine=false;
new_feature='';
">
add
</button>
</div>
</div><!-- row -->
</div>
<hr>
</div>
Write
Preview
Supports
Markdown
0%
Try again
or
attach a new file
.
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment