Revision df6b93e6

View differences:

install.sh
203 203
          $LIB_LOCATION/mads \
204 204
          $LIB_LOCATION/sh \
205 205
          $LIB_LOCATION/ruby/cli \
206
          $LIB_LOCATION/ruby/cli/one_helper \
207
          $LIB_LOCATION/ruby/acct"
206
          $LIB_LOCATION/ruby/cli/one_helper"
208 207

  
209 208
VAR_DIRS="$VAR_LOCATION/remotes \
210 209
          $VAR_LOCATION/remotes/im \
......
423 422
    MAN_FILES:$MAN_LOCATION
424 423
    CLI_LIB_FILES:$LIB_LOCATION/ruby/cli
425 424
    ONE_CLI_LIB_FILES:$LIB_LOCATION/ruby/cli/one_helper
426
    ACCT_LIB_FILES:$LIB_LOCATION/ruby/acct
427
    ACCT_BIN_FILES:$BIN_LOCATION
428 425
)
429 426

  
430 427
INSTALL_CLIENT_FILES=(
......
553 550
    OCCI_ETC_FILES:$ETC_LOCATION
554 551
    OCCI_ETC_TEMPLATE_FILES:$ETC_LOCATION/occi_templates
555 552
    CLI_CONF_FILES:$ETC_LOCATION/cli
556
    ACCT_ETC_FILES:$ETC_LOCATION
557 553
)
558 554

  
559 555
#-------------------------------------------------------------------------------
......
1471 1467
                                  src/cloud/occi/lib/ui/public/locale/fr_CA/fr_datatable.txt"
1472 1468

  
1473 1469
#-----------------------------------------------------------------------------
1474
# ACCT files
1475
#-----------------------------------------------------------------------------
1476

  
1477
ACCT_BIN_FILES="src/acct/oneacctd"
1478

  
1479
ACCT_LIB_FILES="src/acct/monitoring.rb \
1480
                src/acct/accounting.rb \
1481
                src/acct/acctd.rb \
1482
                src/acct/oneacct.rb \
1483
                src/acct/watch_helper.rb \
1484
                src/acct/watch_client.rb"
1485

  
1486
ACCT_ETC_FILES="src/acct/etc/acctd.conf"
1487

  
1488
#-----------------------------------------------------------------------------
1489 1470
# MAN files
1490 1471
#-----------------------------------------------------------------------------
1491 1472

  
share/install_gems/install_gems
2 2

  
3 3
require 'pp'
4 4

  
5
DEFAULT=%w{optional sunstone quota cloud ozones_server acct auth_ldap}
5
DEFAULT=%w{optional sunstone quota cloud ozones_server auth_ldap}
6 6

  
7 7
if defined?(RUBY_VERSION) && RUBY_VERSION>="1.8.7"
8 8
    SQLITE='sqlite3'
......
22 22
    ],
23 23
    :ozones_server_sqlite => %w{json data_mapper dm-sqlite-adapter}<<SQLITE,
24 24
    :ozones_server_mysql => %w{json data_mapper dm-mysql-adapter mysql},
25
    :acct => ['sequel', SQLITE, 'mysql'],
26
    :acct_sqlite => ['sequel', SQLITE],
27
    :acct_mysql => ['sequel', 'mysql'],
28 25
    :auth_ldap => 'net-ldap'
29 26
}
30 27

  
src/acct/accounting.rb
1
# -------------------------------------------------------------------------- #
2
# Copyright 2002-2012, OpenNebula Project Leads (OpenNebula.org)             #
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
module OneWatch
18
    require 'watch_helper'
19

  
20
    class Accounting
21
        def initialize(client)
22
            @client = client
23
            @active_vms = Array.new
24
        end
25

  
26
        def insert(hash)
27
            @ptimestamp = @timestamp
28
            @timestamp  = generate_timestamp
29

  
30
            new_active_vms = Array.new
31
            last_active_vm = @active_vms.empty? ?  -1 : @active_vms.last
32

  
33
            if (vmpool_hash = hash['VM_POOL']) && !vmpool_hash.empty?
34
                [vmpool_hash['VM']].flatten.each { |vm|
35
                    vm_id  = vm['ID'].to_i
36

  
37
                    if vm['STATE'] == 3
38
                        new_active_vms << vm_id
39
                    end
40

  
41
                    # ACTIVE VMs (including those that are stopped in this step)
42
                    # in the last step and NEW VMs
43
                    if @active_vms.include?(vm_id) || vm['STATE'].to_i == 3
44
                        insert_vm(vm)
45
                        @active_vms.delete(vm_id)
46
                    else
47
                        # DONE/STOP VMs and non ACTIVE in the last step
48
                        next
49
                    end
50
                }
51
            end
52

  
53
            # DONE VMs that were ACTIVE in the last step
54
            @active_vms.each { |id|
55
                vm = OpenNebula::VirtualMachine.new_with_id(id, @client)
56
                vm.info
57

  
58
                vm_hash = vm.to_hash
59
                insert_vm(vm_hash)
60
            }
61

  
62
            # DONE VMs that did not exist in the last step
63
            vmpool = OpenNebula::VirtualMachinePool.new(@client)
64
            vmpool.info(-2, last_active_vm, -1, 6)
65
            done_hash = vmpool.to_hash
66
            if (done_vm_hash = done_hash['VM_POOL']) && !done_vm_hash.empty?
67
                [done_vm_hash['VM']].flatten.each { |vm|
68
                    insert_vm(vm)
69
                }
70
            end
71

  
72
            # Upate the active VMs
73
            @active_vms = new_active_vms.sort
74

  
75
            WatchHelper::Vm.flush
76
        end
77

  
78
        private
79

  
80
        def generate_timestamp
81
            Time.now.to_i
82
        end
83

  
84
        def insert_register(vm, register, history)
85
            if register && register.seq == history['SEQ'].to_i
86
                register.update_from_history(history)
