Statistics
| Branch: | Tag: | Revision:

one / src / onedb / fsck.rb @ 49b897fc

History | View | Annotate | Download (53.8 KB)

1
# -------------------------------------------------------------------------- #
2
# Copyright 2002-2014, OpenNebula Project (OpenNebula.org), C12G Labs        #
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
require "rexml/document"
18
include REXML
19
require 'ipaddr'
20
require 'set'
21

    
22
require 'nokogiri'
23

    
24
module OneDBFsck
25
    VERSION = "4.4.1"
26

    
27
    def db_version
28
        VERSION
29
    end
30

    
31
    def one_version
32
        "OpenNebula #{VERSION}"
33
    end
34

    
35
    IMAGE_STATES=%w{INIT READY USED DISABLED LOCKED ERROR CLONE DELETE USED_PERS}
36

    
37
    def fsck
38

    
39
        ########################################################################
40
        # Acl
41
        ########################################################################
42

    
43
        ########################################################################
44
        # Users
45
        #
46
        # USER/GNAME
47
        ########################################################################
48

    
49
        ########################################################################
50
        # Datastore
51
        #
52
        # DATASTORE/UID
53
        # DATASTORE/UNAME
54
        # DATASTORE/GID
55
        # DATASTORE/GNAME
56
        # DATASTORE/SYSTEM ??
57
        ########################################################################
58

    
59
        ########################################################################
60
        # VM Template
61
        #
62
        # VMTEMPLATE/UID
63
        # VMTEMPLATE/UNAME
64
        # VMTEMPLATE/GID
65
        # VMTEMPLATE/GNAME
66
        ########################################################################
67

    
68
        ########################################################################
69
        # Documents
70
        #
71
        # DOCUMENT/UID
72
        # DOCUMENT/UNAME
73
        # DOCUMENT/GID
74
        # DOCUMENT/GNAME
75
        ########################################################################
76

    
77
        ########################################################################
78
        # VM
79
        #
80
        # VM/UID
81
        # VM/UNAME
82
        # VM/GID
83
        # VM/GNAME
84
        #
85
        # VM/STATE        <--- Check transitioning states?
86
        # VM/LCM_STATE    <---- Check consistency state/lcm_state ?
87
        ########################################################################
88

    
89
        ########################################################################
90
        # Image
91
        #
92
        # IMAGE/UID
93
        # IMAGE/UNAME
94
        # IMAGE/GID
95
        # IMAGE/GNAME
96
        ########################################################################
97

    
98
        ########################################################################
99
        # VNet
100
        #
101
        # VNET/UID
102
        # VNET/UNAME
103
        # VNET/GID
104
        # VNET/GNAME
105
        ########################################################################
106

    
107
        init_log_time()
108

    
109
        @errors = 0
110
        puts
111

    
112

    
113
        ########################################################################
114
        # pool_control
115
        ########################################################################
116

    
117
        tables = ["group_pool", "user_pool", "acl", "image_pool", "host_pool",
118
            "network_pool", "template_pool", "vm_pool", "cluster_pool",
119
            "datastore_pool", "document_pool"]
120

    
121
        tables.each do |table|
122
            max_oid = -1
123

    
124
            @db.fetch("SELECT MAX(oid) FROM #{table}") do |row|
125
                max_oid = row[:"MAX(oid)"].to_i
126
            end
127

    
128
            # max(oid) will return 0 if there is none,
129
            # or if the max is actually 0. Check this:
130
            if ( max_oid == 0 )
131
                max_oid = -1
132

    
133
                @db.fetch("SELECT oid FROM #{table} WHERE oid=0") do |row|
134
                    max_oid = 0
135
                end
136
            end
137

    
138
            control_oid = -1
139

    
140
            @db.fetch("SELECT last_oid FROM pool_control WHERE tablename='#{table}'") do |row|
141
                control_oid = row[:last_oid].to_i
142
            end
143

    
144
            if ( max_oid > control_oid )
145
                log_error("pool_control for table #{table} has last_oid #{control_oid}, but it is #{max_oid}")
146

    
147
                if control_oid != -1
148
                    @db.run("UPDATE pool_control SET last_oid=#{max_oid} WHERE tablename='#{table}'")
149
                else
150
                    @db[:pool_control].insert(
151
                        :tablename  => table,
152
                        :last_oid   => max_oid)
153
                end
154
            end
155
        end
156

    
157
        log_time()
158

    
159
        ########################################################################
160
        # Groups
161
        #
162
        # GROUP/USERS/ID
163
        ########################################################################
164

    
165
        ########################################################################
166
        # Users
167
        #
168
        # USER/GID
169
        ########################################################################
170

    
171
        group = {}
172

    
173
        @db.fetch("SELECT oid FROM group_pool") do |row|
174
            group[row[:oid]] = []
175
        end
176

    
177
        users_fix = {}
178

    
179
        @db.fetch("SELECT oid,body,gid FROM user_pool") do |row|
180
            doc = Document.new(row[:body])
181

    
182
            gid = doc.root.get_text('GID').to_s.to_i
183
            user_gid = gid
184
            user_gids = Set.new
185

    
186
            if group[gid].nil?
187
                log_error("User #{row[:oid]} has primary group #{gid}, but it does not exist")
188

    
189
                user_gid = 1
190

    
191
                doc.root.each_element('GID') do |e|
192
                    e.text = "1"
193
                end
194

    
195
                doc.root.each_element('GNAME') do |e|
196
                    e.text = "users"
197
                end
198

    
199
                doc.root.each_element("GROUPS") { |e|
200
                    e.elements.delete("ID[.=#{gid}]")
201
                    e.add_element("ID").text = user_gid.to_s
202
                }
203

    
204
                users_fix[row[:oid]] = {:body => doc.to_s, :gid => user_gid}
205
            end
206

    
207
            doc.root.each_element("GROUPS/ID") { |e|
208
                user_gids.add e.text.to_i
209
            }
210

    
211
            if !user_gids.include?(user_gid)
212
                log_error("User #{row[:oid]} does not have his primary group #{user_gid} in the list of secondary groups")
213

    
214
                doc.root.each_element("GROUPS") { |e|
215
                    e.add_element("ID").text = user_gid.to_s
216
                }
217

    
218
                user_gids.add user_gid.to_i
219

    
220
                users_fix[row[:oid]] = {:body => doc.to_s, :gid => user_gid}
221
            end
222

    
223
            user_gids.each do |secondary_gid|
224
                if group[secondary_gid].nil?
225
                    log_error("User #{row[:oid]} has secondary group #{secondary_gid}, but it does not exist")
226

    
227
                    doc.root.each_element("GROUPS") { |e|
228
                        e.elements.delete("ID[.=#{secondary_gid}]")
229
                    }
230

    
231
                    users_fix[row[:oid]] = {:body => doc.to_s, :gid => user_gid}
232
                else
233
                    group[secondary_gid] << row[:oid]
234
                end
235
            end
236

    
237
            if gid != row[:gid]
238
                log_error(
239
                    "User #{row[:oid]} is in group #{gid}, but the DB "<<
240
                    "table has GID column #{row[:gid]}")
241

    
242
                users_fix[row[:oid]] = {:body => doc.to_s, :gid => user_gid}
243
            end
244
        end
245

    
246
        @db.transaction do
247
            users_fix.each do |id, user|
248
                @db[:user_pool].where(:oid => id).update(
249
                    :body => user[:body],
250
                    :gid => user[:gid])
251
            end
252
        end
