Project

General

Profile

root / branches / 1.1 / src / haizea / core / configfile.py @ 848

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

    
19
from haizea.common.config import ConfigException, Section, Option, Config, OPTTYPE_INT, OPTTYPE_FLOAT, OPTTYPE_STRING, OPTTYPE_BOOLEAN, OPTTYPE_DATETIME, OPTTYPE_TIMEDELTA 
20
from haizea.common.utils import generate_config_name
21
import haizea.common.constants as constants
22
import haizea.common.defaults as defaults
23
import sys
24
from mx.DateTime import TimeDelta
25
import ConfigParser
26

    
27
try:
28
    import xml.etree.ElementTree as ET
29
except ImportError:
30
    # Compatibility with Python <=2.4
31
    import elementtree.ElementTree as ET 
32

    
33
class HaizeaConfig(Config):
34

    
35
    sections = []
36
    
37
    # ============================= #
38
    #                               #
39
    #        GENERAL OPTIONS        #
40
    #                               #
41
    # ============================= #
42

    
43
    general = Section("general", required=True,
44
                      doc = "This section is used for general options affecting Haizea as a whole.")
45
    general.options = \
46
    [
47
     Option(name        = "loglevel",
48
            getter      = "loglevel",
49
            type        = OPTTYPE_STRING,
50
            required    = False,
51
            default     = "INFO",
52
            valid       = ["STATUS","INFO","DEBUG","VDEBUG"],
53
            doc         = """
54
            Controls the level (and amount) of 
55
            log messages. Valid values are:
56
            
57
             - STATUS: Only print status messages
58
             - INFO: Slightly more verbose that STATUS
59
             - DEBUG: Prints information useful for debugging the scheduler.
60
             - VDEBUG: Prints very verbose information
61
               on the scheduler's internal data structures. Use only
62
               for short runs.        
63
            """),
64

    
65
     Option(name        = "logfile",
66
            getter      = "logfile",
67
            type        = OPTTYPE_STRING,
68
            required    = False,
69
            default     = "/var/tmp/haizea.log",
70
            doc         = """
71
            When running Haizea as a daemon, this option specifies the file
72
            that log messages should be written to.        
73
            """),
74
     
75
     Option(name        = "mode",
76
            getter      = "mode",
77
            type        = OPTTYPE_STRING,
78
            required    = True,
79
            valid       = ["simulated","opennebula"],
80
            doc         = """
81
            Sets the mode the scheduler will run in.
82
            Currently the only valid values are "simulated" and
83
            "opennebula". The "simulated" mode expects lease
84
            requests to be provided through a trace file, and
85
            all enactment is simulated. The "opennebula" mode
86
            interacts with the OpenNebula virtual infrastructure
87
            manager (http://www.opennebula.org/) to obtain lease
88
            requests and to do enactment on physical resources.                
89
            """),
90

    
91
     Option(name        = "lease-preparation",
92
            getter      = "lease-preparation",
93
            type        = OPTTYPE_STRING,
94
            required    = False,
95
            default     = constants.PREPARATION_UNMANAGED,
96
            valid       = [constants.PREPARATION_UNMANAGED,
97
                           constants.PREPARATION_TRANSFER],
98
            doc         = """
99
            Sets how the scheduler will handle the
100
            preparation overhead of leases. Valid values are:
101
            
102
             - unmanaged: The scheduler can assume that there
103
               is no deployment overhead, or that some
104
               other entity is taking care of it (e.g., one
105
               of the enactment backends)
106
             - imagetransfer: A disk image has to be transferred
107
               from a repository node before the lease can start.
108
            """),
109

    
110
     Option(name        = "lease-failure-handling",
111
            getter      = "lease-failure-handling",
112
            type        = OPTTYPE_STRING,
113
            required    = False,
114
            default     = constants.ONFAILURE_CANCEL,
115
            valid       = [constants.ONFAILURE_CANCEL,
116
                           constants.ONFAILURE_EXIT,
117
                           constants.ONFAILURE_EXIT_RAISE],
118
            doc         = """
119
            Sets how the scheduler will handle a failure in
120
            a lease. Valid values are:
121
            
122
             - cancel: The lease is cancelled and marked as "FAILED"
123
             - exit: Haizea will exit cleanly, printing relevant debugging
124
               information to its log.
125
             - exit-raise: Haizea will exit by raising an exception. This is
126
               useful for debugging, as IDEs will recognize this as an exception
127
               and will facilitate debugging it.
128
            """),
129

    
130
     Option(name        = "persistence-file",
131
            getter      = "persistence-file",
132
            type        = OPTTYPE_STRING,
133
            required    = False,
134
            default     = defaults.PERSISTENCE_LOCATION,
135
            doc         = """
136
            This is the file where lease information, along with some
137
            additional scheduling information, is persisted to. If set
138
            to "none", no information will be persisted to disk, and
139
            Haizea will run entirely in-memory (this is advisable
140
            when running in simulation, as persisting to disk adds
141
            considerable overhead, compared to running in-memory).
142
            """)
143

    
144
    ]
