rbsim 0.0.3
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.hgignore +6 -0
- data/.rspec +2 -0
- data/Gemfile +12 -0
- data/Gemfile.lock +66 -0
- data/LICENSE.txt +674 -0
- data/README.md +960 -0
- data/TODO +28 -0
- data/basic_sim.rb +62 -0
- data/fast-tcpn.rb +3 -0
- data/lib/rbsim.rb +14 -0
- data/lib/rbsim/dsl.rb +30 -0
- data/lib/rbsim/dsl/infrastructure.rb +48 -0
- data/lib/rbsim/dsl/mapping.rb +32 -0
- data/lib/rbsim/dsl/process.rb +129 -0
- data/lib/rbsim/dsl/program.rb +10 -0
- data/lib/rbsim/experiment.rb +110 -0
- data/lib/rbsim/hlmodel.rb +25 -0
- data/lib/rbsim/hlmodel/infrastructure.rb +116 -0
- data/lib/rbsim/hlmodel/mapping.rb +5 -0
- data/lib/rbsim/hlmodel/process.rb +152 -0
- data/lib/rbsim/numeric_units.rb +107 -0
- data/lib/rbsim/simulator.rb +184 -0
- data/lib/rbsim/statistics.rb +77 -0
- data/lib/rbsim/tokens.rb +146 -0
- data/lib/rbsim/version.rb +3 -0
- data/new_process.rb +49 -0
- data/rbsim.gemspec +42 -0
- data/show_readme.rb +15 -0
- data/sim.rb +142 -0
- data/sim_bamboo.rb +251 -0
- data/sim_process.rb +83 -0
- data/sim_process_dsl.rb +58 -0
- data/spec/dsl/infrastructure_nets_spec.rb +39 -0
- data/spec/dsl/infrastructure_nodes_spec.rb +72 -0
- data/spec/dsl/infrastructure_routes_spec.rb +44 -0
- data/spec/dsl/mapping_spec.rb +70 -0
- data/spec/dsl/process_spec.rb +56 -0
- data/spec/dsl/program_spec.rb +36 -0
- data/spec/dsl_and_hlmodel/new_process_spec.rb +235 -0
- data/spec/hlmodel/net_spec.rb +112 -0
- data/spec/hlmodel/process_spec.rb +242 -0
- data/spec/hlmodel/route_spec.rb +47 -0
- data/spec/hlmodel/routes_spec.rb +44 -0
- data/spec/integration/basic_simulation_spec.rb +104 -0
- data/spec/integration/net_spec.rb +44 -0
- data/spec/integration/process_spec.rb +117 -0
- data/spec/integration/rbsim_spec.rb +40 -0
- data/spec/simulator/logger_spec.rb +35 -0
- data/spec/simulator/stats_spec.rb +93 -0
- data/spec/spec_helper.rb +26 -0
- data/spec/statistics_spec.rb +300 -0
- data/spec/tcpn/add_route_spec.rb +55 -0
- data/spec/tcpn/cpu_spec.rb +53 -0
- data/spec/tcpn/map_data_spec.rb +37 -0
- data/spec/tcpn/network_spec.rb +163 -0
- data/spec/tcpn/register_event_spec.rb +48 -0
- data/spec/tcpn/route_to_self_spec.rb +53 -0
- data/spec/tcpn/stats_spec.rb +77 -0
- data/spec/tokens/data_queue_obsolete.rb +121 -0
- data/spec/tokens/data_queue_spec.rb +111 -0
- data/spec/units_spec.rb +48 -0
- data/tcpn/model.rb +6 -0
- data/tcpn/model/add_route.rb +78 -0
- data/tcpn/model/application.rb +250 -0
- data/tcpn/model/cpu.rb +75 -0
- data/tcpn/model/map_data.rb +42 -0
- data/tcpn/model/network.rb +108 -0
- data/tcpn/model/register_event.rb +89 -0
- data/tcpn/model/stats.rb +46 -0
- metadata +221 -0
@@ -0,0 +1,77 @@
|
|
1
|
+
module RBSim
|
2
|
+
class Statistics
|
3
|
+
UnknownStatsType = Class.new RuntimeError
|
4
|
+
attr_accessor :clock
|
5
|
+
|
6
|
+
def initialize
|
7
|
+
@counter_events = {}
|
8
|
+
@duration_events = {}
|
9
|
+
@saved_values = {}
|
10
|
+
@clock = 0
|
11
|
+
end
|
12
|
+
|
13
|
+
def event(type, params, time)
|
14
|
+
if type == :stats
|
15
|
+
@counter_events[params] ||= []
|
16
|
+
@counter_events[params] << time
|
17
|
+
elsif type == :save
|
18
|
+
tags = params[:tags]
|
19
|
+
value = params[:value]
|
20
|
+
@saved_values[tags] ||= {}
|
21
|
+
@saved_values[tags][time] ||= []
|
22
|
+
@saved_values[tags][time] << value
|
23
|
+
else
|
24
|
+
raise UnknownStatsType.new(type) unless [:start, :stop].include? type
|
25
|
+
@duration_events[params] ||= {}
|
26
|
+
@duration_events[params][type] ||= []
|
27
|
+
@duration_events[params][type] << time
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
def durations(filters = {})
|
32
|
+
return enum_for(:durations, filters) unless block_given?
|
33
|
+
data = @duration_events.select &events_filter(filters)
|
34
|
+
data.each do |tags, times|
|
35
|
+
starts_and_stops = times[:start].zip times[:stop]
|
36
|
+
starts_and_stops.each do |start, stop|
|
37
|
+
yield tags, start, stop
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
def counters(filters = {})
|
43
|
+
return enum_for(:counters, filters) unless block_given?
|
44
|
+
data = @counter_events.select &events_filter(filters)
|
45
|
+
data.each do |tags, events|
|
46
|
+
yield tags, events
|
47
|
+
end
|
48
|
+
end
|
49
|
+
|
50
|
+
def values(filters = {})
|
51
|
+
return enum_for(:values, filters) unless block_given?
|
52
|
+
data = @saved_values.select &events_filter(filters)
|
53
|
+
data.each do |tags, times_and_values|
|
54
|
+
times_and_values.each do |time, values|
|
55
|
+
yield tags, time, values
|
56
|
+
end
|
57
|
+
end
|
58
|
+
end
|
59
|
+
|
60
|
+
private
|
61
|
+
|
62
|
+
# generic event filter for all statistics
|
63
|
+
def events_filter(filters)
|
64
|
+
lambda do |tags, event_list|
|
65
|
+
filters.reduce(true) do |acc, filter_item|
|
66
|
+
filter_key, filter_value = filter_item;
|
67
|
+
if filter_value.is_a? Regexp
|
68
|
+
acc && tags.has_key?(filter_key) && tags[filter_key].to_s =~ filter_value
|
69
|
+
else
|
70
|
+
acc && tags[filter_key] == filter_value
|
71
|
+
end
|
72
|
+
end
|
73
|
+
end
|
74
|
+
end
|
75
|
+
|
76
|
+
end
|
77
|
+
end
|
data/lib/rbsim/tokens.rb
ADDED
@@ -0,0 +1,146 @@
|
|
1
|
+
require 'rbsim/hlmodel'
|
2
|
+
|
3
|
+
module RBSim
|
4
|
+
module Tokens
|
5
|
+
|
6
|
+
class ProcessToken < HLModel::Process
|
7
|
+
#include TCPN::TokenMethods
|
8
|
+
end
|
9
|
+
|
10
|
+
class RoutesToken < HLModel::Routes
|
11
|
+
#include TCPN::TokenMethods
|
12
|
+
end
|
13
|
+
|
14
|
+
class NetToken < HLModel::Net
|
15
|
+
#include TCPN::TokenMethods
|
16
|
+
end
|
17
|
+
|
18
|
+
class CPUToken < HLModel::Node::CPU
|
19
|
+
#include TCPN::TokenMethods
|
20
|
+
end
|
21
|
+
|
22
|
+
class Data
|
23
|
+
IncompleteDataDefinition = Class.new RuntimeError
|
24
|
+
attr_reader :data_id # id of data required to collect fragments
|
25
|
+
attr_reader :src, :dst, :src_node, :size, :type, :content, :id
|
26
|
+
attr_accessor :dst_node, :route
|
27
|
+
attr_accessor :fragments # no. of fragments this data was between
|
28
|
+
|
29
|
+
def initialize(data_id, node, process, opts)
|
30
|
+
@data_id = data_id
|
31
|
+
@src_node = node
|
32
|
+
@src = process
|
33
|
+
@dst = opts[:to]
|
34
|
+
[ :size, :type, :content].each do |a|
|
35
|
+
self.instance_variable_set "@#{a}".to_sym, opts[a]
|
36
|
+
end
|
37
|
+
if @size.nil?
|
38
|
+
raise IncompleteDataDefinition.new("Must define size of data package!");
|
39
|
+
end
|
40
|
+
if @dst.nil?
|
41
|
+
raise IncompleteDataDefinition.new("Must define destination of data package!");
|
42
|
+
end
|
43
|
+
@id = self.object_id
|
44
|
+
end
|
45
|
+
|
46
|
+
def to_s
|
47
|
+
v = [:src, :dst, :src_node, :size, :type, :content].map do |k|
|
48
|
+
"#{k}: #{self.send(k).inspect}"
|
49
|
+
end.join ', '
|
50
|
+
"{#{v}}"
|
51
|
+
end
|
52
|
+
|
53
|
+
def ==(o)
|
54
|
+
return false unless o.kind_of? Data
|
55
|
+
o.id == id
|
56
|
+
end
|
57
|
+
|
58
|
+
def has_next_net?
|
59
|
+
@route.has_next_net?
|
60
|
+
end
|
61
|
+
|
62
|
+
end
|
63
|
+
|
64
|
+
class DataToken < Data
|
65
|
+
#include TCPN::TokenMethods
|
66
|
+
end
|
67
|
+
|
68
|
+
class DataQueue
|
69
|
+
class CannotEnqueueDataError < RuntimeError
|
70
|
+
def initialize(data, message)
|
71
|
+
super message
|
72
|
+
@data = data
|
73
|
+
end
|
74
|
+
|
75
|
+
def to_s
|
76
|
+
super + " " + @data.inspect
|
77
|
+
end
|
78
|
+
end
|
79
|
+
|
80
|
+
attr_reader :process_name, :process_tags
|
81
|
+
def initialize(process_name, process_tags = {})
|
82
|
+
@process_name = process_name
|
83
|
+
@process_tags = process_tags
|
84
|
+
@queue = []
|
85
|
+
@incomplete_data = Hash.new { { fragments: 0, data: nil } }
|
86
|
+
end
|
87
|
+
|
88
|
+
def put(o)
|
89
|
+
if o.fragments.nil?
|
90
|
+
raise CannotEnqueueDataError.new(o, "Tried to enqueue data without fragment count set!")
|
91
|
+
end
|
92
|
+
enqueue_fragment(o)
|
93
|
+
check_if_complete(o)
|
94
|
+
end
|
95
|
+
|
96
|
+
def get
|
97
|
+
@queue.shift
|
98
|
+
end
|
99
|
+
|
100
|
+
def length
|
101
|
+
@queue.length
|
102
|
+
end
|
103
|
+
|
104
|
+
def empty?
|
105
|
+
length == 0
|
106
|
+
end
|
107
|
+
|
108
|
+
private
|
109
|
+
|
110
|
+
def enqueue_fragment(o)
|
111
|
+
already_received = @incomplete_data[o.data_id]
|
112
|
+
already_received[:fragments] += 1
|
113
|
+
already_received[:data] ||= o
|
114
|
+
@incomplete_data[o.data_id] = already_received
|
115
|
+
end
|
116
|
+
|
117
|
+
def check_if_complete(o)
|
118
|
+
already_received = @incomplete_data[o.data_id]
|
119
|
+
if already_received[:fragments] == o.fragments
|
120
|
+
@incomplete_data.delete o.data_id
|
121
|
+
@queue << already_received[:data]
|
122
|
+
end
|
123
|
+
end
|
124
|
+
|
125
|
+
end
|
126
|
+
|
127
|
+
class DataQueueToken < DataQueue
|
128
|
+
#include TCPN::TokenMethods
|
129
|
+
end
|
130
|
+
|
131
|
+
|
132
|
+
class Event
|
133
|
+
attr_reader :process_id, :name, :args
|
134
|
+
def initialize(process_id, name, args)
|
135
|
+
@process_id = process_id
|
136
|
+
@name = name
|
137
|
+
@args = args
|
138
|
+
end
|
139
|
+
end
|
140
|
+
|
141
|
+
class EventToken < Event
|
142
|
+
#include TCPN::TokenMethods
|
143
|
+
end
|
144
|
+
|
145
|
+
end
|
146
|
+
end
|
data/new_process.rb
ADDED
@@ -0,0 +1,49 @@
|
|
1
|
+
require 'rbsim'
|
2
|
+
|
3
|
+
# new_process statement example usage
|
4
|
+
# use this to create specs!
|
5
|
+
# This will be a kind of integration specs for dsl and hlmodel:
|
6
|
+
# Test behavior of hlmodel defined by the DSL statements
|
7
|
+
|
8
|
+
model = RBSim.dsl do
|
9
|
+
new_process :worker do
|
10
|
+
on_event :data do |volume|
|
11
|
+
delay_for 100
|
12
|
+
cpu do |c|
|
13
|
+
12/c.performance
|
14
|
+
end
|
15
|
+
end
|
16
|
+
delay_for 100
|
17
|
+
cpu do |cpu|
|
18
|
+
100/cpu.performance
|
19
|
+
end
|
20
|
+
register_event :data, 1000
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
puts "Model: "
|
25
|
+
p model
|
26
|
+
|
27
|
+
puts "="*60
|
28
|
+
puts "Serving process events"
|
29
|
+
p = model.processes[:worker]
|
30
|
+
p.node = :node01
|
31
|
+
|
32
|
+
puts "-"*60
|
33
|
+
e = p.serve_system_event :delay_for
|
34
|
+
puts "delay_for: #{e.inspect}"
|
35
|
+
|
36
|
+
|
37
|
+
puts "-"*60
|
38
|
+
cpu = Object.new
|
39
|
+
def cpu.performance
|
40
|
+
20
|
41
|
+
end
|
42
|
+
e = p.serve_system_event :cpu
|
43
|
+
puts "cpu: #{e.inspect} computed CPU delay: #{e[:args][:block].call cpu}"
|
44
|
+
|
45
|
+
puts "-"*60
|
46
|
+
puts "Process after serving user event :data (note event_queue modified by this event)"
|
47
|
+
p.serve_user_event # returns process object (self)!
|
48
|
+
puts p.inspect
|
49
|
+
|
data/rbsim.gemspec
ADDED
@@ -0,0 +1,42 @@
|
|
1
|
+
# Used this istruction to create the gem:
|
2
|
+
# http://guides.rubygems.org/make-your-own-gem/
|
3
|
+
# some things below were based on docile gem.
|
4
|
+
|
5
|
+
$:.push File.expand_path('../lib', __FILE__)
|
6
|
+
require 'rbsim/version'
|
7
|
+
|
8
|
+
Gem::Specification.new do |s|
|
9
|
+
s.name = 'rbsim'
|
10
|
+
s.version = RBSim::VERSION
|
11
|
+
s.authors = ['Wojciech Rząsa']
|
12
|
+
s.email = %w(me@wojciechrzasa.pl)
|
13
|
+
s.homepage = 'https://github.com/wrzasa/rbsim'
|
14
|
+
s.summary = 'Distributed inftastructure and system simulator with convenient DSL.'
|
15
|
+
s.description = 'You can model your distributed infrastructora and application and simulate its behavior and observe its efficiency easily.'
|
16
|
+
s.license = 'GPL-3.0'
|
17
|
+
|
18
|
+
s.platform = 'ruby'
|
19
|
+
s.required_ruby_version = '~> 2.0'
|
20
|
+
|
21
|
+
# s.rubyforge_project = ''
|
22
|
+
|
23
|
+
s.files = `git ls-files -z`.split("\x0")
|
24
|
+
s.test_files = s.files.grep(%r{^(test|spec|features)/})
|
25
|
+
s.executables = s.files.grep(%r{^bin/}) { |f| File.basename(f) }
|
26
|
+
s.require_paths = %w(lib)
|
27
|
+
|
28
|
+
s.add_runtime_dependency 'docile', '~> 1.1'
|
29
|
+
s.add_runtime_dependency 'fast-tcpn', '~> 0'
|
30
|
+
|
31
|
+
# Running rspec tests from rake
|
32
|
+
s.add_development_dependency 'rspec', '~> 3.1'
|
33
|
+
s.add_development_dependency 'rspec-its', '~> 1.0'
|
34
|
+
s.add_development_dependency 'simplecov', '~> 0'
|
35
|
+
|
36
|
+
s.extra_rdoc_files << 'README.md'
|
37
|
+
s.rdoc_options << '--main' << 'README.md'
|
38
|
+
s.rdoc_options << '--title' << 'RBSim -- Distributed system modeling and simulation tool'
|
39
|
+
s.rdoc_options << '--line-numbers'
|
40
|
+
s.rdoc_options << '-A'
|
41
|
+
s.rdoc_options << '-x coverage'
|
42
|
+
end
|
data/show_readme.rb
ADDED
@@ -0,0 +1,15 @@
|
|
1
|
+
if RUBY_ENGINE == 'jruby'
|
2
|
+
puts "Unfortunatelly as far as I know markdown does not work in JRuby, so this script won't work too."
|
3
|
+
exit
|
4
|
+
end
|
5
|
+
|
6
|
+
|
7
|
+
begin
|
8
|
+
require 'sinatra'
|
9
|
+
|
10
|
+
get '/' do
|
11
|
+
markdown File.read('README.md')
|
12
|
+
end
|
13
|
+
rescue LoadError
|
14
|
+
puts "This script requires sinatra gem. You have to do:\ngem install sinatra"
|
15
|
+
end
|
data/sim.rb
ADDED
@@ -0,0 +1,142 @@
|
|
1
|
+
require 'rbsim'
|
2
|
+
|
3
|
+
model = RBSim.model do
|
4
|
+
|
5
|
+
program :wget do |opts|
|
6
|
+
sent = 0
|
7
|
+
on_event :send do
|
8
|
+
cpu do |cpu|
|
9
|
+
(150 / cpu.performance).miliseconds
|
10
|
+
end
|
11
|
+
send_data to: opts[:target], size: 1024.bytes, type: :request, content: sent
|
12
|
+
log "Sent data in process #{process.name} #{sent}"
|
13
|
+
sent += 1
|
14
|
+
# delay_for 5.miliseconds
|
15
|
+
register_event :send, delay: 5.miliseconds if sent < opts[:count]
|
16
|
+
end
|
17
|
+
|
18
|
+
on_event :data_received do |data|
|
19
|
+
log "Got data #{data} in process #{process.name}"
|
20
|
+
stats :request_served, process.name
|
21
|
+
end
|
22
|
+
|
23
|
+
register_event :send
|
24
|
+
end
|
25
|
+
|
26
|
+
program :apache_static do
|
27
|
+
on_event :data_received do |data|
|
28
|
+
log "Got #{data.type} from: #{data.src}, size: #{data.size}, content: #{data.content}"
|
29
|
+
cpu do |cpu|
|
30
|
+
(100 * data.size.in_bytes / cpu.performance).miliseconds
|
31
|
+
end
|
32
|
+
send_data to: data.src, size: data.size * 10, type: :response, content: data.content
|
33
|
+
log "Responded to: #{data.src} with content: #{data.content}"
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
program :apache_php do |name|
|
38
|
+
on_event :data_received do |data|
|
39
|
+
stats_start :working, name
|
40
|
+
log "#{name} start #{data.type} from #{data.src} #{data.content}"
|
41
|
+
if data.type == :request
|
42
|
+
cpu do |cpu|
|
43
|
+
(100 * data.size.in_bytes / cpu.performance).miliseconds
|
44
|
+
end
|
45
|
+
send_data to: :db, size: data.size/10, type: :sql, content: { client: data.src, content: data.content }
|
46
|
+
else
|
47
|
+
cpu do |cpu|
|
48
|
+
(500*data.size / cpu.performance).miliseconds
|
49
|
+
end
|
50
|
+
send_data to: data.content[:client], size: data.size*2, type: :response, content: data.content[:content]
|
51
|
+
end
|
52
|
+
log "#{name} finished #{data.type} from #{data.src} #{data.content}"
|
53
|
+
stats_stop :working, name
|
54
|
+
end
|
55
|
+
end
|
56
|
+
|
57
|
+
program :mysql do
|
58
|
+
on_event :data_received do |data|
|
59
|
+
stats_start :query, :mysql
|
60
|
+
log "DB start #{data.src} #{data.content}"
|
61
|
+
delay_for (data.size.in_bytes * rand).miliseconds
|
62
|
+
cpu do |cpu|
|
63
|
+
(data.size / 10 * rand).miliseconds
|
64
|
+
end
|
65
|
+
send_data to: data.src, size: data.size*1000, type: :db_response, content: data.content
|
66
|
+
log "DB finish #{data.src} #{data.content}"
|
67
|
+
stats_stop :query, :mysql
|
68
|
+
end
|
69
|
+
end
|
70
|
+
|
71
|
+
node :desktop do
|
72
|
+
cpu 100
|
73
|
+
end
|
74
|
+
|
75
|
+
node :laptop do
|
76
|
+
cpu 100
|
77
|
+
end
|
78
|
+
|
79
|
+
node :gandalf do
|
80
|
+
cpu 140000
|
81
|
+
cpu 140000
|
82
|
+
end
|
83
|
+
|
84
|
+
node :dbserver do
|
85
|
+
cpu 500
|
86
|
+
end
|
87
|
+
|
88
|
+
new_process :client1, program: :wget, args: { target: :server1, count: 10 }
|
89
|
+
new_process :client2, program: :wget, args: { target: :server2, count: 10 }
|
90
|
+
|
91
|
+
new_process :server1, program: :apache_php, args: 'apache1'
|
92
|
+
new_process :server2, program: :apache_php, args: 'apache2'
|
93
|
+
new_process :db, program: :mysql
|
94
|
+
|
95
|
+
net :net01, bw: 1024.bps
|
96
|
+
net :net02, bw: 768.bps
|
97
|
+
net :lan, bw: 10240.bps
|
98
|
+
|
99
|
+
route from: :desktop, to: :gandalf, via: [ :net01, :net02 ], twoway: true
|
100
|
+
route from: :laptop, to: :gandalf, via: [ :net01, :net02 ], twoway: true
|
101
|
+
route from: :gandalf, to: :dbserver, via: [ :lan ], twoway: true
|
102
|
+
|
103
|
+
put :server1, on: :gandalf
|
104
|
+
put :server2, on: :gandalf
|
105
|
+
put :db, on: :dbserver
|
106
|
+
|
107
|
+
put :client1, on: :desktop
|
108
|
+
put :client2, on: :laptop
|
109
|
+
|
110
|
+
end
|
111
|
+
|
112
|
+
# TODO: modify CPU load event handling, to enable random CPU load time!
|
113
|
+
# TODO: potrzebne gotowe narzędzie generujące raport obciążenia poszczególnych zasobów
|
114
|
+
|
115
|
+
# TODO: use this proof-of-concept to embedd logger into RBSim!
|
116
|
+
=begin
|
117
|
+
model.simulator.cb_for :transition, :after do |t, e|
|
118
|
+
# puts ">> #{e.clock} #{e.transition}"##{e.binding.map {|k, v| "#{k}: #{v}" }}" #if e.clock > 90000
|
119
|
+
if e.transition == 'event::log'
|
120
|
+
message = e.binding[:process][:val].serve_system_event(:log)[:args]
|
121
|
+
puts "#{e.clock} #{message}"
|
122
|
+
end
|
123
|
+
end
|
124
|
+
=end
|
125
|
+
|
126
|
+
#model.simulator.cb_for :clock, :after do |t, e|
|
127
|
+
# puts e.clock
|
128
|
+
#end
|
129
|
+
|
130
|
+
=begin
|
131
|
+
model.logger do |clock, message|
|
132
|
+
puts "MY LOGGER: #{clock} #{message}"
|
133
|
+
end
|
134
|
+
=end
|
135
|
+
|
136
|
+
model.run
|
137
|
+
|
138
|
+
#p model.stats_summary
|
139
|
+
#p model.stats_data
|
140
|
+
|
141
|
+
model.stats_print
|
142
|
+
|