Class: DigitalFabric::Executive

Inherits:
Object
  • Object
show all
Defined in:
lib/tipi/digital_fabric/executive.rb

Overview

agent for managing DF service

Constant Summary collapse

INDEX_HTML =
IO.read(File.join(__dir__, 'executive/index.html'))
TOP_CPU_REGEXP =
/%Cpu(.+)/.freeze
TOP_CPU_IDLE_REGEXP =
/([\d\.]+) id/.freeze
TOP_MEM_REGEXP =
/MiB Mem(.+)/.freeze
TOP_MEM_FREE_REGEXP =
/([\d\.]+) free/.freeze
LOADAVG_REGEXP =
/^([\d\.]+)/.freeze

Instance Attribute Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(service, route = { path: '/executive' }) ⇒ Executive

Returns a new instance of Executive.



13
14
15
16
17
18
19
20
# File 'lib/tipi/digital_fabric/executive.rb', line 13

def initialize(service, route = { path: '/executive' })
  @service = service
  route[:executive] = true
  @service.mount(route, self)
  @current_request_count = 0
  # @updater = spin_loop(:executive_updater, interval: 10) { update_service_stats }
  update_service_stats
end

Instance Attribute Details

#last_service_statsObject (readonly)

Returns the value of attribute last_service_stats.



11
12
13
# File 'lib/tipi/digital_fabric/executive.rb', line 11

def last_service_stats
  @last_service_stats
end

Instance Method Details

#current_request_countObject



22
23
24
# File 'lib/tipi/digital_fabric/executive.rb', line 22

def current_request_count
  @current_request_count
end

#format_sse_event(data) ⇒ Object



60
61
62
# File 'lib/tipi/digital_fabric/executive.rb', line 60

def format_sse_event(data)
  "data: #{data}\n\n"
end

#http_request(req) ⇒ Object



26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
# File 'lib/tipi/digital_fabric/executive.rb', line 26

def http_request(req)
  @current_request_count += 1
  case req.path
  when '/'
    req.respond(INDEX_HTML, 'Content-Type' => 'text/html')
  when '/stats'
    message = last_service_stats
    req.respond(message.to_json, { 'Content-Type' => 'text.json' })
  when '/stream/stats'
    stream_stats(req)
  when '/upload'
    req.respond("body: #{req.read.inspect}")
  else
    req.respond('Invalid path', { ':status' => Qeweney::Status::NOT_FOUND })
  end
rescue => e
  puts "Error: #{e.inspect}"
ensure
  @current_request_count -= 1
end

#machine_statsObject



77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
# File 'lib/tipi/digital_fabric/executive.rb', line 77

def machine_stats
  top = `top -bn1 | head -n4`
  unless top =~ TOP_CPU_REGEXP && Regexp.last_match(1) =~ TOP_CPU_IDLE_REGEXP
    p top =~ TOP_CPU_REGEXP
    p Regexp.last_match(1)
    p Regexp.last_match(1) =~ TOP_CPU_IDLE_REGEXP
    raise 'Invalid output from top (cpu)'
  end
  cpu_utilization = 100 - Regexp.last_match(1).to_i

  unless top =~ TOP_MEM_REGEXP && Regexp.last_match(1) =~ TOP_MEM_FREE_REGEXP
    raise 'Invalid output from top (mem)'
  end

  mem_free = Regexp.last_match(1).to_f

  stats = `cat /proc/loadavg`
  raise 'Invalid output from /proc/loadavg' unless stats =~ LOADAVG_REGEXP
  load_avg = Regexp.last_match(1).to_f

  {
    mem_free: mem_free,
    cpu_utilization: cpu_utilization,
    load_avg: load_avg
  }
end

#stream_stats(req) ⇒ Object



47
48
49
50
51
52
53
54
55
56
57
58
# File 'lib/tipi/digital_fabric/executive.rb', line 47

def stream_stats(req)
  req.send_headers({ 'Content-Type' => 'text/event-stream' })

  every(10) do
    message = last_service_stats
    req.send_chunk(format_sse_event(message.to_json))
  end
rescue IOError, SystemCallError
  # ignore
ensure
  req.send_chunk("retry: 0\n\n", true) rescue nil
end

#update_service_statsObject



64
65
66
67
68
69
# File 'lib/tipi/digital_fabric/executive.rb', line 64

def update_service_stats
  @last_service_stats = {
    service: @service.stats,
    machine: machine_stats
  }
end