87
            else
88
                vm.add_register_from_resource(history)
89
            end
90
        end
91

  
92
        def update_history(vm, vm_sql)
93
            last_register = vm_sql.registers.last
94
            seq = last_register ? last_register.seq : 0
95

  
96
            hr = vm['HISTORY_RECORDS']
97
            if hr and !hr.empty?
98
                if hr['HISTORY']['SEQ'] == seq
99
                    # The VM has not moved from the Host
100
                    insert_register(vm_sql, last_register, hr['HISTORY'])
101
                    return
102
                else
103
                    unless hr['HISTORY'].instance_of?(Array)
104
                        # Get the full HISTORY
105
                        vm = OpenNebula::VirtualMachine.new_with_id(vm['ID'], @client)
106
                        vm.info
107

  
108
                        vm_hash = vm.to_hash['VM']
109
                        hr = vm_hash['HISTORY_RECORDS']
110
                    end
111

  
112
                    # Insert a new entry for each new history record
113
                    [hr['HISTORY']].flatten.each { |history|
114
                        if history['SEQ'].to_i < seq
115
                            next
116
                        else
117
                            insert_register(vm_sql, last_register, history)
118
                        end
119
                    }
120
                end
121
            end
122
        end
123

  
124
        def insert_vm(vm)
125
            vm_sql = WatchHelper::Vm.info(vm)
126
            vm_sql.add_delta_from_resource(vm, @timestamp)
127
            update_history(vm, vm_sql)
128
        end
129
    end
130
end
131

  
src/acct/acctd.rb
1
#!/usr/bin/env ruby
2

  
3
# -------------------------------------------------------------------------- #
4
# Copyright 2002-2012, OpenNebula Project Leads (OpenNebula.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
ONE_LOCATION=ENV["ONE_LOCATION"]
20

  
21
if !ONE_LOCATION
22
    RUBY_LIB_LOCATION="/usr/lib/one/ruby"
23
    ACCTD_CONF="/etc/one/acctd.conf"
24
else
25
    RUBY_LIB_LOCATION=ONE_LOCATION+"/lib/ruby"
26
    ACCTD_CONF=ONE_LOCATION+"/etc/acctd.conf"
27
end
28

  
29
$: << RUBY_LIB_LOCATION
30
$: << RUBY_LIB_LOCATION+"/acct"
31

  
32
require 'yaml'
33

  
34
require 'OpenNebula'
35
require 'watch_helper'
36

  
37
class Watcher
38
    def initialize
39
        @monitors = Array.new
40
    end
41

  
42
    def add(resource, steps, pools)
43
        @monitors <<    {   :resource => resource,
44
                            :steps    => steps,
45
                            :pools    => [pools].flatten
46
                        }
47
    end
48

  
49
    def log(msg)
50
        STDERR.puts "#{Time.now} #{msg}"
51
    end
52

  
53
    def update(step)
54
        # clear pool cache
55
        @pool_cache = Hash.new
56

  
57
        @monitors.each do |monitor|
58
            if monitor[:steps] > 0 and step % monitor[:steps] == 0
59
                monitor[:pools].each do |pool|
60
                    resource = monitor[:resource]
61

  
62
                    log "#{resource.class}"
63

  
64
                    if pool_hash = @pool_cache[pool]
65
                    else
66
                        rc = pool.info
67
                        if OpenNebula.is_error?(rc)
68
                            log "Error: " + rc.message
69
                            log "Shutting down"
70
                            exit 1
71
                        end
72

  
73
                        pool_hash = pool.to_hash
74
                        @pool_cache[pool] = pool_hash
75
                    end
76

  
77
                    resource.insert(pool_hash)
78
                end
79
            end
80
        end
81
    end
82
end
83

  
84
watcher = Watcher.new
85

  
86
# OpenNebula variables
87
one_client  = OpenNebula::Client.new
88
vm_pool     = nil # common for accounting and monitoring
89
host_pool   = nil
90

  
91
# Initialize VM monitoring
92
if vm_steps = WatchHelper::get_config(:VM_MONITORING, :STEPS) and
93
    vm_steps > 0
94

  
95
    require 'monitoring'
96
    vm_monitoring       = OneWatch::VmMonitoring.new
97
    vm_pool     ||= OpenNebula::VirtualMachinePool.new(one_client, -2)
98
    watcher.add(vm_monitoring,   vm_steps, vm_pool)
99
end
100

  
101
# Initialize Host monitoring
102
if host_steps = WatchHelper::get_config(:HOST_MONITORING, :STEPS) and
103
    host_steps > 0
104

  
105
    require 'monitoring'
106
    host_monitoring     = OneWatch::HostMonitoring.new
107
    host_pool   ||= OpenNebula::HostPool.new(one_client)
108
    watcher.add(host_monitoring, host_steps, host_pool)
109
end
110

  
111
# Initialize accounting
112
if accounting_steps = WatchHelper::get_config(:ACCOUNTING, :STEPS) and
113
    accounting_steps > 0
114

  
115
    require 'accounting'
116
    accounting  = OneWatch::Accounting.new(one_client)
117
    vm_pool     ||= OpenNebula::VirtualMachinePool.new(one_client, -2)
118
    watcher.add(accounting, accounting_steps, vm_pool)
119
end
120

  
121
step_time = WatchHelper::get_config(:STEP)
122

  
123
step = 0
124
loop do
125
    start_time  = Time.now
126
    expected_end_time = start_time + step_time
127

  
128
    step += 1
129
    watcher.update(step)
130

  
131
    end_time  = Time.now
132
    sleep_time = start_time + step_time - end_time
133

  
134
    if sleep_time >= 1
135
        sleep sleep_time
136
    else
137
        sleep 1
138
    end
