rbsim 0.0.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (71) hide show
  1. checksums.yaml +7 -0
  2. data/.hgignore +6 -0
  3. data/.rspec +2 -0
  4. data/Gemfile +12 -0
  5. data/Gemfile.lock +66 -0
  6. data/LICENSE.txt +674 -0
  7. data/README.md +960 -0
  8. data/TODO +28 -0
  9. data/basic_sim.rb +62 -0
  10. data/fast-tcpn.rb +3 -0
  11. data/lib/rbsim.rb +14 -0
  12. data/lib/rbsim/dsl.rb +30 -0
  13. data/lib/rbsim/dsl/infrastructure.rb +48 -0
  14. data/lib/rbsim/dsl/mapping.rb +32 -0
  15. data/lib/rbsim/dsl/process.rb +129 -0
  16. data/lib/rbsim/dsl/program.rb +10 -0
  17. data/lib/rbsim/experiment.rb +110 -0
  18. data/lib/rbsim/hlmodel.rb +25 -0
  19. data/lib/rbsim/hlmodel/infrastructure.rb +116 -0
  20. data/lib/rbsim/hlmodel/mapping.rb +5 -0
  21. data/lib/rbsim/hlmodel/process.rb +152 -0
  22. data/lib/rbsim/numeric_units.rb +107 -0
  23. data/lib/rbsim/simulator.rb +184 -0
  24. data/lib/rbsim/statistics.rb +77 -0
  25. data/lib/rbsim/tokens.rb +146 -0
  26. data/lib/rbsim/version.rb +3 -0
  27. data/new_process.rb +49 -0
  28. data/rbsim.gemspec +42 -0
  29. data/show_readme.rb +15 -0
  30. data/sim.rb +142 -0
  31. data/sim_bamboo.rb +251 -0
  32. data/sim_process.rb +83 -0
  33. data/sim_process_dsl.rb +58 -0
  34. data/spec/dsl/infrastructure_nets_spec.rb +39 -0
  35. data/spec/dsl/infrastructure_nodes_spec.rb +72 -0
  36. data/spec/dsl/infrastructure_routes_spec.rb +44 -0
  37. data/spec/dsl/mapping_spec.rb +70 -0
  38. data/spec/dsl/process_spec.rb +56 -0
  39. data/spec/dsl/program_spec.rb +36 -0
  40. data/spec/dsl_and_hlmodel/new_process_spec.rb +235 -0
  41. data/spec/hlmodel/net_spec.rb +112 -0
  42. data/spec/hlmodel/process_spec.rb +242 -0
  43. data/spec/hlmodel/route_spec.rb +47 -0
  44. data/spec/hlmodel/routes_spec.rb +44 -0
  45. data/spec/integration/basic_simulation_spec.rb +104 -0
  46. data/spec/integration/net_spec.rb +44 -0
  47. data/spec/integration/process_spec.rb +117 -0
  48. data/spec/integration/rbsim_spec.rb +40 -0
  49. data/spec/simulator/logger_spec.rb +35 -0
  50. data/spec/simulator/stats_spec.rb +93 -0
  51. data/spec/spec_helper.rb +26 -0
  52. data/spec/statistics_spec.rb +300 -0
  53. data/spec/tcpn/add_route_spec.rb +55 -0
  54. data/spec/tcpn/cpu_spec.rb +53 -0
  55. data/spec/tcpn/map_data_spec.rb +37 -0
  56. data/spec/tcpn/network_spec.rb +163 -0
  57. data/spec/tcpn/register_event_spec.rb +48 -0
  58. data/spec/tcpn/route_to_self_spec.rb +53 -0
  59. data/spec/tcpn/stats_spec.rb +77 -0
  60. data/spec/tokens/data_queue_obsolete.rb +121 -0
  61. data/spec/tokens/data_queue_spec.rb +111 -0
  62. data/spec/units_spec.rb +48 -0
  63. data/tcpn/model.rb +6 -0
  64. data/tcpn/model/add_route.rb +78 -0
  65. data/tcpn/model/application.rb +250 -0
  66. data/tcpn/model/cpu.rb +75 -0
  67. data/tcpn/model/map_data.rb +42 -0
  68. data/tcpn/model/network.rb +108 -0
  69. data/tcpn/model/register_event.rb +89 -0
  70. data/tcpn/model/stats.rb +46 -0
  71. metadata +221 -0