253

    
254
        log_time()
255

    
256

    
257
        @db.run "CREATE TABLE group_pool_new (oid INTEGER PRIMARY KEY, name VARCHAR(128), body MEDIUMTEXT, uid INTEGER, gid INTEGER, owner_u INTEGER, group_u INTEGER, other_u INTEGER, UNIQUE(name));"
258

    
259
        @db.transaction do
260
            @db.fetch("SELECT * from group_pool") do |row|
261
                gid = row[:oid]
262
                doc = Document.new(row[:body])
263

    
264
                users_elem = doc.root.elements.delete("USERS")
265

    
266
                users_new_elem = doc.root.add_element("USERS")
267

    
268
                group[gid].each do |id|
269
                    id_elem = users_elem.elements.delete("ID[.=#{id}]")
270

    
271
                    if id_elem.nil?
272
                        log_error("User #{id} is missing from Group #{gid} users id list")
273
                    end
274

    
275
                    users_new_elem.add_element("ID").text = id.to_s
276
                end
277

    
278
                users_elem.each_element("ID") do |id_elem|
279
                    log_error("User #{id_elem.text} is in Group #{gid} users id list, but it should not")
280
                end
281

    
282
                row[:body] = doc.to_s
283

    
284
                # commit
285
                @db[:group_pool_new].insert(row)
286
            end
287
        end
288

    
289
        # Rename table
290
        @db.run("DROP TABLE group_pool")
291
        @db.run("ALTER TABLE group_pool_new RENAME TO group_pool")
292

    
293
        log_time()
294

    
295
        ########################################################################
296
        # Clusters
297
        #
298
        # CLUSTER/SYSTEM_DS
299
        # CLUSTER/HOSTS/ID
300
        # CLUSTER/DATASTORES/ID
301
        # CLUSTER/VNETS/ID
302
        ########################################################################
303
        # Datastore
304
        #
305
        # DATASTORE/CLUSTER_ID
306
        # DATASTORE/CLUSTER
307
        ########################################################################
308
        # VNet
309
        #
310
        # VNET/CLUSTER_ID
311
        # VNET/CLUSTER
312
        ########################################################################
313
        # Hosts
314
        #
315
        # HOST/CLUSTER_ID
316
        # HOST/CLUSTER
317
        ########################################################################
318

    
319
        cluster = {}
320

    
321
        @db.fetch("SELECT oid, name FROM cluster_pool") do |row|
322
            cluster[row[:oid]] = {}
323

    
324
            cluster[row[:oid]][:name]       = row[:name]
325

    
326
            cluster[row[:oid]][:hosts]      = []
327
            cluster[row[:oid]][:datastores] = []
328
            cluster[row[:oid]][:vnets]      = []
329
        end
330

    
331
        hosts_fix       = {}
332
        datastores_fix  = {}
333
        vnets_fix       = {}
334

    
335
        @db.transaction do
336
            @db.fetch("SELECT oid,body,cid FROM host_pool") do |row|
337
                doc = Document.new(row[:body])
338

    
339
                cluster_id = doc.root.get_text('CLUSTER_ID').to_s.to_i
340
                cluster_name = doc.root.get_text('CLUSTER')
341

    
342
                if cluster_id != row[:cid]
343
                    log_error("Host #{row[:oid]} is in cluster #{cluster_id}, but cid column has cluster #{row[:cid]}")
344
                    hosts_fix[row[:oid]] = {:body => row[:body], :cid => cluster_id}
345
                end
346

    
347
                if cluster_id != -1
348
                    cluster_entry = cluster[cluster_id]
349

    
350
                    if cluster_entry.nil?
351
                        log_error("Host #{row[:oid]} is in cluster #{cluster_id}, but it does not exist")
352

    
353
                        doc.root.each_element('CLUSTER_ID') do |e|
354
                            e.text = "-1"
355
                        end
356

    
357
                        doc.root.each_element('CLUSTER') do |e|
358
                            e.text = ""
359
                        end
360

    
361
                        hosts_fix[row[:oid]] = {:body => doc.to_s, :cid => -1}
362
                    else
363
                        if cluster_name != cluster_entry[:name]
364
                            log_error("Host #{row[:oid]} has a wrong name for cluster #{cluster_id}, #{cluster_name}. It will be changed to #{cluster_entry[:name]}")
365

    
366
                            doc.root.each_element('CLUSTER') do |e|
367
                                e.text = cluster_entry[:name]
368
                            end
369

    
370
                            hosts_fix[row[:oid]] = {:body => doc.to_s, :cid => cluster_id}
371
                        end
372

    
373
                        cluster_entry[:hosts] << row[:oid]
374
                    end
375
                end
376
            end
377

    
378
            hosts_fix.each do |id, entry|
379
                @db[:host_pool].where(:oid => id).update(:body => entry[:body], :cid => entry[:cid])
380
            end
381

    
382
            log_time()
383

    
384
            @db.fetch("SELECT oid,body,cid FROM datastore_pool") do |row|
385
                doc = Document.new(row[:body])
386

    
387
                cluster_id = doc.root.get_text('CLUSTER_ID').to_s.to_i
388
                cluster_name = doc.root.get_text('CLUSTER')
389

    
390
                if cluster_id != row[:cid]
391
                    log_error("Datastore #{row[:oid]} is in cluster #{cluster_id}, but cid column has cluster #{row[:cid]}")
392
                    hosts_fix[row[:oid]] = {:body => row[:body], :cid => cluster_id}
393
                end
394

    
395
                if cluster_id != -1
396
                    cluster_entry = cluster[cluster_id]
397

    
398
                    if cluster_entry.nil?
399
                        log_error("Datastore #{row[:oid]} is in cluster #{cluster_id}, but it does not exist")
400

    
401
                        doc.root.each_element('CLUSTER_ID') do |e|
402
                            e.text = "-1"
403
                        end
404

    
405
                        doc.root.each_element('CLUSTER') do |e|
406
                            e.text = ""
407
                        end
408

    
409
                        datastores_fix[row[:oid]] = {:body => doc.to_s, :cid => -1}
410
                    else
411
                        cluster_entry[:datastores] << row[:oid]
412

    
413
                        if cluster_name != cluster_entry[:name]
414
                            log_error("Datastore #{row[:oid]} has a wrong name for cluster #{cluster_id}, #{cluster_name}. It will be changed to #{cluster_entry[:name]}")
415

    
416
                            doc.root.each_element('CLUSTER') do |e|
417
                                e.text = cluster_entry[:name]
418
                            end
419

    
420
                            datastores_fix[row[:oid]] = {:body => doc.to_s, :cid => cluster_id}
421
                        end
422
                    end
423
                end
424
            end
425

    
426
            datastores_fix.each do |id, entry|
427
                @db[:datastore_pool].where(:oid => id).update(:body => entry[:body], :cid => entry[:cid])
428
            end
429

    
430
            log_time()
431

    
432
            @db.fetch("SELECT oid,body,cid FROM network_pool") do |row|
433
                doc = Document.new(row[:body])
434

    
435
                cluster_id = doc.root.get_text('CLUSTER_ID').to_s.to_i
436
                cluster_name = doc.root.get_text('CLUSTER')
437

    
438
                if cluster_id != row[:cid]
439
                    log_error("VNet #{row[:oid]} is in cluster #{cluster_id}, but cid column has cluster #{row[:cid]}")
440
                    hosts_fix[row[:oid]] = {:body => row[:body], :cid => cluster_id}
