sisfc 0.0.1
Sign up to get free protection for your applications and to get access to all the features.
- 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
|