139
end
src/acct/etc/acctd.conf
1
# -------------------------------------------------------------------------- #
2
# Copyright 2002-2012, OpenNebula Project Leads (OpenNebula.org)             #
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
# Database URI
18
#:DB: sqlite:///var/one/oneacct.db
19

  
20
# Duration of each daemon loop in seconds
21
:STEP: 300 # 5 minutes
22

  
23
#-------------------------------------------------------------------------------
24
# VM Monitoring
25
#-------------------------------------------------------------------------------
26

  
27
:VM_MONITORING:
28

  
29
    # Number of daemon loops until a VM monitoring watch
30
    :STEPS: 1
31

  
32
    # Number of VM records to preserve
33
    :WINDOW_SIZE: 5
34

  
35
#-------------------------------------------------------------------------------
36
# HOST Monitoring
37
#-------------------------------------------------------------------------------
38

  
39
:HOST_MONITORING:
40

  
41
    # Number of daemon loops until a Hosts monitoring watch
42
    :STEPS: 3
43

  
44
    # Number of HOST records to preserve
45
    :WINDOW_SIZE: 5
46

  
47
#-------------------------------------------------------------------------------
48
# Accounting
49
#-------------------------------------------------------------------------------
50

  
51
:ACCOUNTING:
52

  
53
    # Number of daemon loops until an accounting watch
54
    :STEPS: 10
55

  
src/acct/examples/acct_client.rb
1
# -------------------------------------------------------------------------- #
2
# Copyright 2002-2012, OpenNebula Project Leads (OpenNebula.org)             #
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 'watch_helper'
18

  
19
class AcctClient
20
    def prolog_time(t1, t2, opts={})
21
        times(t1, t2, opts) { |reg|
22
            calculate_time(t1, t2, reg.pstime, reg.petime)
23
        }
24
    end
25

  
26
    def running_time(t1, t2, opts={})
27
        # TBD Suspened VMs
28

  
29
        times(t1, t2, opts) { |reg|
30
            calculate_time(t1, t2, reg.rstime, reg.retime)
31
        }
32
    end
33

  
34
    def epilog_time(t1, t2, opts={})
35
        times(t1, t2, opts) { |reg|
36
            calculate_time(t1, t2, reg.estime, reg.eetime)
37
        }
38
    end
39

  
40
    private
41

  
42
    def times(t1, t2, opts={}, &block)
43
        time = 0
44

  
45
        vms = filter_vms(opts)
46

  
47
        if vms && !vms.empty?
48
            vms.each { |vm|
49
                vm.registers.each { |reg|
50
                    time += block.call(reg)
51
                }
52
            }
53
        end
54

  
55
        time
56
    end
57

  
58
    def calculate_time(t1, t2, stime, etime)
59
        if etime < t1 && etime != 0
60
            return 0
61
        elsif stime < t2 && stime != 0
62
            if etime < t2 && etime != 0
63
                e = etime
64
            else
65
                e = t2
66
            end
67

  
68
            s = stime > t1 ? stime : t1
69
            return e - s
70
        end
71

  
72
        return 0
73
    end
74

  
75
    def filter_vms(opts={})
76
        opts ||= {}
77

  
78
        if opts[:uid]
79
            vms = WatchHelper::Vm.filter(:uid=>opts[:uid])
80
        elsif opts[:gid]
81
            vms = WatchHelper::Vm.filter(:gid=>opts[:gid])
82
        elsif opts[:hid]
83
            vms = WatchHelper::Vm.filter(
84
                    :registers=>WatchHelper::Register.filter(:hid => opts[:hid]))
85
        elsif opts[:vmid]
86
            vms = WatchHelper::Vm.filter(:id=>opts[:vmid])
87
        else
88
            vms = WatchHelper::Vm
89
        end
90
    end
91
end
src/acct/monitoring.rb
1
# -------------------------------------------------------------------------- #
2
# Copyright 2002-2012, OpenNebula Project Leads (OpenNebula.org)             #
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
module OneWatch
18
    require 'watch_helper'
19

  
20
    class Monitoring
21
        def insert(hash)
22
            timestamp = generate_timestamp
23

  
24
            if (pool_hash = hash["#{resource}_POOL"]) && !pool_hash.empty?
25
                [pool_hash["#{resource}"]].flatten.each { |elem|
26
                    sql = sql_elem(elem)
27
                    sql.add_sample_from_resource(elem, timestamp)
28
                }
29
            end
30

  
31
            sql_elem.flush
32
        end
33

  
34
        private
35

  
36
        def generate_timestamp
37
            Time.now.to_i
38
        end
39
    end
40

  
41
    class VmMonitoring < Monitoring
42
        def resource
43
            'VM'
44
        end
45

  
46
        def sql_elem(elem=nil)
47
            if elem
48
                WatchHelper::Vm.info(elem)
49
            else
50
                WatchHelper::Vm
51
            end
52
        end
53

  
54
        def generate_timestamp
55
            ts = super
56
            WatchHelper::VmTimestamp.find_or_create(:id=>ts)
57
        end
58
    end
59

  
60
    class HostMonitoring < Monitoring
61
        def resource
62
            'HOST'
63
        end
64

  
65
        def sql_elem(elem=nil)
66
            if elem
67
                WatchHelper::Host.info(elem)
68
            else
69
                WatchHelper::Host
70
            end
71
        end
72

  
73
        def generate_timestamp
74
            ts = super
75
            WatchHelper::HostTimestamp.find_or_create(:id=>ts)
76
        end
77
    end