441
                end
442

    
443
                if cluster_id != -1
444
                    cluster_entry = cluster[cluster_id]
445

    
446
                    if cluster_entry.nil?
447
                        log_error("VNet #{row[:oid]} is in cluster #{cluster_id}, but it does not exist")
448

    
449
                        doc.root.each_element('CLUSTER_ID') do |e|
450
                            e.text = "-1"
451
                        end
452

    
453
                        doc.root.each_element('CLUSTER') do |e|
454
                            e.text = ""
455
                        end
456

    
457
                        vnets_fix[row[:oid]] = {:body => doc.to_s, :cid => -1}
458
                    else
459
                        if cluster_name != cluster_entry[:name]
460
                            log_error("VNet #{row[:oid]} has a wrong name for cluster #{cluster_id}, #{cluster_name}. It will be changed to #{cluster_entry[:name]}")
461

    
462
                            doc.root.each_element('CLUSTER') do |e|
463
                                e.text = cluster_entry[:name]
464
                            end
465

    
466
                            vnets_fix[row[:oid]] = {:body => doc.to_s, :cid => -1}
467
                        end
468

    
469
                        cluster_entry[:vnets] << row[:oid]
470
                    end
471
                end
472
            end
473

    
474
            vnets_fix.each do |id, entry|
475
                @db[:network_pool].where(:oid => id).update(:body => entry[:body], :cid => entry[:cid])
476
            end
477
        end
478

    
479
        log_time()
480

    
481
        @db.run "CREATE TABLE cluster_pool_new (oid INTEGER PRIMARY KEY, name VARCHAR(128), body MEDIUMTEXT, uid INTEGER, gid INTEGER, owner_u INTEGER, group_u INTEGER, other_u INTEGER, UNIQUE(name));"
482

    
483
        @db.transaction do
484
            @db.fetch("SELECT * from cluster_pool") do |row|
485
                cluster_id = row[:oid]
486
                doc = Document.new(row[:body])
487

    
488
                # Hosts
489
                hosts_elem = doc.root.elements.delete("HOSTS")
490

    
491
                hosts_new_elem = doc.root.add_element("HOSTS")
492

    
493
                cluster[cluster_id][:hosts].each do |id|
494
                    id_elem = hosts_elem.elements.delete("ID[.=#{id}]")
495

    
496
                    if id_elem.nil?
497
                        log_error("Host #{id} is missing from Cluster #{cluster_id} host id list")
498
                    end
499

    
500
                    hosts_new_elem.add_element("ID").text = id.to_s
501
                end
502

    
503
                hosts_elem.each_element("ID") do |id_elem|
504
                    log_error("Host #{id_elem.text} is in Cluster #{cluster_id} host id list, but it should not")
505
                end
506

    
507

    
508
                # Datastores
509
                ds_elem = doc.root.elements.delete("DATASTORES")
510

    
511
                ds_new_elem = doc.root.add_element("DATASTORES")
512

    
513
                cluster[cluster_id][:datastores].each do |id|
514
                    id_elem = ds_elem.elements.delete("ID[.=#{id}]")
515

    
516
                    if id_elem.nil?
517
                        log_error("Datastore #{id} is missing from Cluster #{cluster_id} datastore id list")
518
                    end
519

    
520
                    ds_new_elem.add_element("ID").text = id.to_s
521
                end
522

    
523
                ds_elem.each_element("ID") do |id_elem|
524
                    log_error("Datastore #{id_elem.text} is in Cluster #{cluster_id} datastore id list, but it should not")
525
                end
526

    
527

    
528
                # VNets
529
                vnets_elem = doc.root.elements.delete("VNETS")
530

    
531
                vnets_new_elem = doc.root.add_element("VNETS")
532

    
533
                cluster[cluster_id][:vnets].each do |id|
534
                    id_elem = vnets_elem.elements.delete("ID[.=#{id}]")
535

    
536
                    if id_elem.nil?
537
                        log_error("VNet #{id} is missing from Cluster #{cluster_id} vnet id list")
538
                    end
539

    
540
                    vnets_new_elem.add_element("ID").text = id.to_s
541
                end
542

    
543
                vnets_elem.each_element("ID") do |id_elem|
544
                    log_error("VNet #{id_elem.text} is in Cluster #{cluster_id} vnet id list, but it should not")
545
                end
546

    
547

    
548
                row[:body] = doc.to_s
549

    
550
                # commit
551
                @db[:cluster_pool_new].insert(row)
552
            end
553
        end
554

    
555
        log_time()
556

    
557
        # Rename table
558
        @db.run("DROP TABLE cluster_pool")
559
        @db.run("ALTER TABLE cluster_pool_new RENAME TO cluster_pool")
560

    
561

    
562
        ########################################################################
563
        # Datastore
564
        #
565
        # DATASTORE/IMAGES/ID
566
        ########################################################################
567
        # Image
568
        #
569
        # IMAGE/DATASTORE_ID
570
        # IMAGE/DATASTORE
571
        ########################################################################
572

    
573
        datastore = {}
574

    
575
        @db.fetch("SELECT oid, name FROM datastore_pool") do |row|
576
            datastore[row[:oid]] = {:name => row[:name], :images => []}
577
        end
578

    
579
        ds_1_name = datastore[1][:name]
580

    
581
        images_fix = {}
582

    
583
        @db.fetch("SELECT oid,body FROM image_pool") do |row|
584
            doc = Document.new(row[:body])
585

    
586
            ds_id = doc.root.get_text('DATASTORE_ID').to_s.to_i
587
            ds_name = doc.root.get_text('DATASTORE')
588

    
589
            if ds_id != -1
590
                ds_entry = datastore[ds_id]
591

    
592
                if ds_entry.nil?
593
                    log_error("Image #{row[:oid]} has datastore #{ds_id}, but it does not exist. It will be moved to the Datastore #{ds_1_name} (1), but it is probably unusable anymore")
594

    
595
                    doc.root.each_element('DATASTORE_ID') do |e|
596
                        e.text = "1"
597
                    end
598

    
599
                    doc.root.each_element('DATASTORE') do |e|
600
                        e.text = ds_1_name
601
                    end
602

    
603
                    images_fix[row[:oid]] = doc.to_s
604

    
605
                    datastore[1][:images] << row[:oid]
606
                else
607
                    if ds_name != ds_entry[:name]
608
                        log_error("Image #{row[:oid]} has a wrong name for datastore #{ds_id}, #{ds_name}. It will be changed to #{ds_entry[:name]}")
609

    
610
                        doc.root.each_element('DATASTORE') do |e|
611
                            e.text = ds_entry[:name]
612
                        end
613

    
614
                        images_fix[row[:oid]] = doc.to_s
615
                    end
616

    
617
                    ds_entry[:images] << row[:oid]
618
                end
619
            end
620
        end
621

    
622
        @db.transaction do
623
            images_fix.each do |id, body|
624
                @db[:image_pool].where(:oid => id).update(:body => body)
625
            end
626
        end
627

    
628
        log_time()
629

    
630
        @db.run "CREATE TABLE datastore_pool_new (oid INTEGER PRIMARY KEY, name VARCHAR(128), body MEDIUMTEXT, uid INTEGER, gid INTEGER, owner_u INTEGER, group_u INTEGER, other_u INTEGER, cid INTEGER, UNIQUE(name));"
631

    
632
        @db.transaction do
633
            @db.fetch("SELECT * from datastore_pool") do |row|
634
                ds_id = row[:oid]
