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,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
+