Statistics
| Branch: | Tag: | Revision:

one / src / onedb / onedb_live.rb @ c03fcc70

History | View | Annotate | Download (9.17 KB)

1

    
2
require 'opennebula'
3

    
4
class OneDBLive
5
    def initialize
6
        @client = nil
7
        @system = nil
8
    end
9

    
10
    def client
11
        @client ||= OpenNebula::Client.new
12
    end
13

    
14
    def system
15
        @system ||= OpenNebula::System.new(client)
16
    end
17

    
18
    def db_escape(string)
19
        string.gsub("'", "''")
20
    end
21

    
22
    def delete_sql(table, where)
23
        "DELETE from #{table} WHERE #{where}"
24
    end
25

    
26
    def delete(table, where, federate)
27
        sql = delete_sql(table, where)
28
        db_exec(sql, "Error deleting record", federate)
29
    end
30

    
31
    def update_sql(table, values, where)
32
        str = "UPDATE #{table} SET "
33

    
34
        changes = []
35

    
36
        values.each do |key, value|
37
            change = "#{key.to_s} = "
38

    
39
            case value
40
            when String, Symbol
41
                change << "'#{db_escape(value.to_s)}'"
42
            when Numeric
43
                change << value.to_s
44
            else
45
                change << value.to_s
46
            end
47

    
48
            changes << change
49
        end
50

    
51
        str << changes.join(', ')
52
        str << " WHERE #{where}"
53

    
54
        str
55
    end
56

    
57
    def update(table, values, where, federate)
58
        sql = update_sql(table, values, where)
59
        db_exec(sql, "Error updating record", federate)
60
    end
61

    
62
    def update_body_sql(table, body, where)
63
        "UPDATE #{table} SET body = '#{db_escape(body)}' WHERE #{where}"
64
    end
65

    
66
    def update_body(table, body, where, federate)
67
        sql = update_body_sql(table, body, where)
68
        db_exec(sql, "Error updating record", federate)
69
    end
70

    
71
    def db_exec(sql, error_msg, federate = false)
72
        rc = system.sql_command(sql, federate)
73
        if OpenNebula.is_error?(rc)
74
            raise "#{error_msg}: #{rc.message}"
75
        end
76
    end
77

    
78
    def percentage_line(current, max, carriage_return = false)
79
        return_symbol = carriage_return ? "\r" : ""
80
        percentile = current.to_f / max.to_f * 100
81

    
82
        "#{current}/#{max} #{percentile.round(2)}%#{return_symbol}"
83
    end
84

    
85
    def purge_history(options = {})
86
        vmpool = OpenNebula::VirtualMachinePool.new(client)
87
        vmpool.info_all
88

    
89
        ops = {
90
            start_time: 0,
91
            end_time: Time.now
92
        }.merge(options)
93

    
94
        start_time  = ops[:start_time].to_i
95
        end_time    = ops[:end_time].to_i
96

    
97
        last_id = vmpool["/VM_POOL/VM[last()]/ID"]
98

    
99
        vmpool.each do |vm|
100
            print percentage_line(vm.id, last_id, true)
101

    
102
            time = vm["STIME"].to_i
103
            next unless time >= start_time && time < end_time
104

    
105
            # vmpool info only returns the last history record. We can check
106
            # if this VM can have more than one record using the sequence
107
            # number. If it's 0 or it does not exist we can skip the VM.
108
            # Also take tone that xpaths on VM info that comes from VMPool
109
            # or VM is different. We can not use absolute searches with
110
            # objects coming from pool.
111
            seq = vm['HISTORY_RECORDS/HISTORY/SEQ']
112
            next if !seq || seq == '0'
113

    
114
            # If the history can contain more than one record we get
115
            # all the info for two reasons:
116
            #
117
            #   * Make sure that all the info is written back
118
            #   * Refresh the information so it's less probable that the info
119
            #     was modified during this process
120
            vm.info
121

    
122
            hash = vm.to_hash
123
            val_history = hash['VM']['HISTORY_RECORDS']['HISTORY']
124

    
125
            if Array === val_history && val_history.size > 2
126
                last_history = val_history.last(2)
127

    
128
                old_seq = []
129
                seq_num = last_history.first['SEQ']
130

    
131
                # Renumerate the sequence
132
                last_history.each_with_index do |history, index|
133
                    old_seq << history['SEQ'].to_i
134
                    history['SEQ'] = index
135
                end
136

    
137
                vm.delete_element('HISTORY_RECORDS/HISTORY')
138
                vm.add_element('HISTORY_RECORDS', 'HISTORY' => last_history)
139

    
140
                # Update VM body to leave only the last history record
141
                body = db_escape(vm.to_xml)
142
                update_body("vm_pool", vm.to_xml, "oid = #{vm.id}", false)
143

    
144
                # Delete any history record that does not have the same
145
                # SEQ number as the last history record
146
                pp seq_num
147
                delete("history", "vid = #{vm.id} and seq < #{seq_num}", false)
148

    
149
                # Renumerate sequence numbers
150
                old_seq.each_with_index do |seq, index|
151
                    update("history",
152
                           { seq: index },
153
                           "vid = #{vm.id} and seq = #{seq}", false)