635
                doc = Document.new(row[:body])
636

    
637
                images_elem = doc.root.elements.delete("IMAGES")
638

    
639
                images_new_elem = doc.root.add_element("IMAGES")
640

    
641
                datastore[ds_id][:images].each do |id|
642
                    id_elem = images_elem.elements.delete("ID[.=#{id}]")
643

    
644
                    if id_elem.nil?
645
                        log_error(
646
                            "Image #{id} is missing from Datastore #{ds_id} "<<
647
                            "image id list")
648
                    end
649

    
650
                    images_new_elem.add_element("ID").text = id.to_s
651
                end
652

    
653
                images_elem.each_element("ID") do |id_elem|
654
                    log_error(
655
                        "Image #{id_elem.text} is in Cluster #{ds_id} "<<
656
                        "image id list, but it should not")
657
                end
658

    
659

    
660
                row[:body] = doc.to_s
661

    
662
                # commit
663
                @db[:datastore_pool_new].insert(row)
664
            end
665
        end
666

    
667
        # Rename table
668
        @db.run("DROP TABLE datastore_pool")
669
        @db.run("ALTER TABLE datastore_pool_new RENAME TO datastore_pool")
670

    
671
        log_time()
672

    
673
        ########################################################################
674
        # VM Counters for host, image and vnet usage
675
        ########################################################################
676

    
677
        counters = {}
678
        counters[:host]  = {}
679
        counters[:image] = {}
680
        counters[:vnet]  = {}
681

    
682
        # Initialize all the host counters to 0
683
        @db.fetch("SELECT oid, name FROM host_pool") do |row|
684
            counters[:host][row[:oid]] = {
685
                :name   => row[:name],
686
                :memory => 0,
687
                :cpu    => 0,
688
                :rvms   => Set.new
689
            }
690
        end
691

    
692
        log_time()
693

    
694
        # Init image counters
695
        @db.fetch("SELECT oid,body FROM image_pool") do |row|
696
            if counters[:image][row[:oid]].nil?
697
                counters[:image][row[:oid]] = {
698
                    :vms    => Set.new,
699
                    :clones => Set.new
700
                }
701
            end
702

    
703
            doc = Document.new(row[:body])
704

    
705
            doc.root.each_element("CLONING_ID") do |e|
706
                img_id = e.text.to_i
707

    
708
                if counters[:image][img_id].nil?
709
                    counters[:image][img_id] = {
710
                        :vms    => Set.new,
711
                        :clones => Set.new
712
                    }
713
                end
714

    
715
                counters[:image][img_id][:clones].add(row[:oid])
716
            end
717
        end
718

    
719
        log_time()
720

    
721
        # Init vnet counters
722
        @db.fetch("SELECT oid,body FROM network_pool") do |row|
723
            doc = Document.new(row[:body])
724

    
725
            counters[:vnet][row[:oid]] = {
726
                :type           => doc.root.get_text('TYPE').to_s.to_i,
727
                :total_leases   => 0,
728
                :leases         => {}
729
            }
730
        end
731

    
732
        log_time()
733

    
734
        vms_fix = {}
735

    
736
        # Aggregate information of the RUNNING vms
737
        @db.fetch("SELECT oid,body FROM vm_pool WHERE state<>6") do |row|
738
            vm_doc = Nokogiri::XML(row[:body])
739

    
740
            state     = vm_doc.root.at_xpath('STATE').text.to_i
741
            lcm_state = vm_doc.root.at_xpath('LCM_STATE').text.to_i            
742

    
743
            # Images used by this VM
744
            vm_doc.root.xpath("TEMPLATE/DISK/IMAGE_ID").each do |e|
745
                img_id = e.text.to_i
746

    
747
                if counters[:image][img_id].nil?
748
                    log_error("VM #{row[:oid]} is using Image #{img_id}, but "<<
749
                        "it does not exist")
750
                else
751
                    counters[:image][img_id][:vms].add(row[:oid])
752
                end
753
            end
754

    
755
            # VNets used by this VM
756
            vm_doc.root.xpath("TEMPLATE/NIC").each do |e|
757
                net_id = nil
758
                e.xpath("NETWORK_ID").each do |nid|
759
                    net_id = nid.text.to_i
760
                end
761

    
762
                if !net_id.nil?
763
                    if counters[:vnet][net_id].nil?
764
                        log_error("VM #{row[:oid]} is using VNet #{net_id}, "<<
765
                            "but it does not exist")
766
                    else
767
                        counters[:vnet][net_id][:leases][e.at_xpath('IP').text] =
768
                            [
769
                                e.at_xpath('MAC').text,                 # MAC
770
                                "1",                                    # USED
771
                                vm_doc.root.at_xpath('ID').text.to_i    # VID
772
                            ]
773
                    end
774
                end
775
            end
776

    
777
            # Host resources
778

    
779
            # Only states that add to Host resources consumption are
780
            # ACTIVE, SUSPENDED, POWEROFF
781
            next if !([3,5,8].include? state)
782

    
783
            # Get memory (integer)
784
            memory = vm_doc.root.at_xpath("TEMPLATE/MEMORY").text.to_i
785

    
786
            # Get CPU (float)
787
            cpu = vm_doc.root.at_xpath("TEMPLATE/CPU").text.to_f
788

    
789
            # Get hostid, hostname
790
            hid = -1
791
            vm_doc.root.xpath("HISTORY_RECORDS/HISTORY[last()]/HID").each { |e|
792
                hid = e.text.to_i
793
            }
794

    
795
            hostname = ""
796
            vm_doc.root.xpath("HISTORY_RECORDS/HISTORY[last()]/HOSTNAME").each { |e|
797
                hostname = e.text
798
            }
799

    
800
            counters_host = counters[:host][hid]
801

    
802
            if counters_host.nil?
803
                log_error("VM #{row[:oid]} is using Host #{hid}, "<<
804
                    "but it does not exist")
805
            else
806
                if counters_host[:name] != hostname
807
                    log_error("VM #{row[:oid]} has a wrong hostname for "<<
808
                        "Host #{hid}, #{hostname}. It will be changed to "<<
809
                        "#{counters_host[:name]}")
810

    
811
                    vm_doc.root.xpath(
812
                        "HISTORY_RECORDS/HISTORY[last()]/HOSTNAME").each { |e|
813
                        e.content = counters_host[:name]
814
                    }
815

    
816
                    vms_fix[row[:oid]] = vm_doc.root.to_s
817
                end
818

    
819
                counters_host[:memory] += memory
820
                counters_host[:cpu]    += cpu
821
                counters_host[:rvms].add(row[:oid])
822
            end
823
        end
824

    
825
        @db.transaction do
826
            vms_fix.each do |id, body|
827
                @db[:vm_pool].where(:oid => id).update(:body => body)
828
            end
829
        end
830

    
831
        log_time()
832

    
833
        ########################################################################
834
        # Hosts
835
        #
836
        # HOST/HOST_SHARE/MEM_USAGE
837
        # HOST/HOST_SHARE/CPU_USAGE
838
        # HOST/HOST_SHARE/RUNNING_VMS
839
        # HOST/VMS/ID
840
        ########################################################################
841

    
842
        # Create a new empty table where we will store the new calculated values
843
        @db.run "CREATE TABLE host_pool_new (oid INTEGER PRIMARY KEY, " <<
844
                "name VARCHAR(128), body MEDIUMTEXT, state INTEGER, " <<
845
                "last_mon_time INTEGER, uid INTEGER, gid INTEGER, " <<
