Statistics
| Branch: | Tag: | Revision:

one / src / vmm / LibVirtDriverKVM.cc @ 171bf550

History | View | Annotate | Download (34.1 KB)

1
/* -------------------------------------------------------------------------- */
2
/* Copyright 2002-2015, OpenNebula Project, OpenNebula Systems                */
3
/*                                                                            */
4
/* Licensed under the Apache License, Version 2.0 (the "License"); you may    */
5
/* not use this file except in compliance with the License. You may obtain    */
6
/* a copy of the License at                                                   */
7
/*                                                                            */
8
/* http://www.apache.org/licenses/LICENSE-2.0                                 */
9
/*                                                                            */
10
/* Unless required by applicable law or agreed to in writing, software        */
11
/* distributed under the License is distributed on an "AS IS" BASIS,          */
12
/* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.   */
13
/* See the License for the specific language governing permissions and        */
14
/* limitations under the License.                                             */
15
/* -------------------------------------------------------------------------- */
16

    
17
#include "LibVirtDriver.h"
18

    
19
#include "Nebula.h"
20
#include <sstream>
21
#include <fstream>
22
#include <libgen.h>
23
#include <math.h>
24

    
25
const float LibVirtDriver::CGROUP_BASE_CPU_SHARES = 1024;
26

    
27
const int LibVirtDriver::CEPH_DEFAULT_PORT = 6789;
28

    
29
const int LibVirtDriver::GLUSTER_DEFAULT_PORT = 24007;
30

    
31
const int LibVirtDriver::ISCSI_DEFAULT_PORT = 3260;
32

    
33
/**
34
 *  This function generates the <host> element for network disks
35
 */
36
static void do_network_hosts(ofstream& file,
37
                             const string& cg_host,
38
                             const string& transport,
39
                             int   default_port)
40
{
41
    if (cg_host.empty())
42
    {
43
        file << "/>" << endl;
44
        return;
45
    }
46

    
47
    vector<string>::const_iterator it;
48
    vector<string> hosts;
49

    
50
    hosts = one_util::split(cg_host, ' ');
51

    
52
    file << ">" << endl;
53

    
54
    for (it = hosts.begin(); it != hosts.end(); it++)
55
    {
56
        vector<string> parts = one_util::split(*it, ':');
57

    
58
        if (parts.empty())
59
        {
60
            continue;
61
        }
62

    
63
        file << "\t\t\t\t<host name=" << one_util::escape_xml_attr(parts[0]);
64

    
65
        if (parts.size() > 1)
66
        {
67
            file << " port=" << one_util::escape_xml_attr(parts[1]);
68
        }
69
        else if ( default_port != -1 )
70
        {
71
            file << " port=" << one_util::escape_xml_attr(default_port);
72
        }
73

    
74
        if (!transport.empty())
75
        {
76
            file << " transport=" << one_util::escape_xml_attr(transport);
77
        }
78

    
79
        file << "/>" << endl;
80
    }
81

    
82
    file << "\t\t\t</source>" << endl;
83
}
84

    
85
/* -------------------------------------------------------------------------- */
86
/* -------------------------------------------------------------------------- */
87

    
88
int LibVirtDriver::deployment_description_kvm(
89
        const VirtualMachine *  vm,
90
        const string&           file_name) const
