Class: Bookie::Database::System

Inherits:
ActiveRecord::Base
  • Object
show all
Defined in:
lib/bookie/database/system.rb

Overview

A system on the network

Constant Summary collapse

SystemConflictError =

Raised when a system’s specifications are different from those of the active system in the database

Class.new(RuntimeError)

Class Method Summary collapse

Instance Method Summary collapse

Class Method Details

.activeObject

Finds all systems that are active (i.e. all systems with NULL values for end_time)



20
21
22
# File 'lib/bookie/database/system.rb', line 20

def self.active
  where('systems.end_time IS NULL')
end

.by_name(name) ⇒ Object

Filters by name



26
27
28
# File 'lib/bookie/database/system.rb', line 26

def self.by_name(name)
  where('systems.name = ?', name)
end

.by_system_type(sys_type) ⇒ Object

Filters by system type



32
33
34
# File 'lib/bookie/database/system.rb', line 32

def self.by_system_type(sys_type)
  where('systems.system_type_id = ?', sys_type.id)
end

.by_time_range(time_range) ⇒ Object

Finds all systems whose running intervals overlap the given time range



38
39
40
41
42
43
44
45
# File 'lib/bookie/database/system.rb', line 38

def self.by_time_range(time_range)
  if time_range.empty?
    self.none
  else
    time_range = time_range.exclusive
    where('(? < systems.end_time OR systems.end_time IS NULL) AND systems.start_time < ?', time_range.first, time_range.last)
  end
end

.find_current(sender, time = nil) ⇒ Object

Finds the current system for a given sender and time

This method also checks that this system’s specifications are the same as those in the database and raises an error if they are different.

This uses Lock#synchronize internally, so it probably should not be called within a transaction block.



53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
# File 'lib/bookie/database/system.rb', line 53

def self.find_current(sender, time = nil)
  time ||= Time.now
  config = sender.config
  system = nil
  name = config.hostname
  Lock[:systems].synchronize do
    system = by_name(config.hostname).where('systems.start_time <= :time AND (:time <= systems.end_time OR systems.end_time IS NULL)', :time => time).first
    if system
      mismatch = !(system.cores == config.cores && system.memory == config.memory)
      mismatch ||= sender.system_type != system.system_type
      if mismatch
        raise SystemConflictError.new("The specifications on record for '#{name}' do not match this system's specifications.
Please make sure that all previous systems with this hostname have been marked as decommissioned.")
      end
    else
      raise "There is no system with hostname '#{config.hostname}' that was recorded as active at #{time}."
    end
  end
  system
end

.summary(time_range = nil) ⇒ Object

Produces a summary of all the systems for the given time interval

Returns a hash with the following fields:

  • :num_systems

    the number of systems that were active in the given interval

  • :avail_cpu_time

    the total CPU time available for the interval

  • :avail_memory_time

    the total amount of memory-time available (in kilobyte-seconds)

  • :avail_memory_avg

    the average amount of memory available (in kilobytes)

To consider: include the start/end times for the summary (especially if they aren’t provided as arguments)?



84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
# File 'lib/bookie/database/system.rb', line 84

def self.summary(time_range = nil)
  current_time = Time.now

  num_systems = 0
  avail_cpu_time = 0
  avail_memory_time = 0

  #Find all the systems within the time range.
  systems = System
  if time_range
    time_range = time_range.exclusive.normalized
    systems = systems.by_time_range(time_range)
  end

  systems.find_each do |system|
    start_time = system.start_time
    end_time = system.end_time
    #Is there a time range constraint?
    if time_range
      #If so, trim start_time and end_time to fit within the range.
      start_time = [start_time, time_range.first].max
      if end_time
        end_time = [end_time, time_range.last].min
      else
        end_time ||= time_range.last
      end
    else
      end_time ||= current_time
    end
    wall_time = end_time.to_i - start_time.to_i

    num_systems += 1
    avail_cpu_time += system.cores * wall_time
    avail_memory_time += system.memory * wall_time
  end
  
  time_span = 0
  if time_range
    time_span = time_range.last - time_range.first
  else
    time_min = System.minimum(:start_time)
    #Is there actually a minimum start time?
    #(In other words, are there any systems in the database?)
    if time_min
      if System.active.any?
        time_max = current_time
      else
        time_max = System.maximum(:end_time)
      end
      time_span = time_max - time_min
    end
  end
    
  {
    :num_systems => num_systems,
    :avail_cpu_time => avail_cpu_time,
    :avail_memory_time => avail_memory_time,
    :avail_memory_avg => if time_span == 0 then 0.0 else Float(avail_memory_time) / time_span end,
  }
end

Instance Method Details

#decommission(end_time) ⇒ Object

Decommissions the given system, setting its end time to end_time

This should be called any time a system is brought down or its specifications are changed.



149
150
151
152
# File 'lib/bookie/database/system.rb', line 149

def decommission(end_time)
  self.end_time = end_time
  self.save!
end