154
                end
155
            end
156
        end
157
    end
158

    
159
    def purge_done_vm(options = {})
160
        vmpool = OpenNebula::VirtualMachinePool.new(client)
161
        vmpool.info(OpenNebula::Pool::INFO_ALL,
162
                    -1,
163
                    -1,
164
                    OpenNebula::VirtualMachine::VM_STATE.index('DONE'))
165

    
166
        ops = {
167
            start_time: 0,
168
            end_time: Time.now
169
        }.merge(options)
170

    
171
        start_time  = ops[:start_time].to_i
172
        end_time    = ops[:end_time].to_i
173

    
174
        last_id = vmpool["/VM_POOL/VM[last()]/ID"]
175

    
176
        vmpool.each do |vm|
177
            print percentage_line(vm.id, last_id, true)
178

    
179
            time = vm["ETIME"].to_i
180
            next unless time >= start_time && time < end_time
181

    
182
            delete("vm_pool", "oid = #{vm.id}", false)
183
            delete("history", "vid = #{vm.id}", false)
184
        end
185
    end
186

    
187
    def check_expr(object, expr)
188
        reg = /^(?<xpath>.+?)(?<operator>=|!=|>=|<=|>|<)(?<value>.*?)$/
189
        parsed = expr.match(reg)
190

    
191
        raise "Expression malformed: '#{expr}'" unless parsed
192

    
193
        val = object[parsed[:xpath]]
194
        return false if !val
195

    
196
        p_val = parsed[:value].strip
197
        val.strip!
198

    
199
        res = false
200

    
201
        res = case parsed[:operator]
202
        when '='
203
            val == p_val
204
        when '!='
205
            val != p_val
206
        when '<'
207
            val.to_i < p_val.to_i
208
        when '>'
209
            val.to_i > p_val.to_i
210
        when '<='
211
            val.to_i <= p_val.to_i
212
        when '>='
213
            val.to_i >= p_val.to_i
214
        end
215

    
216
        res
217
    end
218

    
219
    def change_body(object, xpath, value, options = {})
220
        case (object||'').downcase.strip.to_sym
221
        when :vm
222
            table = 'vm_pool'
223
            object = OpenNebula::VirtualMachinePool.new(client)
224
            federate = false
225

    
226
        when :host
227
            table = 'host_pool'
228
            object = OpenNebula::HostPool.new(client)
229
            federate = false
230

    
231
        when :vnet
232
            table = 'network_pool'
233
            object = OpenNebula::VirtualNetworkPool.new(client)
234
            federate = false
235

    
236
        when :image
237
            table = 'image_pool'
238
            object = OpenNebula::ImagePool.new(client)
239
            federate = false
240

    
241
        when :cluster
242
            table = 'cluster_pool'
243
            object = OpenNebula::ClusterPool.new(client)
244
            federate = false
245

    
246
        when :document
247
            table = 'document_pool'
248
            object = OpenNebula::DocumentPool.new(client)
249
            federate = false
250

    
251
        when :group
252
            table = 'group_pool'
253
            object = OpenNebula::GroupPool.new(client)
254
            federate = true
255

    
256
        when :marketplace
257
            table = 'marketplace_pool'
258
            object = OpenNebula::MarketPlacePool.new(client)
259
            federate = true
260

    
261
        when :marketplaceapp
262
            table = 'marketplaceapp_pool'
263
            object = OpenNebula::MarketPlaceAppPool.new(client)
264
            federate = true
265

    
266
        when :secgroup
267
            table = 'secgroup_pool'
268
            object = OpenNebula::SecurityGroupPool.new(client)
269
            federate = false
270

    
271
        when :template
272
            table = 'template_pool'
273
            object = OpenNebula::TemplatePool.new(client)
274
            federate = false
275

    
276
        when :vrouter
277
            table = 'vrouter_pool'
278
            object = OpenNebula::VirtualRouterPool.new(client)
279
            federate = false
280

    
281
        when :zone
282
            table = 'zone_pool'
283
            object = OpenNebula::ZonePool.new(client)
284
            federate = true
285

    
286
        else
287
            raise "Object type '#{object}' not supported"
288
        end
289

    
290
        if !value && !options[:delete]
291
            raise "A value or --delete should specified"
292
        end
293

    
294
        object.info
295

    
296
        object.each do |o|
297
            if options[:id]
298
                next unless o.id.to_s.strip == options[:id].to_s
299
            elsif options[:xpath]
300
                next unless o[options[:xpath]]
301
            elsif options[:expr]
302
                next unless check_expr(o, options[:expr])
303
            end
304

    
305
            o.info
306
            doc = Nokogiri::XML(o.to_xml, nil, NOKOGIRI_ENCODING) do |c|
307
                c.default_xml.noblanks
308
            end
309

    
310
            doc.xpath(xpath).each do |e|
311
                if options[:delete]
312
                    e.remove
313
                else
314
                    e.content = value
315
                end
316
            end
317

    
318
            xml = doc.root.to_xml
319

    
320
            if options[:dry]
321
                puts xml
322
            else
323
                update_body(table, xml, "oid = #{o.id}", federate)
324
            end
325
        end
326
    end
327
end
328