145

    
146
    sections.append(general)
147

    
148
    # ============================= #
149
    #                               #
150
    #      SCHEDULING OPTIONS       #
151
    #                               #
152
    # ============================= #
153

    
154
    scheduling = Section("scheduling", required=True,
155
                         doc = "The options in this section control how Haizea schedules leases.")
156
    scheduling.options = \
157
    [
158
     Option(name        = "mapper",
159
            getter      = "mapper",
160
            type        = OPTTYPE_STRING,
161
            required    = False,
162
            default     = "greedy",
163
            doc         = """
164
            VM-to-physical node mapping algorithm used by Haizea. There is currently
165
            only one mapper available (the greedy mapper).
166
            """),
167

    
168
     Option(name        = "policy-admission",
169
            getter      = "policy.admission",
170
            type        = OPTTYPE_STRING,
171
            required    = False,
172
            default     = "accept-all",
173
            doc         = """
174
            Lease admission policy. This controls what leases are accepted by Haizea. 
175
            Take into account that this decision takes place before Haizea even 
176
            attempts to schedule the lease (so, you can think of lease admission as 
177
            "eligibility to be scheduled"). 
178
            
179
            There are two built-in policies:
180
            
181
             - accept-all: Accept all leases.
182
             - no-ARs: Accept all leases except advance reservations.
183
             
184
            See the Haizea documentation for details on how to write your own
185
            policies.
186
            """),
187

    
188
     Option(name        = "policy-preemption",
189
            getter      = "policy.preemption",
190
            type        = OPTTYPE_STRING,
191
            required    = False,
192
            default     = "no-preemption",
193
            doc         = """
194
            Lease preemption policy. Determines what leases can be preempted. There
195
            are two built-in policies:
196
            
197
             - no-preemption: Do not allow any preemptions
198
             - ar-preempts-everything: Allow all ARs to preempt other leases.
199
            
200
            See the Haizea documentation for details on how to write your own
201
            policies.
202
            """),
203
            
204
     Option(name        = "policy-host-selection",
205
            getter      = "policy.host-selection",
206
            type        = OPTTYPE_STRING,
207
            required    = False,
208
            default     = "greedy",
209
            doc         = """
210
            Physical host selection policy. controls how Haizea chooses what physical hosts 
211
            to map VMs to. This option is closely related to the mapper options 
212
            (if the greedy mapper is used, then the greedy host selection policy
213
            should be used, or unexpected results will happen). 
214
            
215
            The two built-in policies are:
216
             - no-policy: Choose nodes arbitrarily
217
             - greedy: Apply a greedy policy that tries to minimize the number
218
               of preemptions.
219
            
220
            See the Haizea documentation for details on how to write your own
221
            policies.
222
            """),
223

    
224
     Option(name        = "policy-pricing",
225
            getter      = "policy.pricing",
226
            type        = OPTTYPE_STRING,
227
            required    = False,
228
            default     = "free",
229
            doc         = """
230
            ...
231
            
232
            See the Haizea documentation for details on how to write your own
233
            policies.
234
            """),
235
                        
236
     Option(name        = "wakeup-interval",
237
            getter      = "wakeup-interval",
238
            type        = OPTTYPE_TIMEDELTA,
239
            required    = False,
240
            default     = TimeDelta(seconds=60),
241
            doc         = """
242
            Interval at which Haizea will wake up
243
            to manage resources and process pending requests.
244
            This option is not used when using a simulated clock,
245
            since the clock will skip directly to the time where an
246
            event is happening.
247
            """),
248

    
249
     Option(name        = "backfilling",
250
            getter      = "backfilling",
251
            type        = OPTTYPE_STRING,
252
            required    = False,
253
            default     = None,
254
            valid       = [constants.BACKFILLING_OFF,
255
                           constants.BACKFILLING_AGGRESSIVE,
256
                           constants.BACKFILLING_CONSERVATIVE,
257
                           constants.BACKFILLING_INTERMEDIATE],
258
            doc         = """
259
            Backfilling algorithm to use. Valid values are:
260
            
261
             - off: don't do backfilling
262
             - aggressive: at most 1 reservation in the future
263
             - conservative: unlimited reservations in the future
264
             - intermediate: N reservations in the future (N is specified
265
               in the backfilling-reservations option)
266
            """),
267

    
268
     Option(name        = "backfilling-reservations",
269
            getter      = "backfilling-reservations",
270
            type        = OPTTYPE_INT,
271
            required    = False,
272
            required_if = [(("scheduling","backfilling"),constants.BACKFILLING_INTERMEDIATE)],
273
            doc         = """
274
            Number of future reservations to allow when
275
            using the "intermediate" backfilling option.
276
            """),
277

    
278
     Option(name        = "suspension",
279
            getter      = "suspension",
280
            type        = OPTTYPE_STRING,
281
            required    = True,
282
            valid       = [constants.SUSPENSION_NONE,
283
                           constants.SUSPENSION_SERIAL,
284
                           constants.SUSPENSION_ALL],
285
            doc         = """
286
            Specifies what can be suspended. Valid values are:
287
            
288
             - none: suspension is never allowed
289
             - serial-only: only 1-node leases can be suspended
290
             - all: any lease can be suspended                
291
            """),
292

    
293
     Option(name        = "suspend-rate",
294
            getter      = "suspend-rate",
295
            type        = OPTTYPE_FLOAT,
296
            required    = False,
297
            required_if = [(("scheduling","suspension"),constants.SUSPENSION_SERIAL),
298
                           (("scheduling","suspension"),constants.SUSPENSION_ALL)],
299
            doc         = """
300
            Rate at which VMs are assumed to suspend (in MB of
301
            memory per second)                
302
            """),
303

    
304
     Option(name        = "resume-rate",
305
            getter      = "resume-rate",
306
            type        = OPTTYPE_FLOAT,
307
            required    = False,
308
            required_if = [(("scheduling","suspension"),constants.SUSPENSION_SERIAL),
309
                           (("scheduling","suspension"),constants.SUSPENSION_ALL)],
310
            doc         = """
311
            Rate at which VMs are assumed to resume (in MB of
312
            memory per second)                
313
            """),
314

    
315
     Option(name        = "suspendresume-exclusion",
316
            getter      = "suspendresume-exclusion",
317
            type        = OPTTYPE_STRING,
318
            required    = False,
319
            default     = constants.SUSPRES_EXCLUSION_LOCAL,
320
            valid       = [constants.SUSPRES_EXCLUSION_LOCAL,
321
                           constants.SUSPRES_EXCLUSION_GLOBAL],
322
            doc         = """
323
            When suspending or resuming a VM, the VM's memory is dumped to a
324
            file on disk. To correctly estimate the time required to suspend
325
            a lease with multiple VMs, Haizea makes sure that no two 
326
            suspensions/resumptions happen at the same time (e.g., if eight
327
            memory files were being saved at the same time to disk, the disk's
328
            performance would be reduced in a way that is not as easy to estimate
329
            as if only one file were being saved at a time).
330
            
331
            Depending on whether the files are being saved to/read from a global
332
            or local filesystem, this exclusion can be either global or local.                        
333
            """),
334

    
335
     Option(name        = "scheduling-threshold-factor",
336
            getter      = "scheduling-threshold-factor",
337
            type        = OPTTYPE_INT,
338
            required    = False,
339
            default     = 1,
340
            doc         = """
341
            To avoid thrashing, Haizea will not schedule a lease unless all overheads
342
            can be correctly scheduled (which includes image transfers, suspensions, etc.).
343
            However, this can still result in situations where a lease is prepared,
344
            and then immediately suspended because of a blocking lease in the future.
345
            The scheduling threshold factor can be used to specify that a lease must
346
            not be scheduled unless it is guaranteed to run for a minimum amount of
347
            time (the rationale behind this is that you ideally don't want leases
348
            to be scheduled if they're not going to be active for at least as much time
349
            as was spent in overheads).
350
            
351
            The default value is 1, meaning that the lease will be active for at least
352
            as much time T as was spent on overheads (e.g., if preparing the lease requires
353
            60 seconds, and we know that it will have to be suspended, requiring 30 seconds,
354
            Haizea won't schedule the lease unless it can run for at least 90 minutes).
355
            In other words, a scheduling factor of F required a minimum duration of 
356
            F*T. A value of 0 could lead to thrashing, since Haizea could end up with
357
            situations where a lease starts and immediately gets suspended.               
358
            """),
359

    
360
     Option(name        = "override-suspend-time",
361
            getter      = "override-suspend-time",
362
            type        = OPTTYPE_INT,
363
            required    = False,
364
            default     = None,
365
            doc         = """
366
            Overrides the time it takes to suspend a VM to a fixed value
367
            (i.e., not computed based on amount of memory, enactment overhead, etc.)
368
            """),
369

    
370
     Option(name        = "override-resume-time",
371
            getter      = "override-resume-time",
372
            type        = OPTTYPE_INT,
373
            required    = False,
374
            default     = None,
375
            doc         = """
376
            Overrides the time it takes to suspend a VM to a fixed value
377
            (i.e., not computed based on amount of memory, enactment overhead, etc.)
378
            """),
379

    
380
     Option(name        = "force-scheduling-threshold",
381
            getter      = "force-scheduling-threshold",
382
            type        = OPTTYPE_TIMEDELTA,
383
            required    = False,
384
            doc         = """
385
            This option can be used to force a specific scheduling threshold time
386
            to be used, instead of calculating one based on overheads.                
387
            """),
388

    
389
     Option(name        = "migration",
390
            getter      = "migration",
391
            type        = OPTTYPE_STRING,
392
            required    = False,
393
            default     = constants.MIGRATE_NO,          
394
            valid       = [constants.MIGRATE_NO,
395
                           constants.MIGRATE_YES,
396
                           constants.MIGRATE_YES_NOTRANSFER],              
397
            doc         = """
398
            Specifies whether leases can be migrated from one
399
            physical node to another. Valid values are: 
400
            
401
             - no
402
             - yes
403
             - yes-notransfer: migration is performed without
404
               transferring any files. 
405
            """),
406

    
407
     Option(name        = "non-schedulable-interval",
408
            getter      = "non-schedulable-interval",
409
            type        = OPTTYPE_TIMEDELTA,
410
            required    = False,
411
            default     = TimeDelta(seconds=10),
412
            doc         = """
413
            The minimum amount of time that must pass between
414
            when a request is scheduled to when it can actually start.
415
            The default should be good for most configurations, but
416
            may need to be increased if you're dealing with exceptionally
417
            high loads.                
418
            """),
419

    
420
     Option(name        = "shutdown-time",
421
            getter      = "shutdown-time",
422
            type        = OPTTYPE_TIMEDELTA,
423
            required    = False,
424
            default     = TimeDelta(seconds=0),
425
            doc         = """
426
            The amount of time that will be allocated for a VM to shutdown.
427
            When running in OpenNebula mode, it is advisable to set this to
428
            a few seconds, so no operation gets scheduled right when a
429
            VM is shutting down. The most common scenario is that a VM
430
            will start resuming right when another VM shuts down. However,
431
            since both these activities involve I/O, it can delay the resume
432
            operation and affect Haizea's estimation of how long the resume
433
            will take.
434
            """),
435

    
436
     Option(name        = "enactment-overhead",
437
            getter      = "enactment-overhead",
438
            type        = OPTTYPE_TIMEDELTA,
439
            required    = False,
440
            default     = TimeDelta(seconds=0),
441
            doc         = """
442
            The amount of time that is required to send
443
            an enactment command. This value will affect suspend/resume
444
            estimations and, in OpenNebula mode, will force a pause
445
            of this much time between suspend/resume enactment
446
            commands. When suspending/resuming many VMs at the same time
447
            (which is likely to happen if suspendresume-exclusion is set
448
            to "local"), it will take OpenNebula 1-2 seconds to process
449
            each command (this is a small amount of time, but if 32 VMs
450
            are being suspended at the same time, on in each physical node,
451
            this time can compound up to 32-64 seconds, which has to be
452
            taken into account when estimating when to start a suspend
453
            operation that must be completed before another lease starts).
454
            """)
455

    
456
    ]
