openstack-vagrant 1.1.2

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,53 @@
1
+ require "log4r"
2
+ require "json"
3
+
4
+ class ::Hash
5
+ def method_missing(name)
6
+ return self[name] if key? name
7
+ self.each { |k,v| return v if k.to_s.to_sym == name }
8
+ super.method_missing name
9
+ end
10
+ end
11
+
12
+ module VagrantPlugins
13
+ module OpenStack
14
+ module Action
15
+ # This action reads the SSH info for the machine and puts it into the
16
+ # `:machine_ssh_info` key in the environment.
17
+ class ReadSSHInfoFromCache
18
+ def initialize(app, env)
19
+ @app = app
20
+ @logger = Log4r::Logger.new("vagrant_openstack::action::read_ssh_info")
21
+ end
22
+
23
+ def call(env)
24
+ ssh_info = read_ssh_info(env[:machine])
25
+
26
+ if ssh_info and ssh_info[:host] != nil
27
+ env[:machine_ssh_info] = ssh_info
28
+ end
29
+ @app.call(env)
30
+ end
31
+
32
+ def read_ssh_info(machine)
33
+ return nil if machine.id.nil?
34
+
35
+ cached_metadata_file = machine.data_dir.join("cached_metadata")
36
+
37
+ @logger.info("Loading cached metadata from #{cached_metadata_file}")
38
+ server = JSON.load(cached_metadata_file.read) rescue nil
39
+
40
+ config = machine.provider_config
41
+
42
+ host = server.addresses[config.public_network_name].last['addr'] rescue nil
43
+
44
+ return {
45
+ :host => host,
46
+ :port => 22,
47
+ :username => config.ssh_username
48
+ }
49
+ end
50
+ end
51
+ end
52
+ end
53
+ end
@@ -0,0 +1,38 @@
1
+ require "log4r"
2
+
3
+ module VagrantPlugins
4
+ module OpenStack
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_openstack::action::read_state")
12
+ end
13
+
14
+ def call(env)
15
+ env[:machine_state_id] = read_state(env[:openstack_compute], env[:machine])
16
+
17
+ @app.call(env)
18
+ end
19
+
20
+ def read_state(openstack, machine)
21
+ return :not_created if machine.id.nil?
22
+
23
+ # Find the machine
24
+ server = openstack.servers.get(machine.id)
25
+ if server.nil? || server.state == "DELETED"
26
+ # The machine can't be found
27
+ @logger.info("Machine not found or deleted, assuming it got destroyed.")
28
+ machine.id = nil
29
+ return :not_created
30
+ end
31
+
32
+ # Return the state
33
+ return server.state.downcase.to_sym
34
+ end
35
+ end
36
+ end
37
+ end
38
+ end
@@ -0,0 +1,58 @@
1
+ require "log4r"
2
+
3
+ require "vagrant/util/subprocess"
4
+
5
+ module VagrantPlugins
6
+ module OpenStack
7
+ module Action
8
+ # This middleware uses `rsync` to sync the folders over to the
9
+ # remote instance.
10
+ class SyncFolders
11
+ def initialize(app, env)
12
+ @app = app
13
+ @logger = Log4r::Logger.new("vagrant_openstack::action::sync_folders")
14
+ end
15
+
16
+ def call(env)
17
+ @app.call(env)
18
+
19
+ ssh_info = env[:machine].ssh_info
20
+
21
+ env[:machine].config.vm.synced_folders.each do |id, data|
22
+ hostpath = File.expand_path(data[:hostpath], env[:root_path])
23
+ guestpath = data[:guestpath]
24
+ next if data[:disabled]
25
+
26
+ # Make sure there is a trailing slash on the host path to
27
+ # avoid creating an additional directory with rsync
28
+ hostpath = "#{hostpath}/" if hostpath !~ /\/$/
29
+
30
+ env[:ui].info(I18n.t("vagrant_openstack.rsync_folder",
31
+ :hostpath => hostpath,
32
+ :guestpath => guestpath))
33
+
34
+ # Create the guest path
35
+ env[:machine].communicate.sudo("mkdir -p '#{guestpath}'")
36
+ env[:machine].communicate.sudo(
37
+ "chown #{ssh_info[:username]} '#{guestpath}'")
38
+
39
+ # Rsync over to the guest path using the SSH info
40
+ command = [
41
+ "rsync", "--verbose", "--archive", "--compress", "--delete",
42
+ "-e", "ssh -p #{ssh_info[:port]} -i '#{ssh_info[:private_key_path][0]}' -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null",
43
+ hostpath,
44
+ "#{ssh_info[:username]}@#{ssh_info[:host]}:#{guestpath}"]
45
+
46
+ r = Vagrant::Util::Subprocess.execute(*command)
47
+ if r.exit_code != 0
48
+ raise Errors::RsyncError,
49
+ :guestpath => guestpath,
50
+ :hostpath => hostpath,
51
+ :stderr => r.stderr
52
+ end
53
+ end
54
+ end
55
+ end
56
+ end
57
+ end
58
+ end
@@ -0,0 +1,129 @@
1
+ require "pathname"
2
+
3
+ require "vagrant/action/builder"
4
+
5
+ module VagrantPlugins
6
+ module OpenStack
7
+ module Action
8
+ # Include the built-in modules so we can use them as top-level things.
9
+ include Vagrant::Action::Builtin
10
+
11
+ # This action is called to destroy the remote machine.
12
+ def self.action_destroy
13
+ Vagrant::Action::Builder.new.tap do |b|
14
+ b.use ConfigValidate
15
+ b.use Call, IsCreated do |env, b2|
16
+ if !env[:result]
17
+ b2.use MessageNotCreated
18
+ next
19
+ end
20
+
21
+ b2.use ConnectOpenStack
22
+ b2.use DeleteServer
23
+ end
24
+ end
25
+ end
26
+
27
+ # This action is called to read the SSH info of the machine. The
28
+ # resulting state is expected to be put into the `:machine_ssh_info`
29
+ # key.
30
+ def self.action_read_ssh_info
31
+ Vagrant::Action::Builder.new.tap do |b|
32
+ b.use ConfigValidate
33
+ b.use Call, ReadSSHInfoFromCache do |env, b2|
34
+ if !env[:machine_ssh_info]
35
+ b2.use ConnectOpenStack
36
+ b2.use ReadSSHInfoFromAPI
37
+ end
38
+ end
39
+ end
40
+ end
41
+
42
+ # This action is called to read the state of the machine. The
43
+ # resulting state is expected to be put into the `:machine_state_id`
44
+ # key.
45
+ def self.action_read_state
46
+ Vagrant::Action::Builder.new.tap do |b|
47
+ b.use ConfigValidate
48
+ b.use ConnectOpenStack
49
+ b.use ReadState
50
+ end
51
+ end
52
+
53
+ def self.action_ssh
54
+ Vagrant::Action::Builder.new.tap do |b|
55
+ b.use ConfigValidate
56
+ b.use Call, IsCreated do |env, b2|
57
+ if !env[:result]
58
+ b2.use MessageNotCreated
59
+ next
60
+ end
61
+
62
+ b2.use SSHExec
63
+ end
64
+ end
65
+ end
66
+
67
+ def self.action_ssh_run
68
+ Vagrant::Action::Builder.new.tap do |b|
69
+ b.use ConfigValidate
70
+ b.use Call, IsCreated do |env, b2|
71
+ if !env[:result]
72
+ b2.use MessageNotCreated
73
+ next
74
+ end
75
+
76
+ b2.use SSHRun
77
+ end
78
+ end
79
+ end
80
+
81
+ def self.action_up
82
+ Vagrant::Action::Builder.new.tap do |b|
83
+ b.use defined?(HandleBox) ? HandleBox : HandleBoxUrl
84
+ b.use ConfigValidate
85
+ b.use Call, IsCreated do |env, b2|
86
+ if env[:result]
87
+ b2.use MessageAlreadyCreated
88
+ next
89
+ end
90
+
91
+ b2.use ConnectOpenStack
92
+ b2.use Provision
93
+ b2.use SyncFolders
94
+ b2.use SetHostname
95
+ b2.use CreateServer
96
+ end
97
+ end
98
+ end
99
+
100
+ def self.action_provision
101
+ Vagrant::Action::Builder.new.tap do |b|
102
+ b.use ConfigValidate
103
+ b.use Call, IsCreated do |env, b2|
104
+ if !env[:result]
105
+ b2.use MessageNotCreated
106
+ next
107
+ end
108
+
109
+ b2.use Provision
110
+ b2.use SyncFolders
111
+ end
112
+ end
113
+ end
114
+
115
+ # The autoload farm
116
+ action_root = Pathname.new(File.expand_path("../action", __FILE__))
117
+ autoload :ConnectOpenStack, action_root.join("connect_openstack")
118
+ autoload :CreateServer, action_root.join("create_server")
119
+ autoload :DeleteServer, action_root.join("delete_server")
120
+ autoload :IsCreated, action_root.join("is_created")
121
+ autoload :MessageAlreadyCreated, action_root.join("message_already_created")
122
+ autoload :MessageNotCreated, action_root.join("message_not_created")
123
+ autoload :ReadSSHInfoFromAPI, action_root.join("read_ssh_info_from_api")
124
+ autoload :ReadSSHInfoFromCache, action_root.join("read_ssh_info_from_cache")
125
+ autoload :ReadState, action_root.join("read_state")
126
+ autoload :SyncFolders, action_root.join("sync_folders")
127
+ end
128
+ end
129
+ end
@@ -0,0 +1,126 @@
1
+ require "vagrant"
2
+
3
+ module VagrantPlugins
4
+ module OpenStack
5
+ class Config < Vagrant.plugin("2", :config)
6
+ # The API key to access OpenStack.
7
+ #
8
+ # @return [String]
9
+ attr_accessor :api_key
10
+
11
+ # The endpoint to access OpenStack.
12
+ #
13
+ # @return [String]
14
+ attr_accessor :endpoint
15
+
16
+ # Openstack region, if your openstack instance uses these.
17
+ # Rackspace typically uses these. You need to provide their three letter acronym (for example: DFW)
18
+ # @return [String]
19
+ attr_accessor :region
20
+
21
+ # The flavor of server to launch, either the ID or name. This
22
+ # can also be a regular expression to partially match a name.
23
+ attr_accessor :flavor
24
+
25
+ # The name or ID of the image to use. This can also be a regular
26
+ # expression to partially match a name.
27
+ attr_accessor :image
28
+
29
+ # The name of the server. This defaults to the name of the machine
30
+ # defined by Vagrant (via `config.vm.define`), but can be overriden
31
+ # here.
32
+ attr_accessor :server_name
33
+
34
+ # The username to access OpenStack.
35
+ #
36
+ # @return [String]
37
+ attr_accessor :username
38
+
39
+ # The name of the keypair to use.
40
+ #
41
+ # @return [String]
42
+ attr_accessor :keypair_name
43
+
44
+ # The SSH username to use with this OpenStack instance. This overrides
45
+ # the `config.ssh.username` variable.
46
+ #
47
+ # @return [String]
48
+ attr_accessor :ssh_username
49
+
50
+ # User data to be sent to the newly created OpenStack instance. Use this
51
+ # e.g. to inject a script at boot time.
52
+ #
53
+ # @return [String]
54
+ attr_accessor :user_data
55
+
56
+ # Metadata to be sent to the newly created OpenStack instance.
57
+ #
58
+ # @return [Hash]
59
+ attr_accessor :metadata
60
+
61
+ # @return [String]
62
+ attr_accessor :public_network_name
63
+
64
+ # @return [String]
65
+ attr_accessor :networks
66
+
67
+ # @return [String]
68
+ attr_accessor :tenant
69
+
70
+ # @return [Hash]
71
+ attr_accessor :scheduler_hints
72
+
73
+ def initialize
74
+ @api_key = UNSET_VALUE
75
+ @endpoint = UNSET_VALUE
76
+ @region = UNSET_VALUE
77
+ @flavor = UNSET_VALUE
78
+ @image = UNSET_VALUE
79
+ @server_name = UNSET_VALUE
80
+ @username = UNSET_VALUE
81
+ @keypair_name = UNSET_VALUE
82
+ @ssh_username = UNSET_VALUE
83
+ @user_data = UNSET_VALUE
84
+ @metadata = UNSET_VALUE
85
+ @public_network_name = UNSET_VALUE
86
+ @networks = UNSET_VALUE
87
+ @tenant = UNSET_VALUE
88
+ @scheduler_hints = UNSET_VALUE
89
+ end
90
+
91
+ def finalize!
92
+ @api_key = nil if @api_key == UNSET_VALUE
93
+ @endpoint = nil if @endpoint == UNSET_VALUE
94
+ @region = nil if @region == UNSET_VALUE
95
+ @flavor = /m1.tiny/ if @flavor == UNSET_VALUE
96
+ @image = /cirros/ if @image == UNSET_VALUE
97
+ @server_name = nil if @server_name == UNSET_VALUE
98
+ @username = nil if @username == UNSET_VALUE
99
+
100
+ # Keypair defaults to nil
101
+ @keypair_name = nil if @keypair_name == UNSET_VALUE
102
+
103
+ # The SSH values by default are nil, and the top-level config
104
+ # `config.ssh` values are used.
105
+ @ssh_username = nil if @ssh_username == UNSET_VALUE
106
+
107
+ @user_data = "" if @user_data == UNSET_VALUE
108
+ @metadata = {} if @metadata == UNSET_VALUE
109
+
110
+ @public_network_name = "public" if @public_network_name == UNSET_VALUE
111
+ @networks = [@public_network_name] if @networks == UNSET_VALUE
112
+ @tenant = nil if @tenant == UNSET_VALUE
113
+ @scheduler_hints = {} if @scheduler_hints == UNSET_VALUE
114
+ end
115
+
116
+ def validate(machine)
117
+ errors = []
118
+
119
+ errors << I18n.t("vagrant_openstack.config.api_key_required") if !@api_key
120
+ errors << I18n.t("vagrant_openstack.config.username_required") if !@username
121
+
122
+ { "OpenStack Provider" => errors }
123
+ end
124
+ end
125
+ end
126
+ end
@@ -0,0 +1,35 @@
1
+ require "vagrant"
2
+
3
+ module VagrantPlugins
4
+ module OpenStack
5
+ module Errors
6
+ class VagrantOpenStackError < Vagrant::Errors::VagrantError
7
+ error_namespace("vagrant_openstack.errors")
8
+ end
9
+
10
+ class CreateBadState < VagrantOpenStackError
11
+ error_key(:create_bad_state)
12
+ end
13
+
14
+ class SshUnavailable < VagrantOpenStackError
15
+ error_key(:ssh_unavailable)
16
+ end
17
+
18
+ class NoMatchingFlavor < VagrantOpenStackError
19
+ error_key(:no_matching_flavor)
20
+ end
21
+
22
+ class NoMatchingImage < VagrantOpenStackError
23
+ error_key(:no_matching_image)
24
+ end
25
+
26
+ class NoMatchingNetwork < VagrantOpenStackError
27
+ error_key(:no_matching_network)
28
+ end
29
+
30
+ class RsyncError < VagrantOpenStackError
31
+ error_key(:rsync_error)
32
+ end
33
+ end
34
+ end
35
+ end
@@ -0,0 +1,37 @@
1
+ begin
2
+ require "vagrant"
3
+ rescue LoadError
4
+ raise "The OpenStack Cloud provider must be run within Vagrant."
5
+ end
6
+
7
+ # This is a sanity check to make sure no one is attempting to install
8
+ # this into an early Vagrant version.
9
+ if Vagrant::VERSION < "1.2.0"
10
+ raise "OpenStack Cloud provider is only compatible with Vagrant 1.2+"
11
+ end
12
+
13
+ module VagrantPlugins
14
+ module OpenStack
15
+ class Plugin < Vagrant.plugin("2")
16
+ name "OpenStack Cloud"
17
+ description <<-DESC
18
+ This plugin enables Vagrant to manage machines in OpenStack Cloud.
19
+ DESC
20
+
21
+ config(:openstack, :provider) do
22
+ require_relative "config"
23
+ Config
24
+ end
25
+
26
+ provider(:openstack, parallel: true) do
27
+ # Setup some things
28
+ OpenStack.init_i18n
29
+ OpenStack.init_logging
30
+
31
+ # Load the actual provider
32
+ require_relative "provider"
33
+ Provider
34
+ end
35
+ end
36
+ end
37
+ end
@@ -0,0 +1,50 @@
1
+ require "vagrant"
2
+
3
+ require "vagrant-openstack/action"
4
+
5
+ module VagrantPlugins
6
+ module OpenStack
7
+ class Provider < Vagrant.plugin("2", :provider)
8
+ def initialize(machine)
9
+ @machine = machine
10
+ end
11
+
12
+ def action(name)
13
+ # Attempt to get the action method from the Action class if it
14
+ # exists, otherwise return nil to show that we don't support the
15
+ # given action.
16
+ action_method = "action_#{name}"
17
+ return Action.send(action_method) if Action.respond_to?(action_method)
18
+ nil
19
+ end
20
+
21
+ def ssh_info
22
+ # Run a custom action called "read_ssh_info" which does what it
23
+ # says and puts the resulting SSH info into the `:machine_ssh_info`
24
+ # key in the environment.
25
+ env = @machine.action("read_ssh_info")
26
+ env[:machine_ssh_info]
27
+ end
28
+
29
+ def state
30
+ # Run a custom action we define called "read_state" which does
31
+ # what it says. It puts the state in the `:machine_state_id`
32
+ # key in the environment.
33
+ env = @machine.action("read_state")
34
+
35
+ state_id = env[:machine_state_id]
36
+
37
+ # Get the short and long description
38
+ short = I18n.t("vagrant_openstack.states.short_#{state_id}")
39
+ long = I18n.t("vagrant_openstack.states.long_#{state_id}")
40
+
41
+ # Return the MachineState object
42
+ Vagrant::MachineState.new(state_id, short, long)
43
+ end
44
+
45
+ def to_s
46
+ "OpenStack Cloud"
47
+ end
48
+ end
49
+ end
50
+ end
@@ -0,0 +1,5 @@
1
+ module VagrantPlugins
2
+ module OpenStack
3
+ VERSION = "1.1.2"
4
+ end
5
+ end
@@ -0,0 +1,53 @@
1
+ require "pathname"
2
+
3
+ require "vagrant-openstack/plugin"
4
+
5
+ module VagrantPlugins
6
+ module OpenStack
7
+ lib_path = Pathname.new(File.expand_path("../vagrant-openstack", __FILE__))
8
+ autoload :Errors, lib_path.join("errors")
9
+
10
+ # This initializes the i18n load path so that the plugin-specific
11
+ # translations work.
12
+ def self.init_i18n
13
+ I18n.load_path << File.expand_path("locales/en.yml", source_root)
14
+ I18n.reload!
15
+ end
16
+
17
+ # This initializes the logging so that our logs are outputted at
18
+ # the same level as Vagrant core logs.
19
+ def self.init_logging
20
+ # Initialize logging
21
+ level = nil
22
+ begin
23
+ level = Log4r.const_get(ENV["VAGRANT_LOG"].upcase)
24
+ rescue NameError
25
+ # This means that the logging constant wasn't found,
26
+ # which is fine. We just keep `level` as `nil`. But
27
+ # we tell the user.
28
+ level = nil
29
+ end
30
+
31
+ # Some constants, such as "true" resolve to booleans, so the
32
+ # above error checking doesn't catch it. This will check to make
33
+ # sure that the log level is an integer, as Log4r requires.
34
+ level = nil if !level.is_a?(Integer)
35
+
36
+ # Set the logging level on all "vagrant" namespaced
37
+ # logs as long as we have a valid level.
38
+ if level
39
+ logger = Log4r::Logger.new("vagrant_openstack")
40
+ logger.outputters = Log4r::Outputter.stderr
41
+ logger.level = level
42
+ logger = nil
43
+ end
44
+ end
45
+
46
+ # This returns the path to the source of this plugin.
47
+ #
48
+ # @return [Pathname]
49
+ def self.source_root
50
+ @source_root ||= Pathname.new(File.expand_path("../../", __FILE__))
51
+ end
52
+ end
53
+ end
data/locales/en.yml ADDED
@@ -0,0 +1,81 @@
1
+ en:
2
+ vagrant_openstack:
3
+ already_created: |-
4
+ The server is already created.
5
+ deleting_server: |-
6
+ Deleting server...
7
+ finding_flavor: |-
8
+ Finding flavor for server...
9
+ finding_image: |-
10
+ Finding image for server...
11
+ finding_network: |-
12
+ Finding network for server...
13
+ launching_server: |-
14
+ Launching a server with the following settings...
15
+ not_created: |-
16
+ The server hasn't been created yet. Run `vagrant up` first.
17
+ ready: |-
18
+ The server is ready!
19
+ rsync_folder: |-
20
+ Rsyncing folder: %{hostpath} => %{guestpath}
21
+ waiting_for_build: |-
22
+ Waiting for the server to be built...
23
+ waiting_for_ssh: |-
24
+ Waiting for SSH to become available...
25
+
26
+ config:
27
+ api_key_required: |-
28
+ An API key is required.
29
+ username_required: |-
30
+ A username is required.
31
+
32
+ errors:
33
+ create_bad_state: |-
34
+ While creating the server, it transitioned to an unexpected
35
+ state: '%{state}', instead of properly booting. Run `vagrant status`
36
+ to find out what can be done about this state, or `vagrant destroy`
37
+ if you want to start over.
38
+ ssh_unavailable: |-
39
+ The server has been created successfully but it refuses incomming
40
+ ssh connections.
41
+ no_matching_flavor: |-
42
+ No matching flavor was found! Please check your flavor setting
43
+ to make sure you have a valid flavor chosen.
44
+ no_matching_image: |-
45
+ No matching image was found! Please check your image setting to
46
+ make sure you have a valid image chosen.
47
+ no_matching_network: |-
48
+ No network was found for name "%{network_name}". Please check your
49
+ network setting to make sure you have a valid network chosen.
50
+ rsync_error: |-
51
+ There was an error when attemping to rsync a share folder.
52
+ Please inspect the error message below for more info.
53
+
54
+ Host path: %{hostpath}
55
+ Guest path: %{guestpath}
56
+ Error: %{stderr}
57
+
58
+ states:
59
+ short_active: |-
60
+ active
61
+ long_active: |-
62
+ The server is up and running. Run `vagrant ssh` to access it.
63
+ short_build: |-
64
+ building
65
+ long_build: |-
66
+ The server is currently being built. You must wait for this to
67
+ complete before you can access it. You can delete the server, however,
68
+ by running `vagrant destroy`.
69
+ short_shutoff: |-
70
+ shutoff
71
+ long_shutoff: |-
72
+ The server is shutoff.
73
+ short_error: |-
74
+ error
75
+ long_error: |-
76
+ The server is in an erroneous state. Destroy the machine with
77
+ `vagrant destroy`.
78
+ short_not_created: |-
79
+ not created
80
+ long_not_created: |-
81
+ The server is not created. Run `vagrant up` to create it.
@@ -0,0 +1,24 @@
1
+ # -*- encoding: utf-8 -*-
2
+ lib = File.expand_path('../lib', __FILE__)
3
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
+ require 'vagrant-openstack/version'
5
+
6
+ Gem::Specification.new do |gem|
7
+ gem.name = "openstack-vagrant"
8
+ gem.version = VagrantPlugins::OpenStack::VERSION
9
+ gem.authors = ["Mitchell Hashimoto"]
10
+ gem.email = ["mitchell@hashicorp.com"]
11
+ gem.description = "Enables Vagrant to manage machines in OpenStack Cloud."
12
+ gem.summary = "Enables Vagrant to manage machines in OpenStack Cloud."
13
+ gem.homepage = "http://www.vagrantup.com"
14
+
15
+ gem.add_runtime_dependency "fog", "~> 1.22"
16
+
17
+ gem.add_development_dependency "rake"
18
+ gem.add_development_dependency "rspec", "~> 2.13.0"
19
+
20
+ gem.files = `git ls-files`.split($/)
21
+ gem.executables = gem.files.grep(%r{^bin/}).map{ |f| File.basename(f) }
22
+ gem.test_files = gem.files.grep(%r{^(test|spec|features)/})
23
+ gem.require_paths = ["lib"]
24
+ end