@@ -0,0 +1,25 @@
1
+ require 'rbsim/hlmodel/infrastructure.rb'
2
+ require 'rbsim/hlmodel/process.rb'
3
+ require 'rbsim/hlmodel/mapping.rb'
4
+
5
+ module RBSim
6
+ module HLModel
7
+
8
+ Model = Struct.new :nodes, :nets, :routes, :programs, :processes, :mapping, :simulator do
9
+ def initialize(*)
10
+ super
11
+ self.programs = {}
12
+ self.processes = {}
13
+ self.mapping = Mapping.new
14
+ self.routes = Tokens::RoutesToken.new
15
+ self.each_pair do |attr, val|
16
+ if val.nil?
17
+ self.send "#{attr}=".to_sym, []
18
+ end
19
+ end
20
+ self.simulator = nil
21
+ end
22
+ end
23
+
24
+ end
25
+ end
@@ -0,0 +1,116 @@
1
+ module RBSim
2
+ module HLModel
3
+
4
+ Node = Struct.new :name, :cpus
5
+ class Node
6
+ CPU = Struct.new :performance, :node, :tags
7
+ end
8
+
9
+ class Net
10
+ attr_reader :name, :bw, :delay, :drop, :tags
11
+
12
+ InvalidTypeOfDropParameter = Class.new RuntimeError
13
+ InvalidValueOfDropProbability = Class.new RuntimeError
14
+
15
+ def initialize(name, bw, delay = 0, drop = 0, tags = {})
16
+ @name, @bw, @delay = name, bw, delay
17
+ @drop = drop
18
+ @tags = tags
19
+ unless @drop.kind_of?(Proc) || @drop.kind_of?(Numeric)
20
+ raise InvalidTypeOfDropParameter.new(@drop.class)
21
+ end
22
+ if @drop.kind_of? Numeric
23
+ if @drop > 1 || @drop < 0
24
+ raise InvalidValueOfDropProbability.new(@drop)
25
+ end
26
+ end
27
+ @drop_next = drop_next?
28
+ end
29
+
30
+ def drop?
31
+ drop_this = @drop_next
32
+ @drop_next = drop_next?
33
+ drop_this
34
+ end
35
+
36
+ private
37
+
38
+ def drop_next?
39
+ return @drop.call if @drop.kind_of? Proc
40
+
41
+ # a little optimization ;-)
42
+ return true if @drop == 1
43
+ return false if @drop == 0
44
+
45
+ return rand <= @drop
46
+ end
47
+ end
48
+
49
+ class Routes
50
+ def initialize
51
+ @routes = {}
52
+ end
53
+
54
+ def add(route)
55
+ key = [route.src, route.dst]
56
+ @routes[key] ||= []
57
+ @routes[key] << route
58
+ if route.twoway?
59
+ @routes[key.reverse] ||= []
60
+ @routes[key.reverse] << route
61
+ end
62
+ end
63
+ alias << add
64
+
65
+ def find(src, dst)
66
+ routes = @routes[[src, dst]]
67
+ return nil if routes.nil?
68
+ route = if routes.size == 1
69
+ routes.first
70
+ else
71
+ routes[rand(routes.size)]
72
+ end
73
+ if route.src == src
74
+ route
75
+ else
76
+ route.reverse!
77
+ end
78
+ end
79
+
80
+ end
81
+
82
+ Route = Struct.new :src, :dst, :via, :twoway do
83
+
84
+ CannotReverseOnewayRoute = Class.new RuntimeError
85
+
86
+ def initialize(*)
87
+ super
88
+ self.twoway = false unless self.twoway
89
+ @net_number = 0
90
+ end
91
+
92
+ def next_net
93
+ raise StopIteration if @net_number >= via.length
94
+ net = via[@net_number]
95
+ @net_number += 1
96
+ net
97
+ end
98
+
99
+ def has_next_net?
100
+ @net_number < via.length
101
+ end
102
+
103
+ def reverse!
104
+ raise CannotReverseOnewayRoute unless self.twoway
105
+ self.via = self.via.reverse
106
+ self.src, self.dst = self.dst, self.src
107
+ @net_enum = nil
108
+ self
109
+ end
110
+
111
+ def twoway?
112
+ twoway
113
+ end
114
+ end
115
+ end
116
+ end
@@ -0,0 +1,5 @@
1
+ module RBSim
2
+ module HLModel
3
+ Mapping = Class.new Hash
4
+ end
5
+ end
@@ -0,0 +1,152 @@
1
+ module RBSim
2
+ module HLModel
3
+
4
+ class Process
5
+ NoHandlerForUserEvent = Class.new RuntimeError
6
+ InvalidEventType = Class.new RuntimeError
7
+ InvalidSystemEventPassed = Class.new RuntimeError
8
+ NoEventToServe = Class.new RuntimeError
9
+ NotAssignedToNode = Class.new RuntimeError
10
+
11
+ attr_reader :name, :program, :functions, :tags
12
+ attr_accessor :node
13
+ # to how many fragments should be
14
+ # divided data sent by this process?
15
+ attr_accessor :data_fragmentation
16
+
17
+ alias id name
18
+
19
+ # +name+: name of this process used to assign it to a node
20
+ # +program+: name of program running in this process (if any name was given); this is just for information
21
+ def initialize(name, program = nil, tags = {})
22
+ @event_handlers = { data_received: generic_data_received_handler }
23
+ @event_queue = []
24
+ @name = name
25
+ @program = program
26
+ @tags = tags || {}
27
+ @functions = {}
28
+ @data_fragmentation = 1
29
+ end
30
+
31
+ # define a new reusable function for this program
32
+ def function(name, &block)
33
+ @functions[name] = block
34
+ end
35
+
36
+ # define event handler
37
+ def on_event(event, &block)
38
+ @event_handlers[event] = block
39
+ end
40
+
41
+ # event just happened
42
+ def enqueue_event(event, args = nil)
43
+ handler = nil # system event has no handling code defined here
44
+ # it must be handled by somethig external, like
45
+ # TCPN transition!
46
+ unless system_event_names.include? event
47
+ handler = @event_handlers[event]
48
+ if handler.nil?
49
+ raise NoHandlerForUserEvent.new(event)
50
+ end
51
+ end
52
+ @event_queue << { name: event, block: handler, args: args }
53
+ end
54
+
55
+ # serve first event if it is a user event
56
+ def serve_user_event
57
+ check_if_assigned!
58
+ if has_system_event?
59
+ raise InvalidEventType.new("#{@event_queue.first[:name]} is not a user event!")
60
+ end
61
+ event = @event_queue.shift
62
+ event[:block].call self, event[:args]
63
+ end
64
+
65
+ # serve first event if it is a system event
66
+ def serve_system_event(name)
67
+ check_if_assigned!
68
+ unless has_event?
69
+ raise NoEventToServe.new
70
+ end
71
+ unless has_event? name
72
+ raise InvalidSystemEventPassed.new("You tried to serve: #{name}, but first in queue is: #{@event_queue.first[:name]}")
73
+ end
74
+ if has_user_event?
75
+ raise InvalidEventType.new("#{@event_queue.first[:name]} is not a system event!")
76
+ end
77
+ event = @event_queue.shift
78
+ { name: event[:name], args: event[:args] }
79
+ end
80
+
81
+ # is there a user event to serve?
82
+ def has_user_event?
83
+ return false unless has_event?
84
+ return false if system_event_names.include? @event_queue.first[:name]
85
+ true
86
+ end
87
+
88
+ # is there a system event to serve?
89
+ def has_system_event?
90
+ return false unless has_event?
91
+ return true if system_event_names.include? @event_queue.first[:name]
92
+ false
93
+ end
94
+
95
+ # is there any event to serve?
96
+ def has_event?(name = nil)
97
+ return false if @event_queue.empty?
98
+ return true if name == nil
99
+ return true if @event_queue.first[:name] == name
100
+ false
101
+ end
102
+
103
+ # returns name of the first event in queue
104
+ def first_event
105
+ event = @event_queue.first
106
+ if event.nil?
107
+ nil
108
+ else
109
+ event[:name]
110
+ end
111
+ end
112
+
113
+ def handlers_size
114
+ @event_handlers.size
115
+ end
116
+
117
+ def event_queue_size
118
+ @event_queue.size
119
+ end
120
+
121
+ # system events need special handling, by special
122
+ # transitions, e.g. :cpu event requires a cpu token
123
+ # :delay_for requires change in timestamp, etc.
124
+ # handlers for system events must be implemented
125
+ # in simulator!
126
+ def system_event_names
127
+ [ :cpu, :delay_for, :send_data, :new_process, :register_event, :log, :stats_start, :stats_stop, :stats, :stats_save ]
128
+ end
129
+
130
+ # Start new process on the same node
131
+ # used when creating new process during simulation
132
+ def new(program = nil)
133
+ self.class.new @node, program
134
+ end
135
+
136
+ private
137
+ def check_if_assigned!
138
+ if @node.nil?
139
+ raise NotAssignedToNode.new("process #{@name} will not serve events until assigned to a node!")
140
+ end
141
+ end
142
+
143
+ def generic_data_received_handler
144
+ proc do |process, data|
145
+ STDERR.puts "WARNING! Process #{process.name} on node #{process.node} received data, but has no handler defined! Define handler for event :data_received! Data packege will be dropped! Received data: #{data}"
146
+ end
147
+ end
148
+
149
+ end
150
+
151
+ end
152
+ end
@@ -0,0 +1,107 @@
1
+ #
2
+ # This file extends Ruby Numeric class by methods
3
+ # used in RBSim to desciribe units of time,
4
+ # data volume and network speed.
5
+ #
6
+ # Native data volume unit is one bit
7
+ # Native network speed unit is one bit per second
8
+ # Time unit is one jiffie, defined by Numric::RBSIM_JIFFIES_PER_SECOND
9
+ class Numeric
10
+
11
+ #
12
+ # Time units
13
+ #
14
+
15
+
16
+ # Defines unit of time -- jiffie is the smallest
17
+ # time unit that can be accounted by simulator
18
+ RBSIM_JIFFIES_PER_SECOND = 1000000
19
+
20
+ def seconds
21
+ self * RBSIM_JIFFIES_PER_SECOND
22
+ end
23
+
24
+ def miliseconds
25
+ self.seconds / 1000
26
+ end
27
+
28
+ def microseconds
29
+ self.miliseconds / 1000
30
+ end
31
+
32
+ def minutes
33
+ self.seconds * 60
34
+ end
35
+
36
+ def hours
37
+ self.minutes * 60
38
+ end
39
+
40
+ def days
41
+ self.hours * 24
42
+ end
43
+
44
+ def in_seconds
45
+ self / RBSIM_JIFFIES_PER_SECOND
46
+ end
47
+
48
+ def in_miliseconds
49
+ self * 1000 / RBSIM_JIFFIES_PER_SECOND
50
+ end
51
+
52
+ def in_microseconds
53
+ self * 1000 * 1000 / RBSIM_JIFFIES_PER_SECOND
54
+ end
55
+
56
+ def in_minutes
57
+ self.in_seconds / 60
58
+ end
59
+
60
+ def in_hours
61
+ self.in_minutes / 60
62
+ end
63
+
64
+ def in_days
65
+ self.in_hours / 24
66
+ end
67
+
68
+ #
69
+ # Data volume units
70
+ #
71
+
72
+ def bytes
73
+ self * 8
74
+ end
75
+
76
+ def in_bytes
77
+ self / 8
78
+ end
79
+
80
+ def in_bits
81
+ self
82
+ end
83
+
84
+ def bits
85
+ self
86
+ end
87
+
88
+ #
89
+ # Network throuput units
90
+ #
91
+
92
+ def bps
93
+ self.to_f.in_seconds
94
+ end
95
+
96
+ def Bps
97
+ (self * 8).to_f.in_seconds
98
+ end
99
+
100
+ def in_bps
101
+ self.seconds
102
+ end
103
+
104
+ def in_Bps
105
+ (self / 8).seconds
106
+ end
107
+ end
@@ -0,0 +1,184 @@
1
+ module RBSim
2
+
3
+ class Simulator
4
+ attr_reader :clock
5
+ # To how many fragments should be divided data
6
+ # sent over the network?
7
+ attr_accessor :data_fragmentation
8
+
9
+ # Create new simulator
10
+ # +block+ defines new model
11
+ # +params+ will be passed as block parameter to the model
12
+ def initialize(params = {}, &block)
13
+ @block = block
14
+ @params = params
15
+ @logger = default_logger
16
+ @stats_collector = Statistics.new
17
+ @resource_stats_collector = Statistics.new
18
+ @clock = 0
19
+ @data_fragmentation = 1
20
+ end
21
+
22
+ def data_fragmentation=(d)
23
+ raise "TCPN already initialized. Too late to set data fragmentation" unless @tcpn.nil?
24
+ @data_fragmentation = d
25
+ end
26
+
27
+ def run
28
+ simulator.run
29
+ end
30
+
31
+ def stop
32
+ simulator.stop
33
+ end
34
+
35
+ def hlmodel
36
+ if @hlmodel.nil?
37
+ @hlmodel = RBSim::HLModel::Model.new
38
+ @hlmodel.simulator = self
39
+ Docile.dsl_eval(RBSim::DSL.new(@hlmodel), @params, &@block)
40
+ end
41
+ @hlmodel
42
+ end
43
+
44
+ def tcpn
45
+ if @tcpn.nil?
46
+ @tcpn = FastTCPN.read File.expand_path '../../../tcpn/model.rb', __FILE__
47
+ hlmodel.nets.each do |net|
48
+ @tcpn.add_marking_for 'net', net
49
+ end
50
+
51
+ hlmodel.nodes.each do |node|
52
+ node.cpus.each do |cpu|
53
+ cpu.node = node.name
54
+ @tcpn.add_marking_for 'CPU', cpu
55
+ end
56
+ end
57
+
58
+ hlmodel.processes.each do |name, process|
59
+ process.node = hlmodel.mapping[name]
60
+ process.data_fragmentation = data_fragmentation
61
+ @tcpn.add_marking_for 'process', process
62
+ @tcpn.add_marking_for 'data to receive', Tokens::DataQueueToken.new(process.name, process.tags)
63
+ end
64
+
65
+
66
+ @tcpn.add_marking_for 'routes', hlmodel.routes
67
+ @tcpn.add_marking_for 'mapping', hlmodel.mapping
68
+
69
+
70
+ end
71
+
72
+ @tcpn
73
+ end
74
+
75
+ def simulator
76
+ if @simulator.nil?
77
+ @simulator = tcpn
78
+
79
+ set_logger_callbacks
80
+ set_stats_collector_callbacks
81
+ set_clock_callbacks
82
+
83
+ end
84
+ @simulator
85
+ end
86
+
87
+ def logger(&block)
88
+ @logger = block
89
+ end
90
+
91
+ def stats
92
+ { application: @stats_collector, resources: @resource_stats_collector }
93
+ end
94
+
95
+ # FIXME: not tested!
96
+ def stats_save(file)
97
+ File.open file, 'w' do |f|
98
+ f.puts Marshal.dump(stats_data)
99
+ end
100
+ end
101
+
102
+ private
103
+
104
+ def default_logger
105
+ proc do |clock, message|
106
+ puts "%.3f: #{message}" % (clock.to_f.in_seconds)
107
+ end
108
+ end
109
+
110
+ def set_logger_callbacks
111
+ @simulator.cb_for :transition, :after do |t, e|
112
+ if e.transition == 'event::log'
113
+ message = e.binding['process'].value.serve_system_event(:log)[:args]
114
+ @logger.call e.clock, message
115
+ end
116
+ end
117
+ end
118
+
119
+ def set_stats_collector_callbacks
120
+ @simulator.cb_for :transition, :after do |t, e|
121
+ if e.transition == "event::stats"
122
+ process = e.binding['process'].value
123
+ event = process.first_event
124
+ params = process.serve_system_event(event)[:args]
125
+ @stats_collector.event event.to_s.sub(/^stats_/,'').to_sym, params, e.clock
126
+ elsif e.transition == "event::cpu"
127
+ cpu = e.binding['CPU'].value
128
+ @resource_stats_collector.event :start, cpu.tags.merge({ resource: 'CPU', node: cpu.node }), e.clock
129
+ elsif e.transition == "event::cpu_finished"
130
+ cpu = e.binding['working CPU'].value[:cpu]
131
+ @resource_stats_collector.event :stop, cpu.tags.merge({ resource: 'CPU', node: cpu.node }), e.clock
132
+ elsif e.transition == "transmitted"
133
+ process = e.binding['data after net'].value.dst
134
+ queue = e.binding['data to receive'].value
135
+ @resource_stats_collector.event :start, queue.process_tags.merge({ resource: 'DATAQ WAIT', process: process }), e.clock
136
+ elsif e.transition == "event::data_received"
137
+ process = e.binding['process'].value
138
+ @resource_stats_collector.event :stop, process.tags.merge({ resource: 'DATAQ WAIT', process: process.name }), e.clock
139
+ elsif e.transition == "net"
140
+ net = e.binding['net'].value
141
+ dropped = e.binding['net'].value.drop?
142
+ if dropped
143
+ @resource_stats_collector.event :stats, net.tags.merge({ event: 'NET DROP', net: net.name }), e.clock
144
+ end
145
+ end
146
+ end
147
+
148
+ @simulator.cb_for :place, :remove do |t, e|
149
+ if e.place == 'net'
150
+ net = e.tokens.first.value
151
+ @resource_stats_collector.event :start, net.tags.merge({ resource: 'NET', name: net.name }), e.clock
152
+ end
153
+ end
154
+ @simulator.cb_for :place, :add do |t, e|
155
+ if e.place == 'net'
156
+ net = e.tokens.first[:val]
157
+ ts = e.tokens.first[:ts]
158
+ @resource_stats_collector.event :stop, net.tags.merge({ resource: 'NET', name: net.name }), ts
159
+ elsif e.place == 'data to receive'
160
+ queue = e.tokens.first[:val]
161
+ process_name = queue.process_name
162
+ process_tags = queue.process_tags
163
+ @resource_stats_collector.event :save,
164
+ { value: queue.length, tags: process_tags.merge({ resource: 'DATAQ LEN', process: process_name }) },
165
+ e.tcpn.clock
166
+ end
167
+ end
168
+ end
169
+
170
+ def set_clock_callbacks
171
+ @simulator.cb_for :clock, :after do |t, e|
172
+ self.clock = e.clock
173
+ end
174
+ end
175
+
176
+ def clock=(clock)
177
+ @clock = clock
178
+ @stats_collector.clock = clock
179
+ @resource_stats_collector.clock = clock
180
+ end
181
+
182
+ end
183
+
184
+ end