846
                "owner_u INTEGER, group_u INTEGER, other_u INTEGER, " <<
847
                "cid INTEGER, UNIQUE(name));"
848

    
849
        # Calculate the host's xml and write them to host_pool_new
850
        @db.transaction do
851
            @db[:host_pool].each do |row|
852
                host_doc = Document.new(row[:body])
853

    
854
                hid = row[:oid]
855

    
856
                counters_host = counters[:host][hid]
857

    
858
                rvms        = counters_host[:rvms].size
859
                cpu_usage   = (counters_host[:cpu]*100).to_i
860
                mem_usage   = counters_host[:memory]*1024
861

    
862
                # rewrite running_vms
863
                host_doc.root.each_element("HOST_SHARE/RUNNING_VMS") {|e|
864
                    if e.text != rvms.to_s
865
                        log_error(
866
                            "Host #{hid} RUNNING_VMS has #{e.text} \tis\t#{rvms}")
867
                        e.text = rvms
868
                    end
869
                }
870

    
871

    
872
                # re-do list of VM IDs 
873
                vms_elem = host_doc.root.elements.delete("VMS")
874

    
875
                vms_new_elem = host_doc.root.add_element("VMS")
876

    
877
                counters_host[:rvms].each do |id|
878
                    id_elem = vms_elem.elements.delete("ID[.=#{id}]")
879

    
880
                    if id_elem.nil?
881
                        log_error(
882
                            "VM #{id} is missing from Host #{hid} VM id list")
883
                    end
884

    
885
                    vms_new_elem.add_element("ID").text = id.to_s
886
                end
887

    
888
                vms_elem.each_element("ID") do |id_elem|
889
                    log_error(
890
                        "VM #{id_elem.text} is in Host #{hid} VM id list, "<<
891
                        "but it should not")
892
                end
893

    
894

    
895
                # rewrite cpu
896
                host_doc.root.each_element("HOST_SHARE/CPU_USAGE") {|e|
897
                    if e.text != cpu_usage.to_s
898
                        log_error(
899
                            "Host #{hid} CPU_USAGE has #{e.text} "<<
900
                            "\tis\t#{cpu_usage}")
901
                        e.text = cpu_usage
902
                    end
903
                }
904

    
905
                # rewrite memory
906
                host_doc.root.each_element("HOST_SHARE/MEM_USAGE") {|e|
907
                    if e.text != mem_usage.to_s
908
                        log_error("Host #{hid} MEM_USAGE has #{e.text} "<<
909
                            "\tis\t#{mem_usage}")
910
                        e.text = mem_usage
911
                    end
912
                }
913

    
914
                row[:body] = host_doc.to_s
915

    
916
                # commit
917
                @db[:host_pool_new].insert(row)
918
            end
919
        end
920

    
921
        # Rename table
922
        @db.run("DROP TABLE host_pool")
923
        @db.run("ALTER TABLE host_pool_new RENAME TO host_pool")
924

    
925
        log_time()
926

    
927
        ########################################################################
928
        # Image
929
        #
930
        # IMAGE/RUNNING_VMS
931
        # IMAGE/VMS/ID
932
        #
933
        # IMAGE/CLONING_OPS
934
        # IMAGE/CLONES/ID
935
        #
936
        # IMAGE/CLONING_ID
937
        #
938
        # IMAGE/STATE
939
        ########################################################################
940

    
941
        # Create a new empty table where we will store the new calculated values
942
        @db.run "CREATE TABLE image_pool_new (oid INTEGER PRIMARY KEY, name VARCHAR(128), body MEDIUMTEXT, uid INTEGER, gid INTEGER, owner_u INTEGER, group_u INTEGER, other_u INTEGER, UNIQUE(name,uid) );"
943

    
944
        @db.transaction do
945
            @db[:image_pool].each do |row|
946
                doc = Document.new(row[:body])
947

    
948
                oid = row[:oid]
949

    
950
                persistent = ( doc.root.get_text('PERSISTENT').to_s == "1" )
951
                current_state = doc.root.get_text('STATE').to_s.to_i
952

    
953
                rvms            = counters[:image][oid][:vms].size
954
                n_cloning_ops   = counters[:image][oid][:clones].size
955

    
956
                # rewrite running_vms
957
                doc.root.each_element("RUNNING_VMS") {|e|
958
                    if e.text != rvms.to_s
959
                        log_error("Image #{oid} RUNNING_VMS has #{e.text} \tis\t#{rvms}")
960
                        e.text = rvms
961
                    end
962
                }
963

    
964
                # re-do list of VM IDs 
965
                vms_elem = doc.root.elements.delete("VMS")
966

    
967
                vms_new_elem = doc.root.add_element("VMS")
968

    
969
                counters[:image][oid][:vms].each do |id|
970
                    id_elem = vms_elem.elements.delete("ID[.=#{id}]")
971

    
972
                    if id_elem.nil?
973
                        log_error("VM #{id} is missing from Image #{oid} VM id list")
974
                    end
975

    
976
                    vms_new_elem.add_element("ID").text = id.to_s
977
                end
978

    
979
                vms_elem.each_element("ID") do |id_elem|
980
                    log_error("VM #{id_elem.text} is in Image #{oid} VM id list, but it should not")
981
                end
982

    
983

    
984
                if ( persistent && rvms > 0 )
985
                    n_cloning_ops = 0
986
                    counters[:image][oid][:clones] = Set.new
987
                end
988

    
989
                # Check number of clones
990
                doc.root.each_element("CLONING_OPS") { |e|
991
                    if e.text != n_cloning_ops.to_s
992
                        log_error("Image #{oid} CLONING_OPS has #{e.text} \tis\t#{n_cloning_ops}")
993
                        e.text = n_cloning_ops
994
                    end
995
                }
996

    
997
                # re-do list of Images cloning this one
998
                clones_elem = doc.root.elements.delete("CLONES")
999

    
1000
                clones_new_elem = doc.root.add_element("CLONES")
1001

    
1002
                counters[:image][oid][:clones].each do |id|
1003
                    id_elem = clones_elem.elements.delete("ID[.=#{id}]")
1004

    
1005
                    if id_elem.nil?
1006
                        log_error("Image #{id} is missing from Image #{oid} CLONES id list")
1007
                    end
1008

    
1009
                    clones_new_elem.add_element("ID").text = id.to_s
1010
                end
1011

    
1012
                clones_elem.each_element("ID") do |id_elem|
1013
                    log_error("Image #{id_elem.text} is in Image #{oid} CLONES id list, but it should not")
1014
                end
1015

    
1016

    
1017
                # Check state
1018

    
1019
                state = current_state
1020

    
1021
                if persistent
1022
                    if ( rvms > 0 )
1023
                        state = 8   # USED_PERS
1024
                    elsif ( n_cloning_ops > 0 )
1025
                        state = 6   # CLONE
1026
                    elsif ( current_state == 8 || current_state == 6 )
1027
                        # rvms == 0 && n_cloning_ops == 0, but image is in state
1028
                        # USED_PERS or CLONE
1029

    
1030
                        state = 1   # READY
1031
                    end
1032
                else
1033
                    if ( rvms > 0 || n_cloning_ops > 0 )
1034
                        state = 2   # USED
1035
                    elsif ( current_state == 2 )
1036
                        # rvms == 0 && n_cloning_ops == 0, but image is in state
1037
                        # USED
1038

    
1039
                        state = 1   # READY
1040
                    end
1041
                end