91
{
92
    ofstream  file;
93

    
94
    int       num;
95

    
96
    string  vcpu;
97
    float   cpu;
98
    int     memory;
99

    
100
    string  emulator_path = "";
101

    
102
    const VectorAttribute * os;
103

    
104
    string  kernel     = "";
105
    string  initrd     = "";
106
    string  boot       = "";
107
    string  root       = "";
108
    string  kernel_cmd = "";
109
    string  bootloader = "";
110
    string  arch       = "";
111
    string  machine    = "";
112

    
113
    vector<string> boots;
114

    
115
    vector<const VectorAttribute *> disk;
116
    const VectorAttribute * context;
117

    
118
    string  type            = "";
119
    string  disk_type       = "";
120
    string  target          = "";
121
    string  bus             = "";
122
    string  ro              = "";
123
    string  driver          = "";
124
    string  cache           = "";
125
    string  disk_io         = "";
126
    string  discard         = "";
127
    string  source          = "";
128
    string  clone           = "";
129
    string  ceph_host       = "";
130
    string  ceph_secret     = "";
131
    string  ceph_user       = "";
132
    string  iscsi_host      = "";
133
    string  iscsi_user      = "";
134
    string  iscsi_usage     = "";
135
    string  iscsi_iqn       = "";
136
    string  pool_name       = "";
137
    string  sheepdog_host   = "";
138
    string  gluster_host    = "";
139
    string  gluster_volume  = "";
140

    
141
    string  total_bytes_sec = "";
142
    string  read_bytes_sec  = "";
143
    string  write_bytes_sec = "";
144
    string  total_iops_sec  = "";
145
    string  read_iops_sec   = "";
146
    string  write_iops_sec  = "";
147

    
148
    string  default_total_bytes_sec = "";
149
    string  default_read_bytes_sec  = "";
150
    string  default_write_bytes_sec = "";
151
    string  default_total_iops_sec  = "";
152
    string  default_read_iops_sec   = "";
153
    string  default_write_iops_sec  = "";
154

    
155
    int     disk_id;
156
    string  default_driver          = "";
157
    string  default_driver_cache    = "";
158
    string  default_driver_disk_io  = "";
159
    string  default_driver_discard  = "";
160
    bool    readonly;
161

    
162
    vector<const VectorAttribute *> nic;
163

    
164
    string  mac        = "";
165
    string  bridge     = "";
166
    string  bridge_ovs = "";
167
    string  script     = "";
168
    string  model      = "";
169
    string  ip         = "";
170
    string  filter     = "";
171

    
172
    string  default_filter = "";
173
    string  default_model  = "";
174

    
175
    const VectorAttribute * graphics;
176

    
177
    string  listen          = "";
178
    string  port            = "";
179
    string  passwd          = "";
180
    string  keymap          = "";
181
    string  spice_options   = "";
182

    
183
    const VectorAttribute * input;
184

    
185
    vector<const VectorAttribute *> pci;
186

    
187
    string  domain          = "";
188
    /* bus is already defined for disks */
189
    string  slot            = "";
190
    string  func            = "";
191

    
192
    const VectorAttribute * features;
193

    
194
    bool pae        = false;
195
    bool acpi       = false;
196
    bool apic       = false;
197
    bool hyperv     = false;
198
    bool localtime  = false;
199

    
200
    int pae_found       = -1;
201
    int acpi_found      = -1;
202
    int apic_found      = -1;
203
    int hyperv_found    = -1;
204
    int localtime_found = -1;
205

    
206
    string hyperv_options = "";
207

    
208
    vector<const VectorAttribute *> raw;
209
    string default_raw = "";
210
    string data        = "";
211

    
212
    string  vm_xml;
213
    string* vm64;
214

    
215
    // ------------------------------------------------------------------------
216

    
217
    file.open(file_name.c_str(), ios::out);
218

    
219
    if (file.fail() == true)
220
    {
221
        goto error_file;
222
    }
223

    
224
    // ------------------------------------------------------------------------
225
    // Starting XML document
226
    // ------------------------------------------------------------------------
227

    
228
    file << "<domain type=" << one_util::escape_xml_attr(emulator)
229
         << " xmlns:qemu='http://libvirt.org/schemas/domain/qemu/1.0'>"
230
         << endl;
231

    
232
    // ------------------------------------------------------------------------
233
    // Domain name
234
    // ------------------------------------------------------------------------
235

    
236
    file << "\t<name>one-" << vm->get_oid() << "</name>" << endl;
237

    
238
    // ------------------------------------------------------------------------
239
    // CPU & Memory
240
    // ------------------------------------------------------------------------
241

    
242
    vm->get_template_attribute("VCPU", vcpu);
243

    
244
    if(vcpu.empty())
245
    {
246
        get_default("VCPU", vcpu);
247
    }
248

    
249
    if (!vcpu.empty())
250
    {
251
        file << "\t<vcpu>" << one_util::escape_xml(vcpu) << "</vcpu>" << endl;
252
    }
253

    
254
    //Every process gets 1024 shares by default (cgroups), scale this with CPU
255
    if(vm->get_template_attribute("CPU", cpu))
256
    {
257
        file << "\t<cputune>" << endl
258
             << "\t\t<shares>"<< ceil( cpu * CGROUP_BASE_CPU_SHARES )
259
             << "</shares>"   << endl
260
             << "\t</cputune>"<< endl;
261
    }
262

    
263
    // Memory must be expressed in Kb
264
    if (vm->get_template_attribute("MEMORY",memory))
265
    {
266
        file << "\t<memory>" << memory * 1024 << "</memory>" << endl;
267
    }
268
    else
269
    {
270
        goto error_memory;
271
    }
272

    
273
    // ------------------------------------------------------------------------
274
    //  OS and boot options
275
    // ------------------------------------------------------------------------
276

    
277
    file << "\t<os>" << endl;
278

    
279
    os = vm->get_template_attribute("OS");
280

    
281
    if( os != 0 )
282
    {
283
        kernel     = os->vector_value("KERNEL");
284
        initrd     = os->vector_value("INITRD");
285
        boot       = os->vector_value("BOOT");
286
        root       = os->vector_value("ROOT");
287
        kernel_cmd = os->vector_value("KERNEL_CMD");
288
        bootloader = os->vector_value("BOOTLOADER");
289
        arch       = os->vector_value("ARCH");
290
        machine    = os->vector_value("MACHINE");
291
    }
292

    
293
    if ( arch.empty() )
294
    {
295
        get_default("OS","ARCH",arch);
296

    
297
        if ( arch.empty() )
298
        {
299
            goto error_arch;
300
        }
301
    }
302

    
303
    if ( machine.empty() )
304
    {
305
        get_default("OS", "MACHINE", machine);
306
    }
307

    
308
    file << "\t\t<type arch=" << one_util::escape_xml_attr(arch);
309

    
310
    if ( !machine.empty() )
311
    {
312
        file << " machine=" << one_util::escape_xml_attr(machine);
313
    }
314

    
315
    file << ">hvm</type>" << endl;
316

    
317
    if ( kernel.empty() )
318
    {
319
        get_default("OS","KERNEL",kernel);
320
    }
321

    
322
    if ( initrd.empty() )
323
    {
324
        get_default("OS","INITRD",initrd);
325
    }
326

    
327
    if ( bootloader.empty() )
328
    {
329
        get_default("OS","BOOTLOADER",bootloader);
330
    }
331

    
332
    if ( boot.empty() )
333
    {
334
        get_default("OS","BOOT",boot);
335

    
336
        if ( boot.empty() )
337
        {
338
            goto error_boot;
339
        }
340
    }
341

    
342
    if ( root.empty() )
343
    {
344
        get_default("OS","ROOT",root);
345
    }
346

    
347
    if ( kernel_cmd.empty() )
348
    {
349
        get_default("OS","KERNEL_CMD",kernel_cmd);
350
    }
351

    
352
    // Start writing to the file with the info we got
353

    
354
    if ( !kernel.empty() )
355
    {
356
        file << "\t\t<kernel>" << one_util::escape_xml(kernel) << "</kernel>\n";
357

    
358
        if ( !initrd.empty() )
359
        {
360
            file << "\t\t<initrd>" << one_util::escape_xml(initrd) << "</initrd>\n";
361
        }
362

    
363
        if ( !root.empty() )
364
        {
365
            kernel_cmd = "root=/dev/" + root + " " + kernel_cmd;
366
        }
367

    
368
        if (!kernel_cmd.empty())
369
        {
370
            file << "\t\t<cmdline>" << one_util::escape_xml(kernel_cmd)
371
                 << "</cmdline>\n";
372
        }
373
    }
374
    else if ( !bootloader.empty() )
375
    {
376
        file << "\t\t<bootloader>" << one_util::escape_xml(bootloader)
377
             << "</bootloader>\n";
378
    }
379

    
380
    boots = one_util::split(boot, ',');
381

    
382
    for (vector<string>::const_iterator it=boots.begin(); it!=boots.end(); it++)
383
    {
384
        file << "\t\t<boot dev=" << one_util::escape_xml_attr(*it) << "/>\n";
385
    }
386

    
387
    file << "\t</os>" << endl;
388

    
389
    // ------------------------------------------------------------------------
390
    // DEVICES SECTION
391
    // ------------------------------------------------------------------------
392
    file << "\t<devices>" << endl;
393

    
394
    get_default("EMULATOR",emulator_path);
395

    
396
    if(emulator_path.empty())
397
    {
398
        emulator_path = "/usr/bin/kvm";
399
    }
400

    
401
    file << "\t\t<emulator>" << one_util::escape_xml(emulator_path)
402
         << "</emulator>\n";
403

    
404
    // ------------------------------------------------------------------------
405
    // Disks
406
    // ------------------------------------------------------------------------
407
    get_default("DISK", "DRIVER", default_driver);
408

    
409
    if (default_driver.empty())
410
    {
411
        default_driver = "raw";
412
    }
413

    
414
    get_default("DISK", "CACHE", default_driver_cache);
415

    
416
    if (default_driver_cache.empty())
417
    {
418
       default_driver_cache = "default";
419
    }
420

    
421
    get_default("DISK", "IO", default_driver_disk_io);
422
    get_default("DISK", "DISCARD", default_driver_discard);
423
    get_default("DISK", "TOTAL_BYTES_SEC", default_total_bytes_sec);
424
    get_default("DISK", "READ_BYTES_SEC", default_read_bytes_sec);
425
    get_default("DISK", "WRITE_BYTES_SEC", default_write_bytes_sec);
426
    get_default("DISK", "TOTAL_IOPS_SEC", default_total_iops_sec);
427
    get_default("DISK", "READ_IOPS_SEC", default_read_iops_sec);
428
    get_default("DISK", "WRITE_IOPS_SEC", default_write_iops_sec);
429

    
430
    // ------------------------------------------------------------------------
431

    
432
    num = vm->get_template_attribute("DISK", disk);
433

    
434
    for (int i=0; i < num ;i++)
435
    {
436
        type            = disk[i]->vector_value("TYPE");
437
        disk_type       = disk[i]->vector_value("DISK_TYPE");
438
        target          = disk[i]->vector_value("TARGET");
439
        ro              = disk[i]->vector_value("READONLY");
440
        driver          = disk[i]->vector_value("DRIVER");
441
        cache           = disk[i]->vector_value("CACHE");
442
        disk_io         = disk[i]->vector_value("IO");
443
        discard         = disk[i]->vector_value("DISCARD");
444
        source          = disk[i]->vector_value("SOURCE");
445
        clone           = disk[i]->vector_value("CLONE");
446

    
447
        ceph_host       = disk[i]->vector_value("CEPH_HOST");
448
        ceph_secret     = disk[i]->vector_value("CEPH_SECRET");
449
        ceph_user       = disk[i]->vector_value("CEPH_USER");
450
        pool_name       = disk[i]->vector_value("POOL_NAME");
451

    
452
        gluster_host    = disk[i]->vector_value("GLUSTER_HOST");
453
        gluster_volume  = disk[i]->vector_value("GLUSTER_VOLUME");
454

    
455
        sheepdog_host   = disk[i]->vector_value("SHEEPDOG_HOST");
456
        total_bytes_sec = disk[i]->vector_value("TOTAL_BYTES_SEC");
457
        read_bytes_sec  = disk[i]->vector_value("READ_BYTES_SEC");
458
        write_bytes_sec = disk[i]->vector_value("WRITE_BYTES_SEC");
459
        total_iops_sec  = disk[i]->vector_value("TOTAL_IOPS_SEC");
460
        read_iops_sec   = disk[i]->vector_value("READ_IOPS_SEC");
461
        write_iops_sec  = disk[i]->vector_value("WRITE_IOPS_SEC");
462

    
463
        if ( total_bytes_sec.empty() && !default_total_bytes_sec.empty())
464
        {
465
            total_bytes_sec = default_total_bytes_sec;
466
        }
467

    
468
        if ( read_bytes_sec.empty() && !default_read_bytes_sec.empty())
469
        {
470
            read_bytes_sec = default_read_bytes_sec;
471
        }
472

    
473
        if ( write_bytes_sec.empty() && !default_write_bytes_sec.empty())
474
        {
475
            write_bytes_sec = default_write_bytes_sec;
476
        }
477

    
478
        if ( total_iops_sec.empty() && !default_total_iops_sec.empty())
479
        {
480
            total_iops_sec = default_total_iops_sec;
481
        }
482

    
483
        if ( read_iops_sec.empty() && !default_read_iops_sec.empty())
484
        {
485
            read_iops_sec = default_read_iops_sec;
486
        }
487

    
488
        if ( write_iops_sec.empty() && !default_write_iops_sec.empty())
489
        {
490
            write_iops_sec = default_write_iops_sec;
491
        }
492

    
493
        disk[i]->vector_value_str("DISK_ID", disk_id);
494

    
495
        if (target.empty())
496
        {
497
            goto error_disk;
498
        }
499

    
500
        readonly = false;
501

    
502
        if ( !ro.empty() )
503
        {
504
            one_util::toupper(ro);
505

    
506
            if ( ro == "YES" )
507
            {
508
                readonly = true;
509
            }
510
        }
511

    
512
        // ---- Disk type and source for the image ----
513

    
514
        if ( type == "BLOCK" )
515
        {
516
            ostringstream dev;
517

    
518
            dev << vm->get_remote_system_dir() << "/disk." << disk_id;
519

    
520
            file << "\t\t<disk type='block' device='disk'>\n"
521
                 << "\t\t\t<source dev=" << one_util::escape_xml_attr(dev.str())
522
                 << "/>\n";
523
        }
524
        else if ( type == "ISCSI" )
525
        {
526
            file << "\t\t<disk type='network' device='disk'>" << endl;
527

    
528
            file << "\t\t\t<source protocol='iscsi' name=";
529

    
530
            if ( !iscsi_iqn.empty() )
531
            {
532
                file << one_util::escape_xml_attr(iscsi_iqn);
533
            }
534
            else
535
            {
536
                file << one_util::escape_xml_attr(source);
537
            }
538

    
539
            do_network_hosts(file, iscsi_host, "", ISCSI_DEFAULT_PORT);
540

    
541
            if ( !iscsi_user.empty() && !iscsi_usage.empty() )
542
            {
543
                file << "\t\t\t<auth username="
544
                     << one_util::escape_xml_attr(iscsi_user) << ">\n"
545
                     << "\t\t\t\t<secret type='iscsi' usage="
546
                     << one_util::escape_xml_attr(iscsi_usage)<< "/>\n"
547
                     << "\t\t\t</auth>\n";
548
            }
549
        }
550
        else if ( type == "RBD" || type == "RBD_CDROM" || disk_type == "RBD" )
551
        {
552
            if (type == "RBD" || disk_type == "RBD")
553
            {
554
                file << "\t\t<disk type='network' device='disk'>" << endl;
555
            }
556
            else
557
            {
558
                file << "\t\t<disk type='network' device='cdrom'>" << endl;
559
            }
560

    
561
            file << "\t\t\t<source protocol='rbd' name=";
562

    
563
            ostringstream rbd_name;
564

    
565
            if ( !source.empty() )
566
            {
567
                rbd_name << source;
568
            }
569
            else
570
            {
571
                if ( !pool_name.empty() )
572
                {
573
                    rbd_name << pool_name;
574
                }
575
                else
576
                {
577
                    rbd_name << "one";
578
                }
579

    
580
                rbd_name << "/one-sys";
581
            }
582

    
583
            if ( clone == "YES" || source.empty() )
584
            {
585
                rbd_name << "-" << vm->get_oid() << "-" << disk_id;
586
            }
587

    
588
            file << one_util::escape_xml_attr(rbd_name.str());
589

    
590
            do_network_hosts(file, ceph_host, "", CEPH_DEFAULT_PORT);
591

    
592
            if ( !ceph_secret.empty() && !ceph_user.empty())
593
            {
594
                file << "\t\t\t<auth username="
595
                     << one_util::escape_xml_attr(ceph_user) << ">\n"
596
                     << "\t\t\t\t<secret type='ceph' uuid="
597
                     << one_util::escape_xml_attr(ceph_secret) <<"/>\n"
598
                     << "\t\t\t</auth>\n";
599
            }
600
        }
601
        else if ( type == "SHEEPDOG" || type == "SHEEPDOG_CDROM" )
602
        {
603
            if (type == "SHEEPDOG")
604
            {
605
                file << "\t\t<disk type='network' device='disk'>" << endl;
606
            }
607
            else
608
            {
609
                file << "\t\t<disk type='network' device='cdrom'>" << endl;
610
            }
611

    
612
            file << "\t\t\t<source protocol='sheepdog' name=";
613

    
614
            ostringstream sheep_name;
615

    
616
            sheep_name << source;
617

    
618
            if ( clone == "YES" )
619
            {
620
                sheep_name << "-" << vm->get_oid() << "-" << disk_id;
621
            }
622

    
623
            file << one_util::escape_xml_attr(sheep_name.str());
624

    
625
            do_network_hosts(file, sheepdog_host, "tcp", -1);
626
        }
627
        else if ( type == "GLUSTER" || type == "GLUSTER_CDROM" )
628
        {
629
            if ( type == "GLUSTER" )
630
            {
631
                file << "\t\t<disk type='network' device='disk'>" << endl;
632
            }
633
            else
634
            {
635
                file << "\t\t<disk type='network' device='cdrom'>" << endl;
636
            }
637

    
638
            file << "\t\t\t<source protocol='gluster' name=";
639

    
640
            ostringstream gluster_name;
641

    
642
            gluster_name << gluster_volume << "/";
643

    
644
            if ( clone == "YES" )
645
            {
646
                gluster_name << vm->get_oid() << "/disk." << disk_id;
647
            }
648
            else
649
            {
650
                gluster_name << one_util::split(source, '/').back();
651
            }
652

    
653
            file << one_util::escape_xml_attr(gluster_name.str());
654

    
655
            do_network_hosts(file, gluster_host, "tcp", GLUSTER_DEFAULT_PORT);
656
        }
657
        else if ( type == "CDROM" )
658
        {
659
            ostringstream cd_name;
660

    
661
            cd_name << vm->get_remote_system_dir() << "/disk." << disk_id;
662

    
663
            file << "\t\t<disk type='file' device='cdrom'>\n"
664
                 << "\t\t\t<source file="
665
                 << one_util::escape_xml_attr(cd_name.str())<< "/>\n";
666
        }
667
        else
668
        {
669
            ostringstream fname;
670

    
671
            fname << vm->get_remote_system_dir() << "/disk." << disk_id;
672

    
673
            file << "\t\t<disk type='file' device='disk'>\n"
674
                 << "\t\t\t<source file="
675
                 << one_util::escape_xml_attr(fname.str()) << "/>\n";
676
        }
677

    
678
        // ---- target device to map the disk ----
679

    
680
        file << "\t\t\t<target dev=" << one_util::escape_xml_attr(target) << "/>\n";
681

    
682
        // ---- readonly attribute for the disk ----
683

    
684
        if (readonly)
685
        {
686
            file << "\t\t\t<readonly/>" << endl;
687
        }
688

    
689
        // ---- Image Format using qemu driver ----
690

    
691
        file << "\t\t\t<driver name='qemu' type=";
692

    
693
        if ( type == "CDROM" ) // Use driver raw for CD's
694
        {
695
            file << one_util::escape_xml_attr("raw");
696
        }
697
        else if ( !driver.empty() )
698
        {
699
            file << one_util::escape_xml_attr(driver);
700
        }
701
        else
702
        {
703
            file << one_util::escape_xml_attr(default_driver);
704
        }
705

    
706
        file << " cache=";
707

    
708
        if ( !cache.empty() )
709
        {
710
            file << one_util::escape_xml_attr(cache);
711
        }
712
        else
713
        {
714
            file << one_util::escape_xml_attr(default_driver_cache);
715
        }
716

    
717
        if ( !disk_io.empty() )
718
        {
719
            file << " io=" << one_util::escape_xml_attr(disk_io);
720
        }
721
        else if ( !default_driver_disk_io.empty() )
722
        {
723
            file << " io=" << one_util::escape_xml_attr(default_driver_disk_io);
724
        }
725

    
726
        if ( !discard.empty() )
727
        {
728
            file << " discard=" << one_util::escape_xml_attr(discard);
729
        }
730
        else if ( !default_driver_discard.empty() )
731
        {
732
            file << " discard=" << one_util::escape_xml_attr(default_driver_discard);
733
        }
734

    
735
        file << "/>" << endl;
736

    
737
        // ---- I/O Options  ----
738

    
739
        if (!(total_bytes_sec.empty() && read_bytes_sec.empty() &&
740
              write_bytes_sec.empty() && total_iops_sec.empty() &&
741
              read_iops_sec.empty() && write_iops_sec.empty()))
742
        {
743
            file << "\t\t\t<iotune>" << endl;
744

    
745
            if ( !total_bytes_sec.empty() )
746
            {
747
                file << "\t\t\t\t<total_bytes_sec>"
748
                     << one_util::escape_xml(total_bytes_sec)
749
                     << "</total_bytes_sec>\n";
750
            }
751

    
752
            if ( !read_bytes_sec.empty() )
753
            {
754
                file << "\t\t\t\t<read_bytes_sec>"
755
                     << one_util::escape_xml(read_bytes_sec)
756
                     << "</read_bytes_sec>\n";
757
            }
758

    
759
            if ( !write_bytes_sec.empty() )
760
            {
761
                file << "\t\t\t\t<write_bytes_sec>"
762
                     << one_util::escape_xml(write_bytes_sec)
763
                     << "</write_bytes_sec>\n";
764
            }
765

    
766
            if ( !total_iops_sec.empty() )
767
            {
768
                file << "\t\t\t\t<total_iops_sec>"
769
                     << one_util::escape_xml(total_iops_sec)
770
                     << "</total_iops_sec>\n";
771
            }
772

    
773
            if ( !read_iops_sec.empty() )
774
            {
775
                file << "\t\t\t\t<read_iops_sec>"
776
                     << one_util::escape_xml(read_iops_sec)
777
                     << "</read_iops_sec>\n";
778
            }
779

    
780
            if ( !write_iops_sec.empty() )
781
            {
782
                file << "\t\t\t\t<write_iops_sec>"
783
                     << one_util::escape_xml(write_iops_sec)
784
                     << "</write_iops_sec>\n";
785
            }
786

    
787
            file << "\t\t\t</iotune>" << endl;
788
        }
789

    
790
        file << "\t\t</disk>" << endl;
791
    }
792

    
793
    // ------------------------------------------------------------------------
794
    // Context Device
795
    // ------------------------------------------------------------------------
796
    context = vm->get_template_attribute("CONTEXT");
797

    
798
    if ( context != 0 )
799
    {
800
        target  = context->vector_value("TARGET");
801
        driver  = context->vector_value("DRIVER");
802

    
803
        context->vector_value_str("DISK_ID", disk_id);
804

    
805
        if ( !target.empty() )
806
        {
807
            ostringstream fname;
808

    
809
            fname << vm->get_remote_system_dir() << "/disk." << disk_id;
810

    
811
            file << "\t\t<disk type='file' device='cdrom'>\n"
812
                 << "\t\t\t<source file="
813
                     << one_util::escape_xml_attr(fname.str())  << "/>\n"
814
                 << "\t\t\t<target dev="
815
                     << one_util::escape_xml_attr(target) << "/>\n"
816
                 << "\t\t\t<readonly/>\n"
817
                 << "\t\t\t<driver name='qemu' type='raw'/>\n"
818
                 << "\t\t</disk>\n";
819
        }
820
        else
821
        {
822
            vm->log("VMM", Log::WARNING, "Could not find target device to"
823
                " attach context, will continue without it.");
824
        }
825
    }
826

    
827
    // ------------------------------------------------------------------------
828
    // Network interfaces
829
    // ------------------------------------------------------------------------
830
    get_default("NIC", "FILTER", default_filter);
831

    
832
    get_default("NIC", "MODEL", default_model);
833

    
834
    num = vm->get_template_attribute("NIC", nic);
835

    
836
    for(int i=0; i<num; i++)
837
    {
838
        bridge     = nic[i]->vector_value("BRIDGE");
839
        bridge_ovs = nic[i]->vector_value("BRIDGE_OVS");
840
        mac        = nic[i]->vector_value("MAC");
841
        target     = nic[i]->vector_value("TARGET");
842
        script     = nic[i]->vector_value("SCRIPT");
843
        model      = nic[i]->vector_value("MODEL");
844
        ip         = nic[i]->vector_value("IP");
845
        filter     = nic[i]->vector_value("FILTER");
846

    
847
        if ( bridge.empty() )
848
        {
849
            file << "\t\t<interface type='ethernet'>" << endl;
850
        }
851
        else
852
        {
853
            file << "\t\t<interface type='bridge'>" << endl;
854

    
855
            string * the_bridge = &bridge;
856

    
857
            if ( vm->get_vnm_mad() == "ovswitch" )
858
            {
859

    
860
                if ( !bridge_ovs.empty() )
861
                {
862
                    the_bridge = &bridge_ovs;
863
                }
864

    
865
                file << "\t\t\t<virtualport type='openvswitch'/>" << endl;
866
            }
867

    
868
            file << "\t\t\t<source bridge="
869
                 << one_util::escape_xml_attr(*the_bridge) << "/>\n";
870
        }
871

    
872
        if( !mac.empty() )
873
        {
874
            file << "\t\t\t<mac address=" << one_util::escape_xml_attr(mac)
875
                 << "/>\n";
876
        }
877

    
878
        if( !target.empty() )
879
        {
880
            file << "\t\t\t<target dev=" << one_util::escape_xml_attr(target)
881
                 << "/>\n";
882
        }
883

    
884
        if( !script.empty() )
885
        {
886
            file << "\t\t\t<script path=" << one_util::escape_xml_attr(script)
887
                 << "/>\n";
888
        }
889

    
890
        string * the_model = 0;
891

    
892
        if (!model.empty())
893
        {
894
            the_model = &model;
895
        }
896
        else if (!default_model.empty())
897
        {
898
            the_model = &default_model;
899
        }
900

    
901
        if (the_model != 0)
902
        {
903
            file << "\t\t\t<model type="
904
                 << one_util::escape_xml_attr(*the_model) << "/>\n";
905
        }
906

    
907
        if (!ip.empty() )
908
        {
909
            string * the_filter = 0;
910

    
911
            if (!filter.empty())
912
            {
913
                the_filter = &filter;
914
            }
915
            else if (!default_filter.empty())
916
            {
917
                the_filter = &default_filter;
918
            }
919

    
920
            if ( the_filter != 0 )
921
            {
922
                file << "\t\t\t<filterref filter="
923
                         << one_util::escape_xml_attr(*the_filter) << ">\n"
924
                     << "\t\t\t\t<parameter name='IP' value="
925
                         << one_util::escape_xml_attr(ip) << "/>\n"
926
                     << "\t\t\t</filterref>\n";
927
            }
928
        }
929

    
930
        file << "\t\t</interface>" << endl;
931
    }
932

    
933
    // ------------------------------------------------------------------------
934
    // Graphics
935
    // ------------------------------------------------------------------------
936
    graphics = vm->get_template_attribute("GRAPHICS");
937

    
938
    if ( graphics != 0 )
939
    {
940
        type   = graphics->vector_value("TYPE");
941
        listen = graphics->vector_value("LISTEN");
942
        port   = graphics->vector_value("PORT");
943
        passwd = graphics->vector_value("PASSWD");
944
        keymap = graphics->vector_value("KEYMAP");
945

    
946
        one_util::tolower(type);
947

    
948
        if ( type == "vnc" || type == "spice" )
949
        {
950
            file << "\t\t<graphics type=" << one_util::escape_xml_attr(type);
951

    
952
            if ( !listen.empty() )
953
            {
954
                file << " listen=" << one_util::escape_xml_attr(listen);
955
            }
956

    
957
            if ( !port.empty() )
958
            {
959
                file << " port=" << one_util::escape_xml_attr(port);
960
            }
961

    
962
            if ( !passwd.empty() )
963
            {
964
                file << " passwd=" << one_util::escape_xml_attr(passwd);
965
            }
966

    
967
            if ( !keymap.empty() )
968
            {
969
                file << " keymap=" << one_util::escape_xml_attr(keymap);
970
            }
971

    
972
            file << "/>" << endl;
973

    
974
            if ( type == "spice" )
975
            {
976
                get_default("SPICE_OPTIONS", spice_options);
977

    
978
                if (spice_options.empty())
979
                {
980
                    file << "\t\t" << spice_options << endl;
981
                }
982
            }
983
        }
984
        else
985
        {
986
            vm->log("VMM", Log::WARNING,
987
                    "Graphics not supported or undefined, ignored.");
988
        }
989
    }
990

    
991
    // ------------------------------------------------------------------------
992
    // Input
993
    // ------------------------------------------------------------------------
994
    input = vm->get_template_attribute("INPUT");
995

    
996
    if ( input != 0 )
997
    {
998
        type = input->vector_value("TYPE");
999
        bus  = input->vector_value("BUS");
1000

    
1001
        if ( !type.empty() )
1002
        {
1003
            file << "\t\t<input type=" << one_util::escape_xml_attr(type);
1004

    
1005
            if ( !bus.empty() )
1006
            {
1007
                file << " bus=" << one_util::escape_xml_attr(bus);
1008
            }
1009

    
1010
            file << "/>" << endl;
1011
        }
1012
    }
1013

    
1014
    // ------------------------------------------------------------------------
1015
    // PCI Passthrough
1016
    // ------------------------------------------------------------------------
1017

    
1018
    num = vm->get_template_attribute("PCI", pci);
1019

    
1020
    for (int i=0; i < num ;i++)
1021
    {
1022
        domain  = pci[i]->vector_value("DOMAIN");
1023
        bus     = pci[i]->vector_value("BUS");
1024
        slot    = pci[i]->vector_value("SLOT");
1025
        func    = pci[i]->vector_value("FUNCTION");
1026

    
1027
        if ( domain.empty() || bus.empty() || slot.empty() || func.empty() )
1028
        {
1029
            vm->log("VMM", Log::WARNING,
1030
                    "DOMAIN, BUS, SLOT and FUNC must be defined for PCI "
1031
                    "passthrough. Ignored.");
1032
            continue;
1033
        }
1034

    
1035
        file << "\t\t<hostdev mode='subsystem' type='pci' managed='yes'>\n";
1036

    
1037
        file << "\t\t\t<source>\n";
1038
        file << "\t\t\t\t<address "
1039
                 << "domain="   << one_util::escape_xml_attr("0x" + domain)
1040
                 << "bus="      << one_util::escape_xml_attr("0x" + bus)
1041
                 << "slot="     << one_util::escape_xml_attr("0x" + slot)
1042
                 << "function=" << one_util::escape_xml_attr("0x" + func)
1043
             << "/>\n";
1044
        file << "\t\t\t</source>\n";
1045

    
1046
        file << "\t\t</hostdev>" << endl;
1047
    }
1048

    
1049
    file << "\t</devices>" << endl;
1050

    
1051
    // ------------------------------------------------------------------------
1052
    // Features
1053
    // ------------------------------------------------------------------------
1054
    features = vm->get_template_attribute("FEATURES");
1055

    
1056
    if ( features != 0 )
1057
    {
1058
        pae_found       = features->vector_value("PAE", pae);
1059
        acpi_found      = features->vector_value("ACPI", acpi);
1060
        apic_found      = features->vector_value("APIC", apic);
1061
        hyperv_found    = features->vector_value("HYPERV", hyperv);
1062
        localtime_found = features->vector_value("LOCALTIME", localtime);
1063
    }
1064

    
1065
    if ( pae_found != 0 )
1066
    {
1067
        get_default("FEATURES", "PAE", pae);
1068
    }
1069

    
1070
    if ( acpi_found != 0 )
1071
    {
1072
        get_default("FEATURES", "ACPI", acpi);
1073
    }
1074

    
1075
    if ( apic_found != 0 )
1076
    {
1077
        get_default("FEATURES", "APIC", apic);
1078
    }
1079

    
1080
    if ( hyperv_found != 0 )
1081
    {
1082
        get_default("FEATURES", "HYPERV", hyperv);
1083
    }
1084

    
1085
    if ( localtime_found != 0 )
1086
    {
1087
        get_default("FEATURES", "LOCALTIME", localtime);
1088
    }
1089

    
1090
    if ( acpi || pae || apic || hyperv )
1091
    {
1092
        file << "\t<features>" << endl;
1093

    
1094
        if ( pae )
1095
        {
1096
            file << "\t\t<pae/>" << endl;
1097
        }
1098

    
1099
        if ( acpi )
1100
        {
1101
            file << "\t\t<acpi/>" << endl;
1102
        }
1103

    
1104
        if ( apic )
1105
        {
1106
            file << "\t\t<apic/>" << endl;
1107
        }
1108

    
1109
        if ( hyperv )
1110
        {
1111
            get_default("HYPERV_OPTIONS", hyperv_options);
1112

    
1113
            file << "\t\t<hyperv>" << endl;
1114
            file << hyperv_options << endl;
1115
            file << "\t\t</hyperv>" << endl;
1116
        }
1117

    
1118
        file << "\t</features>" << endl;
1119
    }
1120

    
1121
    if ( localtime )
1122
    {
1123
        file << "\t<clock offset='localtime'/>" << endl;
1124
    }
1125

    
1126
    // ------------------------------------------------------------------------
1127
    // Raw KVM attributes
1128
    // ------------------------------------------------------------------------
1129
    num = vm->get_template_attribute("RAW", raw);
1130

    
1131
    for(int i=0; i<num;i++)
1132
    {
1133
        type = raw[i]->vector_value("TYPE");
1134

    
1135
        one_util::toupper(type);
1136

    
1137
        if ( type == "KVM" )
1138
        {
1139
            data = raw[i]->vector_value("DATA");
1140
            file << "\t" << data << endl;
1141
        }
1142
    }
1143

    
1144
    get_default("RAW", default_raw);
1145

    
1146
    if ( !default_raw.empty() )
1147
    {
1148
        file << "\t" << default_raw << endl;
1149
    }
1150

    
1151
    // ------------------------------------------------------------------------
1152
    // Metadata used by drivers
1153
    // ------------------------------------------------------------------------
1154
    vm64 = one_util::base64_encode(vm->to_xml(vm_xml));
1155

    
1156
    file << "\t<metadata>\n"
1157
         << "\t\t<system_datastore>"
1158
         << one_util::escape_xml(vm->get_remote_system_dir())
1159
         << "</system_datastore>\n";
1160

    
1161
    if ( vm64 != 0 )
1162
    {
1163
        file << "\t\t<vm_xml64>"<< one_util::escape_xml(*vm64)<< "</vm_xml64>\n";
1164
        delete vm64;
1165
    }
1166

    
1167
    file << "\t</metadata>\n";
1168

    
1169
    file << "</domain>" << endl;
1170

    
1171
    file.close();
1172

    
1173
    return 0;
1174

    
1175
error_file:
1176
    vm->log("VMM", Log::ERROR, "Could not open KVM deployment file.");
1177
    return -1;
1178

    
1179
error_memory:
1180
    vm->log("VMM", Log::ERROR, "No MEMORY defined and no default provided.");
1181
    file.close();
1182
    return -1;
1183

    
1184
error_arch:
1185
    vm->log("VMM", Log::ERROR, "No ARCH defined and no default provided.");
1186
    file.close();
1187
    return -1;
1188

    
1189
error_boot:
1190
    vm->log("VMM", Log::ERROR, "No BOOT device defined and no default provided.");
1191
    file.close();
1192
    return -1;
1193

    
1194
error_disk:
1195
    vm->log("VMM", Log::ERROR, "Wrong target value in DISK.");
1196
    file.close();
1197
    return -1;
1198
}