Ravada.pm 8.86 KB
Newer Older
1
2
3
4
5
package Ravada;

use warnings;
use strict;

6
use Data::Dumper;
7
8
9
10
use DBIx::Connector;
use Moose;
use YAML;

11
use Ravada::Request;
12
use Ravada::VM::KVM;
13
use Ravada::VM::LXC;
14

Francesc Guasch's avatar
Francesc Guasch committed
15
16
17
18
19
20
21
22
23
24
25
26
=head1 NAME

Ravada - Remove Virtual Desktop Manager

=head1 SYNOPSIS

  use Ravada;

  my $ravada = Ravada->new()

=cut

27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44

our $FILE_CONFIG = "/etc/ravada.conf";

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

our $CONNECTOR;
our $CONFIG = {};
_connect_dbh();


has 'vm' => (
          is => 'ro'
        ,isa => 'ArrayRef'
       ,lazy => 1
     , builder => '_create_vm'
);

has 'connector' => (
45
46
47
48
49
50
        is => 'rw'
);

has 'config' => (
    is => 'ro'
    ,isa => 'Str'
51
52
);

Francesc Guasch's avatar
Francesc Guasch committed
53
54
55
56
57
58
59
=head2 BUILD

Internal constructor

=cut


60
61
sub BUILD {
    my $self = shift;
62
    if ($self->config()) {
63
        _init_config($self->config);
64
    } else {
Francesc Guasch's avatar
Francesc Guasch committed
65
        _init_config($FILE_CONFIG) if -e $FILE_CONFIG;
66
    }
67

Francesc Guasch's avatar
Francesc Guasch committed
68
69
70
71
    if ( $self->connector ) {
        $CONNECTOR = $self->connector 
    } else {
        $CONNECTOR = $self->_connect_dbh();
72
        $self->connector($CONNECTOR);
Francesc Guasch's avatar
Francesc Guasch committed
73
    }
74

75
76
77
78
79
80
}

sub _connect_dbh {
    my $driver= ($CONFIG->{db}->{driver} or 'mysql');;
    my $db_user = ($CONFIG->{db}->{user} or getpwnam($>));;
    my $db_pass = ($CONFIG->{db}->{password} or undef);
Francesc Guasch's avatar
Francesc Guasch committed
81
82
    my $db = ( $CONFIG->{db}->{db} or 'ravada' );
    return DBIx::Connector->new("DBI:$driver:$db"
83
84
85
86
87
88
89
90
91
92
93
                        ,$db_user,$db_pass,{RaiseError => 1
                        , PrintError=> 0 });

}

sub _init_config {
    my $file = shift;
    $CONFIG = YAML::LoadFile($file);
    _connect_dbh();
}

94
sub _create_vm_kvm {
95
    my $self = shift;
96

97
98
99
100
    my $cmd_qemu_img = `which qemu-img`;
    chomp $cmd_qemu_img;

    return(undef,"ERROR: Missing qemu-img") if !$cmd_qemu_img;
101
102

    my $vm_kvm;
103

104
105
    eval { $vm_kvm = Ravada::VM::KVM->new( connector => ( $self->connector or $CONNECTOR )) };
    my $err_kvm = $@;
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126

    my ($internal_vm , $storage);
    eval {
        $internal_vm = $vm_kvm->vm;
        $internal_vm->list_all_domains();

        $storage = $vm_kvm->dir_img();
    };
    warn $internal_vm;
    warn $storage;
    $vm_kvm = undef if $@ || !$internal_vm || !$storage;
    return ($vm_kvm,$@);
}

sub _create_vm {
    my $self = shift;

    my @vms = ();

    my ($vm_kvm, $err_kvm) = $self->_create_vm_kvm();

127
128
129
130
131
132
133
134
135
136
    push @vms,($vm_kvm) if $vm_kvm;

    my $vm_lxc;
    eval { $vm_lxc = Ravada::VM::LXC->new( connector => ( $self->connector or $CONNECTOR )) };
    push @vms,($vm_lxc) if $vm_lxc;
    my $err_lxc = $@;

    if (!@vms) {
        die "No VMs found: $err_lxc\n$err_kvm\n";
    }
137
    warn Dumper(@vms);
138
139
    return \@vms;

140
141
}

Francesc Guasch's avatar
Francesc Guasch committed
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
=head2 create_domain

