read system status summary at background, display progress
[opensuse:yast-web-client.git] / plugins / status / app / controllers / status_controller.rb
1 require 'yast/service_resource'
2 require 'client_exception'
3 require 'open-uri'
4
5 class StatusController < ApplicationController
6   include ProxyLoader
7   
8   before_filter :login_required
9   layout "main"
10
11   private
12   def client_permissions
13     @client = YaST::ServiceResource.proxy_for('org.opensuse.yast.system.status')
14     unless @client
15       flash[:notice] = _("Invalid session, please login again.")
16       redirect_to( logout_path ) and return
17     end
18     @permissions = @client.permissions
19   end
20
21   def limits_reached
22     @limits_list.each {|key, data|
23       next if key == :reached
24       keys = key.split "/"
25       group = keys[1]
26       metric_name = keys[2]
27
28       if @data_group.has_key? group and @data_group[group].has_key? metric_name
29         for value in @data_group[group][metric_name]
30           if not @limits_list[key][:min][0].nil? and value[1] < @limits_list[key][:min][0][1]\
31              or not @limits_list[key][:max][0].nil? and value[1] > @limits_list[key][:max][0][1]
32             if key == "df"
33               @limits_list[:reached] += _("Disk free limits exceeded;")
34             else
35               @limits_list[:reached] += key + ";"
36             end
37             break
38           end
39         end
40       else
41         logger.debug "error: metric not found"
42       end
43     }
44     @limits_list[:reached]
45   end
46
47   def write_data_group(label, group, metric_name)
48     metric_name += "/" + label.name if label.name != "value" #more than one labels of a group
49     values = label.attributes["values"]
50     if values.uniq != ["invalid"] #use only entries which have at least one valid value
51       value_size = values.length
52       divisor = (group == "memory")? 1024*1024 : 1 # take MByte for the value
53       data_list = Array.new
54       value_size.times{|t| data_list << [t,values[t].to_f/divisor]}
55       if group == "df"
56         data_list.reject! {|value| value[1] == 0 } #df returns sometime 0 entries
57         data_list = [[0,0]] if data_list.empty? #it is really 0 :-)
58       end
59       @data_group[group].merge!({metric_name => data_list})
60
61       limits = label.attributes["limits"]
62       if limits
63         @limits_list["/#{group}/#{metric_name}"] = {:min=>Array.new, :max=>Array.new}
64         if label.attributes["limits"] and limits.attributes["min"] #limits.has_key? "min"
65           minimum = limits.attributes["min"].to_f/divisor
66           value_size.times{|i| @limits_list["/#{group}/#{metric_name}"][:min] << [i,minimum]}
67         end
68         if label.attributes["limits"] and limits.attributes["max"] #limits.has_key? "min"
69           maximum = limits.attributes["max"].to_f/divisor
70           value_size.times{|i| @limits_list["/#{group}/#{metric_name}"][:max] << [i,maximum]}
71         end
72       end
73     else
74       logger.debug "#{group} #{metric_name} #{label.name} has no valid entry"
75     end 
76   end
77
78   def create_data_map(tree)
79     tree.attributes["metric"].each{ |metric|
80       metric_name = metric.name
81       group = metric.metricgroup
82       @data_group[group] ||= Hash.new
83       interval = metric.interval #TODO: not used yet
84       starttime = metric.starttime
85       case metric.attributes["label"]
86       when YaST::ServiceResource::Proxies::Status::Metric::Label # one label
87         write_data_group(metric.attributes["label"], group, metric_name)
88       when Array # several label
89         metric.attributes["label"].each{ |label|
90           write_data_group(label, group, metric_name)
91       }
92       end
93     }
94   end
95
96   def create_data(from = nil, till = nil, background = false)
97     @limits_list = Hash.new
98     @limits_list[:reached] = String.new
99     @data_group = Hash.new
100     status = []
101     
102     till ||= Time.new
103     from ||= till - 300 #last 5 minutes
104     ActionController::Base.benchmark("Status data read from the server") do
105       stat_params = { :start => from.to_i.to_s, :stop => till.to_i.to_s }
106       stat_params[:background] = "true" if background
107       status = @client.find(:dummy_param, :params => stat_params )
108     end
109
110     # this is a background status result
111     return status.attributes if status.attributes.keys.sort == ["progress", "status", "subprogress"]
112
113     create_data_map status
114 #    logger.debug @data_group.inspect
115
116     #checking if there is one valid data entry at least
117     found = false
118     @data_group.each do |key, map|
119       map.each do |graph_key, list_value|
120          unless list_value.empty?
121            found = true
122            break
123          end
124       end
125       break if found
126     end
127     found
128   end
129
130
131  # Initialize GetText and Content-Type.
132   init_gettext "yast_webclient_status"
133
134   public
135
136   def initialize
137   end
138
139   def edit
140     return unless client_permissions
141     flash[:notice] = _("No data found for showing system status.") unless create_data
142   end
143
144   def ajax_log_custom
145     # set the site to the view so it can load the log
146     # dynamically
147     if not params.has_key?(:id)
148       raise "Unknown log file"
149     end
150     
151 #XXX FIXME Really ugly way how to use REST service
152 # should be something like find(:one, :from => params[:id], params => { :lines => lines })
153     lines = params[:lines] || 5
154     log_url = URI.parse(YaST::ServiceResource::Session.site.to_s)
155     log_url = log_url.merge("logs/#{params[:id]}.xml?lines=#{lines}")
156     logger.info "requesting #{log_url}"
157     @content = open(log_url).read
158     render :partial => 'status_log'
159   end
160   
161   def index
162     return unless client_permissions
163     begin
164       log = YaST::ServiceResource.proxy_for('org.opensuse.yast.system.logs')
165       @logs = log.find(:all) 
166       @logs ||= {}
167       flash[:notice] = _("No data found for showing system status.") unless create_data
168       limits_reached
169       logger.debug "limits reached for #{@limits_list[:reached].inspect}"
170       rescue ActiveResource::ServerError => error
171         error_hash = Hash.from_xml error.response.body
172         logger.warn error_hash.inspect
173         if error_hash["error"] && 
174           (error_hash["error"]["type"] == "SERVICE_NOT_RUNNING" || error_hash["error"]["type"] == "NO_PERM")
175            flash[:error] = error_hash["error"]["description"]
176         else
177            raise error
178         end
179     end
180   end
181
182   def show_summary
183     return unless client_permissions
184     begin
185       till = params['till']
186       from = params['from']
187
188       till ||= Time.new.to_i
189       from ||= till - 300 #last 5 minutes
190
191       result = create_data(from, till, !params['background'].nil?)
192
193       # is it a background progress?
194       if result.class == Hash
195         status_progress = result.symbolize_keys
196         Rails.logger.debug "Received background status progress: #{status_progress.inspect}"
197
198         respond_to do |format|
199           format.html { render :partial  => 'status_progress', :locals => {:status => status_progress, :from => from, :till => till } }
200           format.json  { render :json => status_progress }
201         end
202
203         return
204       end
205
206       if result
207         status = limits_reached
208         status = (_("Limits exceeded for %s") % status) unless status.empty?
209       else
210         status = _("No data found for showing system status.")
211       end
212       render :partial => "status_summary", :locals => { :status => status, :error => nil }
213       rescue ActiveResource::ClientError => error
214         logger.warn error.inspect
215         render :partial => "status_summary", :locals => { :status => nil, :error => ClientException.new(error) } and return
216       rescue ActiveResource::ServerError => error
217         error_hash = Hash.from_xml error.response.body
218         logger.warn error_hash.inspect
219         if error_hash["error"] && 
220           (error_hash["error"]["type"] == "SERVICE_NOT_RUNNING" || error_hash["error"]["type"] == "NO_PERM")
221            status = error_hash["error"]["description"]
222            render :partial => "status_summary", :locals => { :status => status, :error => nil }
223         else
224            render :partial => "status_summary", :locals => { :status => nil, :error => ClientException.new(error) } 
225         end
226     end
227   end
228
229   def save
230     return unless client_permissions
231     limits = Hash.new
232     params.each_pair{|key, value|
233       slizes = key.split "/"
234       value = value.to_f*1024*1024 if !value.empty? && slizes[1]=="memory" #MByte for the value --> change it to Byte
235       unless value.empty?
236         if key =~ /\/[-\w]*\/[-\w]*\/min/ # e.g /interface/if_packets-pan0/max
237           limits[slizes[1]] ||= Hash.new
238           limits[slizes[1]][slizes[2]] ||= Hash.new
239           limits[slizes[1]][slizes[2]].merge!(:min => value) 
240         elsif key =~ /\/[-\w]*\/[-\w]*\/max/
241           limits[slizes[1]] ||= Hash.new
242           limits[slizes[1]][slizes[2]] ||= Hash.new
243           limits[slizes[1]][slizes[2]].merge!(:max => value) unless value.empty?
244         end
245       end
246     }
247
248     Rails.logger.debug "New limits: #{limits.inspect}"
249
250     begin
251       ActionController::Base.benchmark("Limits saved on the server") do
252         #This is a hack and will be removed when the status service has be replaced by the metric service
253         @client.create( :limits=>limits.to_xml(:root => "limits") ) 
254       end
255     rescue Exception => ex
256       flash[:error] = _("Saving limits failed!")
257       redirect_to :controller=>"status", :action=>"edit" and return
258     end
259
260     redirect_to :controller=>"status", :action=>"index"
261   end
262
263 end