sisfc 0.0.1
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.
- checksums.yaml +7 -0
- data/Gemfile +4 -0
- data/LICENSE +20 -0
- data/README.md +55 -0
- data/Rakefile +9 -0
- data/bin/sisfc +32 -0
- data/examples/generator.R +45 -0
- data/examples/simulator.conf +70 -0
- data/examples/vm_allocation.conf +8 -0
- data/lib/sisfc/configuration.rb +60 -0
- data/lib/sisfc/data_center.rb +59 -0
- data/lib/sisfc/evaluation.rb +36 -0
- data/lib/sisfc/event.rb +36 -0
- data/lib/sisfc/generator.rb +80 -0
- data/lib/sisfc/request.rb +122 -0
- data/lib/sisfc/service_type.rb +16 -0
- data/lib/sisfc/simulation.rb +205 -0
- data/lib/sisfc/sorted_array.rb +57 -0
- data/lib/sisfc/statistics.rb +29 -0
- data/lib/sisfc/support/dsl_helper.rb +18 -0
- data/lib/sisfc/version.rb +3 -0
- data/lib/sisfc/vm.rb +86 -0
- data/lib/sisfc.rb +4 -0
- data/sisfc.gemspec +27 -0
- data/test/sisfc/configuration_test.rb +94 -0
- data/test/sisfc/generator_test.rb +63 -0
- data/test/sisfc/reference_configuration.rb +178 -0
- data/test/sisfc/request_test.rb +13 -0
- data/test/test_helper.rb +4 -0
- metadata +148 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: 4db7cb93e8cfd8e52ef91b888359f61d513761a0
|
4
|
+
data.tar.gz: 3370237ff41037080a11ce54c4efb04510d24cab
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 153d84d057e7011351998a401f44139827b72edb6051d274b8ab09bd9aaefc2a61083e26807073bd0e0a9ccfa5b53f5f4f66d654d0415cfa4743ebb2a84d0e90
|
7
|
+
data.tar.gz: f59a60e8c490cca293ef9836fbc990ce56663d55c681f9ecc32017ace690947a6d9ecc06728e5f1fd004762f9c87afeb050a02b790cec82079cc1ae1af244160
|
data/Gemfile
ADDED
data/LICENSE
ADDED
@@ -0,0 +1,20 @@
|
|
1
|
+
The MIT License (MIT)
|
2
|
+
|
3
|
+
Copyright (c) 2014 Mauro Tortonesi
|
4
|
+
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy of
|
6
|
+
this software and associated documentation files (the "Software"), to deal in
|
7
|
+
the Software without restriction, including without limitation the rights to
|
8
|
+
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
|
9
|
+
the Software, and to permit persons to whom the Software is furnished to do so,
|
10
|
+
subject to the following conditions:
|
11
|
+
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
13
|
+
copies or substantial portions of the Software.
|
14
|
+
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
|
17
|
+
FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
|
18
|
+
COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
|
19
|
+
IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
20
|
+
CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README.md
ADDED
@@ -0,0 +1,55 @@
|
|
1
|
+
# A Simulator for IT Service in Federated Clouds (SISFC)
|
2
|
+
|
3
|
+
SISFC is a simulator designed to reenact the behaviour of IT services in
|
4
|
+
federated Cloud environments.
|
5
|
+
|
6
|
+
|
7
|
+
## References
|
8
|
+
|
9
|
+
This simulator (more precisely an earlier version of it) was used in the
|
10
|
+
following research papers:
|
11
|
+
|
12
|
+
1. L. Foschini, M. Tortonesi, "Adaptive and Business-driven Service Placement
|
13
|
+
in Federated Cloud Computing Environments", in Proceedings of the 8th
|
14
|
+
IFIP/IEEE International Workshop on Business-driven IT Management (BDIM 2013),
|
15
|
+
27 May 2013, Ghent, Belgium.
|
16
|
+
|
17
|
+
2. G. Grabarnik, L. Shwartz, M. Tortonesi, "Business-Driven Optimization of
|
18
|
+
Component Placement for Complex Services in Federated Clouds", to appear in
|
19
|
+
Proceedings of the 14th IEEE/IFIP Network Operations and Management Symposium
|
20
|
+
(NOMS 2014) - Mini-conference track, 5-9 May 2014, Krakow, Poland.
|
21
|
+
|
22
|
+
Please, consider citing some of these papers if you find this simulator useful
|
23
|
+
for your research.
|
24
|
+
|
25
|
+
|
26
|
+
## Installation
|
27
|
+
|
28
|
+
You can install the SISFC simulator through RubyGems:
|
29
|
+
|
30
|
+
gem install sisfc
|
31
|
+
|
32
|
+
While SISFC should work on MRI and Rubinius without problems, we highly
|
33
|
+
recommend you to run it on top of JRuby. Since JRuby is our reference
|
34
|
+
development platform, you will be very likely to have a smoother installation
|
35
|
+
and usage experience when deploying SISFC on top of JRuby.
|
36
|
+
|
37
|
+
|
38
|
+
## Usage
|
39
|
+
|
40
|
+
To run the simulator, simply digit:
|
41
|
+
|
42
|
+
sisfc simulator.conf vm_allocation.conf
|
43
|
+
|
44
|
+
where simulator.conf and vm\_allocation.conf are your simulation environment
|
45
|
+
and vm allocation configuration files respectively.
|
46
|
+
|
47
|
+
The examples directory contains a set of example configuration files, including
|
48
|
+
an [R](http://www.r-project.org) script that models a stochastic request
|
49
|
+
generation process. To use that script, you will need R with the VGAM and
|
50
|
+
truncnorm packages installed.
|
51
|
+
|
52
|
+
Note that the SISFC was not designed to be run directly by users, but instead
|
53
|
+
to be integrated within automated tools that implement a continuous
|
54
|
+
optimization framework (for instance, built on top of our [mhl metaheuristics
|
55
|
+
library](https://github.com/mtortonesi/ruby-mhl)).
|
data/Rakefile
ADDED
data/bin/sisfc
ADDED
@@ -0,0 +1,32 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
# should we actually decomment these lines?
|
4
|
+
# require 'rubygems'
|
5
|
+
# require 'bundler/setup'
|
6
|
+
|
7
|
+
require 'awesome_print'
|
8
|
+
|
9
|
+
require 'sisfc'
|
10
|
+
require 'sisfc/evaluation'
|
11
|
+
|
12
|
+
|
13
|
+
if __FILE__ == $0
|
14
|
+
unless File.exists? ARGV[0] and File.exists? ARGV[1]
|
15
|
+
abort("Usage: #{File.basename(__FILE__)} simulator_config_file vm_allocation_config_file")
|
16
|
+
end
|
17
|
+
|
18
|
+
# load simulation configuration
|
19
|
+
conf = SISFC::Configuration.load_from_file(ARGV[0])
|
20
|
+
|
21
|
+
# load vm allocation
|
22
|
+
vm_allocation = eval(File.read(ARGV[1]))
|
23
|
+
|
24
|
+
# create a simulator and launch it
|
25
|
+
sim = SISFC::Simulation.new(configuration: conf,
|
26
|
+
evaluator: SISFC::Evaluator.new(conf))
|
27
|
+
res = sim.evaluate_allocation(vm_allocation)
|
28
|
+
|
29
|
+
# Print results
|
30
|
+
puts 'Result:'
|
31
|
+
ap(res, indent: 2)
|
32
|
+
end
|
@@ -0,0 +1,45 @@
|
|
1
|
+
#!/usr/bin/Rscript
|
2
|
+
|
3
|
+
suppressMessages(library(VGAM))
|
4
|
+
library(truncnorm) # for truncated normal distribution
|
5
|
+
|
6
|
+
# request generation is modeled with a Pareto distribution
|
7
|
+
# these values were chosen to generate roughly 6666.667 requests per second
|
8
|
+
location <- 1.2E-4
|
9
|
+
shape <- 5
|
10
|
+
requests.per.second <- (shape - 1) / (shape * location)
|
11
|
+
|
12
|
+
# generate 10 minutes of requests
|
13
|
+
simulation.time <- 10 * 60
|
14
|
+
|
15
|
+
# number of requests
|
16
|
+
num.requests <- 1.2 * simulation.time * requests.per.second
|
17
|
+
|
18
|
+
# latency is modeled as a gaussian distribution
|
19
|
+
# these values were chosen to model roughly 100ms of communication latency
|
20
|
+
mu <- 1E-1 # mean latency is 100ms
|
21
|
+
sigma <- 2.5E-2 # standard deviation is 25ms
|
22
|
+
min.latency <- 2E-2 # minimum latency is 20ms
|
23
|
+
|
24
|
+
# number of data centers
|
25
|
+
num.data.centers <- 2
|
26
|
+
|
27
|
+
# request generation times
|
28
|
+
first.request.time <- as.POSIXct(as.Date("18/1/2013", "%d/%m/%Y"))
|
29
|
+
request.interarrival.times <- rpareto(num.requests, location, shape)
|
30
|
+
generation.times <- diffinv(request.interarrival.times, xi=first.request.time)
|
31
|
+
|
32
|
+
# data center ids
|
33
|
+
data.center.ids <- sample.int(num.data.centers, length(generation.times), replace=T)
|
34
|
+
|
35
|
+
# request arrival time
|
36
|
+
latencies <- rtruncnorm(length(generation.times), a=min.latency, b=Inf, mean=mu, sd=sigma)
|
37
|
+
arrival.times <- generation.times + latencies
|
38
|
+
workflow.type.ids <- rep(1, length(generation.times))
|
39
|
+
|
40
|
+
# prepare data frame and output it on the console
|
41
|
+
df <- data.frame(Generation.Time = generation.times,
|
42
|
+
Data.Center.ID = data.center.ids,
|
43
|
+
Arrival.Time = arrival.times,
|
44
|
+
Workflow.Type.ID = workflow.type.ids)
|
45
|
+
write.csv(df[order(df$Arrival.Time),], row.names=F)
|
@@ -0,0 +1,70 @@
|
|
1
|
+
start_time DateTime.civil(2013,1,18,0,0,0)
|
2
|
+
warmup_duration 10.seconds
|
3
|
+
duration 1.minute
|
4
|
+
|
5
|
+
|
6
|
+
data_centers \
|
7
|
+
1 => {
|
8
|
+
:maximum_vm_capacity => {
|
9
|
+
:medium => 50_000,
|
10
|
+
:large => 50_000,
|
11
|
+
},
|
12
|
+
},
|
13
|
+
2 => {
|
14
|
+
:maximum_vm_capacity => {
|
15
|
+
:medium => 50_000,
|
16
|
+
:large => 50_000,
|
17
|
+
},
|
18
|
+
}
|
19
|
+
|
20
|
+
|
21
|
+
service_component_types \
|
22
|
+
'Web Server' => {
|
23
|
+
:allowed_vm_types => [ :medium ],
|
24
|
+
:service_time_distribution => {
|
25
|
+
:medium => { :distribution => :gaussian,
|
26
|
+
:mean => 0.009, # 1 request processed every 9ms
|
27
|
+
:sd => 0.001 },
|
28
|
+
},
|
29
|
+
:estimated_workload => 50,
|
30
|
+
},
|
31
|
+
'App Server' => {
|
32
|
+
:allowed_vm_types => [ :large ],
|
33
|
+
:service_time_distribution => {
|
34
|
+
:large => { :distribution => :gaussian,
|
35
|
+
:mean => 0.012, # 1 request processed every 12ms
|
36
|
+
:sd => 0.002 },
|
37
|
+
},
|
38
|
+
:estimated_workload => 70,
|
39
|
+
}
|
40
|
+
|
41
|
+
|
42
|
+
# workflow (or job) types descriptions
|
43
|
+
workflow_types \
|
44
|
+
1 => {
|
45
|
+
:component_sequence => [
|
46
|
+
{ :name => 'Web Server' }, # no need for :type => dedicated / shared
|
47
|
+
{ :name => 'App Server' },
|
48
|
+
],
|
49
|
+
:next_component_selection => :random,
|
50
|
+
}
|
51
|
+
|
52
|
+
|
53
|
+
# input request source (the generator.R script)
|
54
|
+
request_generation \
|
55
|
+
:command => '<pwd>/generator.R'
|
56
|
+
|
57
|
+
|
58
|
+
# evaluation model
|
59
|
+
evaluation \
|
60
|
+
:vm_hourly_cost => [
|
61
|
+
{ :data_center => 1, :vm_type => :medium, :cost => 0.160 },
|
62
|
+
{ :data_center => 1, :vm_type => :large, :cost => 0.320 },
|
63
|
+
{ :data_center => 2, :vm_type => :medium, :cost => 0.184 },
|
64
|
+
{ :data_center => 2, :vm_type => :large, :cost => 0.368 }
|
65
|
+
],
|
66
|
+
# 500$ penalties if MTTR takes more than 50 msecs
|
67
|
+
:penalties => lambda {|kpis,dc_kpis| 500.0 if kpis[:mttr] > 0.050 }
|
68
|
+
|
69
|
+
|
70
|
+
# vim: filetype=ruby
|
@@ -0,0 +1,8 @@
|
|
1
|
+
[
|
2
|
+
{ dc_id: 1, vm_num: 7, vm_size: :medium, component_type: 'Web Server' },
|
3
|
+
{ dc_id: 1, vm_num: 10, vm_size: :large, component_type: 'App Server' },
|
4
|
+
{ dc_id: 2, vm_num: 15, vm_size: :medium, component_type: 'Web Server' },
|
5
|
+
{ dc_id: 2, vm_num: 20, vm_size: :large, component_type: 'App Server' }
|
6
|
+
]
|
7
|
+
|
8
|
+
# vim: filetype=ruby
|
@@ -0,0 +1,60 @@
|
|
1
|
+
require 'sisfc/support/dsl_helper'
|
2
|
+
|
3
|
+
module SISFC
|
4
|
+
|
5
|
+
module Configurable
|
6
|
+
dsl_accessor :start_time,
|
7
|
+
:duration,
|
8
|
+
:warmup_duration,
|
9
|
+
:request_generation,
|
10
|
+
:data_centers,
|
11
|
+
:service_component_types,
|
12
|
+
:evaluation,
|
13
|
+
:workflow_types
|
14
|
+
end
|
15
|
+
|
16
|
+
class Configuration
|
17
|
+
include Configurable
|
18
|
+
|
19
|
+
attr_accessor :filename
|
20
|
+
|
21
|
+
def initialize(filename)
|
22
|
+
@filename = filename
|
23
|
+
end
|
24
|
+
|
25
|
+
def end_time
|
26
|
+
@start_time + @duration
|
27
|
+
end
|
28
|
+
|
29
|
+
def validate
|
30
|
+
# convert datetimes and integers into floats
|
31
|
+
@start_time = @start_time.to_f
|
32
|
+
@duration = @duration.to_f
|
33
|
+
@warmup_duration = @warmup_duration.to_f
|
34
|
+
|
35
|
+
# TODO: might want to restrict this substitution only to the :filename
|
36
|
+
# and :command keys
|
37
|
+
@request_generation.each do |k,v|
|
38
|
+
v.gsub!('<pwd>', File.expand_path(File.dirname(@filename)))
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
def self.load_from_file(filename)
|
43
|
+
# allow filename, string, and IO objects as input
|
44
|
+
raise ArgumentError, "File #{filename} does not exist!" unless File.exists?(filename)
|
45
|
+
|
46
|
+
# create configuration object
|
47
|
+
conf = Configuration.new(filename)
|
48
|
+
|
49
|
+
# take the file content and pass it to instance_eval
|
50
|
+
conf.instance_eval(File.new(filename, 'r').read)
|
51
|
+
|
52
|
+
# validate and finalize configuration
|
53
|
+
conf.validate
|
54
|
+
|
55
|
+
# return new object
|
56
|
+
conf
|
57
|
+
end
|
58
|
+
|
59
|
+
end
|
60
|
+
end
|
@@ -0,0 +1,59 @@
|
|
1
|
+
module SISFC
|
2
|
+
|
3
|
+
class DataCenter
|
4
|
+
|
5
|
+
def initialize(id, opts)
|
6
|
+
@dcid = id
|
7
|
+
@capacity = opts[:maximum_vm_capacity]
|
8
|
+
@available = @capacity.dup
|
9
|
+
@vms = {}
|
10
|
+
end
|
11
|
+
|
12
|
+
def add_vm(vm, component_name)
|
13
|
+
@vms[component_name] ||= []
|
14
|
+
raise 'Error! VM is already present!' if @vms[component_name].include? vm
|
15
|
+
if @available[vm.size] > 0
|
16
|
+
@available[vm.size] -= 1
|
17
|
+
else
|
18
|
+
raise 'Error! VM capacity exceeded!'
|
19
|
+
end
|
20
|
+
@vms[component_name] << vm
|
21
|
+
end
|
22
|
+
|
23
|
+
def remove_vm(vm, component_name)
|
24
|
+
unless @vms.has_key? component_name
|
25
|
+
raise "Error! Service component type #{component_name} not present in data center #{@dcid}!"
|
26
|
+
end
|
27
|
+
unless @vms[component_name].include? vm
|
28
|
+
raise 'Error! VM not present!'
|
29
|
+
end
|
30
|
+
@available[vm.size] += 1
|
31
|
+
@vms.delete(vm)
|
32
|
+
end
|
33
|
+
|
34
|
+
def get_random_vm(component_name)
|
35
|
+
unless @vms.has_key? component_name
|
36
|
+
raise "Error! Service component type #{component_name} not present in data center #{@dcid}!"
|
37
|
+
end
|
38
|
+
@vms[component_name].sample
|
39
|
+
end
|
40
|
+
|
41
|
+
def get_number_of_vms(component_name)
|
42
|
+
unless @vms.has_key? component_name
|
43
|
+
raise "Error! Service component type #{component_name} not present in data center #{@dcid}!"
|
44
|
+
end
|
45
|
+
@vms[component_name].size
|
46
|
+
end
|
47
|
+
|
48
|
+
def available(size)
|
49
|
+
@available[size]
|
50
|
+
end
|
51
|
+
|
52
|
+
def to_s
|
53
|
+
"Data center #{@dcid}, with VMs:" +
|
54
|
+
@vms.inject("") {|s,(k,v)| s += " (#{k}: #{v.size})" }
|
55
|
+
end
|
56
|
+
|
57
|
+
end
|
58
|
+
|
59
|
+
end
|
@@ -0,0 +1,36 @@
|
|
1
|
+
require 'sisfc/configuration'
|
2
|
+
|
3
|
+
module SISFC
|
4
|
+
class Evaluator
|
5
|
+
def initialize(conf)
|
6
|
+
@vm_hourly_cost = conf.evaluation[:vm_hourly_cost]
|
7
|
+
raise ArgumentError, 'No VM hourly costs provided!' unless @vm_hourly_cost
|
8
|
+
|
9
|
+
@penalties_func = conf.evaluation[:penalties]
|
10
|
+
end
|
11
|
+
|
12
|
+
def evaluate_business_impact(kpis, dc_kpis, vm_allocation)
|
13
|
+
# evaluate VM daily costs
|
14
|
+
cost = vm_allocation.inject(0.0) do |s,x|
|
15
|
+
hc = @vm_hourly_cost.find{|i| i[:data_center] == x[:dc_id] and i[:vm_type] == x[:vm_size] }
|
16
|
+
unless hc
|
17
|
+
raise Error, "Cannot find hourly cost for data center #{x[:dc_id]} and VM size #{x[:vm_size]}!"
|
18
|
+
end
|
19
|
+
s += x[:vm_num] * hc[:cost]
|
20
|
+
end
|
21
|
+
cost *= 24.0
|
22
|
+
kpis[:cost] = cost
|
23
|
+
# puts "vm allocation cost: #{cost}"
|
24
|
+
|
25
|
+
# consider SLO violations
|
26
|
+
penalties = (@penalties_func.nil? ? 0.0 : @penalties_func.call(kpis, dc_kpis))
|
27
|
+
kpis[:penalties] = penalties
|
28
|
+
# puts "slo penalties cost: #{penalties}"
|
29
|
+
cost += penalties
|
30
|
+
|
31
|
+
# we want to minimize the cost, so we define fitness as -cost
|
32
|
+
fitness = -cost
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
data/lib/sisfc/event.rb
ADDED
@@ -0,0 +1,36 @@
|
|
1
|
+
module SISFC
|
2
|
+
|
3
|
+
class Event
|
4
|
+
|
5
|
+
ET_REQUEST_ARRIVAL = 0
|
6
|
+
ET_WORKFLOW_STEP_COMPLETED = 1
|
7
|
+
ET_VM_FREE = 2
|
8
|
+
ET_VM_SUSPEND = 3
|
9
|
+
ET_VM_RESUME = 4
|
10
|
+
ET_REQUEST_CLOSURE = 5
|
11
|
+
ET_END_OF_SIMULATION = 100
|
12
|
+
|
13
|
+
# let the comparable mixin provide the < and > operators for us
|
14
|
+
include Comparable
|
15
|
+
|
16
|
+
# should this be attr_accessor instead?
|
17
|
+
attr_reader :type, :data, :time, :destination
|
18
|
+
|
19
|
+
def initialize(type, data, time, destination)
|
20
|
+
@type = type
|
21
|
+
@data = data
|
22
|
+
@time = time
|
23
|
+
@destination = destination
|
24
|
+
end
|
25
|
+
|
26
|
+
def <=> (event)
|
27
|
+
@time <=> event.time
|
28
|
+
end
|
29
|
+
|
30
|
+
def to_s
|
31
|
+
"Event type: #{@type}, data: #{@data}, time: #{@time}, #{@destination}"
|
32
|
+
end
|
33
|
+
|
34
|
+
end
|
35
|
+
|
36
|
+
end
|
@@ -0,0 +1,80 @@
|
|
1
|
+
require 'csv'
|
2
|
+
require 'sisfc/request'
|
3
|
+
|
4
|
+
|
5
|
+
module SISFC
|
6
|
+
|
7
|
+
class RequestGenerator
|
8
|
+
|
9
|
+
def initialize(opts)
|
10
|
+
if opts.has_key? :filename
|
11
|
+
filename = opts[:filename]
|
12
|
+
raise "File #{filename} does not exist!" unless File.exists?(filename)
|
13
|
+
@file = File.open(filename, mode='r')
|
14
|
+
elsif opts.has_key? :command
|
15
|
+
command = opts[:command]
|
16
|
+
@file = IO.popen(command)
|
17
|
+
else
|
18
|
+
raise ArgumentError, 'Need to provide either a filename or a command!'
|
19
|
+
end
|
20
|
+
|
21
|
+
# throw away the first line (containing the CSV headers)
|
22
|
+
@file.gets
|
23
|
+
|
24
|
+
# NOTE: so far we support only sequential integer rids
|
25
|
+
@next_rid = 0
|
26
|
+
|
27
|
+
setup_finalizer
|
28
|
+
end
|
29
|
+
|
30
|
+
|
31
|
+
def generate
|
32
|
+
# read next line from file
|
33
|
+
line = @file.gets
|
34
|
+
raise "End of input reached while reading request #{@next_rid}!" unless line
|
35
|
+
|
36
|
+
# parse data
|
37
|
+
tokens = line.parse_csv
|
38
|
+
generation_time = tokens[0].to_f
|
39
|
+
data_center_id = tokens[1].to_i
|
40
|
+
arrival_time = tokens[2].to_f
|
41
|
+
workflow_type_id = tokens[3].to_i
|
42
|
+
|
43
|
+
# increase @next_rid
|
44
|
+
@next_rid += 1
|
45
|
+
|
46
|
+
# sanity check
|
47
|
+
if generation_time > arrival_time
|
48
|
+
raise "Generation time (#{generation_time}) is larger than arrival time (#{arrival_time})!"
|
49
|
+
end
|
50
|
+
|
51
|
+
# generate and return request
|
52
|
+
Request.new(@next_rid,
|
53
|
+
generation_time,
|
54
|
+
data_center_id,
|
55
|
+
arrival_time,
|
56
|
+
workflow_type_id)
|
57
|
+
end
|
58
|
+
|
59
|
+
|
60
|
+
private
|
61
|
+
# After object destruction, make sure that the input file is closed or
|
62
|
+
# the input command process is killed.
|
63
|
+
def setup_finalizer
|
64
|
+
ObjectSpace.define_finalizer(self, self.class.close_io(@file))
|
65
|
+
end
|
66
|
+
|
67
|
+
# Need to make this a class method, or the deallocation won't take place. See:
|
68
|
+
# http://www.mikeperham.com/2010/02/24/the-trouble-with-ruby-finalizers/
|
69
|
+
def self.close_io(file)
|
70
|
+
Proc.new do
|
71
|
+
if file.respond_to? :pid
|
72
|
+
Process.kill('INT', file.pid)
|
73
|
+
elsif
|
74
|
+
file.close
|
75
|
+
end
|
76
|
+
end
|
77
|
+
end
|
78
|
+
end
|
79
|
+
|
80
|
+
end
|
@@ -0,0 +1,122 @@
|
|
1
|
+
module SISFC
|
2
|
+
|
3
|
+
class Request
|
4
|
+
|
5
|
+
# states
|
6
|
+
STATE_WORKING = 1
|
7
|
+
STATE_SUSPENDED = 2
|
8
|
+
|
9
|
+
attr_accessor :rid, :generation_time, :data_center_id, :arrival_time,
|
10
|
+
:workflow_type_id, :closure_time #, :status
|
11
|
+
attr_reader :communication_latency
|
12
|
+
|
13
|
+
def initialize(rid, generation_time, data_center_id, arrival_time, workflow_type_id)
|
14
|
+
@rid = rid
|
15
|
+
@generation_time = generation_time
|
16
|
+
@data_center_id = data_center_id
|
17
|
+
@arrival_time = arrival_time
|
18
|
+
@workflow_type_id = workflow_type_id
|
19
|
+
|
20
|
+
# steps count from zero
|
21
|
+
@step = 0
|
22
|
+
|
23
|
+
# calculate communication latency
|
24
|
+
@communication_latency = @arrival_time - @generation_time
|
25
|
+
|
26
|
+
# consider initial communication latency
|
27
|
+
@tracking_information = [
|
28
|
+
{
|
29
|
+
:type => :communication,
|
30
|
+
:at => @generation_time,
|
31
|
+
:duration => @communication_latency,
|
32
|
+
}
|
33
|
+
]
|
34
|
+
|
35
|
+
# NOTE: the format for each element of the @tracking_information array is:
|
36
|
+
# { :type => one of [ :queue, :work, :communication ]
|
37
|
+
# :at => begin time
|
38
|
+
# :duration => duration
|
39
|
+
# :vm => vm (optional)
|
40
|
+
# }
|
41
|
+
end
|
42
|
+
|
43
|
+
def queuing_completed(start, duration)
|
44
|
+
@tracking_information << {
|
45
|
+
:type => :queue,
|
46
|
+
:at => start,
|
47
|
+
:duration => duration,
|
48
|
+
# :vm =>
|
49
|
+
}
|
50
|
+
end
|
51
|
+
|
52
|
+
def step_completed(start, duration)
|
53
|
+
@tracking_information << {
|
54
|
+
:type => :work,
|
55
|
+
:at => start,
|
56
|
+
:duration => duration,
|
57
|
+
# :vm =>
|
58
|
+
}
|
59
|
+
@step += 1
|
60
|
+
end
|
61
|
+
|
62
|
+
def finished_processing(time)
|
63
|
+
# consider final communication latency
|
64
|
+
@tracking_information << {
|
65
|
+
:type => :communication,
|
66
|
+
:at => time,
|
67
|
+
:duration => @communication_latency,
|
68
|
+
}
|
69
|
+
# save closure time
|
70
|
+
@closure_time = time + @communication_latency
|
71
|
+
end
|
72
|
+
|
73
|
+
def next_step
|
74
|
+
@step
|
75
|
+
end
|
76
|
+
|
77
|
+
def with_tracking_information(type=:all)
|
78
|
+
selected_ti = if type == :all
|
79
|
+
@tracking_information
|
80
|
+
else
|
81
|
+
@tracking_information.select{|el| el.type == type }
|
82
|
+
end
|
83
|
+
|
84
|
+
selected_ti.each do |ti|
|
85
|
+
yield ti
|
86
|
+
end
|
87
|
+
end
|
88
|
+
|
89
|
+
def total_communication_time
|
90
|
+
calculate_time(:communication)
|
91
|
+
end
|
92
|
+
|
93
|
+
def total_work_time
|
94
|
+
calculate_time(:work)
|
95
|
+
end
|
96
|
+
|
97
|
+
def total_queue_time
|
98
|
+
calculate_time(:queue)
|
99
|
+
end
|
100
|
+
|
101
|
+
def closed?
|
102
|
+
!@closure_time.nil?
|
103
|
+
end
|
104
|
+
|
105
|
+
def ttr
|
106
|
+
# if incident isn't closed yet, just return nil without raising an exception.
|
107
|
+
@closure_time.nil? ? nil : (@closure_time - @arrival_time)
|
108
|
+
end
|
109
|
+
|
110
|
+
def to_s
|
111
|
+
"rid: #{@rid}, generation_time: #{@generation_time}, data_center_id: #{@data_center_id}, arrival_time: #{@arrival_time}"
|
112
|
+
end
|
113
|
+
|
114
|
+
private
|
115
|
+
|
116
|
+
def calculate_time(type)
|
117
|
+
return 0 unless @tracking_information
|
118
|
+
@tracking_information.inject(0) {|sum,x| sum += ((type == :all || type == x[:type]) ? x[:duration].to_i : 0) }
|
119
|
+
end
|
120
|
+
end
|
121
|
+
|
122
|
+
end
|
@@ -0,0 +1,16 @@
|
|
1
|
+
require 'erv'
|
2
|
+
|
3
|
+
module SISFC
|
4
|
+
class ServiceType
|
5
|
+
attr_reader :level
|
6
|
+
|
7
|
+
def initialize(opts)
|
8
|
+
@level = opts[:level]
|
9
|
+
@rv = ERV::RandomVariable.new(opts[:service_time_distribution])
|
10
|
+
end
|
11
|
+
|
12
|
+
def get_random_service_time
|
13
|
+
@rv.next
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|