78
end
src/acct/oneacct.rb
1
# --------------------------------------------------------------------------
2
# Copyright 2010-2012, C12G Labs S.L.
3
#
4
# This file is part of OpenNebula addons.
5
#
6
# OpenNebula addons are free software: you can redistribute it
7
# and/or modify it under the terms of the GNU General Public License
8
# as published by the Free Software Foundation, either version 3 of
9
# the License, or the hope That it will be useful, but (at your
10
# option) any later version.
11
#
12
# OpenNebula addons are distributed in WITHOUT ANY WARRANTY;
13
# without even the implied warranty of MERCHANTABILITY or FITNESS FOR
14
# A PARTICULAR PURPOSE.  See the GNU General Public License for more
15
# details.
16
#
17
# You should have received a copy of the GNU General Public License
18
# along with OpenNebula addons. If not, see
19
# <http://www.gnu.org/licenses/>
20
# --------------------------------------------------------------------------
21

  
22

  
23

  
24
require 'acct/watch_helper'
25

  
26
class AcctClient
27
    def initialize(filters={})
28
        @filters=filters
29
        @deltas=[]
30
        @users={}
31
    end
32

  
33
    def account(time_start=nil, time_end=nil, user_id=nil)
34
        @filters[:start]=time_start if time_start
35
        @filters[:end]=time_end if time_end
36
        @filters[:user]=user_id if user_id
37

  
38
        get_users_consumption
39

  
40
        @users
41
    end
42

  
43
private
44

  
45
    def get_users_consumption
46
        # Get all the deltas that match the filters
47
        @deltas=calculate_deltas.map {|q| q.values }
48

  
49
        @users=slices_by_user
50

  
51
        user_slices_and_deltas_to_vms
52
    end
53

  
54
    def slices_by_user
55
        # Get all VM slices that match the filters
56
        query=get_vm_slices(@filters)
57

  
58
        # This hash will hold the users with the resources consumed
59
        users={}
60

  
61
        query.each do |reg|
62
            vm=reg.vm
63
            uid=vm.uid.to_i
64

  
65
            # Create a new user register if it still does not exist
66
            user=users[uid]||={
67
                :vm_slices => [],
68
            }
69

  
70
            user[:vm_slices] << reg.values
71
        end
72

  
73
        users
74
    end
75

  
76
    def user_slices_and_deltas_to_vms
77
        @users.each do |user, data|
78
            # Get the VM ids array for this user
79
            vms=data[:vm_slices].map {|vm| vm[:id] }.sort.uniq
80

  
81
            data[:vms]={}
82

  
83
            vms.each do |vm|
84
                # Get the slices array for this VM
85
                slices=data[:vm_slices].select {|slice| slice[:id]==vm }
86

  
87
                data[:vms][vm]={
88
                    :slices => [],
89
                    :time => 0,
90
                }
91

  
92
                # Get the deltas sum for this VM
93
                vm_delta=@deltas.find {|d| d[:vm_id]==vm }
94

  
95
                data[:vms][vm][:network]=vm_delta
96
                data[:vms][vm][:vmid]=vm
97

  
98
                # Calculate the time consumed by the VM
99
                slices.each do |slice|
100
                    data[:vms][vm][:slices] << slice
101

  
102
                    time=calculate_time(slice,
103
                        @filters[:start], @filters[:end])
104
                    data[:vms][vm][:time]+=time
105
                end
106
            end
107

  
108
            # Delete redundant slices data
109
            data.delete(:vm_slices)
110
        end
111
    end
112

  
113
    def get_vm_slices(filters={})
114
        vms=WatchHelper::Register
115

  
116
        query=vms.join(:vms, :id => :vm_id)
117
        query=query.filter({:vms__uid => filters[:user]}) if filters[:user]
118
        query=query.filter(
119
            {:retime => 0} | (:retime > filters[:start])) if filters[:start]
120
        query=query.filter(:rstime <= filters[:end]) if filters[:end]
121

  
122
        query
123
    end
124

  
125
    def get_deltas(filters={})
126
        if filters[:data]
127
            query=filters[:data]
128
        else
129
            query=WatchHelper::VmDelta
130
        end
131

  
132
        query=query.filter( :ptimestamp >= filters[:start] ) if filters[:start]
133
        query=query.filter( :ptimestamp <= filters[:end] ) if filters[:end]
134
        query=query.filter( { :vm_id => filters[:vmid] } ) if filters[:vmid]
135

  
136
        query
137
    end
138

  
139
    def calculate_deltas
140
        query=WatchHelper::VmDelta.select(
141
            :ptimestamp, :vm_id,
142
            'sum(net_tx) AS net_tx'.lit, 'sum(net_rx) AS net_rx'.lit)
143

  
144
        query=query.group(:vm_id)
145

  
146
        new_filters=@filters.merge(:data => query)
147

  
148
        get_deltas(new_filters)
149
    end
150

  
151
    def calculate_time(slice, period_start, period_end)
152
        ts=slice[:rstime].to_i
153
        te=slice[:retime].to_i
154

  
155
        pstart=period_start.to_i
156
        pend=period_end.to_i
157

  
158
        pend=Time.now.to_i if pend==0
159

  
160
        ts=pstart if ts<pstart
161
        if te>pend or te==0
162
            te=pend
163
        end
164

  
165
        te-ts
166
    end
167
end
168

  
169
if $0 == __FILE__
170

  
171
    require 'json'
172

  
173
    acct=AcctClient.new(
174
        :start => 1319476322,
175
        :end => 1319637455
176
    )
177

  
178
    a=acct.account()
179

  
180
    puts JSON.pretty_generate(a)
181

  
182
end
183

  
src/acct/oneacctd
1
#!/bin/bash
2

  
3
# -------------------------------------------------------------------------- #
4
# Copyright 2002-2012, OpenNebula Project Leads (OpenNebula.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
if [ -z "$ONE_LOCATION" ]; then
20
    ONE_PID=/var/run/one/oned.pid
21
    LOCK_FILE=/var/lock/one/one
22
    ACCTD_CMD=/usr/lib/one/ruby/acct/acctd.rb
23
    ACCTD_LOG=/var/log/one/oneacctd.log
24
    ACCTD_PID_FILE=/var/run/one/oneacctd.pid
25
else
26
    ONE_PID=$ONE_LOCATION/var/oned.pid
27
    LOCK_FILE=$ONE_LOCATION/var/.lock