1042

    
1043
                doc.root.each_element("STATE") { |e|
1044
                    if e.text != state.to_s
1045
                        log_error("Image #{oid} has STATE #{IMAGE_STATES[e.text.to_i]} \tis\t#{IMAGE_STATES[state]}")
1046
                        e.text = state
1047
                    end
1048
                }
1049

    
1050
                row[:body] = doc.to_s
1051

    
1052
                # commit
1053
                @db[:image_pool_new].insert(row)
1054
            end
1055
        end
1056

    
1057
        # Rename table
1058
        @db.run("DROP TABLE image_pool")
1059
        @db.run("ALTER TABLE image_pool_new RENAME TO image_pool")
1060

    
1061
        log_time()
1062

    
1063
        ########################################################################
1064
        # VNet
1065
        #
1066
        # LEASES
1067
        ########################################################################
1068

    
1069
        @db.run "CREATE TABLE leases_new (oid INTEGER, ip BIGINT, body MEDIUMTEXT, PRIMARY KEY(oid,ip));"
1070

    
1071
        @db.transaction do
1072
            @db[:leases].each do |row|
1073
                doc = Nokogiri::XML(row[:body])
1074

    
1075
                used = (doc.root.at_xpath('USED').text == "1")
1076
                vid  = doc.root.at_xpath('VID').text.to_i
1077

    
1078
                ip_str = IPAddr.new(row[:ip], Socket::AF_INET).to_s
1079

    
1080
                vnet_structure = counters[:vnet][row[:oid]]
1081

    
1082
                if vnet_structure.nil?
1083
                    log_error("Table leases contains the lease #{ip_str} "<<
1084
                        "for VNet #{row[:oid]}, but it does not exit")
1085

    
1086
                    next
1087
                end
1088

    
1089
                ranged = vnet_structure[:type] == 0
1090

    
1091
                counter_mac, counter_used, counter_vid =
1092
                    vnet_structure[:leases][ip_str]
1093

    
1094
                vnet_structure[:leases].delete(ip_str)
1095

    
1096
                insert = true
1097

    
1098
                if used && (vid != -1) # Lease used by a VM
1099
                    if counter_mac.nil?
1100
                        log_error(
1101
                            "VNet #{row[:oid]} has used lease #{ip_str} "<<
1102
                            "(VM #{vid}) \tbut it is free")
1103

    
1104
                        if ranged
1105
                            insert = false
1106
                        end
1107

    
1108
                        doc.root.at_xpath("USED").content = "0"
1109

    
1110
                        doc.root.at_xpath("VID").content = "-1"
1111

    
1112
                        row[:body] = doc.root.to_s
1113

    
1114
                    elsif vid != counter_vid
1115
                        log_error(
1116
                            "VNet #{row[:oid]} has used lease #{ip_str} "<<
1117
                            "(VM #{vid}) \tbut it used by VM #{counter_vid}")
1118

    
1119
                        doc.root.at_xpath("VID").content = counter_vid.to_s
1120

    
1121
                        row[:body] = doc.root.to_s
1122
                    end
1123
                else # Lease is free or on hold (used=1, vid=-1)
1124
                    if !counter_mac.nil?
1125
                        if used
1126
                            log_error(
1127
                                "VNet #{row[:oid]} has lease on hold #{ip_str} "<<
1128
                                "\tbut it is used by VM #{counter_vid}")
1129
                        else
1130
                            log_error(
1131
                                "VNet #{row[:oid]} has free lease #{ip_str} "<<
1132
                                "\tbut it is used by VM #{counter_vid}")
1133
                        end
1134

    
1135
                        doc.root.at_xpath("USED").content = "1"
1136

    
1137
                        doc.root.at_xpath("VID").content = counter_vid.to_s
1138

    
1139
                        row[:body] = doc.root.to_s
1140
                    end
1141
                end
1142

    
1143
                if (doc.root.at_xpath('USED').text == "1")
1144
                    vnet_structure[:total_leases] += 1
1145
                end
1146

    
1147
                # commit
1148
                @db[:leases_new].insert(row) if insert
1149
            end
1150
        end
1151

    
1152
        log_time()
1153

    
1154
        # Now insert all the leases left in the hash, i.e. used by a VM in
1155
        # vm_pool, but not in the leases table. This will only happen in
1156
        # ranged networks
1157
        @db.transaction do
1158
            counters[:vnet].each do |net_id,vnet_structure|
1159
                vnet_structure[:leases].each do |ip,array|
1160
                    mac,used,vid = array
1161

    
1162
                    ip_i = IPAddr.new(ip, Socket::AF_INET).to_i
1163

    
1164
                    # TODO: MAC_PREFIX is now hardcoded to "02:00"
1165
                    body = "<LEASE><IP>#{ip_i}</IP><MAC_PREFIX>512</MAC_PREFIX><MAC_SUFFIX>#{ip_i}</MAC_SUFFIX><USED>#{used}</USED><VID>#{vid}</VID></LEASE>"
1166

    
1167
                    log_error("VNet #{net_id} has free lease #{ip} \tbut it is used by VM #{vid}")
1168

    
1169
                    vnet_structure[:total_leases] += 1
1170

    
1171
                    @db[:leases_new].insert(
1172
                        :oid        => net_id,
1173
                        :ip         => ip_i,
1174
                        :body       => body)
1175
                end
1176
            end
1177
        end
1178

    
1179

    
1180
        # Rename table
1181
        @db.run("DROP TABLE leases")
1182
        @db.run("ALTER TABLE leases_new RENAME TO leases")
1183

    
1184
        log_time()
1185

    
1186
        ########################################################################
1187
        # VNet
1188
        #
1189
        # VNET/TOTAL_LEASES
1190
        ########################################################################
1191

    
1192
        # Create a new empty table where we will store the new calculated values
1193
        @db.run "CREATE TABLE network_pool_new (oid INTEGER PRIMARY KEY, name VARCHAR(128), body MEDIUMTEXT, uid INTEGER, gid INTEGER, owner_u INTEGER, group_u INTEGER, other_u INTEGER, cid INTEGER, UNIQUE(name,uid));"
1194

    
1195
        @db.transaction do
1196
            @db[:network_pool].each do |row|
1197
                doc = Document.new(row[:body])
1198

    
1199
                oid = row[:oid]
1200

    
1201
                total_leases = counters[:vnet][oid][:total_leases]
1202

    
1203
                # rewrite running_vms
1204
                doc.root.each_element("TOTAL_LEASES") {|e|
1205
                    if e.text != total_leases.to_s
1206
                        log_error("VNet #{oid} TOTAL_LEASES has #{e.text} \tis\t#{total_leases}")
1207
                        e.text = total_leases
1208
                    end
1209
                }
1210

    
1211
                row[:body] = doc.to_s
1212

    
1213
                # commit
1214
                @db[:network_pool_new].insert(row)
1215
            end
1216
        end
1217

    
1218
        # Rename table
1219
        @db.run("DROP TABLE network_pool")
1220
        @db.run("ALTER TABLE network_pool_new RENAME TO network_pool")
1221

    
1222
        log_time()
1223

    
1224
        ########################################################################
1225
        # Users
1226
        #
1227
        # USER QUOTAS
1228
        ########################################################################
1229

    
1230
        @db.run "ALTER TABLE user_pool RENAME TO old_user_pool;"
