sisfc 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
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
@@ -0,0 +1,4 @@
1
+ source 'https://rubygems.org'
2
+
3
+ # Specify your gem's dependencies in sisfc.gemspec
4
+ gemspec
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
@@ -0,0 +1,9 @@
1
+ require 'bundler/gem_tasks'
2
+
3
+ require 'rake/testtask'
4
+
5
+ Rake::TestTask.new do |t|
6
+ t.libs << 'test'
7
+ t.test_files = Dir.glob('test/**/*_test.rb').sort
8
+ t.verbose = true
9
+ end
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
+
@@ -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