rbsim 0.0.3

Sign up to get free protection for your applications and to get access to all the features.
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,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
@@ -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
@@ -0,0 +1,3 @@
1
+ module RBSim
2
+ VERSION = '0.0.3'
3
+ 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
+