1231
        @db.run "CREATE TABLE user_pool (oid INTEGER PRIMARY KEY, name VARCHAR(128), body MEDIUMTEXT, uid INTEGER, gid INTEGER, owner_u INTEGER, group_u INTEGER, other_u INTEGER, UNIQUE(name));"
1232

    
1233
        @db.transaction do
1234
            # oneadmin does not have quotas
1235
            @db.fetch("SELECT * FROM old_user_pool WHERE oid=0") do |row|
1236
                @db[:user_pool].insert(row)
1237
            end
1238

    
1239
            @db.fetch("SELECT * FROM old_user_pool WHERE oid>0") do |row|
1240
                doc = Nokogiri::XML(row[:body])
1241

    
1242
                calculate_quotas(doc, "uid=#{row[:oid]}", "User")
1243

    
1244
                @db[:user_pool].insert(
1245
                    :oid        => row[:oid],
1246
                    :name       => row[:name],
1247
                    :body       => doc.root.to_s,
1248
                    :uid        => row[:oid],
1249
                    :gid        => row[:gid],
1250
                    :owner_u    => row[:owner_u],
1251
                    :group_u    => row[:group_u],
1252
                    :other_u    => row[:other_u])
1253
            end
1254
        end
1255

    
1256
        @db.run "DROP TABLE old_user_pool;"
1257

    
1258
        log_time()
1259

    
1260
        ########################################################################
1261
        # Groups
1262
        #
1263
        # GROUP QUOTAS
1264
        ########################################################################
1265

    
1266
        @db.run "ALTER TABLE group_pool RENAME TO old_group_pool;"
1267
        @db.run "CREATE TABLE group_pool (oid INTEGER PRIMARY KEY, name VARCHAR(128), body MEDIUMTEXT, uid INTEGER, gid INTEGER, owner_u INTEGER, group_u INTEGER, other_u INTEGER, UNIQUE(name));"
1268

    
1269
        @db.transaction do
1270
            # oneadmin group does not have quotas
1271
            @db.fetch("SELECT * FROM old_group_pool WHERE oid=0") do |row|
1272
                @db[:group_pool].insert(row)
1273
            end
1274

    
1275
            @db.fetch("SELECT * FROM old_group_pool WHERE oid>0") do |row|
1276
                doc = Nokogiri::XML(row[:body])
1277

    
1278
                calculate_quotas(doc, "gid=#{row[:oid]}", "Group")
1279

    
1280
                @db[:group_pool].insert(
1281
                    :oid        => row[:oid],
1282
                    :name       => row[:name],
1283
                    :body       => doc.root.to_s,
1284
                    :uid        => row[:oid],
1285
                    :gid        => row[:gid],
1286
                    :owner_u    => row[:owner_u],
1287
                    :group_u    => row[:group_u],
1288
                    :other_u    => row[:other_u])
1289
            end
1290
        end
1291

    
1292
        @db.run "DROP TABLE old_group_pool;"
1293

    
1294
        log_time()
1295

    
1296
        log_total_errors()
1297

    
1298
        return true
1299
    end
1300

    
1301
    def log_error(message)
1302
        @errors += 1
1303
        puts message
1304
    end
1305

    
1306
    def log_total_errors()
1307
        puts
1308
        puts "Total errors found: #{@errors}"
1309
    end
1310

    
1311

    
1312

    
1313
    def calculate_quotas(doc, where_filter, resource)
1314

    
1315
        oid = doc.root.at_xpath("ID").text.to_i
1316

    
1317
        # VM quotas
1318
        cpu_used = 0
1319
        mem_used = 0
1320
        vms_used = 0
1321
        vol_used = 0
1322

    
1323
        # VNet quotas
1324
        vnet_usage = {}
1325

    
1326
        # Image quotas
1327
        img_usage = {}
1328

    
1329
        @db.fetch("SELECT body FROM vm_pool WHERE #{where_filter} AND state<>6") do |vm_row|
1330
            vmdoc = Nokogiri::XML(vm_row[:body])
1331

    
1332
            # VM quotas
1333
            vmdoc.root.xpath("TEMPLATE/CPU").each { |e|
1334
                # truncate to 2 decimals
1335
                cpu = (e.text.to_f * 100).to_i
1336
                cpu_used += cpu
1337
            }
1338

    
1339
            vmdoc.root.xpath("TEMPLATE/MEMORY").each { |e|
1340
                mem_used += e.text.to_i
1341
            }
1342

    
1343
            vmdoc.root.xpath("TEMPLATE/DISK").each { |e|
1344
                type = ""
1345

    
1346
                e.xpath("TYPE").each { |t_elem|
1347
                    type = t_elem.text.upcase
1348
                }
1349

    
1350
                if ( type == "SWAP" || type == "FS")
1351
                    e.xpath("SIZE").each { |size_elem|
1352
                        vol_used += size_elem.text.to_i
1353
                    }
1354
                end
1355
            }
1356

    
1357
            vms_used += 1
1358

    
1359
            # VNet quotas
1360
            vmdoc.root.xpath("TEMPLATE/NIC/NETWORK_ID").each { |e|
1361
                vnet_usage[e.text] = 0 if vnet_usage[e.text].nil?
1362
                vnet_usage[e.text] += 1
1363
            }
1364

    
1365
            # Image quotas
1366
            vmdoc.root.xpath("TEMPLATE/DISK/IMAGE_ID").each { |e|
1367
                img_usage[e.text] = 0 if img_usage[e.text].nil?
1368
                img_usage[e.text] += 1
1369
            }
1370
        end
1371

    
1372

    
1373
        # VM quotas
1374

    
1375
        vm_elem = nil
1376
        doc.root.xpath("VM_QUOTA/VM").each { |e| vm_elem = e }
1377

    
1378
        if vm_elem.nil?
1379
            doc.root.xpath("VM_QUOTA").each { |e| e.remove }
1380

    
1381
            vm_quota  = doc.root.add_child(doc.create_element("VM_QUOTA"))
1382
            vm_elem   = vm_quota.add_child(doc.create_element("VM"))
1383

    
1384
            vm_elem.add_child(doc.create_element("CPU")).content         = "-1"
1385
            vm_elem.add_child(doc.create_element("CPU_USED")).content    = "0"
1386

    
1387
            vm_elem.add_child(doc.create_element("MEMORY")).content      = "-1"
1388
            vm_elem.add_child(doc.create_element("MEMORY_USED")).content = "0"
1389

    
1390
            vm_elem.add_child(doc.create_element("VMS")).content         = "-1"
1391
            vm_elem.add_child(doc.create_element("VMS_USED")).content    = "0"
1392

    
1393
            vm_elem.add_child(doc.create_element("VOLATILE_SIZE")).content       = "-1"
1394
            vm_elem.add_child(doc.create_element("VOLATILE_SIZE_USED")).content  = "0"
1395
        end
1396

    
1397

    
1398
        vm_elem.xpath("CPU_USED").each { |e|
1399

    
1400
            # Because of bug http://dev.opennebula.org/issues/1567 the element
1401
            # may contain a float number in scientific notation.
1402

    
1403
            # Check if the float value or the string representation mismatch,
1404
            # but ignoring the precision
1405

    
1406
            cpu_used = (cpu_used / 100.0)
1407

    
1408
            different = ( e.text.to_f != cpu_used ||
1409
                ![sprintf('%.2f', cpu_used), sprintf('%.1f', cpu_used), sprintf('%.0f', cpu_used)].include?(e.text)  )
1410

    
1411
            cpu_used_str = sprintf('%.2f', cpu_used)
1412

    
1413
            if different
1414
                log_error("#{resource} #{oid} quotas: CPU_USED has #{e.text} \tis\t#{cpu_used_str}")
1415
                e.content = cpu_used_str
1416
            end
1417
        }