Creates a new domain based on an ISO image or another domain.

  my $domain = $ravada->create_domain( 
         name => $name
    , id_iso => 1
  );


  my $domain = $ravada->create_domain( 
         name => $name
    , id_base => 3
  );


=cut


161
sub create_domain {
162
163
    my $self = shift;

164
165
166
    return $self->vm->[0]->create_domain(@_);
}

Francesc Guasch's avatar
Francesc Guasch committed
167
168
169
170
171
172
173
174
=head2 remove_domain

Removes a domain

  $ravada->remove_domain($name);

=cut

175
176
177
178
sub remove_domain {
    my $self = shift;
    my $name = shift or confess "Missing domain name";

179
    my $domain = $self->search_domain($name, 1)
180
181
182
183
        or confess "ERROR: I can't find domain $name";
    $domain->remove();
}

Francesc Guasch's avatar
Francesc Guasch committed
184
185
186
187
188
189
=head2 search_domain

  my $domain = $ravada->search_domain($name);

=cut

190
191
192
sub search_domain {
    my $self = shift;
    my $name = shift;
193
    my $import = shift;
194
195
196

    for my $vm (@{$self->vm}) {
        my $domain = $vm->search_domain($name);
197
198
199
200
201
        return if !$domain;
        my $id;
        eval { $id = $domain->id };
        # TODO import the domain in the database with an _insert_db or something
        return $domain if $id || $import;
202
    }
203
    return;
204
}
205

Francesc Guasch's avatar
Francesc Guasch committed
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
=head2 search_domain_by_id

  my $domain = $ravada->search_domain_by_id($id);

=cut

sub search_domain_by_id {
    my $self = shift;
      my $id = shift;

    for my $vm (@{$self->vm}) {
        my $domain = $vm->search_domain_by_id($id);
        return $domain if $domain;
    }
}


223
224
=head2 list_domains

Francesc Guasch's avatar
Francesc Guasch committed
225
List all created domains
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241

  my @list = $ravada->list_domains();

=cut

sub list_domains {
    my $self = shift;
    my @domains;
    for my $vm (@{$self->vm}) {
        for my $domain ($vm->list_domains) {
            push @domains,($domain);
        }
    }
    return @domains;
}

242
243
244
245
246
247
248
249
250
251
252
253
254
=head2 list_domains_data

List all domains in raw format. Return a list of id => { name , id , is_active , is_base }

   my $list = $ravada->list_domains_data();

   $c->render(json => $list);

=cut

sub list_domains_data {
    my $self = shift;
    my @domains;
255
256
257
258
259
260
    for my $domain ($self->list_domains()) {
        push @domains, {                id => $domain->id 
                                    , name => $domain->name
                                  ,is_base => $domain->is_base
                                ,is_active => $domain->is_active
                               
261
262
                           }
    }
263
    return \@domains;
264
265
266
}


Francesc Guasch's avatar
Francesc Guasch committed
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
=head2 list_bases

List all base domains

  my @list = $ravada->list_domains();


=cut

sub list_bases {
    my $self = shift;
    my @domains;
    for my $vm (@{$self->vm}) {
        for my $domain ($vm->list_domains) {
            push @domains,($domain) if $domain->is_base;
        }
    }
    return @domains;
}

287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
=head2 list_bases_data

List information about the bases

=cut

sub list_bases_data {
    my $self = shift;
    my @data;
    for ($self->list_bases ) {
        push @data,{ id => $_->id , name => $_->name };
    }
    return \@data;
}

Francesc Guasch's avatar
Francesc Guasch committed
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
=head2 list_images

List all ISO images

=cut

sub list_images {
    my $self = shift;
    my @domains;
    my $sth = $CONNECTOR->dbh->prepare(
        "SELECT * FROM iso_images ORDER BY name"
    );
    $sth->execute;
    while (my $row = $sth->fetchrow_hashref) {
        push @domains,($row);
    }
    $sth->finish;
    return @domains;
}

322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
=head2 list_images_data

List information about the images

=cut

sub list_images_data {
    my $self = shift;
    my @data;
    for ($self->list_images ) {
        push @data,{ id => $_->{id} , name => $_->{name} };
    }
    return \@data;
}

Francesc Guasch's avatar
Francesc Guasch committed
337

Francesc Guasch's avatar
Francesc Guasch committed
338
339
340
341
=head2 remove_volume

  $ravada->remove_volume($file);

