Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Feature book host devices #2057

Merged
merged 46 commits into from
May 27, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
46 commits
Select commit Hold shift + click to select a range
62bbce4
wip(test): check access by group
frankiejol Mar 26, 2024
6d9da82
test: list bases access
frankiejol Mar 26, 2024
03568b8
wip: group base code
frankiejol Mar 26, 2024
91af9df
wip: group management backend
frankiejol Mar 27, 2024
231ea89
wip(frontend): list local and ldap groups
frankiejol Mar 27, 2024
9ec7bd0
wip(frontend): list users and groups
frankiejol Mar 28, 2024
d044e2d
wip: list local groups for access settings
frankiejol Mar 28, 2024
a1618b9
wip(frontend): groups API and tests
frankiejol Apr 4, 2024
521c61e
wip (frontend): templates wit new groups API
frankiejol Apr 4, 2024
22d6761
wip(frontend): show sql groups when no LDAP enabled
frankiejol Apr 5, 2024
eb177b4
test: allow local-groups tag
frankiejol Apr 8, 2024
4b52e16
wip(bookings): test local group
frankiejol Apr 8, 2024
6351134
wip: booking local group
frankiejol Apr 8, 2024
022882c
wip: add and change local groups
frankiejol Apr 9, 2024
a657b02
wip: create booking with local groups
frankiejol Apr 9, 2024
23bf824
wip: fixed change groups clears pristine flag
frankiejol Apr 9, 2024
754ed76
wip: require one group assigned
frankiejol Apr 9, 2024
9376ac4
wip: default bookings is true and description
frankiejol Apr 9, 2024
71be099
wip: hide LDAP groups select when no LDAP
frankiejol Apr 9, 2024
bb995c8
wip: now bookings are enabled by default
frankiejol Apr 10, 2024
6cd2c4a
wip(doc): new methods
frankiejol Apr 10, 2024
a8ef18c
wip: cloacked angular while loading
frankiejol Apr 11, 2024
4f3ecd5
wip(frontend): upload members
frankiejol Apr 11, 2024
73d6c3e
wip: upload group members
frankiejol Apr 11, 2024
f56443b
wip(CLI): upload group members
frankiejol Apr 12, 2024
88cf2ed
wip: manage access with id_group
frankiejol Apr 15, 2024
23970a0
wip: add and remove access by id_group
frankiejol Apr 15, 2024
7d9199c
wip(frontend): id group
frankiejol Apr 15, 2024
020f71a
wip: working with id group
frankiejol Apr 16, 2024
843dc3c
wip: create groups_local table before
frankiejol Apr 18, 2024
e5a40a2
Merge branch 'main' into feat/sql_groups
frankiejol Apr 18, 2024
d93f471
wip: services do not support slash
frankiejol Apr 18, 2024
4be3179
wip: hide base when removing from group
frankiejol Apr 18, 2024
e9bfd13
wip: test disabled groups
frankiejol Apr 18, 2024
b5616b6
wip: hide clone when not allowed to access group
frankiejol Apr 18, 2024
a00f16d
wip: filter groups
frankiejol Apr 18, 2024
f8587f4
wip: book only the host devices
frankiejol Apr 18, 2024
e378c0a
wip: test book hd
frankiejol Apr 18, 2024
02fb2e0
wip: book host devices
frankiejol Apr 19, 2024
52f90c9
wip: test KVM hd
frankiejol Apr 19, 2024
8bfc294
wip: change options
frankiejol Apr 19, 2024
0c00f78
wip: lang options
frankiejol Apr 19, 2024
a282f9d
wip: test list machines
frankiejol Apr 24, 2024
1e2214f
wip: book only bases with Host Devices
frankiejol Apr 25, 2024
3c3550b
wip: booking options
frankiejol Apr 26, 2024
b393a6a
Merge branch 'main' into feat/book_hd
frankiejol Apr 26, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
20 changes: 16 additions & 4 deletions lib/Ravada.pm
Original file line number Diff line number Diff line change
Expand Up @@ -2347,6 +2347,7 @@ sub _sql_create_tables($self) {
,date_booking => 'date'
,visibility => "enum ('private','public') default 'public'"
,date_changed => 'timestamp DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP'
,options => 'varchar(100)'
}
]
,
Expand Down Expand Up @@ -3715,9 +3716,16 @@ sub list_domains_data($self, %args ) {
$where = " WHERE $where " if $where;
my $query = "SELECT * FROM domains $where ORDER BY name";
my $sth = $CONNECTOR->dbh->prepare($query);

my $sth_hd = $CONNECTOR->dbh->prepare(
"SELECT count(*) FROM host_devices_domain_locked "
." WHERE id_domain=?"
);
$sth->execute(@values);
while (my $row = $sth->fetchrow_hashref) {
$row->{date_changed} = 0 if !defined $row->{date_changed};
$sth_hd->execute($row->{id});
($row->{host_devices})=$sth_hd->fetchrow;
lock_hash(%$row);
push @domains,($row);
}
Expand Down Expand Up @@ -5989,6 +5997,7 @@ sub _refresh_active_vms ($self) {

my %active_vm;
for my $vm ($self->list_vms) {
next if !$vm;
if ( !$vm->enabled() || !$vm->is_active ) {
$vm->shutdown_domains();
$active_vm{$vm->id} = 0;
Expand Down Expand Up @@ -6583,7 +6592,9 @@ sub vm($self) {
warn $@;
next;
}
push @vms, ( $vm );
eval {
push @vms, ( $vm ) if $vm && $vm->vm;
};
};
return [@vms] if @vms;
return $self->_create_vm();
Expand Down Expand Up @@ -6637,14 +6648,15 @@ sub _shutdown_bookings($self) {
my @bookings = Ravada::Booking::bookings();
return if !scalar(@bookings);


my @domains = $self->list_domains_data(status => 'active');
for my $dom ( @domains ) {
next if $dom->{autostart};
next if $self->_user_is_admin($dom->{id_owner});

if ( Ravada::Booking::user_allowed($dom->{id_owner}, $dom->{id_base}) ) {
# warn "\tuser $dom->{id_owner} allowed to start clones from $dom->{id_base}";
if ( Ravada::Booking::user_allowed($dom->{id_owner}, $dom->{id_base}, $dom->{host_devices})
&& Ravada::Booking::user_allowed($dom->{id_owner}, $dom->{id}, $dom->{host_devices})
) {
#warn "\tuser $dom->{id_owner} allowed to start clones from $dom->{id_base}";
next;
}

Expand Down
8 changes: 5 additions & 3 deletions lib/Ravada/Booking.pm
Original file line number Diff line number Diff line change
Expand Up @@ -36,13 +36,13 @@ sub BUILD($self, $args) {
my $day_of_week = delete $args->{day_of_week};

my %entry;
my @fields_entry = qw ( bases ldap_groups local_groups users time_start time_end );
my @fields_entry = qw ( bases ldap_groups local_groups users time_start time_end options);
for (@fields_entry) {
$entry{$_} = delete $args->{$_};
}

my %fields = map { $_ => 1 } keys %$args;
delete @fields{'title','id_owner','description','date_created','local_groups'};
delete @fields{'title','id_owner','description','date_created','local_groups','options'};
die "Error: unknown arguments ".(join("," , keys %fields)) if keys %fields;

$self->_insert_db(%$args
Expand Down Expand Up @@ -325,7 +325,7 @@ sub _search_user_name($id_user) {
return $name;
}

sub user_allowed($user,$id_base) {
sub user_allowed($user,$id_base, $enable_host_devices=1) {
my $user_name = $user;
if ( ref($user) ) {
$user_name = $user->name;
Expand All @@ -347,7 +347,9 @@ sub user_allowed($user,$id_base) {
$allowed = 0;
next unless !scalar($entry->bases_id) || grep { $_ == $id_base } $entry->bases_id;
# look no further if user is allowed

return 1 if $entry->user_allowed($user_name);
return 1 if $entry->options_allowed($id_base, $enable_host_devices);
}
if (!$allowed && !ref($user)) {
my $user0 = Ravada::Auth::SQL->new(name => $user_name);
Expand Down
29 changes: 29 additions & 0 deletions lib/Ravada/Booking/Entry.pm
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@ use strict;

use Carp qw(carp croak);
use Data::Dumper;
use Mojo::JSON qw( encode_json decode_json );

use Ravada::Utils;

use Moose;
Expand Down Expand Up @@ -54,6 +56,9 @@ sub _dbh($self) {

sub _insert_db($self, $field) {

my $options = $field->{options};
$field->{options} = encode_json($options) if $options;

my $query = "INSERT INTO booking_entries "
."(" . join(",",sort keys %$field )." )"
." VALUES (". join(",", map { '?' } keys %$field )." ) "
Expand Down Expand Up @@ -249,6 +254,14 @@ sub _open($self, $id) {
$sth->execute($id);
my $row = $sth->fetchrow_hashref;
confess "Error: Booking entry $id not found " if !keys %$row;
eval {
$row->{options} = decode_json($row->{options}) if $row->{options};
};
if ($@) {
warn "Error decoding options $row->{options} ".$@;
$row->{options} = undef;
}

$self->{_data} = $row;

return $self;
Expand Down Expand Up @@ -277,6 +290,8 @@ sub change($self, %fields) {
} elsif ($field eq 'bases') {
$self->_change_bases($fields{$field});
next;
} elsif ( ref($fields{$field})) {
$fields{$field} = encode_json($fields{$field});
}
next if !exists $self->{_data}->{$field};
my $old_value = $self->_data($field);
Expand Down Expand Up @@ -438,6 +453,20 @@ sub user_allowed($entry, $user_name) {
return 0;
}

sub options_allowed($entry, $id_domain, $enable_host_devices=1) {
my $options = $entry->_data('options');
return 0 if !$options || !ref($options) || !keys %$options;

my $domain = Ravada::Front::Domain->open($id_domain);

if ($options->{host_devices}) {
return 0 if $enable_host_devices && $domain->list_host_devices();
return 1;
}

return 0;
}

sub _remove_users($self) {
my $sth =$self->_dbh->prepare("DELETE FROM booking_entry_users WHERE id_booking_entry=? ");
$sth->execute($self->id);
Expand Down
21 changes: 15 additions & 6 deletions lib/Ravada/Domain.pm
Original file line number Diff line number Diff line change
Expand Up @@ -427,10 +427,19 @@ sub _start_preconditions{
($user) = $_[1];
}
$self->_allowed_start($user);
if ( Ravada->setting('/backend/bookings') && !$self->allowed_booking( $user ) ) {
my @bookings = Ravada::Booking::bookings(date => DateTime->now()->ymd
,time => DateTime->now()->hms);
confess "Error: resource booked ".Dumper(\@bookings);

my $enable_host_devices;
$enable_host_devices = $request->defined_arg('enable_host_devices') if $request;
$enable_host_devices = 1 if !defined $enable_host_devices;

if ( Ravada->setting('/backend/bookings')
&& !$self->allowed_booking( $user, $enable_host_devices ) ) {
my $tz = Ravada::Booking::TZ();
my @bookings = Ravada::Booking::bookings(
date => DateTime->now(time_zone => $tz)->ymd
,time => DateTime->now(time_zone => $tz)->hms);

confess "Error: resource booked for ".join(" , ",(map { $_->_data('title') } @bookings));
}
#_check_used_memory(@_);
$self->status('starting');
Expand All @@ -446,12 +455,12 @@ or its base. Returns false otherwise.
=cut


sub allowed_booking($self, $user) {
sub allowed_booking($self, $user, $enable_hd=1) {
my $id_base = $self->id;
if (!$self->is_base) {
$id_base = $self->_data('id_base') or return 1;
}
return Ravada::Booking::user_allowed($user, $id_base);
return Ravada::Booking::user_allowed($user, $id_base, $enable_hd);
}

sub _start_checks($self, @args) {
Expand Down
4 changes: 3 additions & 1 deletion lib/Ravada/Request.pm
Original file line number Diff line number Diff line change
Expand Up @@ -1703,7 +1703,9 @@ sub done_recently($self, $seconds=60,$command=undef, $args=undef) {

return Ravada::Request->open($id) if !keys %$args_d;

my $args_found_d = decode_json($args_found);
my $args_found_d = {};
eval { $args_found_d = decode_json($args_found)};
warn "Warning: request $id $@" if $@;
delete $args_found_d->{uid};
delete $args_found_d->{at};

Expand Down
152 changes: 150 additions & 2 deletions t/vm/r30_reserve.t
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,8 @@ use Hash::Util qw(lock_hash);
use Test::More;
use YAML qw(DumpFile);

use Ravada::HostDevice::Templates;

use 5.010;

no warnings "experimental::signatures";
Expand Down Expand Up @@ -577,7 +579,7 @@ sub test_conflict_generic($vm, $base, $conflict_start, $conflict_end, $n_expecte
}


sub _create_booking( $base ) {
sub _create_booking( $base , $options=undef ) {
_wait_end_of_hour();
my $date_start = _yesterday();
my $date_end = _now_days(15);
Expand All @@ -586,8 +588,12 @@ sub _create_booking( $base ) {

my $today = DateTime->from_epoch( epoch => time(), time_zone => $TZ);
my $tomorrow = DateTime->from_epoch( epoch => time(), time_zone => $TZ)->add(days => 1);
my @args;
push @args,(bases => $base->id) if $base;
push @args,(options => $options) if $options;

my $booking = Ravada::Booking->new(
bases => $base->id
@args
, ldap_groups => $GROUP
, users => $USER_YES_NAME_1
, date_start => $date_start
Expand Down Expand Up @@ -1299,6 +1305,144 @@ sub test_config {
is(rvd_back->setting('/backend/bookings'),1);
}

sub _test_list_yes($user, @bases) {
my $list = rvd_front->list_machines_user($user);
for my $base ( @bases ) {
my ($found) = grep { $_->{name} eq $base->name} @$list;
ok($found,"Expecting ".$base->name." in ".Dumper($list));
}
}

sub _test_list_no($user, @bases) {
my $list = rvd_front->list_machines_user($user);
for my $base ( @bases ) {
my ($found) = grep { $_->{name} eq $base->name} @$list;
ok(!$found,"Expecting no ".$base->name." in ".Dumper($list));
}
}


sub _create_base_hd($vm, $id_hd) {
my $base_hd = create_domain($vm);
Ravada::Request->add_hardware(
uid => user_admin->id
,id_domain => $base_hd->id
,name => 'usb'
);
wait_request(debug => 0);
$base_hd->add_host_device($id_hd);
$base_hd->prepare_base(user_admin);
$base_hd->is_public(1);
return $base_hd;
}

sub test_booking_host_devices($vm) {
my $templates = Ravada::HostDevice::Templates::list_templates($vm->id);
my ($usb_hd) = grep { $_->{name} =~ /USB/ } @$templates;

die "Error: no USB template found ".Dumper($templates) if !$usb_hd;

my $id_hd = $vm->add_host_device(template => $usb_hd->{name});
my $hd = Ravada::HostDevice->search_by_id($id_hd);

if ($vm->type eq 'KVM') {
my $config = config_host_devices('usb');
if (!$config) {
diag("No USB config in t/etc/host_devices.conf");
return;
}
$hd->_data('list_filter' => $config);
}

my $base = create_domain($vm);
$base->prepare_base(user_admin);
$base->is_public(1);

my $base_hd = _create_base_hd($vm, $id_hd);

_test_list_yes($USER_LOCAL_YES_1, $base, $base_hd);
_test_list_yes($USER_LOCAL_NO, $base, $base_hd);

my $booking = _create_booking(undef , { host_devices => 1 } );

for my $entry ( $booking->entries ) {
$entry->change('time_end' => _now_seconds(120));
ok($entry->_data('options')) or exit;
is($entry->_data('options')->{host_devices},1) or die Dumper($entry->_data('options'));
$entry->change( local_groups => $GROUP_LOCAL->id );
}
my ($entry) = $booking->entries;
$entry->change('options' => { host_devices => 2 });
my ($entry_changed) = $booking->entries;
is($entry_changed->_data('options')->{host_devices},2) or die Dumper($entry_changed->_data('options'));
$entry->change('options' => { host_devices => 1 });

_test_list_yes($USER_LOCAL_YES_1, $base, $base_hd);
_test_list_yes($USER_LOCAL_NO, $base);
_test_list_no($USER_LOCAL_NO, $base_hd);
#####
#
# list yes

#####
#
# list no
my $list_no = rvd_front->list_machines_user($USER_LOCAL_NO);
ok(grep { $_->{name} eq $base->name} @$list_no) or die Dumper($list_no);
ok(!grep { $_->{name} eq $base_hd->name} @$list_no) or die Dumper($list_no);

my $clone_yes = $base->clone(name => new_domain_name, user => $USER_LOCAL_YES_1);
my $clone_hd_yes = $base_hd->clone(name => new_domain_name, user => $USER_LOCAL_YES_1);

# user allowed can start anything
is(Ravada::Booking::user_allowed($USER_LOCAL_YES_1, $clone_yes->id),1);
is(Ravada::Booking::user_allowed($USER_LOCAL_YES_1, $clone_hd_yes->id),1);
for my $c ( $clone_yes, $clone_hd_yes) {
my $req_start_clone = Ravada::Request->start_domain(
uid => $c->id_owner
,id_domain => $c->id
);
wait_request(check_error => 0);
is($req_start_clone->error,'');
is ($c->is_active,1);
}

my $clone_no = $base->clone(name => new_domain_name, user => $USER_LOCAL_NO);
my $clone_hd_no = $base_hd->clone(name => new_domain_name, user => $USER_LOCAL_NO);

# User allowed only to non_hd bases
# allowed
is(Ravada::Booking::user_allowed($USER_LOCAL_NO, $clone_yes->id),1) or exit;
is(Ravada::Booking::user_allowed($USER_LOCAL_NO, $base->id),1) or exit;
# denied
is(Ravada::Booking::user_allowed($USER_LOCAL_NO, $clone_hd_yes->id),0);
is(Ravada::Booking::user_allowed($USER_LOCAL_NO, $base_hd->id),0);
# allow when disabled host_devices
is(Ravada::Booking::user_allowed($USER_LOCAL_NO, $clone_hd_yes->id,0),1);

# can start when forcing no host devices
my $req_start_hd_without = Ravada::Request->start_domain(uid => $clone_hd_no->id_owner, id_domain => $clone_hd_no->id, enable_host_devices => 0);

Ravada::Request->enforce_limits(_force => 1);
wait_request(check_error => 0, debug => 0);
is($req_start_hd_without->error,'');
is($clone_hd_no->is_active,1) or die $clone_hd_no->name;

# user denied can not start hd
my $req_start_no = Ravada::Request->start_domain(uid => $clone_no->id_owner, id_domain => $clone_no->id);
my $req_start_hd_no = Ravada::Request->start_domain(uid => $clone_hd_no->id_owner, id_domain => $clone_hd_no->id);
wait_request(check_error => 0);
is($req_start_no->error,'');
like($req_start_hd_no->error,qr/./);

is($clone_no->is_active,1) or die $clone_no->name;
is($clone_hd_no->is_active,0);

$booking->remove();
remove_domain($base);
remove_domain($base_hd);
}

###################################################################

test_config();
Expand All @@ -1324,6 +1468,10 @@ for my $vm_name ( vm_names()) {

skip($msg,10) if !$vm;

diag("Testing booking in $vm_name");

test_booking_host_devices($vm);

test_bookings_week_2days($vm);
test_search_change_remove_booking($vm);

Expand Down
Loading
Loading