457
    sections.append(scheduling)
458
    
459
    # ============================= #
460
    #                               #
461
    #      SIMULATION OPTIONS       #
462
    #                               #
463
    # ============================= #
464
    
465
    simulation = Section("simulation", required=False,
466
                         required_if = [(("general","mode"),"simulated")],
467
                         doc = "This section is used to specify options when Haizea runs in simulation" )
468
    simulation.options = \
469
    [
470
     Option(name        = "clock",
471
            getter      = "clock",
472
            type        = OPTTYPE_STRING,
473
            required    = False,
474
            default     = constants.CLOCK_REAL,
475
            valid       = [constants.CLOCK_REAL,
476
                           constants.CLOCK_SIMULATED],
477
            doc         = """
478
            Type of clock to use in simulation:
479
            
480
             - simulated: A simulated clock that fastforwards through
481
               time. Can only use the tracefile request
482
               frontend
483
             - real: A real clock is used, but simulated resources and
484
               enactment actions are used. Can only use the RPC
485
               request frontend.                
486
            """),
487

    
488
     Option(name        = "starttime",
489
            getter      = "starttime",
490
            type        = OPTTYPE_DATETIME,
491
            required    = False,
492
            required_if = [(("simulation","clock"),constants.CLOCK_SIMULATED)],
493
            doc         = """
494
            Time at which simulated clock will start.                
495
            """),             
496

    
497
     Option(name        = "resources",
498
            getter      = "simul.resources",
499
            type        = OPTTYPE_STRING,
500
            required    = True,
501
            doc         = """
502
            Simulated resources. This option can take two values,
503
            "in-tracefile" (which means that the description of
504
            the simulated site is in the tracefile) or a string 
505
            specifying a site with homogeneous resources. 
506
            The format is:
507
        
508
            <numnodes> [ <resource_type>:<resource_quantity>]+
509
        
510
            For example, "4  CPU:100 Memory:1024" describes a site
511
            with four nodes, each with one CPU and 1024 MB of memory.
512
            """),
513

    
514
     Option(name        = "imagetransfer-bandwidth",
515
            getter      = "imagetransfer-bandwidth",
516
            type        = OPTTYPE_INT,
517
            required    = True,
518
            doc         = """
519
            Bandwidth (in Mbps) available for image transfers.
520
            This would correspond to the outbound network bandwidth of the
521
            node where the images are stored.                
522
            """),
523

    
524
     Option(name        = "stop-when",
525
            getter      = "stop-when",
526
            type        = OPTTYPE_STRING,
527
            required    = False,
528
            default     = constants.STOPWHEN_ALLDONE,
529
            valid       = [constants.STOPWHEN_ALLDONE,
530
                           constants.STOPWHEN_BESUBMITTED,
531
                           constants.STOPWHEN_BEDONE,
532
                           constants.STOPWHEN_EXACT],
533
            doc         = """
534
            When using the simulated clock, this specifies when the
535
            simulation must end. Valid options are:
536
            
537
             - all-leases-done: All requested leases have been completed
538
               and there are no queued/pending requests.
539
             - besteffort-submitted: When all best-effort leases have been
540
               submitted.
541
             - besteffort-done: When all best-effort leases have been
542
               completed.    
543
             - exact: Stop at a specific time (use option stop-when-time)            
544
            """),
545

    
546
     Option(name        = "stop-when-time",
547
            getter      = "stop-when-time",
548
            type        = OPTTYPE_STRING,
549
            required    = False,
550
            required_if = [(("simulation","stop-when"),constants.STOPWHEN_EXACT)],
551
            doc         = """
552
            A time in format DD:HH:MM:SS at which the simulation should be
553
            stopped (useful for debugging)            
554
            """),
555

    
556
     Option(name        = "status-message-interval",
557
            getter      = "status-message-interval",
558
            type        = OPTTYPE_INT,
559
            required    = False,
560
            default     = None,
561
            doc         = """
562
            If specified, the simulated clock will print a status
563
            message with some basic statistics. This is useful to keep track
564
            of long simulations. The interval is specified in minutes.                
565
            """),
566

    
567
     Option(name        = "sanity-check",
568
            getter      = "sanity-check",
569
            type        = OPTTYPE_BOOLEAN,
570
            required    = False,
571
            default     = False,
572
            doc         = """
573
            Perform a sanity check at every timestep (only for debugging)
574
            """),     
575

    
576
    ]
