Commit dd5c3110 authored by Francesc Guasch's avatar Francesc Guasch
Browse files

Merge branch 'develop' of https://github.com/upc/ravada into develop

parents f25f31ac 7817ed91
......@@ -2,16 +2,9 @@
**Implemented enhancements:**
- Polished action buttons in main screen [\#1408]
- Polished shutdown clones buttons [\#1406]
- Add debug option [\#1402]
- Loading icon [\#1401]
- Feature migrate machine [\#1393]
- Machine schedule reservation [\#1337]
- Manage nodes and networks settings [\#1305]
**Bugfixes**
- Ubuntu 20.04 MD5SUM file gone [\#1420]
- Fix docker files config and dependencies [\#1411]
- Fix docker files for tzdata package compatibility [\#1376]
- Fix sync clones back [\#1403]
- Fix nodes nat [\#1400]
- Machine in a node returns to KVMlocalhost when a machine option is modified [\#1440]
......@@ -210,7 +210,13 @@ A PR can only be merged into master by a maintainer if:
Any maintainer is allowed to merge a PR if all of these conditions are
met.
### 11 Reset my fork to upstream
## 11 Reset my fork to upstream
You may want to ditch everything in your fork
### 11.1 Reset develop branch
If you want to get even with main develop branch.
```sh
git remote add upstream https://github.com/UPC/ravada
......@@ -220,3 +226,28 @@ git reset --hard upstream/develop
git push origin develop --force
```
### 11.2 Work in a new fresh branch
We create a new branch called *feature/cool_thing* and make it exactly like UPC/develop branch:
First we add the upstream remote source and fetch it. If you added this remote before you will get an error *fatal: remote upstream already exists.*. Don't worry and run the `git fetch upstream` anyway so it downloads the UPC source.
```sh
git remote add upstream https://github.com/UPC/ravada
git fetch upstream
```
Now we create a new branch:
```sh
git checkout -b feature/cool_thing upstream/develop
```
Reset this branch, now it will be an exact replica of upstream UPC/develop:
```sh
git reset --hard upstream/develop
git push --set-upstream origin feature/cool_thing
```
Now work on your code, test it so it is great. Then commit, push and create a *pull request*.
# ravada
[![GitHub version](https://img.shields.io/badge/version-0.9.0-brightgreen.svg)](https://github.com/UPC/ravada/releases) [![License: AGPL v3](https://img.shields.io/badge/License-AGPL%20v3-blue.svg)](https://github.com/UPC/ravada/blob/master/LICENSE)
[![GitHub version](https://img.shields.io/badge/version-0.11.0-brightgreen.svg)](https://github.com/UPC/ravada/releases) [![License: AGPL v3](https://img.shields.io/badge/License-AGPL%20v3-blue.svg)](https://github.com/UPC/ravada/blob/master/LICENSE)
[![Documentation Status](https://readthedocs.org/projects/ravada/badge/?version=latest)](http://ravada.readthedocs.io/en/latest/?badge=latest)
[![Follow twitter](https://img.shields.io/twitter/follow/ravada_vdi.svg?style=social&label=Twitter&style=flat-square)](https://twitter.com/ravada_vdi)
[![Telegram Group](https://img.shields.io/badge/Telegram-Group-blue.svg)](https://t.me/ravadavdi)
......
......@@ -4,7 +4,7 @@ TZ=Europe/Madrid
# If you change password remember update dockerfy/dockers/back/ravada.conf
MYSQL_DATABASE=ravada
MYSQL_ROOT_PASSWORD=Pword12345*
MYSQL_HOST=127.0.0.1
MYSQL_HOST=0.0.0.0
MYSQL_PORT=33306
MYSQL_USER=rvd_user
MYSQL_PASSWORD=Pword12345*
......
......@@ -25,6 +25,7 @@ services:
- ravada_network
#By default download from dockerhub
image: ravada/front
env_file: .env
#If you want to local build
#build: dockers/front/.
restart: unless-stopped
......@@ -50,6 +51,7 @@ services:
- ravada_network
#By default download from dockerhub
image: ravada/back
env_file: .env
#If you want to local build
#build: dockers/back/.
privileged: true
......
......@@ -14,12 +14,13 @@ RUN apt-get update \
liblwp-useragent-determined-perl libvirt-clients supervisor net-tools openssh-client apt-utils curl libpbkdf2-tiny-perl \
libio-stringy-perl libvirt-daemon-system libvirt-clients netcat-openbsd qemu-kvm qemu-utils iproute2 wget bridge-utils firewalld dnsmasq iptables ebtables \
libnet-openssh-perl libdatetime-format-dateparse-perl file\
&& apt-get clean
RUN DEBIAN_FRONTEND=noninteractive apt-get install -y tzdata \
&& apt-get clean \
&& rm -rf /var/lib/apt/lists/*
RUN ln -snf /usr/share/zoneinfo/$TZ /etc/localtime && echo $TZ > /etc/timezone
ENV TZ=Europe/Madrid
RUN DEBIAN_FRONTEND=noninteractive apt-get install -y tzdata \
RUN echo "listen_tls = 0" >> /etc/libvirt/libvirtd.conf \
&& echo 'listen_tcp = 1' >> /etc/libvirt/libvirtd.conf \
# && mkdir -p /root/.ssh \
......@@ -40,5 +41,5 @@ COPY supervisord.conf /etc/supervisord.conf
#ADD src/ravada /ravada
COPY ravada.conf /etc/ravada.conf
WORKDIR /ravada
ENV PERL5LIB /ravada/lib
CMD ["/usr/bin/supervisord", "-c", "/etc/supervisord.conf"]
db:
user: rvd_user
password: Pword12345*
host: ravada-mysql
\ No newline at end of file
host: ravada-mysql
ldap:
server: 10.1.36.224
admin_user:
dn: cn=Directory Manager
password: 12345678
base: 'dc=example,dc=com'
......@@ -13,12 +13,16 @@ RUN apt-get update \
libfile-rsync-perl libdate-calc-perl libparallel-forkmanager-perl libdatetime-perl libencode-locale-perl netcat-openbsd \
libio-stringy-perl libvirt-clients liblwp-useragent-determined-perl supervisor net-tools apt-utils lsof mysql-client \
curl bash vim wget libnet-openssh-perl libdatetime-format-dateparse-perl \
&& apt-get clean
&& apt-get clean \
ENV TZ=Europe/Madrid
RUN DEBIAN_FRONTEND=noninteractive apt-get install -y tzdata \
&& apt-get clean \
&& rm -rf /var/lib/apt/lists/*
RUN ln -snf /usr/share/zoneinfo/$TZ /etc/localtime && echo $TZ > /etc/timezone
RUN ln -snf /usr/share/zoneinfo/$TZ /etc/localtime && echo $TZ > /etc/timezone
RUN mkdir -p /var/log/supervisor \
&& mkdir -p /run/sshd
......
db:
user: rvd_user
password: Pword12345*
host: ravada-mysql
\ No newline at end of file
host: ravada-mysql
ldap:
server: 10.1.36.224
admin_user:
dn: cn=Directory Manager
password: 12345678
base: 'dc=example,dc=com'
......@@ -7,7 +7,7 @@ logfile_maxbytes=0
[program:rvd_front]
environment=PERL5LIB="./lib"
command=morbo ./script/rvd_front
command=morbo -v ./script/rvd_front
autostart=true
autorestart=true
startsecs=5
......
https://cdnjs.cloudflare.com/ajax/libs/morris.js/0.5.1/morris.css morris.js/
https://use.fontawesome.com/releases/v5.10.1/fontawesome-free-5.10.1-web.zip
https://cdnjs.cloudflare.com/ajax/libs/intro.js/2.7.0/introjs.css intro.js/bin/
https://code.jquery.com/jquery-3.5.0.min.js jquery/
https://code.jquery.com/jquery-3.5.1.slim.min.js jquery/
https://cdnjs.cloudflare.com/ajax/libs/popper.js/1.14.3/umd/popper.min.js
https://jqueryui.com/resources/download/jquery-ui-1.11.4.zip
https://stackpath.bootstrapcdn.com/bootstrap/4.3.1/js/bootstrap.min.js
https://code.angularjs.org/1.7.8/angular-1.7.8.zip
https://cdnjs.cloudflare.com/ajax/libs/angular-ui-bootstrap/2.5.0/ui-bootstrap.min.js
https://cdn.jsdelivr.net/npm/ui-bootstrap4@3.0.6/dist/ui-bootstrap-tpls.js
https://cdnjs.cloudflare.com/ajax/libs/raphael/2.1.0/raphael-min.js raphael.js/
https://github.com/snapappointments/bootstrap-select/archive/v1.13.15.zip
https://cdnjs.cloudflare.com/ajax/libs/morris.js/0.5.1/morris.min.js morris.js/
https://cdnjs.cloudflare.com/ajax/libs/intro.js/2.7.0/intro.js intro.js/
https://github.com/twbs/bootstrap/releases/download/v4.3.1/bootstrap-4.3.1-dist.zip
......@@ -17,3 +17,14 @@ https://readthedocs.org/projects/ravada/badge/?version=latest ../img/latest.svg
https://img.shields.io/badge/License-AGPL%20v3-blue.svg ../img/License-AGPL%20v3-blue.svg
https://download.cksource.com/CKEditor/CKEditor/CKEditor%204.12.1/ckeditor_4.12.1_standard_easyimage.zip
https://ajax.googleapis.com/ajax/libs/angular_material/1.1.0/angular-material.min.js angular-material/
https://cdn.jsdelivr.net/npm/ui-bootstrap4@3.0.6/dist/ui-bootstrap-csp.css
# bookings
https://cdn.jsdelivr.net/npm/fullcalendar@5.1.0/main.css bookings/
https://cdn.jsdelivr.net/npm/clockpicker@0.0.7/dist/bootstrap-clockpicker.min.css bookings/
https://cdn.jsdelivr.net/npm/angularjs-toast@latest/angularjs-toast.css bookings/
https://cdn.jsdelivr.net/npm/angularjs-toast@latest/angularjs-toast.js bookings/
https://cdn.jsdelivr.net/npm/fullcalendar@5.1.0/main.min.js bookings/
https://cdn.jsdelivr.net/npm/fullcalendar-scheduler@5.1.0/locales-all.min.js bookings/
https://cdn.jsdelivr.net/npm/moment@2.27.0/min/moment-with-locales.min.js bookings/
https://cdn.jsdelivr.net/npm/angular-moment@1.3.0/angular-moment.min.js bookings/
https://cdn.jsdelivr.net/npm/clockpicker@0.0.7/dist/bootstrap-clockpicker.min.js bookings/
......@@ -44,10 +44,12 @@ sub download($url, $dst = $DIR_FALLBACK) {
print "$url downloaded to $dst\n";
$res->content->asset->move_to($dst);
}
elsif ($res->is_error) { print $res->message."\n" }
elsif ($res->is_error) { print $res->message."\n"; exit }
elsif ($res->code == 301) { print $res->headers->location."\n" }
else { print "Error ".$res->code." ".$res->message
." downloading $url\n"}
." downloading $url\n";
exit;
}
return $dst;
}
......
This diff is collapsed.
......@@ -26,7 +26,6 @@ sub init {
$LDAP_OK = 0;
require Ravada::Auth::LDAP;
Ravada::Auth::LDAP::init($config);
Ravada::Auth::LDAP::_connect_ldap();
$LDAP_OK = 1;
};
warn $@ if $@;
......
......@@ -193,11 +193,13 @@ sub search_user {
my $ldap = (delete $args{ldap} or _init_ldap_admin());
my $base = (delete $args{base} or _dc_base());
my $typesonly= (delete $args{typesonly} or 0);
my $escape_username = 1;
$escape_username = delete $args{escape_username} if exists $args{escape_username};
confess "ERROR: Unknown fields ".Dumper(\%args) if keys %args;
confess "ERROR: I can't connect to LDAP " if!$ldap;
$username = escape_filter_value($username);
$username = escape_filter_value($username) if $escape_username;
$username =~ s/ /\\ /g;
my $filter = "($field=$username)";
......@@ -250,6 +252,9 @@ Add a group to the LDAP
sub add_group {
my $name = shift;
my $base = (shift or _dc_base());
my $class = ( shift or [
'groupOfUniqueNames','nsMemberOf','posixGroup','top'
]);
$name = escape_filter_value($name);
......@@ -257,17 +262,32 @@ sub add_group {
cn => $name
,dn => "cn=$name,ou=groups,$base"
, attrs => [ cn=>$name
,objectClass => ['groupOfUniqueNames','top']
,objectClass => $class
,ou => 'Groups'
,description => "Group for $name"
,gidNumber => _search_new_gid()
]
);
if ($mesg->code) {
die "Error afegint $name ".$mesg->error;
die "Error creating group $name : ".$mesg->error."\n";
}
}
sub _search_new_gid() {
my %gid;
for my $group ( search_group( name => '*' ) ) {
my $gid_number = $group->get_value('gidNumber');
next if !$gid_number;
$gid{$gid_number}++;
}
my $new_gid = 100;
for (;;) {
return $new_gid if !$gid{$new_gid};
$new_gid++;
}
}
=head2 remove_group
Removes the group from the LDAP directory. Use with caution
......@@ -301,7 +321,7 @@ sub remove_group {
sub search_group {
my %args = @_;
my $name = delete $args{name} or confess "Missing group name";
my $name = delete $args{name};
my $base = ( delete $args{base} or "ou=groups,"._dc_base() );
my $ldap = ( delete $args{ldap} or _init_ldap_admin());
my $retry =( delete $args{retry} or 0);
......@@ -309,9 +329,6 @@ sub search_group {
confess "ERROR: Unknown fields ".Dumper(\%args) if keys %args;
confess "ERROR: I can't connect to LDAP " if!$ldap;
$name = escape_filter_value($name);
my $mesg = $ldap ->search (
filter => "cn=$name"
,base => $base
......@@ -332,7 +349,8 @@ sub search_group {
}
my @entries = $mesg->entries;
return $entries[0]
return @entries if wantarray;
return $entries[0];
}
=head2 add_to_group
......@@ -440,7 +458,8 @@ sub _login_bind {
for my $user (@user) {
my $dn = $user->dn;
$found++;
my $ldap = _connect_ldap($dn, $password);
my $ldap;
eval { $ldap = _connect_ldap($dn, $password) };
if ( $ldap ) {
$self->{_auth} = 'bind';
$self->{_ldap_entry} = $user;
......
......@@ -379,7 +379,10 @@ sub is_operator {
|| $self->can_list_clones()
|| $self->can_list_clones_from_own_base()
|| $self->can_list_machines()
|| $self->is_user_manager();
|| $self->is_user_manager()
|| $self->can_view_groups()
|| $self->can_manage_groups()
;
return 0;
}
......@@ -613,7 +616,6 @@ sub can_do($self, $grant) {
if $grant !~ /^[a-z_]+$/;
return $self->{_grant}->{$grant} if defined $self->{_grant}->{$grant};
confess "Unknown permission '$grant'. Maybe you are using an old release.\n"
."Try removing the table grant_types and start rvd_back again:\n"
."mysql> drop table grant_types;\n"
......@@ -632,7 +634,7 @@ Returns if the user is allowed to perform a privileged action in a virtual machi
=cut
sub can_do_domain($self, $grant, $domain) {
my %valid_grant = map { $_ => 1 } qw(change_settings shutdown rename);
my %valid_grant = map { $_ => 1 } qw(change_settings shutdown reboot rename);
confess "Invalid grant here '$grant'" if !$valid_grant{$grant};
return 0 if !$self->can_do($grant) && !$self->_domain_id_base($domain);
......@@ -641,7 +643,13 @@ sub can_do_domain($self, $grant, $domain) {
return 1 if $self->_domain_id_owner($domain) == $self->id && $self->can_do($grant);
if ($self->can_do("${grant}_clones") && $self->_domain_id_base($domain)) {
my $base = Ravada::Front::Domain->open($self->_domain_id_base($domain));
my $base;
my $id_base = $self->_domain_id_base($domain);
eval { $base = Ravada::Front::Domain->open($id_base) };
if (!defined $base) {
warn "Error: base $id_base from $domain not found";
return 0;
}
return 1 if $base->id_owner == $self->id;
}
return 0;
......@@ -672,7 +680,7 @@ sub _load_grants($self) {
my $sth;
eval { $sth= $$CON->dbh->prepare(
"SELECT gt.name, gu.allowed, gt.enabled"
"SELECT gt.name, gu.allowed, gt.enabled, gt.is_int"
." FROM grant_types gt LEFT JOIN grants_user gu "
." ON gt.id = gu.id_grant "
." AND gu.id_user=?"
......@@ -680,8 +688,8 @@ sub _load_grants($self) {
$sth->execute($self->id);
};
confess $@ if $@;
my ($name, $allowed, $enabled);
$sth->bind_columns(\($name, $allowed, $enabled));
my ($name, $allowed, $enabled, $is_int);
$sth->bind_columns(\($name, $allowed, $enabled, $is_int));
while ($sth->fetch) {
my $grant_alias = $self->_grant_alias($name);
......@@ -735,6 +743,7 @@ sub grant_user_permissions($self,$user) {
$self->grant($user, 'remove');
$self->grant($user, 'shutdown');
$self->grant($user, 'screenshot');
$self->grant($user, 'reboot');
}
=head2 grant_operator_permissions
......
package Ravada::Booking;
use Carp qw(carp croak);
use Data::Dumper;
use DateTime::Format::DateParse;
use Moose;
use Ravada::Booking::Entry;
use Ravada::Front;
no warnings "experimental::signatures";
use feature qw(signatures);
our $CONNECTOR = \$Ravada::CONNECTOR;
our $TZ;
sub BUILD($self, $args) {
return $self->_open($args->{id}) if $args->{id};
my $date = delete $args->{date_booking};
my $date_start = delete $args->{date_start};
my $date_end = delete $args->{date_end};
confess "Error: supply either date or date_start"
if (!defined $date && !defined $date_start);
$date = $date_start if !defined $date;
$date_end = $date if !defined $date_end;
$date = _datetime($date);
$date_end = _datetime($date_end);
my $time_start = delete $args->{time_start} or confess "Error: missing time start";
my $time_end = delete $args->{time_end} or confess "Error: missing time end";
my $day_of_week = delete $args->{day_of_week};
my %entry;
my @fields_entry = qw ( bases ldap_groups users time_start time_end );
for (@fields_entry) {
$entry{$_} = delete $args->{$_};
}
$self->_insert_db(%$args
, date_start => $date->ymd
, date_end => $date_end->ymd
);
$entry{id_booking} = $self->id;
$entry{time_start} = $time_start;
$entry{time_end} = $time_end;
$entry{title} = $args->{title};
$entry{description} = $args->{description};
$day_of_week = $date->day_of_week if !$day_of_week || $day_of_week =~ /^0+$/;
my %dow = map { $_ => 1 } split //,$day_of_week;
my $saved = 0 ;
for (;;) {
if ( $dow{$date->day_of_week()} ) {
my $entry = Ravada::Booking::Entry->new(%entry, date_booking => $date->ymd);
$saved++;
}
$date->add( days => 1 );
last if DateTime->compare($date, $date_end) >0;
}
die "Error: No entries were saved $date - $date_end\n".Dumper(\%dow)
if !$saved;
return $self;
}
sub _datetime($dt) {
return $dt if ref($dt);
return DateTime::Format::DateParse->parse_datetime($dt);
}
sub _init_connector {
return if $CONNECTOR && $$CONNECTOR;
$CONNECTOR = \$Ravada::CONNECTOR if $Ravada::CONNECTOR;
$CONNECTOR = \$Ravada::Front::CONNECTOR if !defined $$CONNECTOR
&& defined $Ravada::Front::CONNECTOR;
}
sub _insert_db($self, %field) {
my $sth = $self->_dbh->prepare("SELECT * FROM bookings");
$sth->execute();
my $cols = $sth->{NAME};
foreach my $col (keys %field) {
delete $field{$col} if !grep( /^$col$/, @$cols );
}
my $query = "INSERT INTO bookings "
."(" . join(",",sort keys %field )." )"
." VALUES (". join(",", map { '?' } keys %field )." ) "
;
$sth = $self->_dbh->prepare($query);
eval { $sth->execute( map { $field{$_} } sort keys %field ) };
if ($@) {
warn "$query\n".Dumper(\%field);
confess $@;
}
my $new_id = Ravada::Utils::last_insert_id($self->_dbh) or confess "Unkown last id";
$sth->finish;
$sth = $self->_dbh->prepare("SELECT * FROM bookings WHERE id=? ");
$sth->execute($new_id);
$self->{_data} = $sth->fetchrow_hashref;
}
sub _fix_date($dt) {
return if !ref($$dt);
my $date = $$dt->year."-".$$dt->month."-".$$dt->day;
$$dt = $date;
}
sub _fix_time($time) {
confess "Error in time " if ref($$time);
my ($h,$m, $s) = split /:/ ,$$time;
confess "Error in time ".Dumper($time) if!defined $h || !defined $m
|| $h>23 || $m>59 || $s>59;
$$time = $h*3600 + $m*60 + $s;
}
sub _open($self, $id) {
my $sth = $self->_dbh->prepare("SELECT * FROM bookings WHERE id=?");
$sth->execute($id);
my $row = $sth->fetchrow_hashref;
die "Error: booking $id not found " if !$row || !keys %$row || !exists $row->{id};
$self->{_data} = $row;
return $self;
}
sub id($self) { return $self->_data('id'); }
sub search($self, %args) {
_init_connector();
return $self->_search_date($args{date}) if $args{date};
return $self->_search_like(%args);
}
sub _data($self, $field) {
confess if !ref($self);
confess "Error: field '$field' doesn't exist in ".Dumper($self->{_data}, $self)
if !exists $self->{_data}->{$field};
return $self->{_data}->{$field};
}
sub _dbh($self=undef) {
_init_connector();
return $$CONNECTOR->dbh;
}
sub _search_date($self,$date) {
my $sth = $self->_dbh->prepare(
"SELECT id FROM bookings "
."WHERE date_start <= ? "
."AND date_end >= ? "
);
_fix_date(\$date);
$sth->execute($date, $date);
return $self->_found($sth);
}
sub _search_datetime($self, %args) {
my $date = delete $args{date} or confess "Error: missing date";
my $time = delete $args{time} or confess "Error: missing time";
my $id_base = delete $args{id_base} or confess "Error: missing id_base";
my $sth = $self->_dbh->prepare(
"SELECT id FROM booking_entries "
."WHERE date_booking = ? "
." AND time_start <= ? "
." AND time_end >= ? "
." AND id_base = ? "
);
_fix_date(\$date);
_fix_time(\$time);
$sth->execute($date , $time, $time, $id_base);
return $self->_found($sth);
}
sub _found($self, $sth) {
my @found;
while (my $id = $sth->fetchrow) {
push @found, Ravada::Booking->new( id => $id);
}
$sth->finish;
return if !scalar(@found);
return $found[0] if scalar(@found) == 1;
return @found;
}
sub _search_like($self, %field) {
my $sql = '';
my @values;
for my $name(sort keys %field) {
$sql .= "AND " if $sql;