vagrant-cloudstack 0.0.5

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,47 @@
1
+ require "fog"
2
+ require "log4r"
3
+
4
+ module VagrantPlugins
5
+ module Cloudstack
6
+ module Action
7
+ # This action connects to Cloudstack, verifies credentials work, and
8
+ # puts the Cloudstack connection object into the
9
+ # `:cloudstack_compute` key in the environment.
10
+ class ConnectCloudstack
11
+ def initialize(app, env)
12
+ @app = app
13
+ @logger = Log4r::Logger.new("vagrant_cloudstack::action::connect_cloudstack")
14
+ end
15
+
16
+ def call(env)
17
+ # Get the domain we're going to booting up in
18
+ domain = env[:machine].provider_config.domain
19
+
20
+ # Get the configs
21
+ domain_config = env[:machine].provider_config.get_domain_config(domain)
22
+
23
+ # Build the fog config
24
+ fog_config = {
25
+ :provider => :cloudstack
26
+ #:domain => domain_config
27
+ }
28
+
29
+ if domain_config.api_key
30
+ fog_config[:cloudstack_api_key] = domain_config.api_key
31
+ fog_config[:cloudstack_secret_access_key] = domain_config.secret_key
32
+ end
33
+
34
+ fog_config[:cloudstack_host] = domain_config.host if domain_config.host
35
+ fog_config[:cloudstack_path] = domain_config.path if domain_config.path
36
+ fog_config[:cloudstack_port] = domain_config.port if domain_config.port
37
+ fog_config[:cloudstack_scheme] = domain_config.scheme if domain_config.scheme
38
+
39
+ @logger.info("Connecting to Cloudstack...")
40
+ env[:cloudstack_compute] = Fog::Compute.new(fog_config)
41
+
42
+ @app.call(env)
43
+ end
44
+ end
45
+ end
46
+ end
47
+ end
@@ -0,0 +1,18 @@
1
+ module VagrantPlugins
2
+ module Cloudstack
3
+ module Action
4
+ # This can be used with "Call" built-in to check if the machine
5
+ # is created and branch in the middleware.
6
+ class IsCreated
7
+ def initialize(app, env)
8
+ @app = app
9
+ end
10
+
11
+ def call(env)
12
+ env[:result] = env[:machine].state.id != :not_created
13
+ @app.call(env)
14
+ end
15
+ end
16
+ end
17
+ end
18
+ end
@@ -0,0 +1,16 @@
1
+ module VagrantPlugins
2
+ module Cloudstack
3
+ module Action
4
+ class MessageAlreadyCreated
5
+ def initialize(app, env)
6
+ @app = app
7
+ end
8
+
9
+ def call(env)
10
+ env[:ui].info(I18n.t("vagrant_cloudstack.already_created"))
11
+ @app.call(env)
12
+ end
13
+ end
14
+ end
15
+ end
16
+ end
@@ -0,0 +1,16 @@
1
+ module VagrantPlugins
2
+ module Cloudstack
3
+ module Action
4
+ class MessageNotCreated
5
+ def initialize(app, env)
6
+ @app = app
7
+ end
8
+
9
+ def call(env)
10
+ env[:ui].info(I18n.t("vagrant_cloudstack.not_created"))
11
+ @app.call(env)
12
+ end
13
+ end
14
+ end
15
+ end
16
+ end
@@ -0,0 +1,41 @@
1
+ require "log4r"
2
+
3
+ module VagrantPlugins
4
+ module Cloudstack
5
+ module Action
6
+ # This action reads the SSH info for the machine and puts it into the
7
+ # `:machine_ssh_info` key in the environment.
8
+ class ReadSSHInfo
9
+ def initialize(app, env)
10
+ @app = app
11
+ @logger = Log4r::Logger.new("vagrant_cloudstack::action::read_ssh_info")
12
+ end
13
+
14
+ def call(env)
15
+ env[:machine_ssh_info] = read_ssh_info(env[:cloudstack_compute], env[:machine])
16
+
17
+ @app.call(env)
18
+ end
19
+
20
+ def read_ssh_info(cloudstack, machine)
21
+ return nil if machine.id.nil?
22
+
23
+ # Find the machine
24
+ server = cloudstack.servers.get(machine.id)
25
+ if server.nil?
26
+ # The machine can't be found
27
+ @logger.info("Machine couldn't be found, assuming it got destroyed.")
28
+ machine.id = nil
29
+ return nil
30
+ end
31
+
32
+ # Read the DNS info
33
+ return {
34
+ :host => server.nics[0]['ipaddress'],
35
+ :port => 22
36
+ }
37
+ end
38
+ end
39
+ end
40
+ end
41
+ end
@@ -0,0 +1,38 @@
1
+ require "log4r"
2
+
3
+ module VagrantPlugins
4
+ module Cloudstack
5
+ module Action
6
+ # This action reads the state of the machine and puts it in the
7
+ # `:machine_state_id` key in the environment.
8
+ class ReadState
9
+ def initialize(app, env)
10
+ @app = app
11
+ @logger = Log4r::Logger.new("vagrant_cloudstack::action::read_state")
12
+ end
13
+
14
+ def call(env)
15
+ env[:machine_state_id] = read_state(env[:cloudstack_compute], env[:machine])
16
+
17
+ @app.call(env)
18
+ end
19
+
20
+ def read_state(cloudstack, machine)
21
+ return :not_created if machine.id.nil?
22
+
23
+ # Find the machine
24
+ server = cloudstack.servers.get(machine.id)
25
+ if server.nil? || [:"shutting-down", :terminated].include?(server.state.to_sym)
26
+ # The machine can't be found
27
+ @logger.info("Machine not found or terminated, assuming it got destroyed.")
28
+ machine.id = nil
29
+ return :not_created
30
+ end
31
+
32
+ # Return the state
33
+ return server.state.to_sym
34
+ end
35
+ end
36
+ end
37
+ end
38
+ end
@@ -0,0 +1,148 @@
1
+ require "log4r"
2
+ require 'pp' # XXX FIXME REMOVE WHEN NOT NEEDED
3
+
4
+ require 'vagrant/util/retryable'
5
+
6
+ require 'vagrant-cloudstack/util/timer'
7
+
8
+ module VagrantPlugins
9
+ module Cloudstack
10
+ module Action
11
+ # This runs the configured instance.
12
+ class RunInstance
13
+ include Vagrant::Util::Retryable
14
+
15
+ def initialize(app, env)
16
+ @app = app
17
+ @logger = Log4r::Logger.new("vagrant_cloudstack::action::run_instance")
18
+ end
19
+
20
+ def call(env)
21
+ # Initialize metrics if they haven't been
22
+ env[:metrics] ||= {}
23
+
24
+ # Get the domain we're going to booting up in
25
+ domain = env[:machine].provider_config.domain
26
+
27
+ # Get the configs
28
+ domain_config = env[:machine].provider_config.get_domain_config(domain)
29
+ zone_id = domain_config.zone_id
30
+ network_id = domain_config.network_id
31
+ project_id = domain_config.project_id
32
+ service_offering_id = domain_config.service_offering_id
33
+ template_id = domain_config.template_id
34
+
35
+ # Launch!
36
+ env[:ui].info(I18n.t("vagrant_cloudstack.launching_instance"))
37
+ env[:ui].info(" -- Service offering UUID: #{service_offering_id}")
38
+ env[:ui].info(" -- Template UUID: #{template_id}")
39
+ env[:ui].info(" -- Project UUID: #{project_id}") if project_id != nil
40
+ env[:ui].info(" -- Zone UUID: #{zone_id}")
41
+ env[:ui].info(" -- Network UUID: #{network_id}") if network_id
42
+
43
+ local_user = ENV['USER'].dup
44
+ local_user.gsub!(/[^-a-z0-9_]/i, "")
45
+ prefix = env[:root_path].basename.to_s
46
+ prefix.gsub!(/[^-a-z0-9_]/i, "")
47
+ display_name = local_user + "_" + prefix + "_#{Time.now.to_i}"
48
+
49
+ begin
50
+ options = {
51
+ :display_name => display_name,
52
+ :zone_id => zone_id,
53
+ :flavor_id => service_offering_id,
54
+ :image_id => template_id,
55
+ :network_ids => [network_id]
56
+ }
57
+
58
+ options['project_id'] = project_id if project_id != nil
59
+
60
+ server = env[:cloudstack_compute].servers.create(options)
61
+ rescue Fog::Compute::Cloudstack::NotFound => e
62
+ # Invalid subnet doesn't have its own error so we catch and
63
+ # check the error message here.
64
+ # XXX FIXME vpc?
65
+ if e.message =~ /subnet ID/
66
+ raise Errors::FogError,
67
+ :message => "Subnet ID not found: #{network_id}"
68
+ end
69
+
70
+ raise
71
+ rescue Fog::Compute::Cloudstack::Error => e
72
+ raise Errors::FogError, :message => e.message
73
+ end
74
+
75
+ # Immediately save the ID since it is created at this point.
76
+ # XXX FIXME does cloudstack+fog return the job id rather than
77
+ # server id?
78
+ env[:machine].id = server.id
79
+
80
+ # Wait for the instance to be ready first
81
+ env[:metrics]["instance_ready_time"] = Util::Timer.time do
82
+ tries = domain_config.instance_ready_timeout / 2
83
+
84
+ env[:ui].info(I18n.t("vagrant_cloudstack.waiting_for_ready"))
85
+ begin
86
+ retryable(:on => Fog::Errors::TimeoutError, :tries => tries) do
87
+ # If we're interrupted don't worry about waiting
88
+ next if env[:interrupted]
89
+
90
+ # Wait for the server to be ready
91
+ server.wait_for(2) { ready? }
92
+ end
93
+ rescue Fog::Errors::TimeoutError
94
+ # Delete the instance
95
+ terminate(env)
96
+
97
+ # Notify the user
98
+ raise Errors::InstanceReadyTimeout,
99
+ timeout: domain_config.instance_ready_timeout
100
+ end
101
+ end
102
+
103
+ @logger.info("Time to instance ready: #{env[:metrics]["instance_ready_time"]}")
104
+
105
+ if !env[:interrupted]
106
+ env[:metrics]["instance_ssh_time"] = Util::Timer.time do
107
+ # Wait for SSH to be ready.
108
+ env[:ui].info(I18n.t("vagrant_cloudstack.waiting_for_ssh"))
109
+ while true
110
+ # If we're interrupted then just back out
111
+ break if env[:interrupted]
112
+ break if env[:machine].communicate.ready?
113
+ sleep 2
114
+ end
115
+ end
116
+
117
+ @logger.info("Time for SSH ready: #{env[:metrics]["instance_ssh_time"]}")
118
+
119
+ # Ready and booted!
120
+ env[:ui].info(I18n.t("vagrant_cloudstack.ready"))
121
+ end
122
+
123
+ # Terminate the instance if we were interrupted
124
+ terminate(env) if env[:interrupted]
125
+
126
+ @app.call(env)
127
+ end
128
+
129
+ def recover(env)
130
+ return if env["vagrant.error"].is_a?(Vagrant::Errors::VagrantError)
131
+
132
+ if env[:machine].provider.state.id != :not_created
133
+ # Undo the import
134
+ terminate(env)
135
+ end
136
+ end
137
+
138
+ def terminate(env)
139
+ destroy_env = env.dup
140
+ destroy_env.delete(:interrupted)
141
+ destroy_env[:config_validate] = false
142
+ destroy_env[:force_confirm_destroy] = true
143
+ env[:action_runner].run(Action.action_destroy, destroy_env)
144
+ end
145
+ end
146
+ end
147
+ end
148
+ end
@@ -0,0 +1,85 @@
1
+ require "log4r"
2
+
3
+ require "vagrant/util/subprocess"
4
+
5
+ require "vagrant/util/scoped_hash_override"
6
+
7
+ require "vagrant/util/which"
8
+
9
+ module VagrantPlugins
10
+ module Cloudstack
11
+ module Action
12
+ # This middleware uses `rsync` to sync the folders over to the
13
+ # Cloudstack instance.
14
+ class SyncFolders
15
+ include Vagrant::Util::ScopedHashOverride
16
+
17
+ def initialize(app, env)
18
+ @app = app
19
+ @logger = Log4r::Logger.new("vagrant_cloudstack::action::sync_folders")
20
+ end
21
+
22
+ def call(env)
23
+ @app.call(env)
24
+
25
+ ssh_info = env[:machine].ssh_info
26
+
27
+ env[:machine].config.vm.synced_folders.each do |id, data|
28
+ data = scoped_hash_override(data, :cloudstack)
29
+
30
+ # Ignore disabled shared folders
31
+ next if data[:disabled]
32
+
33
+ unless Vagrant::Util::Which.which('rsync')
34
+ env[:ui].warn(I18n.t('vagrant_cloudstack.rsync_not_found_warning'))
35
+ break
36
+ end
37
+
38
+ hostpath = File.expand_path(data[:hostpath], env[:root_path])
39
+ guestpath = data[:guestpath]
40
+
41
+ # Make sure there is a trailing slash on the host path to
42
+ # avoid creating an additional directory with rsync
43
+ hostpath = "#{hostpath}/" if hostpath !~ /\/$/
44
+
45
+ # on windows rsync.exe requires cygdrive-style paths
46
+ if Vagrant::Util::Platform.windows?
47
+ hostpath = hostpath.gsub(/^(\w):/) { "/cygdrive/#{$1}" }
48
+ end
49
+
50
+ env[:ui].info(I18n.t("vagrant_cloudstack.rsync_folder",
51
+ :hostpath => hostpath,
52
+ :guestpath => guestpath))
53
+
54
+ # Create the guest path
55
+ env[:machine].communicate.sudo("mkdir -p '#{guestpath}'")
56
+ env[:machine].communicate.sudo(
57
+ "chown #{ssh_info[:username]} '#{guestpath}'")
58
+
59
+ # Rsync over to the guest path using the SSH info
60
+ command = [
61
+ "rsync", "--verbose", "--archive", "-z",
62
+ "--exclude", ".vagrant/",
63
+ "-e", "ssh -p #{ssh_info[:port]} -o StrictHostKeyChecking=no -i '#{ssh_info[:private_key_path]}'",
64
+ hostpath,
65
+ "#{ssh_info[:username]}@#{ssh_info[:host]}:#{guestpath}"]
66
+
67
+ # we need to fix permissions when using rsync.exe on windows, see
68
+ # http://stackoverflow.com/questions/5798807/rsync-permission-denied-created-directories-have-no-permissions
69
+ if Vagrant::Util::Platform.windows?
70
+ command.insert(1, "--chmod", "ugo=rwX")
71
+ end
72
+
73
+ r = Vagrant::Util::Subprocess.execute(*command)
74
+ if r.exit_code != 0
75
+ raise Errors::RsyncError,
76
+ :guestpath => guestpath,
77
+ :hostpath => hostpath,
78
+ :stderr => r.stderr
79
+ end
80
+ end
81
+ end
82
+ end
83
+ end
84
+ end
85
+ end
@@ -0,0 +1,26 @@
1
+ require "log4r"
2
+
3
+ module VagrantPlugins
4
+ module Cloudstack
5
+ module Action
6
+ # This terminates the running instance.
7
+ class TerminateInstance
8
+ def initialize(app, env)
9
+ @app = app
10
+ @logger = Log4r::Logger.new("vagrant_cloudstack::action::terminate_instance")
11
+ end
12
+
13
+ def call(env)
14
+ server = env[:cloudstack_compute].servers.get(env[:machine].id)
15
+
16
+ # Destroy the server and remove the tracking ID
17
+ env[:ui].info(I18n.t("vagrant_cloudstack.terminating"))
18
+ server.destroy
19
+ env[:machine].id = nil
20
+
21
+ @app.call(env)
22
+ end
23
+ end
24
+ end
25
+ end
26
+ end