28
    ACCTD_CMD=$ONE_LOCATION/lib/ruby/acct/acctd.rb
29
    ACCTD_LOG=$ONE_LOCATION/var/oneacctd.log
30
    ACCTD_PID_FILE=$ONE_LOCATION/var/oneacctd.pid
31
fi
32

  
33
function oned_running {
34
    ONEPID=`cat $ONE_PID 2> /dev/null`
35
    ps $ONEPID > /dev/null 2>&1
36
    if [ ! -f "$LOCK_FILE" -o ! -f "$ONE_PID" -o $? -ne 0 ]; then
37
        echo oned not running
38
        exit 1
39
    fi
40
}
41

  
42
function acctd_running {
43
    ACCTD_PID=`cat $ACCTD_PID_FILE 2>/dev/null`
44
    ps "$ACCTD_PID" &> /dev/null
45
}
46

  
47
COMMAND=$1
48

  
49
case $COMMAND in
50
start)
51
    # check if OpenNebula running
52
    oned_running
53

  
54
    # check if acct already running
55
    acctd_running
56
    if [ "$?" = "0" ]; then
57
        echo "oneacctd already running."
58
        exit 1
59
    fi
60

  
61
    # acctd not running, safe to start
62
    $ACCTD_CMD &> $ACCTD_LOG &
63
    
64
    LASTRC=$?
65
    LASTPID=$!
66

  
67
    if [ $LASTRC -ne 0 ]; then
68
      echo "Error executing oneacctd."
69
      echo "Check $ACCTD_LOG for more information"
70
      exit 1
71
    else
72
      echo $LASTPID > $ACCTD_PID_FILE
73
    fi
74

  
75
    sleep 2
76
    ps $LASTPID > /dev/null 2>&1
77

  
78
    if [ $? -ne 0 ]; then
79
      echo "Error executing oneacctd."
80
      echo "Check $ACCTD_LOG for more information"
81
      exit 1
82
    fi
83

  
84
    echo "oneacctd started"
85
    ;;
86
stop)
87
    # check if running
88
    acctd_running
89
    if [ "$?" != "0" ]; then
90
        echo "oneacctd not running."
91
        exit 1
92
    fi
93

  
94
    # acctd running, safe to stop
95
    ACCTD_PID=`cat $ACCTD_PID_FILE 2>/dev/null`
96
    kill $ACCTD_PID &> /dev/null
97
    rm -f $ACCTD_PID_FILE &> /dev/null
98
    
99
    echo "oneacctd stop"
100
    ;;
101
*)
102
    echo "Usage: oneacctd {start|stop}" >&2
103
    exit 3
104
    ;;
105
esac
src/acct/test/1Vm1His.rb
1
$: << '.'
2

  
3
require 'helper/test_helper.rb'
4

  
5
describe "1 Vm 1 History" do
6
    before(:each) do
7
        clean_db
8

  
9
        @mock_client = MockClient.new
10
        @accounting  = OneWatch::Accounting.new(@mock_client)
11

  
12
        @watch_client = AcctClient.new
13

  
14
        @db = WatchHelper::DB
15
        check_lines(0,0)
16
    end
17

  
18
    it "Prolog testing" do
19
        ts1 = 100
20
        @accounting.set_mock_timestamp(ts1)
21

  
22
        @accounting.insert(create_vmpool_hash)
23
        check_lines(0,0)
24

  
25
        ts2 = 200
26
        @accounting.set_mock_timestamp(ts2)
27

  
28
        values = {
29
            :uid => 2,
30
            :gid => 4,
31
            :history => [
32
                :hid => 7,
33
                :pstime => 150,
34
                :petime => 0,
35
                :rstime => 0,
36
                :retime => 0,
37
                :estime => 0,
38
                :eetime => 0,
39
                :reason => 0
40
            ]
41
        }
42

  
43
        @mock_client.add_vm(1, values)
44

  
45
        @accounting.insert(create_vmpool_hash)
46
        check_lines(1,1)
47

  
48
        history = values[:history].first
49
        sum = ts2 - history[:pstime]
50

  
51
        warn "* T1 PSTIME T2"
52
        @watch_client.prolog_time(ts1, ts2).to_i.should eql(sum)
53
        warn " - By User"
54
        @watch_client.prolog_time(ts1, ts2, :uid => 2).to_i.should eql(sum)
55
        warn " - By Host"
56
        @watch_client.prolog_time(ts1, ts2, :hid => 7).to_i.should eql(sum)
57
        warn " - By Vm"
58
        @watch_client.prolog_time(ts1, ts2, :vmid => 1).to_i.should eql(sum)
59

  
60
        warn " - Non existent User"
61
        @watch_client.prolog_time(ts1, ts2, :uid => 555).to_i.should eql(0)
62
        warn " - Non existent Host"
63
        @watch_client.prolog_time(ts1, ts2, :hid => 555).to_i.should eql(0)
64
        warn " - Non existent Vm"
65
        @watch_client.prolog_time(ts1, ts2, :vmid => 555).to_i.should eql(0)
66

  
67
        warn "* PSTIME T1 T2"
68
        @watch_client.prolog_time(160, ts2).to_i.should eql(sum-10)
69
        warn "* T1 PSTIME T2-10"
70
        @watch_client.prolog_time(ts1, ts2-10).to_i.should eql(sum-10)
71
        warn "* T1 T2 PSTIME"
72
        @watch_client.prolog_time(110, 130).to_i.should eql(0)
73

  
74
        warn "* Non Epilog time"
75
        @watch_client.epilog_time(ts1, ts2).to_i.should eql(0)
76
        warn "* Non Running time"
77
        @watch_client.running_time(ts1, ts2).to_i.should eql(0)
78

  
79
        ts3 = 300
80
        @accounting.set_mock_timestamp(ts3)