577
    sections.append(simulation)
578
    
579

    
580
    # ============================= #
581
    #                               #
582
    #      ACCOUNTING OPTIONS       #
583
    #                               #
584
    # ============================= #
585

    
586
    accounting = Section("accounting", required=True,
587
                      doc = "Haizea can collect information while running, and save that information to a file for off-line processing. This section includes options controlling this feature.")
588

    
589
    accounting.options = \
590
    [
591
     Option(name        = "datafile",
592
            getter      = "datafile",
593
            type        = OPTTYPE_STRING,
594
            required    = False,
595
            default     = None,
596
            doc         = """
597
            This is the file where statistics on
598
            the scheduler's run will be saved to (waiting time of leases,
599
            utilization data, etc.). If omitted, no data will be saved.
600
            """),
601

    
602
     Option(name        = "probes",
603
            getter      = "accounting-probes",
604
            type        = OPTTYPE_STRING,
605
            required    = False,
606
            doc         = """
607
            Accounting probes.
608
            
609
            There are four built-in probes:
610
            
611
             - AR: Collects information on AR leases.
612
             - best-effort: Collects information on best effort leases.
613
             - immediate: Collects information immediate leases.
614
             - utilization: Collects information on resource utilization.
615
             
616
            See the Haizea documentation for details on how to write your
617
            own accounting probes.
618
      
619
            """),
620

    
621
     Option(name        = "attributes",
622
            getter      = "attributes",
623
            type        = OPTTYPE_STRING,
624
            required    = False,
625
            doc         = """
626
            This option is used internally by Haizea when using
627
            multiconfiguration files. See the multiconfiguration
628
            documentation for more details.        
629
            """)
630
    ]