Francesc Guasch's avatar
Francesc Guasch committed
342

Francesc Guasch's avatar
Francesc Guasch committed
343
344
=cut

345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
sub remove_volume {
    my $self = shift;

    my $file = shift;
    my ($name) = $file =~ m{.*/(.*)};

    my $removed = 0;
    for my $vm (@{$self->vm}) {
        my $vol = $vm->search_volume($name);
        next if !$vol;

        $vol->delete();
        $removed++;
    }
    if (!$removed && -e $file ) {
        warn "volume $file not found. removing file $file.\n";
        unlink $file or die "$! $file";
    }

}
365

Francesc Guasch's avatar
Francesc Guasch committed
366
367
368
369
370
371
372
373
=head2 process_requests

This is run in the ravada backend. It processes the commands requested by the fronted

  $ravada->process_requests();

=cut

374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
sub process_requests {
    my $self = shift;

    my $sth = $CONNECTOR->dbh->prepare("SELECT id FROM requests WHERE status='requested'");
    $sth->execute;
    while (my ($id)= $sth->fetchrow) {
        $self->_execute(Ravada::Request->open($id));
    }
    $sth->finish;
}

sub _execute {
    my $self = shift;
    my $request = shift;

Francesc Guasch's avatar
Francesc Guasch committed
389
390
391
392
393
394
395
396
397
398
399
400
401
    my $sub = $self->_req_method($request->command);

    die "Unknown command ".$request->command
        if !$sub;

    return $sub->($self,$request);

}

sub _cmd_create {
    my $self = shift;
    my $request = shift;

402
403
404
405
    $request->status('creating domain');
    my $domain;
    eval {$domain = $self->create_domain(%{$request->args}) };

Francesc Guasch's avatar
Francesc Guasch committed
406
407
408
409
410
411
412
413
414
415
416
417
418
    $request->status('done');
    $request->error($@);

}

sub _cmd_remove {
    my $self = shift;
    my $request = shift;

    $request->status('working');
    eval { $self->remove_domain($request->args('name')) };
    $request->status('done');
    $request->error($@);
Francesc Guasch's avatar
Francesc Guasch committed
419

420
}
Francesc Guasch's avatar
Francesc Guasch committed
421

Francesc Guasch's avatar
Francesc Guasch committed
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
sub _cmd_start {
    my $self = shift;
    my $request = shift;

    $request->status('working');
    my $name = $request->args('name');
    eval { 
        my $domain = $self->search_domain($name);
        die "Unknown domain '$name'\n" if !$domain;
        $domain->start();
    };
    $request->status('done');
    $request->error($@);

}

Francesc Guasch's avatar
Francesc Guasch committed
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
sub _cmd_shutdown {
    my $self = shift;
    my $request = shift;

    $request->status('working');
    my $name = $request->args('name');
    eval { 
        my $domain = $self->search_domain($name);
        die "Unknown domain '$name'\n" if !$domain;
        $domain->shutdown();
    };
    $request->status('done');
    $request->error($@);

}


Francesc Guasch's avatar
Francesc Guasch committed
455
456
457
458
459
sub _req_method {
    my $self = shift;
    my  $cmd = shift;

    my %methods = (
Francesc Guasch's avatar
Francesc Guasch committed
460

Francesc Guasch's avatar
Francesc Guasch committed
461
462
463
          start => \&_cmd_start
        ,create => \&_cmd_create
        ,remove => \&_cmd_remove
Francesc Guasch's avatar
Francesc Guasch committed
464
      ,shutdown => \&_cmd_shutdown
Francesc Guasch's avatar
Francesc Guasch committed
465
466
467
468
    );
    return $methods{$cmd};
}

469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
=head2 search_vm

Searches for a VM of a given type

  my $vm = $ravada->search_vm('kvm');

=cut

sub search_vm {
    my $self = shift;
    my $type = shift;

    my $class = 'Ravada::VM::'.uc($type);
    for my $vm (@{$self->vm}) {
        return $vm if ref($vm) eq $class;
    }
    return;
}
Francesc Guasch's avatar
Francesc Guasch committed
487

Francesc Guasch's avatar
Francesc Guasch committed
488
489
490
491
492
493
494
495
496
497
=head1 AUTHOR

Francesc Guasch-Ortiz	, frankie@telecos.upc.edu

=head1 SEE ALSO

Sys::Virt

=cut

498
1;