81

  
82
        values = {
83
            :uid => 2,
84
            :gid => 4,
85
            :history => [
86
                :hid => 7,
87
                :pstime => 150,
88
                :petime => 240,
89
                :rstime => 0,
90
                :retime => 0,
91
                :estime => 0,
92
                :eetime => 0,
93
                :reason => 0
94
            ]
95
        }
96

  
97
        @mock_client.add_vm(1, values)
98

  
99
        @accounting.insert(create_vmpool_hash)
100
        check_lines(1,1)
101

  
102
        history = values[:history].first
103
        sum = history[:petime] - history[:pstime]
104

  
105
        warn "* T1 PSTIME PETIME T3"
106
        @watch_client.prolog_time(ts1, ts3).to_i.should eql(sum)
107
        warn " - By User"
108
        @watch_client.prolog_time(ts1, ts3, :uid => 2).to_i.should eql(sum)
109
        warn " - By Host"
110
        @watch_client.prolog_time(ts1, ts3, :hid => 7).to_i.should eql(sum)
111
        warn " - By Vm"
112
        @watch_client.prolog_time(ts1, ts3, :vmid => 1).to_i.should eql(sum)
113

  
114
        warn "* T1 PSTIME T3 PETIME"
115
        @watch_client.prolog_time(ts1, 230).to_i.should eql(230 - history[:pstime])
116

  
117
        warn "* PSTIME T1 PETIME T3"
118
        @watch_client.prolog_time(160, ts3).to_i.should eql(history[:petime] - 160)
119

  
120
        warn "* PSTIME T1 T3 PETIME"
121
        @watch_client.prolog_time(160, 230).to_i.should eql(230 - 160)
122
    end
123

  
124
    it "Running testing" do
125
        ts1 = 100
126
        @accounting.set_mock_timestamp(ts1)
127

  
128
        @accounting.insert(create_vmpool_hash)
129
        check_lines(0,0)
130

  
131
        ts2 = 200
132
        @accounting.set_mock_timestamp(ts2)
133

  
134
        values = {
135
            :uid => 2,
136
            :gid => 4,
137
            :history => [
138
                :hid => 7,
139
                :pstime => 150,
140
                :petime => 155,
141
                :rstime => 155,
142
                :retime => 0,
143
                :estime => 0,
144
                :eetime => 0,
145
                :reason => 0
146
            ]
147
        }
148

  
149
        @mock_client.add_vm(1, values)
150

  
151
        @accounting.insert(create_vmpool_hash)
152
        check_lines(1,1)
153

  
154
        history = values[:history].first
155
        sum = ts2 - history[:rstime]
156

  
157
        warn "* T1 RSTIME T2"
158
        @watch_client.running_time(ts1, ts2).to_i.should eql(sum)
159
        warn " - By User"
160
        @watch_client.running_time(ts1, ts2, :uid => 2).to_i.should eql(sum)
161
        warn " - By Host"
162
        @watch_client.running_time(ts1, ts2, :hid => 7).to_i.should eql(sum)
163
        warn " - By Vm"
164
        @watch_client.running_time(ts1, ts2, :vmid => 1).to_i.should eql(sum)
165

  
166
        warn " - Non existent User"
167
        @watch_client.running_time(ts1, ts2, :uid => 555).to_i.should eql(0)
168
        warn " - Non existent Host"
169
        @watch_client.running_time(ts1, ts2, :hid => 555).to_i.should eql(0)
170
        warn " - Non existent Vm"
171
        @watch_client.running_time(ts1, ts2, :vmid => 555).to_i.should eql(0)
172

  
173
        warn "* RSTIME T1 T2"
174
        @watch_client.running_time(160, ts2).to_i.should eql(ts2-160)
175
        warn "* T1 RSTIME T2-10"
176
        @watch_client.running_time(ts1, ts2-10).to_i.should eql(sum-10)
177
        warn "* T1 T2 RSTIME"
178
        @watch_client.running_time(110, 130).to_i.should eql(0)
179

  
180
        warn "* Non Epilog time"
181
        @watch_client.epilog_time(ts1, ts2).to_i.should eql(0)
182
        warn "* Non Prolog time"
183
        @watch_client.prolog_time(ts1, ts2).to_i.should eql(5)
184

  
185
        ts3 = 300
186
        @accounting.set_mock_timestamp(ts3)
187

  
188
        values = {
189
            :uid => 2,
190
            :gid => 4,
191
            :history => [
192
                :hid => 7,
193
                :pstime => 150,
194
                :petime => 155,
195
                :rstime => 155,
196
                :retime => 230,
197
                :estime => 0,
198
                :eetime => 0,
199
                :reason => 0
200
            ]
201
        }
202

  
203
        @mock_client.add_vm(1, values)
204

  
205
        @accounting.insert(create_vmpool_hash)
206
        check_lines(1,1)
207

  
208
        history = values[:history].first
209
        sum = history[:retime] - history[:rstime]
210

  
211
        warn "* T1 PSTIME PETIME T3"
212
        @watch_client.running_time(ts1, ts3).to_i.should eql(sum)
213
        warn " - By User"
214
        @watch_client.running_time(ts1, ts3, :uid => 2).to_i.should eql(sum)
215
        warn " - By Host"
216
        @watch_client.running_time(ts1, ts3, :hid => 7).to_i.should eql(sum)
217
        warn " - By Vm"
218
        @watch_client.running_time(ts1, ts3, :vmid => 1).to_i.should eql(sum)
219

  
220
        warn "* T1 PSTIME T3 PETIME"
221
        @watch_client.running_time(ts1, 230).to_i.should eql(230 - history[:rstime])
222

  
223
        warn "* PSTIME T1 PETIME T3"
224
        @watch_client.running_time(160, ts3).to_i.should eql(history[:retime] - 160)
225

  
226
        warn "* PSTIME T1 T3 PETIME"
227
        @watch_client.running_time(160, 230).to_i.should eql(230 - 160)
228
    end