631
    
632
    sections.append(accounting)
633

    
634
    # ============================= #
635
    #                               #
636
    #      DEPLOYMENT OPTIONS       #
637
    #     (w/ image transfers)      #
638
    #                               #
639
    # ============================= #
640

    
641
    imgtransfer = Section("deploy-imagetransfer", required=False,
642
                         required_if = [(("general","lease-deployment"),"imagetransfer")],
643
                         doc = """
644
                         When lease deployment with disk image transfers is selected,
645
                         this section is used to control image deployment parameters.""")
646
    imgtransfer.options = \
647
    [
648
     Option(name        = "transfer-mechanism",
649
            getter      = "transfer-mechanism",
650
            type        = OPTTYPE_STRING,
651
            required    = True,
652
            valid       = [constants.TRANSFER_UNICAST,
653
                           constants.TRANSFER_MULTICAST],
654
            doc         = """
655
            Specifies how disk images are transferred. Valid values are:
656
            
657
             - unicast: A disk image can be transferred to just one node at a time
658
             - multicast: A disk image can be multicast to multiple nodes at 
659
               the same time.                
660
            """),
661

    
662
     Option(name        = "avoid-redundant-transfers",
663
            getter      = "avoid-redundant-transfers",
664
            type        = OPTTYPE_BOOLEAN,
665
            required    = False,
666
            default     = True,
667
            doc         = """
668
            Specifies whether the scheduler should take steps to
669
            detect and avoid redundant transfers (e.g., if two leases are
670
            scheduled on the same node, and they both require the same disk
671
            image, don't transfer the image twice; allow one to "piggyback"
672
            on the other). There is generally no reason to set this option
673
            to False.
674
            """),
675

    
676
     Option(name        = "force-imagetransfer-time",
677
            getter      = "force-imagetransfer-time",
678
            type        = OPTTYPE_TIMEDELTA,
679
            required    = False,
680
            doc         = """
681
            Forces the image transfer time to a specific amount.
682
            This options is intended for testing purposes.                
683
            """),
684
            
685
     Option(name        = "diskimage-reuse",
686
            getter      = "diskimage-reuse",
687
            type        = OPTTYPE_STRING,
688
            required    = False,
689
            required_if = None,
690
            default     = constants.REUSE_NONE,
691
            valid       = [constants.REUSE_NONE,
692
                           constants.REUSE_IMAGECACHES],
693
            doc         = """
694
            Specifies whether disk image caches should be created
695
            on the nodes, so the scheduler can reduce the number of transfers
696
            by reusing images. Valid values are:
697
            
698
             - none: No image reuse
699
             - image-caches: Use image caching algorithm described in Haizea
700
               publications
701
            """),
702

    
703
     Option(name        = "diskimage-cache-size",
704
            getter      = "diskimage-cache-size",
705
            type        = OPTTYPE_INT,
706
            required    = False,
707
            required_if = [(("deploy-imagetransfer","diskimage-reuse"),True)],
708
            doc         = """
709
            Specifies the size (in MB) of the disk image cache on
710
            each physical node.                
711
            """)
712
    ]