1418

    
1419
        vm_elem.xpath("MEMORY_USED").each { |e|
1420
            if e.text != mem_used.to_s
1421
                log_error("#{resource} #{oid} quotas: MEMORY_USED has #{e.text} \tis\t#{mem_used}")
1422
                e.content = mem_used.to_s
1423
            end
1424
        }
1425

    
1426
        vm_elem.xpath("VMS_USED").each { |e|
1427
            if e.text != vms_used.to_s
1428
                log_error("#{resource} #{oid} quotas: VMS_USED has #{e.text} \tis\t#{vms_used}")
1429
                e.content = vms_used.to_s
1430
            end
1431
        }
1432

    
1433
        vm_elem.xpath("VOLATILE_SIZE_USED").each { |e|
1434
            if e.text != vol_used.to_s
1435
                log_error("#{resource} #{oid} quotas: VOLATILE_SIZE_USED has #{e.text} \tis\t#{vol_used}")
1436
                e.content = vol_used.to_s
1437
            end
1438
        }
1439

    
1440
        # VNet quotas
1441

    
1442
        net_quota = nil
1443
        doc.root.xpath("NETWORK_QUOTA").each { |e| net_quota = e }
1444

    
1445
        if net_quota.nil?
1446
            net_quota = doc.root.add_child(doc.create_element("NETWORK_QUOTA"))
1447
        end
1448

    
1449
        net_quota.xpath("NETWORK").each { |net_elem|
1450
            vnet_id = net_elem.at_xpath("ID").text
1451

    
1452
            leases_used = vnet_usage.delete(vnet_id)
1453

    
1454
            leases_used = 0 if leases_used.nil?
1455

    
1456
            net_elem.xpath("LEASES_USED").each { |e|
1457
                if e.text != leases_used.to_s
1458
                    log_error("#{resource} #{oid} quotas: VNet #{vnet_id}\tLEASES_USED has #{e.text} \tis\t#{leases_used}")
1459
                    e.content = leases_used.to_s
1460
                end
1461
            }
1462
        }
1463

    
1464
        vnet_usage.each { |vnet_id, leases_used|
1465
            log_error("#{resource} #{oid} quotas: VNet #{vnet_id}\tLEASES_USED has 0 \tis\t#{leases_used}")
1466

    
1467
            new_elem = net_quota.add_child(doc.create_element("NETWORK"))
1468

    
1469
            new_elem.add_child(doc.create_element("ID")).content = vnet_id
1470
            new_elem.add_child(doc.create_element("LEASES")).content = "-1"
1471
            new_elem.add_child(doc.create_element("LEASES_USED")).content = leases_used.to_s
1472
        }
1473

    
1474

    
1475
        # Image quotas
1476

    
1477
        img_quota = nil
1478
        doc.root.xpath("IMAGE_QUOTA").each { |e| img_quota = e }
1479

    
1480
        if img_quota.nil?
1481
            img_quota = doc.root.add_child(doc.create_element("IMAGE_QUOTA"))
1482
        end
1483

    
1484
        img_quota.xpath("IMAGE").each { |img_elem|
1485
            img_id = img_elem.at_xpath("ID").text
1486

    
1487
            rvms = img_usage.delete(img_id)
1488

    
1489
            rvms = 0 if rvms.nil?
1490

    
1491
            img_elem.xpath("RVMS_USED").each { |e|
1492
                if e.text != rvms.to_s
1493
                    log_error("#{resource} #{oid} quotas: Image #{img_id}\tRVMS has #{e.text} \tis\t#{rvms}")
1494
                    e.content = rvms.to_s
1495
                end
1496
            }
1497
        }
1498

    
1499
        img_usage.each { |img_id, rvms|
1500
            log_error("#{resource} #{oid} quotas: Image #{img_id}\tRVMS has 0 \tis\t#{rvms}")
1501

    
1502
            new_elem = img_quota.add_child(doc.create_element("IMAGE"))
1503

    
1504
            new_elem.add_child(doc.create_element("ID")).content = img_id
1505
            new_elem.add_child(doc.create_element("RVMS")).content = "-1"
1506
            new_elem.add_child(doc.create_element("RVMS_USED")).content = rvms.to_s
1507
        }
1508

    
1509

    
1510
        # Datastore quotas
1511

    
1512
        ds_usage = {}
1513

    
1514
        @db.fetch("SELECT body FROM image_pool WHERE #{where_filter}") do |img_row|
1515
            img_doc = Nokogiri::XML(img_row[:body])
1516

    
1517
            img_doc.root.xpath("DATASTORE_ID").each { |e|
1518
                ds_usage[e.text] = [0,0] if ds_usage[e.text].nil?
1519
                ds_usage[e.text][0] += 1
1520

    
1521
                img_doc.root.xpath("SIZE").each { |size|
1522
                    ds_usage[e.text][1] += size.text.to_i
1523
                }
1524
            }
1525
        end
1526

    
1527
        ds_quota = nil
1528
        doc.root.xpath("DATASTORE_QUOTA").each { |e| ds_quota = e }
1529

    
1530
        if ds_quota.nil?
1531
            ds_quota = doc.root.add_child(doc.create_element("DATASTORE_QUOTA"))
1532
        end
1533

    
1534
        ds_quota.xpath("DATASTORE").each { |ds_elem|
1535
            ds_id = ds_elem.at_xpath("ID").text
1536

    
1537
            images_used,size_used = ds_usage.delete(ds_id)
1538

    
1539
            images_used = 0 if images_used.nil?
1540
            size_used   = 0 if size_used.nil?
1541

    
1542
            ds_elem.xpath("IMAGES_USED").each { |e|
1543
                if e.text != images_used.to_s
1544
                    log_error("#{resource} #{oid} quotas: Datastore #{ds_id}\tIMAGES_USED has #{e.text} \tis\t#{images_used}")
1545
                    e.content = images_used.to_s
1546
                end
1547
            }
1548

    
1549
            ds_elem.xpath("SIZE_USED").each { |e|
1550
                if e.text != size_used.to_s
1551
                    log_error("#{resource} #{oid} quotas: Datastore #{ds_id}\tSIZE_USED has #{e.text} \tis\t#{size_used}")
1552
                    e.content = size_used.to_s
1553
                end
1554
            }
1555
        }
1556

    
1557
        ds_usage.each { |ds_id, array|
1558
            images_used,size_used = array
1559

    
1560
            log_error("#{resource} #{oid} quotas: Datastore #{ds_id}\tIMAGES_USED has 0 \tis\t#{images_used}")
1561
            log_error("#{resource} #{oid} quotas: Datastore #{ds_id}\tSIZE_USED has 0 \tis\t#{size_used}")
1562

    
1563
            new_elem = ds_quota.add_child(doc.create_element("DATASTORE"))
1564

    
1565
            new_elem.add_child(doc.create_element("ID")).content = ds_id
1566

    
1567
            new_elem.add_child(doc.create_element("IMAGES")).content = "-1"
1568
            new_elem.add_child(doc.create_element("IMAGES_USED")).content = images_used.to_s
1569

    
1570
            new_elem.add_child(doc.create_element("SIZE")).content = "-1"
1571
            new_elem.add_child(doc.create_element("SIZE_USED")).content = size_used.to_s
1572
        }
1573
    end
1574
end