229

  
230
    it "Epilog testing" do
231
        ts1 = 100
232
        @accounting.set_mock_timestamp(ts1)
233

  
234
        @accounting.insert(create_vmpool_hash)
235
        check_lines(0,0)
236

  
237
        ts2 = 200
238
        @accounting.set_mock_timestamp(ts2)
239

  
240
        values = {
241
            :uid => 2,
242
            :gid => 4,
243
            :history => [
244
                :hid => 7,
245
                :pstime => 150,
246
                :petime => 155,
247
                :rstime => 155,
248
                :retime => 170,
249
                :estime => 180,
250
                :eetime => 0,
251
                :reason => 0
252
            ]
253
        }
254

  
255
        @mock_client.add_vm(1, values)
256

  
257
        @accounting.insert(create_vmpool_hash)
258
        check_lines(1,1)
259

  
260
        history = values[:history].first
261
        sum = ts2 - history[:estime]
262

  
263
        warn "* T1 ESTIME T2"
264
        @watch_client.epilog_time(ts1, ts2).to_i.should eql(sum)
265
        warn " - By User"
266
        @watch_client.epilog_time(ts1, ts2, :uid => 2).to_i.should eql(sum)
267
        warn " - By Host"
268
        @watch_client.epilog_time(ts1, ts2, :hid => 7).to_i.should eql(sum)
269
        warn " - By Vm"
270
        @watch_client.epilog_time(ts1, ts2, :vmid => 1).to_i.should eql(sum)
271

  
272
        warn " - Non existent User"
273
        @watch_client.epilog_time(ts1, ts2, :uid => 555).to_i.should eql(0)
274
        warn " - Non existent Host"
275
        @watch_client.epilog_time(ts1, ts2, :hid => 555).to_i.should eql(0)
276
        warn " - Non existent Vm"
277
        @watch_client.epilog_time(ts1, ts2, :vmid => 555).to_i.should eql(0)
278

  
279
        warn "* ESTIME T1 T2"
280
        @watch_client.epilog_time(190, ts2).to_i.should eql(ts2-190)
281
        warn "* T1 ESTIME T2-10"
282
        @watch_client.epilog_time(ts1, ts2-10).to_i.should eql(sum-10)
283
        warn "* T1 T2 ESTIME"
284
        @watch_client.epilog_time(110, 130).to_i.should eql(0)
285

  
286
        warn "* Non Running time"
287
        @watch_client.running_time(ts1, ts2).to_i.should eql(15)
288
        warn "* Non Prolog time"
289
        @watch_client.prolog_time(ts1, ts2).to_i.should eql(5)
290

  
291
        ts3 = 300
292
        @accounting.set_mock_timestamp(ts3)
293

  
294
        values = {
295
            :uid => 2,
296
            :gid => 4,
297
            :history => [
298
                :hid => 7,
299
                :pstime => 150,
300
                :petime => 155,
301
                :rstime => 155,
302
                :retime => 170,
303
                :estime => 180,
304
                :eetime => 230,
305
                :reason => 0
306
            ]
307
        }
308

  
309
        @mock_client.add_vm(1, values)
310

  
311
        @accounting.insert(create_vmpool_hash)
312
        check_lines(1,1)
313

  
314
        history = values[:history].first
315
        sum = history[:eetime] - history[:estime]
316

  
317
        warn "* T1 PSTIME PETIME T3"
318
        @watch_client.epilog_time(ts1, ts3).to_i.should eql(sum)
319
        warn " - By User"
320
        @watch_client.epilog_time(ts1, ts3, :uid => 2).to_i.should eql(sum)
321
        warn " - By Host"
322
        @watch_client.epilog_time(ts1, ts3, :hid => 7).to_i.should eql(sum)
323
        warn " - By Vm"
324
        @watch_client.epilog_time(ts1, ts3, :vmid => 1).to_i.should eql(sum)
325

  
326
        warn "* T1 PSTIME T3 PETIME"
327
        @watch_client.epilog_time(ts1, 230).to_i.should eql(230 - history[:estime])
328

  
329
        warn "* PSTIME T1 PETIME T3"
330
        @watch_client.epilog_time(190, ts3).to_i.should eql(history[:eetime] - 190)
331

  
332
        warn "* PSTIME T1 T3 PETIME"
333
        @watch_client.epilog_time(190, 220).to_i.should eql(220 - 190)
334
    end
335
end
src/acct/test/1VmXHis.rb
1
$: << '.'
2

  
3
require 'helper/test_helper.rb'
4

  
5
describe "1 Vm X History" do
6
    before(:each) do
7
        clean_db
8

  
9
        @mock_client = MockClient.new
10
        @accounting  = OneWatch::Accounting.new(@mock_client)
11

  
12
        @watch_client = AcctClient.new
13

  
14
        @db = WatchHelper::DB
15
        check_lines(0,0)
16
    end
17

  
18
    it "Running testing" do
19
        ts1 = 100
20
        @accounting.set_mock_timestamp(ts1)
21

  
22
        @accounting.insert(create_vmpool_hash)
23
        check_lines(0,0)
24

  
25
        ts2 = 200
26
        @accounting.set_mock_timestamp(ts2)
27

  
28
        values = {
29
            :uid => 2,
30
            :gid => 4,
31
            :history => [
32
                :hid => 7,
33
                :seq => 0,
34
                :pstime => 150,
35
                :petime => 155,
36
                :rstime => 155,
37
                :retime => 0,
38
                :estime => 0,
39
                :eetime => 0,
40
                :reason => 0
41
            ]
42
        }
43

  
44
        @mock_client.add_vm(1, values)
45

  
46
        @accounting.insert(create_vmpool_hash)
47
        check_lines(1,1)
48

  
49
        history = values[:history].first
50
        sum = ts2 - history[:rstime]
51

  
52
        warn "* T1 RSTIME T2"
53
        @watch_client.running_time(ts1, ts2).to_i.should eql(sum)