713
    sections.append(imgtransfer)
714

    
715
    # ============================= #
716
    #                               #
717
    #      TRACEFILE OPTIONS        #
718
    #                               #
719
    # ============================= #
720

    
721
    tracefile = Section("tracefile", required=False, 
722
                        doc="""
723
                        When reading in requests from a tracefile, this section is used
724
                        to specify the tracefile and other parameters.""")
725
    tracefile.options = \
726
    [
727
     Option(name        = "tracefile",
728
            getter      = "tracefile",
729
            type        = OPTTYPE_STRING,
730
            required    = True,
731
            doc         = """
732
            Path to tracefile to use.                
733
            """),
734

    
735
     Option(name        = "annotationfile",
736
            getter      = "annotationfile",
737
            type        = OPTTYPE_STRING,
738
            required    = False,
739
            doc         = """
740
            Path to lease annotation file.                
741
            """),
742

    
743
     Option(name        = "injectionfile",
744
            getter      = "injectionfile",
745
            type        = OPTTYPE_STRING,
746
            required    = False,
747
            doc         = """
748
            Path to file with leases to "inject" into the tracefile.                
749
            """),      
750
               
751
     Option(name        = "runtime-slowdown-overhead",
752
            getter      = "runtime-slowdown-overhead",
753
            type        = OPTTYPE_FLOAT,
754
            required    = False,
755
            default     = 0,
756
            doc         = """
757
            Adds a runtime overhead (in %) to the lease duration.                
758
            """),
759

    
760
     Option(name        = "add-overhead",
761
            getter      = "add-overhead",
762
            type        = OPTTYPE_STRING,
763
            required    = False,
764
            default     = constants.RUNTIMEOVERHEAD_NONE,
765
            valid       = [constants.RUNTIMEOVERHEAD_NONE,
766
                           constants.RUNTIMEOVERHEAD_ALL,
767
                           constants.RUNTIMEOVERHEAD_BE],
768
            doc         = """
769
            Specifies what leases will have a runtime overhead added:
770
            
771
             - none: No runtime overhead must be added.
772
             - besteffort: Add only to best-effort leases
773
             - all: Add runtime overhead to all leases                
774
            """),
775

    
776
     Option(name        = "bootshutdown-overhead",
777
            getter      = "bootshutdown-overhead",
778
            type        = OPTTYPE_TIMEDELTA,
779
            required    = False,
780
            default     = TimeDelta(seconds=0),
781
            doc         = """
782
            Specifies how many seconds will be alloted to
783
            boot and shutdown of the lease.                
784
            """),
785
                  
786
     Option(name        = "override-memory",
787
            getter      = "override-memory",
788
            type        = OPTTYPE_INT,
789
            required    = False,
790
            default     = constants.NO_MEMORY_OVERRIDE,
791
            doc         = """
792
            Overrides memory requirements specified in tracefile.
793
            """),
794
    ]
795
    sections.append(tracefile)
796
    
797
    # ============================= #
798
    #                               #
799
    #      OPENNEBULA OPTIONS       #
800
    #                               #
801
    # ============================= #
802

    
803
    opennebula = Section("opennebula", required=False,
804
                         required_if = [(("general","mode"),"opennebula")],
805
                         doc = """
806
                         This section is used to specify OpenNebula parameters,
807
                         necessary when using Haizea as an OpenNebula scheduling backend.""")
808
    opennebula.options = \
809
    [
810
     Option(name        = "host",
811
            getter      = "one.host",
812
            type        = OPTTYPE_STRING,
813
            required    = True,
814
            doc         = """
815
            Host where OpenNebula is running.
816
            Typically, OpenNebula and Haizea will be installed
817
            on the same host, so the following option should be
818
            set to 'localhost'. If they're on different hosts,
819
            make sure you modify this option accordingly.             
820
            """),
821

    
822
     Option(name        = "port",
823
            getter      = "one.port",
824
            type        = OPTTYPE_INT,
825
            required    = False,
826
            default     = defaults.OPENNEBULA_RPC_PORT,
827
            doc         = """
828
            TCP port of OpenNebula's XML RPC server             
829
            """),
830
            
831
     Option(name        = "stop-when-no-more-leases",
832
            getter      = "stop-when-no-more-leases",
833
            type        = OPTTYPE_BOOLEAN,
834
            required    = False,
835
            default     = False,
836
            doc         = """
837
            This option is useful for testing and running experiments.
838
            If set to True, Haizea will stop when there are no more leases
839
            to process (which allows you to tun Haizea and OpenNebula unattended,
840
            and count on it stopping when there are no more leases to process).
841
            For now, this only makes sense if you're seeding Haizea with requests from
842
            the start (otherwise, it will start and immediately stop).
843
            """),            
844

    
845
     Option(name        = "dry-run",
846
            getter      = "dry-run",
847
            type        = OPTTYPE_BOOLEAN,
848
            required    = False,
849
            default     = False,
850
            doc         = """
851
            This option is useful for testing.
852
            If set to True, Haizea will fast-forward through time (note that this is
853
            different that using the simulated clock, which has to be used with a tracefile;
854
            with an Haizea/OpenNebula dry run, you will have to seed OpenNebula with requests
855
            before starting Haizea). You will generally want to set stop-when-no-more-leases
856
            when doing a dry-run.
857
            
858
            IMPORTANT: Haizea will still send out enactment commands to OpenNebula. Make
859
            sure you replace onevm with a dummy command that does nothing (or that reacts
860
            in some way you want to test; e.g., by emulating a deployment failure, etc.)
861
            """),            
862

    
863
    ]
864
    sections.append(opennebula)
865
    
866
    def __init__(self, config):
867
        Config.__init__(self, config, self.sections)
868

    
869
        self.attrs = {}
870
        if self._options["attributes"] != None:
871
            attrs = self._options["attributes"].split(",")
872
            for attr in attrs:
873
                (k,v) = attr.split("=")
874
                self.attrs[k] = v
875

    
876
        
877
    def get_attr(self, attr):
878
        return self.attrs[attr]
879
        
880
    def get_attrs(self):
881
        return self.attrs.keys()
882

    
883

    
884
class HaizeaMultiConfig(Config):
885
    
886
    MULTI_SEC = "multi"
887
    COMMON_SEC = "common"
888
    TRACEDIR_OPT = "tracedir"
889
    TRACEFILES_OPT = "tracefiles"
890
    ANNOTATIONDIR_OPT = "annotationdir"
891
    ANNOTATIONFILES_OPT = "annotationfiles"
892
    SKIP_NO_ANNOTATION_OPT = "skip-no-annotation"
893
    INJDIR_OPT = "injectiondir"
894
    INJFILES_OPT = "injectionfiles"
895
    SKIP_NO_INJECTION_OPT = "skip-no-injection"
896
    DATADIR_OPT = "datadir"
897
    
898
    def __init__(self, config):
899
        # TODO: Define "multi" section as a Section object
900
        Config.__init__(self, config, [])
901
        
902
    def get_profiles(self):
903
        sections = set([s.split(":")[0] for s in self.config.sections()])
904
        # Remove multi and common sections
905
        sections.difference_update([self.COMMON_SEC, self.MULTI_SEC])
906
        return list(sections)
907

    
908
    def get_trace_files(self):
909
        dir = self.config.get(self.MULTI_SEC, self.TRACEDIR_OPT)
910
        traces = self.config.get(self.MULTI_SEC, self.TRACEFILES_OPT).split()
911
        return [dir + "/" + t for t in traces]
912

    
913
    def get_annotation_files(self):
914
        if not self.config.has_option(self.MULTI_SEC, self.ANNOTATIONDIR_OPT):
915
            return [None]
916
        else:
917
            dir = self.config.get(self.MULTI_SEC, self.ANNOTATIONDIR_OPT)
918
            annot = self.config.get(self.MULTI_SEC, self.ANNOTATIONFILES_OPT).split()
919
            annot = [dir + "/" + t for t in annot]
920
            annot.append(None)
921
            return annot
922

    
923
    def get_inject_files(self):
924
        if not self.config.has_option(self.MULTI_SEC, self.INJDIR_OPT):
925
            return [None]
926
        else:
927
            dir = self.config.get(self.MULTI_SEC, self.INJDIR_OPT)
928
            inj = self.config.get(self.MULTI_SEC, self.INJFILES_OPT).split()