54

  
55
        ts3 = 300
56
        @accounting.set_mock_timestamp(ts3)
57

  
58
        values = {
59
            :uid => 2,
60
            :gid => 4,
61
            :history => [
62
                {
63
                    :hid => 7,
64
                    :seq => 0,
65
                    :pstime => 150,
66
                    :petime => 155,
67
                    :rstime => 155,
68
                    :retime => 230,
69
                    :estime => 230,
70
                    :eetime => 260,
71
                    :reason => 2
72
                },
73
                {
74
                    :hid => 8,
75
                    :seq => 1,
76
                    :pstime => 270,
77
                    :petime => 275,
78
                    :rstime => 275,
79
                    :retime => 0,
80
                    :estime => 0,
81
                    :eetime => 0,
82
                    :reason => 0
83
                }
84
            ]
85
        }
86

  
87
        @mock_client.add_vm(1, values)
88

  
89
        @accounting.insert(create_vmpool_hash)
90
        check_lines(1,2)
91

  
92
        h1 = values[:history].first
93
        sum1 =  h1[:retime] - h1[:rstime]
94

  
95
        h2 = values[:history].last
96
        sum2 = ts3 - h2[:rstime]
97

  
98
        warn "* T1 RSTIME1 RETIME1 RSTIME1 T2"
99
        @watch_client.running_time(ts1, ts3).to_i.should eql(sum1 + sum2)
100
    end
101
end
src/acct/test/XVm1His.rb
1
$: << '.'
2

  
3
require 'helper/test_helper.rb'
4

  
5
describe "X Vm 1 History" do
6
    before(:each) do
7
        clean_db
8

  
9
        @mock_client = MockClient.new
10
        @accounting  = OneWatch::Accounting.new(@mock_client)
11

  
12
        @watch_client = AcctClient.new
13

  
14
        @db = WatchHelper::DB
15
        check_lines(0,0)
16
    end
17

  
18
    it "Running testing" do
19
        ts1 = 100
20
        @accounting.set_mock_timestamp(ts1)
21

  
22
        @accounting.insert(create_vmpool_hash)
23
        check_lines(0,0)
24

  
25
        ts2 = 200
26
        @accounting.set_mock_timestamp(ts2)
27

  
28
        values = {
29
            :uid => 2,
30
            :gid => 4,
31
            :history => [
32
                :hid => 6,
33
                :seq => 0,
34
                :pstime => 150,
35
                :petime => 155,
36
                :rstime => 155,
37
                :retime => 0,
38
                :estime => 0,
39
                :eetime => 0,
40
                :reason => 0
41
            ]
42
        }
43

  
44
        @mock_client.add_vm(1, values)
45

  
46
        @accounting.insert(create_vmpool_hash)
47
        check_lines(1,1)
48

  
49
        history = values[:history].first
50
        sum = ts2 - history[:rstime]
51

  
52
        warn "* T1 RSTIME T2"
53
        @watch_client.running_time(ts1, ts2).to_i.should eql(sum)
54

  
55
        ts3 = 300
56
        @accounting.set_mock_timestamp(ts3)
57

  
58
        values2 = {
59
            :uid => 2,
60
            :gid => 4,
61
            :history => [
62
                :hid => 7,
63
                :seq => 0,
64
                :pstime => 220,
65
                :petime => 260,
66
                :rstime => 260,
67
                :retime => 0,
68
                :estime => 0,
69
                :eetime => 0,
70
                :reason => 0
71
            ]
72
        }
73

  
74
        @mock_client.add_vm(2, values2)
75

  
76
        @accounting.insert(create_vmpool_hash)
77
        check_lines(2,2)
78

  
79
        history1 = values[:history].first
80
        sum1 = ts3 - history1[:rstime]
81

  
82
        history2 = values2[:history].first
83
        sum2 = ts3 - history2[:rstime]
84

  
85
        warn "* T1 RSTIME T2"
86
        @watch_client.running_time(ts1, ts3).to_i.should eql(sum1 + sum2)
87
        @watch_client.running_time(ts1, ts3, :vmid=>1).to_i.should eql(sum1)
88
        @watch_client.running_time(ts1, ts3, :vmid=>2).to_i.should eql(sum2)
89
        @watch_client.running_time(ts1, ts3, :uid=>2).to_i.should eql(sum1 + sum2)
90
        @watch_client.running_time(ts1, ts3, :hid=>6).to_i.should eql(sum1)
91
        @watch_client.running_time(ts1, ts3, :hid=>7).to_i.should eql(sum2)
92
    end
93
end
src/acct/test/cmonitoring_spec.rb
1
$: << '.'
2

  
3
require 'helper/test_helper.rb'
4
require 'watch_client'
5

  
6
describe "VmWatchClient tests" do
7
    before(:all) do
8
        clean_db
9

  
10
        @mock_client = MockClient.new
11
        @monitoring  = OneWatch::VmMonitoring.new
12

  
13
        @watch_client = OneWatchClient::VmWatchClient.new
14

  
15
        @db = WatchHelper::DB
16
        @db[:vms].count.should eql(0)
17
    end
18

  
19
    it "Create a VM: uid=2 gid=4, timestamp=100" do
20
        @monitoring.set_mock_timestamp(100)
21

  
22
        @monitoring.insert(create_vmpool_hash)
23
        @db[:vms].count.should eql(0)
24

  
25
        values = {
26
            :cpu => 1,
27
            :memory => 128,
28
            :net_tx => 200,
29
            :net_rx => 400,
30
            :last_poll => 100,
31
            :uid => 2,
32
            :gid => 4,
33
            :history => [
34
                :hid => 7,
35
                :pstime => 150,
36
                :petime => 0,
37
                :rstime => 0,
38
                :retime => 0,
39
                :estime => 0,
... This diff was truncated because it exceeds the maximum size that can be displayed.

Also available in: Unified diff