929
            inj = [dir + "/" + i for i in inj]
930
            inj.append(None)
931
            return inj
932
    
933
    def get_configs(self):
934
        profiles = self.get_profiles()
935
        tracefiles = self.get_trace_files()
936
        annotationfiles = self.get_annotation_files()
937
        injectfiles = self.get_inject_files()
938

    
939
        if not self.config.has_option(self.MULTI_SEC, self.SKIP_NO_INJECTION_OPT):
940
            skip_no_injection = False
941
        else:
942
            skip_no_injection = self.config.getboolean(self.MULTI_SEC, self.SKIP_NO_INJECTION_OPT)
943
            
944
        if not self.config.has_option(self.MULTI_SEC, self.SKIP_NO_ANNOTATION_OPT):
945
            skip_no_annotation = False
946
        else:
947
            skip_no_annotation = self.config.getboolean(self.MULTI_SEC, self.SKIP_NO_ANNOTATION_OPT)
948
        
949
        no_annotations = (annotationfiles == [None])
950
        no_injections = (injectfiles == [None])
951

    
952
        configs = []
953
        for profile in profiles:
954
            for tracefile in tracefiles:
955
                for annotationfile in annotationfiles:
956
                    for injectfile in injectfiles:
957
                        if annotationfile == None and skip_no_annotation:
958
                            continue
959
                        if injectfile == None and skip_no_injection:
960
                            continue
961
                        profileconfig = ConfigParser.ConfigParser()
962
                        commonsections = [s for s in self.config.sections() if s.startswith("common:")]
963
                        profilesections = [s for s in self.config.sections() if s.startswith(profile +":")]
964
                        sections = commonsections + profilesections
965
                        for s in sections:
966
                            s_noprefix = s.split(":")[1]
967
                            items = self.config.items(s)
968
                            if not profileconfig.has_section(s_noprefix):
969
                                profileconfig.add_section(s_noprefix)
970
                            for item in items:
971
                                profileconfig.set(s_noprefix, item[0], item[1])
972
                                
973
                        # The tracefile section may have not been created
974
                        if not profileconfig.has_section("tracefile"):
975
                            profileconfig.add_section("tracefile")
976
    
977
                        # Add tracefile option
978
                        profileconfig.set("tracefile", "tracefile", tracefile)
979
                        
980
                        # Add injected file option
981
                        if injectfile != None:
982
                            profileconfig.set("tracefile", "injectionfile", injectfile)
983
                            
984
                        # Add annotations file option
985
                        if annotationfile != None:
986
                            profileconfig.set("tracefile", "annotationfile", annotationfile)
987
    
988
                        # Add datafile option
989
                        datadir = self.config.get(self.MULTI_SEC, self.DATADIR_OPT)
990
                        datafilename = generate_config_name(profile, tracefile, annotationfile, injectfile)
991
                        datafile = datadir + "/" + datafilename + ".dat"
992
                        # The accounting section may have not been created
993
                        if not profileconfig.has_section("accounting"):
994
                            profileconfig.add_section("accounting")
995
                        profileconfig.set("accounting", "datafile", datafile)
996
                        
997
                        # Set "attributes" option (only used internally)
998
                        attrs = {"profile":profile,"tracefile":tracefile,"injectfile":injectfile,"annotationfile":annotationfile}
999
                        
1000
                        trace_attrs = self.__load_attributes_from_file(tracefile)
1001
                        attrs.update(trace_attrs)
1002
                        
1003
                        if injectfile != None:
1004
                            inj_attrs = self.__load_attributes_from_file(injectfile)
1005
                            attrs.update(inj_attrs)
1006

    
1007
                        if annotationfile != None:
1008
                            annot_attrs = self.__load_attributes_from_file(annotationfile)
1009
                            attrs.update(annot_attrs)
1010
                        
1011
                        attrs_str = ",".join(["%s=%s" % (k,v) for (k,v) in attrs.items()])
1012
                        if profileconfig.has_option("accounting", "attributes"):
1013
                            attrs_str += ",%s" % profileconfig.get("accounting", "attributes")
1014
                        profileconfig.set("accounting", "attributes", attrs_str)
1015
    
1016
                        try:
1017
                            c = HaizeaConfig(profileconfig)
1018
                        except ConfigException, msg:
1019
                            print >> sys.stderr, "Error in configuration file:"
1020
                            print >> sys.stderr, msg
1021
                            exit(1)
1022
                        configs.append(c)
1023
        
1024
        return configs
1025
    
1026
    def __load_attributes_from_file(self, file):
1027
        attrs = {}
1028
        context = ET.iterparse(file, events=("start", "end"))
1029
        for event, elem in context:
1030
            if event == "start" and elem.tag in ("lease-annotation", "lease-request"):
1031
                break                
1032
            if event == "end" and elem.tag == "attributes":
1033
                for attr_elem in elem:
1034
                    attrs[attr_elem.get("name")] = attr_elem.get("value")
1035
                break
1036
        return attrs
1037